@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.
Files changed (203) hide show
  1. package/README.md +253 -0
  2. package/dist/_colors-CpZO-88A.js +78 -0
  3. package/dist/_module-url-C13kAJ87.js +15 -0
  4. package/dist/_page-shell.d.ts +11 -0
  5. package/dist/_recover-run.d.ts +79 -0
  6. package/dist/_repeat-until.d.ts +30 -0
  7. package/dist/_run-controls.d.ts +33 -0
  8. package/dist/_submission-state.d.ts +92 -0
  9. package/dist/_tap-to-talk-state.d.ts +88 -0
  10. package/dist/_utils-Mi-SCFA1.js +47 -0
  11. package/dist/_utils.d.ts +26 -0
  12. package/dist/_web-storage.d.ts +43 -0
  13. package/dist/_workflow-api-ref.d.ts +37 -0
  14. package/dist/_workflow-files.d.ts +34 -0
  15. package/dist/_workflow-test-defs.d.ts +34 -0
  16. package/dist/aai-logo-D8pMVn9_.js +85 -0
  17. package/dist/agent-state-labels.d.ts +65 -0
  18. package/dist/agent-state.d.ts +108 -0
  19. package/dist/audio/capture.d.ts +44 -0
  20. package/dist/audio/index.d.ts +18 -0
  21. package/dist/audio/index.js +431 -0
  22. package/dist/audio/preconnect.d.ts +87 -0
  23. package/dist/audio/voice-io.d.ts +112 -0
  24. package/dist/build-default-client.d.ts +1 -0
  25. package/dist/chat-view-BCbI7gD4.js +396 -0
  26. package/dist/client-config-C7fQrioM.js +139 -0
  27. package/dist/client-config.d.ts +119 -0
  28. package/dist/client-dir.d.ts +44 -0
  29. package/dist/client-dir.js +58 -0
  30. package/dist/client-identity.d.ts +131 -0
  31. package/dist/client-tool.d.ts +51 -0
  32. package/dist/components/_colors.d.ts +73 -0
  33. package/dist/components/_form-readiness.d.ts +56 -0
  34. package/dist/components/_form-values.d.ts +19 -0
  35. package/dist/components/aai-logo.d.ts +12 -0
  36. package/dist/components/audio-result.d.ts +94 -0
  37. package/dist/components/auto-scroll.d.ts +74 -0
  38. package/dist/components/bullet-list.d.ts +74 -0
  39. package/dist/components/button.d.ts +29 -21
  40. package/dist/components/button.js +62 -23
  41. package/dist/components/chat-view.d.ts +24 -18
  42. package/dist/components/chat-view.js +2 -60
  43. package/dist/components/console-shell.d.ts +85 -0
  44. package/dist/components/controls.d.ts +21 -6
  45. package/dist/components/controls.js +31 -19
  46. package/dist/components/conversation-view.d.ts +130 -0
  47. package/dist/components/eyebrow.d.ts +12 -0
  48. package/dist/components/facts.d.ts +81 -0
  49. package/dist/components/form-fields.d.ts +142 -0
  50. package/dist/components/form-types.d.ts +73 -0
  51. package/dist/components/form.d.ts +96 -0
  52. package/dist/components/markdown.d.ts +57 -0
  53. package/dist/components/message-list.d.ts +34 -7
  54. package/dist/components/message-list.js +2 -75
  55. package/dist/components/session-controls.d.ts +125 -0
  56. package/dist/components/session-error-banner.d.ts +69 -0
  57. package/dist/components/session-state-dot.d.ts +111 -0
  58. package/dist/components/sidebar-layout.d.ts +31 -10
  59. package/dist/components/sidebar-layout.js +32 -12
  60. package/dist/components/start-screen.d.ts +17 -6
  61. package/dist/components/start-screen.js +34 -14
  62. package/dist/components/tool-call-block.d.ts +15 -14
  63. package/dist/components/tool-call-block.js +2 -117
  64. package/dist/components/tool-call-row.d.ts +75 -0
  65. package/dist/components/tool-config-context.d.ts +22 -0
  66. package/dist/components/upload-progress.d.ts +76 -0
  67. package/dist/components/url-chips.d.ts +29 -0
  68. package/dist/components/workflow-fields.d.ts +46 -0
  69. package/dist/components/workflow-pending-note.d.ts +81 -0
  70. package/dist/components/workflow-progress.d.ts +81 -0
  71. package/dist/components/workflow-run-error.d.ts +48 -0
  72. package/dist/components/workflow-run-panel.d.ts +79 -0
  73. package/dist/context.d.ts +288 -0
  74. package/dist/context.js +382 -0
  75. package/dist/conversation-log.d.ts +88 -0
  76. package/dist/default-client/assets/_module-url-BX0RuRU2.js +1 -0
  77. package/dist/default-client/assets/audio-mpgipo2Q.js +1 -0
  78. package/dist/default-client/assets/capture-processor-D0q7Qrdd.js +90 -0
  79. package/dist/default-client/assets/index-8ihb1Urm.css +2 -0
  80. package/dist/default-client/assets/index-DPfmSFmc.js +126 -0
  81. package/dist/default-client/assets/playback-processor-CjDbc9iH.js +314 -0
  82. package/dist/default-client/favicon.ico +0 -0
  83. package/dist/default-client/index.html +15 -0
  84. package/dist/default-client-entry.d.ts +6 -0
  85. package/dist/default-client.d.ts +19 -0
  86. package/dist/define-client.d.ts +169 -26
  87. package/dist/define-client.js +209 -55
  88. package/dist/eyebrow-UfmSz9yy.js +27 -0
  89. package/dist/hooks-DN4gHYsj.js +182 -0
  90. package/dist/hooks.d.ts +172 -0
  91. package/dist/hooks.js +3 -0
  92. package/dist/hooks.test-d.d.ts +14 -0
  93. package/dist/inbox-protocol.d.ts +128 -0
  94. package/dist/inbox.d.ts +132 -0
  95. package/dist/index.d.ts +126 -29
  96. package/dist/index.js +5500 -15
  97. package/dist/internal.d.ts +43 -0
  98. package/dist/internal.js +8 -0
  99. package/dist/linked-client.d.ts +70 -0
  100. package/dist/message-list-DHZcLhRA.js +682 -0
  101. package/dist/notice-player.d.ts +70 -0
  102. package/dist/page.d.ts +114 -0
  103. package/dist/phone.d.ts +50 -0
  104. package/dist/route-fetch.d.ts +79 -0
  105. package/dist/session/audio-effects.d.ts +37 -0
  106. package/dist/session/audio-setup.d.ts +57 -0
  107. package/dist/session/audio-state.d.ts +157 -0
  108. package/dist/session/browser-session.d.ts +39 -0
  109. package/dist/session/close.d.ts +26 -0
  110. package/dist/session/dial.d.ts +65 -0
  111. package/dist/session/handshake.d.ts +60 -0
  112. package/dist/session/index.d.ts +17 -0
  113. package/dist/session/index.js +2 -0
  114. package/dist/session/messages.d.ts +63 -0
  115. package/dist/session/mic.d.ts +64 -0
  116. package/dist/session/preconnect.d.ts +48 -0
  117. package/dist/session/reconnect.d.ts +25 -0
  118. package/dist/session/resume-store.d.ts +57 -0
  119. package/dist/session/state.d.ts +146 -0
  120. package/dist/session/ticket.d.ts +44 -0
  121. package/dist/session/types.d.ts +476 -0
  122. package/dist/session/url.d.ts +17 -0
  123. package/dist/session/user-turn.d.ts +55 -0
  124. package/dist/session-D9jM5-TF.js +2140 -0
  125. package/dist/stored-value.d.ts +103 -0
  126. package/dist/tool-call-block-uXtMOUWN.js +204 -0
  127. package/dist/tool-config-context-Es4YUzV2.js +19 -0
  128. package/dist/types.d.ts +272 -34
  129. package/dist/types.js +35 -4
  130. package/dist/upload/files.d.ts +83 -0
  131. package/dist/upload/index.d.ts +15 -0
  132. package/dist/upload/pause.d.ts +31 -0
  133. package/dist/upload/recall.d.ts +67 -0
  134. package/dist/upload/report.d.ts +26 -0
  135. package/dist/upload/session.d.ts +99 -0
  136. package/dist/url-chips-oyy_Eiqh.js +239 -0
  137. package/dist/use-client-runs.d.ts +87 -0
  138. package/dist/use-conversation-log.d.ts +86 -0
  139. package/dist/use-conversation.d.ts +131 -0
  140. package/dist/use-copy.d.ts +74 -0
  141. package/dist/use-download-url.d.ts +84 -0
  142. package/dist/use-flash.d.ts +61 -0
  143. package/dist/use-inbox.d.ts +101 -0
  144. package/dist/use-push-to-talk.d.ts +105 -0
  145. package/dist/use-route-mutation.d.ts +113 -0
  146. package/dist/use-route.d.ts +82 -0
  147. package/dist/use-run-key.d.ts +133 -0
  148. package/dist/use-session-controls.d.ts +69 -0
  149. package/dist/use-session-id.d.ts +57 -0
  150. package/dist/use-tap-to-talk.d.ts +138 -0
  151. package/dist/use-user-transcript-uyHhzy4d.js +73 -0
  152. package/dist/use-user-transcript.d.ts +75 -0
  153. package/dist/use-workflow-form.d.ts +285 -0
  154. package/dist/use-workflow-progress.d.ts +137 -0
  155. package/dist/use-workflow-run-DdhWmbx-.js +697 -0
  156. package/dist/use-workflow-run.d.ts +100 -0
  157. package/dist/use-workflow-runs.d.ts +71 -0
  158. package/dist/use-workflow-stream.d.ts +154 -0
  159. package/dist/use-workflows.d.ts +77 -0
  160. package/dist/workflow-client.d.ts +136 -0
  161. package/dist/workflow-def-types.d.ts +31 -0
  162. package/dist/workflow-events.d.ts +39 -0
  163. package/dist/workflow-field-kind.d.ts +95 -0
  164. package/dist/workflow-status-labels.d.ts +35 -0
  165. package/dist/worklets/_module-url.d.ts +10 -0
  166. package/dist/worklets/_playback-bench-harness.d.ts +181 -0
  167. package/dist/worklets/_playback-bench-host.d.ts +63 -0
  168. package/dist/worklets/_playback-bench-page.d.ts +65 -0
  169. package/dist/worklets/_tts-trace-harness.d.ts +142 -0
  170. package/dist/worklets/_worklet-test-utils.d.ts +27 -0
  171. package/dist/worklets/capture-processor.d.ts +4 -2
  172. package/dist/worklets/capture-processor.js +73 -33
  173. package/dist/worklets/playback-processor.d.ts +4 -2
  174. package/dist/worklets/playback-processor.js +258 -42
  175. package/package.json +59 -32
  176. package/styles.css +152 -30
  177. package/dist/_jsdom-setup.d.ts +0 -0
  178. package/dist/audio.d.ts +0 -45
  179. package/dist/audio.js +0 -121
  180. package/dist/client-context.d.ts +0 -33
  181. package/dist/client-context.js +0 -15
  182. package/dist/client-handler.d.ts +0 -43
  183. package/dist/components/app.d.ts +0 -23
  184. package/dist/components/app.js +0 -41
  185. package/dist/components/error-banner.d.ts +0 -21
  186. package/dist/components/error-banner.js +0 -27
  187. package/dist/components/message-bubble.d.ts +0 -23
  188. package/dist/components/message-bubble.js +0 -35
  189. package/dist/components/state-indicator.d.ts +0 -29
  190. package/dist/components/state-indicator.js +0 -37
  191. package/dist/components/thinking-indicator.d.ts +0 -16
  192. package/dist/components/thinking-indicator.js +0 -34
  193. package/dist/components/tool-icons.d.ts +0 -17
  194. package/dist/components/tool-icons.js +0 -128
  195. package/dist/components/transcript.d.ts +0 -25
  196. package/dist/components/transcript.js +0 -35
  197. package/dist/session-CWB7vmqz.js +0 -429
  198. package/dist/session.d.ts +0 -69
  199. package/dist/session.js +0 -2
  200. package/dist/signals.d.ts +0 -120
  201. package/dist/signals.js +0 -161
  202. package/dist/tsdown.config.d.ts +0 -2
  203. package/dist/types.test-d.d.ts +0 -7
