@absolutejs/voice 0.0.22-beta.46 → 0.0.22-beta.48

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.
@@ -1,3 +1,4 @@
1
1
  export { VoiceStreamService } from './voice-stream.service';
2
2
  export { VoiceControllerService } from './voice-controller.service';
3
3
  export { VoiceProviderStatusService } from './voice-provider-status.service';
4
+ export { VoiceWorkflowStatusService } from './voice-workflow-status.service';
@@ -1457,7 +1457,132 @@ VoiceProviderStatusService = __decorateElement(_init, 0, "VoiceProviderStatusSer
1457
1457
  __runInitializers(_init, 1, VoiceProviderStatusService);
1458
1458
  __decoratorMetadata(_init, VoiceProviderStatusService);
1459
1459
  let _VoiceProviderStatusService = VoiceProviderStatusService;
1460
+ // src/angular/voice-workflow-status.service.ts
1461
+ import { computed as computed4, Injectable as Injectable4, signal as signal4 } from "@angular/core";
1462
+
1463
+ // src/client/workflowStatus.ts
1464
+ var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
1465
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1466
+ const response = await fetchImpl(path);
1467
+ if (!response.ok) {
1468
+ throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
1469
+ }
1470
+ return await response.json();
1471
+ };
1472
+ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
1473
+ const listeners = new Set;
1474
+ let closed = false;
1475
+ let timer;
1476
+ let snapshot = {
1477
+ error: null,
1478
+ isLoading: false
1479
+ };
1480
+ const emit = () => {
1481
+ for (const listener of listeners) {
1482
+ listener();
1483
+ }
1484
+ };
1485
+ const refresh = async () => {
1486
+ if (closed) {
1487
+ return snapshot.report;
1488
+ }
1489
+ snapshot = {
1490
+ ...snapshot,
1491
+ error: null,
1492
+ isLoading: true
1493
+ };
1494
+ emit();
1495
+ try {
1496
+ const report = await fetchVoiceWorkflowStatus(path, options);
1497
+ snapshot = {
1498
+ error: null,
1499
+ isLoading: false,
1500
+ report,
1501
+ updatedAt: Date.now()
1502
+ };
1503
+ emit();
1504
+ return report;
1505
+ } catch (error) {
1506
+ snapshot = {
1507
+ ...snapshot,
1508
+ error: error instanceof Error ? error.message : String(error),
1509
+ isLoading: false
1510
+ };
1511
+ emit();
1512
+ throw error;
1513
+ }
1514
+ };
1515
+ const close = () => {
1516
+ closed = true;
1517
+ if (timer) {
1518
+ clearInterval(timer);
1519
+ timer = undefined;
1520
+ }
1521
+ listeners.clear();
1522
+ };
1523
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
1524
+ timer = setInterval(() => {
1525
+ refresh().catch(() => {});
1526
+ }, options.intervalMs);
1527
+ }
1528
+ return {
1529
+ close,
1530
+ getServerSnapshot: () => snapshot,
1531
+ getSnapshot: () => snapshot,
1532
+ refresh,
1533
+ subscribe: (listener) => {
1534
+ listeners.add(listener);
1535
+ return () => {
1536
+ listeners.delete(listener);
1537
+ };
1538
+ }
1539
+ };
1540
+ };
1541
+
1542
+ // src/angular/voice-workflow-status.service.ts
1543
+ var _dec = [
1544
+ Injectable4({ providedIn: "root" })
1545
+ ];
1546
+ var _init = __decoratorStart(undefined);
1547
+
1548
+ class VoiceWorkflowStatusService {
1549
+ connect(path = "/evals/scenarios/json", options = {}) {
1550
+ const store = createVoiceWorkflowStatusStore(path, options);
1551
+ const errorSignal = signal4(null);
1552
+ const isLoadingSignal = signal4(false);
1553
+ const reportSignal = signal4(undefined);
1554
+ const updatedAtSignal = signal4(undefined);
1555
+ const sync = () => {
1556
+ const snapshot = store.getSnapshot();
1557
+ errorSignal.set(snapshot.error);
1558
+ isLoadingSignal.set(snapshot.isLoading);
1559
+ reportSignal.set(snapshot.report);
1560
+ updatedAtSignal.set(snapshot.updatedAt);
1561
+ };
1562
+ const unsubscribe = store.subscribe(sync);
1563
+ sync();
1564
+ if (typeof window !== "undefined") {
1565
+ store.refresh().catch(() => {});
1566
+ }
1567
+ return {
1568
+ close: () => {
1569
+ unsubscribe();
1570
+ store.close();
1571
+ },
1572
+ error: computed4(() => errorSignal()),
1573
+ isLoading: computed4(() => isLoadingSignal()),
1574
+ refresh: store.refresh,
1575
+ report: computed4(() => reportSignal()),
1576
+ updatedAt: computed4(() => updatedAtSignal())
1577
+ };
1578
+ }
1579
+ }
1580
+ VoiceWorkflowStatusService = __decorateElement(_init, 0, "VoiceWorkflowStatusService", _dec, VoiceWorkflowStatusService);
1581
+ __runInitializers(_init, 1, VoiceWorkflowStatusService);
1582
+ __decoratorMetadata(_init, VoiceWorkflowStatusService);
1583
+ let _VoiceWorkflowStatusService = VoiceWorkflowStatusService;
1460
1584
  export {
1585
+ VoiceWorkflowStatusService,
1461
1586
  VoiceStreamService,
1462
1587
  VoiceProviderStatusService,
1463
1588
  VoiceControllerService
@@ -0,0 +1,12 @@
1
+ import { type VoiceWorkflowStatusClientOptions } from '../client/workflowStatus';
2
+ import type { VoiceScenarioEvalReport } from '../evalRoutes';
3
+ export declare class VoiceWorkflowStatusService {
4
+ connect(path?: string, options?: VoiceWorkflowStatusClientOptions): {
5
+ close: () => void;
6
+ error: import("@angular/core").Signal<string | null>;
7
+ isLoading: import("@angular/core").Signal<boolean>;
8
+ refresh: () => Promise<VoiceScenarioEvalReport | undefined>;
9
+ report: import("@angular/core").Signal<VoiceScenarioEvalReport | undefined>;
10
+ updatedAt: import("@angular/core").Signal<number | undefined>;
11
+ };
12
+ }
@@ -6,4 +6,6 @@ export { bindVoiceBargeIn, createVoiceDuplexController } from './duplex';
6
6
  export { bindVoiceHTMX } from './htmx';
7
7
  export { createMicrophoneCapture } from './microphone';
8
8
  export { createVoiceProviderStatusStore, fetchVoiceProviderStatus } from './providerStatus';
9
+ export { createVoiceWorkflowStatusStore, fetchVoiceWorkflowStatus } from './workflowStatus';
9
10
  export type { VoiceProviderStatusClientOptions, VoiceProviderStatusSnapshot } from './providerStatus';
11
+ export type { VoiceWorkflowStatusClientOptions, VoiceWorkflowStatusSnapshot } from './workflowStatus';
@@ -1702,9 +1702,89 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
1702
1702
  }
