@absolutejs/voice 0.0.22-beta.183 → 0.0.22-beta.185

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,66 @@
1
+ import { Elysia } from 'elysia';
2
+ import { type VoiceOperationsRecord, type VoiceOperationsRecordOptions } from './operationsRecord';
3
+ import { type VoiceTraceRedactionConfig } from './trace';
4
+ export type VoiceIncidentBundleFormat = 'json' | 'markdown';
5
+ export type VoiceIncidentBundle = {
6
+ auditMarkdown?: string;
7
+ exportedAt: number;
8
+ formatVersion: 1;
9
+ markdown: string;
10
+ record: VoiceOperationsRecord;
11
+ redacted: boolean;
12
+ sessionId: string;
13
+ summary: VoiceIncidentBundleSummary;
14
+ traceMarkdown: string;
15
+ };
16
+ export type VoiceIncidentBundleSummary = {
17
+ auditEvents: number;
18
+ durationMs?: number;
19
+ errors: number;
20
+ handoffs: number;
21
+ providers: string[];
22
+ sessionId: string;
23
+ status: VoiceOperationsRecord['status'];
24
+ tools: number;
25
+ traceEvents: number;
26
+ turns: number;
27
+ };
28
+ export type VoiceIncidentBundleOptions = VoiceOperationsRecordOptions & {
29
+ redact?: VoiceTraceRedactionConfig;
30
+ title?: string;
31
+ };
32
+ export type VoiceIncidentBundleRoutesOptions = Omit<VoiceIncidentBundleOptions, 'sessionId'> & {
33
+ headers?: HeadersInit;
34
+ markdownPath?: false | string;
35
+ name?: string;
36
+ path?: string;
37
+ };
38
+ export declare const buildVoiceIncidentBundle: (options: VoiceIncidentBundleOptions) => Promise<VoiceIncidentBundle>;
39
+ export declare const createVoiceIncidentBundleRoutes: (options: VoiceIncidentBundleRoutesOptions) => Elysia<"", {
40
+ decorator: {};
41
+ store: {};
42
+ derive: {};
43
+ resolve: {};
44
+ }, {
45
+ typebox: {};
46
+ error: {};
47
+ }, {
48
+ schema: {};
49
+ standaloneSchema: {};
50
+ macro: {};
51
+ macroFn: {};
52
+ parser: {};
53
+ response: {};
54
+ }, {}, {
55
+ derive: {};
56
+ resolve: {};
57
+ schema: {};
58
+ standaloneSchema: {};
59
+ response: {};
60
+ }, {
61
+ derive: {};
62
+ resolve: {};
63
+ schema: {};
64
+ standaloneSchema: {};
65
+ response: {};
66
+ }>;
package/dist/index.d.ts CHANGED
@@ -49,6 +49,7 @@ export { createVoiceReadinessProfile, recommendVoiceReadinessProfile } from './r
49
49
  export { buildVoiceProviderContractMatrix, createVoiceProviderContractMatrixHTMLHandler, createVoiceProviderContractMatrixJSONHandler, createVoiceProviderContractMatrixPreset, createVoiceProviderContractMatrixRoutes, evaluateVoiceProviderStackGaps, renderVoiceProviderContractMatrixHTML, recommendVoiceProviderStack } from './providerStackRecommendations';
50
50
  export { buildVoiceOpsConsoleReport, createVoiceOpsConsoleRoutes, renderVoiceOpsConsoleHTML } from './opsConsoleRoutes';
51
51
  export { buildVoiceOperationsRecord, createVoiceOperationsRecordRoutes, renderVoiceOperationsRecordHTML } from './operationsRecord';
52
+ export { buildVoiceIncidentBundle, createVoiceIncidentBundleRoutes } from './incidentBundle';
52
53
  export { summarizeVoiceOpsStatus } from './opsStatus';
53
54
  export { createVoiceOpsStatusRoutes, renderVoiceOpsStatusHTML } from './opsStatusRoutes';
54
55
  export { createVoiceQualityRoutes, evaluateVoiceQuality, renderVoiceQualityHTML } from './qualityRoutes';
