@absolutejs/voice 0.0.22-beta.405 → 0.0.22-beta.407

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.
package/README.md CHANGED
@@ -2193,6 +2193,124 @@ For custom elements:
2193
2193
  </script>
2194
2194
  ```
2195
2195
 
2196
+ ### Call Debugger Launch Widgets
2197
+
2198
+ Mount `createVoiceCallDebuggerRoutes(...)` on the server, then expose a small framework-native launcher anywhere operators or developers need the latest support artifact. The launcher opens the full debugger with session snapshot, operations record, failure replay, provider path, transcript, user-heard output, linked artifacts, and incident markdown.
2199
+
2200
+ React:
2201
+
2202
+ ```tsx
2203
+ import { VoiceCallDebuggerLaunch } from '@absolutejs/voice/react';
2204
+
2205
+ export function DebugLatestCall() {
2206
+ return (
2207
+ <VoiceCallDebuggerLaunch
2208
+ description="Open snapshot, replay, provider path, transcript, and incident markdown."
2209
+ intervalMs={5000}
2210
+ path="/api/voice-call-debugger/latest"
2211
+ title="Debug Latest Call"
2212
+ />
2213
+ );
2214
+ }
2215
+ ```
2216
+
2217
+ Vue:
2218
+
2219
+ ```vue
2220
+ <script setup lang="ts">
2221
+ import { VoiceCallDebuggerLaunch } from '@absolutejs/voice/vue';
2222
+ </script>
2223
+
2224
+ <template>
2225
+ <VoiceCallDebuggerLaunch
2226
+ description="Open snapshot, replay, provider path, transcript, and incident markdown."
2227
+ :interval-ms="5000"
2228
+ path="/api/voice-call-debugger/latest"
2229
+ title="Debug Latest Call"
2230
+ />
2231
+ </template>
2232
+ ```
2233
+
2234
+ Svelte:
2235
+
2236
+ ```svelte
2237
+ <script lang="ts">
2238
+ import { onDestroy, onMount } from 'svelte';
2239
+ import { createVoiceCallDebugger } from '@absolutejs/voice/svelte';
2240
+
2241
+ const debuggerLaunch = createVoiceCallDebugger('/api/voice-call-debugger/latest', {
2242
+ description: 'Open snapshot, replay, provider path, transcript, and incident markdown.',
2243
+ intervalMs: 5000,
2244
+ title: 'Debug Latest Call'
2245
+ });
2246
+ let html = debuggerLaunch.getHTML();
2247
+ let unsubscribe = () => {};
2248
+
2249
+ onMount(() => {
2250
+ unsubscribe = debuggerLaunch.subscribe(() => {
2251
+ html = debuggerLaunch.getHTML();
2252
+ });
2253
+ void debuggerLaunch.refresh().catch(() => {});
2254
+ });
2255
+ onDestroy(() => {
2256
+ unsubscribe();
2257
+ debuggerLaunch.close();
2258
+ });
2259
+ </script>
2260
+
2261
+ {@html html}
2262
+ ```
2263
+
2264
+ Angular:
2265
+
2266
+ ```ts
2267
+ import { Component, computed, inject } from '@angular/core';
2268
+ import { createVoiceCallDebuggerLaunchViewModel } from '@absolutejs/voice/client';
2269
+ import { VoiceCallDebuggerService } from '@absolutejs/voice/angular';
2270
+
2271
+ @Component({
2272
+ selector: 'app-debug-latest-call',
2273
+ template: `
2274
+ <a [href]="model().href">{{ model().label }}</a>
2275
+ <p>{{ model().description }}</p>
2276
+ `
2277
+ })
2278
+ export class DebugLatestCallComponent {
2279
+ private readonly callDebugger = inject(VoiceCallDebuggerService).connect(
2280
+ '/api/voice-call-debugger/latest',
2281
+ { intervalMs: 5000 }
2282
+ );
2283
+ readonly model = computed(() =>
2284
+ createVoiceCallDebuggerLaunchViewModel(
2285
+ '/api/voice-call-debugger/latest',
2286
+ {
2287
+ error: this.callDebugger.error(),
2288
+ isLoading: this.callDebugger.isLoading(),
2289
+ report: this.callDebugger.report(),
2290
+ updatedAt: this.callDebugger.updatedAt()
2291
+ },
2292
+ { title: 'Debug Latest Call' }
2293
+ )
2294
+ );
2295
+ }
2296
+ ```
2297
+
2298
+ HTML or HTMX:
2299
+
2300
+ ```html
2301
+ <absolute-voice-call-debugger-launch
2302
+ interval-ms="5000"
2303
+ path="/api/voice-call-debugger/latest"
2304
+ title="Debug Latest Call"
2305
+ ></absolute-voice-call-debugger-launch>
2306
+
2307
+ <script type="module">
2308
+ import { defineVoiceCallDebuggerLaunchElement } from '@absolutejs/voice/client';
2309
+
2310
+ defineVoiceCallDebuggerLaunchElement();
2311
+ </script>
2312
+ ```
2313
+
2196
2314
  ## Delivery Runtime Widgets
2197
2315
 
2198
2316
  After mounting `createVoiceDeliveryRuntimeRoutes(...)`, apps can expose audit and trace worker health through the same framework-native primitives:
@@ -3056,7 +3174,7 @@ Readiness emits the stable `voice.readiness.ops_recovery` gate code when unresol
3056
3174
 
3057
3175
  ## Customer-Owned Observability Export
3058
3176
 
3059
- Use observability exports when a buyer wants the hosted-dashboard evidence graph, but inside their own storage, warehouse, SIEM, incident flow, or release notes. The export manifest links traces, audits, operations records, delivery queues, provider SLOs, readiness reports, screenshots, and proof-pack artifacts without making AbsoluteJS Voice the dashboard.
3177
+ Use observability exports when a buyer wants the hosted-dashboard evidence graph, but inside their own storage, warehouse, SIEM, incident flow, or release notes. The export manifest links traces, audits, operations records, session snapshots, call-debugger reports, delivery queues, provider SLOs, readiness reports, screenshots, and proof-pack artifacts without making AbsoluteJS Voice the dashboard.
3060
3178
 
3061
3179
  Every export manifest and artifact index includes a stable schema contract:
3062
3180
 
@@ -3169,7 +3287,10 @@ app.use(
3169
3287
  audit: runtimeStorage.audit,
3170
3288
  auditDeliveries: runtimeStorage.auditDeliveries,
3171
3289
  links: {
3172
- operationsRecord: (sessionId) => `/voice-operations/${sessionId}`
3290
+ callDebugger: (sessionId) => `/voice-call-debugger/${sessionId}`,
3291
+ operationsRecord: (sessionId) => `/voice-operations/${sessionId}`,
3292
+ sessionSnapshot: (sessionId) =>
3293
+ `/api/voice/session-snapshot/${sessionId}`
3173
3294
  },
3174
3295
  redact: true,
3175
3296
  store: runtimeStorage.traces,
@@ -3194,16 +3315,20 @@ const exportReport = await buildVoiceObservabilityExport({
3194
3315
  },
3195
3316
  audit: runtimeStorage.audit,
3196
3317
  auditDeliveries: runtimeStorage.auditDeliveries,
3318
+ callDebuggerReports: [latestCallDebuggerReport],
3197
3319
  links: {
3198
- operationsRecord: (sessionId) => `/voice-operations/${sessionId}`
3320
+ callDebugger: (sessionId) => `/voice-call-debugger/${sessionId}`,
3321
+ operationsRecord: (sessionId) => `/voice-operations/${sessionId}`,
3322
+ sessionSnapshot: (sessionId) => `/api/voice/session-snapshot/${sessionId}`
3199
3323
  },
3200
3324
  redact: true,
3325
+ sessionSnapshots: [latestSessionSnapshot],
3201
3326
  store: runtimeStorage.traces,
3202
3327
  traceDeliveries: runtimeStorage.traceDeliveries
3203
3328
  });
3204
3329
  ```
