@absolutejs/voice 0.0.22-beta.3 → 0.0.22-beta.5

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,5 +1,6 @@
1
1
  import { type VoiceAgent, type VoiceAgentModel, type VoiceAgentOptions, type VoiceAgentSquadOptions, type VoiceAgentTool } from './agent';
2
2
  import { type VoiceOutcomeRecipeName, type VoiceOutcomeRecipeOptions } from './outcomeRecipes';
3
+ import { type VoiceAssistantMemoryHandle, type VoiceAssistantMemoryOptions } from './assistantMemory';
3
4
  import type { VoiceNormalizedRouteConfig, VoiceOnTurnObjectHandler, VoiceRouteConfig, VoiceRouteResult, VoiceRuntimeOpsConfig, VoiceSessionRecord } from './types';
4
5
  import type { StoredVoiceTraceEvent, VoiceTraceEventStore } from './trace';
5
6
  export type VoiceAssistantPreset = VoiceOutcomeRecipeName;
@@ -12,6 +13,7 @@ export type VoiceAssistantArtifactPlan<TContext = unknown, TSession extends Voic
12
13
  };
13
14
  export type VoiceAssistantGuardrailInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = Parameters<VoiceOnTurnObjectHandler<TContext, TSession, TResult>>[0] & {
14
15
  assistantId: string;
16
+ memory?: VoiceAssistantMemoryHandle;
15
17
  };
16
18
  export type VoiceAssistantOutputGuardrailInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = VoiceAssistantGuardrailInput<TContext, TSession, TResult> & {
17
19
  result: VoiceRouteResult<TResult>;
@@ -29,6 +31,16 @@ export type VoiceAssistantVariant<TContext = unknown, TSession extends VoiceSess
29
31
  tools?: Array<VoiceAgentTool<TContext, TSession, Record<string, unknown>, unknown, TResult>>;
30
32
  weight?: number;
31
33
  };
34
+ export type VoiceAssistantMemoryLifecycleInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = Parameters<VoiceOnTurnObjectHandler<TContext, TSession, TResult>>[0] & {
35
+ assistantId: string;
36
+ memory: VoiceAssistantMemoryHandle;
37
+ };
38
+ export type VoiceAssistantMemoryLifecycle<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
39
+ afterTurn?: (input: VoiceAssistantMemoryLifecycleInput<TContext, TSession, TResult> & {
40
+ result: VoiceRouteResult<TResult>;
41
+ }) => Promise<void> | void;
42
+ beforeTurn?: (input: VoiceAssistantMemoryLifecycleInput<TContext, TSession, TResult>) => Promise<void> | void;
43
+ };
32
44
  export type VoiceAssistantExperimentResolverInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord> = {
33
45
  assistantId: string;
34
46
  context: TContext;
@@ -83,6 +95,8 @@ export type VoiceAssistantOptions<TContext = unknown, TSession extends VoiceSess
83
95
  experiment?: VoiceAssistantExperiment<TContext, TSession, TResult>;
84
96
  guardrails?: VoiceAssistantGuardrails<TContext, TSession, TResult>;
85
97
  id: string;
98
+ memory?: VoiceAssistantMemoryOptions<TContext, TSession>;
99
+ memoryLifecycle?: VoiceAssistantMemoryLifecycle<TContext, TSession, TResult>;
86
100
  ops?: VoiceRuntimeOpsConfig<TContext, TSession, TResult>;
87
101
  trace?: VoiceAgentOptions<TContext, TSession, TResult>['trace'];
88
102
  };
@@ -109,6 +123,12 @@ export type VoiceAssistantRunSummary = {
109
123
  toolCalls: Record<string, number>;
110
124
  transferCount: number;
111
125
  variants: Record<string, number>;
126
+ memory: {
127
+ deletes: number;
128
+ gets: number;
129
+ lists: number;
130
+ sets: number;
131
+ };
112
132
  };
