@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.
- package/CHANGELOG.md +96 -0
- package/README.md +31 -11
- package/dist/errors/index.d.ts +17 -4
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +19 -3
- package/dist/errors/index.js.map +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +153 -1930
- package/dist/factory.js.map +1 -1
- package/dist/fanout/index.d.ts +13 -1
- package/dist/fanout/index.d.ts.map +1 -1
- package/dist/fanout/index.js +13 -4
- package/dist/fanout/index.js.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -9
- package/dist/index.js.map +1 -1
- package/dist/lateral-leak/index.js +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/run-state/index.d.ts +32 -6
- package/dist/run-state/index.d.ts.map +1 -1
- package/dist/run-state/index.js +46 -22
- package/dist/run-state/index.js.map +1 -1
- package/dist/runtime/agent-surface.js +122 -0
- package/dist/runtime/agent-surface.js.map +1 -0
- package/dist/runtime/agent-to-tool.d.ts +51 -0
- package/dist/runtime/agent-to-tool.d.ts.map +1 -0
- package/dist/runtime/agent-to-tool.js +145 -0
- package/dist/runtime/agent-to-tool.js.map +1 -0
- package/dist/runtime/approvals.js +0 -0
- package/dist/runtime/approvals.js.map +1 -0
- package/dist/runtime/dispatch.js +108 -0
- package/dist/runtime/dispatch.js.map +1 -0
- package/dist/runtime/executor-wiring.js +128 -0
- package/dist/runtime/executor-wiring.js.map +1 -0
- package/dist/runtime/fallback-chain.js +139 -0
- package/dist/runtime/fallback-chain.js.map +1 -0
- package/dist/runtime/handoff.js +307 -0
- package/dist/runtime/handoff.js.map +1 -0
- package/dist/runtime/messages.d.ts +22 -0
- package/dist/runtime/messages.d.ts.map +1 -0
- package/dist/runtime/messages.js +204 -0
- package/dist/runtime/messages.js.map +1 -0
- package/dist/runtime/provider-events.js +117 -0
- package/dist/runtime/provider-events.js.map +1 -0
- package/dist/runtime/run-compaction.js +210 -0
- package/dist/runtime/run-compaction.js.map +1 -0
- package/dist/runtime/run-finish.js +48 -0
- package/dist/runtime/run-finish.js.map +1 -0
- package/dist/runtime/run-gates.js +336 -0
- package/dist/runtime/run-gates.js.map +1 -0
- package/dist/runtime/run-init.js +81 -0
- package/dist/runtime/run-init.js.map +1 -0
- package/dist/runtime/run-input.js +46 -0
- package/dist/runtime/run-input.js.map +1 -0
- package/dist/runtime/step-catalogue.js +173 -0
- package/dist/runtime/step-catalogue.js.map +1 -0
- package/dist/runtime/tool-call-walk.js +189 -0
- package/dist/runtime/tool-call-walk.js.map +1 -0
- package/dist/runtime/tool-wiring.js +159 -0
- package/dist/runtime/tool-wiring.js.map +1 -0
- package/dist/tooling/adapters.js +1 -1
- package/dist/tooling/dataflow.js +1 -1
- package/dist/tooling/policy.js +2 -0
- package/dist/tooling/policy.js.map +1 -1
- package/dist/types.d.ts +63 -13
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/errors/index.ts +320 -0
- package/src/evaluator-optimizer/index.ts +212 -0
- package/src/factory.ts +957 -0
- package/src/fallback/index.ts +108 -0
- package/src/fanout/index.ts +523 -0
- package/src/filters/index.ts +347 -0
- package/src/index.ts +180 -0
- package/src/internal/ids.ts +46 -0
- package/src/internal/usage-accumulator.ts +90 -0
- package/src/lateral-leak/causality-monitor.ts +221 -0
- package/src/lateral-leak/index.ts +35 -0
- package/src/lateral-leak/merge-guard.ts +151 -0
- package/src/lateral-leak/protocol-guard.ts +222 -0
- package/src/preferred-model/index.ts +210 -0
- package/src/progress/index.ts +238 -0
- package/src/run-state/index.ts +607 -0
- package/src/runtime/agent-surface.ts +218 -0
- package/src/runtime/agent-to-tool.ts +323 -0
- package/src/runtime/approvals.ts +0 -0
- package/src/runtime/dispatch.ts +183 -0
- package/src/runtime/executor-wiring.ts +331 -0
- package/src/runtime/fallback-chain.ts +250 -0
- package/src/runtime/handoff.ts +428 -0
- package/src/runtime/messages.ts +309 -0
- package/src/runtime/provider-events.ts +175 -0
- package/src/runtime/run-compaction.ts +288 -0
- package/src/runtime/run-finish.ts +93 -0
- package/src/runtime/run-gates.ts +419 -0
- package/src/runtime/run-init.ts +169 -0
- package/src/runtime/run-input.ts +102 -0
- package/src/runtime/step-catalogue.ts +338 -0
- package/src/runtime/tool-call-walk.ts +301 -0
- package/src/runtime/tool-wiring.ts +218 -0
- package/src/testing/replay-provider.ts +121 -0
- package/src/tooling/adapters.ts +403 -0
- package/src/tooling/catalogue.ts +36 -0
- package/src/tooling/dataflow.ts +171 -0
- package/src/tooling/plan.ts +123 -0
- package/src/tooling/policy.ts +67 -0
- package/src/tooling/registry-build.ts +191 -0
- package/src/types.ts +696 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { ToolNotFoundError } from "../errors/index.js";
|
|
2
|
+
import { buildStepMessages } from "./messages.js";
|
|
3
|
+
import { buildHandoffTool } from "./handoff.js";
|
|
4
|
+
import { registerReadResult, registerToolSearch } from "./tool-wiring.js";
|
|
5
|
+
import { buildToolRegistry } from "../tooling/registry-build.js";
|
|
6
|
+
import { specToProvider } from "./provider-events.js";
|
|
7
|
+
import { resolvePreferredModel } from "../preferred-model/index.js";
|
|
8
|
+
import { orderPromotedTools } from "../tooling/catalogue.js";
|
|
9
|
+
import { projectSchemaToJsonSchema } from "@graphorin/tools/schema";
|
|
10
|
+
|
|
11
|
+
//#region src/runtime/step-catalogue.ts
|
|
12
|
+
/** WARN-once keys for schemas the projection cannot read (per process). */
|
|
13
|
+
const unprojectableSchemaWarned = /* @__PURE__ */ new Set();
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a tool's declared schema - plain Zod (v3/v4), `toJSON()`-bearing,
|
|
16
|
+
* or already-JSON-Schema data - to a JSON Schema record via the shared
|
|
17
|
+
* projection (tools-01). Pre-fix this only honoured `toJSON()` and passed
|
|
18
|
+
* everything else through verbatim, so every plain-Zod tool serialised as
|
|
19
|
+
* `{"_def":...}` internals on OpenAI-shaped/Ollama/vercel wire bodies.
|
|
20
|
+
* `undefined` when nothing usable can be projected (caller substitutes a
|
|
21
|
+
* permissive `{}`), with a WARN so the degradation is never silent.
|
|
22
|
+
*/
|
|
23
|
+
function projectSchema(raw, toolName, slot) {
|
|
24
|
+
return projectSchemaToJsonSchema(raw, { onUnsupported: (detail) => {
|
|
25
|
+
const key = `${toolName}:${slot}:${detail}`;
|
|
26
|
+
if (unprojectableSchemaWarned.has(key)) return;
|
|
27
|
+
unprojectableSchemaWarned.add(key);
|
|
28
|
+
console.warn(`[graphorin/agent] tool '${toolName}' ${slot} schema: '${detail}' cannot be projected to JSON Schema - that fragment degrades to a permissive {} on the provider wire body.`);
|
|
29
|
+
} });
|
|
30
|
+
}
|
|
31
|
+
function toolToDefinition(tool) {
|
|
32
|
+
const ts = tool;
|
|
33
|
+
const inputSchema = projectSchema(ts.inputSchema, tool.name, "input") ?? {};
|
|
34
|
+
const outputSchema = projectSchema(ts.outputSchema, tool.name, "output");
|
|
35
|
+
const examples = renderToolExamples(tool);
|
|
36
|
+
return {
|
|
37
|
+
name: tool.name,
|
|
38
|
+
description: tool.description,
|
|
39
|
+
inputSchema,
|
|
40
|
+
...outputSchema !== void 0 ? { outputSchema } : {},
|
|
41
|
+
...examples !== void 0 ? { examples } : {}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Project a tool's worked `examples` onto the provider wire contract
|
|
46
|
+
* (WI-06 / P2-3). Examples are rendered only when the tool eagerly
|
|
47
|
+
* renders them: the registry resolves the `defer_loading` auto-rule into
|
|
48
|
+
* `examplesEagerlyRendered`, so a deferred tool resolves to `false` and is
|
|
49
|
+
* skipped (its examples stay out of context even once `tool_search`
|
|
50
|
+
* promotes it). `undefined` - the "runtime decides" case for a plain
|
|
51
|
+
* eager tool - renders, since the tool is already advertised this step.
|
|
52
|
+
*
|
|
53
|
+
* Bounded to ≤5 to honour the `ToolExample` `[1,5]` contract even when a
|
|
54
|
+
* tool slipped past the registry's overflow WARN (which does not truncate).
|
|
55
|
+
*/
|
|
56
|
+
function renderToolExamples(tool) {
|
|
57
|
+
const examples = tool.examples;
|
|
58
|
+
if (examples === void 0 || examples.length === 0) return void 0;
|
|
59
|
+
if (tool.examplesEagerlyRendered === false) return void 0;
|
|
60
|
+
return examples.slice(0, 5).map((ex) => ({
|
|
61
|
+
input: ex.input,
|
|
62
|
+
output: ex.output,
|
|
63
|
+
...ex.comment !== void 0 ? { comment: ex.comment } : {}
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the registry + executor for this step. The warm-up
|
|
68
|
+
* pair is bound to config.tools + skills; a `prepareStep` tool
|
|
69
|
+
* override builds a step-scoped pair so the advertised catalogue
|
|
70
|
+
* and the executor agree on the same tool set (incl. deferred
|
|
71
|
+
* discovery for the overridden set). Code-mode does not honour a
|
|
72
|
+
* per-step `tools` override (the meta-tools + bridge are bound to
|
|
73
|
+
* the warm-up registry), so it always uses the warm-up pair.
|
|
74
|
+
*
|
|
75
|
+
* Also assembles the per-step tool catalogue (code-mode meta-tools or
|
|
76
|
+
* eager + handoffs + promotions, read-only filtered under D2), resolves
|
|
77
|
+
* the step's preferred model (AG-15: consulting only the tools the
|
|
78
|
+
* model CALLED on the previous step), and derives the step's provider
|
|
79
|
+
* fallback chain (RB-48: a `prepareStep` provider override suppresses
|
|
80
|
+
* the chain).
|
|
81
|
+
*/
|
|
82
|
+
function resolveStepToolContext(env, overrides, lastStepCalledToolNames) {
|
|
83
|
+
const { config, isCodeMode, toolRegistry, toolExecutor, makeToolExecutor } = env;
|
|
84
|
+
const { resultReader, handoffMap, handoffNames, codeModeAdvertised } = env;
|
|
85
|
+
const { activeRunCapability, promotedDeferred, runStartPromotions } = env;
|
|
86
|
+
const useOverrideRegistry = overrides.tools !== void 0 && !isCodeMode;
|
|
87
|
+
const stepRegistry = useOverrideRegistry ? buildToolRegistry({ tools: overrides.tools }).registry : toolRegistry;
|
|
88
|
+
if (useOverrideRegistry) {
|
|
89
|
+
registerToolSearch(stepRegistry, config.toolPromotion === "run-boundary" ? "next-run" : "next-step");
|
|
90
|
+
registerReadResult(stepRegistry, resultReader);
|
|
91
|
+
}
|
|
92
|
+
const stepExecutor = useOverrideRegistry ? makeToolExecutor(stepRegistry) : toolExecutor;
|
|
93
|
+
const handoffTools = handoffNames.map((n) => {
|
|
94
|
+
const h = handoffMap.get(n);
|
|
95
|
+
if (h === void 0) throw new ToolNotFoundError(n);
|
|
96
|
+
return buildHandoffTool(h.agent);
|
|
97
|
+
});
|
|
98
|
+
const readOnlyRun = activeRunCapability === "read-only";
|
|
99
|
+
const keepReadOnly = (t) => {
|
|
100
|
+
const cls = t.__sideEffectClass ?? t.sideEffectClass;
|
|
101
|
+
return cls === "pure" || cls === "read-only";
|
|
102
|
+
};
|
|
103
|
+
let stepTools;
|
|
104
|
+
if (isCodeMode) stepTools = readOnlyRun ? [...codeModeAdvertised.filter(keepReadOnly)] : [...codeModeAdvertised, ...handoffTools];
|
|
105
|
+
else {
|
|
106
|
+
const eagerTools = stepRegistry.listEager();
|
|
107
|
+
const advertisedPromotions = runStartPromotions ?? promotedDeferred;
|
|
108
|
+
const promotedTools = advertisedPromotions.size === 0 ? [] : orderPromotedTools(advertisedPromotions, stepRegistry.listDeferred());
|
|
109
|
+
stepTools = readOnlyRun ? [...eagerTools.filter(keepReadOnly), ...promotedTools.filter(keepReadOnly)] : [
|
|
110
|
+
...eagerTools,
|
|
111
|
+
...handoffTools,
|
|
112
|
+
...promotedTools
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
const calledLastStep = new Set(lastStepCalledToolNames);
|
|
116
|
+
const toolPreferences = stepTools.filter((t) => calledLastStep.has(t.name)).map((t) => {
|
|
117
|
+
return t.preferredModel;
|
|
118
|
+
});
|
|
119
|
+
const primary = resolvePreferredModel({
|
|
120
|
+
...overrides.provider !== void 0 ? { prepareStepProvider: overrides.provider } : {},
|
|
121
|
+
toolPreferredModels: toolPreferences,
|
|
122
|
+
...config.preferredModel !== void 0 ? { agentPreferredModel: config.preferredModel } : {},
|
|
123
|
+
agentDefaultProvider: config.provider,
|
|
124
|
+
...config.modelTierMap !== void 0 ? { modelTierMap: config.modelTierMap } : {}
|
|
125
|
+
});
|
|
126
|
+
const fallbackChain = primary.source === "prepare-step" ? [primary.resolvedProvider] : [primary.resolvedProvider, ...(config.fallbackModels ?? []).map(specToProvider)];
|
|
127
|
+
return {
|
|
128
|
+
stepRegistry,
|
|
129
|
+
stepExecutor,
|
|
130
|
+
stepTools,
|
|
131
|
+
primary,
|
|
132
|
+
fallbackChain
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Assemble the step's base {@link ProviderRequest} over the shared
|
|
137
|
+
* message buffer: the D6 request-only trailing additions (structured
|
|
138
|
+
* instruction + plan recitation) ride `buildStepMessages`, the
|
|
139
|
+
* `prepareStep` overrides / config knobs spread in exactly as the
|
|
140
|
+
* inline literal did, and the effective reasoning-retention policy
|
|
141
|
+
* rides the request (RB-42).
|
|
142
|
+
*/
|
|
143
|
+
function buildBaseRequest(env, overrides, toolDefs, reasoningPolicy, stepNumber, currentStepSpan) {
|
|
144
|
+
const { config, state, messages, sessionId, agentId, userId, signal } = env;
|
|
145
|
+
const { structuredInstruction, getActiveTodos } = env;
|
|
146
|
+
return {
|
|
147
|
+
messages: buildStepMessages(messages, structuredInstruction, getActiveTodos()),
|
|
148
|
+
...config.outputType !== void 0 ? { outputType: {
|
|
149
|
+
kind: config.outputType.kind,
|
|
150
|
+
...config.outputType.description !== void 0 ? { description: config.outputType.description } : {},
|
|
151
|
+
...config.outputType.jsonSchema !== void 0 ? { jsonSchema: config.outputType.jsonSchema } : {}
|
|
152
|
+
} } : {},
|
|
153
|
+
tools: toolDefs,
|
|
154
|
+
...overrides.toolChoice !== void 0 ? { toolChoice: overrides.toolChoice } : config.toolChoice !== void 0 ? { toolChoice: config.toolChoice } : {},
|
|
155
|
+
metadata: {
|
|
156
|
+
sessionId,
|
|
157
|
+
agentId,
|
|
158
|
+
...userId !== void 0 ? { userId } : {},
|
|
159
|
+
runId: state.id,
|
|
160
|
+
stepNumber
|
|
161
|
+
},
|
|
162
|
+
signal,
|
|
163
|
+
...overrides.temperature !== void 0 ? { temperature: overrides.temperature } : {},
|
|
164
|
+
...overrides.maxTokens !== void 0 ? { maxTokens: overrides.maxTokens } : {},
|
|
165
|
+
...config.cachePolicy !== void 0 ? { cachePolicy: config.cachePolicy } : {},
|
|
166
|
+
...currentStepSpan !== void 0 ? { parentSpan: currentStepSpan } : {},
|
|
167
|
+
reasoningRetention: reasoningPolicy
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
//#endregion
|
|
172
|
+
export { buildBaseRequest, resolveStepToolContext, toolToDefinition };
|
|
173
|
+
//# sourceMappingURL=step-catalogue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-catalogue.js","names":["stepRegistry: ToolRegistry","stepExecutor: ToolExecutor","handoffTools: Tool<unknown, unknown, TDeps>[]","stepTools: ReadonlyArray<Tool<unknown, unknown, TDeps>>","primary: PreferredModelResolution","fallbackChain: Provider[]"],"sources":["../../src/runtime/step-catalogue.ts"],"sourcesContent":["/**\n * Per-step tool-catalogue support for the agent runtime: projection of\n * a tool's declared schemas onto the provider wire contract\n * (`ToolDefinition`), including worked examples, plus the per-step\n * registry / executor / catalogue / preferred-model resolution.\n * Extracted verbatim from `factory.ts` (issue #23).\n *\n * @packageDocumentation\n */\n\nimport type {\n AISpan,\n Message,\n Provider,\n ProviderRequest,\n ReasoningRetention,\n RunState,\n Tool,\n ToolChoice,\n ToolDefinition,\n ToolDefinitionExample,\n} from '@graphorin/core';\nimport type { ToolExecutor } from '@graphorin/tools/executor';\nimport type { ToolRegistry } from '@graphorin/tools/registry';\nimport type { ResultReader } from '@graphorin/tools/result';\nimport { projectSchemaToJsonSchema } from '@graphorin/tools/schema';\nimport { ToolNotFoundError } from '../errors/index.js';\nimport { type PreferredModelResolution, resolvePreferredModel } from '../preferred-model/index.js';\nimport { orderPromotedTools } from '../tooling/catalogue.js';\nimport { buildToolRegistry } from '../tooling/registry-build.js';\nimport type { AgentConfig, PrepareStepOverrides } from '../types.js';\nimport { buildHandoffTool, type HandoffEntry } from './handoff.js';\nimport { buildStepMessages } from './messages.js';\nimport { specToProvider } from './provider-events.js';\nimport type { MutableRunState } from './run-input.js';\nimport { registerReadResult, registerToolSearch } from './tool-wiring.js';\n\n/** WARN-once keys for schemas the projection cannot read (per process). */\nconst unprojectableSchemaWarned = new Set<string>();\n\n/**\n * Resolve a tool's declared schema - plain Zod (v3/v4), `toJSON()`-bearing,\n * or already-JSON-Schema data - to a JSON Schema record via the shared\n * projection (tools-01). Pre-fix this only honoured `toJSON()` and passed\n * everything else through verbatim, so every plain-Zod tool serialised as\n * `{\"_def\":...}` internals on OpenAI-shaped/Ollama/vercel wire bodies.\n * `undefined` when nothing usable can be projected (caller substitutes a\n * permissive `{}`), with a WARN so the degradation is never silent.\n */\nfunction projectSchema(\n raw: unknown,\n toolName: string,\n slot: 'input' | 'output',\n): Readonly<Record<string, unknown>> | undefined {\n return projectSchemaToJsonSchema(raw, {\n onUnsupported: (detail) => {\n const key = `${toolName}:${slot}:${detail}`;\n if (unprojectableSchemaWarned.has(key)) return;\n unprojectableSchemaWarned.add(key);\n console.warn(\n `[graphorin/agent] tool '${toolName}' ${slot} schema: '${detail}' cannot be projected ` +\n 'to JSON Schema - that fragment degrades to a permissive {} on the provider wire body.',\n );\n },\n });\n}\n\nexport function toolToDefinition(tool: Tool): ToolDefinition {\n const ts = tool as Tool & {\n readonly inputSchema?: unknown;\n readonly outputSchema?: unknown;\n };\n const inputSchema = projectSchema(ts.inputSchema, tool.name, 'input') ?? {};\n // A5: project the output schema so structured-output providers + typed\n // code-mode see the tool's result shape.\n const outputSchema = projectSchema(ts.outputSchema, tool.name, 'output');\n const examples = renderToolExamples(tool);\n return {\n name: tool.name,\n description: tool.description,\n inputSchema,\n ...(outputSchema !== undefined ? { outputSchema } : {}),\n ...(examples !== undefined ? { examples } : {}),\n };\n}\n\n/**\n * Project a tool's worked `examples` onto the provider wire contract\n * (WI-06 / P2-3). Examples are rendered only when the tool eagerly\n * renders them: the registry resolves the `defer_loading` auto-rule into\n * `examplesEagerlyRendered`, so a deferred tool resolves to `false` and is\n * skipped (its examples stay out of context even once `tool_search`\n * promotes it). `undefined` - the \"runtime decides\" case for a plain\n * eager tool - renders, since the tool is already advertised this step.\n *\n * Bounded to ≤5 to honour the `ToolExample` `[1,5]` contract even when a\n * tool slipped past the registry's overflow WARN (which does not truncate).\n */\nfunction renderToolExamples(tool: Tool): ReadonlyArray<ToolDefinitionExample> | undefined {\n const examples = tool.examples;\n if (examples === undefined || examples.length === 0) return undefined;\n if (tool.examplesEagerlyRendered === false) return undefined;\n return examples.slice(0, 5).map((ex) => ({\n input: ex.input,\n output: ex.output,\n ...(ex.comment !== undefined ? { comment: ex.comment } : {}),\n }));\n}\n\n/**\n * The run-scoped inputs of the per-step catalogue resolution. Field\n * names mirror the run-loop locals the former inline block captured.\n */\nexport interface StepCatalogueEnv<TDeps, TOutput> {\n readonly config: AgentConfig<TDeps, TOutput>;\n readonly isCodeMode: boolean;\n readonly toolRegistry: ToolRegistry;\n readonly toolExecutor: ToolExecutor;\n readonly makeToolExecutor: (\n registry: ToolRegistry,\n opts?: { readonly quiet?: boolean },\n ) => ToolExecutor;\n readonly resultReader: ResultReader;\n readonly handoffMap: ReadonlyMap<string, HandoffEntry<TDeps>>;\n readonly handoffNames: ReadonlyArray<string>;\n readonly codeModeAdvertised: ReadonlyArray<Tool<unknown, unknown, TDeps>>;\n readonly activeRunCapability: 'read-only' | undefined;\n readonly promotedDeferred: Set<string>;\n readonly runStartPromotions: Set<string> | undefined;\n}\n\n/** What one step's catalogue resolution hands back to the run loop. */\nexport interface StepToolContext<TDeps> {\n readonly stepRegistry: ToolRegistry;\n readonly stepExecutor: ToolExecutor;\n readonly stepTools: ReadonlyArray<Tool<unknown, unknown, TDeps>>;\n readonly primary: PreferredModelResolution;\n readonly fallbackChain: Provider[];\n}\n\n/**\n * Resolve the registry + executor for this step. The warm-up\n * pair is bound to config.tools + skills; a `prepareStep` tool\n * override builds a step-scoped pair so the advertised catalogue\n * and the executor agree on the same tool set (incl. deferred\n * discovery for the overridden set). Code-mode does not honour a\n * per-step `tools` override (the meta-tools + bridge are bound to\n * the warm-up registry), so it always uses the warm-up pair.\n *\n * Also assembles the per-step tool catalogue (code-mode meta-tools or\n * eager + handoffs + promotions, read-only filtered under D2), resolves\n * the step's preferred model (AG-15: consulting only the tools the\n * model CALLED on the previous step), and derives the step's provider\n * fallback chain (RB-48: a `prepareStep` provider override suppresses\n * the chain).\n */\nexport function resolveStepToolContext<TDeps, TOutput>(\n env: StepCatalogueEnv<TDeps, TOutput>,\n overrides: PrepareStepOverrides<TDeps>,\n lastStepCalledToolNames: ReadonlyArray<string>,\n): StepToolContext<TDeps> {\n const { config, isCodeMode, toolRegistry, toolExecutor, makeToolExecutor } = env;\n const { resultReader, handoffMap, handoffNames, codeModeAdvertised } = env;\n const { activeRunCapability, promotedDeferred, runStartPromotions } = env;\n\n const useOverrideRegistry = overrides.tools !== undefined && !isCodeMode;\n const stepRegistry: ToolRegistry = useOverrideRegistry\n ? buildToolRegistry({\n tools: overrides.tools as ReadonlyArray<Tool<unknown, unknown, unknown>>,\n }).registry\n : toolRegistry;\n if (useOverrideRegistry) {\n registerToolSearch(\n stepRegistry,\n config.toolPromotion === 'run-boundary' ? 'next-run' : 'next-step',\n );\n registerReadResult(stepRegistry, resultReader);\n }\n const stepExecutor: ToolExecutor = useOverrideRegistry\n ? makeToolExecutor(stepRegistry)\n : toolExecutor;\n\n // Build the per-step tool catalogue. Handoff tools are synthetic\n // per-step entries and are always advertised.\n const handoffTools: Tool<unknown, unknown, TDeps>[] = handoffNames.map((n) => {\n const h = handoffMap.get(n);\n if (h === undefined) throw new ToolNotFoundError(n);\n return buildHandoffTool<TDeps>(h.agent);\n });\n // Code-mode (WI-11): advertise only the `code_search` /\n // `code_execute` (+ `read_result`) meta-tools - the model reaches\n // every real tool through `code_execute`, so the real tools stay\n // registered (executable via the in-script bridge) but out of the\n // model's catalogue. Otherwise (WI-05): advertise the eager tools\n // (`tool_search` is itself eager iff a deferred tool exists) plus\n // any deferred tools already promoted by a `tool_search` this run -\n // never the rest of the deferred pool.\n // D2 single-writer constraint: a read-only run never ADVERTISES\n // writer tools (side-effecting / external-stateful) nor handoffs\n // (a transfer hands the writer pen to another agent). The\n // executor-level capability gate backs this up for calls the\n // model fabricates anyway.\n const readOnlyRun = activeRunCapability === 'read-only';\n const keepReadOnly = (t: Tool<unknown, unknown, TDeps>): boolean => {\n const cls = (t as { __sideEffectClass?: string }).__sideEffectClass ?? t.sideEffectClass;\n return cls === 'pure' || cls === 'read-only';\n };\n let stepTools: ReadonlyArray<Tool<unknown, unknown, TDeps>>;\n if (isCodeMode) {\n stepTools = readOnlyRun\n ? [...codeModeAdvertised.filter(keepReadOnly)]\n : [...codeModeAdvertised, ...handoffTools];\n } else {\n const eagerTools = stepRegistry.listEager() as ReadonlyArray<Tool<unknown, unknown, TDeps>>;\n // A7: emit promoted deferred tools in PROMOTION order (append-only) so\n // a later promotion joins the END and the prompt-cache prefix stays\n // byte-stable across steps. C1 (agent-11): handoff tools serialize\n // BEFORE the growing promoted section - handoffs are fixed per run,\n // so the stable prefix is now eager + handoffs + earlier promotions\n // and a new promotion shifts nothing that came before it.\n // 'run-boundary' promotion advertises only the run-start snapshot.\n const advertisedPromotions = runStartPromotions ?? promotedDeferred;\n const promotedTools = (\n advertisedPromotions.size === 0\n ? []\n : orderPromotedTools(advertisedPromotions, stepRegistry.listDeferred())\n ) as ReadonlyArray<Tool<unknown, unknown, TDeps>>;\n stepTools = readOnlyRun\n ? [...eagerTools.filter(keepReadOnly), ...promotedTools.filter(keepReadOnly)]\n : [...eagerTools, ...handoffTools, ...promotedTools];\n }\n\n // AG-15: consult the hints of the tools the model CALLED on the\n // previous step - a smart-hinted but never-called tool must not\n // pin the whole conversation to the top cost tier. Steps with no\n // prior calls fall through to the agent-preferred default.\n const calledLastStep = new Set(lastStepCalledToolNames);\n const toolPreferences = stepTools\n .filter((t) => calledLastStep.has(t.name))\n .map((t) => {\n const tt = t as Tool<unknown, unknown, TDeps> & { readonly preferredModel?: unknown };\n return tt.preferredModel as Parameters<\n typeof resolvePreferredModel\n >[0]['toolPreferredModels'][number];\n });\n\n const primary: PreferredModelResolution = resolvePreferredModel({\n ...(overrides.provider !== undefined ? { prepareStepProvider: overrides.provider } : {}),\n toolPreferredModels: toolPreferences,\n ...(config.preferredModel !== undefined ? { agentPreferredModel: config.preferredModel } : {}),\n agentDefaultProvider: config.provider,\n ...(config.modelTierMap !== undefined ? { modelTierMap: config.modelTierMap } : {}),\n });\n\n // RB-48: when `prepareStep` returns an explicit provider\n // override, the fallback chain is NOT consulted for this\n // step (the operator's explicit choice supersedes the\n // implicit fallback chain).\n const fallbackChain: Provider[] =\n primary.source === 'prepare-step'\n ? [primary.resolvedProvider]\n : [primary.resolvedProvider, ...(config.fallbackModels ?? []).map(specToProvider)];\n\n return { stepRegistry, stepExecutor, stepTools, primary, fallbackChain };\n}\n\n/** What the per-step request assembly needs from the run loop's scope. */\nexport interface StepRequestEnv<TDeps, TOutput> {\n readonly config: AgentConfig<TDeps, TOutput>;\n readonly state: MutableRunState & RunState;\n readonly messages: Message[];\n readonly sessionId: string;\n readonly agentId: string;\n readonly userId: string | undefined;\n readonly signal: AbortSignal;\n readonly structuredInstruction: string | undefined;\n readonly getActiveTodos: () => ReadonlyArray<import('@graphorin/core').TodoItem> | undefined;\n}\n\n/**\n * Assemble the step's base {@link ProviderRequest} over the shared\n * message buffer: the D6 request-only trailing additions (structured\n * instruction + plan recitation) ride `buildStepMessages`, the\n * `prepareStep` overrides / config knobs spread in exactly as the\n * inline literal did, and the effective reasoning-retention policy\n * rides the request (RB-42).\n */\nexport function buildBaseRequest<TDeps, TOutput>(\n env: StepRequestEnv<TDeps, TOutput>,\n overrides: PrepareStepOverrides<TDeps>,\n toolDefs: ReadonlyArray<ToolDefinition>,\n reasoningPolicy: ReasoningRetention,\n stepNumber: number,\n currentStepSpan: AISpan<'agent.step'> | undefined,\n): ProviderRequest {\n const { config, state, messages, sessionId, agentId, userId, signal } = env;\n const { structuredInstruction, getActiveTodos } = env;\n return {\n // AG-3 fallback contract: for structured output the request\n // carries ONE trailing system instruction (JSON-only + schema)\n // in the request copy - never in the shared buffer or the\n // persisted RunState. Adapters with native structured output\n // additionally receive `outputType` below (PS-24 consumes it).\n messages: buildStepMessages(messages, structuredInstruction, getActiveTodos()),\n ...(config.outputType !== undefined\n ? {\n outputType: {\n kind: config.outputType.kind,\n ...(config.outputType.description !== undefined\n ? { description: config.outputType.description }\n : {}),\n ...(config.outputType.jsonSchema !== undefined\n ? { jsonSchema: config.outputType.jsonSchema }\n : {}),\n },\n }\n : {}),\n tools: toolDefs,\n ...(overrides.toolChoice !== undefined\n ? { toolChoice: overrides.toolChoice }\n : config.toolChoice !== undefined\n ? { toolChoice: config.toolChoice as ToolChoice }\n : {}),\n metadata: {\n sessionId,\n agentId,\n ...(userId !== undefined ? { userId } : {}),\n runId: state.id,\n stepNumber,\n },\n signal,\n ...(overrides.temperature !== undefined ? { temperature: overrides.temperature } : {}),\n ...(overrides.maxTokens !== undefined ? { maxTokens: overrides.maxTokens } : {}),\n ...(config.cachePolicy !== undefined ? { cachePolicy: config.cachePolicy } : {}),\n ...(currentStepSpan !== undefined ? { parentSpan: currentStepSpan } : {}),\n reasoningRetention: reasoningPolicy,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { serializeRunState } from "../run-state/index.js";
|
|
2
|
+
import { renderToolErrorMessage } from "./messages.js";
|
|
3
|
+
import { getSubAgentToolRefs } from "./agent-to-tool.js";
|
|
4
|
+
import { executeHandoffToolCall, runSubAgentCall } from "./handoff.js";
|
|
5
|
+
import { invokeNeedsApproval, safeParseGatedArgs } from "./approvals.js";
|
|
6
|
+
import { isApprovalGated } from "./tool-wiring.js";
|
|
7
|
+
|
|
8
|
+
//#region src/runtime/tool-call-walk.ts
|
|
9
|
+
/**
|
|
10
|
+
* Walk calls in finalCalls order. Handoffs are special-cased
|
|
11
|
+
* inline (≤1 per step) and never routed through the executor.
|
|
12
|
+
* Non-handoff calls accumulate into a batch dispatched through
|
|
13
|
+
* the ToolExecutor; the batch is flushed before a handoff and
|
|
14
|
+
* before each approval-gated call so execution order is kept.
|
|
15
|
+
* Gated calls are COLLECTED (all of them, agent-01) and the run
|
|
16
|
+
* suspends once after the walk, so every non-gated toolCallId
|
|
17
|
+
* has a tool message before the suspend snapshot - a dropped
|
|
18
|
+
* call would persist a dangling `tool_use` that real providers
|
|
19
|
+
* reject on resume.
|
|
20
|
+
*/
|
|
21
|
+
async function* processStepToolCalls(env, finalCalls, stepRegistry, stepExecutor, execRunContext, stepNumber) {
|
|
22
|
+
const { config, state, messages, signal, handoffMap } = env;
|
|
23
|
+
const { toolDataFlowGuard, promotedDeferred, dispatchBatch } = env;
|
|
24
|
+
let batch = [];
|
|
25
|
+
let stepApprovalsRequested = 0;
|
|
26
|
+
for (const call of finalCalls) {
|
|
27
|
+
const handoff = handoffMap.get(call.toolName);
|
|
28
|
+
if (handoff !== void 0) {
|
|
29
|
+
if (batch.length > 0) {
|
|
30
|
+
yield* dispatchBatch(batch, stepExecutor, execRunContext, stepNumber);
|
|
31
|
+
batch = [];
|
|
32
|
+
}
|
|
33
|
+
if ((yield* executeHandoffToolCall(env, call, handoff, stepNumber)).suspendRequested) stepApprovalsRequested += 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const resolvedTool = stepRegistry.get(call.toolName);
|
|
37
|
+
let gateInput = call.args;
|
|
38
|
+
if (resolvedTool !== void 0 && isApprovalGated(resolvedTool)) {
|
|
39
|
+
const parsed = safeParseGatedArgs(resolvedTool, call.args);
|
|
40
|
+
if (parsed !== void 0 && !parsed.success) {
|
|
41
|
+
const toolError = {
|
|
42
|
+
toolCallId: call.toolCallId,
|
|
43
|
+
toolName: call.toolName,
|
|
44
|
+
kind: "invalid_input",
|
|
45
|
+
message: `Invalid arguments for approval-gated tool '${call.toolName}': ${parsed.message}`
|
|
46
|
+
};
|
|
47
|
+
const stepEntry = state.steps[state.steps.length - 1];
|
|
48
|
+
if (stepEntry !== void 0) stepEntry.toolCalls.push({
|
|
49
|
+
call,
|
|
50
|
+
outcome: toolError,
|
|
51
|
+
stepNumber
|
|
52
|
+
});
|
|
53
|
+
yield {
|
|
54
|
+
type: "tool.execute.start",
|
|
55
|
+
toolCallId: call.toolCallId,
|
|
56
|
+
toolName: call.toolName
|
|
57
|
+
};
|
|
58
|
+
yield {
|
|
59
|
+
type: "tool.execute.error",
|
|
60
|
+
toolCallId: call.toolCallId,
|
|
61
|
+
toolName: call.toolName,
|
|
62
|
+
error: toolError
|
|
63
|
+
};
|
|
64
|
+
const text = renderToolErrorMessage(toolError);
|
|
65
|
+
messages.push({
|
|
66
|
+
role: "tool",
|
|
67
|
+
toolCallId: call.toolCallId,
|
|
68
|
+
content: text
|
|
69
|
+
});
|
|
70
|
+
state.messages.push({
|
|
71
|
+
role: "tool",
|
|
72
|
+
toolCallId: call.toolCallId,
|
|
73
|
+
content: text
|
|
74
|
+
});
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (parsed !== void 0) gateInput = parsed.data;
|
|
78
|
+
}
|
|
79
|
+
if (await invokeNeedsApproval(resolvedTool, gateInput, execRunContext, signal)) {
|
|
80
|
+
if (batch.length > 0) {
|
|
81
|
+
yield* dispatchBatch(batch, stepExecutor, execRunContext, stepNumber);
|
|
82
|
+
batch = [];
|
|
83
|
+
}
|
|
84
|
+
yield {
|
|
85
|
+
type: "tool.execute.start",
|
|
86
|
+
toolCallId: call.toolCallId,
|
|
87
|
+
toolName: call.toolName
|
|
88
|
+
};
|
|
89
|
+
const approval = {
|
|
90
|
+
toolCallId: call.toolCallId,
|
|
91
|
+
toolName: call.toolName,
|
|
92
|
+
args: call.args,
|
|
93
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
94
|
+
};
|
|
95
|
+
state.pendingApprovals.push(approval);
|
|
96
|
+
stepApprovalsRequested += 1;
|
|
97
|
+
yield {
|
|
98
|
+
type: "tool.approval.requested",
|
|
99
|
+
toolCallId: call.toolCallId
|
|
100
|
+
};
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const subRefs = getSubAgentToolRefs(resolvedTool);
|
|
104
|
+
if (subRefs !== void 0) {
|
|
105
|
+
if (batch.length > 0) {
|
|
106
|
+
yield* dispatchBatch(batch, stepExecutor, execRunContext, stepNumber);
|
|
107
|
+
batch = [];
|
|
108
|
+
}
|
|
109
|
+
if ((yield* executeSubAgentToolCall(env, call, subRefs, execRunContext, stepNumber)).suspendRequested) stepApprovalsRequested += 1;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
batch.push(call);
|
|
113
|
+
}
|
|
114
|
+
if (batch.length > 0) yield* dispatchBatch(batch, stepExecutor, execRunContext, stepNumber);
|
|
115
|
+
if (stepApprovalsRequested > 0) {
|
|
116
|
+
state.status = "awaiting_approval";
|
|
117
|
+
const taintSnap = toolDataFlowGuard?.snapshotLedger(state.id);
|
|
118
|
+
if (taintSnap !== void 0) state.taintSummary = taintSnap;
|
|
119
|
+
if (promotedDeferred.size > 0) state.promotedTools = [...promotedDeferred];
|
|
120
|
+
if (signal.aborted) return {
|
|
121
|
+
suspended: true,
|
|
122
|
+
abortPending: true
|
|
123
|
+
};
|
|
124
|
+
if (config.checkpointStore !== void 0) await config.checkpointStore.put(state.id, "agent", {
|
|
125
|
+
id: state.id,
|
|
126
|
+
threadId: state.id,
|
|
127
|
+
namespace: "agent",
|
|
128
|
+
state: serializeRunState(state, { stripTracingApiKey: true }),
|
|
129
|
+
channelVersions: {},
|
|
130
|
+
stepNumber,
|
|
131
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
132
|
+
}, {
|
|
133
|
+
source: "sync",
|
|
134
|
+
status: "suspended",
|
|
135
|
+
nodeName: "agent.run",
|
|
136
|
+
sessionId: state.sessionId
|
|
137
|
+
});
|
|
138
|
+
return { suspended: true };
|
|
139
|
+
}
|
|
140
|
+
return { suspended: false };
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* W-001: execute a `toTool` sub-agent call INLINE (mirroring the
|
|
144
|
+
* handoff seam): reproduce `execute()`'s seed and output shaping via
|
|
145
|
+
* the tool's {@link SubAgentToolRefs}, and settle through the shared
|
|
146
|
+
* {@link runSubAgentCall} so a suspending child parks instead of
|
|
147
|
+
* throwing. The D2 taint fold that the executor would have applied from
|
|
148
|
+
* the ToolReturn envelope is recorded directly on the data-flow guard.
|
|
149
|
+
*/
|
|
150
|
+
async function* executeSubAgentToolCall(env, call, refs, execRunContext, stepNumber) {
|
|
151
|
+
const { config, options, messages, sessionId, signal, toolDataFlowGuard } = env;
|
|
152
|
+
yield {
|
|
153
|
+
type: "tool.execute.start",
|
|
154
|
+
toolCallId: call.toolCallId,
|
|
155
|
+
toolName: call.toolName
|
|
156
|
+
};
|
|
157
|
+
const rawInput = call.args ?? {};
|
|
158
|
+
const input = { input: typeof rawInput.input === "string" ? rawInput.input : "" };
|
|
159
|
+
const parentSpan = env.getCurrentStepSpan?.();
|
|
160
|
+
const callOpts = {
|
|
161
|
+
signal,
|
|
162
|
+
...options.deps !== void 0 || config.deps !== void 0 ? { deps: options.deps ?? config.deps } : {},
|
|
163
|
+
sessionId,
|
|
164
|
+
...refs.capability !== void 0 ? { capability: refs.capability } : {},
|
|
165
|
+
...parentSpan !== void 0 ? { parentSpan } : {}
|
|
166
|
+
};
|
|
167
|
+
const seed = refs.buildSeed(input, messages);
|
|
168
|
+
const subStream = refs.stream(seed, callOpts);
|
|
169
|
+
return yield* runSubAgentCall(env, call, {
|
|
170
|
+
agentName: refs.agentName,
|
|
171
|
+
subStream,
|
|
172
|
+
errorLabel: `sub-agent '${refs.agentName}'`,
|
|
173
|
+
renderCompleted: refs.shapeCompleted,
|
|
174
|
+
...refs.forwardEvents !== void 0 ? { forwardEvents: refs.forwardEvents } : {},
|
|
175
|
+
recordTaint: (taint, renderedText) => {
|
|
176
|
+
toolDataFlowGuard?.record({
|
|
177
|
+
toolName: call.toolName,
|
|
178
|
+
trustClass: "first-party-user-defined",
|
|
179
|
+
taintOverride: taint,
|
|
180
|
+
outputText: renderedText,
|
|
181
|
+
runContext: execRunContext
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}, stepNumber);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
//#endregion
|
|
188
|
+
export { processStepToolCalls };
|
|
189
|
+
//# sourceMappingURL=tool-call-walk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-call-walk.js","names":["batch: ToolCall[]","gateInput: unknown","toolError: ToolError","approval: ToolApproval","callOpts: Record<string, unknown>"],"sources":["../../src/runtime/tool-call-walk.ts"],"sourcesContent":["/**\n * The per-step tool-call walk of the agent run loop: batches\n * non-handoff calls for the executor (flushing before a handoff and\n * before each approval-gated call so execution order is kept), executes\n * the ≤1 handoff inline, pre-screens `needsApproval` with validated\n * args (tools-02), collects EVERY gated call (agent-01), and performs\n * the once-per-step durable-HITL suspend with its taint / promotion\n * snapshot + checkpoint (AG-19 / AG-23). Extracted verbatim from\n * `factory.ts` (issue #23); the former inline walk now takes an\n * explicit {@link ToolCallWalkEnv} and returns `{ suspended }` instead\n * of finishing the run itself.\n *\n * @packageDocumentation\n */\n\nimport type {\n AgentEvent,\n CompletedToolCall,\n RunContext,\n ToolApproval,\n ToolCall,\n ToolError,\n} from '@graphorin/core';\nimport type { ToolExecutor } from '@graphorin/tools/executor';\nimport type { ToolRegistry } from '@graphorin/tools/registry';\nimport { serializeRunState } from '../run-state/index.js';\nimport type { AgentConfig } from '../types.js';\nimport { getSubAgentToolRefs, type SubAgentToolRefs } from './agent-to-tool.js';\nimport { invokeNeedsApproval, safeParseGatedArgs } from './approvals.js';\nimport type { DispatchBatchFn } from './dispatch.js';\nimport {\n executeHandoffToolCall,\n type HandoffEntry,\n type HandoffRunEnv,\n runSubAgentCall,\n} from './handoff.js';\nimport { renderToolErrorMessage } from './messages.js';\nimport type { AssistantCommitEnv } from './run-gates.js';\nimport { isApprovalGated } from './tool-wiring.js';\n\n/**\n * The run-scoped context the walk operates on. Extends the handoff\n * execution env (the walk hands it through verbatim); the extra fields\n * mirror the run-loop locals the former inline walk captured.\n */\nexport interface ToolCallWalkEnv<TDeps, TOutput> extends HandoffRunEnv<TDeps, TOutput> {\n readonly config: Pick<AgentConfig<TDeps, TOutput>, 'deps' | 'checkpointStore'>;\n readonly handoffMap: ReadonlyMap<string, HandoffEntry<TDeps>>;\n readonly toolDataFlowGuard: AssistantCommitEnv['toolDataFlowGuard'];\n readonly promotedDeferred: Set<string>;\n readonly dispatchBatch: DispatchBatchFn<TDeps, TOutput>;\n}\n\n/**\n * Walk calls in finalCalls order. Handoffs are special-cased\n * inline (≤1 per step) and never routed through the executor.\n * Non-handoff calls accumulate into a batch dispatched through\n * the ToolExecutor; the batch is flushed before a handoff and\n * before each approval-gated call so execution order is kept.\n * Gated calls are COLLECTED (all of them, agent-01) and the run\n * suspends once after the walk, so every non-gated toolCallId\n * has a tool message before the suspend snapshot - a dropped\n * call would persist a dangling `tool_use` that real providers\n * reject on resume.\n */\nexport async function* processStepToolCalls<TDeps, TOutput>(\n env: ToolCallWalkEnv<TDeps, TOutput>,\n finalCalls: ReadonlyArray<ToolCall>,\n stepRegistry: ToolRegistry,\n stepExecutor: ToolExecutor,\n execRunContext: RunContext<TDeps>,\n stepNumber: number,\n): AsyncGenerator<\n AgentEvent<TOutput>,\n { readonly suspended: boolean; readonly abortPending?: boolean },\n void\n> {\n const { config, state, messages, signal, handoffMap } = env;\n const { toolDataFlowGuard, promotedDeferred, dispatchBatch } = env;\n let batch: ToolCall[] = [];\n let stepApprovalsRequested = 0;\n\n for (const call of finalCalls) {\n const handoff = handoffMap.get(call.toolName);\n if (handoff !== undefined) {\n if (batch.length > 0) {\n yield* dispatchBatch(batch, stepExecutor, execRunContext, stepNumber);\n batch = [];\n }\n const handed = yield* executeHandoffToolCall<TDeps, TOutput>(env, call, handoff, stepNumber);\n // W-001: the handoff child suspended awaiting approvals - it\n // parked on the parent and the parent suspends once per step\n // exactly like a directly-gated call.\n if (handed.suspendRequested) stepApprovalsRequested += 1;\n continue;\n }\n\n // Approval pre-screen (Adapter G / durable HITL). Evaluate the\n // registry-resolved `needsApproval`; a gated call flushes the\n // queued batch (prior calls' side-effects complete first) and\n // is recorded as a pending approval. The walk CONTINUES: later\n // gated calls are collected too, and later non-gated calls\n // still execute before the suspend (agent-01 - previously\n // everything after the first gated call was silently dropped,\n // never executed and never approvable, leaving dangling\n // `tool_use` ids in the persisted transcript).\n const resolvedTool = stepRegistry.get(call.toolName);\n // tools-02 (agent mirror): the approval decision must be made\n // on the input that will actually execute. For gated tools the\n // args are validated HERE: a schema failure fails the call fast\n // as `invalid_input` (a human is never asked to approve args\n // that cannot run, and the resumed dispatch can therefore never\n // hit the repair hook), and the predicate receives the parsed\n // value its typed signature promises - not raw pre-coercion\n // JSON.\n let gateInput: unknown = call.args;\n if (resolvedTool !== undefined && isApprovalGated(resolvedTool)) {\n const parsed = safeParseGatedArgs(resolvedTool, call.args);\n if (parsed !== undefined && !parsed.success) {\n const toolError: ToolError = {\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n kind: 'invalid_input',\n message: `Invalid arguments for approval-gated tool '${call.toolName}': ${parsed.message}`,\n };\n const stepEntry = state.steps[state.steps.length - 1];\n if (stepEntry !== undefined) {\n (stepEntry.toolCalls as CompletedToolCall[]).push({\n call,\n outcome: toolError,\n stepNumber,\n });\n }\n yield { type: 'tool.execute.start', toolCallId: call.toolCallId, toolName: call.toolName };\n yield {\n type: 'tool.execute.error',\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n error: toolError,\n };\n const text = renderToolErrorMessage(toolError);\n messages.push({ role: 'tool', toolCallId: call.toolCallId, content: text });\n state.messages.push({ role: 'tool', toolCallId: call.toolCallId, content: text });\n continue;\n }\n if (parsed !== undefined) gateInput = parsed.data;\n }\n const needsApproval = await invokeNeedsApproval(\n resolvedTool,\n gateInput,\n execRunContext,\n signal,\n );\n if (needsApproval) {\n if (batch.length > 0) {\n yield* dispatchBatch(batch, stepExecutor, execRunContext, stepNumber);\n batch = [];\n }\n yield { type: 'tool.execute.start', toolCallId: call.toolCallId, toolName: call.toolName };\n const approval: ToolApproval = {\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n args: call.args,\n requestedAt: new Date().toISOString(),\n };\n state.pendingApprovals.push(approval);\n stepApprovalsRequested += 1;\n yield {\n type: 'tool.approval.requested',\n toolCallId: call.toolCallId,\n };\n continue;\n }\n\n // W-001: `toTool` sub-agent tools execute INLINE through the same\n // seam as a handoff (never through the executor, which cannot\n // suspend a run) - a child that parks on `awaiting_approval`\n // suspends the parent instead of surfacing a terminal tool error.\n const subRefs = getSubAgentToolRefs(resolvedTool);\n if (subRefs !== undefined) {\n if (batch.length > 0) {\n yield* dispatchBatch(batch, stepExecutor, execRunContext, stepNumber);\n batch = [];\n }\n const subbed = yield* executeSubAgentToolCall<TDeps, TOutput>(\n env,\n call,\n subRefs,\n execRunContext,\n stepNumber,\n );\n if (subbed.suspendRequested) stepApprovalsRequested += 1;\n continue;\n }\n\n batch.push(call);\n }\n\n if (batch.length > 0) {\n yield* dispatchBatch(batch, stepExecutor, execRunContext, stepNumber);\n }\n\n // Durable-HITL suspend: once per step, carrying EVERY gated call\n // the model batched. Runs after the final batch flush so the\n // suspend snapshot already contains tool messages for the whole\n // non-gated remainder of the step.\n if (stepApprovalsRequested > 0) {\n state.status = 'awaiting_approval';\n // AG-19: persist the coarse taint summary + promoted-tool set into\n // the suspended state so a resume rehydrates the sink gate and the\n // discovered-tool catalogue instead of starting empty.\n const taintSnap = toolDataFlowGuard?.snapshotLedger(state.id);\n if (taintSnap !== undefined) state.taintSummary = taintSnap;\n if (promotedDeferred.size > 0) state.promotedTools = [...promotedDeferred];\n // W-038: an abort that arrived while this step was collecting gated\n // calls must reach the `onPendingApprovals` policy INSTEAD of the\n // unconditional suspend - and no 'suspended awaiting_approval'\n // checkpoint may be written first, or the durable trail would\n // contradict the aborted outcome and resurrect denied approvals on\n // resume. The factory applies the policy and persists the final,\n // policy-consistent state through the same put seam.\n if (signal.aborted) {\n return { suspended: true, abortPending: true };\n }\n if (config.checkpointStore !== undefined) {\n await config.checkpointStore.put(\n state.id,\n 'agent',\n {\n id: state.id,\n threadId: state.id,\n namespace: 'agent',\n // AG-23: persist a detached, secret-redacted snapshot -\n // never the live MutableRunState reference.\n state: serializeRunState(state, { stripTracingApiKey: true }),\n channelVersions: {},\n stepNumber,\n createdAt: new Date().toISOString(),\n },\n { source: 'sync', status: 'suspended', nodeName: 'agent.run', sessionId: state.sessionId },\n );\n }\n return { suspended: true };\n }\n return { suspended: false };\n}\n\n/**\n * W-001: execute a `toTool` sub-agent call INLINE (mirroring the\n * handoff seam): reproduce `execute()`'s seed and output shaping via\n * the tool's {@link SubAgentToolRefs}, and settle through the shared\n * {@link runSubAgentCall} so a suspending child parks instead of\n * throwing. The D2 taint fold that the executor would have applied from\n * the ToolReturn envelope is recorded directly on the data-flow guard.\n */\nasync function* executeSubAgentToolCall<TDeps, TOutput>(\n env: ToolCallWalkEnv<TDeps, TOutput>,\n call: ToolCall,\n refs: SubAgentToolRefs,\n execRunContext: RunContext<TDeps>,\n stepNumber: number,\n): AsyncGenerator<AgentEvent<TOutput>, { readonly suspendRequested: boolean }, void> {\n const { config, options, messages, sessionId, signal, toolDataFlowGuard } = env;\n yield { type: 'tool.execute.start', toolCallId: call.toolCallId, toolName: call.toolName };\n const rawInput = (call.args ?? {}) as { readonly input?: unknown };\n const input = { input: typeof rawInput.input === 'string' ? rawInput.input : '' };\n const parentSpan = env.getCurrentStepSpan?.();\n const callOpts: Record<string, unknown> = {\n signal,\n ...(options.deps !== undefined || config.deps !== undefined\n ? { deps: options.deps ?? config.deps }\n : {}),\n sessionId,\n ...(refs.capability !== undefined ? { capability: refs.capability } : {}),\n // W-036: one trace tree through the inline walk too.\n ...(parentSpan !== undefined ? { parentSpan } : {}),\n };\n const seed = refs.buildSeed(input, messages);\n const subStream = refs.stream(seed, callOpts);\n return yield* runSubAgentCall<TDeps, TOutput>(\n env,\n call,\n {\n agentName: refs.agentName,\n subStream,\n errorLabel: `sub-agent '${refs.agentName}'`,\n renderCompleted: refs.shapeCompleted,\n ...(refs.forwardEvents !== undefined ? { forwardEvents: refs.forwardEvents } : {}),\n recordTaint: (taint, renderedText) => {\n toolDataFlowGuard?.record({\n toolName: call.toolName,\n trustClass: 'first-party-user-defined',\n taintOverride: taint,\n outputText: renderedText,\n runContext: execRunContext as RunContext,\n });\n },\n },\n stepNumber,\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { newId } from "../internal/ids.js";
|
|
2
|
+
import { createReadResultTool, createToolSearchTool } from "@graphorin/tools/built-in";
|
|
3
|
+
import { createCodeExecuteTool, createCodeSearchTool, projectToolApi } from "@graphorin/tools/code-mode";
|
|
4
|
+
|
|
5
|
+
//#region src/runtime/tool-wiring.ts
|
|
6
|
+
/** The built-in deferred-discovery tool's stable name (WI-05 / P0-3). */
|
|
7
|
+
const TOOL_SEARCH_NAME = "tool_search";
|
|
8
|
+
/**
|
|
9
|
+
* Register the built-in `tool_search` into `registry` when - and only
|
|
10
|
+
* when - the registry holds at least one deferred tool
|
|
11
|
+
* (`defer_loading: true`). `tool_search` is itself eager (so it is
|
|
12
|
+
* always advertised while a deferred pool exists) and resolvable by the
|
|
13
|
+
* executor like any other tool, so a model can both *see* it in the
|
|
14
|
+
* per-step catalogue and *call* it.
|
|
15
|
+
*
|
|
16
|
+
* No-op when nothing defers (zero overhead - the tool never appears) or
|
|
17
|
+
* when a user tool already occupies the name (the user's tool wins; we
|
|
18
|
+
* never clobber it). Because deferral is decided at registration time
|
|
19
|
+
* (`normaliseTool`), the deferred set is fixed for the life of the
|
|
20
|
+
* registry - this runs once per registry, not per step.
|
|
21
|
+
*/
|
|
22
|
+
function registerToolSearch(registry, availability) {
|
|
23
|
+
if (registry.listDeferred().length === 0) return;
|
|
24
|
+
if (registry.get(TOOL_SEARCH_NAME) !== void 0) return;
|
|
25
|
+
registry.register(createToolSearchTool({
|
|
26
|
+
registry,
|
|
27
|
+
...availability !== void 0 ? { availability } : {}
|
|
28
|
+
}), {
|
|
29
|
+
kind: "built-in",
|
|
30
|
+
subsystem: "tool-discovery"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** The built-in result-handle reader tool's stable name (WI-10 / P1-4). */
|
|
34
|
+
const READ_RESULT_NAME = "read_result";
|
|
35
|
+
/**
|
|
36
|
+
* Register the built-in `read_result` into `registry` when at least one
|
|
37
|
+
* registered tool opts into the `'spill-to-file'` truncation strategy
|
|
38
|
+
* (the sole producer of spill handles today) - or when `force` is set,
|
|
39
|
+
* which the agent passes when an operator wires external result readers
|
|
40
|
+
* (e.g. an MCP `resource_link` reader; WI-13). The tool is eager, so it
|
|
41
|
+
* is advertised alongside the producing tool and the model can fetch a
|
|
42
|
+
* handle back on demand instead of inlining the full blob. No-op when
|
|
43
|
+
* nothing produces handles (zero overhead) or when a user tool already
|
|
44
|
+
* occupies the name (the user's tool wins).
|
|
45
|
+
*/
|
|
46
|
+
function registerReadResult(registry, reader, opts) {
|
|
47
|
+
if (opts?.force !== true && !registry.list().some((entry) => entry.truncationStrategy === "spill-to-file")) return;
|
|
48
|
+
if (registry.get(READ_RESULT_NAME) !== void 0) return;
|
|
49
|
+
registry.register(createReadResultTool({ reader }), {
|
|
50
|
+
kind: "built-in",
|
|
51
|
+
subsystem: "result-handle"
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Compose result readers into one that tries each in order, returning
|
|
56
|
+
* the first that resolves the handle (WI-13). The spill-file reader is
|
|
57
|
+
* placed first so `graphorin-spill:` handles resolve locally; operator
|
|
58
|
+
* readers (e.g. an MCP resource reader) resolve the rest. Each reader
|
|
59
|
+
* rejects handles it does not own, so resolution falls through cleanly.
|
|
60
|
+
*/
|
|
61
|
+
function composeResultReaders(readers) {
|
|
62
|
+
return { async read(uri, range) {
|
|
63
|
+
let lastError;
|
|
64
|
+
for (const r of readers) try {
|
|
65
|
+
return await r.read(uri, range);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
lastError = err;
|
|
68
|
+
}
|
|
69
|
+
throw lastError instanceof Error ? lastError : /* @__PURE__ */ new Error(`No result reader resolved handle ${JSON.stringify(uri)}.`);
|
|
70
|
+
} };
|
|
71
|
+
}
|
|
72
|
+
/** The code-mode meta-tools' stable names (WI-11 / P1-2). */
|
|
73
|
+
const CODE_EXECUTE_NAME = "code_execute";
|
|
74
|
+
const CODE_SEARCH_NAME = "code_search";
|
|
75
|
+
/** Structural check: is this tool approval-gated (static or predicate form)? */
|
|
76
|
+
const isApprovalGated = (t) => t.needsApproval === true || typeof t.needsApproval === "function";
|
|
77
|
+
/**
|
|
78
|
+
* Wire code-mode (P1-2) into `registry`: register the `code_search` /
|
|
79
|
+
* `code_execute` meta-tools and return them as the tools to advertise in
|
|
80
|
+
* place of the full catalogue. The model reaches every other tool through
|
|
81
|
+
* `code_execute`, whose in-script `tools.<name>(args)` calls route back
|
|
82
|
+
* through `quietExecutor.executeOne(...)` under the calling step's
|
|
83
|
+
* `runContext` - so per-tool ACL / sanitization / truncation still apply,
|
|
84
|
+
* exactly as in direct mode. `quietExecutor` carries no `streamingSink`,
|
|
85
|
+
* so the inner calls do not interleave `tool.execute.*` events into the
|
|
86
|
+
* outer stream.
|
|
87
|
+
*
|
|
88
|
+
* Excluded from the code API (`reservedNames`): the meta-tools, the
|
|
89
|
+
* discovery / handle built-ins, handoff tools (which stay first-class
|
|
90
|
+
* provider tools), and - supplied by the caller - any approval-gated
|
|
91
|
+
* tool, since code-mode has no durable-HITL path to suspend mid-script.
|
|
92
|
+
*
|
|
93
|
+
* Returns `[]` (registering nothing) when no real tool is exposable.
|
|
94
|
+
*/
|
|
95
|
+
function registerCodeMode(registry, quietExecutor, reservedNames, getRunCapability) {
|
|
96
|
+
if (registry.get(CODE_EXECUTE_NAME) !== void 0) return [];
|
|
97
|
+
const codeTools = registry.list().filter((entry) => !reservedNames.has(entry.name) && !isApprovalGated(entry));
|
|
98
|
+
if (codeTools.length === 0) return [];
|
|
99
|
+
const approvalGatedTools = registry.list().filter((entry) => !reservedNames.has(entry.name) && isApprovalGated(entry)).map((entry) => entry.name);
|
|
100
|
+
const approvalGatedSet = new Set(approvalGatedTools);
|
|
101
|
+
const allowedTools = [...codeTools.map((entry) => entry.name), ...approvalGatedTools];
|
|
102
|
+
const allowedSet = new Set(codeTools.map((entry) => entry.name));
|
|
103
|
+
const projection = projectToolApi(codeTools.filter((entry) => entry.__effectiveDeferLoading !== true));
|
|
104
|
+
const executeTool = async (call, ctx) => {
|
|
105
|
+
if (approvalGatedSet.has(call.name)) throw new Error(`${call.name} requires human approval and cannot run inside code_execute - call it directly as a standalone tool call so the run can suspend for the approval.`);
|
|
106
|
+
const runCapability = getRunCapability?.();
|
|
107
|
+
const { outcome } = await quietExecutor.executeOne({
|
|
108
|
+
call: {
|
|
109
|
+
toolCallId: newId("codecall"),
|
|
110
|
+
toolName: call.name,
|
|
111
|
+
args: call.args
|
|
112
|
+
},
|
|
113
|
+
runContext: ctx.runContext,
|
|
114
|
+
stepNumber: ctx.runContext.stepNumber,
|
|
115
|
+
...runCapability !== void 0 ? { capability: runCapability } : {}
|
|
116
|
+
});
|
|
117
|
+
if ("kind" in outcome) throw new Error(`${call.name}: ${outcome.message}`);
|
|
118
|
+
return outcome.output;
|
|
119
|
+
};
|
|
120
|
+
const codeSearch = createCodeSearchTool({
|
|
121
|
+
projection,
|
|
122
|
+
approvalGatedTools,
|
|
123
|
+
searchDeferred: async (query, k) => (await registry.searchDeferred(query, k)).filter((match) => allowedSet.has(match.name))
|
|
124
|
+
});
|
|
125
|
+
const codeExecute = createCodeExecuteTool({
|
|
126
|
+
projection,
|
|
127
|
+
allowedTools,
|
|
128
|
+
executeTool,
|
|
129
|
+
approvalGatedTools
|
|
130
|
+
});
|
|
131
|
+
registry.register(codeSearch, {
|
|
132
|
+
kind: "built-in",
|
|
133
|
+
subsystem: "code-mode"
|
|
134
|
+
});
|
|
135
|
+
registry.register(codeExecute, {
|
|
136
|
+
kind: "built-in",
|
|
137
|
+
subsystem: "code-mode"
|
|
138
|
+
});
|
|
139
|
+
return [codeSearch, codeExecute];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Fold a completed `tool_search` result into the per-run promotion set:
|
|
143
|
+
* every matched tool name becomes advertised (and thus callable) on the
|
|
144
|
+
* next step. Tolerant of unexpected shapes (e.g. a user-shadowed
|
|
145
|
+
* `tool_search`) - only string `name`s inside a `matches` array promote.
|
|
146
|
+
*/
|
|
147
|
+
function recordToolSearchPromotions(output, promoted) {
|
|
148
|
+
if (typeof output !== "object" || output === null) return;
|
|
149
|
+
const matches = output.matches;
|
|
150
|
+
if (!Array.isArray(matches)) return;
|
|
151
|
+
for (const match of matches) {
|
|
152
|
+
const name = match?.name;
|
|
153
|
+
if (typeof name === "string") promoted.add(name);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//#endregion
|
|
158
|
+
export { CODE_EXECUTE_NAME, CODE_SEARCH_NAME, READ_RESULT_NAME, TOOL_SEARCH_NAME, composeResultReaders, isApprovalGated, recordToolSearchPromotions, registerCodeMode, registerReadResult, registerToolSearch };
|
|
159
|
+
//# sourceMappingURL=tool-wiring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-wiring.js","names":["lastError: unknown","executeTool: CodeExecuteBridge"],"sources":["../../src/runtime/tool-wiring.ts"],"sourcesContent":["/**\n * Warm-up tool wiring for the agent runtime: registration of the\n * built-in `tool_search` / `read_result` tools, code-mode meta-tool\n * wiring, result-reader composition, and the `tool_search` promotion\n * fold. Extracted verbatim from `factory.ts` (issue #23).\n *\n * @packageDocumentation\n */\n\nimport type { Tool } from '@graphorin/core';\nimport { createReadResultTool, createToolSearchTool } from '@graphorin/tools/built-in';\nimport {\n type CodeExecuteBridge,\n createCodeExecuteTool,\n createCodeSearchTool,\n type ProjectableTool,\n projectToolApi,\n} from '@graphorin/tools/code-mode';\nimport type { ToolExecutor } from '@graphorin/tools/executor';\nimport type { ToolRegistry } from '@graphorin/tools/registry';\nimport type { ResultReader } from '@graphorin/tools/result';\nimport { newId } from '../internal/ids.js';\n\n/** The built-in deferred-discovery tool's stable name (WI-05 / P0-3). */\nexport const TOOL_SEARCH_NAME = 'tool_search';\n\n/**\n * Register the built-in `tool_search` into `registry` when - and only\n * when - the registry holds at least one deferred tool\n * (`defer_loading: true`). `tool_search` is itself eager (so it is\n * always advertised while a deferred pool exists) and resolvable by the\n * executor like any other tool, so a model can both *see* it in the\n * per-step catalogue and *call* it.\n *\n * No-op when nothing defers (zero overhead - the tool never appears) or\n * when a user tool already occupies the name (the user's tool wins; we\n * never clobber it). Because deferral is decided at registration time\n * (`normaliseTool`), the deferred set is fixed for the life of the\n * registry - this runs once per registry, not per step.\n */\nexport function registerToolSearch(\n registry: ToolRegistry,\n availability?: 'next-step' | 'next-run',\n): void {\n if (registry.listDeferred().length === 0) return;\n if (registry.get(TOOL_SEARCH_NAME) !== undefined) return;\n registry.register(\n createToolSearchTool({ registry, ...(availability !== undefined ? { availability } : {}) }),\n {\n kind: 'built-in',\n subsystem: 'tool-discovery',\n },\n );\n}\n\n/** The built-in result-handle reader tool's stable name (WI-10 / P1-4). */\nexport const READ_RESULT_NAME = 'read_result';\n\n/**\n * Register the built-in `read_result` into `registry` when at least one\n * registered tool opts into the `'spill-to-file'` truncation strategy\n * (the sole producer of spill handles today) - or when `force` is set,\n * which the agent passes when an operator wires external result readers\n * (e.g. an MCP `resource_link` reader; WI-13). The tool is eager, so it\n * is advertised alongside the producing tool and the model can fetch a\n * handle back on demand instead of inlining the full blob. No-op when\n * nothing produces handles (zero overhead) or when a user tool already\n * occupies the name (the user's tool wins).\n */\nexport function registerReadResult(\n registry: ToolRegistry,\n reader: ResultReader,\n opts?: { readonly force?: boolean },\n): void {\n if (\n opts?.force !== true &&\n !registry.list().some((entry) => entry.truncationStrategy === 'spill-to-file')\n ) {\n return;\n }\n if (registry.get(READ_RESULT_NAME) !== undefined) return;\n registry.register(createReadResultTool({ reader }), {\n kind: 'built-in',\n subsystem: 'result-handle',\n });\n}\n\n/**\n * Compose result readers into one that tries each in order, returning\n * the first that resolves the handle (WI-13). The spill-file reader is\n * placed first so `graphorin-spill:` handles resolve locally; operator\n * readers (e.g. an MCP resource reader) resolve the rest. Each reader\n * rejects handles it does not own, so resolution falls through cleanly.\n */\nexport function composeResultReaders(readers: ReadonlyArray<ResultReader>): ResultReader {\n return {\n async read(uri, range) {\n let lastError: unknown;\n for (const r of readers) {\n try {\n return await r.read(uri, range);\n } catch (err) {\n lastError = err;\n }\n }\n throw lastError instanceof Error\n ? lastError\n : new Error(`No result reader resolved handle ${JSON.stringify(uri)}.`);\n },\n };\n}\n\n/** The code-mode meta-tools' stable names (WI-11 / P1-2). */\nexport const CODE_EXECUTE_NAME = 'code_execute';\nexport const CODE_SEARCH_NAME = 'code_search';\n\n/** Structural check: is this tool approval-gated (static or predicate form)? */\nexport const isApprovalGated = (t: { readonly needsApproval?: unknown }): boolean =>\n t.needsApproval === true || typeof t.needsApproval === 'function';\n\n/**\n * Wire code-mode (P1-2) into `registry`: register the `code_search` /\n * `code_execute` meta-tools and return them as the tools to advertise in\n * place of the full catalogue. The model reaches every other tool through\n * `code_execute`, whose in-script `tools.<name>(args)` calls route back\n * through `quietExecutor.executeOne(...)` under the calling step's\n * `runContext` - so per-tool ACL / sanitization / truncation still apply,\n * exactly as in direct mode. `quietExecutor` carries no `streamingSink`,\n * so the inner calls do not interleave `tool.execute.*` events into the\n * outer stream.\n *\n * Excluded from the code API (`reservedNames`): the meta-tools, the\n * discovery / handle built-ins, handoff tools (which stay first-class\n * provider tools), and - supplied by the caller - any approval-gated\n * tool, since code-mode has no durable-HITL path to suspend mid-script.\n *\n * Returns `[]` (registering nothing) when no real tool is exposable.\n */\nexport function registerCodeMode(\n registry: ToolRegistry,\n quietExecutor: ToolExecutor,\n reservedNames: ReadonlySet<string>,\n getRunCapability?: () => 'read-only' | undefined,\n): ReadonlyArray<Tool<unknown, unknown, unknown>> {\n if (registry.get(CODE_EXECUTE_NAME) !== undefined) return []; // already wired\n const codeTools = registry\n .list()\n .filter((entry) => !reservedNames.has(entry.name) && !isApprovalGated(entry));\n if (codeTools.length === 0) return [];\n\n // TL-8: gated tools cannot suspend for HITL mid-script, so they are\n // excluded from the code API - but VISIBLY: they appear in the\n // catalogue/search with a call-directly marker, and a bridged call\n // fails with the same hint instead of an opaque unknown-tool error.\n const approvalGatedTools = registry\n .list()\n .filter((entry) => !reservedNames.has(entry.name) && isApprovalGated(entry))\n .map((entry) => entry.name);\n const approvalGatedSet = new Set(approvalGatedTools);\n\n const allowedTools = [...codeTools.map((entry) => entry.name), ...approvalGatedTools];\n const allowedSet = new Set(codeTools.map((entry) => entry.name));\n const eagerProjectable = codeTools.filter(\n (entry) => entry.__effectiveDeferLoading !== true,\n ) as unknown as ReadonlyArray<ProjectableTool>;\n const projection = projectToolApi(eagerProjectable);\n\n const executeTool: CodeExecuteBridge = async (call, ctx) => {\n if (approvalGatedSet.has(call.name)) {\n throw new Error(\n `${call.name} requires human approval and cannot run inside code_execute - call it directly as a standalone tool call so the run can suspend for the approval.`,\n );\n }\n const runCapability = getRunCapability?.();\n const completed = await quietExecutor.executeOne({\n call: { toolCallId: newId('codecall'), toolName: call.name, args: call.args },\n runContext: ctx.runContext,\n stepNumber: ctx.runContext.stepNumber,\n // D2: in-script calls inherit the active run's capability.\n ...(runCapability !== undefined ? { capability: runCapability } : {}),\n });\n const { outcome } = completed;\n if ('kind' in outcome) throw new Error(`${call.name}: ${outcome.message}`);\n return outcome.output;\n };\n\n const codeSearch = createCodeSearchTool({\n projection,\n approvalGatedTools,\n searchDeferred: async (query, k) =>\n (await registry.searchDeferred(query, k)).filter((match) => allowedSet.has(match.name)),\n });\n const codeExecute = createCodeExecuteTool({\n projection,\n allowedTools,\n executeTool,\n approvalGatedTools,\n });\n registry.register(codeSearch, { kind: 'built-in', subsystem: 'code-mode' });\n registry.register(codeExecute, { kind: 'built-in', subsystem: 'code-mode' });\n return [codeSearch, codeExecute] as ReadonlyArray<Tool<unknown, unknown, unknown>>;\n}\n\n/**\n * Fold a completed `tool_search` result into the per-run promotion set:\n * every matched tool name becomes advertised (and thus callable) on the\n * next step. Tolerant of unexpected shapes (e.g. a user-shadowed\n * `tool_search`) - only string `name`s inside a `matches` array promote.\n */\nexport function recordToolSearchPromotions(output: unknown, promoted: Set<string>): void {\n if (typeof output !== 'object' || output === null) return;\n const matches = (output as { readonly matches?: unknown }).matches;\n if (!Array.isArray(matches)) return;\n for (const match of matches) {\n const name = (match as { readonly name?: unknown } | null)?.name;\n if (typeof name === 'string') promoted.add(name);\n }\n}\n"],"mappings":"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"}
|
package/dist/tooling/adapters.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { countTokensHeuristic } from "@graphorin/tools/result";
|
|
2
1
|
import { createGuard } from "@graphorin/security/guard";
|
|
3
2
|
import "@graphorin/security/sandbox";
|
|
4
3
|
import { parseSecretRef, resolveSecret } from "@graphorin/security/secrets";
|
|
4
|
+
import { countTokensHeuristic } from "@graphorin/tools/result";
|
|
5
5
|
|
|
6
6
|
//#region src/tooling/adapters.ts
|
|
7
7
|
/**
|
package/dist/tooling/dataflow.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { containsPii } from "@graphorin/security/guardrails";
|
|
2
1
|
import { createDataFlowPolicy, createTaintLedger, deriveTaintLabel } from "@graphorin/security/dataflow";
|
|
2
|
+
import { containsPii } from "@graphorin/security/guardrails";
|
|
3
3
|
|
|
4
4
|
//#region src/tooling/dataflow.ts
|
|
5
5
|
/**
|