agentfootprint 9.74.0 → 9.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +126 -0
  3. package/CLAUDE.md +24 -19
  4. package/ai-instructions/claude-code/SKILL.md +1 -1
  5. package/dist/artifacts/index.js +3 -1
  6. package/dist/artifacts/index.js.map +1 -1
  7. package/dist/artifacts/recordingArtifact.js +47 -1
  8. package/dist/artifacts/recordingArtifact.js.map +1 -1
  9. package/dist/core/Agent.js +53 -1
  10. package/dist/core/Agent.js.map +1 -1
  11. package/dist/core/agent/AgentBuilder.js +12 -0
  12. package/dist/core/agent/AgentBuilder.js.map +1 -1
  13. package/dist/core/agent/evidence/gate.js +22 -3
  14. package/dist/core/agent/evidence/gate.js.map +1 -1
  15. package/dist/core/agent/stagedRefs.js +161 -0
  16. package/dist/core/agent/stagedRefs.js.map +1 -0
  17. package/dist/core/agent/stages/callLLM.js +22 -2
  18. package/dist/core/agent/stages/callLLM.js.map +1 -1
  19. package/dist/core/agent/stages/evidenceRecheck.js +21 -2
  20. package/dist/core/agent/stages/evidenceRecheck.js.map +1 -1
  21. package/dist/core/agent/stages/toolCalls.js +42 -0
  22. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  23. package/dist/core/agent/toolDispatch.js +101 -0
  24. package/dist/core/agent/toolDispatch.js.map +1 -0
  25. package/dist/core/flowchartAsTool.js +9 -2
  26. package/dist/core/flowchartAsTool.js.map +1 -1
  27. package/dist/core/runbook/coverage.js +162 -0
  28. package/dist/core/runbook/coverage.js.map +1 -0
  29. package/dist/core/runbook/dispatch.js +119 -0
  30. package/dist/core/runbook/dispatch.js.map +1 -0
  31. package/dist/core/runbook/index.js +24 -0
  32. package/dist/core/runbook/index.js.map +1 -0
  33. package/dist/core/runbook/runbookAsTool.js +358 -0
  34. package/dist/core/runbook/runbookAsTool.js.map +1 -0
  35. package/dist/core/runbook/types.js +22 -0
  36. package/dist/core/runbook/types.js.map +1 -0
  37. package/dist/core/runbook/verdicts.js +156 -0
  38. package/dist/core/runbook/verdicts.js.map +1 -0
  39. package/dist/core/runbook/walk.js +115 -0
  40. package/dist/core/runbook/walk.js.map +1 -0
  41. package/dist/core/tools.js +55 -1
  42. package/dist/core/tools.js.map +1 -1
  43. package/dist/esm/artifacts/index.d.ts +1 -1
  44. package/dist/esm/artifacts/index.js +1 -1
  45. package/dist/esm/artifacts/index.js.map +1 -1
  46. package/dist/esm/artifacts/recordingArtifact.d.ts +36 -0
  47. package/dist/esm/artifacts/recordingArtifact.js +45 -0
  48. package/dist/esm/artifacts/recordingArtifact.js.map +1 -1
  49. package/dist/esm/core/Agent.js +53 -1
  50. package/dist/esm/core/Agent.js.map +1 -1
  51. package/dist/esm/core/agent/AgentBuilder.d.ts +12 -0
  52. package/dist/esm/core/agent/AgentBuilder.js +12 -0
  53. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  54. package/dist/esm/core/agent/evidence/gate.d.ts +11 -1
  55. package/dist/esm/core/agent/evidence/gate.js +22 -3
  56. package/dist/esm/core/agent/evidence/gate.js.map +1 -1
  57. package/dist/esm/core/agent/evidence/types.d.ts +23 -0
  58. package/dist/esm/core/agent/stagedRefs.d.ts +94 -0
  59. package/dist/esm/core/agent/stagedRefs.js +154 -0
  60. package/dist/esm/core/agent/stagedRefs.js.map +1 -0
  61. package/dist/esm/core/agent/stages/callLLM.d.ts +17 -0
  62. package/dist/esm/core/agent/stages/callLLM.js +22 -2
  63. package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
  64. package/dist/esm/core/agent/stages/evidenceRecheck.d.ts +19 -1
  65. package/dist/esm/core/agent/stages/evidenceRecheck.js +21 -2
  66. package/dist/esm/core/agent/stages/evidenceRecheck.js.map +1 -1
  67. package/dist/esm/core/agent/stages/toolCalls.js +42 -0
  68. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  69. package/dist/esm/core/agent/toolDispatch.d.ts +50 -0
  70. package/dist/esm/core/agent/toolDispatch.js +97 -0
  71. package/dist/esm/core/agent/toolDispatch.js.map +1 -0
  72. package/dist/esm/core/flowchartAsTool.d.ts +6 -0
  73. package/dist/esm/core/flowchartAsTool.js +9 -2
  74. package/dist/esm/core/flowchartAsTool.js.map +1 -1
  75. package/dist/esm/core/runbook/coverage.d.ts +69 -0
  76. package/dist/esm/core/runbook/coverage.js +155 -0
  77. package/dist/esm/core/runbook/coverage.js.map +1 -0
  78. package/dist/esm/core/runbook/dispatch.d.ts +69 -0
  79. package/dist/esm/core/runbook/dispatch.js +112 -0
  80. package/dist/esm/core/runbook/dispatch.js.map +1 -0
  81. package/dist/esm/core/runbook/index.d.ts +9 -0
  82. package/dist/esm/core/runbook/index.js +9 -0
  83. package/dist/esm/core/runbook/index.js.map +1 -0
  84. package/dist/esm/core/runbook/runbookAsTool.d.ts +88 -0
  85. package/dist/esm/core/runbook/runbookAsTool.js +354 -0
  86. package/dist/esm/core/runbook/runbookAsTool.js.map +1 -0
  87. package/dist/esm/core/runbook/types.d.ts +196 -0
  88. package/dist/esm/core/runbook/types.js +21 -0
  89. package/dist/esm/core/runbook/types.js.map +1 -0
  90. package/dist/esm/core/runbook/verdicts.d.ts +78 -0
  91. package/dist/esm/core/runbook/verdicts.js +148 -0
  92. package/dist/esm/core/runbook/verdicts.js.map +1 -0
  93. package/dist/esm/core/runbook/walk.d.ts +72 -0
  94. package/dist/esm/core/runbook/walk.js +110 -0
  95. package/dist/esm/core/runbook/walk.js.map +1 -0
  96. package/dist/esm/core/tools.d.ts +112 -0
  97. package/dist/esm/core/tools.js +52 -0
  98. package/dist/esm/core/tools.js.map +1 -1
  99. package/dist/esm/events/payloads.d.ts +39 -0
  100. package/dist/esm/events/registry.d.ts +3 -1
  101. package/dist/esm/events/registry.js +2 -0
  102. package/dist/esm/events/registry.js.map +1 -1
  103. package/dist/esm/index.d.ts +4 -3
  104. package/dist/esm/index.js +19 -2
  105. package/dist/esm/index.js.map +1 -1
  106. package/dist/esm/lib/mcp/toolExtras.d.ts +8 -0
  107. package/dist/esm/lib/mcp/toolExtras.js +7 -1
  108. package/dist/esm/lib/mcp/toolExtras.js.map +1 -1
  109. package/dist/events/registry.js +2 -0
  110. package/dist/events/registry.js.map +1 -1
  111. package/dist/index.js +97 -66
  112. package/dist/index.js.map +1 -1
  113. package/dist/lib/mcp/toolExtras.js +6 -0
  114. package/dist/lib/mcp/toolExtras.js.map +1 -1
  115. package/dist/types/artifacts/index.d.ts +1 -1
  116. package/dist/types/artifacts/index.d.ts.map +1 -1
  117. package/dist/types/artifacts/recordingArtifact.d.ts +36 -0
  118. package/dist/types/artifacts/recordingArtifact.d.ts.map +1 -1
  119. package/dist/types/core/Agent.d.ts.map +1 -1
  120. package/dist/types/core/agent/AgentBuilder.d.ts +12 -0
  121. package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
  122. package/dist/types/core/agent/evidence/gate.d.ts +11 -1
  123. package/dist/types/core/agent/evidence/gate.d.ts.map +1 -1
  124. package/dist/types/core/agent/evidence/types.d.ts +23 -0
  125. package/dist/types/core/agent/evidence/types.d.ts.map +1 -1
  126. package/dist/types/core/agent/stagedRefs.d.ts +95 -0
  127. package/dist/types/core/agent/stagedRefs.d.ts.map +1 -0
  128. package/dist/types/core/agent/stages/callLLM.d.ts +17 -0
  129. package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
  130. package/dist/types/core/agent/stages/evidenceRecheck.d.ts +19 -1
  131. package/dist/types/core/agent/stages/evidenceRecheck.d.ts.map +1 -1
  132. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  133. package/dist/types/core/agent/toolDispatch.d.ts +51 -0
  134. package/dist/types/core/agent/toolDispatch.d.ts.map +1 -0
  135. package/dist/types/core/flowchartAsTool.d.ts +6 -0
  136. package/dist/types/core/flowchartAsTool.d.ts.map +1 -1
  137. package/dist/types/core/runbook/coverage.d.ts +70 -0
  138. package/dist/types/core/runbook/coverage.d.ts.map +1 -0
  139. package/dist/types/core/runbook/dispatch.d.ts +70 -0
  140. package/dist/types/core/runbook/dispatch.d.ts.map +1 -0
  141. package/dist/types/core/runbook/index.d.ts +10 -0
  142. package/dist/types/core/runbook/index.d.ts.map +1 -0
  143. package/dist/types/core/runbook/runbookAsTool.d.ts +89 -0
  144. package/dist/types/core/runbook/runbookAsTool.d.ts.map +1 -0
  145. package/dist/types/core/runbook/types.d.ts +197 -0
  146. package/dist/types/core/runbook/types.d.ts.map +1 -0
  147. package/dist/types/core/runbook/verdicts.d.ts +79 -0
  148. package/dist/types/core/runbook/verdicts.d.ts.map +1 -0
  149. package/dist/types/core/runbook/walk.d.ts +73 -0
  150. package/dist/types/core/runbook/walk.d.ts.map +1 -0
  151. package/dist/types/core/tools.d.ts +112 -0
  152. package/dist/types/core/tools.d.ts.map +1 -1
  153. package/dist/types/events/payloads.d.ts +39 -0
  154. package/dist/types/events/payloads.d.ts.map +1 -1
  155. package/dist/types/events/registry.d.ts +3 -1
  156. package/dist/types/events/registry.d.ts.map +1 -1
  157. package/dist/types/index.d.ts +4 -3
  158. package/dist/types/index.d.ts.map +1 -1
  159. package/dist/types/lib/mcp/toolExtras.d.ts +8 -0
  160. package/dist/types/lib/mcp/toolExtras.d.ts.map +1 -1
  161. package/package.json +1 -1
