@absolutejs/voice 0.0.22-beta.254 → 0.0.22-beta.256

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.
@@ -65,5 +65,34 @@ export type VoiceAgentSquadContractRunOptions<TContext = unknown, TSession exten
65
65
  squad: VoiceAgent<TContext, TSession, TResult>;
66
66
  trace?: VoiceTraceEventStore;
67
67
  };
68
+ export type VoiceAgentSquadContractAssertionInput = {
69
+ maxBlockedHandoffs?: number;
70
+ maxFailed?: number;
71
+ maxIssues?: number;
72
+ minContracts?: number;
73
+ minHandoffs?: number;
74
+ requiredContractIds?: string[];
75
+ requiredFinalAgentIds?: string[];
76
+ requiredHandoffStatuses?: VoiceAgentSquadHandoffStatus[];
77
+ requiredHandoffTargets?: string[];
78
+ requiredScenarioIds?: string[];
79
+ };
80
+ export type VoiceAgentSquadContractAssertionReport = {
81
+ blockedHandoffs: number;
82
+ contractIds: string[];
83
+ failed: number;
84
+ finalAgentIds: string[];
85
+ handoffStatuses: VoiceAgentSquadHandoffStatus[];
86
+ handoffTargets: string[];
87
+ handoffs: number;
88
+ issues: string[];
89
+ issueCount: number;
90
+ ok: boolean;
91
+ passed: number;
92
+ scenarioIds: string[];
93
+ total: number;
94
+ };
68
95
  export declare const runVoiceAgentSquadContract: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoiceAgentSquadContractRunOptions<TContext, TSession, TResult>) => Promise<VoiceAgentSquadContractReport<TResult>>;
69
96
  export declare const assertVoiceAgentSquadContract: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoiceAgentSquadContractRunOptions<TContext, TSession, TResult>) => Promise<VoiceAgentSquadContractReport<TResult>>;
97
+ export declare const evaluateVoiceAgentSquadContractEvidence: (reports: readonly VoiceAgentSquadContractReport[], input?: VoiceAgentSquadContractAssertionInput) => VoiceAgentSquadContractAssertionReport;
98
+ export declare const assertVoiceAgentSquadContractEvidence: (reports: readonly VoiceAgentSquadContractReport[], input?: VoiceAgentSquadContractAssertionInput) => VoiceAgentSquadContractAssertionReport;
package/dist/index.d.ts CHANGED
@@ -32,14 +32,14 @@ export { createVoiceSimulationSuiteRoutes, renderVoiceSimulationSuiteHTML, runVo
32
32
  export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, createVoiceWorkflowContractPreset, createVoiceWorkflowScenario, recordVoiceWorkflowContractTrace, validateVoiceWorkflowRouteResult } from './workflowContract';
33
33
  export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, createVoiceSessionsHTMLHandler, createVoiceSessionsJSONHandler, renderVoiceSessionsHTML, summarizeVoiceProviderFallbackRecovery, summarizeVoiceSessions, summarizeVoiceSessionReplay } from './sessionReplay';
34
34
  export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
35
- export { assertVoiceAgentSquadContract, runVoiceAgentSquadContract } from './agentSquadContract';
35
+ export { assertVoiceAgentSquadContractEvidence, assertVoiceAgentSquadContract, evaluateVoiceAgentSquadContractEvidence, runVoiceAgentSquadContract } from './agentSquadContract';
36
36
  export { createVoiceToolIdempotencyKey, createVoiceToolRuntime } from './toolRuntime';
37
- export { createVoiceToolContract, createVoiceToolContractHTMLHandler, createVoiceToolContractJSONHandler, createVoiceToolContractRoutes, createVoiceToolRuntimeContractDefaults, renderVoiceToolContractHTML, runVoiceToolContractSuite, runVoiceToolContract } from './toolContract';
37
+ export { assertVoiceToolContractEvidence, createVoiceToolContract, createVoiceToolContractHTMLHandler, createVoiceToolContractJSONHandler, createVoiceToolContractRoutes, createVoiceToolRuntimeContractDefaults, evaluateVoiceToolContractEvidence, renderVoiceToolContractHTML, runVoiceToolContractSuite, runVoiceToolContract } from './toolContract';
38
38
  export { createVoiceTurnLatencyHTMLHandler, createVoiceTurnLatencyJSONHandler, createVoiceTurnLatencyRoutes, renderVoiceTurnLatencyHTML, summarizeVoiceTurnLatency } from './turnLatency';
39
39
  export { createVoiceLiveLatencyRoutes, renderVoiceLiveLatencyHTML, summarizeVoiceLiveLatency } from './liveLatency';
40
40
  export { assertVoiceLatencySLOGate, buildVoiceLatencySLOGate, renderVoiceLatencySLOMarkdown } from './latencySlo';
41
41
  export { createVoiceTurnQualityHTMLHandler, createVoiceTurnQualityJSONHandler, createVoiceTurnQualityRoutes, renderVoiceTurnQualityHTML, summarizeVoiceTurnQuality } from './turnQuality';
42
- export { createVoiceOutcomeContractHTMLHandler, createVoiceOutcomeContractJSONHandler, createVoiceOutcomeContractRoutes, renderVoiceOutcomeContractHTML, runVoiceOutcomeContractSuite } from './outcomeContract';
42
+ export { assertVoiceOutcomeContractEvidence, createVoiceOutcomeContractHTMLHandler, createVoiceOutcomeContractJSONHandler, createVoiceOutcomeContractRoutes, evaluateVoiceOutcomeContractEvidence, renderVoiceOutcomeContractHTML, runVoiceOutcomeContractSuite } from './outcomeContract';
43
43
  export { applyVoiceTelephonyOutcome, createMemoryVoiceTelephonyWebhookIdempotencyStore, createVoiceTelephonyOutcomePolicy, createVoiceTelephonyWebhookHandler, createVoiceTelephonyWebhookRoutes, parseVoiceTelephonyWebhookEvent, resolveVoiceTelephonyOutcome, signVoiceTwilioWebhook, verifyVoiceTwilioWebhookSignature, voiceTelephonyOutcomeToRouteResult } from './telephonyOutcome';
