@absolutejs/voice 0.0.22-beta.517 → 0.0.22-beta.519

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.d.ts CHANGED
@@ -75,8 +75,8 @@ export { createVoiceAIJudgeCompletion, createVoiceLLMJudge, } from "./llmJudge";
75
75
  export type { CreateVoiceAIJudgeCompletionOptions, CreateVoiceLLMJudgeOptions, VoiceLLMJudge, VoiceLLMJudgeCompletion, VoiceLLMJudgeCriterionVerdict, VoiceLLMJudgeInput, VoiceLLMJudgeRubric, VoiceLLMJudgeRubricCriterion, VoiceLLMJudgeVerdict, } from "./llmJudge";
76
76
  export { DEFAULT_VOICE_REDACTION_PATTERNS, createVoiceTranscriptRedactor, redactVoiceTranscript, } from "./redaction";
77
77
  export type { CreateVoiceTranscriptRedactorOptions, VoiceRedactionPattern, VoiceTranscriptRedactor, } from "./redaction";
78
- export { deriveVoiceRecordingRedactionRanges } from "./recordingRedaction";
79
- export type { DeriveVoiceRecordingRedactionRangesInput, VoiceRecordingRedactionRange, } from "./recordingRedaction";
78
+ export { deriveVoiceRecordingRedactionRanges, redactVoiceRecording, } from "./recordingRedaction";
79
+ export type { DeriveVoiceRecordingRedactionRangesInput, RedactVoiceRecordingInput, RedactVoiceRecordingResult, VoiceRecordingRedactionRange, } from "./recordingRedaction";
80
80
  export { DEFAULT_VOICE_PRICE_BOOK, createVoiceCostAccountant, } from "./costAccounting";
81
81
  export type { CreateVoiceCostAccountantOptions, VoiceCostAccountant, VoiceCostBreakdown, VoiceCostLLMRecord, VoiceCostSTTRecord, VoiceCostTTSRecord, VoiceCostTelephonyRecord, VoicePriceBook, VoiceProviderRates, } from "./costAccounting";
82
82
  export { describeVoiceAssistantMode, resolveVoiceAssistantMode, } from "./assistantMode";
package/dist/index.js CHANGED
@@ -3769,7 +3769,8 @@ var setTurnResult = (session, turnId, input) => {
3769
3769
  session.turns = session.turns.map((turn) => turn.id === turnId ? {
3770
3770
  ...turn,
3771
3771
  assistantText: input.assistantText ?? turn.assistantText,
3772
- result: input.result ?? turn.result
3772
+ result: input.result ?? turn.result,
3773
+ citations: input.citations && input.citations.length > 0 ? [...input.citations] : turn.citations
3773
3774
  } : turn);
3774
3775
  };
3775
3776
  var ensureCallLifecycleState = (session) => {
@@ -5157,6 +5158,7 @@ var createVoiceSession = (options) => {
5157
5158
  });