1703
1703
  };
1704
1704
  };
1705
+ // src/client/workflowStatus.ts
1706
+ var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
1707
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1708
+ const response = await fetchImpl(path);
1709
+ if (!response.ok) {
1710
+ throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
1711
+ }
1712
+ return await response.json();
1713
+ };
1714
+ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
1715
+ const listeners = new Set;
1716
+ let closed = false;
1717
+ let timer;
1718
+ let snapshot = {
1719
+ error: null,
1720
+ isLoading: false
1721
+ };
1722
+ const emit = () => {
1723
+ for (const listener of listeners) {
1724
+ listener();
1725
+ }
1726
+ };
1727
+ const refresh = async () => {
1728
+ if (closed) {
1729
+ return snapshot.report;
1730
+ }
1731
+ snapshot = {
1732
+ ...snapshot,
1733
+ error: null,
1734
+ isLoading: true
1735
+ };
1736
+ emit();
1737
+ try {
1738
+ const report = await fetchVoiceWorkflowStatus(path, options);
1739
+ snapshot = {
1740
+ error: null,
1741
+ isLoading: false,
1742
+ report,
1743
+ updatedAt: Date.now()
1744
+ };
1745
+ emit();
1746
+ return report;
1747
+ } catch (error) {
1748
+ snapshot = {
1749
+ ...snapshot,
1750
+ error: error instanceof Error ? error.message : String(error),
1751
+ isLoading: false
1752
+ };
1753
+ emit();
1754
+ throw error;
1755
+ }
1756
+ };
1757
+ const close = () => {
1758
+ closed = true;
1759
+ if (timer) {
1760
+ clearInterval(timer);
1761
+ timer = undefined;
1762
+ }
1763
+ listeners.clear();
1764
+ };
1765
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
1766
+ timer = setInterval(() => {
1767
+ refresh().catch(() => {});
1768
+ }, options.intervalMs);
1769
+ }
1770
+ return {
1771
+ close,
1772
+ getServerSnapshot: () => snapshot,
1773
+ getSnapshot: () => snapshot,
1774
+ refresh,
1775
+ subscribe: (listener) => {
1776
+ listeners.add(listener);
1777
+ return () => {
1778
+ listeners.delete(listener);
1779
+ };
1780
+ }
1781
+ };
1782
+ };
1705
1783
  export {
1784
+ fetchVoiceWorkflowStatus,
1706
1785
  fetchVoiceProviderStatus,
1707
1786
  decodeVoiceAudioChunk,
1787
+ createVoiceWorkflowStatusStore,
1708
1788
  createVoiceStream,
1709
1789
  createVoiceProviderStatusStore,
1710
1790
  createVoiceDuplexController,
@@ -0,0 +1,19 @@
1
+ import type { VoiceScenarioEvalReport } from '../evalRoutes';
2
+ export type VoiceWorkflowStatusClientOptions = {
3
+ fetch?: typeof fetch;
4
+ intervalMs?: number;
5
+ };
6
+ export type VoiceWorkflowStatusSnapshot = {
7
+ error: string | null;
8
+ isLoading: boolean;
9
+ report?: VoiceScenarioEvalReport;
10
+ updatedAt?: number;
11
+ };
12
+ export declare const fetchVoiceWorkflowStatus: (path?: string, options?: Pick<VoiceWorkflowStatusClientOptions, "fetch">) => Promise<VoiceScenarioEvalReport>;
13
+ export declare const createVoiceWorkflowStatusStore: (path?: string, options?: VoiceWorkflowStatusClientOptions) => {
14
+ close: () => void;
15
+ getServerSnapshot: () => VoiceWorkflowStatusSnapshot;
16
+ getSnapshot: () => VoiceWorkflowStatusSnapshot;
17
+ refresh: () => Promise<VoiceScenarioEvalReport | undefined>;
18
+ subscribe: (listener: () => void) => () => void;
19
+ };
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { createVoiceAssistant, createVoiceExperiment, summarizeVoiceAssistantRun
3
3
  export { createVoiceAssistantHealthHTMLHandler, createVoiceAssistantHealthJSONHandler, createVoiceAssistantHealthRoutes, renderVoiceAssistantHealthHTML, summarizeVoiceAssistantHealth } from './assistantHealth';
4
4
  export { buildVoiceDiagnosticsMarkdown, createVoiceDiagnosticsRoutes, resolveVoiceDiagnosticsTraceFilter } from './diagnosticsRoutes';
5
5
  export { compareVoiceEvalBaseline, createVoiceFileEvalBaselineStore, createVoiceFileScenarioFixtureStore, createVoiceEvalRoutes, renderVoiceEvalBaselineHTML, renderVoiceEvalHTML, renderVoiceScenarioEvalHTML, renderVoiceScenarioFixtureEvalHTML, runVoiceScenarioEvals, runVoiceScenarioFixtureEvals, runVoiceSessionEvals } from './evalRoutes';
6
- export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, createVoiceWorkflowScenario, recordVoiceWorkflowContractTrace, validateVoiceWorkflowRouteResult } from './workflowContract';
6
+ export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, createVoiceWorkflowContractPreset, createVoiceWorkflowScenario, recordVoiceWorkflowContractTrace, validateVoiceWorkflowRouteResult } from './workflowContract';
7
7
  export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, createVoiceSessionsHTMLHandler, createVoiceSessionsJSONHandler, renderVoiceSessionsHTML, summarizeVoiceSessions, summarizeVoiceSessionReplay } from './sessionReplay';
