agentfootprint 9.92.1 → 9.94.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +2 -1
  3. package/dist/core/Agent.js +15 -0
  4. package/dist/core/Agent.js.map +1 -1
  5. package/dist/core/LLMCall.js +6 -4
  6. package/dist/core/LLMCall.js.map +1 -1
  7. package/dist/core/agent/AgentBuilder.js +2 -2
  8. package/dist/core/agent/AgentBuilder.js.map +1 -1
  9. package/dist/core/agent/messageApiReceipt.js +1 -0
  10. package/dist/core/agent/messageApiReceipt.js.map +1 -1
  11. package/dist/core/agent/stages/callLLM.js +18 -7
  12. package/dist/core/agent/stages/callLLM.js.map +1 -1
  13. package/dist/core/agent/stages/window.js +12 -2
  14. package/dist/core/agent/stages/window.js.map +1 -1
  15. package/dist/core/agent/window/evictedTurns.js +60 -0
  16. package/dist/core/agent/window/evictedTurns.js.map +1 -0
  17. package/dist/esm/core/Agent.d.ts +4 -0
  18. package/dist/esm/core/Agent.js +15 -0
  19. package/dist/esm/core/Agent.js.map +1 -1
  20. package/dist/esm/core/LLMCall.js +6 -4
  21. package/dist/esm/core/LLMCall.js.map +1 -1
  22. package/dist/esm/core/agent/AgentBuilder.js +1 -1
  23. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  24. package/dist/esm/core/agent/messageApiReceipt.d.ts +3 -1
  25. package/dist/esm/core/agent/messageApiReceipt.js +1 -0
  26. package/dist/esm/core/agent/messageApiReceipt.js.map +1 -1
  27. package/dist/esm/core/agent/stages/callLLM.d.ts +9 -0
  28. package/dist/esm/core/agent/stages/callLLM.js +18 -7
  29. package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
  30. package/dist/esm/core/agent/stages/window.d.ts +8 -0
  31. package/dist/esm/core/agent/stages/window.js +12 -2
  32. package/dist/esm/core/agent/stages/window.js.map +1 -1
  33. package/dist/esm/core/agent/window/evictedTurns.d.ts +52 -0
  34. package/dist/esm/core/agent/window/evictedTurns.js +56 -0
  35. package/dist/esm/core/agent/window/evictedTurns.js.map +1 -0
  36. package/dist/esm/lib/time-travel/receipt.d.ts +71 -28
  37. package/dist/esm/lib/time-travel/receipt.js +33 -17
  38. package/dist/esm/lib/time-travel/receipt.js.map +1 -1
  39. package/dist/esm/lib/time-travel/servedView.d.ts +31 -22
  40. package/dist/esm/lib/time-travel/servedView.js +78 -43
  41. package/dist/esm/lib/time-travel/servedView.js.map +1 -1
  42. package/dist/esm/lib/trace-toolpack/lazyToolpack.d.ts +2 -2
  43. package/dist/esm/lib/trace-toolpack/lazyToolpack.js +5 -3
  44. package/dist/esm/lib/trace-toolpack/lazyToolpack.js.map +1 -1
  45. package/dist/esm/lib/trace-toolpack/selfExplain.d.ts +17 -0
  46. package/dist/esm/lib/trace-toolpack/selfExplain.js +53 -5
  47. package/dist/esm/lib/trace-toolpack/selfExplain.js.map +1 -1
  48. package/dist/esm/lib/trace-toolpack/traceToolNames.d.ts +37 -0
  49. package/dist/esm/lib/trace-toolpack/traceToolNames.js +51 -0
  50. package/dist/esm/lib/trace-toolpack/traceToolNames.js.map +1 -0
  51. package/dist/esm/lib/trace-toolpack/traceToolpack.d.ts +5 -7
  52. package/dist/esm/lib/trace-toolpack/traceToolpack.js +5 -19
  53. package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
  54. package/dist/esm/package.json +1 -1
  55. package/dist/lib/time-travel/receipt.js +33 -17
  56. package/dist/lib/time-travel/receipt.js.map +1 -1
  57. package/dist/lib/time-travel/servedView.js +78 -43
  58. package/dist/lib/time-travel/servedView.js.map +1 -1
  59. package/dist/lib/trace-toolpack/lazyToolpack.js +3 -4
  60. package/dist/lib/trace-toolpack/lazyToolpack.js.map +1 -1
  61. package/dist/lib/trace-toolpack/selfExplain.js +54 -6
  62. package/dist/lib/trace-toolpack/selfExplain.js.map +1 -1
  63. package/dist/lib/trace-toolpack/traceToolNames.js +54 -0
  64. package/dist/lib/trace-toolpack/traceToolNames.js.map +1 -0
  65. package/dist/lib/trace-toolpack/traceToolpack.js +6 -19
  66. package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
  67. package/dist/types/core/Agent.d.ts +4 -0
  68. package/dist/types/core/Agent.d.ts.map +1 -1
  69. package/dist/types/core/LLMCall.d.ts.map +1 -1
  70. package/dist/types/core/agent/messageApiReceipt.d.ts +3 -1
  71. package/dist/types/core/agent/messageApiReceipt.d.ts.map +1 -1
  72. package/dist/types/core/agent/stages/callLLM.d.ts +9 -0
  73. package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
  74. package/dist/types/core/agent/stages/window.d.ts +8 -0
  75. package/dist/types/core/agent/stages/window.d.ts.map +1 -1
  76. package/dist/types/core/agent/window/evictedTurns.d.ts +53 -0
  77. package/dist/types/core/agent/window/evictedTurns.d.ts.map +1 -0
  78. package/dist/types/lib/time-travel/receipt.d.ts +71 -28
  79. package/dist/types/lib/time-travel/receipt.d.ts.map +1 -1
  80. package/dist/types/lib/time-travel/servedView.d.ts +31 -22
  81. package/dist/types/lib/time-travel/servedView.d.ts.map +1 -1
  82. package/dist/types/lib/trace-toolpack/lazyToolpack.d.ts +2 -2
  83. package/dist/types/lib/trace-toolpack/lazyToolpack.d.ts.map +1 -1
  84. package/dist/types/lib/trace-toolpack/selfExplain.d.ts +17 -0
  85. package/dist/types/lib/trace-toolpack/selfExplain.d.ts.map +1 -1
  86. package/dist/types/lib/trace-toolpack/traceToolNames.d.ts +38 -0
  87. package/dist/types/lib/trace-toolpack/traceToolNames.d.ts.map +1 -0
  88. package/dist/types/lib/trace-toolpack/traceToolpack.d.ts +5 -7
  89. package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
  90. package/package.json +4 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,204 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.94.0] - 2026-09-11
