@alexkroman1/aai-ui 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/{_colors-DYX7XRTr.js → _colors-Bfh3-BVE.js} +3 -1
  2. package/dist/{_utils-CN1yVgYS.js → _utils-CsqbIVGI.js} +10 -1
  3. package/dist/_utils.d.ts +6 -0
  4. package/dist/{chat-view-CW-tZCMm.js → chat-view-C6N2oXjo.js} +4 -4
  5. package/dist/components/_colors.d.ts +2 -0
  6. package/dist/components/chat-view.js +1 -1
  7. package/dist/components/controls.js +1 -1
  8. package/dist/components/message-list.js +1 -1
  9. package/dist/components/start-screen.js +1 -1
  10. package/dist/components/tool-call-block.js +1 -1
  11. package/dist/components/url-chips.d.ts +2 -1
  12. package/dist/{controls-DV368uhb.js → controls-Cqg7T4FN.js} +5 -8
  13. package/dist/default-client/assets/{audio-BnLV_QB3.js → audio-C1U_foBG.js} +1 -1
  14. package/dist/default-client/assets/{capture-processor-ChXugkMd.js → capture-processor-dyyJArQs.js} +1 -1
  15. package/dist/default-client/assets/index-CGZajs1V.js +192 -0
  16. package/dist/default-client/assets/{playback-processor-BfJzQKW5.js → playback-processor-BO2TdYrv.js} +1 -1
  17. package/dist/default-client/index.html +1 -1
  18. package/dist/define-client.d.ts +6 -8
  19. package/dist/define-client.js +5 -4
  20. package/dist/hooks.js +1 -1
  21. package/dist/index.js +5 -5
  22. package/dist/{message-list-q2lTiFhF.js → message-list-CD9GxrlH.js} +106 -104
  23. package/dist/{session-core-CwCaFzL8.js → session-core-Bvga6tUS.js} +27 -18
  24. package/dist/session-core-audio-setup.d.ts +10 -1
  25. package/dist/session-core-types.d.ts +1 -1
  26. package/dist/session-core.js +1 -1
  27. package/dist/{tool-call-block-DIxpG8GM.js → tool-call-block-O9vLqoyU.js} +2 -2
  28. package/package.json +2 -2
  29. package/dist/default-client/assets/index-22Wylv4z.js +0 -192