44
44
  export { createVoicePhoneAgent } from './phoneAgent';
45
45
  export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileIncidentBundleStore, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileAuditEventStore, createVoiceFileAuditSinkDeliveryStore, createVoiceFileCampaignStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
@@ -49,7 +49,7 @@ export { createOpenAIRealtimeAdapter } from './openaiRealtime';
49
49
  export { createOpenAIVoiceTTS } from './openaiTTS';
50
50
  export { createVoiceProviderHealthHTMLHandler, createVoiceProviderHealthJSONHandler, createVoiceProviderHealthRoutes, renderVoiceProviderHealthHTML, summarizeVoiceProviderHealth } from './providerHealth';
51
51
  export { createVoiceProviderCapabilityHTMLHandler, createVoiceProviderCapabilityJSONHandler, createVoiceProviderCapabilityRoutes, renderVoiceProviderCapabilityHTML, summarizeVoiceProviderCapabilities } from './providerCapabilities';
52
- export { assertVoiceProviderRoutingContract, runVoiceProviderRoutingContract } from './providerRoutingContract';
52
+ export { assertVoiceProviderRoutingContractEvidence, assertVoiceProviderRoutingContract, evaluateVoiceProviderRoutingContractEvidence, runVoiceProviderRoutingContract } from './providerRoutingContract';
53
53
  export { assertVoiceProviderSloEvidence, buildVoiceProviderSloReport, createVoiceProviderSloRoutes, evaluateVoiceProviderSloEvidence, renderVoiceProviderSloHTML, renderVoiceProviderSloMarkdown } from './providerSlo';
54
54
  export { createVoicePhoneAgentProductionSmokeHTMLHandler, createVoicePhoneAgentProductionSmokeJSONHandler, createVoicePhoneAgentProductionSmokeRoutes, renderVoicePhoneAgentProductionSmokeHTML, runVoicePhoneAgentProductionSmokeContract } from './phoneAgentProductionSmoke';
55
55
  export { assertVoiceProductionReadinessEvidence, buildVoiceProductionReadinessGate, buildVoiceProductionReadinessReport, createVoiceProductionReadinessRoutes, evaluateVoiceProductionReadinessEvidence, renderVoiceProductionReadinessHTML, summarizeVoiceProductionReadinessGate } from './productionReadiness';
@@ -108,13 +108,13 @@ export type { OpenAIVoiceTTSOptions, OpenAIVoiceTTSVoice } from './openaiTTS';
108
108
  export type { OpenAIRealtimeAdapterOptions, OpenAIRealtimeModel, OpenAIRealtimeNoiseReduction, OpenAIRealtimeResponseMode, OpenAIRealtimeTranscriptionModel, OpenAIRealtimeVoice } from './openaiRealtime';
109
109
  export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
110
110
  export type { VoiceProviderCapabilityDefinition, VoiceProviderCapabilityHandlerOptions, VoiceProviderCapabilityHTMLHandlerOptions, VoiceProviderCapabilityKind, VoiceProviderCapabilityOptions, VoiceProviderCapabilityReport, VoiceProviderCapabilityRoutesOptions, VoiceProviderCapabilitySummary } from './providerCapabilities';
111
- export type { VoiceProviderRoutingContractDefinition, VoiceProviderRoutingContractIssue, VoiceProviderRoutingContractReport, VoiceProviderRoutingContractRunOptions, VoiceProviderRoutingExpectation, VoiceProviderRoutingStatus } from './providerRoutingContract';
111
+ export type { VoiceProviderRoutingContractAssertionInput, VoiceProviderRoutingContractAssertionReport, VoiceProviderRoutingContractDefinition, VoiceProviderRoutingContractIssue, VoiceProviderRoutingContractReport, VoiceProviderRoutingContractRunOptions, VoiceProviderRoutingExpectation, VoiceProviderRoutingStatus } from './providerRoutingContract';
112
112
  export type { VoiceProviderSloAssertionInput, VoiceProviderSloAssertionReport, VoiceProviderSloIssue, VoiceProviderSloKindReport, VoiceProviderSloMetric, VoiceProviderSloReport, VoiceProviderSloReportOptions, VoiceProviderSloRoutesOptions, VoiceProviderSloSessionReport, VoiceProviderSloStatus, VoiceProviderSloThresholdConfig, VoiceProviderSloThresholds } from './providerSlo';
113
113
  export type { VoiceTurnLatencyHTMLHandlerOptions, VoiceTurnLatencyItem, VoiceTurnLatencyOptions, VoiceTurnLatencyReport, VoiceTurnLatencyRoutesOptions, VoiceTurnLatencyStage, VoiceTurnLatencyStatus } from './turnLatency';
114
114
  export type { VoiceLiveLatencyOptions, VoiceLiveLatencyReport, VoiceLiveLatencyRoutesOptions, VoiceLiveLatencySample, VoiceLiveLatencyStatus } from './liveLatency';
115
115
  export type { VoiceLatencySLOBudget, VoiceLatencySLOGateError, VoiceLatencySLOGateOptions, VoiceLatencySLOGateReport, VoiceLatencySLOMeasurement, VoiceLatencySLOStage, VoiceLatencySLOStageSummary, VoiceLatencySLOStatus } from './latencySlo';
116
116
  export type { VoiceTurnQualityHTMLHandlerOptions, VoiceTurnQualityItem, VoiceTurnQualityOptions, VoiceTurnQualityReport, VoiceTurnQualityRoutesOptions, VoiceTurnQualityStatus } from './turnQuality';
