agentfootprint 9.90.0 → 9.92.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.
Files changed (91) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +213 -0
  3. package/CLAUDE.md +1 -1
  4. package/ai-instructions/claude-code/SKILL.md +1 -1
  5. package/dist/core/Agent.js +23 -7
  6. package/dist/core/Agent.js.map +1 -1
  7. package/dist/core/LLMCall.js +62 -7
  8. package/dist/core/LLMCall.js.map +1 -1
  9. package/dist/core/agent/buildAgentMessageApiChart.js +36 -3
  10. package/dist/core/agent/buildAgentMessageApiChart.js.map +1 -1
  11. package/dist/core/agent/buildMessageApiChart.js +26 -3
  12. package/dist/core/agent/buildMessageApiChart.js.map +1 -1
  13. package/dist/core/agent/buildToolRegistry.js +90 -33
  14. package/dist/core/agent/buildToolRegistry.js.map +1 -1
  15. package/dist/core/agent/messageApiReceipt.js +77 -0
  16. package/dist/core/agent/messageApiReceipt.js.map +1 -0
  17. package/dist/core/agent/stages/callLLM.js +1 -5
  18. package/dist/core/agent/stages/callLLM.js.map +1 -1
  19. package/dist/core/agent/stages/toolCalls.js +176 -28
  20. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  21. package/dist/core/slots/buildToolsSlot.js +209 -62
  22. package/dist/core/slots/buildToolsSlot.js.map +1 -1
  23. package/dist/esm/core/Agent.js +24 -8
  24. package/dist/esm/core/Agent.js.map +1 -1
  25. package/dist/esm/core/LLMCall.d.ts +18 -0
  26. package/dist/esm/core/LLMCall.js +63 -8
  27. package/dist/esm/core/LLMCall.js.map +1 -1
  28. package/dist/esm/core/agent/buildAgentMessageApiChart.d.ts +22 -0
  29. package/dist/esm/core/agent/buildAgentMessageApiChart.js +37 -4
  30. package/dist/esm/core/agent/buildAgentMessageApiChart.js.map +1 -1
  31. package/dist/esm/core/agent/buildMessageApiChart.d.ts +32 -0
  32. package/dist/esm/core/agent/buildMessageApiChart.js +27 -4
  33. package/dist/esm/core/agent/buildMessageApiChart.js.map +1 -1
  34. package/dist/esm/core/agent/buildToolRegistry.d.ts +80 -31
  35. package/dist/esm/core/agent/buildToolRegistry.js +90 -33
  36. package/dist/esm/core/agent/buildToolRegistry.js.map +1 -1
  37. package/dist/esm/core/agent/messageApiReceipt.d.ts +82 -0
  38. package/dist/esm/core/agent/messageApiReceipt.js +73 -0
  39. package/dist/esm/core/agent/messageApiReceipt.js.map +1 -0
  40. package/dist/esm/core/agent/stages/callLLM.js +3 -7
  41. package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
  42. package/dist/esm/core/agent/stages/toolCalls.d.ts +43 -5
  43. package/dist/esm/core/agent/stages/toolCalls.js +174 -27
  44. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  45. package/dist/esm/core/agent/types.d.ts +15 -0
  46. package/dist/esm/core/slots/buildToolsSlot.d.ts +100 -0
  47. package/dist/esm/core/slots/buildToolsSlot.js +207 -61
  48. package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
  49. package/dist/esm/events/payloads.d.ts +92 -18
  50. package/dist/esm/events/registry.d.ts +5 -1
  51. package/dist/esm/events/registry.js +4 -0
  52. package/dist/esm/events/registry.js.map +1 -1
  53. package/dist/esm/lib/time-travel/receipt.d.ts +45 -13
  54. package/dist/esm/lib/time-travel/receipt.js +41 -10
  55. package/dist/esm/lib/time-travel/receipt.js.map +1 -1
  56. package/dist/esm/lib/time-travel/servedView.d.ts +36 -35
  57. package/dist/esm/lib/time-travel/servedView.js +64 -43
  58. package/dist/esm/lib/time-travel/servedView.js.map +1 -1
  59. package/dist/events/registry.js +4 -0
  60. package/dist/events/registry.js.map +1 -1
  61. package/dist/lib/time-travel/receipt.js +43 -11
  62. package/dist/lib/time-travel/receipt.js.map +1 -1
  63. package/dist/lib/time-travel/servedView.js +64 -43
  64. package/dist/lib/time-travel/servedView.js.map +1 -1
  65. package/dist/types/core/Agent.d.ts.map +1 -1
  66. package/dist/types/core/LLMCall.d.ts +18 -0
  67. package/dist/types/core/LLMCall.d.ts.map +1 -1
  68. package/dist/types/core/agent/buildAgentMessageApiChart.d.ts +22 -0
  69. package/dist/types/core/agent/buildAgentMessageApiChart.d.ts.map +1 -1
  70. package/dist/types/core/agent/buildMessageApiChart.d.ts +32 -0
  71. package/dist/types/core/agent/buildMessageApiChart.d.ts.map +1 -1
  72. package/dist/types/core/agent/buildToolRegistry.d.ts +80 -31
  73. package/dist/types/core/agent/buildToolRegistry.d.ts.map +1 -1
  74. package/dist/types/core/agent/messageApiReceipt.d.ts +83 -0
  75. package/dist/types/core/agent/messageApiReceipt.d.ts.map +1 -0
  76. package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
  77. package/dist/types/core/agent/stages/toolCalls.d.ts +43 -5
  78. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  79. package/dist/types/core/agent/types.d.ts +15 -0
  80. package/dist/types/core/agent/types.d.ts.map +1 -1
  81. package/dist/types/core/slots/buildToolsSlot.d.ts +100 -0
  82. package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
  83. package/dist/types/events/payloads.d.ts +92 -18
  84. package/dist/types/events/payloads.d.ts.map +1 -1
  85. package/dist/types/events/registry.d.ts +5 -1
  86. package/dist/types/events/registry.d.ts.map +1 -1
  87. package/dist/types/lib/time-travel/receipt.d.ts +45 -13
  88. package/dist/types/lib/time-travel/receipt.d.ts.map +1 -1
  89. package/dist/types/lib/time-travel/servedView.d.ts +36 -35
  90. package/dist/types/lib/time-travel/servedView.d.ts.map +1 -1
  91. package/package.json +1 -1
