@absolutejs/voice 0.0.22-beta.1 → 0.0.22-beta.11

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/dist/index.js CHANGED
@@ -5150,6 +5150,17 @@ var createVoiceAgent = (options) => {
5150
5150
  if (output.assistantText?.trim()) {
5151
5151
  messages.push({
5152
5152
  content: output.assistantText,
5153
+ metadata: output.toolCalls?.length ? {
5154
+ toolCalls: output.toolCalls
5155
+ } : undefined,
5156
+ role: "assistant"
5157
+ });
5158
+ } else if (output.toolCalls?.length) {
5159
+ messages.push({
5160
+ content: "",
5161
+ metadata: {
5162
+ toolCalls: output.toolCalls
5163
+ },
5153
5164
  role: "assistant"
5154
5165
  });
5155
5166
  }
@@ -5364,8 +5375,730 @@ var createVoiceAgentSquad = (options) => {
5364
5375
  };
5365
5376
  return {
5366
5377
  id: options.id,
5367
- onTurn: async (input) => run(input),
5368
- run
5378
+ onTurn: async (input) => run(input),
5379
+ run
5380
+ };
5381
+ };
5382
+
5383
+ // src/outcomeRecipes.ts
5384
+ var RECIPE_DEFAULTS = {
5385
+ "appointment-booking": {
5386
+ completedAction: "Verify appointment details, confirm calendar state, and send any required confirmation.",
5387
+ completedDescription: "The call completed an appointment-booking flow and should be checked against the scheduling system.",
5388
+ completedKind: "appointment-booking",
5389
+ completedTitle: "Confirm booked appointment",
5390
+ defaultCompletedCreatesTask: true,
5391
+ defaultDueInMs: 30 * 60000,
5392
+ defaultPriority: "normal",
5393
+ defaultQueue: "appointments",
5394
+ description: "Creates appointment confirmation work for completed calls and callback/retry work for missed booking attempts.",
5395
+ escalationQueue: "appointments-escalations"
5396
+ },
5397
+ "lead-qualification": {
5398
+ completedAction: "Review qualification signals, update CRM fields, and route the lead to the right owner.",
5399
+ completedDescription: "The call completed a lead-qualification flow and should be reviewed for sales follow-up.",
5400
+ completedKind: "lead-qualification",
5401
+ completedTitle: "Review qualified lead",
5402
+ defaultCompletedCreatesTask: true,
5403
+ defaultDueInMs: 15 * 60000,
5404
+ defaultPriority: "high",
5405
+ defaultQueue: "sales-leads",
5406
+ description: "Creates sales follow-up work for completed qualification calls and fast callbacks for missed leads.",
5407
+ escalationQueue: "sales-escalations"
5408
+ },
5409
+ "support-triage": {
5410
+ completedAction: "Review the triage result, confirm the support category, and route any unresolved issue.",
5411
+ completedDescription: "The call completed support triage and may need queue routing or human follow-up.",
5412
+ completedKind: "support-triage",
5413
+ completedTitle: "Review support triage",
5414
+ defaultCompletedCreatesTask: true,
5415
+ defaultDueInMs: 20 * 60000,
5416
+ defaultPriority: "normal",
5417
+ defaultQueue: "support-triage",
5418
+ description: "Creates support triage work for completed calls and urgent escalation/callback work for unresolved callers.",
5419
+ escalationQueue: "support-escalations"
5420
+ },
5421
+ "voicemail-callback": {
5422
+ completedAction: "No callback is required for completed calls.",
5423
+ completedDescription: "The call completed without requiring voicemail follow-up.",
5424
+ completedKind: "callback",
5425
+ completedTitle: "Completed call",
5426
+ defaultCompletedCreatesTask: false,
5427
+ defaultDueInMs: 15 * 60000,
5428
+ defaultPriority: "high",
5429
+ defaultQueue: "callbacks",
5430
+ description: "Creates callback work for voicemail, no-answer, failed, or escalated calls while ignoring completed calls.",
5431
+ escalationQueue: "callback-escalations"
5432
+ },
5433
+ "warm-transfer": {
5434
+ completedAction: "Confirm the handoff target received the caller context and close the transfer loop.",
5435
+ completedDescription: "The call is part of a warm-transfer flow and should be verified downstream.",
5436
+ completedKind: "transfer-check",
5437
+ completedTitle: "Verify warm transfer",
5438
+ defaultCompletedCreatesTask: false,
5439
+ defaultDueInMs: 10 * 60000,
5440
+ defaultPriority: "normal",
5441
+ defaultQueue: "transfer-verification",
5442
+ description: "Creates transfer verification work for transferred calls and escalation work when the handoff fails.",
5443
+ escalationQueue: "transfer-escalations"
5444
+ }
5445
+ };
5446
+ var buildRecipeTask = (input) => {
5447
+ const createdAt = input.review.generatedAt ?? Date.now();
5448
+ const queue = input.options.queue ?? input.defaults.defaultQueue;
5449
+ const target = input.options.target ?? input.review.postCall?.target;
5450
+ const common = {
5451
+ assignee: input.options.assignee,
5452
+ createdAt,
5453
+ history: [
5454
+ {
5455
+ actor: "system",
5456
+ at: createdAt,
5457
+ detail: input.review.postCall?.summary,
5458
+ type: "created"
5459
+ }
5460
+ ],
5461
+ id: `${input.review.id}:${input.defaults.completedKind}`,
5462
+ intakeId: input.review.id,
5463
+ outcome: input.review.summary.outcome,
5464
+ priority: input.options.priority ?? input.defaults.defaultPriority,
5465
+ queue,
5466
+ reviewId: input.review.id,
5467
+ status: "open",
5468
+ target,
5469
+ updatedAt: createdAt
5470
+ };
5471
+ switch (input.disposition) {
5472
+ case "completed":
5473
+ if (!(input.options.completedCreatesTask ?? input.defaults.defaultCompletedCreatesTask)) {
5474
+ return null;
5475
+ }
5476
+ return {
5477
+ ...common,
5478
+ description: input.defaults.completedDescription,
5479
+ kind: input.defaults.completedKind,
5480
+ recommendedAction: input.defaults.completedAction,
5481
+ title: target ? `${input.defaults.completedTitle}: ${target}` : input.defaults.completedTitle
5482
+ };
5483
+ case "voicemail":
5484
+ return {
5485
+ ...common,
5486
+ description: input.review.postCall?.summary ?? "The caller reached voicemail and needs a callback.",
5487
+ id: `${input.review.id}:callback`,
5488
+ kind: "callback",
5489
+ recommendedAction: input.review.postCall?.recommendedAction ?? "Call the customer back and continue the original flow.",
5490
+ title: target ? `Call back ${target}` : "Call back voicemail lead"
5491
+ };
5492
+ case "no-answer":
5493
+ return {
5494
+ ...common,
5495
+ description: input.review.postCall?.summary ?? "The call did not reach a live respondent and should be retried.",
5496
+ id: `${input.review.id}:retry`,
5497
+ kind: "callback",
5498
+ recommendedAction: input.review.postCall?.recommendedAction ?? "Retry the call or schedule a callback.",
5499
+ title: "Retry no-answer call"
5500
+ };
5501
+ case "transferred":
5502
+ return {
5503
+ ...common,
5504
+ description: input.review.postCall?.summary ?? "The call was transferred and should be verified downstream.",
5505
+ id: `${input.review.id}:transfer-check`,
5506
+ kind: "transfer-check",
5507
+ recommendedAction: input.review.postCall?.recommendedAction ?? "Confirm the receiving team got the caller context.",
5508
+ title: target ? `Verify transfer to ${target}` : "Verify call transfer"
5509
+ };
5510
+ case "escalated":
5511
+ return {
5512
+ ...common,
5513
+ description: input.review.postCall?.summary ?? "The call escalated and needs human review.",
5514
+ id: `${input.review.id}:escalation`,
5515
+ kind: "escalation",
5516
+ priority: "urgent",
5517
+ queue: input.options.escalationQueue ?? input.defaults.escalationQueue,
5518
+ assignee: input.options.escalationAssignee ?? input.options.assignee,
5519
+ recommendedAction: input.review.postCall?.recommendedAction ?? "Review the escalated call and respond immediately.",
5520
+ title: "Review escalated call"
5521
+ };
5522
+ case "failed":
5523
+ case "closed":
5524
+ return {
5525
+ ...common,
5526
+ description: input.review.postCall?.summary ?? "The call ended before successful completion and needs review.",
5527
+ id: `${input.review.id}:retry-review`,
5528
+ kind: "retry-review",
5529
+ priority: "high",
5530
+ recommendedAction: input.review.postCall?.recommendedAction ?? "Inspect the call and decide whether to retry, escalate, or close.",
5531
+ title: "Inspect incomplete call"
5532
+ };
5533
+ default:
5534
+ return null;
5535
+ }
5536
+ };
5537
+ var resolveVoiceOutcomeRecipe = (name, options = {}) => {
5538
+ const defaults = RECIPE_DEFAULTS[name];
5539
+ const taskPolicies = {
5540
+ completed: {
5541
+ assignee: options.assignee,
5542
+ dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
5543
+ name: `${name}-completed`,
5544
+ priority: options.priority ?? defaults.defaultPriority,
5545
+ queue: options.queue ?? defaults.defaultQueue
5546
+ },
5547
+ escalated: {
5548
+ assignee: options.escalationAssignee ?? options.assignee,
5549
+ dueInMs: Math.min(options.dueInMs ?? defaults.defaultDueInMs, 10 * 60000),
5550
+ name: `${name}-escalation`,
5551
+ priority: "urgent",
5552
+ queue: options.escalationQueue ?? defaults.escalationQueue
5553
+ },
5554
+ failed: {
5555
+ assignee: options.assignee,
5556
+ dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
5557
+ name: `${name}-failed-review`,
5558
+ priority: "high",
5559
+ queue: options.queue ?? defaults.defaultQueue
5560
+ },
5561
+ "no-answer": {
5562
+ assignee: options.assignee,
5563
+ dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
5564
+ name: `${name}-no-answer`,
5565
+ priority: options.priority ?? defaults.defaultPriority,
5566
+ queue: options.queue ?? defaults.defaultQueue
5567
+ },
5568
+ transferred: {
5569
+ assignee: options.assignee,
5570
+ dueInMs: Math.min(options.dueInMs ?? defaults.defaultDueInMs, 20 * 60000),
5571
+ name: `${name}-transfer-check`,
5572
+ priority: options.priority ?? defaults.defaultPriority,
5573
+ queue: name === "warm-transfer" ? options.queue ?? defaults.defaultQueue : "transfer-verification"
5574
+ },
5575
+ voicemail: {
5576
+ assignee: options.assignee,
5577
+ dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
5578
+ name: `${name}-voicemail`,
5579
+ priority: options.priority ?? defaults.defaultPriority,
5580
+ queue: options.queue ?? defaults.defaultQueue
5581
+ }
5582
+ };
5583
+ const taskAssignmentRules = [
5584
+ {
5585
+ assign: options.escalationAssignee ?? options.assignee,
5586
+ description: `Route urgent ${name} work to the escalation lane.`,
5587
+ name: `${name}-urgent-routing`,
5588
+ queue: options.escalationQueue ?? defaults.escalationQueue,
5589
+ when: {
5590
+ priority: "urgent"
5591
+ }
5592
+ }
5593
+ ].filter((rule) => rule.assign || rule.queue);
5594
+ return {
5595
+ createTaskFromReview: ({ disposition, review }) => buildRecipeTask({
5596
+ defaults,
5597
+ disposition,
5598
+ options,
5599
+ review
5600
+ }),
5601
+ description: defaults.description,
5602
+ name,
5603
+ taskAssignmentRules,
5604
+ taskPolicies
5605
+ };
5606
+ };
5607
+
5608
+ // src/assistantMemory.ts
5609
+ var createMemoryId = (input) => `${input.assistantId}:${input.namespace}:${input.key}`;
5610
+ var createVoiceAssistantMemoryRecord = (input) => {
5611
+ const now = Date.now();
5612
+ return {
5613
+ ...input,
5614
+ createdAt: input.createdAt ?? input.updatedAt ?? now,
5615
+ updatedAt: input.updatedAt ?? now
5616
+ };
5617
+ };
5618
+ var createVoiceMemoryAssistantMemoryStore = () => {
5619
+ const records = new Map;
5620
+ return {
5621
+ delete: async (input) => {
5622
+ records.delete(createMemoryId(input));
5623
+ },
5624
+ get: async (input) => records.get(createMemoryId(input)),
5625
+ list: async (input) => [...records.values()].filter((record) => record.assistantId === input.assistantId && (input.namespace === undefined || record.namespace === input.namespace)).sort((left, right) => right.updatedAt - left.updatedAt),
5626
+ set: async (input) => {
5627
+ const id = createMemoryId(input);
5628
+ const existing = records.get(id);
5629
+ const record = createVoiceAssistantMemoryRecord({
5630
+ ...input,
5631
+ createdAt: input.createdAt ?? existing?.createdAt,
5632
+ updatedAt: input.updatedAt
5633
+ });
5634
+ records.set(id, record);
5635
+ return record;
5636
+ }
5637
+ };
5638
+ };
5639
+ var resolveVoiceAssistantMemoryNamespace = async (input) => typeof input.memory.namespace === "function" ? await input.memory.namespace(input) : input.memory.namespace;
5640
+ var createVoiceAssistantMemoryHandle = async (input) => {
5641
+ const namespace = await resolveVoiceAssistantMemoryNamespace({
5642
+ assistantId: input.assistantId,
5643
+ context: input.context,
5644
+ memory: input.memory,
5645
+ session: input.session
5646
+ });
5647
+ const trace = async (event) => {
5648
+ await input.trace?.append({
5649
+ at: Date.now(),
5650
+ payload: {
5651
+ assistantId: input.assistantId,
5652
+ namespace,
5653
+ ...event
5654
+ },
5655
+ scenarioId: input.session.scenarioId,
5656
+ sessionId: input.session.id,
5657
+ type: "assistant.memory"
5658
+ });
5659
+ };
5660
+ return {
5661
+ delete: async (key) => {
5662
+ await input.memory.store.delete({
5663
+ assistantId: input.assistantId,
5664
+ key,
5665
+ namespace
5666
+ });
5667
+ await trace({
5668
+ action: "delete",
5669
+ key
5670
+ });
5671
+ },
5672
+ get: async (key) => {
5673
+ const record = await input.memory.store.get({
5674
+ assistantId: input.assistantId,
5675
+ key,
5676
+ namespace
5677
+ });
5678
+ await trace({
5679
+ action: "get",
5680
+ found: Boolean(record),
5681
+ key
5682
+ });
5683
+ return record?.value;
5684
+ },
5685
+ list: async () => {
5686
+ const records = await input.memory.store.list({
5687
+ assistantId: input.assistantId,
5688
+ namespace
5689
+ });
5690
+ await trace({
5691
+ action: "list",
5692
+ count: records.length
5693
+ });
5694
+ return records;
5695
+ },
5696
+ namespace,
5697
+ set: async (key, value, metadata) => {
5698
+ const record = await input.memory.store.set({
5699
+ assistantId: input.assistantId,
5700
+ key,
5701
+ metadata,
5702
+ namespace,
5703
+ value
5704
+ });
5705
+ await trace({
5706
+ action: "set",
5707
+ key
5708
+ });
5709
+ return record;
5710
+ }
5711
+ };
5712
+ };
5713
+
5714
+ // src/assistant.ts
5715
+ var hashString = (value) => {
5716
+ let hash = 2166136261;
5717
+ for (let index = 0;index < value.length; index += 1) {
5718
+ hash ^= value.charCodeAt(index);
5719
+ hash = Math.imul(hash, 16777619);
5720
+ }
5721
+ return hash >>> 0;
5722
+ };
5723
+ var increment = (record, key) => {
5724
+ record[key] = (record[key] ?? 0) + 1;
5725
+ };
5726
+ var resolveOutcome = (result) => {
5727
+ if (result.transfer) {
5728
+ return "transferred";
5729
+ }
5730
+ if (result.escalate) {
5731
+ return "escalated";
5732
+ }
5733
+ if (result.voicemail) {
5734
+ return "voicemail";
5735
+ }
5736
+ if (result.noAnswer) {
5737
+ return "no-answer";
5738
+ }
5739
+ if (result.complete) {
5740
+ return "completed";
5741
+ }
5742
+ return "continued";
5743
+ };
5744
+ var resolveArtifactPlanName = (artifactPlan) => {
5745
+ const preset = artifactPlan?.preset;
5746
+ if (!preset) {
5747
+ return artifactPlan?.ops ? "custom" : undefined;
5748
+ }
5749
+ return typeof preset === "string" ? preset : preset.name;
5750
+ };
5751
+ var appendAssistantTrace = async (input) => {
5752
+ await input.trace?.append({
5753
+ at: Date.now(),
5754
+ payload: {
5755
+ assistantId: input.assistantId,
5756
+ ...input.event
5757
+ },
5758
+ scenarioId: input.session.scenarioId,
5759
+ sessionId: input.session.id,
5760
+ turnId: input.turnId,
5761
+ type: input.type
5762
+ });
5763
+ };
5764
+ var resolvePresetOps = (artifactPlan) => {
5765
+ const preset = artifactPlan?.preset;
5766
+ if (!preset) {
5767
+ return artifactPlan?.ops;
5768
+ }
5769
+ const recipe = typeof preset === "string" ? resolveVoiceOutcomeRecipe(preset) : resolveVoiceOutcomeRecipe(preset.name, preset.options);
5770
+ return {
5771
+ ...recipe,
5772
+ ...artifactPlan?.ops
5773
+ };
5774
+ };
5775
+ var mergeOps = (base, override) => {
5776
+ if (!base && !override) {
5777
+ return;
5778
+ }
5779
+ return {
5780
+ ...base,
5781
+ ...override,
5782
+ taskAssignmentRules: base?.taskAssignmentRules || override?.taskAssignmentRules ? [
5783
+ ...base?.taskAssignmentRules ?? [],
5784
+ ...override?.taskAssignmentRules ?? []
5785
+ ] : undefined,
5786
+ taskPolicies: base?.taskPolicies || override?.taskPolicies ? {
5787
+ ...base?.taskPolicies ?? {},
5788
+ ...override?.taskPolicies ?? {}
5789
+ } : undefined
5790
+ };
5791
+ };
5792
+ var createVoiceExperiment = (options) => {
5793
+ if (!options.variants.length) {
5794
+ throw new Error("createVoiceExperiment requires at least one variant.");
5795
+ }
5796
+ const firstVariant = options.variants[0];
5797
+ return {
5798
+ id: options.id,
5799
+ resolve: (input) => {
5800
+ const selected = options.selectVariant?.({
5801
+ ...input,
5802
+ variants: options.variants
5803
+ });
5804
+ if (selected && typeof selected !== "object") {
5805
+ const variant = options.variants.find((item) => item.id === selected);
5806
+ if (variant) {
5807
+ return variant;
5808
+ }
5809
+ }
5810
+ if (selected && typeof selected === "object" && "id" in selected) {
5811
+ return selected;
5812
+ }
5813
+ const totalWeight = options.variants.reduce((total, variant) => total + Math.max(0, variant.weight ?? 1), 0);
5814
+ if (totalWeight <= 0) {
5815
+ return firstVariant;
5816
+ }
5817
+ const bucket = hashString(`${options.id}:${input.assistantId}:${input.session.id}`) % totalWeight;
5818
+ let cursor = 0;
5819
+ for (const variant of options.variants) {
5820
+ cursor += Math.max(0, variant.weight ?? 1);
5821
+ if (bucket < cursor) {
5822
+ return variant;
5823
+ }
5824
+ }
5825
+ return firstVariant;
5826
+ },
5827
+ variants: options.variants
5828
+ };
5829
+ };
5830
+ var createVoiceAssistant = (options) => {
5831
+ const ops = mergeOps(resolvePresetOps(options.artifactPlan), options.ops);
5832
+ const artifactPlanName = resolveArtifactPlanName(options.artifactPlan);
5833
+ let agent;
5834
+ const baseModelOptions = "model" in options && options.model ? {
5835
+ maxToolRounds: options.maxToolRounds,
5836
+ model: options.model,
5837
+ system: options.system,
5838
+ tools: options.tools
5839
+ } : undefined;
5840
+ if ("agent" in options && options.agent) {
5841
+ agent = options.agent;
5842
+ } else if ("agents" in options && options.agents) {
5843
+ agent = createVoiceAgentSquad({
5844
+ agents: options.agents,
5845
+ defaultAgentId: options.defaultAgentId,
5846
+ id: options.id,
5847
+ maxHandoffsPerTurn: options.maxHandoffsPerTurn,
5848
+ selectAgent: options.selectAgent,
5849
+ trace: options.trace
5850
+ });
5851
+ } else {
5852
+ agent = createVoiceAgent({
5853
+ id: options.id,
5854
+ maxToolRounds: options.maxToolRounds,
5855
+ model: options.model,
5856
+ system: options.system,
5857
+ trace: options.trace,
5858
+ tools: options.tools
5859
+ });
5860
+ }
5861
+ const onTurn = async (input) => {
5862
+ const memory = options.memory ? await createVoiceAssistantMemoryHandle({
5863
+ assistantId: options.id,
5864
+ context: input.context,
5865
+ memory: options.memory,
5866
+ session: input.session,
5867
+ trace: options.trace
5868
+ }) : undefined;
5869
+ const guardrailInput = {
5870
+ ...input,
5871
+ assistantId: options.id,
5872
+ memory
5873
+ };
5874
+ if (memory) {
5875
+ await options.memoryLifecycle?.beforeTurn?.({
5876
+ ...input,
5877
+ assistantId: options.id,
5878
+ memory
5879
+ });
5880
+ }
5881
+ const blocked = await options.guardrails?.beforeTurn?.(guardrailInput);
5882
+ if (blocked) {
5883
+ if (memory) {
5884
+ await options.memoryLifecycle?.afterTurn?.({
5885
+ ...input,
5886
+ assistantId: options.id,
5887
+ memory,
5888
+ result: blocked
5889
+ });
5890
+ }
5891
+ await appendAssistantTrace({
5892
+ assistantId: options.id,
5893
+ event: {
5894
+ action: "blocked",
5895
+ artifactPlan: artifactPlanName,
5896
+ outcome: resolveOutcome(blocked)
5897
+ },
5898
+ session: input.session,
5899
+ trace: options.trace,
5900
+ turnId: input.turn.id,
5901
+ type: "assistant.guardrail"
5902
+ });
5903
+ await appendAssistantTrace({
5904
+ assistantId: options.id,
5905
+ event: {
5906
+ artifactPlan: artifactPlanName,
5907
+ blocked: true,
5908
+ experimentId: options.experiment?.id,
5909
+ outcome: resolveOutcome(blocked)
5910
+ },
5911
+ session: input.session,
5912
+ trace: options.trace,
5913
+ turnId: input.turn.id,
5914
+ type: "assistant.run"
5915
+ });
5916
+ return blocked;
5917
+ }
5918
+ const startedAt = Date.now();
5919
+ const variant = options.experiment?.resolve({
5920
+ assistantId: options.id,
5921
+ context: input.context,
5922
+ session: input.session,
5923
+ turnId: input.turn.id
5924
+ });
5925
+ const runner = variant && baseModelOptions ? createVoiceAgent({
5926
+ id: `${options.id}:${variant.id}`,
5927
+ maxToolRounds: variant.maxToolRounds ?? baseModelOptions.maxToolRounds,
5928
+ model: variant.model ?? baseModelOptions.model,
5929
+ system: variant.system ?? baseModelOptions.system,
5930
+ trace: options.trace,
5931
+ tools: variant.tools ?? baseModelOptions.tools
5932
+ }) : agent;
5933
+ const runResult = await runner.run(input) ?? {};
5934
+ const result = runResult;
5935
+ const guarded = await options.guardrails?.afterTurn?.({
5936
+ ...guardrailInput,
5937
+ result
5938
+ });
5939
+ const finalResult = guarded ?? result;
5940
+ if (memory) {
5941
+ await options.memoryLifecycle?.afterTurn?.({
5942
+ ...input,
5943
+ assistantId: options.id,
5944
+ memory,
5945
+ result: finalResult
5946
+ });
5947
+ }
5948
+ if (guarded) {
5949
+ await appendAssistantTrace({
5950
+ assistantId: options.id,
5951
+ event: {
5952
+ action: "rewritten",
5953
+ artifactPlan: artifactPlanName,
5954
+ experimentId: options.experiment?.id,
5955
+ outcome: resolveOutcome(finalResult),
5956
+ variantId: variant?.id
5957
+ },
5958
+ session: input.session,
5959
+ trace: options.trace,
5960
+ turnId: input.turn.id,
5961
+ type: "assistant.guardrail"
5962
+ });
5963
+ }
5964
+ await appendAssistantTrace({
5965
+ assistantId: options.id,
5966
+ event: {
5967
+ artifactPlan: artifactPlanName,
5968
+ blocked: false,
5969
+ elapsedMs: Date.now() - startedAt,
5970
+ escalated: Boolean(finalResult.escalate),
5971
+ experimentId: options.experiment?.id,
5972
+ outcome: resolveOutcome(finalResult),
5973
+ toolNames: result.toolResults?.map((tool) => tool.toolName) ?? [],
5974
+ transferred: Boolean(finalResult.transfer),
5975
+ variantId: variant?.id
5976
+ },
5977
+ session: input.session,
5978
+ trace: options.trace,
5979
+ turnId: input.turn.id,
5980
+ type: "assistant.run"
5981
+ });
5982
+ return finalResult;
5983
+ };
5984
+ return {
5985
+ agent,
5986
+ id: options.id,
5987
+ onTurn,
5988
+ ops,
5989
+ route: (overrides) => ({
5990
+ ...overrides,
5991
+ onComplete: overrides.onComplete ?? (() => {
5992
+ return;
5993
+ }),
5994
+ onTurn
5995
+ })
5996
+ };
5997
+ };
5998
+ var summarizeVoiceAssistantRuns = async (input) => {
5999
+ const events = Array.isArray(input) ? input : input.events ?? await input.store?.list() ?? [];
6000
+ const assistantRuns = events.filter((event) => event.type === "assistant.run");
6001
+ const guardrails = events.filter((event) => event.type === "assistant.guardrail");
6002
+ const byAssistant = new Map;
6003
+ const getSummary = (assistantId) => {
6004
+ let summary = byAssistant.get(assistantId);
6005
+ if (!summary) {
6006
+ summary = {
6007
+ assistantId,
6008
+ artifactPlans: {},
6009
+ blockedGuardrailCount: 0,
6010
+ elapsedCount: 0,
6011
+ elapsedTotal: 0,
6012
+ escalationCount: 0,
6013
+ experiments: {},
6014
+ guardrailCount: 0,
6015
+ memory: {
6016
+ deletes: 0,
6017
+ gets: 0,
6018
+ lists: 0,
6019
+ sets: 0
6020
+ },
6021
+ outcomes: {},
6022
+ runCount: 0,
6023
+ sessionIds: new Set,
6024
+ sessions: 0,
6025
+ toolCalls: {},
6026
+ transferCount: 0,
6027
+ variants: {}
6028
+ };
6029
+ byAssistant.set(assistantId, summary);
6030
+ }
6031
+ return summary;
6032
+ };
6033
+ for (const event of assistantRuns) {
6034
+ const assistantId = typeof event.payload.assistantId === "string" ? event.payload.assistantId : "unknown";
6035
+ const summary = getSummary(assistantId);
6036
+ summary.runCount += 1;
6037
+ summary.sessionIds.add(event.sessionId);
6038
+ if (typeof event.payload.artifactPlan === "string") {
6039
+ increment(summary.artifactPlans, event.payload.artifactPlan);
6040
+ }
6041
+ if (typeof event.payload.experimentId === "string") {
6042
+ increment(summary.experiments, event.payload.experimentId);
6043
+ }
6044
+ if (typeof event.payload.variantId === "string") {
6045
+ increment(summary.variants, event.payload.variantId);
6046
+ }
6047
+ if (typeof event.payload.outcome === "string") {
6048
+ increment(summary.outcomes, event.payload.outcome);
6049
+ }
6050
+ if (event.payload.escalated === true) {
6051
+ summary.escalationCount += 1;
6052
+ }
6053
+ if (event.payload.transferred === true) {
6054
+ summary.transferCount += 1;
6055
+ }
6056
+ if (event.payload.blocked === true) {
6057
+ summary.blockedGuardrailCount += 1;
6058
+ }
6059
+ if (typeof event.payload.elapsedMs === "number") {
6060
+ summary.elapsedCount += 1;
6061
+ summary.elapsedTotal += event.payload.elapsedMs;
6062
+ }
6063
+ if (Array.isArray(event.payload.toolNames)) {
6064
+ for (const toolName of event.payload.toolNames) {
6065
+ if (typeof toolName === "string") {
6066
+ increment(summary.toolCalls, toolName);
6067
+ }
6068
+ }
6069
+ }
6070
+ }
6071
+ for (const event of guardrails) {
6072
+ const assistantId = typeof event.payload.assistantId === "string" ? event.payload.assistantId : "unknown";
6073
+ const summary = getSummary(assistantId);
6074
+ summary.guardrailCount += 1;
6075
+ }
6076
+ for (const event of events.filter((event2) => event2.type === "assistant.memory")) {
6077
+ const assistantId = typeof event.payload.assistantId === "string" ? event.payload.assistantId : "unknown";
6078
+ const summary = getSummary(assistantId);
6079
+ switch (event.payload.action) {
6080
+ case "delete":
6081
+ summary.memory.deletes += 1;
6082
+ break;
6083
+ case "get":
6084
+ summary.memory.gets += 1;
6085
+ break;
6086
+ case "list":
6087
+ summary.memory.lists += 1;
6088
+ break;
6089
+ case "set":
6090
+ summary.memory.sets += 1;
6091
+ break;
6092
+ }
6093
+ }
6094
+ const assistants = [...byAssistant.values()].map(({ elapsedCount, elapsedTotal, sessionIds, ...summary }) => ({
6095
+ ...summary,
6096
+ averageElapsedMs: elapsedCount > 0 ? Math.round(elapsedTotal / elapsedCount) : undefined,
6097
+ sessions: sessionIds.size
6098
+ }));
6099
+ return {
6100
+ assistants: assistants.sort((left, right) => left.assistantId.localeCompare(right.assistantId)),
6101
+ totalRuns: assistantRuns.length
5369
6102
  };
5370
6103
  };