8
8
  export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
9
9
  export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
@@ -42,7 +42,7 @@ export type { VoiceAssistantHealthFailure, VoiceAssistantHealthHTMLHandlerOption
42
42
  export type { VoiceAssistantMemoryBinding, VoiceAssistantMemoryHandle, VoiceAssistantMemoryOptions, VoiceAssistantMemoryRecord, VoiceAssistantMemoryStore } from './assistantMemory';
43
43
  export type { VoiceDiagnosticsRoutesOptions } from './diagnosticsRoutes';
44
44
  export type { VoiceEvalBaselineComparison, VoiceEvalBaselineComparisonOptions, VoiceEvalBaselineStore, VoiceEvalBaselineSummary, VoiceEvalLink, VoiceEvalReport, VoiceEvalRoutesOptions, VoiceEvalSessionReport, VoiceEvalStatus, VoiceEvalTrendBucket, VoiceScenarioEvalDefinition, VoiceScenarioEvalReport, VoiceScenarioEvalResult, VoiceScenarioEvalSessionResult, VoiceScenarioFixture, VoiceScenarioFixtureEvalReport, VoiceScenarioFixtureEvalResult, VoiceScenarioFixtureStore } from './evalRoutes';
45
- export type { VoiceWorkflowContract, VoiceWorkflowContractDefinition, VoiceWorkflowContractField, VoiceWorkflowContractFieldMatch, VoiceWorkflowContractTracePayload, VoiceWorkflowContractValidation, VoiceWorkflowContractValidationIssue, VoiceWorkflowOutcome } from './workflowContract';
45
+ export type { VoiceWorkflowContract, VoiceWorkflowContractDefinition, VoiceWorkflowContractField, VoiceWorkflowContractFieldMatch, VoiceWorkflowContractPresetName, VoiceWorkflowContractPresetOptions, VoiceWorkflowContractTracePayload, VoiceWorkflowContractValidation, VoiceWorkflowContractValidationIssue, VoiceWorkflowOutcome } from './workflowContract';
46
46
  export type { VoiceSessionListHTMLHandlerOptions, VoiceSessionListItem, VoiceSessionListOptions, VoiceSessionListRoutesOptions, VoiceSessionListStatus, VoiceSessionReplay, VoiceSessionReplayHTMLHandlerOptions, VoiceSessionReplayOptions, VoiceSessionReplayRoutesOptions, VoiceSessionReplayTurn } from './sessionReplay';
47
47
  export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterHealthOptions, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterProviderHealth, VoiceProviderRouterProviderProfile, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
48
48
  export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
package/dist/index.js CHANGED
@@ -8562,6 +8562,152 @@ var createVoiceWorkflowContract = (definition) => ({
8562
8562
  toScenarioEval: (overrides) => createVoiceWorkflowScenario(definition, overrides),
8563
8563
  validateRouteResult: (routeResult) => validateVoiceWorkflowRouteResult(definition, routeResult)
8564
8564
  });
8565
+ var presetDefinitions = {
8566
+ "appointment-booking": {
8567
+ description: "Appointment booking should complete with enough identity, appointment, and follow-up details to act on.",
8568
+ fields: [
8569
+ { aliases: ["name", "customer.name"], label: "Caller name", path: "caller.name" },
8570
+ {
8571
+ aliases: ["phone", "customer.phone"],
8572
+ label: "Caller phone",
8573
+ path: "caller.phone"
8574
+ },
8575
+ {
8576
+ aliases: ["appointment.start", "appointment.time", "scheduledAt"],
8577
+ label: "Appointment time",
8578
+ path: "appointment.startsAt"
8579
+ },
8580
+ {
8581
+ aliases: ["summary", "assistantSummary"],
8582
+ label: "Summary",
8583
+ path: "appointment.summary"
8584
+ }
8585
+ ],
8586
+ id: "appointment-booking",
8587
+ label: "Appointment booking",
8588
+ outcome: "complete",
8589
+ requiredDisposition: "completed"
8590
+ },
8591
+ "lead-qualification": {
8592
+ description: "Lead qualification should complete with contact, need, qualification, and next-step fields.",
8593
+ fields: [
8594
+ { aliases: ["name", "lead.name"], label: "Lead name", path: "contact.name" },
8595
+ {
8596
+ aliases: ["email", "lead.email"],
8597
+ label: "Lead email",
8598
+ path: "contact.email"
8599
+ },
8600
+ {
8601
+ aliases: ["need", "pain", "summary"],
8602
+ label: "Need",
8603
+ path: "qualification.need"
8604
+ },
8605
+ {
8606
+ aliases: ["qualified", "qualification.qualified"],
8607
+ label: "Qualified",
8608
+ match: "boolean",
8609
+ path: "qualification.isQualified"
8610
+ },
8611
+ {
8612
+ aliases: ["nextStep", "followUp"],
8613
+ label: "Next step",
8614
+ path: "qualification.nextStep"
8615
+ }
8616
+ ],
8617
+ id: "lead-qualification",
8618
+ label: "Lead qualification",
8619
+ outcome: "complete",
8620
+ requiredDisposition: "completed"
8621
+ },
8622
+ "support-triage": {
8623
+ description: "Support triage should capture identity, issue summary, severity, and the operational follow-up.",
8624
+ fields: [
8625
+ {
8626
+ aliases: ["name", "customer.name"],
8627
+ label: "Customer name",
8628
+ path: "customer.name"
8629
+ },
8630
+ {
8631
+ aliases: ["issue", "summary", "assistantSummary"],
8632
+ label: "Issue summary",
8633
+ path: "issue.summary"
8634
+ },
8635
+ {
8636
+ aliases: ["priority", "severity"],
8637
+ label: "Severity",
8638
+ path: "issue.severity"
8639
+ },
8640
+ {
8641
+ aliases: ["nextStep", "task.title"],
8642
+ label: "Next step",
8643
+ path: "resolution.nextStep"
8644
+ }
8645
+ ],
8646
+ id: "support-triage",
8647
+ label: "Support triage",
8648
+ outcome: "complete",
8649
+ requiredDisposition: "completed"
8650
+ },
8651
+ "transfer-handoff": {
8652
+ description: "Transfer handoff should produce a routed transfer plus handoff evidence.",
8653
+ fields: [
8654
+ {
8655
+ aliases: ["target", "callTarget"],
8656
+ label: "Transfer target",
8657
+ path: "transfer.target"
8658
+ },
8659
+ {
8660
+ aliases: ["reason", "callReason"],
8661
+ label: "Transfer reason",
8662
+ path: "transfer.reason"
8663
+ },
8664
+ {
8665
+ aliases: ["summary", "assistantSummary"],
8666
+ label: "Transfer summary",
8667
+ path: "transfer.summary"
8668
+ }
8669
+ ],
8670
+ id: "transfer-handoff",
8671
+ label: "Transfer handoff",
8672
+ outcome: "transfer",
8673
+ requiredDisposition: "transferred",
8674
+ requiredHandoffActions: ["transfer"]
8675
+ },
8676
+ "voicemail-callback": {
8677
+ description: "Voicemail callback should preserve enough caller and callback context for follow-up.",
8678
+ fields: [
8679
+ {
8680
+ aliases: ["name", "caller.name"],
8681
+ label: "Caller name",
8682
+ path: "voicemail.callerName"
8683
+ },
8684
+ {
8685
+ aliases: ["phone", "caller.phone"],
8686
+ label: "Callback phone",
8687
+ path: "voicemail.callbackPhone"
8688
+ },
8689
+ {
8690
+ aliases: ["message", "summary", "assistantSummary"],
8691
+ label: "Voicemail summary",
8692
+ path: "voicemail.summary"
8693
+ }
8694
+ ],
8695
+ id: "voicemail-callback",
8696
+ label: "Voicemail callback",
8697
+ outcome: "voicemail",
8698
+ requiredDisposition: "voicemail",
8699
+ requiredHandoffActions: ["voicemail"]
8700
+ }
8701
+ };
8702
+ var createVoiceWorkflowContractPreset = (name, options = {}) => {
8703
+ const preset = presetDefinitions[name];
8704
+ return createVoiceWorkflowContract({
8705
+ ...preset,
8706
+ ...options,
8707
+ fields: options.fields ?? preset.fields,
8708
+ id: options.id ?? preset.id
8709
+ });
8710
+ };
8565
8711
  var recordVoiceWorkflowContractTrace = async (input) => input.store.append({
8566
8712
  at: input.at ?? Date.now(),
8567
8713
  payload: {
@@ -13608,6 +13754,7 @@ export {
13608
13754
  createVoiceZendeskTicketSyncSinks,
13609
13755
  createVoiceZendeskTicketSink,
13610
13756
  createVoiceWorkflowScenario,
13757
+ createVoiceWorkflowContractPreset,
13611
13758
  createVoiceWorkflowContractHandler,
13612
13759
  createVoiceWorkflowContract,
13613
13760
  createVoiceWebhookHandoffAdapter,
@@ -1,3 +1,4 @@
1
1
  export { useVoiceStream } from './useVoiceStream';
2
2
  export { useVoiceController } from './useVoiceController';
3
3
  export { useVoiceProviderStatus } from './useVoiceProviderStatus';
4
+ export { useVoiceWorkflowStatus } from './useVoiceWorkflowStatus';
@@ -1379,7 +1379,106 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
1379
1379
  refresh: store.refresh
1380
1380
  };
1381
1381
  };
1382
+ // src/react/useVoiceWorkflowStatus.tsx
1383
+ import { useEffect as useEffect4, useRef as useRef4, useSyncExternalStore as useSyncExternalStore4 } from "react";
1384
+
1385
+ // src/client/workflowStatus.ts
1386
+ var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
1387
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1388
+ const response = await fetchImpl(path);
1389
+ if (!response.ok) {
1390
+ throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
1391
+ }
1392
+ return await response.json();
1393
+ };
1394
+ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
1395
+ const listeners = new Set;
1396
+ let closed = false;
1397
+ let timer;
1398
+ let snapshot = {
1399
+ error: null,
1400
+ isLoading: false
1401
+ };
1402
+ const emit = () => {
1403
+ for (const listener of listeners) {
1404
+ listener();
1405
+ }
1406
+ };
1407
+ const refresh = async () => {
1408
+ if (closed) {
1409
+ return snapshot.report;
1410
+ }
1411
+ snapshot = {
1412
+ ...snapshot,
1413
+ error: null,
1414
+ isLoading: true
1415
+ };
1416
+ emit();
1417
+ try {
1418
+ const report = await fetchVoiceWorkflowStatus(path, options);
1419
+ snapshot = {
1420
+ error: null,
1421
+ isLoading: false,
1422
+ report,
1423
+ updatedAt: Date.now()
1424
+ };
1425
+ emit();
1426
+ return report;
1427
+ } catch (error) {
1428
+ snapshot = {
1429
+ ...snapshot,
1430
+ error: error instanceof Error ? error.message : String(error),
1431
+ isLoading: false
1432
+ };
1433
+ emit();
1434
+ throw error;
1435
+ }
1436
+ };
1437
+ const close = () => {
1438
+ closed = true;
1439
+ if (timer) {
1440
+ clearInterval(timer);
1441
+ timer = undefined;
1442
+ }
1443
+ listeners.clear();
1444
+ };
1445
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
1446
+ timer = setInterval(() => {
1447
+ refresh().catch(() => {});
1448
+ }, options.intervalMs);
1449
+ }
1450
+ return {
1451
+ close,
1452
+ getServerSnapshot: () => snapshot,
1453
+ getSnapshot: () => snapshot,
1454
+ refresh,
1455
+ subscribe: (listener) => {
1456
+ listeners.add(listener);
1457
+ return () => {
1458
+ listeners.delete(listener);
1459
+ };
1460
+ }
1461
+ };
1462
+ };
1463
+
1464
+ // src/react/useVoiceWorkflowStatus.tsx
1465
+ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
1466
+ const storeRef = useRef4(null);
1467
+ if (!storeRef.current) {
1468
+ storeRef.current = createVoiceWorkflowStatusStore(path, options);
1469
+ }
1470
+ const store = storeRef.current;
1471
+ useEffect4(() => {
1472
+ store.refresh().catch(() => {});
1473
+ return () => store.close();
1474
+ }, [store]);
1475
+ return {
1476
+ ...useSyncExternalStore4(store.subscribe, store.getSnapshot, store.getServerSnapshot),
1477
+ refresh: store.refresh
1478
+ };
1479
+ };
1382
1480
  export {
1481
+ useVoiceWorkflowStatus,
1383
1482
  useVoiceStream,
1384
1483
  useVoiceProviderStatus,
1385
1484
  useVoiceController
@@ -0,0 +1,8 @@
1
+ import { type VoiceWorkflowStatusClientOptions } from '../client/workflowStatus';
2
+ export declare const useVoiceWorkflowStatus: (path?: string, options?: VoiceWorkflowStatusClientOptions) => {
3
+ refresh: () => Promise<import("..").VoiceScenarioEvalReport | undefined>;
4
+ error: string | null;
5
+ isLoading: boolean;
6
+ report?: import("..").VoiceScenarioEvalReport;
7
+ updatedAt?: number;
8
+ };
@@ -0,0 +1,8 @@
1
+ import type { VoiceWorkflowStatusClientOptions } from '../client/workflowStatus';
2
+ export declare const createVoiceWorkflowStatus: (path?: string, options?: VoiceWorkflowStatusClientOptions) => {
3
+ close: () => void;
4
+ getServerSnapshot: () => import("../client").VoiceWorkflowStatusSnapshot;
5
+ getSnapshot: () => import("../client").VoiceWorkflowStatusSnapshot;
6
+ refresh: () => Promise<import("..").VoiceScenarioEvalReport | undefined>;
7
+ subscribe: (listener: () => void) => () => void;
8
+ };
@@ -1,3 +1,4 @@
1
1
  export { createVoiceStream } from './createVoiceStream';
2
2
  export { createVoiceProviderStatus } from './createVoiceProviderStatus';
3
+ export { createVoiceWorkflowStatus } from './createVoiceWorkflowStatus';
3
4
  export { createVoiceController } from '../client/controller';
@@ -666,6 +666,87 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
666
666
 
667
667
  // src/svelte/createVoiceProviderStatus.ts
668
668
  var createVoiceProviderStatus = (path = "/api/provider-status", options = {}) => createVoiceProviderStatusStore(path, options);
669
+ // src/client/workflowStatus.ts
670
+ var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
671
+ const fetchImpl = options.fetch ?? globalThis.fetch;
672
+ const response = await fetchImpl(path);
673
+ if (!response.ok) {
674
+ throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
675
+ }
676
+ return await response.json();
677
+ };
678
+ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
679
+ const listeners = new Set;
680
+ let closed = false;
681
+ let timer;
682
+ let snapshot = {
683
+ error: null,
684
+ isLoading: false
685
+ };
686
+ const emit = () => {
687
+ for (const listener of listeners) {
688
+ listener();
689
+ }
690
+ };
691
+ const refresh = async () => {
692
+ if (closed) {
693
+ return snapshot.report;
694
+ }
695
+ snapshot = {
696
+ ...snapshot,
697
+ error: null,
698
+ isLoading: true
699
+ };
700
+ emit();
701
+ try {
702
+ const report = await fetchVoiceWorkflowStatus(path, options);
703
+ snapshot = {
704
+ error: null,
705
+ isLoading: false,
706
+ report,
707
+ updatedAt: Date.now()
708
+ };
709
+ emit();
710
+ return report;
711
+ } catch (error) {
712
+ snapshot = {
713
+ ...snapshot,
714
+ error: error instanceof Error ? error.message : String(error),
715
+ isLoading: false
716
+ };
717
+ emit();
718
+ throw error;
719
+ }
720
+ };
721
+ const close = () => {
722
+ closed = true;
723
+ if (timer) {
724
+ clearInterval(timer);
725
+ timer = undefined;
726
+ }
727
+ listeners.clear();
728
+ };
729
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
730
+ timer = setInterval(() => {
731
+ refresh().catch(() => {});
732
+ }, options.intervalMs);
733
+ }
734
+ return {
735
+ close,
736
+ getServerSnapshot: () => snapshot,
737
+ getSnapshot: () => snapshot,
738
+ refresh,
739
+ subscribe: (listener) => {
740
+ listeners.add(listener);
741
+ return () => {
742
+ listeners.delete(listener);
743
+ };
744
+ }
745
+ };
746
+ };
747
+
748
+ // src/svelte/createVoiceWorkflowStatus.ts
749
+ var createVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => createVoiceWorkflowStatusStore(path, options);
669
750
  // src/client/htmx.ts
