la-machina-engine 0.7.5 → 0.7.6

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
@@ -3165,6 +3165,19 @@ async function agentLoop(options) {
3165
3165
  });
3166
3166
  if (compactResult.compacted) {
3167
3167
  messages = compactResult.messages;
3168
+ const used = ctx.getTokensUsed();
3169
+ const total = used.input + used.output;
3170
+ const ratio = total / contextLimit;
3171
+ const dropped = compactResult.result?.dropped ?? 0;
3172
+ await options.inspect?.appendEvent({
3173
+ type: "compaction_threshold",
3174
+ turn: ctx.getTurnCount(),
3175
+ ratio,
3176
+ threshold: compactionConfig.threshold,
3177
+ strategy: compactResult.result?.strategy ?? compactionConfig.strategy,
3178
+ dropped,
3179
+ ts: Date.now()
3180
+ });
3168
3181
  }
3169
3182
  await fireProgress("streaming");
3170
3183
  const textBlocks = [];
@@ -3350,16 +3363,31 @@ async function agentLoop(options) {
3350
3363
  const firstTool = toolCallsToDispatch[0]?.name;
3351
3364
  await fireProgress("tool_dispatch", firstTool);
3352
3365
  const streamExec = new StreamingToolExecutor(executor);
3366
+ let anyConcurrent = false;
3353
3367
  for (const call of toolCallsToDispatch) {
3354
3368
  const tool = options.registry?.get(call.name);
3355
3369
  const safe = tool?.isConcurrencySafe?.(call.input) ?? false;
3370
+ if (safe) anyConcurrent = true;
3356
3371
  streamExec.addTool(call.id, call.name, call.input, safe);
3357
3372
  }
3373
+ await options.inspect?.appendEvent({
3374
+ type: "tool_batch",
3375
+ turn: ctx.getTurnCount(),
3376
+ toolNames: toolCallsToDispatch.map((c) => c.name),
3377
+ concurrent: anyConcurrent && toolCallsToDispatch.length > 1,
3378
+ ts: Date.now()
3379
+ });
3358
3380
  try {
3359
3381
  for await (const { id, result } of streamExec.results()) {
3360
3382
  const missing = result.metadata?.capabilityMissing;
3361
3383
  if (typeof missing === "string") {
3362
3384
  ctx.recordCapabilityMissing(missing);
3385
+ await options.inspect?.appendEvent({
3386
+ type: "capability_stub",
3387
+ turn: ctx.getTurnCount(),
3388
+ toolName: missing,
3389
+ ts: Date.now()
3390
+ });
3363
3391
  }
3364
3392
  const call = toolCallsToDispatch.find((c) => c.id === id);
3365
3393
  let contentForTranscript;
@@ -4508,25 +4536,45 @@ If omitted, defaults to "${defaultAgent.name}".`;
4508
4536
  const lastAssistant = [...parentMsgs].reverse().find((m) => m.role === "assistant");
4509
4537
  const assistantContent = lastAssistant ? Array.isArray(lastAssistant.content) ? lastAssistant.content : [] : [];
4510
4538
  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
- });
4539
+ let result2;
4540
+ try {
4541
+ result2 = await runAgent({
4542
+ agentId: spawn2.agentId,
4543
+ description: taskDescription,
4544
+ storage: options.storage,
4545
+ client: options.client,
4546
+ registry: options.childRegistry,
4547
+ parentLogPath: options.parentLogPath,
4548
+ system: options.system,
4549
+ // inherit parent's system prompt
4550
+ maxTurns: options.maxTurns,
4551
+ contextLimit: options.contextLimit,
4552
+ turnTimeoutMs: options.turnTimeoutMs,
4553
+ flushPolicy: options.flushPolicy,
4554
+ idleFlushMs: options.idleFlushMs,
4555
+ // Fork: prepend parent messages + forked messages
4556
+ prependMessages: [...parentMsgs, ...forkedMsgs],
4557
+ ...options.gateBeforeTool !== void 0 ? { gateBeforeTool: options.gateBeforeTool } : {},
4558
+ ...options.toolResultOffload !== void 0 ? { toolResultOffload: options.toolResultOffload } : {}
4559
+ });
4560
+ await options.inspect?.appendEvent({
4561
+ type: "subagent_spawn_attempt",
4562
+ agentId: spawn2.agentId,
4563
+ subagentType: "fork",
4564
+ success: true,
4565
+ ts: Date.now()
4566
+ });
4567
+ } catch (err) {
4568
+ await options.inspect?.appendEvent({
4569
+ type: "subagent_spawn_attempt",
4570
+ agentId: spawn2.agentId,
4571
+ subagentType: "fork",
4572
+ success: false,
4573
+ error: err instanceof Error ? err.message : String(err),
4574
+ ts: Date.now()
4575
+ });
4576
+ throw err;
4577
+ }
4530
4578
  handlePausedResult(result2, spawn2.agentId, "fork");
4531
4579
  const t2 = result2;
4532
4580
  options.subagentRegistry.setStatus(spawn2.agentId, t2.isError ? "stopped" : "completed");
@@ -4580,7 +4628,7 @@ If omitted, defaults to "${defaultAgent.name}".`;
4580
4628
  const shouldRunAsync = run_in_background === true || options.coordinatorMode === true;
4581
4629
  if (shouldRunAsync) {
4582
4630
  options.subagentRegistry.setBackground(spawn.agentId);
4583
- const bgPromise = runAgent(runOpts).then((result2) => {
4631
+ const bgPromise = runAgent(runOpts).then(async (result2) => {
4584
4632
  if (result2.kind !== "terminal") {
4585
4633
  options.subagentRegistry.setStatus(spawn.agentId, "stopped");
4586
4634
  options.subagentRegistry.setBackgroundResult(spawn.agentId, {
@@ -4589,6 +4637,14 @@ If omitted, defaults to "${defaultAgent.name}".`;
4589
4637
  isError: true,
4590
4638
  description: taskDescription
4591
4639
  });
4640
+ await options.inspect?.appendEvent({
4641
+ type: "subagent_spawn_attempt",
4642
+ agentId: spawn.agentId,
4643
+ subagentType: typeName ?? "unknown",
4644
+ success: false,
4645
+ error: "background subagent paused (pause not supported for async)",
4646
+ ts: Date.now()
4647
+ });
4592
4648
  return;
4593
4649
  }
4594
4650
  options.subagentRegistry.setStatus(
@@ -4601,7 +4657,15 @@ If omitted, defaults to "${defaultAgent.name}".`;
4601
4657
  isError: result2.isError,
4602
4658
  description: taskDescription
4603
4659
  });
4604
- }).catch((err) => {
4660
+ await options.inspect?.appendEvent({
4661
+ type: "subagent_spawn_attempt",
4662
+ agentId: spawn.agentId,
4663
+ subagentType: typeName ?? "unknown",
4664
+ success: !result2.isError,
4665
+ ...result2.isError ? { error: "child returned isError" } : {},
4666
+ ts: Date.now()
4667
+ });
4668
+ }).catch(async (err) => {
4605
4669
  options.subagentRegistry.setStatus(spawn.agentId, "stopped");
4606
4670
  options.subagentRegistry.setBackgroundResult(spawn.agentId, {
4607
4671
  agentId: spawn.agentId,
@@ -4609,6 +4673,14 @@ If omitted, defaults to "${defaultAgent.name}".`;
4609
4673
  isError: true,
4610
4674
  description: taskDescription
4611
4675
  });
4676
+ await options.inspect?.appendEvent({
4677
+ type: "subagent_spawn_attempt",
4678
+ agentId: spawn.agentId,
4679
+ subagentType: typeName ?? "unknown",
4680
+ success: false,
4681
+ error: err instanceof Error ? err.message : String(err),
4682
+ ts: Date.now()
4683
+ });
4612
4684
  });
4613
4685
  options.subagentRegistry.setBackgroundPromise(spawn.agentId, bgPromise);
4614
4686
  return {
@@ -4624,7 +4696,27 @@ Use SendMessage with to: '${spawn.agentId}' to communicate with this agent.`,
4624
4696
  }
4625
4697
  };
4626
4698
  }
4627
- const result = await runAgent(runOpts);
4699
+ let result;
4700
+ try {
4701
+ result = await runAgent(runOpts);
4702
+ await options.inspect?.appendEvent({
4703
+ type: "subagent_spawn_attempt",
4704
+ agentId: spawn.agentId,
4705
+ subagentType: typeName ?? "unknown",
4706
+ success: true,
4707
+ ts: Date.now()
4708
+ });
4709
+ } catch (err) {
4710
+ await options.inspect?.appendEvent({
4711
+ type: "subagent_spawn_attempt",
4712
+ agentId: spawn.agentId,
4713
+ subagentType: typeName ?? "unknown",
4714
+ success: false,
4715
+ error: err instanceof Error ? err.message : String(err),
4716
+ ts: Date.now()
4717
+ });
4718
+ throw err;
4719
+ }
4628
4720
  handlePausedResult(result, spawn.agentId, typeName);
4629
4721
  const t = result;
4630
4722
  options.subagentRegistry.setStatus(spawn.agentId, t.isError ? "stopped" : "completed");
@@ -6959,7 +7051,7 @@ init_contract();
6959
7051
  function mcpToolName(serverName, toolName) {
6960
7052
  return `mcp__${serverName}__${toolName}`;
6961
7053
  }
6962
- function adaptMcpTool(client, serverName, def) {
7054
+ function adaptMcpTool(client, serverName, def, opts) {
6963
7055
  const registeredName = mcpToolName(serverName, def.name);
6964
7056
  return defineTool({
6965
7057
  name: registeredName,
@@ -6969,6 +7061,9 @@ function adaptMcpTool(client, serverName, def) {
6969
7061
  // bypassing Zod-to-JSON-Schema conversion (which would produce `{}`
6970
7062
  // for our `z.unknown()` Zod schema).
6971
7063
  anthropicSchemaOverride: def.inputSchema,
7064
+ // Plan 027 — stdio servers spawn subprocesses; flag so the engine's
7065
+ // capability-stub wrapper can swap them on Workers.
7066
+ ...opts.transport === "stdio" ? { requiresNode: true } : {},
6972
7067
  execute: async (input) => {
6973
7068
  try {
6974
7069
  const result = await client.callTool(def.name, input);
@@ -7012,6 +7107,8 @@ var McpManager = class {
7012
7107
  this.logger = logger;
7013
7108
  this.servers = Object.entries(config.servers).map(([name, serverConfig]) => ({
7014
7109
  name,
7110
+ // Plan 027 — capture transport for the stdio-handoff wiring.
7111
+ transport: serverConfig.type,
7015
7112
  client: new McpClient({
7016
7113
  serverName: name,
7017
7114
  config: serverConfig,
@@ -7151,7 +7248,9 @@ var McpManager = class {
7151
7248
  try {
7152
7249
  await server.client.connect();
7153
7250
  const defs = server.client.listTools();
7154
- server.tools = defs.map((def) => adaptMcpTool(server.client, server.name, def));
7251
+ server.tools = defs.map(
7252
+ (def) => adaptMcpTool(server.client, server.name, def, { transport: server.transport })
7253
+ );
7155
7254
  server.state = "connected";
7156
7255
  this.pendingConnected.push(server.name);
7157
7256
  } catch (err) {
@@ -10472,6 +10571,7 @@ var Engine = class {
10472
10571
  systemPrompt += "\n\n" + buildSchemaPrompt(options.outputSchema);
10473
10572
  }
10474
10573
  const gate = this.resolveGate();
10574
+ const inspect = this.buildInspectWriter(storage.workspace, logPath);
10475
10575
  const registry = buildToolRegistry({
10476
10576
  config: this.config,
10477
10577
  storage,
@@ -10483,6 +10583,7 @@ var Engine = class {
10483
10583
  agents,
10484
10584
  mcpTools,
10485
10585
  memory,
10586
+ inspect,
10486
10587
  ...this.config.hooks.propagateGateToSubagents === true && gate !== void 0 ? { subagentGate: gate } : {},
10487
10588
  ...skillSource !== void 0 ? { skillSource } : {},
10488
10589
  ...apiConfig !== void 0 ? { apiConfig } : {},
@@ -10491,7 +10592,6 @@ var Engine = class {
10491
10592
  ...this.internals.fetch !== void 0 ? { fetch: this.internals.fetch } : {}
10492
10593
  });
10493
10594
  applyRunToolFilter(registry, options);
10494
- const inspect = this.buildInspectWriter(storage.workspace, logPath);
10495
10595
  await inspect.writeStartSnapshot({
10496
10596
  systemPrompt,
10497
10597
  tools: this.snapshotTools(registry, mcpTools),
@@ -10588,10 +10688,16 @@ var Engine = class {
10588
10688
  // events / per-turn rows. Disabled-mode writer is a no-op.
10589
10689
  inspect
10590
10690
  });
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
- });
10691
+ const result = await this.finalizeResult(
10692
+ loopResult,
10693
+ writer,
10694
+ logPath,
10695
+ {
10696
+ ...options.outputFormat !== void 0 ? { outputFormat: options.outputFormat } : {},
10697
+ ...options.outputSchema !== void 0 ? { outputSchema: options.outputSchema } : {}
10698
+ },
10699
+ inspect
10700
+ );
10595
10701
  this.logRunEnd(log, runId, options.nodeId, result);
10596
10702
  await this.firePostRunHook(runId, options.nodeId, result, ctx, logPath);
10597
10703
  await this.writeInspectToolsSummary(inspect, storage.workspace, logPath);
@@ -10662,6 +10768,7 @@ var Engine = class {
10662
10768
  systemPrompt += "\n\n" + buildSchemaPrompt(options.outputSchema);
10663
10769
  }
10664
10770
  const gate = this.resolveGate();
10771
+ const inspect = this.buildInspectWriter(storage.workspace, logPath);
10665
10772
  const registry = buildToolRegistry({
10666
10773
  config: this.config,
10667
10774
  storage,
@@ -10673,6 +10780,7 @@ var Engine = class {
10673
10780
  agents,
10674
10781
  mcpTools,
10675
10782
  memory,
10783
+ inspect,
10676
10784
  ...this.config.hooks.propagateGateToSubagents === true && gate !== void 0 ? { subagentGate: gate } : {},
10677
10785
  ...skillSource !== void 0 ? { skillSource } : {},
10678
10786
  ...apiConfig !== void 0 ? { apiConfig } : {},
@@ -10680,7 +10788,6 @@ var Engine = class {
10680
10788
  ...knowledgeRuntime !== void 0 ? { knowledge: knowledgeRuntime } : {},
10681
10789
  ...this.internals.fetch !== void 0 ? { fetch: this.internals.fetch } : {}
10682
10790
  });
10683
- const inspect = this.buildInspectWriter(storage.workspace, logPath);
10684
10791
  await inspect.writeStartSnapshot({
10685
10792
  systemPrompt,
10686
10793
  tools: this.snapshotTools(registry, mcpTools),
@@ -10811,10 +10918,16 @@ ${inputJson}
10811
10918
  // existing inspect/ logs.
10812
10919
  inspect
10813
10920
  });
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
- });
10921
+ const result = await this.finalizeResult(
10922
+ loopResult,
10923
+ writer,
10924
+ logPath,
10925
+ {
10926
+ ...options.outputFormat !== void 0 ? { outputFormat: options.outputFormat } : {},
10927
+ ...options.outputSchema !== void 0 ? { outputSchema: options.outputSchema } : {}
10928
+ },
10929
+ inspect
10930
+ );
10818
10931
  this.logRunEnd(log, snapshot.runId, snapshot.nodeId, result);
10819
10932
  await this.firePostRunHook(snapshot.runId, snapshot.nodeId, result, ctx, logPath);
10820
10933
  await this.writeInspectToolsSummary(inspect, storage.workspace, logPath);
@@ -11465,7 +11578,7 @@ ${inputJson}
11465
11578
  };
11466
11579
  await dispatchHooks(this.config.hooks.postRun, event);
11467
11580
  }
11468
- async finalizeResult(loopResult, writer, _logPath, jsonOptions) {
11581
+ async finalizeResult(loopResult, writer, _logPath, jsonOptions, inspect) {
11469
11582
  if (loopResult.status === "done") {
11470
11583
  await writer.setStatus("done");
11471
11584
  let data;
@@ -11474,10 +11587,27 @@ ${inputJson}
11474
11587
  if (parsed.ok) {
11475
11588
  if (jsonOptions.outputSchema) {
11476
11589
  const validated = validateOutput(parsed.value, jsonOptions.outputSchema);
11477
- data = validated.ok ? validated.data : void 0;
11590
+ if (validated.ok) {
11591
+ data = validated.data;
11592
+ } else {
11593
+ await inspect?.appendEvent({
11594
+ type: "json_parse_failure",
11595
+ turn: loopResult.turns,
11596
+ validationError: validated.error,
11597
+ ts: Date.now()
11598
+ });
11599
+ data = void 0;
11600
+ }
11478
11601
  } else {
11479
11602
  data = parsed.value;
11480
11603
  }
11604
+ } else {
11605
+ await inspect?.appendEvent({
11606
+ type: "json_parse_failure",
11607
+ turn: loopResult.turns,
11608
+ parseError: parsed.error ?? "unknown parse error",
11609
+ ts: Date.now()
11610
+ });
11481
11611
  }
11482
11612
  }
11483
11613
  return {
@@ -11955,7 +12085,10 @@ function buildToolRegistry(options) {
11955
12085
  agents,
11956
12086
  coordinatorMode: isCoordinatorMode(config),
11957
12087
  ...subagentGate !== void 0 ? { gateBeforeTool: subagentGate } : {},
11958
- ...options.toolResultOffload !== void 0 ? { toolResultOffload: options.toolResultOffload } : {}
12088
+ ...options.toolResultOffload !== void 0 ? { toolResultOffload: options.toolResultOffload } : {},
12089
+ // Plan 026 — Agent tool emits subagent_spawn_attempt to the
12090
+ // parent's events.jsonl on each runAgent call (success and failure).
12091
+ ...options.inspect !== void 0 ? { inspect: options.inspect } : {}
11959
12092
  });
11960
12093
  if (!disabled.has("Agent") && (wantAll || enabled.has("Agent"))) {
11961
12094
  registry.register(agentTool);
@@ -11971,8 +12104,9 @@ function buildToolRegistry(options) {
11971
12104
  for (const tool of mcpTools) {
11972
12105
  if (disabled.has(tool.name)) continue;
11973
12106
  if (wantAll || enabled.has(tool.name)) {
11974
- registry.register(tool);
11975
- childRegistry.register(tool);
12107
+ const wrapped = withCapabilityCheck(tool, spawnAvailable);
12108
+ registry.register(wrapped);
12109
+ childRegistry.register(wrapped);
11976
12110
  }
11977
12111
  }
11978
12112
  if (!disabled.has("ToolSearch") && (wantAll || enabled.has("ToolSearch"))) {