package/AGENTS.md CHANGED
@@ -524,7 +524,7 @@ files `agentfootprint.tools.coverage_declared`; with
524
524
  to the final answer, which is how a limit survives a model that would rather not
525
525
  mention it.
526
526
 
527
- ### Observability — 108 typed events across 24 domains
527
+ ### Observability — 109 typed events across 24 domains
528
528
 
529
529
  ```typescript
530
530
  agent.on('agentfootprint.context.injected', (e) =>
package/CHANGELOG.md CHANGED
@@ -7,6 +7,132 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.76.0] - 2026-08-28
11
+
12
+ ### Added
13
+
14
+ - **`runbookAsTool` — turn a written procedure into a tool whose every answer
15
+ is evidence.** Triage is the most-used agent job in a business: run the
16
+ standing procedure, come back with a verdict somebody can act on. The first
17
+ production tool of that shape hand-rolled ~800 lines of envelope around a
18
+ footprintjs chart — a coverage ledger merging every inner source's own
19
+ ledger, a rule-version stamp on every sentence, capped verdict rows beside a
20
+ pre-rendered table, and the recorded walk that lets a reader CHECK the
21
+ verdict instead of trusting it. `runbookAsTool` is that envelope as one
22
+ declaration bag; the smallest legal call is `{ name, description,
23
+ procedure }` and it still yields the honest spine.
24
+
25
+ **The mandatory honesty spine**, on every answer whatever the runbook's
26
+ shape: `af_coverage` (three lists + a sentence naming the rule set and
27
+ version, with every inner tool's ledger folded upward), `af_provenance`
28
+ re-emitted FIRST (a seeded source's confession survives composition),
29
+ `rule_version` (or the honest `'undeclared'`), and the recorded walk as an
30
+ artifact ticket — new kind **`recording/chart-walk`** beside
31
+ `recording/run`, with `chartWalkPutInput` beside `recordingPutInput`. The
32
+ walk descriptor carries truthful counters, the declared projection (over
33
+ the cap, the CONTROL FLOW survives — stages, forks, and every `condition`
34
+ entry with its decide() evidence — never a head slice that keeps four
35
+ hundred writes and drops every decision), and a `walk_segment` discriminant
36
+ (`'full'` today; the wire is ready for resumed segments before gates land).
37
+ A failed mint costs the ticket, never the answer.
38
+
39
+ **The optional verdict projection**, selected by `resultKind: 'verdict/*'`:
40
+ rows off the chart's `verdicts` state key, ONE cap for the structured list
41
+ and the rendered table, truthful `rows_shown/rows_total/rows_complete`, and
42
+ `verdict_meanings` GENERATED from the decider's declared branches plus the
43
+ rule labels this run's evidence carried — never hand-restated, so a rule
44
+ change and its meaning change on the same day. Three outcomes, honestly: a
45
+ clean envelope; an inner absence passed through VERBATIM (the framework
46
+ still reads it as an absence); and `declined` rows counted into the ledger
47
+ as not-checked ground.
48
+
49
+ `flowchartAsTool` stays for compatibility (its `resultMapper` users stay
50
+ put, byte-identical); its stale pause message now names the runbook
51
+ program's gate phase instead of a version that shipped years ago.
52
+
53
+ - **`ctx.tools` — a tool's body can call other registered tools through the
54
+ run's own dispatch.** The `procedure` factory is invoked per call with the
55
+ agent's dispatch (static + skill-carried tools; ToolProvider-delivered
56
+ tools are invisible — there is no build-time list, the stated 9.72.0
57
+ caveat), so stages compose registered sources instead of importing modules
58
+ and building a second query stack. Inner calls run with `hasArtifacts:
59
+ false` (one answer, one ticket — never competing chips), a derived
60
+ toolCallId naming the outer call, `needs` resolved on the fail-closed
61
+ non-interactive path, and `checkIn`/`wants` tools refused BY NAME (an inner
62
+ call cannot pause, and must never silently skip a consent gate).
63
+
64
+ - **`composedOf` + `gates` on `defineTool`.** A composed tool names its
65
+ ingredient tools; the drift gate runs at AGENT BUILD — the one moment the
66
+ catalog is complete — so a renamed ingredient fails the build by name, not
67
+ the first 3 a.m. run. `gates` declares a procedure that can raise an
68
+ approval gate (read by composition-time checks that must keep a gating tool
69
+ out of a fan-out branch). Both pass the declaration bar (consumer-side
70
+ rails read them; nothing governs execution) and travel the MCP `_meta` bag
71
+ in both directions, judged on ingest by the same exported asserts
72
+ `defineTool` uses.
73
+
74
+ ## [9.75.0] - 2026-08-28
75
+
76
+ ### Added
77
+
78
+ - **Grounded numbers: the staged-refs nudge, and a revise correction that
79
+ names the route.** The field failure this closes, from a consumer's recorded
80
+ run: four tool results carried real numbers, a compute tool that could sum
81
+ them was registered — with `wants` declared over the staged dataset kind —
82
+ and the app's prompt said to use it. The model summed the numbers in its
83
+ head anyway and stated the total; the evidence gate recorded *"appears in no
84
+ tool result"* and the answer shipped, because the posture only observed. The
85
+ app patched it with more prose. The library-shaped fix is two mechanisms it
86
+ already owns, on the one dial it already has:
87
+
88
+ **`nudge: true` on `.namesAndNumbersFromEvidence()`** — when an iteration's
89
+ context holds a tool result staged by reference (an `artifacts.placement`
90
+ ticket) AND a tool the model can currently call declares `wants` over that
91
+ ticket's kind, ONE short line is appended at the very END of that request,
92
+ naming the refs and the spender tool by its registered name: derived numbers
93
+ come from the tool, not from mental arithmetic. Composed entirely from
94
+ declarations (`Tool.resultKind`, `Tool.wants`, matched by the exact-string
95
+ law dispatch uses — never by tool name); no prose surface for apps. The
96
+ placement is the point: the measured failure was recency — the app's own
97
+ instruction sat at the top of a long context and the numbers at the bottom,
98
+ so this one sits beside the data. Request-only (never history, so it never
99
+ enters the gate's exempt corpus), recomposed per iteration so it exists
100
+ exactly while both conditions hold, judged against the tools REALLY served
101
+ this call (the wrap-up's withheld surface arms nothing). Each firing lands
102
+ as `agentfootprint.agent.grounding_nudged` (event 109) with refs and tools
103
+ as data — the line's one record, since the line itself is not conversation.
104
+
105
+ ```ts
106
+ const agent = Agent.create({ provider, model, artifacts: { store, placement } })
107
+ .tool(exportRows) // resultKind: 'dataset/rows' — staged over the threshold
108
+ .tool(compute) // wants: { dataset: 'dataset/rows' } — the declared spender
109
+ .namesAndNumbersFromEvidence({ posture: 'guard', nudge: true })
110
+ .build();
111
+ ```
112
+
113
+ **The guarantee stays the postures the gate has had since 9.35.0** — the
114
+ existing value extraction is THE detector, unchanged: `'assist'` records,
115
+ `'guard'` allows the one bounded revision then delivers with both attempts
116
+ on the record, `'rails'` refuses with `UnsupportedValuesError`. What the
117
+ revision gains: when the flagged turn holds staged refs a served `wants`
118
+ tool can spend, the correction now names them — *"pass 'art_…'
119
+ (dataset/rows) to `compute` — compute the number there and answer with what
120
+ it returns"* — inside the authored frame, so the quoted values still come
121
+ last and the exempt-corpus fence is untouched. The `revision-asked`
122
+ `evidence_checked` event carries the same facts additively (`stagedRefs`,
123
+ `spenderTools`). Absent everything — no gate, `nudge` unset, or no
124
+ `wants`-declaring tool — every request, record and correction keeps its
125
+ exact bytes, pinned by test.
126
+
127
+ ### Changed
128
+
129
+ - **A `guard`/`rails` agent that also registers a `wants`-declaring tool will
130
+ see its evidence correction gain the refs clause above when staged refs are
131
+ in context.** That is the fix, not a side effect: a correction that says
132
+ "call the tool that provides it" without naming WHICH tool over WHICH ref
133
+ leaves the model to head-math again. Agents without a `wants` tool — or
134
+ without staged refs in the flagged turn — keep the exact 9.35.0 sentence.
135
+
10
136
  ## [9.74.0] - 2026-08-27
11
137
 
12
138
  ### Added
package/CLAUDE.md CHANGED
@@ -17,45 +17,50 @@ not in this table, search `src/index.ts` for the nearest noun before writing cod
17
17
 
18
18
  | If you are about to build… | It is | Where | Since |
19
19
  |---|---|---|---|
20
+ | turning a written operational procedure (a runbook, a triage playbook) into ONE agent tool whose every answer is EVIDENCE — coverage folded up from the inner tools it calls, rule name+version, verdict rows with GENERATED meanings, and the recorded walk as an artifact ticket, never bytes | `runbookAsTool` — dials: `procedure` (factory, invoked per call with `ctx.tools`) + `resultKind` (`'verdict/*'` arms the rowset projection; anything else ships spine + the chart's `report`) + `rules {name, version}` (default absent ⇒ `rule_version: 'undeclared'`) + `verdicts {decider, maxRows}` (default 50) + `walk {cap}` (default 500; over-cap ⇒ control-flow projection, declared) + `composedOf` (drift-checked at agent BUILD) + kept `recorders`/`keepRecord`/`keepRecordLimit`/`redact`; reserved state keys `verdicts`/`coverage`/`report`; inner `absent()` passes through VERBATIM unless the call said `allowAbsent`; walk kind `recording/chart-walk` + `walk_segment` discriminant | `src/core/runbook/` | 9.76.0 |
21
+ | calling ANOTHER registered tool from inside a tool's `execute` — composition over the agent's own dispatch map instead of importing the module and building a second query stack | `agentToolDispatch` + `ctx.tools` (ToolDispatch has/call) — sees static and skill-carried tools, NEVER ToolProvider-delivered ones (no build-time list, the 9.72.0 caveat); inner calls get the outer facts with hasArtifacts false and a derived toolCallId, `needs` resolved fail-closed non-interactively, `checkIn` and `wants` tools refused by name, no nested dispatch; declare ingredients via `composedOf` (and `gates` for a pausing procedure) — both travel MCP `_meta` | `src/core/agent/toolDispatch.ts` | 9.76.0 |
20
22
  | a tool returning numbers WITH the caveats that make them honest — interval/aggregation grain, is-it-a-counter, when the world was measured, which ground was NOT covered — as typed data the model reads compactly and the record keeps whole | `semantic()` + `tools.semantics_declared` (model sees `semanticsForModel` projection; `coverage` field absorbed by the coverage()/absent() channel) | `src/lib/semantics/` | 9.53.0 |
21
- | a build gate that refuses a triage/inventory tool that forgot its caveats, by tool name and field name | `checkSemantics` + `defineTool({ resultClass })` + bin `agentfootprint-check-semantics` | `src/lib/semantics/check.ts` | 9.53.0 |
23
+ | a build gate that refuses a triage/inventory tool that forgot its caveats, by tool name and field name | `checkSemantics` + `defineTool({ resultClass })` (`'triage'`/`'inventory'`, the closed set) + bin `agentfootprint-check-semantics` | `src/lib/semantics/check.ts` | 9.53.0 |
22
24
  | an agent that runs out of `maxIterations` mid-task handing back a HALF-SENTENCE as its answer — and nothing saying the budget ran out | `wrapUpAtMaxIterations` (default ON) + `WRAP_UP_INSTRUCTION` + `agent.budget_exhausted` + `stoppedEarly.wrappedUp` | `src/core/agent/stages/wrapUp.ts` | 9.56.0 |
25
+ | the conversation outgrowing the context window — dropping, summarizing or token-budgeting old turns, every removal on the record (the three refusal rows below all guard this window) | `.window()` + `slidingWindow` / `summarizeOldest` (= `.compaction()`) / `tokenBudget` + `keepRecentTurns` (default 6) + `thresholdTokens` (required, deliberately no default) + `retain: 'conversation'` (default) / `'discard'` | `src/core/agent/window/` | 7.17.0 |
23
26
  | keeping the user's own request in context while the window shrinks — the task's anchor, un-droppable by every window strategy including one you wrote | `'current-request'` refusal + `currentRequestIndexOf` + `WindowStrategyInput.currentRequestIndex` | `src/core/agent/window/currentRequest.ts` | 9.55.0 |
24
- | the window evicting the EVIDENCE while keeping the task — the tool result carrying the only valid ids leaves, and the model invents one that has never existed | `keepLastToolResults` (default 2) + `'last-tool-result'` refusal + `toolResultPinsOf` + `WindowRecord.observations` (what was kept, and its exact char cost) | `src/core/agent/window/lastToolResult.ts` | 9.57.0 |
27
+ | the window evicting the EVIDENCE while keeping the task — the tool result carrying the only valid ids leaves, and the model invents one that has never existed | `keepLastToolResults` (default 2; `0`/`false` disables) + `'last-tool-result'` refusal + `toolResultPinsOf` + `WindowRecord.observations` (what was kept, and its exact char cost) | `src/core/agent/window/lastToolResult.ts` | 9.57.0 |
25
28
  | telling the model WHICH tools' results a drop took, so it re-calls instead of reconstructing from memory — and filing the same fact on the record even when no notice was authored | `droppedToolNames` + the notice's tool sentence + `WindowRecord.droppedObservations` | `src/core/agent/window/toolNames.ts` | 9.57.0 |
26
- | an instruction that SAYS a run-time number rather than only gating on it ("you are on action 25 of 30") — with the library owning absence, so never "23 of undefined" and never a fabricated zero | `promptTemplate` + the closed `TEMPLATE_FACTS` vocabulary + `skipped: 'unknown-fact'` + `Injection.templated` | `src/lib/injection-engine/promptTemplate.ts` | 9.57.0 |
29
+ | an instruction that SAYS a run-time number rather than only gating on it ("you are on action 25 of 30") — with the library owning absence, so never "23 of undefined" and never a fabricated zero | `promptTemplate` + the closed `TEMPLATE_FACTS` vocabulary (`action`/`actionBudget`/`actionsRemaining`) + `skipped: 'unknown-fact'` + `Injection.templated` | `src/lib/injection-engine/promptTemplate.ts` | 9.57.0 |
27
30
  | an injection predicate that can see how much of the action budget is left | `InjectionContext.maxIterations` / `.iterationsRemaining` + `iterationsRemainingOf` (the ONE denominator, shared with the cache decision) | `src/lib/iterationBudget.ts` | 9.57.0 |
28
31
  | report progress from inside a tool — "hop 3 of 12 done", said mid-call while a long-running `execute` is still working (the record was otherwise atomic: tool_start, silence, tool_end) | `ctx.progress` + `agentfootprint.stream.tool_progress` | `src/core/tools.ts` | 9.52.0 |
29
- | show tool progress to the user — a mid-call report reaching the live status line / chat bubble, not just the record (`message` shown verbatim, capped; otherwise an honest generic line, never a payload dump) | `selectStatus` + `progressMessageOf` + `tool.progress` templates | `src/recorders/observability/status/statusTemplates.ts` | 9.54.0 |
32
+ | show tool progress to the user — a mid-call report reaching the live status line / chat bubble, not just the record (`message` shown verbatim, capped at 120 chars with the cut stated; otherwise an honest generic line, never a payload dump) | `selectStatus` + `progressMessageOf` + `tool.progress` templates | `src/recorders/observability/status/statusTemplates.ts` | 9.54.0 |
30
33
  | declaring which skills connect — the SkillMap as one named thing (the agent that mounts it is the SkillWalker; there is deliberately no walker class) | `defineSkillMap` + `SkillMap` (permanent reference-equal aliases of `skillGraph`/`SkillGraph`) | `src/lib/injection-engine/skillGraph.ts` | 9.51.0 |
31
- | guarding a skill transition on state or a tool result's fields — a route condition as DATA (comparable, drawable, evidence-recorded), not an opaque predicate | `guard:` + `compileGuard` + `guard-unsatisfiable` | `src/lib/injection-engine/skillGuard.ts` | 9.51.0 |
34
+ | guarding a skill transition on state or a tool result's fields — a route condition as DATA (comparable, drawable, evidence-recorded), not an opaque predicate | `guard:` + `compileGuard` (ops `'eq'`/`'ne'`/`'gt'`/`'gte'`/`'lt'`/`'lte'`/`'in'`/`'notIn'`) + `guard-unsatisfiable` | `src/lib/injection-engine/skillGuard.ts` | 9.51.0 |
32
35
  | seeing the skill map in a recording — the author's nodes + edges (guards included) as DATA, never parsed from prose or inferred from fired hops | `skill.graph_declared` + `buildSkillGraphDeclared` | `src/core/agent/skillGraphDeclared.ts` | 9.50.0 |
33
36
  | "where could the run go next?" — the reachable skill set at every cursor move, typed on the move itself | `cursorMove.reachable` + `reachableSkills` | `src/lib/injection-engine/buildInjectionEngineSubflow.ts` | 9.50.0 |
34
37
  | putting the ASSEMBLED system prompt in the recording — the exact string the model read (opt-in; default OFF is a privacy decision) | `recordSystemPrompt` | `src/core/agent/types.ts` | 9.50.0 |
35
- | a typed HITL prompt — let a person pick from a list, choose a range, use a real control instead of typing prose | `AskComponent` | `src/core/askComponent.ts` | 9.24.0 |
36
- | carrying WHAT the person chose back, with what they could see | `DecisionValue` | `src/core/checkin.ts` | 9.47.0 |
37
- | archiving a finished run — filing it, attaching it to a bug report, feeding it to an analysis tool | `persistRecording` + `RecordingEnvelope` | `src/recorders/observability/recordingEnvelope.ts` | 9.48.0 |
38
- | packaging a run for a HUMAN to file — a zip whose evidence is the archive envelope, plus host facts and a readable transcript | `exportBugReport` | `src/lib/bug-report/build.ts` | — |
38
+ | a typed HITL prompt — let a person pick from a list, choose a range, use a real control instead of typing prose | `AskComponent` + `componentId` (consumer-registered vocabulary, opaque here) + inline `props` + `propsRef` (artifact claim ticket for the big half) | `src/core/askComponent.ts` | 9.24.0 |
39
+ | carrying WHAT the person chose back, with what they could see | `DecisionValue` (`kind` / `value` / `from?` + `coverage {seen, total, filter?}`) | `src/core/checkin.ts` | 9.47.0 |
40
+ | archiving a finished run — filing it, attaching it to a bug report, feeding it to an analysis tool | `persistRecording` + `RecordingEnvelope` + `run.complete` (required) + `privacy.mode` (`'full'` today; `'structure-only'`/`'redacted'` refuse by name) | `src/recorders/observability/recordingEnvelope.ts` | 9.48.0 |
41
+ | packaging a run for a HUMAN to file — a zip whose evidence is the archive envelope, plus host facts and a readable transcript | `exportBugReport` + `include` (the consent seam; left-out units are counted in `manifest.excluded`) + `warnOverBytes` (default 20 MB) | `src/lib/bug-report/build.ts` | — |
39
42
  | writing archived runs somewhere — a directory, one JSON file per run | `fileRecordingSink` + `RecordingSink` | `src/recorders/observability/fileRecordingSink.ts` | 9.48.0 |
40
43
  | declaring an agent's whole setup as one named, versioned thing — a preset, a template, a blueprint, "the support agent we all use" | `defineAgentRecipe` + `AgentBuilder.recipe()` | `src/recipes/` | 9.48.0 |
41
44
  | naming your own branches and nodes — which prefix is reserved, and telling framework plumbing from consumer structure in a trace | `RESERVED_SUBFLOW_PREFIX` + `isReservedSubflowSegment` | `src/conventions.ts` | 9.49.0 |
42
- | keeping a large tool result out of the model's context | `artifacts` + `wants` + `placement` | `src/artifacts/` | 9.21.0 |
45
+ | keeping a large tool result out of the model's context | `artifacts` (bare store, or `{ store, placement: { maxInlineChars }, recordings }` — `placement` omitted ⇒ never placed; `recordings: true` / `{ label }`, default off) + `wants` + `placement` | `src/artifacts/` | 9.21.0 |
43
46
  | the ticket placement just minted being REFUSED by your own `wants` argument as a kind mismatch — because the mint says `tool-result/<toolName>` and your consumer says `dataset/rows` | `Tool.resultKind` + `placedResultKind(toolName, declared?)` (the mint speaks the consumer's vocabulary; the exact-match matcher is untouched) | `src/artifacts/placement.ts` | 9.70.0 |
44
47
  | letting the UI draw an artifact without the model naming a component | `registerArtifactComponent` (in the `agentfootprint-lens` package) | — | — |
45
48
  | finding which tools the model keeps writing by hand | `agentfootprint.tools.code_run` + `codeShape` | `src/core/codeRunnerTool.ts` | 9.46.0 |
46
- | a skill wrongly activated by a keyword staying loaded for the whole turn — suspending/parking a map's prompt + tools when its contribution goes unused, WITHOUT touching its cursor, and re-engaging it on evidence | `.maps()` + `advanceEngagement` + `agentfootprint.map.engaged/parked` + skip reason `'parked'` | `src/maps/` | 9.58.0 |
49
+ | a skill wrongly activated by a keyword staying loaded for the whole turn — suspending/parking a map's prompt + tools when its contribution goes unused, WITHOUT touching its cursor, and re-engaging it on evidence | `.maps({ renewalGrace })` (default 3) + `MountedMap.nonParkable` + `advanceEngagement` + `agentfootprint.map.engaged/parked` + skip reason `'parked'` | `src/maps/` | 9.58.0 |
47
50
  | a value that must say how it knows itself — an unknown count that can never render as zero | `Claim<T>` (`known`/`unknown`/`notApplicable`) | `src/lib/claim/claim.ts` | 9.58.0 |
48
51
  | asking a debugging model "what did this run contradict itself about, and why?" — the Context Integrity findings, joined to the step that filed them | `find_context_errors` | `src/lib/trace-toolpack/traceToolpack.ts` | 9.61.0 |
52
+ | the model inventing an id or a reading that no tool ever returned — a deterministic fabrication detector on the final answer, with a posture for how hard it pushes back | `.namesAndNumbersFromEvidence()` + `posture` (`'assist'` record+flag, the default / `'guard'` one revision then ship flagged / `'rails'` refuse instead) + `shapes` + `exempt` + `minDigits` (default 4) + `nudge` (default off) | `src/core/agent/evidence/` | 9.35.0 |
49
53
  | counting a HUMAN-VERIFIED value as ground for the choice-seam check — the person clicked a row, the app verified the cells against the artifact, and the id the model takes from that selection is not fabricated; the source label travels onto the record | `externalGrounds` + `ExternalGround` + `external_ground_used` | `src/integrity/unsupported-argument/check.ts` | 9.72.0 |
50
- | a tool answering "I looked and found nothing", routably | `absent` | `src/core/agent/coverage/absent.ts` | 9.43.0 |
51
- | stating what a clean answer does NOT rule out | `coverage` | `src/core/agent/coverage/ledger.ts` | 9.43.0 |
54
+ | a model head-mathing a total from tool-result numbers while a compute tool sits unused on the wire — a LATE line each iteration naming the staged refs and the `wants` tool that spends them (recency working FOR the instruction), plus the revise correction naming the same route | `nudge: true` (default off) + `stagedRefsNudgeLine` + `findStagedRefs` + `grounding_nudged` | `src/core/agent/stagedRefs.ts` | 9.75.0 |
55
+ | a tool answering "I looked and found nothing", routably | `absent` + `looked_for` + `checked`/`not_checked`/`cannot_cover` + `tryInstead` (delivered status `'absent'`, routable by `onToolStatus`) | `src/core/agent/coverage/absent.ts` | 9.43.0 |
56
+ | stating what a clean answer does NOT rule out | `coverage` + the declaration `{ checked, not_checked?, cannot_cover? }` | `src/core/agent/coverage/ledger.ts` | 9.43.0 |
52
57
  | minting one of those shapes from a tool that is NOT JavaScript — the canonical note sentences and reserved marker keys as DATA, so a Python/Go/Rust sidecar reads a file instead of regex-scraping `dist/esm` (which a consumer really did) | `canonical-notes.json` at the package root + the `./canonical-notes.json` exports entry, GENERATED from the built barrel by `scripts/gen-canonical-notes.mjs` | `scripts/gen-canonical-notes.mjs` | 9.70.0 |
53
- | proving a session store really honours the port | `runSessionLifecycleConformance` | `src/hosting/conformance/` | 9.37.0 |
58
+ | proving a session store really honours the port | `runSessionLifecycleConformance` + `declared` skips (by case name WITH the reason; a declared case still RUNS) + outcomes `'passed'`/`'not-applicable'`/`'declared'`/`'failed'` | `src/hosting/conformance/` | 9.37.0 |
54
59
  | deciding who owns a contested session write | `resolveSessionOwner` | `src/hosting/sessionOwnership.ts` | 9.37.0 |
55
- | making a caller-supplied id safe for a backend | `encodeIdentityField` | `src/memory/identity/encode.ts` | 9.40.0 |
56
- | scoping a skill's tools so they reach the model only while it is active | `toolsFromActiveSkill` | `src/core/agent/toolsFromActiveSkill.ts` | 9.43.0 |
60
+ | making a caller-supplied id safe for a backend | `encodeIdentityField` | `src/memory/identity/encode.ts` | 9.37.0 |
61
+ | scoping a skill's tools so they reach the model only while it is active | `toolsFromActiveSkill` + a no-arg posture (default off) + the stamp `autoActivate: 'currentSkill'` (a default, never an override) | `src/core/agent/toolsFromActiveSkill.ts` | 9.36.0 |
57
62
  | subscribing to every event in one domain at once | `DomainWildcard` | `src/events/dispatcher.ts` | 9.4.0 |
58
- | pausing a run for a person and resuming it later | `checkInApproved` / `checkInDeclined` | `src/core/checkin.ts` | 9.24.0 |
63
+ | pausing a run for a person and resuming it later | `checkInApproved` / `checkInDeclined` (`{ by, note?, value? }`; a decline is NOT an abort — the model gets a "declined by human" tool result and adapts in-loop) | `src/core/checkin.ts` | 7.5.0 |
59
64
 
60
65
  **One law before you add a mapping.** Any function turning caller data into a KEY,
61
66
  a namespace, a filename or an index entry must be injective, and its collision
@@ -133,7 +138,7 @@ Traps: `src/observability/` holds the finder IMPLEMENTATIONS (canonical home; `d
133
138
  - **Recordings as artifacts (9.26.0)**: `artifacts: { store, recordings: true | { label } }`. `Agent.startRunRecording()` calls the SAME `recordRun` (before `createExecutor` — `attach()` collects for the executor not yet built); `fileRunRecording` mints AFTER `finalizeResult`, awaited, every failure contained to `artifacts.refused`. Pure half in artifacts/recordingArtifact.ts; payload is the recording's JSON TEXT (a live snapshot handed to an in-process store would be a live view into a finished run). No new wire op — `artifact-get` serves it.