@@ -105,10 +106,11 @@ export type { VoicePhoneAgentCarrier, VoicePhoneAgentCarrierSummary, VoicePhoneA
105
106
  export type { VoicePhoneAgentProductionSmokeIssue, VoicePhoneAgentProductionSmokeHandlerOptions, VoicePhoneAgentProductionSmokeHTMLHandlerOptions, VoicePhoneAgentProductionSmokeOptions, VoicePhoneAgentProductionSmokeReport, VoicePhoneAgentProductionSmokeRoutesOptions, VoicePhoneAgentProductionSmokeRequirement } from './phoneAgentProductionSmoke';
106
107
  export type { VoiceOpsConsoleLink, VoiceOpsConsoleReport, VoiceOpsConsoleRoutesOptions } from './opsConsoleRoutes';
107
108
  export type { VoiceOpsStatus, VoiceOpsStatusLink, VoiceOpsStatusOptions, VoiceOpsStatusReport, VoiceOpsStatusRoutesOptions } from './opsStatus';
108
- export type { VoiceProductionReadinessAction, VoiceProductionReadinessAuditOptions, VoiceProductionReadinessAuditRequirement, VoiceProductionReadinessAuditSummary, VoiceProductionReadinessCheck, VoiceProductionReadinessGateIssue, VoiceProductionReadinessGateOptions, VoiceProductionReadinessGateProfile, VoiceProductionReadinessGateProfileSurface, VoiceProductionReadinessGateReport, VoiceProductionReadinessOpsActionHistoryOptions, VoiceProductionReadinessOpsActionHistorySummary, VoiceProductionReadinessProfileExplanation, VoiceProductionReadinessProfileSurface, VoiceProductionReadinessProofSource, VoiceProductionReadinessReport, VoiceProductionReadinessRoutesOptions, VoiceProductionReadinessTraceDeliverySummary, VoiceProductionReadinessAuditDeliveryOptions, VoiceProductionReadinessAuditDeliverySummary, VoiceProductionReadinessTraceDeliveryOptions, VoiceProductionReadinessStatus } from './productionReadiness';
109
+ export type { VoiceProductionReadinessAction, VoiceProductionReadinessAuditOptions, VoiceProductionReadinessAuditRequirement, VoiceProductionReadinessAuditSummary, VoiceProductionReadinessCheck, VoiceProductionReadinessGateIssue, VoiceProductionReadinessGateOptions, VoiceProductionReadinessGateProfile, VoiceProductionReadinessGateProfileSurface, VoiceProductionReadinessGateReport, VoiceProductionReadinessOpsActionHistoryOptions, VoiceProductionReadinessOpsActionHistorySummary, VoiceProductionReadinessOperationsRecordLink, VoiceProductionReadinessOperationsRecordLinks, VoiceProductionReadinessProfileExplanation, VoiceProductionReadinessProfileSurface, VoiceProductionReadinessProofSource, VoiceProductionReadinessReport, VoiceProductionReadinessRoutesOptions, VoiceProductionReadinessTraceDeliverySummary, VoiceProductionReadinessAuditDeliveryOptions, VoiceProductionReadinessAuditDeliverySummary, VoiceProductionReadinessTraceDeliveryOptions, VoiceProductionReadinessStatus } from './productionReadiness';
109
110
  export type { VoiceReadinessProfileName, VoiceReadinessProfileOptions, VoiceReadinessProfileRecommendation, VoiceReadinessProfileRecommendationScore, VoiceReadinessProfileRoutesOptions } from './readinessProfiles';
110
111
  export type { VoiceProviderStackChoice, VoiceProviderStackCapabilities, VoiceProviderStackCapabilityGap, VoiceProviderStackCapabilityGapInput, VoiceProviderStackCapabilityGapReport, VoiceProviderContractCheck, VoiceProviderContractCheckStatus, VoiceProviderContractDefinition, VoiceProviderContractMatrixHandlerOptions, VoiceProviderContractMatrixHTMLHandlerOptions, VoiceProviderContractMatrixInput, VoiceProviderContractMatrixPresetOptions, VoiceProviderContractMatrixReport, VoiceProviderContractMatrixRoutesOptions, VoiceProviderContractMatrixRow, VoiceProviderStackInput, VoiceProviderStackKind, VoiceProviderStackRecommendation } from './providerStackRecommendations';
111
112
  export type { VoiceOperationsRecord, VoiceOperationsRecordAgentHandoff, VoiceOperationsRecordAuditSummary, VoiceOperationsRecordOptions, VoiceOperationsRecordOutcome, VoiceOperationsRecordRoutesOptions, VoiceOperationsRecordStatus, VoiceOperationsRecordTool } from './operationsRecord';
113
+ export type { VoiceIncidentBundle, VoiceIncidentBundleFormat, VoiceIncidentBundleOptions, VoiceIncidentBundleRoutesOptions, VoiceIncidentBundleSummary } from './incidentBundle';
112
114
  export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
113
115
  export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePageData, VoiceResilienceRoutesOptions, VoiceResilienceSimulationProvider, VoiceRoutingKindSummary, VoiceRoutingDecisionSummary, VoiceRoutingDecisionSummaryOptions, VoiceRoutingEvent, VoiceRoutingEventKind, VoiceRoutingSessionSummary, VoiceRoutingSessionSummaryOptions } from './resilienceRoutes';
114
116
  export type { VoiceIOProviderRouterEvent, VoiceIOProviderRouterOptions, VoiceIOProviderRouterPolicy, VoiceIOProviderRouterPolicyConfig, VoiceSTTProviderRouterOptions, VoiceTTSProviderRouterOptions } from './providerAdapters';
