@noetic-tools/core 1.1.2 → 1.2.1
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.
|
@@ -105,6 +105,13 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
105
105
|
readonly layerStateStore: LayerStateStore;
|
|
106
106
|
/** Per-harness memoization cache for `recallMode: 'eventual'` layers. */
|
|
107
107
|
readonly recallCache: RecallCache;
|
|
108
|
+
/**
|
|
109
|
+
* Execution ids whose layer `init` hooks have already run, so repeated/nested
|
|
110
|
+
* `run()` calls and the session turn path never re-init (which would clobber
|
|
111
|
+
* accumulated in-memory state by re-hydrating from storage). Keyed by
|
|
112
|
+
* `ctx.id` (the layer-state store's executionId).
|
|
113
|
+
*/
|
|
114
|
+
private readonly initializedExecutions;
|
|
108
115
|
readonly traceExporter: TraceExporter;
|
|
109
116
|
/**
|
|
110
117
|
* Long-lived shared cwd state. The same reference is seeded into every
|
|
@@ -143,6 +150,14 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
143
150
|
/** Run all layer `init` hooks before the first step executes. Per spec 11,
|
|
144
151
|
* init MUST complete before any recall fires so layer state is populated. */
|
|
145
152
|
private initAndRun;
|
|
153
|
+
/**
|
|
154
|
+
* Idempotently run layer `init` hooks for `ctx` (keyed by `ctx.id`). Init
|
|
155
|
+
* MUST complete before any recall/store fires so init-bearing layers are not
|
|
156
|
+
* treated as disabled. Guarded so nested/repeated `run()` calls and the
|
|
157
|
+
* session turn path never re-init — re-init would re-hydrate from storage and
|
|
158
|
+
* clobber accumulated in-memory state.
|
|
159
|
+
*/
|
|
160
|
+
private ensureLayersInit;
|
|
146
161
|
detachedSpawn<I, O>(s: Step<ContextMemory, I, O>, input: I, parentCtx: Context, overrides?: DetachedSpawnOverrides): DetachedHandle<O>;
|
|
147
162
|
createContext(opts?: {
|
|
148
163
|
parent?: Context;
|
|
@@ -110,6 +110,13 @@ export class AgentHarness {
|
|
|
110
110
|
layerStateStore;
|
|
111
111
|
/** Per-harness memoization cache for `recallMode: 'eventual'` layers. */
|
|
112
112
|
recallCache;
|
|
113
|
+
/**
|
|
114
|
+
* Execution ids whose layer `init` hooks have already run, so repeated/nested
|
|
115
|
+
* `run()` calls and the session turn path never re-init (which would clobber
|
|
116
|
+
* accumulated in-memory state by re-hydrating from storage). Keyed by
|
|
117
|
+
* `ctx.id` (the layer-state store's executionId).
|
|
118
|
+
*/
|
|
119
|
+
initializedExecutions = new Set();
|
|
113
120
|
traceExporter;
|
|
114
121
|
/**
|
|
115
122
|
* Long-lived shared cwd state. The same reference is seeded into every
|
|
@@ -346,17 +353,46 @@ export class AgentHarness {
|
|
|
346
353
|
}
|
|
347
354
|
//#endregion
|
|
348
355
|
async run(s, input, ctx) {
|
|
356
|
+
// Populate the unified tool pool when an embedder drives a step directly on
|
|
357
|
+
// a bare `createContext()` context (the turn pipeline does this per turn).
|
|
358
|
+
// Without it, steps that resolve tools dynamically from `ctx.unifiedTools`
|
|
359
|
+
// — and any sub-agents spawned from them — would see no tools.
|
|
360
|
+
if (!ctx.unifiedTools && (s !== undefined || this.harnessTools.length > 0)) {
|
|
361
|
+
this.setUnifiedTools(ctx, [
|
|
362
|
+
...collectAllTools(s),
|
|
363
|
+
...this.harnessTools,
|
|
364
|
+
]);
|
|
365
|
+
}
|
|
366
|
+
// Hydrate/recall/persist memory on the bare `run()` path too (issue #48):
|
|
367
|
+
// configuring `memory` + `storage` must "just work" without going through
|
|
368
|
+
// `execute()`/`seedSessionHistory`. Idempotent — see `ensureLayersInit`.
|
|
369
|
+
await this.ensureLayersInit(ctx);
|
|
349
370
|
return execute(s, input, ctx);
|
|
350
371
|
}
|
|
351
372
|
/** Run all layer `init` hooks before the first step executes. Per spec 11,
|
|
352
373
|
* init MUST complete before any recall fires so layer state is populated. */
|
|
353
374
|
async initAndRun(s, input, ctx) {
|
|
375
|
+
await this.ensureLayersInit(ctx);
|
|
376
|
+
return execute(s, input, ctx);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Idempotently run layer `init` hooks for `ctx` (keyed by `ctx.id`). Init
|
|
380
|
+
* MUST complete before any recall/store fires so init-bearing layers are not
|
|
381
|
+
* treated as disabled. Guarded so nested/repeated `run()` calls and the
|
|
382
|
+
* session turn path never re-init — re-init would re-hydrate from storage and
|
|
383
|
+
* clobber accumulated in-memory state.
|
|
384
|
+
*/
|
|
385
|
+
async ensureLayersInit(ctx) {
|
|
354
386
|
const layers = ctx.layers;
|
|
355
387
|
const storage = this.config.storage;
|
|
356
|
-
if (layers
|
|
357
|
-
|
|
388
|
+
if (!layers || layers.length === 0 || !storage) {
|
|
389
|
+
return;
|
|
358
390
|
}
|
|
359
|
-
|
|
391
|
+
if (this.initializedExecutions.has(ctx.id)) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
this.initializedExecutions.add(ctx.id);
|
|
395
|
+
await this.initLayers(layers, ctx, storage);
|
|
360
396
|
}
|
|
361
397
|
detachedSpawn(s, input, parentCtx, overrides) {
|
|
362
398
|
return dispatchStepThroughAdapter(this, s, input, parentCtx, overrides);
|
|
@@ -528,6 +564,9 @@ export class AgentHarness {
|
|
|
528
564
|
});
|
|
529
565
|
}
|
|
530
566
|
async disposeLayers(layers, ctx) {
|
|
567
|
+
// Drop the init guard so a deliberate dispose→run cycle re-hydrates from
|
|
568
|
+
// storage (disposeLayers also clears the layer-state store for this id).
|
|
569
|
+
this.initializedExecutions.delete(ctx.id);
|
|
531
570
|
await disposeLayers({
|
|
532
571
|
layers,
|
|
533
572
|
ctx: this.toExecCtx(ctx),
|
|
@@ -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;
|