@@ -0,0 +1,65 @@
1
+ /**
2
+ * How the next connection attempt is DIALLED, and the resume identity it dials
3
+ * with.
4
+ *
5
+ * Split out of `session/browser-session.ts` at the 500-line cap, along the seam that file
6
+ * already established when it moved socket plumbing into
7
+ * `session/reconnect.ts`: the state machine there reads as protocol logic,
8
+ * and this is the address it sends it to. What makes it one module rather than
9
+ * three extracted functions is that the three pieces of mutable state involved —
10
+ * the session id, whether this connection has ever completed a handshake, and
11
+ * whether the server is a broker — are read by nothing else in the core, and
12
+ * every one of them is only meaningful in the sentence "the URL for the next
13
+ * attempt".
14
+ */
15
+ import type { WebSocketConstructor } from "../types.ts";
16
+ import { type SessionTokenOption } from "./ticket.ts";
17
+ /** What the dialer needs from the session's options. */
18
+ export type DialOptions = {
19
+ platformUrl: string;
20
+ /** Tests inject one; it connects to the same-origin path and never reconnects. */
21
+ WebSocket?: WebSocketConstructor | undefined;
22
+ /** An id the caller manages itself — wins over what a previous load stored. */
23
+ resumeSessionId?: string | undefined;
24
+ /** Where the client is — see `VoiceSessionOptions.location`. Read per attempt. */
25
+ location?: string | (() => string | undefined) | undefined;
26
+ /** The owner's number — see `VoiceSessionOptions.phone`. Read per attempt. */
27
+ phone?: string | (() => string | undefined) | undefined;
28
+ /** This client's device id — see `VoiceSessionOptions.client`. Read per attempt. */
29
+ client?: string | (() => string | undefined) | undefined;
30
+ /** The session ticket — see `VoiceSessionOptions.token`. Asked per attempt. */
31
+ token?: SessionTokenOption;
32
+ };
33
+ export type Dialer = {
34
+ /** A socket for this attempt. */
35
+ open(): InstanceType<WebSocketConstructor>;
36
+ /**
37
+ * A completed handshake: adopt the server's session id and record that this
38
+ * connection has been established, so every later attempt resumes.
39
+ */
40
+ configured(sid: string): void;
41
+ /** A ticket for another socket on this server — `SessionIdentity.ticket`. */
42
+ ticket(): string | undefined | Promise<string | undefined>;
43
+ /** Drop the resume identity, so the next connect is a NEW session. */
44
+ forget(): void;
45
+ /**
46
+ * The session id a `config` frame CONFIRMED — `session.identity.sessionId()`.
47
+ * Not the resume identity above: that one is seeded from storage and from
48
+ * `resume(id)` before any server has said the session exists, and a page
49
+ * keying its history off an id no server confirmed files it under a guess.
50
+ */
51
+ sessionId(): string | undefined;
52
+ /**
53
+ * Make `sid` the resume identity — presented as `?sessionId=` by the next
54
+ * attempt and stored as a `config` frame's would be — so the next connect
55
+ * resumes THAT session. The caller has validated it.
56
+ */
57
+ adopt(sid: string): void;
58
+ };
59
+ /**
60
+ * @param onSessionId - Told whenever the CONFIRMED id changes (a `config` frame
61
+ * with a new id, `forget()`), so the session can notify `useSessionId()`
62
+ * even when no snapshot field moved with it.
63
+ * @internal
64
+ */
65
+ export declare function createDialer(options: DialOptions, onSessionId?: () => void): Dialer;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The deadline on a socket that opened but never became a session.
3
+ *
4
+ * A completed WebSocket handshake is not a session. The server builds the
5
+ * session synchronously from its own upgrade callback and sends `config` at
6
+ * zero RTT (`aai/host/ws-handler.ts`), so a socket that has been open for
7
+ * seconds with nothing on it is not slow — its peer is not a healthy agent
8
+ * server. That happens: a tunnel or proxy answers the `101` while the guest
9
+ * behind it is wedged, or the host dies between accepting and building the
10
+ * session.
11
+ *
12
+ * Nothing else catches it. partysocket's `connectionTimeout` covers only the
13
+ * handshake and is cleared the moment `open` fires, so the session reached
14
+ * `state: "ready"` — painted with the same live indicator the UI gives
15
+ * "listening" — and stayed there permanently: no `config` means
16
+ * `initAudioCapture` never runs, so there is no mic, no error, no retry, and
17
+ * nothing on screen to say so. Measured against a server that accepts and
18
+ * then says nothing: `ready` at 34ms, still `ready` and errorless when the
19
+ * probe gave up.
20
+ */
21
+ import type { SessionError } from "../types.ts";
22
+ /** What the session reports once the budget below is spent. */
23
+ export declare const HANDSHAKE_ERROR: SessionError;
24
+ export type HandshakeGuard = {
25
+ /** Start the deadline for the attempt that just opened. */
26
+ arm(): void;
27
+ /** Stop it — this socket is closing, or the connection is being torn down. */
28
+ disarm(): void;
29
+ /**
30
+ * The `config` frame arrived: stop the deadline AND spend nothing.
31
+ *
32
+ * Separate from {@link HandshakeGuard.disarm} because the budget is
33
+ * CONSECUTIVE, and only a completed handshake proves the peer is healthy. One
34
+ * guard covers a whole `connect()`, partysocket's retries included, so with a
35
+ * plain disarm the count survived every successful session in between: an
36
+ * hour-long call whose socket dropped three times, each drop timing out once
37
+ * before the next attempt succeeded, surfaced the permanent
38
+ * "Agent did not complete the session handshake" error against a peer that
39
+ * had answered every time. A close must NOT reset it — a wedged peer closes
40
+ * and reopens on its own, and resetting there is the unbounded re-dial loop
41
+ * the budget exists to bound.
42
+ */
43
+ succeeded(): void;
44
+ };
45
+ /**
46
+ * Watch one connection's handshake.
47
+ *
48
+ * `onRetry` fires when a timed-out attempt has been re-dialed (the sandbox
49
+ * behind the endpoint may have been replaced, and the URL provider re-brokers
50
+ * on the next attempt); `onExhausted` when the budget is spent, or when the
51
+ * socket has no reconnect machinery at all (an injected `options.WebSocket`).
52
+ *
53
+ * @internal
54
+ */
55
+ export declare function createHandshakeGuard(opts: {
56
+ socket: unknown;
57
+ signal: AbortSignal;
58
+ onRetry: () => void;
59
+ onExhausted: () => void;
60
+ }): HandshakeGuard;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The browser session core's import surface for the rest of the package.
3
+ *
4
+ * `createBrowserSession` and the types its handle is described by are what the
5
+ * package builds on; every other module in `session/` (the dialer, the
6
+ * reconnecting socket, the handshake guard, the two statecharts, the message
7
+ * handlers, the audio path's bring-up) is private to this directory. Private
8
+ * means NOT re-exported here: guard-invariants rule 37 fails an import from
9
+ * outside `session/` that names any module but this one.
10
+ *
11
+ * @module
12
+ */
13
+ export { loadAudioModules } from "./audio-setup.ts";
14
+ export { createBrowserSession } from "./browser-session.ts";
15
+ export { CLEARED_SESSION_STATE } from "./messages.ts";
16
+ export { resolveSessionToken, resolveSessionTokenSync, ticketCarriage } from "./ticket.ts";
17
+ export type { AgentCustomEvent, AgentStateFrame, BrowserSession, browserSessionBrand, SendTextOptions, SessionSnapshot, ToolCallOutcome, UserTurnControls, } from "./types.ts";
@@ -0,0 +1,2 @@
1
+ import { a as ticketCarriage, i as resolveSessionTokenSync, n as CLEARED_SESSION_STATE, o as loadAudioModules, r as resolveSessionToken, t as createBrowserSession } from "../session-D9jM5-TF.js";
2
+ export { CLEARED_SESSION_STATE, createBrowserSession, loadAudioModules, resolveSessionToken, resolveSessionTokenSync, ticketCarriage };
@@ -0,0 +1,63 @@
1
+ import type { AudioPath } from "./audio-state.ts";
2
+ import type { SessionStateMachine } from "./state.ts";
3
+ import { type ConnState, type SessionSnapshot } from "./types.ts";
4
+ /**
5
+ * Snapshot fields cleared when a session's conversation state is wiped —
6
+ * shared by the initial snapshot, `resetState()`, and `session.reset`.
7
+ * The empty arrays are safe to share: snapshot collections are never mutated
8
+ * in place, only replaced.
9
+ */
10
+ export declare const CLEARED_SESSION_STATE: {
11
+ messages: never[];
12
+ toolCalls: never[];
13
+ customEvents: never[];
14
+ agentState: null;
15
+ userTranscript: null;
16
+ agentTranscript: null;
17
+ error: null;
18
+ };
19
+ /** Config payload extracted from a `config` server message. */
20
+ export type SessionConfigMessage = {
21
+ sampleRate: number;
22
+ ttsSampleRate: number;
23
+ sid: string;
24
+ };
25
+ /** Dependencies the message handlers need from the owning session core. */
26
+ type MessageHandlerDeps = {
27
+ getSnapshot: () => SessionSnapshot;
28
+ updateState: (partial: Partial<SessionSnapshot>) => void;
29
+ conn: ConnState;
30
+ /** The session's state and error, as one fact — see `session/state.ts`. */
31
+ agentState: SessionStateMachine;
32
+ /**
33
+ * The microphone and the playback queue — see
34
+ * `session/audio-state.ts`. Frames say what happened (`enqueue`,
35
+ * `done`, `teardown`); whether the path is up, and what to do with audio
36
+ * that arrives before it is, are the machine's answers rather than this
37
+ * module's.
38
+ */
39
+ audio: AudioPath;
40
+ };
41
+ type MessageHandlers = {
42
+ /**
43
+ * Dispatch an incoming WebSocket message.
44
+ *
45
+ * Binary frames carry raw PCM16 audio chunks. Text frames are JSON-encoded
46
+ * {@link SessionEvent} values validated via Zod.
47
+ *
48
+ * Returns the parsed config if the message is a `config` message,
49
+ * otherwise `undefined`.
50
+ */
51
+ handleMessage(data: unknown): SessionConfigMessage | undefined;
52
+ };
53
+ /**
54
+ * Create the server→client message handlers for one session core.
55
+ *
56
+ * Encapsulates the per-session dedup counters (`customEventSeq`,
57
+ * `messageSeq`, `toolCallSeq`) that previously lived as closure locals in
58
+ * `createBrowserSession`. The turn-boundary epoch is NOT one of them: it lives
59
+ * on `conn` because the session core bumps it on teardown too (see
60
+ * `ConnState.turn`).
61
+ */
62
+ export declare function createMessageHandlers(deps: MessageHandlerDeps): MessageHandlers;
63
+ export {};
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The microphone's way onto the wire — backpressure, and the caller's MUTE.
3
+ *
4
+ * Split out of `session/browser-session.ts` at the source-length cap, along the seam the
5
+ * two share: both decide what a captured frame becomes on the socket, and
6
+ * nothing else in the core reads either.
7
+ *
8
+ * ## A muted frame is SILENCE, not an absent frame
9
+ *
10
+ * `setMicMuted(true)` keeps sending every frame the capture worklet produces,
11
+ * at the same length and cadence, zero-filled. Sending nothing would look
12
+ * cheaper and is wrong on three counts:
13
+ *
14
+ * - **Endpointing.** Hold-to-talk over an agent with AUTOMATIC turn detection
15
+ * is mute-until-pressed, unmute, speak, mute on release. The transcriber
16
+ * ends the turn on a stretch of silence; a stream that simply stops gives it
17
+ * no silence to measure, so the last utterance is answered late or not at
18
+ * all.
19
+ * - **The stream clock.** The transcriber timestamps by samples received. The
20
+ * runtime's own push-to-talk silences the mic the same way, for the same
21
+ * reason (`aai-runtime/src/transports/pipeline/commands.ts`, "sent as SILENCE rather than
22
+ * withheld").
23
+ * - **Idle.** Nothing is gained on the server side by going quiet: the session
24
+ * idle deadline deliberately does not count user audio (`ServerSession.onAudio`
25
+ * in the runtime), so a muted caller is retired exactly as a silent one is —
26
+ * while a provider that closes a socket fed no audio at all would see a gap.
27
+ *
28
+ * ## It is UI state, not connection state
29
+ *
30
+ * The flag lives on the snapshot (`micMuted`) and nothing that tears a
31
+ * connection down touches it — not a reconnect, a resume, `disconnect()`,
32
+ * `reset()` or `end()` — and it can be set before the first `connect()`, so a
33
+ * UI can open a typed-only session already muted. Muting sends no command and
34
+ * interrupts nothing; it only changes what the next frame carries.
35
+ *
36
+ * It sits AFTER the capture worklet, so the worklet's own digital-silence
37
+ * probe (`onMicSilent`, `MIC_SILENCE_PROBE_MS` in `audio/voice-io.ts`) still measures
38
+ * the real input: zeros this module writes can never read as a dead mic.
39
+ *
40
+ * @module
41
+ */
42
+ import type { ConnState, SessionSnapshot } from "./types.ts";
43
+ /** What the mic sender needs from the session around it. @internal */
44
+ export type MicDeps = {
45
+ conn: ConnState;
46
+ snapshot: () => SessionSnapshot;
47
+ updateState: (partial: Partial<SessionSnapshot>) => void;
48
+ };
49
+ /** The mic's wire half — see this module's doc. @internal */
50
+ export type MicSender = {
51
+ /** Send one captured PCM16 frame — as silence while muted. */
52
+ sendAudio(bytes: ArrayBuffer): void;
53
+ /**
54
+ * Send the pre-connect burst (`session/preconnect.ts`) — muted like
55
+ * any frame, but past the backpressure drop: those frames are the audio the
56
+ * burst exists to deliver, bounded by `PRE_CONNECT_MAX_SECONDS`, and a
57
+ * dropped one would splice the caller's opener mid-word.
58
+ */
59
+ sendBuffered(chunks: ArrayBuffer[]): void;
60
+ /** Mute or unmute the caller. Idempotent, local, and connection-independent. */
61
+ setMicMuted(muted: boolean): void;
62
+ };
63
+ /** Build one session's mic sender. @internal */
64
+ export declare function createMicSender(deps: MicDeps): MicSender;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Pre-connect audio: the microphone opens when the caller presses start, not
3
+ * when the server's `config` frame arrives, and what they say in between is
4
+ * sent ahead of the live stream.
5
+ *
6
+ * Without it the mic is requested only once `config` names the audio rates —
7
+ * a handshake after the press, plus a sandbox boot on the platform — so an
8
+ * eager "hi, I need to…" spoken while the agent is still joining never reaches
9
+ * the server. The capture runs at {@link PRE_CONNECT_SAMPLE_RATE}, a guess
10
+ * `createVoiceIO` adopts whole when right and resamples when not (`audio/preconnect.ts`).
11
+ *
12
+ * ## Who owns the microphone
13
+ *
14
+ * This holder, from `begin()` until `take()`; the audio path's bring-up after
15
+ * that (`session/audio-setup.ts`), which releases it if the bring-up is
16
+ * abandoned. A reconnect BEFORE `config` keeps the capture buffering — that is
17
+ * exactly the audio this exists for — so only the session's terminal paths
18
+ * call `release()`.
19
+ *
20
+ * @module
21
+ */
22
+ import type { PreConnectCapture } from "../audio/index.ts";
23
+ /**
24
+ * The rate the capture guesses: the SDK's default STT rate
25
+ * (`DEFAULT_STT_SAMPLE_RATE`), which is also the only rate an AssemblyAI S2S
26
+ * agent accepts. A miss costs a resample of the buffer, not the audio.
27
+ */
28
+ export declare const PRE_CONNECT_SAMPLE_RATE = 16000;
29
+ /**
30
+ * The most pre-connect audio held — the LATEST this many seconds. Bounds a
31
+ * cold sandbox boot or a stalled handshake; at 16 kHz it is 320 KB, well
32
+ * inside the server's pre-ready buffer (`MAX_WS_PAYLOAD_BYTES`).
33
+ */
34
+ export declare const PRE_CONNECT_MAX_SECONDS = 10;
35
+ /** The session core's handle on its pre-connect capture. @internal */
36
+ export type PreConnectAudio = {
37
+ /**
38
+ * Prefetch the audio modules, then open the mic and start buffering unless
39
+ * a capture is already held (or the session opted out).
40
+ */
41
+ begin(): void;
42
+ /** Hand the held capture to a bring-up, or `null` when none is held. */
43
+ take(): Promise<PreConnectCapture | null> | null;
44
+ /** Release a held capture: the session is over or starting afresh. */
45
+ release(): void;
46
+ };
47
+ /** Build one session's pre-connect capture holder. @internal */
48
+ export declare function createPreConnectAudio(enabled: boolean): PreConnectAudio;
@@ -0,0 +1,25 @@
1
+ import type { WebSocketConstructor } from "../types.ts";
2
+ /**
3
+ * Open partysocket's reconnecting WebSocket. The URL and the subprotocols are
4
+ * *providers*, re-evaluated on every attempt (async supported), so each retry
5
+ * picks up the current broker-named endpoint, resume URL and session ticket
6
+ * rather than the ones the session started with. partysocket calls the URL
7
+ * provider first and the protocols provider straight after it, in the same
8
+ * tick, for each attempt.
9
+ */
10
+ export declare function openReconnectingSocket(urlProvider: () => Promise<string>, protocolsProvider?: () => Promise<string[] | null>): InstanceType<WebSocketConstructor>;
11
+ /**
12
+ * Force a fresh connection attempt on a reconnecting socket, reporting
13
+ * whether it was one. Used for a failure partysocket cannot see: a socket it
14
+ * considers open and healthy, whose peer never completed OUR handshake.
15
+ * Returns false for an injected `options.WebSocket` (tests), which has no
16
+ * reconnect machinery — the caller then treats the failure as terminal.
17
+ */
18
+ export declare function forceReconnect(socket: unknown): boolean;
19
+ /**
20
+ * True while `socket` is a reconnecting socket that will retry after the
21
+ * close event currently being handled. partysocket schedules the retry
22
+ * *before* dispatching `close`, so `retryCount` already names the attempt
23
+ * just scheduled — at `maxRetries` it has given up.
24
+ */
25
+ export declare function reconnectPending(socket: unknown): boolean;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Where a session id survives a page RELOAD.
3
+ *
4
+ * The id is what `?sessionId=` presents on reconnect, and it is the key the
5
+ * agent's slot state and event log live under — so a reload that cannot produce
6
+ * it starts a brand-new session, and a UI driven by `useAgentState` comes back
7
+ * empty even though the agent still holds the cart. The server side of the
8
+ * reconstitution was already built (`pushStateSnapshot` force-pushes the
9
+ * projection after hydration on every start, `state.updated` lands in
10
+ * `agentState`); what was missing is that nothing in the browser remembered the
11
+ * id across a reload. `onSessionId`/`resumeSessionId` let a client wire it by
12
+ * hand and exactly one of fourteen templates did, which is the shape of a
13
+ * default in the wrong place.
14
+ *
15
+ * **`sessionStorage`, deliberately, and this is the opposite call from the
16
+ * studio's session token.** A reload and a same-tab navigation survive it; a new
17
+ * tab and a visit tomorrow do not, which is what we want here rather than a
18
+ * limitation: presenting a day-old id suppresses the greeting
19
+ * (`parseWsUpgradeParams` keys that off the id's mere presence) and rejoins a
20
+ * conversation whose context is long gone. The studio token is a credential
21
+ * whose value is not being asked to sign out; this is a pointer into a live call.
22
+ *
23
+ * Keyed by the agent's own URL, so two agents served from one origin — which is
24
+ * every deployed agent, at `/:slug/` — cannot inherit each other's session.
25
+ *
26
+ * Every access is guarded, and the guard lives in `_web-storage.ts`: a session
27
+ * that cannot be remembered must degrade to today's behaviour rather than
28
+ * failing to start.
29
+ */
30
+ /** The stored session id for this agent, or undefined. @internal */
31
+ export declare function readStoredSessionId(platformUrl: string): string | undefined;
32
+ /** Remember this agent's session id for the next load. @internal */
33
+ export declare function writeStoredSessionId(platformUrl: string, sessionId: string): void;
34
+ /**
35
+ * Forget it, so the next load is a NEW session.
36
+ *
37
+ * Called from `end()`, which is the clear-and-forget the "New Conversation"
38
+ * button runs: leaving the id behind there would have the next load rejoin the
39
+ * conversation the user just discarded, greeting suppressed.
40
+ *
41
+ * @internal
42
+ */
43
+ export declare function clearStoredSessionId(platformUrl: string): void;
44
+ /**
45
+ * The last session ticket this agent's `client-config` issued — beside the id
46
+ * and for its reason: on the managed platform a ticket is BOUND to its session,
47
+ * and presenting it (`SESSION_TICKET_HEADER`) is what makes the broker re-mint
48
+ * for that session rather than a new one, so a reload that lost it cannot
49
+ * resume. As sensitive as the id it stands beside, and stored the same way.
50
+ *
51
+ * @internal
52
+ */
53
+ export declare function readStoredTicket(platformUrl: string): string | undefined;
54
+ /** Remember the last server-issued ticket. @internal */
55
+ export declare function writeStoredTicket(platformUrl: string, ticket: string): void;
56
+ /** Forget it, with the session it was for. @internal */
57
+ export declare function clearStoredTicket(platformUrl: string): void;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * The browser session's {@link AgentState}, and the error beside it, as a
3
+ * statechart.
4
+ *
5
+ * These two were written independently from thirteen sites across
6
+ * `session/browser-session.ts`, `session/messages.ts` and
7
+ * `session/audio-setup.ts`, each one deciding for itself whether its write
8
+ * was legal by reading the snapshot back first. Three shipped bugs came out of
9
+ * that, and all three are the same shape — a transition nothing forbade:
10
+ *
11
+ * - **A straggler audio chunk flipped an errored session to `"speaking"`.**
12
+ * Guarded by hand at the call site
13
+ * (`if (snap.state === "error" || (snap.state === "disconnected" && …))`).
14
+ * Here `error` simply does not handle `SPEAK`.
15
+ * - **The frame that announced a session's death also wiped the banner
16
+ * reporting it.** Every fatal path in the host tears the transport down, and
17
+ * tearing down EMITS — so `reply.cancelled` arrived right behind the error and
18
+ * `toListening()` painted a live-mic state over it. A missing provider key is
19
+ * the case that made it visible: the one message that says exactly what to fix
20
+ * was on screen for a few hundred milliseconds and left a session that looked
21
+ * live and was deaf.
22
+ * - **A later frame RECOVERED the state.** `clearRecoveredError` reads a
23
+ * non-error frame as proof the session works, which is right for a
24
+ * turn-level failure and wrong for a server error that ended the call.
25
+ *
26
+ * The second and third were fixed with a `conn.fatalError` boolean that every
27
+ * writer had to remember to consult. It is the `fatal` region here, so
28
+ * forgetting is not available: `LISTEN` and `ACTIVITY` are declined in one
29
+ * place rather than at each of their five call sites.
30
+ *
31
+ * ## Two regions, because the fatal latch OUTLIVES the error state
32
+ *
33
+ * `fatal` is not a substate of `error`, tempting as that looks. It is cleared by
34
+ * exactly one thing — the next `config` frame, i.e. a completed handshake — and
35
+ * that is per CONNECTION rather than per session, so a reconnect that really
36
+ * works is not pinned to a dead session's banner. Between the error and that
37
+ * frame the phase runs `error → connecting → ready` while the latch stays set,
38
+ * which a substate cannot express.
39
+ *
40
+ * ## The published surface is unchanged
41
+ *
42
+ * {@link AgentState} is a public type in the versioned `aai-ui:session`
43
+ * capability, so {@link AgentStateSnapshot.state} is one of its seven names and
44
+ * nothing here widens it. The internal distinctions live in the second region
45
+ * and in context, not in the projection.
46
+ */
47
+ import type { AgentState, SessionError } from "../types.ts";
48
+ /** What the session core folds into its snapshot after each transition. */
49
+ export type AgentStateSnapshot = {
50
+ state: AgentState;
51
+ error: SessionError | null;
52
+ };
53
+ /**
54
+ * Everything that moves the session's state.
55
+ *
56
+ * Named for what HAPPENED rather than for the state wanted, which is the point:
57
+ * `reply.cancelled` sends `LISTEN` and gets `"error"` back when the session is
58
+ * fatally over, and the caller neither knows nor needs to.
59
+ */
60
+ export type AgentStateEvent =
61
+ /** A connect attempt started — including a reconnect or a handshake retry. */
62
+ {
63
+ type: "CONNECT";
64
+ }
65
+ /** The socket opened; the handshake has not completed yet. */
66
+ | {
67
+ type: "SOCKET_OPEN";
68
+ }
69
+ /** A completed handshake: a live session, whatever ended the last one. */
70
+ | {
71
+ type: "HANDSHAKE_COMPLETE";
72
+ }
73
+ /** A turn boundary, or the audio path coming up: back to the mic. */
74
+ | {
75
+ type: "LISTEN";
76
+ }
77
+ /** A reset: back to the mic AND the conversation cleared. */
78
+ | {
79
+ type: "RESET";
80
+ }
81
+ /** The agent is working. */
82
+ | {
83
+ type: "THINK";
84
+ }
85
+ /** Audio for this turn reached the client. */
86
+ | {
87
+ type: "SPEAK";
88
+ }
89
+ /** A non-error frame arrived, so the session demonstrably works. */
90
+ | {
91
+ type: "ACTIVITY";
92
+ }
93
+ /** A turn-level failure: a banner over a session that keeps running. */
94
+ | {
95
+ type: "TURN_ERROR";
96
+ error: SessionError;
97
+ }
98
+ /** The session is over. */
99
+ | {
100
+ type: "FATAL";
101
+ error: SessionError;
102
+ }
103
+ /**
104
+ * A failure that stops the session but might not be the end of it: the
105
+ * handshake exhausting its retries, a socket error close, or the audio path
106
+ * dying. The `fatal` latch stays clear, so a later non-error frame recovers
107
+ * — which is right for a client-side failure and is exactly what a FATAL
108
+ * server error must not get.
109
+ */
110
+ | {
111
+ type: "FAILED";
112
+ error: SessionError;
113
+ }
114
+ /** The socket closed for good. */
115
+ | {
116
+ type: "CLOSED";
117
+ }
118
+ /** The caller hung up. */
119
+ | {
120
+ type: "DISCONNECT";
121
+ }
122
+ /** The caller ended the session and forgot it. */
123
+ | {
124
+ type: "END";
125
+ };
126
+ /** The session core's handle on its own state. */
127
+ export type SessionStateMachine = {
128
+ /** The published state and the error beside it. */
129
+ snapshot(): AgentStateSnapshot;
130
+ /**
131
+ * Apply `event` and return the resulting projection.
132
+ *
133
+ * Returns rather than writes so the caller folds it into ONE `updateState`
134
+ * call: a snapshot is published on every write, and splitting one transition
135
+ * into two would let a subscriber render half of it.
136
+ */
137
+ apply(event: AgentStateEvent): AgentStateSnapshot;
138
+ /**
139
+ * Is the session fatally over? Asked by the one caller that has to clear the
140
+ * CONVERSATION and not merely the state — a `session.reset` on a dead session
141
+ * keeps its whole banner.
142
+ */
143
+ fatal(): boolean;
144
+ };
145
+ /** Create the state machine for one browser session. */
146
+ export declare function createSessionStateMachine(): SessionStateMachine;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The session ticket one connection attempt presents — `VoiceSessionOptions.token`,
3
+ * else the `sessionToken` a server's `client-config` issued.
4
+ *
5
+ * A browser cannot set headers on a WebSocket, so the ticket rides the
6
+ * `Sec-WebSocket-Protocol` list as `aai.auth.<ticket>`, offered AFTER the plain
7
+ * `aai.session` protocol: a browser fails any handshake whose response selects
8
+ * none of its offers, and the server (`selectSessionProtocol` in
9
+ * `@alexkroman1/aai-runtime/auth`) selects the plain one so the ticket is never
10
+ * echoed back. A server older than tickets selects the first offer, which is
11
+ * the plain one too, so offering a ticket never fails a handshake.
12
+ *
13
+ * A value that is not a valid subprotocol token (RFC 6455 §4.1 — an HTTP
14
+ * token) would make `new WebSocket` throw, so it travels as `?token=` instead,
15
+ * the server's fallback. A minted ticket is base64url with one dot, and so is a
16
+ * JWT: neither takes that path.
17
+ */
18
+ import type { VoiceSessionOptions } from "../types.ts";
19
+ /** The shape of `VoiceSessionOptions.token`. */
20
+ export type SessionTokenOption = VoiceSessionOptions["token"];
21
+ /** What the getter is told about the attempt it serves: the session it resumes. */
22
+ export type SessionTokenAttempt = {
23
+ readonly sessionId: string | undefined;
24
+ };
25
+ /** How one attempt carries its ticket: subprotocols, or the `?token=` fallback. */
26
+ export type TicketCarriage = {
27
+ protocols?: string[];
28
+ queryToken?: string;
29
+ };
30
+ /**
31
+ * Ask the `token` option for THIS attempt's ticket. A getter that throws or
32
+ * rejects yields none — the attempt still dials, and a server that requires a
33
+ * ticket refuses it with a reason the session reports, rather than the attempt
34
+ * failing before a socket exists (partysocket reports a provider failure as an
35
+ * `error` with no `close`, which would leave the session on "connecting").
36
+ */
37
+ export declare function resolveSessionToken(option: SessionTokenOption, attempt: SessionTokenAttempt): Promise<string | undefined>;
38
+ /**
39
+ * The same, for an injected `WebSocket` constructor: that path opens its socket
40
+ * synchronously, so an asynchronous getter cannot be awaited there.
41
+ */
42
+ export declare function resolveSessionTokenSync(option: SessionTokenOption, attempt: SessionTokenAttempt): string | undefined;
43
+ /** How `token` travels on one attempt; nothing at all when there is no ticket. */
44
+ export declare function ticketCarriage(token: string | undefined): TicketCarriage;