11
+
12
+ **An optional family is loaded when its option is enabled, never before.** What
13
+ `import { Agent, defineTool } from 'agentfootprint'` costs a browser consumer
14
+ is the library's DEFAULT graph, not its whole surface — and for four releases
15
+ it was not: the docs site's demo chunk grew 394 → 421 KB gzip across 9.61,
16
+ 9.78, 9.88 and 9.92 for families the demo never calls, and two publishes
17
+ (9.87.0, 9.92.0) were lost to that ceiling. Measured on this commit, same
18
+ webpack build: the demo chunk 421.3 → 382.4 KB gzip (−38.9 KB, 16 → 15 async
19
+ assets; the library's own chunk 199.6 → 171.4 KB), and a plain esbuild bundle
20
+ of the root import 219.3 → 199.7 KB gzip (−19.6 KB, 717.7 → 658.2 KB minified).
21
+ Two root causes, both in the library, so every consumer gets the same cut. A
22
+ minor rather than a patch because a public provider's `list()` changes shape on
23
+ one iteration (below), and because every bundled consumer's output changes.
24
+
25
+ ### Changed
26
+
27
+ - **`.selfExplain()` mounts the trace toolpack lazily.** The toolpack —
28
+ eleven tools over a finished trace, at 47 KB minified the largest single
29
+ module in the package — was on the default graph because `AgentBuilder`
30
+ imported it for `TRACE_TOOL_NAMES`, the list of names it reserves at
31
+ `build()`, and `buildSelfExplainToolProvider` composed the pack eagerly.
32
+ WHY: an agent that never calls `.selfExplain()` was shipping the debugger;
33
+ one that does was shipping it before a single turn had run. Now the names
34
+ live in `lib/trace-toolpack/traceToolNames.ts` (with
35
+ `NO_COMPLETED_RUN_MESSAGE`, the other fact needed before the pack exists —
36
+ both still re-exported from their old modules), and the inline provider
37
+ reaches the pack through `import()` on the first iteration the self-explain
38
+ skill is ACTIVE: not at `build()`, not on a turn where nobody asked why,
39
+ never for an agent without the option. A bundler splits it into its own
40
+ chunk; Node loads it through the same `require`, one microtask later. The
41
+ one visible change: on an active iteration the inline provider's `list(ctx)`
42
+ answers a `Promise<Tool[]>` where it answered an array — the shape
43
+ `ToolProvider.list` has always allowed and the composed provider already
44
+ used; the idle iteration is still a synchronous `[]`, so the tools slot's
45
+ fast path is untouched. Provider id, the reserved-name refusal, delegate
46
+ mode and every trace tool are unchanged.
47
+ `test/lib/trace-toolpack/lazyMount.test.ts` pins load-when-active (the
48
+ module is evaluated exactly once, on that iteration), end-to-end answering
49
+ through the lazy path, and cold-mount = warm-mount on the recorded events,
50
+ catalogs and answers.
51
+ ```ts
52
+ const agent = Agent.create({ provider, model }).tool(lookup).selfExplain().build();
53
+ await agent.run({ message: 'Refund order A-1001?' }); // pack not loaded
54
+ await agent.run({ message: 'Why did you approve it?' }); // loads on the iteration read_skill opened it
55
+ ```
56
+ - **The ESM build now carries `sideEffects`.** A bundler reads that flag from
57
+ the CLOSEST package.json to the module it is deciding about, and
58
+ `dist/esm/package.json` — written at postbuild to mark the build
59
+ `type:module` — was a bare `{"type":"module"}`. WHY: the root's honest
60
+ `sideEffects` list therefore never reached a single ESM module; webpack,
61
+ Vite and esbuild had to presume every file under `dist/esm` might run
62
+ something at load, keep each one a barrel named, and could only strip the
63
+ pure declarations inside — which is also why a dynamic `import()` of a
64
+ module a barrel re-exports (the toolpack, through the `/observe` door the
65
+ lens imports) never split into its own chunk. `scripts/postbuild-esm.mjs`
66
+ now copies the root list across, rebased (`scripts/lib/esmSideEffects.mjs`;
67
+ `test/esm-packaging.test.ts` pins the shipped file against the same rule).
68
+ The list was widened only where it is TRUE: `./dist/index.js` /
69
+ `./dist/esm/index.js` (the root entry imports the three cache strategies for
70
+ their `registerCacheStrategy` calls) and `**/lib/injection-engine/index.js`
71
+ (the barrel imports `devWarnHost` to bind footprintjs's dev flag) — a barrel
72
+ marked side-effect-free is a barrel a bundler may skip, imports and all.
73
+ `test/lib/trace-toolpack/browserGraph.test.ts` proves at the code-split
74
+ graph that the toolpack is off the root entry's sync closure, still
75
+ reachable behind a `dynamic-import` edge, statically present on `/observe`
76
+ by design, and that every registration survives (the three strategies on
77
+ the root entry, the dev-warn host on `/context`). footprintjs's own
78
+ `dist/esm/package.json` has carried its flag all along; this follows it.
79
+
80
+ ### Docs
81
+
82
+ - `docs-next/scripts/check-site-budget.mjs`: the LAW above the demo ceiling
83
+ (an optional family loads when its option is enabled; the demo bundle
84
+ measures the default graph), the two families this release could not move
85
+ and why — the integrity checks run inside synchronous stage helpers and one
86
+ (`wireViolationsOf`) is unconditional; the observability recorders sit
87
+ behind `enable.flowchart()` / `enable.localObservability()`, which return
88
+ synchronously — and the ceiling LOWERED 429.4 → 390.0 KB (~2% over 382.4).
89
+ `DOCS_WEBPACK_STATS=1` on the docs build writes per-module webpack stats and
90
+ `docs-next/scripts/demo-chunk-modules.mjs` names what the demo chunk
91
+ carries, so the next raise is argued per module, not per ceiling.
92
+ - README "Tree-shakeable & ESM-first" and docs/debug/self-explain: the rule,
93
+ the example, the fences.
94
+
95
+ ## [9.93.0] - 2026-09-11
96
+
97
+ **The receipt says which strategy, and what the window dropped.** Four of the
98
+ seven standing recorded-not-built entries close, one is verified rather than
99
+ rebuilt, and one is assessed and half-built. Every item below is a behaviour
100
+ change to a shipped record — a new receipt key on every minting chart, a gap
101
+ that stops firing on three of them, a field that starts being written — which
102
+ is why this is a minor and not a patch. Nothing that had no name collision, no
103
+ window and no cache strategy records a different byte except the one new key.
104
+
105
+ ### Added
106
+
107
+ - **`Receipt.cache.strategy: string | null`** — WHICH cache strategy the
108
+ request went through (its registry `providerName`; `'*'` is the built-in
109
+ pass-through every agent runs), or `null` where none stood between assembly
110
+ and the port. WHY: `cache.transform: 'unchanged'` is the honest verdict both
111
+ when a strategy returned what it was given and when there was no strategy at
112
+ all, so the served view had no way to know a rewrite was impossible and
113
+ raised `cache-transform` on every view — including `LLMCall` and the two
114
+ message-API charts, where a reader was told what a cache strategy may have
115
+ done beside a call none could touch (entry 8). No fourth `transform` value
116
+ was added; the fact is its own field, minted by the one owner
117
+ (`buildReceipt` refuses to mint without it). A receipt from before this
118
+ release has no key, and a reader treats that as "cannot say", never as
119
+ `null`.
120
+ ```ts
121
+ receiptAt(agent.getSnapshot()!, 1)!.cache.strategy; // '*' — an agent on the mock provider
122
+ receiptAt(llmCall.getSnapshot()!, 1)!.cache.strategy; // null — nothing between assembly and the port
123
+ ```
124
+ - **`Receipt.omittedForAttention` is written** — by the agent chart's window
125
+ stage, for every turn it evicts for budget at an iteration's head. WHY: the
126
+ field was declared in 9.88.0 for "why did the model not know that?" and
127
+ excused as "no chart in this library supplies it" (entry 9) — a measurement
128
+ that had looked at the slots, which drop nothing, and not at the window,
129
+ which does (`context.evicted`, `reason: 'budget'`), and never wrote it. The
130
+ window hands what left to the call-llm mint on an in-memory handle
131
+ (`window/evictedTurns.ts`), the seam the compaction meter already crosses —
132
+ never a scope read, which on every windowless run would be a tracked read of
133
+ an absent key and a phantom context source per loop. Each hash is the
134
+ evicted turn's own `messages.entries[].hash`, so a drop on epoch k's receipt
135
+ pairs with the turn as an earlier receipt served it — the pairing law,
136
+ driven on a real sliding window. Absent now means nothing was dropped before
137
+ that call. The field left `UNGAPPED_FIELDS` and is named by
138
+ `no-receipt-on-chart`, the one gap that can lose it. An agent without a
139
+ window hands both stages the deps they always had.
140
+ ```ts
141
+ const agent = Agent.create({ provider, model }).system('bot').tool(lookup)
142
+ .window(slidingWindow({ keepRecentTurns: 1 })).build();
143
+ await agent.run({ message: 'go' });
144
+ receiptAt(agent.getSnapshot()!, 3)!.omittedForAttention; // { count: 2, hashes: [...] } — the pair that left at this head
145
+ ```
146
+
147
+ ### Changed
148
+
149
+ - **`cache-transform` is raised only where a rewrite was possible** — where
150
+ the receipt names a strategy, or where no receipt can say (a receipt-less
151
+ view, a pre-9.93.0 receipt, a refused shape). WHY: entry 8 — a sentence
152
+ about what a strategy may have done was printed where none could run. Only a
153
+ receipt that SAYS `null` lifts it; absence still raises, so this is not the
154
+ inference-from-absence the entry refused. Measured: `LLMCall` and both
155
+ message-API charts with a run id now read `['provider-defaults']`; the
156
+ receipt-less message-API view still reads
157
+ `['no-receipt-on-chart', 'cache-transform']`, honestly. The printed sentence
158
+ is unchanged and every clause of it still holds where it prints.
159
+ ```ts
160
+ servedAt(llmCall.getSnapshot()!, 1)!.gaps.map((g) => g.gap); // ['provider-defaults'] (was ['cache-transform', 'provider-defaults'])
161
+ ```
162
+ - **`cache-transform` names `tools.forced`, `tools.withheld` and
163
+ `cache.strategy`** — fourteen fields, the whole request the strategy holds
164
+ (`params` alone stays off, read past it). WHY: entry 7 — both `tools`
165
+ fields are written from assembly's own decision and never from
166
+ `preparedRequest`, so a strategy that drops the forced answer tool from
167
+ `request.tools` leaves a receipt whose `tools.forced` names a tool the port
168
+ never carried, and the gap did not warn. Driven: an unforcing strategy on a
169
+ `'tool-forced'` agent — wire has no tool and no forcing, receipt says
170
+ `tools.forced: 'respond_with_schema'` and `params.toolChoice` absent, the
171
+ gap names the half that describes the decision. Reading them off the
172
+ prepared request instead was NOT done: that would describe a different fact
173
+ under the same name.
174
+ - **The receipt-conformance law checks the cache verdict outright where no
175
+ strategy ran** — `transform: 'unchanged'`, `transformHash: null`,
176
+ `markersApplied: []`, and no `cache-transform` excuse; where one ran, the gap
177
+ must excuse the unrebuildable fields as before.
178
+ - **Byte-identity references regenerated** (`test/core/tools/reference/`),
179
+ with the whole delta against the 9.92.1 set on record in the test's header:
180
+ the new `cache.strategy` key on every minting fixture, the gap's longer
181
+ field list on every agent view, and the gap leaving the three no-strategy
182
+ views. No message, tool or other key moved on any fixture.
183
+
184
+ ### Verified
185
+
186
+ - **Recorded-not-built entries 4 and 5 hold under the 9.92.0 law** and are
187
+ marked built (verified 2026-09-11 against the 9.92.1 `dist`, before any edit
188
+ of this release). `.selfExplain()`'s `run_overview` against an always-visible
189
+ skill, a never-activated scoped skill and a stepped skill: dispatch follows
190
+ the offer on every epoch (the never-activated skill no longer answers the
191
+ framework's own contract — the framework does), `tools.shadowed` names the
192
+ wire's party and never `provider(skill-scoped:self-explain)`, and the losing
193
+ claim is named by `tools.claim_swallowed` on every epoch it lost. The
194
+ reservation this entry names is deliberately untouched; the measured output
195
+ is pasted on the entries and pinned as `offer-and-answer.test.ts` §7.
196
+
197
+ ### Assessed
198
+
199
+ - **Entry 10 ("an assertion can be weaker than its clause") is split, and the
200
+ mechanical half is built.** Every clause in `gap-sentences.test.ts` declares
201
+ the fields it is about; a seam hands its assertion a view or receipt with
202
+ one declared field altered, then removed; a contract requires the assertion
203
+ to fail under at least one. The first run found one insensitivity and it is
204
+ fixed. What remains is the quantifier half — a clause about "every field" or
205
+ "both chart shapes" asserted on fewer runs — which needs per-clause hand
206
+ work and is named on the entry rather than faked by a mechanism.
207
+
10
208
  ## [9.92.1] - 2026-09-11
11
209
 
12
210
  ### Fixed
package/README.md CHANGED
@@ -975,8 +975,9 @@ Import one thing, ship one thing. agentfootprint is built so your bundle grows o
975
975
  - **Per-file modules + honest `sideEffects`.** The dist is emitted file-by-file (never pre-bundled), so bundlers drop every export you don't touch. A small `import { defineTool }` doesn't pull in the Agent runtime, injection engine, memory stores, or LLM providers.
976
976
  - **Ten doors, named for what you're doing.** `agentfootprint` · `/providers` (plug in a backend) · `/memory` (state that outlives a turn) · `/observe` (everything that watches) · `/context` (how context gets assembled) · `/resilience` (when the call fails) · `/security` (who may do what) · `/hosting` (behind a wire) · `/events` (the typed wire vocabulary) · `/cache` (prompt caching). 8.0.0 consolidated 26 internals-named subpaths into these; every old path still resolves for all of 8.x.
977
977
  - **Lazy peer-deps.** Heavyweight integrations load their SDK **only when you instantiate them** — importing agentfootprint never bundles `@anthropic-ai/sdk`, `ioredis`, the AWS SDKs, or the MCP SDK unless you actually use that adapter.
978
+ - **Optional families load when their option is enabled, never before.** What `import { Agent, defineTool }` costs is the library's *default graph*, not its whole surface. `.selfExplain()`'s trace toolpack — eleven tools over a finished trace, the largest single module in the package — is reached through a dynamic `import()` on the first iteration the self-explain skill is active; an agent that never enables it never ships it, and a bundler splits it into its own chunk. (A door that exists to hand you the debugger, `/observe`, carries it eagerly on purpose.)
978
979
 
979
- **Proven, not promised.** A CI smoke test bundles a minimal `import { defineTool }` and asserts the Agent runtime, injection engine, memory stores, and providers are pruned; a second test loads the main barrel and every subpath as true ESM and verifies the lazy-adapter loader works under ESM (`createRequire`, not a bare `require`). See [`test/esm-packaging.test.ts`](test/esm-packaging.test.ts).
980
+ **Proven, not promised.** A CI smoke test bundles a minimal `import { defineTool }` and asserts the Agent runtime, injection engine, memory stores, and providers are pruned; a second test loads the main barrel and every subpath as true ESM and verifies the lazy-adapter loader works under ESM (`createRequire`, not a bare `require`). See [`test/esm-packaging.test.ts`](test/esm-packaging.test.ts). A third walks the code-split graph of the root entry and fails if the trace toolpack ever returns to its sync closure — [`test/lib/trace-toolpack/browserGraph.test.ts`](test/lib/trace-toolpack/browserGraph.test.ts); the docs site's own demo chunk is ratcheted against the same default graph in [`docs-next/scripts/check-site-budget.mjs`](docs-next/scripts/check-site-budget.mjs).
980
981
 
981
982
  ---
982
983
 
@@ -50,6 +50,7 @@ const ReliabilityRecorder_js_1 = require("../recorders/core/ReliabilityRecorder.
50
50
  const ResilienceRecorder_js_1 = require("../recorders/core/ResilienceRecorder.js");
51
51
  const CheckInRecorder_js_1 = require("../recorders/core/CheckInRecorder.js");
52
52
  const CompactionMeter_js_1 = require("../recorders/core/CompactionMeter.js");
53
+ const evictedTurns_js_1 = require("./agent/window/evictedTurns.js");
53
54
  const durabilityBarrier_js_1 = require("./durabilityBarrier.js");
54
55
  const toolSessions_js_1 = require("./toolSessions.js");
55
56
  const eventMeta_js_1 = require("../bridge/eventMeta.js");
@@ -264,6 +265,10 @@ class Agent extends RunnerBase_js_1.RunnerBase {
264
265
  /** The instrument the window stage reads mid-run (adapter-reported usage +
265
266
  * per-message provenance). Only ever created alongside a strategy. */
266
267
  compactionMeterHandle;
268
+ /** The window stage's seam to the receipt (9.93.0): what left the window at
269
+ * this iteration's head, read by the call-llm mint in the same iteration.
270
+ * Only ever created alongside a strategy, like the meter. */
271
+ evictedTurnsHandle;
267
272
  /** Snapshot read-tracking policy (#18/#14) — forwarded to the internal
268
273
  * executor. Agent default is `'summary'` (cheap markers), NOT
269
274
  * footprintjs's `'full'`. See AgentOptions.readTracking. */
@@ -612,6 +617,7 @@ class Agent extends RunnerBase_js_1.RunnerBase {
612
617
  if (windowStrategy !== undefined) {
613
618
  this.windowStrategy = windowStrategy;
614
619
  this.compactionMeterHandle = (0, CompactionMeter_js_1.compactionMeter)();
620
+ this.evictedTurnsHandle = (0, evictedTurns_js_1.createEvictedTurnsHandle)();
615
621
  }
616
622
  // The two governance chains. Empty arrays (not undefined) so every read
617
623
  // site is a plain `.length > 0` test rather than an optional dance.
@@ -2221,6 +2227,9 @@ class Agent extends RunnerBase_js_1.RunnerBase {
2221
2227
  // `conversationId` is deliberately not carried: it is a thread, not a
2222
2228
  // person, and `sessionId` beside it is the fact the transport delivered.
2223
2229
  const actor = runOptions?.identity ?? this.lastRunIdentity;
2230
+ // A fresh run starts with no evictions filed — the previous run's last
2231
+ // visit must not be read as this run's (`window/evictedTurns.ts`).
2232
+ this.evictedTurnsHandle?.clear();
2224
2233
  this.currentRunContext = {
2225
2234
  runStartMs: Date.now(),
2226
2235
  runId: (0, RunnerBase_js_1.makeRunId)(),
@@ -3253,6 +3262,9 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3253
3262
  // …and its off switch. Value-conditional, so an agent on the default
3254
3263
  // hands `callLLM` exactly the dep bag it always did.
3255
3264
  ...(this.recordReceiptValue === false && { recordReceipt: false }),
3265
+ // …and the window's evictions for `omittedForAttention` (9.93.0) — only
3266
+ // an agent with a window strategy has any, and only it hands the seam.
3267
+ ...(this.evictedTurnsHandle !== undefined && { evictedTurns: this.evictedTurnsHandle }),
3256
3268
  provider,
3257
3269
  model,
3258
3270
  ...(temperature !== undefined && { temperature }),
@@ -3327,6 +3339,9 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3327
3339
  run: (0, window_js_1.buildWindowStage)({
3328
3340
  strategy: this.windowStrategy,
3329
3341
  meter: this.compactionMeterHandle,
3342
+ ...(this.evictedTurnsHandle !== undefined && {
3343
+ evictedTurns: this.evictedTurnsHandle,
3344
+ }),
3330
3345
  agentModel: model,
3331
3346
  providerName: provider.name,
3332
3347
  getRunId: () => this.currentRunContext?.runId,