package/AGENTS.md CHANGED
@@ -544,7 +544,7 @@ files `agentfootprint.tools.coverage_declared`; with
544
544
  to the final answer, which is how a limit survives a model that would rather not
545
545
  mention it.
546
546
 
547
- ### Observability — 109 typed events across 24 domains
547
+ ### Observability — 111 typed events across 24 domains
548
548
 
549
549
  ```typescript
550
550
  agent.on('agentfootprint.context.injected', (e) =>
package/CHANGELOG.md CHANGED
@@ -7,6 +7,219 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.92.1] - 2026-09-11
11
+
12
+ ### Fixed
13
+
14
+ - **9.92.0 never reached npm; this release is the same code.** Its publish job
15
+ failed the docs site's deferred-demo gzip ceiling by 0.4 KB (421.0 KB against
16
+ 420.6 KB): the interactive demo bundles the library itself, and 9.92.0's
17
+ tool-resolution family rides the same main-entry graph as the receipt family
18
+ did in 9.88.0. The ceiling is re-baselined ~2% over the measurement with the
19
+ reasoning beside the number, and the docs-truth report regenerated. No
20
+ library code changes between 9.92.0 and 9.92.1. This is the fourth raise of
21
+ that ceiling for a family the demo never calls; the dynamic-import fix named
22
+ in 9.61.0 is the next move rather than a fifth raise.
23
+
24
+ ## [9.92.0] - 2026-09-11
25
+
26
+ **The offer and the answer are one party.** At every LLM call the model is
27
+ OFFERED a list of tool contracts (the wire; the receipt hashes each one), and
28
+ when it calls a name something ANSWERS. Until this release those two halves
29
+ could come from different parties for one name: the tools slot merges
30
+ `[static, provider, skill, step]` first-occurrence-wins, while dispatch
31
+ consulted the build-time registry first — so a provider's contract on the wire
32
+ was answered by a skill's `execute` (an INACTIVE skill's, even), a provider's
33
+ `skip_step` by the framework's (which then advanced the procedure on a call the
34
+ model made against somebody else's contract), and the report that exists for
35
+ this seam walked `activeInjections` and named the provider on epochs whose wire
36
+ carried the skill's contract. A claimant that lost both the wire and the
37
+ dispatch was simply dead, 22 rows of it in the divergence walk's baseline, with
38
+ nothing on the record. `docs/design/2026-09-the-offer-and-the-answer.md` states
39
+ the law; recorded-not-built entries 1, 2 and 3, the `claim-swallowed` bullet
40
+ under entry 1 and the 9.91.0 tools-slot follow-up are marked built.
41
+
42
+ **The law:** for every tool name on a call, exactly one party owns the OFFER and
43
+ the same party owns the ANSWER — or the record names the disagreement. Three
44
+ consequences, one example each:
45
+
46
+ - **Dispatch follows the offer.** `buildToolsSlot.ts` · `mergeWire` is the one
47
+ pass that produces the wire AND the record of who won each name
48
+ (`ServedToolParties`, closure-shared like `ProviderToolCache`, never scope
49
+ state); `toolCalls.ts` · `lookupTool` reads it first. A provider and a
50
+ never-activated scoped skill both claiming `shared_tool`: the wire carries
51
+ the provider's contract, the provider's tool answers, and the skill's
52
+ `execute` answers no call while the provider holds the name — nor after the
53
+ provider withdraws it, because the skill's contract was never what the
54
+ model read: that call is REFUSED as a recorded tool result (`toolCalls.ts`
55
+ · `notServedResult`). A name NOT on this epoch's wire — a held-out step
56
+ tool, a parked map's tool, a scoped tool named from a restored transcript —
57
+ still dispatches (the capability law's held-out clause, `epoch-laws.test.ts`
58
+ 1(a)–(e)), but only to the party the model LAST read the name under
59
+ (`ServedToolParties.lastServed`) or the name's only holder when it was never
60
+ served; every such dispatch is on the record as the new
61
+ `agentfootprint.tools.answered_off_wire` (`{ toolName, toolCallId,
62
+ iteration, answeredBy, answeredById? }`). A pause that re-dispatches on
63
+ resume (a middleware ask, a check-in, a credential consent) carries the
64
+ served party on its checkpoint (`pausedToolParty`, pause-path-only), so a
65
+ resume in a FRESH Agent instance — empty closure, no Compose — cannot fall
66
+ back to the build-time map's first holder either.
67
+ - **The report's subject is the wire.** `agentfootprint.tools.shadowed` now
68
+ fires once per contested name per iteration when two contracts COMPETED for
69
+ the wire, and `schemaFrom`/`dispatchTo` both name the wire's party — they
70
+ agree by construction. A stepped skill and a provider sharing a name draw
71
+ `skill 'desk-stepped'` in both halves on every epoch, never `'provider'`.
72
+ The vocabulary gained `'framework'` (`ToolNameChannel`), so an auto-attach
73
+ is named as itself.
74
+ - **A dead claim is reported.** New `agentfootprint.tools.claim_swallowed`
75
+ (`ToolsClaimSwallowedPayload`: `{ toolName, iteration, lostBy, lostById?,
76
+ wonBy, wonById? }`, names only) fires once per iteration for every party
77
+ whose claim to a name is held by somebody else — whether its contract
78
+ competed (an active skill against a provider) or never reached the merge
79
+ (the same skill while inactive; a provider tool whose name a static
80
+ `.tool()` owns; the framework's `skip_step` between tenures). Identity is by
81
+ IMPLEMENTATION: two skills sharing one `Tool` reference (documented-legal)
82
+ are one claim and draw nothing. Now 111 typed events across 24 domains.
83
+
84
+ **`skip_step` is a claimant like any other** (`buildToolRegistry.ts` ·
85
+ `toolClaimants`, the registry's new list of every build-time claimant per
86
+ name — a list, not a winner). Its schema still merges last, so it rides the
87
+ wire only when nobody else put the name forward; when a provider serves
88
+ `skip_step`, the provider's tool answers and the step bookkeeping keys on the
89
+ framework's own instance having answered (`toolCalls.ts` ·
90
+ `frameworkSkipStepAnswered`) — the procedure does not advance. The build-time
91
+ refusals (a static `.tool()` or a skill tool named `skip_step`/`present`) stand.
92
+
93
+ **`buildAgentMessageApiChart` hands the model the declared tool set on every
94
+ turn.** Its tools-slot mount now says `arrayMerge: ArrayMergeMode.Replace`, as
95
+ the agent charts always did; turn 2 no longer serves `['weather','weather']`.
96
+
97
+ **Byte-identity for every run with no name collision.**
98
+ `test/core/tools/byte-identity.test.ts` drives fifteen collision-free shapes
99
+ (both agent react modes, a skill-graph hop, a stepped skill, a parked map, the
100
+ wrap-up, a tool-forced output, three provider shapes, two skills sharing one
101
+ `Tool` reference, `LLMCall`, both message-API charts) and compares `commitLog`
102
+ and `servedAt(k)` against references under `test/core/tools/reference/`
103
+ generated on the 9.91.0 tree. All fifteen are identical; the
104
+ receipt-conformance suite is unchanged and green on all four chart shapes.
105
+
106
+ **The divergence walk, re-recorded — and widened.** Every `contract-swap`
107
+ row (12) and every `report-misattributed` row (10) is GONE and ratcheted so
108
+ it cannot reopen quietly; the `claim-swallowed` family is 35 rows, every one
109
+ carrying what `tools.claim_swallowed` said about it. The walk gained the
110
+ cross-epoch × collision cases it never had (walk D: a provider withdrawing a
111
+ name an inactive skill also holds; a fresh-instance resume of a provider's
112
+ call) and a kind for the held-out dispatch the law keeps, `answered-off-wire`
113
+ (4 rows, each carrying the event). The harness moved to
114
+ `test/core/agent/toolDivergenceWalk.harness.ts` so the reproductions in
115
+ `test/core/tools/offer-and-answer.test.ts` — the entries' own, verbatim, each
116
+ red on 9.91.0, plus the review's own shapes — drive the same configurations.
117
+
118
+ **Consumer-facing changes, stated plainly.** (1) A provider tool that shares a
119
+ name with a skill tool now RUNS where the skill's used to — the contract the
120
+ model read is the one that answers. (2) `tools.shadowed` fires on
121
+ configurations that were silent (a provider against a static `.tool()` or a
122
+ framework auto-attach) and no longer names `'provider'` as `schemaFrom` where
123
+ the wire carried the skill's contract; `dispatchTo` now equals `schemaFrom`.
124
+ (3) Two new events, `tools.claim_swallowed` and `tools.answered_off_wire`.
125
+ (4) A call to a name that left the wire, whose last-served party can no longer
126
+ answer, is refused with a recorded result where it used to be answered by
127
+ whichever party the build-time map held first; the same sentence answers a
128
+ provider-served call resumed in a fresh instance (where it used to read
129
+ `Unknown tool`). (5) Two stale comments in `buildToolsSlot.ts` and
130
+ `buildToolRegistry.ts` corrected in the same diff; `Agent.ts`'s "fresh chart
131
+ per run()" comment corrected — the chart is built once at construction.
132
+
133
+ ## [9.91.0] - 2026-09-10
134
+
135
+ **Every chart that serves a model now mints a receipt.** A receipt is the proof
136
+ of what the model was actually handed, minted at the call and committed in the
137
+ bundle the call already writes — and until this release exactly ONE stage minted
138
+ one. Three other charts in this library handed a model a request and left
139
+ `no-receipt-on-chart` on every view they produced, so a reader of an `LLMCall`
140
+ or messageAPI recording could only ever see Reconstructed, never Verified.
141
+
142
+ Two reasons were on the record for that. **Neither survived.** The first — that
143
+ `Receipt.cache.transform` has no honest value on a chart running no cache
144
+ strategy — was already refuted in 9.88.0 and left refuted in the source: an
145
+ `Agent` with a pass-through strategy records `'unchanged'` today, and the value
146
+ is true for the same reason where no strategy exists at all, because *the
147
+ request that went out IS the request that was assembled*. **No fourth enum value
148
+ was added.** The second — the salt — never applied to `LLMCall`, which owns its
149
+ executor and mints a run id exactly as `Agent` does. It DOES apply to the two
150
+ messageAPI charts, and that was verified rather than assumed: footprintjs stamps
151
+ `TraversalContext.runId` on recorder events, not on a stage's scope; there is no
152
+ `$runId` on `ScopeFacade`; `ExecutionEnv` is a fixed type carrying `traceId` and
153
+ no run id. A chart builder run on somebody else's executor genuinely cannot
154
+ invent the value — so it is a dep, and a chart given none mints NOTHING rather
155
+ than salting every hash with an empty string.
156
+
157
+ ### Added
158
+
159
+ - **`LLMCall` mints a receipt** (`LLMCall.ts` · `callLLM`), salted with the run
160
+ id `createExecutor` already mints per run — read at CALL time, not closed over
161
+ at build time, so the second run of the same `LLMCall` is not salted with the
162
+ first run's value. `servedAt` on an `LLMCall` recording now carries
163
+ `basis.model`, `params` and the cache verdict, and stops raising
164
+ `no-receipt-on-chart`.
165
+ - **`LLMCallOptions.recordReceipt`** — the twin of `AgentOptions.recordReceipt`,
166
+ same field, same contract, default ON. `false` declines the mint (one
167
+ commit-log value and a SHA-256 per piece, per message, per tool schema);
168
+ `servedAt` still rebuilds the view and declares the missing witness.
169
+ - **`MessageApiChartDeps.getRunId` / `AgentMessageApiChartDeps.getRunId`** —
170
+ supply the run id and the chart mints on every turn; omit it and it mints
171
+ none. `src/core/agent/messageApiReceipt.ts` owns that rule for both charts,
172
+ because the two are deliberate twins and a mint written out in each is two
173
+ chances to disagree about what the model was handed.
174
+ - **`receipt.ts` · `receiptPieces`** — the injection-record → receipt-piece map,
175
+ which was about to have four copies. One owner, calling the same
176
+ `contributingPieces` the system-prompt join calls, so a piece cannot be on the
177
+ receipt and absent from the string.
178
+
179
+ ### Fixed
180
+
181
+ - **`servedAt` reported an EMPTY tool list on a call that served tools.**
182
+ `servedView.ts` · `viewOf` read the served list from `dynamicToolSchemas`
183
+ alone. The agent charts map the tools slot's output onto that key at the mount
184
+ boundary; the messageAPI charts carry it out under the slot's own name — so a
185
+ `buildAgentMessageApiChart` view said `tools.names: []` about a call that
186
+ served one. An empty list is not an omission, it is a DENIAL, and it went
187
+ unnoticed for as long as no receipt existed to contradict it. The read is now
188
+ a fallback chain (the agent key first, so no agent recording changes), the
189
+ same shape the conversation has had since 9.88.0. Red before, on
190
+ `test/lib/time-travel/receipt-conformance.test.ts` · *the tool the model was
191
+ served is hashed by the receipt AND rebuilt from the log*.
192
+
193
+ ### Changed
194
+
195
+ - **`SERVED_GAPS['no-receipt-on-chart']` is unchanged, and still true.** Its
196
+ printed sentence never named a chart, so nothing in it went false; the
197
+ MECHANISM comment beside it now names the shapes that still reach it — a
198
+ messageAPI chart handed no run id, a run that declined with `recordReceipt:
199
+ false`, a recording made before 9.88.0, and a consumer's own `call-llm` stage,
200
+ which this library does not mint for. The cause `'no-receipt-committed'` is
201
+ still driven by a REAL run in both walks; the driver moved from an `LLMCall`
202
+ (which now mints) to a messageAPI chart with no run id.
203
+ - **The composed request is assembled once per chart.** Each of the three
204
+ charts now builds ONE `LLMRequest` object and both sends it and fingerprints
205
+ it, and each routes its conversation through `stripFrameworkFields` — the
206
+ third rule of the one assembly — so the mint and the rebuild cannot drift.
207
+ Every other byte those charts commit is unchanged: measured by dumping each
208
+ chart's commit log, its per-bundle keys and its final state before and after,
209
+ and diffing (identical apart from the new `receipt` key).
210
+
211
+ ### Docs
212
+
213
+ - `src/lib/time-travel/README.md` — a "Which charts mint a receipt" table (chart
214
+ → mints? → the salt) with the builder example, and every measurement that
215
+ named an `LLMCall` view as receipt-less retaken on the shape that still is.
216
+ - `docs-next` `debug/time-travel.mdx` — the same section for the site. No new
217
+ route.
218
+ - `docs/design/2026-09-recorded-not-built.md` — entry 8's reproduction retaken
219
+ (it still stands; only its driver moved) and an appendix marking the standing
220
+ no-mint fact BUILT in 9.91.0, with the reasons that died and the one that
221
+ survives.
222
+
10
223
  ## [9.90.0] - 2026-09-10
11
224
 
12
225
  **The recording carries its own milestones.** Until now a stored run could not
package/CLAUDE.md CHANGED
@@ -154,7 +154,7 @@ Traps: `src/observability/` holds the finder IMPLEMENTATIONS (canonical home; `d
154
154
  - **Embedder fingerprint** (8.9.0) → `Embedder.id` (optional; every shipped embedder sets one, and NONE include dims — the store appends `@<dims>` itself, so an id carrying its own size double-stamps) + `indexDocuments` defaulting `embedderId` to it + `SqliteVectorStore.reconcileFingerprint` (the only comparison site). Rule: dimensions ALWAYS decide, model ids decide only when BOTH sides named themselves — refusing on an absent name would block the majority of callers who never pass `embedderId`.
