@graphorin/agent 0.6.0 → 0.7.0

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 (110) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +31 -11
  3. package/dist/errors/index.d.ts +17 -4
  4. package/dist/errors/index.d.ts.map +1 -1
  5. package/dist/errors/index.js +19 -3
  6. package/dist/errors/index.js.map +1 -1
  7. package/dist/factory.d.ts.map +1 -1
  8. package/dist/factory.js +153 -1930
  9. package/dist/factory.js.map +1 -1
  10. package/dist/fanout/index.d.ts +13 -1
  11. package/dist/fanout/index.d.ts.map +1 -1
  12. package/dist/fanout/index.js +13 -4
  13. package/dist/fanout/index.js.map +1 -1
  14. package/dist/index.d.ts +7 -6
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +11 -9
  17. package/dist/index.js.map +1 -1
  18. package/dist/lateral-leak/index.js +1 -1
  19. package/dist/package.js +6 -0
  20. package/dist/package.js.map +1 -0
  21. package/dist/run-state/index.d.ts +32 -6
  22. package/dist/run-state/index.d.ts.map +1 -1
  23. package/dist/run-state/index.js +46 -22
  24. package/dist/run-state/index.js.map +1 -1
  25. package/dist/runtime/agent-surface.js +122 -0
  26. package/dist/runtime/agent-surface.js.map +1 -0
  27. package/dist/runtime/agent-to-tool.d.ts +51 -0
  28. package/dist/runtime/agent-to-tool.d.ts.map +1 -0
  29. package/dist/runtime/agent-to-tool.js +145 -0
  30. package/dist/runtime/agent-to-tool.js.map +1 -0
  31. package/dist/runtime/approvals.js +0 -0
  32. package/dist/runtime/approvals.js.map +1 -0
  33. package/dist/runtime/dispatch.js +108 -0
  34. package/dist/runtime/dispatch.js.map +1 -0
  35. package/dist/runtime/executor-wiring.js +128 -0
  36. package/dist/runtime/executor-wiring.js.map +1 -0
  37. package/dist/runtime/fallback-chain.js +139 -0
  38. package/dist/runtime/fallback-chain.js.map +1 -0
  39. package/dist/runtime/handoff.js +307 -0
  40. package/dist/runtime/handoff.js.map +1 -0
  41. package/dist/runtime/messages.d.ts +22 -0
  42. package/dist/runtime/messages.d.ts.map +1 -0
  43. package/dist/runtime/messages.js +204 -0
  44. package/dist/runtime/messages.js.map +1 -0
  45. package/dist/runtime/provider-events.js +117 -0
  46. package/dist/runtime/provider-events.js.map +1 -0
  47. package/dist/runtime/run-compaction.js +210 -0
  48. package/dist/runtime/run-compaction.js.map +1 -0
  49. package/dist/runtime/run-finish.js +48 -0
  50. package/dist/runtime/run-finish.js.map +1 -0
  51. package/dist/runtime/run-gates.js +336 -0
  52. package/dist/runtime/run-gates.js.map +1 -0
  53. package/dist/runtime/run-init.js +81 -0
  54. package/dist/runtime/run-init.js.map +1 -0
  55. package/dist/runtime/run-input.js +46 -0
  56. package/dist/runtime/run-input.js.map +1 -0
  57. package/dist/runtime/step-catalogue.js +173 -0
  58. package/dist/runtime/step-catalogue.js.map +1 -0
  59. package/dist/runtime/tool-call-walk.js +189 -0
  60. package/dist/runtime/tool-call-walk.js.map +1 -0
  61. package/dist/runtime/tool-wiring.js +159 -0
  62. package/dist/runtime/tool-wiring.js.map +1 -0
  63. package/dist/tooling/adapters.js +1 -1
  64. package/dist/tooling/dataflow.js +1 -1
  65. package/dist/tooling/policy.js +2 -0
  66. package/dist/tooling/policy.js.map +1 -1
  67. package/dist/types.d.ts +63 -13
  68. package/dist/types.d.ts.map +1 -1
  69. package/package.json +20 -20
  70. package/src/errors/index.ts +320 -0
  71. package/src/evaluator-optimizer/index.ts +212 -0
  72. package/src/factory.ts +957 -0
  73. package/src/fallback/index.ts +108 -0
  74. package/src/fanout/index.ts +523 -0
  75. package/src/filters/index.ts +347 -0
  76. package/src/index.ts +180 -0
  77. package/src/internal/ids.ts +46 -0
  78. package/src/internal/usage-accumulator.ts +90 -0
  79. package/src/lateral-leak/causality-monitor.ts +221 -0
  80. package/src/lateral-leak/index.ts +35 -0
  81. package/src/lateral-leak/merge-guard.ts +151 -0
  82. package/src/lateral-leak/protocol-guard.ts +222 -0
  83. package/src/preferred-model/index.ts +210 -0
  84. package/src/progress/index.ts +238 -0
  85. package/src/run-state/index.ts +607 -0
  86. package/src/runtime/agent-surface.ts +218 -0
  87. package/src/runtime/agent-to-tool.ts +323 -0
  88. package/src/runtime/approvals.ts +0 -0
  89. package/src/runtime/dispatch.ts +183 -0
  90. package/src/runtime/executor-wiring.ts +331 -0
  91. package/src/runtime/fallback-chain.ts +250 -0
  92. package/src/runtime/handoff.ts +428 -0
  93. package/src/runtime/messages.ts +309 -0
  94. package/src/runtime/provider-events.ts +175 -0
  95. package/src/runtime/run-compaction.ts +288 -0
  96. package/src/runtime/run-finish.ts +93 -0
  97. package/src/runtime/run-gates.ts +419 -0
  98. package/src/runtime/run-init.ts +169 -0
  99. package/src/runtime/run-input.ts +102 -0
  100. package/src/runtime/step-catalogue.ts +338 -0
  101. package/src/runtime/tool-call-walk.ts +301 -0
  102. package/src/runtime/tool-wiring.ts +218 -0
  103. package/src/testing/replay-provider.ts +121 -0
  104. package/src/tooling/adapters.ts +403 -0
  105. package/src/tooling/catalogue.ts +36 -0
  106. package/src/tooling/dataflow.ts +171 -0
  107. package/src/tooling/plan.ts +123 -0
  108. package/src/tooling/policy.ts +67 -0
  109. package/src/tooling/registry-build.ts +191 -0
  110. package/src/types.ts +696 -0
