Documentation
¶
Overview ¶
Package meeting implements a daemon-owned, explicitly started Recall.ai meeting bridge.
Index ¶
- Constants
- Variables
- func CheckRoom(ctx context.Context, ref RoomRef, key string) error
- func MeetingPlatform(raw string) (string, error)
- func NewOpusVoiceSpeech(provider, key string) func(ctx context.Context, voice, text string) ([]byte, error)
- func NewOpusVoiceSpeechStream(provider, key string) func(ctx context.Context, voice, text string) (io.ReadCloser, error)
- func Phase(state string, failed bool) string
- func RecordToolCall(ctx context.Context, tool, target, status string)
- func Redact(v string) string
- func ReportActivity(ctx context.Context, label string)
- func ReportDir(ryshDir, meetingID string) string
- func RevokeInvite(ctx context.Context, ref RoomRef, key, inviteID string) error
- func STTKeyName(stt string) string
- func SpeechKeyName(tts string) string
- func TurnSpeaker(ctx context.Context) string
- func ValidAgentName(name string) error
- func ValidInviteEmail(s string) bool
- func ValidInviteID(id string) bool
- func ValidLang(l string) bool
- func ValidMeetingID(id string) bool
- func ValidSTT(p string) bool
- func ValidTTS(p string) bool
- func WithActivityReporter(ctx context.Context, f func(string)) context.Context
- func WithAuditRecorder(ctx context.Context, agent, speaker string, f func(AuditEntry)) context.Context
- type Agent
- type AuditEntry
- type Command
- type Credentials
- type Execute
- type Invite
- type InviteRequest
- type LiveKitConfig
- type LiveKitCredentials
- type LiveKitDialOptions
- type LiveKitEvent
- type LiveKitProbe
- type LiveKitRoom
- type Manager
- type Meeting
- type OpusSpeech
- type ParticipantGrant
- type Reply
- type ReportAssignment
- type ReportConfig
- type ReportEvent
- type ReportItem
- type ReportNotes
- type ReportStatus
- type ReportTopic
- type RoomRef
- type Segment
- type Service
- type Synthesize
- type SynthesizeStream
- type Transcriber
- type Transport
- type TransportStatus
- type ZoomCredentials
Constants ¶
const ( LiveKitURLSecret = "LIVEKIT_URL" // ws(s):// or http(s):// of the LiveKit server LiveKitKeySecret = "LIVEKIT_API_KEY" // API key (token issuer) LiveKitSecretSecret = "LIVEKIT_API_SECRET" // API secret (token signing key) LiveKitJoinURLSecret = "LIVEKIT_JOIN_URL" // optional: join page base; default a local LiveKit Meet )
Secret names for the LiveKit server; values live in the session store.
const ( LiveKitUtterance = "utterance" // a final STT segment from one participant LiveKitLeft = "left" // a participant left the room )
LiveKitEvent kinds.
const ( KindWeb = "web" // Google Meet / Zoom / Teams; its bot is a Recall bot KindLiveKit = "livekit" // a LiveKit room; its bot is a LiveKit participant )
Meeting kinds (design 043 §Registry data model).
const ( ProviderDeepgram = "deepgram" ProviderElevenLabs = "elevenlabs" ProviderOpenAI = "openai" DefaultTTS = ProviderDeepgram DefaultSTT = ProviderDeepgram )
Provider names, as typed in --tts/--stt and MEETING_TTS/MEETING_STT.
const ( PlatformMeet = "google_meet" PlatformZoom = "zoom" PlatformTeams = "microsoft_teams" // PlatformLiveKit is a rysh-hosted LiveKit room (`rysh meeting host`). PlatformLiveKit = "livekit" )
Platforms Recall.ai joins for us. The platform is chosen from the meeting URL; nothing else in the join differs today (see createPayload).
const ( WallJoining = "joining" WallListening = "listening" WallHeard = "heard" WallApproval = "awaiting_approval" // operator gate (approve mode only) WallThinking = "thinking" WallPreparing = "preparing_speech" WallSpeaking = "speaking" WallError = "error" WallPaused = "paused" WallEnded = "ended" )
Wall phases: the names Phase returns. Keep in sync with LABELS in media.html.
const ( ZoomClientIDSecret = "ZOOM_OAUTH_CLIENT_ID" ZoomClientSecretSecret = "ZOOM_OAUTH_CLIENT_SECRET" ZoomRefreshTokenSecret = "ZOOM_OAUTH_REFRESH_TOKEN" )
Secret names; values live only in the session secret store.
const MaxAgentBots = 4
MaxAgentBots caps the agent bots in one meeting (D9): every bot transcribes the whole call, so each one adds a Recall/STT bill.
const ReporterConsent = "participants know a rysh reporter bot joins to write a meeting report: the meeting's speech is transcribed (Recall or Deepgram), " +
"the transcript is kept in memory only until it is summarized and is never saved, and the report (topics, previous tasks, blockers, plan, assignments, with speakers' display names) is saved in this workspace"
ReporterConsent is what `--consent` confirms for a reporter.
const ReporterName = "reporter"
ReporterName is the reporter's agent name: its public-URL path and its LiveKit identity (agent-reporter). `##meeting add agent <id> reporter` means the reporter, so no ##agent of that name can join a meeting.
const ReporterTranscriptConsent = "participants know a rysh reporter bot joins to write a meeting report and that the raw transcript of this meeting is kept: " + "the meeting's speech is transcribed (Recall or Deepgram) and every line, with the speaker's display name and time, is saved in this workspace in " + TranscriptFile + " beside the report, readable by the workspace owner only; " + "it is kept until the owner deletes it, and nothing deletes it automatically. The report (topics, previous tasks, blockers, plan, assignments, with speakers' display names) is saved there too"
ReporterTranscriptConsent is what `--transcript-consent` confirms when the owner turns on keeping the raw transcript (H-61). It replaces ReporterConsent, whose "never saved" would then be false.
const RyshAPIKeySecret = "RYSH_API_KEY"
RyshAPIKeySecret is the session secret holding a rysh workspace API key (`rysh_…`) with the write permission; without it the daemon's upstream key (upstream.api_key) is used.
const TranscriptFile = "transcript.txt"
TranscriptFile is the kept raw transcript in a meeting's report directory (H-61 opt-in only).
const UpstreamAPIKeyName = "upstream.api_key"
UpstreamAPIKeyName labels the session's upstream.api_key in doctor output.
Variables ¶
var ( LiveKitVoiceSpeech func(tts, key string) func(ctx context.Context, voice, text string) ([]byte, error) LiveKitVoiceSpeechStream func(tts, key string) func(ctx context.Context, voice, text string) (io.ReadCloser, error) )
LiveKitVoiceSpeech and LiveKitVoiceSpeechStream are the same in an agent's own voice. Without them every agent in a room speaks in the default voice — two agent bots (D9) would sound the same.
var DialLiveKit func(ctx context.Context, serverURL, token string, opts LiveKitDialOptions) (LiveKitRoom, error)
DialLiveKit connects the bot to a room (livekit.go registers it). If nil, `host` still creates the room and prints the join link, but reports that the bot could not join.
var DoctorSecretNames = []string{"RECALL_API_KEY", "RECALL_VERIFICATION_SECRET", "OPENAI_API_KEY", "DEEPGRAM_API_KEY", "ELEVENLABS_API_KEY", ZoomClientIDSecret, ZoomClientSecretSecret, ZoomRefreshTokenSecret, RyshAPIKeySecret, UpstreamAPIKeyName, LiveKitURLSecret, LiveKitKeySecret, LiveKitSecretSecret}
DoctorSecretNames is the order doctor output lists its checks in.
var LiveKitSpeech func(tts, key string) Synthesize
LiveKitSpeech, when set, replaces the Service's reply synthesis for LiveKit rooms so the audio arrives in the format Play publishes without transcoding (tts is a ValidTTS provider, key its API key).
var LiveKitSpeechStream func(tts, key string) SynthesizeStream
LiveKitSpeechStream, when set, streams default-voice replies: playback starts with the provider's first Ogg page instead of the whole file.
Functions ¶
func MeetingPlatform ¶
MeetingPlatform validates a meeting link against a strict per-platform allowlist (https only, exact hosts, known paths and query keys, no credentials or fragments) and returns the Recall platform it belongs to. Redirect, shortened or mail-wrapped links are refused: Recall itself only joins real platform URLs, and we never ask a third party to fetch an arbitrary host on the operator's behalf.
func NewOpusVoiceSpeech ¶
func NewOpusVoiceSpeech(provider, key string) func(ctx context.Context, voice, text string) ([]byte, error)
NewOpusVoiceSpeech is NewOpusSpeech in a chosen voice (an agent's; "" is the provider default), so agent bots in a rysh room sound distinct (D9).
func NewOpusVoiceSpeechStream ¶
func NewOpusVoiceSpeechStream(provider, key string) func(ctx context.Context, voice, text string) (io.ReadCloser, error)
NewOpusVoiceSpeechStream is NewOpusSpeechStream in a chosen voice.
func Phase ¶
Phase maps a raw service state to the progress vocabulary the bot's camera tile shows (MTG-13): joining, listening, heard, awaiting_approval (--approve only), thinking, preparing_speech, speaking, error, paused, ended. failed is true while a hands-free turn says failurePhrase, which reads as error.
func RecordToolCall ¶
RecordToolCall appends a tool call to the meeting audit log. It is a no-op outside a meeting turn. target is redacted and clipped here.
func Redact ¶
Redact masks credential-looking parts of a tool target: sensitive KEY=value assignments, bearer tokens, well-known token prefixes, URL userinfo and long opaque strings. Best effort; tool targets are labels, not payloads, and SecretNAT placeholders pass through unchanged.
func ReportActivity ¶
ReportActivity lets the executor describe what it is doing for the current turn ("running agent docs", "reading README.md"). The label is shown on the meeting's camera tile, so it must never contain secrets. It is a no-op outside a meeting turn.
func RevokeInvite ¶
RevokeInvite revokes one invite; the server also takes its guest out of the room.
func STTKeyName ¶
STTKeyName is the session secret an STT provider needs.
func SpeechKeyName ¶
SpeechKeyName is the session secret a TTS provider needs.
func TurnSpeaker ¶
TurnSpeaker returns the participant label of the current meeting turn.
func ValidAgentName ¶
ValidAgentName reports whether name can be attached to a meeting.
func ValidInviteEmail ¶
ValidInviteEmail accepts a bare address (no display name, no brackets).
func ValidInviteID ¶
ValidInviteID reports whether id is an invite id (a UUID) as `invites` lists it.
func ValidMeetingID ¶
ValidMeetingID reports whether id has the registry's id shape.
func WithActivityReporter ¶
WithActivityReporter binds ReportActivity to f, for executors tested outside a Service.
func WithAuditRecorder ¶
func WithAuditRecorder(ctx context.Context, agent, speaker string, f func(AuditEntry)) context.Context
WithAuditRecorder binds RecordToolCall to f outside a Service (tests, other executors).
Types ¶
type Agent ¶
type Agent struct {
Name string
Voice string
Execute Execute
// Model labels the model this agent's turns run on (its `model:` when
// configured in the session, else the session model); status shows it.
Model string
// Note explains a fallback (e.g. an unconfigured `model:`); shown on attach.
Note string
}
Agent is a Rysh agent (`##agent spawn`) attached to a meeting. Its name is its wake word ("hey <name>"), Voice selects its TTS voice ("" = a distinct default) and Execute answers as its persona. The daemon builds Execute from the agent's definition inside the meeting's isolated executor, never from the live agent actor: see design 040 §MTG-12.
type AuditEntry ¶
type AuditEntry struct {
Time time.Time `json:"time"`
Meeting string `json:"meeting,omitempty"` // Recall bot id
// MeetingID is the registry id (m-xxxxxx, design 043) of the meeting
// the turn ran in.
MeetingID string `json:"meeting_id,omitempty"`
Turn string `json:"turn"`
Agent string `json:"agent"`
Speaker string `json:"speaker"` // display name: a label, not authentication
Tool string `json:"tool"`
Target string `json:"target,omitempty"` // redacted, ≤ 160 runes
Status string `json:"status"` // started | ok | error | blocked_by_policy | incomplete (started, never ended: the turn stopped first)
}
AuditEntry is one tool call made from a meeting turn (founder decision 2026-09-28: agents use all their tools with no approval; the allowlist, kill switch and this log are the controls). It never carries tool output, secret values or meeting transcript beyond the tool's own target label.
The audit log is the RECORD of what ran, sub-agent calls (every depth) included: each call has exactly one "started" and one terminal entry. The meeting wall activity (ReportActivity: "thinking with …", "running …") is best-effort LIVE progress, NOT a record: labels for a turn that already ended may never appear, and it must not be cited as evidence of what ran.
type Command ¶
type Command struct {
Action string `json:"action"`
// ID names the meeting (m-xxxxxx, design 043); "" = the only meeting with
// a bot, else the only meeting. join/host without it create a meeting.
ID string `json:"id,omitempty"`
URL string `json:"url,omitempty"`
PublicURL string `json:"public_url,omitempty"`
Listen string `json:"listen,omitempty"`
Region string `json:"region,omitempty"`
ReadDir string `json:"read_dir,omitempty"`
TTS string `json:"tts,omitempty"` // openai | deepgram | elevenlabs (design 048)
STT string `json:"stt,omitempty"` // deepgram | elevenlabs; rysh rooms only (Recall transcribes itself)
Lang string `json:"lang,omitempty"` // ISO 639 code the agents answer and speak in; "" = English (design 048)
Consent bool `json:"consent,omitempty"`
// KeepTranscript keeps a reporter's raw transcript on disk for this
// meeting (H-61, 045 D5; off by default). It needs TranscriptConsent:
// the reporter's own Consent does not cover it.
KeepTranscript bool `json:"keep_transcript,omitempty"`
TranscriptConsent bool `json:"transcript_consent,omitempty"`
AutoSpeak bool `json:"auto_speak,omitempty"`
// Approve restores per-turn local approval (approve, then speak). Without
// it the bot is hands-free: an allowed speaker's request runs and the
// reply is spoken with no terminal input (founder decision 2026-09-27).
Approve bool `json:"approve,omitempty"`
// Allow lists who may wake the bot: display names and/or the keywords
// "host" and "anyone". Empty means "host". Names are labels the
// participant chose, not authentication.
Allow []string `json:"allow,omitempty"`
// AllowFromFlag marks an Allow the operator typed on `##meeting add
// agent`; an attach refuses it (the allowlist is fixed at bot start).
// Set by the daemon, never taken from a client.
AllowFromFlag bool `json:"-"`
Minutes int `json:"minutes,omitempty"`
TurnID string `json:"turn_id,omitempty"`
// Agents names ##agent-spawned agents to attach at join ("hey <name>");
// the first one also answers "hey rysh". Agent is the attach/detach target.
Agents []string `json:"agents,omitempty"`
Agent string `json:"agent,omitempty"`
// Model labels the session model plain "hey rysh" turns run on. Set by the
// daemon at join, never taken from a client.
Model string `json:"-"`
// AuditPath is the JSONL file meeting tool calls are appended to
// (audit.go). Set by the daemon under the session dir, never by a client.
AuditPath string `json:"-"`
// ZoomOBF gives Recall a zoom.obf_token_url on this listener so a Zoom
// meeting hosted by another account can be joined on behalf of an
// authorised Zoom user (zoomobf.go). Off by default.
ZoomOBF bool `json:"zoom_obf,omitempty"`
// Room names the LiveKit room `host` creates; "" = a random rysh-… name.
Room string `json:"room,omitempty"`
// Server, for host, is a rysh-server base URL: the room, the bot token
// and the invite come from it instead of local LiveKit signing.
Server string `json:"server,omitempty"`
// PaneID is the meeting pane a `register` binds (##meeting join). Set by
// the daemon, never taken from a client.
PaneID string `json:"-"`
// Reporter commands (design 045), all set by the daemon: ReportDir is
// the meeting's report directory (<rysh dir>/meetings/<id>), Origin the
// pane told when the report is written, Session the session's name.
ReportDir string `json:"-"`
Origin string `json:"-"`
Session string `json:"-"`
// contains filtered or unexported fields
}
func (Command) ValidateHost ¶
ValidateHost checks a `host` command. The room server comes from session secrets, not arguments, so only local choices are validated here.
type Credentials ¶
type Credentials struct {
Recall, Verification, Speech string
Zoom ZoomCredentials // only with --zoom-obf
LiveKit LiveKitCredentials // only for host
RyshAPIKey string // host --server: rysh API key with write
// UpstreamAPIKey reports (doctor only) that the session has an
// upstream.api_key, which host --server uses when RYSH_API_KEY is unset.
UpstreamAPIKey bool
}
type Invite ¶
type Invite struct {
ID string `json:"id"`
TokenPrefix string `json:"token_prefix"`
Label string `json:"label"`
GuestName string `json:"guest_name"`
AllowGuest bool `json:"allow_guest"`
MaxUses int `json:"max_uses"`
Uses int `json:"uses"`
ExpiresAt string `json:"expires_at"`
RevokedAt string `json:"revoked_at,omitempty"`
LastUsedAt string `json:"last_used_at,omitempty"`
JoinURL string `json:"-"`
GuestIdentity string `json:"-"`
}
Invite is one invite as the server reports it. JoinURL (with its secret) is only set right after creation.
func CreateInvite ¶
CreateInvite asks the room's server for an invite (owner only).
type InviteRequest ¶
type InviteRequest struct {
Email string // personal invite labelled with this address
Guest bool // personal invite without an address
Name string // the guest's default display name
TTL time.Duration // 0 = the server's default (24 h, never past the room)
}
InviteRequest is what `##meeting invite` asks the rysh server for.
func (InviteRequest) Personal ¶
func (r InviteRequest) Personal() bool
Personal reports whether the request is for one person (a /join/ link).
type LiveKitConfig ¶
type LiveKitConfig struct {
URL string // ws(s):// signalling URL
APIKey string
APISecret string
Room string
}
LiveKitConfig names a room and the API key pair that mints tokens for it; used by the synthetic participant (LiveKitProbe) and the dev harness.
type LiveKitCredentials ¶
type LiveKitCredentials struct {
URL, APIKey, APISecret string
JoinPage string // "" = defaultJoinPage
STTKey string // STTKeyName(Command.STT): per-participant streaming STT
}
LiveKitCredentials are the LiveKit server and its API key pair.
type LiveKitDialOptions ¶
type LiveKitDialOptions struct {
STT, Lang, STTKey string
// Keyterms is the bot's current agent names, read by the transcriber
// to bias recognition towards them (E-85); nil = only "Rysh".
Keyterms func() []string
}
LiveKitDialOptions carries what the room client needs besides the token: the streaming STT provider (Command.STT, "" = Deepgram), the language (Command.Lang, "" = English) and the provider's key, for per-participant transcription.
type LiveKitEvent ¶
LiveKitEvent is one room event. Identity comes from the participant's signed token. Start/End are seconds since the bot joined; zero means "now" and the transport stamps the arrival time.
type LiveKitProbe ¶
type LiveKitProbe struct {
// contains filtered or unexported fields
}
LiveKitProbe is a synthetic human for the spike's end-to-end check: a STANDARD participant that speaks prepared Ogg/Opus into the room and records what the other participants (the bot) send back.
func JoinLiveKitProbe ¶
func JoinLiveKitProbe(cfg LiveKitConfig, identity string) (*LiveKitProbe, error)
JoinLiveKitProbe joins cfg.Room as identity (a normal participant).
type LiveKitRoom ¶
type LiveKitRoom interface {
// Events delivers final utterances and participant/connection changes;
// it is closed when the connection ends.
Events() <-chan LiveKitEvent
// Play publishes one reply on the bot's audio track and returns when it
// has been sent, or when ctx is cancelled (barge-in, stop). audio is what
// the Service's speech produced: LiveKitSpeech's format when set (LK-1
// uses Ogg/Opus), else the MP3 of the Recall route.
Play(ctx context.Context, audio []byte) error
Close() error
}
LiveKitRoom is the bot's live connection to a room. The WebRTC side — server-sdk-go, one streaming STT session per participant audio track, and publishing reply audio on the bot's own track — implements it (LK-1, livekit.go) and registers itself in DialLiveKit.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager is the session's meeting registry (design 043): many meetings keyed by id, each holding its bots — one per agent added with `##meeting add agent` (D9), or the one bot of a CLI join/host.
func NewManager ¶
func NewManager() *Manager
func (*Manager) Do ¶
Do runs one meeting command. agents carries the resolved `##agent` definitions for join (--agent) and attach; other actions ignore it. Commands without c.ID act on the only meeting with a bot, else the only meeting; join and host without c.ID create a new pane-less meeting.
m.mu guards only the registry and is never held across provider I/O (E30 PM-3c): a bot start or leave reserves its meeting (Meeting.busy) under the lock, talks to Recall/LiveKit/rysh-server without it, and commits under it again. Registry reads therefore never wait on a provider, and the workspace mailbox that calls them never stalls.
func (*Manager) SetReportHook ¶
func (m *Manager) SetReportHook(f func(ReportEvent))
SetReportHook sets the function told about every report written (final, interim or failed). Set it before the first report_on.
type Meeting ¶
type Meeting struct {
ID string // m-xxxxxx
Kind string // KindWeb | KindLiveKit
Platform string // PlatformMeet | PlatformZoom | PlatformTeams | PlatformLiveKit
Target string // meeting URL (web) or room id (livekit)
Server string // rysh-server base (livekit rooms)
PaneID string // "" for pane-less CLI meetings
// Bots are the meeting's bots in start order: one per agent for
// `##meeting add agent` (D9), or the one bot of a CLI join/host, which
// may carry several attached agents.
Bots []*Service
CreatedAt time.Time
// Reporter is the meeting's listen-only reporter bot (design 045), kept
// apart from Bots: it answers no wake, is not an agent and does not
// count toward MaxAgentBots. lastReport is the newest report written.
Reporter *Service
// contains filtered or unexported fields
}
Meeting is one entry of the session's meeting registry. A pane meeting (##meeting join) has a PaneID and no bot until an agent is added; a CLI `join`/`host` without --id creates a pane-less entry that holds its bot.
func (*Meeting) BusyError ¶
Lookup returns a copy of the meeting with id. BusyError is the refusal a start, leave or agent add/remove on this meeting gets right now ("" when it is not busy). The grammar layer prints it in the invoking pane before it starts anything (E-82); the registry still decides, so a start that slips in between is refused the same way.
type OpusSpeech ¶
OpusSpeech synthesizes text to Ogg/Opus.
func NewOpusSpeech ¶
func NewOpusSpeech(provider, key string) OpusSpeech
NewOpusSpeech is the Recall route's TTS asking for Ogg/Opus instead of MP3: OpenAI gpt-4o-mini-tts (coral), Deepgram Aura 2 or ElevenLabs Flash v2.5.
type ParticipantGrant ¶
type ParticipantGrant struct {
URL string `json:"url"`
Token string `json:"token"`
Identity string `json:"identity"`
ExpiresAt string `json:"expires_at"`
}
ParticipantGrant is the human participant's LiveKit access for a meeting pane. It is handed to the desktop app and never stored or logged.
func ParticipantToken ¶
ParticipantToken asks the rysh server for a participant token for the key's account in ref's room (POST /api/meeting-rooms/<room>/token).
type Reply ¶
type Reply struct {
ID string `json:"id,omitempty"` // meeting id (m-xxxxxx)
Kind string `json:"kind,omitempty"` // "web" | "livekit"
PaneID string `json:"pane_id,omitempty"` // meeting pane; "" = pane-less
Target string `json:"target,omitempty"` // meeting URL (web) or room id (livekit)
Meetings []Reply `json:"meetings,omitempty"` // list
Bots []Reply `json:"bots,omitempty"` // one status per bot of the meeting (D9), in start order
State string `json:"state"`
Platform string `json:"platform,omitempty"`
BotID string `json:"bot_id,omitempty"`
TurnID string `json:"turn_id,omitempty"`
Speaker string `json:"speaker,omitempty"`
Request string `json:"request,omitempty"` // explicit local `pending` only
Result string `json:"result,omitempty"` // explicit local `pending` only
Error string `json:"error,omitempty"`
Notice string `json:"notice,omitempty"`
Phase string `json:"phase,omitempty"` // status-wall vocabulary, see Phase
Mode string `json:"mode,omitempty"` // "hands_free" or "approve"
MediaConnected bool `json:"media_connected"`
SegmentsHeard int `json:"segments_heard,omitempty"`
IgnoredWakes int `json:"ignored_wakes,omitempty"` // wakes from speakers outside the allowlist
Allow []string `json:"allow,omitempty"` // who may wake the bot in this meeting: display names, "host", "anyone"
Secrets map[string]bool `json:"secrets,omitempty"`
Agent string `json:"agent,omitempty"` // agent addressed by the pending turn; "" = rysh
Agents []string `json:"agents,omitempty"` // agents attached to this meeting
Voices []string `json:"voices,omitempty"` // each attached agent's TTS voice, in Agents order ("" = provider default)
Model string `json:"model,omitempty"` // model answering the pending turn
ToolCalls int `json:"tool_calls"` // audited tool calls from meeting turns
AuditLog string `json:"audit_log,omitempty"` // file holding those entries
Room string `json:"room,omitempty"` // LiveKit room (`host`)
JoinURL string `json:"join_url,omitempty"` // link for people to join that room (`host` reply only)
Report *ReportStatus `json:"report,omitempty"` // the meeting's reporter (design 045)
LastReport string `json:"last_report,omitempty"` // the newest report written for this meeting
}
type ReportAssignment ¶
type ReportConfig ¶
type ReportConfig struct {
MeetingID, PaneID, Origin string
Platform, Session string
Dir string
Summarize Execute
OnEvent func(ReportEvent)
KeepTranscript bool // H-61: also append the raw transcript to TranscriptFile
}
ReportConfig is what the daemon gives a reporter.
type ReportEvent ¶
type ReportEvent struct {
MeetingID, PaneID, Origin string
Path string
Interim bool
Error string
Summary string // one line for the notice: counts per section
}
ReportEvent tells the daemon a report was written (or could not be).
func RecoverReport ¶
func RecoverReport(dir string, summarize Execute) (ReportEvent, error)
RecoverReport writes report.md from a notes checkpoint left by a reporter whose daemon stopped before the meeting ended (045 D7). The transcript heard after the last checkpoint is gone; the report says so.
type ReportItem ¶
type ReportItem struct {
Owner string `json:"owner,omitempty"`
Item string `json:"item"`
Status string `json:"status,omitempty"`
}
ReportItem is a previous task/goal (with Status as reported) or a blocker.
type ReportNotes ¶
type ReportNotes struct {
Overview string `json:"overview,omitempty"`
Topics []ReportTopic `json:"topics,omitempty"`
PreviousItems []ReportItem `json:"previous_items,omitempty"`
Blockers []ReportItem `json:"blockers,omitempty"`
Plan []string `json:"plan,omitempty"`
Assignments []ReportAssignment `json:"assignments,omitempty"`
Decisions []string `json:"decisions,omitempty"`
}
ReportNotes is the structured content of a report: what one chunk's summary returns, what the final pass merges them into, and what the markdown is rendered from.
type ReportStatus ¶
type ReportStatus struct {
State string `json:"state"` // listening | summarizing | writing | written | failed
BotState string `json:"bot_state,omitempty"` // the reporter bot's state (joining, listening, ended, …)
Segments int `json:"segments"` // transcript segments heard (count only)
Chunks int `json:"chunks"` // chunks summarized so far
Buffered int `json:"buffered_chars"` // transcript in memory awaiting its summary
Participants []string `json:"participants,omitempty"` // speakers heard, by display name
Path string `json:"path,omitempty"` // report.md once written (interim or final)
Interim bool `json:"interim,omitempty"` // Path holds an interim report
Transcript string `json:"transcript,omitempty"` // the kept raw transcript (H-61 opt-in); "" = not kept
Error string `json:"error,omitempty"`
}
ReportStatus is the reporter's part of a meeting's status reply.
type ReportTopic ¶
type RoomRef ¶
type RoomRef struct {
Server string // rysh-server base, e.g. https://rysh.ai
Room string // room id
// ExpiresAt is when the room ends (RFC 3339), set by CreateRoom only.
ExpiresAt string
}
RoomRef is a rysh room a meeting pane shows.
func CreateRoom ¶
CreateRoom opens a new room on the session's rysh server for a meeting pane (`##meeting join new`). The room lives for ttl (rounded up to hours).
func ParseRoomRef ¶
ParseRoomRef reads `rysh:<room-id>` or `https://<server>/meet/<room-id>` against the session's rysh server. ok is false when arg is neither form (it may be a Meet/Zoom/Teams link); err is set when it is one of them but unusable — a /meet/ link on another host is refused rather than followed, because following it would send the session's key there.
type Segment ¶
type Segment struct {
Participant int // stable per participant for this meeting
Name string // display label; never authority by itself
Identity string // authenticated identity (LiveKit token); "" on Recall
Host bool // the platform/room says this participant is the host
Start, End float64 // seconds, relative to the meeting's own clock
Words []string
}
Segment is one finalized transcript segment from one participant, in the transport-neutral form acceptLocked consumes.
type SynthesizeStream ¶
SynthesizeStream starts synthesis and returns the audio as it arrives (the provider's HTTP body); closing it abandons the request.
func NewOpusSpeechStream ¶
func NewOpusSpeechStream(provider, key string) SynthesizeStream
NewOpusSpeechStream is NewOpusSpeech returning the audio as it arrives, so playback can start on the first Ogg page. Both providers stream the body.
type Transcriber ¶
Transcriber consumes one participant's Ogg/Opus byte stream and calls emit for every final utterance until audio closes or ctx ends.
func DeepgramTranscriber ¶
func DeepgramTranscriber(key string) Transcriber
DeepgramTranscriber streams containerized Ogg/Opus to Deepgram's live API (nova-3, endpointing 300 ms, keyterm "Rysh" so the wake word is spelled as such more often) and emits each endpointed utterance. WebRTC senders go quiet in silence (DTX, or a finished file), so after 700 ms without audio it asks Deepgram to Finalize instead of waiting for more.
func ElevenLabsTranscriber ¶
func ElevenLabsTranscriber(key string) Transcriber
ElevenLabsTranscriber streams a participant to ElevenLabs Scribe v2 Realtime. The endpoint takes PCM only, so the track's Ogg/Opus is decoded here (pure Go, 16 kHz mono) — the one place the meeting path decodes audio. ElevenLabs' VAD commits each utterance; like Deepgram's Finalize, 700 ms without audio (a sender in DTX silence) commits what is pending.
func NewTranscriber ¶
func NewTranscriber(provider, lang, key string, names func() []string) Transcriber
NewTranscriber is the rysh-room STT for provider in lang ("" = English), key its API key. names, when set, is the bot's current agent names: they and "Rysh" bias recognition (sttKeyterms, E-85), read when each participant stream connects. Deepgram also follows a change mid-stream.
type Transport ¶
type Transport interface {
// Kind names the backbone: "recall" or "livekit".
Kind() string
// Join puts the bot into the meeting and returns its id. A *joinError
// says whether a failure is definite (nothing was created) or uncertain.
Join(ctx context.Context) (id string, err error)
// Leave removes the bot. A *leaveError carries the operator notice.
Leave(ctx context.Context) error
// Status reports admission/end; an error is a status outage whose text
// is the pause notice.
Status(ctx context.Context) (TransportStatus, error)
// MediaReadyLocked reports whether audio can be delivered to the meeting.
MediaReadyLocked() bool
// SpeakLocked starts playing one reply; completion is reported through
// Service.playbackLocked(turnID, ok). An error means delivery never
// started.
SpeakLocked(turnID string, audio []byte) error
// ClearLocked stops any playback in progress.
ClearLocked()
}
Transport is the meeting backbone a Service talks through (design 042, LK-2). Recall joins someone else's meeting (Meet, Zoom, Teams); LiveKit joins a room rysh hosts. Everything above it — wake detection, allowlist, hands-free turns, agents, stop phrases, audit log, progress wall — is the same Service code for both.
Methods named ...Locked run with Service.mu held and must not block on the network; the others run without it.
type TransportStatus ¶
type TransportStatus struct {
Unknown bool // nothing to report yet; leave state as it is
Admitted bool // the bot is in the call and hears participants
Ended bool // the meeting ended or the bot was removed
Why string // with Ended: the provider's reason, when it gave one
Waiting string // while joining: the host action the bot waits for
Lost string // after joining: why hearing stopped
}
TransportStatus is one status observation.
type ZoomCredentials ¶
type ZoomCredentials struct {
ClientID, ClientSecret, RefreshToken string
SaveRefresh func(string) error
}
ZoomCredentials is the Zoom OAuth app (General app with Meeting SDK) and the authorising user's refresh token. SaveRefresh persists a rotated refresh token: Zoom invalidates the old one on each refresh.