@absolutejs/voice 0.0.22-beta.247 → 0.0.22-beta.249
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 +12 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +153 -1
- package/dist/operationsRecord.d.ts +52 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2792,7 +2792,18 @@ app.use(
|
|
|
2792
2792
|
);
|
|
2793
2793
|
```
|
|
2794
2794
|
|
|
2795
|
-
`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.
|
|
2796
|
+
|
|
2797
|
+
Use `evaluateVoiceOperationsRecordGuardrails(...)` when a proof pack or deploy gate needs JSON evidence that guardrails actually ran, blocked the expected stages, and produced named proofs/rule IDs. Use `assertVoiceOperationsRecordGuardrails(...)` in tests or smoke scripts when missing guardrail evidence should fail fast:
|
|
2798
|
+
|
|
2799
|
+
```ts
|
|
2800
|
+
const report = assertVoiceOperationsRecordGuardrails(record, {
|
|
2801
|
+
minBlocked: 1,
|
|
2802
|
+
proofs: ['live-guardrails-runtime'],
|
|
2803
|
+
ruleIds: ['support.no-medical-advice'],
|
|
2804
|
+
stages: ['assistant-output', 'tool-input']
|
|
2805
|
+
});
|
|
2806
|
+
```
|
|
2796
2807
|
|
|
2797
2808
|
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.
|
|
2798
2809
|
|
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 { assertVoiceOperationsRecordGuardrails, buildVoiceOperationsRecord, createVoiceOperationsRecordRoutes, evaluateVoiceOperationsRecordGuardrails, 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, VoiceOperationsRecordGuardrailAssertionInput, VoiceOperationsRecordGuardrailAssertionReport, 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) => {
|
|
@@ -22879,6 +22880,15 @@ var hasPayloadValue = (payload, key, values) => {
|
|
|
22879
22880
|
return typeof value === "string" && values.has(value);
|
|
22880
22881
|
};
|
|
22881
22882
|
var countIntegrationDeliveryStatus = (events, status) => events.filter((event) => event.deliveryStatus === status).length;
|
|
22883
|
+
var uniqueSorted = (values) => [
|
|
22884
|
+
...new Set(values.filter((value) => typeof value === "string"))
|
|
22885
|
+
].sort();
|
|
22886
|
+
var pushMissingValuesIssue = (input) => {
|
|
22887
|
+
const missing = (input.expected ?? []).filter((value) => !input.actual.includes(value));
|
|
22888
|
+
if (missing.length > 0) {
|
|
22889
|
+
input.issues.push(`Missing guardrail ${input.label}: ${missing.join(", ")}`);
|
|
22890
|
+
}
|
|
22891
|
+
};
|
|
22882
22892
|
var resolveRoutePath = (path, sessionId) => path.replace(":sessionId", encodeURIComponent(sessionId));
|
|
22883
22893
|
var toHandoff = (event) => ({
|
|
22884
22894
|
at: event.at,
|
|
@@ -22899,6 +22909,44 @@ var toTool = (event) => ({
|
|
|
22899
22909
|
toolName: getString16(event.payload.toolName),
|
|
22900
22910
|
turnId: event.turnId
|
|
22901
22911
|
});
|
|
22912
|
+
var toGuardrailFinding = (value) => {
|
|
22913
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
22914
|
+
return;
|
|
22915
|
+
}
|
|
22916
|
+
const record = value;
|
|
22917
|
+
return {
|
|
22918
|
+
action: getString16(record.action),
|
|
22919
|
+
label: getString16(record.label),
|
|
22920
|
+
ruleId: getString16(record.ruleId)
|
|
22921
|
+
};
|
|
22922
|
+
};
|
|
22923
|
+
var toGuardrailDecision = (event) => ({
|
|
22924
|
+
allowed: getBoolean3(event.payload.allowed),
|
|
22925
|
+
at: event.at,
|
|
22926
|
+
findings: Array.isArray(event.payload.findings) ? event.payload.findings.map(toGuardrailFinding).filter((finding) => finding !== undefined) : [],
|
|
22927
|
+
metadata: event.metadata && typeof event.metadata === "object" && !Array.isArray(event.metadata) ? event.metadata : undefined,
|
|
22928
|
+
proof: getString16(event.metadata?.proof),
|
|
22929
|
+
stage: getString16(event.payload.stage),
|
|
22930
|
+
status: getString16(event.payload.status),
|
|
22931
|
+
toolName: getString16(event.payload.toolName),
|
|
22932
|
+
turnId: event.turnId
|
|
22933
|
+
});
|
|
22934
|
+
var summarizeGuardrails = (events) => {
|
|
22935
|
+
const decisions = events.filter((event) => event.type === "assistant.guardrail").map(toGuardrailDecision);
|
|
22936
|
+
const isBlocked = (decision) => decision.allowed === false || decision.status === "fail";
|
|
22937
|
+
const isWarned = (decision) => decision.status === "warn" || decision.findings.some((finding) => finding.action === "warn");
|
|
22938
|
+
const stages = [
|
|
22939
|
+
...new Set(decisions.map((decision) => decision.stage).filter((stage) => typeof stage === "string"))
|
|
22940
|
+
].sort();
|
|
22941
|
+
return {
|
|
22942
|
+
blocked: decisions.filter(isBlocked).length,
|
|
22943
|
+
decisions,
|
|
22944
|
+
passed: decisions.filter((decision) => !isBlocked(decision) && !isWarned(decision)).length,
|
|
22945
|
+
stages,
|
|
22946
|
+
total: decisions.length,
|
|
22947
|
+
warned: decisions.filter(isWarned).length
|
|
22948
|
+
};
|
|
22949
|
+
};
|
|
22902
22950
|
var toProviderDecision = (event) => {
|
|
22903
22951
|
const provider = getString16(event.payload.provider) ?? getString16(event.payload.selectedProvider) ?? getString16(event.payload.fallbackProvider) ?? getString16(event.payload.variantId);
|
|
22904
22952
|
const status = getString16(event.payload.providerStatus) ?? getString16(event.payload.status) ?? getString16(event.payload.reason);
|
|
@@ -22971,6 +23019,7 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
22971
23019
|
total: auditEvents.length
|
|
22972
23020
|
} : undefined,
|
|
22973
23021
|
checkedAt: Date.now(),
|
|
23022
|
+
guardrails: summarizeGuardrails(traceEvents),
|
|
22974
23023
|
handoffs: traceEvents.filter((event) => event.type === "agent.handoff").map(toHandoff),
|
|
22975
23024
|
integrationEvents: integrationEvents ? {
|
|
22976
23025
|
delivered: countIntegrationDeliveryStatus(integrationEvents, "delivered"),
|
|
@@ -23007,6 +23056,78 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
23007
23056
|
transcript: buildTranscript(replay)
|
|
23008
23057
|
};
|
|
23009
23058
|
};
|
|
23059
|
+
var evaluateVoiceOperationsRecordGuardrails = (record, input = {}) => {
|
|
23060
|
+
const issues = [];
|
|
23061
|
+
const decisions = record.guardrails.decisions;
|
|
23062
|
+
const proofs = uniqueSorted(decisions.map((decision) => decision.proof));
|
|
23063
|
+
const ruleIds = uniqueSorted(decisions.flatMap((decision) => decision.findings.map((finding) => finding.ruleId)));
|
|
23064
|
+
const stages = uniqueSorted(decisions.map((decision) => decision.stage));
|
|
23065
|
+
const statuses = uniqueSorted(decisions.map((decision) => decision.status));
|
|
23066
|
+
const toolNames = uniqueSorted(decisions.map((decision) => decision.toolName));
|
|
23067
|
+
const minDecisions = input.minDecisions ?? 1;
|
|
23068
|
+
if (record.guardrails.total < minDecisions) {
|
|
23069
|
+
issues.push(`Expected at least ${String(minDecisions)} guardrail decisions, found ${String(record.guardrails.total)}.`);
|
|
23070
|
+
}
|
|
23071
|
+
if (input.minBlocked !== undefined && record.guardrails.blocked < input.minBlocked) {
|
|
23072
|
+
issues.push(`Expected at least ${String(input.minBlocked)} blocked guardrail decisions, found ${String(record.guardrails.blocked)}.`);
|
|
23073
|
+
}
|
|
23074
|
+
if (input.minWarned !== undefined && record.guardrails.warned < input.minWarned) {
|
|
23075
|
+
issues.push(`Expected at least ${String(input.minWarned)} warned guardrail decisions, found ${String(record.guardrails.warned)}.`);
|
|
23076
|
+
}
|
|
23077
|
+
if (input.minPassed !== undefined && record.guardrails.passed < input.minPassed) {
|
|
23078
|
+
issues.push(`Expected at least ${String(input.minPassed)} passed guardrail decisions, found ${String(record.guardrails.passed)}.`);
|
|
23079
|
+
}
|
|
23080
|
+
pushMissingValuesIssue({
|
|
23081
|
+
actual: proofs,
|
|
23082
|
+
expected: input.proofs,
|
|
23083
|
+
issues,
|
|
23084
|
+
label: "proofs"
|
|
23085
|
+
});
|
|
23086
|
+
pushMissingValuesIssue({
|
|
23087
|
+
actual: ruleIds,
|
|
23088
|
+
expected: input.ruleIds,
|
|
23089
|
+
issues,
|
|
23090
|
+
label: "rule IDs"
|
|
23091
|
+
});
|
|
23092
|
+
pushMissingValuesIssue({
|
|
23093
|
+
actual: stages,
|
|
23094
|
+
expected: input.stages,
|
|
23095
|
+
issues,
|
|
23096
|
+
label: "stages"
|
|
23097
|
+
});
|
|
23098
|
+
pushMissingValuesIssue({
|
|
23099
|
+
actual: statuses,
|
|
23100
|
+
expected: input.statuses,
|
|
23101
|
+
issues,
|
|
23102
|
+
label: "statuses"
|
|
23103
|
+
});
|
|
23104
|
+
pushMissingValuesIssue({
|
|
23105
|
+
actual: toolNames,
|
|
23106
|
+
expected: input.toolNames,
|
|
23107
|
+
issues,
|
|
23108
|
+
label: "tool names"
|
|
23109
|
+
});
|
|
23110
|
+
return {
|
|
23111
|
+
blocked: record.guardrails.blocked,
|
|
23112
|
+
decisions: record.guardrails.total,
|
|
23113
|
+
issues,
|
|
23114
|
+
ok: issues.length === 0,
|
|
23115
|
+
passed: record.guardrails.passed,
|
|
23116
|
+
proofs,
|
|
23117
|
+
ruleIds,
|
|
23118
|
+
stages,
|
|
23119
|
+
statuses,
|
|
23120
|
+
toolNames,
|
|
23121
|
+
warned: record.guardrails.warned
|
|
23122
|
+
};
|
|
23123
|
+
};
|
|
23124
|
+
var assertVoiceOperationsRecordGuardrails = (record, input = {}) => {
|
|
23125
|
+
const report = evaluateVoiceOperationsRecordGuardrails(record, input);
|
|
23126
|
+
if (!report.ok) {
|
|
23127
|
+
throw new Error(`Voice operations record guardrail assertion failed for ${record.sessionId}: ${report.issues.join(" ")}`);
|
|
23128
|
+
}
|
|
23129
|
+
return report;
|
|
23130
|
+
};
|
|
23010
23131
|
var escapeHtml39 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23011
23132
|
var formatMs4 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
23012
23133
|
var outcomeLabels = (outcome) => [
|
|
@@ -23031,6 +23152,9 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
|
|
|
23031
23152
|
`- Providers: ${record.providers.map((provider) => provider.provider).join(", ") || "none recorded"}`,
|
|
23032
23153
|
`- Open tasks: ${openTasks.join("; ") || "none"}`,
|
|
23033
23154
|
`- Top errors: ${topErrors.join("; ") || "none"}`,
|
|
23155
|
+
`- Guardrails: ${String(record.guardrails.blocked)} blocked / ${String(record.guardrails.warned)} warned / ${String(record.guardrails.total)} decisions`,
|
|
23156
|
+
"",
|
|
23157
|
+
renderVoiceOperationsRecordGuardrailMarkdown(record),
|
|
23034
23158
|
"",
|
|
23035
23159
|
"## Next checks",
|
|
23036
23160
|
"- Review provider decisions and fallback status.",
|
|
@@ -23039,6 +23163,25 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
|
|
|
23039
23163
|
].join(`
|
|
23040
23164
|
`);
|
|
23041
23165
|
};
|
|
23166
|
+
var renderVoiceOperationsRecordGuardrailMarkdown = (record) => {
|
|
23167
|
+
if (record.guardrails.total === 0) {
|
|
23168
|
+
return [
|
|
23169
|
+
"## Guardrail evidence",
|
|
23170
|
+
"",
|
|
23171
|
+
"- No assistant.guardrail events were recorded for this session."
|
|
23172
|
+
].join(`
|
|
23173
|
+
`);
|
|
23174
|
+
}
|
|
23175
|
+
return [
|
|
23176
|
+
"## Guardrail evidence",
|
|
23177
|
+
"",
|
|
23178
|
+
...record.guardrails.decisions.map((decision) => {
|
|
23179
|
+
const findings = decision.findings.map((finding) => [finding.action, finding.ruleId, finding.label].filter((value) => typeof value === "string").join(":")).filter(Boolean).join(", ");
|
|
23180
|
+
return `- assistant.guardrail ${decision.stage ?? "unknown"}: ${decision.status ?? "unknown"}; allowed=${String(decision.allowed ?? "unknown")}; proof=${decision.proof ?? "runtime"}; findings=${findings || "none"}`;
|
|
23181
|
+
})
|
|
23182
|
+
].join(`
|
|
23183
|
+
`);
|
|
23184
|
+
};
|
|
23042
23185
|
var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
23043
23186
|
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
23187
|
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 +23191,10 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
23048
23191
|
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
23192
|
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
23193
|
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>";
|
|
23194
|
+
const guardrails = record.guardrails.total ? record.guardrails.decisions.map((decision) => {
|
|
23195
|
+
const findings = decision.findings.map((finding) => finding.label ?? finding.ruleId ?? finding.action).filter((value) => typeof value === "string").join(", ") || "none";
|
|
23196
|
+
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>`;
|
|
23197
|
+
}).join("") : "<li>No assistant.guardrail events recorded.</li>";
|
|
23051
23198
|
const snippet = escapeHtml39(`app.use(
|
|
23052
23199
|
createVoiceOperationsRecordRoutes({
|
|
23053
23200
|
audit: auditStore,
|
|
@@ -23064,7 +23211,7 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
23064
23211
|
);`);
|
|
23065
23212
|
const incidentMarkdown = escapeHtml39(renderVoiceOperationsRecordIncidentMarkdown(record));
|
|
23066
23213
|
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>`;
|
|
23214
|
+
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
23215
|
};
|
|
23069
23216
|
var createVoiceOperationsRecordRoutes = (options) => {
|
|
23070
23217
|
const path = options.path ?? "/api/voice-operations/:sessionId";
|
|
@@ -26686,6 +26833,8 @@ var renderIncidentMarkdown = (input) => {
|
|
|
26686
26833
|
"",
|
|
26687
26834
|
...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
26835
|
"",
|
|
26836
|
+
renderVoiceOperationsRecordGuardrailMarkdown(input.record),
|
|
26837
|
+
"",
|
|
26689
26838
|
"## Trace Evidence",
|
|
26690
26839
|
"",
|
|
26691
26840
|
input.traceMarkdown,
|
|
@@ -29771,6 +29920,7 @@ export {
|
|
|
29771
29920
|
renderVoiceOpsActionHistoryHTML,
|
|
29772
29921
|
renderVoiceOperationsRecordIncidentMarkdown,
|
|
29773
29922
|
renderVoiceOperationsRecordHTML,
|
|
29923
|
+
renderVoiceOperationsRecordGuardrailMarkdown,
|
|
29774
29924
|
renderVoiceObservabilityExportReplayHTML,
|
|
29775
29925
|
renderVoiceObservabilityExportMarkdown,
|
|
29776
29926
|
renderVoiceLiveLatencyHTML,
|
|
@@ -29836,6 +29986,7 @@ export {
|
|
|
29836
29986
|
evaluateVoiceTelephonyContract,
|
|
29837
29987
|
evaluateVoiceQuality,
|
|
29838
29988
|
evaluateVoiceProviderStackGaps,
|
|
29989
|
+
evaluateVoiceOperationsRecordGuardrails,
|
|
29839
29990
|
evaluateVoiceGuardrailPolicy,
|
|
29840
29991
|
encodeTwilioMulawBase64,
|
|
29841
29992
|
deliverVoiceTraceEventsToSinks,
|
|
@@ -30149,6 +30300,7 @@ export {
|
|
|
30149
30300
|
buildEmptyVoiceProofTrendReport,
|
|
30150
30301
|
assignVoiceOpsTask,
|
|
30151
30302
|
assertVoiceProviderRoutingContract,
|
|
30303
|
+
assertVoiceOperationsRecordGuardrails,
|
|
30152
30304
|
assertVoiceObservabilityExportSchema,
|
|
30153
30305
|
assertVoiceObservabilityExportRecord,
|
|
30154
30306
|
assertVoiceLatencySLOGate,
|
|
@@ -81,9 +81,58 @@ 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
|
+
};
|
|
108
|
+
export type VoiceOperationsRecordGuardrailAssertionInput = {
|
|
109
|
+
minBlocked?: number;
|
|
110
|
+
minDecisions?: number;
|
|
111
|
+
minPassed?: number;
|
|
112
|
+
minWarned?: number;
|
|
113
|
+
proofs?: string[];
|
|
114
|
+
ruleIds?: string[];
|
|
115
|
+
stages?: string[];
|
|
116
|
+
statuses?: string[];
|
|
117
|
+
toolNames?: string[];
|
|
118
|
+
};
|
|
119
|
+
export type VoiceOperationsRecordGuardrailAssertionReport = {
|
|
120
|
+
blocked: number;
|
|
121
|
+
decisions: number;
|
|
122
|
+
issues: string[];
|
|
123
|
+
ok: boolean;
|
|
124
|
+
passed: number;
|
|
125
|
+
proofs: string[];
|
|
126
|
+
ruleIds: string[];
|
|
127
|
+
stages: string[];
|
|
128
|
+
statuses: string[];
|
|
129
|
+
toolNames: string[];
|
|
130
|
+
warned: number;
|
|
131
|
+
};
|
|
84
132
|
export type VoiceOperationsRecord = {
|
|
85
133
|
audit?: VoiceOperationsRecordAuditSummary;
|
|
86
134
|
checkedAt: number;
|
|
135
|
+
guardrails: VoiceOperationsRecordGuardrailSummary;
|
|
87
136
|
handoffs: VoiceOperationsRecordAgentHandoff[];
|
|
88
137
|
integrationEvents?: VoiceOperationsRecordIntegrationEventSummary;
|
|
89
138
|
outcome: VoiceOperationsRecordOutcome;
|
|
@@ -123,7 +172,10 @@ export type VoiceOperationsRecordRoutesOptions = Omit<VoiceOperationsRecordOptio
|
|
|
123
172
|
title?: string;
|
|
124
173
|
};
|
|
125
174
|
export declare const buildVoiceOperationsRecord: (options: VoiceOperationsRecordOptions) => Promise<VoiceOperationsRecord>;
|
|
175
|
+
export declare const evaluateVoiceOperationsRecordGuardrails: (record: VoiceOperationsRecord, input?: VoiceOperationsRecordGuardrailAssertionInput) => VoiceOperationsRecordGuardrailAssertionReport;
|
|
176
|
+
export declare const assertVoiceOperationsRecordGuardrails: (record: VoiceOperationsRecord, input?: VoiceOperationsRecordGuardrailAssertionInput) => VoiceOperationsRecordGuardrailAssertionReport;
|
|
126
177
|
export declare const renderVoiceOperationsRecordIncidentMarkdown: (record: VoiceOperationsRecord) => string;
|
|
178
|
+
export declare const renderVoiceOperationsRecordGuardrailMarkdown: (record: VoiceOperationsRecord) => string;
|
|
127
179
|
export declare const renderVoiceOperationsRecordHTML: (record: VoiceOperationsRecord, options?: {
|
|
128
180
|
incidentHref?: string;
|
|
129
181
|
title?: string;
|