134
139
  - **Code staging-in (9.26.0)**: `CodeSession.stageInputs?(inputs) → StagedCodeInput[]` — OPTIONAL, feature-detected via `canStageCodeInputs`, and its contract is TWO promises: the payloads are readable at the returned paths, AND every later `execute` exposes the manifest as `STAGED_INPUTS_ENV` (`AF_STAGED_INPUTS`, `name → path`). `CodeInput.name` is the MANIFEST KEY (the wants arg name, so a static description can name it) and `fileName` is the on-disk name — separate fields so the two cannot drift. `codeRunnerTool({ wants })` composes the schema properties + the description clause and refuses BY NAME on a non-staging runner. Implemented by localCodeRunner only.
135
140
  - **Repeated-call nudge (9.26.0)**: `core/agent/repeatedCall.ts` (pure `noteRepeatedCall` + `repeatedCallLedgers()`) + the ONE batch-loop hook in toolCalls. Fingerprints (FNV-1a) of stable-stringified args and the tool's OWN delivered result — never values. **The counters are NOT tracked state**: they live in a bounded run-keyed map held by `buildToolCallsHandler` and keyed by `deps.currentRun().runId`, so a turn that repeats nothing is byte-identical in state, commit log, narrative and recordings (a scope key would have changed all four for every agent that merely upgraded); the repeat itself rides `agentfootprint.tools.repeated_call`, the emit channel per-attempt facts belong on. A resume mints a new runId ⇒ counting restarts. Fires at the SECOND identical landing, once. `AgentOptions.repeatedCallNudge: false` disables (threaded value-conditionally; ON is the default). Deliberately NOT applied on the pause-resume dispatch paths.
