agentfootprint 9.99.0 → 9.101.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 (140) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +171 -0
  3. package/CLAUDE.md +1 -1
  4. package/README.md +1 -0
  5. package/ai-instructions/claude-code/SKILL.md +1 -1
  6. package/dist/artifacts/payload.js +5 -1
  7. package/dist/artifacts/payload.js.map +1 -1
  8. package/dist/core/Agent.js +123 -2
  9. package/dist/core/Agent.js.map +1 -1
  10. package/dist/core/agent/AgentBuilder.js +113 -2
  11. package/dist/core/agent/AgentBuilder.js.map +1 -1
  12. package/dist/core/agent/buildDynamicAgentChart.js +11 -0
  13. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
  14. package/dist/core/agent/buildToolRegistry.js +38 -0
  15. package/dist/core/agent/buildToolRegistry.js.map +1 -1
  16. package/dist/core/agent/findings/ledger.js +237 -0
  17. package/dist/core/agent/findings/ledger.js.map +1 -0
  18. package/dist/core/agent/findings/reserved.js +334 -0
  19. package/dist/core/agent/findings/reserved.js.map +1 -0
  20. package/dist/core/agent/findings/serve.js +388 -0
  21. package/dist/core/agent/findings/serve.js.map +1 -0
  22. package/dist/core/agent/findings/types.js +47 -0
  23. package/dist/core/agent/findings/types.js.map +1 -0
  24. package/dist/core/agent/stages/callLLM.js +78 -10
  25. package/dist/core/agent/stages/callLLM.js.map +1 -1
  26. package/dist/core/agent/stages/outputRetry.js +5 -0
  27. package/dist/core/agent/stages/outputRetry.js.map +1 -1
  28. package/dist/core/agent/stages/route.js +84 -12
  29. package/dist/core/agent/stages/route.js.map +1 -1
  30. package/dist/core/agent/stages/seed.js +28 -1
  31. package/dist/core/agent/stages/seed.js.map +1 -1
  32. package/dist/core/agent/stages/toolCalls.js +78 -11
  33. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  34. package/dist/core/runCheckpoint.js +64 -1
  35. package/dist/core/runCheckpoint.js.map +1 -1
  36. package/dist/core/slots/buildToolsSlot.js +13 -1
  37. package/dist/core/slots/buildToolsSlot.js.map +1 -1
  38. package/dist/esm/artifacts/payload.js +5 -1
  39. package/dist/esm/artifacts/payload.js.map +1 -1
  40. package/dist/esm/core/Agent.d.ts +45 -0
  41. package/dist/esm/core/Agent.js +123 -2
  42. package/dist/esm/core/Agent.js.map +1 -1
  43. package/dist/esm/core/agent/AgentBuilder.d.ts +60 -0
  44. package/dist/esm/core/agent/AgentBuilder.js +113 -2
  45. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  46. package/dist/esm/core/agent/buildDynamicAgentChart.js +11 -0
  47. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
  48. package/dist/esm/core/agent/buildToolRegistry.d.ts +7 -0
  49. package/dist/esm/core/agent/buildToolRegistry.js +38 -0
  50. package/dist/esm/core/agent/buildToolRegistry.js.map +1 -1
  51. package/dist/esm/core/agent/findings/ledger.d.ts +95 -0
  52. package/dist/esm/core/agent/findings/ledger.js +230 -0
  53. package/dist/esm/core/agent/findings/ledger.js.map +1 -0
  54. package/dist/esm/core/agent/findings/reserved.d.ts +95 -0
  55. package/dist/esm/core/agent/findings/reserved.js +326 -0
  56. package/dist/esm/core/agent/findings/reserved.js.map +1 -0
  57. package/dist/esm/core/agent/findings/serve.d.ts +136 -0
  58. package/dist/esm/core/agent/findings/serve.js +381 -0
  59. package/dist/esm/core/agent/findings/serve.js.map +1 -0
  60. package/dist/esm/core/agent/findings/types.d.ts +145 -0
  61. package/dist/esm/core/agent/findings/types.js +44 -0
  62. package/dist/esm/core/agent/findings/types.js.map +1 -0
  63. package/dist/esm/core/agent/stages/callLLM.d.ts +32 -0
  64. package/dist/esm/core/agent/stages/callLLM.js +78 -10
  65. package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
  66. package/dist/esm/core/agent/stages/outputRetry.js +5 -0
  67. package/dist/esm/core/agent/stages/outputRetry.js.map +1 -1
  68. package/dist/esm/core/agent/stages/route.d.ts +8 -1
  69. package/dist/esm/core/agent/stages/route.js +84 -12
  70. package/dist/esm/core/agent/stages/route.js.map +1 -1
  71. package/dist/esm/core/agent/stages/seed.d.ts +28 -0
  72. package/dist/esm/core/agent/stages/seed.js +28 -1
  73. package/dist/esm/core/agent/stages/seed.js.map +1 -1
  74. package/dist/esm/core/agent/stages/toolCalls.d.ts +17 -0
  75. package/dist/esm/core/agent/stages/toolCalls.js +76 -9
  76. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  77. package/dist/esm/core/agent/types.d.ts +88 -0
  78. package/dist/esm/core/runCheckpoint.d.ts +27 -1
  79. package/dist/esm/core/runCheckpoint.js +64 -1
  80. package/dist/esm/core/runCheckpoint.js.map +1 -1
  81. package/dist/esm/core/slots/buildToolsSlot.d.ts +9 -0
  82. package/dist/esm/core/slots/buildToolsSlot.js +13 -1
  83. package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
  84. package/dist/esm/events/payloads.d.ts +59 -0
  85. package/dist/esm/events/registry.d.ts +7 -1
  86. package/dist/esm/events/registry.js +6 -0
  87. package/dist/esm/events/registry.js.map +1 -1
  88. package/dist/esm/events/types.d.ts +3 -1
  89. package/dist/esm/index.d.ts +1 -0
  90. package/dist/esm/index.js +6 -0
  91. package/dist/esm/index.js.map +1 -1
  92. package/dist/esm/lib/time-travel/servedView.js +54 -10
  93. package/dist/esm/lib/time-travel/servedView.js.map +1 -1
  94. package/dist/events/registry.js +6 -0
  95. package/dist/events/registry.js.map +1 -1
  96. package/dist/index.js +11 -4
  97. package/dist/index.js.map +1 -1
  98. package/dist/lib/time-travel/servedView.js +54 -10
  99. package/dist/lib/time-travel/servedView.js.map +1 -1
  100. package/dist/types/artifacts/payload.d.ts.map +1 -1
  101. package/dist/types/core/Agent.d.ts +45 -0
  102. package/dist/types/core/Agent.d.ts.map +1 -1
  103. package/dist/types/core/agent/AgentBuilder.d.ts +60 -0
  104. package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
  105. package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
  106. package/dist/types/core/agent/buildToolRegistry.d.ts +7 -0
  107. package/dist/types/core/agent/buildToolRegistry.d.ts.map +1 -1
  108. package/dist/types/core/agent/findings/ledger.d.ts +96 -0
  109. package/dist/types/core/agent/findings/ledger.d.ts.map +1 -0
  110. package/dist/types/core/agent/findings/reserved.d.ts +96 -0
  111. package/dist/types/core/agent/findings/reserved.d.ts.map +1 -0
  112. package/dist/types/core/agent/findings/serve.d.ts +137 -0
  113. package/dist/types/core/agent/findings/serve.d.ts.map +1 -0
  114. package/dist/types/core/agent/findings/types.d.ts +146 -0
  115. package/dist/types/core/agent/findings/types.d.ts.map +1 -0
  116. package/dist/types/core/agent/stages/callLLM.d.ts +32 -0
  117. package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
  118. package/dist/types/core/agent/stages/outputRetry.d.ts.map +1 -1
  119. package/dist/types/core/agent/stages/route.d.ts +8 -1
  120. package/dist/types/core/agent/stages/route.d.ts.map +1 -1
  121. package/dist/types/core/agent/stages/seed.d.ts +28 -0
  122. package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
  123. package/dist/types/core/agent/stages/toolCalls.d.ts +17 -0
  124. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  125. package/dist/types/core/agent/types.d.ts +88 -0
  126. package/dist/types/core/agent/types.d.ts.map +1 -1
  127. package/dist/types/core/runCheckpoint.d.ts +27 -1
  128. package/dist/types/core/runCheckpoint.d.ts.map +1 -1
  129. package/dist/types/core/slots/buildToolsSlot.d.ts +9 -0
  130. package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
  131. package/dist/types/events/payloads.d.ts +59 -0
  132. package/dist/types/events/payloads.d.ts.map +1 -1
  133. package/dist/types/events/registry.d.ts +7 -1
  134. package/dist/types/events/registry.d.ts.map +1 -1
  135. package/dist/types/events/types.d.ts +3 -1
  136. package/dist/types/events/types.d.ts.map +1 -1
  137. package/dist/types/index.d.ts +1 -0
  138. package/dist/types/index.d.ts.map +1 -1
  139. package/dist/types/lib/time-travel/servedView.d.ts.map +1 -1
  140. package/package.json +5 -3
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 — 111 typed events across 24 domains
547
+ ### Observability — 113 typed events across 25 domains
548
548
 