670
751
  var DEFAULT_EVENT_NAME = "voice-refresh";
671
752
  var DEFAULT_QUERY_PARAM = "sessionId";
@@ -1296,6 +1377,7 @@ var createVoiceController = (path, options = {}) => {
1296
1377
  };
1297
1378
  };
1298
1379
  export {
1380
+ createVoiceWorkflowStatus,
1299
1381
  createVoiceStream2 as createVoiceStream,
1300
1382
  createVoiceProviderStatus,
1301
1383
  createVoiceController
@@ -1,3 +1,4 @@
1
1
  export { useVoiceStream } from './useVoiceStream';
2
2
  export { useVoiceController } from './useVoiceController';
3
3
  export { useVoiceProviderStatus } from './useVoiceProviderStatus';
4
+ export { useVoiceWorkflowStatus } from './useVoiceWorkflowStatus';
package/dist/vue/index.js CHANGED
@@ -1428,7 +1428,121 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
1428
1428
  updatedAt
1429
1429
  };
1430
1430
  };
1431
+ // src/vue/useVoiceWorkflowStatus.ts
1432
+ import { onUnmounted as onUnmounted4, ref as ref4, shallowRef as shallowRef4 } from "vue";
1433
+
1434
+ // src/client/workflowStatus.ts
1435
+ var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
1436
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1437
+ const response = await fetchImpl(path);
1438
+ if (!response.ok) {
1439
+ throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
1440
+ }
1441
+ return await response.json();
1442
+ };
1443
+ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
1444
+ const listeners = new Set;
1445
+ let closed = false;
1446
+ let timer;
1447
+ let snapshot = {
1448
+ error: null,
1449
+ isLoading: false
1450
+ };
1451
+ const emit = () => {
1452
+ for (const listener of listeners) {
1453
+ listener();
1454
+ }
1455
+ };
1456
+ const refresh = async () => {
1457
+ if (closed) {
1458
+ return snapshot.report;
1459
+ }
1460
+ snapshot = {
1461
+ ...snapshot,
1462
+ error: null,
1463
+ isLoading: true
1464
+ };
1465
+ emit();
1466
+ try {
1467
+ const report = await fetchVoiceWorkflowStatus(path, options);
1468
+ snapshot = {
1469
+ error: null,
1470
+ isLoading: false,
1471
+ report,
1472
+ updatedAt: Date.now()
1473
+ };
1474
+ emit();
1475
+ return report;
1476
+ } catch (error) {
1477
+ snapshot = {
1478
+ ...snapshot,
1479
+ error: error instanceof Error ? error.message : String(error),
1480
+ isLoading: false
1481
+ };
1482
+ emit();
1483
+ throw error;
1484
+ }
1485
+ };
1486
+ const close = () => {
1487
+ closed = true;
1488
+ if (timer) {
1489
+ clearInterval(timer);
1490
+ timer = undefined;
1491
+ }
1492
+ listeners.clear();
1493
+ };
1494
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
1495
+ timer = setInterval(() => {
1496
+ refresh().catch(() => {});
1497
+ }, options.intervalMs);
1498
+ }
1499
+ return {
1500
+ close,
1501
+ getServerSnapshot: () => snapshot,
1502
+ getSnapshot: () => snapshot,
1503
+ refresh,
1504
+ subscribe: (listener) => {
1505
+ listeners.add(listener);
1506
+ return () => {
1507
+ listeners.delete(listener);
1508
+ };
1509
+ }
1510
+ };
1511
+ };
1512
+
1513
+ // src/vue/useVoiceWorkflowStatus.ts
1514
+ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
1515
+ const store = createVoiceWorkflowStatusStore(path, options);
1516
+ const error = ref4(null);
1517
+ const isLoading = ref4(false);
1518
+ const report = shallowRef4(undefined);
1519
+ const updatedAt = ref4(undefined);
1520
+ const sync = () => {
1521
+ const snapshot = store.getSnapshot();
1522
+ error.value = snapshot.error;
1523
+ isLoading.value = snapshot.isLoading;
1524
+ report.value = snapshot.report;
1525
+ updatedAt.value = snapshot.updatedAt;
1526
+ };
1527
+ const unsubscribe = store.subscribe(sync);
1528
+ sync();
1529
+ if (typeof window !== "undefined") {
1530
+ store.refresh().catch(() => {});
1531
+ }
1532
+ onUnmounted4(() => {
1533
+ unsubscribe();
1534
+ store.close();
1535
+ });
1536
+ return {
1537
+ error,
1538
+ isLoading,
1539
+ refresh: store.refresh,
1540
+ report,
1541
+ updatedAt
1542
+ };
1543
+ };
1431
1544
  export {
1545
+ useVoiceWorkflowStatus,
1432
1546
  useVoiceStream,
1433
1547
  useVoiceProviderStatus,
1434
1548
  useVoiceController
@@ -0,0 +1,9 @@
1
+ import { type VoiceWorkflowStatusClientOptions } from '../client/workflowStatus';
2
+ import type { VoiceScenarioEvalReport } from '../evalRoutes';
3
+ export declare const useVoiceWorkflowStatus: (path?: string, options?: VoiceWorkflowStatusClientOptions) => {
4
+ error: import("vue").Ref<string | null, string | null>;
5
+ isLoading: import("vue").Ref<boolean, boolean>;
6
+ refresh: () => Promise<VoiceScenarioEvalReport | undefined>;
7
+ report: import("vue").ShallowRef<VoiceScenarioEvalReport | undefined, VoiceScenarioEvalReport | undefined>;
8
+ updatedAt: import("vue").Ref<number | undefined, number | undefined>;
9
+ };
@@ -32,6 +32,11 @@ export type VoiceWorkflowContractDefinition<TResult = unknown> = {
32
32
  routeResult: VoiceRouteResult<TResult>;
33
33
  }) => VoiceWorkflowContractValidationIssue[];
