@absolutejs/voice 0.0.22-beta.245 → 0.0.22-beta.247

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
@@ -37,7 +37,7 @@ These are the primitive-first paths a Vapi-style buyer usually needs. Each path
37
37
  | Phone voice assistant | `createVoicePhoneAgent(...)` | carrier matrix, setup instructions, phone smoke contract, production readiness, operations record | phone setup HTML/JSON, smoke HTML/JSON, framework status UI |
38
38
  | Multi-specialist support flow | `createVoiceAgentSquad(...)` | squad contract, handoff traces, context traces, operations record | Agent Squad status hooks/composables/services/widgets |
39
39
  | Business actions and tools | `createVoiceAgentTool(...)` plus agent tool runtime | tool contracts, audit events, integration events, outcome contracts | operations record, action center, contract routes |
40
- | Guardrails and policy checks | `createVoiceGuardrailPolicy(...)` plus `createVoiceGuardrailRoutes(...)` | blocking/warning decisions, redacted content, `assistant.guardrail` trace events | guardrail JSON/Markdown routes and operations record traces |
40
+ | Guardrails and policy checks | `createVoiceGuardrailPolicy(...)`, `createVoiceGuardrailRuntime(...)`, and `createVoiceGuardrailRoutes(...)` | live assistant/tool enforcement, blocking/warning decisions, redacted content, `assistant.guardrail` trace events | guardrail JSON/Markdown routes and operations record traces |
41
41
  | Provider routing and fallback | provider routers, health checks, simulation controls | provider contract matrix, provider-stage traces, latency SLO reports | provider contract hooks/composables/services/widgets |
42
42
  | Production operations | ops status, ops recovery, production readiness, delivery runtime | readiness gates, recovery report, incident Markdown, delivery queues | ops action center, delivery runtime UI, operations record |
43
43
  | Outbound campaigns | `createVoiceCampaignRoutes(...)` | recipient validation, consent/dedupe, carrier dry-run, campaign readiness | campaign routes and operations-record-linked attempt proof |
