agentfootprint 9.101.1 → 9.103.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +241 -0
- package/dist/core/Agent.js +98 -2
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/AgentBuilder.js +26 -4
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/buildAgentChart.js +11 -0
- package/dist/core/agent/buildAgentChart.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/findings/ledger.js +34 -10
- package/dist/core/agent/findings/ledger.js.map +1 -1
- package/dist/core/agent/findings/offer.js +179 -0
- package/dist/core/agent/findings/offer.js.map +1 -0
- package/dist/core/agent/findings/reserved.js +167 -14
- package/dist/core/agent/findings/reserved.js.map +1 -1
- package/dist/core/agent/findings/serve.js +72 -49
- package/dist/core/agent/findings/serve.js.map +1 -1
- package/dist/core/agent/findings/types.js +10 -1
- package/dist/core/agent/findings/types.js.map +1 -1
- package/dist/core/agent/stages/callLLM.js +1 -1
- package/dist/core/agent/stages/callLLM.js.map +1 -1
- package/dist/core/agent/stages/route.js +7 -1
- package/dist/core/agent/stages/route.js.map +1 -1
- package/dist/core/agent/stages/seed.js +16 -2
- package/dist/core/agent/stages/seed.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +16 -10
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/agent/stages/window.js +110 -10
- package/dist/core/agent/stages/window.js.map +1 -1
- package/dist/core/agent/window/index.js +3 -1
- package/dist/core/agent/window/index.js.map +1 -1
- package/dist/core/agent/window/ledgerFactPins.js +200 -0
- package/dist/core/agent/window/ledgerFactPins.js.map +1 -0
- package/dist/core/agent/window/turns.js +45 -11
- package/dist/core/agent/window/turns.js.map +1 -1
- package/dist/core/slots/buildToolsSlot.js +23 -1
- package/dist/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +14 -2
- package/dist/esm/core/Agent.js +98 -2
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.d.ts +13 -3
- package/dist/esm/core/agent/AgentBuilder.js +26 -4
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
- package/dist/esm/core/agent/buildAgentChart.d.ts +14 -0
- package/dist/esm/core/agent/buildAgentChart.js +11 -0
- package/dist/esm/core/agent/buildAgentChart.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/findings/ledger.d.ts +19 -9
- package/dist/esm/core/agent/findings/ledger.js +34 -10
- package/dist/esm/core/agent/findings/ledger.js.map +1 -1
- package/dist/esm/core/agent/findings/offer.d.ts +112 -0
- package/dist/esm/core/agent/findings/offer.js +171 -0
- package/dist/esm/core/agent/findings/offer.js.map +1 -0
- package/dist/esm/core/agent/findings/reserved.d.ts +53 -7
- package/dist/esm/core/agent/findings/reserved.js +166 -13
- package/dist/esm/core/agent/findings/reserved.js.map +1 -1
- package/dist/esm/core/agent/findings/serve.d.ts +39 -11
- package/dist/esm/core/agent/findings/serve.js +74 -47
- package/dist/esm/core/agent/findings/serve.js.map +1 -1
- package/dist/esm/core/agent/findings/types.d.ts +36 -2
- package/dist/esm/core/agent/findings/types.js +9 -0
- package/dist/esm/core/agent/findings/types.js.map +1 -1
- package/dist/esm/core/agent/stages/callLLM.d.ts +9 -1
- package/dist/esm/core/agent/stages/callLLM.js +1 -1
- package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
- package/dist/esm/core/agent/stages/route.js +7 -1
- package/dist/esm/core/agent/stages/route.js.map +1 -1
- package/dist/esm/core/agent/stages/seed.d.ts +10 -0
- package/dist/esm/core/agent/stages/seed.js +16 -2
- package/dist/esm/core/agent/stages/seed.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.js +16 -10
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/stages/window.d.ts +22 -0
- package/dist/esm/core/agent/stages/window.js +111 -11
- package/dist/esm/core/agent/stages/window.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +74 -4
- package/dist/esm/core/agent/window/index.d.ts +1 -0
- package/dist/esm/core/agent/window/index.js +1 -0
- package/dist/esm/core/agent/window/index.js.map +1 -1
- package/dist/esm/core/agent/window/ledgerFactPins.d.ts +147 -0
- package/dist/esm/core/agent/window/ledgerFactPins.js +194 -0
- package/dist/esm/core/agent/window/ledgerFactPins.js.map +1 -0
- package/dist/esm/core/agent/window/strategy.d.ts +25 -0
- package/dist/esm/core/agent/window/turns.d.ts +25 -0
- package/dist/esm/core/agent/window/turns.js +45 -11
- package/dist/esm/core/agent/window/turns.js.map +1 -1
- package/dist/esm/core/agent/window/types.d.ts +64 -0
- package/dist/esm/core/slots/buildToolsSlot.d.ts +11 -0
- package/dist/esm/core/slots/buildToolsSlot.js +23 -1
- package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +6 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/time-travel/servedView.js +15 -3
- package/dist/esm/lib/time-travel/servedView.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/time-travel/servedView.js +15 -3
- package/dist/lib/time-travel/servedView.js.map +1 -1
- package/dist/types/core/Agent.d.ts +14 -2
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +13 -3
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/buildAgentChart.d.ts +14 -0
- package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
- package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
- package/dist/types/core/agent/findings/ledger.d.ts +19 -9
- package/dist/types/core/agent/findings/ledger.d.ts.map +1 -1
- package/dist/types/core/agent/findings/offer.d.ts +113 -0
- package/dist/types/core/agent/findings/offer.d.ts.map +1 -0
- package/dist/types/core/agent/findings/reserved.d.ts +53 -7
- package/dist/types/core/agent/findings/reserved.d.ts.map +1 -1
- package/dist/types/core/agent/findings/serve.d.ts +39 -11
- package/dist/types/core/agent/findings/serve.d.ts.map +1 -1
- package/dist/types/core/agent/findings/types.d.ts +36 -2
- package/dist/types/core/agent/findings/types.d.ts.map +1 -1
- package/dist/types/core/agent/stages/callLLM.d.ts +9 -1
- package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
- package/dist/types/core/agent/stages/route.d.ts.map +1 -1
- package/dist/types/core/agent/stages/seed.d.ts +10 -0
- package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/stages/window.d.ts +22 -0
- package/dist/types/core/agent/stages/window.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +74 -4
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/agent/window/index.d.ts +1 -0
- package/dist/types/core/agent/window/index.d.ts.map +1 -1
- package/dist/types/core/agent/window/ledgerFactPins.d.ts +148 -0
- package/dist/types/core/agent/window/ledgerFactPins.d.ts.map +1 -0
- package/dist/types/core/agent/window/strategy.d.ts +25 -0
- package/dist/types/core/agent/window/strategy.d.ts.map +1 -1
- package/dist/types/core/agent/window/turns.d.ts +25 -0
- package/dist/types/core/agent/window/turns.d.ts.map +1 -1
- package/dist/types/core/agent/window/types.d.ts +64 -0
- package/dist/types/core/agent/window/types.d.ts.map +1 -1
- package/dist/types/core/slots/buildToolsSlot.d.ts +11 -0
- package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +6 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/time-travel/servedView.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,247 @@ 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.103.0] - 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Added — an answer-turn ask on the served ledger piece, as a dial
|
|
11
|
+
|
|
12
|
+
- `findings({ answerAsk: 'quote-facts' })` appends a second model-facing ask
|
|
13
|
+
to the served ledger piece — `FINDINGS_ANSWER_ASK`, its last section after
|
|
14
|
+
a blank line — telling the model HOW to answer from it: answer from the
|
|
15
|
+
`facts` lines and copy each value as written; an `evidenceRefs` or
|
|
16
|
+
`nextSteps` result is unsettled, say so; a `{"collapsed":true,…}` ticket
|
|
17
|
+
carries no data (judged noise or ruled out, or its fact already listed),
|
|
18
|
+
do not draw on it; an undeclared result is
|
|
19
|
+
served in full and may be used; never invent a value. It says what the
|
|
20
|
+
model may do and promises nothing (judged by `unprovable` at the strictest
|
|
21
|
+
lifetime, registered in the model-facing inventory), and it is a constant,
|
|
22
|
+
so the piece's cache law holds with it. The default is `'none'` and the
|
|
23
|
+
dial is BENCH-GATED like `'ledger-only'`: it ships so
|
|
24
|
+
`bench/findings-shuffle.mjs`'s new fourth condition, `ledger+ask`, can
|
|
25
|
+
score it on a real model on `facts-in-answer` against `ledger-and-facts` —
|
|
26
|
+
the design page's fourth run measured one fact value in twelve restated
|
|
27
|
+
when the piece is served, and asked for this instruction variant next. On
|
|
28
|
+
the mock that row equals `ledger-and-facts` to the digit (the scripted
|
|
29
|
+
model ignores prose; the header says so). The dial rides the
|
|
30
|
+
`findingsServe` thread exactly — `Agent.ts` to seed and to call-llm,
|
|
31
|
+
value-conditionally — and lands on the record as the run constant
|
|
32
|
+
`AgentState.findingsAnswerAsk`, written ONLY under `'quote-facts'`, so an
|
|
33
|
+
armed agent on the default commits the key set it committed in 9.102.0,
|
|
34
|
+
every unarmed agent is byte-identical, and `servedAt` re-appends the ask
|
|
35
|
+
from the record by construction. A bad value is refused at build at either
|
|
36
|
+
door. Pinned: `findings/serve.test.ts` (the ask appended only under the
|
|
37
|
+
dial, exact bytes, absent by default, never alone), `findings-served.test.ts`
|
|
38
|
+
(both chart shapes end the served system text with the ask; without the
|
|
39
|
+
dial byte-identical), `findings-declarations.test.ts` (the option, the run
|
|
40
|
+
constant only when armed and on, the refusal), `receipt-conformance.test.ts`
|
|
41
|
+
(the rebuild byte-equal under the dial); all 18 byte-identity references
|
|
42
|
+
pass untouched.
|
|
43
|
+
|
|
44
|
+
### Measured — the ask on two hosted models (`bench/findings-shuffle.mjs`, noise 4 at the end of the order, 1,000-token payloads, ten runs per condition)
|
|
45
|
+
|
|
46
|
+
- Claude Sonnet 5: facts in the answer 0.883 with the ledger alone → 0.950 with the ask (1.000 without the ledger); noise cited 0.100 with either → 0.300 without the ledger; drift 0.70 → 0.40.
|
|
47
|
+
- Claude Haiku 4.5: facts 0.983 → 1.000 with the ask; noise cited 0.000 (0.100 without the ledger); drift 0.30 → 0.20.
|
|
48
|
+
- Collapsing facts too (`serve: 'ledger-only'`) was the worst row on both models and stays bench-gated. Full tables: docs/design/2026-09-findings-ledger-real-model.md.
|
|
49
|
+
|
|
50
|
+
## [9.102.0] - 2026-09-17
|
|
51
|
+
|
|
52
|
+
### Added — a declared fact stays in the window; noise leaves first
|
|
53
|
+
|
|
54
|
+
- The window reads the findings ledger. On an agent with `.findings()` and a
|
|
55
|
+
window strategy, a turn whose tool result the model declared a `fact` is
|
|
56
|
+
HELD beyond `keepRecentTurns` — newest first, up to `keepLedgerFacts`
|
|
57
|
+
(default 4; `false` or `0` for no hold) — and the refusal is on the record
|
|
58
|
+
by name: `WindowRefusalReason` gains `'ledger-fact'`. It is a bounded hold
|
|
59
|
+
in the refusal engine, the content-aware sibling of the last-tool-result
|
|
60
|
+
pin (`ledgerFactPinsOf` beside `toolResultPinsOf`, admitted by the same
|
|
61
|
+
ceiling-spender in `planRemoval`), so `slidingWindow`, `tokenBudget`,
|
|
62
|
+
`summarizeOldest` and a consumer-written strategy all inherit it through
|
|
63
|
+
`planRemoval` — no new strategy file, none of the three shipped ones changed
|
|
64
|
+
a byte, the contiguous span, the drop ladder and the meter's single-seam
|
|
65
|
+
rebase untouched. 'Noise first' follows with no second mechanism: noise,
|
|
66
|
+
ruled-out, open and undeclared turns are unpinned and leave oldest-first as
|
|
67
|
+
they always did, and a judged noise turn that outlives a fact is already a
|
|
68
|
+
ticket on the wire. Why: the served piece restored a declared fact to the
|
|
69
|
+
answer turn, but under a window the fact's RESULT still left by recency —
|
|
70
|
+
the refusal engine saw a declared fact and a declared noise result as the
|
|
71
|
+
same bytes. By the model's claim only: a turn is the removal unit and its
|
|
72
|
+
standing is its most valuable result's (`fact > open > undeclared >
|
|
73
|
+
ruled-out > noise`); a result the model never named is undeclared and is not
|
|
74
|
+
held; the library reads no result's text to decide otherwise.
|
|
75
|
+
- A fact hold never exists without its ceiling and its stand-down. The
|
|
76
|
+
ceiling is spent newest first and a held turn already inside
|
|
77
|
+
`keepRecentTurns` spends no slot (the free-pin law); the turns it turned away
|
|
78
|
+
are `yielded` on the record; nothing at or before the current request is
|
|
79
|
+
pinnable. The stand-down is the pin's: when the two previous visits removed
|
|
80
|
+
nothing and named only pins, the fact pins release for one visit and the
|
|
81
|
+
record says so. It reads BOTH pin names (`'last-tool-result'` and/or
|
|
82
|
+
`'ledger-fact'`), because a turn held by both pins is reported under the
|
|
83
|
+
recency pin's name — a fact stand-down reading only its own would never see
|
|
84
|
+
that turn blocking, and the two pins would alternate under each other's
|
|
85
|
+
name with the window never shrinking (derived, pinned by test, never
|
|
86
|
+
shipped). The recency pin's own stand-down reads only its own name, so its
|
|
87
|
+
9.57.0 rule is unchanged.
|
|
88
|
+
- The record says what was held and whose standing left. `WindowRecord`
|
|
89
|
+
gains two optional keys, present only on an armed agent and filed by the
|
|
90
|
+
STAGE so a consumer-written strategy's record carries them too:
|
|
91
|
+
`ledgerFacts` (the `WindowObservations` shape — `pinned`, `yielded`,
|
|
92
|
+
`limit` = `keepLedgerFacts`, `standDown: true` on the visit it released)
|
|
93
|
+
and `droppedStandings` (`{ toolCallId, standing? }` for every tool result
|
|
94
|
+
that left; `standing` absent is undeclared, never a verdict the library
|
|
95
|
+
inferred). `WindowStrategyInput.standingOf?` hands a strategy a turn's
|
|
96
|
+
declared standing, bound by the stage from ONE read of the ledger and
|
|
97
|
+
absent on an unarmed agent — a strategy never reads scope for it; it is for
|
|
98
|
+
ordering or reporting among what the engine left removable, never for
|
|
99
|
+
inferring. What the model is TOLD about a drop is unchanged: the notice
|
|
100
|
+
names tools and counts, never a standing, never the model's own line.
|
|
101
|
+
- `keepLedgerFacts` is live: `.findings({ keepLedgerFacts })` and
|
|
102
|
+
`Agent.create({ keepLedgerFacts })` (the `.findings()` door wins when both
|
|
103
|
+
are given), resolved once at build and validated there — a negative or
|
|
104
|
+
non-integer value is refused, never mid-run. Without `.findings()` the
|
|
105
|
+
option is accepted and does nothing (the keepLastToolResults-without-a-window
|
|
106
|
+
precedent). Exported from the root: `LedgerFactPin` (the candidate a hold is
|
|
107
|
+
built from: `toolCallIds`, `toolName`, `turnIndex`, `messageIndex`, `chars`),
|
|
108
|
+
beside the widened `WindowRefusalReason` and the `WindowRecord` additions.
|
|
109
|
+
- Measured (`npm run bench:findings`, mock provider, 30 tool calls, a planted
|
|
110
|
+
fact every third, a sliding window keeping 6 turns): the results of 2 of 10
|
|
111
|
+
planted facts reached the answer turn verbatim by recency alone, 6 under the
|
|
112
|
+
default ceiling and 8 under a ceiling of 6, while the piece carried 9 of 10
|
|
113
|
+
on every armed row (the last batch is undeclared by the no-outputSchema law)
|
|
114
|
+
and the noise share of tool-result bytes on the wire stayed at the collapsed
|
|
115
|
+
level; every noise result left on every armed row, so the hold changed WHICH
|
|
116
|
+
facts left, not whether noise did. The design page has the print and its
|
|
117
|
+
reading; no real-model number exists yet.
|
|
118
|
+
|
|
119
|
+
### Unchanged — an agent without `.findings()` plans, records and sends the bytes it did before
|
|
120
|
+
|
|
121
|
+
- The hold, the ledger read, `standingOf`, `ledgerFacts` and `droppedStandings`
|
|
122
|
+
are all gated on the door: an unarmed window stage never reads
|
|
123
|
+
`findingsLedger` (pinned by a getter counting reads), hands its strategy the
|
|
124
|
+
exact input it always did, files the exact record it always did, and sends
|
|
125
|
+
the same request bytes — even when the model emits `_findings` on its own.
|
|
126
|
+
The 16 byte-identity references under `test/core/tools/reference/` pass
|
|
127
|
+
untouched; ONE new reference, `agent-findings-window`, was generated alone,
|
|
128
|
+
with what it holds read back from its bytes on the test file's header.
|
|
129
|
+
- `keepLedgerFacts: false` plans exactly as the unarmed window (the bench
|
|
130
|
+
checks it: same facts verbatim, same tool messages, same receipt count, no
|
|
131
|
+
fact held), so the hold is an addition to the plan, never a rewrite of it.
|
|
132
|
+
|
|
133
|
+
### Added — the ids the model may name are in the schema; the proposition before the call; a bench that can measure
|
|
134
|
+
|
|
135
|
+
- The offer. On a hosted model the ask "by its tool_result id" produced
|
|
136
|
+
standings named by ORDINAL (`"0"`, `"1"`), recorded honestly as `unknownId`
|
|
137
|
+
and settling nothing (`docs/design/2026-09-findings-ledger-real-model.md`).
|
|
138
|
+
So from the second call on, the reserved `_findings` property on every
|
|
139
|
+
served schema binds the ids the model may name: `previous[].toolCallId`
|
|
140
|
+
carries `enum: <the tool results on the wire with no standing yet, newest
|
|
141
|
+
first, at most 32>` and says "one of the ids listed; a result not listed
|
|
142
|
+
cannot be named here"; a clipped list states the cap. The list is what the
|
|
143
|
+
model can still READ: a result with no standing, a `fact` (stood on in the
|
|
144
|
+
piece, served verbatim under the default mode) and an `open` result (served
|
|
145
|
+
verbatim, carrying what would settle it) stay listed, so a later call can
|
|
146
|
+
REVISE a standing — `open` → `fact` when a call settles it, `fact` →
|
|
147
|
+
`ruled-out` when a conflict resolves; the fold's last-wins law is reachable
|
|
148
|
+
through the enum. A result the model declared `noise` or `ruled-out` leaves
|
|
149
|
+
the list: it is a ticket on the wire under every serve mode and the piece
|
|
150
|
+
carries a count or one line, so there is nothing left to re-judge (a wrong
|
|
151
|
+
`ruled-out` is answered by a new call). The instruction asks the model to
|
|
152
|
+
name a result again only to change its standing. An evicted result is not
|
|
153
|
+
on the list, and the first call — with nothing to name — serves the base
|
|
154
|
+
property by reference, byte-identical to 9.101.0. ONE owner of the two sets
|
|
155
|
+
(`findings/offer.ts`: `offeredResultIds` for the enum, `undeclaredIds` for
|
|
156
|
+
the piece's `undeclared:` line — the honest absence, a subset of the offer),
|
|
157
|
+
computed at the Tools mount, where the served history and the ledger meet
|
|
158
|
+
(the slot is an isolated subflow), under the same arm that decorates; bound
|
|
159
|
+
at the ONE decoration site and committed with the tool list, so
|
|
160
|
+
`servedAt(k).tools.schemas` holds exactly what was offered and
|
|
161
|
+
`receipt.tools.schemaHashes` moves when the offer does. The law holds at
|
|
162
|
+
the schema AND at the row: the offer is what the model may COPY, never what
|
|
163
|
+
the library resolves — an id outside it still files as written, `unknownId:
|
|
164
|
+
true`, never mapped to a position or a tool name — and every id INSIDE it
|
|
165
|
+
resolves, because a standing is identified against the same served history
|
|
166
|
+
the offer was read from (`findings/offer.ts · knownResults`: the served
|
|
167
|
+
`role: 'tool'` messages plus the previous batch), so an id copied from the
|
|
168
|
+
offer files with its tool name whichever batch the result came from (the
|
|
169
|
+
first cut resolved against the last batch only, and an offered older id
|
|
170
|
+
filed as `unknownId` — caught in review, never released). The instruction
|
|
171
|
+
asks the JSON answer, which has no schema to bind, for the id exactly as
|
|
172
|
+
the schema listed it.
|
|
173
|
+
- Named, not measured: from the second call on, an armed agent's tool schemas
|
|
174
|
+
vary per call (the enum), so a `'tools'` cache breakpoint cannot hit on such
|
|
175
|
+
a run and, on a prefix-cached wire, every breakpoint after it misses with
|
|
176
|
+
it — the system piece already moved the block on every declaring call
|
|
177
|
+
(step 3's recorded cost); the offer moves the tools prefix ahead of it. No
|
|
178
|
+
bench in the tree counts cache tokens; the lever not taken (the offer in
|
|
179
|
+
the request-only system piece, below the tools breakpoint — prose, which is
|
|
180
|
+
what failed on the real model) is on the design page.
|
|
181
|
+
- The proposition. `FindingsDeclaration` gains `proposition?: string` (what
|
|
182
|
+
the call tests; the schema recommends it when `basis` is `'exploratory'`)
|
|
183
|
+
and `predicts?: string` (what the result should show if it holds), declared
|
|
184
|
+
on the call before its result exists; both land on the `BasisRow`, each cut
|
|
185
|
+
at 240 chars with the cut stated in the text. `FindingsDeclaredPayload`
|
|
186
|
+
gains `hasProposition?: true` — a flag, never the text. The served piece
|
|
187
|
+
quotes the judged call's own proposition on `open` and `ruled-out` lines
|
|
188
|
+
(`… — tested: <proposition>`), never on a fact line; `predicts` is
|
|
189
|
+
record-only.
|
|
190
|
+
- The shuffle bench can measure (`bench/findings-shuffle.mjs`): `NOISE_AT`
|
|
191
|
+
(end / start / spread), `NOISE_SIZE` (about 250 / 1000 / 4000 tokens of
|
|
192
|
+
padding per noise record), noise values within 5% of a fact and never equal,
|
|
193
|
+
a `standing-accuracy` column (the share of the actor's standings on known
|
|
194
|
+
ids that agree with the planted truth — the harness knows the truth, so no
|
|
195
|
+
judge), and `--matrix` (24 cells for one model per invocation, the cost line
|
|
196
|
+
printed before the first call). Default mode prints the pre-packet numbers
|
|
197
|
+
to the digit and the mock smoke stays green. No hosted run was made in
|
|
198
|
+
this release; the real-model page holds the pre-offer tables and names the
|
|
199
|
+
number that must move.
|
|
200
|
+
|
|
201
|
+
### Changed — `.findings()` refuses `reactMode: 'classic'`
|
|
202
|
+
|
|
203
|
+
- The offer needs the tools slot recomposed every call, so `.findings()` is
|
|
204
|
+
REFUSED at build under `reactMode: 'classic'` — through both doors
|
|
205
|
+
(`.findings()` and `Agent.create({ findings })`), the `selfExplain` twin:
|
|
206
|
+
classic selects the Tools branch on turn 1 only, so an armed classic agent
|
|
207
|
+
would have served the offer-less base on every call and filed every
|
|
208
|
+
standing as `unknownId`, the number this packet exists to move silently
|
|
209
|
+
stuck at zero. 9.101.x accepted the combination and degraded it silently;
|
|
210
|
+
the message names the fix. `'dynamic'` (the default) and `'dynamic-grouped'`
|
|
211
|
+
are unchanged.
|
|
212
|
+
|
|
213
|
+
### Fixed — the choice seam's enum fence read the decorated schema
|
|
214
|
+
|
|
215
|
+
- Since 9.101.0, `withoutFindingsArgument` recognised the library's
|
|
216
|
+
decoration by REFERENCE only (`properties._findings ===
|
|
217
|
+
FINDINGS_ARGUMENT_SCHEMA`), and the reference never holds on the live path:
|
|
218
|
+
the served list `callLLM` reads is the committed `dynamicToolSchemas`, a
|
|
219
|
+
`structuredClone` of what the slot planted. So the choice seam's enum fence
|
|
220
|
+
(`declaredEnumValuesOf(withoutFindingsArgument(schema))`, the
|
|
221
|
+
`unsupported-argument` check) judged the model's argument values against a
|
|
222
|
+
schema that still carried `_findings`, and a value equal to one of the
|
|
223
|
+
findings vocabulary words — `direct`, `exploratory`, `low`, `medium`,
|
|
224
|
+
`high`, `fact`, `open`, `noise`, `ruled-out` — was excused as a declared
|
|
225
|
+
enum value the model was entitled to. The decoration is recognised by its
|
|
226
|
+
versioned marker now (the first sentence of the reserved property's
|
|
227
|
+
description), so the frozen base, an offer copy and a committed clone of
|
|
228
|
+
either are all peeled, and an author's own `_findings` is still read as
|
|
229
|
+
written. An armed agent may therefore file an `unsupported-argument`
|
|
230
|
+
finding it previously suppressed. Its unit test had passed because it
|
|
231
|
+
handed the function the live reference; `test/core/agent/findings/
|
|
232
|
+
reserved.test.ts` now pins the clone.
|
|
233
|
+
|
|
234
|
+
### Unchanged — the unarmed agent, and the two armed references regenerated alone
|
|
235
|
+
|
|
236
|
+
- An agent without `.findings()` maps no new key on the Tools mount, reads
|
|
237
|
+
neither `history` nor `findingsLedger` there, and serves and records the
|
|
238
|
+
bytes it did before: the 16 unarmed byte-identity references pass
|
|
239
|
+
untouched. `agent-findings` and `agent-findings-window` were regenerated
|
|
240
|
+
each alone (the rest copied aside and `cmp`-equal after) and their delta is
|
|
241
|
+
on the test file's header: the enum on every epoch after the first, a
|
|
242
|
+
`dynamicToolSchemas` write on each such epoch where an unchanged list used
|
|
243
|
+
to be an empty commit, the two new properties and the instruction's two new
|
|
244
|
+
lines, and the sizes that follow; regenerated once more, each alone, in the
|
|
245
|
+
second review for the revisable offer (a declared fact stays listed) and
|
|
246
|
+
the instruction's revised line. No message, ticket, ledger row, window
|
|
247
|
+
record or gap moved.
|
|
248
|
+
|
|
8
249
|
## [9.101.1] - 2026-09-16
|
|
9
250
|
|
|
10
251
|
### Fixed — the docs site's export-file ceiling
|
package/dist/core/Agent.js
CHANGED
|
@@ -142,6 +142,36 @@ const registry_js_1 = require("../thinking/registry.js");
|
|
|
142
142
|
// Public types (AgentOptions, AgentInput, AgentOutput) extracted to
|
|
143
143
|
// ./agent/types.ts and re-exported above (v2.11.1).
|
|
144
144
|
// AgentState extracted to ./agent/types.ts (v2.11.1).
|
|
145
|
+
/**
|
|
146
|
+
* The ledger-fact hold's default ceiling (9.102.0): how many fact turns the
|
|
147
|
+
* window holds beyond `keepRecentTurns` when `.findings()` and a window
|
|
148
|
+
* strategy are both configured and nobody named a number. Resolved in the
|
|
149
|
+
* constructor, ONCE, and threaded to `buildWindowStage` as a plain number —
|
|
150
|
+
* the stage applies no default of its own (absent = unarmed = no hold), so a
|
|
151
|
+
* reader has one place to look. `window/options.ts ·
|
|
152
|
+
* DEFAULT_KEEP_LAST_TOOL_RESULTS` is the pin's twin of this constant.
|
|
153
|
+
*/
|
|
154
|
+
const DEFAULT_KEEP_LEDGER_FACTS = 4;
|
|
155
|
+
/**
|
|
156
|
+
* Validate the `keepLedgerFacts` dial — `window/options.ts ·
|
|
157
|
+
* requireKeepLastToolResults`'s twin: the same rule, the same moment.
|
|
158
|
+
* Refused at construction, never mid-run.
|
|
159
|
+
*/
|
|
160
|
+
function requireKeepLedgerFacts(value, label) {
|
|
161
|
+
if (value === false)
|
|
162
|
+
return;
|
|
163
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
164
|
+
throw new Error(`${label}: keepLedgerFacts must be a whole number >= 0, or false, got ` +
|
|
165
|
+
`${String(value)}. It is how many fact turns the window holds beyond ` +
|
|
166
|
+
`keepRecentTurns under .findings(); 0 and false both switch the hold off.`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/** `false` → `0`; a number as named; nothing named → the default. */
|
|
170
|
+
function resolveKeepLedgerFacts(named) {
|
|
171
|
+
if (named === false)
|
|
172
|
+
return 0;
|
|
173
|
+
return named ?? DEFAULT_KEEP_LEDGER_FACTS;
|
|
174
|
+
}
|
|
145
175
|
class Agent extends RunnerBase_js_1.RunnerBase {
|
|
146
176
|
name;
|
|
147
177
|
id;
|
|
@@ -249,8 +279,13 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
249
279
|
* conditioned on, so an unarmed agent hands each stage exactly the deps
|
|
250
280
|
* it always did. `serve` is threaded to seed (the run constant
|
|
251
281
|
* `findingsServe`) and to call-llm (`findingsServe` in its deps) on an
|
|
252
|
-
* armed agent only
|
|
253
|
-
*
|
|
282
|
+
* armed agent only; `answerAsk` (9.103.0) rides the same two threads
|
|
283
|
+
* as `findingsAnswerAsk`, and only when it is `'quote-facts'` — the
|
|
284
|
+
* default `'none'` threads nothing. `keepLedgerFacts` is resolved once, here in the
|
|
285
|
+
* constructor (this door over `AgentOptions.keepLedgerFacts`), into the
|
|
286
|
+
* `keepLedgerFacts` field below and threaded to the window stage on an
|
|
287
|
+
* armed agent with a window, where `stages/window.ts · buildWindowStage`
|
|
288
|
+
* spends it as the `'ledger-fact'` pin ceiling. */
|
|
254
289
|
findingsOptions;
|
|
255
290
|
/** The opt-in tool-result ceiling in characters (9.11.0). Absent → results
|
|
256
291
|
* are never measured. See {@link AgentOptions.maxToolResultChars}. */
|
|
@@ -314,6 +349,13 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
314
349
|
/** The last-tool-result pin (9.57.0) — set only when the operator named a
|
|
315
350
|
* value other than the default 2. See AgentOptions.keepLastToolResults. */
|
|
316
351
|
keepLastToolResults;
|
|
352
|
+
/** The ledger-fact hold's ceiling (9.102.0), RESOLVED — set exactly when
|
|
353
|
+
* `.findings()` is on: `findings({ keepLedgerFacts })` over
|
|
354
|
+
* `AgentOptions.keepLedgerFacts`, `false` → `0`, nothing named →
|
|
355
|
+
* `DEFAULT_KEEP_LEDGER_FACTS`. Undefined on every unarmed agent, so the
|
|
356
|
+
* thread into the window stage reads as the decision it is. See
|
|
357
|
+
* AgentOptions.keepLedgerFacts. */
|
|
358
|
+
keepLedgerFacts;
|
|
317
359
|
/** See AgentOptions.integrityPosture (9.60.0). Default 'observe'. */
|
|
318
360
|
integrityPosture = 'observe';
|
|
319
361
|
/**
|
|
@@ -686,6 +728,35 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
686
728
|
(0, options_js_1.requireKeepLastToolResults)(opts.keepLastToolResults, 'Agent');
|
|
687
729
|
this.keepLastToolResults = opts.keepLastToolResults;
|
|
688
730
|
}
|
|
731
|
+
// Its content-aware sibling (9.102.0). The top-level door is refused
|
|
732
|
+
// HERE, whether or not `.findings()` is on — a dial that is silently
|
|
733
|
+
// ignored is a configuration mistake, not a runtime condition (the
|
|
734
|
+
// `.findings()` door was refused by the builder). The value is resolved
|
|
735
|
+
// ONCE, under the arm only: the `.findings()` door wins when both are
|
|
736
|
+
// given, `false` is `0`, nothing named is the default. Without
|
|
737
|
+
// `.findings()` there is no ledger to hold facts from, so the option is
|
|
738
|
+
// accepted and does nothing — the keepLastToolResults-without-a-window
|
|
739
|
+
// precedent.
|
|
740
|
+
if (opts.keepLedgerFacts !== undefined)
|
|
741
|
+
requireKeepLedgerFacts(opts.keepLedgerFacts, 'Agent');
|
|
742
|
+
if (this.findingsOptions !== undefined) {
|
|
743
|
+
// The ledger needs the tools slot recomposed every call: from the second
|
|
744
|
+
// call on, the served `_findings` property binds the ids the model may
|
|
745
|
+
// name (`findings/offer.ts · offeredResultIds`, bound at the Tools
|
|
746
|
+
// mount) — and on a hosted model that binding is what makes a standing
|
|
747
|
+
// resolve at all (docs/design/2026-09-findings-ledger-real-model.md).
|
|
748
|
+
// `reactMode: 'classic'` selects the Tools branch on turn 1 only, so an
|
|
749
|
+
// armed classic agent would serve the offer-less base on every call and
|
|
750
|
+
// file every standing as `unknownId`. Refused loud, here at build, the
|
|
751
|
+
// `AgentBuilder.selfExplain` twin — never a silent degrade.
|
|
752
|
+
if (this.reactMode === 'classic') {
|
|
753
|
+
throw new Error("Agent: .findings() requires per-iteration slot recomposition — reactMode 'classic' " +
|
|
754
|
+
'caches the tools slot on turn 1, so the ids the model may name (bound into every ' +
|
|
755
|
+
'served tool schema from the second call on) would never reach it and every standing ' +
|
|
756
|
+
"would file as unknownId. Use the default 'dynamic' mode (or 'dynamic-grouped').");
|
|
757
|
+
}
|
|
758
|
+
this.keepLedgerFacts = resolveKeepLedgerFacts(this.findingsOptions.keepLedgerFacts ?? opts.keepLedgerFacts);
|
|
759
|
+
}
|
|
689
760
|
// Refused at construction, never mid-run — a misspelled posture that was
|
|
690
761
|
// ignored would leave the liveness theorems switched off in an agent
|
|
691
762
|
// that believes they are on (the concurrency-mode precedent).
|
|
@@ -3139,6 +3210,11 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
3139
3210
|
...(this.findingsOptions !== undefined && {
|
|
3140
3211
|
findings: true,
|
|
3141
3212
|
findingsServe: this.findingsOptions.serve ?? 'ledger-and-facts',
|
|
3213
|
+
// The answer-turn ask (9.103.0): VALUE-conditional inside the arm —
|
|
3214
|
+
// the default `'none'` threads nothing and writes no key.
|
|
3215
|
+
...(this.findingsOptions.answerAsk === 'quote-facts' && {
|
|
3216
|
+
findingsAnswerAsk: 'quote-facts',
|
|
3217
|
+
}),
|
|
3142
3218
|
consumePendingResumeFindingsLedger: () => {
|
|
3143
3219
|
const l = this.pendingResumeFindingsLedger;
|
|
3144
3220
|
this.pendingResumeFindingsLedger = undefined;
|
|
@@ -3492,6 +3568,11 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
3492
3568
|
findings: true,
|
|
3493
3569
|
hasFindingsLedger: true,
|
|
3494
3570
|
findingsServe: this.findingsOptions.serve ?? 'ledger-and-facts',
|
|
3571
|
+
// The answer-turn ask (9.103.0): the same value seed records, so the
|
|
3572
|
+
// wire and the rebuild append it from one fact; absent on `'none'`.
|
|
3573
|
+
...(this.findingsOptions.answerAsk === 'quote-facts' && {
|
|
3574
|
+
findingsAnswerAsk: 'quote-facts',
|
|
3575
|
+
}),
|
|
3495
3576
|
}),
|
|
3496
3577
|
...(this.answerValidationConfig !== undefined && { suppressDraftTokens: true }),
|
|
3497
3578
|
// The receipt's salt (9.88.0) — read per call, like seed's own accessor.
|
|
@@ -3588,6 +3669,16 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
3588
3669
|
...(this.keepLastToolResults !== undefined && {
|
|
3589
3670
|
keepLastToolResults: this.keepLastToolResults,
|
|
3590
3671
|
}),
|
|
3672
|
+
// The findings ledger (9.102.0): the arm and the RESOLVED
|
|
3673
|
+
// ledger-fact ceiling, together or not at all. `keepLedgerFacts`
|
|
3674
|
+
// is set exactly when `.findings()` is on, so an unarmed agent
|
|
3675
|
+
// hands the stage exactly the deps object it always did; an
|
|
3676
|
+
// armed agent with no window strategy threads nothing, because
|
|
3677
|
+
// this whole stage does not exist for it.
|
|
3678
|
+
...(this.keepLedgerFacts !== undefined && {
|
|
3679
|
+
hasFindingsLedger: true,
|
|
3680
|
+
keepLedgerFacts: this.keepLedgerFacts,
|
|
3681
|
+
}),
|
|
3591
3682
|
...(pricingTable !== undefined && { pricingTable }),
|
|
3592
3683
|
...(costBudget !== undefined && { costBudget }),
|
|
3593
3684
|
}),
|
|
@@ -3840,6 +3931,11 @@ class Agent extends RunnerBase_js_1.RunnerBase {
|
|
|
3840
3931
|
// Escalation (9.19.0): the grouped chart threads `skillEscalated`
|
|
3841
3932
|
// across the sf-llm-call boundary only when the policy exists.
|
|
3842
3933
|
...(this.skillBrains?.escalation !== undefined && { hasEscalation: true }),
|
|
3934
|
+
// The findings ledger (9.102.0): both charts compute the OFFER on the
|
|
3935
|
+
// Tools branch's mount only under the arm — the same value-conditional
|
|
3936
|
+
// grammar as the slot's `findings` and callLLM's `hasFindingsLedger`,
|
|
3937
|
+
// so the three can never disagree about whether the ledger is armed.
|
|
3938
|
+
...(this.findingsOptions !== undefined && { hasFindingsLedger: true }),
|
|
3843
3939
|
// `.limitsTravelWithTheAnswer()` (this release) — value-conditional, the
|
|
3844
3940
|
// `resolvedModel` precedent: absent from the deps object entirely for an
|
|
3845
3941
|
// agent that did not ask, so both builders mount the final-branch stage
|