113
133
  export type VoiceAssistantRunsSummary = {
114
134
  assistants: VoiceAssistantRunSummary[];
@@ -0,0 +1,63 @@
1
+ import type { VoiceSessionRecord } from './types';
2
+ import type { VoiceTraceEventStore } from './trace';
3
+ export type VoiceAssistantMemoryRecord<TValue = unknown, TMetadata extends Record<string, unknown> = Record<string, unknown>> = {
4
+ assistantId: string;
5
+ createdAt: number;
6
+ key: string;
7
+ metadata?: TMetadata;
8
+ namespace: string;
9
+ updatedAt: number;
10
+ value: TValue;
11
+ };
12
+ export type VoiceAssistantMemoryStore<TRecord extends VoiceAssistantMemoryRecord = VoiceAssistantMemoryRecord> = {
13
+ delete: (input: {
14
+ assistantId: string;
15
+ key: string;
16
+ namespace: string;
17
+ }) => Promise<void>;
18
+ get: (input: {
19
+ assistantId: string;
20
+ key: string;
21
+ namespace: string;
22
+ }) => Promise<TRecord | undefined>;
23
+ list: (input: {
24
+ assistantId: string;
25
+ namespace?: string;
26
+ }) => Promise<TRecord[]>;
27
+ set: (input: Omit<TRecord, 'createdAt' | 'updatedAt'> & {
28
+ createdAt?: number;
29
+ updatedAt?: number;
30
+ }) => Promise<TRecord>;
31
+ };
32
+ export type VoiceAssistantMemoryNamespaceInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord> = {
33
+ assistantId: string;
34
+ context: TContext;
35
+ session: TSession;
36
+ };
37
+ export type VoiceAssistantMemoryOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TRecord extends VoiceAssistantMemoryRecord = VoiceAssistantMemoryRecord> = {
38
+ namespace: string | ((input: VoiceAssistantMemoryNamespaceInput<TContext, TSession>) => Promise<string> | string);
39
+ store: VoiceAssistantMemoryStore<TRecord>;
40
+ };
41
+ export type VoiceAssistantMemoryBinding<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TRecord extends VoiceAssistantMemoryRecord = VoiceAssistantMemoryRecord> = VoiceAssistantMemoryOptions<TContext, TSession, TRecord>;
42
+ export type VoiceAssistantMemoryHandle<TRecord extends VoiceAssistantMemoryRecord = VoiceAssistantMemoryRecord> = {
43
+ delete: (key: string) => Promise<void>;
44
+ get: <TValue = unknown>(key: string) => Promise<TValue | undefined>;
45
+ list: () => Promise<TRecord[]>;
46
+ namespace: string;
47
+ set: <TValue = unknown>(key: string, value: TValue, metadata?: Record<string, unknown>) => Promise<TRecord>;
48
+ };
49
+ export declare const createVoiceAssistantMemoryRecord: <TValue = unknown, TMetadata extends Record<string, unknown> = Record<string, unknown>>(input: Omit<VoiceAssistantMemoryRecord<TValue, TMetadata>, "createdAt" | "updatedAt"> & {
50
+ createdAt?: number;
51
+ updatedAt?: number;
52
+ }) => VoiceAssistantMemoryRecord<TValue, TMetadata>;
53
+ export declare const createVoiceMemoryAssistantMemoryStore: <TRecord extends VoiceAssistantMemoryRecord = VoiceAssistantMemoryRecord>() => VoiceAssistantMemoryStore<TRecord>;
54
+ export declare const resolveVoiceAssistantMemoryNamespace: <TContext, TSession extends VoiceSessionRecord>(input: VoiceAssistantMemoryNamespaceInput<TContext, TSession> & {
55
+ memory: VoiceAssistantMemoryOptions<TContext, TSession>;
56
+ }) => Promise<string>;
57
+ export declare const createVoiceAssistantMemoryHandle: <TContext, TSession extends VoiceSessionRecord, TRecord extends VoiceAssistantMemoryRecord = VoiceAssistantMemoryRecord>(input: {
58
+ assistantId: string;
59
+ context: TContext;
60
+ memory: VoiceAssistantMemoryOptions<TContext, TSession, TRecord>;
61
+ session: TSession;
62
+ trace?: VoiceTraceEventStore;
63
+ }) => Promise<VoiceAssistantMemoryHandle<TRecord>>;
@@ -1,3 +1,4 @@
1
+ import { type VoiceAssistantMemoryRecord, type VoiceAssistantMemoryStore } from './assistantMemory';
1
2
  import { type StoredVoiceTraceEvent, type VoiceTraceSinkDeliveryRecord, type VoiceTraceSinkDeliveryStore, type VoiceTraceEventStore } from './trace';
2
3
  import type { StoredVoiceIntegrationEvent, StoredVoiceExternalObjectMap, StoredVoiceOpsTask, VoiceExternalObjectMap, VoiceExternalObjectMapStore, VoiceIntegrationEvent, VoiceIntegrationEventStore, VoiceOpsTask, VoiceOpsTaskStore } from './ops';
3
4
  import type { StoredVoiceCallReviewArtifact, VoiceCallReviewArtifact, VoiceCallReviewStore } from './testing/review';
@@ -6,9 +7,10 @@ export type VoiceFileStoreOptions = {
6
7
  directory: string;
7
8
  pretty?: boolean;
8
9
  };
9
- export type VoiceFileRuntimeStorage<TSession extends VoiceSessionRecord = VoiceSessionRecord, TReview extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact, TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask, TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent, TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap, TTrace extends StoredVoiceTraceEvent = StoredVoiceTraceEvent, TTraceDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord> = {
10
+ export type VoiceFileRuntimeStorage<TSession extends VoiceSessionRecord = VoiceSessionRecord, TReview extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact, TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask, TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent, TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap, TTrace extends StoredVoiceTraceEvent = StoredVoiceTraceEvent, TTraceDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord, TMemory extends VoiceAssistantMemoryRecord = VoiceAssistantMemoryRecord> = {
10
11
  events: VoiceIntegrationEventStore<TEvent>;
11
12
  externalObjects: VoiceExternalObjectMapStore<TMapping>;
13
+ memories: VoiceAssistantMemoryStore<TMemory>;
12
14
  reviews: VoiceCallReviewStore<TReview>;
13
15
  session: VoiceSessionStore<TSession>;
14
16
  tasks: VoiceOpsTaskStore<TTask>;
@@ -22,7 +24,8 @@ export declare const createVoiceFileIntegrationEventStore: <TEvent extends Store
22
24
  export declare const createVoiceFileExternalObjectMapStore: <TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap>(options: VoiceFileStoreOptions) => VoiceExternalObjectMapStore<TMapping>;
23
25
  export declare const createVoiceFileTraceEventStore: <TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent>(options: VoiceFileStoreOptions) => VoiceTraceEventStore<TEvent>;
24
26
  export declare const createVoiceFileTraceSinkDeliveryStore: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceFileStoreOptions) => VoiceTraceSinkDeliveryStore<TDelivery>;
25
- export declare const createVoiceFileRuntimeStorage: <TSession extends VoiceSessionRecord = VoiceSessionRecord, TReview extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact, TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask, TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent, TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap, TTrace extends StoredVoiceTraceEvent = StoredVoiceTraceEvent, TTraceDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceFileStoreOptions) => VoiceFileRuntimeStorage<TSession, TReview, TTask, TEvent, TMapping, TTrace, TTraceDelivery>;
27
+ export declare const createVoiceFileAssistantMemoryStore: <TRecord extends VoiceAssistantMemoryRecord = VoiceAssistantMemoryRecord>(options: VoiceFileStoreOptions) => VoiceAssistantMemoryStore<TRecord>;
28
+ export declare const createVoiceFileRuntimeStorage: <TSession extends VoiceSessionRecord = VoiceSessionRecord, TReview extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact, TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask, TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent, TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap, TTrace extends StoredVoiceTraceEvent = StoredVoiceTraceEvent, TTraceDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord, TMemory extends VoiceAssistantMemoryRecord = VoiceAssistantMemoryRecord>(options: VoiceFileStoreOptions) => VoiceFileRuntimeStorage<TSession, TReview, TTask, TEvent, TMapping, TTrace, TTraceDelivery, TMemory>;
26
29
  export declare const createStoredVoiceCallReviewArtifact: <TArtifact extends VoiceCallReviewArtifact = VoiceCallReviewArtifact>(id: string, artifact: TArtifact) => TArtifact & {
27
30
  id: string;
28
31
  };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export { voice } from './plugin';
2
2
  export { createVoiceAssistant, createVoiceExperiment, summarizeVoiceAssistantRuns } from './assistant';
3
3
  export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
4
- export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
4
+ export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
5
+ export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
6
+ export { createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel } from './modelAdapters';
5
7
  export { buildVoiceTraceReplay, createVoiceMemoryTraceSinkDeliveryStore, createVoiceTraceHTTPSink, createVoiceMemoryTraceEventStore, createVoiceTraceSinkDeliveryId, createVoiceTraceSinkDeliveryRecord, createVoiceTraceSinkStore, createVoiceTraceEvent, createVoiceTraceEventId, deliverVoiceTraceEventsToSinks, evaluateVoiceTrace, exportVoiceTrace, filterVoiceTraceEvents, pruneVoiceTraceEvents, redactVoiceTraceEvent, redactVoiceTraceEvents, redactVoiceTraceText, renderVoiceTraceHTML, renderVoiceTraceMarkdown, resolveVoiceTraceRedactionOptions, selectVoiceTraceEventsForPrune, summarizeVoiceTrace } from './trace';
6
8
  export { createVoiceSQLiteExternalObjectMapStore, createVoiceSQLiteIntegrationEventStore, createVoiceSQLiteReviewStore, createVoiceSQLiteRuntimeStorage, createVoiceSQLiteSessionStore, createVoiceSQLiteTaskStore, createVoiceSQLiteTraceSinkDeliveryStore, createVoiceSQLiteTraceEventStore } from './sqliteStore';
7
9
  export { createVoicePostgresExternalObjectMapStore, createVoicePostgresIntegrationEventStore, createVoicePostgresReviewStore, createVoicePostgresRuntimeStorage, createVoicePostgresSessionStore, createVoicePostgresTaskStore, createVoicePostgresTraceSinkDeliveryStore, createVoicePostgresTraceEventStore } from './postgresStore';
@@ -22,7 +24,9 @@ export { conditionAudioChunk, resolveAudioConditioningConfig } from './audioCond
22
24
  export { resolveVoiceRuntimePreset } from './presets';
23
25
  export { resolveTurnDetectionConfig, TURN_PROFILE_DEFAULTS } from './turnProfiles';
24
26
  export { createVoiceCallReviewFromLiveTelephonyReport, createVoiceCallReviewRecorder, renderVoiceCallReviewHTML, renderVoiceCallReviewMarkdown } from './testing/review';
25
- export type { VoiceAssistant, VoiceAssistantArtifactPlan, VoiceAssistantExperiment, VoiceAssistantExperimentOptions, VoiceAssistantGuardrailInput, VoiceAssistantGuardrails, VoiceAssistantOptions, VoiceAssistantOutputGuardrailInput, VoiceAssistantPreset, VoiceAssistantRunsSummary, VoiceAssistantRunSummary, VoiceAssistantVariant } from './assistant';
27
+ export type { VoiceAssistant, VoiceAssistantArtifactPlan, VoiceAssistantExperiment, VoiceAssistantExperimentOptions, VoiceAssistantGuardrailInput, VoiceAssistantGuardrails, VoiceAssistantMemoryLifecycle, VoiceAssistantMemoryLifecycleInput, VoiceAssistantOptions, VoiceAssistantOutputGuardrailInput, VoiceAssistantPreset, VoiceAssistantRunsSummary, VoiceAssistantRunSummary, VoiceAssistantVariant } from './assistant';
28
+ export type { VoiceAssistantMemoryBinding, VoiceAssistantMemoryHandle, VoiceAssistantMemoryOptions, VoiceAssistantMemoryRecord, VoiceAssistantMemoryStore } from './assistantMemory';
29
+ export type { OpenAIVoiceAssistantModelOptions, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
26
30
  export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadOptions, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
27
31
  export type { VoiceOpsRuntime, VoiceOpsRuntimeConfig, VoiceOpsRuntimeSummary, VoiceOpsRuntimeSinkWorkerConfig, VoiceOpsRuntimeTaskWorkerConfig, VoiceOpsRuntimeTickResult, VoiceOpsRuntimeWebhookWorkerConfig } from './opsRuntime';
28
32
  export type { VoiceOpsPresetName, VoiceOpsPresetOverrides, VoiceResolvedOpsPreset } from './opsPresets';
package/dist/index.js CHANGED
@@ -5594,6 +5594,112 @@ var resolveVoiceOutcomeRecipe = (name, options = {}) => {
5594
5594
  };
5595
5595
  };
5596
5596
 
5597
+ // src/assistantMemory.ts
5598
+ var createMemoryId = (input) => `${input.assistantId}:${input.namespace}:${input.key}`;
5599
+ var createVoiceAssistantMemoryRecord = (input) => {
5600
+ const now = Date.now();
5601
+ return {
5602
+ ...input,
5603
+ createdAt: input.createdAt ?? input.updatedAt ?? now,
5604
+ updatedAt: input.updatedAt ?? now
5605
+ };
5606
+ };
5607
+ var createVoiceMemoryAssistantMemoryStore = () => {
5608
+ const records = new Map;
5609
+ return {
5610
+ delete: async (input) => {
5611
+ records.delete(createMemoryId(input));
5612
+ },
5613
+ get: async (input) => records.get(createMemoryId(input)),
5614
+ 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),
5615
+ set: async (input) => {
5616
+ const id = createMemoryId(input);
5617
+ const existing = records.get(id);
5618
+ const record = createVoiceAssistantMemoryRecord({
5619
+ ...input,
5620
+ createdAt: input.createdAt ?? existing?.createdAt,
5621
+ updatedAt: input.updatedAt
5622
+ });
5623
+ records.set(id, record);
5624
+ return record;
5625
+ }
5626
+ };
5627
+ };
5628
+ var resolveVoiceAssistantMemoryNamespace = async (input) => typeof input.memory.namespace === "function" ? await input.memory.namespace(input) : input.memory.namespace;
5629
+ var createVoiceAssistantMemoryHandle = async (input) => {
5630
+ const namespace = await resolveVoiceAssistantMemoryNamespace({
5631
+ assistantId: input.assistantId,
5632
+ context: input.context,
5633
+ memory: input.memory,
5634
+ session: input.session
5635
+ });
5636
+ const trace = async (event) => {
5637
+ await input.trace?.append({
5638
+ at: Date.now(),
5639
+ payload: {
5640
+ assistantId: input.assistantId,
5641
+ namespace,
5642
+ ...event
5643
+ },
5644
+ scenarioId: input.session.scenarioId,
5645
+ sessionId: input.session.id,
5646
+ type: "assistant.memory"
5647
+ });
5648
+ };
5649
+ return {
5650
+ delete: async (key) => {
5651
+ await input.memory.store.delete({
5652
+ assistantId: input.assistantId,
5653
+ key,
5654
+ namespace
5655
+ });
5656
+ await trace({
5657
+ action: "delete",
5658
+ key
5659
+ });
5660
+ },
5661
+ get: async (key) => {
5662
+ const record = await input.memory.store.get({
5663
+ assistantId: input.assistantId,
5664
+ key,
5665
+ namespace
5666
+ });
5667
+ await trace({
5668
+ action: "get",
5669
+ found: Boolean(record),
5670
+ key
5671
+ });
5672
+ return record?.value;
5673
+ },
5674
+ list: async () => {
5675
+ const records = await input.memory.store.list({
5676
+ assistantId: input.assistantId,
5677
+ namespace
5678
+ });
5679
+ await trace({
5680
+ action: "list",
5681
+ count: records.length
5682
+ });
5683
+ return records;
5684
+ },
5685
+ namespace,
5686
+ set: async (key, value, metadata) => {
5687
+ const record = await input.memory.store.set({
5688
+ assistantId: input.assistantId,
5689
+ key,
5690
+ metadata,
5691
+ namespace,
5692
+ value
5693
+ });
5694
+ await trace({
5695
+ action: "set",
5696
+ key
5697
+ });
5698
+ return record;
5699
+ }
5700
+ };
5701
+ };
5702
+
5597
5703
  // src/assistant.ts