117
- export type { VoiceOutcomeContractDefinition, VoiceOutcomeContractHTMLHandlerOptions, VoiceOutcomeContractIssue, VoiceOutcomeContractOptions, VoiceOutcomeContractReport, VoiceOutcomeContractRoutesOptions, VoiceOutcomeContractStatus, VoiceOutcomeContractSuiteReport } from './outcomeContract';
117
+ export type { VoiceOutcomeContractAssertionInput, VoiceOutcomeContractAssertionReport, VoiceOutcomeContractDefinition, VoiceOutcomeContractHTMLHandlerOptions, VoiceOutcomeContractIssue, VoiceOutcomeContractOptions, VoiceOutcomeContractReport, VoiceOutcomeContractRoutesOptions, VoiceOutcomeContractStatus, VoiceOutcomeContractSuiteReport } from './outcomeContract';
118
118
  export type { VoiceTelephonyOutcomeAction, VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomePolicy, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyOutcomeRouteResult, VoiceTelephonyOutcomeStatusDecision, VoiceTelephonyWebhookDecision, VoiceTelephonyWebhookHandlerOptions, VoiceTelephonyWebhookIdempotencyStore, VoiceTelephonyWebhookParseInput, VoiceTelephonyWebhookProvider, VoiceTelephonyWebhookRoutesOptions, VoiceTelephonyWebhookVerificationResult, StoredVoiceTelephonyWebhookDecision } from './telephonyOutcome';
119
119
  export type { VoicePhoneAgentCarrier, VoicePhoneAgentCarrierSummary, VoicePhoneAgentLifecycleStage, VoicePhoneAgentPlivoCarrier, VoicePhoneAgentRoutes, VoicePhoneAgentRoutesOptions, VoicePhoneAgentSetupReport, VoicePhoneAgentTelnyxCarrier, VoicePhoneAgentTwilioCarrier } from './phoneAgent';
120
120
  export type { VoicePhoneAgentProductionSmokeIssue, VoicePhoneAgentProductionSmokeHandlerOptions, VoicePhoneAgentProductionSmokeHTMLHandlerOptions, VoicePhoneAgentProductionSmokeOptions, VoicePhoneAgentProductionSmokeReport, VoicePhoneAgentProductionSmokeRoutesOptions, VoicePhoneAgentProductionSmokeRequirement } from './phoneAgentProductionSmoke';
@@ -131,9 +131,9 @@ export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQua
131
131
  export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePageData, VoiceResilienceRoutesOptions, VoiceResilienceSimulationProvider, VoiceRoutingKindSummary, VoiceRoutingDecisionSummary, VoiceRoutingDecisionSummaryOptions, VoiceRoutingEvent, VoiceRoutingEventKind, VoiceRoutingSessionSummary, VoiceRoutingSessionSummaryOptions } from './resilienceRoutes';
132
132
  export type { VoiceIOProviderRouterEvent, VoiceIOProviderRouterOptions, VoiceIOProviderRouterPolicy, VoiceIOProviderRouterPolicyConfig, VoiceSTTProviderRouterOptions, VoiceTTSProviderRouterOptions } from './providerAdapters';
133
133
  export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadContextPolicyResult, VoiceAgentSquadHandoffPolicyResult, VoiceAgentSquadHandoffStatus, VoiceAgentSquadOptions, VoiceAgentSquadState, VoiceAgentSquadStateHandoff, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
134
- export type { VoiceAgentSquadContractDefinition, VoiceAgentSquadContractIssue, VoiceAgentSquadContractOutcome, VoiceAgentSquadContractReport, VoiceAgentSquadContractRunOptions, VoiceAgentSquadContractTurn, VoiceAgentSquadContractTurnReport, VoiceAgentSquadHandoffExpectation, VoiceAgentSquadTurnExpectation } from './agentSquadContract';
134
+ export type { VoiceAgentSquadContractAssertionInput, VoiceAgentSquadContractAssertionReport, VoiceAgentSquadContractDefinition, VoiceAgentSquadContractIssue, VoiceAgentSquadContractOutcome, VoiceAgentSquadContractReport, VoiceAgentSquadContractRunOptions, VoiceAgentSquadContractTurn, VoiceAgentSquadContractTurnReport, VoiceAgentSquadHandoffExpectation, VoiceAgentSquadTurnExpectation } from './agentSquadContract';
135
135
  export type { VoiceToolRetryDelay, VoiceToolRuntime, VoiceToolRuntimeExecuteInput, VoiceToolRuntimeOptions, VoiceToolRuntimeResult } from './toolRuntime';
136
- export type { VoiceToolContractCase, VoiceToolContractCaseReport, VoiceToolContractDefinition, VoiceToolContractExpectation, VoiceToolContractHandlerOptions, VoiceToolContractHTMLHandlerOptions, VoiceToolContractIssue, VoiceToolContractReport, VoiceToolContractRoutesOptions, VoiceToolContractSuiteReport } from './toolContract';
136
+ export type { VoiceToolContractAssertionInput, VoiceToolContractAssertionReport, VoiceToolContractCase, VoiceToolContractCaseReport, VoiceToolContractDefinition, VoiceToolContractExpectation, VoiceToolContractHandlerOptions, VoiceToolContractHTMLHandlerOptions, VoiceToolContractIssue, VoiceToolContractReport, VoiceToolContractRoutesOptions, VoiceToolContractSuiteReport } from './toolContract';
137
137
  export type { VoiceOpsRuntime, VoiceOpsRuntimeConfig, VoiceOpsRuntimeSummary, VoiceOpsRuntimeSinkWorkerConfig, VoiceOpsRuntimeTaskWorkerConfig, VoiceOpsRuntimeTickResult, VoiceOpsRuntimeWebhookWorkerConfig } from './opsRuntime';
138
138
  export type { VoiceOpsPresetName, VoiceOpsPresetOverrides, VoiceResolvedOpsPreset } from './opsPresets';
139
139
  export type { VoiceOutcomeRecipe, VoiceOutcomeRecipeName, VoiceOutcomeRecipeOptions } from './outcomeRecipes';
package/dist/index.js CHANGED
@@ -14476,6 +14476,89 @@ var runVoiceOutcomeContractSuite = async (options) => {
14476
14476
  total: contracts.length
14477
14477
  };
14478
14478
  };
