omk-agent-core 0.98.3 → 0.98.5

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/agent-loop.d.ts +1 -31
  3. package/dist/agent-loop.d.ts.map +1 -1
  4. package/dist/agent-loop.js +15 -70
  5. package/dist/agent-loop.js.map +1 -1
  6. package/dist/agent.d.ts +7 -0
  7. package/dist/agent.d.ts.map +1 -1
  8. package/dist/agent.js +10 -29
  9. package/dist/agent.js.map +1 -1
  10. package/dist/harness/reverse-skill-text.d.ts +3 -0
  11. package/dist/harness/reverse-skill-text.d.ts.map +1 -0
  12. package/dist/harness/reverse-skill-text.js +20 -0
  13. package/dist/harness/reverse-skill-text.js.map +1 -0
  14. package/dist/harness/reverse-skill-tool-aliases.d.ts +3 -0
  15. package/dist/harness/reverse-skill-tool-aliases.d.ts.map +1 -0
  16. package/dist/harness/reverse-skill-tool-aliases.js +47 -0
  17. package/dist/harness/reverse-skill-tool-aliases.js.map +1 -0
  18. package/dist/harness/reverse-skill-types.d.ts +76 -0
  19. package/dist/harness/reverse-skill-types.d.ts.map +1 -0
  20. package/dist/harness/reverse-skill-types.js +2 -0
  21. package/dist/harness/reverse-skill-types.js.map +1 -0
  22. package/dist/harness/reverse-skill.d.ts +4 -78
  23. package/dist/harness/reverse-skill.d.ts.map +1 -1
  24. package/dist/harness/reverse-skill.js +113 -61
  25. package/dist/harness/reverse-skill.js.map +1 -1
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +3 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/pending-message-queue.d.ts +11 -0
  31. package/dist/pending-message-queue.d.ts.map +1 -0
  32. package/dist/pending-message-queue.js +29 -0
  33. package/dist/pending-message-queue.js.map +1 -0
  34. package/dist/provider-input.d.ts +8 -0
  35. package/dist/provider-input.d.ts.map +1 -0
  36. package/dist/provider-input.js +28 -0
  37. package/dist/provider-input.js.map +1 -0
  38. package/dist/provider-payload-contract.d.ts +6 -0
  39. package/dist/provider-payload-contract.d.ts.map +1 -0
  40. package/dist/provider-payload-contract.js +43 -0
  41. package/dist/provider-payload-contract.js.map +1 -0
  42. package/dist/provider-request-types.d.ts +46 -0
  43. package/dist/provider-request-types.d.ts.map +1 -0
  44. package/dist/provider-request-types.js +2 -0
  45. package/dist/provider-request-types.js.map +1 -0
  46. package/dist/provider-request.d.ts +17 -0
  47. package/dist/provider-request.d.ts.map +1 -0
  48. package/dist/provider-request.js +110 -0
  49. package/dist/provider-request.js.map +1 -0
  50. package/dist/run-model-contract.d.ts +11 -0
  51. package/dist/run-model-contract.d.ts.map +1 -0
  52. package/dist/run-model-contract.js +121 -0
  53. package/dist/run-model-contract.js.map +1 -0
  54. package/dist/tool-execution-boundary.d.ts.map +1 -1
  55. package/dist/tool-execution-boundary.js +6 -1
  56. package/dist/tool-execution-boundary.js.map +1 -1
  57. package/dist/tool-timeout-settlement.d.ts.map +1 -1
  58. package/dist/tool-timeout-settlement.js +2 -2
  59. package/dist/tool-timeout-settlement.js.map +1 -1
  60. package/dist/types.d.ts +4 -1
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/types.js.map +1 -1
  63. package/dist/vision-route.d.ts +19 -0
  64. package/dist/vision-route.d.ts.map +1 -0
  65. package/dist/vision-route.js +19 -0
  66. package/dist/vision-route.js.map +1 -0
  67. package/package.json +2 -2