5371
6104
  // src/fileStore.ts
@@ -6052,6 +6785,7 @@ var listJsonFiles = async (directory) => {
6052
6785
  };
6053
6786
  var encodeStoreId = (id) => `${encodeURIComponent(id)}.json`;
6054
6787
  var resolveFilePath = (directory, id) => join(directory, encodeStoreId(id));
6788
+ var createMemoryStoreId = (input) => `${input.assistantId}:${input.namespace}:${input.key}`;
6055
6789
  var readJsonFile = async (path) => JSON.parse(await readFile(path, "utf8"));
6056
6790
  var writeJsonFile = async (path, value, options) => {
6057
6791
  await mkdir(options.directory, {
@@ -6271,6 +7005,40 @@ var createVoiceFileTraceSinkDeliveryStore = (options) => {
6271
7005
  };
6272
7006
  return { get, list, remove, set };
6273
7007
  };
7008
+ var createVoiceFileAssistantMemoryStore = (options) => {
7009
+ const get = async (input) => {
7010
+ const path = resolveFilePath(options.directory, createMemoryStoreId(input));
7011
+ try {
7012
+ return await readJsonFile(path);
7013
+ } catch (error) {
7014
+ if (error.code === "ENOENT") {
7015
+ return;
7016
+ }
7017
+ throw error;
7018
+ }
7019
+ };
7020
+ const list = async (input) => {
7021
+ const files = await listJsonFiles(options.directory);
7022
+ const records = await Promise.all(files.map((file) => readJsonFile(file)));
7023
+ return records.filter((record) => record.assistantId === input.assistantId && (input.namespace === undefined || record.namespace === input.namespace)).sort((left, right) => right.updatedAt - left.updatedAt);
7024
+ };
7025
+ const set = async (input) => {
7026
+ const existing = await get(input);
7027
+ const record = createVoiceAssistantMemoryRecord({
7028
+ ...input,
7029
+ createdAt: input.createdAt ?? existing?.createdAt,
7030
+ updatedAt: input.updatedAt
7031
+ });
7032
+ await writeJsonFile(resolveFilePath(options.directory, createMemoryStoreId(record)), record, options);
7033
+ return record;
7034
+ };
7035
+ const remove = async (input) => {
7036
+ await rm(resolveFilePath(options.directory, createMemoryStoreId(input)), {
7037
+ force: true
7038
+ });
7039
+ };
7040
+ return { delete: remove, get, list, set };
7041
+ };
6274
7042
  var createVoiceFileRuntimeStorage = (options) => ({
6275
7043
  events: createVoiceFileIntegrationEventStore({
6276
7044
  ...options,
@@ -6280,6 +7048,10 @@ var createVoiceFileRuntimeStorage = (options) => ({
6280
7048
  ...options,
6281
7049
  directory: join(options.directory, "external-objects")
6282
7050
  }),
7051
+ memories: createVoiceFileAssistantMemoryStore({
7052
+ ...options,
7053
+ directory: join(options.directory, "memories")
7054
+ }),
6283
7055
  reviews: createVoiceFileReviewStore({
6284
7056
  ...options,
6285
7057
  directory: join(options.directory, "reviews")
@@ -6312,6 +7084,672 @@ var createStoredVoiceExternalObjectMap = (mapping) => createVoiceExternalObjectM
6312
7084
  sourceId: mapping.sourceId,
6313
7085
  sourceType: mapping.sourceType
6314
7086
  });
7087
+ // src/modelAdapters.ts
7088
+ var OUTPUT_SCHEMA = {
7089
+ additionalProperties: false,
7090
+ properties: {
7091
+ assistantText: {
7092
+ type: "string"
7093
+ },
7094
+ complete: {
7095
+ type: "boolean"
7096
+ },
7097
+ escalate: {
7098
+ additionalProperties: false,
7099
+ properties: {
7100
+ metadata: {
7101
+ additionalProperties: true,
7102
+ type: "object"
7103
+ },
7104
+ reason: {
7105
+ type: "string"
7106
+ }
7107
+ },
7108
+ required: ["reason"],
7109
+ type: "object"
7110
+ },
7111
+ noAnswer: {
7112
+ additionalProperties: false,
7113
+ properties: {
7114
+ metadata: {
7115
+ additionalProperties: true,
7116
+ type: "object"
7117
+ }
7118
+ },
7119
+ type: "object"
7120
+ },
7121
+ result: {
7122
+ additionalProperties: true,
7123
+ type: "object"
7124
+ },
7125
+ transfer: {
7126
+ additionalProperties: false,
7127
+ properties: {
7128
+ metadata: {
7129
+ additionalProperties: true,
7130
+ type: "object"
7131
+ },
7132
+ reason: {
7133
+ type: "string"
7134
+ },
7135
+ target: {
7136
+ type: "string"
7137
+ }
7138
+ },
7139
+ required: ["target"],
7140
+ type: "object"
7141
+ },
7142
+ voicemail: {
7143
+ additionalProperties: false,
7144
+ properties: {
7145
+ metadata: {
7146
+ additionalProperties: true,
7147
+ type: "object"
7148
+ }
7149
+ },
7150
+ type: "object"
7151
+ }
7152
+ },
7153
+ type: "object"
7154
+ };
7155
+ var ROUTE_RESULT_INSTRUCTION = "Return only a JSON object with assistantText, complete, transfer, escalate, voicemail, noAnswer, and result when you are not calling tools. Only set transfer, escalate, voicemail, or noAnswer when the user explicitly asks for that lifecycle outcome or a tool result says that exact outcome. Do not infer voicemail from generic words like voice, voice app, or voice integration.";
7156
+ var stripJSONCodeFence = (value) => {
7157
+ const trimmed = value.trim();
7158
+ const match = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
7159
+ return match?.[1]?.trim() ?? value;
7160
+ };
7161
+ var parseJSON = (value) => {
7162
+ try {
7163
+ const parsed = JSON.parse(stripJSONCodeFence(value));
7164
+ return parsed && typeof parsed === "object" ? parsed : {};
7165
+ } catch {
7166
+ return {
7167
+ assistantText: value
7168
+ };
7169
+ }
7170
+ };
7171
+ var parseJSONValue = (value) => {
7172
+ try {
7173
+ return JSON.parse(value);
7174
+ } catch {
7175
+ return value;
7176
+ }
7177
+ };
7178
+ var getMessageToolCalls = (message) => {
7179
+ const toolCalls = message.metadata?.toolCalls;
7180
+ return Array.isArray(toolCalls) ? toolCalls.filter((toolCall) => toolCall && typeof toolCall === "object" && typeof toolCall.name === "string") : [];
7181
+ };
7182
+ var createHTTPError = (provider, response) => new Error(`${provider} voice assistant model failed: HTTP ${response.status}`);
7183
+ var sleep4 = (ms) => new Promise((resolve2) => {
7184
+ setTimeout(resolve2, ms);
7185
+ });
7186
+ var errorMessage = (error) => error instanceof Error ? error.message : String(error);
7187
+ var defaultIsRateLimitError = (error) => /(\b429\b|rate limit|quota|too many requests)/i.test(errorMessage(error));
7188
+ var normalizeRouteOutput = (output) => {
7189
+ const result = {};
7190
+ if (typeof output.assistantText === "string") {
7191
+ result.assistantText = output.assistantText;
7192
+ }
7193
+ if (typeof output.complete === "boolean") {
7194
+ result.complete = output.complete;
7195
+ }
7196
+ if (output.result !== undefined) {
7197
+ result.result = output.result;
7198
+ }
7199
+ if (output.transfer && typeof output.transfer === "object") {
7200
+ const transfer = output.transfer;
7201
+ if (typeof transfer.target === "string") {
7202
+ result.transfer = {
7203
+ metadata: transfer.metadata && typeof transfer.metadata === "object" ? transfer.metadata : undefined,
7204
+ reason: typeof transfer.reason === "string" ? transfer.reason : undefined,
7205
+ target: transfer.target
7206
+ };
7207
+ }
7208
+ }
7209
+ if (output.escalate && typeof output.escalate === "object") {
7210
+ const escalate = output.escalate;
7211
+ if (typeof escalate.reason === "string") {
7212
+ result.escalate = {
7213
+ metadata: escalate.metadata && typeof escalate.metadata === "object" ? escalate.metadata : undefined,
7214
+ reason: escalate.reason
7215
+ };
7216
+ }
7217
+ }
7218
+ if (output.voicemail && typeof output.voicemail === "object") {
7219
+ const voicemail = output.voicemail;
7220
+ result.voicemail = {
7221
+ metadata: voicemail.metadata && typeof voicemail.metadata === "object" ? voicemail.metadata : undefined
7222
+ };
7223
+ }
7224
+ if (output.noAnswer && typeof output.noAnswer === "object") {
7225
+ const noAnswer = output.noAnswer;
7226
+ result.noAnswer = {
7227
+ metadata: noAnswer.metadata && typeof noAnswer.metadata === "object" ? noAnswer.metadata : undefined
7228
+ };
7229
+ }
7230
+ return result;
7231
+ };
7232
+ var createJSONVoiceAssistantModel = (options) => ({
7233
+ generate: async (input) => {
7234
+ const output = await options.generate(input);
7235
+ if ("assistantText" in output || "toolCalls" in output || "complete" in output || "transfer" in output || "escalate" in output) {
7236
+ return output;
7237
+ }
7238
+ return options.mapOutput?.(output) ?? normalizeRouteOutput(output);
7239
+ }
7240
+ });
7241
+ var createVoiceProviderRouter = (options) => {
7242
+ const providerIds = Object.keys(options.providers);
7243
+ const firstProvider = providerIds[0];
7244
+ const resolveOrder = async (input) => {
7245
+ const selectedProvider = await options.selectProvider?.(input);
7246
+ const fallbackOrder = typeof options.fallback === "function" ? await options.fallback(input) : options.fallback;
7247
+ const preferred = selectedProvider ?? fallbackOrder?.[0] ?? firstProvider;
7248
+ const seen = new Set;
7249
+ const order = [];
7250
+ for (const provider of [
7251
+ preferred,
7252
+ ...fallbackOrder ?? [],
7253
+ ...providerIds
7254
+ ]) {
7255
+ if (!provider || seen.has(provider) || !options.providers[provider]) {
7256
+ continue;
7257
+ }
7258
+ seen.add(provider);
7259
+ order.push(provider);
7260
+ }
7261
+ return {
7262
+ order,
7263
+ selectedProvider: preferred
7264
+ };
7265
+ };
7266
+ const emit = async (event, input) => {
7267
+ await options.onProviderEvent?.(event, input);
7268
+ };
7269
+ return {
7270
+ generate: async (input) => {
7271
+ const { order, selectedProvider } = await resolveOrder(input);
7272
+ if (!selectedProvider || order.length === 0) {
7273
+ throw new Error("Voice provider router has no available providers.");
7274
+ }
7275
+ let lastError;
7276
+ for (const [index, provider] of order.entries()) {
7277
+ const model = options.providers[provider];
7278
+ if (!model) {
7279
+ continue;
7280
+ }
7281
+ const startedAt = Date.now();
7282
+ try {
7283
+ const output = await model.generate(input);
7284
+ await emit({
7285
+ at: Date.now(),
7286
+ elapsedMs: Date.now() - startedAt,
7287
+ fallbackProvider: provider === selectedProvider ? undefined : provider,
7288
+ provider,
7289
+ recovered: provider !== selectedProvider,
7290
+ selectedProvider,
7291
+ status: provider === selectedProvider ? "success" : "fallback"
7292
+ }, input);
7293
+ return output;
7294
+ } catch (error) {
7295
+ lastError = error;
7296
+ const hasNextProvider = index < order.length - 1;
7297
+ const isProviderError = options.isProviderError?.(error, provider) ?? true;
7298
+ const nextProvider = hasNextProvider ? order[index + 1] : undefined;
7299
+ await emit({
7300
+ at: Date.now(),
7301
+ elapsedMs: Date.now() - startedAt,
7302
+ error: errorMessage(error),
7303
+ fallbackProvider: isProviderError ? nextProvider : undefined,
7304
+ provider,
7305
+ rateLimited: options.isRateLimitError?.(error, provider) ?? defaultIsRateLimitError(error),
7306
+ selectedProvider,
7307
+ status: "error"
7308
+ }, input);
7309
+ if (!hasNextProvider || !isProviderError) {
7310
+ throw error;
7311
+ }
7312
+ }
7313
+ }
7314
+ throw lastError ?? new Error("Voice provider router did not run a provider.");
7315
+ }
7316
+ };
7317
+ };
7318
+ var messageToOpenAIInput = (message) => {
7319
+ if (message.role === "tool") {
7320
+ return [
7321
+ {
7322
+ call_id: message.toolCallId ?? message.name ?? crypto.randomUUID(),
7323
+ output: message.content,
7324
+ type: "function_call_output"
7325
+ }
7326
+ ];
7327
+ }
7328
+ const toolCalls = getMessageToolCalls(message);
7329
+ if (message.role === "assistant" && toolCalls.length) {
7330
+ return toolCalls.map((toolCall) => ({
7331
+ arguments: JSON.stringify(toolCall.args),
7332
+ call_id: toolCall.id ?? crypto.randomUUID(),
7333
+ name: toolCall.name,
7334
+ type: "function_call"
7335
+ }));
7336
+ }
7337
+ return [
7338
+ {
7339
+ content: message.content,
7340
+ role: message.role === "system" ? "developer" : message.role
7341
+ }
7342
+ ];
7343
+ };
7344
+ var messagesToOpenAIInput = (messages) => messages.flatMap(messageToOpenAIInput);
7345
+ var messageToAnthropicMessage = (message) => {
7346
+ if (message.role === "system") {
7347
+ return;
7348
+ }
7349
+ if (message.role === "tool") {
7350
+ if (!message.toolCallId) {
7351
+ return {
7352
+ content: `Tool result from ${message.name ?? "tool"}: ${message.content}`,
7353
+ role: "user"
7354
+ };
7355
+ }
7356
+ return {
7357
+ content: [
7358
+ {
7359
+ content: message.content,
7360
+ tool_use_id: message.toolCallId,
7361
+ type: "tool_result"
7362
+ }
7363
+ ],
7364
+ role: "user"
7365
+ };
7366
+ }
7367
+ const toolCalls = getMessageToolCalls(message);
7368
+ if (message.role === "assistant" && toolCalls.length) {
7369
+ return {
7370
+ content: [
7371
+ ...message.content ? [
7372
+ {
7373
+ text: message.content,
7374
+ type: "text"
7375
+ }
7376
+ ] : [],
7377
+ ...toolCalls.map((toolCall) => ({
7378
+ id: toolCall.id ?? crypto.randomUUID(),
7379
+ input: toolCall.args,
7380
+ name: toolCall.name,
7381
+ type: "tool_use"
7382
+ }))
7383
+ ],
7384
+ role: "assistant"
7385
+ };
7386
+ }
7387
+ return {
7388
+ content: message.content,
7389
+ role: message.role
7390
+ };
7391
+ };
7392
+ var toGeminiSchema = (schema) => {
7393
+ const next = {};
7394
+ for (const [key, value] of Object.entries(schema)) {
7395
+ if (key === "additionalProperties") {
7396
+ continue;
7397
+ }
7398
+ if (key === "type" && typeof value === "string") {
7399
+ next[key] = value.toUpperCase();
7400
+ continue;
7401
+ }
7402
+ if (Array.isArray(value)) {
7403
+ next[key] = value.map((item) => item && typeof item === "object" ? toGeminiSchema(item) : item);
7404
+ continue;
7405
+ }
7406
+ if (value && typeof value === "object") {
7407
+ next[key] = toGeminiSchema(value);
7408
+ continue;
7409
+ }
7410
+ next[key] = value;
7411
+ }
7412
+ return next;
7413
+ };
7414
+ var messageToGeminiContent = (message) => {
7415
+ if (message.role === "system") {
7416
+ return;
7417
+ }
7418
+ if (message.role === "tool") {
7419
+ return {
7420
+ parts: [
7421
+ {
7422
+ functionResponse: {
7423
+ id: message.toolCallId,
7424
+ name: message.name ?? "tool",
7425
+ response: {
7426
+ result: parseJSONValue(message.content)
7427
+ }
7428
+ }
7429
+ }
7430
+ ],
7431
+ role: "user"
7432
+ };
7433
+ }
7434
+ const toolCalls = getMessageToolCalls(message);
7435
+ if (message.role === "assistant" && toolCalls.length) {
7436
+ return {
7437
+ parts: [
7438
+ ...message.content ? [
7439
+ {
7440
+ text: message.content
7441
+ }
7442
+ ] : [],
7443
+ ...toolCalls.map((toolCall) => ({
7444
+ functionCall: {
7445
+ args: toolCall.args,
7446
+ id: toolCall.id,
7447
+ name: toolCall.name
7448
+ }
7449
+ }))
7450
+ ],
7451
+ role: "model"
7452
+ };
7453
+ }
7454
+ return {
7455
+ parts: [
7456
+ {
7457
+ text: message.content
7458
+ }
7459
+ ],
7460
+ role: message.role === "assistant" ? "model" : "user"
7461
+ };
7462
+ };
7463
+ var extractText = (response) => {
7464
+ if (typeof response.output_text === "string") {
7465
+ return response.output_text;
7466
+ }
7467
+ const output = Array.isArray(response.output) ? response.output : [];
7468
+ for (const item of output) {
7469
+ if (!item || typeof item !== "object") {
7470
+ continue;
7471
+ }
7472
+ const record = item;
7473
+ const content = Array.isArray(record.content) ? record.content : [];
7474
+ for (const contentItem of content) {
7475
+ if (!contentItem || typeof contentItem !== "object") {
7476
+ continue;
7477
+ }
7478
+ const contentRecord = contentItem;
7479
+ if (typeof contentRecord.text === "string") {
7480
+ return contentRecord.text;
7481
+ }
7482
+ }
7483
+ }
7484
+ return "";
7485
+ };
7486
+ var extractToolCalls = (response) => {
7487
+ const output = Array.isArray(response.output) ? response.output : [];
7488
+ const toolCalls = [];
7489
+ for (const item of output) {
7490
+ if (!item || typeof item !== "object") {
7491
+ continue;
7492
+ }
7493
+ const record = item;
7494
+ if (record.type !== "function_call" || typeof record.name !== "string") {
7495
+ continue;
7496
+ }
7497
+ const args = typeof record.arguments === "string" ? parseJSON(record.arguments) : {};
7498
+ toolCalls.push({
7499
+ args,
7500
+ id: typeof record.call_id === "string" ? record.call_id : typeof record.id === "string" ? record.id : undefined,
7501
+ name: record.name
7502
+ });
7503
+ }
7504
+ return toolCalls;
7505
+ };
7506
+ var createOpenAIVoiceAssistantModel = (options) => {
7507
+ const fetchImpl = options.fetch ?? globalThis.fetch;
7508
+ const baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
7509
+ const model = options.model ?? "gpt-4.1-mini";
7510
+ return {
7511
+ generate: async (input) => {
7512
+ const response = await fetchImpl(`${baseUrl.replace(/\/$/, "")}/responses`, {
7513
+ body: JSON.stringify({
7514
+ input: messagesToOpenAIInput(input.messages),
7515
+ instructions: [
7516
+ input.system,
7517
+ "Return a JSON object with assistantText, complete, transfer, escalate, voicemail, noAnswer, and result when you are not calling tools."
7518
+ ].filter(Boolean).join(`
7519
+
7520
+ `),
7521
+ max_output_tokens: options.maxOutputTokens,
7522
+ model,
7523
+ temperature: options.temperature,
7524
+ text: {
7525
+ format: {
7526
+ name: "voice_route_result",
7527
+ schema: OUTPUT_SCHEMA,
7528
+ strict: false,
7529
+ type: "json_schema"
7530
+ }
7531
+ },
7532
+ tool_choice: input.tools.length ? "auto" : "none",
7533
+ tools: input.tools.map((tool) => ({
7534
+ description: tool.description,
7535
+ name: tool.name,
7536
+ parameters: tool.parameters ?? {
7537
+ additionalProperties: true,
7538
+ type: "object"
7539
+ },
7540
+ strict: false,
7541
+ type: "function"
7542
+ }))
7543
+ }),
7544
+ headers: {
7545
+ authorization: `Bearer ${options.apiKey}`,
7546
+ "content-type": "application/json"
7547
+ },
7548
+ method: "POST"
7549
+ });
7550
+ if (!response.ok) {
7551
+ throw createHTTPError("OpenAI", response);
7552
+ }
7553
+ const body = await response.json();
7554
+ if (body.usage && typeof body.usage === "object") {
7555
+ await options.onUsage?.(body.usage);
7556
+ }
7557
+ const toolCalls = extractToolCalls(body);
7558
+ if (toolCalls.length) {
7559
+ return {
7560
+ toolCalls
7561
+ };
7562
+ }
7563
+ return normalizeRouteOutput(parseJSON(extractText(body)));
7564
+ }
7565
+ };
7566
+ };
7567
+ var extractAnthropicText = (response) => {
7568
+ const content = Array.isArray(response.content) ? response.content : [];
7569
+ return content.map((item) => item && typeof item === "object" && item.type === "text" && typeof item.text === "string" ? item.text : "").filter(Boolean).join(`
7570
+ `);
7571
+ };
7572
+ var extractAnthropicToolCalls = (response) => {
7573
+ const content = Array.isArray(response.content) ? response.content : [];
7574
+ const toolCalls = [];
7575
+ for (const item of content) {
7576
+ if (!item || typeof item !== "object") {
7577
+ continue;
7578
+ }
7579
+ const record = item;
7580
+ if (record.type !== "tool_use" || typeof record.name !== "string") {
7581
+ continue;
7582
+ }
7583
+ toolCalls.push({
7584
+ args: record.input && typeof record.input === "object" ? record.input : {},
7585
+ id: typeof record.id === "string" ? record.id : undefined,
7586
+ name: record.name
7587
+ });
7588
+ }
7589
+ return toolCalls;
7590
+ };
7591
+ var createAnthropicVoiceAssistantModel = (options) => {
7592
+ const fetchImpl = options.fetch ?? globalThis.fetch;
7593
+ const baseUrl = options.baseUrl ?? "https://api.anthropic.com/v1";
7594
+ const model = options.model ?? "claude-sonnet-4-5";
7595
+ return {
7596
+ generate: async (input) => {
7597
+ const response = await fetchImpl(`${baseUrl.replace(/\/$/, "")}/messages`, {
7598
+ body: JSON.stringify({
7599
+ max_tokens: options.maxOutputTokens ?? 1024,
7600
+ messages: input.messages.map(messageToAnthropicMessage).filter(Boolean),
7601
+ model,
7602
+ system: [input.system, ROUTE_RESULT_INSTRUCTION].filter(Boolean).join(`
7603
+
7604
+ `),
7605
+ temperature: options.temperature,
7606
+ tool_choice: input.tools.length ? { type: "auto" } : { type: "none" },
7607
+ tools: input.tools.map((tool) => ({
7608
+ description: tool.description,
7609
+ input_schema: tool.parameters ?? {
7610
+ additionalProperties: true,
7611
+ type: "object"
7612
+ },
7613
+ name: tool.name
7614
+ }))
7615
+ }),
7616
+ headers: {
7617
+ "anthropic-version": options.version ?? "2023-06-01",
7618
+ "content-type": "application/json",
7619
+ "x-api-key": options.apiKey
7620
+ },
7621
+ method: "POST"
7622
+ });
7623
+ if (!response.ok) {
7624
+ throw createHTTPError("Anthropic", response);
7625
+ }
7626
+ const body = await response.json();
7627
+ if (body.usage && typeof body.usage === "object") {
7628
+ await options.onUsage?.(body.usage);
7629
+ }
7630
+ const toolCalls = extractAnthropicToolCalls(body);
7631
+ if (toolCalls.length) {
7632
+ return {
7633
+ assistantText: extractAnthropicText(body) || undefined,
7634
+ toolCalls
7635
+ };
7636
+ }
7637
+ return normalizeRouteOutput(parseJSON(extractAnthropicText(body)));
7638
+ }
7639
+ };
7640
+ };
7641
+ var extractGeminiCandidateParts = (response) => {
7642
+ const candidates = Array.isArray(response.candidates) ? response.candidates : [];
7643
+ const first = candidates[0];
7644
+ if (!first || typeof first !== "object") {
7645
+ return [];
7646
+ }
7647
+ const content = first.content;
7648
+ if (!content || typeof content !== "object") {
7649
+ return [];
7650
+ }
7651
+ const parts = content.parts;
7652
+ return Array.isArray(parts) ? parts : [];
7653
+ };
7654
+ var extractGeminiText = (response) => extractGeminiCandidateParts(response).map((part) => part && typeof part === "object" && typeof part.text === "string" ? part.text : "").filter(Boolean).join(`
7655
+ `);
7656
+ var extractGeminiToolCalls = (response) => {
7657
+ const toolCalls = [];
7658
+ for (const part of extractGeminiCandidateParts(response)) {
7659
+ if (!part || typeof part !== "object") {
7660
+ continue;
7661
+ }
7662
+ const functionCall = part.functionCall;
7663
+ if (!functionCall || typeof functionCall !== "object") {
7664
+ continue;
7665
+ }
7666
+ const record = functionCall;
7667
+ if (typeof record.name !== "string") {
7668
+ continue;
7669
+ }
7670
+ toolCalls.push({
7671
+ args: record.args && typeof record.args === "object" ? record.args : {},
7672
+ id: typeof record.id === "string" ? record.id : undefined,
7673
+ name: record.name
7674
+ });
7675
+ }
7676
+ return toolCalls;
7677
+ };
7678
+ var createGeminiVoiceAssistantModel = (options) => {
7679
+ const fetchImpl = options.fetch ?? globalThis.fetch;
7680
+ const baseUrl = options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta";
7681
+ const model = options.model ?? "gemini-2.5-flash";
7682
+ const maxRetries = Math.max(0, options.maxRetries ?? 2);
7683
+ return {
7684
+ generate: async (input) => {
7685
+ const endpoint = `${baseUrl.replace(/\/$/, "")}/models/${encodeURIComponent(model)}:generateContent?key=${encodeURIComponent(options.apiKey)}`;
7686
+ let response;
7687
+ for (let attempt = 0;attempt <= maxRetries; attempt += 1) {
7688
+ response = await fetchImpl(endpoint, {
7689
+ body: JSON.stringify({
7690
+ contents: input.messages.map(messageToGeminiContent).filter(Boolean),
7691
+ generationConfig: {
7692
+ maxOutputTokens: options.maxOutputTokens,
7693
+ ...input.tools.length ? {} : {
7694
+ responseMimeType: "application/json",
7695
+ responseSchema: toGeminiSchema(OUTPUT_SCHEMA)
7696
+ },
7697
+ temperature: options.temperature
7698
+ },
7699
+ systemInstruction: {
7700
+ parts: [
7701
+ {
7702
+ text: [input.system, ROUTE_RESULT_INSTRUCTION].filter(Boolean).join(`
7703
+
7704
+ `)
7705
+ }
7706
+ ]
7707
+ },
7708
+ tools: input.tools.length ? [
7709
+ {
7710
+ functionDeclarations: input.tools.map((tool) => ({
7711
+ description: tool.description,
7712
+ name: tool.name,
7713
+ parameters: toGeminiSchema(tool.parameters ?? {
7714
+ additionalProperties: true,
7715
+ type: "object"
7716
+ })
7717
+ }))
7718
+ }
7719
+ ] : undefined
7720
+ }),
7721
+ headers: {
7722
+ "content-type": "application/json"
7723
+ },
7724
+ method: "POST"
7725
+ });
7726
+ if (response.ok || response.status !== 429 && response.status < 500 || attempt === maxRetries) {
7727
+ break;
7728
+ }
7729
+ const retryAfter = Number(response.headers.get("retry-after"));
7730
+ await sleep4(Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1000 : 500 * 2 ** attempt);
7731
+ }
7732
+ if (!response) {
7733
+ throw new Error("Gemini voice assistant model failed: no response");
7734
+ }
7735
+ if (!response.ok) {
7736
+ throw createHTTPError("Gemini", response);
7737
+ }
7738
+ const body = await response.json();
7739
+ if (body.usageMetadata && typeof body.usageMetadata === "object") {
7740
+ await options.onUsage?.(body.usageMetadata);
7741
+ }
7742
+ const toolCalls = extractGeminiToolCalls(body);
7743
+ if (toolCalls.length) {
7744
+ return {
7745
+ assistantText: extractGeminiText(body) || undefined,
7746
+ toolCalls
7747
+ };
7748
+ }
7749
+ return normalizeRouteOutput(parseJSON(extractGeminiText(body)));
7750
+ }
7751
+ };
7752
+ };
6315
7753
  // src/sqliteStore.ts
6316
7754
  import { Database } from "bun:sqlite";
6317
7755
  var normalizeTableNameSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
@@ -7509,10 +8947,10 @@ var createVoiceOpsTaskProcessorWorker = (options) => ({
7509
8947
  result.completed += 1;
7510
8948
  } catch (error) {
7511
8949
  await options.onError?.(error, task);
7512
- const errorMessage = error instanceof Error ? error.message : String(error);
8950
+ const errorMessage2 = error instanceof Error ? error.message : String(error);
7513
8951
  const failedTask = failVoiceOpsTask(task, {
7514
8952
  actor: task.claimedBy ?? "ops-worker",
7515
- error: errorMessage
8953
+ error: errorMessage2
7516
8954
  });
7517
8955
  if (shouldDeadLetterTask(failedTask, options.maxFailures)) {
7518
8956
  const deadLetterTask = deadLetterVoiceOpsTask(failedTask, {
@@ -7987,230 +9425,6 @@ var resolveVoiceOpsPreset = (name, overrides = {}) => {
7987
9425
  taskPolicies: mergePolicies(preset.taskPolicies, overrides.taskPolicies)
7988
9426
  };
7989
9427
  };
7990
- // src/outcomeRecipes.ts
7991
- var RECIPE_DEFAULTS = {
7992
- "appointment-booking": {
7993
- completedAction: "Verify appointment details, confirm calendar state, and send any required confirmation.",
7994
- completedDescription: "The call completed an appointment-booking flow and should be checked against the scheduling system.",
7995
- completedKind: "appointment-booking",
7996
- completedTitle: "Confirm booked appointment",
7997
- defaultCompletedCreatesTask: true,
7998
- defaultDueInMs: 30 * 60000,
7999
- defaultPriority: "normal",
8000
- defaultQueue: "appointments",
8001
- description: "Creates appointment confirmation work for completed calls and callback/retry work for missed booking attempts.",
8002
- escalationQueue: "appointments-escalations"
8003
- },
8004
- "lead-qualification": {
8005
- completedAction: "Review qualification signals, update CRM fields, and route the lead to the right owner.",
8006
- completedDescription: "The call completed a lead-qualification flow and should be reviewed for sales follow-up.",
8007
- completedKind: "lead-qualification",
8008
- completedTitle: "Review qualified lead",
8009
- defaultCompletedCreatesTask: true,
8010
- defaultDueInMs: 15 * 60000,
8011
- defaultPriority: "high",
8012
- defaultQueue: "sales-leads",
8013
- description: "Creates sales follow-up work for completed qualification calls and fast callbacks for missed leads.",
8014
- escalationQueue: "sales-escalations"
8015
- },
8016
- "support-triage": {
8017
- completedAction: "Review the triage result, confirm the support category, and route any unresolved issue.",
8018
- completedDescription: "The call completed support triage and may need queue routing or human follow-up.",
8019
- completedKind: "support-triage",
8020
- completedTitle: "Review support triage",
8021
- defaultCompletedCreatesTask: true,
8022
- defaultDueInMs: 20 * 60000,
8023
- defaultPriority: "normal",
8024
- defaultQueue: "support-triage",
8025
- description: "Creates support triage work for completed calls and urgent escalation/callback work for unresolved callers.",
8026
- escalationQueue: "support-escalations"
8027
- },
8028
- "voicemail-callback": {
8029
- completedAction: "No callback is required for completed calls.",
8030
- completedDescription: "The call completed without requiring voicemail follow-up.",
8031
- completedKind: "callback",
8032
- completedTitle: "Completed call",
8033
- defaultCompletedCreatesTask: false,
8034
- defaultDueInMs: 15 * 60000,
8035
- defaultPriority: "high",
8036
- defaultQueue: "callbacks",
8037
- description: "Creates callback work for voicemail, no-answer, failed, or escalated calls while ignoring completed calls.",
8038
- escalationQueue: "callback-escalations"
8039
- },
8040
- "warm-transfer": {
8041
- completedAction: "Confirm the handoff target received the caller context and close the transfer loop.",
8042
- completedDescription: "The call is part of a warm-transfer flow and should be verified downstream.",
8043
- completedKind: "transfer-check",
8044
- completedTitle: "Verify warm transfer",
8045
- defaultCompletedCreatesTask: false,
8046
- defaultDueInMs: 10 * 60000,
8047
- defaultPriority: "normal",
8048
- defaultQueue: "transfer-verification",
8049
- description: "Creates transfer verification work for transferred calls and escalation work when the handoff fails.",
8050
- escalationQueue: "transfer-escalations"
8051
- }
8052
- };
8053
- var buildRecipeTask = (input) => {
8054
- const createdAt = input.review.generatedAt ?? Date.now();
8055
- const queue = input.options.queue ?? input.defaults.defaultQueue;
8056
- const target = input.options.target ?? input.review.postCall?.target;
8057
- const common = {
8058
- assignee: input.options.assignee,
8059
- createdAt,
8060
- history: [
8061
- {
8062
- actor: "system",
8063
- at: createdAt,
8064
- detail: input.review.postCall?.summary,
8065
- type: "created"
8066
- }
8067
- ],
8068
- id: `${input.review.id}:${input.defaults.completedKind}`,
8069
- intakeId: input.review.id,
8070
- outcome: input.review.summary.outcome,
8071
- priority: input.options.priority ?? input.defaults.defaultPriority,
8072
- queue,
8073
- reviewId: input.review.id,
8074
- status: "open",
8075
- target,
8076
- updatedAt: createdAt
8077
- };
8078
- switch (input.disposition) {
8079
- case "completed":
8080
- if (!(input.options.completedCreatesTask ?? input.defaults.defaultCompletedCreatesTask)) {
8081
- return null;
8082
- }
8083
- return {
8084
- ...common,
8085
- description: input.defaults.completedDescription,
8086
- kind: input.defaults.completedKind,
8087
- recommendedAction: input.defaults.completedAction,
8088
- title: target ? `${input.defaults.completedTitle}: ${target}` : input.defaults.completedTitle
8089
- };
8090
- case "voicemail":
8091
- return {
8092
- ...common,
8093
- description: input.review.postCall?.summary ?? "The caller reached voicemail and needs a callback.",
8094
- id: `${input.review.id}:callback`,
8095
- kind: "callback",
8096
- recommendedAction: input.review.postCall?.recommendedAction ?? "Call the customer back and continue the original flow.",
8097
- title: target ? `Call back ${target}` : "Call back voicemail lead"
8098
- };
8099
- case "no-answer":
8100
- return {
8101
- ...common,
8102
- description: input.review.postCall?.summary ?? "The call did not reach a live respondent and should be retried.",
8103
- id: `${input.review.id}:retry`,
8104
- kind: "callback",
8105
- recommendedAction: input.review.postCall?.recommendedAction ?? "Retry the call or schedule a callback.",
8106
- title: "Retry no-answer call"
8107
- };
8108
- case "transferred":
8109
- return {
8110
- ...common,
8111
- description: input.review.postCall?.summary ?? "The call was transferred and should be verified downstream.",
8112
- id: `${input.review.id}:transfer-check`,
8113
- kind: "transfer-check",
8114
- recommendedAction: input.review.postCall?.recommendedAction ?? "Confirm the receiving team got the caller context.",
8115
- title: target ? `Verify transfer to ${target}` : "Verify call transfer"
8116
- };
8117
- case "escalated":
8118
- return {
8119
- ...common,
8120
- description: input.review.postCall?.summary ?? "The call escalated and needs human review.",
8121
- id: `${input.review.id}:escalation`,
8122
- kind: "escalation",
8123
- priority: "urgent",
8124
- queue: input.options.escalationQueue ?? input.defaults.escalationQueue,
8125
- assignee: input.options.escalationAssignee ?? input.options.assignee,
8126
- recommendedAction: input.review.postCall?.recommendedAction ?? "Review the escalated call and respond immediately.",
8127
- title: "Review escalated call"
8128
- };
8129
- case "failed":
8130
- case "closed":
8131
- return {
8132
- ...common,
8133
- description: input.review.postCall?.summary ?? "The call ended before successful completion and needs review.",
8134
- id: `${input.review.id}:retry-review`,
8135
- kind: "retry-review",
8136
- priority: "high",
8137
- recommendedAction: input.review.postCall?.recommendedAction ?? "Inspect the call and decide whether to retry, escalate, or close.",
8138
- title: "Inspect incomplete call"
8139
- };
8140
- default:
8141
- return null;
8142
- }
8143
- };
8144
- var resolveVoiceOutcomeRecipe = (name, options = {}) => {
8145
- const defaults = RECIPE_DEFAULTS[name];
8146
- const taskPolicies = {
8147
- completed: {
8148
- assignee: options.assignee,
8149
- dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
8150
- name: `${name}-completed`,
8151
- priority: options.priority ?? defaults.defaultPriority,
8152
- queue: options.queue ?? defaults.defaultQueue
8153
- },
8154
- escalated: {
8155
- assignee: options.escalationAssignee ?? options.assignee,
8156
- dueInMs: Math.min(options.dueInMs ?? defaults.defaultDueInMs, 10 * 60000),
8157
- name: `${name}-escalation`,
8158
- priority: "urgent",
8159
- queue: options.escalationQueue ?? defaults.escalationQueue
8160
- },
8161
- failed: {
8162
- assignee: options.assignee,
8163
- dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
8164
- name: `${name}-failed-review`,
8165
- priority: "high",
8166
- queue: options.queue ?? defaults.defaultQueue
8167
- },
8168
- "no-answer": {
8169
- assignee: options.assignee,
8170
- dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
8171
- name: `${name}-no-answer`,
8172
- priority: options.priority ?? defaults.defaultPriority,
8173
- queue: options.queue ?? defaults.defaultQueue
8174
- },
8175
- transferred: {
8176
- assignee: options.assignee,
8177
- dueInMs: Math.min(options.dueInMs ?? defaults.defaultDueInMs, 20 * 60000),
8178
- name: `${name}-transfer-check`,
8179
- priority: options.priority ?? defaults.defaultPriority,
8180
- queue: name === "warm-transfer" ? options.queue ?? defaults.defaultQueue : "transfer-verification"
8181
- },
8182
- voicemail: {
8183
- assignee: options.assignee,
8184
- dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
8185
- name: `${name}-voicemail`,
8186
- priority: options.priority ?? defaults.defaultPriority,
8187
- queue: options.queue ?? defaults.defaultQueue
8188
- }
8189
- };
8190
- const taskAssignmentRules = [
8191
- {
8192
- assign: options.escalationAssignee ?? options.assignee,
8193
- description: `Route urgent ${name} work to the escalation lane.`,
8194
- name: `${name}-urgent-routing`,
8195
- queue: options.escalationQueue ?? defaults.escalationQueue,
8196
- when: {
8197
- priority: "urgent"
8198
- }
8199
- }
8200
- ].filter((rule) => rule.assign || rule.queue);
8201
- return {
8202
- createTaskFromReview: ({ disposition, review }) => buildRecipeTask({
8203
- defaults,
8204
- disposition,
8205
- options,
8206
- review
8207
- }),
8208
- description: defaults.description,
8209
- name,
8210
- taskAssignmentRules,
8211
- taskPolicies
8212
- };
8213
- };
8214
9428
  // src/correction.ts
8215
9429
  var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8216
9430
  var buildAliasMatcher = (alias) => new RegExp(`(?<![\\p{L}\\p{N}'])${escapeRegExp(alias)}(?![\\p{L}\\p{N}'])`, "giu");
@@ -8990,6 +10204,7 @@ export {
8990
10204
  summarizeVoiceOpsTaskQueue,
8991
10205
  summarizeVoiceOpsTaskAnalytics,
8992
10206
  summarizeVoiceIntegrationEvents,
10207
+ summarizeVoiceAssistantRuns,
8993
10208
  startVoiceOpsTask,
8994
10209
  shapeTelephonyAssistantText,
8995
10210
  selectVoiceTraceEventsForPrune,
@@ -9001,6 +10216,7 @@ export {
9001
10216
  resolveVoiceOpsTaskAssignment,
9002
10217
  resolveVoiceOpsTaskAgeBucket,
9003
10218
  resolveVoiceOpsPreset,
10219
+ resolveVoiceAssistantMemoryNamespace,
9004
10220
  resolveTurnDetectionConfig,
9005
10221
  resolveAudioConditioningConfig,
9006
10222
  requeueVoiceOpsTask,
@@ -9061,6 +10277,7 @@ export {
9061
10277
  createVoiceReviewSavedEvent,
9062
10278
  createVoiceRedisTaskLeaseCoordinator,
9063
10279
  createVoiceRedisIdempotencyStore,
10280
+ createVoiceProviderRouter,
9064
10281
  createVoicePostgresTraceSinkDeliveryStore,
9065
10282
  createVoicePostgresTraceEventStore,
9066
10283
  createVoicePostgresTaskStore,
@@ -9076,6 +10293,7 @@ export {
9076
10293
  createVoiceMemoryTraceSinkDeliveryStore,
9077
10294
  createVoiceMemoryTraceEventStore,
9078
10295
  createVoiceMemoryStore,
10296
+ createVoiceMemoryAssistantMemoryStore,
9079
10297
  createVoiceLinearIssueUpdateSink,
9080
10298
  createVoiceLinearIssueSyncSinks,
9081
10299
  createVoiceLinearIssueSink,
@@ -9095,13 +10313,18 @@ export {
9095
10313
  createVoiceFileReviewStore,
9096
10314
  createVoiceFileIntegrationEventStore,
9097
10315
  createVoiceFileExternalObjectMapStore,
10316
+ createVoiceFileAssistantMemoryStore,
9098
10317
  createVoiceExternalObjectMapId,
9099
10318
  createVoiceExternalObjectMap,
10319
+ createVoiceExperiment,
9100
10320
  createVoiceCallReviewRecorder,
9101
10321
  createVoiceCallReviewFromSession,
9102
10322
  createVoiceCallReviewFromLiveTelephonyReport,
9103
10323
  createVoiceCallCompletedEvent,
9104
10324
  createVoiceCRMActivitySink,
10325
+ createVoiceAssistantMemoryRecord,
10326
+ createVoiceAssistantMemoryHandle,
10327
+ createVoiceAssistant,
9105
10328
  createVoiceAgentTool,
9106
10329
  createVoiceAgentSquad,
9107
10330
  createVoiceAgent,
@@ -9113,9 +10336,13 @@ export {
9113
10336
  createStoredVoiceCallReviewArtifact,
9114
10337
  createRiskyTurnCorrectionHandler,
9115
10338
  createPhraseHintCorrectionHandler,
10339
+ createOpenAIVoiceAssistantModel,
10340
+ createJSONVoiceAssistantModel,
9116
10341
  createId,
10342
+ createGeminiVoiceAssistantModel,
9117
10343
  createDomainPhraseHints,
9118
10344
  createDomainLexicon,
10345
+ createAnthropicVoiceAssistantModel,
9119
10346
  conditionAudioChunk,
9120
10347
  completeVoiceOpsTask,
9121
10348
  claimVoiceOpsTask,