@alexkroman1/aai-ui 0.10.2 → 0.10.4
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/dist/_components/app.d.ts +4 -4
- package/dist/_components/app.js +6 -6
- package/dist/_components/chat-view.js +2 -2
- package/dist/{mount-context.d.ts → client-context.d.ts} +7 -7
- package/dist/client-context.js +15 -0
- package/dist/{mount.d.ts → define-client.d.ts} +13 -11
- package/dist/{mount.js → define-client.js} +13 -12
- package/dist/index.d.ts +7 -7
- package/dist/index.js +5 -5
- package/dist/{session-D0RdWlWH.js → session-CWB7vmqz.js} +10 -32
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/dist/signals.d.ts +0 -17
- package/dist/signals.js +27 -63
- package/dist/types.d.ts +14 -2
- package/dist/types.test-d.d.ts +7 -0
- package/package.json +5 -4
- package/dist/_sqlite-stub.d.ts +0 -3
- package/dist/mount-context.js +0 -15
|
@@ -2,16 +2,16 @@ import type * as preact from "preact";
|
|
|
2
2
|
/**
|
|
3
3
|
* The default top-level UI component for an AAI voice agent.
|
|
4
4
|
* Renders a {@link StartScreen} (with the AAI logo or a custom title from
|
|
5
|
-
* {@link
|
|
5
|
+
* {@link useClientConfig}) followed by a {@link ChatView} once the session starts.
|
|
6
6
|
*
|
|
7
|
-
* This is the component rendered by {@link
|
|
7
|
+
* This is the component rendered by {@link defineClient} when no custom component is
|
|
8
8
|
* provided.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```tsx
|
|
12
|
-
* import { App,
|
|
12
|
+
* import { App, defineClient } from "@aai/ui";
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* defineClient(App, { target: "#app", title: "My Agent" });
|
|
15
15
|
* ```
|
|
16
16
|
*
|
|
17
17
|
* @param className - Additional CSS class names applied to the root element.
|
package/dist/_components/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useClientConfig } from "../client-context.js";
|
|
2
2
|
import { ChatView } from "./chat-view.js";
|
|
3
3
|
import { StartScreen } from "./start-screen.js";
|
|
4
4
|
import { jsx } from "preact/jsx-runtime";
|
|
@@ -12,16 +12,16 @@ function AnsiLogo() {
|
|
|
12
12
|
/**
|
|
13
13
|
* The default top-level UI component for an AAI voice agent.
|
|
14
14
|
* Renders a {@link StartScreen} (with the AAI logo or a custom title from
|
|
15
|
-
* {@link
|
|
15
|
+
* {@link useClientConfig}) followed by a {@link ChatView} once the session starts.
|
|
16
16
|
*
|
|
17
|
-
* This is the component rendered by {@link
|
|
17
|
+
* This is the component rendered by {@link defineClient} when no custom component is
|
|
18
18
|
* provided.
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
21
|
* ```tsx
|
|
22
|
-
* import { App,
|
|
22
|
+
* import { App, defineClient } from "@aai/ui";
|
|
23
23
|
*
|
|
24
|
-
*
|
|
24
|
+
* defineClient(App, { target: "#app", title: "My Agent" });
|
|
25
25
|
* ```
|
|
26
26
|
*
|
|
27
27
|
* @param className - Additional CSS class names applied to the root element.
|
|
@@ -29,7 +29,7 @@ function AnsiLogo() {
|
|
|
29
29
|
* @public
|
|
30
30
|
*/
|
|
31
31
|
function App({ className }) {
|
|
32
|
-
const { title } =
|
|
32
|
+
const { title } = useClientConfig();
|
|
33
33
|
return /* @__PURE__ */ jsx(StartScreen, {
|
|
34
34
|
icon: title ? void 0 : /* @__PURE__ */ jsx(AnsiLogo, {}),
|
|
35
35
|
title,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useClientConfig } from "../client-context.js";
|
|
2
2
|
import { useSession } from "../signals.js";
|
|
3
3
|
import { Controls } from "./controls.js";
|
|
4
4
|
import { ErrorBanner } from "./error-banner.js";
|
|
@@ -34,7 +34,7 @@ import { jsx, jsxs } from "preact/jsx-runtime";
|
|
|
34
34
|
*/
|
|
35
35
|
function ChatView({ className }) {
|
|
36
36
|
const { session } = useSession();
|
|
37
|
-
const { title } =
|
|
37
|
+
const { title } = useClientConfig();
|
|
38
38
|
return /* @__PURE__ */ jsxs("div", {
|
|
39
39
|
class: clsx("flex flex-col h-screen max-w-130 mx-auto bg-aai-bg text-aai-text font-aai text-sm", className),
|
|
40
40
|
children: [
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @public
|
|
5
5
|
*/
|
|
6
|
-
export type
|
|
6
|
+
export type ClientTheme = {
|
|
7
7
|
/** Background color. Default: `#101010`. */
|
|
8
8
|
bg?: string;
|
|
9
9
|
/** Primary accent color. Default: `#fab283`. */
|
|
@@ -16,18 +16,18 @@ export type MountTheme = {
|
|
|
16
16
|
border?: string;
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
|
-
* Resolved
|
|
19
|
+
* Resolved client-level configuration available to default UI components.
|
|
20
20
|
*
|
|
21
21
|
* @public
|
|
22
22
|
*/
|
|
23
|
-
export type
|
|
23
|
+
export type ClientConfig = {
|
|
24
24
|
title?: string | undefined;
|
|
25
|
-
theme?:
|
|
25
|
+
theme?: ClientTheme | undefined;
|
|
26
26
|
};
|
|
27
|
-
export declare const
|
|
27
|
+
export declare const ClientConfigProvider: import("preact").Provider<ClientConfig>;
|
|
28
28
|
/**
|
|
29
|
-
* Read
|
|
29
|
+
* Read client config (title, theme) from the nearest provider.
|
|
30
30
|
*
|
|
31
31
|
* @public
|
|
32
32
|
*/
|
|
33
|
-
export declare function
|
|
33
|
+
export declare function useClientConfig(): ClientConfig;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext } from "preact";
|
|
2
|
+
import { useContext } from "preact/hooks";
|
|
3
|
+
//#region client-context.ts
|
|
4
|
+
const Ctx = createContext({});
|
|
5
|
+
const ClientConfigProvider = Ctx.Provider;
|
|
6
|
+
/**
|
|
7
|
+
* Read client config (title, theme) from the nearest provider.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
function useClientConfig() {
|
|
12
|
+
return useContext(Ctx);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { ClientConfigProvider, useClientConfig };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { ComponentType } from "preact";
|
|
2
|
-
import { type
|
|
3
|
-
import { type VoiceSession } from "./session.ts";
|
|
2
|
+
import { type ClientTheme } from "./client-context.ts";
|
|
3
|
+
import { type VoiceSession, type WebSocketConstructor } from "./session.ts";
|
|
4
4
|
import { type SessionSignals } from "./signals.ts";
|
|
5
5
|
/**
|
|
6
|
-
* Options for {@link
|
|
6
|
+
* Options for {@link defineClient}.
|
|
7
7
|
*
|
|
8
8
|
* @public
|
|
9
9
|
*/
|
|
10
|
-
export type
|
|
10
|
+
export type ClientOptions = {
|
|
11
11
|
/** CSS selector or DOM element to render into. Defaults to `"#app"`. */
|
|
12
12
|
target?: string | HTMLElement;
|
|
13
13
|
/** Base URL of the AAI platform server. Derived from `location.href` by default. */
|
|
@@ -15,20 +15,22 @@ export type MountOptions = {
|
|
|
15
15
|
/** Agent title shown in the header and start screen. */
|
|
16
16
|
title?: string;
|
|
17
17
|
/** Theme color overrides. */
|
|
18
|
-
theme?:
|
|
18
|
+
theme?: ClientTheme;
|
|
19
19
|
/** Called when the server sends a session ID. Store it for reconnection. */
|
|
20
20
|
onSessionId?: ((sessionId: string) => void) | undefined;
|
|
21
21
|
/** Session ID from a previous connection for resuming persisted state. */
|
|
22
22
|
resumeSessionId?: string | undefined;
|
|
23
|
+
/** WebSocket constructor override. Passed through to VoiceSessionOptions. */
|
|
24
|
+
WebSocket?: WebSocketConstructor | undefined;
|
|
23
25
|
};
|
|
24
26
|
/**
|
|
25
|
-
* Handle returned by {@link
|
|
27
|
+
* Handle returned by {@link defineClient} for cleanup.
|
|
26
28
|
*
|
|
27
29
|
* Implements `Disposable` so it can be used with `using`.
|
|
28
30
|
*
|
|
29
31
|
* @public
|
|
30
32
|
*/
|
|
31
|
-
export type
|
|
33
|
+
export type ClientHandle = {
|
|
32
34
|
/** The underlying voice session. */
|
|
33
35
|
session: VoiceSession;
|
|
34
36
|
/** Reactive session controls for the mounted UI. */
|
|
@@ -39,17 +41,17 @@ export type MountHandle = {
|
|
|
39
41
|
[Symbol.dispose](): void;
|
|
40
42
|
};
|
|
41
43
|
/**
|
|
42
|
-
*
|
|
44
|
+
* Define and mount a client UI for a voice agent.
|
|
43
45
|
*
|
|
44
46
|
* Creates a {@link VoiceSession}, wraps it in
|
|
45
47
|
* {@link SessionSignals}, and renders the component
|
|
46
48
|
* inside a {@link SessionProvider}.
|
|
47
49
|
*
|
|
48
50
|
* @param Component - The Preact component to render.
|
|
49
|
-
* @param options -
|
|
50
|
-
* @returns A {@link
|
|
51
|
+
* @param options - Client options (target element, platform URL, theme).
|
|
52
|
+
* @returns A {@link ClientHandle} for cleanup.
|
|
51
53
|
* @throws If the target element is not found in the DOM.
|
|
52
54
|
*
|
|
53
55
|
* @public
|
|
54
56
|
*/
|
|
55
|
-
export declare function
|
|
57
|
+
export declare function defineClient(Component: ComponentType<any>, options?: ClientOptions): ClientHandle;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClientConfigProvider } from "./client-context.js";
|
|
2
2
|
import { SessionProvider, createSessionControls } from "./signals.js";
|
|
3
|
-
import { t as createVoiceSession } from "./session-
|
|
3
|
+
import { t as createVoiceSession } from "./session-CWB7vmqz.js";
|
|
4
4
|
import { render } from "preact";
|
|
5
5
|
import { batch, signal } from "@preact/signals";
|
|
6
6
|
import { jsx } from "preact/jsx-runtime";
|
|
7
|
-
//#region
|
|
7
|
+
//#region define-client.tsx
|
|
8
8
|
function resolveContainer(target = "#app") {
|
|
9
9
|
if (typeof target !== "string") return target;
|
|
10
10
|
const el = document.querySelector(target);
|
|
@@ -12,30 +12,31 @@ function resolveContainer(target = "#app") {
|
|
|
12
12
|
return el;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Define and mount a client UI for a voice agent.
|
|
16
16
|
*
|
|
17
17
|
* Creates a {@link VoiceSession}, wraps it in
|
|
18
18
|
* {@link SessionSignals}, and renders the component
|
|
19
19
|
* inside a {@link SessionProvider}.
|
|
20
20
|
*
|
|
21
21
|
* @param Component - The Preact component to render.
|
|
22
|
-
* @param options -
|
|
23
|
-
* @returns A {@link
|
|
22
|
+
* @param options - Client options (target element, platform URL, theme).
|
|
23
|
+
* @returns A {@link ClientHandle} for cleanup.
|
|
24
24
|
* @throws If the target element is not found in the DOM.
|
|
25
25
|
*
|
|
26
26
|
* @public
|
|
27
27
|
*/
|
|
28
|
-
function
|
|
28
|
+
function defineClient(Component, options) {
|
|
29
29
|
const container = resolveContainer(options?.target);
|
|
30
30
|
const session = createVoiceSession({
|
|
31
31
|
platformUrl: options?.platformUrl ?? globalThis.location.origin + globalThis.location.pathname,
|
|
32
32
|
reactiveFactory: signal,
|
|
33
33
|
batch,
|
|
34
34
|
onSessionId: options?.onSessionId,
|
|
35
|
-
resumeSessionId: options?.resumeSessionId
|
|
35
|
+
resumeSessionId: options?.resumeSessionId,
|
|
36
|
+
...options?.WebSocket ? { WebSocket: options.WebSocket } : {}
|
|
36
37
|
});
|
|
37
38
|
const signals = createSessionControls(session);
|
|
38
|
-
const
|
|
39
|
+
const clientConfig = {
|
|
39
40
|
title: options?.title,
|
|
40
41
|
theme: options?.theme
|
|
41
42
|
};
|
|
@@ -48,8 +49,8 @@ function mount(Component, options) {
|
|
|
48
49
|
if (t.surface) el.style.setProperty("--color-aai-surface", t.surface);
|
|
49
50
|
if (t.border) el.style.setProperty("--color-aai-border", t.border);
|
|
50
51
|
}
|
|
51
|
-
render(/* @__PURE__ */ jsx(
|
|
52
|
-
value:
|
|
52
|
+
render(/* @__PURE__ */ jsx(ClientConfigProvider, {
|
|
53
|
+
value: clientConfig,
|
|
53
54
|
children: /* @__PURE__ */ jsx(SessionProvider, {
|
|
54
55
|
value: signals,
|
|
55
56
|
children: /* @__PURE__ */ jsx(Component, {})
|
|
@@ -70,4 +71,4 @@ function mount(Component, options) {
|
|
|
70
71
|
return handle;
|
|
71
72
|
}
|
|
72
73
|
//#endregion
|
|
73
|
-
export {
|
|
74
|
+
export { defineClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```tsx
|
|
10
|
-
* import { App,
|
|
10
|
+
* import { App, defineClient } from "@aai/ui";
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* defineClient(App, { target: "#app" });
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
15
|
export { App } from "./_components/app.tsx";
|
|
@@ -26,12 +26,12 @@ export { StateIndicator } from "./_components/state-indicator.tsx";
|
|
|
26
26
|
export { ThinkingIndicator } from "./_components/thinking-indicator.tsx";
|
|
27
27
|
export { ToolCallBlock } from "./_components/tool-call-block.tsx";
|
|
28
28
|
export { Transcript } from "./_components/transcript.tsx";
|
|
29
|
-
export type {
|
|
30
|
-
export {
|
|
31
|
-
export type {
|
|
32
|
-
export {
|
|
29
|
+
export type { ClientConfig, ClientTheme } from "./client-context.ts";
|
|
30
|
+
export { useClientConfig } from "./client-context.ts";
|
|
31
|
+
export type { ClientHandle, ClientOptions } from "./define-client.tsx";
|
|
32
|
+
export { defineClient } from "./define-client.tsx";
|
|
33
33
|
export type { VoiceSession } from "./session.ts";
|
|
34
34
|
export { createVoiceSession } from "./session.ts";
|
|
35
35
|
export type { SessionSignals } from "./signals.ts";
|
|
36
|
-
export { createSessionControls, SessionProvider, useAutoScroll, useSession, useToolCallStart,
|
|
36
|
+
export { createSessionControls, SessionProvider, useAutoScroll, useSession, useToolCallStart, useToolResult, } from "./signals.ts";
|
|
37
37
|
export type { AgentState, ChatMessage, Reactive, SessionError, SessionErrorCode, ToolCallInfo, VoiceSessionOptions, } from "./types.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SessionProvider, createSessionControls, useAutoScroll, useSession, useToolCallStart,
|
|
1
|
+
import { useClientConfig } from "./client-context.js";
|
|
2
|
+
import { SessionProvider, createSessionControls, useAutoScroll, useSession, useToolCallStart, useToolResult } from "./signals.js";
|
|
3
3
|
import { Button } from "./_components/button.js";
|
|
4
4
|
import { Controls } from "./_components/controls.js";
|
|
5
5
|
import { ErrorBanner } from "./_components/error-banner.js";
|
|
@@ -13,6 +13,6 @@ import { ChatView } from "./_components/chat-view.js";
|
|
|
13
13
|
import { StartScreen } from "./_components/start-screen.js";
|
|
14
14
|
import { App } from "./_components/app.js";
|
|
15
15
|
import { SidebarLayout } from "./_components/sidebar-layout.js";
|
|
16
|
-
import { t as createVoiceSession } from "./session-
|
|
17
|
-
import {
|
|
18
|
-
export { App, Button, ChatView, Controls, ErrorBanner, MessageBubble, MessageList, SessionProvider, SidebarLayout, StartScreen, StateIndicator, ThinkingIndicator, ToolCallBlock, Transcript, createSessionControls, createVoiceSession,
|
|
16
|
+
import { t as createVoiceSession } from "./session-CWB7vmqz.js";
|
|
17
|
+
import { defineClient } from "./define-client.js";
|
|
18
|
+
export { App, Button, ChatView, Controls, ErrorBanner, MessageBubble, MessageList, SessionProvider, SidebarLayout, StartScreen, StateIndicator, ThinkingIndicator, ToolCallBlock, Transcript, createSessionControls, createVoiceSession, defineClient, useAutoScroll, useClientConfig, useSession, useToolCallStart, useToolResult };
|
|
@@ -72,24 +72,9 @@ var ClientHandler = class {
|
|
|
72
72
|
toolName: e.toolName,
|
|
73
73
|
args: e.args,
|
|
74
74
|
status: "pending",
|
|
75
|
-
updates: [],
|
|
76
75
|
afterMessageIndex: this.#messages.value.length - 1
|
|
77
76
|
}];
|
|
78
77
|
break;
|
|
79
|
-
case "tool_call_update": {
|
|
80
|
-
const tcs = this.#toolCalls.value;
|
|
81
|
-
const idx = tcs.findIndex((tc) => tc.toolCallId === e.toolCallId);
|
|
82
|
-
if (idx !== -1) {
|
|
83
|
-
const updated = [...tcs];
|
|
84
|
-
const existing = updated[idx];
|
|
85
|
-
if (existing) updated[idx] = {
|
|
86
|
-
...existing,
|
|
87
|
-
updates: [...existing.updates, e.data]
|
|
88
|
-
};
|
|
89
|
-
this.#toolCalls.value = updated;
|
|
90
|
-
}
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
78
|
case "tool_call_done": {
|
|
94
79
|
const tcs = this.#toolCalls.value;
|
|
95
80
|
const idx = tcs.findIndex((tc) => tc.toolCallId === e.toolCallId);
|
|
@@ -208,14 +193,7 @@ var ClientHandler = class {
|
|
|
208
193
|
};
|
|
209
194
|
//#endregion
|
|
210
195
|
//#region session.ts
|
|
211
|
-
|
|
212
|
-
function plainReactive(initial) {
|
|
213
|
-
return { value: initial };
|
|
214
|
-
}
|
|
215
|
-
/** No-op batch — just calls the function. */
|
|
216
|
-
function plainBatch(fn) {
|
|
217
|
-
fn();
|
|
218
|
-
}
|
|
196
|
+
const WS_OPEN = 1;
|
|
219
197
|
/**
|
|
220
198
|
* Initialize audio capture and playback after the server sends a ready config.
|
|
221
199
|
*
|
|
@@ -261,7 +239,7 @@ async function initAudioCapture(conn, msg, deps) {
|
|
|
261
239
|
}
|
|
262
240
|
}
|
|
263
241
|
});
|
|
264
|
-
if (conn.generation !== gen || !conn.ws || conn.ws.readyState !==
|
|
242
|
+
if (conn.generation !== gen || !conn.ws || conn.ws.readyState !== WS_OPEN) {
|
|
265
243
|
io.close();
|
|
266
244
|
return;
|
|
267
245
|
}
|
|
@@ -269,7 +247,7 @@ async function initAudioCapture(conn, msg, deps) {
|
|
|
269
247
|
deps.send({ type: "audio_ready" });
|
|
270
248
|
deps.state.value = "listening";
|
|
271
249
|
} catch (err) {
|
|
272
|
-
if (conn.generation !== gen || !conn.ws || conn.ws.readyState !==
|
|
250
|
+
if (conn.generation !== gen || !conn.ws || conn.ws.readyState !== WS_OPEN) return;
|
|
273
251
|
deps.batch(() => {
|
|
274
252
|
deps.error.value = {
|
|
275
253
|
code: "audio",
|
|
@@ -299,8 +277,9 @@ function buildWsUrl(platformUrl, resume, sessionId) {
|
|
|
299
277
|
* @public
|
|
300
278
|
*/
|
|
301
279
|
function createVoiceSession(options) {
|
|
302
|
-
const
|
|
303
|
-
const
|
|
280
|
+
const WS = options.WebSocket ?? WebSocket;
|
|
281
|
+
const reactive = options.reactiveFactory ?? ((initial) => ({ value: initial }));
|
|
282
|
+
const batchFn = options.batch ?? ((fn) => fn());
|
|
304
283
|
const state = reactive("disconnected");
|
|
305
284
|
const messages = reactive([]);
|
|
306
285
|
const toolCalls = reactive([]);
|
|
@@ -331,10 +310,10 @@ function createVoiceSession(options) {
|
|
|
331
310
|
});
|
|
332
311
|
}
|
|
333
312
|
function send(msg) {
|
|
334
|
-
if (conn.ws && conn.ws.readyState ===
|
|
313
|
+
if (conn.ws && conn.ws.readyState === WS_OPEN) conn.ws.send(JSON.stringify(msg));
|
|
335
314
|
}
|
|
336
315
|
function sendBinary(data) {
|
|
337
|
-
if (conn.ws && conn.ws.readyState ===
|
|
316
|
+
if (conn.ws && conn.ws.readyState === WS_OPEN) conn.ws.send(data);
|
|
338
317
|
}
|
|
339
318
|
const audioDeps = {
|
|
340
319
|
send,
|
|
@@ -356,8 +335,7 @@ function createVoiceSession(options) {
|
|
|
356
335
|
const { signal: sig } = controller;
|
|
357
336
|
if (opts?.signal) opts.signal.addEventListener("abort", () => disconnect(), { signal: sig });
|
|
358
337
|
const resumeId = !hasConnected ? options.resumeSessionId : void 0;
|
|
359
|
-
const
|
|
360
|
-
const socket = new WebSocket(wsUrl.toString());
|
|
338
|
+
const socket = new WS(buildWsUrl(options.platformUrl, hasConnected, resumeId).toString());
|
|
361
339
|
socket.binaryType = "arraybuffer";
|
|
362
340
|
conn.ws = socket;
|
|
363
341
|
const handler = new ClientHandler({
|
|
@@ -412,7 +390,7 @@ function createVoiceSession(options) {
|
|
|
412
390
|
}
|
|
413
391
|
function reset() {
|
|
414
392
|
conn.voiceIO?.flush();
|
|
415
|
-
if (conn.ws && conn.ws.readyState ===
|
|
393
|
+
if (conn.ws && conn.ws.readyState === WS_OPEN) {
|
|
416
394
|
send({ type: "reset" });
|
|
417
395
|
return;
|
|
418
396
|
}
|
package/dist/session.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentState, ChatMessage, Reactive, SessionError, ToolCallInfo, VoiceSessionOptions } from "./types.ts";
|
|
2
2
|
export { ClientHandler } from "./client-handler.ts";
|
|
3
|
-
export type { AgentState, ChatMessage, Reactive, SessionError, SessionErrorCode, ToolCallInfo, VoiceSessionOptions, } from "./types.ts";
|
|
3
|
+
export type { AgentState, ChatMessage, Reactive, SessionError, SessionErrorCode, ToolCallInfo, VoiceSessionOptions, WebSocketConstructor, } from "./types.ts";
|
|
4
4
|
/**
|
|
5
5
|
* A reactive voice session that manages WebSocket communication,
|
|
6
6
|
* audio capture/playback, and agent state transitions.
|
package/dist/session.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as ClientHandler, t as createVoiceSession } from "./session-
|
|
1
|
+
import { n as ClientHandler, t as createVoiceSession } from "./session-CWB7vmqz.js";
|
|
2
2
|
export { ClientHandler, createVoiceSession };
|
package/dist/signals.d.ts
CHANGED
|
@@ -110,23 +110,6 @@ export declare function useToolResult(callback: (toolName: string, result: unkno
|
|
|
110
110
|
* @public
|
|
111
111
|
*/
|
|
112
112
|
export declare function useToolCallStart(callback: (toolName: string, args: Record<string, unknown>, toolCall: ToolCallInfo) => void): void;
|
|
113
|
-
/**
|
|
114
|
-
* Hook that fires a callback for each intermediate update pushed by a tool
|
|
115
|
-
* via `ctx.sendUpdate()`.
|
|
116
|
-
*
|
|
117
|
-
* Use this to render progressive/streaming data from a tool before it
|
|
118
|
-
* finishes — e.g. showing a recipe preview card while nutrition data is
|
|
119
|
-
* still loading.
|
|
120
|
-
*
|
|
121
|
-
* The callback fires once per update. The `data` argument is the parsed
|
|
122
|
-
* JSON update (or raw string if parsing fails).
|
|
123
|
-
*
|
|
124
|
-
* @param callback - Called once per intermediate update with the tool name,
|
|
125
|
-
* parsed data, and full {@link ToolCallInfo}.
|
|
126
|
-
*
|
|
127
|
-
* @public
|
|
128
|
-
*/
|
|
129
|
-
export declare function useToolCallUpdate(callback: (toolName: string, data: unknown, toolCall: ToolCallInfo) => void): void;
|
|
130
113
|
/**
|
|
131
114
|
* Auto-scroll a container to the bottom when messages, tool calls,
|
|
132
115
|
* or utterances change. Returns a ref to attach to a sentinel `<div>`
|
package/dist/signals.js
CHANGED
|
@@ -86,13 +86,23 @@ function isNewCompletedCall(tc, seen, filterName) {
|
|
|
86
86
|
if (filterName && tc.toolName !== filterName) return false;
|
|
87
87
|
return true;
|
|
88
88
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Shared helper for hooks that need to fire a callback once per new tool call.
|
|
91
|
+
*
|
|
92
|
+
* Handles deduplication via a `Set<string>` ref, automatic reset when the
|
|
93
|
+
* signal array is cleared, and a stable callback ref to avoid stale closures.
|
|
94
|
+
*
|
|
95
|
+
* @param session - The voice session whose `toolCalls` signal to watch.
|
|
96
|
+
* @param shouldProcess - Predicate that decides whether a tool call is eligible.
|
|
97
|
+
* Called with the tool call and the seen-IDs set. Must NOT mutate the set.
|
|
98
|
+
* @param onNew - Invoked for each eligible tool call that hasn't been seen yet.
|
|
99
|
+
*/
|
|
100
|
+
function useToolCallEffect(session, shouldProcess, onNew) {
|
|
93
101
|
const seenRef = useRef(/* @__PURE__ */ new Set());
|
|
94
|
-
const cbRef = useRef(
|
|
95
|
-
cbRef.current =
|
|
102
|
+
const cbRef = useRef(onNew);
|
|
103
|
+
cbRef.current = onNew;
|
|
104
|
+
const predicateRef = useRef(shouldProcess);
|
|
105
|
+
predicateRef.current = shouldProcess;
|
|
96
106
|
useEffect(() => effect(() => {
|
|
97
107
|
const toolCalls = session.toolCalls.value;
|
|
98
108
|
if (toolCalls.length === 0) {
|
|
@@ -100,12 +110,19 @@ function useToolResult(toolNameOrCallback, maybeCallback) {
|
|
|
100
110
|
return;
|
|
101
111
|
}
|
|
102
112
|
for (const tc of toolCalls) {
|
|
103
|
-
if (!
|
|
113
|
+
if (!predicateRef.current(tc, seenRef.current)) continue;
|
|
114
|
+
if (seenRef.current.has(tc.toolCallId)) continue;
|
|
104
115
|
seenRef.current.add(tc.toolCallId);
|
|
105
|
-
cbRef.current(tc
|
|
116
|
+
cbRef.current(tc);
|
|
106
117
|
}
|
|
107
118
|
}), [session]);
|
|
108
119
|
}
|
|
120
|
+
function useToolResult(toolNameOrCallback, maybeCallback) {
|
|
121
|
+
const filterName = typeof toolNameOrCallback === "string" ? toolNameOrCallback : void 0;
|
|
122
|
+
const callback = typeof toolNameOrCallback === "function" ? toolNameOrCallback : (_name, result, tc) => maybeCallback?.(result, tc);
|
|
123
|
+
const { session } = useSession();
|
|
124
|
+
useToolCallEffect(session, (tc, seen) => isNewCompletedCall(tc, seen, filterName), (tc) => callback(tc.toolName, tryParseJSON(tc.result), tc));
|
|
125
|
+
}
|
|
109
126
|
/**
|
|
110
127
|
* Hook that fires a callback when a new tool call starts (status: "pending").
|
|
111
128
|
*
|
|
@@ -119,60 +136,7 @@ function useToolResult(toolNameOrCallback, maybeCallback) {
|
|
|
119
136
|
*/
|
|
120
137
|
function useToolCallStart(callback) {
|
|
121
138
|
const { session } = useSession();
|
|
122
|
-
|
|
123
|
-
const cbRef = useRef(callback);
|
|
124
|
-
cbRef.current = callback;
|
|
125
|
-
useEffect(() => effect(() => {
|
|
126
|
-
const toolCalls = session.toolCalls.value;
|
|
127
|
-
if (toolCalls.length === 0) {
|
|
128
|
-
seenRef.current.clear();
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
for (const tc of toolCalls) {
|
|
132
|
-
if (seenRef.current.has(tc.toolCallId)) continue;
|
|
133
|
-
seenRef.current.add(tc.toolCallId);
|
|
134
|
-
cbRef.current(tc.toolName, tc.args, tc);
|
|
135
|
-
}
|
|
136
|
-
}), [session]);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Hook that fires a callback for each intermediate update pushed by a tool
|
|
140
|
-
* via `ctx.sendUpdate()`.
|
|
141
|
-
*
|
|
142
|
-
* Use this to render progressive/streaming data from a tool before it
|
|
143
|
-
* finishes — e.g. showing a recipe preview card while nutrition data is
|
|
144
|
-
* still loading.
|
|
145
|
-
*
|
|
146
|
-
* The callback fires once per update. The `data` argument is the parsed
|
|
147
|
-
* JSON update (or raw string if parsing fails).
|
|
148
|
-
*
|
|
149
|
-
* @param callback - Called once per intermediate update with the tool name,
|
|
150
|
-
* parsed data, and full {@link ToolCallInfo}.
|
|
151
|
-
*
|
|
152
|
-
* @public
|
|
153
|
-
*/
|
|
154
|
-
function useToolCallUpdate(callback) {
|
|
155
|
-
const { session } = useSession();
|
|
156
|
-
const countRef = useRef(/* @__PURE__ */ new Map());
|
|
157
|
-
const cbRef = useRef(callback);
|
|
158
|
-
cbRef.current = callback;
|
|
159
|
-
useEffect(() => effect(() => {
|
|
160
|
-
const toolCalls = session.toolCalls.value;
|
|
161
|
-
if (toolCalls.length === 0) {
|
|
162
|
-
countRef.current.clear();
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
for (const tc of toolCalls) processNewUpdates(tc, countRef.current, cbRef.current);
|
|
166
|
-
}), [session]);
|
|
167
|
-
}
|
|
168
|
-
function processNewUpdates(tc, seenCounts, cb) {
|
|
169
|
-
const seen = seenCounts.get(tc.toolCallId) ?? 0;
|
|
170
|
-
if (tc.updates.length <= seen) return;
|
|
171
|
-
for (let i = seen; i < tc.updates.length; i++) {
|
|
172
|
-
const raw = tc.updates[i];
|
|
173
|
-
if (raw !== void 0) cb(tc.toolName, tryParseJSON(raw), tc);
|
|
174
|
-
}
|
|
175
|
-
seenCounts.set(tc.toolCallId, tc.updates.length);
|
|
139
|
+
useToolCallEffect(session, () => true, (tc) => callback(tc.toolName, tc.args, tc));
|
|
176
140
|
}
|
|
177
141
|
/**
|
|
178
142
|
* Auto-scroll a container to the bottom when messages, tool calls,
|
|
@@ -194,4 +158,4 @@ function useAutoScroll() {
|
|
|
194
158
|
return ref;
|
|
195
159
|
}
|
|
196
160
|
//#endregion
|
|
197
|
-
export { SessionProvider, createSessionControls, useAutoScroll, useSession, useToolCallStart,
|
|
161
|
+
export { SessionProvider, createSessionControls, useAutoScroll, useSession, useToolCallStart, useToolResult };
|
package/dist/types.d.ts
CHANGED
|
@@ -29,8 +29,6 @@ export type ToolCallInfo = {
|
|
|
29
29
|
args: Record<string, unknown>;
|
|
30
30
|
status: "pending" | "done";
|
|
31
31
|
result?: string | undefined;
|
|
32
|
-
/** Intermediate updates pushed by the tool via `ctx.sendUpdate()`. */
|
|
33
|
-
updates: string[];
|
|
34
32
|
/** Index in the messages array where this tool call should appear. */
|
|
35
33
|
afterMessageIndex: number;
|
|
36
34
|
};
|
|
@@ -86,4 +84,18 @@ export type VoiceSessionOptions = {
|
|
|
86
84
|
* `persistence` enabled).
|
|
87
85
|
*/
|
|
88
86
|
resumeSessionId?: string | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* WebSocket constructor override. Defaults to the native `WebSocket`.
|
|
89
|
+
* Primarily useful for testing with a mock WebSocket.
|
|
90
|
+
*/
|
|
91
|
+
WebSocket?: WebSocketConstructor | undefined;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Minimal WebSocket constructor type accepted by {@link VoiceSessionOptions}.
|
|
95
|
+
*
|
|
96
|
+
* @public
|
|
97
|
+
*/
|
|
98
|
+
export type WebSocketConstructor = {
|
|
99
|
+
new (url: string | URL, protocols?: string | string[]): WebSocket;
|
|
100
|
+
readonly OPEN: number;
|
|
89
101
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexkroman1/aai-ui",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"clsx": "^2.1.1",
|
|
24
|
-
"@alexkroman1/aai": "0.10.
|
|
24
|
+
"@alexkroman1/aai": "0.10.4"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@preact/signals": "^2.8.2",
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"@testing-library/preact": "^3.2.4",
|
|
39
39
|
"jsdom": "^29.0.1",
|
|
40
40
|
"preact": "^10.29.0",
|
|
41
|
-
"tsdown": "^0.21.5"
|
|
41
|
+
"tsdown": "^0.21.5",
|
|
42
|
+
"vitest": "^4.1.1"
|
|
42
43
|
},
|
|
43
44
|
"engines": {
|
|
44
45
|
"node": ">=22.6"
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"build": "tsdown && tsc -p tsconfig.build.json",
|
|
53
54
|
"typecheck": "tsc --noEmit",
|
|
54
55
|
"lint": "biome check .",
|
|
55
|
-
"check:
|
|
56
|
+
"check:publint": "publint",
|
|
56
57
|
"check:attw": "attw --pack --profile esm-only --entrypoints . ./session"
|
|
57
58
|
}
|
|
58
59
|
}
|
package/dist/_sqlite-stub.d.ts
DELETED
package/dist/mount-context.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { createContext } from "preact";
|
|
2
|
-
import { useContext } from "preact/hooks";
|
|
3
|
-
//#region mount-context.ts
|
|
4
|
-
const Ctx = createContext({});
|
|
5
|
-
const MountConfigProvider = Ctx.Provider;
|
|
6
|
-
/**
|
|
7
|
-
* Read mount config (title, theme) from the nearest provider.
|
|
8
|
-
*
|
|
9
|
-
* @public
|
|
10
|
-
*/
|
|
11
|
-
function useMountConfig() {
|
|
12
|
-
return useContext(Ctx);
|
|
13
|
-
}
|
|
14
|
-
//#endregion
|
|
15
|
-
export { MountConfigProvider, useMountConfig };
|