@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,307 @@
1
+ import { foldChildRunUsage, renderToolErrorMessage } from "./messages.js";
2
+ import { filters } from "../filters/index.js";
3
+
4
+ //#region src/runtime/handoff.ts
5
+ const HANDOFF_TOOL_PREFIX = "transfer_to_";
6
+ const PASSTHROUGH_SCHEMA = {
7
+ parse: (value) => value,
8
+ safeParse: (value) => ({
9
+ success: true,
10
+ data: value
11
+ }),
12
+ toJSON: () => ({ type: "object" })
13
+ };
14
+ function isDescribedFilter(value) {
15
+ return typeof value === "function" && "descriptor" in value && typeof value.descriptor === "object";
16
+ }
17
+ function buildHandoffTool(target) {
18
+ const cfg = target.config;
19
+ return {
20
+ name: `${HANDOFF_TOOL_PREFIX}${cfg.name}`,
21
+ description: `Hand off control to agent '${cfg.name}'.`,
22
+ inputSchema: PASSTHROUGH_SCHEMA,
23
+ sideEffectClass: "pure",
24
+ async execute() {
25
+ return `[handoff: ${cfg.name}]`;
26
+ }
27
+ };
28
+ }
29
+ /**
30
+ * A handoff child's seed must be a well-formed transcript in its own
31
+ * right: the parent history it is cut from ends with the in-flight
32
+ * handoff call itself (a dangling tool_use), and `lastN`-style filters
33
+ * can orphan tool results whose assistant partner was cut. Real
34
+ * providers reject both shapes with `invalid-request`, so strip
35
+ * unresolved tool calls (dropping the assistant message when nothing
36
+ * remains) and orphan tool messages before seeding the child.
37
+ */
38
+ function sanitizeHandoffSeed(messages) {
39
+ const announcedSoFar = /* @__PURE__ */ new Set();
40
+ const resolved = /* @__PURE__ */ new Set();
41
+ for (const m of messages) if (m.role === "assistant" && m.toolCalls !== void 0) for (const c of m.toolCalls) announcedSoFar.add(c.toolCallId);
42
+ else if (m.role === "tool" && announcedSoFar.has(m.toolCallId)) resolved.add(m.toolCallId);
43
+ const out = [];
44
+ const keptAnnounced = /* @__PURE__ */ new Set();
45
+ for (const m of messages) {
46
+ if (m.role === "tool") {
47
+ if (!keptAnnounced.has(m.toolCallId)) continue;
48
+ out.push(m);
49
+ continue;
50
+ }
51
+ if (m.role === "assistant" && m.toolCalls !== void 0) {
52
+ const kept = m.toolCalls.filter((c) => resolved.has(c.toolCallId));
53
+ for (const c of kept) keptAnnounced.add(c.toolCallId);
54
+ if (kept.length === m.toolCalls.length) {
55
+ out.push(m);
56
+ continue;
57
+ }
58
+ const { toolCalls: _dropped, ...rest } = m;
59
+ const hasContent = typeof rest.content === "string" ? rest.content.length > 0 : Array.isArray(rest.content) && rest.content.length > 0;
60
+ if (kept.length === 0 && !hasContent) continue;
61
+ out.push(kept.length > 0 ? {
62
+ ...rest,
63
+ toolCalls: kept
64
+ } : rest);
65
+ continue;
66
+ }
67
+ out.push(m);
68
+ }
69
+ return out;
70
+ }
71
+ /**
72
+ * Execute one handoff tool call inline (handoffs are special-cased by
73
+ * the tool-call walk, ≤1 per step, and never routed through the
74
+ * executor): record the {@link HandoffRecord}, transfer
75
+ * `currentAgentId`, stream the sub-agent over the filtered history, and
76
+ * surface its outcome as the long-standing `tool.execute.*` events +
77
+ * tool message.
78
+ */
79
+ async function* executeHandoffToolCall(env, call, handoff, stepNumber) {
80
+ const { config, options, state, messages, sessionId, agentId, signal } = env;
81
+ yield {
82
+ type: "tool.execute.start",
83
+ toolCallId: call.toolCallId,
84
+ toolName: call.toolName
85
+ };
86
+ const filter = handoff.filter ?? filters.defaultHandoffFilter();
87
+ const filtered = sanitizeHandoffSeed(filter(messages));
88
+ const targetId = handoff.agent.id;
89
+ const handoffRec = {
90
+ fromAgentId: agentId,
91
+ toAgentId: targetId,
92
+ stepNumber,
93
+ at: (/* @__PURE__ */ new Date()).toISOString(),
94
+ inputFilter: filter.descriptor,
95
+ secretsInheritance: "inherit-allowlist",
96
+ inheritedSecrets: []
97
+ };
98
+ state.handoffs.push(handoffRec);
99
+ yield {
100
+ type: "handoff",
101
+ fromAgentId: agentId,
102
+ toAgentId: targetId
103
+ };
104
+ const previousAgentId = state.currentAgentId;
105
+ state.currentAgentId = targetId;
106
+ const subAgent = handoff.agent;
107
+ try {
108
+ const parentSpan = env.getCurrentStepSpan?.();
109
+ const subStream = subAgent.stream(filtered, {
110
+ signal,
111
+ ...options.deps !== void 0 || config.deps !== void 0 ? { deps: options.deps ?? config.deps } : {},
112
+ sessionId,
113
+ ...parentSpan !== void 0 ? { parentSpan } : {}
114
+ });
115
+ return yield* runSubAgentCall(env, call, {
116
+ agentName: handoff.agent.config.name,
117
+ subStream,
118
+ errorLabel: `handoff to '${targetId}'`,
119
+ renderCompleted: (_subResult, turns) => ({ output: turns.join("") }),
120
+ ...handoff.forwardEvents !== void 0 ? { forwardEvents: handoff.forwardEvents } : {}
121
+ }, stepNumber);
122
+ } finally {
123
+ state.currentAgentId = previousAgentId;
124
+ }
125
+ }
126
+ /**
127
+ * W-036: the `'lifecycle'` forwarding whitelist - load-bearing child
128
+ * events, never the high-frequency text/reasoning deltas.
129
+ */
130
+ const LIFECYCLE_FORWARD_TYPES = new Set([
131
+ "tool.execute.start",
132
+ "tool.execute.progress",
133
+ "tool.execute.partial",
134
+ "tool.execute.end",
135
+ "tool.execute.error",
136
+ "tool.approval.requested",
137
+ "tool.approval.granted",
138
+ "tool.approval.denied",
139
+ "guardrail.tripped",
140
+ "agent.lateral-leak.detected",
141
+ "context.compacted",
142
+ "agent.error"
143
+ ]);
144
+ function shouldForwardSubagentEvent(policy, eventType) {
145
+ if (policy === "none") return false;
146
+ if (policy === "all") return true;
147
+ return LIFECYCLE_FORWARD_TYPES.has(eventType);
148
+ }
149
+ /**
150
+ * W-001: compose the sub-run routing path. An approval mirrored from a
151
+ * child that itself parked a grandchild already carries the CHILD-level
152
+ * routing; prefixing this level's park key keeps the full path intact
153
+ * (`<parentCallId>/<childCallId>/...`), so each resume level strips one
154
+ * segment and routes the remainder down. Park-key toolCallIds must not
155
+ * contain `/` (provider call ids do not).
156
+ */
157
+ function composeSubRunPath(parkKey, nested) {
158
+ return nested === void 0 ? parkKey : `${parkKey}/${nested}`;
159
+ }
160
+ /** Split one routing segment off a composed sub-run path (W-001). */
161
+ function splitSubRunPath(path) {
162
+ const idx = path.indexOf("/");
163
+ if (idx === -1) return {
164
+ head: path,
165
+ rest: void 0
166
+ };
167
+ return {
168
+ head: path.slice(0, idx),
169
+ rest: path.slice(idx + 1)
170
+ };
171
+ }
172
+ /** Park (or refresh) a suspended child on the parent state (W-001). */
173
+ function parkSubRun(state, call, agentName, childState) {
174
+ const entry = {
175
+ toolCallId: call.toolCallId,
176
+ toolName: call.toolName,
177
+ targetAgentName: agentName,
178
+ state: childState
179
+ };
180
+ const subs = state.pendingSubRuns;
181
+ if (subs === void 0) {
182
+ state.pendingSubRuns = [entry];
183
+ return;
184
+ }
185
+ const idx = subs.findIndex((s) => s.toolCallId === call.toolCallId);
186
+ if (idx === -1) subs.push(entry);
187
+ else subs[idx] = entry;
188
+ }
189
+ /**
190
+ * W-001: the shared sub-run seam behind handoff and inline `toTool`
191
+ * execution. Observes the child stream and settles the outcome:
192
+ *
193
+ * - `awaiting_approval`: the child PARKS on `state.pendingSubRuns`, its
194
+ * pending approvals mirror onto the parent's `pendingApprovals` with
195
+ * `subRunToolCallId` set, and the walk suspends the parent once per
196
+ * step. The parked toolCallId keeps NO tool message - exactly like a
197
+ * directly-gated call - and the resume guard keeps it out of the
198
+ * provider loop.
199
+ * - terminal failure: a typed tool error (the pre-W-001 behavior).
200
+ * - completed: the branch-shaped output becomes the tool message.
201
+ *
202
+ * Usage folds on terminal outcomes only - the child's cumulative usage
203
+ * folds exactly once when it finally completes or fails, never at a
204
+ * park (a park would double-count the pre-suspend tokens on resume).
205
+ */
206
+ async function* runSubAgentCall(env, call, spec, stepNumber) {
207
+ const { state, messages, usageAcc } = env;
208
+ const subStart = Date.now();
209
+ const turns = [];
210
+ const forwardPolicy = spec.forwardEvents ?? "lifecycle";
211
+ let subResult;
212
+ for await (const subEv of spec.subStream) {
213
+ if (subEv.type === "text.complete") turns.push(subEv.text);
214
+ else if (subEv.type === "agent.end") subResult = subEv.result;
215
+ if (shouldForwardSubagentEvent(forwardPolicy, subEv.type)) yield {
216
+ type: "subagent.event",
217
+ toolCallId: call.toolCallId,
218
+ agentName: spec.agentName,
219
+ event: subEv
220
+ };
221
+ }
222
+ const subDurationMs = Date.now() - subStart;
223
+ if (subResult !== void 0 && subResult.status === "awaiting_approval") {
224
+ parkSubRun(state, call, spec.agentName, subResult.state);
225
+ for (const approval of subResult.state.pendingApprovals) {
226
+ state.pendingApprovals.push({
227
+ ...approval,
228
+ subRunToolCallId: composeSubRunPath(call.toolCallId, approval.subRunToolCallId)
229
+ });
230
+ yield {
231
+ type: "tool.approval.requested",
232
+ toolCallId: approval.toolCallId,
233
+ ...approval.reason !== void 0 ? { reason: approval.reason } : {}
234
+ };
235
+ }
236
+ return { suspendRequested: true };
237
+ }
238
+ if (subResult !== void 0) foldChildRunUsage(state, usageAcc, subResult.state, spec.agentName);
239
+ const stepEntry = state.steps[state.steps.length - 1];
240
+ if (subResult !== void 0 && subResult.status !== "completed") {
241
+ const toolError = {
242
+ toolCallId: call.toolCallId,
243
+ toolName: call.toolName,
244
+ kind: subResult.status === "aborted" ? "aborted" : "execution_failed",
245
+ message: `${spec.errorLabel} ${subResult.status}${subResult.error !== void 0 ? `: ${subResult.error.message}` : ""}`
246
+ };
247
+ if (stepEntry !== void 0) stepEntry.toolCalls.push({
248
+ call,
249
+ outcome: toolError,
250
+ stepNumber
251
+ });
252
+ yield {
253
+ type: "tool.execute.error",
254
+ toolCallId: call.toolCallId,
255
+ toolName: call.toolName,
256
+ error: toolError
257
+ };
258
+ const text = renderToolErrorMessage(toolError);
259
+ messages.push({
260
+ role: "tool",
261
+ toolCallId: call.toolCallId,
262
+ content: text
263
+ });
264
+ state.messages.push({
265
+ role: "tool",
266
+ toolCallId: call.toolCallId,
267
+ content: text
268
+ });
269
+ return { suspendRequested: false };
270
+ }
271
+ const shaped = subResult !== void 0 ? spec.renderCompleted(subResult, turns) : { output: turns.join("") };
272
+ const result = typeof shaped.output === "string" ? shaped.output : JSON.stringify(shaped.output) ?? "";
273
+ if ("taint" in shaped && shaped.taint !== void 0) spec.recordTaint?.(shaped.taint, result);
274
+ const completed = {
275
+ call,
276
+ outcome: {
277
+ toolCallId: call.toolCallId,
278
+ toolName: call.toolName,
279
+ output: result,
280
+ durationMs: subDurationMs
281
+ },
282
+ stepNumber
283
+ };
284
+ if (stepEntry !== void 0) stepEntry.toolCalls.push(completed);
285
+ yield {
286
+ type: "tool.execute.end",
287
+ toolCallId: call.toolCallId,
288
+ toolName: call.toolName,
289
+ result,
290
+ durationMs: subDurationMs
291
+ };
292
+ messages.push({
293
+ role: "tool",
294
+ toolCallId: call.toolCallId,
295
+ content: result
296
+ });
297
+ state.messages.push({
298
+ role: "tool",
299
+ toolCallId: call.toolCallId,
300
+ content: result
301
+ });
302
+ return { suspendRequested: false };
303
+ }
304
+
305
+ //#endregion
306
+ export { HANDOFF_TOOL_PREFIX, buildHandoffTool, composeSubRunPath, executeHandoffToolCall, isDescribedFilter, runSubAgentCall, splitSubRunPath };
307
+ //# sourceMappingURL=handoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff.js","names":["out: Message[]","filterLib","handoffRec: HandoffRecord","LIFECYCLE_FORWARD_TYPES: ReadonlySet<string>","turns: string[]","subResult: AgentResult<unknown> | undefined","toolError: ToolError","completed: CompletedToolCall"],"sources":["../../src/runtime/handoff.ts"],"sourcesContent":["/**\n * Multi-agent handoff support for the agent runtime: the synthetic\n * `transfer_to_<name>` tool advertised per step, the `DescribedFilter`\n * structural check, and the inline execution of a handoff call (the\n * sub-agent stream observation that surfaces a failed / aborted sub-run\n * as a tool error). Extracted verbatim from `factory.ts` (issue #23).\n *\n * @packageDocumentation\n */\n\nimport type {\n AgentEvent,\n AgentResult,\n CompletedToolCall,\n HandoffRecord,\n Message,\n RunState,\n Tool,\n ToolCall,\n ToolError,\n UsageAccumulator,\n} from '@graphorin/core';\nimport { type DescribedFilter, filters as filterLib } from '../filters/index.js';\nimport type { Agent, AgentCallOptions, AgentConfig, SubagentForwardPolicy } from '../types.js';\nimport type { SubAgentFoldTaint } from './agent-to-tool.js';\nimport { foldChildRunUsage, renderToolErrorMessage } from './messages.js';\nimport type { MutableRunState } from './run-input.js';\n\nexport const HANDOFF_TOOL_PREFIX = 'transfer_to_';\n\nexport const PASSTHROUGH_SCHEMA = {\n parse: <T>(value: unknown): T => value as T,\n safeParse: <T>(value: unknown) => ({ success: true as const, data: value as T }),\n toJSON: (): Record<string, unknown> => ({ type: 'object' }),\n} as const;\n\nexport function isDescribedFilter(value: unknown): value is DescribedFilter {\n return (\n typeof value === 'function' &&\n 'descriptor' in value &&\n typeof (value as DescribedFilter).descriptor === 'object'\n );\n}\n\nexport function buildHandoffTool<TDeps>(\n target: Agent<TDeps, unknown>,\n): Tool<unknown, unknown, TDeps> {\n const cfg = target.config;\n const name = `${HANDOFF_TOOL_PREFIX}${cfg.name}`;\n const tool: Tool<unknown, unknown, TDeps> = {\n name,\n description: `Hand off control to agent '${cfg.name}'.`,\n inputSchema: PASSTHROUGH_SCHEMA as unknown as Tool<unknown, unknown, TDeps>['inputSchema'],\n sideEffectClass: 'pure',\n async execute(): Promise<string> {\n return `[handoff: ${cfg.name}]`;\n },\n };\n return tool;\n}\n\n/**\n * A handoff child's seed must be a well-formed transcript in its own\n * right: the parent history it is cut from ends with the in-flight\n * handoff call itself (a dangling tool_use), and `lastN`-style filters\n * can orphan tool results whose assistant partner was cut. Real\n * providers reject both shapes with `invalid-request`, so strip\n * unresolved tool calls (dropping the assistant message when nothing\n * remains) and orphan tool messages before seeding the child.\n */\nexport function sanitizeHandoffSeed(messages: ReadonlyArray<Message>): Message[] {\n const announcedSoFar = new Set<string>();\n const resolved = new Set<string>();\n for (const m of messages) {\n if (m.role === 'assistant' && m.toolCalls !== undefined) {\n for (const c of m.toolCalls) announcedSoFar.add(c.toolCallId);\n } else if (m.role === 'tool' && announcedSoFar.has(m.toolCallId)) {\n resolved.add(m.toolCallId);\n }\n }\n const out: Message[] = [];\n const keptAnnounced = new Set<string>();\n for (const m of messages) {\n if (m.role === 'tool') {\n if (!keptAnnounced.has(m.toolCallId)) continue;\n out.push(m);\n continue;\n }\n if (m.role === 'assistant' && m.toolCalls !== undefined) {\n const kept = m.toolCalls.filter((c) => resolved.has(c.toolCallId));\n for (const c of kept) keptAnnounced.add(c.toolCallId);\n if (kept.length === m.toolCalls.length) {\n out.push(m);\n continue;\n }\n const { toolCalls: _dropped, ...rest } = m;\n const hasContent =\n typeof rest.content === 'string'\n ? rest.content.length > 0\n : Array.isArray(rest.content) && rest.content.length > 0;\n if (kept.length === 0 && !hasContent) continue;\n out.push(kept.length > 0 ? { ...rest, toolCalls: kept } : rest);\n continue;\n }\n out.push(m);\n }\n return out;\n}\n\n/** One resolved handoff target (the factory's `handoffMap` values). */\nexport interface HandoffEntry<TDeps> {\n readonly agent: Agent<TDeps, unknown>;\n readonly filter: DescribedFilter | undefined;\n /** W-036: which child events forward into the parent stream. */\n readonly forwardEvents?: SubagentForwardPolicy | undefined;\n}\n\n/** The run-scoped context a handoff execution operates on. */\nexport interface HandoffRunEnv<TDeps, TOutput> {\n readonly config: Pick<AgentConfig<TDeps, TOutput>, 'deps'>;\n readonly options: AgentCallOptions<TDeps>;\n readonly state: MutableRunState & RunState;\n readonly messages: Message[];\n readonly sessionId: string;\n readonly agentId: string;\n readonly signal: AbortSignal;\n /** The run's usage accumulator - child-run usage folds into it (W-033). */\n readonly usageAcc: UsageAccumulator;\n /**\n * W-036: live read of the parent's current step span, the parent for\n * a sub-agent's `agent.run` span (one trace tree).\n */\n readonly getCurrentStepSpan?: () => import('@graphorin/core').AISpan | undefined;\n}\n\n/**\n * Execute one handoff tool call inline (handoffs are special-cased by\n * the tool-call walk, ≤1 per step, and never routed through the\n * executor): record the {@link HandoffRecord}, transfer\n * `currentAgentId`, stream the sub-agent over the filtered history, and\n * surface its outcome as the long-standing `tool.execute.*` events +\n * tool message.\n */\nexport async function* executeHandoffToolCall<TDeps, TOutput>(\n env: HandoffRunEnv<TDeps, TOutput>,\n call: ToolCall,\n handoff: HandoffEntry<TDeps>,\n stepNumber: number,\n): AsyncGenerator<AgentEvent<TOutput>, { readonly suspendRequested: boolean }, void> {\n const { config, options, state, messages, sessionId, agentId, signal } = env;\n yield { type: 'tool.execute.start', toolCallId: call.toolCallId, toolName: call.toolName };\n const filter = (handoff.filter ?? filterLib.defaultHandoffFilter()) as DescribedFilter;\n const filtered = sanitizeHandoffSeed(filter(messages) as Message[]);\n const targetId = handoff.agent.id;\n // The secrets fields record the structural reality: no\n // inheritance mechanism exists at this boundary, so the\n // target receives nothing - an empty allowlist is the\n // factually-true provenance (AG-17).\n const handoffRec: HandoffRecord = {\n fromAgentId: agentId,\n toAgentId: targetId,\n stepNumber,\n at: new Date().toISOString(),\n inputFilter: filter.descriptor,\n secretsInheritance: 'inherit-allowlist',\n inheritedSecrets: [],\n };\n state.handoffs.push(handoffRec);\n yield { type: 'handoff', fromAgentId: agentId, toAgentId: targetId };\n // W-034: `currentAgentId` identifies the agent whose model drives the\n // NEXT step - the parent resumes driving once the child returns, so\n // the transfer is scoped to the child observation and restored in\n // `finally` (every branch, including the W-001 park, and on generator\n // teardown). The child's identity is durably recorded by the\n // HandoffRecord + handoff event.\n const previousAgentId = state.currentAgentId;\n state.currentAgentId = targetId;\n const subAgent = handoff.agent;\n try {\n // AG-22: the sub-agent inherits the parent's abort signal,\n // deps, and sessionId; its terminal `agent.end` is observed\n // so a failed/aborted sub-run surfaces as a TOOL ERROR -\n // never an empty-string success with durationMs 0.\n const parentSpan = env.getCurrentStepSpan?.();\n const subStream = subAgent.stream(filtered as Message[], {\n signal,\n ...(options.deps !== undefined || config.deps !== undefined\n ? { deps: (options.deps ?? config.deps) as TDeps }\n : {}),\n sessionId,\n // W-036: one trace tree - the child's run span parents here.\n ...(parentSpan !== undefined ? { parentSpan } : {}),\n });\n return yield* runSubAgentCall<TDeps, TOutput>(\n env,\n call,\n {\n agentName: handoff.agent.config.name,\n subStream: subStream as AsyncIterable<AgentEvent<unknown>>,\n errorLabel: `handoff to '${targetId}'`,\n renderCompleted: (_subResult, turns) => ({ output: turns.join('') }),\n ...(handoff.forwardEvents !== undefined ? { forwardEvents: handoff.forwardEvents } : {}),\n },\n stepNumber,\n );\n } finally {\n state.currentAgentId = previousAgentId;\n }\n}\n\n/** Branch-specific spec for the shared sub-run seam (W-001). */\nexport interface SubRunSpec {\n /** The child agent's configured name (parking + usage folding). */\n readonly agentName: string;\n readonly subStream: AsyncIterable<AgentEvent<unknown>>;\n /** Prefix for the failed/aborted tool-error message. */\n readonly errorLabel: string;\n /** Shape a completed child into the tool-message payload. */\n readonly renderCompleted: (\n subResult: AgentResult<unknown>,\n turns: ReadonlyArray<string>,\n ) => { readonly output: unknown; readonly taint?: SubAgentFoldTaint };\n /** Optional taint sink (the inline toTool path records D2 folds). */\n readonly recordTaint?: (taint: SubAgentFoldTaint, renderedText: string) => void;\n /** W-036: forwarding policy (default `'lifecycle'`). */\n readonly forwardEvents?: SubagentForwardPolicy;\n}\n\n/**\n * W-036: the `'lifecycle'` forwarding whitelist - load-bearing child\n * events, never the high-frequency text/reasoning deltas.\n */\nconst LIFECYCLE_FORWARD_TYPES: ReadonlySet<string> = new Set([\n 'tool.execute.start',\n 'tool.execute.progress',\n 'tool.execute.partial',\n 'tool.execute.end',\n 'tool.execute.error',\n 'tool.approval.requested',\n 'tool.approval.granted',\n 'tool.approval.denied',\n 'guardrail.tripped',\n 'agent.lateral-leak.detected',\n 'context.compacted',\n 'agent.error',\n]);\n\nfunction shouldForwardSubagentEvent(policy: SubagentForwardPolicy, eventType: string): boolean {\n if (policy === 'none') return false;\n if (policy === 'all') return true;\n return LIFECYCLE_FORWARD_TYPES.has(eventType);\n}\n\n/**\n * W-001: compose the sub-run routing path. An approval mirrored from a\n * child that itself parked a grandchild already carries the CHILD-level\n * routing; prefixing this level's park key keeps the full path intact\n * (`<parentCallId>/<childCallId>/...`), so each resume level strips one\n * segment and routes the remainder down. Park-key toolCallIds must not\n * contain `/` (provider call ids do not).\n */\nexport function composeSubRunPath(parkKey: string, nested: string | undefined): string {\n return nested === undefined ? parkKey : `${parkKey}/${nested}`;\n}\n\n/** Split one routing segment off a composed sub-run path (W-001). */\nexport function splitSubRunPath(path: string): {\n readonly head: string;\n readonly rest: string | undefined;\n} {\n const idx = path.indexOf('/');\n if (idx === -1) return { head: path, rest: undefined };\n return { head: path.slice(0, idx), rest: path.slice(idx + 1) };\n}\n\n/** Park (or refresh) a suspended child on the parent state (W-001). */\nfunction parkSubRun(\n state: MutableRunState & RunState,\n call: ToolCall,\n agentName: string,\n childState: RunState,\n): void {\n const entry = {\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n targetAgentName: agentName,\n state: childState,\n };\n const subs = state.pendingSubRuns;\n if (subs === undefined) {\n state.pendingSubRuns = [entry];\n return;\n }\n const idx = subs.findIndex((s) => s.toolCallId === call.toolCallId);\n if (idx === -1) subs.push(entry);\n else subs[idx] = entry;\n}\n\n/**\n * W-001: the shared sub-run seam behind handoff and inline `toTool`\n * execution. Observes the child stream and settles the outcome:\n *\n * - `awaiting_approval`: the child PARKS on `state.pendingSubRuns`, its\n * pending approvals mirror onto the parent's `pendingApprovals` with\n * `subRunToolCallId` set, and the walk suspends the parent once per\n * step. The parked toolCallId keeps NO tool message - exactly like a\n * directly-gated call - and the resume guard keeps it out of the\n * provider loop.\n * - terminal failure: a typed tool error (the pre-W-001 behavior).\n * - completed: the branch-shaped output becomes the tool message.\n *\n * Usage folds on terminal outcomes only - the child's cumulative usage\n * folds exactly once when it finally completes or fails, never at a\n * park (a park would double-count the pre-suspend tokens on resume).\n */\nexport async function* runSubAgentCall<TDeps, TOutput>(\n env: HandoffRunEnv<TDeps, TOutput>,\n call: ToolCall,\n spec: SubRunSpec,\n stepNumber: number,\n): AsyncGenerator<AgentEvent<TOutput>, { readonly suspendRequested: boolean }, void> {\n const { state, messages, usageAcc } = env;\n const subStart = Date.now();\n const turns: string[] = [];\n const forwardPolicy = spec.forwardEvents ?? 'lifecycle';\n let subResult: AgentResult<unknown> | undefined;\n for await (const subEv of spec.subStream) {\n if (subEv.type === 'text.complete') turns.push(subEv.text);\n else if (subEv.type === 'agent.end') {\n subResult = subEv.result as AgentResult<unknown>;\n }\n // W-036: surface the child's load-bearing events in the parent\n // stream, wrapped so they never alias the parent's own lifecycle.\n if (shouldForwardSubagentEvent(forwardPolicy, subEv.type)) {\n yield {\n type: 'subagent.event',\n toolCallId: call.toolCallId,\n agentName: spec.agentName,\n event: subEv as AgentEvent<unknown>,\n } as AgentEvent<TOutput>;\n }\n }\n const subDurationMs = Date.now() - subStart;\n\n if (subResult !== undefined && subResult.status === 'awaiting_approval') {\n parkSubRun(state, call, spec.agentName, subResult.state);\n for (const approval of subResult.state.pendingApprovals) {\n state.pendingApprovals.push({\n ...approval,\n subRunToolCallId: composeSubRunPath(call.toolCallId, approval.subRunToolCallId),\n });\n yield {\n type: 'tool.approval.requested',\n toolCallId: approval.toolCallId,\n ...(approval.reason !== undefined ? { reason: approval.reason } : {}),\n };\n }\n return { suspendRequested: true };\n }\n\n // W-033: fold the child's usage into the parent's accounting on every\n // TERMINAL outcome - tokens were spent whether it completed or failed.\n if (subResult !== undefined) {\n foldChildRunUsage(state, usageAcc, subResult.state, spec.agentName);\n }\n const stepEntry = state.steps[state.steps.length - 1];\n if (subResult !== undefined && subResult.status !== 'completed') {\n const toolError: ToolError = {\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n kind: subResult.status === 'aborted' ? 'aborted' : 'execution_failed',\n message: `${spec.errorLabel} ${subResult.status}${\n subResult.error !== undefined ? `: ${subResult.error.message}` : ''\n }`,\n };\n if (stepEntry !== undefined) {\n (stepEntry.toolCalls as CompletedToolCall[]).push({\n call,\n outcome: toolError,\n stepNumber,\n });\n }\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 return { suspendRequested: false };\n }\n const shaped =\n subResult !== undefined ? spec.renderCompleted(subResult, turns) : { output: turns.join('') };\n const result =\n typeof shaped.output === 'string' ? shaped.output : (JSON.stringify(shaped.output) ?? '');\n if ('taint' in shaped && shaped.taint !== undefined) {\n spec.recordTaint?.(shaped.taint, result);\n }\n const completed: CompletedToolCall = {\n call,\n outcome: {\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n output: result,\n durationMs: subDurationMs,\n },\n stepNumber,\n };\n if (stepEntry !== undefined) {\n (stepEntry.toolCalls as CompletedToolCall[]).push(completed);\n }\n yield {\n type: 'tool.execute.end',\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n result,\n durationMs: subDurationMs,\n };\n messages.push({ role: 'tool', toolCallId: call.toolCallId, content: result });\n state.messages.push({\n role: 'tool',\n toolCallId: call.toolCallId,\n content: result,\n });\n return { suspendRequested: false };\n}\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { Message, Provider, ReasoningRetention, RunState, Usage, UsageAccumulator } from "@graphorin/core";
2
+ import "@graphorin/memory";
3
+
4
+ //#region src/runtime/messages.d.ts
5
+
6
+ /**
7
+ * Fold a completed (or failed - tokens were spent either way) child
8
+ * run's usage into the parent run's accounting: `state.usage`,
9
+ * `state.usageByModel` and the run's {@link UsageAccumulator} (W-033).
10
+ * Children carrying a per-model breakdown fold model-by-model (each
11
+ * child model entry counts as one attempt on the parent); a child
12
+ * without `usageByModel` folds its aggregate under the synthetic id
13
+ * `sub-agent:<name>`, skipped entirely when all-zero so phantom
14
+ * entries never appear.
15
+ *
16
+ * Must be called exactly once per child run at exactly one seam -
17
+ * a second call double-counts (pinned by test).
18
+ */
19
+ declare function foldChildRunUsage(state: RunState, usageAcc: UsageAccumulator | undefined, childState: RunState, childName: string): void;
20
+ //#endregion
21
+ export { foldChildRunUsage };
22
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.d.ts","names":[],"sources":["../../src/runtime/messages.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;iBA+PgB,iBAAA,QACP,oBACG,0CACE"}
@@ -0,0 +1,204 @@
1
+ import { addModelUsage } from "../run-state/index.js";
2
+ import { renderPlanRecitation } from "../tooling/plan.js";
3
+ import { COMPACTION_SUMMARY_MARKER } from "@graphorin/memory";
4
+
5
+ //#region src/runtime/messages.ts
6
+ /** Most-recent user-role text in `messages` (for context-engine auto-recall). */
7
+ function lastUserText(messages) {
8
+ for (let i = messages.length - 1; i >= 0; i--) {
9
+ const m = messages[i];
10
+ if (m?.role !== "user") continue;
11
+ if (typeof m.content === "string") return m.content;
12
+ const text = m.content.filter((p) => p.type === "text").map((p) => p.text).join(" ");
13
+ return text.length > 0 ? text : void 0;
14
+ }
15
+ }
16
+ /**
17
+ * Resolve the effective {@link ReasoningRetention} for a step. The
18
+ * agent-level setting wins over the provider-level default; when
19
+ * neither is supplied, the provider's `reasoningContract`
20
+ * capability drives the default per RB-42 / suggested DEC-158.
21
+ */
22
+ function effectiveReasoningRetention(agentOverride, provider) {
23
+ if (agentOverride !== void 0) return agentOverride;
24
+ switch (provider.capabilities.reasoningContract) {
25
+ case "round-trip-required": return "pass-through-claude";
26
+ case "optional": return "pass-through-all";
27
+ case "hidden": return "strip";
28
+ default: return "strip";
29
+ }
30
+ }
31
+ /**
32
+ * Build the assistant message that the runtime appends to the
33
+ * message buffer after a successful provider call. When the
34
+ * effective {@link ReasoningRetention} is not `'strip'`, the
35
+ * assembled `reasoning` content parts ride along on `content` so
36
+ * the next provider call honours the wire-correct round-trip
37
+ * contract per RB-42.
38
+ */
39
+ function buildAssistantMessage(text, reasoningParts, toolCalls, agentId, retention) {
40
+ if (retention !== "strip" && reasoningParts.length > 0) {
41
+ const parts = [...reasoningParts];
42
+ if (text.length > 0) parts.push({
43
+ type: "text",
44
+ text
45
+ });
46
+ return {
47
+ role: "assistant",
48
+ content: parts,
49
+ ...toolCalls.length > 0 ? { toolCalls } : {},
50
+ agentId
51
+ };
52
+ }
53
+ return {
54
+ role: "assistant",
55
+ content: text,
56
+ ...toolCalls.length > 0 ? { toolCalls } : {},
57
+ agentId
58
+ };
59
+ }
60
+ /**
61
+ * Strip every {@link ReasoningContent} part from each message in
62
+ * the supplied list. Used at the swap point when `prepareStep`
63
+ * downgrades the provider's `reasoningContract` mid-run.
64
+ */
65
+ function stripReasoningFromMessages(messages) {
66
+ let stripped = 0;
67
+ for (let i = 0; i < messages.length; i++) {
68
+ const msg = messages[i];
69
+ if (msg === void 0) continue;
70
+ if (msg.role === "system" || msg.role === "tool") continue;
71
+ if (typeof msg.content === "string") continue;
72
+ const filtered = msg.content.filter((p) => p.type !== "reasoning");
73
+ if (filtered.length === msg.content.length) continue;
74
+ stripped += msg.content.length - filtered.length;
75
+ if (msg.role === "assistant") messages[i] = {
76
+ ...msg,
77
+ content: filtered
78
+ };
79
+ else messages[i] = {
80
+ ...msg,
81
+ content: filtered
82
+ };
83
+ }
84
+ return { stripped };
85
+ }
86
+ function countLeadingSystemMessages(messages) {
87
+ let i = 0;
88
+ while (i < messages.length) {
89
+ const msg = messages[i];
90
+ if (msg?.role !== "system") break;
91
+ if (typeof msg.content === "string" && msg.content.startsWith(COMPACTION_SUMMARY_MARKER)) break;
92
+ i += 1;
93
+ }
94
+ return i;
95
+ }
96
+ /**
97
+ * Immutable usage sum. Optional token fields (reasoning + prompt-cache
98
+ * legs, core-provider-02) appear in the result only when at least one
99
+ * side carries them, so pre-cache serialized shapes stay byte-identical.
100
+ */
101
+ /**
102
+ * D6: assemble the per-step request messages. Trailing, request-only
103
+ * additions ride the LAST prompt-cache anchor and never touch the shared
104
+ * `messages` buffer or the persisted RunState: the structured-output
105
+ * instruction (AG-3) and the attention-recitation plan block are both
106
+ * appended here, in that order, so the stable prompt prefix is unchanged
107
+ * and only the small trailing tail is re-sent each step.
108
+ */
109
+ function buildStepMessages(messages, structuredInstruction, todos) {
110
+ const out = [...messages];
111
+ if (structuredInstruction !== void 0) out.push({
112
+ role: "system",
113
+ content: structuredInstruction
114
+ });
115
+ const recitation = renderPlanRecitation(todos);
116
+ if (recitation !== null) out.push({
117
+ role: "system",
118
+ content: recitation
119
+ });
120
+ return out;
121
+ }
122
+ function addUsage(a, b) {
123
+ const optional = (x, y) => x === void 0 && y === void 0 ? void 0 : (x ?? 0) + (y ?? 0);
124
+ const reasoningTokens = optional(a.reasoningTokens, b.reasoningTokens);
125
+ const cachedReadTokens = optional(a.cachedReadTokens, b.cachedReadTokens);
126
+ const cacheWriteTokens = optional(a.cacheWriteTokens, b.cacheWriteTokens);
127
+ return {
128
+ promptTokens: a.promptTokens + b.promptTokens,
129
+ completionTokens: a.completionTokens + b.completionTokens,
130
+ totalTokens: a.totalTokens + b.totalTokens,
131
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {},
132
+ ...cachedReadTokens !== void 0 ? { cachedReadTokens } : {},
133
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
134
+ };
135
+ }
136
+ /**
137
+ * C3: render a ToolError for the model. The first line keeps the
138
+ * long-standing `Error: <message>` shape; a bracketed second line carries
139
+ * the typed kind + the recovery envelope, which is what actually changes
140
+ * model behaviour after a failure (retry vs. fix args vs. give up).
141
+ */
142
+ function renderToolErrorMessage(error) {
143
+ const parts = [`kind: ${error.kind}`];
144
+ if (error.recoverable !== void 0) parts.push(error.recoverable ? "recoverable: yes" : "recoverable: no");
145
+ if (error.recoveryHint !== void 0) parts.push(`suggested action: ${error.recoveryHint}`);
146
+ if (error.hint !== void 0) parts.push(error.hint);
147
+ return `Error: ${error.message}\n[${parts.join("; ")}]`;
148
+ }
149
+ /** In-place variant of {@link addUsage} for mutable accumulators. */
150
+ function accumulateUsage(target, delta) {
151
+ target.promptTokens += delta.promptTokens;
152
+ target.completionTokens += delta.completionTokens;
153
+ target.totalTokens += delta.totalTokens;
154
+ if (delta.reasoningTokens !== void 0) target.reasoningTokens = (target.reasoningTokens ?? 0) + delta.reasoningTokens;
155
+ if (delta.cachedReadTokens !== void 0) target.cachedReadTokens = (target.cachedReadTokens ?? 0) + delta.cachedReadTokens;
156
+ if (delta.cacheWriteTokens !== void 0) target.cacheWriteTokens = (target.cacheWriteTokens ?? 0) + delta.cacheWriteTokens;
157
+ }
158
+ /**
159
+ * Fold a completed (or failed - tokens were spent either way) child
160
+ * run's usage into the parent run's accounting: `state.usage`,
161
+ * `state.usageByModel` and the run's {@link UsageAccumulator} (W-033).
162
+ * Children carrying a per-model breakdown fold model-by-model (each
163
+ * child model entry counts as one attempt on the parent); a child
164
+ * without `usageByModel` folds its aggregate under the synthetic id
165
+ * `sub-agent:<name>`, skipped entirely when all-zero so phantom
166
+ * entries never appear.
167
+ *
168
+ * Must be called exactly once per child run at exactly one seam -
169
+ * a second call double-counts (pinned by test).
170
+ */
171
+ function foldChildRunUsage(state, usageAcc, childState, childName) {
172
+ const byModel = childState.usageByModel;
173
+ if (byModel !== void 0 && Object.keys(byModel).length > 0) {
174
+ for (const [modelId, usage] of Object.entries(byModel)) {
175
+ addModelUsage(state, modelId, usage);
176
+ accumulateUsage(state.usage, usage);
177
+ usageAcc?.add(modelId, usage);
178
+ }
179
+ return;
180
+ }
181
+ const aggregate = childState.usage;
182
+ if (aggregate.promptTokens === 0 && aggregate.completionTokens === 0 && aggregate.totalTokens === 0) return;
183
+ const syntheticId = `sub-agent:${childName}`;
184
+ addModelUsage(state, syntheticId, aggregate);
185
+ accumulateUsage(state.usage, aggregate);
186
+ usageAcc?.add(syntheticId, aggregate);
187
+ }
188
+ /**
189
+ * Resolve the effective reasoning-retention policy for
190
+ * this step (RB-42). Drop any buffered reasoning when the
191
+ * contract downgrades to `'strip'`.
192
+ */
193
+ function applyReasoningRetention(agentOverride, provider, messages, stateMessages) {
194
+ const reasoningPolicy = effectiveReasoningRetention(agentOverride, provider);
195
+ if (reasoningPolicy === "strip") {
196
+ const { stripped } = stripReasoningFromMessages(messages);
197
+ if (stripped > 0) stripReasoningFromMessages(stateMessages);
198
+ }
199
+ return reasoningPolicy;
200
+ }
201
+
202
+ //#endregion
203
+ export { accumulateUsage, addUsage, applyReasoningRetention, buildAssistantMessage, buildStepMessages, countLeadingSystemMessages, foldChildRunUsage, lastUserText, renderToolErrorMessage };
204
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","names":["parts: MessageContent[]","out: Message[]","parts: string[]"],"sources":["../../src/runtime/messages.ts"],"sourcesContent":["/**\n * Message-buffer shaping and step-accounting helpers for the agent\n * runtime: reasoning-retention resolution, assistant-message assembly,\n * the compaction-stable system-prefix scan, per-step request assembly,\n * tool-error rendering for the model, and usage arithmetic. Extracted\n * verbatim from `factory.ts` (issue #23).\n *\n * @packageDocumentation\n */\n\nimport type {\n AssistantMessage,\n Message,\n MessageContent,\n Provider,\n ReasoningContent,\n ReasoningRetention,\n RunState,\n ToolCall,\n ToolError,\n Usage,\n UsageAccumulator,\n} from '@graphorin/core';\nimport { COMPACTION_SUMMARY_MARKER } from '@graphorin/memory';\nimport { addModelUsage } from '../run-state/index.js';\nimport { renderPlanRecitation } from '../tooling/plan.js';\n\n/** Most-recent user-role text in `messages` (for context-engine auto-recall). */\nexport function lastUserText(messages: ReadonlyArray<Message>): string | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i];\n if (m?.role !== 'user') continue;\n if (typeof m.content === 'string') return m.content;\n const text = m.content\n .filter((p): p is { readonly type: 'text'; readonly text: string } => p.type === 'text')\n .map((p) => p.text)\n .join(' ');\n return text.length > 0 ? text : undefined;\n }\n return undefined;\n}\n\n/**\n * Resolve the effective {@link ReasoningRetention} for a step. The\n * agent-level setting wins over the provider-level default; when\n * neither is supplied, the provider's `reasoningContract`\n * capability drives the default per RB-42 / suggested DEC-158.\n */\nexport function effectiveReasoningRetention(\n agentOverride: ReasoningRetention | undefined,\n provider: Provider,\n): ReasoningRetention {\n if (agentOverride !== undefined) return agentOverride;\n const contract = provider.capabilities.reasoningContract;\n switch (contract) {\n case 'round-trip-required':\n return 'pass-through-claude';\n case 'optional':\n return 'pass-through-all';\n case 'hidden':\n return 'strip';\n default:\n return 'strip';\n }\n}\n\n/**\n * Build the assistant message that the runtime appends to the\n * message buffer after a successful provider call. When the\n * effective {@link ReasoningRetention} is not `'strip'`, the\n * assembled `reasoning` content parts ride along on `content` so\n * the next provider call honours the wire-correct round-trip\n * contract per RB-42.\n */\nexport function buildAssistantMessage(\n text: string,\n reasoningParts: ReadonlyArray<ReasoningContent>,\n toolCalls: ReadonlyArray<ToolCall>,\n agentId: string,\n retention: ReasoningRetention,\n): AssistantMessage {\n const preserveReasoning = retention !== 'strip' && reasoningParts.length > 0;\n if (preserveReasoning) {\n const parts: MessageContent[] = [...reasoningParts];\n if (text.length > 0) parts.push({ type: 'text', text });\n return {\n role: 'assistant',\n content: parts,\n ...(toolCalls.length > 0 ? { toolCalls } : {}),\n agentId,\n };\n }\n return {\n role: 'assistant',\n content: text,\n ...(toolCalls.length > 0 ? { toolCalls } : {}),\n agentId,\n };\n}\n\n/**\n * Strip every {@link ReasoningContent} part from each message in\n * the supplied list. Used at the swap point when `prepareStep`\n * downgrades the provider's `reasoningContract` mid-run.\n */\nexport function stripReasoningFromMessages(messages: Message[]): { stripped: number } {\n let stripped = 0;\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n if (msg === undefined) continue;\n if (msg.role === 'system' || msg.role === 'tool') continue;\n if (typeof msg.content === 'string') continue;\n const filtered = msg.content.filter((p) => p.type !== 'reasoning');\n if (filtered.length === msg.content.length) continue;\n stripped += msg.content.length - filtered.length;\n if (msg.role === 'assistant') {\n messages[i] = { ...msg, content: filtered };\n } else {\n messages[i] = { ...msg, content: filtered };\n }\n }\n return { stripped };\n}\n\n/**\n * Count the leading contiguous run of `system` messages in the initial\n * buffer - the trusted, KV-cache-stable instruction prefix. Captured\n * once at run start (WI-09 / P1-1): auto-compaction summarises only the\n * messages after this prefix, so the prefix stays byte-identical across\n * every step (the provider's cache breakpoint is real) and a long run\n * never re-pays for the system prompt.\n *\n * The length is fixed for the run rather than re-derived per compaction\n * on purpose: each compaction inserts its summary as a `system` message\n * right after the prefix, so re-scanning the leading run would absorb\n * that summary into the prefix and shield it from the next compaction -\n * summaries would stack unbounded. Pinning the original length keeps\n * each prior summary inside the compactable body, where the next pass\n * folds it into a fresh summary-of-summary.\n */\n/**\n * Marker prefix stamped on every compaction summary. context-engine-05:\n * the prefix scan must stop at it - after a compact-then-suspend cycle\n * the summary is a SYSTEM message sitting right after the true prefix,\n * and counting it in would pin it (and every later summary) outside the\n * compactable window forever, growing the uncompactable prefix by one\n * summary per cycle. W-056: the constant is canonical in\n * `@graphorin/memory` (next to the summary template that stamps it);\n * re-exported here for the runtime's internal imports.\n */\nexport { COMPACTION_SUMMARY_MARKER };\n\nexport function countLeadingSystemMessages(messages: ReadonlyArray<Message>): number {\n let i = 0;\n while (i < messages.length) {\n const msg = messages[i];\n if (msg?.role !== 'system') break;\n if (typeof msg.content === 'string' && msg.content.startsWith(COMPACTION_SUMMARY_MARKER)) {\n break;\n }\n i += 1;\n }\n return i;\n}\n\n/**\n * Immutable usage sum. Optional token fields (reasoning + prompt-cache\n * legs, core-provider-02) appear in the result only when at least one\n * side carries them, so pre-cache serialized shapes stay byte-identical.\n */\n/**\n * D6: assemble the per-step request messages. Trailing, request-only\n * additions ride the LAST prompt-cache anchor and never touch the shared\n * `messages` buffer or the persisted RunState: the structured-output\n * instruction (AG-3) and the attention-recitation plan block are both\n * appended here, in that order, so the stable prompt prefix is unchanged\n * and only the small trailing tail is re-sent each step.\n */\nexport function buildStepMessages(\n messages: ReadonlyArray<Message>,\n structuredInstruction: string | undefined,\n todos: ReadonlyArray<import('@graphorin/core').TodoItem> | undefined,\n): Message[] {\n const out: Message[] = [...messages];\n if (structuredInstruction !== undefined) {\n out.push({ role: 'system', content: structuredInstruction });\n }\n const recitation = renderPlanRecitation(todos);\n if (recitation !== null) {\n out.push({ role: 'system', content: recitation });\n }\n return out;\n}\n\nexport function addUsage(a: Usage, b: Usage): Usage {\n const optional = (x: number | undefined, y: number | undefined): number | undefined =>\n x === undefined && y === undefined ? undefined : (x ?? 0) + (y ?? 0);\n const reasoningTokens = optional(a.reasoningTokens, b.reasoningTokens);\n const cachedReadTokens = optional(a.cachedReadTokens, b.cachedReadTokens);\n const cacheWriteTokens = optional(a.cacheWriteTokens, b.cacheWriteTokens);\n return {\n promptTokens: a.promptTokens + b.promptTokens,\n completionTokens: a.completionTokens + b.completionTokens,\n totalTokens: a.totalTokens + b.totalTokens,\n ...(reasoningTokens !== undefined ? { reasoningTokens } : {}),\n ...(cachedReadTokens !== undefined ? { cachedReadTokens } : {}),\n ...(cacheWriteTokens !== undefined ? { cacheWriteTokens } : {}),\n };\n}\n\n/**\n * C3: render a ToolError for the model. The first line keeps the\n * long-standing `Error: <message>` shape; a bracketed second line carries\n * the typed kind + the recovery envelope, which is what actually changes\n * model behaviour after a failure (retry vs. fix args vs. give up).\n */\nexport function renderToolErrorMessage(error: ToolError): string {\n const parts: string[] = [`kind: ${error.kind}`];\n if (error.recoverable !== undefined) {\n parts.push(error.recoverable ? 'recoverable: yes' : 'recoverable: no');\n }\n if (error.recoveryHint !== undefined) parts.push(`suggested action: ${error.recoveryHint}`);\n if (error.hint !== undefined) parts.push(error.hint);\n return `Error: ${error.message}\\n[${parts.join('; ')}]`;\n}\n\n/** In-place variant of {@link addUsage} for mutable accumulators. */\nexport function accumulateUsage(target: Usage, delta: Usage): void {\n target.promptTokens += delta.promptTokens;\n target.completionTokens += delta.completionTokens;\n target.totalTokens += delta.totalTokens;\n if (delta.reasoningTokens !== undefined) {\n target.reasoningTokens = (target.reasoningTokens ?? 0) + delta.reasoningTokens;\n }\n if (delta.cachedReadTokens !== undefined) {\n target.cachedReadTokens = (target.cachedReadTokens ?? 0) + delta.cachedReadTokens;\n }\n if (delta.cacheWriteTokens !== undefined) {\n target.cacheWriteTokens = (target.cacheWriteTokens ?? 0) + delta.cacheWriteTokens;\n }\n}\n\n/**\n * Fold a completed (or failed - tokens were spent either way) child\n * run's usage into the parent run's accounting: `state.usage`,\n * `state.usageByModel` and the run's {@link UsageAccumulator} (W-033).\n * Children carrying a per-model breakdown fold model-by-model (each\n * child model entry counts as one attempt on the parent); a child\n * without `usageByModel` folds its aggregate under the synthetic id\n * `sub-agent:<name>`, skipped entirely when all-zero so phantom\n * entries never appear.\n *\n * Must be called exactly once per child run at exactly one seam -\n * a second call double-counts (pinned by test).\n */\nexport function foldChildRunUsage(\n state: RunState,\n usageAcc: UsageAccumulator | undefined,\n childState: RunState,\n childName: string,\n): void {\n const byModel = childState.usageByModel;\n if (byModel !== undefined && Object.keys(byModel).length > 0) {\n for (const [modelId, usage] of Object.entries(byModel)) {\n addModelUsage(state, modelId, usage);\n accumulateUsage(state.usage, usage);\n usageAcc?.add(modelId, usage);\n }\n return;\n }\n const aggregate = childState.usage;\n if (\n aggregate.promptTokens === 0 &&\n aggregate.completionTokens === 0 &&\n aggregate.totalTokens === 0\n ) {\n return;\n }\n const syntheticId = `sub-agent:${childName}`;\n addModelUsage(state, syntheticId, aggregate);\n accumulateUsage(state.usage, aggregate);\n usageAcc?.add(syntheticId, aggregate);\n}\n\n/**\n * Resolve the effective reasoning-retention policy for\n * this step (RB-42). Drop any buffered reasoning when the\n * contract downgrades to `'strip'`.\n */\nexport function applyReasoningRetention(\n agentOverride: ReasoningRetention | undefined,\n provider: Provider,\n messages: Message[],\n stateMessages: Message[],\n): ReasoningRetention {\n const reasoningPolicy = effectiveReasoningRetention(agentOverride, provider);\n if (reasoningPolicy === 'strip') {\n const { stripped } = stripReasoningFromMessages(messages);\n // Mirror the strip into RunState so the persisted state\n // matches the in-flight buffer.\n if (stripped > 0) {\n // The structural drop is bytes-equal across `messages`\n // and `state.messages` (both arrays carry the same\n // references); re-strip RunState explicitly to be safe.\n stripReasoningFromMessages(stateMessages);\n }\n }\n return reasoningPolicy;\n}\n"],"mappings":"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"}