14479
+ var evaluateVoiceOutcomeContractEvidence = (report, input = {}) => {
14480
+ const issues = [];
14481
+ const maxFailed = input.maxFailed ?? 0;
14482
+ const maxIssues = input.maxIssues ?? 0;
14483
+ const contractIds = [
14484
+ ...new Set(report.contracts.map((contract) => contract.contractId))
14485
+ ].sort();
14486
+ const issueCount = report.contracts.reduce((total, contract) => total + contract.issues.length, 0);
14487
+ const totals = report.contracts.reduce((result, contract) => ({
14488
+ handoffs: result.handoffs + contract.matched.handoffs,
14489
+ integrationEvents: result.integrationEvents + contract.matched.integrationEvents,
14490
+ operationsRecordHrefs: result.operationsRecordHrefs + contract.operationsRecordHrefs.length,
14491
+ reviews: result.reviews + contract.matched.reviews,
14492
+ sessions: result.sessions + contract.matched.sessions,
14493
+ tasks: result.tasks + contract.matched.tasks
14494
+ }), {
14495
+ handoffs: 0,
14496
+ integrationEvents: 0,
14497
+ operationsRecordHrefs: 0,
14498
+ reviews: 0,
14499
+ sessions: 0,
14500
+ tasks: 0
14501
+ });
14502
+ const contractsMissingOperationRecordHrefs = report.contracts.filter((contract) => contract.operationsRecordHrefs.length === 0).length;
14503
+ if (input.minContracts !== undefined && report.total < input.minContracts) {
14504
+ issues.push(`Expected at least ${String(input.minContracts)} outcome contract(s), found ${String(report.total)}.`);
14505
+ }
14506
+ if (report.failed > maxFailed) {
14507
+ issues.push(`Expected at most ${String(maxFailed)} failing outcome contract(s), found ${String(report.failed)}.`);
14508
+ }
14509
+ if (issueCount > maxIssues) {
14510
+ issues.push(`Expected at most ${String(maxIssues)} outcome contract issue(s), found ${String(issueCount)}.`);
14511
+ }
14512
+ if (input.minSessions !== undefined && totals.sessions < input.minSessions) {
14513
+ issues.push(`Expected at least ${String(input.minSessions)} matched outcome session(s), found ${String(totals.sessions)}.`);
14514
+ }
14515
+ if (input.minReviews !== undefined && totals.reviews < input.minReviews) {
14516
+ issues.push(`Expected at least ${String(input.minReviews)} matched outcome review(s), found ${String(totals.reviews)}.`);
14517
+ }
14518
+ if (input.minTasks !== undefined && totals.tasks < input.minTasks) {
14519
+ issues.push(`Expected at least ${String(input.minTasks)} matched outcome task(s), found ${String(totals.tasks)}.`);
14520
+ }
14521
+ if (input.minHandoffs !== undefined && totals.handoffs < input.minHandoffs) {
14522
+ issues.push(`Expected at least ${String(input.minHandoffs)} matched outcome handoff(s), found ${String(totals.handoffs)}.`);
14523
+ }
14524
+ if (input.minIntegrationEvents !== undefined && totals.integrationEvents < input.minIntegrationEvents) {
14525
+ issues.push(`Expected at least ${String(input.minIntegrationEvents)} matched outcome integration event(s), found ${String(totals.integrationEvents)}.`);
14526
+ }
14527
+ if (input.minOperationsRecordHrefs !== undefined && totals.operationsRecordHrefs < input.minOperationsRecordHrefs) {
14528
+ issues.push(`Expected at least ${String(input.minOperationsRecordHrefs)} outcome operations record href(s), found ${String(totals.operationsRecordHrefs)}.`);
14529
+ }
14530
+ if ((input.requireOperationRecordHrefs ?? false) && contractsMissingOperationRecordHrefs > 0) {
14531
+ issues.push(`Expected every outcome contract to include operations record hrefs; ${String(contractsMissingOperationRecordHrefs)} contract(s) missing.`);
14532
+ }
14533
+ for (const contractId of input.requiredContractIds ?? []) {
14534
+ if (!contractIds.includes(contractId)) {
14535
+ issues.push(`Missing outcome contract: ${contractId}.`);
14536
+ }
14537
+ }
14538
+ return {
14539
+ contractIds,
14540
+ failed: report.failed,
14541
+ handoffs: totals.handoffs,
14542
+ integrationEvents: totals.integrationEvents,
14543
+ issues,
14544
+ issueCount,
14545
+ ok: issues.length === 0,
14546
+ operationsRecordHrefs: totals.operationsRecordHrefs,
14547
+ passed: report.passed,
14548
+ reviews: totals.reviews,
14549
+ sessions: totals.sessions,
14550
+ status: report.status,
14551
+ tasks: totals.tasks,
14552
+ total: report.total
14553
+ };
14554
+ };
14555
+ var assertVoiceOutcomeContractEvidence = (report, input = {}) => {
14556
+ const assertion = evaluateVoiceOutcomeContractEvidence(report, input);
14557
+ if (!assertion.ok) {
14558
+ throw new Error(`Voice outcome contract evidence assertion failed: ${assertion.issues.join(" ")}`);
14559
+ }
14560
+ return assertion;
14561
+ };
14479
14562
  var renderVoiceOutcomeContractHTML = (report, options = {}) => {
14480
14563
  const title = options.title ?? "Voice Outcome Contracts";
14481
14564
  const contracts = report.contracts.map((contract) => {
@@ -14910,6 +14993,78 @@ var runVoiceToolContractSuite = async (options) => {
14910
14993
  total: contracts.length
14911
14994
  };
14912
14995
  };
14996
+ var evaluateVoiceToolContractEvidence = (report, input = {}) => {
14997
+ const issues = [];
14998
+ const maxFailed = input.maxFailed ?? 0;
14999
+ const maxIssues = input.maxIssues ?? 0;
15000
+ const maxTimedOut = input.maxTimedOut ?? Infinity;
15001
+ const cases = report.contracts.flatMap((contract) => contract.cases);
15002
+ const contractIds = [
15003
+ ...new Set(report.contracts.map((contract) => contract.contractId))
15004
+ ].sort();
15005
+ const toolNames = [
15006
+ ...new Set(report.contracts.map((contract) => contract.toolName))
15007
+ ].sort();
15008
+ const caseStatuses = [...new Set(cases.map((testCase) => testCase.status))].sort();
15009
+ const issueCount = report.contracts.reduce((total, contract) => total + contract.issues.length, 0);
15010
+ const timedOut = cases.filter((testCase) => testCase.timedOut).length;
15011
+ const missingOperationsRecordHrefs = cases.filter((testCase) => !testCase.operationsRecordHref).length;
15012
+ if (input.minContracts !== undefined && report.total < input.minContracts) {
15013
+ issues.push(`Expected at least ${String(input.minContracts)} tool contract(s), found ${String(report.total)}.`);
15014
+ }
15015
+ if (input.minCases !== undefined && cases.length < input.minCases) {
15016
+ issues.push(`Expected at least ${String(input.minCases)} tool contract case(s), found ${String(cases.length)}.`);
15017
+ }
15018
+ if (report.failed > maxFailed) {
15019
+ issues.push(`Expected at most ${String(maxFailed)} failing tool contract(s), found ${String(report.failed)}.`);
15020
+ }
15021
+ if (issueCount > maxIssues) {
15022
+ issues.push(`Expected at most ${String(maxIssues)} tool contract issue(s), found ${String(issueCount)}.`);
15023
+ }
15024
+ if (timedOut > maxTimedOut) {
15025
+ issues.push(`Expected at most ${String(maxTimedOut)} timed out tool contract case(s), found ${String(timedOut)}.`);
15026
+ }
15027
+ if ((input.requireOperationRecordHrefs ?? false) && missingOperationsRecordHrefs > 0) {
15028
+ issues.push(`Expected every tool contract case to include an operations record href; ${String(missingOperationsRecordHrefs)} missing.`);
15029
+ }
15030
+ for (const contractId of input.requiredContractIds ?? []) {
15031
+ if (!contractIds.includes(contractId)) {
15032
+ issues.push(`Missing tool contract: ${contractId}.`);
15033
+ }
15034
+ }
15035
+ for (const toolName of input.requiredToolNames ?? []) {
15036
+ if (!toolNames.includes(toolName)) {
15037
+ issues.push(`Missing tool contract tool: ${toolName}.`);
15038
+ }
15039
+ }
15040
+ for (const status of input.requiredCaseStatuses ?? []) {
15041
+ if (!caseStatuses.includes(status)) {
15042
+ issues.push(`Missing tool contract case status: ${status}.`);
15043
+ }
15044
+ }
15045
+ return {
15046
+ caseStatuses,
15047
+ cases: cases.length,
15048
+ contractIds,
15049
+ failed: report.failed,
15050
+ issues,
15051
+ issueCount,
15052
+ missingOperationsRecordHrefs,
15053
+ ok: issues.length === 0,
15054
+ passed: report.passed,
15055
+ status: report.status,
15056
+ timedOut,
15057
+ toolNames,
15058
+ total: report.total
15059
+ };
15060
+ };
15061
+ var assertVoiceToolContractEvidence = (report, input = {}) => {
15062
+ const assertion = evaluateVoiceToolContractEvidence(report, input);
15063
+ if (!assertion.ok) {
15064
+ throw new Error(`Voice tool contract evidence assertion failed: ${assertion.issues.join(" ")}`);
15065
+ }
15066
+ return assertion;
15067
+ };
14913
15068
  var renderVoiceToolContractHTML = (report, options = {}) => {
14914
15069
  const title = options.title ?? "Voice Tool Contracts";
14915
15070
  const snippet = escapeHtml22(`app.use(
@@ -15983,6 +16138,91 @@ var assertVoiceAgentSquadContract = async (options) => {
15983
16138
  }
15984
16139
  return report;
15985
16140
  };
16141
+ var evaluateVoiceAgentSquadContractEvidence = (reports, input = {}) => {
16142
+ const issues = [];
16143
+ const maxFailed = input.maxFailed ?? 0;
16144
+ const maxIssues = input.maxIssues ?? 0;
16145
+ const maxBlockedHandoffs = input.maxBlockedHandoffs ?? Infinity;
16146
+ const allHandoffs = reports.flatMap((report) => report.turns.flatMap((turn) => turn.handoffs));
16147
+ const contractIds = [...new Set(reports.map((report) => report.contractId))].sort();
16148
+ const scenarioIds = [
16149
+ ...new Set(reports.map((report) => report.scenarioId).filter((scenarioId) => Boolean(scenarioId)))
16150
+ ].sort();
16151
+ const finalAgentIds = [
16152
+ ...new Set(reports.flatMap((report) => report.turns.map((turn) => turn.agentId)))
16153
+ ].sort();
16154
+ const handoffTargets = [
16155
+ ...new Set(allHandoffs.map((handoff) => handoff.targetAgentId).filter((target) => Boolean(target)))
16156
+ ].sort();
16157
+ const handoffStatuses = [
16158
+ ...new Set(allHandoffs.map((handoff) => handoff.status).filter((status) => status !== undefined))
16159
+ ].sort();
16160
+ const failed = reports.filter((report) => !report.pass).length;
16161
+ const issueCount = reports.reduce((total, report) => total + report.issues.length, 0);
16162
+ const blockedHandoffs = allHandoffs.filter((handoff) => handoff.status === "blocked").length;
16163
+ if (input.minContracts !== undefined && reports.length < input.minContracts) {
16164
+ issues.push(`Expected at least ${String(input.minContracts)} agent squad contract(s), found ${String(reports.length)}.`);
16165
+ }
16166
+ if (failed > maxFailed) {
16167
+ issues.push(`Expected at most ${String(maxFailed)} failing agent squad contract(s), found ${String(failed)}.`);
16168
+ }
16169
+ if (issueCount > maxIssues) {
16170
+ issues.push(`Expected at most ${String(maxIssues)} agent squad contract issue(s), found ${String(issueCount)}.`);
16171
+ }
16172
+ if (input.minHandoffs !== undefined && allHandoffs.length < input.minHandoffs) {
16173
+ issues.push(`Expected at least ${String(input.minHandoffs)} agent squad handoff(s), found ${String(allHandoffs.length)}.`);
16174
+ }
16175
+ if (blockedHandoffs > maxBlockedHandoffs) {
16176
+ issues.push(`Expected at most ${String(maxBlockedHandoffs)} blocked agent squad handoff(s), found ${String(blockedHandoffs)}.`);
16177
+ }
16178
+ for (const contractId of input.requiredContractIds ?? []) {
16179
+ if (!contractIds.includes(contractId)) {
16180
+ issues.push(`Missing agent squad contract: ${contractId}.`);
16181
+ }
16182
+ }
16183
+ for (const scenarioId of input.requiredScenarioIds ?? []) {
16184
+ if (!scenarioIds.includes(scenarioId)) {
16185
+ issues.push(`Missing agent squad scenario: ${scenarioId}.`);
16186
+ }
16187
+ }
16188
+ for (const agentId of input.requiredFinalAgentIds ?? []) {
16189
+ if (!finalAgentIds.includes(agentId)) {
16190
+ issues.push(`Missing final agent: ${agentId}.`);
16191
+ }
16192
+ }
16193
+ for (const target of input.requiredHandoffTargets ?? []) {
16194
+ if (!handoffTargets.includes(target)) {
16195
+ issues.push(`Missing agent squad handoff target: ${target}.`);
16196
+ }
16197
+ }
16198
+ for (const status of input.requiredHandoffStatuses ?? []) {
16199
+ if (!handoffStatuses.includes(status)) {
16200
+ issues.push(`Missing agent squad handoff status: ${status}.`);
16201
+ }
16202
+ }
16203
+ return {
16204
+ blockedHandoffs,
16205
+ contractIds,
16206
+ failed,
16207
+ finalAgentIds,
16208
+ handoffStatuses,
16209
+ handoffTargets,
16210
+ handoffs: allHandoffs.length,
16211
+ issues,
16212
+ issueCount,
16213
+ ok: issues.length === 0,
16214
+ passed: reports.length - failed,
16215
+ scenarioIds,
16216
+ total: reports.length
16217
+ };
16218
+ };
16219
+ var assertVoiceAgentSquadContractEvidence = (reports, input = {}) => {
16220
+ const report = evaluateVoiceAgentSquadContractEvidence(reports, input);
16221
+ if (!report.ok) {
16222
+ throw new Error(`Voice agent squad contract evidence assertion failed: ${report.issues.join(" ")}`);
16223
+ }
16224
+ return report;
16225
+ };
15986
16226
  // src/turnLatency.ts
15987
16227
  import { Elysia as Elysia25 } from "elysia";
15988
16228
  var DEFAULT_WARN_AFTER_MS = 1800;
@@ -22146,6 +22386,110 @@ var assertVoiceProviderRoutingContract = async (options) => {
22146
22386
  }
22147
22387
  return report;
22148
22388
  };
22389
+ var evaluateVoiceProviderRoutingContractEvidence = (reports, input = {}) => {
22390
+ const issues = [];
22391
+ const maxFailed = input.maxFailed ?? 0;
22392
+ const maxIssues = input.maxIssues ?? 0;
22393
+ const events = reports.flatMap((report) => report.events);
22394
+ const contractIds = [...new Set(reports.map((report) => report.contractId))].sort();
22395
+ const scenarioIds = [
22396
+ ...new Set(reports.map((report) => report.scenarioId).filter((scenarioId) => Boolean(scenarioId)))
22397
+ ].sort();
22398
+ const kinds = [...new Set(events.map((event) => event.kind))].sort();
22399
+ const operations = [
22400
+ ...new Set(events.map((event) => event.operation).filter((operation) => Boolean(operation)))
22401
+ ].sort();
22402
+ const providers = [
22403
+ ...new Set(events.map((event) => event.provider).filter((provider) => Boolean(provider)))
22404
+ ].sort();
22405
+ const selectedProviders = [
22406
+ ...new Set(events.map((event) => event.selectedProvider).filter((provider) => Boolean(provider)))
22407
+ ].sort();
22408
+ const fallbackProviders = [
22409
+ ...new Set(events.map((event) => event.fallbackProvider).filter((provider) => Boolean(provider)))
22410
+ ].sort();
22411
+ const statuses = [
22412
+ ...new Set(events.map((event) => event.status).filter((status) => status !== undefined))
22413
+ ].sort();
22414
+ const failed = reports.filter((report) => !report.pass).length;
22415
+ const issueCount = reports.reduce((total, report) => total + report.issues.length, 0);
22416
+ if (input.minContracts !== undefined && reports.length < input.minContracts) {
22417
+ issues.push(`Expected at least ${String(input.minContracts)} provider routing contract(s), found ${String(reports.length)}.`);
22418
+ }
22419
+ if (failed > maxFailed) {
22420
+ issues.push(`Expected at most ${String(maxFailed)} failing provider routing contract(s), found ${String(failed)}.`);
22421
+ }
22422
+ if (issueCount > maxIssues) {
22423
+ issues.push(`Expected at most ${String(maxIssues)} provider routing contract issue(s), found ${String(issueCount)}.`);
22424
+ }
22425
+ if (input.minEvents !== undefined && events.length < input.minEvents) {
22426
+ issues.push(`Expected at least ${String(input.minEvents)} provider routing event(s), found ${String(events.length)}.`);
22427
+ }
22428
+ for (const contractId of input.requiredContractIds ?? []) {
22429
+ if (!contractIds.includes(contractId)) {
22430
+ issues.push(`Missing provider routing contract: ${contractId}.`);
22431
+ }
22432
+ }
22433
+ for (const scenarioId of input.requiredScenarioIds ?? []) {
22434
+ if (!scenarioIds.includes(scenarioId)) {
22435
+ issues.push(`Missing provider routing scenario: ${scenarioId}.`);
22436
+ }
22437
+ }
22438
+ for (const kind of input.requiredKinds ?? []) {
22439
+ if (!kinds.includes(kind)) {
22440
+ issues.push(`Missing provider routing kind: ${kind}.`);
22441
+ }
22442
+ }
22443
+ for (const operation of input.requiredOperations ?? []) {
22444
+ if (!operations.includes(operation)) {
22445
+ issues.push(`Missing provider routing operation: ${operation}.`);
22446
+ }
22447
+ }
22448
+ for (const provider of input.requiredProviders ?? []) {
22449
+ if (!providers.includes(provider)) {
22450
+ issues.push(`Missing provider routing provider: ${provider}.`);
22451
+ }
22452
+ }
22453
+ for (const provider of input.requiredSelectedProviders ?? []) {
22454
+ if (!selectedProviders.includes(provider)) {
22455
+ issues.push(`Missing selected provider: ${provider}.`);
22456
+ }
22457
+ }
22458
+ for (const provider of input.requiredFallbackProviders ?? []) {
22459
+ if (!fallbackProviders.includes(provider)) {
22460
+ issues.push(`Missing fallback provider: ${provider}.`);
22461
+ }
22462
+ }
22463
+ for (const status of input.requiredStatuses ?? []) {
22464
+ if (!statuses.includes(status)) {
22465
+ issues.push(`Missing provider routing status: ${status}.`);
22466
+ }
22467
+ }
22468
+ return {
22469
+ contractIds,
22470
+ events: events.length,
22471
+ failed,
22472
+ fallbackProviders,
22473
+ issues,
22474
+ issueCount,
22475
+ kinds,
22476
+ ok: issues.length === 0,
22477
+ operations,
22478
+ passed: reports.length - failed,
22479
+ providers,
22480
+ scenarioIds,
22481
+ selectedProviders,
22482
+ statuses,
22483
+ total: reports.length
22484
+ };
22485
+ };
22486
+ var assertVoiceProviderRoutingContractEvidence = (reports, input = {}) => {
22487
+ const report = evaluateVoiceProviderRoutingContractEvidence(reports, input);
22488
+ if (!report.ok) {
22489
+ throw new Error(`Voice provider routing contract evidence assertion failed: ${report.issues.join(" ")}`);
22490
+ }
22491
+ return report;
22492
+ };
22149
22493
  // src/providerSlo.ts
22150
22494
  import { Elysia as Elysia37 } from "elysia";
22151
22495
  var defaultThresholds = {
@@ -30506,19 +30850,23 @@ export {
30506
30850
  exportVoiceTrace,
30507
30851
  exportVoiceAuditTrail,
30508
30852
  evaluateVoiceTrace,
30853
+ evaluateVoiceToolContractEvidence,
30509
30854
  evaluateVoiceTelephonyContract,
30510
30855
  evaluateVoiceQuality,
30511
30856
  evaluateVoiceProviderStackGaps,
30512
30857
  evaluateVoiceProviderStackEvidence,
30513
30858
  evaluateVoiceProviderSloEvidence,
30859
+ evaluateVoiceProviderRoutingContractEvidence,
30514
30860
  evaluateVoiceProviderContractMatrixEvidence,
30515
30861
  evaluateVoiceProofTrendEvidence,
30516
30862
  evaluateVoiceProductionReadinessEvidence,
30517
30863
  evaluateVoicePlatformCoverage,
30864
+ evaluateVoiceOutcomeContractEvidence,
30518
30865
  evaluateVoiceOperationsRecordGuardrails,
30519
30866
  evaluateVoiceObservabilityExportReplayEvidence,
30520
30867
  evaluateVoiceObservabilityExportDeliveryEvidence,
30521
30868
  evaluateVoiceGuardrailPolicy,
30869
+ evaluateVoiceAgentSquadContractEvidence,
30522
30870
  encodeTwilioMulawBase64,
30523
30871
  deliverVoiceTraceEventsToSinks,
30524
30872
  deliverVoiceObservabilityExport,
@@ -30830,19 +31178,23 @@ export {
30830
31178
  buildVoiceAuditDeliveryReport,
30831
31179
  buildEmptyVoiceProofTrendReport,
30832
31180
  assignVoiceOpsTask,
31181
+ assertVoiceToolContractEvidence,
30833
31182
  assertVoiceProviderStackEvidence,
30834
31183
  assertVoiceProviderSloEvidence,
31184
+ assertVoiceProviderRoutingContractEvidence,
30835
31185
  assertVoiceProviderRoutingContract,
30836
31186
  assertVoiceProviderContractMatrixEvidence,
30837
31187
  assertVoiceProofTrendEvidence,
30838
31188
  assertVoiceProductionReadinessEvidence,
30839
31189
  assertVoicePlatformCoverage,
31190
+ assertVoiceOutcomeContractEvidence,
30840
31191
  assertVoiceOperationsRecordGuardrails,
30841
31192
  assertVoiceObservabilityExportSchema,
30842
31193
  assertVoiceObservabilityExportReplayEvidence,
30843
31194
  assertVoiceObservabilityExportRecord,
30844
31195
  assertVoiceObservabilityExportDeliveryEvidence,
30845
31196
  assertVoiceLatencySLOGate,
31197
+ assertVoiceAgentSquadContractEvidence,
30846
31198
  assertVoiceAgentSquadContract,
30847
31199
  applyVoiceTelephonyOutcome,
30848
31200
  applyVoiceOpsTaskPolicy,
@@ -44,6 +44,35 @@ export type VoiceOutcomeContractSuiteReport = {
44
44
  status: VoiceOutcomeContractStatus;
45
45
  total: number;
46
46
  };
47
+ export type VoiceOutcomeContractAssertionInput = {
48
+ maxFailed?: number;
49
+ maxIssues?: number;
50
+ minContracts?: number;
51
+ minHandoffs?: number;
52
+ minIntegrationEvents?: number;
53
+ minOperationsRecordHrefs?: number;
54
+ minReviews?: number;
55
+ minSessions?: number;
56
+ minTasks?: number;
57
+ requiredContractIds?: string[];
58
+ requireOperationRecordHrefs?: boolean;
59
+ };
60
+ export type VoiceOutcomeContractAssertionReport = {
61
+ contractIds: string[];
62
+ failed: number;
63
+ handoffs: number;
64
+ integrationEvents: number;
65
+ issues: string[];
66
+ issueCount: number;
67
+ ok: boolean;
68
+ operationsRecordHrefs: number;
69
+ passed: number;
70
+ reviews: number;
71
+ sessions: number;
72
+ status: VoiceOutcomeContractStatus;
73
+ tasks: number;
74
+ total: number;
75
+ };
47
76
  type ListStore<T> = {
48
77
  list: () => Promise<T[]> | T[];
49
78
  };
@@ -67,6 +96,8 @@ export type VoiceOutcomeContractRoutesOptions<TSession extends VoiceSessionRecor
67
96
  path?: string;
68
97
  };
69
98
  export declare const runVoiceOutcomeContractSuite: <TSession extends VoiceSessionRecord = VoiceSessionRecord>(options: VoiceOutcomeContractOptions<TSession>) => Promise<VoiceOutcomeContractSuiteReport>;
99
+ export declare const evaluateVoiceOutcomeContractEvidence: (report: VoiceOutcomeContractSuiteReport, input?: VoiceOutcomeContractAssertionInput) => VoiceOutcomeContractAssertionReport;
100
+ export declare const assertVoiceOutcomeContractEvidence: (report: VoiceOutcomeContractSuiteReport, input?: VoiceOutcomeContractAssertionInput) => VoiceOutcomeContractAssertionReport;
70
101
  export declare const renderVoiceOutcomeContractHTML: (report: VoiceOutcomeContractSuiteReport, options?: {
71
102
  title?: string;
72
103
  }) => string;
@@ -34,5 +34,38 @@ export type VoiceProviderRoutingContractRunOptions = {
34
34
  events?: StoredVoiceTraceEvent[] | VoiceRoutingEvent[];
35
35
  store?: VoiceTraceEventStore;
36
36
  };
37
+ export type VoiceProviderRoutingContractAssertionInput = {
38
+ maxFailed?: number;
39
+ maxIssues?: number;
40
+ minContracts?: number;
41
+ minEvents?: number;
42
+ requiredContractIds?: string[];
43
+ requiredFallbackProviders?: string[];
44
+ requiredKinds?: VoiceRoutingEventKind[];
45
+ requiredOperations?: string[];
46
+ requiredProviders?: string[];
47
+ requiredScenarioIds?: string[];
48
+ requiredSelectedProviders?: string[];
49
+ requiredStatuses?: VoiceProviderRoutingStatus[];
50
+ };
51
+ export type VoiceProviderRoutingContractAssertionReport = {
52
+ contractIds: string[];
53
+ events: number;
54
+ failed: number;
55
+ fallbackProviders: string[];
56
+ issues: string[];
57
+ issueCount: number;
58
+ kinds: VoiceRoutingEventKind[];
59
+ ok: boolean;
60
+ operations: string[];
61
+ passed: number;
62
+ providers: string[];
63
+ scenarioIds: string[];
64
+ selectedProviders: string[];
65
+ statuses: VoiceProviderRoutingStatus[];
66
+ total: number;
67
+ };
37
68
  export declare const runVoiceProviderRoutingContract: (options: VoiceProviderRoutingContractRunOptions) => Promise<VoiceProviderRoutingContractReport>;
38
69
  export declare const assertVoiceProviderRoutingContract: (options: VoiceProviderRoutingContractRunOptions) => Promise<VoiceProviderRoutingContractReport>;
70
+ export declare const evaluateVoiceProviderRoutingContractEvidence: (reports: readonly VoiceProviderRoutingContractReport[], input?: VoiceProviderRoutingContractAssertionInput) => VoiceProviderRoutingContractAssertionReport;
71
+ export declare const assertVoiceProviderRoutingContractEvidence: (reports: readonly VoiceProviderRoutingContractReport[], input?: VoiceProviderRoutingContractAssertionInput) => VoiceProviderRoutingContractAssertionReport;
@@ -64,6 +64,32 @@ export type VoiceToolContractSuiteReport = {
64
64
  status: 'fail' | 'pass';
65
65
  total: number;
66
66
  };
67
+ export type VoiceToolContractAssertionInput = {
68
+ maxFailed?: number;
69
+ maxIssues?: number;
70
+ maxTimedOut?: number;
71
+ minCases?: number;
72
+ minContracts?: number;
73
+ requireOperationRecordHrefs?: boolean;
74
+ requiredCaseStatuses?: Array<'error' | 'ok'>;
75
+ requiredContractIds?: string[];
76
+ requiredToolNames?: string[];
77
+ };
78
+ export type VoiceToolContractAssertionReport = {
79
+ caseStatuses: Array<'error' | 'ok'>;
80
+ cases: number;
81
+ contractIds: string[];
82
+ failed: number;
83
+ issues: string[];
84
+ issueCount: number;
85
+ missingOperationsRecordHrefs: number;
86
+ ok: boolean;
87
+ passed: number;
88
+ status: VoiceToolContractSuiteReport['status'];
89
+ timedOut: number;
90
+ toolNames: string[];
91
+ total: number;
92
+ };
67
93
  export type VoiceToolContractHandlerOptions = {
68
94
  contracts: VoiceToolContractDefinition[];
69
95
  operationsRecordHref?: false | string | ((sessionId: string) => string);
@@ -86,6 +112,8 @@ export declare const createVoiceToolContract: <TContext = unknown, TSession exte
86
112
  };
87
113
  export declare const createVoiceToolRuntimeContractDefaults: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TRouteResult = unknown>() => VoiceToolRuntimeOptions<TContext, TSession, TRouteResult>;
88
114
  export declare const runVoiceToolContractSuite: (options: VoiceToolContractHandlerOptions) => Promise<VoiceToolContractSuiteReport>;
115
+ export declare const evaluateVoiceToolContractEvidence: (report: VoiceToolContractSuiteReport, input?: VoiceToolContractAssertionInput) => VoiceToolContractAssertionReport;
116
+ export declare const assertVoiceToolContractEvidence: (report: VoiceToolContractSuiteReport, input?: VoiceToolContractAssertionInput) => VoiceToolContractAssertionReport;
89
117
  export declare const renderVoiceToolContractHTML: (report: VoiceToolContractSuiteReport, options?: {
90
118
  title?: string;
91
119
  }) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.254",
3
+ "version": "0.0.22-beta.256",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",