UNPKG

agents

Version:

A home for your AI agents

360 lines (359 loc) 15.9 kB
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js"; import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js"; import { VoiceProviderError } from "./voice/errors.js"; //#region src/voice/diagnostics.ts const MAX_STRING_LENGTH = 160; const PRIVATE_KEY = /(^|_)(audio|body|contents?|exceptions?|messages?|prompts?|query|results?|stack|text|tokens?|tools?|transcripts?|arguments?)(_|$)/i; function boundedString(value, maxLength = MAX_STRING_LENGTH) { const normalized = value.replace(/\s+/g, " ").trim(); return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 1)}…`; } function diagnosticErrorData(error) { const summary = { name: boundedString(error.name || "Error"), message: boundedString(error.message || "Unknown error") }; if (error instanceof VoiceProviderError) { if (error.code !== void 0) summary.code = error.code; if (error.status !== void 0) summary.status = error.status; if (error.closeCode !== void 0) summary.closeCode = error.closeCode; if (error.closeReason !== void 0) summary.closeReason = boundedString(error.closeReason); if (error.wasClean !== void 0) summary.wasClean = error.wasClean; } return summary; } /** * Keep diagnostic metadata bounded and reject fields that could carry user or * provider content. Instrumentation should still pass only known-safe values. */ function sanitizeDiagnosticData(data) { if (!data) return void 0; const sanitized = {}; for (const [key, value] of Object.entries(data)) { if (PRIVATE_KEY.test(key)) continue; sanitized[key] = value instanceof Error ? diagnosticErrorData(value) : typeof value === "string" ? boundedString(value) : value; } return Object.keys(sanitized).length > 0 ? sanitized : void 0; } function createDiagnosticEvent(event, data, timestamp = Date.now()) { const safeData = sanitizeDiagnosticData(data); return { event, timestamp, ...safeData ? { data: safeData } : {} }; } /** Server-only diagnostics. The sink is the reserved voice protocol message. */ var ServerDiagnostics = class { constructor(browserConsole) { this.browserConsole = browserConsole; } emit(connection, event, data) { if (!this.browserConsole) return; try { connection.send(JSON.stringify({ type: "diagnostic", ...createDiagnosticEvent(event, data) })); } catch {} } turn(connection, turnId, source = "speech", now = Date.now) { return new TurnDiagnostics(this, connection, turnId, source, now); } }; function safeDuration(value) { return Math.max(0, value); } var _turn = /* @__PURE__ */ new WeakMap(); var _startedAt = /* @__PURE__ */ new WeakMap(); /** One sentence attempt measured by its parent turn clock. */ var TtsSentenceTracker = class { constructor(turn, startedAt) { _classPrivateFieldInitSpec(this, _turn, void 0); _classPrivateFieldInitSpec(this, _startedAt, void 0); _classPrivateFieldSet2(_turn, this, turn); _classPrivateFieldSet2(_startedAt, this, startedAt); } providerStarted() { _classPrivateFieldGet2(_turn, this).recordTtsProviderStarted(); } settle(outcome) { return _classPrivateFieldGet2(_turn, this).recordTtsSentence(_classPrivateFieldGet2(_startedAt, this), outcome); } }; var _diagnostics = /* @__PURE__ */ new WeakMap(); var _connection = /* @__PURE__ */ new WeakMap(); var _now = /* @__PURE__ */ new WeakMap(); var _startedAt2 = /* @__PURE__ */ new WeakMap(); var _speechStartedAt = /* @__PURE__ */ new WeakMap(); var _firstInterimObserved = /* @__PURE__ */ new WeakMap(); var _finalInputAt = /* @__PURE__ */ new WeakMap(); var _firstTtsProviderAt = /* @__PURE__ */ new WeakMap(); var _firstAudioAt = /* @__PURE__ */ new WeakMap(); var _ttsFinished = /* @__PURE__ */ new WeakMap(); var _hasTtsFailures = /* @__PURE__ */ new WeakMap(); var _metrics = /* @__PURE__ */ new WeakMap(); /** * One content-free turn lifecycle. Diagnostics and stable metrics are emitted * from the same recorded landmarks; application metrics never parse event * names or diagnostic metadata. */ var TurnDiagnostics = class { constructor(diagnostics, connection, turnId, source, now) { _classPrivateFieldInitSpec(this, _diagnostics, void 0); _classPrivateFieldInitSpec(this, _connection, void 0); _classPrivateFieldInitSpec(this, _now, void 0); _classPrivateFieldInitSpec(this, _startedAt2, void 0); _classPrivateFieldInitSpec(this, _speechStartedAt, void 0); _classPrivateFieldInitSpec(this, _firstInterimObserved, false); _classPrivateFieldInitSpec(this, _finalInputAt, void 0); _classPrivateFieldInitSpec(this, _firstTtsProviderAt, void 0); _classPrivateFieldInitSpec(this, _firstAudioAt, void 0); _classPrivateFieldInitSpec(this, _ttsFinished, false); _classPrivateFieldInitSpec(this, _hasTtsFailures, false); _classPrivateFieldInitSpec(this, _metrics, {}); _classPrivateFieldSet2(_diagnostics, this, diagnostics); _classPrivateFieldSet2(_connection, this, connection); _classPrivateFieldSet2(_now, this, now); _classPrivateFieldSet2(_startedAt2, this, now()); this.turnId = turnId; this.source = source; } get hasTtsFailures() { return _classPrivateFieldGet2(_hasTtsFailures, this); } get ttsWorkMs() { return _classPrivateFieldGet2(_metrics, this).ttsWorkMs ?? 0; } emit(event, data) { const scopedData = { ...data, turn_id: this.turnId }; _classPrivateFieldGet2(_diagnostics, this).emit(_classPrivateFieldGet2(_connection, this), event, scopedData); } startModel() { return new ModelDiagnosticTracker(this, _classPrivateFieldGet2(_now, this)); } markTextInput() { _classPrivateFieldGet2(_finalInputAt, this) ?? _classPrivateFieldSet2(_finalInputAt, this, _classPrivateFieldGet2(_startedAt2, this)); } speechStarted() { _classPrivateFieldGet2(_speechStartedAt, this) ?? _classPrivateFieldSet2(_speechStartedAt, this, _classPrivateFieldGet2(_now, this).call(this)); this.emit("speech.started"); } firstInterim(characters) { if (_classPrivateFieldGet2(_firstInterimObserved, this)) return; _classPrivateFieldSet2(_firstInterimObserved, this, true); const elapsed = _classPrivateFieldGet2(_speechStartedAt, this) === void 0 ? void 0 : safeDuration(_classPrivateFieldGet2(_now, this).call(this) - _classPrivateFieldGet2(_speechStartedAt, this)); if (elapsed !== void 0) _classPrivateFieldGet2(_metrics, this).speechStartToFirstInterimMs = elapsed; this.emit("stt.interim", { ...characters === void 0 ? {} : { characters }, ...elapsed === void 0 ? {} : { speech_to_interim_ms: elapsed } }); } finalInput(characters) { _classPrivateFieldSet2(_finalInputAt, this, _classPrivateFieldGet2(_now, this).call(this)); const elapsed = _classPrivateFieldGet2(_speechStartedAt, this) === void 0 ? void 0 : safeDuration(_classPrivateFieldGet2(_finalInputAt, this) - _classPrivateFieldGet2(_speechStartedAt, this)); if (elapsed !== void 0) _classPrivateFieldGet2(_metrics, this).speechStartToFinalMs = elapsed; this.emit("stt.utterance", { ...characters === void 0 ? {} : { characters }, ...elapsed === void 0 ? {} : { speech_to_final_ms: elapsed } }); } recordAfterTranscribe(durationMs, outcome, characters) { _classPrivateFieldGet2(_metrics, this).afterTranscribeMs = safeDuration(durationMs); this.emit("after_transcribe.completed", { duration_ms: safeDuration(durationMs), outcome, characters }); } beginTtsSentence() { _classPrivateFieldGet2(_metrics, this).ttsWorkMs ??= 0; return new TtsSentenceTracker(this, _classPrivateFieldGet2(_now, this).call(this)); } recordTtsProviderStarted() { _classPrivateFieldGet2(_firstTtsProviderAt, this) ?? _classPrivateFieldSet2(_firstTtsProviderAt, this, _classPrivateFieldGet2(_now, this).call(this)); } recordTtsSentence(startedAt, outcome) { const duration = safeDuration(_classPrivateFieldGet2(_now, this).call(this) - startedAt); _classPrivateFieldGet2(_metrics, this).ttsWorkMs = (_classPrivateFieldGet2(_metrics, this).ttsWorkMs ?? 0) + duration; if (outcome === "failed") _classPrivateFieldSet2(_hasTtsFailures, this, true); return duration; } finishTts() { if (_classPrivateFieldGet2(_ttsFinished, this)) return; _classPrivateFieldSet2(_ttsFinished, this, true); if (_classPrivateFieldGet2(_firstTtsProviderAt, this) !== void 0) _classPrivateFieldGet2(_metrics, this).ttsWallMs = safeDuration(_classPrivateFieldGet2(_now, this).call(this) - _classPrivateFieldGet2(_firstTtsProviderAt, this)); } audioSent() { const now = _classPrivateFieldGet2(_now, this).call(this); if (_classPrivateFieldGet2(_firstAudioAt, this) !== void 0) return; _classPrivateFieldSet2(_firstAudioAt, this, now); if (_classPrivateFieldGet2(_finalInputAt, this) !== void 0) _classPrivateFieldGet2(_metrics, this).finalInputToFirstAudioMs = safeDuration(now - _classPrivateFieldGet2(_finalInputAt, this)); if (_classPrivateFieldGet2(_firstTtsProviderAt, this) !== void 0) _classPrivateFieldGet2(_metrics, this).ttsToFirstAudioMs = safeDuration(now - _classPrivateFieldGet2(_firstTtsProviderAt, this)); } recordModelFirstText(durationMs) { _classPrivateFieldGet2(_metrics, this).modelToFirstTextMs = safeDuration(durationMs); } recordReasoning(durationMs) { _classPrivateFieldGet2(_metrics, this).exposedReasoningMs = (_classPrivateFieldGet2(_metrics, this).exposedReasoningMs ?? 0) + safeDuration(durationMs); } recordModelTerminal(durationMs) { _classPrivateFieldGet2(_metrics, this).modelStreamConsumptionMs = safeDuration(durationMs); } finish(outcome) { this.finishTts(); const summary = { turnId: this.turnId, source: this.source, outcome, turnTotalMs: safeDuration(_classPrivateFieldGet2(_now, this).call(this) - _classPrivateFieldGet2(_startedAt2, this)), ..._classPrivateFieldGet2(_metrics, this) }; this.emit("turn.ended", { outcome, duration_ms: summary.turnTotalMs }); try { _classPrivateFieldGet2(_connection, this).send(JSON.stringify({ type: "turn_metrics", ...summary })); } catch {} return summary; } }; var _turn2 = /* @__PURE__ */ new WeakMap(); var _now2 = /* @__PURE__ */ new WeakMap(); var _startedAt3 = /* @__PURE__ */ new WeakMap(); var _firstTextObserved = /* @__PURE__ */ new WeakMap(); var _reasoningActive = /* @__PURE__ */ new WeakMap(); var _reasoningStartedAt = /* @__PURE__ */ new WeakMap(); var _characters = /* @__PURE__ */ new WeakMap(); var _terminal = /* @__PURE__ */ new WeakMap(); var _ModelDiagnosticTracker_brand = /* @__PURE__ */ new WeakSet(); /** * Tracks one model stream without retaining model or reasoning content. * Lifecycle methods are idempotent so stale turn cleanup cannot affect a * successor tracker. */ var ModelDiagnosticTracker = class { constructor(turn, now) { _classPrivateMethodInitSpec(this, _ModelDiagnosticTracker_brand); _classPrivateFieldInitSpec(this, _turn2, void 0); _classPrivateFieldInitSpec(this, _now2, void 0); _classPrivateFieldInitSpec(this, _startedAt3, void 0); _classPrivateFieldInitSpec(this, _firstTextObserved, false); _classPrivateFieldInitSpec(this, _reasoningActive, false); _classPrivateFieldInitSpec(this, _reasoningStartedAt, 0); _classPrivateFieldInitSpec(this, _characters, 0); _classPrivateFieldInitSpec(this, _terminal, false); _classPrivateFieldSet2(_turn2, this, turn); _classPrivateFieldSet2(_now2, this, now); _classPrivateFieldSet2(_startedAt3, this, now()); _classPrivateFieldGet2(_turn2, this).emit("model.started"); } observe(event) { if (_classPrivateFieldGet2(_terminal, this)) return; switch (event.type) { case "text": _classPrivateFieldSet2(_characters, this, _classPrivateFieldGet2(_characters, this) + event.text.length); if (!_classPrivateFieldGet2(_firstTextObserved, this) && event.text.trim().length > 0) { _classPrivateFieldSet2(_firstTextObserved, this, true); const elapsed = safeDuration(_classPrivateFieldGet2(_now2, this).call(this) - _classPrivateFieldGet2(_startedAt3, this)); _classPrivateFieldGet2(_turn2, this).recordModelFirstText(elapsed); _classPrivateFieldGet2(_turn2, this).emit("model.first_text", { elapsed_ms: elapsed }); } break; case "reasoning-start": _assertClassBrand(_ModelDiagnosticTracker_brand, this, _startReasoning).call(this); break; case "reasoning-end": _assertClassBrand(_ModelDiagnosticTracker_brand, this, _completeReasoning).call(this, "completed"); break; case "finish": _assertClassBrand(_ModelDiagnosticTracker_brand, this, _completeReasoning).call(this, "stream_finished"); break; case "error": break; case "boundary": break; } } complete(outcome, finishReason) { if (_classPrivateFieldGet2(_terminal, this)) return; _assertClassBrand(_ModelDiagnosticTracker_brand, this, _completeReasoning).call(this, "stream_completed"); _classPrivateFieldSet2(_terminal, this, true); const duration = this.elapsedMs(); _classPrivateFieldGet2(_turn2, this).recordModelTerminal(duration); _classPrivateFieldGet2(_turn2, this).emit("model.completed", { duration_ms: duration, outcome, characters: _classPrivateFieldGet2(_characters, this), ...finishReason === void 0 ? {} : { finish_reason: finishReason } }); } fail(error) { if (_classPrivateFieldGet2(_terminal, this)) return; _assertClassBrand(_ModelDiagnosticTracker_brand, this, _completeReasoning).call(this, "error"); _classPrivateFieldSet2(_terminal, this, true); _classPrivateFieldGet2(_turn2, this).recordModelTerminal(this.elapsedMs()); _classPrivateFieldGet2(_turn2, this).emit("model.failed", { error }); } abort() { if (_classPrivateFieldGet2(_terminal, this)) return; _assertClassBrand(_ModelDiagnosticTracker_brand, this, _completeReasoning).call(this, "aborted"); _classPrivateFieldSet2(_terminal, this, true); _classPrivateFieldGet2(_turn2, this).recordModelTerminal(this.elapsedMs()); } elapsedMs() { return safeDuration(_classPrivateFieldGet2(_now2, this).call(this) - _classPrivateFieldGet2(_startedAt3, this)); } }; function _startReasoning() { if (_classPrivateFieldGet2(_reasoningActive, this)) return; _classPrivateFieldSet2(_reasoningActive, this, true); _classPrivateFieldSet2(_reasoningStartedAt, this, _classPrivateFieldGet2(_now2, this).call(this)); _classPrivateFieldGet2(_turn2, this).emit("model.reasoning_started"); } function _completeReasoning(outcome) { if (!_classPrivateFieldGet2(_reasoningActive, this)) return; _classPrivateFieldSet2(_reasoningActive, this, false); const duration = safeDuration(_classPrivateFieldGet2(_now2, this).call(this) - _classPrivateFieldGet2(_reasoningStartedAt, this)); _classPrivateFieldGet2(_turn2, this).recordReasoning(duration); _classPrivateFieldGet2(_turn2, this).emit("model.reasoning_completed", { duration_ms: duration, outcome }); } var _enabled = /* @__PURE__ */ new WeakMap(); var _ClientDiagnostics_brand = /* @__PURE__ */ new WeakSet(); /** Browser-only diagnostics. Activation always comes from the server welcome. */ var ClientDiagnostics = class { constructor() { _classPrivateMethodInitSpec(this, _ClientDiagnostics_brand); _classPrivateFieldInitSpec(this, _enabled, false); } setEnabled(enabled) { _classPrivateFieldSet2(_enabled, this, enabled); } emit(event, data) { if (!_classPrivateFieldGet2(_enabled, this)) return; _assertClassBrand(_ClientDiagnostics_brand, this, _log).call(this, "client", createDiagnosticEvent(event, data)); } receive(event) { if (!_classPrivateFieldGet2(_enabled, this)) return; _assertClassBrand(_ClientDiagnostics_brand, this, _log).call(this, "server", event); } }; function _log(origin, event) { try { console.info(`[voice:${origin}] ${event.event}`, { timestamp: event.timestamp, ...event.data ?? {} }); } catch {} } //#endregion export { ServerDiagnostics as n, ClientDiagnostics as t }; //# sourceMappingURL=diagnostics-C4jcz3VK.js.map