@voiceloop/web-sdk 0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/logger.ts","../src/audio.ts","../src/state-machine.ts","../src/session-state.ts","../src/client.ts"],"sourcesContent":["// Module-level singleton — works outside React, zero dependencies.\n\nexport type LogLevel = \"info\" | \"warn\" | \"error\"\n\nexport type LogEntry = {\n id: number\n ts: number\n level: LogLevel\n tag: string\n msg: string\n detail?: string\n}\n\nconst MAX_ENTRIES = 300\nlet seq = 0\nconst entries: LogEntry[] = []\nconst listeners = new Set<() => void>()\n\nfunction emit() {\n listeners.forEach(fn => fn())\n}\n\nexport function log(tag: string, msg: string, detail?: string, level: LogLevel = \"info\") {\n entries.push({ id: seq++, ts: Date.now(), level, tag, msg, detail })\n if (entries.length > MAX_ENTRIES) entries.splice(0, entries.length - MAX_ENTRIES)\n emit()\n}\n\nexport function warn(tag: string, msg: string, detail?: string) {\n log(tag, msg, detail, \"warn\")\n}\n\nexport function logError(tag: string, msg: string, detail?: string) {\n log(tag, msg, detail, \"error\")\n}\n\nexport function getEntries(): readonly LogEntry[] {\n return entries\n}\n\nexport function clearLog() {\n entries.length = 0\n seq = 0\n emit()\n}\n\nexport function subscribe(fn: () => void): () => void {\n listeners.add(fn)\n return () => listeners.delete(fn)\n}\n\nexport function formatForCopy(): string {\n return entries.map(e => {\n const d = new Date(e.ts)\n const ts = [\n d.getHours().toString().padStart(2, \"0\"),\n d.getMinutes().toString().padStart(2, \"0\"),\n d.getSeconds().toString().padStart(2, \"0\"),\n ].join(\":\") + \".\" + d.getMilliseconds().toString().padStart(3, \"0\")\n const detail = e.detail ? ` ${e.detail}` : \"\"\n return `[${ts}] ${e.tag.padEnd(8)} ${e.msg}${detail}`\n }).join(\"\\n\")\n}\n","// Audio capture and PCM playback utilities.\n\nexport const MIC_SAMPLE_RATE = 16_000 // server expects 16 kHz\nexport const TTS_SAMPLE_RATE = 24_000 // server sends 24 kHz\nconst SCRIPT_PROCESSOR_FRAMES = 1024 // ~64 ms @ 16 kHz\n\n// ── Microphone capture ────────────────────────────────────────────────────────\n\nexport type AudioCaptureHandle = {\n stop: () => void\n getVolume: () => number // 0–1\n}\n\nexport async function startMicCapture(\n onPcm: (chunk: ArrayBuffer) => void\n): Promise<AudioCaptureHandle> {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: {\n sampleRate: MIC_SAMPLE_RATE,\n channelCount: 1,\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n },\n })\n\n const ctx = new AudioContext({ sampleRate: MIC_SAMPLE_RATE })\n const source = ctx.createMediaStreamSource(stream)\n const processor = ctx.createScriptProcessor(SCRIPT_PROCESSOR_FRAMES, 1, 1)\n\n let lastVolume = 0\n\n processor.onaudioprocess = (e) => {\n const f32 = e.inputBuffer.getChannelData(0)\n\n // RMS volume for meter\n let sum = 0\n for (let i = 0; i < f32.length; i++) sum += f32[i] * f32[i]\n lastVolume = Math.min(1, Math.sqrt(sum / f32.length) * 10)\n\n // Float32 → Int16 LE\n const i16 = new Int16Array(f32.length)\n for (let i = 0; i < f32.length; i++) {\n i16[i] = Math.max(-32768, Math.min(32767, f32[i] * 32768))\n }\n onPcm(i16.buffer)\n }\n\n source.connect(processor)\n processor.connect(ctx.destination)\n\n return {\n stop() {\n processor.disconnect()\n source.disconnect()\n ctx.close()\n stream.getTracks().forEach((t) => t.stop())\n lastVolume = 0\n },\n getVolume: () => lastVolume,\n }\n}\n\n// ── TTS PCM playback ──────────────────────────────────────────────────────────\n\nexport type PcmPlayerHandle = {\n enqueue: (pcm: Uint8Array) => void\n flush: () => void // discard buffered audio immediately (barge-in)\n resume: () => void // call on button click to unblock browser autoplay policy\n}\n\nexport function createPcmPlayer(): PcmPlayerHandle {\n let ctx: AudioContext | null = null\n let nextPlayTime = 0\n\n function ensureCtx() {\n if (!ctx) ctx = new AudioContext({ sampleRate: TTS_SAMPLE_RATE })\n if (ctx.state === \"suspended\") ctx.resume()\n return ctx\n }\n\n return {\n resume() {\n ensureCtx()\n },\n\n enqueue(pcm: Uint8Array) {\n const ac = ensureCtx()\n const byteLen = pcm.byteLength & ~1\n if (byteLen === 0) return\n\n const buf = new ArrayBuffer(byteLen)\n new Uint8Array(buf).set(pcm.subarray(0, byteLen))\n\n const i16 = new Int16Array(buf)\n const f32 = new Float32Array(i16.length)\n for (let i = 0; i < i16.length; i++) f32[i] = i16[i] / 32768\n\n const audioBuf = ac.createBuffer(1, f32.length, TTS_SAMPLE_RATE)\n audioBuf.copyToChannel(f32, 0)\n\n const src = ac.createBufferSource()\n src.buffer = audioBuf\n src.connect(ac.destination)\n\n const startAt = Math.max(ac.currentTime, nextPlayTime)\n src.start(startAt)\n nextPlayTime = startAt + audioBuf.duration\n },\n\n flush() {\n if (ctx) {\n ctx.close()\n ctx = null\n }\n nextPlayTime = 0\n },\n }\n}\n","import { log, logError, warn } from \"./logger\"\nimport type { ServerEvent } from \"./types\"\nimport type { TurnMetricsSummary, Utterance, VoiceSessionState } from \"./session-state\"\n\n// Mutable context that tracks cross-event state (previously useRef values in the hook).\nexport type StateMachineContext = {\n assistantBubbleOpen: boolean\n userTurnActive: boolean\n incompleteAccum: string\n hasIncompleteUserTurn: boolean\n preLLMText: string\n}\n\nexport function createStateMachineContext(): StateMachineContext {\n return {\n assistantBubbleOpen: false,\n userTurnActive: false,\n incompleteAccum: \"\",\n hasIncompleteUserTurn: false,\n preLLMText: \"\",\n }\n}\n\nexport type SideEffect = { kind: \"flush_player\" }\n\nexport type ReduceResult = {\n state: VoiceSessionState\n ctx: StateMachineContext\n effects: SideEffect[]\n}\n\n// ── Pure state helpers ────────────────────────────────────────────────────────\n\nfunction patch(state: VoiceSessionState, p: Partial<VoiceSessionState>): VoiceSessionState {\n return { ...state, ...p }\n}\n\nfunction finaliseLastUtterance(\n state: VoiceSessionState,\n role: \"user\" | \"assistant\",\n finalText?: string,\n): VoiceSessionState {\n const list = [...state.utterances]\n for (let i = list.length - 1; i >= 0; i--) {\n if (list[i].role === role && list[i].partial) {\n list[i] = {\n ...list[i],\n text: finalText ?? list[i].text,\n partial: false,\n fillerPrefix: undefined,\n }\n break\n }\n }\n return { ...state, utterances: list }\n}\n\nfunction upsertAssistantBubble(\n state: VoiceSessionState,\n update: (prev: Utterance | null) => Utterance,\n): VoiceSessionState {\n const list = [...state.utterances]\n for (let i = list.length - 1; i >= 0; i--) {\n if (list[i].role === \"assistant\" && list[i].partial) {\n list[i] = update(list[i])\n return { ...state, utterances: list }\n }\n }\n const created = update(null)\n return { ...state, utterances: [...list, created] }\n}\n\n// ── Core reducer ──────────────────────────────────────────────────────────────\n\nexport function reduceServerEvent(\n state: VoiceSessionState,\n ctx: StateMachineContext,\n evt: ServerEvent,\n): ReduceResult {\n // Clone ctx so callers can diff before/after\n ctx = { ...ctx }\n const effects: SideEffect[] = []\n\n switch (evt.type) {\n case \"session.config\":\n log(\"session\", `config brain=${evt.brain_mode} vad_stop=${evt.turn_policy.vad_stop_secs * 1000}ms eot=${evt.turn_policy.eot_strategy} smart_eot=${evt.capabilities.smart_eot}`)\n state = patch(state, { session: \"idle\", error: null })\n break\n\n case \"transcript.partial\": {\n state = patch(state, { session: \"listening\" })\n const list = [...state.utterances]\n const last = list.length > 0 ? list[list.length - 1] : null\n const prefix = ctx.incompleteAccum\n const display = prefix ? prefix + evt.text : evt.text\n if (last?.role === \"user\" && last.partial) {\n list[list.length - 1] = { ...last, text: display }\n } else {\n list.push({ role: \"user\", text: display, partial: true })\n }\n state = { ...state, utterances: list }\n break\n }\n\n case \"transcript.final\": {\n const prefix = ctx.incompleteAccum\n const display = prefix ? prefix + evt.text : evt.text\n log(\"asr\", `final \"${evt.text}\"`)\n ctx.preLLMText = ctx.preLLMText ? ctx.preLLMText + display : display\n ctx.userTurnActive = false\n break\n }\n\n case \"transcript.timing\":\n log(\"asr\", `timing wait=${evt.wait_ms}ms`)\n {\n const list = [...state.utterances]\n for (let i = list.length - 1; i >= 0; i--) {\n if (list[i].role === \"user\") {\n const prev = list[i]\n list[i] = { ...prev, waitTimes: [...(prev.waitTimes ?? []), evt.wait_ms] }\n break\n }\n }\n state = { ...state, utterances: list }\n }\n break\n\n case \"transcript.incomplete\":\n log(\"asr\", `incomplete \"${evt.text}\" wait=${evt.wait_ms}ms`)\n ctx.incompleteAccum = (ctx.incompleteAccum || \"\") + evt.text\n ctx.hasIncompleteUserTurn = true\n {\n const list = [...state.utterances]\n for (let i = list.length - 1; i >= 0; i--) {\n if (list[i].role === \"user\") {\n const prev = list[i]\n list[i] = {\n ...prev,\n text: ctx.incompleteAccum,\n partial: true,\n incomplete: true,\n waitTimes: [...(prev.waitTimes ?? []), ...(evt.wait_ms != null ? [evt.wait_ms] : [])],\n }\n break\n }\n }\n state = { ...state, utterances: list }\n }\n state = patch(state, { session: \"idle\" })\n break\n\n case \"turn.started\":\n log(\"turn\", \"started\")\n ctx.userTurnActive = true\n if (ctx.assistantBubbleOpen) {\n ctx.assistantBubbleOpen = false\n state = finaliseLastUtterance(state, \"assistant\")\n }\n if (!ctx.hasIncompleteUserTurn) {\n ctx.incompleteAccum = \"\"\n }\n state = patch(state, { session: \"listening\" })\n break\n\n case \"turn.ended\":\n log(\"turn\", `ended transcript=\"${evt.transcript ?? \"\"}\"`)\n if (!ctx.hasIncompleteUserTurn) {\n state = finaliseLastUtterance(state, \"user\", ctx.preLLMText || undefined)\n ctx.incompleteAccum = \"\"\n ctx.preLLMText = \"\"\n }\n state = patch(state, { session: \"thinking\" })\n break\n\n case \"filler.started\":\n log(\"filler\", `started id=${evt.id} text=\"${evt.text}\"`)\n state = patch(state, { session: \"speaking\" })\n state = upsertAssistantBubble(state, (prev) => {\n if (!prev) {\n return { role: \"assistant\", text: evt.text, partial: true, fillerPrefix: evt.text }\n }\n if (prev.fillerPrefix || prev.text.startsWith(evt.text)) {\n return prev\n }\n const body = prev.text\n return {\n ...prev,\n fillerPrefix: evt.text,\n text: body ? `${evt.text} ${body}` : evt.text,\n }\n })\n ctx.assistantBubbleOpen = true\n break\n\n case \"filler.stopped\":\n log(\"filler\", `stopped id=${evt.id}`)\n break\n\n case \"filler.skipped\":\n log(\"filler\", `skipped wait_ms=${evt.wait_ms}`)\n break\n\n case \"assistant.delta\":\n state = patch(state, { session: \"speaking\" })\n state = upsertAssistantBubble(state, (prev) => {\n if (!prev) {\n return { role: \"assistant\", text: evt.text, partial: true }\n }\n const prefix = prev.fillerPrefix ?? \"\"\n const body = prefix && prev.text.startsWith(prefix)\n ? prev.text.slice(prefix.length).trimStart()\n : prev.text\n const joined = body + evt.text\n return {\n ...prev,\n text: prefix ? `${prefix} ${joined}` : joined,\n }\n })\n ctx.assistantBubbleOpen = true\n break\n\n case \"tts.started\":\n log(\"tts\", \"started\")\n state = patch(state, { session: \"speaking\" })\n break\n\n case \"tts.stopped\":\n log(\"tts\", `stopped reason=${evt.reason}`)\n break\n\n case \"turn.metrics\": {\n const prevCurrent = state.latency.current\n const merged: TurnMetricsSummary =\n prevCurrent && prevCurrent.turn_id === evt.turn_id\n ? { turn_id: evt.turn_id, ftl: evt.ftl ?? prevCurrent.ftl, il: evt.il ?? prevCurrent.il, ts: Date.now() }\n : { turn_id: evt.turn_id, ftl: evt.ftl, il: evt.il, ts: Date.now() }\n const history =\n state.latency.history[0]?.turn_id === evt.turn_id\n ? [merged, ...state.latency.history.slice(1)]\n : [merged, ...state.latency.history].slice(0, 50)\n state = { ...state, latency: { current: merged, history } }\n log(\"turn\", `metrics ftl=${evt.ftl?.total_ms ?? \"-\"}ms il=${evt.il?.total_ms ?? \"-\"}ms`)\n break\n }\n\n case \"assistant.done\":\n log(\"turn\", \"assistant done\")\n ctx.assistantBubbleOpen = false\n state = finaliseLastUtterance(state, \"assistant\")\n if (ctx.hasIncompleteUserTurn) {\n state = finaliseLastUtterance(state, \"user\")\n ctx.incompleteAccum = \"\"\n ctx.hasIncompleteUserTurn = false\n }\n ctx.preLLMText = \"\"\n state = patch(state, { session: \"idle\" })\n break\n\n case \"interrupted\":\n log(\"turn\", \"interrupted\")\n effects.push({ kind: \"flush_player\" })\n ctx.assistantBubbleOpen = false\n state = finaliseLastUtterance(state, \"assistant\")\n if (ctx.hasIncompleteUserTurn) {\n ctx.incompleteAccum = \"\"\n ctx.hasIncompleteUserTurn = false\n }\n ctx.preLLMText = \"\"\n state = patch(state, { session: \"listening\" })\n break\n\n case \"error\":\n logError(\"ws\", evt.message)\n state = patch(state, { error: evt.message })\n break\n\n case \"brain.request\":\n // Handled by VoiceLoopClient before reaching the state machine\n warn(\"ws\", \"unexpected brain.request in state machine\")\n break\n }\n\n return { state, ctx, effects }\n}\n","import type { ConnectionState, FtlMetrics, IlMetrics, SessionState } from \"./types\"\n\nexport type Utterance = {\n role: \"user\" | \"assistant\"\n text: string\n partial?: boolean\n incomplete?: boolean // semantic EoT rejected at least once (user turns)\n fillerPrefix?: string // filler phrase prepended to this assistant turn\n waitTimes?: number[] // fallback wait durations (ms) per rejected pause\n}\n\nexport type TurnMetricsSummary = {\n turn_id: string\n ftl: FtlMetrics | null\n il: IlMetrics | null\n ts: number\n}\n\nexport type VoiceSessionState = {\n connection: ConnectionState\n session: SessionState\n utterances: Utterance[]\n volume: number // 0–1, microphone RMS\n error: string | null\n latency: {\n current: TurnMetricsSummary | null\n history: TurnMetricsSummary[]\n }\n}\n\nexport function createInitialState(): VoiceSessionState {\n return {\n connection: \"disconnected\",\n session: \"idle\",\n utterances: [],\n volume: 0,\n error: null,\n latency: { current: null, history: [] },\n }\n}\n","import { log, logError, warn, type LogEntry } from \"./logger\"\nimport { createPcmPlayer, startMicCapture, type AudioCaptureHandle, type PcmPlayerHandle } from \"./audio\"\nimport { reduceServerEvent, createStateMachineContext, type StateMachineContext } from \"./state-machine\"\nimport { createInitialState, type VoiceSessionState } from \"./session-state\"\nimport type { BrainDeltaFrame, BrainDoneFrame, BrainErrorFrame, BrainRequestEvent, ServerEvent, TurnPolicy } from \"./types\"\n\n// ── Minimal typed EventEmitter ────────────────────────────────────────────────\n\ntype EventMap = {\n state: (s: VoiceSessionState) => void\n log: (e: LogEntry) => void\n}\n\nclass TypedEmitter {\n private _listeners = new Map<string, Set<(...args: unknown[]) => void>>()\n\n on<K extends keyof EventMap>(event: K, fn: EventMap[K]): this {\n if (!this._listeners.has(event)) this._listeners.set(event, new Set())\n this._listeners.get(event)!.add(fn as (...args: unknown[]) => void)\n return this\n }\n\n off<K extends keyof EventMap>(event: K, fn: EventMap[K]): this {\n this._listeners.get(event)?.delete(fn as (...args: unknown[]) => void)\n return this\n }\n\n protected emit<K extends keyof EventMap>(event: K, ...args: Parameters<EventMap[K]>): void {\n this._listeners.get(event)?.forEach(fn => fn(...(args as unknown[])))\n }\n}\n\n// ── Options ───────────────────────────────────────────────────────────────────\n\nexport type BrainSendFrame = (frame: BrainDeltaFrame | BrainDoneFrame | BrainErrorFrame) => void\n\nexport type VoiceLoopClientOptions = {\n serverUrl?: string\n apiKey?: string\n /** 话轮策略与 TTS 参数,含垫词、打断、EoT、音色、语速等。 */\n turnPolicy?: TurnPolicy\n /** Called when the server delegates an LLM request to the client. */\n onBrainRequest?: (evt: BrainRequestEvent, sendFrame: BrainSendFrame) => void | Promise<void>\n /** Inject custom microphone capture (useful for testing). */\n micCapture?: (onPcm: (chunk: ArrayBuffer) => void) => Promise<AudioCaptureHandle>\n /** Inject custom PCM player factory (useful for testing). */\n playerFactory?: () => PcmPlayerHandle\n}\n\nconst DEFAULT_SERVER = \"http://localhost:9000\"\nconst VOLUME_INTERVAL_MS = 50\n\n// ── VoiceLoopClient ───────────────────────────────────────────────────────────\n\nexport class VoiceLoopClient extends TypedEmitter {\n private _state: VoiceSessionState = createInitialState()\n private _ctx: StateMachineContext = createStateMachineContext()\n\n private _ws: WebSocket | null = null\n private _mic: AudioCaptureHandle | null = null\n private _player: PcmPlayerHandle\n private _volTimer: ReturnType<typeof setInterval> | null = null\n\n private readonly _serverUrl: string\n private readonly _apiKey: string\n private readonly _turnPolicy: TurnPolicy | undefined\n private readonly _onBrainRequest: VoiceLoopClientOptions[\"onBrainRequest\"]\n private readonly _micCapture: (onPcm: (chunk: ArrayBuffer) => void) => Promise<AudioCaptureHandle>\n\n constructor(opts: VoiceLoopClientOptions = {}) {\n super()\n this._serverUrl = opts.serverUrl ?? DEFAULT_SERVER\n this._apiKey = opts.apiKey ?? \"dev-key\"\n this._turnPolicy = opts.turnPolicy\n this._onBrainRequest = opts.onBrainRequest\n this._micCapture = opts.micCapture ?? startMicCapture\n this._player = (opts.playerFactory ?? createPcmPlayer)()\n }\n\n get state(): VoiceSessionState {\n return this._state\n }\n\n // ── Connection ──────────────────────────────────────────────────────────────\n\n async connect(): Promise<void> {\n if (this._ws) return\n\n this._player.resume()\n\n log(\"session\", `create server=${this._serverUrl}`)\n this._applyPatch({ connection: \"connecting\", error: null, utterances: [] })\n this._ctx = createStateMachineContext()\n\n // 1. Create session\n let token: string\n try {\n let res: Response\n try {\n const { tts_options, ...policyRest } = this._turnPolicy ?? {}\n const sessionBody = {\n brain_mode: \"client\",\n ...(Object.keys(policyRest).length > 0 ? { turn_policy: policyRest } : {}),\n ...(tts_options ? { tts_options } : {}),\n }\n log(\"session\", `create body=${JSON.stringify(sessionBody)}`)\n res = await fetch(`${this._serverUrl}/v1/sessions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${this._apiKey}`,\n },\n body: JSON.stringify(sessionBody),\n })\n } catch {\n throw new Error(`无法连接服务器(${this._serverUrl}),请检查 serverUrl 是否正确,以及服务是否在运行`)\n }\n if (res.status === 401) throw new Error(`鉴权失败(401),请检查 apiKey 是否正确`)\n if (res.status === 403) throw new Error(`无访问权限(403),请联系管理员确认 apiKey 权限`)\n if (res.status === 404) throw new Error(`接口不存在(404),请检查 serverUrl 是否正确,不要带路径后缀`)\n if (!res.ok) throw new Error(`服务器错误(${res.status}),请联系管理员排查服务端日志`)\n const data = await res.json() as { session_token: string; session_id: string }\n token = data.session_token\n log(\"session\", `created id=${data.session_id}`)\n } catch (e) {\n logError(\"session\", `create failed ${e}`)\n this._applyPatch({ connection: \"disconnected\", error: String(e) })\n return\n }\n\n // 2. Open WebSocket\n const wsUrl = this._serverUrl.replace(/^http/, \"ws\") + `/ws?token=${token}`\n log(\"ws\", `connecting ${wsUrl}`)\n const ws = new WebSocket(wsUrl)\n ws.binaryType = \"arraybuffer\"\n this._ws = ws\n\n ws.onopen = async () => {\n log(\"ws\", \"open\")\n this._applyPatch({ connection: \"connected\" })\n\n // 3. Start microphone\n try {\n this._mic = await this._micCapture((chunk) => {\n if (ws.readyState === WebSocket.OPEN) ws.send(chunk)\n })\n log(\"mic\", \"capture started\")\n\n this._volTimer = setInterval(() => {\n this._applyPatch({ volume: this._mic?.getVolume() ?? 0 })\n }, VOLUME_INTERVAL_MS)\n } catch (e) {\n logError(\"mic\", `capture failed ${e}`)\n const name = e instanceof Error ? e.name : \"\"\n const msg = name === \"NotAllowedError\"\n ? \"麦克风权限被拒绝,请在浏览器地址栏允许麦克风访问\"\n : name === \"NotFoundError\"\n ? \"未找到麦克风设备,请确认设备已连接\"\n : `麦克风启动失败:${e}`\n this._applyPatch({ error: msg })\n ws.close()\n }\n }\n\n ws.onmessage = (e) => {\n if (e.data instanceof ArrayBuffer) {\n this._player.enqueue(new Uint8Array(e.data))\n return\n }\n try {\n const evt = JSON.parse(e.data) as ServerEvent\n if (evt.type === \"brain.request\") {\n if (this._onBrainRequest) {\n this._onBrainRequest(evt, (frame) => {\n if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(frame))\n })\n } else {\n warn(\"ws\", \"brain.request received but no onBrainRequest handler provided\")\n this._applyPatch({ error: \"收到 brain.request 但未提供 onBrainRequest 回调,AI 将无法回答。请在 VoiceLoopClient 初始化时传入 onBrainRequest\" })\n }\n return\n }\n this._handleEvent(evt)\n } catch {\n warn(\"ws\", \"malformed frame\", typeof e.data === \"string\" ? e.data.slice(0, 100) : \"binary\")\n }\n }\n\n ws.onclose = (e) => {\n log(\"ws\", `closed code=${e.code} reason=${e.reason || \"(none)\"}`, undefined,\n e.code >= 4000 ? \"warn\" : \"info\")\n this._cleanup()\n // Skip if disconnect() already handled the state update\n if (this._ws !== null || this._state.connection !== \"disconnected\") {\n this._ws = null\n this._applyPatch({ connection: \"disconnected\", session: \"idle\", volume: 0 })\n }\n }\n\n ws.onerror = () => {\n logError(\"ws\", \"error\")\n this._applyPatch({ error: `WebSocket 连接失败,请检查网络或联系管理员(serverUrl: ${this._serverUrl})` })\n }\n }\n\n disconnect(): void {\n log(\"session\", \"disconnect\")\n this._cleanup()\n const ws = this._ws\n this._ws = null // nullify first so onclose skips the duplicate patch\n ws?.close()\n this._applyPatch({ connection: \"disconnected\", session: \"idle\", volume: 0 })\n }\n\n // ── Control frames ──────────────────────────────────────────────────────────\n\n barge(): void {\n this._send({ type: \"barge\" })\n }\n\n endTurn(transcript?: string): void {\n this._send({ type: \"turn.end\", ...(transcript ? { transcript } : {}) })\n }\n\n say(text: string, voice?: string): void {\n this._send({ type: \"say\", text, ...(voice ? { voice } : {}) })\n }\n\n // ── Private ─────────────────────────────────────────────────────────────────\n\n private _send(frame: object): void {\n if (this._ws?.readyState === WebSocket.OPEN) {\n this._ws.send(JSON.stringify(frame))\n }\n }\n\n private _handleEvent(evt: ServerEvent): void {\n const { state, ctx, effects } = reduceServerEvent(this._state, this._ctx, evt)\n this._ctx = ctx\n if (state !== this._state) {\n this._state = state\n this.emit(\"state\", state)\n }\n for (const effect of effects) {\n if (effect.kind === \"flush_player\") {\n this._player.flush()\n }\n }\n }\n\n private _applyPatch(p: Partial<VoiceSessionState>): void {\n this._state = { ...this._state, ...p }\n this.emit(\"state\", this._state)\n }\n\n private _cleanup(): void {\n if (this._volTimer) {\n clearInterval(this._volTimer)\n this._volTimer = null\n }\n this._mic?.stop()\n this._mic = null\n this._player.flush()\n }\n}\n"],"mappings":"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","names":["_a","_a","_b","e"]}