@absolutejs/voice 0.0.22-beta.62 → 0.0.22-beta.63

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/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, create
8
8
  export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, createVoiceSessionsHTMLHandler, createVoiceSessionsJSONHandler, renderVoiceSessionsHTML, summarizeVoiceSessions, summarizeVoiceSessionReplay } from './sessionReplay';
9
9
  export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
10
10
  export { createVoiceToolIdempotencyKey, createVoiceToolRuntime } from './toolRuntime';
11
+ export { createVoiceToolContract, createVoiceToolRuntimeContractDefaults, runVoiceToolContract } from './toolContract';
11
12
  export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
12
13
  export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
13
14
  export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel, resolveVoiceProviderRoutingPolicyPreset, createVoiceProviderRouter } from './modelAdapters';
@@ -55,6 +56,7 @@ export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePa
55
56
  export type { VoiceIOProviderRouterEvent, VoiceIOProviderRouterOptions, VoiceIOProviderRouterPolicy, VoiceIOProviderRouterPolicyConfig, VoiceSTTProviderRouterOptions, VoiceTTSProviderRouterOptions } from './providerAdapters';
56
57
  export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadOptions, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
57
58
  export type { VoiceToolRetryDelay, VoiceToolRuntime, VoiceToolRuntimeExecuteInput, VoiceToolRuntimeOptions, VoiceToolRuntimeResult } from './toolRuntime';
59
+ export type { VoiceToolContractCase, VoiceToolContractCaseReport, VoiceToolContractDefinition, VoiceToolContractExpectation, VoiceToolContractIssue, VoiceToolContractReport } from './toolContract';
58
60
  export type { VoiceOpsRuntime, VoiceOpsRuntimeConfig, VoiceOpsRuntimeSummary, VoiceOpsRuntimeSinkWorkerConfig, VoiceOpsRuntimeTaskWorkerConfig, VoiceOpsRuntimeTickResult, VoiceOpsRuntimeWebhookWorkerConfig } from './opsRuntime';
59
61
  export type { VoiceOpsPresetName, VoiceOpsPresetOverrides, VoiceResolvedOpsPreset } from './opsPresets';
60
62
  export type { VoiceOutcomeRecipe, VoiceOutcomeRecipeName, VoiceOutcomeRecipeOptions } from './outcomeRecipes';
package/dist/index.js CHANGED
@@ -9946,6 +9946,163 @@ var createVoiceToolIdempotencyKey = (input) => {
9946
9946
  args
9947
9947
  ].join(":");
9948
9948
  };
