agentfootprint 9.91.0 → 9.93.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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +236 -0
- package/CLAUDE.md +1 -1
- package/ai-instructions/claude-code/SKILL.md +1 -1
- package/dist/core/Agent.js +38 -7
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/LLMCall.js +6 -4
- package/dist/core/LLMCall.js.map +1 -1
- package/dist/core/agent/buildAgentMessageApiChart.js +10 -0
- package/dist/core/agent/buildAgentMessageApiChart.js.map +1 -1
- package/dist/core/agent/buildToolRegistry.js +90 -33
- package/dist/core/agent/buildToolRegistry.js.map +1 -1
- package/dist/core/agent/messageApiReceipt.js +1 -0
- package/dist/core/agent/messageApiReceipt.js.map +1 -1
- package/dist/core/agent/stages/callLLM.js +18 -7
- package/dist/core/agent/stages/callLLM.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +176 -28
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/agent/stages/window.js +12 -2
- package/dist/core/agent/stages/window.js.map +1 -1
- package/dist/core/agent/window/evictedTurns.js +60 -0
- package/dist/core/agent/window/evictedTurns.js.map +1 -0
- package/dist/core/slots/buildToolsSlot.js +209 -62
- package/dist/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +4 -0
- package/dist/esm/core/Agent.js +39 -8
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/LLMCall.js +6 -4
- package/dist/esm/core/LLMCall.js.map +1 -1
- package/dist/esm/core/agent/buildAgentMessageApiChart.js +10 -0
- package/dist/esm/core/agent/buildAgentMessageApiChart.js.map +1 -1
- package/dist/esm/core/agent/buildToolRegistry.d.ts +80 -31
- package/dist/esm/core/agent/buildToolRegistry.js +90 -33
- package/dist/esm/core/agent/buildToolRegistry.js.map +1 -1
- package/dist/esm/core/agent/messageApiReceipt.d.ts +3 -1
- package/dist/esm/core/agent/messageApiReceipt.js +1 -0
- package/dist/esm/core/agent/messageApiReceipt.js.map +1 -1
- package/dist/esm/core/agent/stages/callLLM.d.ts +9 -0
- package/dist/esm/core/agent/stages/callLLM.js +18 -7
- package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +43 -5
- package/dist/esm/core/agent/stages/toolCalls.js +174 -27
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/stages/window.d.ts +8 -0
- package/dist/esm/core/agent/stages/window.js +12 -2
- package/dist/esm/core/agent/stages/window.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +15 -0
- package/dist/esm/core/agent/window/evictedTurns.d.ts +52 -0
- package/dist/esm/core/agent/window/evictedTurns.js +56 -0
- package/dist/esm/core/agent/window/evictedTurns.js.map +1 -0
- package/dist/esm/core/slots/buildToolsSlot.d.ts +100 -0
- package/dist/esm/core/slots/buildToolsSlot.js +207 -61
- package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +92 -18
- package/dist/esm/events/registry.d.ts +5 -1
- package/dist/esm/events/registry.js +4 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/lib/time-travel/receipt.d.ts +71 -28
- package/dist/esm/lib/time-travel/receipt.js +33 -17
- package/dist/esm/lib/time-travel/receipt.js.map +1 -1
- package/dist/esm/lib/time-travel/servedView.d.ts +31 -22
- package/dist/esm/lib/time-travel/servedView.js +78 -43
- package/dist/esm/lib/time-travel/servedView.js.map +1 -1
- package/dist/events/registry.js +4 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/lib/time-travel/receipt.js +33 -17
- package/dist/lib/time-travel/receipt.js.map +1 -1
- package/dist/lib/time-travel/servedView.js +78 -43
- package/dist/lib/time-travel/servedView.js.map +1 -1
- package/dist/types/core/Agent.d.ts +4 -0
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/LLMCall.d.ts.map +1 -1
- package/dist/types/core/agent/buildAgentMessageApiChart.d.ts.map +1 -1
- package/dist/types/core/agent/buildToolRegistry.d.ts +80 -31
- package/dist/types/core/agent/buildToolRegistry.d.ts.map +1 -1
- package/dist/types/core/agent/messageApiReceipt.d.ts +3 -1
- package/dist/types/core/agent/messageApiReceipt.d.ts.map +1 -1
- package/dist/types/core/agent/stages/callLLM.d.ts +9 -0
- package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +43 -5
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/stages/window.d.ts +8 -0
- package/dist/types/core/agent/stages/window.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +15 -0
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/agent/window/evictedTurns.d.ts +53 -0
- package/dist/types/core/agent/window/evictedTurns.d.ts.map +1 -0
- package/dist/types/core/slots/buildToolsSlot.d.ts +100 -0
- package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +92 -18
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +5 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/lib/time-travel/receipt.d.ts +71 -28
- package/dist/types/lib/time-travel/receipt.d.ts.map +1 -1
- package/dist/types/lib/time-travel/servedView.d.ts +31 -22
- package/dist/types/lib/time-travel/servedView.d.ts.map +1 -1
- 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 —
|
|
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,242 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [9.93.0] - 2026-09-11
|
|
11
|
+
|
|
12
|
+
**The receipt says which strategy, and what the window dropped.** Four of the
|
|
13
|
+
seven standing recorded-not-built entries close, one is verified rather than
|
|
14
|
+
rebuilt, and one is assessed and half-built. Every item below is a behaviour
|
|
15
|
+
change to a shipped record — a new receipt key on every minting chart, a gap
|
|
16
|
+
that stops firing on three of them, a field that starts being written — which
|
|
17
|
+
is why this is a minor and not a patch. Nothing that had no name collision, no
|
|
18
|
+
window and no cache strategy records a different byte except the one new key.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **`Receipt.cache.strategy: string | null`** — WHICH cache strategy the
|
|
23
|
+
request went through (its registry `providerName`; `'*'` is the built-in
|
|
24
|
+
pass-through every agent runs), or `null` where none stood between assembly
|
|
25
|
+
and the port. WHY: `cache.transform: 'unchanged'` is the honest verdict both
|
|
26
|
+
when a strategy returned what it was given and when there was no strategy at
|
|
27
|
+
all, so the served view had no way to know a rewrite was impossible and
|
|
28
|
+
raised `cache-transform` on every view — including `LLMCall` and the two
|
|
29
|
+
message-API charts, where a reader was told what a cache strategy may have
|
|
30
|
+
done beside a call none could touch (entry 8). No fourth `transform` value
|
|
31
|
+
was added; the fact is its own field, minted by the one owner
|
|
32
|
+
(`buildReceipt` refuses to mint without it). A receipt from before this
|
|
33
|
+
release has no key, and a reader treats that as "cannot say", never as
|
|
34
|
+
`null`.
|
|
35
|
+
```ts
|
|
36
|
+
receiptAt(agent.getSnapshot()!, 1)!.cache.strategy; // '*' — an agent on the mock provider
|
|
37
|
+
receiptAt(llmCall.getSnapshot()!, 1)!.cache.strategy; // null — nothing between assembly and the port
|
|
38
|
+
```
|
|
39
|
+
- **`Receipt.omittedForAttention` is written** — by the agent chart's window
|
|
40
|
+
stage, for every turn it evicts for budget at an iteration's head. WHY: the
|
|
41
|
+
field was declared in 9.88.0 for "why did the model not know that?" and
|
|
42
|
+
excused as "no chart in this library supplies it" (entry 9) — a measurement
|
|
43
|
+
that had looked at the slots, which drop nothing, and not at the window,
|
|
44
|
+
which does (`context.evicted`, `reason: 'budget'`), and never wrote it. The
|
|
45
|
+
window hands what left to the call-llm mint on an in-memory handle
|
|
46
|
+
(`window/evictedTurns.ts`), the seam the compaction meter already crosses —
|
|
47
|
+
never a scope read, which on every windowless run would be a tracked read of
|
|
48
|
+
an absent key and a phantom context source per loop. Each hash is the
|
|
49
|
+
evicted turn's own `messages.entries[].hash`, so a drop on epoch k's receipt
|
|
50
|
+
pairs with the turn as an earlier receipt served it — the pairing law,
|
|
51
|
+
driven on a real sliding window. Absent now means nothing was dropped before
|
|
52
|
+
that call. The field left `UNGAPPED_FIELDS` and is named by
|
|
53
|
+
`no-receipt-on-chart`, the one gap that can lose it. An agent without a
|
|
54
|
+
window hands both stages the deps they always had.
|
|
55
|
+
```ts
|
|
56
|
+
const agent = Agent.create({ provider, model }).system('bot').tool(lookup)
|
|
57
|
+
.window(slidingWindow({ keepRecentTurns: 1 })).build();
|
|
58
|
+
await agent.run({ message: 'go' });
|
|
59
|
+
receiptAt(agent.getSnapshot()!, 3)!.omittedForAttention; // { count: 2, hashes: [...] } — the pair that left at this head
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
|
|
64
|
+
- **`cache-transform` is raised only where a rewrite was possible** — where
|
|
65
|
+
the receipt names a strategy, or where no receipt can say (a receipt-less
|
|
66
|
+
view, a pre-9.93.0 receipt, a refused shape). WHY: entry 8 — a sentence
|
|
67
|
+
about what a strategy may have done was printed where none could run. Only a
|
|
68
|
+
receipt that SAYS `null` lifts it; absence still raises, so this is not the
|
|
69
|
+
inference-from-absence the entry refused. Measured: `LLMCall` and both
|
|
70
|
+
message-API charts with a run id now read `['provider-defaults']`; the
|
|
71
|
+
receipt-less message-API view still reads
|
|
72
|
+
`['no-receipt-on-chart', 'cache-transform']`, honestly. The printed sentence
|
|
73
|
+
is unchanged and every clause of it still holds where it prints.
|
|
74
|
+
```ts
|
|
75
|
+
servedAt(llmCall.getSnapshot()!, 1)!.gaps.map((g) => g.gap); // ['provider-defaults'] (was ['cache-transform', 'provider-defaults'])
|
|
76
|
+
```
|
|
77
|
+
- **`cache-transform` names `tools.forced`, `tools.withheld` and
|
|
78
|
+
`cache.strategy`** — fourteen fields, the whole request the strategy holds
|
|
79
|
+
(`params` alone stays off, read past it). WHY: entry 7 — both `tools`
|
|
80
|
+
fields are written from assembly's own decision and never from
|
|
81
|
+
`preparedRequest`, so a strategy that drops the forced answer tool from
|
|
82
|
+
`request.tools` leaves a receipt whose `tools.forced` names a tool the port
|
|
83
|
+
never carried, and the gap did not warn. Driven: an unforcing strategy on a
|
|
84
|
+
`'tool-forced'` agent — wire has no tool and no forcing, receipt says
|
|
85
|
+
`tools.forced: 'respond_with_schema'` and `params.toolChoice` absent, the
|
|
86
|
+
gap names the half that describes the decision. Reading them off the
|
|
87
|
+
prepared request instead was NOT done: that would describe a different fact
|
|
88
|
+
under the same name.
|
|
89
|
+
- **The receipt-conformance law checks the cache verdict outright where no
|
|
90
|
+
strategy ran** — `transform: 'unchanged'`, `transformHash: null`,
|
|
91
|
+
`markersApplied: []`, and no `cache-transform` excuse; where one ran, the gap
|
|
92
|
+
must excuse the unrebuildable fields as before.
|
|
93
|
+
- **Byte-identity references regenerated** (`test/core/tools/reference/`),
|
|
94
|
+
with the whole delta against the 9.92.1 set on record in the test's header:
|
|
95
|
+
the new `cache.strategy` key on every minting fixture, the gap's longer
|
|
96
|
+
field list on every agent view, and the gap leaving the three no-strategy
|
|
97
|
+
views. No message, tool or other key moved on any fixture.
|
|
98
|
+
|
|
99
|
+
### Verified
|
|
100
|
+
|
|
101
|
+
- **Recorded-not-built entries 4 and 5 hold under the 9.92.0 law** and are
|
|
102
|
+
marked built (verified 2026-09-11 against the 9.92.1 `dist`, before any edit
|
|
103
|
+
of this release). `.selfExplain()`'s `run_overview` against an always-visible
|
|
104
|
+
skill, a never-activated scoped skill and a stepped skill: dispatch follows
|
|
105
|
+
the offer on every epoch (the never-activated skill no longer answers the
|
|
106
|
+
framework's own contract — the framework does), `tools.shadowed` names the
|
|
107
|
+
wire's party and never `provider(skill-scoped:self-explain)`, and the losing
|
|
108
|
+
claim is named by `tools.claim_swallowed` on every epoch it lost. The
|
|
109
|
+
reservation this entry names is deliberately untouched; the measured output
|
|
110
|
+
is pasted on the entries and pinned as `offer-and-answer.test.ts` §7.
|
|
111
|
+
|
|
112
|
+
### Assessed
|
|
113
|
+
|
|
114
|
+
- **Entry 10 ("an assertion can be weaker than its clause") is split, and the
|
|
115
|
+
mechanical half is built.** Every clause in `gap-sentences.test.ts` declares
|
|
116
|
+
the fields it is about; a seam hands its assertion a view or receipt with
|
|
117
|
+
one declared field altered, then removed; a contract requires the assertion
|
|
118
|
+
to fail under at least one. The first run found one insensitivity and it is
|
|
119
|
+
fixed. What remains is the quantifier half — a clause about "every field" or
|
|
120
|
+
"both chart shapes" asserted on fewer runs — which needs per-clause hand
|
|
121
|
+
work and is named on the entry rather than faked by a mechanism.
|
|
122
|
+
|
|
123
|
+
## [9.92.1] - 2026-09-11
|
|
124
|
+
|
|
125
|
+
### Fixed
|
|
126
|
+
|
|
127
|
+
- **9.92.0 never reached npm; this release is the same code.** Its publish job
|
|
128
|
+
failed the docs site's deferred-demo gzip ceiling by 0.4 KB (421.0 KB against
|
|
129
|
+
420.6 KB): the interactive demo bundles the library itself, and 9.92.0's
|
|
130
|
+
tool-resolution family rides the same main-entry graph as the receipt family
|
|
131
|
+
did in 9.88.0. The ceiling is re-baselined ~2% over the measurement with the
|
|
132
|
+
reasoning beside the number, and the docs-truth report regenerated. No
|
|
133
|
+
library code changes between 9.92.0 and 9.92.1. This is the fourth raise of
|
|
134
|
+
that ceiling for a family the demo never calls; the dynamic-import fix named
|
|
135
|
+
in 9.61.0 is the next move rather than a fifth raise.
|
|
136
|
+
|
|
137
|
+
## [9.92.0] - 2026-09-11
|
|
138
|
+
|
|
139
|
+
**The offer and the answer are one party.** At every LLM call the model is
|
|
140
|
+
OFFERED a list of tool contracts (the wire; the receipt hashes each one), and
|
|
141
|
+
when it calls a name something ANSWERS. Until this release those two halves
|
|
142
|
+
could come from different parties for one name: the tools slot merges
|
|
143
|
+
`[static, provider, skill, step]` first-occurrence-wins, while dispatch
|
|
144
|
+
consulted the build-time registry first — so a provider's contract on the wire
|
|
145
|
+
was answered by a skill's `execute` (an INACTIVE skill's, even), a provider's
|
|
146
|
+
`skip_step` by the framework's (which then advanced the procedure on a call the
|
|
147
|
+
model made against somebody else's contract), and the report that exists for
|
|
148
|
+
this seam walked `activeInjections` and named the provider on epochs whose wire
|
|
149
|
+
carried the skill's contract. A claimant that lost both the wire and the
|
|
150
|
+
dispatch was simply dead, 22 rows of it in the divergence walk's baseline, with
|
|
151
|
+
nothing on the record. `docs/design/2026-09-the-offer-and-the-answer.md` states
|
|
152
|
+
the law; recorded-not-built entries 1, 2 and 3, the `claim-swallowed` bullet
|
|
153
|
+
under entry 1 and the 9.91.0 tools-slot follow-up are marked built.
|
|
154
|
+
|
|
155
|
+
**The law:** for every tool name on a call, exactly one party owns the OFFER and
|
|
156
|
+
the same party owns the ANSWER — or the record names the disagreement. Three
|
|
157
|
+
consequences, one example each:
|
|
158
|
+
|
|
159
|
+
- **Dispatch follows the offer.** `buildToolsSlot.ts` · `mergeWire` is the one
|
|
160
|
+
pass that produces the wire AND the record of who won each name
|
|
161
|
+
(`ServedToolParties`, closure-shared like `ProviderToolCache`, never scope
|
|
162
|
+
state); `toolCalls.ts` · `lookupTool` reads it first. A provider and a
|
|
163
|
+
never-activated scoped skill both claiming `shared_tool`: the wire carries
|
|
164
|
+
the provider's contract, the provider's tool answers, and the skill's
|
|
165
|
+
`execute` answers no call while the provider holds the name — nor after the
|
|
166
|
+
provider withdraws it, because the skill's contract was never what the
|
|
167
|
+
model read: that call is REFUSED as a recorded tool result (`toolCalls.ts`
|
|
168
|
+
· `notServedResult`). A name NOT on this epoch's wire — a held-out step
|
|
169
|
+
tool, a parked map's tool, a scoped tool named from a restored transcript —
|
|
170
|
+
still dispatches (the capability law's held-out clause, `epoch-laws.test.ts`
|
|
171
|
+
1(a)–(e)), but only to the party the model LAST read the name under
|
|
172
|
+
(`ServedToolParties.lastServed`) or the name's only holder when it was never
|
|
173
|
+
served; every such dispatch is on the record as the new
|
|
174
|
+
`agentfootprint.tools.answered_off_wire` (`{ toolName, toolCallId,
|
|
175
|
+
iteration, answeredBy, answeredById? }`). A pause that re-dispatches on
|
|
176
|
+
resume (a middleware ask, a check-in, a credential consent) carries the
|
|
177
|
+
served party on its checkpoint (`pausedToolParty`, pause-path-only), so a
|
|
178
|
+
resume in a FRESH Agent instance — empty closure, no Compose — cannot fall
|
|
179
|
+
back to the build-time map's first holder either.
|
|
180
|
+
- **The report's subject is the wire.** `agentfootprint.tools.shadowed` now
|
|
181
|
+
fires once per contested name per iteration when two contracts COMPETED for
|
|
182
|
+
the wire, and `schemaFrom`/`dispatchTo` both name the wire's party — they
|
|
183
|
+
agree by construction. A stepped skill and a provider sharing a name draw
|
|
184
|
+
`skill 'desk-stepped'` in both halves on every epoch, never `'provider'`.
|
|
185
|
+
The vocabulary gained `'framework'` (`ToolNameChannel`), so an auto-attach
|
|
186
|
+
is named as itself.
|
|
187
|
+
- **A dead claim is reported.** New `agentfootprint.tools.claim_swallowed`
|
|
188
|
+
(`ToolsClaimSwallowedPayload`: `{ toolName, iteration, lostBy, lostById?,
|
|
189
|
+
wonBy, wonById? }`, names only) fires once per iteration for every party
|
|
190
|
+
whose claim to a name is held by somebody else — whether its contract
|
|
191
|
+
competed (an active skill against a provider) or never reached the merge
|
|
192
|
+
(the same skill while inactive; a provider tool whose name a static
|
|
193
|
+
`.tool()` owns; the framework's `skip_step` between tenures). Identity is by
|
|
194
|
+
IMPLEMENTATION: two skills sharing one `Tool` reference (documented-legal)
|
|
195
|
+
are one claim and draw nothing. Now 111 typed events across 24 domains.
|
|
196
|
+
|
|
197
|
+
**`skip_step` is a claimant like any other** (`buildToolRegistry.ts` ·
|
|
198
|
+
`toolClaimants`, the registry's new list of every build-time claimant per
|
|
199
|
+
name — a list, not a winner). Its schema still merges last, so it rides the
|
|
200
|
+
wire only when nobody else put the name forward; when a provider serves
|
|
201
|
+
`skip_step`, the provider's tool answers and the step bookkeeping keys on the
|
|
202
|
+
framework's own instance having answered (`toolCalls.ts` ·
|
|
203
|
+
`frameworkSkipStepAnswered`) — the procedure does not advance. The build-time
|
|
204
|
+
refusals (a static `.tool()` or a skill tool named `skip_step`/`present`) stand.
|
|
205
|
+
|
|
206
|
+
**`buildAgentMessageApiChart` hands the model the declared tool set on every
|
|
207
|
+
turn.** Its tools-slot mount now says `arrayMerge: ArrayMergeMode.Replace`, as
|
|
208
|
+
the agent charts always did; turn 2 no longer serves `['weather','weather']`.
|
|
209
|
+
|
|
210
|
+
**Byte-identity for every run with no name collision.**
|
|
211
|
+
`test/core/tools/byte-identity.test.ts` drives fifteen collision-free shapes
|
|
212
|
+
(both agent react modes, a skill-graph hop, a stepped skill, a parked map, the
|
|
213
|
+
wrap-up, a tool-forced output, three provider shapes, two skills sharing one
|
|
214
|
+
`Tool` reference, `LLMCall`, both message-API charts) and compares `commitLog`
|
|
215
|
+
and `servedAt(k)` against references under `test/core/tools/reference/`
|
|
216
|
+
generated on the 9.91.0 tree. All fifteen are identical; the
|
|
217
|
+
receipt-conformance suite is unchanged and green on all four chart shapes.
|
|
218
|
+
|
|
219
|
+
**The divergence walk, re-recorded — and widened.** Every `contract-swap`
|
|
220
|
+
row (12) and every `report-misattributed` row (10) is GONE and ratcheted so
|
|
221
|
+
it cannot reopen quietly; the `claim-swallowed` family is 35 rows, every one
|
|
222
|
+
carrying what `tools.claim_swallowed` said about it. The walk gained the
|
|
223
|
+
cross-epoch × collision cases it never had (walk D: a provider withdrawing a
|
|
224
|
+
name an inactive skill also holds; a fresh-instance resume of a provider's
|
|
225
|
+
call) and a kind for the held-out dispatch the law keeps, `answered-off-wire`
|
|
226
|
+
(4 rows, each carrying the event). The harness moved to
|
|
227
|
+
`test/core/agent/toolDivergenceWalk.harness.ts` so the reproductions in
|
|
228
|
+
`test/core/tools/offer-and-answer.test.ts` — the entries' own, verbatim, each
|
|
229
|
+
red on 9.91.0, plus the review's own shapes — drive the same configurations.
|
|
230
|
+
|
|
231
|
+
**Consumer-facing changes, stated plainly.** (1) A provider tool that shares a
|
|
232
|
+
name with a skill tool now RUNS where the skill's used to — the contract the
|
|
233
|
+
model read is the one that answers. (2) `tools.shadowed` fires on
|
|
234
|
+
configurations that were silent (a provider against a static `.tool()` or a
|
|
235
|
+
framework auto-attach) and no longer names `'provider'` as `schemaFrom` where
|
|
236
|
+
the wire carried the skill's contract; `dispatchTo` now equals `schemaFrom`.
|
|
237
|
+
(3) Two new events, `tools.claim_swallowed` and `tools.answered_off_wire`.
|
|
238
|
+
(4) A call to a name that left the wire, whose last-served party can no longer
|
|
239
|
+
answer, is refused with a recorded result where it used to be answered by
|
|
240
|
+
whichever party the build-time map held first; the same sentence answers a
|
|
241
|
+
provider-served call resumed in a fresh instance (where it used to read
|
|
242
|
+
`Unknown tool`). (5) Two stale comments in `buildToolsSlot.ts` and
|
|
243
|
+
`buildToolRegistry.ts` corrected in the same diff; `Agent.ts`'s "fresh chart
|
|
244
|
+
per run()" comment corrected — the chart is built once at construction.
|
|
245
|
+
|
|
10
246
|
## [9.91.0] - 2026-09-10
|
|
11
247
|
|
|
12
248
|
**Every chart that serves a model now mints a receipt.** A receipt is the proof
|
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/** →
|
|
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
|
-
**
|
|
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
|
|
package/dist/core/Agent.js
CHANGED
|
@@ -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)(),
|
|
@@ -2980,7 +2989,7 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
2980
2989
|
// auto-attached `present` tool when a store is attached, 9.22.0) +
|
|
2981
2990
|
// skill-supplied tools (with autoActivate scoping); validates
|
|
2982
2991
|
// name uniqueness; produces the dispatch map.
|
|
2983
|
-
const { registryByName, toolSchemas, toolDeclaringSkills } = (0, buildToolRegistry_js_1.buildToolRegistry)(registry, this.injections, {
|
|
2992
|
+
const { registryByName, toolSchemas, toolDeclaringSkills, toolClaimants } = (0, buildToolRegistry_js_1.buildToolRegistry)(registry, this.injections, {
|
|
2984
2993
|
hasArtifactStore: artifactStore !== undefined,
|
|
2985
2994
|
});
|
|
2986
2995
|
// A statically registered tool that declares `wants` on an agent with no
|
|
@@ -3137,13 +3146,25 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
3137
3146
|
const messagesSubflow = (0, buildMessagesSlot_js_1.buildMessagesSlot)({
|
|
3138
3147
|
...(budget?.messages !== undefined && { budgetCap: budget.messages }),
|
|
3139
3148
|
});
|
|
3140
|
-
//
|
|
3141
|
-
//
|
|
3142
|
-
//
|
|
3143
|
-
//
|
|
3144
|
-
//
|
|
3145
|
-
//
|
|
3149
|
+
// Cache shared between buildToolsSlot (writer, each iteration) and
|
|
3150
|
+
// buildToolCallsHandler (reader, same iteration). Holds the resolved
|
|
3151
|
+
// Tool[] from `provider.list(ctx)` so dispatch doesn't re-invoke `list()`
|
|
3152
|
+
// — vital for async network providers.
|
|
3153
|
+
//
|
|
3154
|
+
// LIFETIME, stated correctly (9.92.0): the chart is built ONCE, at
|
|
3155
|
+
// construction (`initChart`), and reused by every `run()` and `resume()`
|
|
3156
|
+
// of this Agent — so this cache and the record below outlive a run. They
|
|
3157
|
+
// are safe because one Agent runs one turn at a time and the slot
|
|
3158
|
+
// overwrites them every iteration before dispatch reads them. What that
|
|
3159
|
+
// does NOT cover is a resume in a FRESH instance, whose closure is empty
|
|
3160
|
+
// and whose Compose never re-runs before the resumed dispatch — which is
|
|
3161
|
+
// why the pause paths carry `pausedToolParty` on the checkpoint.
|
|
3146
3162
|
const providerToolCache = { current: [] };
|
|
3163
|
+
// Who put each name on the wire (9.92.0) — the same closure-shared shape,
|
|
3164
|
+
// written by the tools slot's merge and read by dispatch so the party
|
|
3165
|
+
// whose contract the model read is the party that answers. `lastServed`
|
|
3166
|
+
// is the run's memory of the same fact, for a name that left the wire.
|
|
3167
|
+
const servedTools = { current: new Map(), lastServed: new Map() };
|
|
3147
3168
|
const readSkillFor = this.readSkillOfferFor();
|
|
3148
3169
|
// Per-role skill visibility. The RESOLVER is handed to the tools slot, which
|
|
3149
3170
|
// is the fact's one owner: it resolves the ids once per iteration and
|
|
@@ -3225,6 +3246,8 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
3225
3246
|
integrityLedger: this.integrityLedgerHolder,
|
|
3226
3247
|
...(this.externalToolProvider && { toolProvider: this.externalToolProvider }),
|
|
3227
3248
|
...(this.externalToolProvider && { providerToolCache }),
|
|
3249
|
+
toolClaimants,
|
|
3250
|
+
servedTools,
|
|
3228
3251
|
...(readSkillFor && { readSkillFor }),
|
|
3229
3252
|
...(hiddenSkillIds && { hiddenSkillIds }),
|
|
3230
3253
|
...(budget?.tools !== undefined && { budgetCap: budget.tools }),
|
|
@@ -3239,6 +3262,9 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
3239
3262
|
// …and its off switch. Value-conditional, so an agent on the default
|
|
3240
3263
|
// hands `callLLM` exactly the dep bag it always did.
|
|
3241
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 }),
|
|
3242
3268
|
provider,
|
|
3243
3269
|
model,
|
|
3244
3270
|
...(temperature !== undefined && { temperature }),
|
|
@@ -3313,6 +3339,9 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
3313
3339
|
run: (0, window_js_1.buildWindowStage)({
|
|
3314
3340
|
strategy: this.windowStrategy,
|
|
3315
3341
|
meter: this.compactionMeterHandle,
|
|
3342
|
+
...(this.evictedTurnsHandle !== undefined && {
|
|
3343
|
+
evictedTurns: this.evictedTurnsHandle,
|
|
3344
|
+
}),
|
|
3316
3345
|
agentModel: model,
|
|
3317
3346
|
providerName: provider.name,
|
|
3318
3347
|
getRunId: () => this.currentRunContext?.runId,
|
|
@@ -3397,6 +3426,8 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
3397
3426
|
}),
|
|
3398
3427
|
...(this.externalToolProvider && { externalToolProvider: this.externalToolProvider }),
|
|
3399
3428
|
...(this.externalToolProvider && { providerToolCache }),
|
|
3429
|
+
servedTools,
|
|
3430
|
+
toolClaimants,
|
|
3400
3431
|
...(permissionChecker && { permissionChecker }),
|
|
3401
3432
|
...(credentialProvider && { credentialProvider }),
|
|
3402
3433
|
// The claim-check store (9.21.0). Absent → not one new line runs in
|