155
155
  - **Retrieval record** (8.8.0) → FOUR stages write one object in sequence: `loadRelevant` (candidates+scores+threshold verdicts) → `pickByBudget` (re-marks admitted→over-budget/over-max-entries) → `formatDefault` (`promptFragment` + `promptPosition`) → the read mount's outputMapper lifts it to root as `retrievalEvidence_<id>`. `memoryRecallInjections` then splits ONE recall into one ActiveInjection PER CHUNK — guarded by a byte-equality check (`fragments.join('\n\n') === systemContent`) that falls back to the single injection rather than change the prompt. `rank` (score order) and `promptPosition` (picker order) are DIFFERENT and both load-bearing: joining fragments in rank order reproduces the right bytes in a sequence the model never saw.
156
156
  - **AgentState** → all 8 stages/ files, both builders' mappers, memory-wire STRING-TYPED keys ('runIdentity'/'turnNumber'/… buildAgentChart.ts:177-180 — not refactor-safe), finalizeResult's `reliabilityFail*`/`policyHalt*` reads (rename silently kills the typed errors).
157
- - **events/** → 109 typed events across 24 domains (counts anti-drift-tested against this file — update BOTH when adding events): ALL_EVENT_TYPES exhaustiveness tests, DomainWildcard hand-list, ~42 importers (recorders, strategies, stream, commentary).
157
+ - **events/** → 111 typed events across 24 domains (counts anti-drift-tested against this file — update BOTH when adding events): ALL_EVENT_TYPES exhaustiveness tests, DomainWildcard hand-list, ~42 importers (recorders, strategies, stream, commentary).
158
158
  - **Run-configuration manifest (9.41.0)** → `agentfootprint.agent.run_configured`, the JOIN KEY that turns N runs into N labelled ARMS: one event naming the adapters/strategies in play (provider+model, reactMode, each memory's declared strategy/retrieval/embedder, window, graph posture+classifier, evidence posture, artifacts-present). Composed by the PURE `core/agent/runManifest.ts`, dispatched from `Agent.emitRunManifest()` at the END of `createExecutor` — the ONE funnel `run()` AND `resume()` share, both of which mint a fresh runId. Direct `dispatcher.dispatch` with a STATED pseudo-stage (`run-configured#0`), the `emitToolSessionReport` precedent — there is no stage yet, and `minimalMeta()` would make the one joinable-by-design event unjoinable. TWO laws, both tested: NAMES ONLY (a store is reported PRESENT and unnamed rather than identified by a directory/endpoint — `MemoryStore` and `ArtifactStore` declare no id), and ABSENT means "not configured", never a guessed `'default'`. Graph presence is read off `skillGraphNextSkill`, NOT `skillGraphCascade` (a 9.16-style mount sets no cascade and would read as "no graph"). `MemoryDefinition` gained `strategy`/`retrieval`/`embedderId` for it — declared names the compiled pipeline had closed over, the `store`-in-the-open precedent.
159
159
  - **adapters/types.ts LLMMessage/LLMRequest** → 62 importers: tool_use round-trip (toolCalls.ts:115-135), wire assembly (callLLM.ts:150-160), providers, cache strategies, security/extractSequence, reliability loop.
160
160
  - **Cache** → strategy registration is a MODULE SIDE EFFECT (src/index.ts:15-17); an entry point skipping that import silently falls back to NoOp. Resolved once per Agent at construction (Agent.ts:347).
@@ -252,7 +252,7 @@ const agent = Agent.create({ provider, model })
252
252
  agent.on('agentfootprint.context.evaluated', (e) => console.log(e.payload.activeIds));
253
253
  ```
254
254
 
255
- **109 typed events across 24 domains.** Two subscription shapes and no third:
255
+ **111 typed events across 24 domains.** Two subscription shapes and no third:
256
256
  `'*'` (every event) and `'agentfootprint.<domain>.*'` (one domain). **`'agentfootprint.*'`
257
257
  is not a pattern** — TypeScript rejects it, and at runtime it would match nothing.
258
258
 
@@ -2980,7 +2980,7 @@ class Agent extends RunnerBase_js_1.RunnerBase {
2980
2980
  // auto-attached `present` tool when a store is attached, 9.22.0) +
2981
2981
  // skill-supplied tools (with autoActivate scoping); validates
2982
2982
  // name uniqueness; produces the dispatch map.
2983
- const { registryByName, toolSchemas, toolDeclaringSkills } = (0, buildToolRegistry_js_1.buildToolRegistry)(registry, this.injections, {
2983
+ const { registryByName, toolSchemas, toolDeclaringSkills, toolClaimants } = (0, buildToolRegistry_js_1.buildToolRegistry)(registry, this.injections, {
2984
2984
  hasArtifactStore: artifactStore !== undefined,
2985
2985
  });
2986
2986
  // A statically registered tool that declares `wants` on an agent with no
@@ -3137,13 +3137,25 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3137
3137
  const messagesSubflow = (0, buildMessagesSlot_js_1.buildMessagesSlot)({
3138
3138
  ...(budget?.messages !== undefined && { budgetCap: budget.messages }),
3139
3139
  });
3140
- // Per-run cache shared between buildToolsSlot (writer, each
3141
- // iteration) and buildToolCallsHandler (reader, same iteration).
3142
- // Holds the resolved Tool[] from `provider.list(ctx)` so dispatch
3143
- // doesn't re-invoke `list()` — vital for async network providers.
3144
- // A fresh chart (and thus fresh cache) is built per `agent.run()`,
3145
- // so concurrent runs don't share state.
3140
+ // Cache shared between buildToolsSlot (writer, each iteration) and
3141
+ // buildToolCallsHandler (reader, same iteration). Holds the resolved
3142
+ // Tool[] from `provider.list(ctx)` so dispatch doesn't re-invoke `list()`
3143
+ // — vital for async network providers.
3144
+ //
3145
+ // LIFETIME, stated correctly (9.92.0): the chart is built ONCE, at
3146
+ // construction (`initChart`), and reused by every `run()` and `resume()`
3147
+ // of this Agent — so this cache and the record below outlive a run. They
3148
+ // are safe because one Agent runs one turn at a time and the slot
3149
+ // overwrites them every iteration before dispatch reads them. What that
3150
+ // does NOT cover is a resume in a FRESH instance, whose closure is empty
3151
+ // and whose Compose never re-runs before the resumed dispatch — which is
3152
+ // why the pause paths carry `pausedToolParty` on the checkpoint.
3146
3153
  const providerToolCache = { current: [] };
3154
+ // Who put each name on the wire (9.92.0) — the same closure-shared shape,
3155
+ // written by the tools slot's merge and read by dispatch so the party
3156
+ // whose contract the model read is the party that answers. `lastServed`
3157
+ // is the run's memory of the same fact, for a name that left the wire.
3158
+ const servedTools = { current: new Map(), lastServed: new Map() };
3147
3159
  const readSkillFor = this.readSkillOfferFor();
3148
3160
  // Per-role skill visibility. The RESOLVER is handed to the tools slot, which
3149
3161
  // is the fact's one owner: it resolves the ids once per iteration and
@@ -3225,6 +3237,8 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3225
3237
  integrityLedger: this.integrityLedgerHolder,
3226
3238
  ...(this.externalToolProvider && { toolProvider: this.externalToolProvider }),
3227
3239
  ...(this.externalToolProvider && { providerToolCache }),
3240
+ toolClaimants,
3241
+ servedTools,
3228
3242
  ...(readSkillFor && { readSkillFor }),
3229
3243
  ...(hiddenSkillIds && { hiddenSkillIds }),
3230
3244
  ...(budget?.tools !== undefined && { budgetCap: budget.tools }),
@@ -3397,6 +3411,8 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3397
3411
  }),
3398
3412
  ...(this.externalToolProvider && { externalToolProvider: this.externalToolProvider }),
3399
3413
  ...(this.externalToolProvider && { providerToolCache }),
3414
+ servedTools,
3415
+ toolClaimants,
3400
3416
  ...(permissionChecker && { permissionChecker }),
3401
3417
  ...(credentialProvider && { credentialProvider }),
3402
3418
  // The claim-check store (9.21.0). Absent → not one new line runs in