la-machina-engine 0.7.5 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2114,11 +2114,12 @@ function createModelAdapter(config, options = {}) {
2114
2114
 
2115
2115
  // src/runtime/detect.ts
2116
2116
  init_cjs_shims();
2117
- var cached = null;
2117
+ var runtimeCache = null;
2118
+ var spawnCache = null;
2118
2119
  function detectRuntime() {
2119
- if (cached !== null) return cached;
2120
- cached = isNodeRuntime() ? "node" : "worker";
2121
- return cached;
2120
+ if (runtimeCache !== null) return runtimeCache;
2121
+ runtimeCache = isNodeRuntime() ? "node" : "worker";
2122
+ return runtimeCache;
2122
2123
  }
2123
2124
  function isNodeRuntime() {
2124
2125
  if (typeof globalThis.process !== "undefined" && globalThis.process.versions != null && typeof globalThis.process.versions.node === "string") {
@@ -2127,10 +2128,29 @@ function isNodeRuntime() {
2127
2128
  return false;
2128
2129
  }
2129
2130
  function canSpawnProcesses() {
2130
- return detectRuntime() === "node";
2131
+ if (spawnCache !== null) return spawnCache;
2132
+ spawnCache = probeSpawn();
2133
+ return spawnCache;
2134
+ }
2135
+ function probeSpawn() {
2136
+ if (typeof globalThis.process === "undefined" || globalThis.process.versions == null || typeof globalThis.process.versions.node !== "string") {
2137
+ return false;
2138
+ }
2139
+ try {
2140
+ const req = eval("require");
2141
+ if (typeof req !== "function") return false;
2142
+ const cp = req("node:child_process");
2143
+ return typeof cp.spawn === "function";
2144
+ } catch {
2145
+ return false;
2146
+ }
2131
2147
  }
2132
2148
  function hasProcessLifecycle() {
2133
- return typeof process !== "undefined" && typeof process.on === "function" && typeof process.removeListener === "function" && detectRuntime() === "node";
2149
+ return typeof process !== "undefined" && typeof process.on === "function" && typeof process.removeListener === "function" && canSpawnProcesses();
2150
+ }
2151
+ function _resetRuntimeCachesForTests() {
2152
+ runtimeCache = null;
2153
+ spawnCache = null;
2134
2154
  }
2135
2155
 
2136
2156
  // src/tools/capabilityStub.ts
@@ -3165,6 +3185,19 @@ async function agentLoop(options) {
3165
3185
  });
3166
3186
  if (compactResult.compacted) {
3167
3187
  messages = compactResult.messages;
3188
+ const used = ctx.getTokensUsed();
3189
+ const total = used.input + used.output;
3190
+ const ratio = total / contextLimit;
3191
+ const dropped = compactResult.result?.dropped ?? 0;
3192
+ await options.inspect?.appendEvent({
3193
+ type: "compaction_threshold",
3194
+ turn: ctx.getTurnCount(),
3195
+ ratio,
3196
+ threshold: compactionConfig.threshold,
3197
+ strategy: compactResult.result?.strategy ?? compactionConfig.strategy,
3198
+ dropped,
3199
+ ts: Date.now()
3200
+ });
3168
3201
  }
3169
3202
  await fireProgress("streaming");
3170
3203
  const textBlocks = [];
@@ -3350,16 +3383,31 @@ async function agentLoop(options) {
3350
3383
  const firstTool = toolCallsToDispatch[0]?.name;
3351
3384
  await fireProgress("tool_dispatch", firstTool);
3352
3385
  const streamExec = new StreamingToolExecutor(executor);
3386
+ let anyConcurrent = false;
3353
3387
  for (const call of toolCallsToDispatch) {
3354
3388
  const tool = options.registry?.get(call.name);
3355
3389
  const safe = tool?.isConcurrencySafe?.(call.input) ?? false;
3390
+ if (safe) anyConcurrent = true;
3356
3391
  streamExec.addTool(call.id, call.name, call.input, safe);
3357
3392
  }
3393
+ await options.inspect?.appendEvent({
3394
+ type: "tool_batch",
3395
+ turn: ctx.getTurnCount(),
3396
+ toolNames: toolCallsToDispatch.map((c) => c.name),
3397
+ concurrent: anyConcurrent && toolCallsToDispatch.length > 1,
3398
+ ts: Date.now()
3399
+ });
3358
3400
  try {
3359
3401
  for await (const { id, result } of streamExec.results()) {
3360
3402
  const missing = result.metadata?.capabilityMissing;
3361
3403
  if (typeof missing === "string") {
3362
3404
  ctx.recordCapabilityMissing(missing);
3405
+ await options.inspect?.appendEvent({
3406
+ type: "capability_stub",
3407
+ turn: ctx.getTurnCount(),
3408
+ toolName: missing,
3409
+ ts: Date.now()
3410
+ });
3363
3411
  }
3364
3412
  const call = toolCallsToDispatch.find((c) => c.id === id);
3365
3413
  let contentForTranscript;
@@ -4508,25 +4556,45 @@ If omitted, defaults to "${defaultAgent.name}".`;
4508
4556
  const lastAssistant = [...parentMsgs].reverse().find((m) => m.role === "assistant");
4509
4557
  const assistantContent = lastAssistant ? Array.isArray(lastAssistant.content) ? lastAssistant.content : [] : [];
4510
4558
  const forkedMsgs = buildForkedMessages(taskDescription, assistantContent);
4511
- const result2 = await runAgent({
4512
- agentId: spawn2.agentId,
4513
- description: taskDescription,
4514
- storage: options.storage,
4515
- client: options.client,
4516
- registry: options.childRegistry,
4517
- parentLogPath: options.parentLogPath,
4518
- system: options.system,
4519
- // inherit parent's system prompt
4520
- maxTurns: options.maxTurns,
4521
- contextLimit: options.contextLimit,
4522
- turnTimeoutMs: options.turnTimeoutMs,
4523
- flushPolicy: options.flushPolicy,
4524
- idleFlushMs: options.idleFlushMs,
4525
- // Fork: prepend parent messages + forked messages
4526
- prependMessages: [...parentMsgs, ...forkedMsgs],
4527
- ...options.gateBeforeTool !== void 0 ? { gateBeforeTool: options.gateBeforeTool } : {},
4528
- ...options.toolResultOffload !== void 0 ? { toolResultOffload: options.toolResultOffload } : {}
4529
- });
4559
+ let result2;
4560
+ try {
4561
+ result2 = await runAgent({
4562
+ agentId: spawn2.agentId,
4563
+ description: taskDescription,
4564
+ storage: options.storage,
4565
+ client: options.client,
4566
+ registry: options.childRegistry,
4567
+ parentLogPath: options.parentLogPath,
4568
+ system: options.system,
4569
+ // inherit parent's system prompt
4570
+ maxTurns: options.maxTurns,
4571
+ contextLimit: options.contextLimit,
4572
+ turnTimeoutMs: options.turnTimeoutMs,
4573
+ flushPolicy: options.flushPolicy,
4574
+ idleFlushMs: options.idleFlushMs,
4575
+ // Fork: prepend parent messages + forked messages
4576
+ prependMessages: [...parentMsgs, ...forkedMsgs],
4577
+ ...options.gateBeforeTool !== void 0 ? { gateBeforeTool: options.gateBeforeTool } : {},
4578
+ ...options.toolResultOffload !== void 0 ? { toolResultOffload: options.toolResultOffload } : {}
4579
+ });
4580
+ await options.inspect?.appendEvent({
4581
+ type: "subagent_spawn_attempt",
4582
+ agentId: spawn2.agentId,
4583
+ subagentType: "fork",
4584
+ success: true,
4585
+ ts: Date.now()
4586
+ });
4587
+ } catch (err) {
4588
+ await options.inspect?.appendEvent({
4589
+ type: "subagent_spawn_attempt",
4590
+ agentId: spawn2.agentId,
4591
+ subagentType: "fork",
4592
+ success: false,
4593
+ error: err instanceof Error ? err.message : String(err),
4594
+ ts: Date.now()
4595
+ });
4596
+ throw err;
4597
+ }
4530
4598
  handlePausedResult(result2, spawn2.agentId, "fork");
4531
4599
  const t2 = result2;
4532
4600
  options.subagentRegistry.setStatus(spawn2.agentId, t2.isError ? "stopped" : "completed");
@@ -4580,7 +4648,7 @@ If omitted, defaults to "${defaultAgent.name}".`;
4580
4648
  const shouldRunAsync = run_in_background === true || options.coordinatorMode === true;
4581
4649
  if (shouldRunAsync) {
4582
4650
  options.subagentRegistry.setBackground(spawn.agentId);
4583
- const bgPromise = runAgent(runOpts).then((result2) => {
4651
+ const bgPromise = runAgent(runOpts).then(async (result2) => {
4584
4652
  if (result2.kind !== "terminal") {
4585
4653
  options.subagentRegistry.setStatus(spawn.agentId, "stopped");
4586
4654
  options.subagentRegistry.setBackgroundResult(spawn.agentId, {
@@ -4589,6 +4657,14 @@ If omitted, defaults to "${defaultAgent.name}".`;
4589
4657
  isError: true,
4590
4658
  description: taskDescription
4591
4659
  });
4660
+ await options.inspect?.appendEvent({
4661
+ type: "subagent_spawn_attempt",
4662
+ agentId: spawn.agentId,
4663
+ subagentType: typeName ?? "unknown",
4664
+ success: false,
4665
+ error: "background subagent paused (pause not supported for async)",
4666
+ ts: Date.now()
4667
+ });
4592
4668
  return;
4593
4669
  }
4594
4670
  options.subagentRegistry.setStatus(
@@ -4601,7 +4677,15 @@ If omitted, defaults to "${defaultAgent.name}".`;
4601
4677
  isError: result2.isError,
4602
4678
  description: taskDescription
4603
4679
  });
4604
- }).catch((err) => {
4680
+ await options.inspect?.appendEvent({
4681
+ type: "subagent_spawn_attempt",
4682
+ agentId: spawn.agentId,
4683
+ subagentType: typeName ?? "unknown",
4684
+ success: !result2.isError,
4685
+ ...result2.isError ? { error: "child returned isError" } : {},
4686
+ ts: Date.now()
4687
+ });
4688
+ }).catch(async (err) => {
4605
4689
  options.subagentRegistry.setStatus(spawn.agentId, "stopped");
4606
4690
  options.subagentRegistry.setBackgroundResult(spawn.agentId, {
4607
4691
  agentId: spawn.agentId,
@@ -4609,6 +4693,14 @@ If omitted, defaults to "${defaultAgent.name}".`;
4609
4693
  isError: true,
4610
4694
  description: taskDescription
4611
4695
  });
4696
+ await options.inspect?.appendEvent({
4697
+ type: "subagent_spawn_attempt",
4698
+ agentId: spawn.agentId,
4699
+ subagentType: typeName ?? "unknown",
4700
+ success: false,
4701
+ error: err instanceof Error ? err.message : String(err),
4702
+ ts: Date.now()
4703
+ });
4612
4704
  });
4613
4705
  options.subagentRegistry.setBackgroundPromise(spawn.agentId, bgPromise);
4614
4706
  return {
@@ -4624,7 +4716,27 @@ Use SendMessage with to: '${spawn.agentId}' to communicate with this agent.`,
4624
4716
  }
4625
4717
  };
4626
4718
  }
4627
- const result = await runAgent(runOpts);
4719
+ let result;
4720
+ try {
4721
+ result = await runAgent(runOpts);
4722
+ await options.inspect?.appendEvent({
4723
+ type: "subagent_spawn_attempt",
4724
+ agentId: spawn.agentId,
4725
+ subagentType: typeName ?? "unknown",
4726
+ success: true,
4727
+ ts: Date.now()
4728
+ });
4729
+ } catch (err) {
4730
+ await options.inspect?.appendEvent({
4731
+ type: "subagent_spawn_attempt",
4732
+ agentId: spawn.agentId,
4733
+ subagentType: typeName ?? "unknown",
4734
+ success: false,
4735
+ error: err instanceof Error ? err.message : String(err),
4736
+ ts: Date.now()
4737
+ });
4738
+ throw err;
4739
+ }
4628
4740
  handlePausedResult(result, spawn.agentId, typeName);
4629
4741
  const t = result;
4630
4742
  options.subagentRegistry.setStatus(spawn.agentId, t.isError ? "stopped" : "completed");
@@ -4675,8 +4787,8 @@ init_contract();
4675
4787
  var _spawn = null;
4676
4788
  async function getSpawn() {
4677
4789
  if (_spawn === null) {
4678
- const cp = await import("child_process");
4679
- _spawn = cp.spawn;
4790
+ const cp2 = await import("child_process");
4791
+ _spawn = cp2.spawn;
4680
4792
  }
4681
4793
  return _spawn;
4682
4794
  }
@@ -5343,12 +5455,12 @@ async function runRipgrep(input, ctx) {
5343
5455
  args.push("--max-count", String(MAX_MATCHES_PER_FILE));
5344
5456
  args.push("--", input.pattern);
5345
5457
  if (input.path) args.push(input.path);
5346
- const cp = getChildProcessSync() ?? await getChildProcessAsync();
5347
- if (cp === null) {
5458
+ const cp2 = getChildProcessSync() ?? await getChildProcessAsync();
5459
+ if (cp2 === null) {
5348
5460
  return { content: "ripgrep not available in this runtime", isError: true };
5349
5461
  }
5350
5462
  return new Promise((resolve) => {
5351
- const child = cp.spawn("rg", args, {
5463
+ const child = cp2.spawn("rg", args, {
5352
5464
  stdio: ["ignore", "pipe", "pipe"],
5353
5465
  timeout: 3e4
5354
5466
  });
@@ -6959,7 +7071,7 @@ init_contract();
6959
7071
  function mcpToolName(serverName, toolName) {
6960
7072
  return `mcp__${serverName}__${toolName}`;
6961
7073
  }
6962
- function adaptMcpTool(client, serverName, def) {
7074
+ function adaptMcpTool(client, serverName, def, opts) {
6963
7075
  const registeredName = mcpToolName(serverName, def.name);
6964
7076
  return defineTool({
6965
7077
  name: registeredName,
@@ -6969,6 +7081,9 @@ function adaptMcpTool(client, serverName, def) {
6969
7081
  // bypassing Zod-to-JSON-Schema conversion (which would produce `{}`
6970
7082
  // for our `z.unknown()` Zod schema).
6971
7083
  anthropicSchemaOverride: def.inputSchema,
7084
+ // Plan 027 — stdio servers spawn subprocesses; flag so the engine's
7085
+ // capability-stub wrapper can swap them on Workers.
7086
+ ...opts.transport === "stdio" ? { requiresNode: true } : {},
6972
7087
  execute: async (input) => {
6973
7088
  try {
6974
7089
  const result = await client.callTool(def.name, input);
@@ -7012,6 +7127,8 @@ var McpManager = class {
7012
7127
  this.logger = logger;
7013
7128
  this.servers = Object.entries(config.servers).map(([name, serverConfig]) => ({
7014
7129
  name,
7130
+ // Plan 027 — capture transport for the stdio-handoff wiring.
7131
+ transport: serverConfig.type,
7015
7132
  client: new McpClient({
7016
7133
  serverName: name,
7017
7134
  config: serverConfig,
@@ -7151,7 +7268,9 @@ var McpManager = class {
7151
7268
  try {
7152
7269
  await server.client.connect();
7153
7270
  const defs = server.client.listTools();
7154
- server.tools = defs.map((def) => adaptMcpTool(server.client, server.name, def));
7271
+ server.tools = defs.map(
7272
+ (def) => adaptMcpTool(server.client, server.name, def, { transport: server.transport })
7273
+ );
7155
7274
  server.state = "connected";
7156
7275
  this.pendingConnected.push(server.name);
7157
7276
  } catch (err) {
@@ -8716,8 +8835,8 @@ function truncatePreview(s) {
8716
8835
  return s.slice(0, 200) + "\u2026";
8717
8836
  }
8718
8837
  async function loadIndex(adapter, base, cache) {
8719
- const cached2 = cache.get(base);
8720
- if (cached2 !== void 0) return cached2;
8838
+ const cached = cache.get(base);
8839
+ if (cached !== void 0) return cached;
8721
8840
  let raw = null;
8722
8841
  try {
8723
8842
  raw = await adapter.readFile(`${base}/_index.json`);
@@ -8998,8 +9117,8 @@ ${payload}`,
8998
9117
  };
8999
9118
  }
9000
9119
  async function loadIndex2(adapter, base, cache) {
9001
- const cached2 = cache.get(base);
9002
- if (cached2 !== void 0) return cached2;
9120
+ const cached = cache.get(base);
9121
+ if (cached !== void 0) return cached;
9003
9122
  let raw = null;
9004
9123
  try {
9005
9124
  raw = await adapter.readFile(`${base}/_index.json`);
@@ -9023,8 +9142,8 @@ async function loadIndex2(adapter, base, cache) {
9023
9142
  }
9024
9143
  }
9025
9144
  async function readFile(adapter, path, cache) {
9026
- const cached2 = cache.get(path);
9027
- if (cached2 !== void 0) return cached2;
9145
+ const cached = cache.get(path);
9146
+ if (cached !== void 0) return cached;
9028
9147
  const content = await adapter.readFile(path).catch(() => null);
9029
9148
  if (content === null) return null;
9030
9149
  cache.set(path, content);
@@ -9180,8 +9299,8 @@ var InlineSkillSource = class {
9180
9299
  async resolve(cacheKey, inline, url, headers) {
9181
9300
  if (inline !== void 0) return inline;
9182
9301
  if (url === void 0) return null;
9183
- const cached2 = this.cache.get(cacheKey);
9184
- if (cached2 !== void 0) return cached2;
9302
+ const cached = this.cache.get(cacheKey);
9303
+ if (cached !== void 0) return cached;
9185
9304
  this.assertUrlAllowed(url);
9186
9305
  const controller = new AbortController();
9187
9306
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
@@ -9490,9 +9609,9 @@ var R2StorageAdapter = class {
9490
9609
  results.push(name);
9491
9610
  }
9492
9611
  }
9493
- for (const cp of resp.CommonPrefixes ?? []) {
9494
- if (!cp.Prefix) continue;
9495
- const name = cp.Prefix.slice(prefix.length).replace(/\/$/, "");
9612
+ for (const cp2 of resp.CommonPrefixes ?? []) {
9613
+ if (!cp2.Prefix) continue;
9614
+ const name = cp2.Prefix.slice(prefix.length).replace(/\/$/, "");
9496
9615
  if (name) results.push(name);
9497
9616
  }
9498
9617
  continuationToken = resp.IsTruncated ? resp.NextContinuationToken : void 0;
@@ -10472,6 +10591,7 @@ var Engine = class {
10472
10591
  systemPrompt += "\n\n" + buildSchemaPrompt(options.outputSchema);
10473
10592
  }
10474
10593
  const gate = this.resolveGate();
10594
+ const inspect = this.buildInspectWriter(storage.workspace, logPath);
10475
10595
  const registry = buildToolRegistry({
10476
10596
  config: this.config,
10477
10597
  storage,
@@ -10483,6 +10603,7 @@ var Engine = class {
10483
10603
  agents,
10484
10604
  mcpTools,
10485
10605
  memory,
10606
+ inspect,
10486
10607
  ...this.config.hooks.propagateGateToSubagents === true && gate !== void 0 ? { subagentGate: gate } : {},
10487
10608
  ...skillSource !== void 0 ? { skillSource } : {},
10488
10609
  ...apiConfig !== void 0 ? { apiConfig } : {},
@@ -10491,7 +10612,6 @@ var Engine = class {
10491
10612
  ...this.internals.fetch !== void 0 ? { fetch: this.internals.fetch } : {}
10492
10613
  });
10493
10614
  applyRunToolFilter(registry, options);
10494
- const inspect = this.buildInspectWriter(storage.workspace, logPath);
10495
10615
  await inspect.writeStartSnapshot({
10496
10616
  systemPrompt,
10497
10617
  tools: this.snapshotTools(registry, mcpTools),
@@ -10588,10 +10708,16 @@ var Engine = class {
10588
10708
  // events / per-turn rows. Disabled-mode writer is a no-op.
10589
10709
  inspect
10590
10710
  });
10591
- const result = await this.finalizeResult(loopResult, writer, logPath, {
10592
- ...options.outputFormat !== void 0 ? { outputFormat: options.outputFormat } : {},
10593
- ...options.outputSchema !== void 0 ? { outputSchema: options.outputSchema } : {}
10594
- });
10711
+ const result = await this.finalizeResult(
10712
+ loopResult,
10713
+ writer,
10714
+ logPath,
10715
+ {
10716
+ ...options.outputFormat !== void 0 ? { outputFormat: options.outputFormat } : {},
10717
+ ...options.outputSchema !== void 0 ? { outputSchema: options.outputSchema } : {}
10718
+ },
10719
+ inspect
10720
+ );
10595
10721
  this.logRunEnd(log, runId, options.nodeId, result);
10596
10722
  await this.firePostRunHook(runId, options.nodeId, result, ctx, logPath);
10597
10723
  await this.writeInspectToolsSummary(inspect, storage.workspace, logPath);
@@ -10662,6 +10788,7 @@ var Engine = class {
10662
10788
  systemPrompt += "\n\n" + buildSchemaPrompt(options.outputSchema);
10663
10789
  }
10664
10790
  const gate = this.resolveGate();
10791
+ const inspect = this.buildInspectWriter(storage.workspace, logPath);
10665
10792
  const registry = buildToolRegistry({
10666
10793
  config: this.config,
10667
10794
  storage,
@@ -10673,6 +10800,7 @@ var Engine = class {
10673
10800
  agents,
10674
10801
  mcpTools,
10675
10802
  memory,
10803
+ inspect,
10676
10804
  ...this.config.hooks.propagateGateToSubagents === true && gate !== void 0 ? { subagentGate: gate } : {},
10677
10805
  ...skillSource !== void 0 ? { skillSource } : {},
10678
10806
  ...apiConfig !== void 0 ? { apiConfig } : {},
@@ -10680,7 +10808,6 @@ var Engine = class {
10680
10808
  ...knowledgeRuntime !== void 0 ? { knowledge: knowledgeRuntime } : {},
10681
10809
  ...this.internals.fetch !== void 0 ? { fetch: this.internals.fetch } : {}
10682
10810
  });
10683
- const inspect = this.buildInspectWriter(storage.workspace, logPath);
10684
10811
  await inspect.writeStartSnapshot({
10685
10812
  systemPrompt,
10686
10813
  tools: this.snapshotTools(registry, mcpTools),
@@ -10811,10 +10938,16 @@ ${inputJson}
10811
10938
  // existing inspect/ logs.
10812
10939
  inspect
10813
10940
  });
10814
- const result = await this.finalizeResult(loopResult, writer, logPath, {
10815
- ...options.outputFormat !== void 0 ? { outputFormat: options.outputFormat } : {},
10816
- ...options.outputSchema !== void 0 ? { outputSchema: options.outputSchema } : {}
10817
- });
10941
+ const result = await this.finalizeResult(
10942
+ loopResult,
10943
+ writer,
10944
+ logPath,
10945
+ {
10946
+ ...options.outputFormat !== void 0 ? { outputFormat: options.outputFormat } : {},
10947
+ ...options.outputSchema !== void 0 ? { outputSchema: options.outputSchema } : {}
10948
+ },
10949
+ inspect
10950
+ );
10818
10951
  this.logRunEnd(log, snapshot.runId, snapshot.nodeId, result);
10819
10952
  await this.firePostRunHook(snapshot.runId, snapshot.nodeId, result, ctx, logPath);
10820
10953
  await this.writeInspectToolsSummary(inspect, storage.workspace, logPath);
@@ -11465,7 +11598,7 @@ ${inputJson}
11465
11598
  };
11466
11599
  await dispatchHooks(this.config.hooks.postRun, event);
11467
11600
  }
11468
- async finalizeResult(loopResult, writer, _logPath, jsonOptions) {
11601
+ async finalizeResult(loopResult, writer, _logPath, jsonOptions, inspect) {
11469
11602
  if (loopResult.status === "done") {
11470
11603
  await writer.setStatus("done");
11471
11604
  let data;
@@ -11474,10 +11607,27 @@ ${inputJson}
11474
11607
  if (parsed.ok) {
11475
11608
  if (jsonOptions.outputSchema) {
11476
11609
  const validated = validateOutput(parsed.value, jsonOptions.outputSchema);
11477
- data = validated.ok ? validated.data : void 0;
11610
+ if (validated.ok) {
11611
+ data = validated.data;
11612
+ } else {
11613
+ await inspect?.appendEvent({
11614
+ type: "json_parse_failure",
11615
+ turn: loopResult.turns,
11616
+ validationError: validated.error,
11617
+ ts: Date.now()
11618
+ });
11619
+ data = void 0;
11620
+ }
11478
11621
  } else {
11479
11622
  data = parsed.value;
11480
11623
  }
11624
+ } else {
11625
+ await inspect?.appendEvent({
11626
+ type: "json_parse_failure",
11627
+ turn: loopResult.turns,
11628
+ parseError: parsed.error ?? "unknown parse error",
11629
+ ts: Date.now()
11630
+ });
11481
11631
  }
11482
11632
  }
11483
11633
  return {
@@ -11955,7 +12105,10 @@ function buildToolRegistry(options) {
11955
12105
  agents,
11956
12106
  coordinatorMode: isCoordinatorMode(config),
11957
12107
  ...subagentGate !== void 0 ? { gateBeforeTool: subagentGate } : {},
11958
- ...options.toolResultOffload !== void 0 ? { toolResultOffload: options.toolResultOffload } : {}
12108
+ ...options.toolResultOffload !== void 0 ? { toolResultOffload: options.toolResultOffload } : {},
12109
+ // Plan 026 — Agent tool emits subagent_spawn_attempt to the
12110
+ // parent's events.jsonl on each runAgent call (success and failure).
12111
+ ...options.inspect !== void 0 ? { inspect: options.inspect } : {}
11959
12112
  });
11960
12113
  if (!disabled.has("Agent") && (wantAll || enabled.has("Agent"))) {
11961
12114
  registry.register(agentTool);
@@ -11971,8 +12124,9 @@ function buildToolRegistry(options) {
11971
12124
  for (const tool of mcpTools) {
11972
12125
  if (disabled.has(tool.name)) continue;
11973
12126
  if (wantAll || enabled.has(tool.name)) {
11974
- registry.register(tool);
11975
- childRegistry.register(tool);
12127
+ const wrapped = withCapabilityCheck(tool, spawnAvailable);
12128
+ registry.register(wrapped);
12129
+ childRegistry.register(wrapped);
11976
12130
  }
11977
12131
  }
11978
12132
  if (!disabled.has("ToolSearch") && (wantAll || enabled.has("ToolSearch"))) {