136
- - **Evidence gate (9.35.0)** — `.namesAndNumbersFromEvidence({ posture, shapes, exempt, minDigits })`: every name/number in the final answer must appear in a `role:'tool'` result. Postures reuse the routing VOCABULARY (`assist`|`guard`|`rails`) as a SEPARATE option — routing authority ≠ evidence discipline, and overloading `skillGraphCascade.strictness` would deny "strict routing, loose evidence". Wiring is the stepNudge blast radius verbatim: `ResolvedEvidenceGate` (builder-resolved, refusals at the CALL SITE) → Agent ctor trailing param → `buildRouteDeciderStage`'s 4th arg (`judgeEvidence` runs LAST of the three judges — schema > steps > evidence — and NOT on a denied or schema-exhausted answer) → `evidenceRecheckStage` branch (`{loopTo}`, mounted only for a revising posture) → `STAGE_IDS.EVIDENCE_RECHECK` + BOUNDARY_LOCAL_IDS + milestoneFor. TWO deps flags, and the second is the one a reader misses: `evidenceRecheckStage` (branch) AND `hasEvidenceGate` (bubbles `systemPromptInjections` out of sf-llm-call in the GROUPED chart — without it the gate flags the app's own prompt). Per-check facts ride `agentfootprint.agent.evidence_checked` (emit channel); only the terminal verdict is committed (`unsupportedValues`), because the boundary raises off it. `UnsupportedValuesError` joins the TERMINAL-typed-error list in `run()`'s catch (a verdict is not a crash — no retry handle for a wall).
141
+ - **Evidence gate (9.35.0)** — `.namesAndNumbersFromEvidence({ posture, shapes, exempt, minDigits })`: every name/number in the final answer must appear in a `role:'tool'` result. Postures reuse the routing VOCABULARY (`assist`|`guard`|`rails`) as a SEPARATE option — routing authority ≠ evidence discipline, and overloading `skillGraphCascade.strictness` would deny "strict routing, loose evidence". Wiring is the stepNudge blast radius verbatim: `ResolvedEvidenceGate` (builder-resolved, refusals at the CALL SITE) → Agent ctor trailing param → `buildRouteDeciderStage`'s 4th arg (`judgeEvidence` runs LAST of the three judges — schema > steps > evidence — and NOT on a denied or schema-exhausted answer) → `evidenceRecheckStage` branch (`{loopTo}`, mounted only for a revising posture) → `STAGE_IDS.EVIDENCE_RECHECK` + BOUNDARY_LOCAL_IDS + milestoneFor. TWO deps flags, and the second is the one a reader misses: `evidenceRecheckStage` (branch) AND `hasEvidenceGate` (bubbles `systemPromptInjections` out of sf-llm-call in the GROUPED chart — without it the gate flags the app's own prompt). Per-check facts ride `agentfootprint.agent.evidence_checked` (emit channel); only the terminal verdict is committed (`unsupportedValues`), because the boundary raises off it. `UnsupportedValuesError` joins the TERMINAL-typed-error list in `run()`'s catch (a verdict is not a crash — no retry handle for a wall). **Grounded numbers (9.75.0)** rides the SAME dial: `nudge: true` arms the staged-refs nudge — `toolWantsOf` harvests `Tool.wants` beside the `toolGrounding` harvest in `Agent.buildChart` (same ToolProvider blindness), callLLM appends ONE request-only late line when a placed ticket's kind matches a SERVED `wants` tool (judged on `registeredToolSchemas`, so wrap-up's withheld surface arms nothing; history untouched ⇒ never in the exempt corpus), recorded as `agent.grounding_nudged`; and the recheck correction names the same refs+spender via `buildEvidenceCorrection`'s third arg — threaded whenever a `wants` tool exists, NOT gated on `nudge`, clause INSIDE the authored frame so `isLibraryAuthoredTurn`'s prefix match and values-last both hold.
137
142
  - **Coverage primitives (this release)** — `absent()` / `coverage()`, both copied from FIELD USE. THE argument is the direction of the error: a *nothing-found* misread as an *outage* costs an investigation, an *outage* misread as *nothing-found* declares a system healthy that was never checked — so the two must not share a shape. Blast radius, and it is the `resultCeiling` radius verbatim: `readCoverageResult` called by `declareCoverage` at BOTH execute boundaries in toolCalls.ts (batch loop + `resolveCredentialAndExecute`), on the UNWRAPPED content and BEFORE the ceiling. FOUR downstream changes and no more: (1) `ToolResultStatus` gained a SEVENTH word `'absent'` — routable by `onToolStatus`, because folding it into `'failure'` is the confusion itself and into `'success'` leaves nothing to route on; (2) two events (`tools.absent`, `tools.coverage_declared`); (3) tracked `AgentState.coverageDeclared` — a limit is a fact about the ANSWER, not about an attempt, which is why it is state and the repeated-call counters are not; (4) the evidence corpus indexes an absence's COVERAGE ONLY (`coverage/evidence.ts` — a failed lookup is the cheapest laundering machine, and `absent()` would have made it cheaper: this is frames.ts's argument on the tool side). Deliberately UNCHANGED: no `error: true`, no retry, no refusal, no gate flag. Survival into the answer is `.limitsTravelWithTheAnswer()` → `attachCoverageLimits` dep → BOTH builders swap the final branch's first stage for `prepareFinalWithLimitsStage` (same id, same position). It APPENDS rather than judges: a check for "did the model state its limits?" needs a second model to decide what counts, which is what evidence/README.md forbids.