package/dist/index.js CHANGED
@@ -20482,6 +20482,7 @@ var readinessGateCodes = {
20482
20482
  "Delivery runtime": "voice.readiness.delivery_runtime",
20483
20483
  "Handoff delivery": "voice.readiness.handoff_delivery",
20484
20484
  "Live latency proof": "voice.readiness.live_latency",
20485
+ "Operations records": "voice.readiness.operations_records",
20485
20486
  "Operator action history": "voice.readiness.operator_action_history",
20486
20487
  "Phone agent production smoke": "voice.readiness.phone_agent_smoke",
20487
20488
  "Provider contract matrix": "voice.readiness.provider_contract_matrix",
@@ -20810,6 +20811,46 @@ var summarizeLiveLatency = (events, options) => {
20810
20811
  warnings
20811
20812
  };
20812
20813
  };
20814
+ var getString12 = (value) => typeof value === "string" ? value : undefined;
20815
+ var getNumber7 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
20816
+ var voiceOperationsRecordHref = (base, sessionId) => {
20817
+ const encoded = encodeURIComponent(sessionId);
20818
+ if (base.includes(":sessionId")) {
20819
+ return base.replace(":sessionId", encoded);
20820
+ }
20821
+ return `${base.replace(/\/+$/, "")}/${encoded}`;
20822
+ };
20823
+ var buildOperationsRecordLinks = (input) => {
20824
+ const failedSessionSet = new Set(input.failedSessionIds);
20825
+ const providerErrors = input.events.filter((event) => event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.error === "string")).map((event) => ({
20826
+ detail: getString12(event.payload.error),
20827
+ href: voiceOperationsRecordHref(input.base, event.sessionId),
20828
+ label: "Open provider error operations record",
20829
+ sessionId: event.sessionId,
20830
+ status: "fail"
20831
+ }));
20832
+ const failingLatency = input.events.filter((event) => event.type === "client.live_latency").map((event) => ({
20833
+ event,
20834
+ latencyMs: getNumber7(event.payload.latencyMs) ?? getNumber7(event.payload.elapsedMs)
20835
+ })).filter((entry) => entry.latencyMs !== undefined && entry.latencyMs > input.liveLatencyWarnAfterMs).map(({ event, latencyMs }) => ({
20836
+ detail: `${latencyMs}ms live latency`,
20837
+ href: voiceOperationsRecordHref(input.base, event.sessionId),
20838
+ label: "Open latency operations record",
20839
+ sessionId: event.sessionId,
20840
+ status: latencyMs > input.liveLatencyFailAfterMs ? "fail" : "warn"
20841
+ }));
20842
+ return {
20843
+ failedSessions: input.failedSessionIds.map((sessionId) => ({
20844
+ href: voiceOperationsRecordHref(input.base, sessionId),
20845
+ label: "Open failed session operations record",
20846
+ sessionId,
20847
+ status: failedSessionSet.has(sessionId) ? "fail" : "warn"
20848
+ })),
20849
+ failingLatency,
20850
+ providerErrors
20851
+ };
20852
+ };
20853
+ var firstOperationsRecordHref = (links) => links[0]?.href;
20813
20854
  var buildVoiceProductionReadinessReport = async (options, input = {}) => {
20814
20855
  const request = input.request ?? new Request("http://localhost/");
20815
20856
  const query = input.query ?? {};
@@ -20873,6 +20914,14 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
20873
20914
  const deliveryRuntime = summarizeDeliveryRuntime(deliveryRuntimeSummary);
20874
20915
  const degradedProviders = providers.filter((provider) => provider.status === "degraded" || provider.status === "rate-limited" || provider.status === "suppressed").length;
20875
20916
  const failedSessions = sessions.filter((session) => session.status === "failed").length;
20917
+ const failedSessionItems = sessions.filter((session) => session.status === "failed");
20918
+ const operationsRecords = buildOperationsRecordLinks({
20919
+ base: options.links?.operationsRecords ?? "/voice-operations",
20920
+ events,
20921
+ failedSessionIds: failedSessionItems.map((session) => session.sessionId),
20922
+ liveLatencyFailAfterMs: options.liveLatencyFailAfterMs ?? 3200,
20923
+ liveLatencyWarnAfterMs: options.liveLatencyWarnAfterMs ?? 1800
20924
+ });
20876
20925
  const checks = [
20877
20926
  {
20878
20927
  detail: quality.status === "pass" ? "Quality gates are passing." : "Quality gates need attention.",
@@ -20904,11 +20953,18 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
20904
20953
  },
20905
20954
  {
20906
20955
  detail: providerRecovery.unresolvedErrors > 0 ? `${providerRecovery.unresolvedErrors} provider error(s) have no recovered fallback evidence.` : providerRecovery.recovered > 0 ? `${providerRecovery.recovered} provider fallback recovery event(s) kept sessions healthy.` : "No provider fallback recovery was needed in the current trace window.",
20907
- href: options.links?.resilience ?? "/resilience",
20956
+ href: firstOperationsRecordHref(operationsRecords.providerErrors) ?? options.links?.resilience ?? "/resilience",
20908
20957
  label: "Provider fallback recovery",
20909
20958
  status: providerRecovery.status,
20910
20959
  value: providerRecovery.total === 0 ? "0 events" : `${providerRecovery.recovered}/${providerRecovery.total}`,
20911
20960
  actions: providerRecovery.status === "pass" ? [] : [
20961
+ ...firstOperationsRecordHref(operationsRecords.providerErrors) ? [
20962
+ {
20963
+ description: "Open the exact call/session operations record for the first unresolved provider error.",
20964
+ href: firstOperationsRecordHref(operationsRecords.providerErrors),
20965
+ label: "Open failing operations record"
20966
+ }
20967
+ ] : [],
20912
20968
  {
20913
20969
  description: "Open provider resilience traces and inspect unresolved provider errors.",
20914
20970
  href: options.links?.resilience ?? "/resilience",
@@ -20918,11 +20974,18 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
20918
20974
  },
20919
20975
  {
20920
20976
  detail: failedSessions === 0 ? sessions.length > 0 ? "Recent sessions have no recorded provider/session failures." : "No sessions have been recorded yet; run a smoke or live session for proof." : `${failedSessions} recent session(s) have failures.`,
20921
- href: options.links?.sessions ?? "/sessions",
20977
+ href: firstOperationsRecordHref(operationsRecords.failedSessions) ?? options.links?.sessions ?? "/sessions",
20922
20978
  label: "Session health",
20923
20979
  status: failedSessions > 0 ? "fail" : sessions.length === 0 ? "warn" : "pass",
20924
20980
  value: `${sessions.length - failedSessions}/${sessions.length}`,
20925
20981
  actions: failedSessions > 0 ? [
20982
+ ...firstOperationsRecordHref(operationsRecords.failedSessions) ? [
20983
+ {
20984
+ description: "Open the exact failed call/session operations record.",
20985
+ href: firstOperationsRecordHref(operationsRecords.failedSessions),
20986
+ label: "Open failed operations record"
20987
+ }
20988
+ ] : [],
20926
20989
  {
20927
20990
  description: "Open failed sessions and replay traces.",
20928
20991
  href: `${options.links?.sessions ?? "/sessions"}?status=failed`,
@@ -20974,12 +21037,19 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
20974
21037
  const proofSource = (...keys) => keys.map((key) => proofSources?.[key]).find((source) => source !== undefined);
20975
21038
  checks.push({
20976
21039
  detail: liveLatency.total === 0 ? "No browser live-latency measurements are recorded yet." : liveLatency.status === "pass" ? `Live browser turn latency averages ${liveLatency.averageLatencyMs}ms.` : `${liveLatency.failed} failed and ${liveLatency.warnings} warned live-latency measurement(s).`,
20977
- href: options.links?.liveLatency ?? "/traces",
21040
+ href: firstOperationsRecordHref(operationsRecords.failingLatency) ?? options.links?.liveLatency ?? "/traces",
20978
21041
  label: "Live latency proof",
20979
21042
  proofSource: proofSource("liveLatency", "liveLatencyProof"),
20980
21043
  status: liveLatency.status,
20981
21044
  value: liveLatency.averageLatencyMs === undefined ? `${liveLatency.total} samples` : `${liveLatency.averageLatencyMs}ms avg`,
20982
21045
  actions: liveLatency.status === "pass" ? [] : [
21046
+ ...firstOperationsRecordHref(operationsRecords.failingLatency) ? [
21047
+ {
21048
+ description: "Open the exact call/session operations record for the slow live turn.",
21049
+ href: firstOperationsRecordHref(operationsRecords.failingLatency),
21050
+ label: "Open latency operations record"
21051
+ }
21052
+ ] : [],
20983
21053
  {
20984
21054
  description: "Run a live browser voice turn and inspect the persisted latency trace.",
20985
21055
  href: options.links?.liveLatency ?? "/traces",
@@ -21260,6 +21330,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
21260
21330
  handoffs: "/handoffs",
21261
21331
  handoffRetry: "/api/voice-handoffs/retry",
21262
21332
  liveLatency: "/traces",
21333
+ operationsRecords: "/voice-operations",
21263
21334
  opsActions: "/voice/ops-actions",
21264
21335
  phoneAgentSmoke: "/sessions",
21265
21336
  providerContracts: "/provider-contracts",
@@ -21272,6 +21343,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
21272
21343
  ...options.links
21273
21344
  },
21274
21345
  profile: options.profile || undefined,
21346
+ operationsRecords,
21275
21347
  proofSources,
21276
21348
  status: rollupStatus2(checks),
21277
21349
  summary: {
@@ -22193,11 +22265,11 @@ import { Elysia as Elysia37 } from "elysia";
22193
22265
  // src/traceTimeline.ts
22194
22266
  import { Elysia as Elysia36 } from "elysia";
22195
22267
  var escapeHtml38 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
22196
- var getString12 = (value) => typeof value === "string" && value.trim() ? value : undefined;
22197
- var getNumber7 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
22268
+ var getString13 = (value) => typeof value === "string" && value.trim() ? value : undefined;
22269
+ var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
22198
22270
  var firstString3 = (payload, keys) => {
22199
22271
  for (const key of keys) {
22200
- const value = getString12(payload[key]);
22272
+ const value = getString13(payload[key]);
22201
22273
  if (value) {
22202
22274
  return value;
22203
22275
  }
@@ -22206,7 +22278,7 @@ var firstString3 = (payload, keys) => {
22206
22278
  };
22207
22279
  var firstNumber3 = (payload, keys) => {
22208
22280
  for (const key of keys) {
22209
- const value = getNumber7(payload[key]);
22281
+ const value = getNumber8(payload[key]);
22210
22282
  if (value !== undefined) {
22211
22283
  return value;
22212
22284
  }
@@ -22234,15 +22306,15 @@ var timelineLabel = (event) => {
22234
22306
  case "turn.transcript":
22235
22307
  return event.payload.isFinal === true ? "Final transcript" : "Partial transcript";
22236
22308
  case "turn.committed":
22237
- return `Committed turn${getString12(event.payload.reason) ? ` (${getString12(event.payload.reason)})` : ""}`;
22309
+ return `Committed turn${getString13(event.payload.reason) ? ` (${getString13(event.payload.reason)})` : ""}`;
22238
22310
  case "turn.assistant":
22239
22311
  return "Assistant reply";
22240
22312
  case "agent.model":
22241
22313
  return `Model call${eventProvider(event) ? ` via ${eventProvider(event)}` : ""}`;
22242
22314
  case "agent.tool":
22243
- return `Tool ${getString12(event.payload.toolName) ?? "call"}`;
22315
+ return `Tool ${getString13(event.payload.toolName) ?? "call"}`;
22244
22316
  case "agent.handoff":
22245
- return `Agent handoff${getString12(event.payload.targetAgentId) ? ` to ${getString12(event.payload.targetAgentId)}` : ""}`;
22317
+ return `Agent handoff${getString13(event.payload.targetAgentId) ? ` to ${getString13(event.payload.targetAgentId)}` : ""}`;
22246
22318
  case "assistant.run":
22247
22319
  return `Assistant run${eventProvider(event) ? ` via ${eventProvider(event)}` : ""}`;
22248
22320
  case "assistant.guardrail":
@@ -22252,11 +22324,11 @@ var timelineLabel = (event) => {
22252
22324
  case "client.live_latency":
22253
22325
  return `Live latency${eventElapsedMs(event) !== undefined ? ` ${eventElapsedMs(event)}ms` : ""}`;
22254
22326
  case "session.error":
22255
- return `Error${getString12(event.payload.error) ? `: ${getString12(event.payload.error)}` : ""}`;
22327
+ return `Error${getString13(event.payload.error) ? `: ${getString13(event.payload.error)}` : ""}`;
22256
22328
  case "turn.cost":
22257
22329
  return "Cost telemetry";
22258
22330
  case "turn_latency.stage":
22259
- return `Latency ${getString12(event.payload.stage) ?? "stage"}`;
22331
+ return `Latency ${getString13(event.payload.stage) ?? "stage"}`;
22260
22332
  case "workflow.contract":
22261
22333
  return `Workflow contract ${eventStatus(event) ?? ""}`.trim();
22262
22334
  default:
@@ -22449,26 +22521,26 @@ var createVoiceTraceTimelineRoutes = (options) => {
22449
22521
  };
22450
22522
 
22451
22523
  // src/operationsRecord.ts
22452
- var getString13 = (value) => typeof value === "string" ? value : undefined;
22453
- var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
22524
+ var getString14 = (value) => typeof value === "string" ? value : undefined;
22525
+ var getNumber9 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
22454
22526
  var countOutcome = (events, outcome) => events.filter((event) => event.outcome === outcome).length;
22455
22527
  var toHandoff = (event) => ({
22456
22528
  at: event.at,
22457
- fromAgentId: getString13(event.payload.fromAgentId),
22529
+ fromAgentId: getString14(event.payload.fromAgentId),
22458
22530
  metadata: event.payload.metadata && typeof event.payload.metadata === "object" && !Array.isArray(event.payload.metadata) ? event.payload.metadata : undefined,
22459
- reason: getString13(event.payload.reason),
22460
- status: getString13(event.payload.status),
22461
- summary: getString13(event.payload.summary),
22462
- targetAgentId: getString13(event.payload.targetAgentId),
22531
+ reason: getString14(event.payload.reason),
22532
+ status: getString14(event.payload.status),
22533
+ summary: getString14(event.payload.summary),
22534
+ targetAgentId: getString14(event.payload.targetAgentId),
22463
22535
  turnId: event.turnId
22464
22536
  });
22465
22537
  var toTool = (event) => ({
22466
22538
  at: event.at,
22467
- elapsedMs: getNumber8(event.payload.elapsedMs),
22468
- error: getString13(event.payload.error),
22469
- status: getString13(event.payload.status),
22470
- toolCallId: getString13(event.payload.toolCallId),
22471
- toolName: getString13(event.payload.toolName),
22539
+ elapsedMs: getNumber9(event.payload.elapsedMs),
22540
+ error: getString14(event.payload.error),
22541
+ status: getString14(event.payload.status),
22542
+ toolCallId: getString14(event.payload.toolCallId),
22543
+ toolName: getString14(event.payload.toolName),
22472
22544
  turnId: event.turnId
22473
22545
  });
22474
22546
  var resolveOutcome4 = (events) => {
@@ -22572,6 +22644,159 @@ var createVoiceOperationsRecordRoutes = (options) => {
22572
22644
  }
22573
22645
  return routes;
22574
22646
  };
22647
+ // src/incidentBundle.ts
22648
+ import { Elysia as Elysia38 } from "elysia";
22649
+ var buildSummary = (record) => ({
22650
+ auditEvents: record.audit?.total ?? 0,
22651
+ durationMs: record.summary.callDurationMs,
22652
+ errors: record.summary.errorCount,
22653
+ handoffs: record.handoffs.length,
22654
+ providers: record.providers.map((provider) => provider.provider),
22655
+ sessionId: record.sessionId,
22656
+ status: record.status,
22657
+ tools: record.tools.length,
22658
+ traceEvents: record.traceEvents.length,
22659
+ turns: record.summary.turnCount
22660
+ });
22661
+ var renderIncidentMarkdown = (input) => {
22662
+ const lines = [
22663
+ `# ${input.title ?? `Voice Incident ${input.summary.sessionId}`}`,
22664
+ "",
22665
+ `Session: ${input.summary.sessionId}`,
22666
+ `Status: ${input.summary.status}`,
22667
+ `Trace events: ${input.summary.traceEvents}`,
22668
+ `Audit events: ${input.summary.auditEvents}`,
22669
+ `Turns: ${input.summary.turns}`,
22670
+ `Errors: ${input.summary.errors}`,
22671
+ `Handoffs: ${input.summary.handoffs}`,
22672
+ `Tools: ${input.summary.tools}`,
22673
+ `Providers: ${input.summary.providers.join(", ") || "none"}`,
22674
+ input.summary.durationMs === undefined ? undefined : `Duration: ${input.summary.durationMs}ms`,
22675
+ "",
22676
+ "## Outcome",
22677
+ "",
22678
+ `- Assistant replies: ${input.record.outcome.assistantReplies}`,
22679
+ `- Complete: ${input.record.outcome.complete ? "yes" : "no"}`,
22680
+ `- Escalated: ${input.record.outcome.escalated ? "yes" : "no"}`,
22681
+ `- Transferred: ${input.record.outcome.transferred ? "yes" : "no"}`,
22682
+ `- Voicemail: ${input.record.outcome.voicemail ? "yes" : "no"}`,
22683
+ `- No answer: ${input.record.outcome.noAnswer ? "yes" : "no"}`,
22684
+ "",
22685
+ "## Handoffs",
22686
+ "",
22687
+ ...input.record.handoffs.length ? input.record.handoffs.map((handoff) => `- ${handoff.fromAgentId ?? "unknown"} -> ${handoff.targetAgentId ?? "unknown"} ${handoff.status ?? ""} ${handoff.summary ?? handoff.reason ?? ""}`.trim()) : ["- none"],
22688
+ "",
22689
+ "## Tools",
22690
+ "",
22691
+ ...input.record.tools.length ? input.record.tools.map((tool) => `- ${tool.toolName ?? "tool"} ${tool.status ?? ""} ${tool.elapsedMs === undefined ? "" : `${tool.elapsedMs}ms`} ${tool.error ?? ""}`.trim()) : ["- none"],
22692
+ "",
22693
+ "## Trace Evidence",
22694
+ "",
22695
+ input.traceMarkdown,
22696
+ ""
22697
+ ].filter((line) => line !== undefined);
22698
+ if (input.auditMarkdown) {
22699
+ lines.push("## Audit Evidence", "", input.auditMarkdown);
22700
+ }
22701
+ return lines.join(`
22702
+ `);
22703
+ };
22704
+ var redactRecordValue = (value, redactedEvents, redact) => {
22705
+ if (!redact) {
22706
+ return value;
22707
+ }
22708
+ if (Array.isArray(value)) {
22709
+ return value.map((item) => redactRecordValue(item, redactedEvents, redact));
22710
+ }
22711
+ if (typeof value === "string") {
22712
+ return redactVoiceTraceText(value, redact);
22713
+ }
22714
+ if (typeof value === "object" && value) {
22715
+ return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [
22716
+ key,
22717
+ key === "events" || key === "traceEvents" ? redactedEvents : redactRecordValue(entryValue, redactedEvents, redact)
22718
+ ]));
22719
+ }
22720
+ return value;
22721
+ };
22722
+ var redactOperationsRecord = (record, input) => ({
22723
+ ...redactRecordValue(record, input.traceEvents, input.redact),
22724
+ audit: record.audit ? {
22725
+ ...record.audit,
22726
+ events: input.auditEvents ?? []
22727
+ } : undefined,
22728
+ traceEvents: input.traceEvents
22729
+ });
22730
+ var buildVoiceIncidentBundle = async (options) => {
22731
+ const record = await buildVoiceOperationsRecord(options);
22732
+ const redactedTraceEvents = options.redact ? redactVoiceTraceEvents(record.traceEvents, options.redact) : record.traceEvents;
22733
+ const redactedAuditEvents = options.redact && record.audit ? redactVoiceAuditEvents(record.audit.events, options.redact) : record.audit?.events;
22734
+ const redactedRecord = redactOperationsRecord(record, {
22735
+ auditEvents: redactedAuditEvents,
22736
+ redact: options.redact,
22737
+ traceEvents: redactedTraceEvents
22738
+ });
22739
+ const summary = buildSummary(redactedRecord);
22740
+ const traceMarkdown = renderVoiceTraceMarkdown(record.traceEvents, {
22741
+ evaluation: options.evaluation,
22742
+ redact: options.redact,
22743
+ title: `Voice Incident Trace ${options.sessionId}`
22744
+ });
22745
+ const auditMarkdown = record.audit ? renderVoiceAuditMarkdown(record.audit.events, {
22746
+ redact: options.redact,
22747
+ title: `Voice Incident Audit ${options.sessionId}`
22748
+ }) : undefined;
22749
+ const markdown = renderIncidentMarkdown({
22750
+ auditMarkdown,
22751
+ record: redactedRecord,
22752
+ summary,
22753
+ title: options.title,
22754
+ traceMarkdown
22755
+ });
22756
+ return {
22757
+ auditMarkdown,
22758
+ exportedAt: Date.now(),
22759
+ formatVersion: 1,
22760
+ markdown,
22761
+ record: redactedRecord,
22762
+ redacted: Boolean(options.redact),
22763
+ sessionId: options.sessionId,
22764
+ summary,
22765
+ traceMarkdown
22766
+ };
22767
+ };
22768
+ var createVoiceIncidentBundleRoutes = (options) => {
22769
+ const path = options.path ?? "/api/voice-incidents/:sessionId";
22770
+ const markdownPath = options.markdownPath === undefined ? "/voice-incidents/:sessionId/markdown" : options.markdownPath;
22771
+ const routes = new Elysia38({
22772
+ name: options.name ?? "absolutejs-voice-incident-bundle"
22773
+ });
22774
+ const getSessionId = (params) => params.sessionId ?? "";
22775
+ const buildBundle = (sessionId) => buildVoiceIncidentBundle({
22776
+ audit: options.audit,
22777
+ evaluation: options.evaluation,
22778
+ events: options.events,
22779
+ redact: options.redact,
22780
+ sessionId,
22781
+ store: options.store,
22782
+ title: options.title
22783
+ });
22784
+ routes.get(path, async ({ params }) => Response.json(await buildBundle(getSessionId(params)), {
22785
+ headers: options.headers
22786
+ }));
22787
+ if (markdownPath) {
22788
+ routes.get(markdownPath, async ({ params }) => {
22789
+ const bundle = await buildBundle(getSessionId(params));
22790
+ return new Response(bundle.markdown, {
22791
+ headers: {
22792
+ "Content-Type": "text/markdown; charset=utf-8",
22793
+ ...options.headers
22794
+ }
22795
+ });
22796
+ });
22797
+ }
22798
+ return routes;
22799
+ };
22575
22800
  // src/opsStatus.ts
22576
22801
  var DEFAULT_LINKS2 = [
22577
22802
  {
@@ -22744,7 +22969,7 @@ var summarizeVoiceOpsStatus = async (options) => {
22744
22969
  };
22745
22970
  };
22746
22971
  // src/opsStatusRoutes.ts
22747
- import { Elysia as Elysia38 } from "elysia";
22972
+ import { Elysia as Elysia39 } from "elysia";
22748
22973
  var escapeHtml40 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
22749
22974
  var renderVoiceOpsStatusHTML = (report, options = {}) => {
22750
22975
  const title = options.title ?? "AbsoluteJS Voice Ops Status";
@@ -22756,7 +22981,7 @@ var renderVoiceOpsStatusHTML = (report, options = {}) => {
22756
22981
  };
22757
22982
  var createVoiceOpsStatusRoutes = (options) => {
22758
22983
  const path = options.path ?? "/api/voice/ops-status";
22759
- const routes = new Elysia38({
22984
+ const routes = new Elysia39({
22760
22985
  name: options.name ?? "absolutejs-voice-ops-status"
22761
22986
  });
22762
22987
  routes.get(path, async () => summarizeVoiceOpsStatus(options));
@@ -23189,10 +23414,10 @@ var createVoiceTTSProviderRouter = (options) => {
23189
23414
  };
23190
23415
  };
23191
23416
  // src/traceDeliveryRoutes.ts
23192
- import { Elysia as Elysia39 } from "elysia";
23417
+ import { Elysia as Elysia40 } from "elysia";
23193
23418
  var escapeHtml41 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
23194
- var getString14 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
23195
- var getNumber9 = (value) => {
23419
+ var getString15 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
23420
+ var getNumber10 = (value) => {
23196
23421
  if (typeof value === "number" && Number.isFinite(value)) {
23197
23422
  return value;
23198
23423
  }
@@ -23203,13 +23428,13 @@ var getNumber9 = (value) => {
23203
23428
  return;
23204
23429
  };
23205
23430
  var parseStatus2 = (value) => {
23206
- const text = getString14(value);
23431
+ const text = getString15(value);
23207
23432
  return text === "pending" || text === "delivered" || text === "failed" || text === "skipped" || text === "all" ? text : undefined;
23208
23433
  };
23209
23434
  var resolveVoiceTraceDeliveryFilter = (query = {}, base = {}) => ({
23210
23435
  ...base,
23211
- limit: getNumber9(query.limit) ?? base.limit,
23212
- q: getString14(query.q) ?? base.q,
23436
+ limit: getNumber10(query.limit) ?? base.limit,
23437
+ q: getString15(query.q) ?? base.q,
23213
23438
  status: parseStatus2(query.status) ?? base.status
23214
23439
  });
23215
23440
  var deliverySearchText2 = (delivery) => [
@@ -23298,7 +23523,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
23298
23523
  const path = options.path ?? "/api/voice-trace-deliveries";
23299
23524
  const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
23300
23525
  const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
23301
- const routes = new Elysia39({
23526
+ const routes = new Elysia40({
23302
23527
  name: options.name ?? "absolutejs-voice-trace-deliveries"
23303
23528
  }).get(path, createVoiceTraceDeliveryJSONHandler(options));
23304
23529
  if (htmlPath !== false) {
@@ -23922,7 +24147,7 @@ var createVoiceMemoryStore = () => {
23922
24147
  return { get, getOrCreate, list, remove, set };
23923
24148
  };
23924
24149
  // src/opsWebhook.ts
23925
- import { Elysia as Elysia40 } from "elysia";
24150
+ import { Elysia as Elysia41 } from "elysia";
23926
24151
  var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
23927
24152
  var signVoiceOpsWebhookBody = async (input) => {
23928
24153
  const encoder = new TextEncoder;
@@ -24052,7 +24277,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
24052
24277
  };
24053
24278
  var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
24054
24279
  const path = options.path ?? "/api/voice-ops/webhook";
24055
- return new Elysia40().post(path, async ({ body, request, set }) => {
24280
+ return new Elysia41().post(path, async ({ body, request, set }) => {
24056
24281
  const bodyText = typeof body === "string" ? body : JSON.stringify(body);
24057
24282
  if (options.signingSecret) {
24058
24283
  const verification = await verifyVoiceOpsWebhookSignature({
@@ -25200,6 +25425,7 @@ export {
25200
25425
  createVoiceIntegrationSinkWorker,
25201
25426
  createVoiceIntegrationHTTPSink,
25202
25427
  createVoiceIntegrationEvent,
25428
+ createVoiceIncidentBundleRoutes,
25203
25429
  createVoiceHubSpotTaskUpdateSink,
25204
25430
  createVoiceHubSpotTaskSyncSinks,
25205
25431
  createVoiceHubSpotTaskSink,
@@ -25309,6 +25535,7 @@ export {
25309
25535
  buildVoiceOpsConsoleReport,
25310
25536
  buildVoiceOpsActionHistoryReport,
25311
25537
  buildVoiceOperationsRecord,
25538
+ buildVoiceIncidentBundle,
25312
25539
  buildVoiceDiagnosticsMarkdown,
25313
25540
  buildVoiceDemoReadyReport,
25314
25541
  buildVoiceDeliverySinkReport,
@@ -86,6 +86,7 @@ export type VoiceProductionReadinessReport = {
86
86
  handoffs?: string;
87
87
  handoffRetry?: string;
88
88
  liveLatency?: string;
89
+ operationsRecords?: string;
89
90
  opsActions?: string;
90
91
  phoneAgentSmoke?: string;
91
92
  providerContracts?: string;
@@ -98,6 +99,7 @@ export type VoiceProductionReadinessReport = {
98
99
  };
99
100
  profile?: VoiceProductionReadinessProfileExplanation;
100
101
  proofSources?: Record<string, VoiceProductionReadinessProofSource>;
102
+ operationsRecords?: VoiceProductionReadinessOperationsRecordLinks;
101
103
  status: VoiceProductionReadinessStatus;
102
104
  summary: {
103
105
  agentSquadContracts?: {
@@ -174,6 +176,18 @@ export type VoiceProductionReadinessReport = {
174
176
  traceDeliveries?: VoiceProductionReadinessTraceDeliverySummary;
175
177
  };
176
178
  };
179
+ export type VoiceProductionReadinessOperationsRecordLink = {
180
+ detail?: string;
181
+ href: string;
182
+ label: string;
183
+ sessionId: string;
184
+ status: VoiceProductionReadinessStatus;
185
+ };
186
+ export type VoiceProductionReadinessOperationsRecordLinks = {
187
+ failedSessions: VoiceProductionReadinessOperationsRecordLink[];
188
+ failingLatency: VoiceProductionReadinessOperationsRecordLink[];
189
+ providerErrors: VoiceProductionReadinessOperationsRecordLink[];
190
+ };
177
191
  export type VoiceProductionReadinessAuditRequirement = {
178
192
  label?: string;
179
193
  maxAgeMs?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.183",
3
+ "version": "0.0.22-beta.185",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",