@alexkroman1/aai-ui 0.12.3 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +253 -0
- package/dist/_colors-CpZO-88A.js +78 -0
- package/dist/_module-url-C13kAJ87.js +15 -0
- package/dist/_page-shell.d.ts +11 -0
- package/dist/_recover-run.d.ts +79 -0
- package/dist/_repeat-until.d.ts +30 -0
- package/dist/_run-controls.d.ts +33 -0
- package/dist/_submission-state.d.ts +92 -0
- package/dist/_tap-to-talk-state.d.ts +88 -0
- package/dist/_utils-Mi-SCFA1.js +47 -0
- package/dist/_utils.d.ts +26 -0
- package/dist/_web-storage.d.ts +43 -0
- package/dist/_workflow-api-ref.d.ts +37 -0
- package/dist/_workflow-files.d.ts +34 -0
- package/dist/_workflow-test-defs.d.ts +34 -0
- package/dist/aai-logo-D8pMVn9_.js +85 -0
- package/dist/agent-state-labels.d.ts +65 -0
- package/dist/agent-state.d.ts +108 -0
- package/dist/audio/capture.d.ts +44 -0
- package/dist/audio/index.d.ts +18 -0
- package/dist/audio/index.js +431 -0
- package/dist/audio/preconnect.d.ts +87 -0
- package/dist/audio/voice-io.d.ts +112 -0
- package/dist/build-default-client.d.ts +1 -0
- package/dist/chat-view-BCbI7gD4.js +396 -0
- package/dist/client-config-C7fQrioM.js +139 -0
- package/dist/client-config.d.ts +119 -0
- package/dist/client-dir.d.ts +44 -0
- package/dist/client-dir.js +58 -0
- package/dist/client-identity.d.ts +131 -0
- package/dist/client-tool.d.ts +51 -0
- package/dist/components/_colors.d.ts +73 -0
- package/dist/components/_form-readiness.d.ts +56 -0
- package/dist/components/_form-values.d.ts +19 -0
- package/dist/components/aai-logo.d.ts +12 -0
- package/dist/components/audio-result.d.ts +94 -0
- package/dist/components/auto-scroll.d.ts +74 -0
- package/dist/components/bullet-list.d.ts +74 -0
- package/dist/components/button.d.ts +29 -21
- package/dist/components/button.js +62 -23
- package/dist/components/chat-view.d.ts +24 -18
- package/dist/components/chat-view.js +2 -60
- package/dist/components/console-shell.d.ts +85 -0
- package/dist/components/controls.d.ts +21 -6
- package/dist/components/controls.js +31 -19
- package/dist/components/conversation-view.d.ts +130 -0
- package/dist/components/eyebrow.d.ts +12 -0
- package/dist/components/facts.d.ts +81 -0
- package/dist/components/form-fields.d.ts +142 -0
- package/dist/components/form-types.d.ts +73 -0
- package/dist/components/form.d.ts +96 -0
- package/dist/components/markdown.d.ts +57 -0
- package/dist/components/message-list.d.ts +34 -7
- package/dist/components/message-list.js +2 -75
- package/dist/components/session-controls.d.ts +125 -0
- package/dist/components/session-error-banner.d.ts +69 -0
- package/dist/components/session-state-dot.d.ts +111 -0
- package/dist/components/sidebar-layout.d.ts +31 -10
- package/dist/components/sidebar-layout.js +32 -12
- package/dist/components/start-screen.d.ts +17 -6
- package/dist/components/start-screen.js +34 -14
- package/dist/components/tool-call-block.d.ts +15 -14
- package/dist/components/tool-call-block.js +2 -117
- package/dist/components/tool-call-row.d.ts +75 -0
- package/dist/components/tool-config-context.d.ts +22 -0
- package/dist/components/upload-progress.d.ts +76 -0
- package/dist/components/url-chips.d.ts +29 -0
- package/dist/components/workflow-fields.d.ts +46 -0
- package/dist/components/workflow-pending-note.d.ts +81 -0
- package/dist/components/workflow-progress.d.ts +81 -0
- package/dist/components/workflow-run-error.d.ts +48 -0
- package/dist/components/workflow-run-panel.d.ts +79 -0
- package/dist/context.d.ts +288 -0
- package/dist/context.js +382 -0
- package/dist/conversation-log.d.ts +88 -0
- package/dist/default-client/assets/_module-url-BX0RuRU2.js +1 -0
- package/dist/default-client/assets/audio-mpgipo2Q.js +1 -0
- package/dist/default-client/assets/capture-processor-D0q7Qrdd.js +90 -0
- package/dist/default-client/assets/index-8ihb1Urm.css +2 -0
- package/dist/default-client/assets/index-DPfmSFmc.js +126 -0
- package/dist/default-client/assets/playback-processor-CjDbc9iH.js +314 -0
- package/dist/default-client/favicon.ico +0 -0
- package/dist/default-client/index.html +15 -0
- package/dist/default-client-entry.d.ts +6 -0
- package/dist/default-client.d.ts +19 -0
- package/dist/define-client.d.ts +169 -26
- package/dist/define-client.js +209 -55
- package/dist/eyebrow-UfmSz9yy.js +27 -0
- package/dist/hooks-DN4gHYsj.js +182 -0
- package/dist/hooks.d.ts +172 -0
- package/dist/hooks.js +3 -0
- package/dist/hooks.test-d.d.ts +14 -0
- package/dist/inbox-protocol.d.ts +128 -0
- package/dist/inbox.d.ts +132 -0
- package/dist/index.d.ts +126 -29
- package/dist/index.js +5500 -15
- package/dist/internal.d.ts +43 -0
- package/dist/internal.js +8 -0
- package/dist/linked-client.d.ts +70 -0
- package/dist/message-list-DHZcLhRA.js +682 -0
- package/dist/notice-player.d.ts +70 -0
- package/dist/page.d.ts +114 -0
- package/dist/phone.d.ts +50 -0
- package/dist/route-fetch.d.ts +79 -0
- package/dist/session/audio-effects.d.ts +37 -0
- package/dist/session/audio-setup.d.ts +57 -0
- package/dist/session/audio-state.d.ts +157 -0
- package/dist/session/browser-session.d.ts +39 -0
- package/dist/session/close.d.ts +26 -0
- package/dist/session/dial.d.ts +65 -0
- package/dist/session/handshake.d.ts +60 -0
- package/dist/session/index.d.ts +17 -0
- package/dist/session/index.js +2 -0
- package/dist/session/messages.d.ts +63 -0
- package/dist/session/mic.d.ts +64 -0
- package/dist/session/preconnect.d.ts +48 -0
- package/dist/session/reconnect.d.ts +25 -0
- package/dist/session/resume-store.d.ts +57 -0
- package/dist/session/state.d.ts +146 -0
- package/dist/session/ticket.d.ts +44 -0
- package/dist/session/types.d.ts +476 -0
- package/dist/session/url.d.ts +17 -0
- package/dist/session/user-turn.d.ts +55 -0
- package/dist/session-D9jM5-TF.js +2140 -0
- package/dist/stored-value.d.ts +103 -0
- package/dist/tool-call-block-uXtMOUWN.js +204 -0
- package/dist/tool-config-context-Es4YUzV2.js +19 -0
- package/dist/types.d.ts +272 -34
- package/dist/types.js +35 -4
- package/dist/upload/files.d.ts +83 -0
- package/dist/upload/index.d.ts +15 -0
- package/dist/upload/pause.d.ts +31 -0
- package/dist/upload/recall.d.ts +67 -0
- package/dist/upload/report.d.ts +26 -0
- package/dist/upload/session.d.ts +99 -0
- package/dist/url-chips-oyy_Eiqh.js +239 -0
- package/dist/use-client-runs.d.ts +87 -0
- package/dist/use-conversation-log.d.ts +86 -0
- package/dist/use-conversation.d.ts +131 -0
- package/dist/use-copy.d.ts +74 -0
- package/dist/use-download-url.d.ts +84 -0
- package/dist/use-flash.d.ts +61 -0
- package/dist/use-inbox.d.ts +101 -0
- package/dist/use-push-to-talk.d.ts +105 -0
- package/dist/use-route-mutation.d.ts +113 -0
- package/dist/use-route.d.ts +82 -0
- package/dist/use-run-key.d.ts +133 -0
- package/dist/use-session-controls.d.ts +69 -0
- package/dist/use-session-id.d.ts +57 -0
- package/dist/use-tap-to-talk.d.ts +138 -0
- package/dist/use-user-transcript-uyHhzy4d.js +73 -0
- package/dist/use-user-transcript.d.ts +75 -0
- package/dist/use-workflow-form.d.ts +285 -0
- package/dist/use-workflow-progress.d.ts +137 -0
- package/dist/use-workflow-run-DdhWmbx-.js +697 -0
- package/dist/use-workflow-run.d.ts +100 -0
- package/dist/use-workflow-runs.d.ts +71 -0
- package/dist/use-workflow-stream.d.ts +154 -0
- package/dist/use-workflows.d.ts +77 -0
- package/dist/workflow-client.d.ts +136 -0
- package/dist/workflow-def-types.d.ts +31 -0
- package/dist/workflow-events.d.ts +39 -0
- package/dist/workflow-field-kind.d.ts +95 -0
- package/dist/workflow-status-labels.d.ts +35 -0
- package/dist/worklets/_module-url.d.ts +10 -0
- package/dist/worklets/_playback-bench-harness.d.ts +181 -0
- package/dist/worklets/_playback-bench-host.d.ts +63 -0
- package/dist/worklets/_playback-bench-page.d.ts +65 -0
- package/dist/worklets/_tts-trace-harness.d.ts +142 -0
- package/dist/worklets/_worklet-test-utils.d.ts +27 -0
- package/dist/worklets/capture-processor.d.ts +4 -2
- package/dist/worklets/capture-processor.js +73 -33
- package/dist/worklets/playback-processor.d.ts +4 -2
- package/dist/worklets/playback-processor.js +258 -42
- package/package.json +59 -32
- package/styles.css +152 -30
- package/dist/_jsdom-setup.d.ts +0 -0
- package/dist/audio.d.ts +0 -45
- package/dist/audio.js +0 -121
- package/dist/client-context.d.ts +0 -33
- package/dist/client-context.js +0 -15
- package/dist/client-handler.d.ts +0 -43
- package/dist/components/app.d.ts +0 -23
- package/dist/components/app.js +0 -41
- package/dist/components/error-banner.d.ts +0 -21
- package/dist/components/error-banner.js +0 -27
- package/dist/components/message-bubble.d.ts +0 -23
- package/dist/components/message-bubble.js +0 -35
- package/dist/components/state-indicator.d.ts +0 -29
- package/dist/components/state-indicator.js +0 -37
- package/dist/components/thinking-indicator.d.ts +0 -16
- package/dist/components/thinking-indicator.js +0 -34
- package/dist/components/tool-icons.d.ts +0 -17
- package/dist/components/tool-icons.js +0 -128
- package/dist/components/transcript.d.ts +0 -25
- package/dist/components/transcript.js +0 -35
- package/dist/session-CWB7vmqz.js +0 -429
- package/dist/session.d.ts +0 -69
- package/dist/session.js +0 -2
- package/dist/signals.d.ts +0 -120
- package/dist/signals.js +0 -161
- package/dist/tsdown.config.d.ts +0 -2
- package/dist/types.test-d.d.ts +0 -7
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for the framework-agnostic voice session core.
|
|
3
|
+
*
|
|
4
|
+
* Split out of `session/browser-session.ts` to keep that module focused on behaviour.
|
|
5
|
+
*/
|
|
6
|
+
import type { Epoch } from "@alexkroman1/aai/internal";
|
|
7
|
+
import type { SessionIdentity } from "../client-identity.ts";
|
|
8
|
+
import type { AgentState, ChatMessage, SessionError, ToolCallInfo, WebSocketConstructor } from "../types.ts";
|
|
9
|
+
import type { AudioPath } from "./audio-state.ts";
|
|
10
|
+
/**
|
|
11
|
+
* A custom event emitted by the agent via `ctx.send(event, data)` — the
|
|
12
|
+
* payload the session records in `SessionSnapshot.customEvents` (`id` is a
|
|
13
|
+
* monotonic session-unique counter, `event` the name, `data` the payload).
|
|
14
|
+
*
|
|
15
|
+
* Deliberately NOT the DOM `CustomEvent`: it shares nothing with that
|
|
16
|
+
* interface, and the old name shadowed the global in `.tsx` files.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export type AgentCustomEvent = {
|
|
21
|
+
readonly id: number;
|
|
22
|
+
readonly event: string;
|
|
23
|
+
readonly data: unknown;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The agent's projected state as the client holds it: one entry per slot the
|
|
27
|
+
* agent's `syncState` names, keyed by that slot's name.
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
export type AgentStateFrame = Readonly<Record<string, unknown>>;
|
|
32
|
+
/**
|
|
33
|
+
* Immutable snapshot of the session state.
|
|
34
|
+
*
|
|
35
|
+
* Consumers (e.g. React hooks via `useSyncExternalStore`) read this to render.
|
|
36
|
+
* A new object reference is created on every state change.
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* **Four fields describe liveness and they answer different questions.** They
|
|
40
|
+
* are routinely collapsed into one truthy check, which is how a chrome ends up
|
|
41
|
+
* showing a live indicator over a call that has ended:
|
|
42
|
+
*
|
|
43
|
+
* | Field | The question it answers |
|
|
44
|
+
* | --- | --- |
|
|
45
|
+
* | `started` | Has the caller pressed Start? `end()` puts it back to `false`; `disconnect()` does not. |
|
|
46
|
+
* | `running` | Is the socket MEANT to be up? `toggle()` is what flips it. |
|
|
47
|
+
* | `recording` | Is the microphone actually live right now? |
|
|
48
|
+
* | `state` | What is the agent doing — see {@link AgentState}. |
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
export type SessionSnapshot = {
|
|
53
|
+
/** What the agent is doing. See {@link AgentState} for the seven members. */
|
|
54
|
+
readonly state: AgentState;
|
|
55
|
+
/**
|
|
56
|
+
* True while the microphone is live and streaming to the server.
|
|
57
|
+
*
|
|
58
|
+
* This is the mic, not the session: a session can be `running` with the mic
|
|
59
|
+
* still opening, and a failure to acquire it leaves this `false` with the
|
|
60
|
+
* socket up.
|
|
61
|
+
*/
|
|
62
|
+
readonly recording: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Whether the caller has MUTED the microphone — see
|
|
65
|
+
* {@link BrowserSession.setMicMuted}. Independent of `recording`: a muted
|
|
66
|
+
* mic stays open and keeps streaming (as silence), so `recording` stays
|
|
67
|
+
* `true`. UI state, so it survives reconnects, `disconnect()` and `end()`.
|
|
68
|
+
*/
|
|
69
|
+
readonly micMuted: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* The WebSocket URL a program can connect to directly — the long-living
|
|
72
|
+
* platform endpoint, e.g. `wss://host/my-agent/websocket`. Derived from
|
|
73
|
+
* `platformUrl` at construction — available before connecting — and never
|
|
74
|
+
* replaced by the brokered sandbox tunnel URL the session may actually be
|
|
75
|
+
* connected to: that URL is ephemeral (it dies when the sandbox is
|
|
76
|
+
* replaced), while the platform endpoint is stable and upgrades
|
|
77
|
+
* programmatic clients to the current sandbox endpoint itself.
|
|
78
|
+
*/
|
|
79
|
+
readonly apiUrl: string;
|
|
80
|
+
/**
|
|
81
|
+
* Monotonically increasing counter bumped whenever rendered conversation
|
|
82
|
+
* content changes (`messages`, `toolCalls`, or either live transcript).
|
|
83
|
+
* Cheap dependency for scroll-to-bottom effects — unlike summed lengths it
|
|
84
|
+
* never collides when the capped arrays slide.
|
|
85
|
+
*/
|
|
86
|
+
readonly contentVersion: number;
|
|
87
|
+
/**
|
|
88
|
+
* The conversation so far, oldest first — user and assistant turns only.
|
|
89
|
+
* Tool activity is NOT in here; it is in `toolCalls`. Capped, so the oldest
|
|
90
|
+
* entries slide off a long call.
|
|
91
|
+
*/
|
|
92
|
+
readonly messages: ChatMessage[];
|
|
93
|
+
/**
|
|
94
|
+
* Every tool call the agent has made this session, in order, each carrying
|
|
95
|
+
* its own pending/settled state. Capped like `messages`. `useToolResult` and
|
|
96
|
+
* `useToolCallStart` are the narrow readers; this is the whole log.
|
|
97
|
+
*/
|
|
98
|
+
readonly toolCalls: ToolCallInfo[];
|
|
99
|
+
/**
|
|
100
|
+
* Custom events the agent pushed with `ctx.send(event, data)`, in order.
|
|
101
|
+
* A LOG rather than a value — `useEvent(name, cb)` is the reader that
|
|
102
|
+
* delivers each one exactly once; reading this array directly means owning
|
|
103
|
+
* the cursor yourself.
|
|
104
|
+
*/
|
|
105
|
+
readonly customEvents: AgentCustomEvent[];
|
|
106
|
+
/**
|
|
107
|
+
* Latest state the agent projected via `syncState` — keyed by slot name,
|
|
108
|
+
* `{ [slot]: view }` — or `null` before the first push. A value, not a log —
|
|
109
|
+
* a component that mounts mid-session reads current state rather than
|
|
110
|
+
* replaying events it missed. A slot whose view did not change keeps its
|
|
111
|
+
* previous value object, so a selector over one slot is stable across pushes.
|
|
112
|
+
*/
|
|
113
|
+
readonly agentState: AgentStateFrame | null;
|
|
114
|
+
/**
|
|
115
|
+
* The caller's in-progress turn, as STT reports it.
|
|
116
|
+
*
|
|
117
|
+
* **`null` and `""` are different turns, and collapsing them is the mistake
|
|
118
|
+
* this field invites.** `null` is silence; `""` is speech DETECTED with no
|
|
119
|
+
* words back yet — where a live session sits for a few hundred milliseconds
|
|
120
|
+
* at the start of every turn. Read as one falsy check, the live-transcript
|
|
121
|
+
* row appears a beat late, on the first word rather than on the first sound,
|
|
122
|
+
* which is the moment it exists for.
|
|
123
|
+
*
|
|
124
|
+
* Prefer {@link useUserTranscript}, which returns the distinction as two
|
|
125
|
+
* named things (`speaking` to render on, `text` with a placeholder) rather
|
|
126
|
+
* than leaving each chrome to re-derive the ternary.
|
|
127
|
+
*
|
|
128
|
+
* Cleared when the turn is committed to `messages`.
|
|
129
|
+
*/
|
|
130
|
+
readonly userTranscript: string | null;
|
|
131
|
+
/**
|
|
132
|
+
* The agent's reply as it streams, or `null` when it is not speaking.
|
|
133
|
+
* Cleared when the reply is committed to `messages`, so a chrome renders
|
|
134
|
+
* this row and the finished message, never both.
|
|
135
|
+
*/
|
|
136
|
+
readonly agentTranscript: string | null;
|
|
137
|
+
/**
|
|
138
|
+
* The session's current failure, or `null`. Carries a `code`
|
|
139
|
+
* ({@link SessionErrorCode}), a message, and whether it was FATAL.
|
|
140
|
+
*
|
|
141
|
+
* A fatal error LATCHES: nothing clears it but the next completed handshake,
|
|
142
|
+
* because the frame announcing a session's death is also the frame that used
|
|
143
|
+
* to wipe the message explaining it. A non-fatal one is retired by later
|
|
144
|
+
* activity, which is what the recovery was written for.
|
|
145
|
+
*/
|
|
146
|
+
readonly error: SessionError | null;
|
|
147
|
+
/**
|
|
148
|
+
* Whether the caller has pressed Start. `false` until the first `start()`,
|
|
149
|
+
* and back to `false` after `end()` — which is what makes a start-screen
|
|
150
|
+
* chrome show its Start control again. `disconnect()` leaves it `true`.
|
|
151
|
+
*/
|
|
152
|
+
readonly started: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Whether the session is MEANT to be connected — the pause/resume state
|
|
155
|
+
* `toggle()` flips, not a report of the socket. A reconnecting session is
|
|
156
|
+
* still `running`.
|
|
157
|
+
*/
|
|
158
|
+
readonly running: boolean;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* The seal on a {@link BrowserSession}.
|
|
162
|
+
*
|
|
163
|
+
* TYPE-ONLY — there is no value at run time, so an object literal cannot carry
|
|
164
|
+
* the key and only `createBrowserSession` mints a session. That is what lets
|
|
165
|
+
* the handle grow a member without breaking anybody: push-to-talk added three
|
|
166
|
+
* REQUIRED methods and broke every hand-written double, which is the failure a
|
|
167
|
+
* handle a caller RECEIVES should not be able to cause.
|
|
168
|
+
*
|
|
169
|
+
* @public
|
|
170
|
+
*/
|
|
171
|
+
export declare const browserSessionBrand: unique symbol;
|
|
172
|
+
/**
|
|
173
|
+
* Push-to-talk's three edges on a {@link BrowserSession} — `session.userTurn`.
|
|
174
|
+
*
|
|
175
|
+
* Only an agent declaring `turnTaking: { detection: "manual" }` honours them; any other
|
|
176
|
+
* agent logs once and ignores them, because its transcriber already ends each
|
|
177
|
+
* turn on a pause. `usePushToTalk` is the hook a button is built on, and the
|
|
178
|
+
* way a `client.tsx` reaches these: a sub-handle rather than three methods on
|
|
179
|
+
* the session, so a feature one agent in many declares is not three members
|
|
180
|
+
* every session carries in its autocomplete.
|
|
181
|
+
*
|
|
182
|
+
* @public
|
|
183
|
+
*/
|
|
184
|
+
export type UserTurnControls = {
|
|
185
|
+
/**
|
|
186
|
+
* OPEN a turn — the button went down. Stops the agent if it is speaking
|
|
187
|
+
* (discarding its queued audio here at once, rather than a round trip later)
|
|
188
|
+
* and lets the microphone through to the transcriber.
|
|
189
|
+
*/
|
|
190
|
+
start(): void;
|
|
191
|
+
/** CLOSE the turn and have the agent answer everything said since `start()` — the button came up. */
|
|
192
|
+
commit(): void;
|
|
193
|
+
/**
|
|
194
|
+
* Close the turn and THROW AWAY what was said in it — a cancelled press (the
|
|
195
|
+
* pointer left the button, Escape). The agent answers nothing.
|
|
196
|
+
*/
|
|
197
|
+
clear(): void;
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* How a page answers a tool call it ran — see {@link BrowserSession.sendToolResult}.
|
|
201
|
+
*
|
|
202
|
+
* @public
|
|
203
|
+
*/
|
|
204
|
+
export type ToolCallOutcome = {
|
|
205
|
+
result: unknown;
|
|
206
|
+
} | {
|
|
207
|
+
error: string;
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Options for {@link BrowserSession.sendText}.
|
|
211
|
+
*
|
|
212
|
+
* @public
|
|
213
|
+
*/
|
|
214
|
+
export type SendTextOptions = {
|
|
215
|
+
/**
|
|
216
|
+
* Open the session for this message if it is not up: `start()` before the
|
|
217
|
+
* first call, `toggle()` after a hang-up (so a resumable session RESUMES),
|
|
218
|
+
* then send once the server has configured it. Messages typed meanwhile are
|
|
219
|
+
* kept in order. They are DROPPED if the session stops before it is up — a
|
|
220
|
+
* hang-up, `end()`, a failed connection — so a message typed into a call
|
|
221
|
+
* that never came is not answered by a later one. Default `false`: sending
|
|
222
|
+
* while disconnected does nothing.
|
|
223
|
+
*/
|
|
224
|
+
connect?: boolean | undefined;
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* A framework-agnostic voice session that manages WebSocket communication,
|
|
228
|
+
* audio capture/playback, and agent state transitions.
|
|
229
|
+
*
|
|
230
|
+
* Uses a subscribe/getSnapshot pattern (compatible with React's
|
|
231
|
+
* `useSyncExternalStore`). Implements `Disposable` for resource cleanup.
|
|
232
|
+
*
|
|
233
|
+
* @sealed Only `createBrowserSession` produces one — see
|
|
234
|
+
* {@link browserSessionBrand}. A test double for a component is a real
|
|
235
|
+
* session with no socket, not an object literal.
|
|
236
|
+
*
|
|
237
|
+
* @public
|
|
238
|
+
*/
|
|
239
|
+
export type BrowserSession = {
|
|
240
|
+
/** The seal — see {@link browserSessionBrand}. */
|
|
241
|
+
readonly [browserSessionBrand]: true;
|
|
242
|
+
/** Return the current immutable state snapshot. */
|
|
243
|
+
getSnapshot(): SessionSnapshot;
|
|
244
|
+
/** Subscribe to state changes. Returns an unsubscribe function. */
|
|
245
|
+
subscribe(callback: () => void): () => void;
|
|
246
|
+
/**
|
|
247
|
+
* Open a WebSocket connection to the server and begin audio capture,
|
|
248
|
+
* without touching the `started`/`running` flags — the low-level half of
|
|
249
|
+
* `start()`. Most UIs call `start()` (first activation) or `toggle()`
|
|
250
|
+
* (mute-style connect/disconnect) instead.
|
|
251
|
+
* @param options - Optional. `signal` is an AbortSignal that, when aborted, disconnects the session.
|
|
252
|
+
*/
|
|
253
|
+
connect(options?: {
|
|
254
|
+
signal?: AbortSignal;
|
|
255
|
+
}): void;
|
|
256
|
+
/** Cancel the current agent turn and discard in-flight TTS audio. */
|
|
257
|
+
cancel(): void;
|
|
258
|
+
/** Push-to-talk's three edges — see {@link UserTurnControls}. */
|
|
259
|
+
readonly userTurn: UserTurnControls;
|
|
260
|
+
/**
|
|
261
|
+
* Who this session is to its agent — the base URL it dials, the client id it
|
|
262
|
+
* sends, this tab's inbox holder id, and the server's session id. What
|
|
263
|
+
* `useInbox()`, `useClientId()` and `useSessionId()` read; see
|
|
264
|
+
* {@link SessionIdentity}.
|
|
265
|
+
*/
|
|
266
|
+
readonly identity: SessionIdentity;
|
|
267
|
+
/**
|
|
268
|
+
* Send a TYPED user turn: the agent answers `text` exactly as if the caller
|
|
269
|
+
* had said it — aloud, with tools, under any `turnTaking.detection`.
|
|
270
|
+
*
|
|
271
|
+
* Interrupts the agent if it is speaking or thinking (its queued audio is
|
|
272
|
+
* discarded here at once, as `cancel()` does). The message is NOT echoed into
|
|
273
|
+
* `messages` locally: the server reports it as the same
|
|
274
|
+
* `userTranscript.committed` a spoken turn produces, and that is what adds
|
|
275
|
+
* the row — so it is also what a resumed session replays.
|
|
276
|
+
*
|
|
277
|
+
* `text` is trimmed; an empty message sends nothing, and so does a call
|
|
278
|
+
* while disconnected — unless `options.connect` is set, which opens the
|
|
279
|
+
* session (see {@link SendTextOptions}) and sends once it is up. Text longer than `MAX_TRANSCRIPT_CHARS` (100,000) is refused by
|
|
280
|
+
* the server. A speech-to-speech agent cannot take a typed turn: the server
|
|
281
|
+
* logs a warning once and the message is ignored.
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```ts
|
|
285
|
+
* declare const session: import("@alexkroman1/aai-ui").Session;
|
|
286
|
+
* session.sendText("What's the weather tomorrow?");
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
sendText(text: string, options?: SendTextOptions): void;
|
|
290
|
+
/**
|
|
291
|
+
* Answer a pending tool call the agent asked THIS PAGE to run — a
|
|
292
|
+
* `clientTool` on the server. `result` is JSON-serialized and becomes the
|
|
293
|
+
* value the model reads; `error` fails the call with that message. A no-op
|
|
294
|
+
* while disconnected, and ignored by the server for a call nothing waits on.
|
|
295
|
+
*
|
|
296
|
+
* Most pages use `useClientTool(name, handler)`, which calls this for them.
|
|
297
|
+
*/
|
|
298
|
+
sendToolResult(toolCallId: string, outcome: ToolCallOutcome): void;
|
|
299
|
+
/**
|
|
300
|
+
* Mute or unmute the microphone WITHOUT dropping the session — the gate a
|
|
301
|
+
* hold-to-talk button over an agent with automatic turn detection is built
|
|
302
|
+
* on (mute by default, unmute while held).
|
|
303
|
+
*
|
|
304
|
+
* While muted the session keeps streaming frames of the same length and
|
|
305
|
+
* cadence, zero-filled, so the server's transcriber hears silence: its
|
|
306
|
+
* automatic endpointing closes the turn on release, and the stream's clock
|
|
307
|
+
* stays true. Purely local — no command is sent and a speaking agent is not
|
|
308
|
+
* interrupted (call `cancel()` for that).
|
|
309
|
+
*
|
|
310
|
+
* UI state rather than connection state: it survives reconnects, resumes,
|
|
311
|
+
* `disconnect()` → `connect()` and `end()`, and may be set before the first
|
|
312
|
+
* `connect()` so a session opens already muted. Read it back as
|
|
313
|
+
* `micMuted` on the snapshot.
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* declare const session: import("@alexkroman1/aai-ui").Session;
|
|
318
|
+
* session.setMicMuted(true); // typed-only until the talk button is held
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
setMicMuted(muted: boolean): void;
|
|
322
|
+
/**
|
|
323
|
+
* Clear messages, transcripts, and error state while keeping the current
|
|
324
|
+
* connection (unlike `reset()`, which also reconnects).
|
|
325
|
+
*/
|
|
326
|
+
resetState(): void;
|
|
327
|
+
/**
|
|
328
|
+
* Reset the session: clear state as `resetState()` does, then drop and
|
|
329
|
+
* reopen the connection for a fresh conversation.
|
|
330
|
+
*/
|
|
331
|
+
reset(): void;
|
|
332
|
+
/** Close the WebSocket and release all audio resources. */
|
|
333
|
+
disconnect(): void;
|
|
334
|
+
/**
|
|
335
|
+
* Start the session for the first time: sets `started` and `running`, then
|
|
336
|
+
* connects. Use this for the initial "start conversation" action;
|
|
337
|
+
* afterwards `toggle()` is the pause/resume control.
|
|
338
|
+
*/
|
|
339
|
+
start(): void;
|
|
340
|
+
/** Toggle between connected and disconnected states (after `start()`). */
|
|
341
|
+
toggle(): void;
|
|
342
|
+
/**
|
|
343
|
+
* End the call: close the connection, clear the conversation, and return
|
|
344
|
+
* to the not-started state (`started` flips back to false, so a
|
|
345
|
+
* start-screen UI shows its Start control again). Unlike `reset()` —
|
|
346
|
+
* which keeps the call live and only clears the conversation — the next
|
|
347
|
+
* `start()` mints a brand-new session: a new session id, fresh
|
|
348
|
+
* per-session tool state, greeting included.
|
|
349
|
+
*/
|
|
350
|
+
end(): void;
|
|
351
|
+
/**
|
|
352
|
+
* End the current call and immediately begin a new one — `end()` then
|
|
353
|
+
* `start()`, which is what "New Conversation" means for an agent that keeps
|
|
354
|
+
* SESSION-SCOPED STATE.
|
|
355
|
+
*
|
|
356
|
+
* `reset()` is the one whose name suggests this and it is not the same
|
|
357
|
+
* thing: it clears the transcript and reconnects, but the reconnect carries
|
|
358
|
+
* the same `?sessionId=`, so every `sessionSlot` on the server survives —
|
|
359
|
+
* the game world, the incident board, the cart. A caller who asked to start
|
|
360
|
+
* over gets a blank transcript in front of the old state. This drops the
|
|
361
|
+
* session id, so the next connect mints a fresh one and the greeting plays
|
|
362
|
+
* again.
|
|
363
|
+
*
|
|
364
|
+
* Three templates had each written `session.end(); session.start();` with
|
|
365
|
+
* the same paragraph explaining why `reset()` was wrong; the six on the
|
|
366
|
+
* stock shell could not, because {@link Controls} called `reset()` for them.
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* ```ts
|
|
370
|
+
* declare const session: import("@alexkroman1/aai-ui").Session;
|
|
371
|
+
* session.restart();
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
restart(): void;
|
|
375
|
+
/**
|
|
376
|
+
* Continue an EARLIER session — one of this client's past conversations, say,
|
|
377
|
+
* picked from a list — without reloading the page: hang up the current call
|
|
378
|
+
* if there is one, clear the transcript on screen, and connect presenting
|
|
379
|
+
* `sessionId` as `?sessionId=`, so the server resumes THAT session.
|
|
380
|
+
*
|
|
381
|
+
* The server restores it the way a reload does: the session's own turns come
|
|
382
|
+
* back in `history.restored` (a session bound to a `client` keeps its events
|
|
383
|
+
* after it ends), together with the client's other prior sessions as seed —
|
|
384
|
+
* never counting this one twice. The greeting is skipped, as on any resume.
|
|
385
|
+
* The id is stored like a `config` frame's, so a reload continues it too.
|
|
386
|
+
*
|
|
387
|
+
* Throws a `RangeError` for an id the server would not honor (1-128 letters,
|
|
388
|
+
* digits, `-` or `_`) BEFORE touching the current call. An id that names no
|
|
389
|
+
* session the server has starts a fresh conversation under it, greeting
|
|
390
|
+
* skipped — the same answer a stale bookmark gets.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```ts
|
|
394
|
+
* declare const session: import("@alexkroman1/aai-ui").Session;
|
|
395
|
+
* declare const picked: { sessionId: string };
|
|
396
|
+
* session.resume(picked.sessionId);
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
resume(sessionId: string): void;
|
|
400
|
+
/** Alias for `disconnect` for use with `using`. */
|
|
401
|
+
[Symbol.dispose](): void;
|
|
402
|
+
};
|
|
403
|
+
/**
|
|
404
|
+
* Shared mutable connection state.
|
|
405
|
+
*
|
|
406
|
+
* **It used to carry the audio path too**, and the four fields that went are
|
|
407
|
+
* worth naming because the shape recurs: a `voiceIO` slot, an
|
|
408
|
+
* `audioSetupInFlight` latch, a `generation` epoch whose only readers were the
|
|
409
|
+
* bring-up's four staleness checks, and the two pre-init buffers. Each was a
|
|
410
|
+
* question about a position the connection did not have, readable and writable
|
|
411
|
+
* from anywhere. They are the audio path's own statechart now — see
|
|
412
|
+
* `session/audio-state.ts`.
|
|
413
|
+
*
|
|
414
|
+
* What is left is what the CONNECTION really owns: the socket, the server's
|
|
415
|
+
* idle retirement, and the turn boundary.
|
|
416
|
+
*/
|
|
417
|
+
export type ConnState = {
|
|
418
|
+
ws: InstanceType<WebSocketConstructor> | null;
|
|
419
|
+
/**
|
|
420
|
+
* The server retired this session for idleness (`idle_timeout`), so the
|
|
421
|
+
* close that follows is EXPECTED and must not be retried.
|
|
422
|
+
*
|
|
423
|
+
* Without this the automatic reconnect would immediately re-open the
|
|
424
|
+
* session the server just reclaimed — a tab left open would cycle forever
|
|
425
|
+
* and the guest would never see zero sessions, which is the whole point of
|
|
426
|
+
* the timeout. Reconnecting is the user's call from here (the controls
|
|
427
|
+
* reconnect on demand).
|
|
428
|
+
*/
|
|
429
|
+
retiredByServer: boolean;
|
|
430
|
+
/**
|
|
431
|
+
* Turn epoch, bumped at every turn boundary — a committed user turn, a
|
|
432
|
+
* barge-in, a reset, AND every audio-path teardown (`cleanupAudio`).
|
|
433
|
+
*
|
|
434
|
+
* Async playback completions (`settleWhenAudioDrained`) capture it and
|
|
435
|
+
* discard themselves when it has moved. The teardown bumps are why it lives
|
|
436
|
+
* here rather than as a closure local in the message handlers: a reply's
|
|
437
|
+
* drain resolves whenever the AudioContext closes, so a session torn down
|
|
438
|
+
* mid-speech (hang up, fatal error, reconnect) would otherwise have
|
|
439
|
+
* `state: "listening"` written over its "disconnected"/"error" a moment
|
|
440
|
+
* later — a dead session reporting a live mic.
|
|
441
|
+
*/
|
|
442
|
+
turn: Epoch;
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* The two liveness fields at rest.
|
|
446
|
+
*
|
|
447
|
+
* `running` and `recording` ride with almost every state transition and were
|
|
448
|
+
* spread as a pair of literals at seven sites across three modules — the same
|
|
449
|
+
* shape `session/state.ts` folded `state` and `error` out of, one field
|
|
450
|
+
* short. Naming it relates them: a transition that ends the call says so once,
|
|
451
|
+
* and a reader looking for "what stops the mic" finds one symbol rather than a
|
|
452
|
+
* grep.
|
|
453
|
+
*
|
|
454
|
+
* It is deliberately NOT the whole snapshot patch — a transition still supplies
|
|
455
|
+
* its own `agentState.apply(...)` projection beside this.
|
|
456
|
+
*/
|
|
457
|
+
export declare const STOPPED: {
|
|
458
|
+
readonly running: false;
|
|
459
|
+
readonly recording: false;
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* A turn boundary: end the current turn and settle whatever it was playing.
|
|
463
|
+
*
|
|
464
|
+
* The two calls are one fact and were written out at four sites — `cancel()`
|
|
465
|
+
* and `reset()` here, `reply.cancelled` and `session.reset` on the server side
|
|
466
|
+
* — where the pair is load-bearing in both halves. The bump stops a stale drain
|
|
467
|
+
* continuation from stamping `"listening"` over a state the session has since
|
|
468
|
+
* moved to; the flush settles the interrupted turn's `done()` so it cannot
|
|
469
|
+
* strand.
|
|
470
|
+
*
|
|
471
|
+
* Two further sites bump WITHOUT flushing (`cleanupAudio`, a committed user
|
|
472
|
+
* transcript) and stay spelled out, which is the point of naming this one: a
|
|
473
|
+
* bump on its own now reads as a deliberate choice rather than a forgotten
|
|
474
|
+
* flush.
|
|
475
|
+
*/
|
|
476
|
+
export declare function bargeIn(conn: ConnState, audio: AudioPath): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the client reports about itself on every attempt: `?location=`,
|
|
3
|
+
* `?phone=` and `?client=`, each already resolved and trimmed (empty = not
|
|
4
|
+
* reported).
|
|
5
|
+
*/
|
|
6
|
+
export type ClientReport = {
|
|
7
|
+
location?: string | undefined;
|
|
8
|
+
phone?: string | undefined;
|
|
9
|
+
client?: string | undefined;
|
|
10
|
+
};
|
|
11
|
+
/** Build the session WebSocket URL from the platform URL and resume state. */
|
|
12
|
+
export declare function buildWsUrl(platformUrl: string, sessionId?: string, report?: ClientReport): URL;
|
|
13
|
+
/**
|
|
14
|
+
* Turn a broker-provided session URL (`sessionUrl` from `GET client-config`
|
|
15
|
+
* — the agent's live sandbox endpoint) into this attempt's connect URL.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildBrokeredWsUrl(sessionUrl: string, sessionId?: string, report?: ClientReport): URL;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push-to-talk's three edges on the browser session — `session.userTurn`'s
|
|
3
|
+
* `start`, `commit` and `clear` — for an agent that declares
|
|
4
|
+
* `turnTaking: { detection: "manual" }`; and `session.sendText`, a turn the caller TYPED.
|
|
5
|
+
*
|
|
6
|
+
* Split out of `session/browser-session.ts` at the source-length cap. Each is a frame to
|
|
7
|
+
* the server; what the server does with it is `aai-runtime`'s
|
|
8
|
+
* `aai-runtime/src/transports/pipeline/speech/manual-turn.ts`. The two things done HERE rather than waiting a
|
|
9
|
+
* round trip for are the reason these are more than `sendJson` calls: a press
|
|
10
|
+
* stops the agent's queued audio at once, and a discarded turn's caption is
|
|
11
|
+
* cleared because no transcript for it will ever arrive.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import type { SessionCommand } from "@alexkroman1/aai/protocol";
|
|
16
|
+
import type { SessionStateMachine } from "./state.ts";
|
|
17
|
+
import type { SendTextOptions, SessionSnapshot, ToolCallOutcome, UserTurnControls } from "./types.ts";
|
|
18
|
+
/** What the three edges need from the session around them. @internal */
|
|
19
|
+
export type UserTurnDeps = {
|
|
20
|
+
snapshot: () => SessionSnapshot;
|
|
21
|
+
/** A socket is open and can carry a frame. */
|
|
22
|
+
connected: () => boolean;
|
|
23
|
+
/** The local turn boundary — see `bargeIn` in `session/types.ts`. */
|
|
24
|
+
bargeIn: () => void;
|
|
25
|
+
agentState: SessionStateMachine;
|
|
26
|
+
updateState: (partial: Partial<SessionSnapshot>) => void;
|
|
27
|
+
sendJson: (msg: SessionCommand) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Typed turns waiting for a session — `sendText(text, { connect: true })`
|
|
30
|
+
* while none is up. OWNED by the session core, which empties it whenever the
|
|
31
|
+
* session stops being meant to run (`running: false`): a hang-up, `end()`, a
|
|
32
|
+
* terminal close. A message typed into a call that then failed must not be
|
|
33
|
+
* answered by the next one.
|
|
34
|
+
*/
|
|
35
|
+
queued: string[];
|
|
36
|
+
/** Bring the session up if it is not meant to be running — `start()` or `toggle()`. */
|
|
37
|
+
open: () => void;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Everything a caller's turn can be started by: push-to-talk's three edges
|
|
41
|
+
* and a TYPED turn — `session.userTurn` and `session.sendText`. One builder
|
|
42
|
+
* over one set of deps, because both are the same kind of thing (a user turn
|
|
43
|
+
* the client, not the transcriber, delimits) and both interrupt the same way.
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
export declare function createUserInput(deps: UserTurnDeps): {
|
|
48
|
+
userTurn: UserTurnControls;
|
|
49
|
+
sendText: (text: string, options?: SendTextOptions) => void;
|
|
50
|
+
/** A session came up (a `config` frame): send what was typed while it was not. */
|
|
51
|
+
flushQueued: () => void;
|
|
52
|
+
sendToolResult: (toolCallId: string, outcome: ToolCallOutcome) => void;
|
|
53
|
+
};
|
|
54
|
+
/** Build the three push-to-talk edges. @internal */
|
|
55
|
+
export declare function createUserTurnActions(deps: UserTurnDeps): UserTurnControls;
|