138
143
  - **Out-of-budget wrap-up (9.56.0)** — `wrapUpAtMaxIterations` (AgentOptions, default ON, `repeatedCallNudge`'s opt-out grammar): the FOURTH Route branch, and the SchemaRetry mechanism verbatim — `STAGE_IDS.WRAP_UP` + same `{loopTo}`, so the last call is one ordinary turn with its own `iteration_start`/`llm_start`/`cost.tick`. Two things are its own: (1) the tools are WITHHELD at REQUEST ASSEMBLY in callLLM (`scope.wrapUpAsked`, the `schemaTool` seam's mirror — the schema tool still rides, so an output contract survives), which is what makes the call terminal BY CONSTRUCTION rather than by a rule, and is why it is exempt from `maxIterations`; (2) the CONDITIONAL MOUNT is on the agent having a TOOL SURFACE (`registryByName.size > 0 || externalToolProvider`) — a limit only cuts a turn short when tool calls were pending, so a toolless agent's chart must not grow a box that can never run. `decideBranch` treats a spent wrap-up as cut-short for every downstream judge (`toolCalls.length > 0 || scope.wrapUpAsked`), or a step nudge / evidence revision would loop past the limit that fired. `wrapUpAsked` is deliberately NOT seeded — a turn that finishes inside its budget commits the exact key set it always did. The record is FOUR channels: `stoppedEarly` (now with `wrappedUp`, corrected on the pass after so `answerWasEmpty` describes the answer the caller GOT), `cost.limit_hit` (unchanged), the new `agent.budget_exhausted {action: 'wrapped-up'|'cut-short'}`, and an optional `turn_end.stoppedEarly` projection.