9949
+ // src/toolContract.ts
9950
+ var createDefaultSession = (contractId, caseId) => createVoiceSessionRecord(`tool-contract-${contractId}-${caseId}`);
9951
+ var createDefaultTurn = (caseId) => ({
9952
+ committedAt: Date.now(),
9953
+ id: `turn-${caseId}`,
9954
+ text: `Run tool contract case ${caseId}.`,
9955
+ transcripts: []
9956
+ });
9957
+ var defaultApi = {};
9958
+ var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
9959
+ var evaluateExpectation = (input) => {
9960
+ const issues = [];
9961
+ const expect = input.expect;
9962
+ if (!expect) {
9963
+ return issues;
9964
+ }
9965
+ if (expect.expectStatus && input.status !== expect.expectStatus) {
9966
+ issues.push({
9967
+ caseId: input.caseId,
9968
+ code: "tool.status_mismatch",
9969
+ message: `Expected ${expect.expectStatus}, saw ${input.status}.`
9970
+ });
9971
+ }
9972
+ if (typeof expect.expectedAttempts === "number" && input.attempts !== expect.expectedAttempts) {
9973
+ issues.push({
9974
+ caseId: input.caseId,
9975
+ code: "tool.attempt_mismatch",
9976
+ message: `Expected ${expect.expectedAttempts} attempts, saw ${input.attempts}.`
9977
+ });
9978
+ }
9979
+ if (expect.expectedResult !== undefined && !sameJSON(input.result, expect.expectedResult)) {
9980
+ issues.push({
9981
+ caseId: input.caseId,
9982
+ code: "tool.result_mismatch",
9983
+ message: "Tool result did not match expected result."
9984
+ });
9985
+ }
9986
+ if (expect.expectedErrorIncludes && !input.error?.includes(expect.expectedErrorIncludes)) {
9987
+ issues.push({
9988
+ caseId: input.caseId,
9989
+ code: "tool.error_mismatch",
9990
+ message: `Expected error to include ${expect.expectedErrorIncludes}.`
9991
+ });
9992
+ }
9993
+ if (typeof expect.expectTimedOut === "boolean" && input.timedOut !== expect.expectTimedOut) {
9994
+ issues.push({
9995
+ caseId: input.caseId,
9996
+ code: "tool.timeout_mismatch",
9997
+ message: `Expected timedOut=${String(expect.expectTimedOut)}, saw ${String(input.timedOut)}.`
9998
+ });
9999
+ }
10000
+ if (typeof expect.maxElapsedMs === "number" && input.elapsedMs > expect.maxElapsedMs) {
10001
+ issues.push({
10002
+ caseId: input.caseId,
10003
+ code: "tool.elapsed_exceeded",
10004
+ message: `Expected elapsed <= ${expect.maxElapsedMs}ms, saw ${input.elapsedMs}ms.`
10005
+ });
10006
+ }
10007
+ return issues;
10008
+ };
10009
+ var runVoiceToolContract = async (definition) => {
10010
+ const cases = [];
10011
+ for (const testCase of definition.cases) {
10012
+ const session = testCase.session ?? createDefaultSession(definition.id, testCase.id);
10013
+ const turn = testCase.turn ?? createDefaultTurn(testCase.id);
10014
+ const context = testCase.context ?? {};
10015
+ const runtimeOptions = {
10016
+ ...definition.defaultRuntime,
10017
+ ...testCase.runtime
10018
+ };
10019
+ const runtime = createVoiceToolRuntime(runtimeOptions);
10020
+ const toolCall = {
10021
+ args: testCase.args,
10022
+ id: testCase.toolCallId ?? testCase.id,
10023
+ name: definition.tool.name
10024
+ };
10025
+ const executeOnce = () => runtime.execute({
10026
+ api: defaultApi,
10027
+ args: toolCall.args,
10028
+ context,
10029
+ session,
10030
+ tool: definition.tool,
10031
+ toolCallId: toolCall.id,
10032
+ turn
10033
+ });
10034
+ const result = await executeOnce();
10035
+ let issues2 = evaluateExpectation({
10036
+ attempts: result.attempts,
10037
+ caseId: testCase.id,
10038
+ elapsedMs: result.elapsedMs,
10039
+ error: result.error,
10040
+ expect: testCase.expect,
10041
+ result: result.result,
10042
+ status: result.status,
10043
+ timedOut: result.timedOut
10044
+ });
10045
+ if (testCase.expect?.expectIdempotent) {
10046
+ const second = await executeOnce();
10047
+ if (second.result !== result.result && !sameJSON(second.result, result.result)) {
10048
+ issues2.push({
10049
+ caseId: testCase.id,
10050
+ code: "tool.idempotency_result_mismatch",
10051
+ message: "Repeated idempotent execution returned a different result."
10052
+ });
10053
+ }
10054
+ if (second.idempotencyKey !== result.idempotencyKey) {
10055
+ issues2.push({
10056
+ caseId: testCase.id,
10057
+ code: "tool.idempotency_key_mismatch",
10058
+ message: "Repeated idempotent execution used a different idempotency key."
10059
+ });
10060
+ }
10061
+ }
10062
+ cases.push({
10063
+ attempts: result.attempts,
10064
+ caseId: testCase.id,
10065
+ elapsedMs: result.elapsedMs,
10066
+ error: result.error,
10067
+ issues: issues2,
10068
+ label: testCase.label,
10069
+ pass: issues2.length === 0,
10070
+ status: result.status,
10071
+ timedOut: result.timedOut
10072
+ });
10073
+ }
10074
+ const issues = cases.flatMap((testCase) => testCase.issues);
10075
+ return {
10076
+ cases,
10077
+ contractId: definition.id,
10078
+ issues,
10079
+ pass: issues.length === 0,
10080
+ toolName: definition.tool.name
10081
+ };
10082
+ };
10083
+ var createVoiceToolContract = (definition) => ({
10084
+ assert: async () => {
10085
+ const report = await runVoiceToolContract(definition);
10086
+ if (!report.pass) {
10087
+ throw new Error(`Voice tool contract ${definition.id} failed: ${report.issues.map((issue) => issue.message).join(" ")}`);
10088
+ }
10089
+ return report;
10090
+ },
10091
+ definition,
10092
+ run: () => runVoiceToolContract(definition)
10093
+ });
10094
+ var createVoiceToolRuntimeContractDefaults = () => ({
10095
+ idempotencyKey: ({ args, session, toolCallId, toolName, turn }) => createVoiceToolIdempotencyKey({
10096
+ args,
10097
+ sessionId: session.id,
10098
+ toolCallId,
10099
+ toolName,
10100
+ turnId: turn.id
10101
+ }),
10102
+ idempotencyTtlMs: 60000,
10103
+ maxRetries: 1,
10104
+ timeoutMs: 5000
10105
+ });
9949
10106
  // src/fileStore.ts