3205
3330
 
3206
- The route helper exposes JSON at `/api/voice/observability-export`, an artifact index at `/api/voice/observability-export/artifacts`, per-artifact downloads at `/api/voice/observability-export/artifacts/:artifactId`, delivery at `POST /api/voice/observability-export/deliveries`, delivery history at `GET /api/voice/observability-export/deliveries`, Markdown at `/voice/observability-export.md`, and HTML at `/voice/observability-export`. `createVoiceObservabilityExportReplayRoutes(...)` adds JSON replay proof at `/api/voice/observability-export/replay` and a readable replay proof page at `/voice/observability-export/replay`. Path-backed artifacts are hashed with SHA-256 by default, include byte size and freshness metadata, and can fail the export when required evidence is missing or stale. File delivery writes `manifest.json`, `artifact-index.json`, and artifact files into a customer-owned archive directory; webhook delivery posts the manifest and artifact index to a buyer-owned collector, SIEM bridge, or warehouse endpoint; S3 delivery writes the same manifest, index, and artifact files through Bun's native S3 client; SQLite and Postgres delivery persist the schema id/version, manifest, artifact index, checksum metadata, status, run id, and timestamps into buyer-owned database tables. Delivery receipt stores persist run id, destinations, status, schema, and target history so operators can prove exports have been continuously healthy. Failed trace/audit deliveries fail the export report, pending deliveries warn, and every trace/audit envelope includes the linked operations-record URL when one is configured. This is the primitive to use when customers ask how voice evidence leaves the app without going through a hosted vendor dashboard.
3331
+ The route helper exposes JSON at `/api/voice/observability-export`, an artifact index at `/api/voice/observability-export/artifacts`, per-artifact downloads at `/api/voice/observability-export/artifacts/:artifactId`, delivery at `POST /api/voice/observability-export/deliveries`, delivery history at `GET /api/voice/observability-export/deliveries`, Markdown at `/voice/observability-export.md`, and HTML at `/voice/observability-export`. `createVoiceObservabilityExportReplayRoutes(...)` adds JSON replay proof at `/api/voice/observability-export/replay` and a readable replay proof page at `/voice/observability-export/replay`. Path-backed artifacts are hashed with SHA-256 by default, include byte size and freshness metadata, and can fail the export when required evidence is missing or stale. File delivery writes `manifest.json`, `artifact-index.json`, and artifact files into a customer-owned archive directory; webhook delivery posts the manifest and artifact index to a buyer-owned collector, SIEM bridge, or warehouse endpoint; S3 delivery writes the same manifest, index, and artifact files through Bun's native S3 client; SQLite and Postgres delivery persist the schema id/version, manifest, artifact index, checksum metadata, status, run id, and timestamps into buyer-owned database tables. Delivery receipt stores persist run id, destinations, status, schema, and target history so operators can prove exports have been continuously healthy. Failed trace/audit deliveries fail the export report, pending deliveries warn, and every trace/audit envelope includes the linked operations-record URL when one is configured. Session snapshots and call-debugger reports become first-class artifact-index rows when passed through `sessionSnapshots` and `callDebuggerReports`, so support bundles, incident handoffs, SIEM records, and warehouse exports share one customer-owned evidence graph. This is the primitive to use when customers ask how voice evidence leaves the app without going through a hosted vendor dashboard.
3207
3332
 