139
144
  - **Closed seams**: Agent chart internals (AgentChartDeps not exported — extend via injections/tools/memory/thinking, never by adding a ReAct stage); ContextSlot (3 slots fixed); ProviderKind factory; dormant ports with no consumer (ContextSourceAdapter, EmbeddingProvider, RiskDetector — adapters/types.ts only); reserved tool names under selfExplain — 8.16.0 made the list DERIVED: `TRACE_TOOL_NAMES` (traceToolpack.ts, 11 names since 9.61.0: run_overview/find_context_errors/find_in_trace/trace_node/trace_slice/backtrack/who_wrote/get_value/inspect_tool_call/inspect_tool_run/read_narrative) is what AgentBuilder.ts:~1560 reserves in inline mode (`explain_run` in delegate mode), so a NEW toolpack tool joins ONE list and the reservation follows — but it must ALSO join the lazy template's mounted set (lazyToolpack builds over `{narrative: [], events: []}` so the catalog shape is fixed at build time) and the count assertion in test/lib/trace-toolpack/selfExplainAgent.test.ts. **8.17.0 `inspect_tool_run`** is the descent THROUGH the tool boundary: `flowchartAsTool({ keepRecord: true })` files each invocation's record in a bounded LRU store (lib/trace-toolpack/innerRunRecords.ts) keyed by the executing `ctx.toolCallId`, riding the `Tool` under the `INNER_RUN_RECORDS` registry symbol; `AgentBuilder.build()` → `collectInnerRuns(registry, injections)` → `SelfExplainSource.getInnerRuns` → `TraceToolpackArtifacts.innerRuns`. The inner views are the pack ITSELF re-run over `openRecording(record.recording)` — do not add a second query implementation. Provider-delivered tools are NOT collected (no build-time list); inner runtimeStageIds are a SEPARATE namespace and only `inspect_tool_run` accepts them. **9.61.0 `find_context_errors`** is the Context Integrity read-out: it reads `agentfootprint.integrity.context_error` + `…disposition` off the artifacts' EVENT TAIL (never re-running a check), joins each finding to the step it was filed at, and mounts UNCONDITIONALLY — a tool that vanished with the tail could not say the evidence channel is ABSENT, which is the one sentence this family forbids collapsing into "no errors found".
@@ -145,7 +150,7 @@ Traps: `src/observability/` holds the finder IMPLEMENTATIONS (canonical home; `d
145
150
  - **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`.
146
151
  - **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.
147
152
  - **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).