9950
10107
  import { mkdir as mkdir2, readFile, readdir, rename, rm, writeFile } from "fs/promises";
9951
10108
  import { join } from "path";
@@ -14334,6 +14491,7 @@ export {
14334
14491
  startVoiceOpsTask,
14335
14492
  shapeTelephonyAssistantText,
14336
14493
  selectVoiceTraceEventsForPrune,
14494
+ runVoiceToolContract,
14337
14495
  runVoiceSessionEvals,
14338
14496
  runVoiceScenarioFixtureEvals,
14339
14497
  runVoiceScenarioEvals,
@@ -14412,8 +14570,10 @@ export {
14412
14570
  createVoiceTraceHTTPSink,
14413
14571
  createVoiceTraceEventId,
14414
14572
  createVoiceTraceEvent,
14573
+ createVoiceToolRuntimeContractDefaults,
14415
14574
  createVoiceToolRuntime,
14416
14575
  createVoiceToolIdempotencyKey,
14576
+ createVoiceToolContract,
14417
14577
  createVoiceTaskUpdatedEvent,
14418
14578
  createVoiceTaskSLABreachedEvent,
14419
14579
  createVoiceTaskCreatedEvent,
@@ -0,0 +1,61 @@
1
+ import type { VoiceAgentTool } from './agent';
2
+ import { type VoiceToolRuntimeOptions } from './toolRuntime';
3
+ import type { VoiceSessionRecord, VoiceTurnRecord } from './types';
4
+ export type VoiceToolContractExpectation = {
5
+ expectedAttempts?: number;
6
+ expectedErrorIncludes?: string;
7
+ expectedResult?: unknown;
8
+ expectIdempotent?: boolean;
9
+ expectStatus?: 'error' | 'ok';
10
+ expectTimedOut?: boolean;
11
+ maxElapsedMs?: number;
12
+ };
13
+ export type VoiceToolContractCase<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TArgs = Record<string, unknown>, TToolResult = unknown, TRouteResult = unknown> = {
14
+ args: TArgs;
15
+ context?: TContext;
16
+ expect?: VoiceToolContractExpectation;
17
+ id: string;
18
+ label?: string;
19
+ runtime?: VoiceToolRuntimeOptions<TContext, TSession, TRouteResult>;
20
+ session?: TSession;
21
+ toolCallId?: string;
22
+ turn?: VoiceTurnRecord;
23
+ };
24
+ export type VoiceToolContractDefinition<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TArgs = Record<string, unknown>, TToolResult = unknown, TRouteResult = unknown> = {
25
+ cases: Array<VoiceToolContractCase<TContext, TSession, TArgs, TToolResult, TRouteResult>>;
26
+ defaultRuntime?: VoiceToolRuntimeOptions<TContext, TSession, TRouteResult>;
27
+ description?: string;
28
+ id: string;
29
+ label?: string;
30
+ tool: VoiceAgentTool<TContext, TSession, TArgs, TToolResult, TRouteResult>;
31
+ };
32
+ export type VoiceToolContractIssue = {
33
+ caseId: string;
34
+ code: string;
35
+ message: string;
36
+ };
37
+ export type VoiceToolContractCaseReport = {
38
+ attempts: number;
39
+ caseId: string;
40
+ elapsedMs: number;
41
+ error?: string;
42
+ issues: VoiceToolContractIssue[];
43
+ label?: string;
44
+ pass: boolean;
45
+ status: 'error' | 'ok';
46
+ timedOut: boolean;
47
+ };
48
+ export type VoiceToolContractReport = {
49
+ cases: VoiceToolContractCaseReport[];
50
+ contractId: string;
51
+ issues: VoiceToolContractIssue[];
52
+ pass: boolean;
53
+ toolName: string;
54
+ };
55
+ export declare const runVoiceToolContract: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TArgs = Record<string, unknown>, TToolResult = unknown, TRouteResult = unknown>(definition: VoiceToolContractDefinition<TContext, TSession, TArgs, TToolResult, TRouteResult>) => Promise<VoiceToolContractReport>;
56
+ export declare const createVoiceToolContract: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TArgs = Record<string, unknown>, TToolResult = unknown, TRouteResult = unknown>(definition: VoiceToolContractDefinition<TContext, TSession, TArgs, TToolResult, TRouteResult>) => {
57
+ assert: () => Promise<VoiceToolContractReport>;
58
+ definition: VoiceToolContractDefinition<TContext, TSession, TArgs, TToolResult, TRouteResult>;
59
+ run: () => Promise<VoiceToolContractReport>;
60
+ };
61
+ export declare const createVoiceToolRuntimeContractDefaults: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TRouteResult = unknown>() => VoiceToolRuntimeOptions<TContext, TSession, TRouteResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.62",
3
+ "version": "0.0.22-beta.63",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",