omk-agent-core 0.90.7 → 0.90.9

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 (62) hide show
  1. package/README.md +97 -1
  2. package/dist/agent-loop.d.ts +25 -2
  3. package/dist/agent-loop.d.ts.map +1 -1
  4. package/dist/agent-loop.js +524 -189
  5. package/dist/agent-loop.js.map +1 -1
  6. package/dist/agent.d.ts +29 -7
  7. package/dist/agent.d.ts.map +1 -1
  8. package/dist/agent.js +81 -46
  9. package/dist/agent.js.map +1 -1
  10. package/dist/builtin-tool-resource-claims.d.ts +19 -0
  11. package/dist/builtin-tool-resource-claims.d.ts.map +1 -0
  12. package/dist/builtin-tool-resource-claims.js +200 -0
  13. package/dist/builtin-tool-resource-claims.js.map +1 -0
  14. package/dist/index.d.ts +5 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +8 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/node-resource-resolver.d.ts +42 -0
  19. package/dist/node-resource-resolver.d.ts.map +1 -0
  20. package/dist/node-resource-resolver.js +149 -0
  21. package/dist/node-resource-resolver.js.map +1 -0
  22. package/dist/node.d.ts +1 -0
  23. package/dist/node.d.ts.map +1 -1
  24. package/dist/node.js +2 -0
  25. package/dist/node.js.map +1 -1
  26. package/dist/parallel-tool-batch.d.ts +12 -1
  27. package/dist/parallel-tool-batch.d.ts.map +1 -1
  28. package/dist/parallel-tool-batch.js +71 -49
  29. package/dist/parallel-tool-batch.js.map +1 -1
  30. package/dist/path-segments.d.ts +21 -1
  31. package/dist/path-segments.d.ts.map +1 -1
  32. package/dist/path-segments.js +91 -9
  33. package/dist/path-segments.js.map +1 -1
  34. package/dist/plain-data.d.ts +7 -0
  35. package/dist/plain-data.d.ts.map +1 -0
  36. package/dist/plain-data.js +70 -0
  37. package/dist/plain-data.js.map +1 -0
  38. package/dist/tool-dag-scheduler.d.ts +86 -0
  39. package/dist/tool-dag-scheduler.d.ts.map +1 -0
  40. package/dist/tool-dag-scheduler.js +171 -0
  41. package/dist/tool-dag-scheduler.js.map +1 -0
  42. package/dist/tool-execution-boundary.d.ts +52 -0
  43. package/dist/tool-execution-boundary.d.ts.map +1 -0
  44. package/dist/tool-execution-boundary.js +185 -0
  45. package/dist/tool-execution-boundary.js.map +1 -0
  46. package/dist/tool-resource-claims.d.ts +31 -0
  47. package/dist/tool-resource-claims.d.ts.map +1 -0
  48. package/dist/tool-resource-claims.js +128 -0
  49. package/dist/tool-resource-claims.js.map +1 -0
  50. package/dist/tool-timeout.d.ts +96 -0
  51. package/dist/tool-timeout.d.ts.map +1 -0
  52. package/dist/tool-timeout.js +173 -0
  53. package/dist/tool-timeout.js.map +1 -0
  54. package/dist/tool-transcript-integrity.d.ts +65 -0
  55. package/dist/tool-transcript-integrity.d.ts.map +1 -0
  56. package/dist/tool-transcript-integrity.js +223 -0
  57. package/dist/tool-transcript-integrity.js.map +1 -0
  58. package/dist/types.d.ts +219 -10
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/types.js +50 -1
  61. package/dist/types.js.map +1 -1
  62. package/package.json +2 -2
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Pure, browser-safe deterministic resource-claim DAG scheduler.
3
+ *
4
+ * Given a source-ordered batch of tool calls and their resolved resource
5
+ * claims, this module assigns each call after every earlier call it conflicts
6
+ * with. Levels run in source order; calls inside one level are mutually
7
+ * conflict-free and may execute concurrently. A positive `maxConcurrency`
8
+ * width cap splits each level into deterministic contiguous source-ordered
9
+ * chunks.
10
+ *
11
+ * Determinism contract:
12
+ * - The level assignment is a pure function of the source-ordered input
13
+ * claims. The canonical example is the head-of-line example
14
+ * `write x, write x, write y`, which schedules as `[[0, 2], [1]]`: the second
15
+ * write to `x` is pushed to level 1, but the independent write to `y` is not
16
+ * blocked and joins level 0.
17
+ * - Reordering claims *within* a single call does not change the plan: claims
18
+ * are canonicalized (sorted, fixed property order) before comparison.
19
+ * - {@link DagSchedulePlan.planKey} is a canonical serialization of the
20
+ * resolved claim sequence (canonical claim data only — never execution
21
+ * timing or outcomes). It is collision-free for distinct canonical claim
22
+ * sequences.
23
+ *
24
+ * This module uses no platform APIs (no `process`, fs, `node:path`, or timers).
25
+ */
26
+ import { canonicalizeClaims, claimsConflict, resolveToolClaimsForCall, } from "./tool-resource-claims.js";
27
+ /**
28
+ * Resolve and canonicalize claims for a whole batch, preserving source order.
29
+ * Registered custom resolvers are awaited one call at a time in source order,
30
+ * and every resolution completes before a schedule is constructed.
31
+ */
32
+ export async function resolveBatchClaims(toolCalls, options) {
33
+ const entries = [];
34
+ for (let index = 0; index < toolCalls.length; index++) {
35
+ const resolution = await resolveToolClaimsForCall(toolCalls[index], options);
36
+ const canonicalClaims = resolution.kind === "claims" ? canonicalizeClaims(resolution.claims) : [];
37
+ entries.push({ sourceIndex: index, resolution, canonicalClaims });
38
+ }
39
+ return entries;
40
+ }
41
+ function claimConflictsAny(claim, candidates) {
42
+ for (const candidate of candidates) {
43
+ if (claimsConflict(claim, candidate)) {
44
+ return true;
45
+ }
46
+ }
47
+ return false;
48
+ }
49
+ /**
50
+ * True when `resolution` cannot join `level`. Equivalent to the full pairwise
51
+ * `resolutionsConflict` check, optimized so read/read never triggers a scan:
52
+ * a read claim only needs to scan the level's writes, and a write claim scans
53
+ * both writes and reads. An exclusive resolution, or a level that already holds
54
+ * an exclusive call, conflicts unconditionally.
55
+ */
56
+ function resolutionConflictsLevel(resolution, level) {
57
+ if (level.hasExclusive) {
58
+ return true;
59
+ }
60
+ if (resolution.kind === "exclusive") {
61
+ return true;
62
+ }
63
+ for (const claim of resolution.claims) {
64
+ if (claim.access === "exclusive") {
65
+ return true;
66
+ }
67
+ if (claim.access === "write") {
68
+ if (claimConflictsAny(claim, level.writeClaims)) {
69
+ return true;
70
+ }
71
+ if (claimConflictsAny(claim, level.readClaims)) {
72
+ return true;
73
+ }
74
+ }
75
+ else if (claimConflictsAny(claim, level.writeClaims)) {
76
+ return true;
77
+ }
78
+ }
79
+ return false;
80
+ }
81
+ /**
82
+ * Assign each source-ordered claim entry one level after its latest earlier
83
+ * conflict. Deterministic and stable: equal inputs (including claim reordering
84
+ * within a call, which is canonicalized away) always produce equal levels, and
85
+ * every directed conflict edge advances at least one level.
86
+ */
87
+ export function assignDagLevels(entries) {
88
+ const levels = [];
89
+ for (const entry of entries) {
90
+ const resolution = entry.resolution;
91
+ let targetIndex = 0;
92
+ for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) {
93
+ if (resolutionConflictsLevel(resolution, levels[levelIndex])) {
94
+ targetIndex = levelIndex + 1;
95
+ }
96
+ }
97
+ let target = levels[targetIndex];
98
+ if (!target) {
99
+ target = { indices: [], writeClaims: [], readClaims: [], hasExclusive: false };
100
+ levels.push(target);
101
+ }
102
+ target.indices.push(entry.sourceIndex);
103
+ if (resolution.kind === "exclusive" || entry.canonicalClaims.some((claim) => claim.access === "exclusive")) {
104
+ target.hasExclusive = true;
105
+ }
106
+ else {
107
+ for (const claim of entry.canonicalClaims) {
108
+ if (claim.access === "write") {
109
+ target.writeClaims.push(claim);
110
+ }
111
+ else {
112
+ target.readClaims.push(claim);
113
+ }
114
+ }
115
+ }
116
+ }
117
+ return levels.map((level) => level.indices);
118
+ }
119
+ /**
120
+ * Split each level into contiguous source-ordered chunks of at most `cap` calls.
121
+ * Absent/non-finite/non-positive `cap` returns the levels unchanged.
122
+ */
123
+ export function applyConcurrencyCap(levels, cap) {
124
+ if (typeof cap !== "number" || !Number.isFinite(cap) || cap <= 0) {
125
+ return levels.map((level) => level.slice());
126
+ }
127
+ const integerCap = Math.max(1, Math.floor(cap));
128
+ const chunked = [];
129
+ for (const level of levels) {
130
+ if (level.length <= integerCap) {
131
+ chunked.push(level.slice());
132
+ continue;
133
+ }
134
+ for (let start = 0; start < level.length; start += integerCap) {
135
+ chunked.push(level.slice(start, start + integerCap));
136
+ }
137
+ }
138
+ return chunked;
139
+ }
140
+ /**
141
+ * Canonical deterministic key over the resolved claim sequence. Uses
142
+ * `JSON.stringify` of each call's canonicalized claims (fixed property order,
143
+ * sorted) with an `"E"`/`"C"` discriminator, so it is collision-free for
144
+ * distinct canonical claim sequences and stable under claim reordering within a
145
+ * call. Contains no execution timing or outcomes.
146
+ */
147
+ export function computePlanKey(entries) {
148
+ let key = "";
149
+ for (const entry of entries) {
150
+ if (entry.resolution.kind === "exclusive") {
151
+ key += "E,";
152
+ }
153
+ else {
154
+ key += `C${JSON.stringify(entry.canonicalClaims)},`;
155
+ }
156
+ }
157
+ return key;
158
+ }
159
+ /**
160
+ * Schedule a source-ordered tool-call batch into deterministic DAG levels.
161
+ * Resolves default claims, computes source-directed dependency levels, applies
162
+ * the optional width cap, and computes the canonical plan key. Pure and
163
+ * deterministic.
164
+ */
165
+ export async function scheduleDagLevels(toolCalls, options) {
166
+ const entries = await resolveBatchClaims(toolCalls, options);
167
+ const baseLevels = assignDagLevels(entries);
168
+ const levels = applyConcurrencyCap(baseLevels, options.maxConcurrency);
169
+ return { levels, planKey: computePlanKey(entries) };
170
+ }
171
+ //# sourceMappingURL=tool-dag-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-dag-scheduler.js","sourceRoot":"","sources":["../src/tool-dag-scheduler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure, browser-safe deterministic resource-claim DAG scheduler.\n *\n * Given a source-ordered batch of tool calls and their resolved resource\n * claims, this module assigns each call after every earlier call it conflicts\n * with. Levels run in source order; calls inside one level are mutually\n * conflict-free and may execute concurrently. A positive `maxConcurrency`\n * width cap splits each level into deterministic contiguous source-ordered\n * chunks.\n *\n * Determinism contract:\n * - The level assignment is a pure function of the source-ordered input\n * claims. The canonical example is the head-of-line example\n * `write x, write x, write y`, which schedules as `[[0, 2], [1]]`: the second\n * write to `x` is pushed to level 1, but the independent write to `y` is not\n * blocked and joins level 0.\n * - Reordering claims *within* a single call does not change the plan: claims\n * are canonicalized (sorted, fixed property order) before comparison.\n * - {@link DagSchedulePlan.planKey} is a canonical serialization of the\n * resolved claim sequence (canonical claim data only — never execution\n * timing or outcomes). It is collision-free for distinct canonical claim\n * sequences.\n *\n * This module uses no platform APIs (no `process`, fs, `node:path`, or timers).\n */\n\nimport {\n\ttype ClaimableToolCall,\n\tcanonicalizeClaims,\n\tclaimsConflict,\n\ttype ResolveToolClaimsOptions,\n\tresolveToolClaimsForCall,\n\ttype ToolClaimResolution,\n\ttype ToolResourceClaim,\n} from \"./tool-resource-claims.ts\";\n\n/** Options for scheduling a batch into DAG levels. */\nexport interface ScheduleDagLevelsOptions extends ResolveToolClaimsOptions {\n\t/**\n\t * Optional positive width cap. When set, each level is split into contiguous\n\t * source-ordered chunks of at most this many calls so a wide conflict-free\n\t * level does not fan out unbounded. Absent, non-finite, or non-positive\n\t * values leave each level whole. Does not affect `planKey`.\n\t */\n\tmaxConcurrency?: number;\n}\n\n/** A scheduled plan: ordered levels of source indices plus a canonical key. */\nexport interface DagSchedulePlan {\n\t/** Levels in execution order; each level holds source indices that may run concurrently. */\n\tlevels: number[][];\n\t/**\n\t * Canonical deterministic key over the resolved claim sequence only (never\n\t * execution timing/outcomes). Stable under claim reordering within a call.\n\t */\n\tplanKey: string;\n}\n\n/** One tool call's resolved claim data, canonicalized for stable planning. */\nexport interface ResolvedClaimEntry {\n\tsourceIndex: number;\n\tresolution: ToolClaimResolution;\n\tcanonicalClaims: ToolResourceClaim[];\n}\n\n/**\n * Resolve and canonicalize claims for a whole batch, preserving source order.\n * Registered custom resolvers are awaited one call at a time in source order,\n * and every resolution completes before a schedule is constructed.\n */\nexport async function resolveBatchClaims(\n\ttoolCalls: readonly ClaimableToolCall[],\n\toptions: ResolveToolClaimsOptions,\n): Promise<ResolvedClaimEntry[]> {\n\tconst entries: ResolvedClaimEntry[] = [];\n\tfor (let index = 0; index < toolCalls.length; index++) {\n\t\tconst resolution = await resolveToolClaimsForCall(toolCalls[index], options);\n\t\tconst canonicalClaims = resolution.kind === \"claims\" ? canonicalizeClaims(resolution.claims) : [];\n\t\tentries.push({ sourceIndex: index, resolution, canonicalClaims });\n\t}\n\treturn entries;\n}\n\ninterface DagLevel {\n\tindices: number[];\n\t/** Write claims currently placed in this level (for fast read-vs-write checks). */\n\twriteClaims: ToolResourceClaim[];\n\t/** Read claims currently placed in this level (for fast write-vs-read checks). */\n\treadClaims: ToolResourceClaim[];\n\t/** True once an exclusive call is placed here; such a level accepts no more. */\n\thasExclusive: boolean;\n}\n\nfunction claimConflictsAny(claim: ToolResourceClaim, candidates: readonly ToolResourceClaim[]): boolean {\n\tfor (const candidate of candidates) {\n\t\tif (claimsConflict(claim, candidate)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * True when `resolution` cannot join `level`. Equivalent to the full pairwise\n * `resolutionsConflict` check, optimized so read/read never triggers a scan:\n * a read claim only needs to scan the level's writes, and a write claim scans\n * both writes and reads. An exclusive resolution, or a level that already holds\n * an exclusive call, conflicts unconditionally.\n */\nfunction resolutionConflictsLevel(resolution: ToolClaimResolution, level: DagLevel): boolean {\n\tif (level.hasExclusive) {\n\t\treturn true;\n\t}\n\tif (resolution.kind === \"exclusive\") {\n\t\treturn true;\n\t}\n\tfor (const claim of resolution.claims) {\n\t\tif (claim.access === \"exclusive\") {\n\t\t\treturn true;\n\t\t}\n\t\tif (claim.access === \"write\") {\n\t\t\tif (claimConflictsAny(claim, level.writeClaims)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (claimConflictsAny(claim, level.readClaims)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (claimConflictsAny(claim, level.writeClaims)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Assign each source-ordered claim entry one level after its latest earlier\n * conflict. Deterministic and stable: equal inputs (including claim reordering\n * within a call, which is canonicalized away) always produce equal levels, and\n * every directed conflict edge advances at least one level.\n */\nexport function assignDagLevels(entries: readonly ResolvedClaimEntry[]): number[][] {\n\tconst levels: DagLevel[] = [];\n\tfor (const entry of entries) {\n\t\tconst resolution = entry.resolution;\n\t\tlet targetIndex = 0;\n\t\tfor (let levelIndex = 0; levelIndex < levels.length; levelIndex++) {\n\t\t\tif (resolutionConflictsLevel(resolution, levels[levelIndex])) {\n\t\t\t\ttargetIndex = levelIndex + 1;\n\t\t\t}\n\t\t}\n\t\tlet target = levels[targetIndex];\n\t\tif (!target) {\n\t\t\ttarget = { indices: [], writeClaims: [], readClaims: [], hasExclusive: false };\n\t\t\tlevels.push(target);\n\t\t}\n\t\ttarget.indices.push(entry.sourceIndex);\n\t\tif (resolution.kind === \"exclusive\" || entry.canonicalClaims.some((claim) => claim.access === \"exclusive\")) {\n\t\t\ttarget.hasExclusive = true;\n\t\t} else {\n\t\t\tfor (const claim of entry.canonicalClaims) {\n\t\t\t\tif (claim.access === \"write\") {\n\t\t\t\t\ttarget.writeClaims.push(claim);\n\t\t\t\t} else {\n\t\t\t\t\ttarget.readClaims.push(claim);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn levels.map((level) => level.indices);\n}\n\n/**\n * Split each level into contiguous source-ordered chunks of at most `cap` calls.\n * Absent/non-finite/non-positive `cap` returns the levels unchanged.\n */\nexport function applyConcurrencyCap(levels: readonly number[][], cap: number | undefined): number[][] {\n\tif (typeof cap !== \"number\" || !Number.isFinite(cap) || cap <= 0) {\n\t\treturn levels.map((level) => level.slice());\n\t}\n\tconst integerCap = Math.max(1, Math.floor(cap));\n\tconst chunked: number[][] = [];\n\tfor (const level of levels) {\n\t\tif (level.length <= integerCap) {\n\t\t\tchunked.push(level.slice());\n\t\t\tcontinue;\n\t\t}\n\t\tfor (let start = 0; start < level.length; start += integerCap) {\n\t\t\tchunked.push(level.slice(start, start + integerCap));\n\t\t}\n\t}\n\treturn chunked;\n}\n\n/**\n * Canonical deterministic key over the resolved claim sequence. Uses\n * `JSON.stringify` of each call's canonicalized claims (fixed property order,\n * sorted) with an `\"E\"`/`\"C\"` discriminator, so it is collision-free for\n * distinct canonical claim sequences and stable under claim reordering within a\n * call. Contains no execution timing or outcomes.\n */\nexport function computePlanKey(entries: readonly ResolvedClaimEntry[]): string {\n\tlet key = \"\";\n\tfor (const entry of entries) {\n\t\tif (entry.resolution.kind === \"exclusive\") {\n\t\t\tkey += \"E,\";\n\t\t} else {\n\t\t\tkey += `C${JSON.stringify(entry.canonicalClaims)},`;\n\t\t}\n\t}\n\treturn key;\n}\n\n/**\n * Schedule a source-ordered tool-call batch into deterministic DAG levels.\n * Resolves default claims, computes source-directed dependency levels, applies\n * the optional width cap, and computes the canonical plan key. Pure and\n * deterministic.\n */\nexport async function scheduleDagLevels(\n\ttoolCalls: readonly ClaimableToolCall[],\n\toptions: ScheduleDagLevelsOptions,\n): Promise<DagSchedulePlan> {\n\tconst entries = await resolveBatchClaims(toolCalls, options);\n\tconst baseLevels = assignDagLevels(entries);\n\tconst levels = applyConcurrencyCap(baseLevels, options.maxConcurrency);\n\treturn { levels, planKey: computePlanKey(entries) };\n}\n"]}
@@ -0,0 +1,52 @@
1
+ import type { AssistantMessage } from "omk-ai";
2
+ import type { AgentContext, AgentLoopConfig, AgentToolCall, AgentToolResult, ToolResultEnvelope, ToolTimeoutDisposition } from "./types.ts";
3
+ export { createImmutableJsonSnapshot, createImmutableSnapshot, parseJsonValue } from "./plain-data.ts";
4
+ export type AbortBoundResult<T> = {
5
+ kind: "completed";
6
+ value: T;
7
+ } | {
8
+ kind: "aborted";
9
+ };
10
+ /** Race one async extension boundary against the parent run's abort signal. */
11
+ export declare function awaitWithAbort<T>(start: () => Promise<T> | T, signal: AbortSignal | undefined): Promise<AbortBoundResult<T>>;
12
+ export interface ToolDispositionEnvelope {
13
+ omk: ToolResultEnvelope;
14
+ }
15
+ /** Build the immutable terminal committed when a tool timeout wins. */
16
+ export declare function createTimeoutToolResult(toolName: string, timeoutMs: number): AgentToolResult<ToolDispositionEnvelope>;
17
+ /** Build the immutable terminal committed when parent abort wins. */
18
+ export declare function createAbortedToolResult(executionStarted: boolean): AgentToolResult<ToolDispositionEnvelope>;
19
+ export interface ExecutedToolCallOutcome {
20
+ result: AgentToolResult<unknown>;
21
+ isError: boolean;
22
+ executionStarted: boolean;
23
+ terminalDisposition?: ToolTimeoutDisposition;
24
+ isRealPromiseSettled: () => boolean;
25
+ commitTerminal: () => void;
26
+ }
27
+ export interface FinalizedToolCallOutcome {
28
+ toolCall: AgentToolCall;
29
+ result: AgentToolResult<unknown>;
30
+ isError: boolean;
31
+ envelope: ToolResultEnvelope;
32
+ isRealPromiseSettled?: () => boolean;
33
+ commitTerminal?: () => void;
34
+ }
35
+ interface FinalizeToolCallOptions {
36
+ currentContext: AgentContext;
37
+ assistantMessage: AssistantMessage;
38
+ prepared: {
39
+ toolCall: AgentToolCall;
40
+ args: unknown;
41
+ timeoutMs: number;
42
+ };
43
+ executed: ExecutedToolCallOutcome;
44
+ afterToolCall: AgentLoopConfig["afterToolCall"];
45
+ signal: AbortSignal | undefined;
46
+ }
47
+ export declare function createErrorToolResult(message: string): AgentToolResult<unknown>;
48
+ /** Commit a real result or immutable timeout/abort result across the after hook boundary. */
49
+ export declare function finalizeExecutedToolCall(options: FinalizeToolCallOptions): Promise<FinalizedToolCallOutcome>;
50
+ /** Preserve compatibility details while replacing any untrusted `omk` field. */
51
+ export declare function stampToolResultEnvelope(details: unknown, envelope: ToolResultEnvelope): unknown;
52
+ //# sourceMappingURL=tool-execution-boundary.d.ts.map
@@ -0,0 +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"]}
@@ -0,0 +1,185 @@
1
+ import { createImmutableSnapshot } from "./plain-data.js";
2
+ import { createToolResultEnvelope, isToolResultEnvelope } from "./types.js";
3
+ export { createImmutableJsonSnapshot, createImmutableSnapshot, parseJsonValue } from "./plain-data.js";
4
+ /** Race one async extension boundary against the parent run's abort signal. */
5
+ export async function awaitWithAbort(start, signal) {
6
+ if (signal?.aborted)
7
+ return { kind: "aborted" };
8
+ if (signal === undefined)
9
+ return { kind: "completed", value: await start() };
10
+ let notifyAbort;
11
+ const aborted = new Promise((resolve) => {
12
+ notifyAbort = () => resolve({ kind: "aborted" });
13
+ signal.addEventListener("abort", notifyAbort, { once: true });
14
+ });
15
+ if (signal.aborted)
16
+ notifyAbort?.();
17
+ try {
18
+ const operation = Promise.resolve(start()).then((value) => ({ kind: "completed", value }));
19
+ return await Promise.race([operation, aborted]);
20
+ }
21
+ finally {
22
+ if (notifyAbort !== undefined)
23
+ signal.removeEventListener("abort", notifyAbort);
24
+ }
25
+ }
26
+ function createValidatedToolResultSnapshot(result) {
27
+ const snapshot = createImmutableSnapshot(result);
28
+ if (!Array.isArray(snapshot.content))
29
+ throw new TypeError("Tool result content must be an array");
30
+ for (const block of snapshot.content) {
31
+ if (typeof block !== "object" || block === null)
32
+ throw new TypeError("Invalid tool result content block");
33
+ const type = Reflect.get(block, "type");
34
+ if (type === "text" && typeof Reflect.get(block, "text") === "string")
35
+ continue;
36
+ if (type === "image" &&
37
+ typeof Reflect.get(block, "data") === "string" &&
38
+ typeof Reflect.get(block, "mimeType") === "string") {
39
+ continue;
40
+ }
41
+ throw new TypeError("Invalid tool result content block");
42
+ }
43
+ if (snapshot.terminate !== undefined && typeof snapshot.terminate !== "boolean") {
44
+ throw new TypeError("Tool result terminate must be a boolean");
45
+ }
46
+ return snapshot;
47
+ }
48
+ /** Build the immutable terminal committed when a tool timeout wins. */
49
+ export function createTimeoutToolResult(toolName, timeoutMs) {
50
+ return createImmutableSnapshot({
51
+ content: [{ type: "text", text: `Tool "${toolName}" timed out after ${timeoutMs}ms and was terminated.` }],
52
+ details: {
53
+ omk: createToolResultEnvelope({
54
+ synthetic: true,
55
+ disposition: "timeout",
56
+ reason: `Tool "${toolName}" timed out after ${timeoutMs}ms`,
57
+ timeoutMs,
58
+ executionStarted: true,
59
+ }),
60
+ },
61
+ });
62
+ }
63
+ /** Build the immutable terminal committed when parent abort wins. */
64
+ export function createAbortedToolResult(executionStarted) {
65
+ return createImmutableSnapshot({
66
+ content: [{ type: "text", text: "Operation aborted" }],
67
+ details: {
68
+ omk: createToolResultEnvelope({
69
+ synthetic: true,
70
+ disposition: "aborted",
71
+ reason: "Operation aborted",
72
+ executionStarted,
73
+ }),
74
+ },
75
+ });
76
+ }
77
+ export function createErrorToolResult(message) {
78
+ return createImmutableSnapshot({ content: [{ type: "text", text: message }], details: {} });
79
+ }
80
+ /** Commit a real result or immutable timeout/abort result across the after hook boundary. */
81
+ export async function finalizeExecutedToolCall(options) {
82
+ const { currentContext, assistantMessage, prepared, executed, afterToolCall, signal } = options;
83
+ let result = executed.result;
84
+ let isError = executed.isError;
85
+ let syntheticFailure = false;
86
+ let terminalDisposition = executed.terminalDisposition;
87
+ try {
88
+ result = createValidatedToolResultSnapshot(result);
89
+ }
90
+ catch (error) {
91
+ result = createErrorToolResult(`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`);
92
+ isError = true;
93
+ syntheticFailure = true;
94
+ }
95
+ if (terminalDisposition === undefined && signal?.aborted) {
96
+ terminalDisposition = "aborted";
97
+ result = createAbortedToolResult(executed.executionStarted);
98
+ isError = true;
99
+ }
100
+ if (terminalDisposition === undefined && afterToolCall && !syntheticFailure) {
101
+ try {
102
+ const bounded = await awaitWithAbort(() => afterToolCall({
103
+ assistantMessage,
104
+ toolCall: prepared.toolCall,
105
+ args: prepared.args,
106
+ result,
107
+ isError,
108
+ context: currentContext,
109
+ }, signal), signal);
110
+ if (bounded.kind === "aborted" || signal?.aborted) {
111
+ terminalDisposition = "aborted";
112
+ result = createAbortedToolResult(executed.executionStarted);
113
+ isError = true;
114
+ }
115
+ else if (bounded.value) {
116
+ result = {
117
+ content: bounded.value.content ?? result.content,
118
+ details: Object.hasOwn(bounded.value, "details") ? bounded.value.details : result.details,
119
+ terminate: bounded.value.terminate ?? result.terminate,
120
+ };
121
+ isError = bounded.value.isError ?? isError;
122
+ }
123
+ }
124
+ catch (error) {
125
+ result = createErrorToolResult(error instanceof Error ? error.message : String(error));
126
+ isError = true;
127
+ syntheticFailure = true;
128
+ }
129
+ }
130
+ if (terminalDisposition === undefined) {
131
+ try {
132
+ result = createValidatedToolResultSnapshot(result);
133
+ }
134
+ catch (error) {
135
+ result = createErrorToolResult(`Invalid tool result: ${error instanceof Error ? error.message : String(error)}`);
136
+ isError = true;
137
+ syntheticFailure = true;
138
+ }
139
+ }
140
+ else {
141
+ isError = true;
142
+ }
143
+ const envelope = terminalDisposition !== undefined
144
+ ? createToolResultEnvelope({
145
+ disposition: terminalDisposition,
146
+ synthetic: true,
147
+ executionStarted: executed.executionStarted,
148
+ reason: terminalDisposition === "timeout"
149
+ ? `Tool "${prepared.toolCall.name}" timed out after ${prepared.timeoutMs}ms`
150
+ : "Operation aborted",
151
+ ...(terminalDisposition === "timeout" ? { timeoutMs: prepared.timeoutMs } : {}),
152
+ })
153
+ : createToolResultEnvelope({
154
+ disposition: isError ? "failed" : "completed",
155
+ synthetic: syntheticFailure,
156
+ executionStarted: executed.executionStarted,
157
+ });
158
+ return {
159
+ toolCall: prepared.toolCall,
160
+ result,
161
+ isError,
162
+ envelope,
163
+ isRealPromiseSettled: executed.isRealPromiseSettled,
164
+ commitTerminal: executed.commitTerminal,
165
+ };
166
+ }
167
+ function isPlainDetails(details) {
168
+ if (typeof details !== "object" || details === null || Array.isArray(details))
169
+ return false;
170
+ const prototype = Object.getPrototypeOf(details);
171
+ return prototype === Object.prototype || prototype === null;
172
+ }
173
+ /** Preserve compatibility details while replacing any untrusted `omk` field. */
174
+ export function stampToolResultEnvelope(details, envelope) {
175
+ if (!isToolResultEnvelope(envelope))
176
+ throw new TypeError("Refusing to persist an invalid tool-result/v2 envelope");
177
+ if (details === undefined)
178
+ return { omk: envelope };
179
+ if (!isPlainDetails(details))
180
+ return { originalDetails: details, omk: envelope };
181
+ const preserved = { ...details };
182
+ delete preserved.omk;
183
+ return { ...preserved, omk: envelope };
184
+ }
185
+ //# sourceMappingURL=tool-execution-boundary.js.map
@@ -0,0 +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"]}
@@ -0,0 +1,31 @@
1
+ /** Browser-safe resource claims for the opt-in dag-v2 scheduler. */
2
+ import type { ToolParallelPolicy } from "./parallel-tool-batch.ts";
3
+ import type { AgentTool, ResourceKeyResolver, ToolResourceClaim } from "./types.ts";
4
+ export { resolvePathClaimKey, resolveToolClaims } from "./builtin-tool-resource-claims.ts";
5
+ export type { ResourceAccess, ToolResourceAccess, ToolResourceClaim, ToolResourceClaims, ToolResourceClaimsContext, } from "./types.ts";
6
+ export type ToolClaimResolution = {
7
+ kind: "exclusive";
8
+ } | {
9
+ kind: "claims";
10
+ claims: ToolResourceClaim[];
11
+ };
12
+ export interface ClaimableToolCall {
13
+ id?: string;
14
+ name: string;
15
+ arguments: unknown;
16
+ }
17
+ export type RegisteredToolClaimDefinition = Pick<AgentTool, "name" | "executionMode" | "resourceClaims">;
18
+ export interface ResolveToolClaimsOptions {
19
+ cwd: string;
20
+ toolPolicies?: ReadonlyMap<string, ToolParallelPolicy>;
21
+ registeredTools?: readonly RegisteredToolClaimDefinition[];
22
+ strictExtensionClaims?: boolean;
23
+ resourceKeyResolver?: ResourceKeyResolver;
24
+ }
25
+ /** Resolve one call, failing malformed or rejected extension claims closed. */
26
+ export declare function resolveToolClaimsForCall(toolCall: ClaimableToolCall, options: ResolveToolClaimsOptions): Promise<ToolClaimResolution>;
27
+ export declare function compareClaims(left: ToolResourceClaim, right: ToolResourceClaim): number;
28
+ export declare function canonicalizeClaims(claims: readonly ToolResourceClaim[]): ToolResourceClaim[];
29
+ export declare function claimsConflict(left: ToolResourceClaim, right: ToolResourceClaim): boolean;
30
+ export declare function resolutionsConflict(left: ToolClaimResolution, right: ToolClaimResolution): boolean;
31
+ //# sourceMappingURL=tool-resource-claims.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-resource-claims.d.ts","sourceRoot":"","sources":["../src/tool-resource-claims.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAYpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAkB,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpG,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3F,YAAY,EACX,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,GACzB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,mBAAmB,GAAG;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,iBAAiB,EAAE,CAAA;CAAE,CAAC;AAE1G,MAAM,WAAW,iBAAiB;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,6BAA6B,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,gBAAgB,CAAC,CAAC;AAEzG,MAAM,WAAW,wBAAwB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACvD,eAAe,CAAC,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAC3D,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC1C;AAkCD,+EAA+E;AAC/E,wBAAsB,wBAAwB,CAC7C,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,EAAE,wBAAwB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CAkC9B;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAWvF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,GAAG,iBAAiB,EAAE,CAe5F;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CASzF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAQlG","sourcesContent":["/** Browser-safe resource claims for the opt-in dag-v2 scheduler. */\n\nimport {\n\tfindRegisteredToolClaimDefinition,\n\tisBuiltinPathClaimTool,\n\tisPlainArguments,\n\tpathClaimsOverlap,\n\tresolveBuiltinPathClaimWithIdentity,\n\tresolvePathClaimWithIdentity,\n\tresolveToolClaims,\n\tresolveToolPolicy,\n} from \"./builtin-tool-resource-claims.ts\";\nimport type { ToolParallelPolicy } from \"./parallel-tool-batch.ts\";\nimport { NEVER_PARALLEL_TOOLS } from \"./parallel-tool-batch.ts\";\nimport type { AgentTool, ResourceAccess, ResourceKeyResolver, ToolResourceClaim } from \"./types.ts\";\n\nexport { resolvePathClaimKey, resolveToolClaims } from \"./builtin-tool-resource-claims.ts\";\nexport type {\n\tResourceAccess,\n\tToolResourceAccess,\n\tToolResourceClaim,\n\tToolResourceClaims,\n\tToolResourceClaimsContext,\n} from \"./types.ts\";\n\nexport type ToolClaimResolution = { kind: \"exclusive\" } | { kind: \"claims\"; claims: ToolResourceClaim[] };\n\nexport interface ClaimableToolCall {\n\tid?: string;\n\tname: string;\n\targuments: unknown;\n}\n\nexport type RegisteredToolClaimDefinition = Pick<AgentTool, \"name\" | \"executionMode\" | \"resourceClaims\">;\n\nexport interface ResolveToolClaimsOptions {\n\tcwd: string;\n\ttoolPolicies?: ReadonlyMap<string, ToolParallelPolicy>;\n\tregisteredTools?: readonly RegisteredToolClaimDefinition[];\n\tstrictExtensionClaims?: boolean;\n\tresourceKeyResolver?: ResourceKeyResolver;\n}\n\nfunction isNonPathKind(value: unknown): value is Exclude<ToolResourceClaim[\"kind\"], \"path\"> {\n\treturn value === \"session\" || value === \"terminal\" || value === \"network\" || value === \"global\";\n}\n\nasync function normalizeCustomClaims(value: unknown, options: ResolveToolClaimsOptions): Promise<ToolClaimResolution> {\n\tif (value === \"exclusive\") return { kind: \"exclusive\" };\n\tif (!Array.isArray(value) || value.length === 0) return { kind: \"exclusive\" };\n\n\tconst claims: ToolResourceClaim[] = [];\n\tlet hasExclusiveAccess = false;\n\tfor (const candidate of value) {\n\t\tif (!isPlainArguments(candidate) || typeof candidate.key !== \"string\" || candidate.key.trim().length === 0) {\n\t\t\treturn { kind: \"exclusive\" };\n\t\t}\n\t\tif (candidate.kind === \"path\") {\n\t\t\tif (candidate.access !== \"read\" && candidate.access !== \"write\") return { kind: \"exclusive\" };\n\t\t\tconst pathResolution = await resolvePathClaimWithIdentity(candidate.key, candidate.access, options);\n\t\t\tif (pathResolution.kind === \"exclusive\") return pathResolution;\n\t\t\tclaims.push(...pathResolution.claims);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!isNonPathKind(candidate.kind)) return { kind: \"exclusive\" };\n\t\tif (candidate.access !== \"read\" && candidate.access !== \"write\" && candidate.access !== \"exclusive\") {\n\t\t\treturn { kind: \"exclusive\" };\n\t\t}\n\t\tconst access: ResourceAccess = candidate.access;\n\t\tclaims.push({ kind: candidate.kind, key: candidate.key, access });\n\t\tif (access === \"exclusive\") hasExclusiveAccess = true;\n\t}\n\treturn hasExclusiveAccess ? { kind: \"exclusive\" } : { kind: \"claims\", claims };\n}\n\n/** Resolve one call, failing malformed or rejected extension claims closed. */\nexport async function resolveToolClaimsForCall(\n\ttoolCall: ClaimableToolCall,\n\toptions: ResolveToolClaimsOptions,\n): Promise<ToolClaimResolution> {\n\tconst registeredTool = findRegisteredToolClaimDefinition(toolCall.name, options.registeredTools);\n\tif (!registeredTool?.resourceClaims) {\n\t\tif (\n\t\t\toptions.resourceKeyResolver &&\n\t\t\tisBuiltinPathClaimTool(toolCall.name) &&\n\t\t\tisPlainArguments(toolCall.arguments) &&\n\t\t\t!NEVER_PARALLEL_TOOLS.has(toolCall.name) &&\n\t\t\tresolveToolPolicy(toolCall.name, options) !== \"sequential\"\n\t\t) {\n\t\t\treturn resolveBuiltinPathClaimWithIdentity(toolCall, options);\n\t\t}\n\t\treturn resolveToolClaims(toolCall, options);\n\t}\n\tif (!isPlainArguments(toolCall.arguments)) return { kind: \"exclusive\" };\n\n\tlet resolution: ToolClaimResolution;\n\ttry {\n\t\tconst claims = await registeredTool.resourceClaims(toolCall.arguments, {\n\t\t\tcwd: options.cwd,\n\t\t\ttoolCallId: toolCall.id ?? \"\",\n\t\t});\n\t\tresolution = await normalizeCustomClaims(claims, options);\n\t} catch {\n\t\treturn { kind: \"exclusive\" };\n\t}\n\tif (\n\t\tNEVER_PARALLEL_TOOLS.has(toolCall.name) ||\n\t\ttoolCall.name === \"bash\" ||\n\t\tresolveToolPolicy(toolCall.name, options) === \"sequential\"\n\t) {\n\t\treturn { kind: \"exclusive\" };\n\t}\n\treturn resolution;\n}\n\nexport function compareClaims(left: ToolResourceClaim, right: ToolResourceClaim): number {\n\tif (left.kind !== right.kind) return left.kind < right.kind ? -1 : 1;\n\tif (left.key !== right.key) return left.key < right.key ? -1 : 1;\n\tif (left.access !== right.access) return left.access < right.access ? -1 : 1;\n\tconst leftReal = left.kind === \"path\" ? (left.realKey ?? \"\") : \"\";\n\tconst rightReal = right.kind === \"path\" ? (right.realKey ?? \"\") : \"\";\n\tif (leftReal !== rightReal) return leftReal < rightReal ? -1 : 1;\n\tconst leftInode = left.kind === \"path\" ? (left.inodeKey ?? \"\") : \"\";\n\tconst rightInode = right.kind === \"path\" ? (right.inodeKey ?? \"\") : \"\";\n\tif (leftInode !== rightInode) return leftInode < rightInode ? -1 : 1;\n\treturn 0;\n}\n\nexport function canonicalizeClaims(claims: readonly ToolResourceClaim[]): ToolResourceClaim[] {\n\treturn claims\n\t\t.map(\n\t\t\t(claim): ToolResourceClaim =>\n\t\t\t\tclaim.kind === \"path\"\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tkind: \"path\",\n\t\t\t\t\t\t\tkey: claim.key,\n\t\t\t\t\t\t\taccess: claim.access,\n\t\t\t\t\t\t\t...(claim.realKey === undefined ? {} : { realKey: claim.realKey }),\n\t\t\t\t\t\t\t...(claim.inodeKey === undefined ? {} : { inodeKey: claim.inodeKey }),\n\t\t\t\t\t\t}\n\t\t\t\t\t: { kind: claim.kind, key: claim.key, access: claim.access },\n\t\t)\n\t\t.sort(compareClaims);\n}\n\nexport function claimsConflict(left: ToolResourceClaim, right: ToolResourceClaim): boolean {\n\tif (left.access === \"exclusive\" || right.access === \"exclusive\") return true;\n\tif (left.kind !== right.kind) return false;\n\tif (left.kind === \"path\" && right.kind === \"path\") {\n\t\tif (!pathClaimsOverlap(left, right)) return false;\n\t} else if (left.key !== right.key) {\n\t\treturn false;\n\t}\n\treturn !(left.access === \"read\" && right.access === \"read\");\n}\n\nexport function resolutionsConflict(left: ToolClaimResolution, right: ToolClaimResolution): boolean {\n\tif (left.kind === \"exclusive\" || right.kind === \"exclusive\") return true;\n\tfor (const leftClaim of left.claims) {\n\t\tfor (const rightClaim of right.claims) {\n\t\t\tif (claimsConflict(leftClaim, rightClaim)) return true;\n\t\t}\n\t}\n\treturn false;\n}\n"]}