@pagelines/sdk 1.0.822 → 1.0.824

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.
@@ -6,19 +6,32 @@ export interface VoiceLevelEvent {
6
6
  /** Speech-spectrum energies, low pitch first. */
7
7
  bands?: readonly number[];
8
8
  }
9
+ /** One speech/noise boundary shared by audible UI state and work-cue ducking. */
10
+ export declare function isVoiceAudible(level: number): boolean;
9
11
  export interface VoiceSessionError {
10
12
  code: string;
11
13
  message: string;
12
14
  }
13
15
  export type VoiceDisconnectCause = 'agent' | 'error' | 'network' | 'unknown';
16
+ /**
17
+ * Who the transport says is talking. Raw energy is not a speech signal — room
18
+ * tone and the speaker's own playback both read as audible — so ducking,
19
+ * tone, and which meter the surface follows come from here.
20
+ */
21
+ export interface VoiceSpeakingState {
22
+ user: boolean;
23
+ agent: boolean;
24
+ }
14
25
  export interface VoiceProviderClient {
15
26
  connect: (handlers: {
16
27
  onDisconnected: (cause: VoiceDisconnectCause) => void;
17
28
  onError: (error: VoiceSessionError) => void;
18
29
  onThinkingChange?: (thinking: boolean) => void;
30
+ onSpeakingChange?: (state: VoiceSpeakingState) => void;
19
31
  }) => Promise<void>;
20
32
  disconnect: () => Promise<void>;
21
- sampleLevel: () => VoiceLevelEvent | undefined;
33
+ /** Loudest source by default; `prefer` pins a meter while someone is known to be speaking. */
34
+ sampleLevel: (prefer?: VoiceLevelEvent['source']) => VoiceLevelEvent | undefined;
22
35
  /** Self-mute without ending the live session. */
23
36
  setMuted?: (muted: boolean) => Promise<void>;
24
37
  /**
@@ -1,5 +1,5 @@
1
1
  import { VoiceSessionMint } from '@pagelines/core';
2
- import { VoiceProviderClient } from '../../provider';
2
+ import { VoiceLevelEvent, VoiceProviderClient } from '../../provider';
3
3
  type LiveKitMint = Extract<VoiceSessionMint, {
4
4
  provider: 'livekit';
5
5
  }>;
@@ -40,6 +40,10 @@ export declare class LiveKitVoiceProvider implements VoiceProviderClient {
40
40
  private levelSources;
41
41
  private attachedElements;
42
42
  private closing;
43
+ private userSpeaking;
44
+ private agentActiveSpeaker;
45
+ private agentStateSpeaking;
46
+ private lastSpeaking?;
43
47
  constructor(mint: LiveKitMint, deps?: {
44
48
  createRoom?: () => VoiceRoom;
45
49
  createAnalyser?: (track: unknown) => VoiceLevelAnalyser;
@@ -47,7 +51,8 @@ export declare class LiveKitVoiceProvider implements VoiceProviderClient {
47
51
  connect(handlers: Parameters<VoiceProviderClient['connect']>[0]): Promise<void>;
48
52
  disconnect(): Promise<void>;
49
53
  setMuted(muted: boolean): Promise<void>;
50
- sampleLevel(): ReturnType<VoiceProviderClient['sampleLevel']>;
54
+ sampleLevel(prefer?: VoiceLevelEvent['source']): ReturnType<VoiceProviderClient['sampleLevel']>;
55
+ private publishSpeaking;
51
56
  private waitForAgentReady;
52
57
  private analyserFor;
53
58
  private watchMicLevel;
@@ -1,8 +1,8 @@
1
1
  import { Ref } from 'vue';
2
- import { CreateVoiceProviderClient, VoiceLevelEvent, VoiceSessionError } from './provider';
2
+ import { CreateVoiceProviderClient, VoiceLevelEvent, VoiceSessionError, VoiceSpeakingState } from './provider';
3
3
  import { SettingsObject } from '@pagelines/core';
4
- export type { VoiceLevelEvent, VoiceSessionError } from './provider';
5
- export { VOICE_SPECTRUM_BANDS, voiceSpectrum } from './provider';
4
+ export type { VoiceLevelEvent, VoiceSessionError, VoiceSpeakingState } from './provider';
5
+ export { isVoiceAudible, VOICE_SPECTRUM_BANDS, voiceSpectrum } from './provider';
6
6
  export type VoiceSessionClientState = 'idle' | 'minting' | 'connecting' | 'live' | 'ended';
7
7
  export type VoiceChimeKind = 'start' | 'end';
8
8
  export interface VoiceWorkStatus {
@@ -20,6 +20,7 @@ export interface VoiceClientHandlers {
20
20
  onLevel?: (event: VoiceLevelEvent) => void;
21
21
  onError?: (error: VoiceSessionError) => void;
22
22
  onThinkingChange?: (thinking: boolean) => void;
23
+ onSpeakingChange?: (state: VoiceSpeakingState) => void;
23
24
  /** Fire-and-forget latency phases for product observability. */
24
25
  onMetric?: (metric: VoiceMetric) => void;
25
26
  onWorkChange?: (work: VoiceWorkStatus) => void;
@@ -28,6 +29,8 @@ export interface VoiceSessionClientSettings extends VoiceClientHandlers, Record<
28
29
  agentId: string;
29
30
  orgId: string;
30
31
  conversationId?: string;
32
+ /** Last message already loaded by the host; the work stream starts after it. */
33
+ latestSequence?: string;
31
34
  apiBase?: string;
32
35
  isDev?: boolean;
33
36
  token?: string;
@@ -48,8 +51,8 @@ export declare class VoiceSessionClient extends SettingsObject<VoiceSessionClien
48
51
  readonly state: Ref<VoiceSessionClientState>;
49
52
  readonly isMuted: Ref<boolean>;
50
53
  private provider?;
51
- private readonly chime;
52
- private readonly workCue;
54
+ private chime?;
55
+ private workCue?;
53
56
  private generation;
54
57
  private levelTimer;
55
58
  private startAtMs;
@@ -60,7 +63,7 @@ export declare class VoiceSessionClient extends SettingsObject<VoiceSessionClien
60
63
  private workAbort?;
61
64
  private workState;
62
65
  private workCuePlaying;
63
- private audible;
66
+ private speaking;
64
67
  constructor(settings: VoiceSessionClientSettings);
65
68
  start(): Promise<void>;
66
69
  end(): Promise<void>;
@@ -77,8 +80,11 @@ export declare class VoiceSessionClient extends SettingsObject<VoiceSessionClien
77
80
  private startLevelTimer;
78
81
  private authHeaders;
79
82
  private startWorkStream;
83
+ private setSpeaking;
84
+ /** Duck on speech, not on energy: room tone kept the loop from ever starting. */
80
85
  private updateWorkCue;
81
86
  private setWorkCue;
87
+ private playChime;
82
88
  private post;
83
89
  private mintSession;
84
90
  private toSessionError;
package/dist/agent.js CHANGED
@@ -2,17 +2,17 @@ import "./rolldown-runtime.js";
2
2
  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
- import { t as d } from "./provider.js";
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";
5
+ import { n as d, t as f } from "./provider.js";
6
+ import { SettingsObject as p, readChatEventStream as m, zVoiceSessionMint as h } from "@pagelines/core";
7
+ import { Fragment as g, createBlock as _, createCommentVNode as v, createElementBlock as y, createElementVNode as b, createVNode as x, defineComponent as S, nextTick as C, normalizeClass as w, normalizeStyle as T, openBlock as E, ref as D, renderList as O, watch as k, withCtx as A } from "vue";
8
8
  //#region agent/ui/AgentWidget.vue?vue&type=script&setup=true&lang.ts
9
- var A = {
9
+ var j = {
10
10
  key: 0,
11
11
  class: "relative h-full w-full"
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 = {
12
+ }, M = { 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" }, N = ["src", "alt"], P = {
13
13
  key: 0,
14
14
  class: "absolute top-0.5 right-0.5"
15
- }, P = /* @__PURE__ */ x({
15
+ }, F = /* @__PURE__ */ S({
16
16
  __name: "AgentWidget",
17
17
  props: {
18
18
  sdk: {},
@@ -22,12 +22,12 @@ var A = {
22
22
  position: {}
23
23
  },
24
24
  setup(e, { expose: t }) {
25
- let n = e, r = E(!1), i = E(!1);
25
+ let n = e, r = D(!1), i = D(!1);
26
26
  function toggle() {
27
27
  r.value = !r.value;
28
28
  }
29
- O(r, async (e) => {
30
- e ? (await S(), setTimeout(() => {
29
+ k(r, async (e) => {
30
+ e ? (await C(), setTimeout(() => {
31
31
  i.value = !0;
32
32
  }, 400)) : i.value = !1;
33
33
  });
@@ -44,29 +44,29 @@ var A = {
44
44
  toggle,
45
45
  open,
46
46
  close
47
- }), (t, a) => (T(), g(c, {
47
+ }), (t, a) => (E(), _(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: k(({ agent: t, sdk: o }) => [t ? (T(), v("div", {
53
+ default: A(({ agent: t, sdk: o }) => [t ? (E(), y("div", {
54
54
  key: 0,
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" }),
55
+ class: w(["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
+ }, [b("div", {
57
+ class: w(["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: T({ "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 ? _("", !0) : (T(), v("div", A, [y("div", j, [y("img", {
60
+ }, [r.value ? v("", !0) : (E(), y("div", j, [b("div", M, [b("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, 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", {
66
+ }, null, 8, N), a[1] || (a[1] = b("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" ? (E(), y("div", P, [...a[2] || (a[2] = [b("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), y("div", { class: "size-3 bg-green-500 rounded-full ring-2 ring-white" }, null, -1)])])) : _("", !0)])), r.value && t ? (T(), g(l, {
69
+ }, null, -1), b("div", { class: "size-3 bg-green-500 rounded-full ring-2 ring-white" }, null, -1)])])) : v("", !0)])), r.value && t ? (E(), _(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 A = {
82
82
  "context",
83
83
  "first-message",
84
84
  "is-active"
85
- ])) : _("", !0)], 6)], 2)) : _("", !0)]),
85
+ ])) : v("", !0)], 6)], 2)) : v("", !0)]),
86
86
  _: 1
87
87
  }, 8, [
88
88
  "sdk",
@@ -91,25 +91,25 @@ var A = {
91
91
  "first-message"
92
92
  ]));
93
93
  }
94
- }), F = ["data-sender"], I = /* @__PURE__ */ x({
94
+ }), I = ["data-sender"], L = /* @__PURE__ */ S({
95
95
  __name: "ElAgentChatPreview",
96
96
  props: {
97
97
  messages: {},
98
98
  variant: {}
99
99
  },
100
100
  setup(e) {
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"]),
101
+ return (t, r) => (E(), y("div", {
102
+ class: w(["@container/chat-preview flex flex-col gap-3", e.variant === "dark" ? "text-white" : "text-slate-950"]),
103
103
  "data-test": "agent-chat-preview"
104
- }, [(T(!0), v(h, null, D(e.messages, (t, r) => (T(), v("div", {
104
+ }, [(E(!0), y(g, null, O(e.messages, (t, r) => (E(), y("div", {
105
105
  key: `${t.sender}-${r}`,
106
- class: C(["flex", t.sender === "user" ? "justify-end" : "justify-start"]),
106
+ class: w(["flex", t.sender === "user" ? "justify-end" : "justify-start"]),
107
107
  "data-test": "agent-chat-preview-message",
108
108
  "data-sender": t.sender
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, {
109
+ }, [b("div", { class: w(["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"]) }, [x(n, {
110
110
  text: t.text,
111
111
  inverted: t.sender === "user" || e.variant === "dark"
112
- }, null, 8, ["text", "inverted"])], 2)], 10, F))), 128))], 2));
112
+ }, null, 8, ["text", "inverted"])], 2)], 10, I))), 128))], 2));
113
113
  }
114
114
  });
115
115
  //#endregion
@@ -151,14 +151,14 @@ function reduceVoiceWorkEvent(e, t) {
151
151
  }
152
152
  //#endregion
153
153
  //#region agent/voice/voice-session-client.ts
154
- var L = {
154
+ var R = {
155
155
  start: "/audio/start-call.mp3",
156
156
  end: "/audio/end-call.mp3"
157
- }, R = .7, z = 50, B = 15e3;
157
+ }, z = .7, B = 50, V = 15e3;
158
158
  function createBrowserVoiceChimePlayer(e) {
159
- let t = Object.fromEntries(Object.entries(L).map(([t, n]) => {
159
+ let t = Object.fromEntries(Object.entries(R).map(([t, n]) => {
160
160
  let r = new Audio(`${e}${n}`);
161
- return r.preload = "auto", r.volume = R, [t, r];
161
+ return r.preload = "auto", r.volume = z, [t, r];
162
162
  }));
163
163
  return (e) => {
164
164
  t[e].currentTime = 0, t[e].play().catch(() => {});
@@ -174,9 +174,12 @@ function createBrowserVoiceWorkCue(e) {
174
174
  t.pause(), t.currentTime = 0;
175
175
  };
176
176
  }
177
- var V = .08, H = .08, VoiceSessionClient = class extends f {
178
- constructor(n) {
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)) : () => {});
177
+ var VoiceSessionClient = class extends p {
178
+ constructor(e) {
179
+ super("VoiceSessionClient", e), t(this, "state", D("idle")), t(this, "isMuted", D(!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, "speaking", {
180
+ user: !1,
181
+ agent: !1
182
+ }), this.chime = e.playChime, this.workCue = e.setWorkCue;
180
183
  }
181
184
  async start() {
182
185
  if (this.state.value !== "idle") return;
@@ -197,10 +200,10 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
197
200
  async end() {
198
201
  if (this.state.value === "idle" || this.state.value === "ended") return;
199
202
  let e = this.state.value === "live";
200
- this.generation++, e && this.chime("end"), this.setState("ended"), await this.teardown();
203
+ this.generation++, e && this.playChime("end"), this.setState("ended"), await this.teardown();
201
204
  }
202
205
  async connect(e) {
203
- let { mint: t, generation: n } = e, r = await (this.settings.createProvider ?? d)(t);
206
+ let { mint: t, generation: n } = e, r = await (this.settings.createProvider ?? f)(t);
204
207
  if (n !== this.generation) {
205
208
  await r.disconnect().catch(() => {});
206
209
  return;
@@ -210,7 +213,7 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
210
213
  onDisconnected: (e) => {
211
214
  if (!(n !== this.generation || this.provider !== r)) {
212
215
  if (this.state.value === "live" && e === "agent") {
213
- this.generation++, this.chime("end"), this.setState("ended"), this.teardown();
216
+ this.generation++, this.playChime("end"), this.setState("ended"), this.teardown();
214
217
  return;
215
218
  }
216
219
  this.fail({
@@ -224,11 +227,14 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
224
227
  },
225
228
  onThinkingChange: (e) => {
226
229
  n === this.generation && this.provider === r && this.settings.onThinkingChange?.(e);
230
+ },
231
+ onSpeakingChange: (e) => {
232
+ n === this.generation && this.provider === r && this.setSpeaking(e);
227
233
  }
228
234
  }), a;
229
235
  try {
230
236
  await Promise.race([i, new Promise((e, t) => {
231
- a = setTimeout(() => t(Object.assign(/* @__PURE__ */ Error("Voice connection timed out."), { code: "VOICE_CONNECT_TIMEOUT" })), B);
237
+ a = setTimeout(() => t(Object.assign(/* @__PURE__ */ Error("Voice connection timed out."), { code: "VOICE_CONNECT_TIMEOUT" })), V);
232
238
  })]);
233
239
  } catch (e) {
234
240
  if (i.then(() => r.disconnect()).catch(() => {}), n !== this.generation || this.provider !== r) return;
@@ -237,7 +243,7 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
237
243
  } finally {
238
244
  a && clearTimeout(a);
239
245
  }
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"));
246
+ 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.playChime("start"));
241
247
  }
242
248
  get canSendUserMessage() {
243
249
  return this.state.value === "live" && !!this.provider?.sendUserMessage;
@@ -252,7 +258,10 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
252
258
  let t = this.generation, n = !this.isMuted.value;
253
259
  this.mutePending = !0;
254
260
  try {
255
- return await e.setMuted(n), t !== this.generation || this.provider !== e || this.state.value !== "live" ? void 0 : (this.isMuted.value = n, n);
261
+ return await e.setMuted(n), t !== this.generation || this.provider !== e || this.state.value !== "live" ? void 0 : (this.isMuted.value = n, n && this.speaking.user && this.setSpeaking({
262
+ ...this.speaking,
263
+ user: !1
264
+ }), n);
256
265
  } catch {
257
266
  return;
258
267
  } finally {
@@ -270,12 +279,15 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
270
279
  }
271
280
  fail(e) {
272
281
  let t = this.state.value === "live";
273
- this.generation++, t && this.chime("end"), this.settings.onError?.(e), this.setState("ended"), this.teardown();
282
+ this.generation++, t && this.playChime("end"), this.settings.onError?.(e), this.setState("ended"), this.teardown();
274
283
  }
275
284
  async teardown() {
276
285
  this.workAbort?.abort(), this.workAbort = void 0;
277
286
  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);
287
+ this.workState = createVoiceWorkState(), e && this.settings.onWorkChange?.({ active: !1 }), this.setSpeaking({
288
+ user: !1,
289
+ agent: !1
290
+ }), this.settings.onThinkingChange?.(!1), this.setWorkCue(!1), this.levelTimer !== void 0 && (clearInterval(this.levelTimer), this.levelTimer = void 0);
279
291
  let t = this.provider;
280
292
  this.provider = void 0, this.isMuted.value = !1, this.settings.onLevel?.({
281
293
  source: "playback",
@@ -285,21 +297,20 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
285
297
  startLevelTimer(e, t) {
286
298
  this.levelTimer === void 0 && (this.levelTimer = setInterval(() => {
287
299
  if (t !== this.generation || this.provider !== e) return;
288
- let n = e.sampleLevel();
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));
300
+ let n = e.sampleLevel(this.speaking.agent ? "playback" : this.speaking.user ? "mic" : void 0);
301
+ n && (this.settings.onLevel?.(n), !this.firstPlaybackEmitted && n.source === "playback" && d(n.level) && this.liveAtMs > 0 && (this.firstPlaybackEmitted = !0, this.emitMetric("voice.first_playback", Date.now() - this.liveAtMs)));
302
+ }, B));
291
303
  }
292
304
  authHeaders() {
293
305
  return this.settings.token ? { Authorization: `Bearer ${this.settings.token}` } : {};
294
306
  }
295
307
  startWorkStream(t, n) {
296
- if (!this.settings.onWorkChange) return;
297
308
  this.workAbort?.abort();
298
309
  let r = new AbortController();
299
310
  this.workAbort = r;
300
- let i = e(this.settings.apiBase, this.settings.isDev), a = this.settings.fetchFn ?? fetch;
311
+ let i = e(this.settings.apiBase, this.settings.isDev), a = this.settings.fetchFn ?? fetch, o = this.settings.latestSequence ? `${i}/api/conversations/${encodeURIComponent(t)}/stream?since=${encodeURIComponent(this.settings.latestSequence)}` : `${i}/api/conversations/${encodeURIComponent(t)}/stream`;
301
312
  (async () => {
302
- let e = await a(`${i}/api/conversations/${encodeURIComponent(t)}/stream`, {
313
+ let e = await a(o, {
303
314
  headers: {
304
315
  Accept: "text/event-stream",
305
316
  ...this.authHeaders()
@@ -307,7 +318,7 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
307
318
  credentials: "include",
308
319
  signal: r.signal
309
320
  });
310
- if (!(!e.ok || !e.body)) for await (let t of p(e.body.getReader())) {
321
+ if (!(!e.ok || !e.body)) for await (let t of m(e.body.getReader())) {
311
322
  if (r.signal.aborted || n !== this.generation) return;
312
323
  let e = this.workState, i = reduceVoiceWorkEvent(e, t);
313
324
  i !== e && (this.workState = i, (i.activeTurnId !== e.activeTurnId || i.description !== e.description) && (this.settings.onWorkChange?.({
@@ -317,11 +328,21 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
317
328
  }
318
329
  })().catch(() => {});
319
330
  }
331
+ setSpeaking(e) {
332
+ let t = {
333
+ user: e.user && !this.isMuted.value,
334
+ agent: e.agent
335
+ };
336
+ t.user === this.speaking.user && t.agent === this.speaking.agent || (this.speaking = t, this.settings.onSpeakingChange?.(t), this.updateWorkCue());
337
+ }
320
338
  updateWorkCue() {
321
- this.setWorkCue(this.state.value === "live" && !!this.workState.activeTurnId && !this.audible);
339
+ this.setWorkCue(this.state.value === "live" && !!this.workState.activeTurnId && !this.speaking.user && !this.speaking.agent);
340
+ }
341
+ setWorkCue(t) {
342
+ this.workCuePlaying !== t && (this.workCuePlaying = t, this.workCue ?? (this.workCue = createBrowserVoiceWorkCue(e(this.settings.apiBase, this.settings.isDev))), this.workCue(t));
322
343
  }
323
- setWorkCue(e) {
324
- this.workCuePlaying !== e && (this.workCuePlaying = e, this.workCue(e));
344
+ playChime(t) {
345
+ this.chime ?? (this.chime = createBrowserVoiceChimePlayer(e(this.settings.apiBase, this.settings.isDev))), this.chime(t);
325
346
  }
326
347
  async post(t) {
327
348
  let n = e(this.settings.apiBase, this.settings.isDev), r = await (this.settings.fetchFn ?? fetch)(`${n}${t.path}`, {
@@ -356,7 +377,7 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
356
377
  body: { conversationId: t }
357
378
  });
358
379
  if (e !== this.generation) return null;
359
- let r = m.safeParse(n);
380
+ let r = h.safeParse(n);
360
381
  if (!r.success) throw Object.assign(/* @__PURE__ */ Error("Voice session mint returned an unexpected shape."), { code: "VOICE_MINT_INVALID" });
361
382
  return this.conversationId = t, r.data;
362
383
  }
@@ -371,6 +392,6 @@ var V = .08, H = .08, VoiceSessionClient = class extends f {
371
392
  }
372
393
  };
373
394
  //#endregion
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 };
395
+ export { i as AGENT_MODES, r as Agent, l as AgentChat, o as AgentChatController, o as AgentController, u as AgentProvider, F as AgentWidget, c as AgentWrap, a as ElAgentChat, L as ElAgentChatPreview, s as VoiceMode, VoiceSessionClient };
375
396
 
376
397
  //# sourceMappingURL=agent.js.map