@pagelines/sdk 1.0.820 → 1.0.822

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.
@@ -4,6 +4,13 @@ type __VLS_Props = {
4
4
  agent: Agent;
5
5
  chatController?: AgentChatController;
6
6
  isLight?: boolean;
7
+ /**
8
+ * The avatar to fill the background with, already decoded by the host
9
+ * (`resolveVoiceBackdrop`). The whole surface enters as one root, so the
10
+ * backdrop is decided before presentation: absent means the dark fallback
11
+ * for this call, never an image that pops in over the slide-up.
12
+ */
13
+ backdropSrc?: string;
7
14
  };
8
15
  /** Await provider teardown before the host reloads the persisted transcript. */
9
16
  declare function end(): Promise<void>;
@@ -10,10 +10,12 @@ export interface VoiceSessionError {
10
10
  code: string;
11
11
  message: string;
12
12
  }
13
+ export type VoiceDisconnectCause = 'agent' | 'error' | 'network' | 'unknown';
13
14
  export interface VoiceProviderClient {
14
15
  connect: (handlers: {
15
- onDisconnected: () => void;
16
+ onDisconnected: (cause: VoiceDisconnectCause) => void;
16
17
  onError: (error: VoiceSessionError) => void;
18
+ onThinkingChange?: (thinking: boolean) => void;
17
19
  }) => Promise<void>;
18
20
  disconnect: () => Promise<void>;
19
21
  sampleLevel: () => VoiceLevelEvent | undefined;
@@ -27,7 +27,9 @@ type StartSession = (options: {
27
27
  voiceId?: string;
28
28
  };
29
29
  };
30
- onDisconnect: () => void;
30
+ onDisconnect: (details: {
31
+ reason: 'agent' | 'error' | 'user';
32
+ }) => void;
31
33
  onError: (message: string) => void;
32
34
  }) => Promise<ElevenLabsConversation>;