@@ -0,0 +1,46 @@
1
+ import type { SimpleStreamOptions } from "omk-ai";
2
+ type ContractThinkingLevel = "off" | NonNullable<SimpleStreamOptions["reasoning"]>;
3
+ export interface ModelIdentity {
4
+ readonly provider: string;
5
+ readonly id: string;
6
+ }
7
+ /** Logical dispatch policy, not endpoint, credential, or serialized-payload attestation. */
8
+ export interface ModelContract {
9
+ readonly allowedModels: readonly ModelIdentity[];
10
+ readonly allowedProviders: readonly string[];
11
+ readonly allowedAuthOrigins: readonly string[];
12
+ readonly thinking: boolean;
13
+ readonly maxOutputTokens: number;
14
+ readonly thinkingLevel?: ContractThinkingLevel;
15
+ }
16
+ export interface RouteRequest {
17
+ readonly model: ModelIdentity;
18
+ readonly provider: string;
19
+ readonly thinking: boolean;
20
+ readonly maxOutputTokens?: number;
21
+ /** Logical credential resolver identity; omission means request.provider. */
22
+ readonly authOrigin?: string;
23
+ readonly thinkingLevel?: ContractThinkingLevel;
24
+ }
25
+ export type ModelContractViolationCode = "invalid-contract" | "invalid-contract-limit" | "invalid-request" | "invalid-request-limit" | "model-not-allowed" | "provider-not-allowed" | "provider-mismatch" | "auth-origin-not-allowed" | "thinking-not-allowed" | "thinking-level-mismatch" | "output-limit-exceeded";
26
+ export type ProviderRequestEvent = {
27
+ readonly type: "provider_denied";
28
+ readonly requestId: string;
29
+ readonly deniedReason: "contract-violation" | "aborted";
30
+ } | {
31
+ readonly type: "provider_request";
32
+ readonly requestId: string;
33
+ readonly provider: string;
34
+ readonly model: string;
35
+ readonly maxOutputTokens: number;
36
+ /** Tool attachments replaced by explicit text placeholders in this provider view. */
37
+ readonly omittedToolImages?: number;
38
+ readonly boundary: "stream-dispatch";
39
+ } | {
40
+ readonly type: "provider_request_end";
41
+ readonly requestId: string;
42
+ readonly outcome: "completed" | "error" | "aborted";
43
+ readonly boundary: "stream-dispatch";
44
+ };
45
+ export {};
46
+ //# sourceMappingURL=provider-request-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-request-types.d.ts","sourceRoot":"","sources":["../src/provider-request-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAElD,KAAK,qBAAqB,GAAG,KAAK,GAAG,WAAW,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC;AACnF,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACpB;AAED,4FAA4F;AAC5F,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,aAAa,EAAE,SAAS,aAAa,EAAE,CAAC;IACjD,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,qBAAqB,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,qBAAqB,CAAC;CAC/C;AAED,MAAM,MAAM,0BAA0B,GACnC,kBAAkB,GAClB,wBAAwB,GACxB,iBAAiB,GACjB,uBAAuB,GACvB,mBAAmB,GACnB,sBAAsB,GACtB,mBAAmB,GACnB,yBAAyB,GACzB,sBAAsB,GACtB,yBAAyB,GACzB,uBAAuB,CAAC;AAE3B,MAAM,MAAM,oBAAoB,GAC7B;IACA,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,oBAAoB,GAAG,SAAS,CAAC;CACvD,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,qFAAqF;IACrF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACpC,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACpC,CAAC","sourcesContent":["import type { SimpleStreamOptions } from \"omk-ai\";\n\ntype ContractThinkingLevel = \"off\" | NonNullable<SimpleStreamOptions[\"reasoning\"]>;\nexport interface ModelIdentity {\n\treadonly provider: string;\n\treadonly id: string;\n}\n\n/** Logical dispatch policy, not endpoint, credential, or serialized-payload attestation. */\nexport interface ModelContract {\n\treadonly allowedModels: readonly ModelIdentity[];\n\treadonly allowedProviders: readonly string[];\n\treadonly allowedAuthOrigins: readonly string[];\n\treadonly thinking: boolean;\n\treadonly maxOutputTokens: number;\n\treadonly thinkingLevel?: ContractThinkingLevel;\n}\n\nexport interface RouteRequest {\n\treadonly model: ModelIdentity;\n\treadonly provider: string;\n\treadonly thinking: boolean;\n\treadonly maxOutputTokens?: number;\n\t/** Logical credential resolver identity; omission means request.provider. */\n\treadonly authOrigin?: string;\n\treadonly thinkingLevel?: ContractThinkingLevel;\n}\n\nexport type ModelContractViolationCode =\n\t| \"invalid-contract\"\n\t| \"invalid-contract-limit\"\n\t| \"invalid-request\"\n\t| \"invalid-request-limit\"\n\t| \"model-not-allowed\"\n\t| \"provider-not-allowed\"\n\t| \"provider-mismatch\"\n\t| \"auth-origin-not-allowed\"\n\t| \"thinking-not-allowed\"\n\t| \"thinking-level-mismatch\"\n\t| \"output-limit-exceeded\";\n\nexport type ProviderRequestEvent =\n\t| {\n\t\t\treadonly type: \"provider_denied\";\n\t\t\treadonly requestId: string;\n\t\t\treadonly deniedReason: \"contract-violation\" | \"aborted\";\n\t }\n\t| {\n\t\t\treadonly type: \"provider_request\";\n\t\t\treadonly requestId: string;\n\t\t\treadonly provider: string;\n\t\t\treadonly model: string;\n\t\t\treadonly maxOutputTokens: number;\n\t\t\t/** Tool attachments replaced by explicit text placeholders in this provider view. */\n\t\t\treadonly omittedToolImages?: number;\n\t\t\treadonly boundary: \"stream-dispatch\";\n\t }\n\t| {\n\t\t\treadonly type: \"provider_request_end\";\n\t\t\treadonly requestId: string;\n\t\t\treadonly outcome: \"completed\" | \"error\" | \"aborted\";\n\t\t\treadonly boundary: \"stream-dispatch\";\n\t };\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider-request-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-request-types.js","sourceRoot":"","sources":["../src/provider-request-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SimpleStreamOptions } from \"omk-ai\";\n\ntype ContractThinkingLevel = \"off\" | NonNullable<SimpleStreamOptions[\"reasoning\"]>;\nexport interface ModelIdentity {\n\treadonly provider: string;\n\treadonly id: string;\n}\n\n/** Logical dispatch policy, not endpoint, credential, or serialized-payload attestation. */\nexport interface ModelContract {\n\treadonly allowedModels: readonly ModelIdentity[];\n\treadonly allowedProviders: readonly string[];\n\treadonly allowedAuthOrigins: readonly string[];\n\treadonly thinking: boolean;\n\treadonly maxOutputTokens: number;\n\treadonly thinkingLevel?: ContractThinkingLevel;\n}\n\nexport interface RouteRequest {\n\treadonly model: ModelIdentity;\n\treadonly provider: string;\n\treadonly thinking: boolean;\n\treadonly maxOutputTokens?: number;\n\t/** Logical credential resolver identity; omission means request.provider. */\n\treadonly authOrigin?: string;\n\treadonly thinkingLevel?: ContractThinkingLevel;\n}\n\nexport type ModelContractViolationCode =\n\t| \"invalid-contract\"\n\t| \"invalid-contract-limit\"\n\t| \"invalid-request\"\n\t| \"invalid-request-limit\"\n\t| \"model-not-allowed\"\n\t| \"provider-not-allowed\"\n\t| \"provider-mismatch\"\n\t| \"auth-origin-not-allowed\"\n\t| \"thinking-not-allowed\"\n\t| \"thinking-level-mismatch\"\n\t| \"output-limit-exceeded\";\n\nexport type ProviderRequestEvent =\n\t| {\n\t\t\treadonly type: \"provider_denied\";\n\t\t\treadonly requestId: string;\n\t\t\treadonly deniedReason: \"contract-violation\" | \"aborted\";\n\t }\n\t| {\n\t\t\treadonly type: \"provider_request\";\n\t\t\treadonly requestId: string;\n\t\t\treadonly provider: string;\n\t\t\treadonly model: string;\n\t\t\treadonly maxOutputTokens: number;\n\t\t\t/** Tool attachments replaced by explicit text placeholders in this provider view. */\n\t\t\treadonly omittedToolImages?: number;\n\t\t\treadonly boundary: \"stream-dispatch\";\n\t }\n\t| {\n\t\t\treadonly type: \"provider_request_end\";\n\t\t\treadonly requestId: string;\n\t\t\treadonly outcome: \"completed\" | \"error\" | \"aborted\";\n\t\t\treadonly boundary: \"stream-dispatch\";\n\t };\n"]}
@@ -0,0 +1,17 @@
1
+ import { type Api, type AssistantMessage, type AssistantMessageEventStream, type Context, type Model, type SimpleStreamOptions } from "omk-ai";
2
+ import { type ModelContract } from "./run-model-contract.ts";
3
+ import type { AgentEvent, AgentLoopConfig, StreamFn } from "./types.ts";
4
+ export type { ProviderRequestEvent } from "./provider-request-types.ts";
5
+ /** Pin policy before any lifecycle subscriber can alter caller-owned configuration. */
6
+ export declare function pinProviderConfig(config: AgentLoopConfig, emit: (event: AgentEvent) => Promise<void> | void): Promise<AgentLoopConfig>;
7
+ /** Restrict logical dispatch and attach the supported final-payload checks. */
8
+ export declare function applyModelContract(contract: ModelContract, model: Model<Api>, options: SimpleStreamOptions): SimpleStreamOptions;
9
+ interface ProviderRuntime {
10
+ readonly signal?: AbortSignal;
11
+ readonly emit: (event: AgentEvent) => Promise<void> | void;
12
+ readonly streamFn?: StreamFn;
13
+ readonly consume: (stream: AssistantMessageEventStream) => Promise<AssistantMessage>;
14
+ }
15
+ /** Emit only bounded dispatch metadata; never payloads, headers, keys, or raw failures. */
16
+ export declare function requestAssistantResponse(context: Context, config: AgentLoopConfig, runtime: ProviderRuntime): Promise<AssistantMessage>;
17
+ //# sourceMappingURL=provider-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-request.d.ts","sourceRoot":"","sources":["../src/provider-request.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EACR,KAAK,gBAAgB,EACrB,KAAK,2BAA2B,EAChC,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,mBAAmB,EAExB,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAEN,KAAK,aAAa,EAGlB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGxE,YAAY,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAExE,uFAAuF;AACvF,wBAAsB,iBAAiB,CACtC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAC/C,OAAO,CAAC,eAAe,CAAC,CAU1B;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CACjC,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,mBAAmB,GAC1B,mBAAmB,CAerB;AAED,UAAU,eAAe;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,2BAA2B,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACrF;AAED,2FAA2F;AAC3F,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAsE3B","sourcesContent":["import {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype AssistantMessageEventStream,\n\ttype Context,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n} from \"omk-ai\";\nimport { projectToolImagesForModel } from \"./provider-input.ts\";\nimport { createContractPayloadHook } from \"./provider-payload-contract.ts\";\nimport {\n\tassertModelContract,\n\ttype ModelContract,\n\tModelContractViolation,\n\tsnapshotModelContract,\n} from \"./run-model-contract.ts\";\nimport type { AgentEvent, AgentLoopConfig, StreamFn } from \"./types.ts\";\nimport { getVisionRouteModel } from \"./vision-route.ts\";\n\nexport type { ProviderRequestEvent } from \"./provider-request-types.ts\";\n\n/** Pin policy before any lifecycle subscriber can alter caller-owned configuration. */\nexport async function pinProviderConfig(\n\tconfig: AgentLoopConfig,\n\temit: (event: AgentEvent) => Promise<void> | void,\n): Promise<AgentLoopConfig> {\n\tif (config.modelContract === undefined) return config;\n\ttry {\n\t\treturn { ...config, modelContract: snapshotModelContract(config.modelContract) };\n\t} catch (error) {\n\t\tif (error instanceof ModelContractViolation) {\n\t\t\tawait emit({ type: \"provider_denied\", requestId: crypto.randomUUID(), deniedReason: \"contract-violation\" });\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/** Restrict logical dispatch and attach the supported final-payload checks. */\nexport function applyModelContract(\n\tcontract: ModelContract,\n\tmodel: Model<Api>,\n\toptions: SimpleStreamOptions,\n): SimpleStreamOptions {\n\tconst maxTokens = options.maxTokens ?? Math.min(model.maxTokens, contract.maxOutputTokens);\n\tassertModelContract(contract, {\n\t\tmodel,\n\t\tprovider: model.provider,\n\t\tauthOrigin: model.provider,\n\t\tthinking: options.reasoning !== undefined,\n\t\tthinkingLevel: options.reasoning ?? \"off\",\n\t\tmaxOutputTokens: maxTokens,\n\t});\n\treturn {\n\t\t...options,\n\t\tmaxTokens,\n\t\tonPayload: createContractPayloadHook(model, maxTokens, options.onPayload),\n\t};\n}\n\ninterface ProviderRuntime {\n\treadonly signal?: AbortSignal;\n\treadonly emit: (event: AgentEvent) => Promise<void> | void;\n\treadonly streamFn?: StreamFn;\n\treadonly consume: (stream: AssistantMessageEventStream) => Promise<AssistantMessage>;\n}\n\n/** Emit only bounded dispatch metadata; never payloads, headers, keys, or raw failures. */\nexport async function requestAssistantResponse(\n\tcontext: Context,\n\tconfig: AgentLoopConfig,\n\truntime: ProviderRuntime,\n): Promise<AssistantMessage> {\n\tconst { signal, emit, consume } = runtime;\n\tconst needsVisionRoute = context.messages.some(\n\t\t(message) =>\n\t\t\t(!config.modelContract || message.role !== \"toolResult\") &&\n\t\t\tArray.isArray(message.content) &&\n\t\t\tmessage.content.some((part) => part.type === \"image\"),\n\t);\n\tconst model =\n\t\tneedsVisionRoute && !(config.model.input ?? []).includes(\"image\")\n\t\t\t? getVisionRouteModel(config.model)\n\t\t\t: config.model;\n\tconst projection = config.modelContract\n\t\t? projectToolImagesForModel(context, model)\n\t\t: { context, omittedToolImages: 0 };\n\tconst changedProvider = model.provider !== config.model.provider;\n\tconst requestId = crypto.randomUUID();\n\tconst { modelContract, ...configured } = config;\n\tlet options: SimpleStreamOptions = configured;\n\ttry {\n\t\tif (signal?.aborted) {\n\t\t\tif (modelContract) await emit({ type: \"provider_denied\", requestId, deniedReason: \"aborted\" });\n\t\t\tsignal.throwIfAborted();\n\t\t}\n\t\tif (modelContract) options = applyModelContract(modelContract, model, configured);\n\t} catch (error) {\n\t\tif (error instanceof ModelContractViolation) {\n\t\t\tawait emit({ type: \"provider_denied\", requestId, deniedReason: \"contract-violation\" });\n\t\t}\n\t\tthrow error;\n\t}\n\tconst key = await config.getApiKey?.(model.provider);\n\tif (signal?.aborted) {\n\t\tif (modelContract) await emit({ type: \"provider_denied\", requestId, deniedReason: \"aborted\" });\n\t\tsignal.throwIfAborted();\n\t}\n\tconst requestOptions: SimpleStreamOptions = {\n\t\t...options,\n\t\tapiKey: key || (changedProvider ? undefined : options.apiKey),\n\t\theaders: changedProvider ? undefined : options.headers,\n\t\tsignal,\n\t};\n\tlet outcome: \"completed\" | \"error\" | \"aborted\" = \"error\";\n\ttry {\n\t\tif (modelContract) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"provider_request\",\n\t\t\t\trequestId,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tmaxOutputTokens: requestOptions.maxTokens ?? modelContract.maxOutputTokens,\n\t\t\t\t...(projection.omittedToolImages > 0 ? { omittedToolImages: projection.omittedToolImages } : {}),\n\t\t\t\tboundary: \"stream-dispatch\",\n\t\t\t});\n\t\t}\n\t\tsignal?.throwIfAborted();\n\t\tconst response = await (runtime.streamFn ?? streamSimple)(model, projection.context, requestOptions);\n\t\tconst message = await consume(response);\n\t\toutcome = message.stopReason === \"aborted\" ? \"aborted\" : message.stopReason === \"error\" ? \"error\" : \"completed\";\n\t\treturn message;\n\t} finally {\n\t\tif (modelContract) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"provider_request_end\",\n\t\t\t\trequestId,\n\t\t\t\toutcome: signal?.aborted ? \"aborted\" : outcome,\n\t\t\t\tboundary: \"stream-dispatch\",\n\t\t\t});\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,110 @@
1
+ import { streamSimple, } from "omk-ai";
2
+ import { projectToolImagesForModel } from "./provider-input.js";
3
+ import { createContractPayloadHook } from "./provider-payload-contract.js";
4
+ import { assertModelContract, ModelContractViolation, snapshotModelContract, } from "./run-model-contract.js";
5
+ import { getVisionRouteModel } from "./vision-route.js";
6
+ /** Pin policy before any lifecycle subscriber can alter caller-owned configuration. */
7
+ export async function pinProviderConfig(config, emit) {
8
+ if (config.modelContract === undefined)
9
+ return config;
10
+ try {
11
+ return { ...config, modelContract: snapshotModelContract(config.modelContract) };
12
+ }
13
+ catch (error) {
14
+ if (error instanceof ModelContractViolation) {
15
+ await emit({ type: "provider_denied", requestId: crypto.randomUUID(), deniedReason: "contract-violation" });
16
+ }
17
+ throw error;
18
+ }
19
+ }
20
+ /** Restrict logical dispatch and attach the supported final-payload checks. */
21
+ export function applyModelContract(contract, model, options) {
22
+ const maxTokens = options.maxTokens ?? Math.min(model.maxTokens, contract.maxOutputTokens);
23
+ assertModelContract(contract, {
24
+ model,
25
+ provider: model.provider,
26
+ authOrigin: model.provider,
27
+ thinking: options.reasoning !== undefined,
28
+ thinkingLevel: options.reasoning ?? "off",
29
+ maxOutputTokens: maxTokens,
30
+ });
31
+ return {
32
+ ...options,
33
+ maxTokens,
34
+ onPayload: createContractPayloadHook(model, maxTokens, options.onPayload),
35
+ };
36
+ }
37
+ /** Emit only bounded dispatch metadata; never payloads, headers, keys, or raw failures. */
38
+ export async function requestAssistantResponse(context, config, runtime) {
39
+ const { signal, emit, consume } = runtime;
40
+ const needsVisionRoute = context.messages.some((message) => (!config.modelContract || message.role !== "toolResult") &&
41
+ Array.isArray(message.content) &&
42
+ message.content.some((part) => part.type === "image"));
43
+ const model = needsVisionRoute && !(config.model.input ?? []).includes("image")
44
+ ? getVisionRouteModel(config.model)
45
+ : config.model;
46
+ const projection = config.modelContract
47
+ ? projectToolImagesForModel(context, model)
48
+ : { context, omittedToolImages: 0 };
49
+ const changedProvider = model.provider !== config.model.provider;
50
+ const requestId = crypto.randomUUID();
51
+ const { modelContract, ...configured } = config;
52
+ let options = configured;
53
+ try {
54
+ if (signal?.aborted) {
55
+ if (modelContract)
56
+ await emit({ type: "provider_denied", requestId, deniedReason: "aborted" });
57
+ signal.throwIfAborted();
58
+ }
59
+ if (modelContract)
60
+ options = applyModelContract(modelContract, model, configured);
61
+ }
62
+ catch (error) {
63
+ if (error instanceof ModelContractViolation) {
64
+ await emit({ type: "provider_denied", requestId, deniedReason: "contract-violation" });
65
+ }
66
+ throw error;
67
+ }
68
+ const key = await config.getApiKey?.(model.provider);
69
+ if (signal?.aborted) {
70
+ if (modelContract)
71
+ await emit({ type: "provider_denied", requestId, deniedReason: "aborted" });
72
+ signal.throwIfAborted();
73
+ }
74
+ const requestOptions = {
75
+ ...options,
76
+ apiKey: key || (changedProvider ? undefined : options.apiKey),
77
+ headers: changedProvider ? undefined : options.headers,
78
+ signal,
79
+ };
80
+ let outcome = "error";
81
+ try {
82
+ if (modelContract) {
83
+ await emit({
84
+ type: "provider_request",
85
+ requestId,
86
+ provider: model.provider,
87
+ model: model.id,
88
+ maxOutputTokens: requestOptions.maxTokens ?? modelContract.maxOutputTokens,
89
+ ...(projection.omittedToolImages > 0 ? { omittedToolImages: projection.omittedToolImages } : {}),
90
+ boundary: "stream-dispatch",
91
+ });
92
+ }
93
+ signal?.throwIfAborted();
94
+ const response = await (runtime.streamFn ?? streamSimple)(model, projection.context, requestOptions);
95
+ const message = await consume(response);
96
+ outcome = message.stopReason === "aborted" ? "aborted" : message.stopReason === "error" ? "error" : "completed";
97
+ return message;
98
+ }
99
+ finally {
100
+ if (modelContract) {
101
+ await emit({
102
+ type: "provider_request_end",
103
+ requestId,
104
+ outcome: signal?.aborted ? "aborted" : outcome,
105
+ boundary: "stream-dispatch",
106
+ });
107
+ }
108
+ }
109
+ }
110
+ //# sourceMappingURL=provider-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-request.js","sourceRoot":"","sources":["../src/provider-request.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype AssistantMessageEventStream,\n\ttype Context,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n} from \"omk-ai\";\nimport { projectToolImagesForModel } from \"./provider-input.ts\";\nimport { createContractPayloadHook } from \"./provider-payload-contract.ts\";\nimport {\n\tassertModelContract,\n\ttype ModelContract,\n\tModelContractViolation,\n\tsnapshotModelContract,\n} from \"./run-model-contract.ts\";\nimport type { AgentEvent, AgentLoopConfig, StreamFn } from \"./types.ts\";\nimport { getVisionRouteModel } from \"./vision-route.ts\";\n\nexport type { ProviderRequestEvent } from \"./provider-request-types.ts\";\n\n/** Pin policy before any lifecycle subscriber can alter caller-owned configuration. */\nexport async function pinProviderConfig(\n\tconfig: AgentLoopConfig,\n\temit: (event: AgentEvent) => Promise<void> | void,\n): Promise<AgentLoopConfig> {\n\tif (config.modelContract === undefined) return config;\n\ttry {\n\t\treturn { ...config, modelContract: snapshotModelContract(config.modelContract) };\n\t} catch (error) {\n\t\tif (error instanceof ModelContractViolation) {\n\t\t\tawait emit({ type: \"provider_denied\", requestId: crypto.randomUUID(), deniedReason: \"contract-violation\" });\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/** Restrict logical dispatch and attach the supported final-payload checks. */\nexport function applyModelContract(\n\tcontract: ModelContract,\n\tmodel: Model<Api>,\n\toptions: SimpleStreamOptions,\n): SimpleStreamOptions {\n\tconst maxTokens = options.maxTokens ?? Math.min(model.maxTokens, contract.maxOutputTokens);\n\tassertModelContract(contract, {\n\t\tmodel,\n\t\tprovider: model.provider,\n\t\tauthOrigin: model.provider,\n\t\tthinking: options.reasoning !== undefined,\n\t\tthinkingLevel: options.reasoning ?? \"off\",\n\t\tmaxOutputTokens: maxTokens,\n\t});\n\treturn {\n\t\t...options,\n\t\tmaxTokens,\n\t\tonPayload: createContractPayloadHook(model, maxTokens, options.onPayload),\n\t};\n}\n\ninterface ProviderRuntime {\n\treadonly signal?: AbortSignal;\n\treadonly emit: (event: AgentEvent) => Promise<void> | void;\n\treadonly streamFn?: StreamFn;\n\treadonly consume: (stream: AssistantMessageEventStream) => Promise<AssistantMessage>;\n}\n\n/** Emit only bounded dispatch metadata; never payloads, headers, keys, or raw failures. */\nexport async function requestAssistantResponse(\n\tcontext: Context,\n\tconfig: AgentLoopConfig,\n\truntime: ProviderRuntime,\n): Promise<AssistantMessage> {\n\tconst { signal, emit, consume } = runtime;\n\tconst needsVisionRoute = context.messages.some(\n\t\t(message) =>\n\t\t\t(!config.modelContract || message.role !== \"toolResult\") &&\n\t\t\tArray.isArray(message.content) &&\n\t\t\tmessage.content.some((part) => part.type === \"image\"),\n\t);\n\tconst model =\n\t\tneedsVisionRoute && !(config.model.input ?? []).includes(\"image\")\n\t\t\t? getVisionRouteModel(config.model)\n\t\t\t: config.model;\n\tconst projection = config.modelContract\n\t\t? projectToolImagesForModel(context, model)\n\t\t: { context, omittedToolImages: 0 };\n\tconst changedProvider = model.provider !== config.model.provider;\n\tconst requestId = crypto.randomUUID();\n\tconst { modelContract, ...configured } = config;\n\tlet options: SimpleStreamOptions = configured;\n\ttry {\n\t\tif (signal?.aborted) {\n\t\t\tif (modelContract) await emit({ type: \"provider_denied\", requestId, deniedReason: \"aborted\" });\n\t\t\tsignal.throwIfAborted();\n\t\t}\n\t\tif (modelContract) options = applyModelContract(modelContract, model, configured);\n\t} catch (error) {\n\t\tif (error instanceof ModelContractViolation) {\n\t\t\tawait emit({ type: \"provider_denied\", requestId, deniedReason: \"contract-violation\" });\n\t\t}\n\t\tthrow error;\n\t}\n\tconst key = await config.getApiKey?.(model.provider);\n\tif (signal?.aborted) {\n\t\tif (modelContract) await emit({ type: \"provider_denied\", requestId, deniedReason: \"aborted\" });\n\t\tsignal.throwIfAborted();\n\t}\n\tconst requestOptions: SimpleStreamOptions = {\n\t\t...options,\n\t\tapiKey: key || (changedProvider ? undefined : options.apiKey),\n\t\theaders: changedProvider ? undefined : options.headers,\n\t\tsignal,\n\t};\n\tlet outcome: \"completed\" | \"error\" | \"aborted\" = \"error\";\n\ttry {\n\t\tif (modelContract) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"provider_request\",\n\t\t\t\trequestId,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tmaxOutputTokens: requestOptions.maxTokens ?? modelContract.maxOutputTokens,\n\t\t\t\t...(projection.omittedToolImages > 0 ? { omittedToolImages: projection.omittedToolImages } : {}),\n\t\t\t\tboundary: \"stream-dispatch\",\n\t\t\t});\n\t\t}\n\t\tsignal?.throwIfAborted();\n\t\tconst response = await (runtime.streamFn ?? streamSimple)(model, projection.context, requestOptions);\n\t\tconst message = await consume(response);\n\t\toutcome = message.stopReason === \"aborted\" ? \"aborted\" : message.stopReason === \"error\" ? \"error\" : \"completed\";\n\t\treturn message;\n\t} finally {\n\t\tif (modelContract) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"provider_request_end\",\n\t\t\t\trequestId,\n\t\t\t\toutcome: signal?.aborted ? \"aborted\" : outcome,\n\t\t\t\tboundary: \"stream-dispatch\",\n\t\t\t});\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { ModelContract, ModelContractViolationCode, RouteRequest } from "./provider-request-types.ts";
2
+ export type { ModelContract, ModelContractViolationCode, RouteRequest } from "./provider-request-types.ts";
3
+ export declare class ModelContractViolation extends Error {
4
+ readonly code: ModelContractViolationCode;
5
+ constructor(code: ModelContractViolationCode);
6
+ }
7
+ /** Compatibility validator: an omitted limit is not evidence of an effective cap. */
8
+ export declare function assertModelContract(contract: ModelContract, request: RouteRequest): void;
9
+ /** Copy only policy fields, never full model metadata or credential-bearing headers. */
10
+ export declare function snapshotModelContract(contract: unknown): ModelContract;
11
+ //# sourceMappingURL=run-model-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-model-contract.d.ts","sourceRoot":"","sources":["../src/run-model-contract.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,aAAa,EACb,0BAA0B,EAE1B,YAAY,EACZ,MAAM,6BAA6B,CAAC;AAErC,YAAY,EAAE,aAAa,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAiB3G,qBAAa,sBAAuB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAE1C,YAAY,IAAI,EAAE,0BAA0B,EAK3C;CACD;AA4DD,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAmCxF;AAED,wFAAwF;AACxF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,aAAa,CAUtE","sourcesContent":["import { createImmutableSnapshot } from \"./plain-data.ts\";\nimport type {\n\tModelContract,\n\tModelContractViolationCode,\n\tModelIdentity,\n\tRouteRequest,\n} from \"./provider-request-types.ts\";\n\nexport type { ModelContract, ModelContractViolationCode, RouteRequest } from \"./provider-request-types.ts\";\n\nconst MESSAGES: Record<ModelContractViolationCode, string> = {\n\t\"invalid-contract\": \"Invalid model contract shape\",\n\t\"invalid-contract-limit\": \"contract maxOutputTokens must be a positive safe integer\",\n\t\"invalid-request\": \"Invalid model contract request shape\",\n\t\"invalid-request-limit\": \"request maxOutputTokens must be a positive safe integer\",\n\t\"model-not-allowed\": \"Model is not allowed by the model contract\",\n\t\"provider-not-allowed\": \"Provider is not allowed by the model contract\",\n\t\"provider-mismatch\": \"Request provider does not match model provider\",\n\t\"auth-origin-not-allowed\": \"Credential resolver origin is not allowed by the model contract\",\n\t\"thinking-not-allowed\": \"Thinking is not allowed by the model contract\",\n\t\"thinking-level-mismatch\": \"Thinking level does not match the model contract\",\n\t\"output-limit-exceeded\": \"Request output limit exceeds the model contract\",\n};\nconst LEVELS: readonly string[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\", \"ultra\"];\n\nexport class ModelContractViolation extends Error {\n\treadonly code: ModelContractViolationCode;\n\n\tconstructor(code: ModelContractViolationCode) {\n\t\tconst safeCode = typeof code === \"string\" && Object.hasOwn(MESSAGES, code) ? code : \"invalid-contract\";\n\t\tsuper(MESSAGES[safeCode]);\n\t\tthis.name = \"ModelContractViolation\";\n\t\tthis.code = safeCode;\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isIdentifier(value: unknown): value is string {\n\treturn (\n\t\ttypeof value === \"string\" &&\n\t\tvalue.length > 0 &&\n\t\tvalue.length <= 256 &&\n\t\tvalue.trim() === value &&\n\t\t!/[\\u0000-\\u001f\\u007f]/u.test(value)\n\t);\n}\n\nfunction isIdentity(value: unknown): value is ModelIdentity {\n\treturn isRecord(value) && isIdentifier(value.provider) && isIdentifier(value.id);\n}\n\nfunction isBoundedList<T>(value: unknown, guard: (entry: unknown) => entry is T): value is readonly T[] {\n\tif (!Array.isArray(value) || value.length < 1 || value.length > 64) return false;\n\t// Array.every skips holes; a sparse allowlist must not silently pass validation.\n\tfor (const entry of value) if (!guard(entry)) return false;\n\treturn true;\n}\n\nfunction validateContract(contract: unknown): asserts contract is ModelContract {\n\tif (!isRecord(contract)) throw new ModelContractViolation(\"invalid-contract\");\n\tif (\n\t\ttypeof contract.maxOutputTokens !== \"number\" ||\n\t\t!Number.isSafeInteger(contract.maxOutputTokens) ||\n\t\tcontract.maxOutputTokens <= 0\n\t) {\n\t\tthrow new ModelContractViolation(\"invalid-contract-limit\");\n\t}\n\tif (\n\t\t!isBoundedList(contract.allowedModels, isIdentity) ||\n\t\t!isBoundedList(contract.allowedProviders, isIdentifier) ||\n\t\t!isBoundedList(contract.allowedAuthOrigins, isIdentifier) ||\n\t\ttypeof contract.thinking !== \"boolean\" ||\n\t\tObject.keys(contract).some(\n\t\t\t(key) =>\n\t\t\t\t![\n\t\t\t\t\t\"allowedModels\",\n\t\t\t\t\t\"allowedProviders\",\n\t\t\t\t\t\"allowedAuthOrigins\",\n\t\t\t\t\t\"thinking\",\n\t\t\t\t\t\"maxOutputTokens\",\n\t\t\t\t\t\"thinkingLevel\",\n\t\t\t\t].includes(key),\n\t\t) ||\n\t\t(contract.thinkingLevel !== undefined &&\n\t\t\t(typeof contract.thinkingLevel !== \"string\" || !LEVELS.includes(contract.thinkingLevel))) ||\n\t\t(contract.thinkingLevel !== undefined && contract.thinkingLevel !== \"off\" && !contract.thinking)\n\t) {\n\t\tthrow new ModelContractViolation(\"invalid-contract\");\n\t}\n}\n\n/** Compatibility validator: an omitted limit is not evidence of an effective cap. */\nexport function assertModelContract(contract: ModelContract, request: RouteRequest): void {\n\tvalidateContract(contract);\n\tif (\n\t\t!isRecord(request) ||\n\t\t!isIdentity(request.model) ||\n\t\t!isIdentifier(request.provider) ||\n\t\ttypeof request.thinking !== \"boolean\" ||\n\t\t(request.authOrigin !== undefined && !isIdentifier(request.authOrigin)) ||\n\t\t(request.thinkingLevel !== undefined &&\n\t\t\t(!LEVELS.includes(request.thinkingLevel) || (request.thinkingLevel !== \"off\") !== request.thinking))\n\t) {\n\t\tthrow new ModelContractViolation(\"invalid-request\");\n\t}\n\tif (\n\t\trequest.maxOutputTokens !== undefined &&\n\t\t(!Number.isSafeInteger(request.maxOutputTokens) || request.maxOutputTokens <= 0)\n\t) {\n\t\tthrow new ModelContractViolation(\"invalid-request-limit\");\n\t}\n\tif (request.provider !== request.model.provider) throw new ModelContractViolation(\"provider-mismatch\");\n\tif (!contract.allowedProviders.includes(request.provider)) throw new ModelContractViolation(\"provider-not-allowed\");\n\tif (!contract.allowedModels.some((entry) => entry.provider === request.provider && entry.id === request.model.id)) {\n\t\tthrow new ModelContractViolation(\"model-not-allowed\");\n\t}\n\tif (!contract.allowedAuthOrigins.includes(request.authOrigin ?? request.provider)) {\n\t\tthrow new ModelContractViolation(\"auth-origin-not-allowed\");\n\t}\n\tif (request.thinking && !contract.thinking) throw new ModelContractViolation(\"thinking-not-allowed\");\n\tconst effectiveLevel = request.thinkingLevel ?? \"off\";\n\tif (contract.thinkingLevel !== undefined && contract.thinkingLevel !== effectiveLevel) {\n\t\tthrow new ModelContractViolation(\"thinking-level-mismatch\");\n\t}\n\tif (request.maxOutputTokens !== undefined && request.maxOutputTokens > contract.maxOutputTokens) {\n\t\tthrow new ModelContractViolation(\"output-limit-exceeded\");\n\t}\n}\n\n/** Copy only policy fields, never full model metadata or credential-bearing headers. */\nexport function snapshotModelContract(contract: unknown): ModelContract {\n\tvalidateContract(contract);\n\treturn createImmutableSnapshot({\n\t\tallowedModels: contract.allowedModels.map(({ provider, id }) => ({ provider, id })),\n\t\tallowedProviders: [...contract.allowedProviders],\n\t\tallowedAuthOrigins: [...contract.allowedAuthOrigins],\n\t\tthinking: contract.thinking,\n\t\tmaxOutputTokens: contract.maxOutputTokens,\n\t\t...(contract.thinkingLevel === undefined ? {} : { thinkingLevel: contract.thinkingLevel }),\n\t});\n}\n"]}
@@ -0,0 +1,121 @@
1
+ import { createImmutableSnapshot } from "./plain-data.js";
2
+ const MESSAGES = {
3
+ "invalid-contract": "Invalid model contract shape",
4
+ "invalid-contract-limit": "contract maxOutputTokens must be a positive safe integer",
5
+ "invalid-request": "Invalid model contract request shape",
6
+ "invalid-request-limit": "request maxOutputTokens must be a positive safe integer",
7
+ "model-not-allowed": "Model is not allowed by the model contract",
8
+ "provider-not-allowed": "Provider is not allowed by the model contract",
9
+ "provider-mismatch": "Request provider does not match model provider",
10
+ "auth-origin-not-allowed": "Credential resolver origin is not allowed by the model contract",
11
+ "thinking-not-allowed": "Thinking is not allowed by the model contract",
12
+ "thinking-level-mismatch": "Thinking level does not match the model contract",
13
+ "output-limit-exceeded": "Request output limit exceeds the model contract",
14
+ };
15
+ const LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
16
+ export class ModelContractViolation extends Error {
17
+ code;
18
+ constructor(code) {
19
+ const safeCode = typeof code === "string" && Object.hasOwn(MESSAGES, code) ? code : "invalid-contract";
20
+ super(MESSAGES[safeCode]);
21
+ this.name = "ModelContractViolation";
22
+ this.code = safeCode;
23
+ }
24
+ }
25
+ function isRecord(value) {
26
+ return typeof value === "object" && value !== null && !Array.isArray(value);
27
+ }
28
+ function isIdentifier(value) {
29
+ return (typeof value === "string" &&
30
+ value.length > 0 &&
31
+ value.length <= 256 &&
32
+ value.trim() === value &&
33
+ !/[\u0000-\u001f\u007f]/u.test(value));
34
+ }
35
+ function isIdentity(value) {
36
+ return isRecord(value) && isIdentifier(value.provider) && isIdentifier(value.id);
37
+ }
38
+ function isBoundedList(value, guard) {
39
+ if (!Array.isArray(value) || value.length < 1 || value.length > 64)
40
+ return false;
41
+ // Array.every skips holes; a sparse allowlist must not silently pass validation.
42
+ for (const entry of value)
43
+ if (!guard(entry))
44
+ return false;
45
+ return true;
46
+ }
47
+ function validateContract(contract) {
48
+ if (!isRecord(contract))
49
+ throw new ModelContractViolation("invalid-contract");
50
+ if (typeof contract.maxOutputTokens !== "number" ||
51
+ !Number.isSafeInteger(contract.maxOutputTokens) ||
52
+ contract.maxOutputTokens <= 0) {
53
+ throw new ModelContractViolation("invalid-contract-limit");
54
+ }
55
+ if (!isBoundedList(contract.allowedModels, isIdentity) ||
56
+ !isBoundedList(contract.allowedProviders, isIdentifier) ||
57
+ !isBoundedList(contract.allowedAuthOrigins, isIdentifier) ||
58
+ typeof contract.thinking !== "boolean" ||
59
+ Object.keys(contract).some((key) => ![
60
+ "allowedModels",
61
+ "allowedProviders",
62
+ "allowedAuthOrigins",
63
+ "thinking",
64
+ "maxOutputTokens",
65
+ "thinkingLevel",
66
+ ].includes(key)) ||
67
+ (contract.thinkingLevel !== undefined &&
68
+ (typeof contract.thinkingLevel !== "string" || !LEVELS.includes(contract.thinkingLevel))) ||
69
+ (contract.thinkingLevel !== undefined && contract.thinkingLevel !== "off" && !contract.thinking)) {
70
+ throw new ModelContractViolation("invalid-contract");
71
+ }
72
+ }
73
+ /** Compatibility validator: an omitted limit is not evidence of an effective cap. */
74
+ export function assertModelContract(contract, request) {
75
+ validateContract(contract);
76
+ if (!isRecord(request) ||
77
+ !isIdentity(request.model) ||
78
+ !isIdentifier(request.provider) ||
79
+ typeof request.thinking !== "boolean" ||
80
+ (request.authOrigin !== undefined && !isIdentifier(request.authOrigin)) ||
81
+ (request.thinkingLevel !== undefined &&
82
+ (!LEVELS.includes(request.thinkingLevel) || (request.thinkingLevel !== "off") !== request.thinking))) {
83
+ throw new ModelContractViolation("invalid-request");
84
+ }
85
+ if (request.maxOutputTokens !== undefined &&
86
+ (!Number.isSafeInteger(request.maxOutputTokens) || request.maxOutputTokens <= 0)) {
87
+ throw new ModelContractViolation("invalid-request-limit");
88
+ }
89
+ if (request.provider !== request.model.provider)
90
+ throw new ModelContractViolation("provider-mismatch");
91
+ if (!contract.allowedProviders.includes(request.provider))
92
+ throw new ModelContractViolation("provider-not-allowed");
93
+ if (!contract.allowedModels.some((entry) => entry.provider === request.provider && entry.id === request.model.id)) {
94
+ throw new ModelContractViolation("model-not-allowed");
95
+ }
96
+ if (!contract.allowedAuthOrigins.includes(request.authOrigin ?? request.provider)) {
97
+ throw new ModelContractViolation("auth-origin-not-allowed");
98
+ }
99
+ if (request.thinking && !contract.thinking)
100
+ throw new ModelContractViolation("thinking-not-allowed");
101
+ const effectiveLevel = request.thinkingLevel ?? "off";
102
+ if (contract.thinkingLevel !== undefined && contract.thinkingLevel !== effectiveLevel) {
103
+ throw new ModelContractViolation("thinking-level-mismatch");
104
+ }
105
+ if (request.maxOutputTokens !== undefined && request.maxOutputTokens > contract.maxOutputTokens) {
106
+ throw new ModelContractViolation("output-limit-exceeded");
107
+ }
108
+ }
109
+ /** Copy only policy fields, never full model metadata or credential-bearing headers. */
110
+ export function snapshotModelContract(contract) {
111
+ validateContract(contract);
112
+ return createImmutableSnapshot({
113
+ allowedModels: contract.allowedModels.map(({ provider, id }) => ({ provider, id })),
114
+ allowedProviders: [...contract.allowedProviders],
115
+ allowedAuthOrigins: [...contract.allowedAuthOrigins],
116
+ thinking: contract.thinking,
117
+ maxOutputTokens: contract.maxOutputTokens,
118
+ ...(contract.thinkingLevel === undefined ? {} : { thinkingLevel: contract.thinkingLevel }),
119
+ });
120
+ }
121
+ //# sourceMappingURL=run-model-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-model-contract.js","sourceRoot":"","sources":["../src/run-model-contract.ts"],"names":[],"mappings":"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","sourcesContent":["import { createImmutableSnapshot } from \"./plain-data.ts\";\nimport type {\n\tModelContract,\n\tModelContractViolationCode,\n\tModelIdentity,\n\tRouteRequest,\n} from \"./provider-request-types.ts\";\n\nexport type { ModelContract, ModelContractViolationCode, RouteRequest } from \"./provider-request-types.ts\";\n\nconst MESSAGES: Record<ModelContractViolationCode, string> = {\n\t\"invalid-contract\": \"Invalid model contract shape\",\n\t\"invalid-contract-limit\": \"contract maxOutputTokens must be a positive safe integer\",\n\t\"invalid-request\": \"Invalid model contract request shape\",\n\t\"invalid-request-limit\": \"request maxOutputTokens must be a positive safe integer\",\n\t\"model-not-allowed\": \"Model is not allowed by the model contract\",\n\t\"provider-not-allowed\": \"Provider is not allowed by the model contract\",\n\t\"provider-mismatch\": \"Request provider does not match model provider\",\n\t\"auth-origin-not-allowed\": \"Credential resolver origin is not allowed by the model contract\",\n\t\"thinking-not-allowed\": \"Thinking is not allowed by the model contract\",\n\t\"thinking-level-mismatch\": \"Thinking level does not match the model contract\",\n\t\"output-limit-exceeded\": \"Request output limit exceeds the model contract\",\n};\nconst LEVELS: readonly string[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\", \"ultra\"];\n\nexport class ModelContractViolation extends Error {\n\treadonly code: ModelContractViolationCode;\n\n\tconstructor(code: ModelContractViolationCode) {\n\t\tconst safeCode = typeof code === \"string\" && Object.hasOwn(MESSAGES, code) ? code : \"invalid-contract\";\n\t\tsuper(MESSAGES[safeCode]);\n\t\tthis.name = \"ModelContractViolation\";\n\t\tthis.code = safeCode;\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isIdentifier(value: unknown): value is string {\n\treturn (\n\t\ttypeof value === \"string\" &&\n\t\tvalue.length > 0 &&\n\t\tvalue.length <= 256 &&\n\t\tvalue.trim() === value &&\n\t\t!/[\\u0000-\\u001f\\u007f]/u.test(value)\n\t);\n}\n\nfunction isIdentity(value: unknown): value is ModelIdentity {\n\treturn isRecord(value) && isIdentifier(value.provider) && isIdentifier(value.id);\n}\n\nfunction isBoundedList<T>(value: unknown, guard: (entry: unknown) => entry is T): value is readonly T[] {\n\tif (!Array.isArray(value) || value.length < 1 || value.length > 64) return false;\n\t// Array.every skips holes; a sparse allowlist must not silently pass validation.\n\tfor (const entry of value) if (!guard(entry)) return false;\n\treturn true;\n}\n\nfunction validateContract(contract: unknown): asserts contract is ModelContract {\n\tif (!isRecord(contract)) throw new ModelContractViolation(\"invalid-contract\");\n\tif (\n\t\ttypeof contract.maxOutputTokens !== \"number\" ||\n\t\t!Number.isSafeInteger(contract.maxOutputTokens) ||\n\t\tcontract.maxOutputTokens <= 0\n\t) {\n\t\tthrow new ModelContractViolation(\"invalid-contract-limit\");\n\t}\n\tif (\n\t\t!isBoundedList(contract.allowedModels, isIdentity) ||\n\t\t!isBoundedList(contract.allowedProviders, isIdentifier) ||\n\t\t!isBoundedList(contract.allowedAuthOrigins, isIdentifier) ||\n\t\ttypeof contract.thinking !== \"boolean\" ||\n\t\tObject.keys(contract).some(\n\t\t\t(key) =>\n\t\t\t\t![\n\t\t\t\t\t\"allowedModels\",\n\t\t\t\t\t\"allowedProviders\",\n\t\t\t\t\t\"allowedAuthOrigins\",\n\t\t\t\t\t\"thinking\",\n\t\t\t\t\t\"maxOutputTokens\",\n\t\t\t\t\t\"thinkingLevel\",\n\t\t\t\t].includes(key),\n\t\t) ||\n\t\t(contract.thinkingLevel !== undefined &&\n\t\t\t(typeof contract.thinkingLevel !== \"string\" || !LEVELS.includes(contract.thinkingLevel))) ||\n\t\t(contract.thinkingLevel !== undefined && contract.thinkingLevel !== \"off\" && !contract.thinking)\n\t) {\n\t\tthrow new ModelContractViolation(\"invalid-contract\");\n\t}\n}\n\n/** Compatibility validator: an omitted limit is not evidence of an effective cap. */\nexport function assertModelContract(contract: ModelContract, request: RouteRequest): void {\n\tvalidateContract(contract);\n\tif (\n\t\t!isRecord(request) ||\n\t\t!isIdentity(request.model) ||\n\t\t!isIdentifier(request.provider) ||\n\t\ttypeof request.thinking !== \"boolean\" ||\n\t\t(request.authOrigin !== undefined && !isIdentifier(request.authOrigin)) ||\n\t\t(request.thinkingLevel !== undefined &&\n\t\t\t(!LEVELS.includes(request.thinkingLevel) || (request.thinkingLevel !== \"off\") !== request.thinking))\n\t) {\n\t\tthrow new ModelContractViolation(\"invalid-request\");\n\t}\n\tif (\n\t\trequest.maxOutputTokens !== undefined &&\n\t\t(!Number.isSafeInteger(request.maxOutputTokens) || request.maxOutputTokens <= 0)\n\t) {\n\t\tthrow new ModelContractViolation(\"invalid-request-limit\");\n\t}\n\tif (request.provider !== request.model.provider) throw new ModelContractViolation(\"provider-mismatch\");\n\tif (!contract.allowedProviders.includes(request.provider)) throw new ModelContractViolation(\"provider-not-allowed\");\n\tif (!contract.allowedModels.some((entry) => entry.provider === request.provider && entry.id === request.model.id)) {\n\t\tthrow new ModelContractViolation(\"model-not-allowed\");\n\t}\n\tif (!contract.allowedAuthOrigins.includes(request.authOrigin ?? request.provider)) {\n\t\tthrow new ModelContractViolation(\"auth-origin-not-allowed\");\n\t}\n\tif (request.thinking && !contract.thinking) throw new ModelContractViolation(\"thinking-not-allowed\");\n\tconst effectiveLevel = request.thinkingLevel ?? \"off\";\n\tif (contract.thinkingLevel !== undefined && contract.thinkingLevel !== effectiveLevel) {\n\t\tthrow new ModelContractViolation(\"thinking-level-mismatch\");\n\t}\n\tif (request.maxOutputTokens !== undefined && request.maxOutputTokens > contract.maxOutputTokens) {\n\t\tthrow new ModelContractViolation(\"output-limit-exceeded\");\n\t}\n}\n\n/** Copy only policy fields, never full model metadata or credential-bearing headers. */\nexport function snapshotModelContract(contract: unknown): ModelContract {\n\tvalidateContract(contract);\n\treturn createImmutableSnapshot({\n\t\tallowedModels: contract.allowedModels.map(({ provider, id }) => ({ provider, id })),\n\t\tallowedProviders: [...contract.allowedProviders],\n\t\tallowedAuthOrigins: [...contract.allowedAuthOrigins],\n\t\tthinking: contract.thinking,\n\t\tmaxOutputTokens: contract.maxOutputTokens,\n\t\t...(contract.thinkingLevel === undefined ? {} : { thinkingLevel: contract.thinkingLevel }),\n\t});\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tool-execution-boundary.d.ts","sourceRoot":"","sources":["../src/tool-execution-boundary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,KAAK,EACX,YAAY,EACZ,eAAe,EACf,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEvG,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExF,+EAA+E;AAC/E,wBAAsB,cAAc,CAAC,CAAC,EACrC,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC3B,MAAM,EAAE,WAAW,GAAG,SAAS,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAiB9B;AAED,MAAM,WAAW,uBAAuB;IACvC,GAAG,EAAE,kBAAkB,CAAC;CACxB;AAwBD,uEAAuE;AACvE,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAAC,uBAAuB,CAAC,CAarH;AAED,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,gBAAgB,EAAE,OAAO,GAAG,eAAe,CAAC,uBAAuB,CAAC,CAY3G;AAED,MAAM,WAAW,uBAAuB;IACvC,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,CAAC,EAAE,sBAAsB,CAAC;IAC7C,oBAAoB,EAAE,MAAM,OAAO,CAAC;IACpC,cAAc,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,UAAU,uBAAuB;IAChC,cAAc,EAAE,YAAY,CAAC;IAC7B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,EAAE;QAAE,QAAQ,EAAE,aAAa,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,QAAQ,EAAE,uBAAuB,CAAC;IAClC,aAAa,EAAE,eAAe,CAAC,eAAe,CAAC,CAAC;IAChD,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,CAE/E;AAED,6FAA6F;AAC7F,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CA8FlH;AAQD,gFAAgF;AAChF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAO/F","sourcesContent":["import type { AssistantMessage } from \"omk-ai\";\nimport { createImmutableSnapshot } from \"./plain-data.ts\";\nimport type {\n\tAgentContext,\n\tAgentLoopConfig,\n\tAgentToolCall,\n\tAgentToolResult,\n\tToolResultEnvelope,\n\tToolTimeoutDisposition,\n} from \"./types.ts\";\nimport { createToolResultEnvelope, isToolResultEnvelope } from \"./types.ts\";\n\nexport { createImmutableJsonSnapshot, createImmutableSnapshot, parseJsonValue } from \"./plain-data.ts\";\n\nexport type AbortBoundResult<T> = { kind: \"completed\"; value: T } | { kind: \"aborted\" };\n\n/** Race one async extension boundary against the parent run's abort signal. */\nexport async function awaitWithAbort<T>(\n\tstart: () => Promise<T> | T,\n\tsignal: AbortSignal | undefined,\n): Promise<AbortBoundResult<T>> {\n\tif (signal?.aborted) return { kind: \"aborted\" };\n\tif (signal === undefined) return { kind: \"completed\", value: await start() };\n\n\tlet notifyAbort: (() => void) | undefined;\n\tconst aborted = new Promise<AbortBoundResult<T>>((resolve) => {\n\t\tnotifyAbort = () => resolve({ kind: \"aborted\" });\n\t\tsignal.addEventListener(\"abort\", notifyAbort, { once: true });\n\t});\n\tif (signal.aborted) notifyAbort?.();\n\n\ttry {\n\t\tconst operation = Promise.resolve(start()).then((value): AbortBoundResult<T> => ({ kind: \"completed\", value }));\n\t\treturn await Promise.race([operation, aborted]);\n\t} finally {\n\t\tif (notifyAbort !== undefined) signal.removeEventListener(\"abort\", notifyAbort);\n\t}\n}\n\nexport interface ToolDispositionEnvelope {\n\tomk: ToolResultEnvelope;\n}\n\nfunction createValidatedToolResultSnapshot(result: AgentToolResult<unknown>): AgentToolResult<unknown> {\n\tconst snapshot = createImmutableSnapshot(result);\n\tif (!Array.isArray(snapshot.content)) throw new TypeError(\"Tool result content must be an array\");\n\tfor (const block of snapshot.content) {\n\t\tif (typeof block !== \"object\" || block === null) throw new TypeError(\"Invalid tool result content block\");\n\t\tconst type = Reflect.get(block, \"type\");\n\t\tif (type === \"text\" && typeof Reflect.get(block, \"text\") === \"string\") continue;\n\t\tif (\n\t\t\ttype === \"image\" &&\n\t\t\ttypeof Reflect.get(block, \"data\") === \"string\" &&\n\t\t\ttypeof Reflect.get(block, \"mimeType\") === \"string\"\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new TypeError(\"Invalid tool result content block\");\n\t}\n\tif (snapshot.terminate !== undefined && typeof snapshot.terminate !== \"boolean\") {\n\t\tthrow new TypeError(\"Tool result terminate must be a boolean\");\n\t}\n\treturn snapshot;\n}\n\n/** Build the immutable terminal committed when a tool timeout wins. */\nexport function createTimeoutToolResult(toolName: string, timeoutMs: number): AgentToolResult<ToolDispositionEnvelope> {\n\treturn createImmutableSnapshot({\n\t\tcontent: [{ type: \"text\", text: `Tool \"${toolName}\" timed out after ${timeoutMs}ms and was terminated.` }],\n\t\tdetails: {\n\t\t\tomk: createToolResultEnvelope({\n\t\t\t\tsynthetic: true,\n\t\t\t\tdisposition: \"timeout\",\n\t\t\t\treason: `Tool \"${toolName}\" timed out after ${timeoutMs}ms`,\n\t\t\t\ttimeoutMs,\n\t\t\t\texecutionStarted: true,\n\t\t\t}),\n\t\t},\n\t});\n}\n\n/** Build the immutable terminal committed when parent abort wins. */\nexport function createAbortedToolResult(executionStarted: boolean): AgentToolResult<ToolDispositionEnvelope> {\n\treturn createImmutableSnapshot({\n\t\tcontent: [{ type: \"text\", text: \"Operation aborted\" }],\n\t\tdetails: {\n\t\t\tomk: createToolResultEnvelope({\n\t\t\t\tsynthetic: true,\n\t\t\t\tdisposition: \"aborted\",\n\t\t\t\treason: \"Operation aborted\",\n\t\t\t\texecutionStarted,\n\t\t\t}),\n\t\t},\n\t});\n}\n\nexport interface ExecutedToolCallOutcome {\n\tresult: AgentToolResult<unknown>;\n\tisError: boolean;\n\texecutionStarted: boolean;\n\tterminalDisposition?: ToolTimeoutDisposition;\n\tisRealPromiseSettled: () => boolean;\n\tcommitTerminal: () => void;\n}\n\nexport interface FinalizedToolCallOutcome {\n\ttoolCall: AgentToolCall;\n\tresult: AgentToolResult<unknown>;\n\tisError: boolean;\n\tenvelope: ToolResultEnvelope;\n\tisRealPromiseSettled?: () => boolean;\n\tcommitTerminal?: () => void;\n}\n\ninterface FinalizeToolCallOptions {\n\tcurrentContext: AgentContext;\n\tassistantMessage: AssistantMessage;\n\tprepared: { toolCall: AgentToolCall; args: unknown; timeoutMs: number };\n\texecuted: ExecutedToolCallOutcome;\n\tafterToolCall: AgentLoopConfig[\"afterToolCall\"];\n\tsignal: AbortSignal | undefined;\n}\n\nexport function createErrorToolResult(message: string): AgentToolResult<unknown> {\n\treturn createImmutableSnapshot({ content: [{ type: \"text\", text: message }], details: {} });\n}\n\n/** Commit a real result or immutable timeout/abort result across the after hook boundary. */\nexport async function finalizeExecutedToolCall(options: FinalizeToolCallOptions): Promise<FinalizedToolCallOutcome> {\n\tconst { currentContext, assistantMessage, prepared, executed, afterToolCall, signal } = options;\n\tlet result = executed.result;\n\tlet isError = executed.isError;\n\tlet syntheticFailure = false;\n\tlet terminalDisposition = executed.terminalDisposition;\n\n\ttry {\n\t\tresult = createValidatedToolResultSnapshot(result);\n\t} catch (error) {\n\t\tresult = createErrorToolResult(`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`);\n\t\tisError = true;\n\t\tsyntheticFailure = true;\n\t}\n\n\tif (terminalDisposition === undefined && signal?.aborted) {\n\t\tterminalDisposition = \"aborted\";\n\t\tresult = createAbortedToolResult(executed.executionStarted);\n\t\tisError = true;\n\t}\n\tif (terminalDisposition === undefined && afterToolCall && !syntheticFailure) {\n\t\ttry {\n\t\t\tconst bounded = await awaitWithAbort(\n\t\t\t\t() =>\n\t\t\t\t\tafterToolCall(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tassistantMessage,\n\t\t\t\t\t\t\ttoolCall: prepared.toolCall,\n\t\t\t\t\t\t\targs: prepared.args,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\tisError,\n\t\t\t\t\t\t\tcontext: currentContext,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (bounded.kind === \"aborted\" || signal?.aborted) {\n\t\t\t\tterminalDisposition = \"aborted\";\n\t\t\t\tresult = createAbortedToolResult(executed.executionStarted);\n\t\t\t\tisError = true;\n\t\t\t} else if (bounded.value) {\n\t\t\t\tresult = {\n\t\t\t\t\tcontent: bounded.value.content ?? result.content,\n\t\t\t\t\tdetails: Object.hasOwn(bounded.value, \"details\") ? bounded.value.details : result.details,\n\t\t\t\t\tterminate: bounded.value.terminate ?? result.terminate,\n\t\t\t\t};\n\t\t\t\tisError = bounded.value.isError ?? isError;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tresult = createErrorToolResult(error instanceof Error ? error.message : String(error));\n\t\t\tisError = true;\n\t\t\tsyntheticFailure = true;\n\t\t}\n\t}\n\n\tif (terminalDisposition === undefined) {\n\t\ttry {\n\t\t\tresult = createValidatedToolResultSnapshot(result);\n\t\t} catch (error) {\n\t\t\tresult = createErrorToolResult(\n\t\t\t\t`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tisError = true;\n\t\t\tsyntheticFailure = true;\n\t\t}\n\t} else {\n\t\tisError = true;\n\t}\n\tconst envelope =\n\t\tterminalDisposition !== undefined\n\t\t\t? createToolResultEnvelope({\n\t\t\t\t\tdisposition: terminalDisposition,\n\t\t\t\t\tsynthetic: true,\n\t\t\t\t\texecutionStarted: executed.executionStarted,\n\t\t\t\t\treason:\n\t\t\t\t\t\tterminalDisposition === \"timeout\"\n\t\t\t\t\t\t\t? `Tool \"${prepared.toolCall.name}\" timed out after ${prepared.timeoutMs}ms`\n\t\t\t\t\t\t\t: \"Operation aborted\",\n\t\t\t\t\t...(terminalDisposition === \"timeout\" ? { timeoutMs: prepared.timeoutMs } : {}),\n\t\t\t\t})\n\t\t\t: createToolResultEnvelope({\n\t\t\t\t\tdisposition: isError ? \"failed\" : \"completed\",\n\t\t\t\t\tsynthetic: syntheticFailure,\n\t\t\t\t\texecutionStarted: executed.executionStarted,\n\t\t\t\t});\n\treturn {\n\t\ttoolCall: prepared.toolCall,\n\t\tresult,\n\t\tisError,\n\t\tenvelope,\n\t\tisRealPromiseSettled: executed.isRealPromiseSettled,\n\t\tcommitTerminal: executed.commitTerminal,\n\t};\n}\n\nfunction isPlainDetails(details: unknown): details is Record<string, unknown> {\n\tif (typeof details !== \"object\" || details === null || Array.isArray(details)) return false;\n\tconst prototype = Object.getPrototypeOf(details);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\n/** Preserve compatibility details while replacing any untrusted `omk` field. */\nexport function stampToolResultEnvelope(details: unknown, envelope: ToolResultEnvelope): unknown {\n\tif (!isToolResultEnvelope(envelope)) throw new TypeError(\"Refusing to persist an invalid tool-result/v2 envelope\");\n\tif (details === undefined) return { omk: envelope };\n\tif (!isPlainDetails(details)) return { originalDetails: details, omk: envelope };\n\tconst preserved = { ...details };\n\tdelete preserved.omk;\n\treturn { ...preserved, omk: envelope };\n}\n"]}
1
+ {"version":3,"file":"tool-execution-boundary.d.ts","sourceRoot":"","sources":["../src/tool-execution-boundary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,KAAK,EACX,YAAY,EACZ,eAAe,EACf,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEvG,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExF,+EAA+E;AAC/E,wBAAsB,cAAc,CAAC,CAAC,EACrC,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC3B,MAAM,EAAE,WAAW,GAAG,SAAS,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAiB9B;AAED,MAAM,WAAW,uBAAuB;IACvC,GAAG,EAAE,kBAAkB,CAAC;CACxB;AAwBD,uEAAuE;AACvE,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAAC,uBAAuB,CAAC,CAkBrH;AAED,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,gBAAgB,EAAE,OAAO,GAAG,eAAe,CAAC,uBAAuB,CAAC,CAY3G;AAED,MAAM,WAAW,uBAAuB;IACvC,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,CAAC,EAAE,sBAAsB,CAAC;IAC7C,oBAAoB,EAAE,MAAM,OAAO,CAAC;IACpC,cAAc,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,UAAU,uBAAuB;IAChC,cAAc,EAAE,YAAY,CAAC;IAC7B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,EAAE;QAAE,QAAQ,EAAE,aAAa,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,QAAQ,EAAE,uBAAuB,CAAC;IAClC,aAAa,EAAE,eAAe,CAAC,eAAe,CAAC,CAAC;IAChD,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,CAE/E;AAED,6FAA6F;AAC7F,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CA8FlH;AAQD,gFAAgF;AAChF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAO/F","sourcesContent":["import type { AssistantMessage } from \"omk-ai\";\nimport { createImmutableSnapshot } from \"./plain-data.ts\";\nimport type {\n\tAgentContext,\n\tAgentLoopConfig,\n\tAgentToolCall,\n\tAgentToolResult,\n\tToolResultEnvelope,\n\tToolTimeoutDisposition,\n} from \"./types.ts\";\nimport { createToolResultEnvelope, isToolResultEnvelope } from \"./types.ts\";\n\nexport { createImmutableJsonSnapshot, createImmutableSnapshot, parseJsonValue } from \"./plain-data.ts\";\n\nexport type AbortBoundResult<T> = { kind: \"completed\"; value: T } | { kind: \"aborted\" };\n\n/** Race one async extension boundary against the parent run's abort signal. */\nexport async function awaitWithAbort<T>(\n\tstart: () => Promise<T> | T,\n\tsignal: AbortSignal | undefined,\n): Promise<AbortBoundResult<T>> {\n\tif (signal?.aborted) return { kind: \"aborted\" };\n\tif (signal === undefined) return { kind: \"completed\", value: await start() };\n\n\tlet notifyAbort: (() => void) | undefined;\n\tconst aborted = new Promise<AbortBoundResult<T>>((resolve) => {\n\t\tnotifyAbort = () => resolve({ kind: \"aborted\" });\n\t\tsignal.addEventListener(\"abort\", notifyAbort, { once: true });\n\t});\n\tif (signal.aborted) notifyAbort?.();\n\n\ttry {\n\t\tconst operation = Promise.resolve(start()).then((value): AbortBoundResult<T> => ({ kind: \"completed\", value }));\n\t\treturn await Promise.race([operation, aborted]);\n\t} finally {\n\t\tif (notifyAbort !== undefined) signal.removeEventListener(\"abort\", notifyAbort);\n\t}\n}\n\nexport interface ToolDispositionEnvelope {\n\tomk: ToolResultEnvelope;\n}\n\nfunction createValidatedToolResultSnapshot(result: AgentToolResult<unknown>): AgentToolResult<unknown> {\n\tconst snapshot = createImmutableSnapshot(result);\n\tif (!Array.isArray(snapshot.content)) throw new TypeError(\"Tool result content must be an array\");\n\tfor (const block of snapshot.content) {\n\t\tif (typeof block !== \"object\" || block === null) throw new TypeError(\"Invalid tool result content block\");\n\t\tconst type = Reflect.get(block, \"type\");\n\t\tif (type === \"text\" && typeof Reflect.get(block, \"text\") === \"string\") continue;\n\t\tif (\n\t\t\ttype === \"image\" &&\n\t\t\ttypeof Reflect.get(block, \"data\") === \"string\" &&\n\t\t\ttypeof Reflect.get(block, \"mimeType\") === \"string\"\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new TypeError(\"Invalid tool result content block\");\n\t}\n\tif (snapshot.terminate !== undefined && typeof snapshot.terminate !== \"boolean\") {\n\t\tthrow new TypeError(\"Tool result terminate must be a boolean\");\n\t}\n\treturn snapshot;\n}\n\n/** Build the immutable terminal committed when a tool timeout wins. */\nexport function createTimeoutToolResult(toolName: string, timeoutMs: number): AgentToolResult<ToolDispositionEnvelope> {\n\treturn createImmutableSnapshot({\n\t\tcontent: [\n\t\t\t{\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: `Tool \"${toolName}\" timed out after ${timeoutMs}ms. Cancellation was requested; execution may still be running.`,\n\t\t\t},\n\t\t],\n\t\tdetails: {\n\t\t\tomk: createToolResultEnvelope({\n\t\t\t\tsynthetic: true,\n\t\t\t\tdisposition: \"timeout\",\n\t\t\t\treason: `Tool \"${toolName}\" timed out after ${timeoutMs}ms`,\n\t\t\t\ttimeoutMs,\n\t\t\t\texecutionStarted: true,\n\t\t\t}),\n\t\t},\n\t});\n}\n\n/** Build the immutable terminal committed when parent abort wins. */\nexport function createAbortedToolResult(executionStarted: boolean): AgentToolResult<ToolDispositionEnvelope> {\n\treturn createImmutableSnapshot({\n\t\tcontent: [{ type: \"text\", text: \"Operation aborted\" }],\n\t\tdetails: {\n\t\t\tomk: createToolResultEnvelope({\n\t\t\t\tsynthetic: true,\n\t\t\t\tdisposition: \"aborted\",\n\t\t\t\treason: \"Operation aborted\",\n\t\t\t\texecutionStarted,\n\t\t\t}),\n\t\t},\n\t});\n}\n\nexport interface ExecutedToolCallOutcome {\n\tresult: AgentToolResult<unknown>;\n\tisError: boolean;\n\texecutionStarted: boolean;\n\tterminalDisposition?: ToolTimeoutDisposition;\n\tisRealPromiseSettled: () => boolean;\n\tcommitTerminal: () => void;\n}\n\nexport interface FinalizedToolCallOutcome {\n\ttoolCall: AgentToolCall;\n\tresult: AgentToolResult<unknown>;\n\tisError: boolean;\n\tenvelope: ToolResultEnvelope;\n\tisRealPromiseSettled?: () => boolean;\n\tcommitTerminal?: () => void;\n}\n\ninterface FinalizeToolCallOptions {\n\tcurrentContext: AgentContext;\n\tassistantMessage: AssistantMessage;\n\tprepared: { toolCall: AgentToolCall; args: unknown; timeoutMs: number };\n\texecuted: ExecutedToolCallOutcome;\n\tafterToolCall: AgentLoopConfig[\"afterToolCall\"];\n\tsignal: AbortSignal | undefined;\n}\n\nexport function createErrorToolResult(message: string): AgentToolResult<unknown> {\n\treturn createImmutableSnapshot({ content: [{ type: \"text\", text: message }], details: {} });\n}\n\n/** Commit a real result or immutable timeout/abort result across the after hook boundary. */\nexport async function finalizeExecutedToolCall(options: FinalizeToolCallOptions): Promise<FinalizedToolCallOutcome> {\n\tconst { currentContext, assistantMessage, prepared, executed, afterToolCall, signal } = options;\n\tlet result = executed.result;\n\tlet isError = executed.isError;\n\tlet syntheticFailure = false;\n\tlet terminalDisposition = executed.terminalDisposition;\n\n\ttry {\n\t\tresult = createValidatedToolResultSnapshot(result);\n\t} catch (error) {\n\t\tresult = createErrorToolResult(`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`);\n\t\tisError = true;\n\t\tsyntheticFailure = true;\n\t}\n\n\tif (terminalDisposition === undefined && signal?.aborted) {\n\t\tterminalDisposition = \"aborted\";\n\t\tresult = createAbortedToolResult(executed.executionStarted);\n\t\tisError = true;\n\t}\n\tif (terminalDisposition === undefined && afterToolCall && !syntheticFailure) {\n\t\ttry {\n\t\t\tconst bounded = await awaitWithAbort(\n\t\t\t\t() =>\n\t\t\t\t\tafterToolCall(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tassistantMessage,\n\t\t\t\t\t\t\ttoolCall: prepared.toolCall,\n\t\t\t\t\t\t\targs: prepared.args,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\tisError,\n\t\t\t\t\t\t\tcontext: currentContext,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (bounded.kind === \"aborted\" || signal?.aborted) {\n\t\t\t\tterminalDisposition = \"aborted\";\n\t\t\t\tresult = createAbortedToolResult(executed.executionStarted);\n\t\t\t\tisError = true;\n\t\t\t} else if (bounded.value) {\n\t\t\t\tresult = {\n\t\t\t\t\tcontent: bounded.value.content ?? result.content,\n\t\t\t\t\tdetails: Object.hasOwn(bounded.value, \"details\") ? bounded.value.details : result.details,\n\t\t\t\t\tterminate: bounded.value.terminate ?? result.terminate,\n\t\t\t\t};\n\t\t\t\tisError = bounded.value.isError ?? isError;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tresult = createErrorToolResult(error instanceof Error ? error.message : String(error));\n\t\t\tisError = true;\n\t\t\tsyntheticFailure = true;\n\t\t}\n\t}\n\n\tif (terminalDisposition === undefined) {\n\t\ttry {\n\t\t\tresult = createValidatedToolResultSnapshot(result);\n\t\t} catch (error) {\n\t\t\tresult = createErrorToolResult(\n\t\t\t\t`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tisError = true;\n\t\t\tsyntheticFailure = true;\n\t\t}\n\t} else {\n\t\tisError = true;\n\t}\n\tconst envelope =\n\t\tterminalDisposition !== undefined\n\t\t\t? createToolResultEnvelope({\n\t\t\t\t\tdisposition: terminalDisposition,\n\t\t\t\t\tsynthetic: true,\n\t\t\t\t\texecutionStarted: executed.executionStarted,\n\t\t\t\t\treason:\n\t\t\t\t\t\tterminalDisposition === \"timeout\"\n\t\t\t\t\t\t\t? `Tool \"${prepared.toolCall.name}\" timed out after ${prepared.timeoutMs}ms`\n\t\t\t\t\t\t\t: \"Operation aborted\",\n\t\t\t\t\t...(terminalDisposition === \"timeout\" ? { timeoutMs: prepared.timeoutMs } : {}),\n\t\t\t\t})\n\t\t\t: createToolResultEnvelope({\n\t\t\t\t\tdisposition: isError ? \"failed\" : \"completed\",\n\t\t\t\t\tsynthetic: syntheticFailure,\n\t\t\t\t\texecutionStarted: executed.executionStarted,\n\t\t\t\t});\n\treturn {\n\t\ttoolCall: prepared.toolCall,\n\t\tresult,\n\t\tisError,\n\t\tenvelope,\n\t\tisRealPromiseSettled: executed.isRealPromiseSettled,\n\t\tcommitTerminal: executed.commitTerminal,\n\t};\n}\n\nfunction isPlainDetails(details: unknown): details is Record<string, unknown> {\n\tif (typeof details !== \"object\" || details === null || Array.isArray(details)) return false;\n\tconst prototype = Object.getPrototypeOf(details);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\n/** Preserve compatibility details while replacing any untrusted `omk` field. */\nexport function stampToolResultEnvelope(details: unknown, envelope: ToolResultEnvelope): unknown {\n\tif (!isToolResultEnvelope(envelope)) throw new TypeError(\"Refusing to persist an invalid tool-result/v2 envelope\");\n\tif (details === undefined) return { omk: envelope };\n\tif (!isPlainDetails(details)) return { originalDetails: details, omk: envelope };\n\tconst preserved = { ...details };\n\tdelete preserved.omk;\n\treturn { ...preserved, omk: envelope };\n}\n"]}
@@ -48,7 +48,12 @@ function createValidatedToolResultSnapshot(result) {
48
48
  /** Build the immutable terminal committed when a tool timeout wins. */
49
49
  export function createTimeoutToolResult(toolName, timeoutMs) {
50
50
  return createImmutableSnapshot({
51
- content: [{ type: "text", text: `Tool "${toolName}" timed out after ${timeoutMs}ms and was terminated.` }],
51
+ content: [
52
+ {
53
+ type: "text",
54
+ text: `Tool "${toolName}" timed out after ${timeoutMs}ms. Cancellation was requested; execution may still be running.`,
55
+ },
56
+ ],
52
57
  details: {
53
58
  omk: createToolResultEnvelope({
54
59
  synthetic: true,
@@ -1 +1 @@
1
- {"version":3,"file":"tool-execution-boundary.js","sourceRoot":"","sources":["../src/tool-execution-boundary.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"omk-ai\";\nimport { createImmutableSnapshot } from \"./plain-data.ts\";\nimport type {\n\tAgentContext,\n\tAgentLoopConfig,\n\tAgentToolCall,\n\tAgentToolResult,\n\tToolResultEnvelope,\n\tToolTimeoutDisposition,\n} from \"./types.ts\";\nimport { createToolResultEnvelope, isToolResultEnvelope } from \"./types.ts\";\n\nexport { createImmutableJsonSnapshot, createImmutableSnapshot, parseJsonValue } from \"./plain-data.ts\";\n\nexport type AbortBoundResult<T> = { kind: \"completed\"; value: T } | { kind: \"aborted\" };\n\n/** Race one async extension boundary against the parent run's abort signal. */\nexport async function awaitWithAbort<T>(\n\tstart: () => Promise<T> | T,\n\tsignal: AbortSignal | undefined,\n): Promise<AbortBoundResult<T>> {\n\tif (signal?.aborted) return { kind: \"aborted\" };\n\tif (signal === undefined) return { kind: \"completed\", value: await start() };\n\n\tlet notifyAbort: (() => void) | undefined;\n\tconst aborted = new Promise<AbortBoundResult<T>>((resolve) => {\n\t\tnotifyAbort = () => resolve({ kind: \"aborted\" });\n\t\tsignal.addEventListener(\"abort\", notifyAbort, { once: true });\n\t});\n\tif (signal.aborted) notifyAbort?.();\n\n\ttry {\n\t\tconst operation = Promise.resolve(start()).then((value): AbortBoundResult<T> => ({ kind: \"completed\", value }));\n\t\treturn await Promise.race([operation, aborted]);\n\t} finally {\n\t\tif (notifyAbort !== undefined) signal.removeEventListener(\"abort\", notifyAbort);\n\t}\n}\n\nexport interface ToolDispositionEnvelope {\n\tomk: ToolResultEnvelope;\n}\n\nfunction createValidatedToolResultSnapshot(result: AgentToolResult<unknown>): AgentToolResult<unknown> {\n\tconst snapshot = createImmutableSnapshot(result);\n\tif (!Array.isArray(snapshot.content)) throw new TypeError(\"Tool result content must be an array\");\n\tfor (const block of snapshot.content) {\n\t\tif (typeof block !== \"object\" || block === null) throw new TypeError(\"Invalid tool result content block\");\n\t\tconst type = Reflect.get(block, \"type\");\n\t\tif (type === \"text\" && typeof Reflect.get(block, \"text\") === \"string\") continue;\n\t\tif (\n\t\t\ttype === \"image\" &&\n\t\t\ttypeof Reflect.get(block, \"data\") === \"string\" &&\n\t\t\ttypeof Reflect.get(block, \"mimeType\") === \"string\"\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new TypeError(\"Invalid tool result content block\");\n\t}\n\tif (snapshot.terminate !== undefined && typeof snapshot.terminate !== \"boolean\") {\n\t\tthrow new TypeError(\"Tool result terminate must be a boolean\");\n\t}\n\treturn snapshot;\n}\n\n/** Build the immutable terminal committed when a tool timeout wins. */\nexport function createTimeoutToolResult(toolName: string, timeoutMs: number): AgentToolResult<ToolDispositionEnvelope> {\n\treturn createImmutableSnapshot({\n\t\tcontent: [{ type: \"text\", text: `Tool \"${toolName}\" timed out after ${timeoutMs}ms and was terminated.` }],\n\t\tdetails: {\n\t\t\tomk: createToolResultEnvelope({\n\t\t\t\tsynthetic: true,\n\t\t\t\tdisposition: \"timeout\",\n\t\t\t\treason: `Tool \"${toolName}\" timed out after ${timeoutMs}ms`,\n\t\t\t\ttimeoutMs,\n\t\t\t\texecutionStarted: true,\n\t\t\t}),\n\t\t},\n\t});\n}\n\n/** Build the immutable terminal committed when parent abort wins. */\nexport function createAbortedToolResult(executionStarted: boolean): AgentToolResult<ToolDispositionEnvelope> {\n\treturn createImmutableSnapshot({\n\t\tcontent: [{ type: \"text\", text: \"Operation aborted\" }],\n\t\tdetails: {\n\t\t\tomk: createToolResultEnvelope({\n\t\t\t\tsynthetic: true,\n\t\t\t\tdisposition: \"aborted\",\n\t\t\t\treason: \"Operation aborted\",\n\t\t\t\texecutionStarted,\n\t\t\t}),\n\t\t},\n\t});\n}\n\nexport interface ExecutedToolCallOutcome {\n\tresult: AgentToolResult<unknown>;\n\tisError: boolean;\n\texecutionStarted: boolean;\n\tterminalDisposition?: ToolTimeoutDisposition;\n\tisRealPromiseSettled: () => boolean;\n\tcommitTerminal: () => void;\n}\n\nexport interface FinalizedToolCallOutcome {\n\ttoolCall: AgentToolCall;\n\tresult: AgentToolResult<unknown>;\n\tisError: boolean;\n\tenvelope: ToolResultEnvelope;\n\tisRealPromiseSettled?: () => boolean;\n\tcommitTerminal?: () => void;\n}\n\ninterface FinalizeToolCallOptions {\n\tcurrentContext: AgentContext;\n\tassistantMessage: AssistantMessage;\n\tprepared: { toolCall: AgentToolCall; args: unknown; timeoutMs: number };\n\texecuted: ExecutedToolCallOutcome;\n\tafterToolCall: AgentLoopConfig[\"afterToolCall\"];\n\tsignal: AbortSignal | undefined;\n}\n\nexport function createErrorToolResult(message: string): AgentToolResult<unknown> {\n\treturn createImmutableSnapshot({ content: [{ type: \"text\", text: message }], details: {} });\n}\n\n/** Commit a real result or immutable timeout/abort result across the after hook boundary. */\nexport async function finalizeExecutedToolCall(options: FinalizeToolCallOptions): Promise<FinalizedToolCallOutcome> {\n\tconst { currentContext, assistantMessage, prepared, executed, afterToolCall, signal } = options;\n\tlet result = executed.result;\n\tlet isError = executed.isError;\n\tlet syntheticFailure = false;\n\tlet terminalDisposition = executed.terminalDisposition;\n\n\ttry {\n\t\tresult = createValidatedToolResultSnapshot(result);\n\t} catch (error) {\n\t\tresult = createErrorToolResult(`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`);\n\t\tisError = true;\n\t\tsyntheticFailure = true;\n\t}\n\n\tif (terminalDisposition === undefined && signal?.aborted) {\n\t\tterminalDisposition = \"aborted\";\n\t\tresult = createAbortedToolResult(executed.executionStarted);\n\t\tisError = true;\n\t}\n\tif (terminalDisposition === undefined && afterToolCall && !syntheticFailure) {\n\t\ttry {\n\t\t\tconst bounded = await awaitWithAbort(\n\t\t\t\t() =>\n\t\t\t\t\tafterToolCall(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tassistantMessage,\n\t\t\t\t\t\t\ttoolCall: prepared.toolCall,\n\t\t\t\t\t\t\targs: prepared.args,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\tisError,\n\t\t\t\t\t\t\tcontext: currentContext,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (bounded.kind === \"aborted\" || signal?.aborted) {\n\t\t\t\tterminalDisposition = \"aborted\";\n\t\t\t\tresult = createAbortedToolResult(executed.executionStarted);\n\t\t\t\tisError = true;\n\t\t\t} else if (bounded.value) {\n\t\t\t\tresult = {\n\t\t\t\t\tcontent: bounded.value.content ?? result.content,\n\t\t\t\t\tdetails: Object.hasOwn(bounded.value, \"details\") ? bounded.value.details : result.details,\n\t\t\t\t\tterminate: bounded.value.terminate ?? result.terminate,\n\t\t\t\t};\n\t\t\t\tisError = bounded.value.isError ?? isError;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tresult = createErrorToolResult(error instanceof Error ? error.message : String(error));\n\t\t\tisError = true;\n\t\t\tsyntheticFailure = true;\n\t\t}\n\t}\n\n\tif (terminalDisposition === undefined) {\n\t\ttry {\n\t\t\tresult = createValidatedToolResultSnapshot(result);\n\t\t} catch (error) {\n\t\t\tresult = createErrorToolResult(\n\t\t\t\t`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tisError = true;\n\t\t\tsyntheticFailure = true;\n\t\t}\n\t} else {\n\t\tisError = true;\n\t}\n\tconst envelope =\n\t\tterminalDisposition !== undefined\n\t\t\t? createToolResultEnvelope({\n\t\t\t\t\tdisposition: terminalDisposition,\n\t\t\t\t\tsynthetic: true,\n\t\t\t\t\texecutionStarted: executed.executionStarted,\n\t\t\t\t\treason:\n\t\t\t\t\t\tterminalDisposition === \"timeout\"\n\t\t\t\t\t\t\t? `Tool \"${prepared.toolCall.name}\" timed out after ${prepared.timeoutMs}ms`\n\t\t\t\t\t\t\t: \"Operation aborted\",\n\t\t\t\t\t...(terminalDisposition === \"timeout\" ? { timeoutMs: prepared.timeoutMs } : {}),\n\t\t\t\t})\n\t\t\t: createToolResultEnvelope({\n\t\t\t\t\tdisposition: isError ? \"failed\" : \"completed\",\n\t\t\t\t\tsynthetic: syntheticFailure,\n\t\t\t\t\texecutionStarted: executed.executionStarted,\n\t\t\t\t});\n\treturn {\n\t\ttoolCall: prepared.toolCall,\n\t\tresult,\n\t\tisError,\n\t\tenvelope,\n\t\tisRealPromiseSettled: executed.isRealPromiseSettled,\n\t\tcommitTerminal: executed.commitTerminal,\n\t};\n}\n\nfunction isPlainDetails(details: unknown): details is Record<string, unknown> {\n\tif (typeof details !== \"object\" || details === null || Array.isArray(details)) return false;\n\tconst prototype = Object.getPrototypeOf(details);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\n/** Preserve compatibility details while replacing any untrusted `omk` field. */\nexport function stampToolResultEnvelope(details: unknown, envelope: ToolResultEnvelope): unknown {\n\tif (!isToolResultEnvelope(envelope)) throw new TypeError(\"Refusing to persist an invalid tool-result/v2 envelope\");\n\tif (details === undefined) return { omk: envelope };\n\tif (!isPlainDetails(details)) return { originalDetails: details, omk: envelope };\n\tconst preserved = { ...details };\n\tdelete preserved.omk;\n\treturn { ...preserved, omk: envelope };\n}\n"]}
1
+ {"version":3,"file":"tool-execution-boundary.js","sourceRoot":"","sources":["../src/tool-execution-boundary.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAS1D,OAAO,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIvG,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,KAA2B,EAC3B,MAA+B,EACA;IAC/B,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAChD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,KAAK,EAAE,EAAE,CAAC;IAE7E,IAAI,WAAqC,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,OAAO,CAAsB,CAAC,OAAO,EAAE,EAAE,CAAC;QAC7D,WAAW,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QACjD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAAA,CAC9D,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,OAAO;QAAE,WAAW,EAAE,EAAE,CAAC;IAEpC,IAAI,CAAC;QACJ,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAuB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAChH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC;YAAS,CAAC;QACV,IAAI,WAAW,KAAK,SAAS;YAAE,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACjF,CAAC;AAAA,CACD;AAMD,SAAS,iCAAiC,CAAC,MAAgC,EAA4B;IACtG,MAAM,QAAQ,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAClG,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;QAC1G,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACxC,IAAI,IAAI,KAAK,MAAM,IAAI,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,QAAQ;YAAE,SAAS;QAChF,IACC,IAAI,KAAK,OAAO;YAChB,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,QAAQ;YAC9C,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,QAAQ,EACjD,CAAC;YACF,SAAS;QACV,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACjF,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,QAAQ,CAAC;AAAA,CAChB;AAED,uEAAuE;AACvE,MAAM,UAAU,uBAAuB,CAAC,QAAgB,EAAE,SAAiB,EAA4C;IACtH,OAAO,uBAAuB,CAAC;QAC9B,OAAO,EAAE;YACR;gBACC,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,SAAS,QAAQ,qBAAqB,SAAS,iEAAiE;aACtH;SACD;QACD,OAAO,EAAE;YACR,GAAG,EAAE,wBAAwB,CAAC;gBAC7B,SAAS,EAAE,IAAI;gBACf,WAAW,EAAE,SAAS;gBACtB,MAAM,EAAE,SAAS,QAAQ,qBAAqB,SAAS,IAAI;gBAC3D,SAAS;gBACT,gBAAgB,EAAE,IAAI;aACtB,CAAC;SACF;KACD,CAAC,CAAC;AAAA,CACH;AAED,qEAAqE;AACrE,MAAM,UAAU,uBAAuB,CAAC,gBAAyB,EAA4C;IAC5G,OAAO,uBAAuB,CAAC;QAC9B,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;QACtD,OAAO,EAAE;YACR,GAAG,EAAE,wBAAwB,CAAC;gBAC7B,SAAS,EAAE,IAAI;gBACf,WAAW,EAAE,SAAS;gBACtB,MAAM,EAAE,mBAAmB;gBAC3B,gBAAgB;aAChB,CAAC;SACF;KACD,CAAC,CAAC;AAAA,CACH;AA6BD,MAAM,UAAU,qBAAqB,CAAC,OAAe,EAA4B;IAChF,OAAO,uBAAuB,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;AAAA,CAC5F;AAED,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,OAAgC,EAAqC;IACnH,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAChG,IAAI,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC7B,IAAI,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;IAC/B,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,mBAAmB,GAAG,QAAQ,CAAC,mBAAmB,CAAC;IAEvD,IAAI,CAAC;QACJ,MAAM,GAAG,iCAAiC,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,GAAG,qBAAqB,CAAC,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjH,OAAO,GAAG,IAAI,CAAC;QACf,gBAAgB,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,IAAI,mBAAmB,KAAK,SAAS,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QAC1D,mBAAmB,GAAG,SAAS,CAAC;QAChC,MAAM,GAAG,uBAAuB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QAC5D,OAAO,GAAG,IAAI,CAAC;IAChB,CAAC;IACD,IAAI,mBAAmB,KAAK,SAAS,IAAI,aAAa,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC7E,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,cAAc,CACnC,GAAG,EAAE,CACJ,aAAa,CACZ;gBACC,gBAAgB;gBAChB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,MAAM;gBACN,OAAO;gBACP,OAAO,EAAE,cAAc;aACvB,EACD,MAAM,CACN,EACF,MAAM,CACN,CAAC;YACF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACnD,mBAAmB,GAAG,SAAS,CAAC;gBAChC,MAAM,GAAG,uBAAuB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;gBAC5D,OAAO,GAAG,IAAI,CAAC;YAChB,CAAC;iBAAM,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBAC1B,MAAM,GAAG;oBACR,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO;oBAChD,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO;oBACzF,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS;iBACtD,CAAC;gBACF,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,IAAI,OAAO,CAAC;YAC5C,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,GAAG,qBAAqB,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACvF,OAAO,GAAG,IAAI,CAAC;YACf,gBAAgB,GAAG,IAAI,CAAC;QACzB,CAAC;IACF,CAAC;IAED,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;QACvC,IAAI,CAAC;YACJ,MAAM,GAAG,iCAAiC,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,GAAG,qBAAqB,CAC7B,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAChF,CAAC;YACF,OAAO,GAAG,IAAI,CAAC;YACf,gBAAgB,GAAG,IAAI,CAAC;QACzB,CAAC;IACF,CAAC;SAAM,CAAC;QACP,OAAO,GAAG,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,QAAQ,GACb,mBAAmB,KAAK,SAAS;QAChC,CAAC,CAAC,wBAAwB,CAAC;YACzB,WAAW,EAAE,mBAAmB;YAChC,SAAS,EAAE,IAAI;YACf,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;YAC3C,MAAM,EACL,mBAAmB,KAAK,SAAS;gBAChC,CAAC,CAAC,SAAS,QAAQ,CAAC,QAAQ,CAAC,IAAI,qBAAqB,QAAQ,CAAC,SAAS,IAAI;gBAC5E,CAAC,CAAC,mBAAmB;YACvB,GAAG,CAAC,mBAAmB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/E,CAAC;QACH,CAAC,CAAC,wBAAwB,CAAC;YACzB,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW;YAC7C,SAAS,EAAE,gBAAgB;YAC3B,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;SAC3C,CAAC,CAAC;IACN,OAAO;QACN,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,MAAM;QACN,OAAO;QACP,QAAQ;QACR,oBAAoB,EAAE,QAAQ,CAAC,oBAAoB;QACnD,cAAc,EAAE,QAAQ,CAAC,cAAc;KACvC,CAAC;AAAA,CACF;AAED,SAAS,cAAc,CAAC,OAAgB,EAAsC;IAC7E,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5F,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IACjD,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC;AAAA,CAC5D;AAED,gFAAgF;AAChF,MAAM,UAAU,uBAAuB,CAAC,OAAgB,EAAE,QAA4B,EAAW;IAChG,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,wDAAwD,CAAC,CAAC;IACnH,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;IACpD,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;IACjF,MAAM,SAAS,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IACjC,OAAO,SAAS,CAAC,GAAG,CAAC;IACrB,OAAO,EAAE,GAAG,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;AAAA,CACvC","sourcesContent":["import type { AssistantMessage } from \"omk-ai\";\nimport { createImmutableSnapshot } from \"./plain-data.ts\";\nimport type {\n\tAgentContext,\n\tAgentLoopConfig,\n\tAgentToolCall,\n\tAgentToolResult,\n\tToolResultEnvelope,\n\tToolTimeoutDisposition,\n} from \"./types.ts\";\nimport { createToolResultEnvelope, isToolResultEnvelope } from \"./types.ts\";\n\nexport { createImmutableJsonSnapshot, createImmutableSnapshot, parseJsonValue } from \"./plain-data.ts\";\n\nexport type AbortBoundResult<T> = { kind: \"completed\"; value: T } | { kind: \"aborted\" };\n\n/** Race one async extension boundary against the parent run's abort signal. */\nexport async function awaitWithAbort<T>(\n\tstart: () => Promise<T> | T,\n\tsignal: AbortSignal | undefined,\n): Promise<AbortBoundResult<T>> {\n\tif (signal?.aborted) return { kind: \"aborted\" };\n\tif (signal === undefined) return { kind: \"completed\", value: await start() };\n\n\tlet notifyAbort: (() => void) | undefined;\n\tconst aborted = new Promise<AbortBoundResult<T>>((resolve) => {\n\t\tnotifyAbort = () => resolve({ kind: \"aborted\" });\n\t\tsignal.addEventListener(\"abort\", notifyAbort, { once: true });\n\t});\n\tif (signal.aborted) notifyAbort?.();\n\n\ttry {\n\t\tconst operation = Promise.resolve(start()).then((value): AbortBoundResult<T> => ({ kind: \"completed\", value }));\n\t\treturn await Promise.race([operation, aborted]);\n\t} finally {\n\t\tif (notifyAbort !== undefined) signal.removeEventListener(\"abort\", notifyAbort);\n\t}\n}\n\nexport interface ToolDispositionEnvelope {\n\tomk: ToolResultEnvelope;\n}\n\nfunction createValidatedToolResultSnapshot(result: AgentToolResult<unknown>): AgentToolResult<unknown> {\n\tconst snapshot = createImmutableSnapshot(result);\n\tif (!Array.isArray(snapshot.content)) throw new TypeError(\"Tool result content must be an array\");\n\tfor (const block of snapshot.content) {\n\t\tif (typeof block !== \"object\" || block === null) throw new TypeError(\"Invalid tool result content block\");\n\t\tconst type = Reflect.get(block, \"type\");\n\t\tif (type === \"text\" && typeof Reflect.get(block, \"text\") === \"string\") continue;\n\t\tif (\n\t\t\ttype === \"image\" &&\n\t\t\ttypeof Reflect.get(block, \"data\") === \"string\" &&\n\t\t\ttypeof Reflect.get(block, \"mimeType\") === \"string\"\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new TypeError(\"Invalid tool result content block\");\n\t}\n\tif (snapshot.terminate !== undefined && typeof snapshot.terminate !== \"boolean\") {\n\t\tthrow new TypeError(\"Tool result terminate must be a boolean\");\n\t}\n\treturn snapshot;\n}\n\n/** Build the immutable terminal committed when a tool timeout wins. */\nexport function createTimeoutToolResult(toolName: string, timeoutMs: number): AgentToolResult<ToolDispositionEnvelope> {\n\treturn createImmutableSnapshot({\n\t\tcontent: [\n\t\t\t{\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: `Tool \"${toolName}\" timed out after ${timeoutMs}ms. Cancellation was requested; execution may still be running.`,\n\t\t\t},\n\t\t],\n\t\tdetails: {\n\t\t\tomk: createToolResultEnvelope({\n\t\t\t\tsynthetic: true,\n\t\t\t\tdisposition: \"timeout\",\n\t\t\t\treason: `Tool \"${toolName}\" timed out after ${timeoutMs}ms`,\n\t\t\t\ttimeoutMs,\n\t\t\t\texecutionStarted: true,\n\t\t\t}),\n\t\t},\n\t});\n}\n\n/** Build the immutable terminal committed when parent abort wins. */\nexport function createAbortedToolResult(executionStarted: boolean): AgentToolResult<ToolDispositionEnvelope> {\n\treturn createImmutableSnapshot({\n\t\tcontent: [{ type: \"text\", text: \"Operation aborted\" }],\n\t\tdetails: {\n\t\t\tomk: createToolResultEnvelope({\n\t\t\t\tsynthetic: true,\n\t\t\t\tdisposition: \"aborted\",\n\t\t\t\treason: \"Operation aborted\",\n\t\t\t\texecutionStarted,\n\t\t\t}),\n\t\t},\n\t});\n}\n\nexport interface ExecutedToolCallOutcome {\n\tresult: AgentToolResult<unknown>;\n\tisError: boolean;\n\texecutionStarted: boolean;\n\tterminalDisposition?: ToolTimeoutDisposition;\n\tisRealPromiseSettled: () => boolean;\n\tcommitTerminal: () => void;\n}\n\nexport interface FinalizedToolCallOutcome {\n\ttoolCall: AgentToolCall;\n\tresult: AgentToolResult<unknown>;\n\tisError: boolean;\n\tenvelope: ToolResultEnvelope;\n\tisRealPromiseSettled?: () => boolean;\n\tcommitTerminal?: () => void;\n}\n\ninterface FinalizeToolCallOptions {\n\tcurrentContext: AgentContext;\n\tassistantMessage: AssistantMessage;\n\tprepared: { toolCall: AgentToolCall; args: unknown; timeoutMs: number };\n\texecuted: ExecutedToolCallOutcome;\n\tafterToolCall: AgentLoopConfig[\"afterToolCall\"];\n\tsignal: AbortSignal | undefined;\n}\n\nexport function createErrorToolResult(message: string): AgentToolResult<unknown> {\n\treturn createImmutableSnapshot({ content: [{ type: \"text\", text: message }], details: {} });\n}\n\n/** Commit a real result or immutable timeout/abort result across the after hook boundary. */\nexport async function finalizeExecutedToolCall(options: FinalizeToolCallOptions): Promise<FinalizedToolCallOutcome> {\n\tconst { currentContext, assistantMessage, prepared, executed, afterToolCall, signal } = options;\n\tlet result = executed.result;\n\tlet isError = executed.isError;\n\tlet syntheticFailure = false;\n\tlet terminalDisposition = executed.terminalDisposition;\n\n\ttry {\n\t\tresult = createValidatedToolResultSnapshot(result);\n\t} catch (error) {\n\t\tresult = createErrorToolResult(`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`);\n\t\tisError = true;\n\t\tsyntheticFailure = true;\n\t}\n\n\tif (terminalDisposition === undefined && signal?.aborted) {\n\t\tterminalDisposition = \"aborted\";\n\t\tresult = createAbortedToolResult(executed.executionStarted);\n\t\tisError = true;\n\t}\n\tif (terminalDisposition === undefined && afterToolCall && !syntheticFailure) {\n\t\ttry {\n\t\t\tconst bounded = await awaitWithAbort(\n\t\t\t\t() =>\n\t\t\t\t\tafterToolCall(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tassistantMessage,\n\t\t\t\t\t\t\ttoolCall: prepared.toolCall,\n\t\t\t\t\t\t\targs: prepared.args,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\tisError,\n\t\t\t\t\t\t\tcontext: currentContext,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (bounded.kind === \"aborted\" || signal?.aborted) {\n\t\t\t\tterminalDisposition = \"aborted\";\n\t\t\t\tresult = createAbortedToolResult(executed.executionStarted);\n\t\t\t\tisError = true;\n\t\t\t} else if (bounded.value) {\n\t\t\t\tresult = {\n\t\t\t\t\tcontent: bounded.value.content ?? result.content,\n\t\t\t\t\tdetails: Object.hasOwn(bounded.value, \"details\") ? bounded.value.details : result.details,\n\t\t\t\t\tterminate: bounded.value.terminate ?? result.terminate,\n\t\t\t\t};\n\t\t\t\tisError = bounded.value.isError ?? isError;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tresult = createErrorToolResult(error instanceof Error ? error.message : String(error));\n\t\t\tisError = true;\n\t\t\tsyntheticFailure = true;\n\t\t}\n\t}\n\n\tif (terminalDisposition === undefined) {\n\t\ttry {\n\t\t\tresult = createValidatedToolResultSnapshot(result);\n\t\t} catch (error) {\n\t\t\tresult = createErrorToolResult(\n\t\t\t\t`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tisError = true;\n\t\t\tsyntheticFailure = true;\n\t\t}\n\t} else {\n\t\tisError = true;\n\t}\n\tconst envelope =\n\t\tterminalDisposition !== undefined\n\t\t\t? createToolResultEnvelope({\n\t\t\t\t\tdisposition: terminalDisposition,\n\t\t\t\t\tsynthetic: true,\n\t\t\t\t\texecutionStarted: executed.executionStarted,\n\t\t\t\t\treason:\n\t\t\t\t\t\tterminalDisposition === \"timeout\"\n\t\t\t\t\t\t\t? `Tool \"${prepared.toolCall.name}\" timed out after ${prepared.timeoutMs}ms`\n\t\t\t\t\t\t\t: \"Operation aborted\",\n\t\t\t\t\t...(terminalDisposition === \"timeout\" ? { timeoutMs: prepared.timeoutMs } : {}),\n\t\t\t\t})\n\t\t\t: createToolResultEnvelope({\n\t\t\t\t\tdisposition: isError ? \"failed\" : \"completed\",\n\t\t\t\t\tsynthetic: syntheticFailure,\n\t\t\t\t\texecutionStarted: executed.executionStarted,\n\t\t\t\t});\n\treturn {\n\t\ttoolCall: prepared.toolCall,\n\t\tresult,\n\t\tisError,\n\t\tenvelope,\n\t\tisRealPromiseSettled: executed.isRealPromiseSettled,\n\t\tcommitTerminal: executed.commitTerminal,\n\t};\n}\n\nfunction isPlainDetails(details: unknown): details is Record<string, unknown> {\n\tif (typeof details !== \"object\" || details === null || Array.isArray(details)) return false;\n\tconst prototype = Object.getPrototypeOf(details);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\n/** Preserve compatibility details while replacing any untrusted `omk` field. */\nexport function stampToolResultEnvelope(details: unknown, envelope: ToolResultEnvelope): unknown {\n\tif (!isToolResultEnvelope(envelope)) throw new TypeError(\"Refusing to persist an invalid tool-result/v2 envelope\");\n\tif (details === undefined) return { omk: envelope };\n\tif (!isPlainDetails(details)) return { originalDetails: details, omk: envelope };\n\tconst preserved = { ...details };\n\tdelete preserved.omk;\n\treturn { ...preserved, omk: envelope };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tool-timeout-settlement.d.ts","sourceRoot":"","sources":["../src/tool-timeout-settlement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAE7E,4EAA4E;AAC5E,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,mFAAmF;AACnF,wBAAsB,mBAAmB,CAAC,cAAc,EAAE,SAAS,wBAAwB,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ/G","sourcesContent":["import type { FinalizedToolCallOutcome } from \"./tool-execution-boundary.ts\";\n\n/** Bounded OS/event-loop teardown window paid only after a tool timeout. */\nexport const DEFAULT_TOOL_TEARDOWN_GRACE_MS = 250;\n\n/** Return true when a timed-out tool is still active after the teardown window. */\nexport async function hasUnsettledTimeout(finalizedCalls: readonly FinalizedToolCallOutcome[]): Promise<boolean> {\n\tconst stillRunning = () =>\n\t\tfinalizedCalls.some(\n\t\t\t(finalized) => finalized.envelope.disposition === \"timeout\" && finalized.isRealPromiseSettled?.() === false,\n\t\t);\n\tconst deadline = Date.now() + DEFAULT_TOOL_TEARDOWN_GRACE_MS;\n\twhile (Date.now() < deadline && stillRunning()) await new Promise((resolve) => setTimeout(resolve, 5));\n\treturn stillRunning();\n}\n"]}
1
+ {"version":3,"file":"tool-timeout-settlement.d.ts","sourceRoot":"","sources":["../src/tool-timeout-settlement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAE7E,4EAA4E;AAC5E,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,mFAAmF;AACnF,wBAAsB,mBAAmB,CAAC,cAAc,EAAE,SAAS,wBAAwB,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ/G","sourcesContent":["import type { FinalizedToolCallOutcome } from \"./tool-execution-boundary.ts\";\n\n/** Bounded OS/event-loop teardown window paid only after a tool timeout. */\nexport const DEFAULT_TOOL_TEARDOWN_GRACE_MS = 250;\n\n/** Return true when a timed-out tool is still active after the teardown window. */\nexport async function hasUnsettledTimeout(finalizedCalls: readonly FinalizedToolCallOutcome[]): Promise<boolean> {\n\tconst stillRunning = () =>\n\t\tfinalizedCalls.some(\n\t\t\t(finalized) => finalized.envelope.disposition === \"timeout\" && finalized.isRealPromiseSettled?.() === false,\n\t\t);\n\tconst deadline = performance.now() + DEFAULT_TOOL_TEARDOWN_GRACE_MS;\n\twhile (performance.now() < deadline && stillRunning()) await new Promise((resolve) => setTimeout(resolve, 5));\n\treturn stillRunning();\n}\n"]}
@@ -3,8 +3,8 @@ export const DEFAULT_TOOL_TEARDOWN_GRACE_MS = 250;
3
3
  /** Return true when a timed-out tool is still active after the teardown window. */
4
4
  export async function hasUnsettledTimeout(finalizedCalls) {
5
5
  const stillRunning = () => finalizedCalls.some((finalized) => finalized.envelope.disposition === "timeout" && finalized.isRealPromiseSettled?.() === false);
6
- const deadline = Date.now() + DEFAULT_TOOL_TEARDOWN_GRACE_MS;
7
- while (Date.now() < deadline && stillRunning())
6
+ const deadline = performance.now() + DEFAULT_TOOL_TEARDOWN_GRACE_MS;
7
+ while (performance.now() < deadline && stillRunning())
8
8
  await new Promise((resolve) => setTimeout(resolve, 5));
9
9
  return stillRunning();
10
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool-timeout-settlement.js","sourceRoot":"","sources":["../src/tool-timeout-settlement.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,cAAmD,EAAoB;IAChH,MAAM,YAAY,GAAG,GAAG,EAAE,CACzB,cAAc,CAAC,IAAI,CAClB,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,WAAW,KAAK,SAAS,IAAI,SAAS,CAAC,oBAAoB,EAAE,EAAE,KAAK,KAAK,CAC3G,CAAC;IACH,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,8BAA8B,CAAC;IAC7D,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,IAAI,YAAY,EAAE;QAAE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IACvG,OAAO,YAAY,EAAE,CAAC;AAAA,CACtB","sourcesContent":["import type { FinalizedToolCallOutcome } from \"./tool-execution-boundary.ts\";\n\n/** Bounded OS/event-loop teardown window paid only after a tool timeout. */\nexport const DEFAULT_TOOL_TEARDOWN_GRACE_MS = 250;\n\n/** Return true when a timed-out tool is still active after the teardown window. */\nexport async function hasUnsettledTimeout(finalizedCalls: readonly FinalizedToolCallOutcome[]): Promise<boolean> {\n\tconst stillRunning = () =>\n\t\tfinalizedCalls.some(\n\t\t\t(finalized) => finalized.envelope.disposition === \"timeout\" && finalized.isRealPromiseSettled?.() === false,\n\t\t);\n\tconst deadline = Date.now() + DEFAULT_TOOL_TEARDOWN_GRACE_MS;\n\twhile (Date.now() < deadline && stillRunning()) await new Promise((resolve) => setTimeout(resolve, 5));\n\treturn stillRunning();\n}\n"]}
1
+ {"version":3,"file":"tool-timeout-settlement.js","sourceRoot":"","sources":["../src/tool-timeout-settlement.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,cAAmD,EAAoB;IAChH,MAAM,YAAY,GAAG,GAAG,EAAE,CACzB,cAAc,CAAC,IAAI,CAClB,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,WAAW,KAAK,SAAS,IAAI,SAAS,CAAC,oBAAoB,EAAE,EAAE,KAAK,KAAK,CAC3G,CAAC;IACH,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,8BAA8B,CAAC;IACpE,OAAO,WAAW,CAAC,GAAG,EAAE,GAAG,QAAQ,IAAI,YAAY,EAAE;QAAE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9G,OAAO,YAAY,EAAE,CAAC;AAAA,CACtB","sourcesContent":["import type { FinalizedToolCallOutcome } from \"./tool-execution-boundary.ts\";\n\n/** Bounded OS/event-loop teardown window paid only after a tool timeout. */\nexport const DEFAULT_TOOL_TEARDOWN_GRACE_MS = 250;\n\n/** Return true when a timed-out tool is still active after the teardown window. */\nexport async function hasUnsettledTimeout(finalizedCalls: readonly FinalizedToolCallOutcome[]): Promise<boolean> {\n\tconst stillRunning = () =>\n\t\tfinalizedCalls.some(\n\t\t\t(finalized) => finalized.envelope.disposition === \"timeout\" && finalized.isRealPromiseSettled?.() === false,\n\t\t);\n\tconst deadline = performance.now() + DEFAULT_TOOL_TEARDOWN_GRACE_MS;\n\twhile (performance.now() < deadline && stillRunning()) await new Promise((resolve) => setTimeout(resolve, 5));\n\treturn stillRunning();\n}\n"]}