5598
5704
  var hashString = (value) => {
5599
5705
  let hash = 2166136261;
@@ -5742,12 +5848,35 @@ var createVoiceAssistant = (options) => {
5742
5848
  });
5743
5849
  }
5744
5850
  const onTurn = async (input) => {
5851
+ const memory = options.memory ? await createVoiceAssistantMemoryHandle({
5852
+ assistantId: options.id,
5853
+ context: input.context,
5854
+ memory: options.memory,
5855
+ session: input.session,
5856
+ trace: options.trace
5857
+ }) : undefined;
5745
5858
  const guardrailInput = {
5746
5859
  ...input,
5747
- assistantId: options.id
5860
+ assistantId: options.id,
5861
+ memory
5748
5862
  };
5863
+ if (memory) {
5864
+ await options.memoryLifecycle?.beforeTurn?.({
5865
+ ...input,
5866
+ assistantId: options.id,
5867
+ memory
5868
+ });
5869
+ }
5749
5870
  const blocked = await options.guardrails?.beforeTurn?.(guardrailInput);
5750
5871
  if (blocked) {
5872
+ if (memory) {
5873
+ await options.memoryLifecycle?.afterTurn?.({
5874
+ ...input,
5875
+ assistantId: options.id,
5876
+ memory,
5877
+ result: blocked
5878
+ });
5879
+ }
5751
5880
  await appendAssistantTrace({
5752
5881
  assistantId: options.id,
5753
5882
  event: {
@@ -5797,6 +5926,14 @@ var createVoiceAssistant = (options) => {
5797
5926
  result
5798
5927
  });
5799
5928
  const finalResult = guarded ?? result;
5929
+ if (memory) {
5930
+ await options.memoryLifecycle?.afterTurn?.({
5931
+ ...input,
5932
+ assistantId: options.id,
5933
+ memory,
5934
+ result: finalResult
5935
+ });
5936
+ }
5800
5937
  if (guarded) {
5801
5938
  await appendAssistantTrace({
5802
5939
  assistantId: options.id,
@@ -5864,6 +6001,12 @@ var summarizeVoiceAssistantRuns = async (input) => {
5864
6001
  escalationCount: 0,
5865
6002
  experiments: {},
5866
6003
  guardrailCount: 0,
6004
+ memory: {
6005
+ deletes: 0,
6006
+ gets: 0,
6007
+ lists: 0,
6008
+ sets: 0
6009
+ },
5867
6010
  outcomes: {},
5868
6011
  runCount: 0,
5869
6012
  sessionIds: new Set,
@@ -5919,6 +6062,24 @@ var summarizeVoiceAssistantRuns = async (input) => {
5919
6062
  const summary = getSummary(assistantId);
5920
6063
  summary.guardrailCount += 1;
5921
6064
  }
6065
+ for (const event of events.filter((event2) => event2.type === "assistant.memory")) {
6066
+ const assistantId = typeof event.payload.assistantId === "string" ? event.payload.assistantId : "unknown";
6067
+ const summary = getSummary(assistantId);
6068
+ switch (event.payload.action) {
6069
+ case "delete":
6070
+ summary.memory.deletes += 1;
6071
+ break;
6072
+ case "get":
6073
+ summary.memory.gets += 1;
6074
+ break;
6075
+ case "list":
6076
+ summary.memory.lists += 1;
6077
+ break;
6078
+ case "set":
6079
+ summary.memory.sets += 1;
6080
+ break;
6081
+ }
6082
+ }
5922
6083
  const assistants = [...byAssistant.values()].map(({ elapsedCount, elapsedTotal, sessionIds, ...summary }) => ({
5923
6084
  ...summary,
5924
6085
  averageElapsedMs: elapsedCount > 0 ? Math.round(elapsedTotal / elapsedCount) : undefined,
@@ -6613,6 +6774,7 @@ var listJsonFiles = async (directory) => {
6613
6774
  };
6614
6775
  var encodeStoreId = (id) => `${encodeURIComponent(id)}.json`;
6615
6776
  var resolveFilePath = (directory, id) => join(directory, encodeStoreId(id));
6777
+ var createMemoryStoreId = (input) => `${input.assistantId}:${input.namespace}:${input.key}`;
6616
6778
  var readJsonFile = async (path) => JSON.parse(await readFile(path, "utf8"));
6617
6779
  var writeJsonFile = async (path, value, options) => {
6618
6780
  await mkdir(options.directory, {
@@ -6832,6 +6994,40 @@ var createVoiceFileTraceSinkDeliveryStore = (options) => {
6832
6994
  };
6833
6995
  return { get, list, remove, set };
6834
6996
  };
6997
+ var createVoiceFileAssistantMemoryStore = (options) => {
6998
+ const get = async (input) => {
6999
+ const path = resolveFilePath(options.directory, createMemoryStoreId(input));
7000
+ try {
7001
+ return await readJsonFile(path);
7002
+ } catch (error) {
7003
+ if (error.code === "ENOENT") {
7004
+ return;
7005
+ }
7006
+ throw error;
7007
+ }
7008
+ };
7009
+ const list = async (input) => {
7010
+ const files = await listJsonFiles(options.directory);
7011
+ const records = await Promise.all(files.map((file) => readJsonFile(file)));
7012
+ return records.filter((record) => record.assistantId === input.assistantId && (input.namespace === undefined || record.namespace === input.namespace)).sort((left, right) => right.updatedAt - left.updatedAt);
7013
+ };
7014
+ const set = async (input) => {
7015
+ const existing = await get(input);
7016
+ const record = createVoiceAssistantMemoryRecord({
7017
+ ...input,
7018
+ createdAt: input.createdAt ?? existing?.createdAt,
7019
+ updatedAt: input.updatedAt
7020
+ });
7021
+ await writeJsonFile(resolveFilePath(options.directory, createMemoryStoreId(record)), record, options);
7022
+ return record;
7023
+ };
7024
+ const remove = async (input) => {
7025
+ await rm(resolveFilePath(options.directory, createMemoryStoreId(input)), {
7026
+ force: true
7027
+ });
7028
+ };
7029
+ return { delete: remove, get, list, set };
7030
+ };
6835
7031
  var createVoiceFileRuntimeStorage = (options) => ({
6836
7032
  events: createVoiceFileIntegrationEventStore({
6837
7033
  ...options,
@@ -6841,6 +7037,10 @@ var createVoiceFileRuntimeStorage = (options) => ({
6841
7037
  ...options,
6842
7038
  directory: join(options.directory, "external-objects")
6843
7039
  }),
7040
+ memories: createVoiceFileAssistantMemoryStore({
7041
+ ...options,
7042
+ directory: join(options.directory, "memories")
7043
+ }),
6844
7044
  reviews: createVoiceFileReviewStore({
6845
7045
  ...options,
6846
7046
  directory: join(options.directory, "reviews")
@@ -6873,6 +7073,254 @@ var createStoredVoiceExternalObjectMap = (mapping) => createVoiceExternalObjectM
6873
7073
  sourceId: mapping.sourceId,
6874
7074
  sourceType: mapping.sourceType
6875
7075
  });
7076
+ // src/modelAdapters.ts
7077
+ var OUTPUT_SCHEMA = {
7078
+ additionalProperties: false,
7079
+ properties: {
7080
+ assistantText: {
7081
+ type: "string"
7082
+ },
7083
+ complete: {
7084
+ type: "boolean"
7085
+ },
7086
+ escalate: {
7087
+ additionalProperties: false,
7088
+ properties: {
7089
+ metadata: {
7090
+ additionalProperties: true,
7091
+ type: "object"
7092
+ },
7093
+ reason: {
7094
+ type: "string"
7095
+ }
7096
+ },
7097
+ required: ["reason"],
7098
+ type: "object"
7099
+ },
7100
+ noAnswer: {
7101
+ additionalProperties: false,
7102
+ properties: {
7103
+ metadata: {
7104
+ additionalProperties: true,
7105
+ type: "object"
7106
+ }
7107
+ },
7108
+ type: "object"
7109
+ },
7110
+ result: {
7111
+ additionalProperties: true,
7112
+ type: "object"
7113
+ },
7114
+ transfer: {
7115
+ additionalProperties: false,
7116
+ properties: {
7117
+ metadata: {
7118
+ additionalProperties: true,
7119
+ type: "object"
7120
+ },
7121
+ reason: {
7122
+ type: "string"
7123
+ },
7124
+ target: {
7125
+ type: "string"
7126
+ }
7127
+ },
7128
+ required: ["target"],
7129
+ type: "object"
7130
+ },
7131
+ voicemail: {
7132
+ additionalProperties: false,
7133
+ properties: {
7134
+ metadata: {
7135
+ additionalProperties: true,
7136
+ type: "object"
7137
+ }
7138
+ },
7139
+ type: "object"
7140
+ }
7141
+ },
7142
+ type: "object"
7143
+ };
7144
+ var parseJSON = (value) => {
7145
+ try {
7146
+ const parsed = JSON.parse(value);
7147
+ return parsed && typeof parsed === "object" ? parsed : {};
7148
+ } catch {
7149
+ return {
7150
+ assistantText: value
7151
+ };
7152
+ }
7153
+ };
7154
+ var normalizeRouteOutput = (output) => {
7155
+ const result = {};
7156
+ if (typeof output.assistantText === "string") {
7157
+ result.assistantText = output.assistantText;
7158
+ }
7159
+ if (typeof output.complete === "boolean") {
7160
+ result.complete = output.complete;
7161
+ }
7162
+ if (output.result !== undefined) {
7163
+ result.result = output.result;
7164
+ }
7165
+ if (output.transfer && typeof output.transfer === "object") {
7166
+ const transfer = output.transfer;
7167
+ if (typeof transfer.target === "string") {
7168
+ result.transfer = {
7169
+ metadata: transfer.metadata && typeof transfer.metadata === "object" ? transfer.metadata : undefined,
7170
+ reason: typeof transfer.reason === "string" ? transfer.reason : undefined,
7171
+ target: transfer.target
7172
+ };
7173
+ }
7174
+ }
7175
+ if (output.escalate && typeof output.escalate === "object") {
7176
+ const escalate = output.escalate;
7177
+ if (typeof escalate.reason === "string") {
7178
+ result.escalate = {
7179
+ metadata: escalate.metadata && typeof escalate.metadata === "object" ? escalate.metadata : undefined,
7180
+ reason: escalate.reason
7181
+ };
7182
+ }
7183
+ }
7184
+ if (output.voicemail && typeof output.voicemail === "object") {
7185
+ const voicemail = output.voicemail;
7186
+ result.voicemail = {
7187
+ metadata: voicemail.metadata && typeof voicemail.metadata === "object" ? voicemail.metadata : undefined
7188
+ };
7189
+ }
7190
+ if (output.noAnswer && typeof output.noAnswer === "object") {
7191
+ const noAnswer = output.noAnswer;
7192
+ result.noAnswer = {
7193
+ metadata: noAnswer.metadata && typeof noAnswer.metadata === "object" ? noAnswer.metadata : undefined
7194
+ };
7195
+ }
7196
+ return result;
7197
+ };
7198
+ var createJSONVoiceAssistantModel = (options) => ({
7199
+ generate: async (input) => {
7200
+ const output = await options.generate(input);
7201
+ if ("assistantText" in output || "toolCalls" in output || "complete" in output || "transfer" in output || "escalate" in output) {
7202
+ return output;
7203
+ }
7204
+ return options.mapOutput?.(output) ?? normalizeRouteOutput(output);
7205
+ }
7206
+ });
7207
+ var messageToOpenAIInput = (message) => {
7208
+ if (message.role === "tool") {
7209
+ return {
7210
+ call_id: message.toolCallId ?? message.name ?? crypto.randomUUID(),
7211
+ output: message.content,
7212
+ type: "function_call_output"
7213
+ };
7214
+ }
7215
+ return {
7216
+ content: message.content,
7217
+ role: message.role === "system" ? "developer" : message.role
7218
+ };
7219
+ };
7220
+ var extractText = (response) => {
7221
+ if (typeof response.output_text === "string") {
7222
+ return response.output_text;
7223
+ }
7224
+ const output = Array.isArray(response.output) ? response.output : [];
7225
+ for (const item of output) {
7226
+ if (!item || typeof item !== "object") {
7227
+ continue;
7228
+ }
7229
+ const record = item;
7230
+ const content = Array.isArray(record.content) ? record.content : [];
7231
+ for (const contentItem of content) {
7232
+ if (!contentItem || typeof contentItem !== "object") {
7233
+ continue;
7234
+ }
7235
+ const contentRecord = contentItem;
7236
+ if (typeof contentRecord.text === "string") {
7237
+ return contentRecord.text;
7238
+ }
7239
+ }
7240
+ }
7241
+ return "";
7242
+ };
7243
+ var extractToolCalls = (response) => {
7244
+ const output = Array.isArray(response.output) ? response.output : [];
7245
+ const toolCalls = [];
7246
+ for (const item of output) {
7247
+ if (!item || typeof item !== "object") {
7248
+ continue;
7249
+ }
7250
+ const record = item;
7251
+ if (record.type !== "function_call" || typeof record.name !== "string") {
7252
+ continue;
7253
+ }
7254
+ const args = typeof record.arguments === "string" ? parseJSON(record.arguments) : {};
7255
+ toolCalls.push({
7256
+ args,
7257
+ id: typeof record.call_id === "string" ? record.call_id : typeof record.id === "string" ? record.id : undefined,
7258
+ name: record.name
7259
+ });
7260
+ }
7261
+ return toolCalls;
7262
+ };
7263
+ var createOpenAIVoiceAssistantModel = (options) => {
7264
+ const fetchImpl = options.fetch ?? globalThis.fetch;
7265
+ const baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
7266
+ const model = options.model ?? "gpt-4.1-mini";
7267
+ return {
7268
+ generate: async (input) => {
7269
+ const response = await fetchImpl(`${baseUrl.replace(/\/$/, "")}/responses`, {
7270
+ body: JSON.stringify({
7271
+ input: input.messages.map(messageToOpenAIInput),
7272
+ instructions: [
7273
+ input.system,
7274
+ "Return a JSON object with assistantText, complete, transfer, escalate, voicemail, noAnswer, and result when you are not calling tools."
7275
+ ].filter(Boolean).join(`
7276
+
7277
+ `),
7278
+ max_output_tokens: options.maxOutputTokens,
7279
+ model,
7280
+ temperature: options.temperature,
7281
+ text: {
7282
+ format: {
7283
+ name: "voice_route_result",
7284
+ schema: OUTPUT_SCHEMA,
7285
+ strict: false,
7286
+ type: "json_schema"
7287
+ }
7288
+ },
7289
+ tool_choice: input.tools.length ? "auto" : "none",
7290
+ tools: input.tools.map((tool) => ({
7291
+ description: tool.description,
7292
+ name: tool.name,
7293
+ parameters: tool.parameters ?? {
7294
+ additionalProperties: true,
7295
+ type: "object"
7296
+ },
7297
+ strict: false,
7298
+ type: "function"
7299
+ }))
7300
+ }),
7301
+ headers: {
7302
+ authorization: `Bearer ${options.apiKey}`,
7303
+ "content-type": "application/json"
7304
+ },
7305
+ method: "POST"
7306
+ });
7307
+ if (!response.ok) {
7308
+ throw new Error(`OpenAI voice assistant model failed: HTTP ${response.status}`);
7309
+ }
7310
+ const body = await response.json();
7311
+ if (body.usage && typeof body.usage === "object") {
7312
+ await options.onUsage?.(body.usage);
7313
+ }
7314
+ const toolCalls = extractToolCalls(body);
7315
+ if (toolCalls.length) {
7316
+ return {
7317
+ toolCalls
7318
+ };
7319
+ }
7320
+ return normalizeRouteOutput(parseJSON(extractText(body)));
7321
+ }
7322
+ };
7323
+ };
6876
7324
  // src/sqliteStore.ts
6877
7325
  import { Database } from "bun:sqlite";
6878
7326
  var normalizeTableNameSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
@@ -9339,6 +9787,7 @@ export {
9339
9787
  resolveVoiceOpsTaskAssignment,
9340
9788
  resolveVoiceOpsTaskAgeBucket,
9341
9789
  resolveVoiceOpsPreset,
9790
+ resolveVoiceAssistantMemoryNamespace,
9342
9791
  resolveTurnDetectionConfig,
9343
9792
  resolveAudioConditioningConfig,
9344
9793
  requeueVoiceOpsTask,
@@ -9414,6 +9863,7 @@ export {
9414
9863
  createVoiceMemoryTraceSinkDeliveryStore,
9415
9864
  createVoiceMemoryTraceEventStore,
9416
9865
  createVoiceMemoryStore,
9866
+ createVoiceMemoryAssistantMemoryStore,
9417
9867
  createVoiceLinearIssueUpdateSink,
9418
9868
  createVoiceLinearIssueSyncSinks,
9419
9869
  createVoiceLinearIssueSink,
@@ -9433,6 +9883,7 @@ export {
9433
9883
  createVoiceFileReviewStore,
9434
9884
  createVoiceFileIntegrationEventStore,
9435
9885
  createVoiceFileExternalObjectMapStore,
9886
+ createVoiceFileAssistantMemoryStore,
9436
9887
  createVoiceExternalObjectMapId,
9437
9888
  createVoiceExternalObjectMap,
9438
9889
  createVoiceExperiment,
@@ -9441,6 +9892,8 @@ export {
9441
9892
  createVoiceCallReviewFromLiveTelephonyReport,
9442
9893
  createVoiceCallCompletedEvent,
9443
9894
  createVoiceCRMActivitySink,
9895
+ createVoiceAssistantMemoryRecord,
9896
+ createVoiceAssistantMemoryHandle,
9444
9897
  createVoiceAssistant,
9445
9898
  createVoiceAgentTool,
9446
9899
  createVoiceAgentSquad,
@@ -9453,6 +9906,8 @@ export {
9453
9906
  createStoredVoiceCallReviewArtifact,
9454
9907
  createRiskyTurnCorrectionHandler,
9455
9908
  createPhraseHintCorrectionHandler,
9909
+ createOpenAIVoiceAssistantModel,
9910
+ createJSONVoiceAssistantModel,
9456
9911
  createId,
9457
9912
  createDomainPhraseHints,
9458
9913
  createDomainLexicon,
@@ -0,0 +1,18 @@
1
+ import type { VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput } from './agent';
2
+ import type { VoiceSessionRecord } from './types';
3
+ export type VoiceJSONAssistantModelHandler<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = (input: VoiceAgentModelInput<TContext, TSession>) => Promise<Record<string, unknown> | VoiceAgentModelOutput<TResult>> | Record<string, unknown> | VoiceAgentModelOutput<TResult>;
4
+ export type VoiceJSONAssistantModelOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
5
+ generate: VoiceJSONAssistantModelHandler<TContext, TSession, TResult>;
6
+ mapOutput?: (output: Record<string, unknown>) => VoiceAgentModelOutput<TResult>;
7
+ };
8
+ export type OpenAIVoiceAssistantModelOptions = {
9
+ apiKey: string;
10
+ baseUrl?: string;
11
+ fetch?: typeof fetch;
12
+ maxOutputTokens?: number;
13
+ model?: string;
14
+ onUsage?: (usage: Record<string, unknown>) => Promise<void> | void;
15
+ temperature?: number;
16
+ };
17
+ export declare const createJSONVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoiceJSONAssistantModelOptions<TContext, TSession, TResult>) => VoiceAgentModel<TContext, TSession, TResult>;
18
+ export declare const createOpenAIVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: OpenAIVoiceAssistantModelOptions) => VoiceAgentModel<TContext, TSession, TResult>;
package/dist/trace.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.lifecycle' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn.transcript';
1
+ export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.lifecycle' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn.transcript';
2
2
  export type VoiceTraceEvent<TPayload extends Record<string, unknown> = Record<string, unknown>> = {
3
3
  at: number;
4
4
  id?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.3",
3
+ "version": "0.0.22-beta.5",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",