@noetic-tools/core 1.1.2 → 1.2.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.
@@ -346,6 +346,16 @@ export class AgentHarness {
346
346
  }
347
347
  //#endregion
348
348
  async run(s, input, ctx) {
349
+ // Populate the unified tool pool when an embedder drives a step directly on
350
+ // a bare `createContext()` context (the turn pipeline does this per turn).
351
+ // Without it, steps that resolve tools dynamically from `ctx.unifiedTools`
352
+ // — and any sub-agents spawned from them — would see no tools.
353
+ if (!ctx.unifiedTools && (s !== undefined || this.harnessTools.length > 0)) {
354
+ this.setUnifiedTools(ctx, [
355
+ ...collectAllTools(s),
356
+ ...this.harnessTools,
357
+ ]);
358
+ }
349
359
  return execute(s, input, ctx);
350
360
  }
351
361
  /** Run all layer `init` hooks before the first step executes. Per spec 11,
@@ -411,6 +411,19 @@ async function collectSpawnItems({ layers, parentExecutionCtx, childExecutionCtx
411
411
  });
412
412
  return spawnResults.flatMap((r) => r.items);
413
413
  }
414
+ /** Adds a spawned child's accumulated token/cost usage to its parent. */
415
+ function rollUpSpawnUsage(parent, child) {
416
+ if (!isMutableContext(parent)) {
417
+ return;
418
+ }
419
+ parent.tokens.input += child.tokens.input;
420
+ parent.tokens.output += child.tokens.output;
421
+ parent.tokens.total += child.tokens.total;
422
+ if (child.tokens.cached !== undefined) {
423
+ parent.tokens.cached = (parent.tokens.cached ?? 0) + child.tokens.cached;
424
+ }
425
+ parent.cost += child.cost;
426
+ }
414
427
  export async function executeSpawn(step, input, ctx, executeStep, opts) {
415
428
  const baseCtx = frameworkCast(ctx);
416
429
  const layers = resolveLayersForSpawn(step, opts?.parentLayers);
@@ -441,12 +454,20 @@ export async function executeSpawn(step, input, ctx, executeStep, opts) {
441
454
  itemSchemas: opts?.itemSchemas,
442
455
  });
443
456
  }
444
- // Build unified tool set for child from its step tree + layers
457
+ // Build unified tool set for child from its step tree + layers, plus the
458
+ // parent's unified tools. Sub-agents that read tools dynamically (via
459
+ // `ctx.unifiedTools`) otherwise lose the harness tool pool at the spawn
460
+ // boundary — a child whose `step.llm` resolves `tools` from context would
461
+ // see nothing. Inheriting the parent's tools keeps the harness toolset
462
+ // available across spawns; the child's own step/layer tools take precedence
463
+ // on name collision (dedup keeps the first occurrence), and children that
464
+ // need a restricted set filter explicitly.
445
465
  const childStepTools = collectAllTools(step.child);
446
466
  const childLayerTools = layers.length > 0 ? resolveLayerTools(layers, baseCtx.harness, baseCtx) : [];
447
467
  const childUnifiedTools = deduplicateTools([
448
468
  ...childStepTools,
449
469
  ...childLayerTools,
470
+ ...(baseCtx.unifiedTools ?? []),
450
471
  ]);
451
472
  // Create child context — empty by default, layers provide items via onSpawn.
452
473
  // `id: childId` keeps the ContextImpl's id in sync with the executionId
@@ -468,6 +489,12 @@ export async function executeSpawn(step, input, ctx, executeStep, opts) {
468
489
  try {
469
490
  // Execute the child step
470
491
  const childOutput = await executeStep(step.child, input, frameworkCast(childCtx));
492
+ // Roll the child's token/cost usage up into the parent. A sub-agent's
493
+ // spend is the parent's spend; without this it would be stranded on the
494
+ // child context and invisible to `ctx.tokens` / `ctx.cost` / until.maxCost.
495
+ // Because every spawn boundary rolls up, nested sub-agents propagate to the
496
+ // root recursively.
497
+ rollUpSpawnUsage(baseCtx, childCtx);
471
498
  // Pipeline result through layer onReturn hooks
472
499
  if (!hasLayers || !parentExecutionCtx) {
473
500
  return childOutput;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noetic-tools/core",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "Step primitives, interpreter, runtime, and memory layer contract for Noetic agents.",
6
6
  "license": "Apache-2.0",