5158
5159
  const output = {
5159
5160
  assistantText: committedOutput?.assistantText,
5161
+ citations: committedOutput?.citations,
5160
5162
  complete: committedOutput?.complete,
5161
5163
  escalate: committedOutput?.escalate,
5162
5164
  noAnswer: committedOutput?.noAnswer,
@@ -5164,6 +5166,12 @@ var createVoiceSession = (options) => {
5164
5166
  transfer: committedOutput?.transfer,
5165
5167
  voicemail: committedOutput?.voicemail
5166
5168
  };
5169
+ if (output.citations && output.citations.length > 0) {
5170
+ const turnCitations = output.citations;
5171
+ await writeSession((currentSession) => {
5172
+ setTurnResult(currentSession, turn.id, { citations: turnCitations });
5173
+ });
5174
+ }
5167
5175
  if (output?.assistantText) {
5168
5176
  const assistantTextStartedAt = Date.now();
5169
5177
  await writeSession((currentSession) => {
@@ -8733,6 +8741,159 @@ var assertVoiceCampaignDialerProofEvidence = (report, input = {}) => {
8733
8741
  }
8734
8742
  return assertion;
8735
8743
  };
8744
+ // src/ragTool.ts
8745
+ var extractVoiceRAGCitations = (toolResults, toolName = "searchKnowledgeBase") => {
8746
+ const out = [];
8747
+ for (const entry of toolResults) {
8748
+ if (entry.toolName !== toolName) {
8749
+ continue;
8750
+ }
8751
+ const result = entry.result;
8752
+ const citations = result?.citations;
8753
+ if (!Array.isArray(citations)) {
8754
+ continue;
8755
+ }
8756
+ for (const citation of citations) {
8757
+ out.push({
8758
+ chunkId: citation.chunkId,
8759
+ score: citation.score,
8760
+ source: citation.source,
8761
+ title: citation.title
8762
+ });
8763
+ }
8764
+ }
8765
+ return out;
8766
+ };
8767
+ var DEFAULT_TOOL_NAME = "searchKnowledgeBase";
8768
+ var DEFAULT_DESCRIPTION = "Search the knowledge base and return short grounded citations. Use this whenever the caller asks a question that may be answered by indexed reference material.";
8769
+ var DEFAULT_TOP_K = 6;
8770
+ var DEFAULT_MAX_TOP_K = 20;
8771
+ var DEFAULT_MAX_CHUNK_CHARS = 320;
8772
+ var truncate = (value, limit) => {
8773
+ if (limit <= 0 || value.length <= limit)
8774
+ return value;
8775
+ return `${value.slice(0, Math.max(0, limit - 1)).trimEnd()}\u2026`;
8776
+ };
8777
+ var formatScore = (score) => {
8778
+ if (!Number.isFinite(score))
8779
+ return "n/a";
8780
+ return score.toFixed(3);
8781
+ };
8782
+ var buildDefaultCitationMessage = (citations, args, maxChunkChars) => {
8783
+ if (citations.length === 0) {
8784
+ return `No knowledge base results for "${args.query}".`;
8785
+ }
8786
+ const lines = citations.map((citation, index) => {
8787
+ const label = citation.title ?? citation.source ?? citation.chunkId;
8788
+ const text = truncate(citation.chunkText, maxChunkChars);
8789
+ return `${String(index + 1)}. ${label} (score ${formatScore(citation.score)}): ${text}`;
8790
+ });
8791
+ return [
8792
+ `Knowledge base results for "${args.query}":`,
8793
+ ...lines
8794
+ ].join(`
8795
+ `);
8796
+ };
8797
+ var filterAllowedFilterKeys = (filter, allowedKeys) => {
8798
+ if (!filter)
8799
+ return;
8800
+ if (!allowedKeys)
8801
+ return filter;
8802
+ const allowed = new Set(allowedKeys);
8803
+ const entries = Object.entries(filter).filter(([key]) => allowed.has(key));
8804
+ if (entries.length === 0)
8805
+ return;
8806
+ return Object.fromEntries(entries);
8807
+ };
8808
+ var mergeFilters = (...filters) => {
8809
+ const present = filters.filter((entry) => entry !== undefined);
8810
+ if (present.length === 0)
8811
+ return;
8812
+ return Object.assign({}, ...present);
8813
+ };
8814
+ var buildVoiceRAGToolParameters = (options) => {
8815
+ if (options.parameters)
8816
+ return options.parameters;
8817
+ const defaultTopK = options.topK ?? DEFAULT_TOP_K;
8818
+ const maxTopK = options.maxTopK ?? DEFAULT_MAX_TOP_K;
8819
+ const properties = {
8820
+ query: {
8821
+ description: "Natural-language question to look up in the knowledge base.",
8822
+ type: "string"
8823
+ },
8824
+ topK: {
8825
+ default: defaultTopK,
8826
+ description: `How many citations to return (1-${String(maxTopK)}).`,
8827
+ maximum: maxTopK,
8828
+ minimum: 1,
8829
+ type: "integer"
8830
+ }
8831
+ };
8832
+ if (options.allowedFilterKeys && options.allowedFilterKeys.length > 0) {
8833
+ properties.filter = {
8834
+ additionalProperties: false,
8835
+ description: "Optional metadata filter. Only keys listed here are honored: " + options.allowedFilterKeys.join(", "),
8836
+ properties: Object.fromEntries(options.allowedFilterKeys.map((key) => [key, {}])),
8837
+ type: "object"
8838
+ };
8839
+ }
8840
+ return {
8841
+ additionalProperties: false,
8842
+ properties,
8843
+ required: ["query"],
8844
+ type: "object"
8845
+ };
8846
+ };
8847
+ var createVoiceRAGTool = (collection, options = {}) => {
8848
+ const name = options.name ?? DEFAULT_TOOL_NAME;
8849
+ const description = options.description ?? DEFAULT_DESCRIPTION;
8850
+ const defaultTopK = options.topK ?? DEFAULT_TOP_K;
8851
+ const maxTopK = options.maxTopK ?? DEFAULT_MAX_TOP_K;
8852
+ const maxChunkChars = options.maxChunkChars ?? DEFAULT_MAX_CHUNK_CHARS;
8853
+ const parameters = buildVoiceRAGToolParameters(options);
8854
+ return createVoiceAgentTool({
8855
+ description,
8856
+ execute: async ({ args, context }) => {
8857
+ const query = typeof args?.query === "string" ? args.query.trim() : "";
8858
+ if (query.length === 0) {
8859
+ const empty = {
8860
+ citations: [],
8861
+ message: "Knowledge base search requires a non-empty query.",
8862
+ query: "",
8863
+ topK: 0
8864
+ };
8865
+ return empty;
8866
+ }
8867
+ const requestedTopK = typeof args?.topK === "number" && Number.isFinite(args.topK) ? Math.min(maxTopK, Math.max(1, Math.floor(args.topK))) : defaultTopK;
8868
+ const llmFilter = filterAllowedFilterKeys(args?.filter, options.allowedFilterKeys);
8869
+ const fixedFilter = typeof options.fixedFilter === "function" ? options.fixedFilter({ context }) : options.fixedFilter;
8870
+ const filter = mergeFilters(fixedFilter, llmFilter);
8871
+ const rawResults = await collection.search({
8872
+ filter,
8873
+ query,
8874
+ scoreThreshold: options.scoreThreshold,
8875
+ topK: requestedTopK
8876
+ });
8877
+ const citations = Array.from(rawResults).slice(0, requestedTopK);
8878
+ const formatter = options.formatResult ? options.formatResult : (entries, innerArgs) => buildDefaultCitationMessage(entries, innerArgs, maxChunkChars);
8879
+ const message = formatter(citations, {
8880
+ filter,
8881
+ query,
8882
+ topK: requestedTopK
8883
+ });
8884
+ return {
8885
+ citations,
8886
+ message,
8887
+ query,
8888
+ topK: requestedTopK
8889
+ };
8890
+ },
8891
+ name,
8892
+ parameters,
8893
+ resultToMessage: options.resultToMessage ?? ((result) => result.message)
8894
+ });
8895
+ };
8896
+
8736
8897
  // src/agent.ts
8737
8898
  var normalizeText3 = (value) => typeof value === "string" ? value.trim() : "";
8738
8899
  var toErrorMessage3 = (error) => error instanceof Error ? error.message : String(error);
@@ -9106,9 +9267,11 @@ var createVoiceAgent = (options) => {
9106
9267
  turn: input.turn,
9107
9268
  type: "agent.result"
9108
9269
  });
9270
+ const citations = extractVoiceRAGCitations(toolResults);
9109
9271
  return {
9110
9272
  agentId: options.id,
9111
9273
  assistantText: output.assistantText,
9274
+ ...citations.length > 0 ? { citations } : {},
9112
9275
  complete: output.complete,
9113
9276
  escalate: output.escalate,
9114
9277
  handoff: output.handoff,
@@ -35409,6 +35572,10 @@ var createVoiceTranscriptRedactor = (options = {}) => {
35409
35572
  };
35410
35573
  var redactVoiceTranscript = (transcript, patterns = DEFAULT_VOICE_REDACTION_PATTERNS) => createVoiceTranscriptRedactor({ patterns })(transcript);
35411
35574
  // src/recordingRedaction.ts
35575
+ import {
35576
+ applyAudioRedaction,
35577
+ mergeAudioRedactionRanges
35578
+ } from "@absolutejs/media";
35412
35579
  var matchesAnyPattern = (text, patterns) => {
35413
35580
  for (const pattern of patterns) {
35414
35581
  pattern.regex.lastIndex = 0;
@@ -35446,6 +35613,23 @@ var deriveVoiceRecordingRedactionRanges = (input) => {
35446
35613
  }
35447
35614
  return out;
35448
35615
  };
35616
+ var redactVoiceRecording = (input) => {
35617
+ const derived = deriveVoiceRecordingRedactionRanges({
35618
+ transcripts: input.transcripts,
35619
+ ...input.recordingStartedAtEpochMs !== undefined ? { recordingStartedAtEpochMs: input.recordingStartedAtEpochMs } : {},
35620
+ ...input.paddingMs !== undefined ? { paddingMs: input.paddingMs } : {},
35621
+ ...input.patterns !== undefined ? { patterns: input.patterns } : {}
35622
+ });
35623
+ const ranges = mergeAudioRedactionRanges(derived);
35624
+ const bytes = applyAudioRedaction(input.pcm, input.format, ranges, {
35625
+ ...input.fill !== undefined ? { fill: input.fill } : {}
35626
+ });
35627
+ return {
35628
+ bytes,
35629
+ ranges,
35630
+ redactedCount: ranges.length
35631
+ };
35632
+ };
35449
35633
  // src/costAccounting.ts
35450
35634
  var DEFAULT_VOICE_PRICE_BOOK = {
35451
35635
  "anthropic:claude-opus-4-5": {
@@ -36320,158 +36504,6 @@ var createMonologueAMDDetector = (options = {}) => {
36320
36504
  intervalMs: options.intervalMs ?? 1000
36321
36505
  };
36322
36506
  };
36323
- // src/ragTool.ts
36324
- var extractVoiceRAGCitations = (toolResults, toolName = "searchKnowledgeBase") => {
36325
- const out = [];
36326
- for (const entry of toolResults) {
36327
- if (entry.toolName !== toolName) {
36328
- continue;
36329
- }
36330
- const result = entry.result;
36331
- const citations = result?.citations;
36332
- if (!Array.isArray(citations)) {
36333
- continue;
36334
- }
36335
- for (const citation of citations) {
36336
- out.push({
36337
- chunkId: citation.chunkId,
36338
- score: citation.score,
36339
- source: citation.source,
36340
- title: citation.title
36341
- });
36342
- }
36343
- }
36344
- return out;
36345
- };
36346
- var DEFAULT_TOOL_NAME = "searchKnowledgeBase";
36347
- var DEFAULT_DESCRIPTION = "Search the knowledge base and return short grounded citations. Use this whenever the caller asks a question that may be answered by indexed reference material.";
36348
- var DEFAULT_TOP_K = 6;
36349
- var DEFAULT_MAX_TOP_K = 20;
36350
- var DEFAULT_MAX_CHUNK_CHARS = 320;
36351
- var truncate = (value, limit) => {
36352
- if (limit <= 0 || value.length <= limit)
36353
- return value;
36354
- return `${value.slice(0, Math.max(0, limit - 1)).trimEnd()}\u2026`;
36355
- };
36356
- var formatScore = (score) => {
36357
- if (!Number.isFinite(score))
36358
- return "n/a";
36359
- return score.toFixed(3);
36360
- };
36361
- var buildDefaultCitationMessage = (citations, args, maxChunkChars) => {
36362
- if (citations.length === 0) {
36363
- return `No knowledge base results for "${args.query}".`;
36364
- }
36365
- const lines = citations.map((citation, index) => {
36366
- const label = citation.title ?? citation.source ?? citation.chunkId;
36367
- const text = truncate(citation.chunkText, maxChunkChars);
36368
- return `${String(index + 1)}. ${label} (score ${formatScore(citation.score)}): ${text}`;
36369
- });
36370
- return [
36371
- `Knowledge base results for "${args.query}":`,
36372
- ...lines
36373
- ].join(`
36374
- `);
36375
- };
36376
- var filterAllowedFilterKeys = (filter, allowedKeys) => {
36377
- if (!filter)
36378
- return;
36379
- if (!allowedKeys)
36380
- return filter;
36381
- const allowed = new Set(allowedKeys);
36382
- const entries = Object.entries(filter).filter(([key]) => allowed.has(key));
36383
- if (entries.length === 0)
36384
- return;
36385
- return Object.fromEntries(entries);
36386
- };
36387
- var mergeFilters = (...filters) => {
36388
- const present = filters.filter((entry) => entry !== undefined);
36389
- if (present.length === 0)
36390
- return;
36391
- return Object.assign({}, ...present);
36392
- };
36393
- var buildVoiceRAGToolParameters = (options) => {
36394
- if (options.parameters)
36395
- return options.parameters;
36396
- const defaultTopK = options.topK ?? DEFAULT_TOP_K;
36397
- const maxTopK = options.maxTopK ?? DEFAULT_MAX_TOP_K;
36398
- const properties = {
36399
- query: {
36400
- description: "Natural-language question to look up in the knowledge base.",
36401
- type: "string"
36402
- },
36403
- topK: {
36404
- default: defaultTopK,
36405
- description: `How many citations to return (1-${String(maxTopK)}).`,
36406
- maximum: maxTopK,
36407
- minimum: 1,
36408
- type: "integer"
36409
- }
36410
- };
36411
- if (options.allowedFilterKeys && options.allowedFilterKeys.length > 0) {
36412
- properties.filter = {
36413
- additionalProperties: false,
36414
- description: "Optional metadata filter. Only keys listed here are honored: " + options.allowedFilterKeys.join(", "),
36415
- properties: Object.fromEntries(options.allowedFilterKeys.map((key) => [key, {}])),
36416
- type: "object"
36417
- };
36418
- }
36419
- return {
36420
- additionalProperties: false,
36421
- properties,
36422
- required: ["query"],
36423
- type: "object"
36424
- };
36425
- };
36426
- var createVoiceRAGTool = (collection, options = {}) => {
36427
- const name = options.name ?? DEFAULT_TOOL_NAME;
36428
- const description = options.description ?? DEFAULT_DESCRIPTION;
36429
- const defaultTopK = options.topK ?? DEFAULT_TOP_K;
36430
- const maxTopK = options.maxTopK ?? DEFAULT_MAX_TOP_K;
36431
- const maxChunkChars = options.maxChunkChars ?? DEFAULT_MAX_CHUNK_CHARS;
36432
- const parameters = buildVoiceRAGToolParameters(options);
36433
- return createVoiceAgentTool({
36434
- description,
36435
- execute: async ({ args, context }) => {
36436
- const query = typeof args?.query === "string" ? args.query.trim() : "";
36437
- if (query.length === 0) {
36438
- const empty = {
36439
- citations: [],
36440
- message: "Knowledge base search requires a non-empty query.",
36441
- query: "",
36442
- topK: 0
36443
- };
36444
- return empty;
36445
- }
36446
- const requestedTopK = typeof args?.topK === "number" && Number.isFinite(args.topK) ? Math.min(maxTopK, Math.max(1, Math.floor(args.topK))) : defaultTopK;
36447
- const llmFilter = filterAllowedFilterKeys(args?.filter, options.allowedFilterKeys);
36448
- const fixedFilter = typeof options.fixedFilter === "function" ? options.fixedFilter({ context }) : options.fixedFilter;
36449
- const filter = mergeFilters(fixedFilter, llmFilter);
36450
- const rawResults = await collection.search({
36451
- filter,
36452
- query,
36453
- scoreThreshold: options.scoreThreshold,
36454
- topK: requestedTopK
36455
- });
36456
- const citations = Array.from(rawResults).slice(0, requestedTopK);
36457
- const formatter = options.formatResult ? options.formatResult : (entries, innerArgs) => buildDefaultCitationMessage(entries, innerArgs, maxChunkChars);
36458
- const message = formatter(citations, {
36459
- filter,
36460
- query,
36461
- topK: requestedTopK
36462
- });
36463
- return {
36464
- citations,
36465
- message,
36466
- query,
36467
- topK: requestedTopK
36468
- };
36469
- },
36470
- name,
36471
- parameters,
36472
- resultToMessage: options.resultToMessage ?? ((result) => result.message)
36473
- });
36474
- };
36475
36507
  // src/agentTools.ts
36476
36508
  var createVoiceEndCallTool = (options = {}) => createVoiceAgentTool({
36477
36509
  description: options.description ?? "End the call gracefully. Call this only when the conversation is complete or the caller asks to hang up.",
@@ -51767,6 +51799,7 @@ export {
51767
51799
  redactVoiceTraceText,
51768
51800
  redactVoiceTraceEvents,
51769
51801
  redactVoiceTraceEvent,
51802
+ redactVoiceRecording,
51770
51803
  redactVoiceAuditEvents,
51771
51804
  redactVoiceAuditEvent,
51772
51805
  recordVoiceWorkflowContractTrace,
package/dist/ragTool.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type VoiceAgentTool } from "./agent";
2
- import type { VoiceSessionRecord } from "./types";
2
+ import type { VoiceSessionRecord, VoiceTurnCitation } from "./types";
3
3
  export type VoiceRAGQueryResult = {
4
4
  chunkId: string;
5
5
  chunkText: string;
@@ -29,12 +29,7 @@ export type VoiceRAGToolResult = {
29
29
  query: string;
30
30
  topK: number;
31
31
  };
32
- export type VoiceRAGCitationSummary = {
33
- chunkId: string;
34
- score: number;
35
- source?: string;
36
- title?: string;
37
- };
32
+ export type VoiceRAGCitationSummary = VoiceTurnCitation;
38
33
  export declare const extractVoiceRAGCitations: (toolResults: ReadonlyArray<{
39
34
  result?: unknown;
40
35
  toolName: string;
@@ -1,4 +1,5 @@
1
- import type { Transcript } from "./types";
1
+ import { type AudioRedactionFill } from "@absolutejs/media";
2
+ import type { AudioFormat, Transcript } from "./types";
2
3
  import { type VoiceRedactionPattern } from "./redaction";
3
4
  export type VoiceRecordingRedactionRange = {
4
5
  endMs: number;
@@ -16,3 +17,31 @@ export type DeriveVoiceRecordingRedactionRangesInput = {
16
17
  transcripts: ReadonlyArray<Transcript>;
17
18
  };
18
19
  export declare const deriveVoiceRecordingRedactionRanges: (input: DeriveVoiceRecordingRedactionRangesInput) => VoiceRecordingRedactionRange[];
20
+ export type RedactVoiceRecordingInput = {
21
+ /** Raw pcm_s16le bytes of the recorded artifact. */
22
+ pcm: ArrayBuffer | ArrayBufferView;
23
+ /** Format of the recording (must be raw pcm_s16le). */
24
+ format: AudioFormat;
25
+ /** Final transcripts with timing, scanned for sensitive content. */
26
+ transcripts: ReadonlyArray<Transcript>;
27
+ recordingStartedAtEpochMs?: number;
28
+ paddingMs?: number;
29
+ patterns?: ReadonlyArray<VoiceRedactionPattern>;
30
+ /** Bleep style — silence (default) or a tone. */
31
+ fill?: AudioRedactionFill;
32
+ };
33
+ export type RedactVoiceRecordingResult = {
34
+ /** The redacted pcm_s16le bytes. */
35
+ bytes: Uint8Array;
36
+ /** The merged ranges that were bleeped. */
37
+ ranges: VoiceRecordingRedactionRange[];
38
+ /** How many ranges were redacted. */
39
+ redactedCount: number;
40
+ };
41
+ /**
42
+ * End-to-end recording redaction: derives sensitive ranges from final
43
+ * transcripts, merges overlaps, and applies the audio bleep via
44
+ * `@absolutejs/media`'s `applyAudioRedaction`. Returns the redacted bytes
45
+ * ready to re-store in place of the original artifact.
46
+ */
47
+ export declare const redactVoiceRecording: (input: RedactVoiceRecordingInput) => RedactVoiceRecordingResult;
@@ -5628,7 +5628,8 @@ var setTurnResult = (session, turnId, input) => {
5628
5628
  session.turns = session.turns.map((turn) => turn.id === turnId ? {
5629
5629
  ...turn,
5630
5630
  assistantText: input.assistantText ?? turn.assistantText,
5631
- result: input.result ?? turn.result
5631
+ result: input.result ?? turn.result,
5632
+ citations: input.citations && input.citations.length > 0 ? [...input.citations] : turn.citations
5632
5633
  } : turn);
5633
5634
  };
5634
5635
  var ensureCallLifecycleState = (session) => {
@@ -7016,6 +7017,7 @@ var createVoiceSession = (options) => {
7016
7017
  });
7017
7018
  const output = {
7018
7019
  assistantText: committedOutput?.assistantText,
7020
+ citations: committedOutput?.citations,
7019
7021
  complete: committedOutput?.complete,
7020
7022
  escalate: committedOutput?.escalate,
7021
7023
  noAnswer: committedOutput?.noAnswer,
@@ -7023,6 +7025,12 @@ var createVoiceSession = (options) => {
7023
7025
  transfer: committedOutput?.transfer,
7024
7026
  voicemail: committedOutput?.voicemail
7025
7027
  };
7028
+ if (output.citations && output.citations.length > 0) {
7029
+ const turnCitations = output.citations;
7030
+ await writeSession((currentSession) => {
7031
+ setTurnResult(currentSession, turn.id, { citations: turnCitations });
7032
+ });
7033
+ }
7026
7034
  if (output?.assistantText) {
7027
7035
  const assistantTextStartedAt = Date.now();
7028
7036
  await writeSession((currentSession) => {
package/dist/types.d.ts CHANGED
@@ -247,6 +247,12 @@ export type VoiceReconnectClientState = {
247
247
  nextAttemptAt?: number;
248
248
  status: VoiceReconnectClientStatus;
249
249
  };
250
+ export type VoiceTurnCitation = {
251
+ chunkId: string;
252
+ score: number;
253
+ source?: string;
254
+ title?: string;
255
+ };
250
256
  export type VoiceTurnRecord<TResult = unknown> = {
251
257
  id: string;
252
258
  text: string;
@@ -254,6 +260,7 @@ export type VoiceTurnRecord<TResult = unknown> = {
254
260
  transcripts: Transcript[];
255
261
  assistantText?: string;
256
262
  attachments?: import("./agent").VoiceAgentMessageAttachment[];
263
+ citations?: VoiceTurnCitation[];
257
264
  committedAt: number;
258
265
  result?: TResult;
259
266
  };
@@ -502,6 +509,7 @@ export type VoiceRouteResult<TResult = unknown> = {
502
509
  complete?: boolean;
503
510
  result?: TResult;
504
511
  assistantText?: string;
512
+ citations?: ReadonlyArray<VoiceTurnCitation>;
505
513
  transfer?: {
506
514
  metadata?: Record<string, unknown>;
507
515
  reason?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.517",
3
+ "version": "0.0.22-beta.519",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",