148
- - **events/** → 108 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).
153
+ - **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).
149
154
  - **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.
150
155
  - **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.
151
156
  - **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
- **108 typed events across 24 domains.** Two subscription shapes and no third:
255
+ **109 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
 
@@ -11,7 +11,7 @@
11
11
  * this folder for the one-job map and the import direction.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.placedToolResult = exports.placedResultKind = exports.isPlacedToolResult = exports.assertArtifactPlacement = exports.runArtifactStoreConformance = exports.runArtifactStoreCase = exports.formatArtifactStoreReport = exports.artifactStoreConformance = exports.UnserializableRecordingError = exports.RECORDING_MEDIA_TYPE = exports.RECORDING_ARTIFACT_KIND = exports.recordingPutInput = exports.presentArtifact = exports.PRESENT_TOOL_NAME = exports.wantsNeedsStoreRefusal = exports.resolveToolWants = exports.assertToolWants = exports.unconfiguredArtifacts = exports.bindArtifacts = exports.collectStream = exports.canStreamArtifacts = exports.canPutArtifactStream = exports.canGetArtifactStream = exports.bytesAsStream = exports.assertStreamBytes = exports.gcsArtifacts = exports.s3Artifacts = exports.UnreadableArtifactStoreError = exports.sqliteArtifacts = exports.UnreadableArtifactFileError = exports.fileArtifacts = exports.DEFAULT_IN_MEMORY_ARTIFACT_RETENTION = exports.inMemoryArtifacts = exports.mintArtifactRef = exports.isArtifactRef = exports.ARTIFACT_REF_PREFIX = exports.UnknownParentRefError = exports.InvalidArtifactError = exports.ArtifactIntegrityError = void 0;
14
+ exports.placedToolResult = exports.placedResultKind = exports.isPlacedToolResult = exports.assertArtifactPlacement = exports.runArtifactStoreConformance = exports.runArtifactStoreCase = exports.formatArtifactStoreReport = exports.artifactStoreConformance = exports.UnserializableRecordingError = exports.chartWalkPutInput = exports.CHART_WALK_ARTIFACT_KIND = exports.RECORDING_MEDIA_TYPE = exports.RECORDING_ARTIFACT_KIND = exports.recordingPutInput = exports.presentArtifact = exports.PRESENT_TOOL_NAME = exports.wantsNeedsStoreRefusal = exports.resolveToolWants = exports.assertToolWants = exports.unconfiguredArtifacts = exports.bindArtifacts = exports.collectStream = exports.canStreamArtifacts = exports.canPutArtifactStream = exports.canGetArtifactStream = exports.bytesAsStream = exports.assertStreamBytes = exports.gcsArtifacts = exports.s3Artifacts = exports.UnreadableArtifactStoreError = exports.sqliteArtifacts = exports.UnreadableArtifactFileError = exports.fileArtifacts = exports.DEFAULT_IN_MEMORY_ARTIFACT_RETENTION = exports.inMemoryArtifacts = exports.mintArtifactRef = exports.isArtifactRef = exports.ARTIFACT_REF_PREFIX = exports.UnknownParentRefError = exports.InvalidArtifactError = exports.ArtifactIntegrityError = void 0;
15
15
  var types_js_1 = require("./types.js");
16
16
  Object.defineProperty(exports, "ArtifactIntegrityError", { enumerable: true, get: function () { return types_js_1.ArtifactIntegrityError; } });
17
17
  Object.defineProperty(exports, "InvalidArtifactError", { enumerable: true, get: function () { return types_js_1.InvalidArtifactError; } });
@@ -54,6 +54,8 @@ var recordingArtifact_js_1 = require("./recordingArtifact.js");
54
54
  Object.defineProperty(exports, "recordingPutInput", { enumerable: true, get: function () { return recordingArtifact_js_1.recordingPutInput; } });
55
55
  Object.defineProperty(exports, "RECORDING_ARTIFACT_KIND", { enumerable: true, get: function () { return recordingArtifact_js_1.RECORDING_ARTIFACT_KIND; } });
56
56
  Object.defineProperty(exports, "RECORDING_MEDIA_TYPE", { enumerable: true, get: function () { return recordingArtifact_js_1.RECORDING_MEDIA_TYPE; } });
57
+ Object.defineProperty(exports, "CHART_WALK_ARTIFACT_KIND", { enumerable: true, get: function () { return recordingArtifact_js_1.CHART_WALK_ARTIFACT_KIND; } });
58
+ Object.defineProperty(exports, "chartWalkPutInput", { enumerable: true, get: function () { return recordingArtifact_js_1.chartWalkPutInput; } });
57
59
  Object.defineProperty(exports, "UnserializableRecordingError", { enumerable: true, get: function () { return recordingArtifact_js_1.UnserializableRecordingError; } });
58
60
  // The battery a store must pass to CLAIM the port, exported beside the port
59
61
  // itself so an out-of-tree store imports the check the same way it imports the
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/artifacts/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAEH,uCAkBoB;AAjBlB,kHAAA,sBAAsB,OAAA;AACtB,gHAAA,oBAAoB,OAAA;AACpB,iHAAA,qBAAqB,OAAA;AAgBvB,yCAAkF;AAAzE,gHAAA,mBAAmB,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,4GAAA,eAAe,OAAA;AAE5D,+DAKgC;AAJ9B,yHAAA,iBAAiB,OAAA;AACjB,4IAAA,oCAAoC,OAAA;AAItC,uDAI4B;AAH1B,iHAAA,aAAa,OAAA;AACb,+HAAA,2BAA2B,OAAA;AAG7B,2DAK8B;AAJ5B,qHAAA,eAAe,OAAA;AACf,kIAAA,4BAA4B,OAAA;AAI9B,mDAAwE;AAA/D,6GAAA,WAAW,OAAA;AACpB,qDAA2E;AAAlE,+GAAA,YAAY,OAAA;AACrB,+CAUwB;AATtB,iHAAA,iBAAiB,OAAA;AACjB,6GAAA,aAAa,OAAA;AACb,oHAAA,oBAAoB,OAAA;AACpB,oHAAA,oBAAoB,OAAA;AACpB,kHAAA,kBAAkB,OAAA;AAClB,6GAAA,aAAa,OAAA;AAKf,iDAUyB;AATvB,8GAAA,aAAa,OAAA;AACb,sHAAA,qBAAqB,OAAA;AASvB,uCAOoB;AANlB,2GAAA,eAAe,OAAA;AACf,4GAAA,gBAAgB,OAAA;AAChB,kHAAA,sBAAsB,OAAA;AAKxB,2CAMsB;AALpB,+GAAA,iBAAiB,OAAA;AACjB,6GAAA,eAAe,OAAA;AAKjB,+DAMgC;AAL9B,yHAAA,iBAAiB,OAAA;AACjB,+HAAA,uBAAuB,OAAA;AACvB,4HAAA,oBAAoB,OAAA;AACpB,oIAAA,4BAA4B,OAAA;AAG9B,4EAA4E;AAC5E,+EAA+E;AAC/E,6EAA6E;AAC7E,mCAAmC;AACnC,mDAagC;AAZ9B,oHAAA,wBAAwB,OAAA;AACxB,qHAAA,yBAAyB,OAAA;AACzB,gHAAA,oBAAoB,OAAA;AACpB,uHAAA,2BAA2B,OAAA;AAU7B,+CAOwB;AANtB,uHAAA,uBAAuB,OAAA;AACvB,kHAAA,kBAAkB,OAAA;AAClB,gHAAA,gBAAgB,OAAA;AAChB,gHAAA,gBAAgB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/artifacts/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAEH,uCAkBoB;AAjBlB,kHAAA,sBAAsB,OAAA;AACtB,gHAAA,oBAAoB,OAAA;AACpB,iHAAA,qBAAqB,OAAA;AAgBvB,yCAAkF;AAAzE,gHAAA,mBAAmB,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,4GAAA,eAAe,OAAA;AAE5D,+DAKgC;AAJ9B,yHAAA,iBAAiB,OAAA;AACjB,4IAAA,oCAAoC,OAAA;AAItC,uDAI4B;AAH1B,iHAAA,aAAa,OAAA;AACb,+HAAA,2BAA2B,OAAA;AAG7B,2DAK8B;AAJ5B,qHAAA,eAAe,OAAA;AACf,kIAAA,4BAA4B,OAAA;AAI9B,mDAAwE;AAA/D,6GAAA,WAAW,OAAA;AACpB,qDAA2E;AAAlE,+GAAA,YAAY,OAAA;AACrB,+CAUwB;AATtB,iHAAA,iBAAiB,OAAA;AACjB,6GAAA,aAAa,OAAA;AACb,oHAAA,oBAAoB,OAAA;AACpB,oHAAA,oBAAoB,OAAA;AACpB,kHAAA,kBAAkB,OAAA;AAClB,6GAAA,aAAa,OAAA;AAKf,iDAUyB;AATvB,8GAAA,aAAa,OAAA;AACb,sHAAA,qBAAqB,OAAA;AASvB,uCAOoB;AANlB,2GAAA,eAAe,OAAA;AACf,4GAAA,gBAAgB,OAAA;AAChB,kHAAA,sBAAsB,OAAA;AAKxB,2CAMsB;AALpB,+GAAA,iBAAiB,OAAA;AACjB,6GAAA,eAAe,OAAA;AAKjB,+DASgC;AAR9B,yHAAA,iBAAiB,OAAA;AACjB,+HAAA,uBAAuB,OAAA;AACvB,4HAAA,oBAAoB,OAAA;AACpB,gIAAA,wBAAwB,OAAA;AACxB,yHAAA,iBAAiB,OAAA;AACjB,oIAAA,4BAA4B,OAAA;AAI9B,4EAA4E;AAC5E,+EAA+E;AAC/E,6EAA6E;AAC7E,mCAAmC;AACnC,mDAagC;AAZ9B,oHAAA,wBAAwB,OAAA;AACxB,qHAAA,yBAAyB,OAAA;AACzB,gHAAA,oBAAoB,OAAA;AACpB,uHAAA,2BAA2B,OAAA;AAU7B,+CAOwB;AANtB,uHAAA,uBAAuB,OAAA;AACvB,kHAAA,kBAAkB,OAAA;AAClB,gHAAA,gBAAgB,OAAA;AAChB,gHAAA,gBAAgB,OAAA"}
@@ -33,10 +33,18 @@
33
33
  * than at whatever reads it next.
34
34
  */
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.recordingPutInput = exports.UnserializableRecordingError = exports.RECORDING_MEDIA_TYPE = exports.RECORDING_ARTIFACT_KIND = void 0;
36
+ exports.chartWalkPutInput = exports.recordingPutInput = exports.UnserializableRecordingError = exports.RECORDING_MEDIA_TYPE = exports.CHART_WALK_ARTIFACT_KIND = exports.RECORDING_ARTIFACT_KIND = void 0;
37
37
  /** The consumer vocabulary a run recording is stored under. One kind, exact
38
38
  * match — the same rule every `wants` declaration is judged by. */
39
39
  exports.RECORDING_ARTIFACT_KIND = 'recording/run';
40
+ /**
41
+ * The consumer vocabulary a chart WALK is stored under (9.76.0) — one row per
42
+ * execution step of a runbook's inner chart, with the decider evidence
43
+ * sentences in the `condition` rows. Namespaced by what it IS (`recording/…`,
44
+ * beside `recording/run`), never by what produced it: a walk is a recording
45
+ * projection, not a dataset that happens to mention stages.
46
+ */
47
+ exports.CHART_WALK_ARTIFACT_KIND = 'recording/chart-walk';
40
48
  /** The media type a recording is minted with. */
41
49
  exports.RECORDING_MEDIA_TYPE = 'application/json';
42
50
  /**
@@ -85,4 +93,42 @@ function recordingPutInput(recording, facts = {}) {
85
93
  };
86
94
  }
87
95
  exports.recordingPutInput = recordingPutInput;
96
+ /**
97
+ * Turn one chart walk into the `put` input that stores it under
98
+ * {@link CHART_WALK_ARTIFACT_KIND}.
99
+ *
100
+ * Pure, and serialized to JSON TEXT at the mint for exactly the reasons the
101
+ * run recording is (see the file header): a walk row must never be a live
102
+ * view into engine memory, and a walk JSON cannot carry could not cross any
103
+ * wire either.
104
+ *
105
+ * @throws UnserializableRecordingError when the rows cannot be
106
+ * JSON-serialized. Walk rows are projected to plain data upstream, so this
107
+ * firing means the projection let a live value through — fail at the mint,
108
+ * loudly.
109
+ */
110
+ function chartWalkPutInput(rows, facts = {}) {
111
+ let text;
112
+ try {
113
+ text = JSON.stringify(rows) ?? '';
114
+ }
115
+ catch (err) {
116
+ throw new UnserializableRecordingError(err instanceof Error ? err.message : String(err));
117
+ }
118
+ if (text === '') {
119
+ throw new UnserializableRecordingError('it serializes to nothing');
120
+ }
121
+ const origin = {
122
+ ...(facts.runId !== undefined && { runId: facts.runId }),
123
+ ...(facts.toolCallId !== undefined && { toolCallId: facts.toolCallId }),
124
+ };
125
+ return {
126
+ kind: exports.CHART_WALK_ARTIFACT_KIND,
127
+ mediaType: exports.RECORDING_MEDIA_TYPE,
128
+ data: text,
129
+ label: facts.label ?? (facts.toolName !== undefined ? `${facts.toolName} walk` : 'chart walk'),
130
+ ...(Object.keys(origin).length > 0 && { origin }),
131
+ };
132
+ }
133
+ exports.chartWalkPutInput = chartWalkPutInput;
88
134
  //# sourceMappingURL=recordingArtifact.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"recordingArtifact.js","sourceRoot":"","sources":["../../src/artifacts/recordingArtifact.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;;;AAIH;oEACoE;AACvD,QAAA,uBAAuB,GAAG,eAAe,CAAC;AAEvD,iDAAiD;AACpC,QAAA,oBAAoB,GAAG,kBAAkB,CAAC;AAoBvD;;;;;GAKG;AACH,MAAa,4BAA6B,SAAQ,KAAK;IAC5C,IAAI,GAAG,8BAAuC,CAAC;IAExD,YAAY,MAAc;QACxB,KAAK,CACH,0DAA0D,MAAM,mBAAmB;YACjF,kFAAkF;YAClF,oFAAoF;YACpF,kFAAkF,CACrF,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC7C,CAAC;CACF;AAZD,oEAYC;AAED;;;;;;;;GAQG;AACH,SAAgB,iBAAiB,CAC/B,SAAkB,EAClB,QAA4B,EAAE;IAE9B,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,4BAA4B,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,4BAA4B,CAAC,0BAA0B,CAAC,CAAC;IACrE,CAAC;IACD,OAAO;QACL,IAAI,EAAE,+BAAuB;QAC7B,SAAS,EAAE,4BAAoB;QAC/B,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC;QAC1F,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;KACrE,CAAC;AACJ,CAAC;AApBD,8CAoBC"}