34
34
  };
35
+ export type VoiceWorkflowContractPresetName = 'appointment-booking' | 'lead-qualification' | 'support-triage' | 'transfer-handoff' | 'voicemail-callback';
36
+ export type VoiceWorkflowContractPresetOptions<TResult = unknown> = Partial<Omit<VoiceWorkflowContractDefinition<TResult>, 'fields' | 'id'>> & {
37
+ fields?: VoiceWorkflowContractField[];
38
+ id?: string;
39
+ };
35
40
  export type VoiceWorkflowContractValidationIssue = {
36
41
  code: string;
37
42
  field?: string;
@@ -62,6 +67,7 @@ export type VoiceWorkflowContractTracePayload = {
62
67
  export declare const validateVoiceWorkflowRouteResult: <TResult = unknown>(definition: VoiceWorkflowContractDefinition<TResult>, routeResult: VoiceRouteResult<TResult>) => VoiceWorkflowContractValidation;
63
68
  export declare const createVoiceWorkflowScenario: <TResult = unknown>(definition: VoiceWorkflowContractDefinition<TResult>, overrides?: Partial<VoiceScenarioEvalDefinition>) => VoiceScenarioEvalDefinition;
64
69
  export declare const createVoiceWorkflowContract: <TResult = unknown>(definition: VoiceWorkflowContractDefinition<TResult>) => VoiceWorkflowContract<TResult>;
70
+ export declare const createVoiceWorkflowContractPreset: <TResult = unknown>(name: VoiceWorkflowContractPresetName, options?: VoiceWorkflowContractPresetOptions<TResult>) => VoiceWorkflowContract<TResult>;
65
71
  export declare const recordVoiceWorkflowContractTrace: (input: {
66
72
  at?: number;
67
73
  contractId?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.46",
3
+ "version": "0.0.22-beta.48",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",