@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,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A string a page remembers in this browser — a setting, a phone number, a
|
|
3
|
+
* linked device — readable outside React (a `mountClient({ phone })` getter
|
|
4
|
+
* asked on every connect) and reactive inside it.
|
|
5
|
+
*
|
|
6
|
+
* Built on `_web-storage.ts`, so it inherits that module's rule: every access
|
|
7
|
+
* is guarded, and a browser that refuses storage (private mode, a blocked
|
|
8
|
+
* iframe) degrades to a value held in memory for the tab — the page still
|
|
9
|
+
* works, it just forgets on reload. On top of that:
|
|
10
|
+
*
|
|
11
|
+
* - **Every holder of a key sees a write** — two components, a component and a
|
|
12
|
+
* getter, and other TABS (the `storage` event), so a setting changed in one
|
|
13
|
+
* place is not stale in another.
|
|
14
|
+
* - **An empty value is no value**: writing `""` REMOVES the entry, so the next
|
|
15
|
+
* read is the `initial` again rather than a stored blank.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Options for {@link createStoredValue}.
|
|
21
|
+
*
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
export type StoredValueOptions = {
|
|
25
|
+
/** What a read answers while nothing is stored. Default `""`. */
|
|
26
|
+
initial?: string | undefined;
|
|
27
|
+
/** `"local"` (the default) outlives the tab; `"session"` does not. */
|
|
28
|
+
storage?: "local" | "session" | undefined;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* One remembered string — see {@link createStoredValue}.
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
35
|
+
export type StoredValue = {
|
|
36
|
+
/** The storage key, as given. */
|
|
37
|
+
readonly key: string;
|
|
38
|
+
/** The value now: what is stored, else the `initial`. */
|
|
39
|
+
get(): string;
|
|
40
|
+
/** Remember `value`; `""` or `undefined` forgets it. Every holder of the key is told. */
|
|
41
|
+
set(value: string | undefined): void;
|
|
42
|
+
/** Be told when the value changes — here, in another holder, or in another tab. */
|
|
43
|
+
subscribe(listener: () => void): () => void;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* A string remembered in this browser under `key` — see this module's doc.
|
|
47
|
+
*
|
|
48
|
+
* Create it once at module scope and share it: `get()` in a getter outside
|
|
49
|
+
* React, {@link useStoredValue} in a component.
|
|
50
|
+
*
|
|
51
|
+
* The key is used as given, so choose one that is yours (`"my-app:phone"`);
|
|
52
|
+
* two agents on one origin share a key they both name.
|
|
53
|
+
*
|
|
54
|
+
* @example A phone number for the session, edited in a settings field
|
|
55
|
+
* ```tsx
|
|
56
|
+
* import { createStoredValue, mountClient, phoneE164, useStoredValue } from "@alexkroman1/aai-ui";
|
|
57
|
+
*
|
|
58
|
+
* const phone = createStoredValue("my-speaker:phone");
|
|
59
|
+
*
|
|
60
|
+
* function PhoneField() {
|
|
61
|
+
* const [value, setValue] = useStoredValue(phone);
|
|
62
|
+
* return <input defaultValue={value} onBlur={(e) => setValue(e.currentTarget.value.trim())} />;
|
|
63
|
+
* }
|
|
64
|
+
*
|
|
65
|
+
* mountClient({ phone: () => phoneE164(phone.get()), component: PhoneField });
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @param key - The storage key.
|
|
69
|
+
* @param options - `initial` and which storage; see {@link StoredValueOptions}.
|
|
70
|
+
* @returns The value's handle; see {@link StoredValue}.
|
|
71
|
+
*
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
export declare function createStoredValue(key: string, options?: StoredValueOptions): StoredValue;
|
|
75
|
+
/**
|
|
76
|
+
* A remembered string as React state: the value, and a setter that writes it
|
|
77
|
+
* through to storage — see this module's doc.
|
|
78
|
+
*
|
|
79
|
+
* Pass the key (and an `initial`) for a value one component owns, or a
|
|
80
|
+
* {@link StoredValue} shared with code outside React.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* import { useStoredValue } from "@alexkroman1/aai-ui";
|
|
85
|
+
*
|
|
86
|
+
* function Units() {
|
|
87
|
+
* const [units, setUnits] = useStoredValue("my-app:units", "metric");
|
|
88
|
+
* return (
|
|
89
|
+
* <button type="button" onClick={() => setUnits(units === "metric" ? "imperial" : "metric")}>
|
|
90
|
+
* {units}
|
|
91
|
+
* </button>
|
|
92
|
+
* );
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @param source - A storage key, or a {@link StoredValue}.
|
|
97
|
+
* @param initial - With a key: what to answer while nothing is stored. Default `""`.
|
|
98
|
+
* Ignored with a `StoredValue`, which carries its own.
|
|
99
|
+
* @returns `[value, setValue]`; `setValue("")` forgets it.
|
|
100
|
+
*
|
|
101
|
+
* @public
|
|
102
|
+
*/
|
|
103
|
+
export declare function useStoredValue(source: StoredValue | string, initial?: string): [string, (value: string | undefined) => void];
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { a as tryParseJSON, i as truncate } from "./_utils-Mi-SCFA1.js";
|
|
2
|
+
import { useTheme } from "./context.js";
|
|
3
|
+
import { a as inkTint } from "./_colors-CpZO-88A.js";
|
|
4
|
+
import { t as Eyebrow } from "./eyebrow-UfmSz9yy.js";
|
|
5
|
+
import { n as useToolConfig } from "./tool-config-context-Es4YUzV2.js";
|
|
6
|
+
import { memo, useMemo, useState } from "react";
|
|
7
|
+
import clsx from "clsx";
|
|
8
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/components/tool-call-row.tsx
|
|
10
|
+
/** @jsxImportSource react */
|
|
11
|
+
const VARIANT_CLASSES = {
|
|
12
|
+
default: {
|
|
13
|
+
button: "gap-2.5 px-3.5 py-2.5",
|
|
14
|
+
title: "text-[13px]",
|
|
15
|
+
chevron: "text-[10px]"
|
|
16
|
+
},
|
|
17
|
+
compact: {
|
|
18
|
+
button: "gap-2 px-3 py-2",
|
|
19
|
+
title: "text-[11px]",
|
|
20
|
+
chevron: "text-[9px]"
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The design system's console row for one tool invocation: a small outlined
|
|
25
|
+
* "TOOL" chip (or a custom `icon`), the tool title in mono, a truncated
|
|
26
|
+
* detail preview, and a rotating chevron that expands to the panel content.
|
|
27
|
+
*
|
|
28
|
+
* Purely presentational — callers own the mapping from their tool-call data
|
|
29
|
+
* to `title`/`detail`/`pending` and the expanded panel. The deployed agent
|
|
30
|
+
* UI's message list renders it via its tool-call block, and the studio's
|
|
31
|
+
* chat transcript renders it with `variant="compact"`, so the two surfaces
|
|
32
|
+
* read as one component.
|
|
33
|
+
*
|
|
34
|
+
* Colors come from the nearest theme context (see {@link useTheme}); without
|
|
35
|
+
* a provider the default AssemblyAI theme applies.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* import { ToolCallRow, useSession } from "@alexkroman1/aai-ui";
|
|
40
|
+
*
|
|
41
|
+
* // A custom chrome's tool log, from the snapshot's own `toolCalls`.
|
|
42
|
+
* function ToolLog() {
|
|
43
|
+
* const { toolCalls } = useSession();
|
|
44
|
+
* return (
|
|
45
|
+
* <div>
|
|
46
|
+
* {toolCalls.map((call) => (
|
|
47
|
+
* <ToolCallRow
|
|
48
|
+
* key={call.callId}
|
|
49
|
+
* title={call.name}
|
|
50
|
+
* detail={call.result}
|
|
51
|
+
* pending={call.status === "pending"}
|
|
52
|
+
* />
|
|
53
|
+
* ))}
|
|
54
|
+
* </div>
|
|
55
|
+
* );
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @param props - See {@link ToolCallRowProps}.
|
|
60
|
+
*
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
function ToolCallRow({ title, detail, pending = false, icon, variant = "default", className, children }) {
|
|
64
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
65
|
+
const theme = useTheme();
|
|
66
|
+
const sizes = VARIANT_CLASSES[variant];
|
|
67
|
+
const canExpand = children != null;
|
|
68
|
+
const faint = inkTint(theme.text, theme.bg, 65);
|
|
69
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
70
|
+
className: clsx("flex flex-col rounded-md border overflow-hidden", className),
|
|
71
|
+
style: {
|
|
72
|
+
borderColor: theme.border,
|
|
73
|
+
background: theme.bg
|
|
74
|
+
},
|
|
75
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
76
|
+
type: "button",
|
|
77
|
+
"aria-expanded": canExpand ? isOpen : void 0,
|
|
78
|
+
disabled: !canExpand,
|
|
79
|
+
className: clsx("flex items-center select-none text-left w-full appearance-none border-none bg-transparent", sizes.button, canExpand && "cursor-pointer"),
|
|
80
|
+
onClick: () => {
|
|
81
|
+
if (canExpand) setIsOpen(!isOpen);
|
|
82
|
+
},
|
|
83
|
+
children: [
|
|
84
|
+
icon ? /* @__PURE__ */ jsx("span", {
|
|
85
|
+
className: "w-4 h-4 shrink-0 text-center leading-4",
|
|
86
|
+
children: icon
|
|
87
|
+
}) : /* @__PURE__ */ jsx(Eyebrow, {
|
|
88
|
+
className: "shrink-0",
|
|
89
|
+
style: { color: faint },
|
|
90
|
+
children: "Tool"
|
|
91
|
+
}),
|
|
92
|
+
/* @__PURE__ */ jsx("span", {
|
|
93
|
+
className: clsx("min-w-0 truncate font-aai-mono font-medium", sizes.title, pending && "tool-shimmer"),
|
|
94
|
+
style: { color: theme.text },
|
|
95
|
+
children: title
|
|
96
|
+
}),
|
|
97
|
+
/* @__PURE__ */ jsx("span", {
|
|
98
|
+
className: clsx("font-aai-mono truncate flex-1 min-w-0", sizes.title),
|
|
99
|
+
style: { color: faint },
|
|
100
|
+
children: detail
|
|
101
|
+
}),
|
|
102
|
+
canExpand && /* @__PURE__ */ jsx("span", {
|
|
103
|
+
className: clsx("shrink-0 transition-transform duration-150", sizes.chevron, isOpen && "rotate-90"),
|
|
104
|
+
style: { color: faint },
|
|
105
|
+
children: "▶"
|
|
106
|
+
})
|
|
107
|
+
]
|
|
108
|
+
}), isOpen && canExpand && /* @__PURE__ */ jsx("div", {
|
|
109
|
+
className: "border-t max-h-64 overflow-auto",
|
|
110
|
+
style: {
|
|
111
|
+
borderColor: theme.border,
|
|
112
|
+
background: theme.surface
|
|
113
|
+
},
|
|
114
|
+
children
|
|
115
|
+
})]
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/components/tool-call-block.tsx
|
|
120
|
+
/** @jsxImportSource react */
|
|
121
|
+
function formatResult(result) {
|
|
122
|
+
const parsed = tryParseJSON(result);
|
|
123
|
+
return parsed === result ? result : JSON.stringify(parsed, null, 2);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The expanded result pane.
|
|
127
|
+
*
|
|
128
|
+
* Its own component so the parse-and-pretty-print happens only when the row is
|
|
129
|
+
* actually OPEN. `ToolCallRow` renders `children` under `isOpen && canExpand`,
|
|
130
|
+
* and a row starts closed — so computing this in the parent meant every row
|
|
131
|
+
* paid `JSON.parse` + `JSON.stringify(_, null, 2)` over its whole result, and
|
|
132
|
+
* then RETAINED the pretty string, for a panel nobody clicked. The burst case
|
|
133
|
+
* is `history.restored`, which mounts up to `MAX_CLIENT_MESSAGES` rows in one
|
|
134
|
+
* commit.
|
|
135
|
+
*
|
|
136
|
+
* Creating this element is free; React only runs the body once it is mounted.
|
|
137
|
+
*/
|
|
138
|
+
function ToolCallResult({ result }) {
|
|
139
|
+
const theme = useTheme();
|
|
140
|
+
const formatted = useMemo(() => formatResult(result), [result]);
|
|
141
|
+
if (!formatted) return null;
|
|
142
|
+
return /* @__PURE__ */ jsx("pre", {
|
|
143
|
+
className: "font-aai-mono text-xs px-3.5 py-3 m-0 whitespace-pre-wrap wrap-break-word",
|
|
144
|
+
style: { color: inkTint(theme.text, theme.surface, 75) },
|
|
145
|
+
children: formatted
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Renders a tool invocation as the design system's console row (see
|
|
150
|
+
* `ToolCallRow`): a small outlined "TOOL" chip (or the tool's configured
|
|
151
|
+
* icon), the tool name in mono, a truncated args preview, and a rotating
|
|
152
|
+
* chevron that expands the formatted result.
|
|
153
|
+
*
|
|
154
|
+
* Tool display is configured via `ToolConfigContext`. If no config is found
|
|
155
|
+
* for a tool name, the raw tool name is shown as the title.
|
|
156
|
+
*
|
|
157
|
+
* While the tool call is pending a shimmer animation is shown. Once
|
|
158
|
+
* complete, clicking the row expands the formatted JSON result.
|
|
159
|
+
*
|
|
160
|
+
* Memoized: tool-call objects are referentially stable across session
|
|
161
|
+
* snapshots and rows are keyed on the stable `callId`, so a list update only
|
|
162
|
+
* re-renders the rows whose tool call actually changed.
|
|
163
|
+
*
|
|
164
|
+
* @param toolCall - The tool call to render (see {@link ToolCallInfo}).
|
|
165
|
+
* @param className - Additional CSS class names.
|
|
166
|
+
*
|
|
167
|
+
* @internal Not exported from the package — rendered by `MessageList`.
|
|
168
|
+
*/
|
|
169
|
+
const ToolCallBlock = memo(function ToolCallBlock({ toolCall, className }) {
|
|
170
|
+
const theme = useTheme();
|
|
171
|
+
const toolConfig = useToolConfig();
|
|
172
|
+
const config = Object.hasOwn(toolConfig, toolCall.name) ? toolConfig[toolCall.name] : void 0;
|
|
173
|
+
const isPending = toolCall.status === "pending";
|
|
174
|
+
const title = config?.label || toolCall.name;
|
|
175
|
+
const icon = config?.icon;
|
|
176
|
+
const canExpand = !isPending && Boolean(toolCall.result);
|
|
177
|
+
const subtitle = useMemo(() => {
|
|
178
|
+
const args = toolCall.args;
|
|
179
|
+
if (toolCall.name === "run_code" && args.code) return truncate(String(args.code).split("\n")[0] ?? "");
|
|
180
|
+
for (const key of [
|
|
181
|
+
"query",
|
|
182
|
+
"url",
|
|
183
|
+
"question"
|
|
184
|
+
]) if (args[key]) return String(args[key]);
|
|
185
|
+
return truncate(JSON.stringify(args));
|
|
186
|
+
}, [toolCall.name, toolCall.args]);
|
|
187
|
+
return /* @__PURE__ */ jsx(ToolCallRow, {
|
|
188
|
+
title,
|
|
189
|
+
detail: subtitle,
|
|
190
|
+
pending: isPending,
|
|
191
|
+
icon,
|
|
192
|
+
className,
|
|
193
|
+
children: canExpand ? /* @__PURE__ */ jsxs(Fragment$1, { children: [toolCall.name === "run_code" && Boolean(toolCall.args.code) && /* @__PURE__ */ jsx("pre", {
|
|
194
|
+
className: "font-aai-mono text-xs px-3.5 py-3 m-0 whitespace-pre-wrap wrap-break-word border-b",
|
|
195
|
+
style: {
|
|
196
|
+
color: theme.text,
|
|
197
|
+
borderColor: theme.border
|
|
198
|
+
},
|
|
199
|
+
children: String(toolCall.args.code)
|
|
200
|
+
}), toolCall.result && /* @__PURE__ */ jsx(ToolCallResult, { result: toolCall.result })] }) : void 0
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
//#endregion
|
|
204
|
+
export { ToolCallRow as n, ToolCallBlock as t };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
//#region src/components/tool-config-context.ts
|
|
3
|
+
/**
|
|
4
|
+
* Context for tool display configuration. Installed by `mountClient()` from
|
|
5
|
+
* `ClientConfig.tools`; the built-in components read it via `useToolConfig`.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
const ToolConfigContext = createContext({});
|
|
10
|
+
/**
|
|
11
|
+
* Read tool display configuration from the nearest `ToolConfigContext.Provider`.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
function useToolConfig() {
|
|
16
|
+
return useContext(ToolConfigContext);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { useToolConfig as n, ToolConfigContext as t };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,8 +1,60 @@
|
|
|
1
|
+
import type { DefaultToolResult } from "@alexkroman1/aai";
|
|
1
2
|
import type { SessionErrorCode } from "@alexkroman1/aai/protocol";
|
|
2
|
-
|
|
3
|
-
export declare const MIC_BUFFER_SECONDS = 0.1;
|
|
3
|
+
export { CAPTURE_STOP_ACK_TIMEOUT_MS, CLIENT_AUDIO_LEAD_MS, HEARD_AUDIO_LAG_MS, MIC_BUFFER_SECONDS, MIC_SEND_MAX_BUFFERED_BYTES, MIC_SILENCE_PROBE_MS, PACER_BURST_MS, PIPELINE_PLAYBACK_GRACE_MS, PLAYBACK_BUFFER_SECONDS, PLAYBACK_CONCEAL_FADE_MS, PLAYBACK_CONCEAL_FLOOR, PLAYBACK_DONE_MAX_WAIT_MS, PLAYBACK_DONE_POLL_MS, PLAYBACK_FILL_MS, PLAYBACK_PROGRESS_INTERVAL_MS, } from "@alexkroman1/aai/internal";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* `getUserMedia` audio constraints for every capture path in this package.
|
|
6
|
+
*
|
|
7
|
+
* Defined once because four copies of this object drifted apart trivially, and
|
|
8
|
+
* the flags are not cosmetic — each one rewrites the signal before STT (and
|
|
9
|
+
* before the sync path's energy VAD) ever sees it:
|
|
10
|
+
*
|
|
11
|
+
* - **`autoGainControl: false`** — AGC continuously retargets level, which
|
|
12
|
+
* means riding the noise floor up through silence. An energy VAD calibrated
|
|
13
|
+
* against a moving floor is calibrated against nothing.
|
|
14
|
+
* - **`noiseSuppression: false`** / **`voiceIsolation: false`** — both discard
|
|
15
|
+
* signal to make speech sound cleaner to a human, and both can gate a quiet
|
|
16
|
+
* room to *exact* zeros, which is also what a dead microphone looks like
|
|
17
|
+
* (see `MIC_SILENCE_PROBE_MS`).
|
|
18
|
+
* - **`echoCancellation: true`** — this one stays on. The mic is open while
|
|
19
|
+
* the agent speaks (barge-in needs it), so without AEC the agent hears
|
|
20
|
+
* itself and interrupts its own reply.
|
|
21
|
+
*
|
|
22
|
+
* Cast because `voiceIsolation` is newer than TypeScript's DOM lib.
|
|
23
|
+
*
|
|
24
|
+
* On `@alexkroman1/aai-ui/internal` rather than the root barrel, for the same
|
|
25
|
+
* reason as the audio budgets above: it is a framework decision with no
|
|
26
|
+
* `mountClient()` field to set, and the root is the authoring surface. A custom
|
|
27
|
+
* chrome that bypasses `mountClient()` and opens its own microphone reaches it there
|
|
28
|
+
* alongside the providers it also needs.
|
|
29
|
+
*/
|
|
30
|
+
export declare const VOICE_CAPTURE_CONSTRAINTS: MediaTrackConstraints;
|
|
31
|
+
/**
|
|
32
|
+
* Current state of the voice agent session — the `state` field of
|
|
33
|
+
* {@link SessionSnapshot}, and what a chrome paints its status indicator from.
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* The seven members, in the order a call passes through them:
|
|
37
|
+
*
|
|
38
|
+
* - `"disconnected"` — no socket. The state before the first `start()` and
|
|
39
|
+
* after `disconnect()` / `end()`.
|
|
40
|
+
* - `"connecting"` — dialling. Covers the broker lookup and every automatic
|
|
41
|
+
* reconnect attempt, so a session flickers back through it mid-call.
|
|
42
|
+
* - `"ready"` — the socket is open and the handshake is done, but no turn has
|
|
43
|
+
* happened yet. **The default chrome paints this with the same live
|
|
44
|
+
* indicator as `"listening"`**, which is deliberate — to a caller they are
|
|
45
|
+
* the same "the agent is there" — but they are not the same thing, and a
|
|
46
|
+
* session can wedge here (see `session/handshake.ts`).
|
|
47
|
+
* - `"listening"` — the microphone is open and the agent is waiting for the
|
|
48
|
+
* caller. Check {@link SessionSnapshot.recording} for whether the mic is
|
|
49
|
+
* actually live.
|
|
50
|
+
* - `"thinking"` — the caller's turn is committed and the agent is working:
|
|
51
|
+
* the LLM step, and any tool calls under it.
|
|
52
|
+
* - `"speaking"` — the agent's reply is playing. A caller may still barge in;
|
|
53
|
+
* the mic stays open throughout.
|
|
54
|
+
* - `"error"` — the session reported a failure. See
|
|
55
|
+
* {@link SessionSnapshot.error} for what it was. A FATAL error latches here
|
|
56
|
+
* until the next completed handshake, so a later frame cannot quietly paint
|
|
57
|
+
* over the banner explaining a dead call.
|
|
6
58
|
*
|
|
7
59
|
* @public
|
|
8
60
|
*/
|
|
@@ -10,9 +62,20 @@ export type AgentState = "disconnected" | "connecting" | "ready" | "listening" |
|
|
|
10
62
|
/**
|
|
11
63
|
* A chat message exchanged between user and assistant.
|
|
12
64
|
*
|
|
65
|
+
* `role` is `"user" | "assistant"` only — unlike the SDK's `Message`, there
|
|
66
|
+
* is no `"tool"` role here. Tool activity never arrives as messages: it is
|
|
67
|
+
* surfaced via `SessionSnapshot.toolCalls` (or `useEvent` for `ctx.send`
|
|
68
|
+
* events).
|
|
69
|
+
*
|
|
13
70
|
* @public
|
|
14
71
|
*/
|
|
15
72
|
export type ChatMessage = {
|
|
73
|
+
/**
|
|
74
|
+
* Monotonically increasing, session-unique message id assigned at append
|
|
75
|
+
* time. Stable across snapshot updates and window slides — use it as a
|
|
76
|
+
* render key.
|
|
77
|
+
*/
|
|
78
|
+
id: number;
|
|
16
79
|
/** The sender of the message. */
|
|
17
80
|
role: "user" | "assistant";
|
|
18
81
|
/** The text content of the message. */
|
|
@@ -24,14 +87,43 @@ export type ChatMessage = {
|
|
|
24
87
|
* @public
|
|
25
88
|
*/
|
|
26
89
|
export type ToolCallInfo = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
90
|
+
callId: string;
|
|
91
|
+
name: string;
|
|
92
|
+
/**
|
|
93
|
+
* The tool's arguments, as the model sent them.
|
|
94
|
+
*
|
|
95
|
+
* Values are {@link DefaultToolResult} — `any` — for the same reason a tool
|
|
96
|
+
* *result* is: the shape is the author's own Zod schema, which the framework
|
|
97
|
+
* cannot see from here. As `Record<string, unknown>` the ordinary
|
|
98
|
+
* `toolCall.args.url` was a compile error in a client that runs correctly,
|
|
99
|
+
* and the escape hatch agents reached for next (`args as FetchJsonArgs`) is
|
|
100
|
+
* itself an error — TypeScript rejects the cast as insufficiently
|
|
101
|
+
* overlapping. That pair cost two build rounds in one run.
|
|
102
|
+
*
|
|
103
|
+
* Annotate at the read site for real checking:
|
|
104
|
+
* `const { url } = toolCall.args as { url: string }` is still available, and
|
|
105
|
+
* now actually compiles.
|
|
106
|
+
*/
|
|
107
|
+
args: Record<string, DefaultToolResult>;
|
|
30
108
|
status: "pending" | "done";
|
|
31
109
|
result?: string | undefined;
|
|
32
|
-
/**
|
|
33
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Monotonically increasing, session-unique insertion sequence number.
|
|
112
|
+
* Tool calls in a snapshot are always sorted ascending by `seq`.
|
|
113
|
+
*/
|
|
114
|
+
seq: number;
|
|
115
|
+
/**
|
|
116
|
+
* `id` of the last {@link ChatMessage} present when this tool call was
|
|
117
|
+
* inserted (`-1` when there were none). The tool call renders immediately
|
|
118
|
+
* after that message; if the anchor message has slid out of the retained
|
|
119
|
+
* window, the tool call renders before all messages.
|
|
120
|
+
*/
|
|
121
|
+
afterMessageId: number;
|
|
34
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* Re-exported from `@alexkroman1/aai/protocol` (the canonical definition)
|
|
125
|
+
* so client code needs only this package.
|
|
126
|
+
*/
|
|
35
127
|
export type { SessionErrorCode } from "@alexkroman1/aai/protocol";
|
|
36
128
|
/**
|
|
37
129
|
* Error reported by the voice session.
|
|
@@ -43,50 +135,179 @@ export type SessionError = {
|
|
|
43
135
|
readonly code: SessionErrorCode;
|
|
44
136
|
/** A human-readable description of the error. */
|
|
45
137
|
readonly message: string;
|
|
138
|
+
/**
|
|
139
|
+
* Whether the session is OVER.
|
|
140
|
+
*
|
|
141
|
+
* `false` means surface the message and keep the session interactive — a
|
|
142
|
+
* turn-level failure over a server that kept running. `true` means the call
|
|
143
|
+
* is dead and the microphone has been released.
|
|
144
|
+
*
|
|
145
|
+
* Required rather than optional, because the wire always carries it
|
|
146
|
+
* (`error.reported` declares `fatal: z.boolean()`) and a client that cannot
|
|
147
|
+
* tell the two apart has to guess which banner to render. It was dropped one
|
|
148
|
+
* line before reaching here for long enough that this type's own doc, and
|
|
149
|
+
* the reference page generated from it, described a field that did not
|
|
150
|
+
* exist.
|
|
151
|
+
*/
|
|
152
|
+
readonly fatal: boolean;
|
|
46
153
|
};
|
|
47
154
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
|
|
52
|
-
export type Reactive<T> = {
|
|
53
|
-
value: T;
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* Options for creating a voice session.
|
|
155
|
+
* Options for creating a voice session — the shared field set accepted by
|
|
156
|
+
* both `mountClient()` and `createBrowserSession`. The one difference: `mountClient()`
|
|
157
|
+
* defaults `platformUrl` from `location.href`, while `createBrowserSession`
|
|
158
|
+
* requires it.
|
|
57
159
|
*
|
|
58
160
|
* @public
|
|
59
161
|
*/
|
|
60
162
|
export type VoiceSessionOptions = {
|
|
61
163
|
/** Base URL of the AAI platform server. */
|
|
62
164
|
platformUrl: string;
|
|
63
|
-
/**
|
|
64
|
-
* Factory for creating reactive state containers.
|
|
65
|
-
* Defaults to a plain mutable wrapper. Pass `signal` from
|
|
66
|
-
* `@preact/signals` for automatic Preact component re-rendering.
|
|
67
|
-
*/
|
|
68
|
-
reactiveFactory?: <T>(initial: T) => Reactive<T>;
|
|
69
|
-
/**
|
|
70
|
-
* Function to batch multiple reactive updates.
|
|
71
|
-
* Defaults to calling the function directly. Pass `batch` from
|
|
72
|
-
* `@preact/signals` for optimized batched updates.
|
|
73
|
-
*/
|
|
74
|
-
batch?: (fn: () => void) => void;
|
|
75
165
|
/**
|
|
76
166
|
* Called when the server sends a session ID in the config message.
|
|
77
167
|
* Use this to store the ID (e.g. in localStorage) for reconnection
|
|
78
168
|
* via `resumeSessionId`.
|
|
169
|
+
*
|
|
170
|
+
* Treat session IDs as sensitive: whoever holds one can resume the
|
|
171
|
+
* session and read its replayed history. They travel as a WebSocket
|
|
172
|
+
* query parameter (browsers cannot set WS headers), so they may appear
|
|
173
|
+
* in proxy and server access logs — don't put them in shared URLs.
|
|
79
174
|
*/
|
|
80
175
|
onSessionId?: ((sessionId: string) => void) | undefined;
|
|
81
176
|
/**
|
|
82
|
-
* Session ID from a previous connection. When set, the server
|
|
83
|
-
*
|
|
84
|
-
* `
|
|
177
|
+
* Session ID from a previous connection. When set, the server resumes
|
|
178
|
+
* that session if its per-session state is still within the resume grace
|
|
179
|
+
* window (`SESSION_RESUME_GRACE_MS`), replaying history into the new
|
|
180
|
+
* connection. Sensitive — see {@link onSessionId}.
|
|
85
181
|
*/
|
|
86
182
|
resumeSessionId?: string | undefined;
|
|
87
183
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
184
|
+
* Where the client is — e.g. a street address or `"Portland, Oregon"` — sent
|
|
185
|
+
* as `?location=` on every connection attempt (the first, a resume and each
|
|
186
|
+
* reconnect, brokered or not). Location-aware builtins read it: `open_meteo`
|
|
187
|
+
* falls back to it when the model names no place, and `google_places` biases
|
|
188
|
+
* its searches toward it.
|
|
189
|
+
*
|
|
190
|
+
* A string, or a getter asked on EVERY attempt — so a UI whose location can
|
|
191
|
+
* change (a settings field) passes `() => current` and the next reconnect
|
|
192
|
+
* carries the new value without a remount. An empty or `undefined` answer
|
|
193
|
+
* sends none. The server strips control characters and ignores a value
|
|
194
|
+
* longer than 200 characters.
|
|
195
|
+
*
|
|
196
|
+
* Treat it as personal data: like the session id it travels as a query
|
|
197
|
+
* parameter (browsers cannot set WebSocket headers), so it may appear in
|
|
198
|
+
* proxy and access logs.
|
|
199
|
+
*/
|
|
200
|
+
location?: string | (() => string | undefined) | undefined;
|
|
201
|
+
/**
|
|
202
|
+
* The phone number of whoever owns this client — sent as `?phone=` on every
|
|
203
|
+
* connection attempt, with the same rules as {@link location}: a string or a
|
|
204
|
+
* getter asked per attempt, trimmed, an empty answer sends none. A tool reads
|
|
205
|
+
* it with `sessionClientPhone(ctx)`, e.g. to text the caller a link.
|
|
206
|
+
*
|
|
207
|
+
* Give it in E.164 form, with the `+` and country code (`"+1 503 555 0123"`):
|
|
208
|
+
* the server strips spaces, dashes, dots and parentheses and then needs a `+`
|
|
209
|
+
* and 8–15 digits. A bare `"5035550123"` is NOT assumed to be North American
|
|
210
|
+
* — it is dropped (the server logs that it was, never the value).
|
|
211
|
+
*
|
|
212
|
+
* The server takes it on the client's word. Personal data: it travels as a
|
|
213
|
+
* query parameter, so it may appear in proxy and access logs.
|
|
214
|
+
*/
|
|
215
|
+
phone?: string | (() => string | undefined) | undefined;
|
|
216
|
+
/**
|
|
217
|
+
* The name of THIS client — a device id — sent as `?client=` on every
|
|
218
|
+
* connection attempt, with the same rules as {@link location}: a string or a
|
|
219
|
+
* getter asked per attempt, trimmed, an empty answer sends none.
|
|
220
|
+
*
|
|
221
|
+
* **`"auto"`** has the SDK mint and keep one: this browser's
|
|
222
|
+
* `browserClientId()` — `browser-<32 hex>`, stored in `localStorage` per
|
|
223
|
+
* agent URL (for this tab only where storage is unavailable). It is the id
|
|
224
|
+
* `useInbox()` holds the inbox under too, with a holder id per TAB so two tabs
|
|
225
|
+
* of one browser do not replace each other's inbox socket. Read it back with
|
|
226
|
+
* `useClientId()` or `session.identity.clientId()`. (So `"auto"` itself can
|
|
227
|
+
* never be a client id.)
|
|
228
|
+
*
|
|
229
|
+
* It is the id a tool reads with `sessionClientId(ctx)` and the one a client
|
|
230
|
+
* holds its `WS /inbox?client=` socket open under, so a tool can hand it to a
|
|
231
|
+
* workflow run and the run's `stepNotifyClient` reaches this client AFTER the
|
|
232
|
+
* voice session has ended (a reminder, a finished research job). Send the
|
|
233
|
+
* same id the inbox socket uses, or the run delivers to a client that is not
|
|
234
|
+
* listening.
|
|
235
|
+
*
|
|
236
|
+
* Letters, digits, `-` and `_`, at most 64 characters: the server checks it
|
|
237
|
+
* against the same pattern the inbox uses and ignores one that does not
|
|
238
|
+
* match — the session still opens, it just has no client id.
|
|
239
|
+
*
|
|
240
|
+
* It is also the key of this client's DURABLE conversation: every connection
|
|
241
|
+
* that sends it is seeded with that client's earlier sessions. The server
|
|
242
|
+
* takes it on the client's word and authenticates nothing, so on a server
|
|
243
|
+
* reachable from a network (a LAN-listening dev server, a self-hosted one)
|
|
244
|
+
* the id is the ONLY credential for that history — anyone who can reach the
|
|
245
|
+
* server and knows the id reads what was said. Use an unguessable id.
|
|
246
|
+
*/
|
|
247
|
+
client?: string | (() => string | undefined) | undefined;
|
|
248
|
+
/**
|
|
249
|
+
* Open the microphone when the session connects, rather than once the
|
|
250
|
+
* server has configured it, and send what the caller said in between ahead
|
|
251
|
+
* of the live audio — so an opener spoken while the agent is still joining
|
|
252
|
+
* (a handshake, or a sandbox boot on the platform) reaches it instead of
|
|
253
|
+
* being lost. The latest 10 seconds are kept.
|
|
254
|
+
*
|
|
255
|
+
* Default `true`. `false` asks for the microphone only after the server's
|
|
256
|
+
* `config` frame, as before; a UI that wants the permission prompt to
|
|
257
|
+
* follow its own greeting might.
|
|
258
|
+
*/
|
|
259
|
+
preConnectAudio?: boolean | undefined;
|
|
260
|
+
/**
|
|
261
|
+
* The session ticket this client presents — for a server that requires one
|
|
262
|
+
* (`AAI_SESSION_SECRET`, or `createSessionAuth()` on
|
|
263
|
+
* `@alexkroman1/aai-runtime/auth`).
|
|
264
|
+
*
|
|
265
|
+
* A string, or a getter asked on EVERY connection attempt — the first, each
|
|
266
|
+
* reconnect and each resume — so a short-lived ticket (60 s by default) is
|
|
267
|
+
* fresh each time. Fetch it from your own backend, which checks its own login
|
|
268
|
+
* and mints with `createSessionToken()`; the secret must never reach the
|
|
269
|
+
* browser. The getter is told the session the attempt RESUMES (`undefined`
|
|
270
|
+
* for a new one), so a backend can bind a resume ticket to it
|
|
271
|
+
* (`createSessionToken({ sessionId })`) — what a resume needs after the
|
|
272
|
+
* server restarted.
|
|
273
|
+
*
|
|
274
|
+
* It travels in `Sec-WebSocket-Protocol` as `aai.auth.<ticket>` beside the
|
|
275
|
+
* plain `aai.session` protocol, never in the URL, so it stays out of access
|
|
276
|
+
* logs (a value that is not a valid protocol token falls back to `?token=`).
|
|
277
|
+
* An empty or `undefined` answer sends none, and so does a getter that throws
|
|
278
|
+
* or rejects: the attempt still dials, and a server that requires a ticket
|
|
279
|
+
* refuses it with a reason. Give a fetch inside the getter a deadline — the
|
|
280
|
+
* attempt waits for it. With an injected {@link WebSocket} the getter must
|
|
281
|
+
* answer synchronously.
|
|
282
|
+
*
|
|
283
|
+
* When omitted, a ticket the server's `client-config` issued is used — which
|
|
284
|
+
* `aai dev` does for its own client when `AAI_SESSION_SECRET` is set.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```ts
|
|
288
|
+
* import { mountClient } from "@alexkroman1/aai-ui";
|
|
289
|
+
*
|
|
290
|
+
* mountClient({
|
|
291
|
+
* token: async ({ sessionId }) => {
|
|
292
|
+
* const res = await fetch("/my-backend/session-ticket", {
|
|
293
|
+
* method: "POST",
|
|
294
|
+
* body: JSON.stringify({ sessionId }),
|
|
295
|
+
* signal: AbortSignal.timeout(5000),
|
|
296
|
+
* });
|
|
297
|
+
* return (await res.json()).token;
|
|
298
|
+
* },
|
|
299
|
+
* });
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
token?: string | ((attempt: {
|
|
303
|
+
readonly sessionId: string | undefined;
|
|
304
|
+
}) => string | undefined | Promise<string | undefined>) | undefined;
|
|
305
|
+
/**
|
|
306
|
+
* WebSocket constructor override. Primarily useful for testing with a mock
|
|
307
|
+
* WebSocket. When omitted, the session uses a reconnecting WebSocket
|
|
308
|
+
* (partysocket) that retries with exponential backoff after an unexpected
|
|
309
|
+
* close and resumes the session; an injected constructor is used as-is and
|
|
310
|
+
* never reconnects on its own.
|
|
90
311
|
*/
|
|
91
312
|
WebSocket?: WebSocketConstructor | undefined;
|
|
92
313
|
};
|
|
@@ -99,3 +320,20 @@ export type WebSocketConstructor = {
|
|
|
99
320
|
new (url: string | URL, protocols?: string | string[]): WebSocket;
|
|
100
321
|
readonly OPEN: number;
|
|
101
322
|
};
|
|
323
|
+
/**
|
|
324
|
+
* Theme color overrides for the AAI UI components.
|
|
325
|
+
*
|
|
326
|
+
* @public
|
|
327
|
+
*/
|
|
328
|
+
export type ClientTheme = {
|
|
329
|
+
/** Background color, also painted on `html`/`body`. Default: `#FBF8F2`. */
|
|
330
|
+
bg?: string;
|
|
331
|
+
/** Primary accent color. Default: `#3F2BC1`. */
|
|
332
|
+
primary?: string;
|
|
333
|
+
/** Main text color. Default: `#1B1A18`. */
|
|
334
|
+
text?: string;
|
|
335
|
+
/** Surface/card color. Default: `#FFFFFF`. */
|
|
336
|
+
surface?: string;
|
|
337
|
+
/** Border color. Default: `#DCD7CC`. */
|
|
338
|
+
border?: string;
|
|
339
|
+
};
|