3208
3333
  Pass the same report into production readiness when export health should block deploys:
3209
3334
 
package/dist/index.js CHANGED
@@ -32368,6 +32368,25 @@ var createOperationArtifact = (record, href) => ({
32368
32368
  sessionId: record.sessionId,
32369
32369
  status: record.status === "failed" ? "fail" : record.status === "warning" ? "warn" : "pass"
32370
32370
  });
32371
+ var toSnapshotArtifactStatus = (status) => status === "fail" ? "fail" : status === "warn" ? "warn" : "pass";
32372
+ var createSessionSnapshotArtifact = (snapshot, href) => ({
32373
+ generatedAt: snapshot.capturedAt,
32374
+ href,
32375
+ id: `session-snapshot:${snapshot.sessionId}`,
32376
+ kind: "session-snapshot",
32377
+ label: `Session snapshot ${snapshot.sessionId}`,
32378
+ sessionId: snapshot.sessionId,
32379
+ status: toSnapshotArtifactStatus(snapshot.status)
32380
+ });
32381
+ var createCallDebuggerArtifact = (report, href) => ({
32382
+ generatedAt: report.checkedAt,
32383
+ href,
32384
+ id: `call-debugger:${report.sessionId}`,
32385
+ kind: "call-debugger",
32386
+ label: `Call debugger ${report.sessionId}`,
32387
+ sessionId: report.sessionId,
32388
+ status: report.status === "failed" ? "fail" : report.status === "warning" ? "warn" : "pass"
32389
+ });
32371
32390
  var unique2 = (values) => [...new Set(values)].sort();
