@tangle-network/agent-runtime 0.86.0 → 0.88.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.
@@ -66,6 +66,83 @@ function pickFiniteNumber(data, keys) {
66
66
  }
67
67
  return void 0;
68
68
  }
69
+ function createSandboxToolPartState() {
70
+ return { statusByCall: /* @__PURE__ */ new Map(), seq: 0 };
71
+ }
72
+ var TERMINAL_TOOL_FAILURE = /^(error|errored|failed|failure|cancelled|canceled|timeout|timed_out)$/i;
73
+ function mapSandboxToolEvent(event, state) {
74
+ if (!event || typeof event !== "object") return [];
75
+ const type = String(event.type ?? "");
76
+ const data = event.data && typeof event.data === "object" ? event.data : {};
77
+ if (type === "message.part.updated") {
78
+ const part = data.part && typeof data.part === "object" ? data.part : {};
79
+ if (String(part.type ?? "") !== "tool") return [];
80
+ return projectToolPart(part, state, typeof event.id === "string" ? event.id : void 0);
81
+ }
82
+ if (type.includes("tool")) {
83
+ const callId = pickString(data, ["toolCallId", "tool_use_id", "id"]) ?? (typeof event.id === "string" ? event.id : void 0) ?? `sandbox-tool-${++state.seq}`;
84
+ const toolName = pickString(data, ["name", "toolName", "tool"]) ?? "sandbox_tool";
85
+ if (type.includes("result")) {
86
+ return [
87
+ {
88
+ type: "tool_result",
89
+ toolName,
90
+ toolCallId: callId,
91
+ result: data.output ?? data.result ?? data.content ?? data
92
+ }
93
+ ];
94
+ }
95
+ return [
96
+ { type: "tool_call", toolName, toolCallId: callId, args: data.input ?? data.args ?? {} }
97
+ ];
98
+ }
99
+ return [];
100
+ }
101
+ function projectToolPart(part, state, eventId) {
102
+ const callId = pickString(part, ["callID", "callId", "toolCallId", "id"]) ?? eventId ?? `sandbox-tool-${++state.seq}`;
103
+ const toolName = pickString(part, ["tool", "toolName", "name"]) ?? "sandbox_tool";
104
+ const toolState = part.state && typeof part.state === "object" ? part.state : {};
105
+ const metadata = toolState.metadata && typeof toolState.metadata === "object" ? toolState.metadata : {};
106
+ const status = pickString(toolState, ["status"]) ?? "updated";
107
+ const previous = state.statusByCall.get(callId);
108
+ const settled = previous === "completed" || previous !== void 0 && TERMINAL_TOOL_FAILURE.test(previous);
109
+ if (settled) return [];
110
+ const out = [];
111
+ if (previous === void 0) {
112
+ out.push({
113
+ type: "tool_call",
114
+ toolName,
115
+ toolCallId: callId,
116
+ args: toolState.input ?? metadata.input ?? {}
117
+ });
118
+ }
119
+ state.statusByCall.set(callId, status);
120
+ if (status === "completed") {
121
+ out.push({
122
+ type: "tool_result",
123
+ toolName,
124
+ toolCallId: callId,
125
+ result: toolState.output ?? metadata.output ?? ""
126
+ });
127
+ } else if (TERMINAL_TOOL_FAILURE.test(status)) {
128
+ const message = pickString(toolState, ["error", "message"]) ?? pickString(metadata, ["error", "message"]) ?? `sandbox tool ended with status ${status}`;
129
+ const output = toolState.output ?? metadata.output;
130
+ out.push({
131
+ type: "tool_result",
132
+ toolName,
133
+ toolCallId: callId,
134
+ result: { error: message, status, ...output !== void 0 ? { output } : {} }
135
+ });
136
+ }
137
+ return out;
138
+ }
139
+ function pickString(data, keys) {
140
+ for (const key of keys) {
141
+ const value = data[key];
142
+ if (typeof value === "string" && value.length > 0) return value;
143
+ }
144
+ return void 0;
145
+ }
69
146
  function mapSandboxEvent(event, opts = {}) {
70
147
  if (!event || typeof event !== "object") return void 0;
71
148
  const type = String(event.type ?? "");
@@ -87,6 +164,8 @@ function mapSandboxEvent(event, opts = {}) {
87
164
  export {
88
165
  extractLlmCallEvent,
89
166
  sumSandboxUsage,
167
+ createSandboxToolPartState,
168
+ mapSandboxToolEvent,
90
169
  mapSandboxEvent
91
170
  };
92
- //# sourceMappingURL=chunk-4J6RBI3K.js.map
171
+ //# sourceMappingURL=chunk-FVJ7M3DA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/sandbox-events.ts"],"sourcesContent":["/**\n * Sandbox-event → runtime-event mapping.\n *\n * The sandbox SDK emits a polymorphic `SandboxEvent = { type, data, id? }`\n * whose `type` vocabulary is backend-determined (opencode, etc.) rather than\n * enumerated by the SDK. Two consumers project it:\n * - the loop kernel's cost ledger (`extractLlmCallEvent`) — sums usage off\n * every cost-bearing event, regardless of stream shape;\n * - the `AgentRuntime.act` streaming contract (`mapSandboxEvent`) — projects\n * incremental events to the `RuntimeStreamEvent` chat-UX vocabulary.\n *\n * Both live here so the empirically-observed `type` vocabulary has one home.\n */\n\nimport type { SandboxEvent } from '@tangle-network/sandbox'\nimport type { RuntimeStreamEvent } from '../types'\n\n/**\n * Extract a `RuntimeStreamEvent`-shaped `llm_call` from a sandbox event when\n * the event carries usage/cost data. Returns `undefined` for non-cost events\n * so the kernel can iterate the full stream without branching.\n *\n * Canonical cost-carrying types observed in the wild:\n * - `llm_call` — `data: { model, tokensIn, tokensOut, costUsd, ... }`\n * - `message.completed` / `result` — `data: { usage: { inputTokens,\n * outputTokens, totalCostUsd? } }`\n * - `cost.usage` / `usage` — same shape under a dedicated type\n *\n * Numeric coercion is strict: `Number.isFinite` gates every accumulator write\n * so a sentinel `NaN` from a misbehaving backend cannot poison the ledger.\n */\nexport function extractLlmCallEvent(\n event: SandboxEvent,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'llm_call' || type === 'cost.usage' || type === 'usage') {\n return buildLlmCall(data, agentRunName)\n }\n if (type === 'message.completed' || type === 'result' || type === 'final') {\n const usage = data.usage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n return buildLlmCall({ ...usage, model: data.model ?? usage.model }, agentRunName)\n }\n // sandbox 0.4.0 terminal event: `data = { tokenUsage: { inputTokens, outputTokens,\n // reasoningTokens, cacheReadInputTokens }, totalCostUsd }`. Usage lives under\n // `tokenUsage` (not `usage`) and the cost is top-level — neither matched the\n // branches above, so an in-process loopDispatch run reported {0,0} and the\n // backend-integrity guard misread a real run as a stub. Reasoning tokens are\n // billed output (reasoning models), so they fold into the output count.\n if (type === 'done') {\n const usage = data.tokenUsage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n const out = pickFiniteNumber(usage, ['outputTokens', 'completion_tokens', 'tokensOut'])\n const reasoning = pickFiniteNumber(usage, ['reasoningTokens'])\n const mergedOut =\n out !== undefined || reasoning !== undefined ? (out ?? 0) + (reasoning ?? 0) : undefined\n return buildLlmCall(\n {\n inputTokens: usage.inputTokens,\n outputTokens: mergedOut,\n totalCostUsd: data.totalCostUsd,\n model: data.model ?? usage.model,\n },\n agentRunName,\n )\n }\n return undefined\n}\n\n/**\n * Sum the token usage + USD cost of a sandbox turn's events — the one honest way to meter an\n * `openSandboxRun` cell. Folds `extractLlmCallEvent` over the stream (which reads usage off EVERY backend\n * event shape), so a `runProfileMatrix` dispatch can report it to `ctx.cost`:\n *\n * const turn = await run.start(prompt)\n * const u = sumSandboxUsage(turn.events)\n * if (u.input || u.output) ctx.cost.observeTokens({ input: u.input, output: u.output })\n * if (u.costUsd) ctx.cost.observe(u.costUsd, 'sandbox-cell')\n *\n * Without this a cell reads `{tokens:0, cost:0}` and the backend-integrity guard correctly aborts the\n * matrix as a stub. `agentRunName` is the fallback model label for cost-only events (default `'agent'`).\n */\nexport function sumSandboxUsage(\n events: readonly SandboxEvent[],\n agentRunName = 'agent',\n): { input: number; output: number; costUsd: number } {\n let input = 0\n let output = 0\n let costUsd = 0\n for (const ev of events) {\n const call = extractLlmCallEvent(ev, agentRunName)\n if (!call) continue\n input += call.tokensIn ?? 0\n output += call.tokensOut ?? 0\n costUsd += call.costUsd ?? 0\n }\n return { input, output, costUsd }\n}\n\nfunction buildLlmCall(\n data: Record<string, unknown>,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n const tokensIn = pickFiniteNumber(data, ['tokensIn', 'inputTokens', 'prompt_tokens'])\n const tokensOut = pickFiniteNumber(data, ['tokensOut', 'outputTokens', 'completion_tokens'])\n const costUsd = pickFiniteNumber(data, ['costUsd', 'totalCostUsd', 'cost_usd', 'cost'])\n if (tokensIn === undefined && tokensOut === undefined && costUsd === undefined) {\n return undefined\n }\n const model = typeof data.model === 'string' && data.model.length > 0 ? data.model : agentRunName\n const event: RuntimeStreamEvent & { type: 'llm_call' } = {\n type: 'llm_call',\n model,\n }\n if (tokensIn !== undefined) event.tokensIn = tokensIn\n if (tokensOut !== undefined) event.tokensOut = tokensOut\n if (costUsd !== undefined) event.costUsd = costUsd\n return event\n}\n\nfunction pickFiniteNumber(data: Record<string, unknown>, keys: string[]): number | undefined {\n for (const key of keys) {\n const value = data[key]\n if (typeof value === 'number' && Number.isFinite(value)) return value\n }\n return undefined\n}\n\n/**\n * Cross-event state for {@link mapSandboxToolEvent}. Sandbox backends emit a\n * tool invocation as MANY `message.part.updated` frames on the same call id\n * (pending → running → completed), so faithful projection needs per-call\n * status memory: one `tool_call` on first sighting, at most one `tool_result`\n * on the terminal transition, nothing on intermediate re-frames. Create one\n * state per turn via {@link createSandboxToolPartState}.\n *\n * @experimental\n */\nexport interface SandboxToolPartState {\n /** Last seen status per tool call id. A terminal status is sticky — later\n * frames on a settled call project to nothing. */\n statusByCall: Map<string, string>\n /** Sequence for synthesized call ids when an event carries none. */\n seq: number\n}\n\n/**\n * Fresh per-turn {@link SandboxToolPartState} for {@link mapSandboxToolEvent} — an\n * empty call-status map so each turn projects tool frames independently.\n *\n * @experimental\n */\nexport function createSandboxToolPartState(): SandboxToolPartState {\n return { statusByCall: new Map(), seq: 0 }\n}\n\n/** Terminal tool statuses that are failures (everything here settles the call). */\nconst TERMINAL_TOOL_FAILURE =\n /^(error|errored|failed|failure|cancelled|canceled|timeout|timed_out)$/i\n\n/**\n * Project one `SandboxEvent` onto the `tool_call` / `tool_result` variants of\n * `RuntimeStreamEvent` — the tool-part projection `mapSandboxEvent`\n * deliberately does NOT perform. Opt-in and additive: `mapSandboxEvent`'s\n * default vocabulary (text/reasoning deltas + `llm_call`) is unchanged;\n * consumers that need the tool surface (chat UIs rendering tool activity)\n * compose this projector alongside it — `streamAgentTurn` does exactly that\n * under its `preserveToolParts` option.\n *\n * Handled shapes (observed on the opencode / claude-code sandbox backends):\n * - `message.part.updated` with `part.type === 'tool'` — stateful: a\n * `tool_call` on the call id's first frame (args from `state.input` or\n * `state.metadata.input`), a `tool_result` when the status transitions to\n * `completed` (result from `state.output` / `metadata.output`) or to a\n * terminal failure (result is `{ error, status, output? }` — the error\n * surfaced in-band, never dropped).\n * - bare `tool*` event types (`tool.call`, `tool_result`, …) — stateless:\n * `*result*` types project to `tool_result`, the rest to `tool_call`.\n *\n * Returns `[]` for every non-tool event.\n *\n * @experimental\n */\nexport function mapSandboxToolEvent(\n event: SandboxEvent,\n state: SandboxToolPartState,\n): (RuntimeStreamEvent & { type: 'tool_call' | 'tool_result' })[] {\n if (!event || typeof event !== 'object') return []\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'message.part.updated') {\n const part =\n data.part && typeof data.part === 'object' ? (data.part as Record<string, unknown>) : {}\n if (String(part.type ?? '') !== 'tool') return []\n return projectToolPart(part, state, typeof event.id === 'string' ? event.id : undefined)\n }\n\n if (type.includes('tool')) {\n const callId =\n pickString(data, ['toolCallId', 'tool_use_id', 'id']) ??\n (typeof event.id === 'string' ? event.id : undefined) ??\n `sandbox-tool-${++state.seq}`\n const toolName = pickString(data, ['name', 'toolName', 'tool']) ?? 'sandbox_tool'\n if (type.includes('result')) {\n return [\n {\n type: 'tool_result',\n toolName,\n toolCallId: callId,\n result: data.output ?? data.result ?? data.content ?? data,\n },\n ]\n }\n return [\n { type: 'tool_call', toolName, toolCallId: callId, args: data.input ?? data.args ?? {} },\n ]\n }\n\n return []\n}\n\nfunction projectToolPart(\n part: Record<string, unknown>,\n state: SandboxToolPartState,\n eventId: string | undefined,\n): (RuntimeStreamEvent & { type: 'tool_call' | 'tool_result' })[] {\n const callId =\n pickString(part, ['callID', 'callId', 'toolCallId', 'id']) ??\n eventId ??\n `sandbox-tool-${++state.seq}`\n const toolName = pickString(part, ['tool', 'toolName', 'name']) ?? 'sandbox_tool'\n const toolState =\n part.state && typeof part.state === 'object' ? (part.state as Record<string, unknown>) : {}\n const metadata =\n toolState.metadata && typeof toolState.metadata === 'object'\n ? (toolState.metadata as Record<string, unknown>)\n : {}\n const status = pickString(toolState, ['status']) ?? 'updated'\n\n const previous = state.statusByCall.get(callId)\n const settled =\n previous === 'completed' || (previous !== undefined && TERMINAL_TOOL_FAILURE.test(previous))\n if (settled) return []\n\n const out: (RuntimeStreamEvent & { type: 'tool_call' | 'tool_result' })[] = []\n if (previous === undefined) {\n out.push({\n type: 'tool_call',\n toolName,\n toolCallId: callId,\n args: toolState.input ?? metadata.input ?? {},\n })\n }\n state.statusByCall.set(callId, status)\n\n if (status === 'completed') {\n out.push({\n type: 'tool_result',\n toolName,\n toolCallId: callId,\n result: toolState.output ?? metadata.output ?? '',\n })\n } else if (TERMINAL_TOOL_FAILURE.test(status)) {\n const message =\n pickString(toolState, ['error', 'message']) ??\n pickString(metadata, ['error', 'message']) ??\n `sandbox tool ended with status ${status}`\n const output = toolState.output ?? metadata.output\n out.push({\n type: 'tool_result',\n toolName,\n toolCallId: callId,\n result: { error: message, status, ...(output !== undefined ? { output } : {}) },\n })\n }\n return out\n}\n\nfunction pickString(data: Record<string, unknown>, keys: string[]): string | undefined {\n for (const key of keys) {\n const value = data[key]\n if (typeof value === 'string' && value.length > 0) return value\n }\n return undefined\n}\n\n/**\n * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,\n * for runtimes that bridge a sandbox `streamPrompt` into the\n * `AgentRuntime.act` streaming contract. Returns `undefined` for events that\n * have no faithful projection — the raw stream is preserved separately for the\n * `OutputAdapter`, so an unmapped event never loses data.\n *\n * Mapped (the task-optional incremental variants — no synthesized task\n * lifecycle, no guessed tool-part shapes):\n * - `message.part.updated` text part → `text_delta`\n * - `message.part.updated` reasoning/thinking part → `reasoning_delta`\n * - cost-bearing events → `llm_call` (shared with the ledger extractor)\n *\n * Tool parts are deliberately NOT mapped here (unchanged default) — compose\n * {@link mapSandboxToolEvent} alongside when a consumer needs them.\n *\n * The opencode backend emits incremental text as\n * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;\n * `delta` is the increment, `part.text` the running accumulation.\n */\nexport function mapSandboxEvent(\n event: SandboxEvent,\n opts: { agentRunName?: string } = {},\n): RuntimeStreamEvent | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'message.part.updated') {\n const part =\n data.part && typeof data.part === 'object' ? (data.part as Record<string, unknown>) : {}\n const partType = String(part.type ?? '')\n const delta = typeof data.delta === 'string' ? data.delta : undefined\n const text = delta ?? (typeof part.text === 'string' ? part.text : undefined)\n if (text === undefined) return undefined\n if (partType === 'text') return { type: 'text_delta', text }\n if (partType === 'reasoning' || partType === 'thinking')\n return { type: 'reasoning_delta', text }\n return undefined\n }\n\n return extractLlmCallEvent(event, opts.agentRunName ?? 'agent')\n}\n"],"mappings":"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","names":[]}
@@ -5,13 +5,13 @@ import {
5
5
  definePersona,
6
6
  runPersonified,
7
7
  worktreeFanout
8
- } from "./chunk-3TZOXS7B.js";
8
+ } from "./chunk-22HPUH77.js";
9
9
  import {
10
10
  createExecutorRegistry
11
- } from "./chunk-F7OO2SKB.js";
11
+ } from "./chunk-LRNRPJAV.js";
12
12
  import {
13
13
  runAnalystLoop
14
- } from "./chunk-YPA5MLVE.js";
14
+ } from "./chunk-ZQZX77MM.js";
15
15
  import {
16
16
  ConfigError
17
17
  } from "./chunk-YEJR7IXO.js";