1
+ {"version":3,"file":"recordingArtifact.js","sourceRoot":"","sources":["../../src/artifacts/recordingArtifact.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;;;AAIH;oEACoE;AACvD,QAAA,uBAAuB,GAAG,eAAe,CAAC;AAEvD;;;;;;GAMG;AACU,QAAA,wBAAwB,GAAG,sBAAsB,CAAC;AAE/D,iDAAiD;AACpC,QAAA,oBAAoB,GAAG,kBAAkB,CAAC;AAoBvD;;;;;GAKG;AACH,MAAa,4BAA6B,SAAQ,KAAK;IAC5C,IAAI,GAAG,8BAAuC,CAAC;IAExD,YAAY,MAAc;QACxB,KAAK,CACH,0DAA0D,MAAM,mBAAmB;YACjF,kFAAkF;YAClF,oFAAoF;YACpF,kFAAkF,CACrF,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC7C,CAAC;CACF;AAZD,oEAYC;AAED;;;;;;;;GAQG;AACH,SAAgB,iBAAiB,CAC/B,SAAkB,EAClB,QAA4B,EAAE;IAE9B,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,4BAA4B,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,4BAA4B,CAAC,0BAA0B,CAAC,CAAC;IACrE,CAAC;IACD,OAAO;QACL,IAAI,EAAE,+BAAuB;QAC7B,SAAS,EAAE,4BAAoB;QAC/B,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC;QAC1F,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;KACrE,CAAC;AACJ,CAAC;AApBD,8CAoBC;AAgBD;;;;;;;;;;;;;GAaG;AACH,SAAgB,iBAAiB,CAC/B,IAAwB,EACxB,QAA4B,EAAE;IAE9B,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,4BAA4B,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,4BAA4B,CAAC,0BAA0B,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,MAAM,GAAG;QACb,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QACxD,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;KACxE,CAAC;IACF,OAAO;QACL,IAAI,EAAE,gCAAwB;QAC9B,SAAS,EAAE,4BAAoB;QAC/B,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC;QAC9F,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;KAClD,CAAC;AACJ,CAAC;AAxBD,8CAwBC"}
@@ -105,6 +105,7 @@ const skillTools_js_1 = require("../lib/injection-engine/skillTools.js");
105
105
  const skillSteps_js_1 = require("../lib/injection-engine/skillSteps.js");
106
106
  const stepNudge_js_1 = require("./agent/stages/stepNudge.js");
107
107
  const evidenceRecheck_js_1 = require("./agent/stages/evidenceRecheck.js");
108
+ const stagedRefs_js_1 = require("./agent/stagedRefs.js");
108
109
  const wrapUp_js_1 = require("./agent/stages/wrapUp.js");
109
110
  const gate_js_1 = require("./agent/evidence/gate.js");
110
111
  const errors_js_2 = require("./agent/evidence/errors.js");
@@ -2901,6 +2902,36 @@ class Agent extends RunnerBase_js_1.RunnerBase {
2901
2902
  }
2902
2903
  }
2903
2904
  }
2905
+ // The composedOf drift gate (9.76.0), judged HERE and not at defineTool —
2906
+ // the one moment the catalog is complete. A tool that declares its
2907
+ // ingredients (`composedOf`, the runbookAsTool law) whose ingredient was
2908
+ // renamed or never registered fails the BUILD by name, instead of failing
2909
+ // its first run inside a stage. Tools delivered by a ToolProvider are
2910
+ // invisible to this check (no build-time list — the 9.72.0 caveat), so
2911
+ // with a provider configured an unmatched name is a dev-mode heads-up
2912
+ // rather than a refusal: the ingredient may genuinely arrive at dispatch.
2913
+ for (const [composedName, registered] of registryByName) {
2914
+ for (const ingredient of registered.composedOf ?? []) {
2915
+ if (registryByName.has(ingredient))
2916
+ continue;
2917
+ if (this.externalToolProvider !== undefined) {
2918
+ if ((0, footprintjs_2.isDevMode)()) {
2919
+ // eslint-disable-next-line no-console
2920
+ console.warn(`[agentfootprint] tool '${composedName}' declares composedOf ingredient ` +
2921
+ `'${ingredient}', which is not in the static catalog. A ToolProvider is ` +
2922
+ `configured, so it may arrive at dispatch — but provider-delivered tools ` +
2923
+ `cannot be drift-checked at build. If '${ingredient}' is static, this is ` +
2924
+ `the rename the check exists to catch.`);
2925
+ }
2926
+ continue;
2927
+ }
2928
+ throw new Error(`Agent: tool '${composedName}' declares composedOf ingredient '${ingredient}', ` +
2929
+ `but no tool of that name is registered on this agent. The declaration names ` +
2930
+ `the tools its procedure calls through ctx.tools, and an ingredient that is ` +
2931
+ `not in the dispatch map fails at its first inner call — refused at build ` +
2932
+ `instead. Register '${ingredient}' (or fix the name in composedOf).`);
2933
+ }
2934
+ }
2904
2935
  // Late-bind toolSchemas into the seed stage's deps (the factory was
2905
2936
  // built earlier with a getter; this resolves the actual value).
2906
2937
  toolSchemasResolved = toolSchemas;
@@ -3041,6 +3072,11 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3041
3072
  .filter(([, tool]) => tool.argumentsFrom !== undefined)
3042
3073
  .map(([name, tool]) => [name, tool.argumentsFrom]));
3043
3074
  this.integrityDanglingPresent = toolGrounding.size > 0;
3075
+ // The staged-refs join's other half (grounded numbers): `Tool.wants` by
3076
+ // tool name, harvested the same way and with the same ToolProvider caveat.
3077
+ // Consumed only by the evidence gate (the callLLM nudge and the recheck
3078
+ // correction), so an agent without the gate never reads it.
3079
+ const toolWants = (0, stagedRefs_js_1.toolWantsOf)(registryByName);
3044
3080
  const toolsSubflow = (0, buildToolsSlot_js_1.buildToolsSlot)({
3045
3081
  tools: toolSchemas,
3046
3082
  ...(toolOwners.size > 0 && { toolOwners }),
@@ -3074,6 +3110,10 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3074
3110
  // The declared argument-ground edges (9.60.0) — value-conditional, so
3075
3111
  // an agent whose tools declare none runs the exact bytes it always did.
3076
3112
  ...(toolGrounding.size > 0 && { toolGrounding }),
3113
+ // The staged-refs nudge (grounded numbers) — armed only when the dial
3114
+ // is ON and a registered tool declares `wants`; otherwise the stage
3115
+ // reads nothing new and every request keeps its exact bytes.
3116
+ ...(this.evidenceGate?.nudge === true && toolWants.size > 0 && { toolWants }),
3077
3117
  // The external-ground door (9.72.0) — value-conditional for the same
3078
3118
  // reason: no provider, no key, byte-identical corpus assembly.
3079
3119
  ...(this.externalGrounds !== undefined && { externalGrounds: this.externalGrounds }),
@@ -3319,7 +3359,19 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3319
3359
  ...(this.evidenceGate !== undefined && {
3320
3360
  hasEvidenceGate: true,
3321
3361
  ...(this.evidenceGate.posture !== 'assist' && {
3322
- evidenceRecheckStage: (0, evidenceRecheck_js_1.buildEvidenceRecheckStage)(this.evidenceGate),
3362
+ evidenceRecheckStage: (0, evidenceRecheck_js_1.buildEvidenceRecheckStage)(this.evidenceGate,
3363
+ // The staged-refs join (grounded numbers) — the correction names
3364
+ // the refs and the spender when the run declared both. Threaded
3365
+ // whenever a `wants` tool exists (not gated on `nudge`: a
3366
+ // revision that cannot say HOW to compute leaves the model to
3367
+ // head-math again); absent for every agent without one, so the
3368
+ // correction keeps its exact bytes.
3369
+ toolWants.size > 0
3370
+ ? {
3371
+ toolWants,
3372
+ staticToolNames: () => toolSchemasResolved.map((t) => t.name),
3373
+ }
3374
+ : undefined),
3323
3375
  }),
3324
3376
  }),
3325
3377
  // Escalation (9.19.0): the grouped chart threads `skillEscalated`