32372
32391
  var stripArtifactPathAnchor = (path) => path.split("#")[0] ?? path;
32373
32392
  var toEpochMs = (value) => {
@@ -33027,7 +33046,9 @@ var collectSessionIds = (input) => unique2([
33027
33046
  ...input.sessionIds ?? [],
33028
33047
  ...input.events.map((event) => event.sessionId),
33029
33048
  ...input.auditEvents.map((event) => event.sessionId).filter((sessionId) => Boolean(sessionId)),
33030
- ...input.operationsRecords.map((record) => record.sessionId)
33049
+ ...input.operationsRecords.map((record) => record.sessionId),
33050
+ ...input.sessionSnapshots.map((snapshot) => snapshot.sessionId),
33051
+ ...input.callDebuggerReports.map((report) => report.sessionId)
33031
33052
  ]);
33032
33053
  var collectIssues = (input) => {
33033
33054
  const issues = [];
@@ -33133,11 +33154,15 @@ var buildVoiceObservabilityExport = async (options = {}) => {
33133
33154
  const events = options.events ?? await options.store?.list() ?? [];
33134
33155
  const auditEvents = options.audit ? await options.audit.list() : [];
33135
33156
  const baseOperationsRecords = options.operationsRecords ?? [];
33157
+ const sessionSnapshots = options.sessionSnapshots ?? [];
33158
+ const callDebuggerReports = options.callDebuggerReports ?? [];
33136
33159
  const sessionIds = collectSessionIds({
33137
33160
  auditEvents,
33161
+ callDebuggerReports,
33138
33162
  events,
33139
33163
  operationsRecords: baseOperationsRecords,
33140
- sessionIds: options.sessionIds
33164
+ sessionIds: options.sessionIds,
33165
+ sessionSnapshots
33141
33166
  });
33142
33167
  const shouldBuildOperationsRecords = options.includeOperationsRecords === true && options.store;
33143
33168
  const builtOperationsRecords = shouldBuildOperationsRecords ? await Promise.all(sessionIds.map((sessionId) => buildVoiceOperationsRecord({
@@ -33155,7 +33180,14 @@ var buildVoiceObservabilityExport = async (options = {}) => {
33155
33180
  const traceDeliverySummary = traceDeliveries ? await summarizeVoiceTraceSinkDeliveries(traceDeliveries) : undefined;
33156
33181
  const auditDeliverySummary = auditDeliveries ? await summarizeVoiceAuditSinkDeliveries(auditDeliveries) : undefined;
33157
33182
  const operationArtifacts = operationsRecords.map((record) => createOperationArtifact(record, options.links?.operationsRecord?.(record.sessionId)));
33158
- const artifacts = addArtifactDownloadHrefs(await verifyArtifacts([...operationArtifacts, ...options.artifacts ?? []], options.artifactIntegrity), options.links);
33183
+ const sessionSnapshotArtifacts = sessionSnapshots.map((snapshot) => createSessionSnapshotArtifact(snapshot, options.links?.sessionSnapshot?.(snapshot.sessionId)));
33184
+ const callDebuggerArtifacts = callDebuggerReports.map((report) => createCallDebuggerArtifact(report, options.links?.callDebugger?.(report.sessionId)));
33185
+ const artifacts = addArtifactDownloadHrefs(await verifyArtifacts([
33186
+ ...operationArtifacts,
33187
+ ...sessionSnapshotArtifacts,
33188
+ ...callDebuggerArtifacts,
33189
+ ...options.artifacts ?? []
33190
+ ], options.artifactIntegrity), options.links);
33159
33191
  const operationHrefBySessionId = new Map(sessionIds.map((sessionId) => [
33160
33192
  sessionId,
33161
33193
  options.links?.operationsRecord?.(sessionId)
@@ -3,7 +3,9 @@ import type { S3Client, S3Options } from 'bun';
3
3
  import { Database } from 'bun:sqlite';
4
4
  import { type VoiceAuditSinkDeliveryQueueSummary, type VoiceAuditSinkDeliveryRecord, type VoiceAuditSinkDeliveryStore } from './auditSinks';
5
5
  import type { VoiceAuditEventStore, VoiceAuditEventType } from './audit';
6
+ import type { VoiceCallDebuggerReport } from './callDebugger';
6
7
  import { type VoiceOperationsRecord } from './operationsRecord';
8
+ import type { VoiceSessionSnapshot } from './sessionSnapshot';
7
9
  import { type VoiceTraceSinkDeliveryQueueSummary } from './queue';
8
10
  import { type StoredVoiceTraceEvent, type VoiceTraceEventStore, type VoiceTraceEventType, type VoiceTraceRedactionConfig, type VoiceTraceSinkDeliveryRecord, type VoiceTraceSinkDeliveryStore, type VoiceTraceSummary } from './trace';
9
11
  import type { VoicePostgresClient } from './postgresStore';
@@ -41,7 +43,7 @@ export type VoiceObservabilityExportRecordValidationOptions = {
41
43
  };
42
44
  export declare const validateVoiceObservabilityExportRecord: (input: unknown, options?: VoiceObservabilityExportRecordValidationOptions) => VoiceObservabilityExportValidationResult;
43
45
  export declare const assertVoiceObservabilityExportRecord: (input: unknown, options?: VoiceObservabilityExportRecordValidationOptions) => VoiceObservabilityExportValidationResult;
44
- export type VoiceObservabilityExportArtifactKind = 'incident' | 'markdown' | 'operations-record' | 'proof-pack' | 'readiness' | 'screenshot' | 'slo' | 'trace' | 'audit' | 'custom';
46
+ export type VoiceObservabilityExportArtifactKind = 'call-debugger' | 'incident' | 'markdown' | 'operations-record' | 'proof-pack' | 'readiness' | 'screenshot' | 'session-snapshot' | 'slo' | 'trace' | 'audit' | 'custom';
45
47
  export type VoiceObservabilityExportArtifactChecksum = {
46
48
  algorithm: 'sha256';
47
49
  value: string;
@@ -380,11 +382,15 @@ export type VoiceObservabilityExportOptions = {
380
382
  includeOperationsRecords?: boolean;
381
383
  links?: {
382
384
  artifactDownload?: (artifact: VoiceObservabilityExportArtifact) => string;
385
+ callDebugger?: (sessionId: string) => string;
383
386
  operationsRecord?: (sessionId: string) => string;
387
+ sessionSnapshot?: (sessionId: string) => string;
384
388
  };
389
+ callDebuggerReports?: VoiceCallDebuggerReport[];
385
390
  operationsRecords?: VoiceOperationsRecord[];
386
391
  redact?: VoiceTraceRedactionConfig;
387
392
  sessionIds?: string[];
393
+ sessionSnapshots?: VoiceSessionSnapshot[];
388
394
  store?: VoiceTraceEventStore;
389
395
  traceDeliveries?: VoiceTraceSinkDeliveryRecord[] | VoiceTraceSinkDeliveryStore;
390
396
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.405",
3
+ "version": "0.0.22-beta.407",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",