@alexkroman1/aai-ui 5.0.1 → 5.1.1
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/{_colors-DYX7XRTr.js → _colors-Bfh3-BVE.js} +3 -1
- package/dist/{_utils-CN1yVgYS.js → _utils-CsqbIVGI.js} +10 -1
- package/dist/_utils.d.ts +6 -0
- package/dist/{chat-view-CW-tZCMm.js → chat-view-C6N2oXjo.js} +4 -4
- package/dist/client-config.d.ts +3 -2
- package/dist/components/_colors.d.ts +2 -0
- package/dist/components/chat-view.js +1 -1
- package/dist/components/controls.js +1 -1
- package/dist/components/message-list.js +1 -1
- package/dist/components/start-screen.js +1 -1
- package/dist/components/tool-call-block.js +1 -1
- package/dist/components/url-chips.d.ts +2 -1
- package/dist/{controls-DV368uhb.js → controls-Cqg7T4FN.js} +5 -8
- package/dist/default-client/assets/{audio-BdEKgLwT.js → audio-C1U_foBG.js} +1 -1
- package/dist/default-client/assets/{capture-processor-_QJe3nzY.js → capture-processor-dyyJArQs.js} +1 -1
- package/dist/default-client/assets/index-CGZajs1V.js +192 -0
- package/dist/default-client/assets/{playback-processor-C_F19eUJ.js → playback-processor-BO2TdYrv.js} +1 -1
- package/dist/default-client/index.html +1 -1
- package/dist/define-client.d.ts +6 -8
- package/dist/define-client.js +5 -4
- package/dist/hooks.js +1 -1
- package/dist/index.js +5 -5
- package/dist/{message-list-q2lTiFhF.js → message-list-CD9GxrlH.js} +106 -104
- package/dist/{session-core-ByBLnLfW.js → session-core-Bvga6tUS.js} +31 -25
- package/dist/session-core-audio-setup.d.ts +10 -1
- package/dist/session-core-types.d.ts +8 -4
- package/dist/session-core.js +1 -1
- package/dist/{tool-call-block-DIxpG8GM.js → tool-call-block-O9vLqoyU.js} +2 -2
- package/package.json +2 -2
- package/dist/default-client/assets/index-RQnhfgSS.js +0 -192
package/dist/default-client/assets/{playback-processor-C_F19eUJ.js → playback-processor-BO2TdYrv.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{i as e}from"./index-
|
|
1
|
+
import{i as e}from"./index-CGZajs1V.js";import{t}from"./_module-url-BX0RuRU2.js";var n=t(`
|
|
2
2
|
class PlaybackProcessor extends AudioWorkletProcessor {
|
|
3
3
|
constructor(options) {
|
|
4
4
|
super();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>aai</title>
|
|
7
7
|
<link rel="icon" href="./favicon.ico" />
|
|
8
8
|
<style>html, body { background: #FBF8F2; margin: 0; }</style>
|
|
9
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
+
<script type="module" crossorigin src="./assets/index-CGZajs1V.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-CRfdZPH1.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
package/dist/define-client.d.ts
CHANGED
|
@@ -2,25 +2,23 @@
|
|
|
2
2
|
import { type ComponentType } from "react";
|
|
3
3
|
import { type ToolDisplayConfig } from "./components/tool-config-context.ts";
|
|
4
4
|
import type { SessionCore } from "./session-core-types.ts";
|
|
5
|
-
import type { ClientTheme,
|
|
5
|
+
import type { ClientTheme, VoiceSessionOptions } from "./types.ts";
|
|
6
6
|
/**
|
|
7
7
|
* Base options shared by both client tiers.
|
|
8
8
|
*
|
|
9
|
+
* The session-forwarded fields are picked from {@link VoiceSessionOptions}
|
|
10
|
+
* (one source of truth for types and docs) rather than re-declared — a
|
|
11
|
+
* re-declared copy is exactly how doc comments drift.
|
|
12
|
+
*
|
|
9
13
|
* @public
|
|
10
14
|
*/
|
|
11
|
-
type BaseOptions = {
|
|
15
|
+
type BaseOptions = Pick<VoiceSessionOptions, "onSessionId" | "resumeSessionId" | "WebSocket"> & {
|
|
12
16
|
/** CSS selector or DOM element to render into. Defaults to `"#app"`. */
|
|
13
17
|
target?: string | HTMLElement;
|
|
14
18
|
/** Base URL of the AAI platform server. Derived from `location.href` by default. */
|
|
15
19
|
platformUrl?: string;
|
|
16
20
|
/** Theme color overrides. */
|
|
17
21
|
theme?: ClientTheme;
|
|
18
|
-
/** Called when the server sends a session ID. Store it for reconnection. */
|
|
19
|
-
onSessionId?: (sessionId: string) => void;
|
|
20
|
-
/** Session ID from a previous connection for resuming persisted state. */
|
|
21
|
-
resumeSessionId?: string;
|
|
22
|
-
/** WebSocket constructor override. Passed through to session options. */
|
|
23
|
-
WebSocket?: WebSocketConstructor;
|
|
24
22
|
};
|
|
25
23
|
/**
|
|
26
24
|
* Tier 1: Config-only options. Renders the default shell (StartScreen + ChatView).
|
package/dist/define-client.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { r as fetchClientConfig, t as createSessionCore } from "./session-core-
|
|
1
|
+
import { r as fetchClientConfig, t as createSessionCore } from "./session-core-Bvga6tUS.js";
|
|
2
2
|
import { SessionProvider, ThemeProvider } from "./context.js";
|
|
3
|
-
import { t as ChatView } from "./chat-view-
|
|
4
|
-
import {
|
|
3
|
+
import { t as ChatView } from "./chat-view-C6N2oXjo.js";
|
|
4
|
+
import { t as pageBaseUrl } from "./_utils-CsqbIVGI.js";
|
|
5
|
+
import { n as ToolConfigContext } from "./tool-call-block-O9vLqoyU.js";
|
|
5
6
|
import { SidebarLayout } from "./components/sidebar-layout.js";
|
|
6
7
|
import { StartScreen } from "./components/start-screen.js";
|
|
7
8
|
import { createElement, useEffect, useState } from "react";
|
|
@@ -86,7 +87,7 @@ function DefaultRoot({ platformUrl, name, Sidebar, sidebarWidth }) {
|
|
|
86
87
|
*/
|
|
87
88
|
function client(config) {
|
|
88
89
|
const container = resolveContainer(config.target);
|
|
89
|
-
const platformUrl = config.platformUrl ??
|
|
90
|
+
const platformUrl = config.platformUrl ?? pageBaseUrl();
|
|
90
91
|
const session = createSessionCore({
|
|
91
92
|
platformUrl,
|
|
92
93
|
onSessionId: config.onSessionId,
|
package/dist/hooks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useSessionSelector } from "./context.js";
|
|
2
|
-
import {
|
|
2
|
+
import { r as tryParseJSON } from "./_utils-CsqbIVGI.js";
|
|
3
3
|
import { useEffect, useRef } from "react";
|
|
4
4
|
//#region hooks.ts
|
|
5
5
|
/** Index of the first item whose sequence number is above the watermark (tail scan from the end). */
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { n as buildAgentUrl, r as fetchClientConfig, t as createSessionCore } from "./session-core-
|
|
1
|
+
import { n as buildAgentUrl, r as fetchClientConfig, t as createSessionCore } from "./session-core-Bvga6tUS.js";
|
|
2
2
|
import { SessionProvider, ThemeProvider, useSession, useSessionSelector, useTheme } from "./context.js";
|
|
3
3
|
import { Button } from "./components/button.js";
|
|
4
|
-
import { t as ChatView } from "./chat-view-
|
|
5
|
-
import { i as UiUrlChip, n as ApiUrlChip, r as SessionUrlChips, t as Controls } from "./controls-
|
|
6
|
-
import { n as Markdown, t as MessageList } from "./message-list-
|
|
7
|
-
import { n as ToolConfigContext } from "./tool-call-block-
|
|
4
|
+
import { t as ChatView } from "./chat-view-C6N2oXjo.js";
|
|
5
|
+
import { i as UiUrlChip, n as ApiUrlChip, r as SessionUrlChips, t as Controls } from "./controls-Cqg7T4FN.js";
|
|
6
|
+
import { n as Markdown, t as MessageList } from "./message-list-CD9GxrlH.js";
|
|
7
|
+
import { n as ToolConfigContext } from "./tool-call-block-O9vLqoyU.js";
|
|
8
8
|
import { SidebarLayout } from "./components/sidebar-layout.js";
|
|
9
9
|
import { StartScreen } from "./components/start-screen.js";
|
|
10
10
|
import { VOICE_CAPTURE_CONSTRAINTS } from "./types.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useSessionSelector, useTheme } from "./context.js";
|
|
2
|
-
import {
|
|
3
|
-
import { t as ToolCallBlock } from "./tool-call-block-
|
|
2
|
+
import { i as TEXT_MUTED, n as SURFACE_TINT, o as primaryTint, r as TEXT_FAINT } from "./_colors-Bfh3-BVE.js";
|
|
3
|
+
import { t as ToolCallBlock } from "./tool-call-block-O9vLqoyU.js";
|
|
4
4
|
import clsx from "clsx";
|
|
5
5
|
import { memo, useCallback, useEffect, useMemo, useRef } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -52,110 +52,111 @@ function escapeBareListMarkers(text) {
|
|
|
52
52
|
*/
|
|
53
53
|
const Markdown = memo(function Markdown({ text }) {
|
|
54
54
|
const theme = useTheme();
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
style
|
|
89
|
-
color: theme.primary,
|
|
90
|
-
...style
|
|
91
|
-
},
|
|
92
|
-
target: "_blank",
|
|
93
|
-
rel: "noreferrer noopener",
|
|
94
|
-
...props
|
|
95
|
-
}),
|
|
96
|
-
code: ({ className, style, children, ...rest }) => {
|
|
97
|
-
return /language-/.test(className ?? "") ? /* @__PURE__ */ jsx("code", {
|
|
98
|
-
className: "font-aai-mono text-[12.5px]",
|
|
99
|
-
...rest,
|
|
100
|
-
children
|
|
101
|
-
}) : /* @__PURE__ */ jsx("code", {
|
|
102
|
-
className: "rounded-sm border px-1 py-0.5 font-aai-mono text-[12.5px]",
|
|
103
|
-
style: {
|
|
104
|
-
borderColor: theme.border,
|
|
105
|
-
background: SURFACE_TINT,
|
|
106
|
-
...style
|
|
107
|
-
},
|
|
108
|
-
...rest,
|
|
109
|
-
children
|
|
110
|
-
});
|
|
55
|
+
const components = useMemo(() => ({
|
|
56
|
+
p: (props) => /* @__PURE__ */ jsx("p", {
|
|
57
|
+
className: "my-1.5 first:mt-0 last:mb-0",
|
|
58
|
+
...props
|
|
59
|
+
}),
|
|
60
|
+
ul: (props) => /* @__PURE__ */ jsx("ul", {
|
|
61
|
+
className: "my-1.5 list-disc pl-5",
|
|
62
|
+
...props
|
|
63
|
+
}),
|
|
64
|
+
ol: (props) => /* @__PURE__ */ jsx("ol", {
|
|
65
|
+
className: "my-1.5 list-decimal pl-5",
|
|
66
|
+
...props
|
|
67
|
+
}),
|
|
68
|
+
li: (props) => /* @__PURE__ */ jsx("li", {
|
|
69
|
+
className: "my-0.5",
|
|
70
|
+
...props
|
|
71
|
+
}),
|
|
72
|
+
h1: (props) => /* @__PURE__ */ jsx("h1", {
|
|
73
|
+
className: "mt-3 mb-1.5 text-[17px] font-semibold",
|
|
74
|
+
...props
|
|
75
|
+
}),
|
|
76
|
+
h2: (props) => /* @__PURE__ */ jsx("h2", {
|
|
77
|
+
className: "mt-3 mb-1.5 text-[16px] font-semibold",
|
|
78
|
+
...props
|
|
79
|
+
}),
|
|
80
|
+
h3: (props) => /* @__PURE__ */ jsx("h3", {
|
|
81
|
+
className: "mt-3 mb-1.5 text-[15px] font-semibold",
|
|
82
|
+
...props
|
|
83
|
+
}),
|
|
84
|
+
a: ({ style, ...props }) => /* @__PURE__ */ jsx("a", {
|
|
85
|
+
className: "underline underline-offset-2",
|
|
86
|
+
style: {
|
|
87
|
+
color: theme.primary,
|
|
88
|
+
...style
|
|
111
89
|
},
|
|
112
|
-
|
|
113
|
-
|
|
90
|
+
target: "_blank",
|
|
91
|
+
rel: "noreferrer noopener",
|
|
92
|
+
...props
|
|
93
|
+
}),
|
|
94
|
+
code: ({ className, style, children, ...rest }) => {
|
|
95
|
+
return /language-/.test(className ?? "") ? /* @__PURE__ */ jsx("code", {
|
|
96
|
+
className: "font-aai-mono text-[12.5px]",
|
|
97
|
+
...rest,
|
|
98
|
+
children
|
|
99
|
+
}) : /* @__PURE__ */ jsx("code", {
|
|
100
|
+
className: "rounded-sm border px-1 py-0.5 font-aai-mono text-[12.5px]",
|
|
114
101
|
style: {
|
|
115
102
|
borderColor: theme.border,
|
|
116
103
|
background: SURFACE_TINT,
|
|
117
104
|
...style
|
|
118
105
|
},
|
|
119
|
-
...
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
className: "my-1.5 border-l-2 pl-3",
|
|
123
|
-
style: {
|
|
124
|
-
borderColor: theme.border,
|
|
125
|
-
color: TEXT_MUTED,
|
|
126
|
-
...style
|
|
127
|
-
},
|
|
128
|
-
...props
|
|
129
|
-
}),
|
|
130
|
-
table: (props) => /* @__PURE__ */ jsx("table", {
|
|
131
|
-
className: "my-1.5 w-full border-collapse text-sm",
|
|
132
|
-
...props
|
|
133
|
-
}),
|
|
134
|
-
th: ({ style, ...props }) => /* @__PURE__ */ jsx("th", {
|
|
135
|
-
className: "border px-2 py-1 text-left font-medium",
|
|
136
|
-
style: {
|
|
137
|
-
borderColor: theme.border,
|
|
138
|
-
...style
|
|
139
|
-
},
|
|
140
|
-
...props
|
|
141
|
-
}),
|
|
142
|
-
td: ({ style, ...props }) => /* @__PURE__ */ jsx("td", {
|
|
143
|
-
className: "border px-2 py-1",
|
|
144
|
-
style: {
|
|
145
|
-
borderColor: theme.border,
|
|
146
|
-
...style
|
|
147
|
-
},
|
|
148
|
-
...props
|
|
149
|
-
}),
|
|
150
|
-
hr: ({ style, ...props }) => /* @__PURE__ */ jsx("hr", {
|
|
151
|
-
className: "my-2.5 border-t",
|
|
152
|
-
style: {
|
|
153
|
-
borderColor: theme.border,
|
|
154
|
-
...style
|
|
155
|
-
},
|
|
156
|
-
...props
|
|
157
|
-
})
|
|
106
|
+
...rest,
|
|
107
|
+
children
|
|
108
|
+
});
|
|
158
109
|
},
|
|
110
|
+
pre: ({ style, ...props }) => /* @__PURE__ */ jsx("pre", {
|
|
111
|
+
className: "my-1.5 overflow-x-auto rounded-md border p-2.5 whitespace-pre-wrap wrap-break-word",
|
|
112
|
+
style: {
|
|
113
|
+
borderColor: theme.border,
|
|
114
|
+
background: SURFACE_TINT,
|
|
115
|
+
...style
|
|
116
|
+
},
|
|
117
|
+
...props
|
|
118
|
+
}),
|
|
119
|
+
blockquote: ({ style, ...props }) => /* @__PURE__ */ jsx("blockquote", {
|
|
120
|
+
className: "my-1.5 border-l-2 pl-3",
|
|
121
|
+
style: {
|
|
122
|
+
borderColor: theme.border,
|
|
123
|
+
color: TEXT_MUTED,
|
|
124
|
+
...style
|
|
125
|
+
},
|
|
126
|
+
...props
|
|
127
|
+
}),
|
|
128
|
+
table: (props) => /* @__PURE__ */ jsx("table", {
|
|
129
|
+
className: "my-1.5 w-full border-collapse text-sm",
|
|
130
|
+
...props
|
|
131
|
+
}),
|
|
132
|
+
th: ({ style, ...props }) => /* @__PURE__ */ jsx("th", {
|
|
133
|
+
className: "border px-2 py-1 text-left font-medium",
|
|
134
|
+
style: {
|
|
135
|
+
borderColor: theme.border,
|
|
136
|
+
...style
|
|
137
|
+
},
|
|
138
|
+
...props
|
|
139
|
+
}),
|
|
140
|
+
td: ({ style, ...props }) => /* @__PURE__ */ jsx("td", {
|
|
141
|
+
className: "border px-2 py-1",
|
|
142
|
+
style: {
|
|
143
|
+
borderColor: theme.border,
|
|
144
|
+
...style
|
|
145
|
+
},
|
|
146
|
+
...props
|
|
147
|
+
}),
|
|
148
|
+
hr: ({ style, ...props }) => /* @__PURE__ */ jsx("hr", {
|
|
149
|
+
className: "my-2.5 border-t",
|
|
150
|
+
style: {
|
|
151
|
+
borderColor: theme.border,
|
|
152
|
+
...style
|
|
153
|
+
},
|
|
154
|
+
...props
|
|
155
|
+
})
|
|
156
|
+
}), [theme]);
|
|
157
|
+
return /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
158
|
+
remarkPlugins: [remarkGfm],
|
|
159
|
+
components,
|
|
159
160
|
children: escapeBareListMarkers(text)
|
|
160
161
|
});
|
|
161
162
|
});
|
|
@@ -342,6 +343,10 @@ const MessageList = memo(function MessageList({ className }) {
|
|
|
342
343
|
messages
|
|
343
344
|
]);
|
|
344
345
|
const { anchorRef, onScroll } = useAutoScroll(contentVersion, userTranscript !== null);
|
|
346
|
+
const streamingMessage = useMemo(() => agentTranscript ? {
|
|
347
|
+
role: "assistant",
|
|
348
|
+
content: agentTranscript
|
|
349
|
+
} : null, [agentTranscript]);
|
|
345
350
|
const items = useMemo(() => interleave(messages, toolCalls, (msg) => /* @__PURE__ */ jsx(MessageBubble, {
|
|
346
351
|
message: msg,
|
|
347
352
|
theme
|
|
@@ -359,11 +364,8 @@ const MessageList = memo(function MessageList({ className }) {
|
|
|
359
364
|
className: "flex flex-col gap-4 p-7",
|
|
360
365
|
children: [
|
|
361
366
|
items,
|
|
362
|
-
|
|
363
|
-
message:
|
|
364
|
-
role: "assistant",
|
|
365
|
-
content: agentTranscript
|
|
366
|
-
},
|
|
367
|
+
streamingMessage && /* @__PURE__ */ jsx(MessageBubble, {
|
|
368
|
+
message: streamingMessage,
|
|
367
369
|
theme
|
|
368
370
|
}),
|
|
369
371
|
userTranscript !== null && /* @__PURE__ */ jsx(UserBubble, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MIC_SEND_MAX_BUFFERED_BYTES } from "./types.js";
|
|
2
2
|
import { CLIENT_CONFIG_PATH, ClientConfigResponseSchema, ServerMessageSchema, lenientParse } from "@alexkroman1/aai/protocol";
|
|
3
|
-
import { DEFAULT_MAX_HISTORY, WS_OPEN, createEpoch, errorMessage, safeJsonParse } from "@alexkroman1/aai";
|
|
3
|
+
import { DEFAULT_MAX_HISTORY, WS_OPEN, createEpoch, errorMessage, safeJsonParse, toArgsRecord } from "@alexkroman1/aai";
|
|
4
4
|
import ReconnectingWebSocket from "partysocket/ws";
|
|
5
5
|
//#region client-config.ts
|
|
6
6
|
/**
|
|
@@ -32,8 +32,9 @@ const AGENT_DEFAULT = {};
|
|
|
32
32
|
* server) — a durable fact worth latching. A lookup that FAILED means
|
|
33
33
|
* nothing about the server, and treating the two alike is how a single 503
|
|
34
34
|
* (a sandbox mid-boot, or one that failed to start) pinned a session to the
|
|
35
|
-
* platform's `/:slug/websocket` —
|
|
36
|
-
* no re-brokering even after the agent
|
|
35
|
+
* platform's `/:slug/websocket` — a WebSocket redirect browsers don't
|
|
36
|
+
* follow, so every retry failed with no re-brokering even after the agent
|
|
37
|
+
* recovered.
|
|
37
38
|
*/
|
|
38
39
|
async function loadClientConfig(platformUrl, fetchFn) {
|
|
39
40
|
const doFetch = fetchFn ?? ((input, init) => globalThis.fetch(input, init));
|
|
@@ -60,6 +61,24 @@ async function fetchClientConfig(platformUrl, fetchFn) {
|
|
|
60
61
|
* staleness/failure handling), while `session-core.ts` owns the state store
|
|
61
62
|
* and connection lifecycle.
|
|
62
63
|
*/
|
|
64
|
+
let audioModulesPromise = null;
|
|
65
|
+
/**
|
|
66
|
+
* Load the audio module and worklet sources, memoized so a prefetch at
|
|
67
|
+
* `connect()` time overlaps the chunk fetch with the WebSocket handshake
|
|
68
|
+
* instead of serializing it behind the server's `config` frame. A failed
|
|
69
|
+
* load clears the memo so the next attempt retries the import.
|
|
70
|
+
*/
|
|
71
|
+
function loadAudioModules() {
|
|
72
|
+
audioModulesPromise ??= Promise.all([
|
|
73
|
+
import("./audio.js"),
|
|
74
|
+
import("./worklets/capture-processor.js").then((m) => m.default),
|
|
75
|
+
import("./worklets/playback-processor.js").then((m) => m.default)
|
|
76
|
+
]).catch((err) => {
|
|
77
|
+
audioModulesPromise = null;
|
|
78
|
+
throw err;
|
|
79
|
+
});
|
|
80
|
+
return audioModulesPromise;
|
|
81
|
+
}
|
|
63
82
|
/**
|
|
64
83
|
* Initialize audio capture and playback after the server sends a ready config.
|
|
65
84
|
*
|
|
@@ -93,11 +112,7 @@ async function initAudioCapture(conn, msg, deps) {
|
|
|
93
112
|
});
|
|
94
113
|
};
|
|
95
114
|
try {
|
|
96
|
-
const [{ createVoiceIO }, captureWorklet, playbackWorklet] = await
|
|
97
|
-
import("./audio.js"),
|
|
98
|
-
import("./worklets/capture-processor.js").then((m) => m.default),
|
|
99
|
-
import("./worklets/playback-processor.js").then((m) => m.default)
|
|
100
|
-
]);
|
|
115
|
+
const [{ createVoiceIO }, captureWorklet, playbackWorklet] = await loadAudioModules();
|
|
101
116
|
const io = await createVoiceIO({
|
|
102
117
|
sttSampleRate: msg.sampleRate,
|
|
103
118
|
ttsSampleRate: msg.ttsSampleRate,
|
|
@@ -105,17 +120,11 @@ async function initAudioCapture(conn, msg, deps) {
|
|
|
105
120
|
playbackWorkletSrc: playbackWorklet,
|
|
106
121
|
onMicData: (pcm16) => {
|
|
107
122
|
try {
|
|
108
|
-
deps.sendAudio(
|
|
123
|
+
deps.sendAudio(pcm16);
|
|
109
124
|
} catch {
|
|
110
125
|
console.debug("[aai-ui] sendAudio dropped: connection closed");
|
|
111
126
|
}
|
|
112
127
|
},
|
|
113
|
-
onPlaybackStats: (stats) => {
|
|
114
|
-
console.warn("[aai-ui] playback concealed a gap in this turn", stats);
|
|
115
|
-
},
|
|
116
|
-
onMicSilent: () => {
|
|
117
|
-
console.warn("[aai-ui] microphone is delivering only silence — check the selected input device");
|
|
118
|
-
},
|
|
119
128
|
onError: (err) => {
|
|
120
129
|
if (!conn.generation.isCurrent(gen)) return;
|
|
121
130
|
reportAudioFailure(err.message);
|
|
@@ -128,7 +137,7 @@ async function initAudioCapture(conn, msg, deps) {
|
|
|
128
137
|
conn.voiceIO?.close().catch(() => {});
|
|
129
138
|
conn.voiceIO = io;
|
|
130
139
|
if (conn.preInitAudio.length > 0) {
|
|
131
|
-
for (const chunk of conn.preInitAudio) io.enqueue(chunk
|
|
140
|
+
for (const chunk of conn.preInitAudio) io.enqueue(chunk);
|
|
132
141
|
conn.preInitAudio = [];
|
|
133
142
|
}
|
|
134
143
|
deps.sendJson({ type: "audio_ready" });
|
|
@@ -304,7 +313,7 @@ function createMessageHandlers(deps) {
|
|
|
304
313
|
updateState({ toolCalls: appendCapped(getSnapshot().toolCalls, {
|
|
305
314
|
callId: e.toolCallId,
|
|
306
315
|
name: e.toolName,
|
|
307
|
-
args: e.args
|
|
316
|
+
args: toArgsRecord(e.args),
|
|
308
317
|
status: "pending",
|
|
309
318
|
seq: ++toolCallSeq,
|
|
310
319
|
afterMessageId: getSnapshot().messages.at(-1)?.id ?? -1
|
|
@@ -363,7 +372,7 @@ function createMessageHandlers(deps) {
|
|
|
363
372
|
const snap = getSnapshot();
|
|
364
373
|
if (snap.state === "error" || snap.state === "disconnected" && snap.error !== null) return;
|
|
365
374
|
if (snap.state !== "speaking") updateState({ state: "speaking" });
|
|
366
|
-
if (conn.voiceIO) conn.voiceIO.enqueue(chunk
|
|
375
|
+
if (conn.voiceIO) conn.voiceIO.enqueue(chunk);
|
|
367
376
|
else if (conn.preInitAudio.length < MAX_PREINIT_AUDIO_CHUNKS) conn.preInitAudio.push(chunk);
|
|
368
377
|
}
|
|
369
378
|
/** See {@link MessageHandlers.settleWhenAudioDrained}. Captures
|
|
@@ -393,7 +402,7 @@ function createMessageHandlers(deps) {
|
|
|
393
402
|
}
|
|
394
403
|
function handleMessage(data) {
|
|
395
404
|
if (data instanceof ArrayBuffer) {
|
|
396
|
-
playAudioChunk(
|
|
405
|
+
playAudioChunk(data);
|
|
397
406
|
return;
|
|
398
407
|
}
|
|
399
408
|
if (typeof data !== "string") {
|
|
@@ -468,7 +477,7 @@ function reconnectPending(socket) {
|
|
|
468
477
|
//#region session-core-url.ts
|
|
469
478
|
/** Build the session WebSocket URL from the platform URL and resume state. */
|
|
470
479
|
function buildWsUrl(platformUrl, resume, sessionId) {
|
|
471
|
-
return applyResumeParams(
|
|
480
|
+
return applyResumeParams(buildAgentUrl(platformUrl, "websocket"), resume, sessionId);
|
|
472
481
|
}
|
|
473
482
|
/**
|
|
474
483
|
* Turn a broker-provided session URL (`sessionUrl` from `GET client-config`
|
|
@@ -658,11 +667,7 @@ function createSessionCore(options) {
|
|
|
658
667
|
async function currentWsUrl() {
|
|
659
668
|
const cfg = serverIsBroker === false ? null : await loadClientConfig(options.platformUrl);
|
|
660
669
|
if (cfg) serverIsBroker = cfg.sessionUrl !== void 0;
|
|
661
|
-
|
|
662
|
-
const display = new URL(url);
|
|
663
|
-
display.search = "";
|
|
664
|
-
if (display.toString() !== currentSnapshot.apiUrl) updateState({ apiUrl: display.toString() });
|
|
665
|
-
return url.toString();
|
|
670
|
+
return (cfg?.sessionUrl ? buildBrokeredWsUrl(cfg.sessionUrl, hasConnected, sessionId) : buildWsUrl(options.platformUrl, hasConnected, sessionId)).toString();
|
|
666
671
|
}
|
|
667
672
|
/** Open a socket: an injected constructor as-is (tests — connects to the
|
|
668
673
|
* same-origin path, no brokering), or partysocket's reconnecting
|
|
@@ -680,6 +685,7 @@ function createSessionCore(options) {
|
|
|
680
685
|
state: "connecting",
|
|
681
686
|
error: null
|
|
682
687
|
});
|
|
688
|
+
loadAudioModules().catch(() => {});
|
|
683
689
|
teardownConnection();
|
|
684
690
|
conn.generation.bump();
|
|
685
691
|
const controller = new AbortController();
|
|
@@ -4,7 +4,7 @@ import type { ConnState, SessionSnapshot } from "./session-core-types.ts";
|
|
|
4
4
|
/** Dependencies `initAudioCapture` needs from the owning session core. */
|
|
5
5
|
export type AudioSetupDeps = {
|
|
6
6
|
sendJson: (msg: ClientMessage) => void;
|
|
7
|
-
sendAudio: (bytes:
|
|
7
|
+
sendAudio: (bytes: ArrayBuffer) => void;
|
|
8
8
|
updateState: (partial: Partial<SessionSnapshot>) => void;
|
|
9
9
|
/** Turn-boundary-guarded drain from the message handlers — replays a
|
|
10
10
|
* buffered `audio_done` without stomping a barge-in's state. */
|
|
@@ -12,6 +12,14 @@ export type AudioSetupDeps = {
|
|
|
12
12
|
/** Release the mic/VoiceIO (used when the audio path dies non-fatally). */
|
|
13
13
|
cleanupAudio: () => void;
|
|
14
14
|
};
|
|
15
|
+
type AudioModules = [typeof import("./audio.ts"), string, string];
|
|
16
|
+
/**
|
|
17
|
+
* Load the audio module and worklet sources, memoized so a prefetch at
|
|
18
|
+
* `connect()` time overlaps the chunk fetch with the WebSocket handshake
|
|
19
|
+
* instead of serializing it behind the server's `config` frame. A failed
|
|
20
|
+
* load clears the memo so the next attempt retries the import.
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadAudioModules(): Promise<AudioModules>;
|
|
15
23
|
/**
|
|
16
24
|
* Initialize audio capture and playback after the server sends a ready config.
|
|
17
25
|
*
|
|
@@ -31,3 +39,4 @@ export declare function initAudioCapture(conn: ConnState, msg: {
|
|
|
31
39
|
sampleRate: number;
|
|
32
40
|
ttsSampleRate: number;
|
|
33
41
|
}, deps: AudioSetupDeps): Promise<void>;
|
|
42
|
+
export {};
|
|
@@ -29,9 +29,13 @@ export type SessionSnapshot = {
|
|
|
29
29
|
/** True while the microphone is live and streaming to the server. */
|
|
30
30
|
readonly recording: boolean;
|
|
31
31
|
/**
|
|
32
|
-
* The WebSocket URL a program can connect to directly
|
|
33
|
-
*
|
|
34
|
-
* `platformUrl` at construction — available before connecting
|
|
32
|
+
* The WebSocket URL a program can connect to directly — the long-living
|
|
33
|
+
* platform endpoint, e.g. `wss://host/my-agent/websocket`. Derived from
|
|
34
|
+
* `platformUrl` at construction — available before connecting — and never
|
|
35
|
+
* replaced by the brokered sandbox tunnel URL the session may actually be
|
|
36
|
+
* connected to: that URL is ephemeral (it dies when the sandbox is
|
|
37
|
+
* replaced), while the platform endpoint is stable and upgrades
|
|
38
|
+
* programmatic clients to the current sandbox endpoint itself.
|
|
35
39
|
*/
|
|
36
40
|
readonly apiUrl: string;
|
|
37
41
|
/**
|
|
@@ -112,7 +116,7 @@ export type ConnState = {
|
|
|
112
116
|
* the playback worklet once init completes. Closes the race between the
|
|
113
117
|
* server starting greeting audio (immediately on S2S connect) and the
|
|
114
118
|
* client awaiting mic permission + worklet registration. */
|
|
115
|
-
preInitAudio:
|
|
119
|
+
preInitAudio: ArrayBuffer[];
|
|
116
120
|
/** True if `audio_done` arrived before `voiceIO` was initialized. The done
|
|
117
121
|
* signal must be replayed after draining preInitAudio, or a short greeting
|
|
118
122
|
* buffered during mic-permission never finishes playing. */
|
package/dist/session-core.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createSessionCore } from "./session-core-
|
|
1
|
+
import { t as createSessionCore } from "./session-core-Bvga6tUS.js";
|
|
2
2
|
export { createSessionCore };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useTheme } from "./context.js";
|
|
2
|
-
import { r as TEXT_FAINT } from "./_colors-
|
|
2
|
+
import { r as TEXT_FAINT } from "./_colors-Bfh3-BVE.js";
|
|
3
3
|
import { t as Eyebrow } from "./eyebrow-C6ZFuiz6.js";
|
|
4
|
-
import { n as
|
|
4
|
+
import { n as truncate, r as tryParseJSON } from "./_utils-CsqbIVGI.js";
|
|
5
5
|
import clsx from "clsx";
|
|
6
6
|
import { createContext, memo, useContext, useMemo, useState } from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexkroman1/aai-ui",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"react-markdown": "^10.1.0",
|
|
23
23
|
"remark-gfm": "^4.0.1",
|
|
24
24
|
"use-sync-external-store": "^1.6.0",
|
|
25
|
-
"@alexkroman1/aai": "5.
|
|
25
|
+
"@alexkroman1/aai": "5.1.1"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react": "^19.0.0",
|