@@ -116,14 +116,33 @@ app.use(
116
116
 
117
117
  ## Guardrails
118
118
 
119
- Use `createVoiceGuardrailRoutes(...)` when you need code-owned policy checks for what an agent may say, what tool payloads may contain, or which transcript content should warn/redact before downstream workflow. The primitive does not force a moderation vendor or hosted dashboard; it returns JSON/Markdown proof and can emit `assistant.guardrail` trace events.
119
+ Use `createVoiceGuardrailRuntime(...)` when you need code-owned live enforcement for what an agent may say, what tool payloads may contain, or which transcript content should warn/redact before downstream workflow. Use `createVoiceGuardrailRoutes(...)` beside it when you also want JSON/Markdown proof. The primitive does not force a moderation vendor or hosted dashboard; it emits `assistant.guardrail` trace events from the runtime and route surfaces.
120
120
 
121
121
  ```ts
122
122
  import {
123
+ createVoiceGuardrailRuntime,
123
124
  createVoiceGuardrailRoutes,
124
125
  voiceGuardrailPolicyPresets
125
126
  } from '@absolutejs/voice';
126
127
 
128
+ const guardrails = createVoiceGuardrailRuntime({
129
+ blockResult: ({ decision }) => ({
130
+ assistantText: 'I need to route this to a human specialist.',
131
+ escalate: {
132
+ reason: `guardrail-blocked-${decision.stage}`
133
+ }
134
+ }),
135
+ policies: [voiceGuardrailPolicyPresets.supportSafeDefaults],
136
+ trace: runtime.traces
137
+ });
138
+
139
+ const assistant = createVoiceAssistant({
140
+ guardrails: guardrails.assistantGuardrails,
141
+ id: 'support',
142
+ model,
143
+ tools: guardrails.wrapTools([lookupCustomerTool, createTicketTool])
144
+ });
145
+
127
146
  app.use(
128
147
  createVoiceGuardrailRoutes({
129
148
  path: '/api/voice/guardrails',
@@ -1,5 +1,8 @@
1
1
  import { Elysia } from 'elysia';
2
+ import type { VoiceAgentTool } from './agent';
2
3
  import type { VoiceTraceEventStore } from './trace';
4
+ import type { VoiceOnTurnObjectHandler, VoiceRouteResult, VoiceSessionRecord } from './types';
5
+ import type { VoiceAssistantGuardrails } from './assistant';
3
6
  export type VoiceGuardrailStage = 'assistant-output' | 'handoff' | 'model-input' | 'tool-input' | 'tool-output' | 'transcript';
4
7
  export type VoiceGuardrailSeverity = 'block' | 'warn';
5
8
  export type VoiceGuardrailStatus = 'blocked' | 'pass' | 'warn';
@@ -68,12 +71,29 @@ export type VoiceGuardrailRoutesOptions = {
68
71
  source?: ((input: VoiceGuardrailEvaluationInput) => Promise<VoiceGuardrailDecision | VoiceGuardrailReport> | VoiceGuardrailDecision | VoiceGuardrailReport) | VoiceGuardrailDecision | VoiceGuardrailReport;
69
72
  trace?: VoiceTraceEventStore;
70
73
  };
74
+ export type VoiceGuardrailRuntimeBlockInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = Parameters<VoiceOnTurnObjectHandler<TContext, TSession, TResult>>[0] & {
75
+ decision: VoiceGuardrailDecision;
76
+ stage: VoiceGuardrailStage;
77
+ };
78
+ export type VoiceGuardrailRuntimeOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
79
+ blockResult?: (input: VoiceGuardrailRuntimeBlockInput<TContext, TSession, TResult>) => Promise<VoiceRouteResult<TResult>> | VoiceRouteResult<TResult>;
80
+ name?: string;
81
+ policies: VoiceGuardrailPolicy[];
82
+ trace?: VoiceTraceEventStore;
83
+ };
84
+ export type VoiceGuardrailRuntime<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
85
+ assistantGuardrails: VoiceAssistantGuardrails<TContext, TSession, TResult>;
86
+ evaluate: (input: VoiceGuardrailEvaluationInput) => Promise<VoiceGuardrailDecision>;
87
+ wrapTool: <TArgs extends Record<string, unknown>, TToolResult>(tool: VoiceAgentTool<TContext, TSession, TArgs, TToolResult, TResult>) => VoiceAgentTool<TContext, TSession, TArgs, TToolResult, TResult>;
88
+ wrapTools: (tools: Array<VoiceAgentTool<TContext, TSession, Record<string, unknown>, unknown, TResult>>) => Array<VoiceAgentTool<TContext, TSession, Record<string, unknown>, unknown, TResult>>;
89
+ };
71
90
  export declare const evaluateVoiceGuardrailPolicy: (policy: VoiceGuardrailPolicy, input: VoiceGuardrailEvaluationInput) => Promise<VoiceGuardrailDecision>;
72
91
  export declare const buildVoiceGuardrailReport: (input?: {
73
92
  decisions: VoiceGuardrailDecision[];
74
93
  policies?: VoiceGuardrailPolicy[];
75
94
  }) => VoiceGuardrailReport;
76
95
  export declare const createVoiceGuardrailPolicy: (policy: VoiceGuardrailPolicy) => VoiceGuardrailPolicy;
96
+ export declare const createVoiceGuardrailRuntime: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoiceGuardrailRuntimeOptions<TContext, TSession, TResult>) => VoiceGuardrailRuntime<TContext, TSession, TResult>;
77
97
  export declare const voiceGuardrailPolicyPresets: {
78
98
  supportSafeDefaults: VoiceGuardrailPolicy;
79
99
  };
package/dist/index.d.ts CHANGED
@@ -84,7 +84,7 @@ export { createVoiceOpsRuntime } from './opsRuntime';
84
84
  export { resolveVoiceOpsPreset } from './opsPresets';
85
85
  export { resolveVoiceOutcomeRecipe } from './outcomeRecipes';
86
86
  export { buildVoicePostCallAnalysisReport, createVoicePostCallAnalysisRoutes, renderVoicePostCallAnalysisMarkdown } from './postCallAnalysis';
87
- export { buildVoiceGuardrailReport, createVoiceGuardrailPolicy, createVoiceGuardrailRoutes, evaluateVoiceGuardrailPolicy, renderVoiceGuardrailMarkdown, voiceGuardrailPolicyPresets } from './guardrails';
87
+ export { buildVoiceGuardrailReport, createVoiceGuardrailPolicy, createVoiceGuardrailRuntime, createVoiceGuardrailRoutes, evaluateVoiceGuardrailPolicy, renderVoiceGuardrailMarkdown, voiceGuardrailPolicyPresets } from './guardrails';
88
88
  export { createId, createVoiceSessionRecord } from './store';
89
89
  export { createVoiceSTTRoutingCorrectionHandler, resolveVoiceSTTRoutingStrategy } from './routing';
90
90
  export { applyRiskTieredPhraseHintCorrections, applyPhraseHintCorrections, createDomainLexicon, createDomainPhraseHints, createPhraseHintCorrectionHandler, createRiskyTurnCorrectionHandler } from './correction';
@@ -138,7 +138,7 @@ export type { VoiceOpsRuntime, VoiceOpsRuntimeConfig, VoiceOpsRuntimeSummary, Vo
138
138
  export type { VoiceOpsPresetName, VoiceOpsPresetOverrides, VoiceResolvedOpsPreset } from './opsPresets';
139
139
  export type { VoiceOutcomeRecipe, VoiceOutcomeRecipeName, VoiceOutcomeRecipeOptions } from './outcomeRecipes';
140
140
  export type { VoicePostCallAnalysisFieldRequirement, VoicePostCallAnalysisFieldResult, VoicePostCallAnalysisIssue, VoicePostCallAnalysisIssueCode, VoicePostCallAnalysisOptions, VoicePostCallAnalysisReport, VoicePostCallAnalysisRoutesOptions, VoicePostCallAnalysisStatus } from './postCallAnalysis';
141
- export type { VoiceGuardrailDecision, VoiceGuardrailEvaluationInput, VoiceGuardrailFinding, VoiceGuardrailPolicy, VoiceGuardrailReport, VoiceGuardrailRoutesOptions, VoiceGuardrailRule, VoiceGuardrailSeverity, VoiceGuardrailStage, VoiceGuardrailStatus } from './guardrails';
141
+ export type { VoiceGuardrailDecision, VoiceGuardrailEvaluationInput, VoiceGuardrailFinding, VoiceGuardrailPolicy, VoiceGuardrailReport, VoiceGuardrailRuntime, VoiceGuardrailRuntimeBlockInput, VoiceGuardrailRuntimeOptions, VoiceGuardrailRoutesOptions, VoiceGuardrailRule, VoiceGuardrailSeverity, VoiceGuardrailStage, VoiceGuardrailStatus } from './guardrails';
142
142
  export type { VoiceCRMActivitySinkOptions, VoiceHubSpotTaskSinkOptions, VoiceHubSpotTaskUpdateSinkOptions, VoiceHelpdeskTicketSinkOptions, VoiceIntegrationHTTPSinkOptions, VoiceIntegrationSink, VoiceIntegrationSinkDeliveryResult, VoiceLinearIssueSinkOptions, VoiceLinearIssueUpdateSinkOptions, VoiceZendeskTicketSinkOptions, VoiceZendeskTicketUpdateSinkOptions } from './opsSinks';
143
143
  export type { VoiceOpsWebhookEnvelope, VoiceOpsWebhookEntity, VoiceOpsWebhookLinkResolver, VoiceOpsWebhookReceiverRoutesOptions, VoiceOpsWebhookSinkOptions, VoiceOpsWebhookVerificationResult } from './opsWebhook';
144
144
  export type { VoiceHandoffDelivery, VoiceHandoffDeliveryRecord, VoiceHandoffDeliveryRecordInput, VoiceHandoffFanoutResult, VoiceQueuedHandoffDeliveryOptions, VoiceTwilioRedirectHandoffAdapterOptions, VoiceWebhookHandoffAdapterOptions } from './handoff';
package/dist/index.js CHANGED
@@ -29015,6 +29015,158 @@ var buildVoiceGuardrailReport = (input = { decisions: [] }) => {
29015
29015
  };
29016
29016
  };
29017
29017
  var createVoiceGuardrailPolicy = (policy) => policy;
29018
+ var appendGuardrailTrace = async (trace, decision, metadata) => {
29019
+ await trace?.append({
29020
+ at: decision.checkedAt,
29021
+ payload: {
29022
+ allowed: decision.allowed,
29023
+ findingCount: decision.findings.length,
29024
+ findings: decision.findings,
29025
+ metadata,
29026
+ redactedContent: decision.redactedContent,
29027
+ stage: decision.stage,
29028
+ status: decision.status
29029
+ },
29030
+ sessionId: decision.sessionId ?? "guardrail-check",
29031
+ turnId: decision.turnId,
29032
+ type: "assistant.guardrail"
29033
+ });
29034
+ };
29035
+ var defaultGuardrailBlockResult = (reason) => ({
29036
+ assistantText: "I cannot safely complete that in the automated flow. I am routing this to a human specialist.",
29037
+ escalate: {
29038
+ metadata: {
29039
+ guardrail: true
29040
+ },
29041
+ reason
29042
+ }
29043
+ });
29044
+ var createVoiceGuardrailRuntime = (options) => {
29045
+ const evaluate = async (input) => {
29046
+ const decisions = await Promise.all(options.policies.map((policy) => evaluateVoiceGuardrailPolicy(policy, input)));
29047
+ await Promise.all(decisions.map((decision) => appendGuardrailTrace(options.trace, decision, input.metadata)));
29048
+ return decisions.find((decision) => !decision.allowed) ?? decisions[0];
29049
+ };
29050
+ const blockResult = async (input) => options.blockResult?.(input) ?? defaultGuardrailBlockResult(`guardrail-blocked-${input.stage}`);
29051
+ const assistantGuardrails = {
29052
+ beforeTurn: async (input) => {
29053
+ const transcriptDecision = await evaluate({
29054
+ content: input.turn.text,
29055
+ metadata: {
29056
+ runtime: options.name,
29057
+ surface: "live-transcript"
29058
+ },
29059
+ sessionId: input.session.id,
29060
+ stage: "transcript",
29061
+ turnId: input.turn.id
29062
+ });
29063
+ if (!transcriptDecision.allowed) {
29064
+ return blockResult({
29065
+ ...input,
29066
+ decision: transcriptDecision,
29067
+ stage: "transcript"
29068
+ });
29069
+ }
29070
+ const modelInputDecision = await evaluate({
29071
+ content: JSON.stringify({
29072
+ scenarioId: input.session.scenarioId,
29073
+ turn: input.turn.text
29074
+ }),
29075
+ metadata: {
29076
+ runtime: options.name,
29077
+ surface: "live-model-input"
29078
+ },
29079
+ sessionId: input.session.id,
29080
+ stage: "model-input",
29081
+ turnId: input.turn.id
29082
+ });
29083
+ if (!modelInputDecision.allowed) {
29084
+ return blockResult({
29085
+ ...input,
29086
+ decision: modelInputDecision,
29087
+ stage: "model-input"
29088
+ });
29089
+ }
29090
+ return;
29091
+ },
29092
+ afterTurn: async (input) => {
29093
+ if (!input.result.assistantText) {
29094
+ return;
29095
+ }
29096
+ const decision = await evaluate({
29097
+ content: input.result.assistantText,
29098
+ metadata: {
29099
+ runtime: options.name,
29100
+ surface: "live-assistant-output"
29101
+ },
29102
+ sessionId: input.session.id,
29103
+ stage: "assistant-output",
29104
+ turnId: input.turn.id
29105
+ });
29106
+ if (!decision.allowed) {
29107
+ return blockResult({
29108
+ ...input,
29109
+ decision,
29110
+ stage: "assistant-output"
29111
+ });
29112
+ }
29113
+ if (typeof decision.redactedContent === "string" && decision.redactedContent !== input.result.assistantText) {
29114
+ return {
29115
+ ...input.result,
29116
+ assistantText: decision.redactedContent
29117
+ };
29118
+ }
29119
+ return;
29120
+ }
29121
+ };
29122
+ const wrapTool = (tool) => ({
29123
+ ...tool,
29124
+ execute: async (input) => {
29125
+ const inputDecision = await evaluate({
29126
+ content: JSON.stringify({
29127
+ args: input.args,
29128
+ toolName: tool.name
29129
+ }),
29130
+ metadata: {
29131
+ runtime: options.name,
29132
+ surface: "live-tool-input",
29133
+ toolName: tool.name
29134
+ },
29135
+ sessionId: input.session.id,
29136
+ stage: "tool-input",
29137
+ turnId: input.turn.id
29138
+ });
29139
+ if (!inputDecision.allowed) {
29140
+ throw new Error(`Guardrail blocked tool input for ${tool.name}.`);
29141
+ }
29142
+ const result = await tool.execute(input);
29143
+ const outputDecision = await evaluate({
29144
+ content: JSON.stringify({
29145
+ result,
29146
+ toolName: tool.name
29147
+ }),
29148
+ metadata: {
29149
+ runtime: options.name,
29150
+ surface: "live-tool-output",
29151
+ toolName: tool.name
29152
+ },
29153
+ sessionId: input.session.id,
29154
+ stage: "tool-output",
29155
+ turnId: input.turn.id
29156
+ });
29157
+ if (!outputDecision.allowed) {
29158
+ throw new Error(`Guardrail blocked tool output for ${tool.name}.`);
29159
+ }
29160
+ return result;
29161
+ }
29162
+ });
29163
+ return {
29164
+ assistantGuardrails,
29165
+ evaluate,
29166
+ wrapTool,
29167
+ wrapTools: (tools) => tools.map((tool) => wrapTool(tool))
29168
+ };
29169
+ };
29018
29170
  var voiceGuardrailPolicyPresets = {
29019
29171
  supportSafeDefaults: createVoiceGuardrailPolicy({
29020
29172
  id: "support-safe-defaults",
@@ -29864,6 +30016,7 @@ export {
29864
30016
  createVoiceHandoffDeliveryWorkerLoop,
29865
30017
  createVoiceHandoffDeliveryWorker,
29866
30018
  createVoiceHandoffDeliveryRecord,
30019
+ createVoiceGuardrailRuntime,
29867
30020
  createVoiceGuardrailRoutes,
29868
30021
  createVoiceGuardrailPolicy,
29869
30022
  createVoiceFileTraceSinkDeliveryStore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.245",
3
+ "version": "0.0.22-beta.247",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",