@absolutejs/voice 0.0.22-beta.193 → 0.0.22-beta.194

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1045,6 +1045,19 @@ const frontDesk = createVoiceAgentSquad({
1045
1045
  id: 'front-desk',
1046
1046
  defaultAgentId: 'support',
1047
1047
  agents: [supportAgent, billingAgent],
1048
+ contextPolicy: ({ summaryMessage, turn }) => ({
1049
+ messages: [
1050
+ summaryMessage,
1051
+ {
1052
+ content: turn.text,
1053
+ role: 'user'
1054
+ }
1055
+ ],
1056
+ metadata: {
1057
+ contextPolicy: 'handoff-summary-and-current-turn'
1058
+ },
1059
+ system: 'Use only the handoff summary and current caller turn.'
1060
+ }),
1048
1061
  handoffPolicy: ({ handoff }) => {
1049
1062
  if (handoff.targetAgentId === 'billing') {
1050
1063
  return {
@@ -1074,6 +1087,10 @@ voice({
1074
1087
 
1075
1088
  For production call centers, pass `handoffPolicy` to keep routing code-owned instead of dashboard-owned. The policy can allow a handoff, reroute it to a different specialist, merge handoff metadata, summarize the reason for the target agent, or block the handoff and return an escalation. Squad traces mark each handoff as `allowed`, `blocked`, `unknown-target`, or `max-exceeded`, so support teams can audit why a caller moved between specialists.
1076
1089
 
1090
+ Pass `contextPolicy` when a specialist should receive a controlled context window. The default behavior preserves the accumulated conversation plus a system handoff summary. A context policy can trim that to a handoff summary and current turn, add a specialist-specific system prompt, or attach metadata that appears in the returned squad state. This is the code-owned equivalent of Vapi Squads context controls: the app decides what each specialist sees, and `agent.context` traces show whether default or custom context was applied.
1091
+
1092
+ Each specialist owns its own `tools`, so tool permissions stay explicit per agent. For example, support can have `lookup_order`, billing can have `refund_invoice`, and scheduling can have `book_appointment`. The squad only routes; it does not give every specialist every tool by default.
1093
+
1077
1094
  Use `runVoiceAgentSquadContract(...)` in tests or readiness checks when you need proof that a specialist graph still routes correctly:
1078
1095
 
1079
1096
  ```ts
package/dist/agent.d.ts CHANGED
@@ -95,6 +95,11 @@ export type VoiceAgentSquadHandoffPolicyResult<TResult = unknown> = {
95
95
  summary?: string;
96
96
  targetAgentId?: string;
97
97
  };
98
+ export type VoiceAgentSquadContextPolicyResult = {
99
+ messages?: VoiceAgentMessage[];
100
+ metadata?: Record<string, unknown>;
101
+ system?: string;
102
+ };
98
103
  export type VoiceAgent<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
99
104
  id: string;
100
105
  onTurn: VoiceOnTurnObjectHandler<TContext, TSession, TResult>;
@@ -136,6 +141,16 @@ export type VoiceAgentSquadOptions<TContext = unknown, TSession extends VoiceSes
136
141
  targetAgent?: VoiceAgent<TContext, TSession, TResult>;
137
142
  turn: VoiceTurnRecord;
138
143
  }) => Promise<VoiceAgentSquadHandoffPolicyResult<TResult> | void> | VoiceAgentSquadHandoffPolicyResult<TResult> | void;
144
+ contextPolicy?: (input: {
145
+ context: TContext;
146
+ fromAgentId: string;
147
+ handoff: VoiceAgentSquadStateHandoff;
148
+ messages: VoiceAgentMessage[];
149
+ session: TSession;
150
+ summaryMessage: VoiceAgentMessage;
151
+ targetAgent: VoiceAgent<TContext, TSession, TResult>;
152
+ turn: VoiceTurnRecord;
153
+ }) => Promise<VoiceAgentSquadContextPolicyResult | void> | VoiceAgentSquadContextPolicyResult | void;
139
154
  id: string;
140
155
  maxHandoffsPerTurn?: number;
141
156
  onHandoff?: (input: {
@@ -3533,6 +3533,8 @@ var renderTraceEventMarkdown = (event, startedAt) => {
3533
3533
  return event.payload.text ? `${label} assistant "${formatTraceValue(event.payload.text)}"` : `${label} ${formatTraceValue(event.payload.status)}`;
3534
3534
  case "agent.tool":
3535
3535
  return `${label} ${formatTraceValue(event.payload.toolName)} ${formatTraceValue(event.payload.status)}`;
3536
+ case "agent.context":
3537
+ return `${label} ${formatTraceValue(event.payload.fromAgentId)} -> ${formatTraceValue(event.payload.targetAgentId)} ${formatTraceValue(event.payload.status)}`;
3536
3538
  case "agent.handoff":
3537
3539
  return `${label} ${formatTraceValue(event.payload.fromAgentId)} -> ${formatTraceValue(event.payload.targetAgentId)}`;
3538
3540
  case "session.error":
package/dist/index.d.ts CHANGED
@@ -118,7 +118,7 @@ export type { StoredVoiceIncidentBundleArtifact, VoiceIncidentBundle, VoiceIncid
118
118
  export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
119
119
  export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePageData, VoiceResilienceRoutesOptions, VoiceResilienceSimulationProvider, VoiceRoutingKindSummary, VoiceRoutingDecisionSummary, VoiceRoutingDecisionSummaryOptions, VoiceRoutingEvent, VoiceRoutingEventKind, VoiceRoutingSessionSummary, VoiceRoutingSessionSummaryOptions } from './resilienceRoutes';
120
120
  export type { VoiceIOProviderRouterEvent, VoiceIOProviderRouterOptions, VoiceIOProviderRouterPolicy, VoiceIOProviderRouterPolicyConfig, VoiceSTTProviderRouterOptions, VoiceTTSProviderRouterOptions } from './providerAdapters';
121
- export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadHandoffPolicyResult, VoiceAgentSquadHandoffStatus, VoiceAgentSquadOptions, VoiceAgentSquadState, VoiceAgentSquadStateHandoff, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
121
+ export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadContextPolicyResult, VoiceAgentSquadHandoffPolicyResult, VoiceAgentSquadHandoffStatus, VoiceAgentSquadOptions, VoiceAgentSquadState, VoiceAgentSquadStateHandoff, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
122
122
  export type { VoiceAgentSquadContractDefinition, VoiceAgentSquadContractIssue, VoiceAgentSquadContractOutcome, VoiceAgentSquadContractReport, VoiceAgentSquadContractRunOptions, VoiceAgentSquadContractTurn, VoiceAgentSquadContractTurnReport, VoiceAgentSquadHandoffExpectation, VoiceAgentSquadTurnExpectation } from './agentSquadContract';
123
123
  export type { VoiceToolRetryDelay, VoiceToolRuntime, VoiceToolRuntimeExecuteInput, VoiceToolRuntimeOptions, VoiceToolRuntimeResult } from './toolRuntime';
124
124
  export type { VoiceToolContractCase, VoiceToolContractCaseReport, VoiceToolContractDefinition, VoiceToolContractExpectation, VoiceToolContractHandlerOptions, VoiceToolContractHTMLHandlerOptions, VoiceToolContractIssue, VoiceToolContractReport, VoiceToolContractRoutesOptions, VoiceToolContractSuiteReport } from './toolContract';
package/dist/index.js CHANGED
@@ -7756,7 +7756,7 @@ var createVoiceAgentSquad = (options) => {
7756
7756
  targetAgentId: nextAgent.id,
7757
7757
  turn: input.turn
7758
7758
  });
7759
- await appendVoiceAgentSquadHandoff({
7759
+ const handoff = await appendVoiceAgentSquadHandoff({
7760
7760
  agentId: options.id,
7761
7761
  fromAgentId: agent.id,
7762
7762
  handoffs,
@@ -7782,17 +7782,54 @@ var createVoiceAgentSquad = (options) => {
7782
7782
  sessionId: input.session.id,
7783
7783
  toAgentId: nextAgent.id
7784
7784
  });
7785
- messages.push({
7785
+ const summaryMessage = {
7786
7786
  content: handoffSummary ?? handoffReason ?? `Handoff to ${nextAgent.id}`,
7787
7787
  metadata,
7788
7788
  name: nextAgent.id,
7789
7789
  role: "system"
7790
+ };
7791
+ messages.push(summaryMessage);
7792
+ const contextPolicy = await options.contextPolicy?.({
7793
+ context: input.context,
7794
+ fromAgentId: agent.id,
7795
+ handoff,
7796
+ messages,
7797
+ session: input.session,
7798
+ summaryMessage,
7799
+ targetAgent: nextAgent,
7800
+ turn: input.turn
7801
+ });
7802
+ if (contextPolicy?.metadata && Object.keys(contextPolicy.metadata).length > 0) {
7803
+ handoff.metadata = {
7804
+ ...handoff.metadata,
7805
+ ...contextPolicy.metadata
7806
+ };
7807
+ const latest = handoffs.at(-1);
7808
+ if (latest === handoff) {
7809
+ latest.metadata = handoff.metadata;
7810
+ }
7811
+ }
7812
+ await appendVoiceAgentTrace({
7813
+ agentId: options.id,
7814
+ event: {
7815
+ fromAgentId: handoff.fromAgentId,
7816
+ messageCount: messages.length,
7817
+ nextMessageCount: contextPolicy?.messages?.length ?? messages.length,
7818
+ status: contextPolicy ? "applied" : "default",
7819
+ summaryIncluded: (contextPolicy?.messages ?? messages).some((message) => message === summaryMessage),
7820
+ targetAgentId: nextAgent.id
7821
+ },
7822
+ session: input.session,
7823
+ trace: options.trace,
7824
+ turn: input.turn,
7825
+ type: "agent.context"
7790
7826
  });
7791
7827
  agent = nextAgent;
7792
7828
  agentId = nextAgent.id;
7793
7829
  result = await agent.run({
7794
7830
  ...input,
7795
- messages
7831
+ messages: contextPolicy?.messages ?? messages,
7832
+ system: contextPolicy?.system ?? input.system
7796
7833
  });
7797
7834
  toolResults.push(...result.toolResults);
7798
7835
  }
@@ -9503,6 +9540,8 @@ var renderTraceEventMarkdown = (event, startedAt) => {
9503
9540
  return event.payload.text ? `${label} assistant "${formatTraceValue(event.payload.text)}"` : `${label} ${formatTraceValue(event.payload.status)}`;
9504
9541
  case "agent.tool":
9505
9542
  return `${label} ${formatTraceValue(event.payload.toolName)} ${formatTraceValue(event.payload.status)}`;
9543
+ case "agent.context":
9544
+ return `${label} ${formatTraceValue(event.payload.fromAgentId)} -> ${formatTraceValue(event.payload.targetAgentId)} ${formatTraceValue(event.payload.status)}`;
9506
9545
  case "agent.handoff":
9507
9546
  return `${label} ${formatTraceValue(event.payload.fromAgentId)} -> ${formatTraceValue(event.payload.targetAgentId)}`;
9508
9547
  case "session.error":
@@ -1621,6 +1621,8 @@ var renderTraceEventMarkdown = (event, startedAt) => {
1621
1621
  return event.payload.text ? `${label} assistant "${formatTraceValue(event.payload.text)}"` : `${label} ${formatTraceValue(event.payload.status)}`;
1622
1622
  case "agent.tool":
1623
1623
  return `${label} ${formatTraceValue(event.payload.toolName)} ${formatTraceValue(event.payload.status)}`;
1624
+ case "agent.context":
1625
+ return `${label} ${formatTraceValue(event.payload.fromAgentId)} -> ${formatTraceValue(event.payload.targetAgentId)} ${formatTraceValue(event.payload.status)}`;
1624
1626
  case "agent.handoff":
1625
1627
  return `${label} ${formatTraceValue(event.payload.fromAgentId)} -> ${formatTraceValue(event.payload.targetAgentId)}`;
1626
1628
  case "session.error":
package/dist/trace.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { S3Client, S3Options } from 'bun';
2
- export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'client.barge_in' | 'client.live_latency' | 'client.reconnect' | 'operator.action' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn_latency.stage' | 'turn.transcript' | 'workflow.contract';
2
+ export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.context' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'client.barge_in' | 'client.live_latency' | 'client.reconnect' | 'operator.action' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn_latency.stage' | 'turn.transcript' | 'workflow.contract';
3
3
  export type VoiceTraceEvent<TPayload extends Record<string, unknown> = Record<string, unknown>> = {
4
4
  at: number;
5
5
  id?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.193",
3
+ "version": "0.0.22-beta.194",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",