@alexkroman1/aai-ui 0.12.2 → 0.13.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 +257 -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 +38 -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 +58 -0
- package/dist/components/button.js +85 -0
- package/dist/components/chat-view.d.ts +35 -0
- package/dist/components/chat-view.js +3 -0
- package/dist/components/console-shell.d.ts +85 -0
- package/dist/components/controls.d.ts +33 -0
- package/dist/components/controls.js +49 -0
- 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 +48 -0
- package/dist/components/message-list.js +3 -0
- 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 +42 -0
- package/dist/components/sidebar-layout.js +56 -0
- package/dist/components/start-screen.d.ts +37 -0
- package/dist/components/start-screen.js +71 -0
- package/dist/components/tool-call-block.d.ts +28 -0
- package/dist/components/tool-call-block.js +3 -0
- 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-cGFGID-2.js +1 -0
- package/dist/default-client/assets/capture-processor-BUABoo3_.js +90 -0
- package/dist/default-client/assets/index-8ihb1Urm.css +2 -0
- package/dist/default-client/assets/index-BQURVARl.js +126 -0
- package/dist/default-client/assets/playback-processor-ABOa11j0.js +314 -0
- package/dist/default-client/favicon.ico +0 -0
- package/dist/default-client/index.html +15 -0
- package/dist/default-client.d.ts +23 -0
- package/dist/define-client.d.ts +162 -26
- package/dist/define-client.js +201 -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 +129 -32
- package/dist/index.js +5503 -18
- 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 +31 -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 +30 -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-CE0Ipsb3.js +2129 -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 +58 -31
- package/styles.css +152 -30
- package/dist/_components/app.d.ts +0 -23
- package/dist/_components/app.js +0 -41
- package/dist/_components/button.d.ts +0 -50
- package/dist/_components/button.js +0 -46
- package/dist/_components/chat-view.d.ts +0 -29
- package/dist/_components/chat-view.js +0 -61
- package/dist/_components/controls.d.ts +0 -18
- package/dist/_components/controls.js +0 -37
- 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/message-list.d.ts +0 -21
- package/dist/_components/message-list.js +0 -76
- package/dist/_components/sidebar-layout.d.ts +0 -21
- package/dist/_components/sidebar-layout.js +0 -36
- package/dist/_components/start-screen.d.ts +0 -26
- package/dist/_components/start-screen.js +0 -51
- 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-call-block.d.ts +0 -27
- package/dist/_components/tool-call-block.js +0 -118
- 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/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/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,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `useInbox()`'s built-in playback: a notice's PCM through an `AudioContext` of
|
|
3
|
+
* its own.
|
|
4
|
+
*
|
|
5
|
+
* **Its own context, not the session's.** The session's playback context exists
|
|
6
|
+
* only while a session is up (it is built on the `config` frame and closed on
|
|
7
|
+
* hang-up), and a notice arrives exactly when no session is — that is the
|
|
8
|
+
* inbox's whole reason to exist. It also runs through a jitter-buffer worklet
|
|
9
|
+
* tuned for a streamed reply, and a notice is one finished buffer.
|
|
10
|
+
*
|
|
11
|
+
* **Unlocked by the first user gesture, because of the autoplay policy.** A
|
|
12
|
+
* context created outside a gesture starts `suspended` and stays silent until
|
|
13
|
+
* `resume()` is called INSIDE one — and a reminder arrives hours after the last
|
|
14
|
+
* click. So the player is unlocked from a capture-phase `pointerdown` /
|
|
15
|
+
* `keydown` listener (`useInbox` installs it), which creates and resumes the
|
|
16
|
+
* context while the browser still counts the page as activated; afterwards a
|
|
17
|
+
* notice plays whenever it lands. A page that has had no gesture at all plays
|
|
18
|
+
* nothing, which is the browser's rule and not one this can route around.
|
|
19
|
+
*
|
|
20
|
+
* **16 kHz** because that is what an inbox notice carries: the rate the device
|
|
21
|
+
* protocol fixed (`inbox.c` plays at `BOARD_SAMPLE_RATE`), so a step building a
|
|
22
|
+
* notice asks `stepSpeak` for it (`stepSpeak`'s own default is 24 kHz — a notice
|
|
23
|
+
* at that rate plays slow and low here; pass `play: false` and play it
|
|
24
|
+
* yourself). The buffer is created at 16 kHz and the context resamples it — no
|
|
25
|
+
* worklet, no resampler of ours.
|
|
26
|
+
*/
|
|
27
|
+
/** The rate a notice's PCM16LE mono is played at. */
|
|
28
|
+
export declare const NOTICE_SAMPLE_RATE = 16000;
|
|
29
|
+
/** The slice of `AudioBuffer` the player writes. */
|
|
30
|
+
export type NoticeBuffer = {
|
|
31
|
+
getChannelData(channel: number): Float32Array;
|
|
32
|
+
};
|
|
33
|
+
/** The slice of `AudioBufferSourceNode` the player drives. */
|
|
34
|
+
export type NoticeSource = {
|
|
35
|
+
buffer: NoticeBuffer | null;
|
|
36
|
+
onended: ((ev: never) => unknown) | null;
|
|
37
|
+
connect(destination: unknown): unknown;
|
|
38
|
+
start(): void;
|
|
39
|
+
stop(): void;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The slice of `AudioContext` the player uses — declared rather than `Pick`ed
|
|
43
|
+
* so a test's fake satisfies it without a cast, and injectable because jsdom
|
|
44
|
+
* has no `AudioContext`. `defaultContext()` below is what proves the real one
|
|
45
|
+
* still fits.
|
|
46
|
+
*/
|
|
47
|
+
export type NoticeAudioContext = {
|
|
48
|
+
readonly state: AudioContextState;
|
|
49
|
+
readonly destination: unknown;
|
|
50
|
+
resume(): Promise<void>;
|
|
51
|
+
close(): Promise<void>;
|
|
52
|
+
createBuffer(channels: number, length: number, sampleRate: number): NoticeBuffer;
|
|
53
|
+
createBufferSource(): NoticeSource;
|
|
54
|
+
};
|
|
55
|
+
/** A notice player — see the module doc. */
|
|
56
|
+
export type NoticePlayer = {
|
|
57
|
+
/** Create and resume the context — call from inside a user gesture. */
|
|
58
|
+
unlock(): void;
|
|
59
|
+
/** Play `pcm` (PCM16LE mono at {@link NOTICE_SAMPLE_RATE}); empty audio plays nothing. */
|
|
60
|
+
play(pcm: Uint8Array): void;
|
|
61
|
+
/** Stop every notice still playing. */
|
|
62
|
+
stop(): void;
|
|
63
|
+
/** Stop, and release the context. A later `unlock()`/`play()` makes a new one. */
|
|
64
|
+
close(): void;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* @param makeContext - How a context is made; the page's `AudioContext` by
|
|
68
|
+
* default. Where there is none (a server render, jsdom) the player is silent.
|
|
69
|
+
*/
|
|
70
|
+
export declare function createNoticePlayer(makeContext?: (() => NoticeAudioContext) | undefined): NoticePlayer;
|
package/dist/page.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/** @jsxImportSource react */
|
|
2
|
+
/**
|
|
3
|
+
* `mountPage()` — mount a WORKFLOW APP's UI: React, theme, no session.
|
|
4
|
+
*
|
|
5
|
+
* The twin of `mountClient()` for an agent whose front door is a form rather than a
|
|
6
|
+
* microphone (`workflowApp()`). It is a separate entry rather than
|
|
7
|
+
* an option on `mountClient()` because of what `mountClient()` unavoidably does: it
|
|
8
|
+
* constructs a `BrowserSession`, which owns a WebSocket URL provider, an audio
|
|
9
|
+
* graph, and a microphone request. A flag would have to make all of that
|
|
10
|
+
* conditional, and every session hook would then have to answer "what does this
|
|
11
|
+
* mean with no session?" — so the honest split is two mounts. A page that wants
|
|
12
|
+
* voice uses `mountClient()`; a page that wants neither audio nor a socket uses this.
|
|
13
|
+
*
|
|
14
|
+
* Authoring is otherwise identical — the file is still `client.tsx`, still
|
|
15
|
+
* React, still Tailwind, still the same theme tokens — so a workflow app reads
|
|
16
|
+
* like every other agent. What it reaches for instead of `useSession()` is
|
|
17
|
+
* `useWorkflowSubmit()` / `useWorkflowRun()`, neither of which needs a client
|
|
18
|
+
* built for it.
|
|
19
|
+
*
|
|
20
|
+
* And `component` is OPTIONAL, the way `mountClient()`'s is: with none, this
|
|
21
|
+
* mounts the generated shell in `_page-shell.tsx`. That is also what a workflow
|
|
22
|
+
* app with NO `client.tsx` at all now gets — `default-client.tsx`, the prebuilt
|
|
23
|
+
* page, branches on the agent's declared front door and mounts this.
|
|
24
|
+
*/
|
|
25
|
+
import { type ComponentType } from "react";
|
|
26
|
+
import type { ClientTheme } from "./types.ts";
|
|
27
|
+
/**
|
|
28
|
+
* Configuration for {@link mountPage}.
|
|
29
|
+
*
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export type PageConfig = {
|
|
33
|
+
/**
|
|
34
|
+
* The root component, rendered instead of the generated shell.
|
|
35
|
+
*
|
|
36
|
+
* **Optional**, the way `mountClient()`'s is: leave it out and `mountPage()`
|
|
37
|
+
* renders a form per declared workflow, the run's progress, its failure and
|
|
38
|
+
* its output — see `_page-shell.tsx` for what that shell is composed of and
|
|
39
|
+
* why it is deliberately functional rather than designed. It was REQUIRED,
|
|
40
|
+
* because "a workflow app has no default shell to fall back to" — true of a
|
|
41
|
+
* session and false of the page, and it cost the six shipped workflow
|
|
42
|
+
* templates 220-511 lines each of the same composition.
|
|
43
|
+
*/
|
|
44
|
+
component?: ComponentType;
|
|
45
|
+
/** CSS selector or DOM element to render into. Defaults to `"#app"`. */
|
|
46
|
+
target?: string | HTMLElement;
|
|
47
|
+
/**
|
|
48
|
+
* Page title. Set only when given, so a title the HTML shell declared is never
|
|
49
|
+
* clobbered — the same rule `mountClient()`'s custom-component tier follows.
|
|
50
|
+
*/
|
|
51
|
+
name?: string;
|
|
52
|
+
/** Theme color overrides, read by the same tokens the voice components use. */
|
|
53
|
+
theme?: ClientTheme;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Handle returned by {@link mountPage}. `Disposable`, so `using` works.
|
|
57
|
+
*
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
60
|
+
export type PageHandle = {
|
|
61
|
+
/** Unmount the React tree. */
|
|
62
|
+
dispose(): void;
|
|
63
|
+
/** Alias for `dispose` for use with `using`. */
|
|
64
|
+
[Symbol.dispose](): void;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Mount a page for an agent whose work happens in workflows.
|
|
68
|
+
*
|
|
69
|
+
* There is deliberately no session, no microphone, and no socket: the page talks
|
|
70
|
+
* to the agent over the workflow HTTP API (`useWorkflowSubmit`/`useWorkflowRun`),
|
|
71
|
+
* which is durable and outlives the tab.
|
|
72
|
+
*
|
|
73
|
+
* **Config only:** leave `component` out and the generated shell renders — a
|
|
74
|
+
* form per declared workflow, the run's progress, its failure, its output.
|
|
75
|
+
*
|
|
76
|
+
* **A custom component:** pass `component` and it is rendered inside the theme
|
|
77
|
+
* provider instead. The pieces the default shell is built from are all published
|
|
78
|
+
* (`useWorkflows`, `<WorkflowFields>`, `useWorkflowSubmit`,
|
|
79
|
+
* `<WorkflowProgress>`, `<WorkflowRunError>`), so replacing the shell does not
|
|
80
|
+
* mean starting from `fetch`.
|
|
81
|
+
*
|
|
82
|
+
* @example The default shell
|
|
83
|
+
* ```tsx
|
|
84
|
+
* import { mountPage } from "@alexkroman1/aai-ui";
|
|
85
|
+
*
|
|
86
|
+
* mountPage({ name: "Digest" });
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @example A custom component
|
|
90
|
+
* ```tsx
|
|
91
|
+
* import { mountPage, useWorkflows } from "@alexkroman1/aai-ui";
|
|
92
|
+
*
|
|
93
|
+
* function App() {
|
|
94
|
+
* // No client to build and none to hoist: every workflow hook defaults to one
|
|
95
|
+
* // aimed at the agent serving this page, built lazily and once.
|
|
96
|
+
* const { workflows, loading } = useWorkflows();
|
|
97
|
+
* if (loading) return <p>Loading…</p>;
|
|
98
|
+
* return (
|
|
99
|
+
* <ul>
|
|
100
|
+
* {workflows.map((entry) => (
|
|
101
|
+
* <li key={entry.name}>{entry.description ?? entry.name}</li>
|
|
102
|
+
* ))}
|
|
103
|
+
* </ul>
|
|
104
|
+
* );
|
|
105
|
+
* }
|
|
106
|
+
*
|
|
107
|
+
* mountPage({ name: "Digest", component: App });
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @throws If the target element is not found in the DOM.
|
|
111
|
+
*
|
|
112
|
+
* @public
|
|
113
|
+
*/
|
|
114
|
+
export declare function mountPage(config?: PageConfig): PageHandle;
|
package/dist/phone.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `phoneE164` — a number as a person types it, in the E.164 form
|
|
3
|
+
* `VoiceSessionOptions.phone` must carry, or `undefined` when it cannot be
|
|
4
|
+
* one. The server strips punctuation too, but DROPS a number with no `+`
|
|
5
|
+
* country code (guessing one would text a stranger), so a settings field that
|
|
6
|
+
* accepts `(503) 555-0123` must say which country it means — this is where it
|
|
7
|
+
* says so, and where a page can tell the user the number will not be used
|
|
8
|
+
* before a connect silently ignores it.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Options for {@link phoneE164}.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export type PhoneE164Options = {
|
|
18
|
+
/**
|
|
19
|
+
* The country calling code to assume for a number typed WITHOUT one — `"1"`
|
|
20
|
+
* for the US and Canada, `"44"` for the UK. Without it, only a number that
|
|
21
|
+
* already starts with `+` (or the `00` international prefix) is accepted.
|
|
22
|
+
*
|
|
23
|
+
* With `"1"`, a 10-digit number takes `+1`, and an 11-digit one starting
|
|
24
|
+
* with `1` is taken as already carrying it. With any other code, one leading
|
|
25
|
+
* trunk `0` is dropped before the code is prepended (`020 7946 0958` with
|
|
26
|
+
* `"44"` is `+442079460958`).
|
|
27
|
+
*/
|
|
28
|
+
countryCode?: string | undefined;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The phone number `typed` in E.164 form (`"+15035550123"`), or `undefined`
|
|
32
|
+
* when it cannot be one — see {@link PhoneE164Options} for how a number
|
|
33
|
+
* without a country code is read.
|
|
34
|
+
*
|
|
35
|
+
* @example The session's phone, from a stored setting, assuming North America
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { mountClient, phoneE164 } from "@alexkroman1/aai-ui";
|
|
38
|
+
*
|
|
39
|
+
* declare function storedPhone(): string;
|
|
40
|
+
*
|
|
41
|
+
* mountClient({ phone: () => phoneE164(storedPhone(), { countryCode: "1" }) });
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @param typed - The number as entered.
|
|
45
|
+
* @param options - The country to assume; see {@link PhoneE164Options}.
|
|
46
|
+
* @returns The E.164 number, or `undefined`.
|
|
47
|
+
*
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
export declare function phoneE164(typed: string, options?: PhoneE164Options): string | undefined;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `routeFetch` — a call to the agent's OWN JSON routes, `agent({ routes })`,
|
|
3
|
+
* which its server mounts under `/api`.
|
|
4
|
+
*
|
|
5
|
+
* Every page that used them wrote the same fifteen lines: resolve `/api/…`
|
|
6
|
+
* against the page, add `?client=` (a route's `req.clientId` is how it knows
|
|
7
|
+
* whose data to read), send the body as JSON, and turn a failure into an
|
|
8
|
+
* `Error` carrying the route's own `{ error }` sentence rather than a status
|
|
9
|
+
* code. Two of those are easy to get subtly wrong, and both are why this is
|
|
10
|
+
* here:
|
|
11
|
+
*
|
|
12
|
+
* - **The URL is resolved against the page's DIRECTORY**, not the origin, so an
|
|
13
|
+
* agent served below a path (`/:slug/`) reaches its own `/:slug/api`. An
|
|
14
|
+
* absolute `/api/…` would reach the origin's.
|
|
15
|
+
* - **A non-JSON failure** (a proxy's HTML 502, an empty 500) still becomes an
|
|
16
|
+
* `Error` naming the method, path and status, never a `SyntaxError` from
|
|
17
|
+
* parsing a body that was not JSON.
|
|
18
|
+
*
|
|
19
|
+
* `useRoute` (`use-route.ts`) is the reading half, for a component.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The HTTP methods an `agent({ routes })` key can declare.
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export type RouteMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
29
|
+
/**
|
|
30
|
+
* Options for {@link routeFetch}.
|
|
31
|
+
*
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
export type RouteFetchOptions = {
|
|
35
|
+
/**
|
|
36
|
+
* The client the call is about, sent as `?client=` — the route's
|
|
37
|
+
* `req.clientId`. `useClientId()` is the session's; absent or empty sends
|
|
38
|
+
* none.
|
|
39
|
+
*/
|
|
40
|
+
client?: string | undefined;
|
|
41
|
+
/** The agent's base URL. Default: the page's own directory. */
|
|
42
|
+
baseUrl?: string | undefined;
|
|
43
|
+
/** Abort the request. */
|
|
44
|
+
signal?: AbortSignal | undefined;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Call one of the agent's own routes and return its JSON — see this module's
|
|
48
|
+
* doc for what it handles.
|
|
49
|
+
*
|
|
50
|
+
* `path` is the route as declared, without `/api`: `routeFetch("GET",
|
|
51
|
+
* "/memories")` calls `"GET /memories"`. A body is sent as JSON. A response
|
|
52
|
+
* that is not 2xx throws an `Error` whose message is the route's `{ error }`
|
|
53
|
+
* field when it answered one (`routeResponse(404, { error: "No such memory" })`),
|
|
54
|
+
* else `"<METHOD> <path>: <status>"`. A 2xx with no JSON body resolves `{}`.
|
|
55
|
+
*
|
|
56
|
+
* @example Saving a field for the session's client
|
|
57
|
+
* ```tsx
|
|
58
|
+
* import { routeFetch, useClientId } from "@alexkroman1/aai-ui";
|
|
59
|
+
*
|
|
60
|
+
* function SaveName({ name }: { name: string }) {
|
|
61
|
+
* const client = useClientId();
|
|
62
|
+
* return (
|
|
63
|
+
* <button type="button" onClick={() => void routeFetch("PUT", "/profile", { name }, { client })}>
|
|
64
|
+
* Save
|
|
65
|
+
* </button>
|
|
66
|
+
* );
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @typeParam T - The shape the route answers. Not checked: annotate what you know.
|
|
71
|
+
* @param method - The route's method.
|
|
72
|
+
* @param path - The route's path as declared, starting with `/`; `?query` allowed.
|
|
73
|
+
* @param body - Sent as JSON when given.
|
|
74
|
+
* @param options - `client`, `baseUrl` and `signal`; see {@link RouteFetchOptions}.
|
|
75
|
+
* @returns The parsed JSON body.
|
|
76
|
+
*
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export declare function routeFetch<T = unknown>(method: RouteMethod, path: string, body?: unknown, options?: RouteFetchOptions): Promise<T>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session core's side of the audio path: every socket frame and snapshot
|
|
3
|
+
* write the statechart decides on but cannot make itself.
|
|
4
|
+
*
|
|
5
|
+
* The split is the one `s2s-lifecycle.ts` draws in the runtime. WHEN a
|
|
6
|
+
* microphone opens, whether an opening is still wanted, and where audio goes
|
|
7
|
+
* before there is anywhere to play it are `session/audio-state.ts`'s;
|
|
8
|
+
* this module is HOW — and it is the only half that touches `conn`, the
|
|
9
|
+
* snapshot, or the wire, which is what lets the machine be specced without a
|
|
10
|
+
* browser.
|
|
11
|
+
*/
|
|
12
|
+
import type { SessionCommand } from "@alexkroman1/aai/protocol";
|
|
13
|
+
import type { AudioPathEffects } from "./audio-state.ts";
|
|
14
|
+
import type { MicSender } from "./mic.ts";
|
|
15
|
+
import type { PreConnectAudio } from "./preconnect.ts";
|
|
16
|
+
import type { SessionStateMachine } from "./state.ts";
|
|
17
|
+
import { type ConnState, type SessionSnapshot } from "./types.ts";
|
|
18
|
+
/** What the audio effects need from the owning session core. */
|
|
19
|
+
export type AudioEffectsDeps = {
|
|
20
|
+
conn: ConnState;
|
|
21
|
+
updateState: (partial: Partial<SessionSnapshot>) => void;
|
|
22
|
+
/** The session's state and error, as one fact — see `session/state.ts`. */
|
|
23
|
+
agentState: SessionStateMachine;
|
|
24
|
+
sendJson: (msg: SessionCommand) => void;
|
|
25
|
+
/** The mic's wire half: live frames, and the pre-connect burst. */
|
|
26
|
+
mic: Pick<MicSender, "sendAudio" | "sendBuffered">;
|
|
27
|
+
/** The microphone opened at `connect()`, which the next bring-up adopts. */
|
|
28
|
+
preConnect: PreConnectAudio;
|
|
29
|
+
};
|
|
30
|
+
/** Build the effects for one session's audio path. */
|
|
31
|
+
export declare function createAudioEffects(deps: AudioEffectsDeps): AudioPathEffects;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the audio path is OPENED: the module load, the mic grant, the worklet
|
|
3
|
+
* registration, and the {@link VoiceIO} those three produce.
|
|
4
|
+
*
|
|
5
|
+
* WHEN one is opened, whether it is still wanted by the time it settles, and
|
|
6
|
+
* what happens to audio that arrives meanwhile all live in
|
|
7
|
+
* `session/audio-state.ts` — the same split `s2s-lifecycle.ts` and
|
|
8
|
+
* `aai-runtime/src/transports/pipeline/speech/edges.ts` draw in the runtime, and for the same reason:
|
|
9
|
+
* nothing here reads or writes session state, so it is callable from an
|
|
10
|
+
* invoked actor and testable without one.
|
|
11
|
+
*/
|
|
12
|
+
import type { PreConnectCapture, VoiceIO } from "../audio/index.ts";
|
|
13
|
+
type AudioModules = [typeof import("../audio/index.ts"), string, string];
|
|
14
|
+
/**
|
|
15
|
+
* Load the audio module and worklet sources, memoized so a prefetch at
|
|
16
|
+
* `connect()` time overlaps the chunk fetch with the WebSocket handshake
|
|
17
|
+
* instead of serializing it behind the server's `config` frame. A failed
|
|
18
|
+
* load clears the memo so the next attempt retries the import.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadAudioModules(): Promise<AudioModules>;
|
|
21
|
+
/** The audio parameters the server's `config` frame carries. */
|
|
22
|
+
export type AudioPathConfig = {
|
|
23
|
+
sampleRate: number;
|
|
24
|
+
ttsSampleRate: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* What a live audio path reports back.
|
|
28
|
+
*
|
|
29
|
+
* Both of the last two used to be guarded at the callback by
|
|
30
|
+
* `conn.generation.isCurrent(gen)`; they are plain reports now, and whether
|
|
31
|
+
* one still matters is the state machine's answer — see
|
|
32
|
+
* `session/audio-state.ts`.
|
|
33
|
+
*/
|
|
34
|
+
export type AudioPathCallbacks = {
|
|
35
|
+
/** Buffered PCM16 from the microphone, for the socket. */
|
|
36
|
+
onMicData(pcm16: ArrayBuffer): void;
|
|
37
|
+
/** Unplayed agent audio in the buffer, in ms — the host's closed playback loop. */
|
|
38
|
+
onProgress(bufferedMs: number): void;
|
|
39
|
+
/** A worklet processor died after setup: the audio path is gone. */
|
|
40
|
+
onFailure(message: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* The audio captured before the session was configured, in order and at the
|
|
43
|
+
* STT rate — sent once, ahead of the first `onMicData` frame.
|
|
44
|
+
*/
|
|
45
|
+
onPreConnectAudio(chunks: ArrayBuffer[]): void;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Open one audio path: load the worklets, ask for the microphone, and build
|
|
49
|
+
* the {@link VoiceIO} wired to `callbacks`.
|
|
50
|
+
*
|
|
51
|
+
* Rejects when any of the three refuses — a denied mic prompt being the
|
|
52
|
+
* ordinary case. The caller owns closing what this resolves: a path that comes
|
|
53
|
+
* up for a connection nobody wants any more is a live microphone, and no
|
|
54
|
+
* amount of cancellation upstream releases the device.
|
|
55
|
+
*/
|
|
56
|
+
export declare function openAudioPath(config: AudioPathConfig, callbacks: AudioPathCallbacks, preConnect?: Promise<PreConnectCapture | null> | null): Promise<VoiceIO>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser session's AUDIO PATH — the mic grant, the worklets, and the
|
|
3
|
+
* {@link VoiceIO} they produce — as a statechart.
|
|
4
|
+
*
|
|
5
|
+
* It was a latch, an epoch counter and three buffers spread across
|
|
6
|
+
* `ConnState`, and every one of them was a question the position already
|
|
7
|
+
* answers:
|
|
8
|
+
*
|
|
9
|
+
* - **`audioSetupInFlight` was a dedup, and dedup is a state.**
|
|
10
|
+
* `initAudioCapture` opened with `if (conn.audioSetupInFlight) return` and
|
|
11
|
+
* closed with "only the init that still owns the flag may clear it", which is
|
|
12
|
+
* an ownership problem a boolean cannot express and a position does not have.
|
|
13
|
+
* Here `starting` simply does not handle `START`, and there is no flag to
|
|
14
|
+
* hand back.
|
|
15
|
+
* - **`conn.generation` existed for this module and nothing else.** Four
|
|
16
|
+
* `bump()` sites, four `isCurrent(gen)` reads, and its own field on
|
|
17
|
+
* `ConnState`. All four bumps sat immediately after a `cleanupAudio()` that
|
|
18
|
+
* had *just* cleared the in-flight flag — the reconnect close handler said so
|
|
19
|
+
* outright ("cleanupAudio just cleared the in-flight flag, so a survivor
|
|
20
|
+
* would otherwise pass the same-generation guard and double-run"). Two
|
|
21
|
+
* mechanisms for one question, the second one layered on because the first
|
|
22
|
+
* could not stop the work already running. A stopped `invoke` can, so the
|
|
23
|
+
* counter is gone.
|
|
24
|
+
* - **A counter cannot tell two bring-ups in ONE generation apart, and they
|
|
25
|
+
* happen.** A repeated `config` frame on a live connection re-opened the path
|
|
26
|
+
* without bumping anything, so the outgoing `VoiceIO`'s `onError` still
|
|
27
|
+
* passed `isCurrent(gen)` and could tear down the replacement that had just
|
|
28
|
+
* taken its slot. `PROGRESS` and `IO_FAILED` carry the instance that fired
|
|
29
|
+
* them now, and identity is exact where a counter was approximate.
|
|
30
|
+
* - **`preInitAudio` / `preInitDone` are only meaningful while the path is
|
|
31
|
+
* down.** As connection fields they were readable — and writable — from
|
|
32
|
+
* anywhere; as context of `down`/`starting` they are cleared by entering
|
|
33
|
+
* `down` rather than by three call sites remembering to.
|
|
34
|
+
*
|
|
35
|
+
* ## What cancellation does NOT do, and the one guard that had to survive
|
|
36
|
+
*
|
|
37
|
+
* Stopping the `bringUp` actor hides its resolution; it does not close a
|
|
38
|
+
* microphone the browser has already granted. So `bringUp` checks its own
|
|
39
|
+
* `signal` after `openAudioPath` settles and releases what it built. That is
|
|
40
|
+
* the single remaining "is this still wanted?" test, and it lives in the
|
|
41
|
+
* producer — which owns the value nobody adopted — rather than at the four
|
|
42
|
+
* consumer sites that used to re-ask.
|
|
43
|
+
*
|
|
44
|
+
* ## Effects stay in the session core
|
|
45
|
+
*
|
|
46
|
+
* The machine owns WHEN. Every HOW — the socket frame, the snapshot write, the
|
|
47
|
+
* turn-boundary drain — arrives as an injected {@link AudioPathEffects} call,
|
|
48
|
+
* the same shape `s2s-lifecycle.ts` uses. So this module touches no socket and
|
|
49
|
+
* no snapshot.
|
|
50
|
+
*/
|
|
51
|
+
import type { VoiceIO } from "../audio/index.ts";
|
|
52
|
+
import type { AudioPathCallbacks, AudioPathConfig } from "./audio-setup.ts";
|
|
53
|
+
/** Where the audio path is. */
|
|
54
|
+
export type AudioPathPhase = "down" | "starting" | "up";
|
|
55
|
+
/**
|
|
56
|
+
* The session core's side of the machine: everything the audio path decides to
|
|
57
|
+
* do but does not know how to.
|
|
58
|
+
*/
|
|
59
|
+
export type AudioPathEffects = {
|
|
60
|
+
/**
|
|
61
|
+
* Open one audio path — `openAudioPath` in `session/audio-setup.ts`.
|
|
62
|
+
* Injected rather than imported so this module needs no browser: the specs
|
|
63
|
+
* hand it a promise they resolve by hand, which is the only way to hold a
|
|
64
|
+
* bring-up open across the events that abandon it.
|
|
65
|
+
*/
|
|
66
|
+
open(config: AudioPathConfig, callbacks: AudioPathCallbacks): Promise<VoiceIO>;
|
|
67
|
+
/** Relay captured microphone audio to the server. */
|
|
68
|
+
sendMicAudio(pcm16: ArrayBuffer): void;
|
|
69
|
+
/**
|
|
70
|
+
* Relay the audio captured before the session was configured — one burst,
|
|
71
|
+
* ahead of the first live frame (`session/preconnect.ts`).
|
|
72
|
+
*/
|
|
73
|
+
sendPreConnectAudio(chunks: ArrayBuffer[]): void;
|
|
74
|
+
/** Tell the server how much agent audio is still unplayed. */
|
|
75
|
+
reportProgress(bufferedMs: number): void;
|
|
76
|
+
/** Tell the server the microphone is live. */
|
|
77
|
+
announceReady(): void;
|
|
78
|
+
/** The microphone is live: the snapshot's `recording`. */
|
|
79
|
+
micLive(): void;
|
|
80
|
+
/** Back to the mic, with no audio left to wait for. */
|
|
81
|
+
listen(): void;
|
|
82
|
+
/**
|
|
83
|
+
* Wait for `io` to drain, then listen — guarded by the turn boundary, so a
|
|
84
|
+
* completion landing after a barge-in or a teardown is discarded.
|
|
85
|
+
*/
|
|
86
|
+
settleWhenDrained(io: VoiceIO): void;
|
|
87
|
+
/**
|
|
88
|
+
* Report a non-fatal audio failure and stop the mic. Non-fatal because the
|
|
89
|
+
* socket may well still be fine, so a later server frame is allowed to
|
|
90
|
+
* recover the banner — see `session/state.ts`.
|
|
91
|
+
*/
|
|
92
|
+
reportFailure(message: string): void;
|
|
93
|
+
/**
|
|
94
|
+
* The audio path is gone: end whatever turn it was playing.
|
|
95
|
+
*
|
|
96
|
+
* Paired with the release below rather than folded into it, because the
|
|
97
|
+
* bump has to happen on every path into `down` — including a bring-up that
|
|
98
|
+
* failed before there was anything to close.
|
|
99
|
+
*/
|
|
100
|
+
endTurn(): void;
|
|
101
|
+
/** Close `io` and let go of the microphone. Best-effort. */
|
|
102
|
+
release(io: VoiceIO): void;
|
|
103
|
+
};
|
|
104
|
+
/** Everything that happens to the audio path. */
|
|
105
|
+
export type AudioPathEvent =
|
|
106
|
+
/** The server's `config` frame: bring the path up for these rates. */
|
|
107
|
+
{
|
|
108
|
+
type: "START";
|
|
109
|
+
config: AudioPathConfig;
|
|
110
|
+
}
|
|
111
|
+
/** A PCM16 chunk arrived for playback. */
|
|
112
|
+
| {
|
|
113
|
+
type: "CHUNK";
|
|
114
|
+
chunk: ArrayBuffer;
|
|
115
|
+
}
|
|
116
|
+
/** The server finished sending this turn's audio. */
|
|
117
|
+
| {
|
|
118
|
+
type: "DONE";
|
|
119
|
+
}
|
|
120
|
+
/** A live path reported its unplayed depth. */
|
|
121
|
+
| {
|
|
122
|
+
type: "PROGRESS";
|
|
123
|
+
io: VoiceIO;
|
|
124
|
+
bufferedMs: number;
|
|
125
|
+
}
|
|
126
|
+
/** A live path's worklet died. */
|
|
127
|
+
| {
|
|
128
|
+
type: "IO_FAILED";
|
|
129
|
+
io: VoiceIO;
|
|
130
|
+
message: string;
|
|
131
|
+
}
|
|
132
|
+
/** Release the audio path: hang-up, reconnect, fatal frame, session end. */
|
|
133
|
+
| {
|
|
134
|
+
type: "TEARDOWN";
|
|
135
|
+
};
|
|
136
|
+
/** The session core's handle on its audio path. */
|
|
137
|
+
export type AudioPath = {
|
|
138
|
+
/** Where the path is. */
|
|
139
|
+
phase(): AudioPathPhase;
|
|
140
|
+
/** The server named the audio rates: bring the path up. */
|
|
141
|
+
start(config: AudioPathConfig): void;
|
|
142
|
+
/** Play a chunk, or hold it until there is somewhere to play it. */
|
|
143
|
+
enqueue(chunk: ArrayBuffer): void;
|
|
144
|
+
/** The server finished this turn's audio. */
|
|
145
|
+
done(): void;
|
|
146
|
+
/**
|
|
147
|
+
* Barge-in: discard what is queued for playback.
|
|
148
|
+
*
|
|
149
|
+
* A read rather than an event — it moves the path nowhere, and a turn
|
|
150
|
+
* boundary is the caller's to declare (see `bargeIn`).
|
|
151
|
+
*/
|
|
152
|
+
flush(): void;
|
|
153
|
+
/** Release the microphone and forget anything buffered. */
|
|
154
|
+
teardown(): void;
|
|
155
|
+
};
|
|
156
|
+
/** Create the audio path for one browser session. */
|
|
157
|
+
export declare function createAudioPath(effects: AudioPathEffects): AudioPath;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { VoiceSessionOptions } from "../types.ts";
|
|
2
|
+
import { type BrowserSession } from "./types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Create a framework-agnostic voice session core that connects to an AAI
|
|
5
|
+
* server via WebSocket.
|
|
6
|
+
*
|
|
7
|
+
* Uses a subscribe/getSnapshot pattern for state management, compatible with
|
|
8
|
+
* React's `useSyncExternalStore` and other external store integrations.
|
|
9
|
+
*
|
|
10
|
+
* Most clients never call this: `mountClient()` creates a core and installs it in
|
|
11
|
+
* React context for the hooks. Reach for it directly when building a
|
|
12
|
+
* non-React UI (or wiring the session into another framework's store).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { createBrowserSession, type SessionSnapshot } from "@alexkroman1/aai-ui";
|
|
17
|
+
*
|
|
18
|
+
* declare function render(snapshot: SessionSnapshot): void;
|
|
19
|
+
*
|
|
20
|
+
* const session = createBrowserSession({ platformUrl: "https://host/my-agent/" });
|
|
21
|
+
* session.subscribe(() => render(session.getSnapshot()));
|
|
22
|
+
* session.start();
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @param options - Session configuration including the platform server URL.
|
|
26
|
+
* @returns A {@link BrowserSession} handle for controlling the session.
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export declare function createBrowserSession(options: VoiceSessionOptions): BrowserSession;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a socket's CLOSE means to the caller.
|
|
3
|
+
*
|
|
4
|
+
* One decision, in its own module because `session/browser-session.ts` is at its length cap
|
|
5
|
+
* and because the decision is worth reading on its own: which endings are
|
|
6
|
+
* ordinary, and which carry something the user has to be told.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* What to REPORT for a socket that closed for good, or null for an ordinary end.
|
|
12
|
+
*
|
|
13
|
+
* A sentence the peer wrote beats one this file invented. A guest that cannot
|
|
14
|
+
* build its runtime closes 1011 with the remedy already in it — "Anthropic LLM:
|
|
15
|
+
* missing API key. Set ANTHROPIC_API_KEY in the agent env." — and this handler
|
|
16
|
+
* used to discard `event.reason` entirely, so the one thing a misconfigured
|
|
17
|
+
* deployment needed to be told surfaced as "WebSocket connection error", or as a
|
|
18
|
+
* plain disconnect with nothing at all. Measured against a deployed agent with no
|
|
19
|
+
* provider key.
|
|
20
|
+
*
|
|
21
|
+
* A NORMAL close carries no reason worth showing (1000 is the caller hanging up
|
|
22
|
+
* or the server retiring a finished session; a browser reports a status-less
|
|
23
|
+
* close as 1005, which has no reason either), so the two ordinary endings are
|
|
24
|
+
* untouched.
|
|
25
|
+
*/
|
|
26
|
+
export declare function closeFailure(event: CloseEvent, socketErrored: boolean): string | null;
|