@@ -196,4 +196,4 @@ export {
196
196
  runLoopRunnerCli,
197
197
  parseLoopRunnerArgv
198
198
  };
199
- //# sourceMappingURL=chunk-EJ7MAQN4.js.map
199
+ //# sourceMappingURL=chunk-HBE77SWV.js.map
@@ -8,7 +8,7 @@ import {
8
8
  DELEGATION_STATUS_DESCRIPTION,
9
9
  DELEGATION_STATUS_INPUT_SCHEMA,
10
10
  DELEGATION_STATUS_TOOL_NAME
11
- } from "./chunk-F7OO2SKB.js";
11
+ } from "./chunk-LRNRPJAV.js";
12
12
 
13
13
  // src/mcp/openai-tools.ts
14
14
  function buildTool(name, description, parameters) {
@@ -45,4 +45,4 @@ export {
45
45
  mcpToolsForRuntimeMcp,
46
46
  mcpToolsForRuntimeMcpSubset
47
47
  };
48
- //# sourceMappingURL=chunk-U4TS65XZ.js.map
48
+ //# sourceMappingURL=chunk-JHULWWQD.js.map
@@ -18,7 +18,7 @@ import {
18
18
  } from "./chunk-BZF3KQ6G.js";
19
19
  import {
20
20
  extractLlmCallEvent
21
- } from "./chunk-4J6RBI3K.js";
21
+ } from "./chunk-FVJ7M3DA.js";
22
22
  import {
23
23
  harnessInvocation,
24
24
  runLocalHarness
@@ -342,9 +342,13 @@ function buildBackendOptions(profile, overrides) {
342
342
  // src/runtime/sandbox-lineage.ts
343
343
  var TEARDOWN_TIMEOUT_MS = 15e3;
344
344
  var DEFAULT_FORK_CONCURRENCY = 4;
345
- async function* pollPromptEvents(box, prompt, sessionId, signal) {
345
+ async function* pollPromptEvents(box, prompt, sessionId, signal, promptOptions) {
346
346
  if (signal.aborted) throwAbort();
347
- const dispatched = await box.dispatchPrompt(prompt, { sessionId, signal });
347
+ const dispatched = await box.dispatchPrompt(prompt, {
348
+ ...promptOptions ?? {},
349
+ sessionId,
350
+ signal
351
+ });
348
352
  const activeSessionId = dispatched.sessionId;
349
353
  const result = await box.session(activeSessionId).result();
350
354
  if (signal.aborted) throwAbort();
@@ -359,8 +363,8 @@ async function* pollPromptEvents(box, prompt, sessionId, signal) {
359
363
  }
360
364
  };
361
365
  }
362
- function promptEvents(streaming, box, prompt, sessionId, signal) {
363
- return streaming === "poll" ? pollPromptEvents(box, prompt, sessionId, signal) : box.streamPrompt(prompt, { sessionId, signal });
366
+ function promptEvents(streaming, box, prompt, sessionId, signal, promptOptions) {
367
+ return streaming === "poll" ? pollPromptEvents(box, prompt, sessionId, signal, promptOptions) : box.streamPrompt(prompt, { ...promptOptions ?? {}, sessionId, signal });
364
368
  }
365
369
  function createSandboxLineage(client, capabilities, options = {}) {
366
370
  if (!client || typeof client.create !== "function") {
@@ -383,16 +387,16 @@ function createSandboxLineage(client, capabilities, options = {}) {
383
387
  return box;
384
388
  };
385
389
  return {
386
- async start(spec, prompt, signal) {
390
+ async start(spec, prompt, signal, promptOptions) {
387
391
  const box = await acquireFresh(spec, signal);
388
392
  const sessionId = mintSessionId();
389
- const events = promptEvents(streaming, box, prompt, sessionId, signal);
393
+ const events = promptEvents(streaming, box, prompt, sessionId, signal, promptOptions);
390
394
  return { handle: { box, sessionId }, events };
391
395
  },
392
- async continue(handle, prompt, signal) {
396
+ async continue(handle, prompt, signal, promptOptions) {
393
397
  if (signal.aborted) throwAbort();
394
398
  await assertSessionLive(handle.box, handle.sessionId);
395
- return promptEvents(streaming, handle.box, prompt, handle.sessionId, signal);
399
+ return promptEvents(streaming, handle.box, prompt, handle.sessionId, signal, promptOptions);
396
400
  },
397
401
  async fork(parent, prompts, specs, signal) {
398
402
  if (prompts.length === 0) {
@@ -5255,6 +5259,243 @@ function canonicalize(value) {
5255
5259
  return out;
5256
5260
  }
5257
5261
 
5262
+ // src/runtime/supervise/loop-executor.ts
5263
+ var loopRuntime = "loop";
5264
+ var loopRole = "loop";
5265
+ function defineLoop(name, spec) {
5266
+ if (!name) throw new ValidationError("defineLoop: a loop needs a name");
5267
+ if (!Number.isInteger(spec.maxRounds) || spec.maxRounds < 1) {
5268
+ throw new ValidationError(`defineLoop: "${name}" needs an integer maxRounds >= 1`);
5269
+ }
5270
+ const hasRound = typeof spec.round === "function";
5271
+ const hasAgents = Array.isArray(spec.agents) && spec.agents.length > 0;
5272
+ if (hasRound === hasAgents) {
5273
+ throw new ValidationError(
5274
+ `defineLoop: "${name}" needs exactly one of round(ctx) or a non-empty agents[]`
5275
+ );
5276
+ }
5277
+ const round = hasRound ? spec.round : agentPipelineRound(name, spec.agents ?? []);
5278
+ return {
5279
+ name,
5280
+ maxRounds: spec.maxRounds,
5281
+ round,
5282
+ ...spec.check ? { check: spec.check } : {},
5283
+ ...spec.describe ? { describe: spec.describe } : {}
5284
+ };
5285
+ }
5286
+ function agentPipelineRound(name, agents) {
5287
+ for (const agent of agents) {
5288
+ if (!agent || typeof agent.run !== "function" || !agent.name) {
5289
+ throw new ValidationError(`defineLoop: "${name}" has an agent missing a name or run()`);
5290
+ }
5291
+ }
5292
+ return async (ctx) => {
5293
+ let out = ctx.task;
5294
+ for (const agent of agents) {
5295
+ out = await agent.run(ctx, out);
5296
+ }
5297
+ return { out };
5298
+ };
5299
+ }
5300
+ function loopChild(loop, journal) {
5301
+ const spec = {
5302
+ profile: { name: loop.name, metadata: { role: loopRole } },
5303
+ harness: null,
5304
+ loop,
5305
+ journal
5306
+ };
5307
+ return {
5308
+ name: loop.name,
5309
+ executorSpec: spec,
5310
+ act() {
5311
+ throw new ValidationError(
5312
+ `loopChild: "${loop.name}" was run directly; a loop child runs through its nested-scope executor`
5313
+ );
5314
+ }
5315
+ };
5316
+ }
5317
+ function isLoopSpec(spec) {
5318
+ const role = spec.profile.metadata?.role;
5319
+ if (role !== loopRole) return false;
5320
+ const loop = spec.loop;
5321
+ if (!isLoopDef(loop)) {
5322
+ throw new ValidationError(
5323
+ "loopExecutor: a loop-role spec must carry a `loop` LoopDef to run inside its nested scope"
5324
+ );
5325
+ }
5326
+ return true;
5327
+ }
5328
+ var loopExecutorFactory = (spec, ctx) => {
5329
+ if (!isLoopSpec(spec)) {
5330
+ throw new ValidationError(
5331
+ 'loopExecutorFactory: spec is not a loop child (no role:"loop" marker)'
5332
+ );
5333
+ }
5334
+ const loop = spec.loop;
5335
+ const journal = spec.journal;
5336
+ const seam = readNestedScopeSeam(ctx);
5337
+ const inbox = createInbox();
5338
+ let artifact;
5339
+ let meteredSpend;
5340
+ return {
5341
+ runtime: loopRuntime,
5342
+ // The steer seam: `steer_agent` → `Scope.send` → here. Never throws (inbox contract).
5343
+ deliver(msg) {
5344
+ inbox.deliver(msg);
5345
+ },
5346
+ async execute(task, signal) {
5347
+ const nestedRoot = nestedTreeKey(seam, journal);
5348
+ await journal.beginTree(nestedRoot, (/* @__PURE__ */ new Date(0)).toISOString());
5349
+ const nestedScope = seam.mount(nestedRoot, signal);
5350
+ let out;
5351
+ let delivered = false;
5352
+ try {
5353
+ for (let round = 1; round <= loop.maxRounds; round++) {
5354
+ if (signal.aborted || nestedScope.signal.aborted) break;
5355
+ const steer = drainSteer(inbox);
5356
+ const roundInterrupt = inbox.freshInterrupt();
5357
+ const roundSignal = AbortSignal.any([signal, nestedScope.signal, roundInterrupt]);
5358
+ const result = await loop.round({
5359
+ task,
5360
+ scope: nestedScope,
5361
+ round,
5362
+ maxRounds: loop.maxRounds,
5363
+ steer,
5364
+ budget: nestedScope.budget,
5365
+ signal: roundSignal
5366
+ });
5367
+ out = result.out;
5368
+ if (loop.check && await runCheck(loop.check, out)) {
5369
+ delivered = true;
5370
+ break;
5371
+ }
5372
+ if (result.done) break;
5373
+ }
5374
+ const events = await loadTreeEvents(journal, nestedRoot);
5375
+ const settled = events.filter(isSettled);
5376
+ meteredSpend = nonZeroOrUndef(sumMetered(events));
5377
+ const valid = loop.check ? await runCheck(loop.check, out) : delivered;
5378
+ const verdict = { valid, score: valid ? 1 : 0 };
5379
+ artifact = {
5380
+ outRef: `${loopRuntime}:${nestedRoot}`,
5381
+ out,
5382
+ spent: sumSpend(settled),
5383
+ verdict
5384
+ };
5385
+ return artifact;
5386
+ } catch (err) {
5387
+ meteredSpend = await safeSumMetered(journal, nestedRoot);
5388
+ throw err;
5389
+ }
5390
+ },
5391
+ metered() {
5392
+ return meteredSpend;
5393
+ },
5394
+ teardown() {
5395
+ return Promise.resolve({ destroyed: true });
5396
+ },
5397
+ resultArtifact() {
5398
+ if (!artifact)
5399
+ throw new ValidationError("loopExecutor: resultArtifact() read before execute()");
5400
+ return artifact;
5401
+ }
5402
+ };
5403
+ };
5404
+ function withLoopExecutor(base) {
5405
+ return {
5406
+ register: base.register.bind(base),
5407
+ resolve(spec) {
5408
+ const role = spec.profile.metadata?.role;
5409
+ if (role === loopRole && !spec.executor) {
5410
+ return { succeeded: true, value: loopExecutorFactory };
5411
+ }
5412
+ return base.resolve(spec);
5413
+ }
5414
+ };
5415
+ }
5416
+ function drainSteer(inbox) {
5417
+ return inbox.drain().map((m) => m.text);
5418
+ }
5419
+ async function runCheck(check, out) {
5420
+ try {
5421
+ return await check(out) === true;
5422
+ } catch {
5423
+ return false;
5424
+ }
5425
+ }
5426
+ function nestedTreeKey(seam, journal) {
5427
+ return `${seam.journalRoot}/l${nextNestOrdinal(journal)}`;
5428
+ }
5429
+ var nestCounters = /* @__PURE__ */ new WeakMap();
5430
+ function nextNestOrdinal(journal) {
5431
+ let c = nestCounters.get(journal);
5432
+ if (!c) {
5433
+ c = { n: 0 };
5434
+ nestCounters.set(journal, c);
5435
+ }
5436
+ return c.n++;
5437
+ }
5438
+ async function loadTreeEvents(journal, nestedRoot) {
5439
+ const events = await journal.loadTree(nestedRoot);
5440
+ if (events === void 0) {
5441
+ throw new ValidationError(
5442
+ `loopExecutor: nested tree '${nestedRoot}' missing from the journal after run (corrupted log)`
5443
+ );
5444
+ }
5445
+ return events;
5446
+ }
5447
+ function isSettled(ev) {
5448
+ return ev.kind === "settled";
5449
+ }
5450
+ function sumSpend(settled) {
5451
+ const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
5452
+ for (const ev of settled) {
5453
+ total.iterations += ev.spent.iterations;
5454
+ total.tokens.input += ev.spent.tokens.input;
5455
+ total.tokens.output += ev.spent.tokens.output;
5456
+ total.usd += ev.spent.usd;
5457
+ total.ms += ev.spent.ms;
5458
+ }
5459
+ return total;
5460
+ }
5461
+ function sumMetered(events) {
5462
+ const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
5463
+ for (const ev of events) {
5464
+ if (ev.kind !== "metered") continue;
5465
+ total.iterations += ev.spend.iterations;
5466
+ total.tokens.input += ev.spend.tokens.input;
5467
+ total.tokens.output += ev.spend.tokens.output;
5468
+ total.usd += ev.spend.usd;
5469
+ total.ms += ev.spend.ms;
5470
+ }
5471
+ return total;
5472
+ }
5473
+ function isNonZeroSpend(s) {
5474
+ return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
5475
+ }
5476
+ function nonZeroOrUndef(s) {
5477
+ return isNonZeroSpend(s) ? s : void 0;
5478
+ }
5479
+ async function safeSumMetered(journal, nestedRoot) {
5480
+ try {
5481
+ return nonZeroOrUndef(sumMetered(await loadTreeEvents(journal, nestedRoot)));
5482
+ } catch {
5483
+ return void 0;
5484
+ }
5485
+ }
5486
+ function readNestedScopeSeam(ctx) {
5487
+ const seam = ctx.seams[nestedScopeSeamKey];
5488
+ if (!seam || typeof seam.mount !== "function") {
5489
+ throw new ValidationError(
5490
+ `loopExecutor: missing required seam "${nestedScopeSeamKey}" \u2014 a loop child must be spawned through a Scope that seeds it (the keystone scope does)`
5491
+ );
5492
+ }
5493
+ return seam;
5494
+ }
5495
+ function isLoopDef(value) {
5496
+ return typeof value === "object" && value !== null && typeof value.round === "function" && typeof value.name === "string" && Number.isInteger(value.maxRounds);
5497
+ }
5498
+
5258
5499
  // src/runtime/supervise/driver-executor.ts
5259
5500
  var driverRuntime = "driver";
5260
5501
  var driverRole = "driver";
@@ -5277,30 +5518,30 @@ var driverExecutorFactory = (spec, ctx) => {
5277
5518
  }
5278
5519
  const driver = spec.driver;
5279
5520
  const journal = spec.journal;
5280
- const seam = readNestedScopeSeam(ctx);
5521
+ const seam = readNestedScopeSeam2(ctx);
5281
5522
  let artifact;
5282
5523
  let meteredSpend;
5283
5524
  return {
5284
5525
  runtime: driverRuntime,
5285
5526
  async execute(task, signal) {
5286
- const nestedRoot = nestedTreeKey(seam, journal);
5527
+ const nestedRoot = nestedTreeKey2(seam, journal);
5287
5528
  await journal.beginTree(nestedRoot, (/* @__PURE__ */ new Date(0)).toISOString());
5288
5529
  const nestedScope = seam.mount(nestedRoot, signal);
5289
5530
  try {
5290
5531
  const out = await driver.act(task, nestedScope);
5291
- const events = await loadTreeEvents(journal, nestedRoot);
5292
- const settled = events.filter(isSettled);
5293
- meteredSpend = nonZeroOrUndef(sumMetered(events));
5532
+ const events = await loadTreeEvents2(journal, nestedRoot);
5533
+ const settled = events.filter(isSettled2);
5534
+ meteredSpend = nonZeroOrUndef2(sumMetered2(events));
5294
5535
  const verdict = deriveDeliveryVerdict(settled);
5295
5536
  artifact = {
5296
5537
  outRef: `${driverRuntime}:${nestedRoot}`,
5297
5538
  out,
5298
- spent: sumSpend(settled),
5539
+ spent: sumSpend2(settled),
5299
5540
  ...verdict ? { verdict } : {}
5300
5541
  };
5301
5542
  return artifact;
5302
5543
  } catch (err) {
5303
- meteredSpend = await safeSumMetered(journal, nestedRoot);
5544
+ meteredSpend = await safeSumMetered2(journal, nestedRoot);
5304
5545
  throw err;
5305
5546
  }
5306
5547
  },
@@ -5330,19 +5571,19 @@ function withDriverExecutor(base) {
5330
5571
  }
5331
5572
  };
5332
5573
  }
5333
- function nestedTreeKey(seam, journal) {
5334
- return `${seam.journalRoot}/d${nextNestOrdinal(journal)}`;
5574
+ function nestedTreeKey2(seam, journal) {
5575
+ return `${seam.journalRoot}/d${nextNestOrdinal2(journal)}`;
5335
5576
  }
5336
- var nestCounters = /* @__PURE__ */ new WeakMap();
5337
- function nextNestOrdinal(journal) {
5338
- let c = nestCounters.get(journal);
5577
+ var nestCounters2 = /* @__PURE__ */ new WeakMap();
5578
+ function nextNestOrdinal2(journal) {
5579
+ let c = nestCounters2.get(journal);
5339
5580
  if (!c) {
5340
5581
  c = { n: 0 };
5341
- nestCounters.set(journal, c);
5582
+ nestCounters2.set(journal, c);
5342
5583
  }
5343
5584
  return c.n++;
5344
5585
  }
5345
- async function loadTreeEvents(journal, nestedRoot) {
5586
+ async function loadTreeEvents2(journal, nestedRoot) {
5346
5587
  const events = await journal.loadTree(nestedRoot);
5347
5588
  if (events === void 0) {
5348
5589
  throw new ValidationError(
@@ -5351,10 +5592,10 @@ async function loadTreeEvents(journal, nestedRoot) {
5351
5592
  }
5352
5593
  return events;
5353
5594
  }
5354
- function isSettled(ev) {
5595
+ function isSettled2(ev) {
5355
5596
  return ev.kind === "settled";
5356
5597
  }
5357
- function sumSpend(settled) {
5598
+ function sumSpend2(settled) {
5358
5599
  const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
5359
5600
  for (const ev of settled) {
5360
5601
  total.iterations += ev.spent.iterations;
@@ -5365,7 +5606,7 @@ function sumSpend(settled) {
5365
5606
  }
5366
5607
  return total;
5367
5608
  }
5368
- function sumMetered(events) {
5609
+ function sumMetered2(events) {
5369
5610
  const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
5370
5611
  for (const ev of events) {
5371
5612
  if (ev.kind !== "metered") continue;
@@ -5377,15 +5618,15 @@ function sumMetered(events) {
5377
5618
  }
5378
5619
  return total;
5379
5620
  }
5380
- function isNonZeroSpend(s) {
5621
+ function isNonZeroSpend2(s) {
5381
5622
  return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
5382
5623
  }
5383
- function nonZeroOrUndef(s) {
5384
- return isNonZeroSpend(s) ? s : void 0;
5624
+ function nonZeroOrUndef2(s) {
5625
+ return isNonZeroSpend2(s) ? s : void 0;
5385
5626
  }
5386
- async function safeSumMetered(journal, nestedRoot) {
5627
+ async function safeSumMetered2(journal, nestedRoot) {
5387
5628
  try {
5388
- return nonZeroOrUndef(sumMetered(await loadTreeEvents(journal, nestedRoot)));
5629
+ return nonZeroOrUndef2(sumMetered2(await loadTreeEvents2(journal, nestedRoot)));
5389
5630
  } catch {
5390
5631
  return void 0;
5391
5632
  }
@@ -5415,7 +5656,7 @@ function deriveDeliveryVerdict(settled) {
5415
5656
  score: anyValid ? bestValidScore ?? 1 : bestDoneScore ?? 0
5416
5657
  };
5417
5658
  }
5418
- function readNestedScopeSeam(ctx) {
5659
+ function readNestedScopeSeam2(ctx) {
5419
5660
  const seam = ctx.seams[nestedScopeSeamKey];
5420
5661
  if (!seam || typeof seam.mount !== "function") {
5421
5662
  throw new ValidationError(
@@ -5431,10 +5672,13 @@ function isAgent(value) {
5431
5672
  // src/runtime/supervise/run-context.ts
5432
5673
  function createInMemoryRunContext(opts = {}) {
5433
5674
  const base = createExecutorRegistry();
5675
+ let executors = base;
5676
+ if (opts.withDriver) executors = withDriverExecutor(executors);
5677
+ if (opts.withLoop) executors = withLoopExecutor(executors);
5434
5678
  return {
5435
5679
  journal: new InMemorySpawnJournal(),
5436
5680
  blobs: new InMemoryResultBlobStore(),
5437
- executors: opts.withDriver ? withDriverExecutor(base) : base
5681
+ executors
5438
5682
  };
5439
5683
  }
5440
5684
 
@@ -6575,6 +6819,10 @@ export {
6575
6819
  composeLoopTraceEmitters,
6576
6820
  DelegationTaskQueue,
6577
6821
  hashIdempotencyInput,
6822
+ loopRuntime,
6823
+ defineLoop,
6824
+ loopChild,
6825
+ withLoopExecutor,
6578
6826
  createInMemoryRunContext,
6579
6827
  supervisorAgent,
6580
6828
  workerFromBackend,
@@ -6610,4 +6858,4 @@ export {
6610
6858
  createInProcessTransport,
6611
6859
  serveCoordinationMcp
6612
6860
  };
6613
- //# sourceMappingURL=chunk-F7OO2SKB.js.map
6861
+ //# sourceMappingURL=chunk-LRNRPJAV.js.map