@@ -1,4 +1,4 @@
1
- import{i as e}from"./index-22Wylv4z.js";import{t}from"./_module-url-BX0RuRU2.js";var n=t(`
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-22Wylv4z.js"></script>
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>
@@ -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, WebSocketConstructor } from "./types.ts";
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).
@@ -1,7 +1,8 @@
1
- import { r as fetchClientConfig, t as createSessionCore } from "./session-core-CwCaFzL8.js";
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-CW-tZCMm.js";
4
- import { n as ToolConfigContext } from "./tool-call-block-DIxpG8GM.js";
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 ?? globalThis.location.origin + globalThis.location.pathname;
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 { n as tryParseJSON } from "./_utils-CN1yVgYS.js";
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-CwCaFzL8.js";
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-CW-tZCMm.js";
5
- import { i as UiUrlChip, n as ApiUrlChip, r as SessionUrlChips, t as Controls } from "./controls-DV368uhb.js";
6
- import { n as Markdown, t as MessageList } from "./message-list-q2lTiFhF.js";
7
- import { n as ToolConfigContext } from "./tool-call-block-DIxpG8GM.js";
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 { a as primaryTint, i as TEXT_MUTED, n as SURFACE_TINT, r as TEXT_FAINT } from "./_colors-DYX7XRTr.js";
3
- import { t as ToolCallBlock } from "./tool-call-block-DIxpG8GM.js";
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
- return /* @__PURE__ */ jsx(ReactMarkdown, {
56
- remarkPlugins: [remarkGfm],
57
- components: {
58
- p: (props) => /* @__PURE__ */ jsx("p", {
59
- className: "my-1.5 first:mt-0 last:mb-0",
60
- ...props
61
- }),
62
- ul: (props) => /* @__PURE__ */ jsx("ul", {
63
- className: "my-1.5 list-disc pl-5",
64
- ...props
65
- }),
66
- ol: (props) => /* @__PURE__ */ jsx("ol", {
67
- className: "my-1.5 list-decimal pl-5",
68
- ...props
69
- }),
70
- li: (props) => /* @__PURE__ */ jsx("li", {
71
- className: "my-0.5",
72
- ...props
73
- }),
74
- h1: (props) => /* @__PURE__ */ jsx("h1", {
75
- className: "mt-3 mb-1.5 text-[17px] font-semibold",
76
- ...props
77
- }),
78
- h2: (props) => /* @__PURE__ */ jsx("h2", {
79
- className: "mt-3 mb-1.5 text-[16px] font-semibold",
80
- ...props
81
- }),
82
- h3: (props) => /* @__PURE__ */ jsx("h3", {
83
- className: "mt-3 mb-1.5 text-[15px] font-semibold",
84
- ...props
85
- }),
86
- a: ({ style, ...props }) => /* @__PURE__ */ jsx("a", {
87
- className: "underline underline-offset-2",
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
- pre: ({ style, ...props }) => /* @__PURE__ */ jsx("pre", {
113
- className: "my-1.5 overflow-x-auto rounded-md border p-2.5 whitespace-pre-wrap wrap-break-word",
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
- ...props
120
- }),
121
- blockquote: ({ style, ...props }) => /* @__PURE__ */ jsx("blockquote", {
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
- agentTranscript && /* @__PURE__ */ jsx(MessageBubble, {
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
  /**
@@ -61,6 +61,24 @@ async function fetchClientConfig(platformUrl, fetchFn) {
61
61
  * staleness/failure handling), while `session-core.ts` owns the state store
62
62
  * and connection lifecycle.
63
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
+ }
64
82
  /**
65
83
  * Initialize audio capture and playback after the server sends a ready config.
66
84
  *
@@ -94,11 +112,7 @@ async function initAudioCapture(conn, msg, deps) {
94
112
  });
95
113
  };
96
114
  try {
97
- const [{ createVoiceIO }, captureWorklet, playbackWorklet] = await Promise.all([
98
- import("./audio.js"),
99
- import("./worklets/capture-processor.js").then((m) => m.default),
100
- import("./worklets/playback-processor.js").then((m) => m.default)
101
- ]);
115
+ const [{ createVoiceIO }, captureWorklet, playbackWorklet] = await loadAudioModules();
102
116
  const io = await createVoiceIO({
103
117
  sttSampleRate: msg.sampleRate,
104
118
  ttsSampleRate: msg.ttsSampleRate,
@@ -106,17 +120,11 @@ async function initAudioCapture(conn, msg, deps) {
106
120
  playbackWorkletSrc: playbackWorklet,
107
121
  onMicData: (pcm16) => {
108
122
  try {
109
- deps.sendAudio(new Uint8Array(pcm16));
123
+ deps.sendAudio(pcm16);
110
124
  } catch {
111
125
  console.debug("[aai-ui] sendAudio dropped: connection closed");
112
126
  }
113
127
  },
114
- onPlaybackStats: (stats) => {
115
- console.warn("[aai-ui] playback concealed a gap in this turn", stats);
116
- },
117
- onMicSilent: () => {
118
- console.warn("[aai-ui] microphone is delivering only silence — check the selected input device");
119
- },
120
128
  onError: (err) => {
121
129
  if (!conn.generation.isCurrent(gen)) return;
122
130
  reportAudioFailure(err.message);
@@ -129,7 +137,7 @@ async function initAudioCapture(conn, msg, deps) {
129
137
  conn.voiceIO?.close().catch(() => {});
130
138
  conn.voiceIO = io;
131
139
  if (conn.preInitAudio.length > 0) {
132
- for (const chunk of conn.preInitAudio) io.enqueue(chunk.buffer);
140
+ for (const chunk of conn.preInitAudio) io.enqueue(chunk);
133
141
  conn.preInitAudio = [];
134
142
  }
135
143
  deps.sendJson({ type: "audio_ready" });
@@ -305,7 +313,7 @@ function createMessageHandlers(deps) {
305
313
  updateState({ toolCalls: appendCapped(getSnapshot().toolCalls, {
306
314
  callId: e.toolCallId,
307
315
  name: e.toolName,
308
- args: e.args ?? {},
316
+ args: toArgsRecord(e.args),
309
317
  status: "pending",
310
318
  seq: ++toolCallSeq,
311
319
  afterMessageId: getSnapshot().messages.at(-1)?.id ?? -1
@@ -364,7 +372,7 @@ function createMessageHandlers(deps) {
364
372
  const snap = getSnapshot();
365
373
  if (snap.state === "error" || snap.state === "disconnected" && snap.error !== null) return;
366
374
  if (snap.state !== "speaking") updateState({ state: "speaking" });
367
- if (conn.voiceIO) conn.voiceIO.enqueue(chunk.buffer);
375
+ if (conn.voiceIO) conn.voiceIO.enqueue(chunk);
368
376
  else if (conn.preInitAudio.length < MAX_PREINIT_AUDIO_CHUNKS) conn.preInitAudio.push(chunk);
369
377
  }
370
378
  /** See {@link MessageHandlers.settleWhenAudioDrained}. Captures
@@ -394,7 +402,7 @@ function createMessageHandlers(deps) {
394
402
  }
395
403
  function handleMessage(data) {
396
404
  if (data instanceof ArrayBuffer) {
397
- playAudioChunk(new Uint8Array(data));
405
+ playAudioChunk(data);
398
406
  return;
399
407
  }
400
408
  if (typeof data !== "string") {
@@ -469,7 +477,7 @@ function reconnectPending(socket) {
469
477
  //#region session-core-url.ts
470
478
  /** Build the session WebSocket URL from the platform URL and resume state. */
471
479
  function buildWsUrl(platformUrl, resume, sessionId) {
472
- return applyResumeParams(new URL("websocket", platformUrl.endsWith("/") ? platformUrl : `${platformUrl}/`), resume, sessionId);
480
+ return applyResumeParams(buildAgentUrl(platformUrl, "websocket"), resume, sessionId);
473
481
  }
474
482
  /**
475
483
  * Turn a broker-provided session URL (`sessionUrl` from `GET client-config`
@@ -677,6 +685,7 @@ function createSessionCore(options) {
677
685
  state: "connecting",
678
686
  error: null
679
687
  });
688
+ loadAudioModules().catch(() => {});
680
689
  teardownConnection();
681
690
  conn.generation.bump();
682
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: Uint8Array) => void;
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 {};
@@ -116,7 +116,7 @@ export type ConnState = {
116
116
  * the playback worklet once init completes. Closes the race between the
117
117
  * server starting greeting audio (immediately on S2S connect) and the
118
118
  * client awaiting mic permission + worklet registration. */
119
- preInitAudio: Uint8Array[];
119
+ preInitAudio: ArrayBuffer[];
120
120
  /** True if `audio_done` arrived before `voiceIO` was initialized. The done
121
121
  * signal must be replayed after draining preInitAudio, or a short greeting
122
122
  * buffered during mic-permission never finishes playing. */
@@ -1,2 +1,2 @@
1
- import { t as createSessionCore } from "./session-core-CwCaFzL8.js";
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-DYX7XRTr.js";
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 tryParseJSON, t as truncate } from "./_utils-CN1yVgYS.js";
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.1.0",
3
+ "version": "5.2.0",
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.1.0"
25
+ "@alexkroman1/aai": "5.2.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": "^19.0.0",