33
35
  export declare class ElevenLabsVoiceProvider implements VoiceProviderClient {
@@ -4,7 +4,11 @@ import { SettingsObject } from '@pagelines/core';
4
4
  export type { VoiceLevelEvent, VoiceSessionError } from './provider';
5
5
  export { VOICE_SPECTRUM_BANDS, voiceSpectrum } from './provider';
6
6
  export type VoiceSessionClientState = 'idle' | 'minting' | 'connecting' | 'live' | 'ended';
7
- export type VoiceChimeKind = 'start' | 'engaged' | 'end';
7
+ export type VoiceChimeKind = 'start' | 'end';
8
+ export interface VoiceWorkStatus {
9
+ active: boolean;
10
+ description?: string;
11
+ }
8
12
  /** Client-owned latency phases — speech-end-to-first-text is measured on the handler. */
9
13
  export type VoiceMetricName = 'voice.ready' | 'voice.first_playback';
10
14
  export interface VoiceMetric {
@@ -15,8 +19,10 @@ export interface VoiceClientHandlers {
15
19
  onStateChange?: (state: VoiceSessionClientState) => void;
16
20
  onLevel?: (event: VoiceLevelEvent) => void;
17
21
  onError?: (error: VoiceSessionError) => void;
22
+ onThinkingChange?: (thinking: boolean) => void;
18
23
  /** Fire-and-forget latency phases for product observability. */
19
24
  onMetric?: (metric: VoiceMetric) => void;
25
+ onWorkChange?: (work: VoiceWorkStatus) => void;
20
26
  }
21
27
  export interface VoiceSessionClientSettings extends VoiceClientHandlers, Record<string, unknown> {
22
28
  agentId: string;
@@ -29,6 +35,8 @@ export interface VoiceSessionClientSettings extends VoiceClientHandlers, Record<
29
35
  /** Provider composition seam; the default dynamically loads the minted provider. */
30
36
  createProvider?: CreateVoiceProviderClient;
31
37
  playChime?: (kind: VoiceChimeKind) => void;
38
+ /** Test seam; production uses the quiet looping work asset. */
39
+ setWorkCue?: (active: boolean) => void;
32
40
  }
33
41
  /**
34
42
  * The readiness cue is the only signal an eyes-off user gets, so it is served
@@ -41,13 +49,18 @@ export declare class VoiceSessionClient extends SettingsObject<VoiceSessionClien
41
49
  readonly isMuted: Ref<boolean>;
42
50
  private provider?;
43
51
  private readonly chime;
52
+ private readonly workCue;
44
53
  private generation;
45
54
  private levelTimer;
46
55
  private startAtMs;
47
56
  private liveAtMs;
48
57
  private firstPlaybackEmitted;
49
- private engagedChimed;
50
58
  private mutePending;
59
+ private conversationId?;
60
+ private workAbort?;
61
+ private workState;
62
+ private workCuePlaying;
63
+ private audible;
51
64
  constructor(settings: VoiceSessionClientSettings);
52
65
  start(): Promise<void>;
53
66
  end(): Promise<void>;
@@ -63,6 +76,9 @@ export declare class VoiceSessionClient extends SettingsObject<VoiceSessionClien
63
76
  private teardown;
64
77
  private startLevelTimer;
65
78
  private authHeaders;
79
+ private startWorkStream;
80
+ private updateWorkCue;
81
+ private setWorkCue;
66
82
  private post;
67
83
  private mintSession;
68
84
  private toSessionError;
@@ -0,0 +1,9 @@
1
+ import { ChatEvent } from '@pagelines/core';
2
+ export interface VoiceWorkState {
3
+ turnIds: ReadonlySet<string>;
4
+ activeTurnId?: string;
5
+ description?: string;
6
+ }
7
+ export declare function createVoiceWorkState(): VoiceWorkState;
8
+ /** Project canonical conversation events onto the one active voice work line. */
9
+ export declare function reduceVoiceWorkEvent(state: VoiceWorkState, event: ChatEvent): VoiceWorkState;
package/dist/agent.js CHANGED
@@ -3,16 +3,16 @@ import { c as e, s as t } from "./sdkClient.js";
3
3
  import { a as n, h as r, m as i, n as a, p as o, r as s, t as c } from "./AgentWrap.js";
4
4
  import { n as l, t as u } from "./AgentProvider.js";
5
5
  import { t as d } from "./provider.js";
6
- import { SettingsObject as f, zVoiceSessionMint as p } from "@pagelines/core";
7
- import { Fragment as m, createBlock as h, createCommentVNode as g, createElementBlock as _, createElementVNode as v, createVNode as y, defineComponent as b, nextTick as x, normalizeClass as S, normalizeStyle as C, openBlock as w, ref as T, renderList as E, watch as D, withCtx as O } from "vue";
6
+ import { SettingsObject as f, readChatEventStream as p, zVoiceSessionMint as m } from "@pagelines/core";
7
+ import { Fragment as h, createBlock as g, createCommentVNode as _, createElementBlock as v, createElementVNode as y, createVNode as b, defineComponent as x, nextTick as S, normalizeClass as C, normalizeStyle as w, openBlock as T, ref as E, renderList as D, watch as O, withCtx as k } from "vue";
8
8
  //#region agent/ui/AgentWidget.vue?vue&type=script&setup=true&lang.ts
9
- var k = {
9
+ var A = {
10
10
  key: 0,
11
11
  class: "relative h-full w-full"
12
- }, A = { class: "group relative h-full w-full rounded-3xl overflow-hidden shadow-lg transition-all duration-300 ease-[cubic-bezier(0.25,1,0.33,1)] hover:shadow-2xl hover:translate-y-[-1px] hover:brightness-110 active:scale-[1.06] active:-translate-y-0.5 active:duration-100" }, j = ["src", "alt"], M = {
12
+ }, j = { class: "group relative h-full w-full rounded-3xl overflow-hidden shadow-lg transition-all duration-300 ease-[cubic-bezier(0.25,1,0.33,1)] hover:shadow-2xl hover:translate-y-[-1px] hover:brightness-110 active:scale-[1.06] active:-translate-y-0.5 active:duration-100" }, M = ["src", "alt"], N = {
13
13
  key: 0,
14
14
  class: "absolute top-0.5 right-0.5"
15
- }, N = /* @__PURE__ */ b({
15
+ }, P = /* @__PURE__ */ x({
16
16
  __name: "AgentWidget",
17
17
  props: {
18
18
  sdk: {},
@@ -22,12 +22,12 @@ var k = {
22
22
  position: {}
23
23
  },
24
24
  setup(e, { expose: t }) {
25
- let n = e, r = T(!1), i = T(!1);
25
+ let n = e, r = E(!1), i = E(!1);
26
26
  function toggle() {
27
27
  r.value = !r.value;
28
28
  }
29
- D(r, async (e) => {
30
- e ? (await x(), setTimeout(() => {
29
+ O(r, async (e) => {
30
+ e ? (await S(), setTimeout(() => {
31
31
  i.value = !0;
32
32
  }, 400)) : i.value = !1;
33
33
  });
@@ -44,29 +44,29 @@ var k = {
44
44
  toggle,
45
45
  open,
46
46
  close
47
- }), (t, a) => (w(), h(c, {
47
+ }), (t, a) => (T(), g(c, {
48
48
  sdk: e.sdk,
49
49
  handle: e.handle,
50
50
  context: e.context,
51
51
  "first-message": e.firstMessage
52
52
  }, {
53
- default: O(({ agent: t, sdk: o }) => [t ? (w(), _("div", {
53
+ default: k(({ agent: t, sdk: o }) => [t ? (T(), v("div", {
54
54
  key: 0,
55
- class: S(["fixed z-[999999] pointer-events-auto", [n.position === "bottom-left" ? "bottom-6 left-6" : n.position === "top-right" ? "top-6 right-6" : n.position === "top-left" ? "top-6 left-6" : "bottom-6 right-6"]])
56
- }, [v("div", {
57
- class: S(["transition-all duration-500 ease-[cubic-bezier(0.25,1,0.33,1)] transform-gpu", [r.value ? "widget-expanded w-96 h-[600px] max-w-[calc(100vw-48px)] max-h-[calc(100vh-48px)] rounded-3xl opacity-100 scale-100" : "widget-button w-[80px] h-[80px] opacity-100 scale-100 flex items-center justify-center cursor-pointer"]]),
58
- style: C({ "transform-origin": n.position === "bottom-left" ? "bottom left" : n.position === "top-right" ? "top right" : n.position === "top-left" ? "top left" : "bottom right" }),
55
+ class: C(["fixed z-[999999] pointer-events-auto", [n.position === "bottom-left" ? "bottom-6 left-6" : n.position === "top-right" ? "top-6 right-6" : n.position === "top-left" ? "top-6 left-6" : "bottom-6 right-6"]])
56
+ }, [y("div", {
57
+ class: C(["transition-all duration-500 ease-[cubic-bezier(0.25,1,0.33,1)] transform-gpu", [r.value ? "widget-expanded w-96 h-[600px] max-w-[calc(100vw-48px)] max-h-[calc(100vh-48px)] rounded-3xl opacity-100 scale-100" : "widget-button w-[80px] h-[80px] opacity-100 scale-100 flex items-center justify-center cursor-pointer"]]),
58
+ style: w({ "transform-origin": n.position === "bottom-left" ? "bottom left" : n.position === "top-right" ? "top right" : n.position === "top-left" ? "top left" : "bottom right" }),
59
59
  onClick: a[0] || (a[0] = (e) => r.value ? void 0 : toggle())
60
- }, [r.value ? g("", !0) : (w(), _("div", k, [v("div", A, [v("img", {
60
+ }, [r.value ? _("", !0) : (T(), v("div", A, [y("div", j, [y("img", {
61
61
  src: t.avatarUrl.value,
62
62
  alt: `${t.displayName.value} assistant`,
63
63
  class: "h-full w-full object-cover pointer-events-none",
64
64
  width: "80",
65
65
  height: "80"
66
- }, null, 8, j), a[1] || (a[1] = v("div", { class: "absolute inset-0 pointer-events-none bg-gradient-to-br from-transparent via-black/10 to-black/40 opacity-60 transition-opacity duration-300 group-hover:opacity-40" }, null, -1))]), t.state.value.lifecycle === "running" ? (w(), _("div", M, [...a[2] || (a[2] = [v("div", {
66
+ }, null, 8, M), a[1] || (a[1] = y("div", { class: "absolute inset-0 pointer-events-none bg-gradient-to-br from-transparent via-black/10 to-black/40 opacity-60 transition-opacity duration-300 group-hover:opacity-40" }, null, -1))]), t.state.value.lifecycle === "running" ? (T(), v("div", N, [...a[2] || (a[2] = [y("div", {
67
67
  class: "size-3 bg-green-500 rounded-full ring-2 ring-white absolute animate-ping",
68
68
  style: { "animation-duration": "3s" }
69
- }, null, -1), v("div", { class: "size-3 bg-green-500 rounded-full ring-2 ring-white" }, null, -1)])])) : g("", !0)])), r.value && t ? (w(), h(l, {
69
+ }, null, -1), y("div", { class: "size-3 bg-green-500 rounded-full ring-2 ring-white" }, null, -1)])])) : _("", !0)])), r.value && t ? (T(), g(l, {
70
70
  key: 1,
71
71
  class: "rounded-3xl shadow-2xl border border-white/10",
72
72
  sdk: o,
@@ -82,7 +82,7 @@ var k = {
82
82
  "context",
83
83
  "first-message",
84
84
  "is-active"
85
- ])) : g("", !0)], 6)], 2)) : g("", !0)]),
85
+ ])) : _("", !0)], 6)], 2)) : _("", !0)]),
86
86
  _: 1
87
87
  }, 8, [
88
88
  "sdk",
@@ -91,52 +91,101 @@ var k = {
91
91
  "first-message"
92
92
  ]));
93
93
  }
94
- }), P = ["data-sender"], F = /* @__PURE__ */ b({
94
+ }), F = ["data-sender"], I = /* @__PURE__ */ x({
95
95
  __name: "ElAgentChatPreview",
96
96
  props: {
97
97
  messages: {},
98
98
  variant: {}
99
99
  },
100
100
  setup(e) {
101
- return (t, r) => (w(), _("div", {
102
- class: S(["@container/chat-preview flex flex-col gap-3", e.variant === "dark" ? "text-white" : "text-slate-950"]),
101
+ return (t, r) => (T(), v("div", {
102
+ class: C(["@container/chat-preview flex flex-col gap-3", e.variant === "dark" ? "text-white" : "text-slate-950"]),
103
103
  "data-test": "agent-chat-preview"
104
- }, [(w(!0), _(m, null, E(e.messages, (t, r) => (w(), _("div", {
104
+ }, [(T(!0), v(h, null, D(e.messages, (t, r) => (T(), v("div", {
105
105
  key: `${t.sender}-${r}`,
106
- class: S(["flex", t.sender === "user" ? "justify-end" : "justify-start"]),
106
+ class: C(["flex", t.sender === "user" ? "justify-end" : "justify-start"]),
107
107
  "data-test": "agent-chat-preview-message",
108
108
  "data-sender": t.sender
109
- }, [v("div", { class: S(["max-w-[88%] rounded-2xl px-4 py-3 text-sm shadow-sm @sm/chat-preview:max-w-[78%]", t.sender === "user" ? "bg-slate-950 text-white" : e.variant === "dark" ? "border border-white/10 bg-white/10 text-white" : "border border-slate-200 bg-white text-slate-950"]) }, [y(n, {
109
+ }, [y("div", { class: C(["max-w-[88%] rounded-2xl px-4 py-3 text-sm shadow-sm @sm/chat-preview:max-w-[78%]", t.sender === "user" ? "bg-slate-950 text-white" : e.variant === "dark" ? "border border-white/10 bg-white/10 text-white" : "border border-slate-200 bg-white text-slate-950"]) }, [b(n, {
110
110
  text: t.text,
111
111
  inverted: t.sender === "user" || e.variant === "dark"
112
- }, null, 8, ["text", "inverted"])], 2)], 10, P))), 128))], 2));
112
+ }, null, 8, ["text", "inverted"])], 2)], 10, F))), 128))], 2));
113
113
  }
114
- }), I = {
114
+ });
115
+ //#endregion
116
+ //#region agent/voice/voice-work-state.ts
117
+ function createVoiceWorkState() {
118
+ return { turnIds: /* @__PURE__ */ new Set() };
119
+ }
120
+ function reduceVoiceWorkEvent(e, t) {
121
+ if (t.event === "message") {
122
+ let n = t.data.message;
123
+ if (n.role === "user" && n.metadata?.clientContext === "voice") return {
124
+ ...e,
125
+ turnIds: new Set(e.turnIds).add(n.messageId)
126
+ };
127
+ let r = typeof n.metadata?.turnId == "string" ? n.metadata.turnId : void 0;
128
+ if (r && e.turnIds.has(r)) {
129
+ let t = new Set(e.turnIds);
130
+ return t.delete(r), e.activeTurnId === r ? { turnIds: t } : {
131
+ ...e,
132
+ turnIds: t
133
+ };
134
+ }
135
+ return e;
136
+ }
137
+ let n = "turnId" in t.data && typeof t.data.turnId == "string" ? t.data.turnId : void 0;
138
+ return !n || !e.turnIds.has(n) ? e : t.event === "working_state" ? {
139
+ ...e,
140
+ activeTurnId: n,
141
+ description: t.data.description.trim() || "Working on it"
142
+ } : t.event === "tool_activity" && t.data.status === "started" ? {
143
+ ...e,
144
+ activeTurnId: n,
145
+ description: e.activeTurnId === n && e.description ? e.description : t.data.label
146
+ } : t.event === "working_end" || t.event === "stream_end" ? {
147
+ ...e,
148
+ activeTurnId: void 0,
149
+ description: void 0
150
+ } : e;
151
+ }
152
+ //#endregion
153
+ //#region agent/voice/voice-session-client.ts
154
+ var L = {
115
155
  start: "/audio/start-call.mp3",
116
- engaged: "/audio/engaged-call.mp3",
117
156
  end: "/audio/end-call.mp3"
118
- }, L = .7, R = 50, z = 15e3;
157
+ }, R = .7, z = 50, B = 15e3;
119
158
  function createBrowserVoiceChimePlayer(e) {
120
- let t = Object.fromEntries(Object.entries(I).map(([t, n]) => {
159
+ let t = Object.fromEntries(Object.entries(L).map(([t, n]) => {
121
160
  let r = new Audio(`${e}${n}`);
122
- return r.preload = "auto", r.volume = L, [t, r];
161
+ return r.preload = "auto", r.volume = R, [t, r];
123
162
  }));
124
163
  return (e) => {
125
164
  t[e].currentTime = 0, t[e].play().catch(() => {});
126
165
  };
127
166
  }
128
- var B = .08, V = .12, VoiceSessionClient = class extends f {
167
+ function createBrowserVoiceWorkCue(e) {
168
+ let t = new Audio(`${e}/audio/thinking.mp3`);
169
+ return t.preload = "auto", t.loop = !0, t.volume = .16, (e) => {
170
+ if (e) {
171
+ t.play().catch(() => {});
172
+ return;
173
+ }
174
+ t.pause(), t.currentTime = 0;
175
+ };
176
+ }
177
+ var V = .08, H = .08, VoiceSessionClient = class extends f {
129
178
  constructor(n) {
130
- super("VoiceSessionClient", n), t(this, "state", T("idle")), t(this, "isMuted", T(!1)), t(this, "provider", void 0), t(this, "chime", void 0), t(this, "generation", 0), t(this, "levelTimer", void 0), t(this, "startAtMs", 0), t(this, "liveAtMs", 0), t(this, "firstPlaybackEmitted", !1), t(this, "engagedChimed", !1), t(this, "mutePending", !1), this.chime = n.playChime ?? createBrowserVoiceChimePlayer(e(n.apiBase, n.isDev));
179
+ super("VoiceSessionClient", n), t(this, "state", E("idle")), t(this, "isMuted", E(!1)), t(this, "provider", void 0), t(this, "chime", void 0), t(this, "workCue", void 0), t(this, "generation", 0), t(this, "levelTimer", void 0), t(this, "startAtMs", 0), t(this, "liveAtMs", 0), t(this, "firstPlaybackEmitted", !1), t(this, "mutePending", !1), t(this, "conversationId", void 0), t(this, "workAbort", void 0), t(this, "workState", createVoiceWorkState()), t(this, "workCuePlaying", !1), t(this, "audible", !1), this.chime = n.playChime ?? createBrowserVoiceChimePlayer(e(n.apiBase, n.isDev)), this.workCue = n.setWorkCue ?? (n.onWorkChange ? createBrowserVoiceWorkCue(e(n.apiBase, n.isDev)) : () => {});
131
180
  }
132
181
  async start() {
133
182
  if (this.state.value !== "idle") return;
134
183
  let e = ++this.generation;
135
- this.startAtMs = Date.now(), this.liveAtMs = 0, this.firstPlaybackEmitted = !1, this.engagedChimed = !1, this.setState("minting");
184
+ this.startAtMs = Date.now(), this.liveAtMs = 0, this.firstPlaybackEmitted = !1, this.workState = createVoiceWorkState(), this.setState("minting");
136
185
  try {
137
186
  let t = await this.mintSession(e);
138
187
  if (!t || e !== this.generation) return;
139
- this.setState("connecting"), await this.connect({
188
+ this.conversationId && this.startWorkStream(this.conversationId, e), this.setState("connecting"), await this.connect({
140
189
  mint: t,
141
190
  generation: e
142
191
  });
@@ -158,19 +207,28 @@ var B = .08, V = .12, VoiceSessionClient = class extends f {
158
207
  }
159
208
  this.provider = r;
160
209
  let i = r.connect({
161
- onDisconnected: () => {
162
- n !== this.generation || this.provider !== r || this.fail({
163
- code: this.state.value === "live" ? "VOICE_CONNECTION_LOST" : "VOICE_CONNECT_FAILED",
164
- message: this.state.value === "live" ? "The voice connection dropped." : "Could not open the voice connection."
165
- });
210
+ onDisconnected: (e) => {
211
+ if (!(n !== this.generation || this.provider !== r)) {
212
+ if (this.state.value === "live" && e === "agent") {
213
+ this.generation++, this.chime("end"), this.setState("ended"), this.teardown();
214
+ return;
215
+ }
216
+ this.fail({
217
+ code: this.state.value === "live" ? "VOICE_CONNECTION_LOST" : "VOICE_CONNECT_FAILED",
218
+ message: this.state.value === "live" ? "The voice connection dropped." : "Could not open the voice connection."
219
+ });
220
+ }
166
221
  },
167
222
  onError: (e) => {
168
223
  n === this.generation && this.provider === r && this.fail(e);
224
+ },
225
+ onThinkingChange: (e) => {
226
+ n === this.generation && this.provider === r && this.settings.onThinkingChange?.(e);
169
227
  }
170
228
  }), a;
171
229
  try {
172
230
  await Promise.race([i, new Promise((e, t) => {
173
- a = setTimeout(() => t(Object.assign(/* @__PURE__ */ Error("Voice connection timed out."), { code: "VOICE_CONNECT_TIMEOUT" })), z);
231
+ a = setTimeout(() => t(Object.assign(/* @__PURE__ */ Error("Voice connection timed out."), { code: "VOICE_CONNECT_TIMEOUT" })), B);
174
232
  })]);
175
233
  } catch (e) {
176
234
  if (i.then(() => r.disconnect()).catch(() => {}), n !== this.generation || this.provider !== r) return;
@@ -179,7 +237,7 @@ var B = .08, V = .12, VoiceSessionClient = class extends f {
179
237
  } finally {
180
238
  a && clearTimeout(a);
181
239
  }
182
- n !== this.generation || this.provider !== r || (this.startLevelTimer(r, n), this.liveAtMs = Date.now(), this.setState("live"), this.emitMetric("voice.ready", this.liveAtMs - this.startAtMs), this.chime("start"));
240
+ n !== this.generation || this.provider !== r || (this.startLevelTimer(r, n), this.liveAtMs = Date.now(), this.setState("live"), this.updateWorkCue(), this.emitMetric("voice.ready", this.liveAtMs - this.startAtMs), this.chime("start"));
183
241
  }
184
242
  get canSendUserMessage() {
185
243
  return this.state.value === "live" && !!this.provider?.sendUserMessage;
@@ -211,26 +269,60 @@ var B = .08, V = .12, VoiceSessionClient = class extends f {
211
269
  this.state.value !== e && (this.state.value = e, this.settings.onStateChange?.(e));
212
270
  }
213
271
  fail(e) {
214
- this.generation++, this.settings.onError?.(e), this.setState("ended"), this.teardown();
272
+ let t = this.state.value === "live";
273
+ this.generation++, t && this.chime("end"), this.settings.onError?.(e), this.setState("ended"), this.teardown();
215
274
  }
216
275
  async teardown() {
217
- this.levelTimer !== void 0 && (clearInterval(this.levelTimer), this.levelTimer = void 0);
218
- let e = this.provider;
276
+ this.workAbort?.abort(), this.workAbort = void 0;
277
+ let e = !!this.workState.activeTurnId;
278
+ this.workState = createVoiceWorkState(), e && this.settings.onWorkChange?.({ active: !1 }), this.audible = !1, this.settings.onThinkingChange?.(!1), this.setWorkCue(!1), this.levelTimer !== void 0 && (clearInterval(this.levelTimer), this.levelTimer = void 0);
279
+ let t = this.provider;
219
280
  this.provider = void 0, this.isMuted.value = !1, this.settings.onLevel?.({
220
281
  source: "playback",
221
282
  level: 0
222
- }), e && await e.disconnect().catch(() => {});
283
+ }), t && await t.disconnect().catch(() => {});
223
284
  }
224
285
  startLevelTimer(e, t) {
225
286
  this.levelTimer === void 0 && (this.levelTimer = setInterval(() => {
226
287
  if (t !== this.generation || this.provider !== e) return;
227
288
  let n = e.sampleLevel();
228
- n && (this.settings.onLevel?.(n), !this.engagedChimed && n.source === "mic" && n.level >= V && this.liveAtMs > 0 && (this.engagedChimed = !0, this.chime("engaged")), !this.firstPlaybackEmitted && n.source === "playback" && n.level >= B && this.liveAtMs > 0 && (this.firstPlaybackEmitted = !0, this.emitMetric("voice.first_playback", Date.now() - this.liveAtMs)));
229
- }, R));
289
+ n && (this.settings.onLevel?.(n), this.audible = n.level >= H, this.updateWorkCue(), !this.firstPlaybackEmitted && n.source === "playback" && n.level >= V && this.liveAtMs > 0 && (this.firstPlaybackEmitted = !0, this.emitMetric("voice.first_playback", Date.now() - this.liveAtMs)));
290
+ }, z));
230
291
  }
231
292
  authHeaders() {
232
293
  return this.settings.token ? { Authorization: `Bearer ${this.settings.token}` } : {};
233
294
  }
295
+ startWorkStream(t, n) {
296
+ if (!this.settings.onWorkChange) return;
297
+ this.workAbort?.abort();
298
+ let r = new AbortController();
299
+ this.workAbort = r;
300
+ let i = e(this.settings.apiBase, this.settings.isDev), a = this.settings.fetchFn ?? fetch;
301
+ (async () => {
302
+ let e = await a(`${i}/api/conversations/${encodeURIComponent(t)}/stream`, {
303
+ headers: {
304
+ Accept: "text/event-stream",
305
+ ...this.authHeaders()
306
+ },
307
+ credentials: "include",
308
+ signal: r.signal
309
+ });
310
+ if (!(!e.ok || !e.body)) for await (let t of p(e.body.getReader())) {
311
+ if (r.signal.aborted || n !== this.generation) return;
312
+ let e = this.workState, i = reduceVoiceWorkEvent(e, t);
313
+ i !== e && (this.workState = i, (i.activeTurnId !== e.activeTurnId || i.description !== e.description) && (this.settings.onWorkChange?.({
314
+ active: !!i.activeTurnId,
315
+ ...i.description ? { description: i.description } : {}
316
+ }), this.updateWorkCue()));
317
+ }
318
+ })().catch(() => {});
319
+ }
320
+ updateWorkCue() {
321
+ this.setWorkCue(this.state.value === "live" && !!this.workState.activeTurnId && !this.audible);
322
+ }
323
+ setWorkCue(e) {
324
+ this.workCuePlaying !== e && (this.workCuePlaying = e, this.workCue(e));
325
+ }
234
326
  async post(t) {
235
327
  let n = e(this.settings.apiBase, this.settings.isDev), r = await (this.settings.fetchFn ?? fetch)(`${n}${t.path}`, {
236
328
  method: "POST",
@@ -264,9 +356,9 @@ var B = .08, V = .12, VoiceSessionClient = class extends f {
264
356
  body: { conversationId: t }
265
357
  });
266
358
  if (e !== this.generation) return null;
267
- let r = p.safeParse(n);
359
+ let r = m.safeParse(n);
268
360
  if (!r.success) throw Object.assign(/* @__PURE__ */ Error("Voice session mint returned an unexpected shape."), { code: "VOICE_MINT_INVALID" });
269
- return r.data;
361
+ return this.conversationId = t, r.data;
270
362
  }
271
363
  toSessionError(e, t) {
272
364
  return e instanceof Error ? {
@@ -279,6 +371,6 @@ var B = .08, V = .12, VoiceSessionClient = class extends f {
279
371
  }
280
372
  };
281
373
  //#endregion
282
- export { i as AGENT_MODES, r as Agent, l as AgentChat, o as AgentChatController, o as AgentController, u as AgentProvider, N as AgentWidget, c as AgentWrap, a as ElAgentChat, F as ElAgentChatPreview, s as VoiceMode, VoiceSessionClient };
374
+ export { i as AGENT_MODES, r as Agent, l as AgentChat, o as AgentChatController, o as AgentController, u as AgentProvider, P as AgentWidget, c as AgentWrap, a as ElAgentChat, I as ElAgentChatPreview, s as VoiceMode, VoiceSessionClient };
283
375
 
284
376
  //# sourceMappingURL=agent.js.map