agentfootprint 9.86.0 → 9.86.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/CHANGELOG.md +179 -8
- package/canonical-notes.json +1 -1
- package/dist/core/agent/coverage/ledger.js +8 -2
- package/dist/core/agent/coverage/ledger.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +109 -20
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/agent/stages/wrapUp.js +15 -4
- package/dist/core/agent/stages/wrapUp.js.map +1 -1
- package/dist/core/runbook/recording.js +3 -2
- package/dist/core/runbook/recording.js.map +1 -1
- package/dist/esm/core/agent/coverage/ledger.d.ts +5 -0
- package/dist/esm/core/agent/coverage/ledger.js +8 -2
- package/dist/esm/core/agent/coverage/ledger.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +16 -2
- package/dist/esm/core/agent/stages/toolCalls.js +109 -20
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/stages/wrapUp.d.ts +11 -0
- package/dist/esm/core/agent/stages/wrapUp.js +15 -4
- package/dist/esm/core/agent/stages/wrapUp.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +5 -4
- package/dist/esm/core/runbook/recording.js +3 -2
- package/dist/esm/core/runbook/recording.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +8 -0
- package/dist/esm/lib/injection-engine/skillSteps.js +6 -2
- package/dist/esm/lib/injection-engine/skillSteps.js.map +1 -1
- package/dist/esm/lib/trace-toolpack/traceToolpack.js +21 -14
- package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
- package/dist/lib/injection-engine/skillSteps.js +6 -2
- package/dist/lib/injection-engine/skillSteps.js.map +1 -1
- package/dist/lib/trace-toolpack/traceToolpack.js +21 -14
- package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
- package/dist/types/core/agent/coverage/ledger.d.ts +5 -0
- package/dist/types/core/agent/coverage/ledger.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +16 -2
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/stages/wrapUp.d.ts +11 -0
- package/dist/types/core/agent/stages/wrapUp.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +5 -4
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/runbook/recording.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +8 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/skillSteps.d.ts.map +1 -1
- package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,168 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [9.86.1] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
The release that removed hand-counted lists shipped with one, and with main red.
|
|
13
|
+
|
|
14
|
+
`chore: release v9.86.0` failed CI (run 34008382993, the `coverage` job) while both
|
|
15
|
+
plain `test` jobs and the local release gate passed. Two tests parse every file
|
|
16
|
+
under `src/` with the TypeScript compiler and were called fresh inside every `it`
|
|
17
|
+
— seven parses in one suite, three in the other — and under the coverage job's
|
|
18
|
+
v8 instrumentation each parse took 5.3–6.2 s on the CI runner, past vitest's 5 s
|
|
19
|
+
default. Locally the same parse takes about a second. The release script ran
|
|
20
|
+
`npm test` and never `npm run test:coverage`, so the one command that would have
|
|
21
|
+
shown the failure was the one it did not run. Both walks now parse `src/` once per
|
|
22
|
+
suite and carry a 60 s budget of their own, and Gate 4 of `scripts/release.sh` runs
|
|
23
|
+
`npm run test:coverage` — the instrumented run is a superset of the plain one, so
|
|
24
|
+
the gate now sees what CI sees.
|
|
25
|
+
|
|
26
|
+
Everything else here is one review of 9.86.0, taken finding by finding.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **The `'guard'` refusal asserted a fact it did not have.** With no menu
|
|
31
|
+
outstanding, `composeReadSkillRefusal` ended every refusal with "Declared routes
|
|
32
|
+
moved the cursor instead." The gate handed it a boolean derived from two of
|
|
33
|
+
`TurnRoute.by`'s six values, so the clause was composed for the other four too:
|
|
34
|
+
false for `'continuity'` (the cursor was carried over from the previous turn and
|
|
35
|
+
nothing moved it — the verdict every follow-up produces under `{ strictness:
|
|
36
|
+
'guard', continuity: 'conversation' }`), false for `'menu'` resolved by the
|
|
37
|
+
model's own pick, and unprovable for `'none'`. The composer now takes
|
|
38
|
+
`turnStartedBy: TurnRoute['by']` and says one past fact per value — "the turn's
|
|
39
|
+
start had already been resolved decisively", "the cursor had been carried over
|
|
40
|
+
from the previous turn", "the menu had already been resolved by an earlier
|
|
41
|
+
pick", "the menu had been resolved by the configured decider before the turn's
|
|
42
|
+
first call" — and nothing for `'none'`. The old tail is gone from every arm.
|
|
43
|
+
|
|
44
|
+
- **Two refusals named a cursor the role may not be told about.** The `read_skill`
|
|
45
|
+
description withholds a hidden cursor's name by its own law, and the gate's
|
|
46
|
+
refusal printed the same id raw in two clauses ("was not reachable from 'alpha'.
|
|
47
|
+
Skills reachable from 'alpha' when that call was made: …"). The cursor now goes
|
|
48
|
+
through the same filter as the hops: a hidden cursor is anchored as "the skill
|
|
49
|
+
the cursor stood in" — the skill is real and merely unnamed — and "the turn's
|
|
50
|
+
start" is kept for a genuine cold start, which is a different fact. The
|
|
51
|
+
`propose-transition` refusal in the tool-effects judge had the same leak twice
|
|
52
|
+
over: it composed "(reachable: beta, gamma)" from the raw hop set and "from
|
|
53
|
+
'alpha'" from the raw cursor, and that sentence is appended to the tool result
|
|
54
|
+
the model reads. It reads `scope.hiddenSkillIds` now, names the filtered hops,
|
|
55
|
+
omits the clause when the filter emptied them, and anchors a hidden cursor the
|
|
56
|
+
same way. `skill.rejected.currentSkillId` stays raw on purpose: it is the
|
|
57
|
+
operator's record on the event channel, not a sentence the model reads.
|
|
58
|
+
|
|
59
|
+
- **A same-batch STAY did not compete for the transition slot.** The law is "first
|
|
60
|
+
ACCEPTED proposal wins; later proposals to OTHER targets are superseded", and a
|
|
61
|
+
stay is accepted — but it `continue`d past the bookkeeping, so a tool that judged
|
|
62
|
+
its data first and said "stay" lost to a sibling later in call order that said
|
|
63
|
+
"move", with two `'accepted'` events in one batch, the cursor moved, and no
|
|
64
|
+
`route_conflict` on the record. A stay judged first now holds the slot (writing
|
|
65
|
+
nothing to `pendingToolTransition`, because a stay moves nothing) and the later
|
|
66
|
+
hop is `'superseded'` with the batch's `route_conflict` naming the stay as the
|
|
67
|
+
winner; a stay judged after an accepted hop is the one superseded. Two stays are
|
|
68
|
+
both accepted, as two hops to one target are.
|
|
69
|
+
|
|
70
|
+
- **Both 9.86.0 frames pointed with the word the same release repaired elsewhere.**
|
|
71
|
+
The wrap-up instruction read "exhausted before this call … This call was for the
|
|
72
|
+
final answer" and the stepped-skill nudge "This call was for running them". A
|
|
73
|
+
frame is written into the `iteration_end` payload the checkpoint snapshots and
|
|
74
|
+
restored verbatim by `applyContinuation`, so on the next `.continue()` turn —
|
|
75
|
+
tools back on the wire — a model resolves "this call" to the call it is
|
|
76
|
+
answering and reads "no tools were offered on it" about a request that offers
|
|
77
|
+
them. They now name the call: "the wrap-up call this message opened … That call
|
|
78
|
+
was for the final answer", and "This message asked for them to be run". The
|
|
79
|
+
checker only knew `on this call`; a bare `this call` row catches the shape now,
|
|
80
|
+
and it found nine more: seven `inspect_tool_call` result lines and the
|
|
81
|
+
`inspect_tool_run` retention note, all anchored to `call '<id>'`; the coverage
|
|
82
|
+
ledger's `COVERAGE_NOTE` ("ground the call this result answers did not look at";
|
|
83
|
+
`canonical-notes.json` is regenerated by the build); and the runbook
|
|
84
|
+
`recording_note`. The checkIn-resume refusal in the tool-calls stage — "cannot be
|
|
85
|
+
retried this turn … Answer without it, or finish", a forecast plus a standing
|
|
86
|
+
order on a persistent result — is a past fact about the resumed call now.
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
|
|
90
|
+
- **The shape rows match the grammar they claim.** A second probe of seventeen
|
|
91
|
+
sentences written AGAINST the rows — the plainest forecast forms, not the ones the
|
|
92
|
+
rows had been derived from — walked past all four 9.86.0 shape rows: the
|
|
93
|
+
effect-verb row knew no future or modal tense ("will move you", "can switch
|
|
94
|
+
you"), the cursor row wanted a quote right after `in ` ("You are in skill
|
|
95
|
+
'alpha'", "You're in 'alpha'", "Your current skill is 'alpha'"), the copula row
|
|
96
|
+
knew six nouns ("is enabled", "is mounted", "are offered", "is off the wire",
|
|
97
|
+
"have been withheld"), and a headed list ("Available tools: calc, probe.")
|
|
98
|
+
has no copula at all. The rows are widened, a headed-inventory row and a
|
|
99
|
+
next-call-forecast row are added, and the seventeen sit beside the fifteen in
|
|
100
|
+
`test/modelFacingSurfaces.test.ts` so the next narrowing fails by name. The
|
|
101
|
+
`src/` walk then flagged **twenty-five** more literals: ten are repaired above,
|
|
102
|
+
fourteen are host-facing errors and check-up warnings now classified, and one —
|
|
103
|
+
an integrity finding's frame line — joins the work list. The ledger stands at
|
|
104
|
+
**ninety-one files / one hundred and seventy-eight literals**, with
|
|
105
|
+
**thirty-four** unrepaired across thirteen entries; the suite computes those
|
|
106
|
+
numbers.
|
|
107
|
+
|
|
108
|
+
- **A row may no longer exempt every lifetime.** `provableWhen` naming both
|
|
109
|
+
lifetimes compiled, carried an argument, and disabled the row everywhere — the
|
|
110
|
+
exemption-with-no-argument defect in a new coat. The suite asserts a strict
|
|
111
|
+
subset now. The header of `test/helpers/modelFacingClaims.ts` also says what
|
|
112
|
+
"may speak in the present" means beside the `now` row: present TENSE reported
|
|
113
|
+
as the state of the request, not the deictic adverbs, which point at the moment
|
|
114
|
+
of reading on every surface.
|
|
115
|
+
|
|
116
|
+
- **The divergence walk's summary block is checked whole.** Only `walk.cases` was
|
|
117
|
+
read back; the other five numbers were written on update and never compared.
|
|
118
|
+
All six are now derived from the recorded case outcomes and the row set. The
|
|
119
|
+
placeholder gate also refuses `TODOs` and `to-do`, and the header names five
|
|
120
|
+
defects, not three. A `claim-swallowed` row's auto-composed `cause` says "names
|
|
121
|
+
this claimant as the winner of a wire it never reached" when the shadow report
|
|
122
|
+
names the swallowed claimant itself — the framework's `skill-scoped:self-explain`
|
|
123
|
+
provider — instead of "describes a different pair", which it does not.
|
|
124
|
+
|
|
125
|
+
- **Anchors in the walk's baseline and the design note name symbols, not lines.**
|
|
126
|
+
The ten 9.86.0 rows cited `buildToolRegistry.ts` line ranges from the 9.85.0 tree
|
|
127
|
+
that the same release had moved by about twenty-nine lines, beside a
|
|
128
|
+
`buildToolsSlot.ts` line from HEAD. They name the checks now (`holders.includes
|
|
129
|
+
(PRESENT_TOOL_NAME)`, the `seenNames` loop, the `sharedSkillTools` backfill), and
|
|
130
|
+
the baseline's `note` says so. The `present-vs-mcp` row no longer claims the MCP
|
|
131
|
+
cell proves the blind spot is the provider channel: both claimants mount through
|
|
132
|
+
`staticTools()`, so the cell shows an MCP catalog inherits that seam unchanged and
|
|
133
|
+
nothing more.
|
|
134
|
+
|
|
135
|
+
- **`scripts/release.sh` Gate 4 runs `npm run test:coverage`.** See the opening.
|
|
136
|
+
|
|
137
|
+
### Docs
|
|
138
|
+
|
|
139
|
+
- `docs/design/2026-09-recorded-not-built.md`: entry 1's "16 baseline rows" is 22
|
|
140
|
+
(18 with a provider's or a skill's tool dead, 4 with the framework's own), the
|
|
141
|
+
appendix lists it as the third corrected sentence, and the row paragraph says
|
|
142
|
+
four-and-six rather than eight-and-two.
|
|
143
|
+
- `docs/api-reference/interfaces/AgentOptions.md`, tracked and last regenerated at
|
|
144
|
+
9.58.0, still quoted the pre-9.86.0 wrap-up sentence ("Do not request tools");
|
|
145
|
+
the quote is updated in place, as are the three other copies.
|
|
146
|
+
- The skill-graph quickstart says a host wiring its own `read_skill` under a
|
|
147
|
+
`tree()` must set `ReadSkillOffer.treeRouted`, and why.
|
|
148
|
+
|
|
149
|
+
### Changelog corrections
|
|
150
|
+
|
|
151
|
+
Four sentences in 9.86.0 are corrected in place, each marked where it stands:
|
|
152
|
+
|
|
153
|
+
- _"Eight of the ten are already-recorded seams … Two are defects"_ — four rows
|
|
154
|
+
reach recorded seams and six record the two new defects.
|
|
155
|
+
- `ToolRegistryArtifacts.toolDeclaringSkills` and `AgentState.hiddenSkillIds` were
|
|
156
|
+
listed under Added as if public; neither type is exported from any door.
|
|
157
|
+
- `unknownToolResult` was called "exported"; it is a module export inside the
|
|
158
|
+
tool-calls stage and not on any door.
|
|
159
|
+
- The `report-misattributed` bullet did not say that `reported` — a field the
|
|
160
|
+
ratchet compares — changed body on nine unrenamed rows.
|
|
161
|
+
|
|
162
|
+
### Deliberately not changed
|
|
163
|
+
|
|
164
|
+
- **The two permission-denied arms** ("This will not change during this run — do
|
|
165
|
+
not call it again") stay on the unrepaired ledger. Making the sentence true means
|
|
166
|
+
latching a denial per run, a behaviour change with no field finding behind it;
|
|
167
|
+
rewording it is that entry's own packet.
|
|
168
|
+
- **The `now` row keeps no exemption.** The header now argues the same thing the
|
|
169
|
+
row does, rather than the row being softened.
|
|
170
|
+
- **`skill.rejected.currentSkillId`** is not role-filtered — see above.
|
|
171
|
+
|
|
10
172
|
## [9.86.0] - 2026-09-05
|
|
11
173
|
|
|
12
174
|
Every hand-counted list in 9.84.0 and 9.85.0 was short by one or two.
|
|
@@ -75,7 +237,9 @@ the cursor?", and one scope key answers "which skill ids may this role see?".
|
|
|
75
237
|
refers back to that call.
|
|
76
238
|
|
|
77
239
|
- **`Unknown tool: X` told the model it was wrong and never what would have
|
|
78
|
-
worked.** Both dispatch doors now compose one
|
|
240
|
+
worked.** Both dispatch doors now compose one `unknownToolResult` (a module
|
|
241
|
+
export inside the tool-calls stage, not on any package door — _corrected in
|
|
242
|
+
9.86.1_), which
|
|
79
243
|
names the dispatch roster: `Unknown tool 'X' on that call. Tool names that
|
|
80
244
|
resolved to an implementation on that call: …`, or, with an empty roster, that
|
|
81
245
|
none did. The leading `Unknown tool` token is preserved, so every matcher on it
|
|
@@ -226,10 +390,12 @@ the cursor?", and one scope key answers "which skill ids may this role see?".
|
|
|
226
390
|
`todo`/`tbd`/`fixme`/`xxx` on a word boundary in any case, or under forty
|
|
227
391
|
characters — a floor on effort, not a measure of truth).
|
|
228
392
|
|
|
229
|
-
|
|
230
|
-
never been crossed, and say so.
|
|
231
|
-
|
|
232
|
-
rather than
|
|
393
|
+
Four of the ten reach seams the 9.85.0 baseline already recorded, through a
|
|
394
|
+
source that had never been crossed, and say so. Six record two defects nobody
|
|
395
|
+
had recorded — four rows for entry 4 and two for entry 5 of
|
|
396
|
+
`docs/design/2026-09-recorded-not-built.md` — rather than papering over them
|
|
397
|
+
(_corrected in 9.86.1: this paragraph said "eight of the ten" and "two", a
|
|
398
|
+
count of defects presented as a count of rows_): `.selfExplain()` reserves its trace-tool names against
|
|
233
399
|
`this.registry` and never `this.injectionList`, making it the one auto-attach
|
|
234
400
|
family with no net at all against a skill's `tools: []`; and the misattributed
|
|
235
401
|
shadow report can now name a `skill-scoped:self-explain` provider — one the
|
|
@@ -239,14 +405,18 @@ the cursor?", and one scope key answers "which skill ids may this role see?".
|
|
|
239
405
|
event's meaning lives in its `schemaFromId`/`dispatchToId`, and the row was keyed
|
|
240
406
|
on case + tool + epoch, so two reports naming different sources in one epoch — a
|
|
241
407
|
strictly worse fact than one wrong report — collapsed into one `Map` entry and
|
|
242
|
-
vanished.
|
|
408
|
+
vanished. The `reported` column of every row now carries the `*Id` halves too
|
|
409
|
+
(`schemaFrom=provider(static) dispatchTo=skill(desk-active)`), which is why nine
|
|
410
|
+
rows whose ids did not move changed body in the same re-record (_added in
|
|
411
|
+
9.86.1; the re-record changed a compared field and the entry did not say so_).
|
|
243
412
|
|
|
244
413
|
- **`SkillRejectedPayload.allowed` is what the model was actually told.** Role-
|
|
245
414
|
filtered rather than the graph's raw set. Shape unchanged; only agents with a
|
|
246
415
|
`PermissionChecker` governing `'skill_read'` see any difference. The field's own
|
|
247
416
|
JSDoc says so at the call site, which is the doc a consumer actually reads.
|
|
248
417
|
|
|
249
|
-
- **`ToolRegistryArtifacts.toolDeclaringSkills`** —
|
|
418
|
+
- **`ToolRegistryArtifacts.toolDeclaringSkills`** (internal — `ToolRegistryArtifacts`
|
|
419
|
+
is not exported from any door; _corrected in 9.86.1_) — tool name → the ids of the
|
|
250
420
|
skills whose `inject.tools` carry it, recorded on the walk `buildToolRegistry`
|
|
251
421
|
was already doing and thrown away. Empty for an agent whose skills carry no
|
|
252
422
|
tools. Its one consumer is the unknown-tool roster's role filter; it exists so
|
|
@@ -258,7 +428,8 @@ the cursor?", and one scope key answers "which skill ids may this role see?".
|
|
|
258
428
|
Deliberately not a fourth `outcome`, so an exhaustive consumer switch keeps
|
|
259
429
|
compiling.
|
|
260
430
|
|
|
261
|
-
- **`AgentState.hiddenSkillIds?: readonly string[]`** —
|
|
431
|
+
- **`AgentState.hiddenSkillIds?: readonly string[]`** (internal — `AgentState` is
|
|
432
|
+
not exported from any door; _corrected in 9.86.1_) — the per-iteration
|
|
262
433
|
role-hidden set, written by the tools slot and read by the `read_skill` gate.
|
|
263
434
|
|
|
264
435
|
- **`ReadSkillOffer.treeRouted?: boolean`** — declares the mounted graph a decision
|
package/canonical-notes.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$comment": "GENERATED by scripts/gen-canonical-notes.mjs from this package's own built exports - do not edit by hand. These are the strings a tool written in another language must reproduce byte for byte: the static notes each result shape carries, the reserved marker keys that make those shapes recognizable, and the coverage block's heading. Keys are the exported constant names, so the same value is reachable as a TypeScript import from the package's main entry point. The package version lives in package.json.",
|
|
3
3
|
"notes": {
|
|
4
4
|
"ABSENCE_NOTE": "The search ran and matched nothing. This is an ANSWER, not an error: nothing failed, nothing was substituted for what was asked, and calling this tool again with the same arguments returns this same result. `checked` is the ground this answer covers; anything under `not_checked` or `cannot_cover` is ground it does NOT cover, and reaching that needs a different question, not a retry.",
|
|
5
|
-
"COVERAGE_NOTE": "This result covers only what `checked` lists. `not_checked` is ground
|
|
5
|
+
"COVERAGE_NOTE": "This result covers only what `checked` lists. `not_checked` is ground the call this result answers did not look at, and `cannot_cover` is ground this tool can never see — a clean result here is NOT evidence about either, and no retry changes `cannot_cover`. Carry these limits into any answer you build on this result.",
|
|
6
6
|
"SEMANTICS_NOTE": "Typed data, not prose. `grain` and `provenance` are caveats that travel with the numbers: check `is_counter` before summing values from a series, read `measured_at` (and `age_seconds`) as how old the data is, and treat everything under `not_covered` as ground this result does NOT cover — a clean look here says nothing about it."
|
|
7
7
|
},
|
|
8
8
|
"markers": {
|
|
@@ -42,9 +42,15 @@ exports.COVERAGE_MARKER = 'af_coverage';
|
|
|
42
42
|
* of survival — the model is told to carry the limits into its answer. The
|
|
43
43
|
* ENFORCEMENT half (`.limitsTravelWithTheAnswer()`) does not depend on the
|
|
44
44
|
* model obeying it; see `answer.ts` for why both exist.
|
|
45
|
+
*
|
|
46
|
+
* "the call this result answers", not "this call" (9.86.1): the note rides a
|
|
47
|
+
* tool result, which is re-read on every later call of the turn, and a bare
|
|
48
|
+
* `this call` there denotes whichever call is reading it. The container
|
|
49
|
+
* deictic's own anchor names the call instead.
|
|
45
50
|
*/
|
|
46
|
-
exports.COVERAGE_NOTE = 'This result covers only what `checked` lists. `not_checked` is ground
|
|
47
|
-
'look at, and `cannot_cover` is ground this tool can never see — a clean
|
|
51
|
+
exports.COVERAGE_NOTE = 'This result covers only what `checked` lists. `not_checked` is ground the call this result ' +
|
|
52
|
+
'answers did not look at, and `cannot_cover` is ground this tool can never see — a clean ' +
|
|
53
|
+
'result here is ' +
|
|
48
54
|
'NOT evidence about either, and no retry changes `cannot_cover`. Carry these limits into ' +
|
|
49
55
|
'any answer you build on this result.';
|
|
50
56
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../../../src/core/agent/coverage/ledger.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;;;AAEH,yCAAmD;AAGnD,yDAAyD;AAC5C,QAAA,eAAe,GAAG,aAAa,CAAC;AAE7C
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../../../src/core/agent/coverage/ledger.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;;;AAEH,yCAAmD;AAGnD,yDAAyD;AAC5C,QAAA,eAAe,GAAG,aAAa,CAAC;AAE7C;;;;;;;;;;GAUG;AACU,QAAA,aAAa,GACxB,6FAA6F;IAC7F,0FAA0F;IAC1F,iBAAiB;IACjB,0FAA0F;IAC1F,sCAAsC,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,SAAgB,QAAQ,CAAI,OAAU,EAAE,IAAyB;IAC/D,MAAM,EAAE,GAAG,UAAU,CAAC;IACtB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,kFAAkF;YACrF,gFAAgF,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,IAAA,gCAAqB,EAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC1E,MAAM,UAAU,GAAG,IAAA,gCAAqB,EAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACnF,MAAM,WAAW,GAAG,IAAA,gCAAqB,EAAC,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACrF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,+EAA+E;YAClF,oFAAoF;YACpF,kFAAkF;YAClF,yBAAyB,CAC5B,CAAC;IACJ,CAAC;IACD,OAAO;QACL,WAAW,EAAE;YACX,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;YACtC,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;YACzD,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;YAC5D,IAAI,EAAE,qBAAa;SACpB;QACD,MAAM,EAAE,OAAO;KAChB,CAAC;AACJ,CAAC;AA5BD,4BA4BC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1F,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,MAAM,GAAG,GAAG,CAAC,uBAAe,CAAC,CAAC;IACpC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7F,IAAI,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,KAAsB,CAAC;AAChC,CAAC;AAPD,gDAOC;AAED,iEAAiE;AACjE,SAAgB,gBAAgB,CAAC,OAAsB;IACrD,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IAC9B,OAAO;QACL,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE;QACxB,UAAU,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE;QAC/B,WAAW,EAAE,CAAC,CAAC,YAAY,IAAI,EAAE;KAClC,CAAC;AACJ,CAAC;AAPD,4CAOC"}
|
|
@@ -276,7 +276,7 @@ exports.unknownToolResult = unknownToolResult;
|
|
|
276
276
|
* composers rather than re-deriving what it says.
|
|
277
277
|
*/
|
|
278
278
|
function composeReadSkillRefusal(args) {
|
|
279
|
-
const { requestedId, targetClass, cursorId, hops, openIds, isTree, posture, menuOffered,
|
|
279
|
+
const { requestedId, targetClass, cursorId, hops, openIds, isTree, posture, menuOffered, turnStartedBy, cursorWithheld, } = args;
|
|
280
280
|
const head = `read_skill("${requestedId}") was not granted on that call: `;
|
|
281
281
|
// Open skills are the one list every arm may name: they are admitted from
|
|
282
282
|
// every cursor and no posture governs them (the posture arm below is reached
|
|
@@ -307,10 +307,23 @@ function composeReadSkillRefusal(args) {
|
|
|
307
307
|
return (`${head}this graph's 'guard' posture admits a routing pick only from the menu the ` +
|
|
308
308
|
`framework offered, and '${requestedId}' was not admitted on that call.${openClause}`);
|
|
309
309
|
}
|
|
310
|
+
// What the gate KNOWS about why no menu was outstanding is `TurnRoute.by`,
|
|
311
|
+
// and each value is a different finished fact. Nothing is asserted about
|
|
312
|
+
// how the cursor came to be where it was beyond that — the old tail,
|
|
313
|
+
// "Declared routes moved the cursor instead.", was composed for every
|
|
314
|
+
// value and was true of none of them in particular.
|
|
315
|
+
const startClause = turnStartedBy === 'entry' || turnStartedBy === 'intent'
|
|
316
|
+
? " — the turn's start had already been resolved decisively"
|
|
317
|
+
: turnStartedBy === 'continuity'
|
|
318
|
+
? ' — the cursor had been carried over from the previous turn'
|
|
319
|
+
: turnStartedBy === 'menu'
|
|
320
|
+
? ' — the menu had already been resolved by an earlier pick'
|
|
321
|
+
: turnStartedBy === 'decider'
|
|
322
|
+
? " — the menu had been resolved by the configured decider before the turn's first call"
|
|
323
|
+
: '';
|
|
310
324
|
return (`${head}this graph's 'guard' posture admits a routing pick only while the framework ` +
|
|
311
325
|
`has declared ambiguity, and no menu was outstanding when that call was made` +
|
|
312
|
-
`${
|
|
313
|
-
`. Declared routes moved the cursor instead.${openClause}`);
|
|
326
|
+
`${startClause}.${openClause}`);
|
|
314
327
|
}
|
|
315
328
|
if (isTree === true) {
|
|
316
329
|
return (`${head}this map is a decision tree. A tree routes by predicate on every iteration ` +
|
|
@@ -321,7 +334,14 @@ function composeReadSkillRefusal(args) {
|
|
|
321
334
|
// this arm — 'self' is answered by the notice, 'hop' and 'open' are admitted
|
|
322
335
|
// — and it is carried rather than re-derived so the sentence and the verdict
|
|
323
336
|
// read the same classification.
|
|
324
|
-
|
|
337
|
+
// Three anchors, not two: a named cursor, a cursor this caller may not be
|
|
338
|
+
// told the name of, and a genuine cold start. The middle one used to print
|
|
339
|
+
// the raw id — the one name the description's own law withholds.
|
|
340
|
+
const from = cursorId !== undefined
|
|
341
|
+
? `'${cursorId}'`
|
|
342
|
+
: cursorWithheld === true
|
|
343
|
+
? 'the skill the cursor stood in'
|
|
344
|
+
: "the turn's start";
|
|
325
345
|
// The gate reaches this arm only with `'unreachable'`; the weaker sentence is
|
|
326
346
|
// for a caller that refused an admissible class for a reason of its own, and
|
|
327
347
|
// it is deliberately not an invented explanation of one.
|
|
@@ -931,6 +951,11 @@ function buildToolCallsHandler(deps) {
|
|
|
931
951
|
}
|
|
932
952
|
const currentSkillId = scope.currentSkillId;
|
|
933
953
|
const hops = deps.allowedSkillIds(currentSkillId);
|
|
954
|
+
const targetClass = (0, skillGraph_js_1.classifySkillTarget)({
|
|
955
|
+
...(currentSkillId !== undefined && { cursor: currentSkillId }),
|
|
956
|
+
target,
|
|
957
|
+
hops,
|
|
958
|
+
});
|
|
934
959
|
// ── A PROPOSAL TO THE CURSOR'S OWN SKILL IS A STAY (9.86.0) ──────
|
|
935
960
|
// `makeReachableSkills` filters the cursor out of its own successor
|
|
936
961
|
// set — right for a MOVE, and this judge read it as "not reachable"
|
|
@@ -943,11 +968,47 @@ function buildToolCallsHandler(deps) {
|
|
|
943
968
|
// The event carries the existing `'accepted'` outcome plus an
|
|
944
969
|
// ADDITIVE `stay: true`, deliberately not a new enum member: an
|
|
945
970
|
// exhaustive switch over `outcome` in a consumer must keep compiling.
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
971
|
+
//
|
|
972
|
+
// A STAY COMPETES FOR THE SLOT LIKE ANY OTHER ACCEPTED PROPOSAL
|
|
973
|
+
// (9.86.1). The batch law is "first ACCEPTED proposal wins, later
|
|
974
|
+
// proposals to OTHER targets are superseded", and a stay is accepted
|
|
975
|
+
// — so a stay judged first holds the slot (with nothing written to
|
|
976
|
+
// `pendingToolTransition`, because a stay moves nothing) and a later
|
|
977
|
+
// sibling proposing a hop is `'superseded'` and lands in the
|
|
978
|
+
// `route_conflict` losers, exactly as two conflicting hops do. Before
|
|
979
|
+
// this the stay `continue`d past the bookkeeping, so the tool that
|
|
980
|
+
// judged its data first lost to whichever sibling came second, with
|
|
981
|
+
// two `'accepted'` events in one batch and no conflict on the record.
|
|
982
|
+
// The same law in the other order: a stay proposed AFTER an accepted
|
|
983
|
+
// hop is superseded by it, not accepted beside it.
|
|
984
|
+
if (targetClass === 'self') {
|
|
985
|
+
if (state.winner !== undefined && state.winner.targetSkillId !== target) {
|
|
986
|
+
state.losers.push({
|
|
987
|
+
toolCallId: call.toolCallId,
|
|
988
|
+
toolName: call.toolName,
|
|
989
|
+
target,
|
|
990
|
+
});
|
|
991
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.tools.effect', {
|
|
992
|
+
kind: 'propose-transition',
|
|
993
|
+
outcome: 'superseded',
|
|
994
|
+
toolName: call.toolName,
|
|
995
|
+
toolCallId: call.toolCallId,
|
|
996
|
+
iteration: call.iteration,
|
|
997
|
+
targetSkillId: target,
|
|
998
|
+
reason: effect.reason,
|
|
999
|
+
supersededBy: 'earlier-proposal',
|
|
1000
|
+
});
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
if (state.winner === undefined) {
|
|
1004
|
+
state.winner = {
|
|
1005
|
+
targetSkillId: target,
|
|
1006
|
+
toolName: call.toolName,
|
|
1007
|
+
toolCallId: call.toolCallId,
|
|
1008
|
+
reason: effect.reason,
|
|
1009
|
+
iteration: call.iteration,
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
951
1012
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.tools.effect', {
|
|
952
1013
|
kind: 'propose-transition',
|
|
953
1014
|
outcome: 'accepted',
|
|
@@ -960,10 +1021,25 @@ function buildToolCallsHandler(deps) {
|
|
|
960
1021
|
});
|
|
961
1022
|
continue;
|
|
962
1023
|
}
|
|
963
|
-
if (
|
|
1024
|
+
if (targetClass === 'unreachable') {
|
|
1025
|
+
// ── THE REFUSAL SPEAKS WITH THE FILTERED SETS (9.86.1) ──────────
|
|
1026
|
+
// This sentence is appended to the tool result the model reads and
|
|
1027
|
+
// rides the `tools.effect` payload, and it named the raw hop set and
|
|
1028
|
+
// the raw cursor — the leak the `read_skill` refusals closed one
|
|
1029
|
+
// function up. Same filter, same law: the hidden set the tools slot
|
|
1030
|
+
// resolved for this iteration, and OMIT, NEVER DENY — a hop set the
|
|
1031
|
+
// filter emptied composes no reachable clause rather than an empty
|
|
1032
|
+
// one. Admission is still judged on the raw `hops`.
|
|
1033
|
+
const hiddenIds = new Set(scope.hiddenSkillIds ?? []);
|
|
1034
|
+
const mayName = (id) => !hiddenIds.has(id);
|
|
1035
|
+
const reachable = (0, spokenIds_js_1.spoken)(hops, mayName);
|
|
1036
|
+
const from = currentSkillId === undefined
|
|
1037
|
+
? 'the turn start'
|
|
1038
|
+
: mayName(currentSkillId)
|
|
1039
|
+
? `'${currentSkillId}'`
|
|
1040
|
+
: 'the skill the cursor stood in';
|
|
964
1041
|
refuse('propose-transition', `propose-transition → '${target}' was refused: '${target}' is not reachable from ` +
|
|
965
|
-
`${
|
|
966
|
-
`the graph's own law${hops.length > 0 ? ` (reachable: ${hops.join(', ')})` : ''}. The graph decides — a proposal is evidence, never authority.`, { targetSkillId: target, reason: effect.reason });
|
|
1042
|
+
`${from} per the graph's own law${reachable.named.length > 0 ? ` (reachable: ${reachable.named.join(', ')})` : ''}. The graph decides — a proposal is evidence, never authority.`, { targetSkillId: target, reason: effect.reason });
|
|
967
1043
|
continue;
|
|
968
1044
|
}
|
|
969
1045
|
if (state.winner !== undefined && state.winner.targetSkillId !== target) {
|
|
@@ -2785,6 +2861,15 @@ function buildToolCallsHandler(deps) {
|
|
|
2785
2861
|
// "nothing reachable may be named", and it used to assert the first.
|
|
2786
2862
|
const hopsSpoken = (0, spokenIds_js_1.spoken)(hops, mayName);
|
|
2787
2863
|
const openSpoken = (0, spokenIds_js_1.spoken)(openAll, mayName);
|
|
2864
|
+
// The cursor through the SAME filter (9.86.1). The description
|
|
2865
|
+
// withholds a hidden cursor's name and every refusal below printed
|
|
2866
|
+
// it raw in two clauses; `cursorWithheld` keeps the sentence's
|
|
2867
|
+
// anchor honest ("the skill the cursor stood in") without the id.
|
|
2868
|
+
const cursorSpoken = currentSkillId === undefined
|
|
2869
|
+
? {}
|
|
2870
|
+
: mayName(currentSkillId)
|
|
2871
|
+
? { cursorId: currentSkillId }
|
|
2872
|
+
: { cursorWithheld: true };
|
|
2788
2873
|
const hopsNamed = hopsSpoken.named;
|
|
2789
2874
|
const openNamed = openSpoken.named;
|
|
2790
2875
|
// The ONE owner of "is this target the cursor?" — the same function
|
|
@@ -2902,7 +2987,7 @@ function buildToolCallsHandler(deps) {
|
|
|
2902
2987
|
result = composeReadSkillRefusal({
|
|
2903
2988
|
requestedId: reqId,
|
|
2904
2989
|
targetClass,
|
|
2905
|
-
...
|
|
2990
|
+
...cursorSpoken,
|
|
2906
2991
|
hops: hopsSpoken,
|
|
2907
2992
|
openIds: openSpoken,
|
|
2908
2993
|
...(deps.skillGraphIsTree === true && { isTree: true }),
|
|
@@ -2939,7 +3024,7 @@ function buildToolCallsHandler(deps) {
|
|
|
2939
3024
|
result = composeReadSkillRefusal({
|
|
2940
3025
|
requestedId: reqId,
|
|
2941
3026
|
targetClass,
|
|
2942
|
-
...
|
|
3027
|
+
...cursorSpoken,
|
|
2943
3028
|
// No hop is named under a posture, and the graph's hop set
|
|
2944
3029
|
// is not this arm's subject — `held: false` is the honest
|
|
2945
3030
|
// shape, not a claim that nothing was reachable (this arm
|
|
@@ -2948,9 +3033,9 @@ function buildToolCallsHandler(deps) {
|
|
|
2948
3033
|
openIds: openSpoken,
|
|
2949
3034
|
posture: deps.skillStrictness,
|
|
2950
3035
|
...(menuNamed !== undefined && { menuOffered: menuNamed }),
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
}),
|
|
3036
|
+
// The whole verdict, not a boolean derived from two of its
|
|
3037
|
+
// six values: the composer says one past fact per value.
|
|
3038
|
+
...(turnRoute !== undefined && { turnStartedBy: turnRoute.by }),
|
|
2954
3039
|
});
|
|
2955
3040
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.skill.rejected', {
|
|
2956
3041
|
requestedId: reqId,
|
|
@@ -3396,10 +3481,14 @@ function buildToolCallsHandler(deps) {
|
|
|
3396
3481
|
// silently invents a decision, for the same reason it never silently
|
|
3397
3482
|
// drops one.
|
|
3398
3483
|
error = true;
|
|
3484
|
+
// A past fact about the resumed call, not a forecast about the
|
|
3485
|
+
// turn (9.86.1): "cannot be retried this turn … Answer without it,
|
|
3486
|
+
// or finish" was a prediction plus a standing order on a result
|
|
3487
|
+
// that is re-read on every later call.
|
|
3399
3488
|
result =
|
|
3400
|
-
`tool '${toolName}' was not executed
|
|
3401
|
-
`
|
|
3402
|
-
`
|
|
3489
|
+
`tool '${toolName}' was not executed on that call, and the resumed dispatch had ` +
|
|
3490
|
+
`no second checkpoint to retry it on: it declares its own checkIn consent gate, ` +
|
|
3491
|
+
`and that gate tripped for those arguments. (To ` +
|
|
3403
3492
|
`the agent's author: the middleware '${askedBy}' and the tool's checkIn ask ` +
|
|
3404
3493
|
`different questions — one is the rule's, one is the tool's with the evidence ` +
|
|
3405
3494
|
`pack attached — so approving one is not answering the other. Keep one gate for ` +
|