agentfootprint 9.100.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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +144 -0
- package/CLAUDE.md +1 -1
- package/README.md +1 -0
- package/ai-instructions/claude-code/SKILL.md +1 -1
- package/dist/core/Agent.js +123 -2
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/AgentBuilder.js +113 -2
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/buildDynamicAgentChart.js +11 -0
- package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
- package/dist/core/agent/buildToolRegistry.js +38 -0
- package/dist/core/agent/buildToolRegistry.js.map +1 -1
- package/dist/core/agent/findings/ledger.js +237 -0
- package/dist/core/agent/findings/ledger.js.map +1 -0
- package/dist/core/agent/findings/reserved.js +334 -0
- package/dist/core/agent/findings/reserved.js.map +1 -0
- package/dist/core/agent/findings/serve.js +388 -0
- package/dist/core/agent/findings/serve.js.map +1 -0
- package/dist/core/agent/findings/types.js +47 -0
- package/dist/core/agent/findings/types.js.map +1 -0
- package/dist/core/agent/stages/callLLM.js +78 -10
- package/dist/core/agent/stages/callLLM.js.map +1 -1
- package/dist/core/agent/stages/outputRetry.js +5 -0
- package/dist/core/agent/stages/outputRetry.js.map +1 -1
- package/dist/core/agent/stages/route.js +65 -12
- package/dist/core/agent/stages/route.js.map +1 -1
- package/dist/core/agent/stages/seed.js +28 -1
- package/dist/core/agent/stages/seed.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +78 -11
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/runCheckpoint.js +64 -1
- package/dist/core/runCheckpoint.js.map +1 -1
- package/dist/core/slots/buildToolsSlot.js +13 -1
- package/dist/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +45 -0
- package/dist/esm/core/Agent.js +123 -2
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.d.ts +60 -0
- package/dist/esm/core/agent/AgentBuilder.js +113 -2
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
- package/dist/esm/core/agent/buildDynamicAgentChart.js +11 -0
- package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
- package/dist/esm/core/agent/buildToolRegistry.d.ts +7 -0
- package/dist/esm/core/agent/buildToolRegistry.js +38 -0
- package/dist/esm/core/agent/buildToolRegistry.js.map +1 -1
- package/dist/esm/core/agent/findings/ledger.d.ts +95 -0
- package/dist/esm/core/agent/findings/ledger.js +230 -0
- package/dist/esm/core/agent/findings/ledger.js.map +1 -0
- package/dist/esm/core/agent/findings/reserved.d.ts +95 -0
- package/dist/esm/core/agent/findings/reserved.js +326 -0
- package/dist/esm/core/agent/findings/reserved.js.map +1 -0
- package/dist/esm/core/agent/findings/serve.d.ts +136 -0
- package/dist/esm/core/agent/findings/serve.js +381 -0
- package/dist/esm/core/agent/findings/serve.js.map +1 -0
- package/dist/esm/core/agent/findings/types.d.ts +145 -0
- package/dist/esm/core/agent/findings/types.js +44 -0
- package/dist/esm/core/agent/findings/types.js.map +1 -0
- package/dist/esm/core/agent/stages/callLLM.d.ts +32 -0
- package/dist/esm/core/agent/stages/callLLM.js +78 -10
- package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
- package/dist/esm/core/agent/stages/outputRetry.js +5 -0
- package/dist/esm/core/agent/stages/outputRetry.js.map +1 -1
- package/dist/esm/core/agent/stages/route.d.ts +8 -1
- package/dist/esm/core/agent/stages/route.js +65 -12
- package/dist/esm/core/agent/stages/route.js.map +1 -1
- package/dist/esm/core/agent/stages/seed.d.ts +28 -0
- package/dist/esm/core/agent/stages/seed.js +28 -1
- package/dist/esm/core/agent/stages/seed.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +17 -0
- package/dist/esm/core/agent/stages/toolCalls.js +76 -9
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +74 -0
- package/dist/esm/core/runCheckpoint.d.ts +27 -1
- package/dist/esm/core/runCheckpoint.js +64 -1
- package/dist/esm/core/runCheckpoint.js.map +1 -1
- package/dist/esm/core/slots/buildToolsSlot.d.ts +9 -0
- package/dist/esm/core/slots/buildToolsSlot.js +13 -1
- package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +59 -0
- package/dist/esm/events/registry.d.ts +7 -1
- package/dist/esm/events/registry.js +6 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/events/types.d.ts +3 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/time-travel/servedView.js +54 -10
- package/dist/esm/lib/time-travel/servedView.js.map +1 -1
- package/dist/events/registry.js +6 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/index.js +11 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/time-travel/servedView.js +54 -10
- package/dist/lib/time-travel/servedView.js.map +1 -1
- package/dist/types/core/Agent.d.ts +45 -0
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +60 -0
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
- package/dist/types/core/agent/buildToolRegistry.d.ts +7 -0
- package/dist/types/core/agent/buildToolRegistry.d.ts.map +1 -1
- package/dist/types/core/agent/findings/ledger.d.ts +96 -0
- package/dist/types/core/agent/findings/ledger.d.ts.map +1 -0
- package/dist/types/core/agent/findings/reserved.d.ts +96 -0
- package/dist/types/core/agent/findings/reserved.d.ts.map +1 -0
- package/dist/types/core/agent/findings/serve.d.ts +137 -0
- package/dist/types/core/agent/findings/serve.d.ts.map +1 -0
- package/dist/types/core/agent/findings/types.d.ts +146 -0
- package/dist/types/core/agent/findings/types.d.ts.map +1 -0
- package/dist/types/core/agent/stages/callLLM.d.ts +32 -0
- package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
- package/dist/types/core/agent/stages/outputRetry.d.ts.map +1 -1
- package/dist/types/core/agent/stages/route.d.ts +8 -1
- package/dist/types/core/agent/stages/route.d.ts.map +1 -1
- package/dist/types/core/agent/stages/seed.d.ts +28 -0
- package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +17 -0
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +74 -0
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/runCheckpoint.d.ts +27 -1
- package/dist/types/core/runCheckpoint.d.ts.map +1 -1
- package/dist/types/core/slots/buildToolsSlot.d.ts +9 -0
- package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +59 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +7 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/events/types.d.ts +3 -1
- package/dist/types/events/types.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/time-travel/servedView.d.ts.map +1 -1
- package/package.json +2 -1
package/AGENTS.md
CHANGED
|
@@ -544,7 +544,7 @@ files `agentfootprint.tools.coverage_declared`; with
|
|
|
544
544
|
to the final answer, which is how a limit survives a model that would rather not
|
|
545
545
|
mention it.
|
|
546
546
|
|
|
547
|
-
### Observability —
|
|
547
|
+
### Observability — 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,150 @@ 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
|
+
|
|
8
152
|
## [9.100.0] - 2026-09-16
|
|
9
153
|
|
|
10
154
|
### Added — the delivered answer names its shape guarantee
|
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/** →
|
|
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
|
-
**
|
|
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
|
|
package/dist/core/Agent.js
CHANGED
|
@@ -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
|