549
549
  ```typescript
550
550
  agent.on('agentfootprint.context.injected', (e) =>
package/CHANGELOG.md CHANGED
@@ -5,6 +5,177 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [9.101.1] - 2026-09-16
9
+
10
+ ### Fixed — the docs site's export-file ceiling
11
+
12
+ - 9.101.0 never reached npm: its publish workflow stopped at the docs site's
13
+ performance budget (`docs-next/scripts/check-site-budget.mjs`), where the
14
+ generated API-reference routes for the findings ledger's new exports took the
15
+ static export past the file-count ceiling. The ceiling is raised to the
16
+ measured count plus the same thin headroom; no byte ceiling moved. The library
17
+ is byte-identical to 9.101.0; the entry below is what ships.
18
+
19
+ ## [9.101.0] - 2026-09-16
20
+
21
+ ### Added — the model's own findings, on the record, at zero extra calls
22
+
23
+ - `.findings()` on the agent builder (`AgentBuilder.findings`) — every
24
+ SERVED tool schema gains one reserved optional argument, `_findings`
25
+ (`RESERVED_ARGUMENT`): the model declares a `basis` for each call before
26
+ the result exists (`direct` when it expects the answer, `exploratory` when
27
+ it is looking; optional `expect`: `low` / `medium` / `high`) and, on its
28
+ next tool call or as a top-level `_findings.previous` on a JSON answer,
29
+ the STANDING of each earlier tool result by its tool_result id — `fact`
30
+ with the assertions it stands on, `open` with what would settle it,
31
+ `ruled-out` with one line, `noise` with nothing. The library peels the
32
+ argument off before the tool, the middleware chain, the argument
33
+ validator, the permission gate and every pause carrier see the call, files
34
+ the rows through ONE writer as an append-only `AgentState.findingsLedger`
35
+ (`basis` / `standing` / `conflict` rows — a conflict is `conflictsOf`'s
36
+ fact about two stood-on readings that disagree, witnesses by identity),
37
+ and leaves the assistant turn in history verbatim. Read it back with
38
+ `Agent.findings()` (detached; `undefined` when unarmed or when the model
39
+ declared nothing — never an empty array standing in for "no findings").
40
+ Why: a long tool loop serves every result back in full on every call and
41
+ what the model already judged is nowhere but in its head; asking a second
42
+ model would cost a call per result and put a second voice on the record.
43
+ Nothing is inferred (a call with no declaration files no row; a result
44
+ nobody names has no standing — undeclared, never `open`); what the answer
45
+ turn is then SERVED from the record is the next block; and the always-on
46
+ `findings-ledger` instruction is a system piece hashed on every receipt,
47
+ so a reworded ask is a different hash a bench can name. `keepLedgerFacts`
48
+ is accepted now so no public name changes later and is inert until
49
+ standing-aware eviction lands.
50
+ - Two typed events — `agentfootprint.findings.declared` (one per basis row)
51
+ and `agentfootprint.findings.standing` (one per standing row) — carry
52
+ identities, enums and counts only; assertion values, `settles` and `line`
53
+ live in the committed key under whatever redaction the run configured.
54
+ - The checkpoint carries `findingsLedger` only when present, and
55
+ `continueFrom` re-seeds it, so a continued conversation never reports its
56
+ earlier declarations as undeclared. `validateCheckpoint` checks each row's
57
+ shape per kind — shape only, never the values.
58
+ - A registry tool that declares its own `_findings` property is refused at
59
+ build, naming the tool, and only when `.findings()` is armed; a provider-
60
+ or MCP-ingested schema that carries the name is left undecorated (the
61
+ author's property wins, recorded by the committed schema itself), and a
62
+ call to that tool is not peeled — the value runs as the author's argument
63
+ and files no row.
64
+ - The answer that stands is the peeled JSON; a re-ask (`output-retry`,
65
+ `step-nudge`, `evidence-recheck`) quotes the emission — the string the
66
+ provider returned — into the conversation, never the peeled form. A policy
67
+ halt hands the app the peeled args (`PolicyHaltError.proposed.args`), the
68
+ same carrier law every pause carrier follows.
69
+ - Exported from the root: `RESERVED_ARGUMENT` and the row types
70
+ (`FindingsLedger`, `FindingsRow`, `BasisRow`, `StandingRow`,
71
+ `ConflictRow`, `ConflictWitness`, `FindingsDeclaration`, `Basis`,
72
+ `Expect`, `Standing`).
73
+
74
+ ### Added — served from the ledger
75
+
76
+ - The answer turn reads the ledger. Once the model has declared at least one
77
+ standing, every later call on an armed agent is served a request-only
78
+ system piece composed from the folded ledger — `source: 'findings'` on the
79
+ receipt's `system.pieces` and on `servedAt(snapshot, k).system.pieces` —
80
+ headed by the context contract's own field meanings and holding `facts`,
81
+ `limitations`, `evidenceRefs` and `nextSteps`, each bucket marked "declared
82
+ by the model" and quoting the declaration (a conflict names both witnesses
83
+ and no verdict; a ruled-out branch is its one line), then `noise` as a count
84
+ and `undeclared` for the results nobody named — the honest absence, never
85
+ `open`. Bounded, every overflow stated, an empty bucket omitted. On the
86
+ wire, and only on the wire, a tool result the model judged `noise` or
87
+ `ruled-out` is served as a ticket, `{"collapsed":true,"standing":…,
88
+ "toolCallId":…}`, in place of its content; `open` and undeclared results
89
+ stay verbatim; nothing is dropped or reordered, and `toolName` /
90
+ `toolCallId` are untouched, so the tool_use/tool_result pair stays
91
+ wire-valid. `history` never changes — the window stage stays its only
92
+ writer — and `servedAt` rebuilds the piece and the collapse with the same
93
+ functions in the same order, so `receiptAt(k)` agrees by construction on
94
+ an armed run with a collapsed entry; no new served gap, `withheld`
95
+ untouched. Why a piece and a ticket rather than a rewritten history: the
96
+ record must keep the emission, and a judged result's bytes should not be
97
+ read again at full size on every call after the model said what it was.
98
+ - `.findings({ serve })` chooses how much of the pile stays:
99
+ `'ledger-and-facts'` (the default — facts, open and undeclared results
100
+ verbatim beside the piece; noise and ruled-out as tickets) or
101
+ `'ledger-only'` (fact results as tickets too; the model answers from the
102
+ piece, its own paraphrase). The second is BENCH-GATED: shipped so
103
+ `bench/findings-shuffle.mjs` can measure it on a real model, not a
104
+ recommendation, and never a default until that run shows the answer does
105
+ not drift when the same evidence arrives in a different order. The run
106
+ constant `findingsServe` is committed on every armed run so a served view
107
+ knows which dial produced the wire.
108
+ - The piece carries no per-call byte — it is a function of the folded
109
+ ledger and the wire's tool ids and nothing else — because it joins the ONE
110
+ system block the cache marker covers (`systemPromptCachePolicy` is
111
+ `'always'` by default, and the Anthropic adapters mark the whole joined
112
+ system prompt as one block). A re-ask is served the same system bytes and
113
+ the same system hash as the call before it. What that does NOT save: a
114
+ model that declares on every call moves the ledger on every call, so from
115
+ the first standing on each such call writes a new system cache entry and
116
+ reads none, and on Anthropic's wire the message breakpoints behind it miss
117
+ too. The feature's claim is wire bytes, not cache reads; no bench in the
118
+ tree counts cache tokens yet, and the design page names the trade.
119
+ - Measured (`npm run bench:findings`, mock provider, 20 tool calls, a
120
+ planted fact every 3rd): the piece carries all 6 planted facts at the
121
+ answer turn on every armed row — including under a sliding window that had
122
+ evicted 4 of them — and 13 of the 14 noise results are tickets, the noise
123
+ share of tool-result bytes on the wire falling from 93.5% to 15.2% with no
124
+ window; the fourteenth is the last batch's, undeclared by the
125
+ no-outputSchema law and served in full. The design page has the printed
126
+ table and its reading; no real-model number exists yet.
127
+ - Grounding is unchanged: the evidence gate keeps indexing raw history, so a
128
+ faithful ledger fact grounds through the result it cites and an invented
129
+ value is flagged. The choice seam no longer credits the whole system prompt
130
+ while a request-only piece is joined, so a subject id invented in a `fact`
131
+ assertion cannot excuse an argument equal to it; an unarmed agent takes the
132
+ branch it always did. The grouped chart carries `findingsLedger` across the
133
+ call-llm boundary, so both chart shapes serve byte-equal text.
134
+
135
+ ### Unchanged — an agent without `.findings()` records the bytes it recorded before
136
+
137
+ - Every decoration, peel, write, piece and event is gated on the door. The
138
+ 15 byte-identity references under `test/core/tools/reference/` were run on
139
+ this tree first and pass untouched; ONE new reference, `agent-findings`,
140
+ was generated alone — and regenerated alone for the served piece, its
141
+ delta (the run constant, the piece on one receipt, nothing else) on the
142
+ test file's header. `npm run bench:findings` runs each configuration
143
+ unarmed, armed with the mock declaring, and armed with the mock declaring
144
+ NOTHING, and exits non-zero if any of the six baseline columns moves under
145
+ that silent arm — they do not: an armed agent whose model declares no
146
+ standing is served the bytes it always was, plus the instruction.
147
+ - The name was proved before it shipped: `_findings` survives every
148
+ provider's `inputSchema` mapping byte-for-byte, `required` untouched —
149
+ Anthropic, OpenAI, Gemini, Bedrock, Ollama, Foundry (hosted and local) and
150
+ both browser providers (`test/adapters/reservedArgumentSurvives.test.ts`).
151
+
152
+ ## [9.100.0] - 2026-09-16
153
+
154
+ ### Added — the delivered answer names its shape guarantee
155
+
156
+ - `AgentState.answerGuarantee` — written by the Route decider on the turn
157
+ it picks `final`: `'tool-forced'` (the provider was forced by name to
158
+ answer through the output schema's synthetic tool, so the shape held on
159
+ the wire), `'checked'` (generated as text, parsed by the output schema
160
+ after), or `'none'` (no output schema, free text). A fact about HOW the
161
+ answer was obtained, beside `answerValidation`, which is about its
162
+ content. Why: three different strengths of "the answer has a shape"
163
+ existed and the record could not say which applied; a lens showing the
164
+ Answer stop now can. Borrowed from constrained decoding, where the shape
165
+ holds by construction — that word joins the vocabulary when a provider
166
+ that constrains its own decoding ships.
167
+
168
+ ### Changed — artifact bytes are encoded with footprintjs's own encoder
169
+
170
+ - `canonicalPayloadBytes` — the one place every artifact store turns a JSON
171
+ payload into bytes — uses footprintjs 9.26.0's `stringifySnapshot`: the
172
+ same bytes `JSON.stringify` produces, with its own stack. A recording of
173
+ a long linear run (about ten thousand stages) nests one level per stage
174
+ and the engine's recursive encoder threw; such a recording could not be
175
+ minted. Byte-identical for everything that could be minted before, so
176
+ digests and `bytes` do not change. The file store's envelope already held
177
+ the payload as encoded text, so it needed nothing. footprintjs `^9.26.0`.
178
+
8
179
  ## [9.99.0] - 2026-09-16
9
180
 
10
181
  ### Added — the story knows which stage each beat came from
package/CLAUDE.md CHANGED
@@ -157,7 +157,7 @@ Traps: `src/observability/` holds the finder IMPLEMENTATIONS (canonical home; `d
157
157
  - **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`.
158
158
  - **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.
159
159
  - **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).
160
- - **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).
160
+ - **events/** → 113 typed events across 25 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).
161
161
  - **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.
162
162
  - **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.
163
163
  - **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).
package/README.md CHANGED
@@ -931,6 +931,7 @@ The flowchart, recorders, and tests don't change between dev and prod.
931
931
  - Human-in-the-loop pause / resume — a tool calls `pauseHere(...)` (or `askHuman(...)`); `isPaused(result)` hands you a JSON-serializable checkpoint, and `agent.resume(checkpoint, input)` continues hours later on a different server
932
932
  - Resilience primitives — `withRetry`, `withFallback`, `withCircuitBreaker`, `.outputFallback`, `agent.resumeOnError`
933
933
  - Context Integrity — deterministic checks at the seams where a run contradicts ITSELF: a tool parked but still on the wire, a tool offered after the results grounding it were evicted, an answer field that disagrees with the fact it claims to report (`.claims()`, requires `.outputSchema()`). Nothing is blocked or rewritten — each defect is one typed finding, and every run files a disposition ledger so "no findings" and "no check ran" stay different states. `integrityPosture: 'dev'` adds the liveness proofs (a start-of-run canary; `CheckerDeadError` instead of a green report from a checker that never ran). Read it back with `find_context_errors` over a recording — [Context Integrity](https://agentfootprint.dev/docs/monitor/context-integrity/)
934
+ - Findings ledger — `.findings()` adds one reserved optional argument, `_findings`, to every served tool schema: the model declares why it makes each call (`basis`: `direct` or `exploratory`) and, on its next call or as a top-level key of a JSON answer, the standing of each earlier result by its tool_result id (`fact` with the assertions it stands on, `open`, `ruled-out`, `noise`). Zero extra model calls. The argument is peeled off before the tool, the middleware or the validator sees the call; the rows land as one append-only `findingsLedger` on the run's state (`agent.findings()`), with a `conflict` row whenever two stood-on readings disagree. Nothing is inferred — a call that declares nothing files nothing — and nothing is served differently yet: the ledger is a record. An agent that never calls `.findings()` is byte-identical to one built before it existed
934
935
  - 60+ typed observability events — `agent` · `composition` · `context` · `stream` · `tools` · `skill` · `memory` · `cache` · `cost` · `permission` · `eval` · `embedding` · `pause` · `error` · `fallback` · `resilience` · `reliability` · `risk`
935
936
 
936
937
  **Debugging & compliance** (`agentfootprint/observe`)
@@ -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
- **111 typed events across 24 domains.** Two subscription shapes and no third:
255
+ **113 typed events across 25 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
 
@@ -25,6 +25,7 @@
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.decodeArtifactData = exports.encodeArtifactData = exports.computeArtifactDigest = exports.decodeCanonicalPayload = exports.payloadShapeOf = exports.measureArtifactBytes = exports.canonicalPayloadBytes = void 0;
27
27
  const types_js_1 = require("./types.js");
28
+ const footprintjs_1 = require("footprintjs");
28
29
  const textEncoder = new TextEncoder();
29
30
  const textDecoder = new TextDecoder();
30
31
  /**
@@ -39,7 +40,10 @@ function canonicalPayloadBytes(data) {
39
40
  return textEncoder.encode(data);
40
41
  let serialized;
41
42
  try {
42
- serialized = JSON.stringify(data);
43
+ // footprintjs's own encoder (9.26.0): the same bytes as JSON.stringify,
44
+ // with its own stack — a recording of a long linear run nests one level
45
+ // per stage and the engine's recursive encoder refused it.
46
+ serialized = (0, footprintjs_1.stringifySnapshot)(data);
43
47
  }
44
48
  catch (err) {
45
49
  throw new types_js_1.InvalidArtifactError(`the payload cannot be JSON-serialized (${err instanceof Error ? err.message : String(err)}). ` +
@@ -1 +1 @@
1
- {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/artifacts/payload.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;AAEH,yCAAkD;AAElD,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AACtC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AAEtC;;;;GAIG;AACH,SAAgB,qBAAqB,CAAC,IAAa;IACjD,IAAI,IAAI,YAAY,UAAU;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9D,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,+BAAoB,CAC5B,0CACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,KAAK;YACH,mFAAmF;YACnF,qEAAqE,CACxE,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,IAAI,+BAAoB,CAC5B,0EAA0E;YACxE,4EAA4E;YAC5E,2CAA2C,CAC9C,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AACxC,CAAC;AAvBD,sDAuBC;AAED,0EAA0E;AAC1E,SAAgB,oBAAoB,CAAC,IAAa;IAChD,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC;AAChD,CAAC;AAFD,oDAEC;AAUD;;6BAE6B;AAC7B,SAAgB,cAAc,CAAC,IAAa;IAC1C,IAAI,IAAI,YAAY,UAAU;QAAE,OAAO,QAAQ,CAAC;IAChD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC5C,OAAO,MAAM,CAAC;AAChB,CAAC;AAJD,wCAIC;AAED;;;;;;;;;GASG;AACH,SAAgB,sBAAsB,CAAC,KAAmB,EAAE,KAAiB;IAC3E,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,MAAM;YACT,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAY,CAAC;IAC5D,CAAC;AACH,CAAC;AATD,wDASC;AAED,gDAAgD;AACzC,KAAK,UAAU,qBAAqB,CAAC,IAAa;IACvD,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,uDAAuD;IACvD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;IACtF,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7D,OAAO,WAAW,GAAG,EAAE,CAAC;AAC1B,CAAC;AATD,sDASC;AAkBD,uEAAuE;AACvE,SAAgB,kBAAkB,CAAC,IAAa;IAC9C,IAAI,IAAI,YAAY,UAAU;QAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IAClF,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACpE,uEAAuE;IACvE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;AACnF,CAAC;AALD,gDAKC;AAED;;;YAGY;AACZ,SAAgB,kBAAkB,CAAC,OAAuB;IACxD,QAAQ,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,KAAK,MAAM;YACT,OAAO,OAAO,CAAC,KAAK,CAAC;QACvB,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAY,CAAC;QAC9C,KAAK,UAAU;YACb,MAAM,IAAI,+BAAoB,CAC5B,4DAA4D,OAAO,CAAC,KAAK,aAAa;gBACpF,iFAAiF;gBACjF,uEAAuE,CAC1E,CAAC;IACN,CAAC;AACH,CAAC;AAfD,gDAeC;AAED,wEAAwE;AACxE,SAAS,QAAQ,CAAC,KAAiB;IACjC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,MAAM,KAAK,GAAG,MAAM,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;QAC7C,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/artifacts/payload.ts"],"names":[],"mappings":"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"}
@@ -244,6 +244,14 @@ class Agent extends RunnerBase_js_1.RunnerBase {
244
244
  contextBudget;
245
245
  permissionChecker;
246
246
  toolArgValidation;
247
+ /** See AgentOptions.findings (9.101.0). Set by `.findings()` and undefined
248
+ * on every other agent — the one value every findings gate below is
249
+ * conditioned on, so an unarmed agent hands each stage exactly the deps
250
+ * it always did. `serve` is threaded to seed (the run constant
251
+ * `findingsServe`) and to call-llm (`findingsServe` in its deps) on an
252
+ * armed agent only. `keepLedgerFacts` is inert until standing-aware
253
+ * eviction lands. */
254
+ findingsOptions;
247
255
  /** The opt-in tool-result ceiling in characters (9.11.0). Absent → results
248
256
  * are never measured. See {@link AgentOptions.maxToolResultChars}. */
249
257
  maxToolResultChars;
@@ -427,6 +435,10 @@ class Agent extends RunnerBase_js_1.RunnerBase {
427
435
  * destroyed by the act of continuing, which is the one thing retention
428
436
  * exists to prevent. Cleared on first read, exactly like the history. */
429
437
  pendingResumeFolded;
438
+ /** Its sibling for the findings ledger (9.101.0) — the model's standings
439
+ * on results this process never saw, carried by the checkpoint and
440
+ * restored as a stored record. Cleared on first read, like the history. */
441
+ pendingResumeFindingsLedger;
430
442
  /** The last completed run's final answer — see `checkpoint()` for why it is
431
443
  * kept here rather than read back from the recording. Undefined after a run
432
444
  * that failed or paused. */
@@ -605,6 +617,8 @@ class Agent extends RunnerBase_js_1.RunnerBase {
605
617
  this.permissionChecker = opts.permissionChecker;
606
618
  if (opts.toolArgValidation !== undefined)
607
619
  this.toolArgValidation = opts.toolArgValidation;
620
+ if (opts.findings !== undefined)
621
+ this.findingsOptions = opts.findings;
608
622
  // The tool-result ceiling (9.11.0). Refused HERE, naming the value, rather
609
623
  // than at the first tool call of the first run — a dial that cannot cap
610
624
  // anything is a configuration mistake, not a runtime condition.
@@ -1356,7 +1370,10 @@ class Agent extends RunnerBase_js_1.RunnerBase {
1356
1370
  this.conversationOwner(),
1357
1371
  // …and the same graph cursor (SG-C), from the same snapshot reader —
1358
1372
  // one reader, two carriers, so neither can lose what the other keeps.
1359
- this.continuityCursorOf(this.getLastSnapshot()?.sharedState), this.evidenceRecoveryOf(this.getLastSnapshot()?.sharedState));
1373
+ this.continuityCursorOf(this.getLastSnapshot()?.sharedState), this.evidenceRecoveryOf(this.getLastSnapshot()?.sharedState),
1374
+ // …and the findings ledger (9.101.0), from the same snapshot reader
1375
+ // `checkpoint()` uses — one reader, two carriers.
1376
+ this.findingsLedgerOf(this.getLastSnapshot()?.sharedState));
1360
1377
  throw new runCheckpoint_js_1.RunCheckpointError(cause, checkpoint);
1361
1378
  }
1362
1379
  throw cause;
@@ -1377,6 +1394,7 @@ class Agent extends RunnerBase_js_1.RunnerBase {
1377
1394
  // nobody asked it to. One run, one continuation.
1378
1395
  this.pendingResumeHistory = undefined;
1379
1396
  this.pendingResumeFolded = undefined;
1397
+ this.pendingResumeFindingsLedger = undefined;
1380
1398
  this.pendingResumeSkillCursor = undefined;
1381
1399
  this.pendingEvidenceRecovery = undefined;
1382
1400
  }
@@ -1783,6 +1801,7 @@ class Agent extends RunnerBase_js_1.RunnerBase {
1783
1801
  const owner = this.conversationOwner();
1784
1802
  const skillCursor = this.continuityCursorOf(state);
1785
1803
  const evidenceRecovery = this.evidenceRecoveryOf(state);
1804
+ const findingsLedger = this.findingsLedgerOf(state);
1786
1805
  return {
1787
1806
  version: 1,
1788
1807
  runId: this.currentRunContext.runId,
@@ -1802,6 +1821,9 @@ class Agent extends RunnerBase_js_1.RunnerBase {
1802
1821
  // exact byte shape.
1803
1822
  ...(skillCursor !== undefined && { skillCursor }),
1804
1823
  ...(evidenceRecovery !== undefined && { evidenceRecovery }),
1824
+ // The model's standings (9.101.0) — absent unless the run recorded any,
1825
+ // by the `folded` rule: an optional key, never a format change.
1826
+ ...(findingsLedger !== undefined && { findingsLedger }),
1805
1827
  };
1806
1828
  }
1807
1829
  /** Both checkpoint doors keep the repair budget separately from conversation text. */
@@ -1855,6 +1877,20 @@ class Agent extends RunnerBase_js_1.RunnerBase {
1855
1877
  return undefined;
1856
1878
  return structuredClone(spans);
1857
1879
  }
1880
+ /**
1881
+ * The findings ledger for a checkpoint (9.101.0) — `foldedSpansOf`'s twin:
1882
+ * one reader for `checkpoint()` and the crash carrier, `undefined` when the
1883
+ * run recorded no rows (so the key stays absent), and a detached copy
1884
+ * otherwise, never a reference into the live heap.
1885
+ *
1886
+ * @internal
1887
+ */
1888
+ findingsLedgerOf(state) {
1889
+ const ledger = state?.findingsLedger;
1890
+ if (ledger === undefined || ledger.length === 0)
1891
+ return undefined;
1892
+ return structuredClone(ledger);
1893
+ }
1858
1894
  /**
1859
1895
  * The two owner facts every conversation carrier stamps — who the run was
1860
1896
  * for, and which agent ran it (9.2.0).
@@ -1901,6 +1937,11 @@ class Agent extends RunnerBase_js_1.RunnerBase {
1901
1937
  // and `undefined` is the right answer there — it means "this conversation
1902
1938
  // recorded no folds", which is exactly true.
1903
1939
  this.pendingResumeFolded = cp.folded;
1940
+ // The findings ledger beside it (9.101.0). Stashed whether or not THIS
1941
+ // agent is armed: seed restores it only under `.findings()` (the deps
1942
+ // gate), and an unarmed continuation consumes and ignores it — exactly
1943
+ // like a `folded` field on an agent that never folds.
1944
+ this.pendingResumeFindingsLedger = cp.findingsLedger;
1904
1945
  // The conversation's skill cursor (SG-C). Stashed unconditionally —
1905
1946
  // whether it is HONORED is seed's `restoreSkillCursor` gate, which reads
1906
1947
  // the mounted graph's `continuity` declaration; a checkpoint written by a
@@ -2460,6 +2501,19 @@ class Agent extends RunnerBase_js_1.RunnerBase {
2460
2501
  getRunContext: getRunCtx,
2461
2502
  }));
2462
2503
  }
2504
+ // Same wiring for `agentfootprint.findings.*` (9.101.0) — the ledger's two
2505
+ // events, filed by `recordFindings` on scope. Attached only under
2506
+ // `.findings()`: an unarmed agent gains no bridge, no listener and no
2507
+ // per-event work, and `agent.on('agentfootprint.findings.*')` can only
2508
+ // ever fire on an agent that could have filed a row.
2509
+ if (this.findingsOptions !== undefined) {
2510
+ attachObserver(new EmitBridge_js_1.EmitBridge({
2511
+ id: 'agentfootprint.findings-bridge',
2512
+ prefix: 'agentfootprint.findings.',
2513
+ dispatcher,
2514
+ getRunContext: getRunCtx,
2515
+ }));
2516
+ }
2463
2517
  for (const r of this.attachedRecorders) {
2464
2518
  // A recorder's OWN `delivery` field is more specific than the
2465
2519
  // agent-level default — footprintjs's options bag would override the
@@ -2797,6 +2851,34 @@ class Agent extends RunnerBase_js_1.RunnerBase {
2797
2851
  const state = this.getLastSnapshot()?.sharedState;
2798
2852
  return state?.unsupportedValues;
2799
2853
  }
2854
+ /**
2855
+ * The last run's findings ledger (9.101.0) — the model's OWN standings on
2856
+ * its tool results, as `.findings()` recorded them: `basis` rows (what a
2857
+ * call was for, declared before it ran), `standing` rows (`fact` with the
2858
+ * assertions stood on, `open`, `ruled-out`, `noise` — the LAST row per
2859
+ * `toolCallId` is the current reading; earlier ones are quotable history)
2860
+ * and `conflict` rows (two stood-on readings that disagree, witnesses by
2861
+ * identity). Undefined when the agent has no `.findings()` OR the model
2862
+ * declared nothing — never an empty array standing in for "no findings",
2863
+ * and an id with no standing row is undeclared, never `open`.
2864
+ *
2865
+ * Detached from the execution record (`structuredClone`), so a caller may
2866
+ * keep or mutate it without touching the run's state.
2867
+ *
2868
+ * @example
2869
+ * ```ts
2870
+ * await agent.run({ message: 'which port is down?' });
2871
+ * const current = new Map<string, string>();
2872
+ * for (const row of agent.findings() ?? []) {
2873
+ * if (row.kind === 'standing') current.set(row.toolCallId, row.standing);
2874
+ * }
2875
+ * ```
2876
+ */
2877
+ findings() {
2878
+ const ledger = this.getLastSnapshot()?.sharedState
2879
+ ?.findingsLedger;
2880
+ return ledger === undefined ? undefined : structuredClone(ledger);
2881
+ }
2800
2882
  /** The last run's answer checks, detached from its execution record.
2801
2883
  * Undefined means no terminal validation ran, never an implicit pass. */
2802
2884
  answerValidation() {
@@ -3049,6 +3131,20 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3049
3131
  this.pendingResumeFolded = undefined;
3050
3132
  return f;
3051
3133
  },
3134
+ // The findings ledger (9.101.0): the arm, the serve mode it puts on the
3135
+ // record (the `forcedOutputToolName` precedent — a build-time constant
3136
+ // the rebuild reads instead of the receipt), and the continued
3137
+ // conversation's rows, all under the one gate — an unarmed agent hands
3138
+ // seed exactly the deps object it always did.
3139
+ ...(this.findingsOptions !== undefined && {
3140
+ findings: true,
3141
+ findingsServe: this.findingsOptions.serve ?? 'ledger-and-facts',
3142
+ consumePendingResumeFindingsLedger: () => {
3143
+ const l = this.pendingResumeFindingsLedger;
3144
+ this.pendingResumeFindingsLedger = undefined;
3145
+ return l;
3146
+ },
3147
+ }),
3052
3148
  // The conversation's inherited skill cursor (SG-C). Consumed (cleared)
3053
3149
  // on every run; HONORED only when the mounted graph declared
3054
3150
  // `continuity: 'conversation'` — the same one-option-one-behavior gate
@@ -3109,6 +3205,9 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3109
3205
  // name uniqueness; produces the dispatch map.
3110
3206
  const { registryByName, toolSchemas, toolDeclaringSkills, toolClaimants } = (0, buildToolRegistry_js_1.buildToolRegistry)(registry, this.injections, {
3111
3207
  hasArtifactStore: artifactStore !== undefined,
3208
+ // The reserved-argument refusal (9.101.0) — only when the ledger is
3209
+ // armed may a registry tool's own `_findings` be refused.
3210
+ ...(this.findingsOptions !== undefined && { findings: true }),
3112
3211
  });
3113
3212
  // A statically registered tool that declares `wants` on an agent with no
3114
3213
  // store is configuration that lies: every call would be refused at
@@ -3371,6 +3470,9 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3371
3470
  ...(budget?.tools !== undefined && { budgetCap: budget.tools }),
3372
3471
  // Steps (9.18.0): per-step narrowing + banner + the skip_step offer.
3373
3472
  ...(stepPlanFor !== undefined && { stepPlanFor }),
3473
+ // The findings ledger (9.101.0): the ONE decoration site is inside this
3474
+ // slot; the gate rides in value-conditionally.
3475
+ ...(this.findingsOptions !== undefined && { findings: true }),
3374
3476
  });
3375
3477
  // callLLM extracted to ./agent/stages/callLLM.ts (v2.11.2). Same
3376
3478
  // late-binding pattern as seed for toolSchemas (computed below).
@@ -3379,6 +3481,18 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3379
3481
  this.evidenceGate.posture !== 'assist' && {
3380
3482
  hasEvidenceRecovery: true,
3381
3483
  }),
3484
+ // The findings ledger (9.101.0): the choice seam and `postValidate`
3485
+ // read peeled args / content under `findings`; the SERVING (step 3) —
3486
+ // the ledger piece after the recovery piece, the wire-only collapse of
3487
+ // judged results — rides `hasFindingsLedger` with its mode, the same
3488
+ // value seed records as the run constant `findingsServe`. One gate,
3489
+ // the `hasEvidenceRecovery` grammar: an unarmed agent hands the stage
3490
+ // exactly the deps it always did and reads no new key.
3491
+ ...(this.findingsOptions !== undefined && {
3492
+ findings: true,
3493
+ hasFindingsLedger: true,
3494
+ findingsServe: this.findingsOptions.serve ?? 'ledger-and-facts',
3495
+ }),
3382
3496
  ...(this.answerValidationConfig !== undefined && { suppressDraftTokens: true }),
3383
3497
  // The receipt's salt (9.88.0) — read per call, like seed's own accessor.
3384
3498
  getRunId: () => this.currentRunContext?.runId,
@@ -3513,7 +3627,11 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3513
3627
  // exactly the arguments it always did — and `buildRouteDeciderStage`'s
3514
3628
  // no-judge fast path still returns the very function reference every
3515
3629
  // pre-9.83.0 chart was given.
3516
- this.noticePriorTurnEvidence && this.evidenceGate !== undefined ? true : undefined);
3630
+ this.noticePriorTurnEvidence && this.evidenceGate !== undefined ? true : undefined,
3631
+ // THE ANSWER TURN'S STANDINGS (9.101.0) — the same value-conditional
3632
+ // trailing positional, for the same reason: an unarmed agent hands the
3633
+ // builder exactly the arguments it always did.
3634
+ this.findingsOptions !== undefined ? true : undefined);
3517
3635
  const routeDecider = this.answerValidationConfig === undefined
3518
3636
  ? baseRouteDecider
3519
3637
  : (0, answerValidation_js_1.withAnswerValidation)(baseRouteDecider, this.answerValidationConfig, this.outputSchemaParser, artifactStore, () => this.consentOutstanding.size > 0);
@@ -3529,6 +3647,9 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3529
3647
  // contract to read it (9.61.0) — value-conditional, so every other
3530
3648
  // agent commits exactly what it always did.
3531
3649
  ...(this.claimContract !== undefined && { collectClaimFacts: true }),
3650
+ // The findings ledger (9.101.0) — the peel, the basis row and the
3651
+ // previous batch's standings all live in this handler under this gate.
3652
+ ...(this.findingsOptions !== undefined && { findings: true }),
3532
3653
  // THE WRITE SEAM (9.77.0) — `empty-lookup`. Handed the SAME harvested
3533
3654
  // map callLLM reads at the choice seam, so the two stages agree by
3534
3655
  // construction about which calls are armed. Value-conditional on both