@@ -0,0 +1,108 @@
1
+ import { renderToolErrorMessage } from "./messages.js";
2
+ import { createExecutorEventBridge } from "../tooling/adapters.js";
3
+ import { TOOL_SEARCH_NAME, recordToolSearchPromotions } from "./tool-wiring.js";
4
+
5
+ //#region src/runtime/dispatch.ts
6
+ /**
7
+ * Dispatch a batch of (non-handoff) tool calls through the
8
+ * {@link ToolExecutor} and surface the results as `AgentEvent`s.
9
+ *
10
+ * The agent owns the `tool.execute.start` / `.end` / `.error`
11
+ * lifecycle (derived deterministically from the returned
12
+ * {@link CompletedToolCall} outcomes) so every outcome kind -
13
+ * success, unknown-tool, invalid-input, sanitization-blocked,
14
+ * execution error - yields a consistent event and tool message,
15
+ * preserving the pre-WI-03 stream shape (R10). The executor's
16
+ * genuinely-live streaming events (`tool.execute.progress` /
17
+ * `.partial`, emitted only by streaming-hint tools) are bridged
18
+ * through Adapter E while the batch runs and are purely additive.
19
+ *
20
+ * Parallelism (WI-04): the executor runs independent calls in this
21
+ * batch concurrently, bounded by `maxParallelTools`. `tool.execute.start`
22
+ * is emitted up-front in call order and `.end` / `.error` after the
23
+ * batch settles, also in call order - so result mapping and tool-message
24
+ * history are deterministic regardless of which call finishes first,
25
+ * while `.progress` / `.partial` events for concurrent calls interleave
26
+ * (keyed by `toolCallId`). Tools declaring `executionMode: 'sequential'`
27
+ * are serialised by the executor and never overlap.
28
+ */
29
+ async function* dispatchToolBatch(env, calls, executor, runContext, stepNum, dispatchOpts = {}) {
30
+ const { state, messages, causalityMonitor, promotedDeferred, activeRunCapability } = env;
31
+ const { executorBridgeSlot } = env;
32
+ if (calls.length === 0) return;
33
+ for (const call of calls) yield {
34
+ type: "tool.execute.start",
35
+ toolCallId: call.toolCallId,
36
+ toolName: call.toolName
37
+ };
38
+ const bridge = createExecutorEventBridge();
39
+ executorBridgeSlot.current = bridge;
40
+ const resultsPromise = executor.executeBatch({
41
+ calls,
42
+ runContext,
43
+ stepNumber: stepNum,
44
+ ...dispatchOpts.disableRepair !== void 0 ? { disableRepair: dispatchOpts.disableRepair } : {},
45
+ ...activeRunCapability !== void 0 ? { capability: activeRunCapability } : {}
46
+ });
47
+ const closeOnSettle = resultsPromise.then(() => bridge.close(), () => bridge.close());
48
+ for await (const event of bridge.drain()) if (event.type === "tool.execute.progress" || event.type === "tool.execute.partial") yield event;
49
+ await closeOnSettle;
50
+ executorBridgeSlot.current = void 0;
51
+ const completed = await resultsPromise;
52
+ const byCallId = new Map(completed.map((c) => [c.outcome.toolCallId, c]));
53
+ const stepEntry = state.steps[state.steps.length - 1];
54
+ for (const call of calls) {
55
+ const result = byCallId.get(call.toolCallId);
56
+ if (result === void 0) continue;
57
+ if (stepEntry !== void 0) stepEntry.toolCalls.push(result);
58
+ const outcome = result.outcome;
59
+ if ("kind" in outcome) {
60
+ yield {
61
+ type: "tool.execute.error",
62
+ toolCallId: call.toolCallId,
63
+ toolName: call.toolName,
64
+ error: outcome
65
+ };
66
+ const text = renderToolErrorMessage(outcome);
67
+ messages.push({
68
+ role: "tool",
69
+ toolCallId: call.toolCallId,
70
+ content: text
71
+ });
72
+ state.messages.push({
73
+ role: "tool",
74
+ toolCallId: call.toolCallId,
75
+ content: text
76
+ });
77
+ causalityMonitor?.recordCall(`tool.error:${call.toolName}`);
78
+ } else {
79
+ const output = outcome.output;
80
+ yield {
81
+ type: "tool.execute.end",
82
+ toolCallId: call.toolCallId,
83
+ toolName: call.toolName,
84
+ result: output,
85
+ durationMs: outcome.durationMs
86
+ };
87
+ const handle = outcome.resultHandle;
88
+ const rendered = handle !== void 0 ? `${handle.preview}\n\n[Full result${handle.bytes !== void 0 ? ` (${handle.bytes} bytes)` : ""} stored behind a handle. Call read_result with handle ${JSON.stringify(handle.uri)} to retrieve it - optionally narrow with offset/length (bytes) or startLine/endLine.]` : typeof output === "string" ? output : JSON.stringify(output);
89
+ const text = rendered === void 0 || rendered.trim().length === 0 ? "(tool ran successfully with no output)" : rendered;
90
+ messages.push({
91
+ role: "tool",
92
+ toolCallId: call.toolCallId,
93
+ content: text
94
+ });
95
+ state.messages.push({
96
+ role: "tool",
97
+ toolCallId: call.toolCallId,
98
+ content: text
99
+ });
100
+ causalityMonitor?.recordCall(`tool:${call.toolName}`);
101
+ if (call.toolName === TOOL_SEARCH_NAME) recordToolSearchPromotions(output, promotedDeferred);
102
+ }
103
+ }
104
+ }
105
+
106
+ //#endregion
107
+ export { dispatchToolBatch };
108
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.js","names":[],"sources":["../../src/runtime/dispatch.ts"],"sourcesContent":["/**\n * Tool-batch dispatch bridging for the agent runtime: routes a batch of\n * (non-handoff) tool calls through the shared `@graphorin/tools`\n * executor and surfaces the outcomes as the long-standing\n * `tool.execute.*` agent events + tool messages (R10), including the\n * WI-10 result-handle rendering and the WI-05 `tool_search` promotion\n * fold. Extracted verbatim from `factory.ts` (issue #23); the former\n * run-loop closure now takes an explicit {@link DispatchRunEnv}.\n *\n * @packageDocumentation\n */\n\nimport type {\n AgentEvent,\n CompletedToolCall,\n Message,\n RunContext,\n RunState,\n ToolCall,\n} from '@graphorin/core';\nimport type { ToolExecutor } from '@graphorin/tools/executor';\nimport type { CausalityMonitor } from '../lateral-leak/causality-monitor.js';\nimport { createExecutorEventBridge, type ExecutorEventBridge } from '../tooling/adapters.js';\nimport { renderToolErrorMessage } from './messages.js';\nimport type { MutableRunState } from './run-input.js';\nimport { recordToolSearchPromotions, TOOL_SEARCH_NAME } from './tool-wiring.js';\n\n/**\n * Shape of the run-bound `dispatchBatch` wrapper the factory threads\n * into the resume-dispatch and tool-call-walk modules, so their call\n * sites read exactly as the former in-loop closure calls did.\n */\nexport type DispatchBatchFn<TDeps, TOutput> = (\n calls: ReadonlyArray<ToolCall>,\n executor: ToolExecutor,\n runContext: RunContext<TDeps>,\n stepNum: number,\n dispatchOpts?: { readonly disableRepair?: boolean },\n) => AsyncGenerator<AgentEvent<TOutput>, void, void>;\n\n/**\n * The run-scoped context a batch dispatch operates on. Field names\n * mirror the run-loop locals the former closure captured; the\n * executor-bridge slot is the shared mutable cell the warm-up\n * streaming sink reads while a batch is in flight.\n */\nexport interface DispatchRunEnv {\n readonly state: MutableRunState & RunState;\n readonly messages: Message[];\n readonly causalityMonitor: CausalityMonitor | undefined;\n readonly promotedDeferred: Set<string>;\n readonly activeRunCapability: 'read-only' | undefined;\n readonly executorBridgeSlot: { current: ExecutorEventBridge | undefined };\n}\n\n/**\n * Dispatch a batch of (non-handoff) tool calls through the\n * {@link ToolExecutor} and surface the results as `AgentEvent`s.\n *\n * The agent owns the `tool.execute.start` / `.end` / `.error`\n * lifecycle (derived deterministically from the returned\n * {@link CompletedToolCall} outcomes) so every outcome kind -\n * success, unknown-tool, invalid-input, sanitization-blocked,\n * execution error - yields a consistent event and tool message,\n * preserving the pre-WI-03 stream shape (R10). The executor's\n * genuinely-live streaming events (`tool.execute.progress` /\n * `.partial`, emitted only by streaming-hint tools) are bridged\n * through Adapter E while the batch runs and are purely additive.\n *\n * Parallelism (WI-04): the executor runs independent calls in this\n * batch concurrently, bounded by `maxParallelTools`. `tool.execute.start`\n * is emitted up-front in call order and `.end` / `.error` after the\n * batch settles, also in call order - so result mapping and tool-message\n * history are deterministic regardless of which call finishes first,\n * while `.progress` / `.partial` events for concurrent calls interleave\n * (keyed by `toolCallId`). Tools declaring `executionMode: 'sequential'`\n * are serialised by the executor and never overlap.\n */\nexport async function* dispatchToolBatch<TDeps, TOutput>(\n env: DispatchRunEnv,\n calls: ReadonlyArray<ToolCall>,\n executor: ToolExecutor,\n runContext: RunContext<TDeps>,\n stepNum: number,\n dispatchOpts: { readonly disableRepair?: boolean } = {},\n): AsyncGenerator<AgentEvent<TOutput>, void, void> {\n const { state, messages, causalityMonitor, promotedDeferred, activeRunCapability } = env;\n const { executorBridgeSlot } = env;\n if (calls.length === 0) return;\n for (const call of calls) {\n // W-049: toolName duplicated for subscriber convenience.\n yield { type: 'tool.execute.start', toolCallId: call.toolCallId, toolName: call.toolName };\n }\n\n const bridge = createExecutorEventBridge();\n executorBridgeSlot.current = bridge;\n const resultsPromise = executor.executeBatch({\n calls,\n runContext,\n stepNumber: stepNum,\n ...(dispatchOpts.disableRepair !== undefined\n ? { disableRepair: dispatchOpts.disableRepair }\n : {}),\n // D2: the run's capability restriction rides every batch.\n ...(activeRunCapability !== undefined ? { capability: activeRunCapability } : {}),\n });\n // Close the bridge once the batch settles so `drain()` ends; the\n // executor catches per-call errors, so the batch never rejects.\n const closeOnSettle = resultsPromise.then(\n () => bridge.close(),\n () => bridge.close(),\n );\n for await (const event of bridge.drain()) {\n if (event.type === 'tool.execute.progress' || event.type === 'tool.execute.partial') {\n yield event as AgentEvent<TOutput>;\n }\n }\n await closeOnSettle;\n executorBridgeSlot.current = undefined;\n\n const completed = await resultsPromise;\n const byCallId = new Map(completed.map((c) => [c.outcome.toolCallId, c]));\n const stepEntry = state.steps[state.steps.length - 1];\n for (const call of calls) {\n const result = byCallId.get(call.toolCallId);\n if (result === undefined) continue;\n if (stepEntry !== undefined) {\n (stepEntry.toolCalls as CompletedToolCall[]).push(result);\n }\n const outcome = result.outcome;\n if ('kind' in outcome) {\n yield {\n type: 'tool.execute.error',\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n error: outcome,\n };\n const text = renderToolErrorMessage(outcome);\n messages.push({ role: 'tool', toolCallId: call.toolCallId, content: text });\n state.messages.push({ role: 'tool', toolCallId: call.toolCallId, content: text });\n causalityMonitor?.recordCall(`tool.error:${call.toolName}`);\n } else {\n const output = outcome.output;\n yield {\n type: 'tool.execute.end',\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n result: output,\n durationMs: outcome.durationMs,\n };\n // WI-10 (P1-4): when the result spilled to a handle, inline only\n // the bounded preview plus a retrieval hint so the full blob never\n // enters the context window - the model fetches the rest on demand\n // via `read_result`. Inlined results serialise exactly as before\n // (preserves the happy-path message contract, R10).\n const handle = outcome.resultHandle;\n const rendered =\n handle !== undefined\n ? `${handle.preview}\\n\\n[Full result${\n handle.bytes !== undefined ? ` (${handle.bytes} bytes)` : ''\n } stored behind a handle. Call read_result with handle ${JSON.stringify(\n handle.uri,\n )} to retrieve it - optionally narrow with offset/length (bytes) or startLine/endLine.]`\n : typeof output === 'string'\n ? output\n : JSON.stringify(output);\n // C3 (ACI): an empty body reads as a glitch to models; say\n // explicitly that the tool ran and simply had nothing to print.\n const text =\n rendered === undefined || rendered.trim().length === 0\n ? '(tool ran successfully with no output)'\n : rendered;\n messages.push({ role: 'tool', toolCallId: call.toolCallId, content: text });\n state.messages.push({ role: 'tool', toolCallId: call.toolCallId, content: text });\n causalityMonitor?.recordCall(`tool:${call.toolName}`);\n // WI-05: a successful `tool_search` promotes its matches so the\n // catalogue advertises them on the next step.\n if (call.toolName === TOOL_SEARCH_NAME) {\n recordToolSearchPromotions(output, promotedDeferred);\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8EA,gBAAuB,kBACrB,KACA,OACA,UACA,YACA,SACA,eAAqD,EAAE,EACN;CACjD,MAAM,EAAE,OAAO,UAAU,kBAAkB,kBAAkB,wBAAwB;CACrF,MAAM,EAAE,uBAAuB;AAC/B,KAAI,MAAM,WAAW,EAAG;AACxB,MAAK,MAAM,QAAQ,MAEjB,OAAM;EAAE,MAAM;EAAsB,YAAY,KAAK;EAAY,UAAU,KAAK;EAAU;CAG5F,MAAM,SAAS,2BAA2B;AAC1C,oBAAmB,UAAU;CAC7B,MAAM,iBAAiB,SAAS,aAAa;EAC3C;EACA;EACA,YAAY;EACZ,GAAI,aAAa,kBAAkB,SAC/B,EAAE,eAAe,aAAa,eAAe,GAC7C,EAAE;EAEN,GAAI,wBAAwB,SAAY,EAAE,YAAY,qBAAqB,GAAG,EAAE;EACjF,CAAC;CAGF,MAAM,gBAAgB,eAAe,WAC7B,OAAO,OAAO,QACd,OAAO,OAAO,CACrB;AACD,YAAW,MAAM,SAAS,OAAO,OAAO,CACtC,KAAI,MAAM,SAAS,2BAA2B,MAAM,SAAS,uBAC3D,OAAM;AAGV,OAAM;AACN,oBAAmB,UAAU;CAE7B,MAAM,YAAY,MAAM;CACxB,MAAM,WAAW,IAAI,IAAI,UAAU,KAAK,MAAM,CAAC,EAAE,QAAQ,YAAY,EAAE,CAAC,CAAC;CACzE,MAAM,YAAY,MAAM,MAAM,MAAM,MAAM,SAAS;AACnD,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,SAAS,SAAS,IAAI,KAAK,WAAW;AAC5C,MAAI,WAAW,OAAW;AAC1B,MAAI,cAAc,OAChB,CAAC,UAAU,UAAkC,KAAK,OAAO;EAE3D,MAAM,UAAU,OAAO;AACvB,MAAI,UAAU,SAAS;AACrB,SAAM;IACJ,MAAM;IACN,YAAY,KAAK;IACjB,UAAU,KAAK;IACf,OAAO;IACR;GACD,MAAM,OAAO,uBAAuB,QAAQ;AAC5C,YAAS,KAAK;IAAE,MAAM;IAAQ,YAAY,KAAK;IAAY,SAAS;IAAM,CAAC;AAC3E,SAAM,SAAS,KAAK;IAAE,MAAM;IAAQ,YAAY,KAAK;IAAY,SAAS;IAAM,CAAC;AACjF,qBAAkB,WAAW,cAAc,KAAK,WAAW;SACtD;GACL,MAAM,SAAS,QAAQ;AACvB,SAAM;IACJ,MAAM;IACN,YAAY,KAAK;IACjB,UAAU,KAAK;IACf,QAAQ;IACR,YAAY,QAAQ;IACrB;GAMD,MAAM,SAAS,QAAQ;GACvB,MAAM,WACJ,WAAW,SACP,GAAG,OAAO,QAAQ,kBAChB,OAAO,UAAU,SAAY,KAAK,OAAO,MAAM,WAAW,GAC3D,wDAAwD,KAAK,UAC5D,OAAO,IACR,CAAC,yFACF,OAAO,WAAW,WAChB,SACA,KAAK,UAAU,OAAO;GAG9B,MAAM,OACJ,aAAa,UAAa,SAAS,MAAM,CAAC,WAAW,IACjD,2CACA;AACN,YAAS,KAAK;IAAE,MAAM;IAAQ,YAAY,KAAK;IAAY,SAAS;IAAM,CAAC;AAC3E,SAAM,SAAS,KAAK;IAAE,MAAM;IAAQ,YAAY,KAAK;IAAY,SAAS;IAAM,CAAC;AACjF,qBAAkB,WAAW,QAAQ,KAAK,WAAW;AAGrD,OAAI,KAAK,aAAa,iBACpB,4BAA2B,QAAQ,iBAAiB"}
@@ -0,0 +1,128 @@
1
+ import { PLAN_TOOL_NAME, createPlanTool } from "../tooling/plan.js";
2
+ import { buildMemoryGuard, buildSecretResolver, buildToolTokenCounter, createMemoryRegionReader } from "../tooling/adapters.js";
3
+ import { CODE_EXECUTE_NAME, CODE_SEARCH_NAME, READ_RESULT_NAME, TOOL_SEARCH_NAME, composeResultReaders, registerCodeMode, registerReadResult, registerToolSearch } from "./tool-wiring.js";
4
+ import { buildDataFlowGuard } from "../tooling/dataflow.js";
5
+ import { buildToolArgumentPolicy } from "../tooling/policy.js";
6
+ import { buildToolRegistry } from "../tooling/registry-build.js";
7
+ import { createDefaultSpillWriter, createFileResultReader } from "@graphorin/tools/result";
8
+ import { createToolExecutor } from "@graphorin/tools/executor";
9
+
10
+ //#region src/runtime/executor-wiring.ts
11
+ /**
12
+ * Construct the unified ToolExecutor once at warm-up (WI-03 / P0-1),
13
+ * bound to the registry above. Routing tool execution through the
14
+ * executor activates the documented tool fields the inline loop
15
+ * bypassed: per-tool `secretsAllowed` ACL, result truncation
16
+ * (`maxResultTokens` / `truncationStrategy`), inbound sanitization,
17
+ * memory-guard, idempotency keys and single-round repair.
18
+ *
19
+ * Durable HITL stays in the agent: approval is pre-screened below and
20
+ * suspends the run, so the executor's `ApprovalGate` only ever sees
21
+ * no-approval / pre-approved calls - it auto-grants and never blocks
22
+ * the generator (Adapter G).
23
+ *
24
+ * Sandbox note: `config.tools` are inline `tool({...})` closures that
25
+ * cannot be serialised to an out-of-process sandbox, and
26
+ * `resolveSandbox` defaults user-defined tools to `worker-threads`.
27
+ * Wiring a resolver that returned a real sandbox for that kind would
28
+ * break every inline tool, so `sandboxResolver` is intentionally left
29
+ * unset (the executor then runs inline - its documented fallback).
30
+ * The resolved policy is still surfaced on the tool-execute span /
31
+ * audit; real isolation applies to module-loadable (skill / MCP)
32
+ * tools and is wired when those land.
33
+ */
34
+ function wireToolExecution(deps) {
35
+ const { config, memory, agentId, executorBridgeSlot } = deps;
36
+ const { getActiveRunState, getActiveRunCapability, handoffToolNames } = deps;
37
+ const toolRegistry = buildToolRegistry({
38
+ ...config.tools !== void 0 ? { tools: config.tools } : {},
39
+ ...config.skills !== void 0 ? { skills: config.skills } : {}
40
+ }).registry;
41
+ registerToolSearch(toolRegistry, config.toolPromotion === "run-boundary" ? "next-run" : "next-step");
42
+ if (config.plan === true && toolRegistry.get(PLAN_TOOL_NAME) === void 0) toolRegistry.register(createPlanTool((todos) => {
43
+ const activeRunState = getActiveRunState();
44
+ if (activeRunState !== void 0) activeRunState.todos = todos;
45
+ }), {
46
+ kind: "built-in",
47
+ subsystem: "planning"
48
+ });
49
+ const spillWriter = createDefaultSpillWriter();
50
+ const fileResultReader = createFileResultReader({ artifactRoot: spillWriter.artifactRoot });
51
+ const externalResultReaders = config.resultReaders ?? [];
52
+ const resultReader = externalResultReaders.length === 0 ? fileResultReader : composeResultReaders([fileResultReader, ...externalResultReaders]);
53
+ registerReadResult(toolRegistry, resultReader, { force: externalResultReaders.length > 0 });
54
+ const toolApprovalGate = { request: async () => ({ granted: true }) };
55
+ const toolSecretResolver = buildSecretResolver();
56
+ const toolTokenCounter = buildToolTokenCounter();
57
+ const memoryGuardWiring = buildMemoryGuard(memory, memory === void 0 ? {} : { regionReader: createMemoryRegionReader(["working"], async (region) => {
58
+ if (region !== "working") return "";
59
+ const activeRunState = getActiveRunState();
60
+ const scope = {
61
+ userId: activeRunState?.userId ?? agentId,
62
+ ...activeRunState?.sessionId !== void 0 ? { sessionId: activeRunState.sessionId } : {},
63
+ agentId
64
+ };
65
+ try {
66
+ return await memory.working.compile(scope, agentId);
67
+ } catch {
68
+ return "";
69
+ }
70
+ }) });
71
+ const toolMemoryGuardFactory = memoryGuardWiring.memoryGuardFactory;
72
+ if (memory === void 0) {
73
+ const guarded = (config.tools ?? []).filter((t) => t.memoryGuardTier !== void 0);
74
+ if (guarded.length > 0) console.warn(`[graphorin/agent] ${guarded.length} tool(s) declare memoryGuardTier but no memory is wired - the DEC-153 snapshot/verify guard is skipped (SDF-1). Wire \`memory\` to activate it.`);
75
+ }
76
+ const toolDataFlowGuard = config.dataFlowPolicy !== void 0 && config.dataFlowPolicy.mode !== "off" ? buildDataFlowGuard(config.dataFlowPolicy) : void 0;
77
+ if (toolDataFlowGuard !== void 0 && (config.dataFlowPolicy?.guardTrifecta ?? true) && config.dataFlowPolicy?.treatPiiAsSensitive !== true) {
78
+ const effectiveTiers = config.dataFlowPolicy?.sensitiveTiers ?? ["secret"];
79
+ if (!(config.tools ?? []).some((t) => t.sensitivity !== void 0 && effectiveTiers.includes(t.sensitivity))) console.warn("[graphorin/agent] dataFlowPolicy is enabled but no registered tool declares a sensitivity within sensitiveTiers (default ['secret']) and treatPiiAsSensitive is off - the lethal-trifecta leg cannot arm; only the best-effort verbatim untrusted-to-sink probe is active. Tag private-data tools (sensitivity: 'secret') or widen sensitiveTiers / treatPiiAsSensitive.");
80
+ }
81
+ const { guard: toolArgumentPolicyGuard, capabilityFloor: ruleOfTwoCapabilityFloor } = buildToolArgumentPolicy(config.toolPolicy, config.ruleOfTwo);
82
+ const toolStreamingSink = (event) => executorBridgeSlot.current?.sink(event);
83
+ const makeToolExecutor = (registry, opts) => createToolExecutor({
84
+ registry,
85
+ approvalGate: toolApprovalGate,
86
+ secretResolver: toolSecretResolver,
87
+ tokenCounter: toolTokenCounter,
88
+ memoryGuardFactory: toolMemoryGuardFactory,
89
+ ...memoryGuardWiring.memoryRegionReader !== void 0 ? { memoryRegionReader: memoryGuardWiring.memoryRegionReader } : {},
90
+ spill: spillWriter,
91
+ ...toolDataFlowGuard !== void 0 ? { dataFlowGuard: toolDataFlowGuard } : {},
92
+ ...toolArgumentPolicyGuard !== void 0 ? { argumentPolicy: toolArgumentPolicyGuard } : {},
93
+ ...opts?.quiet === true ? {} : { streamingSink: toolStreamingSink },
94
+ ...config.maxParallelTools !== void 0 ? { maxParallelTools: config.maxParallelTools } : {},
95
+ ...config.toolRetry !== void 0 ? { retry: config.toolRetry } : {}
96
+ });
97
+ const toolExecutor = makeToolExecutor(toolRegistry);
98
+ const isCodeMode = config.toolInvocation === "code-mode";
99
+ let codeModeAdvertised = [];
100
+ if (isCodeMode) {
101
+ const reserved = new Set([
102
+ CODE_EXECUTE_NAME,
103
+ CODE_SEARCH_NAME,
104
+ TOOL_SEARCH_NAME,
105
+ READ_RESULT_NAME,
106
+ ...handoffToolNames
107
+ ]);
108
+ const metas = registerCodeMode(toolRegistry, makeToolExecutor(toolRegistry, { quiet: true }), reserved, getActiveRunCapability);
109
+ registerReadResult(toolRegistry, resultReader);
110
+ const readResult = toolRegistry.get(READ_RESULT_NAME);
111
+ codeModeAdvertised = [...metas, ...readResult !== void 0 ? [readResult] : []];
112
+ }
113
+ return {
114
+ toolRegistry,
115
+ spillWriter,
116
+ resultReader,
117
+ makeToolExecutor,
118
+ toolExecutor,
119
+ toolDataFlowGuard,
120
+ ruleOfTwoCapabilityFloor,
121
+ isCodeMode,
122
+ codeModeAdvertised
123
+ };
124
+ }
125
+
126
+ //#endregion
127
+ export { wireToolExecution };
128
+ //# sourceMappingURL=executor-wiring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor-wiring.js","names":["resultReader: ResultReader","toolApprovalGate: NonNullable<ExecutorOptions['approvalGate']>","toolStreamingSink: NonNullable<ExecutorOptions['streamingSink']>","codeModeAdvertised: ReadonlyArray<Tool<unknown, unknown, TDeps>>"],"sources":["../../src/runtime/executor-wiring.ts"],"sourcesContent":["/**\n * Warm-up wiring of the agent's tool-execution stack: the spill\n * writer / result-reader pair (WI-10 / WI-13), the memory guard,\n * data-flow guard and Progent argument-policy hooks, the shared\n * `ToolExecutor` factory (WI-03), and the opt-in code-mode meta-tool\n * surface (WI-11). Extracted verbatim from `factory.ts` (issue #23).\n *\n * @packageDocumentation\n */\n\nimport type { RunState, Tool } from '@graphorin/core';\nimport type { Memory } from '@graphorin/memory';\nimport {\n createToolExecutor,\n type ExecutorOptions,\n type ToolExecutor,\n} from '@graphorin/tools/executor';\nimport type { ToolRegistry } from '@graphorin/tools/registry';\nimport {\n createDefaultSpillWriter,\n createFileResultReader,\n type ResultReader,\n} from '@graphorin/tools/result';\nimport {\n buildMemoryGuard,\n buildSecretResolver,\n buildToolTokenCounter,\n createMemoryRegionReader,\n type ExecutorEventBridge,\n} from '../tooling/adapters.js';\nimport { buildDataFlowGuard } from '../tooling/dataflow.js';\nimport { createPlanTool, PLAN_TOOL_NAME } from '../tooling/plan.js';\nimport { buildToolArgumentPolicy } from '../tooling/policy.js';\nimport { buildToolRegistry } from '../tooling/registry-build.js';\nimport type { AgentConfig } from '../types.js';\nimport {\n CODE_EXECUTE_NAME,\n CODE_SEARCH_NAME,\n composeResultReaders,\n READ_RESULT_NAME,\n registerCodeMode,\n registerReadResult,\n registerToolSearch,\n TOOL_SEARCH_NAME,\n} from './tool-wiring.js';\n\n/** What {@link wireToolExecution} needs from the agent factory scope. */\nexport interface ExecutorWiringDeps<TDeps, TOutput> {\n readonly config: AgentConfig<TDeps, TOutput>;\n readonly memory: Memory | undefined;\n readonly agentId: string;\n /** The synthetic handoff tool names (reserved from the code API). */\n readonly handoffToolNames: Iterable<string>;\n /** Lazy view of the in-flight run (read mid-run by the memory guard). */\n readonly getActiveRunState: () => RunState | undefined;\n /** Lazy view of the active run's capability (read by the code bridge). */\n readonly getActiveRunCapability: () => 'read-only' | undefined;\n /** Shared cell the streaming sink reads while a batch is in flight. */\n readonly executorBridgeSlot: { current: ExecutorEventBridge | undefined };\n}\n\n/** The executor stack {@link wireToolExecution} hands back to the factory. */\nexport interface ExecutorWiring<TDeps> {\n readonly toolRegistry: ToolRegistry;\n readonly spillWriter: ReturnType<typeof createDefaultSpillWriter>;\n readonly resultReader: ResultReader;\n readonly makeToolExecutor: (\n registry: ToolRegistry,\n opts?: { readonly quiet?: boolean },\n ) => ToolExecutor;\n readonly toolExecutor: ToolExecutor;\n readonly toolDataFlowGuard: ReturnType<typeof buildDataFlowGuard> | undefined;\n readonly ruleOfTwoCapabilityFloor: 'read-only' | undefined;\n readonly isCodeMode: boolean;\n readonly codeModeAdvertised: ReadonlyArray<Tool<unknown, unknown, TDeps>>;\n}\n\n/**\n * Construct the unified ToolExecutor once at warm-up (WI-03 / P0-1),\n * bound to the registry above. Routing tool execution through the\n * executor activates the documented tool fields the inline loop\n * bypassed: per-tool `secretsAllowed` ACL, result truncation\n * (`maxResultTokens` / `truncationStrategy`), inbound sanitization,\n * memory-guard, idempotency keys and single-round repair.\n *\n * Durable HITL stays in the agent: approval is pre-screened below and\n * suspends the run, so the executor's `ApprovalGate` only ever sees\n * no-approval / pre-approved calls - it auto-grants and never blocks\n * the generator (Adapter G).\n *\n * Sandbox note: `config.tools` are inline `tool({...})` closures that\n * cannot be serialised to an out-of-process sandbox, and\n * `resolveSandbox` defaults user-defined tools to `worker-threads`.\n * Wiring a resolver that returned a real sandbox for that kind would\n * break every inline tool, so `sandboxResolver` is intentionally left\n * unset (the executor then runs inline - its documented fallback).\n * The resolved policy is still surfaced on the tool-execute span /\n * audit; real isolation applies to module-loadable (skill / MCP)\n * tools and is wired when those land.\n */\nexport function wireToolExecution<TDeps, TOutput>(\n deps: ExecutorWiringDeps<TDeps, TOutput>,\n): ExecutorWiring<TDeps> {\n const { config, memory, agentId, executorBridgeSlot } = deps;\n const { getActiveRunState, getActiveRunCapability, handoffToolNames } = deps;\n\n // Assemble the unified tool registry at warm-up (Principle #12): one\n // registry across first-party + skill sources, with deterministic\n // cross-source collision resolution. Exposed read-only as\n // `agent.registry`; the run loop and `tool_search` consume it. The\n // registry is the tool-validation authority, so a malformed tool\n // fails fast here at construction.\n const toolRegistry = buildToolRegistry({\n ...(config.tools !== undefined\n ? { tools: config.tools as ReadonlyArray<Tool<unknown, unknown, unknown>> }\n : {}),\n ...(config.skills !== undefined ? { skills: config.skills } : {}),\n }).registry;\n\n // WI-05 (deferred loading + tool_search / P0-3): if any registered\n // tool sets `defer_loading: true`, register the built-in `tool_search`\n // so the model can discover those tools on demand. Tools that defer are\n // withheld from the per-step catalogue (see the loop below) until a\n // `tool_search` match promotes them, keeping large tool sets out of the\n // context window. When nothing defers this is a no-op.\n registerToolSearch(\n toolRegistry,\n config.toolPromotion === 'run-boundary' ? 'next-run' : 'next-step',\n );\n\n // D6: opt-in structured plan tool (TodoWrite-style, journaled). It\n // mutates the ACTIVE run's todos through a closure; attention\n // recitation renders them back into each step's request copy.\n if (config.plan === true && toolRegistry.get(PLAN_TOOL_NAME) === undefined) {\n toolRegistry.register(\n createPlanTool((todos) => {\n const activeRunState = getActiveRunState();\n if (activeRunState !== undefined) {\n (activeRunState as { todos?: ReadonlyArray<import('@graphorin/core').TodoItem> }).todos =\n todos;\n }\n }) as unknown as Parameters<typeof toolRegistry.register>[0],\n { kind: 'built-in', subsystem: 'planning' },\n );\n }\n\n // WI-10 (result references / handles / P1-4): construct one spill\n // writer + reader pair at warm-up. The writer is handed to the executor\n // so a tool's `'spill-to-file'` truncation strategy externalises large\n // bodies to disk (0600, run-scoped); the reader - over the *same*\n // artifact root - backs the built-in `read_result` tool so the model can\n // page through a spilled artifact on demand instead of inlining the\n // whole blob. `read_result` is registered only when some tool spills.\n const spillWriter = createDefaultSpillWriter();\n const fileResultReader = createFileResultReader({ artifactRoot: spillWriter.artifactRoot });\n // WI-13 (P2-2): compose any operator-supplied result readers (e.g. an\n // MCP resource reader from `createMcpResourceReader`, for resolving\n // `resource_link` handles) after the spill-file reader. `read_result`\n // then pages both `graphorin-spill:` artifacts and external handles,\n // and is force-registered when external readers exist (even if no tool\n // spills) so those handles are resolvable.\n const externalResultReaders = config.resultReaders ?? [];\n const resultReader: ResultReader =\n externalResultReaders.length === 0\n ? fileResultReader\n : composeResultReaders([fileResultReader, ...externalResultReaders]);\n registerReadResult(toolRegistry, resultReader, { force: externalResultReaders.length > 0 });\n\n const toolApprovalGate: NonNullable<ExecutorOptions['approvalGate']> = {\n request: async () => ({ granted: true }),\n };\n const toolSecretResolver = buildSecretResolver();\n const toolTokenCounter = buildToolTokenCounter();\n // SDF-1: when memory is wired, bind a scope-aware region reader so the\n // executor's DEC-153 snapshot/verify cycle actually runs (the scope\n // resolves lazily from the in-flight run - the executor only invokes\n // the reader mid-run). Without memory the guard tiers stay skipped,\n // and a one-time WARN below makes that silent no-op visible.\n const memoryGuardWiring = buildMemoryGuard(\n memory,\n memory === undefined\n ? {}\n : {\n regionReader: createMemoryRegionReader(['working'], async (region) => {\n if (region !== 'working') return '';\n const activeRunState = getActiveRunState();\n const scope = {\n userId: activeRunState?.userId ?? agentId,\n ...(activeRunState?.sessionId !== undefined\n ? { sessionId: activeRunState.sessionId }\n : {}),\n agentId,\n };\n // W-054: call the statically-typed `Memory.working.compile`\n // directly - a signature change must break THIS build, not\n // silently turn the DEC-153 guard into a no-op through an\n // unchecked double cast returning ''.\n try {\n return await memory.working.compile(scope, agentId);\n } catch {\n // The reader is best-effort: a failed region read must not\n // turn the guard step into a run failure.\n return '';\n }\n }),\n },\n );\n const toolMemoryGuardFactory = memoryGuardWiring.memoryGuardFactory;\n if (memory === undefined) {\n const guarded = (config.tools ?? []).filter((t) => t.memoryGuardTier !== undefined);\n if (guarded.length > 0) {\n console.warn(\n `[graphorin/agent] ${guarded.length} tool(s) declare memoryGuardTier but no memory is wired - ` +\n 'the DEC-153 snapshot/verify guard is skipped (SDF-1). Wire `memory` to activate it.',\n );\n }\n }\n // Provenance / data-flow guard (WI-12 / P1-3, opt-in). Built once and\n // shared by every executor (direct + code-mode quiet), so the sink gate\n // and taint recording apply uniformly. Off unless configured with a\n // non-`'off'` mode - zero overhead on the default path.\n const toolDataFlowGuard =\n config.dataFlowPolicy !== undefined && config.dataFlowPolicy.mode !== 'off'\n ? buildDataFlowGuard(config.dataFlowPolicy)\n : undefined;\n // W-103: the lethal-trifecta leg arms only when some tool can classify\n // as `sensitive` - by default that means `sensitivity: 'secret'`, and\n // no built-in tool ships with that tag. An operator who enables the\n // policy without tagging private-data tools silently gets only the\n // best-effort verbatim probe. One deterministic warn at construction\n // (mirrors the memoryGuardTier warn above); tools registered later are\n // not scanned - this is a wiring aid, not a gate.\n if (\n toolDataFlowGuard !== undefined &&\n (config.dataFlowPolicy?.guardTrifecta ?? true) &&\n config.dataFlowPolicy?.treatPiiAsSensitive !== true\n ) {\n const effectiveTiers = config.dataFlowPolicy?.sensitiveTiers ?? (['secret'] as const);\n const anySensitiveTool = (config.tools ?? []).some(\n (t) => t.sensitivity !== undefined && effectiveTiers.includes(t.sensitivity),\n );\n if (!anySensitiveTool) {\n console.warn(\n '[graphorin/agent] dataFlowPolicy is enabled but no registered tool declares a ' +\n \"sensitivity within sensitiveTiers (default ['secret']) and treatPiiAsSensitive is \" +\n 'off - the lethal-trifecta leg cannot arm; only the best-effort verbatim ' +\n \"untrusted-to-sink probe is active. Tag private-data tools (sensitivity: 'secret') \" +\n 'or widen sensitiveTiers / treatPiiAsSensitive.',\n );\n }\n }\n // D4 Progent tool-argument policy + Rule-of-Two floor. `ruleOfTwo`\n // compiles to a policy (+ a read-only capability floor when it denies\n // external side effects); an explicit `toolPolicy` composes on top\n // (its rules are appended, so an explicit forbid still wins). Built\n // once, shared by every executor.\n const { guard: toolArgumentPolicyGuard, capabilityFloor: ruleOfTwoCapabilityFloor } =\n buildToolArgumentPolicy(config.toolPolicy, config.ruleOfTwo);\n const toolStreamingSink: NonNullable<ExecutorOptions['streamingSink']> = (event) =>\n executorBridgeSlot.current?.sink(event);\n // `quiet` builds an executor without the streaming sink - used for\n // code-mode's in-script tool calls (WI-11), whose `tool.execute.*`\n // events must not interleave into the outer agent stream.\n const makeToolExecutor = (\n registry: ToolRegistry,\n opts?: { readonly quiet?: boolean },\n ): ToolExecutor =>\n createToolExecutor({\n registry,\n approvalGate: toolApprovalGate,\n secretResolver: toolSecretResolver,\n tokenCounter: toolTokenCounter,\n memoryGuardFactory: toolMemoryGuardFactory,\n ...(memoryGuardWiring.memoryRegionReader !== undefined\n ? { memoryRegionReader: memoryGuardWiring.memoryRegionReader }\n : {}),\n spill: spillWriter,\n ...(toolDataFlowGuard !== undefined ? { dataFlowGuard: toolDataFlowGuard } : {}),\n ...(toolArgumentPolicyGuard !== undefined ? { argumentPolicy: toolArgumentPolicyGuard } : {}),\n ...(opts?.quiet === true ? {} : { streamingSink: toolStreamingSink }),\n ...(config.maxParallelTools !== undefined\n ? { maxParallelTools: config.maxParallelTools }\n : {}),\n ...(config.toolRetry !== undefined ? { retry: config.toolRetry } : {}),\n });\n const toolExecutor = makeToolExecutor(toolRegistry);\n\n // Code-mode (WI-11 / P1-2, opt-in): advertise only the `code_search` /\n // `code_execute` meta-tools and let the model orchestrate tools inside a\n // sandbox, so intermediate results stay out of context. A quiet executor\n // backs the in-script tool bridge (same per-tool governance as direct\n // mode). `read_result` is registered after the meta-tools because\n // `code_execute` opts into `'spill-to-file'`, so a large final result can\n // be fetched back on demand. Default `'direct'` mode leaves all of this\n // untouched - `codeModeAdvertised` stays empty and the loop is unchanged.\n const isCodeMode = config.toolInvocation === 'code-mode';\n let codeModeAdvertised: ReadonlyArray<Tool<unknown, unknown, TDeps>> = [];\n if (isCodeMode) {\n const reserved = new Set<string>([\n CODE_EXECUTE_NAME,\n CODE_SEARCH_NAME,\n TOOL_SEARCH_NAME,\n READ_RESULT_NAME,\n ...handoffToolNames,\n ]);\n const metas = registerCodeMode(\n toolRegistry,\n makeToolExecutor(toolRegistry, { quiet: true }),\n reserved,\n getActiveRunCapability,\n );\n registerReadResult(toolRegistry, resultReader);\n const readResult = toolRegistry.get(READ_RESULT_NAME);\n codeModeAdvertised = [\n ...metas,\n ...(readResult !== undefined ? [readResult] : []),\n ] as ReadonlyArray<Tool<unknown, unknown, TDeps>>;\n }\n\n return {\n toolRegistry,\n spillWriter,\n resultReader,\n makeToolExecutor,\n toolExecutor,\n toolDataFlowGuard,\n ruleOfTwoCapabilityFloor,\n isCodeMode,\n codeModeAdvertised,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ import { AgentRuntimeError } from "../errors/index.js";
2
+ import { accumulateUsage, addUsage, buildStepMessages } from "./messages.js";
3
+ import { isAgentFallbackEligible } from "../fallback/index.js";
4
+ import { classifyThrownProviderErrorKind, handleProviderEvent } from "./provider-events.js";
5
+ import { zeroUsage } from "@graphorin/core";
6
+
7
+ //#region src/runtime/fallback-chain.ts
8
+ async function* runProviderFallbackChain(env, fallbackChain, baseRequest, primary, stepNumber) {
9
+ const { state, messages, sessionId, agentId, signal, fallbackPolicy } = env;
10
+ const { structuredInstruction, getPendingAbort, getActiveTodos, tryEmergencyCompact } = env;
11
+ const stepUsage = zeroUsage();
12
+ let attempt = 0;
13
+ let textBuffer = "";
14
+ let providerForStep = primary.resolvedProvider;
15
+ let lastModelId = primary.resolvedModelId;
16
+ let modelSucceeded = false;
17
+ let lastError;
18
+ let finalCalls = [];
19
+ let stepReasoningParts = [];
20
+ let requestForStep = baseRequest;
21
+ let emergencyCompactTried = false;
22
+ for (let chainIdx = 0; chainIdx < fallbackChain.length; chainIdx++) {
23
+ const candidate = fallbackChain[chainIdx];
24
+ if (candidate === void 0) continue;
25
+ providerForStep = candidate;
26
+ const providerModelId = providerForStep.modelId;
27
+ if (chainIdx > 0) {
28
+ attempt += 1;
29
+ const reason = lastError ? isAgentFallbackEligible(lastError, fallbackPolicy).reason ?? "transient" : "transient";
30
+ yield {
31
+ type: "agent.model.fellback",
32
+ runId: state.id,
33
+ sessionId,
34
+ agentId,
35
+ from: lastModelId,
36
+ to: providerModelId,
37
+ reason,
38
+ stepNumber,
39
+ attempt
40
+ };
41
+ lastModelId = providerModelId;
42
+ }
43
+ const evState = {
44
+ textBuffer: "",
45
+ reasoningBuffer: "",
46
+ reasoningParts: [],
47
+ calls: /* @__PURE__ */ new Map(),
48
+ finalCalls: []
49
+ };
50
+ let providerError;
51
+ let providerCallCompleted = false;
52
+ let providerStepUsage = zeroUsage();
53
+ try {
54
+ const stream = providerForStep.stream(requestForStep);
55
+ for await (const ev of stream) {
56
+ if (signal.aborted && getPendingAbort()?.drain !== true) throw new AgentRuntimeError("run-aborted", "aborted");
57
+ const out = handleProviderEvent(ev, evState);
58
+ if (out.emit !== void 0) yield out.emit;
59
+ if (out.providerError !== void 0) providerError = out.providerError;
60
+ if (out.usage !== void 0) providerStepUsage = addUsage(providerStepUsage, out.usage);
61
+ if (out.finished === true) providerCallCompleted = true;
62
+ }
63
+ } catch (cause) {
64
+ if (signal.aborted || cause instanceof AgentRuntimeError && cause.code === "run-aborted") break;
65
+ const message = cause instanceof Error ? cause.message : String(cause);
66
+ providerError = {
67
+ kind: classifyThrownProviderErrorKind(cause),
68
+ message,
69
+ cause
70
+ };
71
+ }
72
+ if (providerError !== void 0) {
73
+ lastError = providerError;
74
+ if (providerError.kind === "context-length" && !emergencyCompactTried) {
75
+ emergencyCompactTried = true;
76
+ if (yield* tryEmergencyCompact()) {
77
+ requestForStep = {
78
+ ...baseRequest,
79
+ messages: buildStepMessages(messages, structuredInstruction, getActiveTodos())
80
+ };
81
+ chainIdx -= 1;
82
+ continue;
83
+ }
84
+ }
85
+ if (!isAgentFallbackEligible(providerError, fallbackPolicy).eligible || chainIdx === fallbackChain.length - 1) {
86
+ yield {
87
+ type: "agent.error",
88
+ error: {
89
+ message: providerError.message,
90
+ code: providerError.kind
91
+ }
92
+ };
93
+ state.status = "failed";
94
+ state.error = {
95
+ message: providerError.message,
96
+ code: providerError.kind
97
+ };
98
+ return {
99
+ failed: true,
100
+ modelSucceeded,
101
+ textBuffer,
102
+ finalCalls,
103
+ stepReasoningParts,
104
+ stepUsage,
105
+ lastModelId
106
+ };
107
+ }
108
+ continue;
109
+ }
110
+ if (providerCallCompleted) {
111
+ modelSucceeded = true;
112
+ textBuffer = evState.textBuffer;
113
+ finalCalls = evState.finalCalls;
114
+ if (evState.reasoningParts.length > 0) stepReasoningParts = [...evState.reasoningParts, ...evState.reasoningBuffer.length > 0 ? [{
115
+ type: "reasoning",
116
+ text: evState.reasoningBuffer
117
+ }] : []];
118
+ else if (evState.reasoningBuffer.length > 0) stepReasoningParts = [{
119
+ type: "reasoning",
120
+ text: evState.reasoningBuffer
121
+ }];
122
+ accumulateUsage(stepUsage, providerStepUsage);
123
+ break;
124
+ }
125
+ }
126
+ return {
127
+ failed: false,
128
+ modelSucceeded,
129
+ textBuffer,
130
+ finalCalls,
131
+ stepReasoningParts,
132
+ stepUsage,
133
+ lastModelId
134
+ };
135
+ }
136
+
137
+ //#endregion
138
+ export { runProviderFallbackChain };
139
+ //# sourceMappingURL=fallback-chain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback-chain.js","names":["stepUsage: Usage","lastError: ProviderError | undefined","finalCalls: ToolCall[]","stepReasoningParts: ReasoningContent[]","evState: ProviderEventCollector","providerError: ProviderError | undefined","providerStepUsage: Usage"],"sources":["../../src/runtime/fallback-chain.ts"],"sourcesContent":["/**\n * The per-step provider fallback chain of the agent run loop: streams\n * the primary provider (emitting the accumulated agent events), applies\n * the AG-6 abort semantics, retries a hard context overflow once after\n * an emergency compaction (context-engine-06), walks the configured\n * fallback providers on eligible errors (emitting\n * `agent.model.fellback`), and records a terminal provider failure on\n * the run state. Extracted verbatim from `factory.ts` (issue #23); the\n * former in-loop chain now takes an explicit {@link FallbackChainEnv}\n * and returns a {@link ProviderChainOutcome} instead of finishing the\n * run itself (`failed: true` tells the loop to finish).\n *\n * @packageDocumentation\n */\n\nimport type {\n AgentEvent,\n Message,\n Provider,\n ProviderError,\n ProviderRequest,\n ReasoningContent,\n RunState,\n ToolCall,\n Usage,\n} from '@graphorin/core';\nimport { zeroUsage } from '@graphorin/core';\nimport { AgentRuntimeError } from '../errors/index.js';\nimport { type AgentFallbackPolicy, isAgentFallbackEligible } from '../fallback/index.js';\nimport type { PreferredModelResolution } from '../preferred-model/index.js';\nimport type { AbortOptions } from '../types.js';\nimport { accumulateUsage, addUsage, buildStepMessages } from './messages.js';\nimport {\n classifyThrownProviderErrorKind,\n handleProviderEvent,\n type ProviderEventCollector,\n type ToolCallAccumulator,\n} from './provider-events.js';\nimport type { MutableRunState } from './run-input.js';\n\n/**\n * The run-scoped context the chain operates on. Field names mirror the\n * run-loop locals the former inline chain captured; `getPendingAbort` /\n * `getActiveTodos` are live reads of the factory's mutable scratch, and\n * `tryEmergencyCompact` is pre-bound to the run's compaction env.\n */\nexport interface FallbackChainEnv<TOutput> {\n readonly state: MutableRunState & RunState;\n readonly messages: Message[];\n readonly sessionId: string;\n readonly agentId: string;\n readonly signal: AbortSignal;\n readonly fallbackPolicy: AgentFallbackPolicy;\n readonly structuredInstruction: string | undefined;\n readonly getPendingAbort: () => AbortOptions | undefined;\n readonly getActiveTodos: () => ReadonlyArray<import('@graphorin/core').TodoItem> | undefined;\n readonly tryEmergencyCompact: () => AsyncGenerator<AgentEvent<TOutput>, boolean, void>;\n}\n\n/** What one step's provider chain resolved to (read by the run loop). */\nexport interface ProviderChainOutcome {\n /**\n * `true` when the chain recorded a terminal provider failure on the\n * run state (the `agent.error` event already streamed) - the loop\n * must finish the run.\n */\n readonly failed: boolean;\n readonly modelSucceeded: boolean;\n readonly textBuffer: string;\n readonly finalCalls: ToolCall[];\n readonly stepReasoningParts: ReasoningContent[];\n readonly stepUsage: Usage;\n readonly lastModelId: string;\n}\n\nexport async function* runProviderFallbackChain<TOutput>(\n env: FallbackChainEnv<TOutput>,\n fallbackChain: Provider[],\n baseRequest: ProviderRequest,\n primary: PreferredModelResolution,\n stepNumber: number,\n): AsyncGenerator<AgentEvent<TOutput>, ProviderChainOutcome, void> {\n const { state, messages, sessionId, agentId, signal, fallbackPolicy } = env;\n const { structuredInstruction, getPendingAbort, getActiveTodos, tryEmergencyCompact } = env;\n\n const stepUsage: Usage = zeroUsage();\n let attempt = 0;\n let textBuffer = '';\n let providerForStep = primary.resolvedProvider;\n let lastModelId = primary.resolvedModelId;\n let modelSucceeded = false;\n let lastError: ProviderError | undefined;\n let finalCalls: ToolCall[] = [];\n let stepReasoningParts: ReasoningContent[] = [];\n // context-engine-06: the request actually sent - rebuilt after an\n // emergency compaction so the retry carries the shrunk buffer\n // even on the structured-output path (which snapshots messages).\n let requestForStep = baseRequest;\n let emergencyCompactTried = false;\n\n for (let chainIdx = 0; chainIdx < fallbackChain.length; chainIdx++) {\n const candidate = fallbackChain[chainIdx];\n if (candidate === undefined) continue;\n providerForStep = candidate;\n const providerModelId = providerForStep.modelId;\n if (chainIdx > 0) {\n attempt += 1;\n const reason = lastError\n ? (isAgentFallbackEligible(lastError, fallbackPolicy).reason ?? 'transient')\n : 'transient';\n yield {\n type: 'agent.model.fellback',\n runId: state.id,\n sessionId,\n agentId,\n from: lastModelId,\n to: providerModelId,\n reason,\n stepNumber,\n attempt,\n };\n lastModelId = providerModelId;\n }\n const evState: ProviderEventCollector = {\n textBuffer: '',\n reasoningBuffer: '',\n reasoningParts: [],\n calls: new Map<string, ToolCallAccumulator>(),\n finalCalls: [] as ToolCall[],\n };\n let providerError: ProviderError | undefined;\n let providerCallCompleted = false;\n let providerStepUsage: Usage = zeroUsage();\n try {\n const stream = providerForStep.stream(requestForStep);\n for await (const ev of stream) {\n // AG-6 `drain`: the default hard-kills the in-flight provider\n // stream mid-event; `abort({ drain: true })` instead lets the\n // current step finish (the documented \"wait for the current step\n // to complete\") and stops gracefully at the next loop-top check.\n if (signal.aborted && getPendingAbort()?.drain !== true) {\n throw new AgentRuntimeError('run-aborted', 'aborted');\n }\n const out = handleProviderEvent(ev, evState);\n if (out.emit !== undefined) {\n yield out.emit as AgentEvent<TOutput>;\n }\n if (out.providerError !== undefined) {\n providerError = out.providerError;\n }\n if (out.usage !== undefined) {\n providerStepUsage = addUsage(providerStepUsage, out.usage);\n }\n if (out.finished === true) providerCallCompleted = true;\n }\n } catch (cause) {\n // AG-6: a mid-stream abort (our run-aborted sentinel, or any error\n // once the signal is aborted - e.g. a native AbortError from the\n // provider) is NOT a provider failure. Break out of the fallback\n // chain WITHOUT a providerError; the post-stream abort check below\n // ends the run as 'aborted', never 'no-provider-completed'. Don't\n // continue the fallback chain against an already-aborted signal.\n if (signal.aborted || (cause instanceof AgentRuntimeError && cause.code === 'run-aborted')) {\n break;\n }\n const message = cause instanceof Error ? cause.message : String(cause);\n // AG-21: preserve the thrown error's kind (e.g. a RateLimitExceededError\n // from `withRateLimit`) so the fallback chain treats it like the same\n // error emitted as a structured event, instead of flattening it to\n // an always-ineligible 'unknown'.\n providerError = { kind: classifyThrownProviderErrorKind(cause), message, cause };\n }\n if (providerError !== undefined) {\n lastError = providerError;\n // context-engine-06: a hard context overflow gets ONE\n // emergency-compaction retry against the SAME candidate\n // before the fallback chain (whose members usually share the\n // window) or a terminal failure.\n if (providerError.kind === 'context-length' && !emergencyCompactTried) {\n emergencyCompactTried = true;\n const shrank = yield* tryEmergencyCompact();\n if (shrank) {\n requestForStep = {\n ...baseRequest,\n messages: buildStepMessages(messages, structuredInstruction, getActiveTodos()),\n };\n chainIdx -= 1; // negate the loop increment: retry this candidate\n continue;\n }\n }\n const eligibility = isAgentFallbackEligible(providerError, fallbackPolicy);\n if (!eligibility.eligible || chainIdx === fallbackChain.length - 1) {\n yield {\n type: 'agent.error',\n error: { message: providerError.message, code: providerError.kind },\n };\n state.status = 'failed';\n state.error = { message: providerError.message, code: providerError.kind };\n return {\n failed: true,\n modelSucceeded,\n textBuffer,\n finalCalls,\n stepReasoningParts,\n stepUsage,\n lastModelId,\n };\n }\n continue;\n }\n if (providerCallCompleted) {\n modelSucceeded = true;\n textBuffer = evState.textBuffer;\n finalCalls = evState.finalCalls;\n // W-024: adapters with per-block structure ('reasoning-end'\n // terminators) produce reasoningParts carrying the provider's\n // round-trip meta (thinking signatures) - use them as-is, plus a\n // meta-less tail for any deltas after the last terminator. The\n // single-part collapse remains the fallback for adapters without\n // block structure (ollama, openai-shaped, llamacpp).\n if (evState.reasoningParts.length > 0) {\n stepReasoningParts = [\n ...evState.reasoningParts,\n ...(evState.reasoningBuffer.length > 0\n ? [{ type: 'reasoning' as const, text: evState.reasoningBuffer }]\n : []),\n ];\n } else if (evState.reasoningBuffer.length > 0) {\n stepReasoningParts = [\n {\n type: 'reasoning',\n text: evState.reasoningBuffer,\n },\n ];\n }\n accumulateUsage(stepUsage, providerStepUsage);\n break;\n }\n }\n\n return {\n failed: false,\n modelSucceeded,\n textBuffer,\n finalCalls,\n stepReasoningParts,\n stepUsage,\n lastModelId,\n };\n}\n"],"mappings":"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"}