@absolutejs/voice 0.0.22-beta.246 → 0.0.22-beta.248
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 +22 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +70 -1
- package/dist/operationsRecord.d.ts +26 -0
- package/package.json +1 -1
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(...)`
|
|
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 `
|
|
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',
|
|
@@ -2773,7 +2792,7 @@ app.use(
|
|
|
2773
2792
|
);
|
|
2774
2793
|
```
|
|
2775
2794
|
|
|
2776
|
-
`createVoiceOperationsRecordRoutes(...)` links the call/session timeline, transcript, replay, provider decisions, tools, handoffs, audit, reviews, ops tasks, integration events, and sink delivery attempts into one debuggable object. Use `/voice-operations/:sessionId` as the first place to investigate failed calls, provider failures, handoff failures, slow turns, and campaign attempts. The same mount also exposes incident handoff Markdown at `/voice-operations/:sessionId/incident.md` and `/api/voice-operations/:sessionId/incident.md` for support tooling.
|
|
2795
|
+
`createVoiceOperationsRecordRoutes(...)` links the call/session timeline, transcript, replay, provider decisions, tools, handoffs, guardrail decisions, audit, reviews, ops tasks, integration events, and sink delivery attempts into one debuggable object. Use `/voice-operations/:sessionId` as the first place to investigate failed calls, blocked assistant output, blocked tool payloads, provider failures, handoff failures, slow turns, and campaign attempts. The same mount also exposes incident handoff Markdown at `/voice-operations/:sessionId/incident.md` and `/api/voice-operations/:sessionId/incident.md` for support tooling, including an `assistant.guardrail` blocked-stage summary when guardrail trace events exist.
|
|
2777
2796
|
|
|
2778
2797
|
Most proof surfaces can link to the same record by passing an operations-record URL template such as `/voice-operations/:sessionId`. Use that template anywhere a report emits session-level failures: production readiness, ops recovery, trace timelines, session lists, reviews, campaign attempts, eval reports, simulation-suite actions, tool-contract cases, and outcome-contract matched sessions. The goal is that no operator has to guess which trace, review, task, or delivery queue belongs to the failing call.
|
|
2779
2798
|
|
package/dist/index.d.ts
CHANGED
|
@@ -56,7 +56,7 @@ export { buildVoiceProductionReadinessGate, buildVoiceProductionReadinessReport,
|
|
|
56
56
|
export { createVoiceReadinessProfile, recommendVoiceReadinessProfile } from './readinessProfiles';
|
|
57
57
|
export { buildVoiceProviderContractMatrix, createVoiceProviderContractMatrixHTMLHandler, createVoiceProviderContractMatrixJSONHandler, createVoiceProviderContractMatrixPreset, createVoiceProviderContractMatrixRoutes, evaluateVoiceProviderStackGaps, renderVoiceProviderContractMatrixHTML, recommendVoiceProviderStack } from './providerStackRecommendations';
|
|
58
58
|
export { buildVoiceOpsConsoleReport, createVoiceOpsConsoleRoutes, renderVoiceOpsConsoleHTML } from './opsConsoleRoutes';
|
|
59
|
-
export { buildVoiceOperationsRecord, createVoiceOperationsRecordRoutes, renderVoiceOperationsRecordHTML, renderVoiceOperationsRecordIncidentMarkdown } from './operationsRecord';
|
|
59
|
+
export { buildVoiceOperationsRecord, createVoiceOperationsRecordRoutes, renderVoiceOperationsRecordGuardrailMarkdown, renderVoiceOperationsRecordHTML, renderVoiceOperationsRecordIncidentMarkdown } from './operationsRecord';
|
|
60
60
|
export { assertVoiceObservabilityExportRecord, buildVoiceObservabilityArtifactIndex, buildVoiceObservabilityExportDeliveryHistory, buildVoiceObservabilityExportReplayReport, buildVoiceObservabilityExport, assertVoiceObservabilityExportSchema, createVoiceObservabilityExportSchema, createVoiceFileObservabilityExportDeliveryReceiptStore, createVoiceMemoryObservabilityExportDeliveryReceiptStore, createVoiceObservabilityExportRoutes, createVoiceObservabilityExportReplayRoutes, deliverVoiceObservabilityExport, loadVoiceObservabilityExportReplaySource, replayVoiceObservabilityExport, renderVoiceObservabilityExportReplayHTML, renderVoiceObservabilityExportMarkdown, validateVoiceObservabilityExportRecord, voiceObservabilityExportSchemaId, voiceObservabilityExportSchemaVersion } from './observabilityExport';
|
|
61
61
|
export { buildVoiceOpsRecoveryReadinessCheck, buildVoiceOpsRecoveryReport, createVoiceOpsRecoveryRoutes, renderVoiceOpsRecoveryHTML, renderVoiceOpsRecoveryMarkdown } from './opsRecovery';
|
|
62
62
|
export { buildVoiceIncidentBundle, createStoredVoiceIncidentBundleArtifact, createVoiceIncidentBundleRoutes, createVoiceMemoryIncidentBundleStore, pruneVoiceIncidentBundleArtifacts, saveVoiceIncidentBundleArtifact } from './incidentBundle';
|
|
@@ -123,7 +123,7 @@ export type { VoiceOpsStatus, VoiceOpsStatusLink, VoiceOpsStatusOptions, VoiceOp
|
|
|
123
123
|
export type { VoiceProductionReadinessAction, VoiceProductionReadinessAuditOptions, VoiceProductionReadinessAuditRequirement, VoiceProductionReadinessAuditSummary, VoiceProductionReadinessCheck, VoiceProductionReadinessGateIssue, VoiceProductionReadinessGateOptions, VoiceProductionReadinessGateProfile, VoiceProductionReadinessGateProfileSurface, VoiceProductionReadinessGateReport, VoiceProductionReadinessOpsActionHistoryOptions, VoiceProductionReadinessOpsActionHistorySummary, VoiceProductionReadinessOperationsRecordLink, VoiceProductionReadinessOperationsRecordLinks, VoiceProductionReadinessProfileExplanation, VoiceProductionReadinessProfileSurface, VoiceProductionReadinessProofSource, VoiceProductionReadinessReport, VoiceProductionReadinessRoutesOptions, VoiceProductionReadinessTraceDeliverySummary, VoiceProductionReadinessAuditDeliveryOptions, VoiceProductionReadinessAuditDeliverySummary, VoiceProductionReadinessTraceDeliveryOptions, VoiceProductionReadinessStatus } from './productionReadiness';
|
|
124
124
|
export type { VoiceReadinessProfileName, VoiceReadinessProfileOptions, VoiceReadinessProfileRecommendation, VoiceReadinessProfileRecommendationScore, VoiceReadinessProfileRoutesOptions } from './readinessProfiles';
|
|
125
125
|
export type { VoiceProviderStackChoice, VoiceProviderStackCapabilities, VoiceProviderStackCapabilityGap, VoiceProviderStackCapabilityGapInput, VoiceProviderStackCapabilityGapReport, VoiceProviderContractCheck, VoiceProviderContractCheckStatus, VoiceProviderContractDefinition, VoiceProviderContractMatrixHandlerOptions, VoiceProviderContractMatrixHTMLHandlerOptions, VoiceProviderContractMatrixInput, VoiceProviderContractMatrixPresetOptions, VoiceProviderContractMatrixReport, VoiceProviderContractMatrixRoutesOptions, VoiceProviderContractMatrixRow, VoiceProviderStackInput, VoiceProviderStackKind, VoiceProviderStackRecommendation } from './providerStackRecommendations';
|
|
126
|
-
export type { VoiceOperationsRecord, VoiceOperationsRecordAgentHandoff, VoiceOperationsRecordAuditSummary, VoiceOperationsRecordIntegrationEventSummary, VoiceOperationsRecordOptions, VoiceOperationsRecordOutcome, VoiceOperationsRecordProviderDecision, VoiceOperationsRecordReviewSummary, VoiceOperationsRecordRoutesOptions, VoiceOperationsRecordStatus, VoiceOperationsRecordTaskSummary, VoiceOperationsRecordTranscriptTurn, VoiceOperationsRecordTool } from './operationsRecord';
|
|
126
|
+
export type { VoiceOperationsRecord, VoiceOperationsRecordAgentHandoff, VoiceOperationsRecordAuditSummary, VoiceOperationsRecordGuardrailDecision, VoiceOperationsRecordGuardrailFinding, VoiceOperationsRecordGuardrailSummary, VoiceOperationsRecordIntegrationEventSummary, VoiceOperationsRecordOptions, VoiceOperationsRecordOutcome, VoiceOperationsRecordProviderDecision, VoiceOperationsRecordReviewSummary, VoiceOperationsRecordRoutesOptions, VoiceOperationsRecordStatus, VoiceOperationsRecordTaskSummary, VoiceOperationsRecordTranscriptTurn, VoiceOperationsRecordTool } from './operationsRecord';
|
|
127
127
|
export type { VoiceObservabilityExportArtifact, VoiceObservabilityExportArtifactChecksum, VoiceObservabilityExportArtifactFreshness, VoiceObservabilityExportArtifactIndex, VoiceObservabilityExportArtifactIndexItem, VoiceObservabilityExportArtifactKind, VoiceObservabilityExportDeliverySummary, VoiceObservabilityExportDeliveryDestination, VoiceObservabilityExportDeliveryDestinationResult, VoiceObservabilityExportDeliveryHistory, VoiceObservabilityExportDeliveryOptions, VoiceObservabilityExportDeliveryReceipt, VoiceObservabilityExportDeliveryReceiptStore, VoiceObservabilityExportDeliveryReport, VoiceObservabilityExportEnvelope, VoiceObservabilityExportIssue, VoiceObservabilityExportIssueCode, VoiceObservabilityExportOptions, VoiceObservabilityExportIngestedRecordKind, VoiceObservabilityExportRedactionSummary, VoiceObservabilityExportRecordValidationOptions, VoiceObservabilityExportReplayIssue, VoiceObservabilityExportReplayIssueCode, VoiceObservabilityExportReplayRecords, VoiceObservabilityExportReplayReport, VoiceObservabilityExportReplayRoutesOptions, VoiceObservabilityExportReplaySource, VoiceObservabilityExportReport, VoiceObservabilityExportRoutesOptions, VoiceObservabilityExportSchema, VoiceObservabilityExportStatus, VoiceObservabilityExportValidationIssue, VoiceObservabilityExportValidationResult } from './observabilityExport';
|
|
128
128
|
export type { VoiceOpsRecoveryFailedSession, VoiceOpsRecoveryInterventionSummary, VoiceOpsRecoveryIssue, VoiceOpsRecoveryIssueCode, VoiceOpsRecoveryLinks, VoiceOpsRecoveryProviderSummary, VoiceOpsRecoveryReport, VoiceOpsRecoveryReportOptions, VoiceOpsRecoveryRoutesOptions, VoiceOpsRecoveryStatus } from './opsRecovery';
|
|
129
129
|
export type { StoredVoiceIncidentBundleArtifact, VoiceIncidentBundle, VoiceIncidentBundleArtifactOptions, VoiceIncidentBundleFormat, VoiceIncidentBundleOptions, VoiceIncidentBundleRetentionOptions, VoiceIncidentBundleRetentionReport, VoiceIncidentBundleRoutesOptions, VoiceIncidentBundleStore, VoiceIncidentBundleStoreFilter, VoiceIncidentBundleSummary } from './incidentBundle';
|
package/dist/index.js
CHANGED
|
@@ -22872,6 +22872,7 @@ var createVoiceTraceTimelineRoutes = (options) => {
|
|
|
22872
22872
|
// src/operationsRecord.ts
|
|
22873
22873
|
var getString16 = (value) => typeof value === "string" ? value : undefined;
|
|
22874
22874
|
var getNumber9 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
22875
|
+
var getBoolean3 = (value) => typeof value === "boolean" ? value : undefined;
|
|
22875
22876
|
var countOutcome = (events, outcome) => events.filter((event) => event.outcome === outcome).length;
|
|
22876
22877
|
var matchesSessionScopedId = (id, sessionId) => id === sessionId || id.startsWith(`${sessionId}:`);
|
|
22877
22878
|
var hasPayloadValue = (payload, key, values) => {
|
|
@@ -22899,6 +22900,44 @@ var toTool = (event) => ({
|
|
|
22899
22900
|
toolName: getString16(event.payload.toolName),
|
|
22900
22901
|
turnId: event.turnId
|
|
22901
22902
|
});
|
|
22903
|
+
var toGuardrailFinding = (value) => {
|
|
22904
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
22905
|
+
return;
|
|
22906
|
+
}
|
|
22907
|
+
const record = value;
|
|
22908
|
+
return {
|
|
22909
|
+
action: getString16(record.action),
|
|
22910
|
+
label: getString16(record.label),
|
|
22911
|
+
ruleId: getString16(record.ruleId)
|
|
22912
|
+
};
|
|
22913
|
+
};
|
|
22914
|
+
var toGuardrailDecision = (event) => ({
|
|
22915
|
+
allowed: getBoolean3(event.payload.allowed),
|
|
22916
|
+
at: event.at,
|
|
22917
|
+
findings: Array.isArray(event.payload.findings) ? event.payload.findings.map(toGuardrailFinding).filter((finding) => finding !== undefined) : [],
|
|
22918
|
+
metadata: event.metadata && typeof event.metadata === "object" && !Array.isArray(event.metadata) ? event.metadata : undefined,
|
|
22919
|
+
proof: getString16(event.metadata?.proof),
|
|
22920
|
+
stage: getString16(event.payload.stage),
|
|
22921
|
+
status: getString16(event.payload.status),
|
|
22922
|
+
toolName: getString16(event.payload.toolName),
|
|
22923
|
+
turnId: event.turnId
|
|
22924
|
+
});
|
|
22925
|
+
var summarizeGuardrails = (events) => {
|
|
22926
|
+
const decisions = events.filter((event) => event.type === "assistant.guardrail").map(toGuardrailDecision);
|
|
22927
|
+
const isBlocked = (decision) => decision.allowed === false || decision.status === "fail";
|
|
22928
|
+
const isWarned = (decision) => decision.status === "warn" || decision.findings.some((finding) => finding.action === "warn");
|
|
22929
|
+
const stages = [
|
|
22930
|
+
...new Set(decisions.map((decision) => decision.stage).filter((stage) => typeof stage === "string"))
|
|
22931
|
+
].sort();
|
|
22932
|
+
return {
|
|
22933
|
+
blocked: decisions.filter(isBlocked).length,
|
|
22934
|
+
decisions,
|
|
22935
|
+
passed: decisions.filter((decision) => !isBlocked(decision) && !isWarned(decision)).length,
|
|
22936
|
+
stages,
|
|
22937
|
+
total: decisions.length,
|
|
22938
|
+
warned: decisions.filter(isWarned).length
|
|
22939
|
+
};
|
|
22940
|
+
};
|
|
22902
22941
|
var toProviderDecision = (event) => {
|
|
22903
22942
|
const provider = getString16(event.payload.provider) ?? getString16(event.payload.selectedProvider) ?? getString16(event.payload.fallbackProvider) ?? getString16(event.payload.variantId);
|
|
22904
22943
|
const status = getString16(event.payload.providerStatus) ?? getString16(event.payload.status) ?? getString16(event.payload.reason);
|
|
@@ -22971,6 +23010,7 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
22971
23010
|
total: auditEvents.length
|
|
22972
23011
|
} : undefined,
|
|
22973
23012
|
checkedAt: Date.now(),
|
|
23013
|
+
guardrails: summarizeGuardrails(traceEvents),
|
|
22974
23014
|
handoffs: traceEvents.filter((event) => event.type === "agent.handoff").map(toHandoff),
|
|
22975
23015
|
integrationEvents: integrationEvents ? {
|
|
22976
23016
|
delivered: countIntegrationDeliveryStatus(integrationEvents, "delivered"),
|
|
@@ -23031,6 +23071,9 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
|
|
|
23031
23071
|
`- Providers: ${record.providers.map((provider) => provider.provider).join(", ") || "none recorded"}`,
|
|
23032
23072
|
`- Open tasks: ${openTasks.join("; ") || "none"}`,
|
|
23033
23073
|
`- Top errors: ${topErrors.join("; ") || "none"}`,
|
|
23074
|
+
`- Guardrails: ${String(record.guardrails.blocked)} blocked / ${String(record.guardrails.warned)} warned / ${String(record.guardrails.total)} decisions`,
|
|
23075
|
+
"",
|
|
23076
|
+
renderVoiceOperationsRecordGuardrailMarkdown(record),
|
|
23034
23077
|
"",
|
|
23035
23078
|
"## Next checks",
|
|
23036
23079
|
"- Review provider decisions and fallback status.",
|
|
@@ -23039,6 +23082,25 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
|
|
|
23039
23082
|
].join(`
|
|
23040
23083
|
`);
|
|
23041
23084
|
};
|
|
23085
|
+
var renderVoiceOperationsRecordGuardrailMarkdown = (record) => {
|
|
23086
|
+
if (record.guardrails.total === 0) {
|
|
23087
|
+
return [
|
|
23088
|
+
"## Guardrail evidence",
|
|
23089
|
+
"",
|
|
23090
|
+
"- No assistant.guardrail events were recorded for this session."
|
|
23091
|
+
].join(`
|
|
23092
|
+
`);
|
|
23093
|
+
}
|
|
23094
|
+
return [
|
|
23095
|
+
"## Guardrail evidence",
|
|
23096
|
+
"",
|
|
23097
|
+
...record.guardrails.decisions.map((decision) => {
|
|
23098
|
+
const findings = decision.findings.map((finding) => [finding.action, finding.ruleId, finding.label].filter((value) => typeof value === "string").join(":")).filter(Boolean).join(", ");
|
|
23099
|
+
return `- assistant.guardrail ${decision.stage ?? "unknown"}: ${decision.status ?? "unknown"}; allowed=${String(decision.allowed ?? "unknown")}; proof=${decision.proof ?? "runtime"}; findings=${findings || "none"}`;
|
|
23100
|
+
})
|
|
23101
|
+
].join(`
|
|
23102
|
+
`);
|
|
23103
|
+
};
|
|
23042
23104
|
var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
23043
23105
|
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${escapeHtml39(provider.provider)}</strong><span>${String(provider.eventCount)} events</span><span>${formatMs4(provider.averageElapsedMs)} avg</span><span>${String(provider.errorCount)} errors</span></article>`).join("") : '<p class="muted">No provider events recorded.</p>';
|
|
23044
23106
|
const transcript = record.transcript.length ? record.transcript.map((turn) => `<li><strong>${escapeHtml39(turn.id)}</strong>${turn.committedText ? `<p><span class="label">Caller</span>${escapeHtml39(turn.committedText)}</p>` : ""}${turn.assistantReplies.map((reply) => `<p><span class="label">Assistant</span>${escapeHtml39(reply)}</p>`).join("")}${turn.errors.map((error) => `<p class="error"><span class="label">Error</span>${escapeHtml39(error)}</p>`).join("")}</li>`).join("") : "<li>No transcript turns recorded.</li>";
|
|
@@ -23048,6 +23110,10 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
23048
23110
|
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml39(review.title)}</strong> <span>${escapeHtml39(review.summary.outcome ?? "")}</span><p>${escapeHtml39(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
|
|
23049
23111
|
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${escapeHtml39(task.title)}</strong> <span>${escapeHtml39(task.status)}</span><p>${escapeHtml39(task.recommendedAction)}</p></li>`).join("") : "<li>No ops tasks recorded.</li>";
|
|
23050
23112
|
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${escapeHtml39(event.type)}</strong> <span>${escapeHtml39(event.deliveryStatus ?? "local")}</span><p>${escapeHtml39(event.deliveryError ?? event.deliveredTo ?? "")}</p></li>`).join("") : "<li>No integration events recorded.</li>";
|
|
23113
|
+
const guardrails = record.guardrails.total ? record.guardrails.decisions.map((decision) => {
|
|
23114
|
+
const findings = decision.findings.map((finding) => finding.label ?? finding.ruleId ?? finding.action).filter((value) => typeof value === "string").join(", ") || "none";
|
|
23115
|
+
return `<li><strong>assistant.guardrail ${escapeHtml39(decision.stage ?? "unknown")}</strong> <span>${escapeHtml39(decision.status ?? "")}</span><p>Allowed: ${escapeHtml39(String(decision.allowed ?? "unknown"))} \xB7 Proof: ${escapeHtml39(decision.proof ?? "runtime")}${decision.turnId ? ` \xB7 Turn: ${escapeHtml39(decision.turnId)}` : ""}</p><p>${escapeHtml39(findings)}</p></li>`;
|
|
23116
|
+
}).join("") : "<li>No assistant.guardrail events recorded.</li>";
|
|
23051
23117
|
const snippet = escapeHtml39(`app.use(
|
|
23052
23118
|
createVoiceOperationsRecordRoutes({
|
|
23053
23119
|
audit: auditStore,
|
|
@@ -23064,7 +23130,7 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
23064
23130
|
);`);
|
|
23065
23131
|
const incidentMarkdown = escapeHtml39(renderVoiceOperationsRecordIncidentMarkdown(record));
|
|
23066
23132
|
const incidentLink = options.incidentHref ? `<a href="${escapeHtml39(options.incidentHref)}">Download incident.md</a>` : "";
|
|
23067
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted,.label{color:#a9b4bd}.label{display:block;font-size:.72rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.hero-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.hero-actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.two-column{display:grid;gap:18px;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr)}@media(max-width:860px){main{padding:20px}.two-column{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">Call log replacement</p><h1>${escapeHtml39(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml39(record.status)}">${escapeHtml39(record.status)}</p><div class="hero-actions"><a href="#transcript">Transcript</a><a href="#provider-decisions">Provider decisions</a><a href="#incident-handoff">Incident handoff</a>${incidentLink}</div><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs4(record.summary.callDurationMs)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="two-column"><div><h2 id="transcript">Transcript</h2><ul>${transcript}</ul></div><div><h2 id="provider-decisions">Provider Decisions</h2><ul>${providerDecisions}</ul></div></section><section id="incident-handoff"><h2>Copyable Incident Handoff</h2><p class="muted">Paste this into Slack, Linear, Zendesk, or an incident review. ${incidentLink}</p><pre><code>${incidentMarkdown}</code></pre></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, audit, latency, replay, reviews, tasks, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Provider Summary</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
|
|
23133
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted,.label{color:#a9b4bd}.label{display:block;font-size:.72rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.hero-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.hero-actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.two-column{display:grid;gap:18px;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr)}@media(max-width:860px){main{padding:20px}.two-column{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">Call log replacement</p><h1>${escapeHtml39(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml39(record.status)}">${escapeHtml39(record.status)}</p><div class="hero-actions"><a href="#transcript">Transcript</a><a href="#provider-decisions">Provider decisions</a><a href="#guardrails">Guardrails</a><a href="#incident-handoff">Incident handoff</a>${incidentLink}</div><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs4(record.summary.callDurationMs)}</strong></div><div class="card"><span>Guardrails</span><strong>${String(record.guardrails.blocked)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="two-column"><div><h2 id="transcript">Transcript</h2><ul>${transcript}</ul></div><div><h2 id="provider-decisions">Provider Decisions</h2><ul>${providerDecisions}</ul></div></section><section id="guardrails"><h2>Guardrail Evidence</h2><p class="muted">Live <code>assistant.guardrail</code> decisions attached to this session.</p><ul>${guardrails}</ul></section><section id="incident-handoff"><h2>Copyable Incident Handoff</h2><p class="muted">Paste this into Slack, Linear, Zendesk, or an incident review. ${incidentLink}</p><pre><code>${incidentMarkdown}</code></pre></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, guardrails, audit, latency, replay, reviews, tasks, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Provider Summary</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
|
|
23068
23134
|
};
|
|
23069
23135
|
var createVoiceOperationsRecordRoutes = (options) => {
|
|
23070
23136
|
const path = options.path ?? "/api/voice-operations/:sessionId";
|
|
@@ -26686,6 +26752,8 @@ var renderIncidentMarkdown = (input) => {
|
|
|
26686
26752
|
"",
|
|
26687
26753
|
...input.record.tools.length ? input.record.tools.map((tool) => `- ${tool.toolName ?? "tool"} ${tool.status ?? ""} ${tool.elapsedMs === undefined ? "" : `${tool.elapsedMs}ms`} ${tool.error ?? ""}`.trim()) : ["- none"],
|
|
26688
26754
|
"",
|
|
26755
|
+
renderVoiceOperationsRecordGuardrailMarkdown(input.record),
|
|
26756
|
+
"",
|
|
26689
26757
|
"## Trace Evidence",
|
|
26690
26758
|
"",
|
|
26691
26759
|
input.traceMarkdown,
|
|
@@ -29771,6 +29839,7 @@ export {
|
|
|
29771
29839
|
renderVoiceOpsActionHistoryHTML,
|
|
29772
29840
|
renderVoiceOperationsRecordIncidentMarkdown,
|
|
29773
29841
|
renderVoiceOperationsRecordHTML,
|
|
29842
|
+
renderVoiceOperationsRecordGuardrailMarkdown,
|
|
29774
29843
|
renderVoiceObservabilityExportReplayHTML,
|
|
29775
29844
|
renderVoiceObservabilityExportMarkdown,
|
|
29776
29845
|
renderVoiceLiveLatencyHTML,
|
|
@@ -81,9 +81,34 @@ export type VoiceOperationsRecordIntegrationEventSummary = {
|
|
|
81
81
|
skipped: number;
|
|
82
82
|
total: number;
|
|
83
83
|
};
|
|
84
|
+
export type VoiceOperationsRecordGuardrailFinding = {
|
|
85
|
+
action?: string;
|
|
86
|
+
label?: string;
|
|
87
|
+
ruleId?: string;
|
|
88
|
+
};
|
|
89
|
+
export type VoiceOperationsRecordGuardrailDecision = {
|
|
90
|
+
allowed?: boolean;
|
|
91
|
+
at: number;
|
|
92
|
+
findings: VoiceOperationsRecordGuardrailFinding[];
|
|
93
|
+
metadata?: Record<string, unknown>;
|
|
94
|
+
proof?: string;
|
|
95
|
+
stage?: string;
|
|
96
|
+
status?: string;
|
|
97
|
+
toolName?: string;
|
|
98
|
+
turnId?: string;
|
|
99
|
+
};
|
|
100
|
+
export type VoiceOperationsRecordGuardrailSummary = {
|
|
101
|
+
blocked: number;
|
|
102
|
+
decisions: VoiceOperationsRecordGuardrailDecision[];
|
|
103
|
+
passed: number;
|
|
104
|
+
stages: string[];
|
|
105
|
+
total: number;
|
|
106
|
+
warned: number;
|
|
107
|
+
};
|
|
84
108
|
export type VoiceOperationsRecord = {
|
|
85
109
|
audit?: VoiceOperationsRecordAuditSummary;
|
|
86
110
|
checkedAt: number;
|
|
111
|
+
guardrails: VoiceOperationsRecordGuardrailSummary;
|
|
87
112
|
handoffs: VoiceOperationsRecordAgentHandoff[];
|
|
88
113
|
integrationEvents?: VoiceOperationsRecordIntegrationEventSummary;
|
|
89
114
|
outcome: VoiceOperationsRecordOutcome;
|
|
@@ -124,6 +149,7 @@ export type VoiceOperationsRecordRoutesOptions = Omit<VoiceOperationsRecordOptio
|
|
|
124
149
|
};
|
|
125
150
|
export declare const buildVoiceOperationsRecord: (options: VoiceOperationsRecordOptions) => Promise<VoiceOperationsRecord>;
|
|
126
151
|
export declare const renderVoiceOperationsRecordIncidentMarkdown: (record: VoiceOperationsRecord) => string;
|
|
152
|
+
export declare const renderVoiceOperationsRecordGuardrailMarkdown: (record: VoiceOperationsRecord) => string;
|
|
127
153
|
export declare const renderVoiceOperationsRecordHTML: (record: VoiceOperationsRecord, options?: {
|
|
128
154
|
incidentHref?: string;
|
|
129
155
|
title?: string;
|