agentfootprint 9.81.0 → 9.82.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 +58 -0
- package/CLAUDE.md +1 -1
- package/dist/core/runbook/verdicts.js +30 -6
- package/dist/core/runbook/verdicts.js.map +1 -1
- package/dist/esm/core/runbook/types.d.ts +8 -0
- package/dist/esm/core/runbook/verdicts.d.ts +18 -5
- package/dist/esm/core/runbook/verdicts.js +30 -6
- package/dist/esm/core/runbook/verdicts.js.map +1 -1
- package/dist/types/core/runbook/types.d.ts +8 -0
- package/dist/types/core/runbook/types.d.ts.map +1 -1
- package/dist/types/core/runbook/verdicts.d.ts +18 -5
- package/dist/types/core/runbook/verdicts.d.ts.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [9.82.0] - 2026-08-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **A runbook can finally name the verdict no rule chose.** `verdict_meanings`
|
|
15
|
+
is generated from what the run itself said: the branch descriptions the chart
|
|
16
|
+
declared, and the rule labels this run's `decide()` evidence carried. For one
|
|
17
|
+
branch, both sources are silent by construction — the DEFAULT. It is the
|
|
18
|
+
branch chosen by *no rule* (it fires exactly when every rule failed, so no
|
|
19
|
+
`label` describes it), and when the decider lives inside a dynamically
|
|
20
|
+
generated fan-out branch the branch chart does not exist at build time either,
|
|
21
|
+
so there is no declared description to fall back on.
|
|
22
|
+
|
|
23
|
+
The library shipped visible proof of the gap: this repo's own worked example
|
|
24
|
+
and the published docs page showed a `"verdict": "protected"` row beside a
|
|
25
|
+
`verdict_meanings` map with no `protected` key.
|
|
26
|
+
|
|
27
|
+
The meaning is now declared where the rules are declared — one line at the
|
|
28
|
+
`decide()` call, on `footprintjs` ≥ 9.16.0:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// before
|
|
32
|
+
decide(scope, POSTURE_RULES, 'protected');
|
|
33
|
+
// after
|
|
34
|
+
decide(scope, POSTURE_RULES, {
|
|
35
|
+
branch: 'protected',
|
|
36
|
+
label: 'no rule fired — last backup within the 7-day threshold',
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
It rides `DecisionEvidence.defaultLabel` and is harvested exactly like a rule
|
|
41
|
+
label — on every decision, including runs where a rule won, so a published
|
|
42
|
+
meanings map does not gain and lose a key with the day's data.
|
|
43
|
+
|
|
44
|
+
What deliberately did NOT change: there is **no caller-supplied meanings map**
|
|
45
|
+
at the tool boundary. A map a caller can hand in is a map that can describe
|
|
46
|
+
rules that never ran, and it would be indistinguishable in the answer from
|
|
47
|
+
meanings the run produced. Declare nothing and `verdict_meanings` stays
|
|
48
|
+
honestly silent about that branch — the bridge never invents a sentence from a
|
|
49
|
+
branch id. A blank label (`''`) is recorded as no meaning at all, for a rule or
|
|
50
|
+
for the default.
|
|
51
|
+
|
|
52
|
+
Example: `examples/features/68-runbook-as-tool.ts` — its `verdict_meanings`
|
|
53
|
+
now explains every verdict its own rowset shows.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- **`footprintjs` peer dependency: `^9.15.0` → `^9.16.1`** — 9.16.0 carries
|
|
58
|
+
`DefaultBranch` / `DecisionEvidence.defaultLabel`; 9.16.1 is the floor because
|
|
59
|
+
9.16.0 threw on a `decide()` call that omits its default (this repo's own
|
|
60
|
+
suite caught it).
|
|
61
|
+
- **The engine version stamp on a recording envelope is real again.** footprintjs
|
|
62
|
+
9.15.1 added `'./package.json'` to its `exports` map, so `engineVersion()` can
|
|
63
|
+
resolve the manifest it always tried to read: `producer.footprintjsVersion` and
|
|
64
|
+
a bug report's `environment.footprintjs` now carry the installed version
|
|
65
|
+
instead of the honest-but-useless `'unknown'`. The test that pinned the defect
|
|
66
|
+
(and asked to be tripped when it was fixed) now pins the version instead.
|
|
67
|
+
|
|
10
68
|
## [9.81.0] - 2026-08-30
|
|
11
69
|
|
|
12
70
|
### Added
|
package/CLAUDE.md
CHANGED
|
@@ -17,7 +17,7 @@ not in this table, search `src/index.ts` for the nearest noun before writing cod
|
|
|
17
17
|
|
|
18
18
|
| If you are about to build… | It is | Where | Since |
|
|
19
19
|
|---|---|---|---|
|
|
20
|
-
| turning a written operational procedure (a runbook, a triage playbook) into ONE agent tool whose every answer is EVIDENCE — coverage folded up from the inner tools it calls, rule name+version, verdict rows with GENERATED meanings, and the recorded walk as an artifact ticket, never bytes | `runbookAsTool` — dials: `procedure` (factory, invoked per call with `ctx.tools`) + `resultKind` (`'verdict/*'` arms the rowset projection; anything else ships spine + the chart's `report`) + `rules {name, version}` (default absent ⇒ `rule_version: 'undeclared'`) + `verdicts {decider, maxRows}` (default 50) + `presentation` (`'prose'` default = ship `table` + `VERDICT_RENDER_NOTE` "output it VERBATIM"; `'panel'` = the HOST renders the rowset, so NO `table` key at all + `PANEL_RENDER_NOTE` "the rows are already on the reader's screen — do not reproduce them"; the rowset half is byte-identical across modes, `table` stays RESERVED in both, an unknown value THROWS at definition) + `walk {cap, recording}` (cap default 500; over-cap ⇒ control-flow projection, declared · `recording: true` or `{label, maxBytes}`, default OFF — ALSO files the inner chart's own `{snapshot, events, structure}` under `recording/run` and puts its ref on the spine as `walk.recording_ref`, because the ROW projection cannot be drawn; snapshot read from the REDACTED mirror so one `redact` means the same for both; over `maxBytes` (`DEFAULT_RECORDING_MAX_BYTES` = 5,000,000) it is REFUSED not truncated; every absence — no store / over size / unserializable / store threw — is STATED in `walk.recording_note`, and the four `recording_*` fields are absent entirely when the dial is off) + `composedOf` (drift-checked at agent BUILD) + kept `recorders`/`keepRecord`/`keepRecordLimit`/`redact`; reserved state keys `verdicts`/`coverage`/`report` (the `report` bag lands BESIDE the spine, never over it — a report field spelling `af_coverage`/`af_provenance`/`rule_version`/`walk`/`report_note` or a live projection key is discarded and NAMED in `result.report_note`; precedence is explicit in `report.ts`, never spread order); inner `absent()` passes through VERBATIM unless the call said `allowAbsent`; walk kind `recording/chart-walk` + `walk_segment` discriminant | `src/core/runbook/` | 9.76.0, recording 9.79.0 |
|
|
20
|
+
| turning a written operational procedure (a runbook, a triage playbook) into ONE agent tool whose every answer is EVIDENCE — coverage folded up from the inner tools it calls, rule name+version, verdict rows with GENERATED meanings, and the recorded walk as an artifact ticket, never bytes | `runbookAsTool` — dials: `procedure` (factory, invoked per call with `ctx.tools`) + `resultKind` (`'verdict/*'` arms the rowset projection; anything else ships spine + the chart's `report`) + `rules {name, version}` (default absent ⇒ `rule_version: 'undeclared'`) + `verdicts {decider, maxRows}` (default 50; `verdict_meanings` = statically declared branches + this run's rule labels + the DEFAULT branch's label, which reaches evidence only when the chart calls `decide(s, rules, {branch, label})` — fp ≥9.16.1, the default is chosen by NO rule so nothing else can name it, and inside a generated fan-out branch the static walk is blind too; an undeclared or blank label stays ABSENT from the map, never invented from a branch id, and there is deliberately NO caller-supplied meanings map) + `presentation` (`'prose'` default = ship `table` + `VERDICT_RENDER_NOTE` "output it VERBATIM"; `'panel'` = the HOST renders the rowset, so NO `table` key at all + `PANEL_RENDER_NOTE` "the rows are already on the reader's screen — do not reproduce them"; the rowset half is byte-identical across modes, `table` stays RESERVED in both, an unknown value THROWS at definition) + `walk {cap, recording}` (cap default 500; over-cap ⇒ control-flow projection, declared · `recording: true` or `{label, maxBytes}`, default OFF — ALSO files the inner chart's own `{snapshot, events, structure}` under `recording/run` and puts its ref on the spine as `walk.recording_ref`, because the ROW projection cannot be drawn; snapshot read from the REDACTED mirror so one `redact` means the same for both; over `maxBytes` (`DEFAULT_RECORDING_MAX_BYTES` = 5,000,000) it is REFUSED not truncated; every absence — no store / over size / unserializable / store threw — is STATED in `walk.recording_note`, and the four `recording_*` fields are absent entirely when the dial is off) + `composedOf` (drift-checked at agent BUILD) + kept `recorders`/`keepRecord`/`keepRecordLimit`/`redact`; reserved state keys `verdicts`/`coverage`/`report` (the `report` bag lands BESIDE the spine, never over it — a report field spelling `af_coverage`/`af_provenance`/`rule_version`/`walk`/`report_note` or a live projection key is discarded and NAMED in `result.report_note`; precedence is explicit in `report.ts`, never spread order); inner `absent()` passes through VERBATIM unless the call said `allowAbsent`; walk kind `recording/chart-walk` + `walk_segment` discriminant | `src/core/runbook/` | 9.76.0, recording 9.79.0 |
|
|
21
21
|
| speaking MCP from a BROWSER — the SDK is browser-clean (its client + streamableHttp bundle at `platform:'browser'` with zero `node:` edges and never pull in `client/stdio.js`); the one barrier was that `lazyRequire` gets CALLED there, so the fix is to let the caller supply what the loader would have found | `mcpClient({ sdk })` (`McpSdk` = the two SDK modules you imported statically; the library STILL builds the transport, so headers/fetch/gateway vending/`retryOnThrottle`/`_meta` all keep working) · `mcpClient({ connection })` (`McpConnection` = listTools/callTool/close, deliberately NO `connect` — you already did; the only arm that reaches the SDK's `jsonSchemaValidator`, i.e. CSP) + `McpConnectionOptions`; `refuseConflictingOptions` REFUSES at construction every option a transport would have consumed, naming where it moved (a knob naming a behaviour that no longer happens is the defect class); `transportUrl` resolves a relative `url` against `globalThis.location.href` (absolute takes the identical branch, Node refuses by name); `sdkLoadFailure` classifies the seven load sites so a browser is never told to install a package it has (resolution failure ⇒ byte-identical historical message); `retryingFetch` + `ThrottleFetch` promoted out of `@internal` so the connection arm keeps its 429 handling. stdio keeps `lazyRequire` FOREVER — it spawns a subprocess. ZERO packaging change: no subpath, no `browser` condition (TypeScript is blind to it), peer stays optional (no literal dynamic `import()`). Fenced by `test/lib/mcp/browserGraph.test.ts` | `src/lib/mcp/` | 9.81.0 |
|
|
22
22
|
| calling ANOTHER registered tool from inside a tool's `execute` — composition over the agent's own dispatch map instead of importing the module and building a second query stack | `agentToolDispatch` + `ctx.tools` (ToolDispatch has/call) — sees static and skill-carried tools, NEVER ToolProvider-delivered ones (no build-time list, the 9.72.0 caveat); inner calls get the outer facts with hasArtifacts false and a derived toolCallId, `needs` resolved fail-closed non-interactively, `checkIn` and `wants` tools refused by name, no nested dispatch; declare ingredients via `composedOf` (and `gates` for a pausing procedure) — both travel MCP `_meta` | `src/core/agent/toolDispatch.ts` | 9.76.0 |
|
|
23
23
|
| a tool returning numbers WITH the caveats that make them honest — interval/aggregation grain, is-it-a-counter, when the world was measured, which ground was NOT covered — as typed data the model reads compactly and the record keeps whole | `semantic()` + `tools.semantics_declared` (model sees `semanticsForModel` projection; `coverage` field absorbed by the coverage()/absent() channel) | `src/lib/semantics/` | 9.53.0 |
|
|
@@ -24,10 +24,21 @@
|
|
|
24
24
|
* GENERATED MEANINGS, never hand-restated: `verdict_meanings` is composed
|
|
25
25
|
* from (a) the named decider's declared branches in the chart's own structure
|
|
26
26
|
* (branch description, falling back to branch name), overlaid by (b) the rule
|
|
27
|
-
* LABELS this run's decide() evidence carried — the rule speaking for itself
|
|
27
|
+
* LABELS this run's decide() evidence carried — the rule speaking for itself —
|
|
28
|
+
* and (c) the DEFAULT branch's declared label, carried by the same evidence.
|
|
28
29
|
* A decider inside a dynamically generated fan-out branch is invisible to (a)
|
|
29
30
|
* by construction (the branch chart does not exist at build time); (b) still
|
|
30
31
|
* covers every verdict an executed rule produced.
|
|
32
|
+
*
|
|
33
|
+
* (c) exists because the default branch is chosen by NO rule — it fires
|
|
34
|
+
* exactly when every rule failed, so it appears in no `rules[]` entry and (b)
|
|
35
|
+
* can never name it. In a generated fan-out branch, where (a) is blind too, it
|
|
36
|
+
* was the one verdict the rowset could show and the meanings map could not
|
|
37
|
+
* explain. `decide(scope, rules, { branch, label })` (footprintjs ≥ 9.16.1) puts
|
|
38
|
+
* that label on the decision evidence, so it arrives here the way every other
|
|
39
|
+
* label does: harvested from the run, never declared at this boundary. There is
|
|
40
|
+
* deliberately no caller-supplied meanings map — a map a caller can write is a
|
|
41
|
+
* map that can describe rules that never ran.
|
|
31
42
|
*/
|
|
32
43
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
44
|
exports.composeMeanings = exports.meaningsRecorder = exports.resolveDecider = exports.renderVerdictTable = exports.verdictRowsOf = exports.DECLINED_VERDICT = exports.PANEL_RENDER_NOTE = exports.VERDICT_RENDER_NOTE = exports.DEFAULT_MAX_ROWS = void 0;
|
|
@@ -132,11 +143,17 @@ function resolveDecider(chart, decider) {
|
|
|
132
143
|
return { spellings, declared };
|
|
133
144
|
}
|
|
134
145
|
exports.resolveDecider = resolveDecider;
|
|
146
|
+
/** A label is a meaning only when it says something — a blank one is recorded
|
|
147
|
+
* as no meaning at all, never as a verdict that means the empty string. */
|
|
148
|
+
function meaningful(label) {
|
|
149
|
+
return typeof label === 'string' && label.length > 0;
|
|
150
|
+
}
|
|
135
151
|
/**
|
|
136
152
|
* A tiny flow recorder capturing the named decider's decide() evidence as it
|
|
137
153
|
* fires — collected DURING the traversal, never reconstructed after. Rule
|
|
138
154
|
* labels are harvested for every rule the evidence lists (matched or not):
|
|
139
|
-
* a rule that was evaluated has spoken its label, whichever branch won.
|
|
155
|
+
* a rule that was evaluated has spoken its label, whichever branch won. The
|
|
156
|
+
* DEFAULT branch's label rides the same evidence and is harvested with them.
|
|
140
157
|
*
|
|
141
158
|
* A decider inside a subflow (including a generated fan-out branch) reports
|
|
142
159
|
* itself PATH-PREFIXED (`per-subject~0/Protection posture`), so the match is
|
|
@@ -153,8 +170,14 @@ function meaningsRecorder(identity) {
|
|
|
153
170
|
if (!identity.spellings.has(lastSegment(event.decider)))
|
|
154
171
|
return;
|
|
155
172
|
const evidence = event.evidence;
|
|
173
|
+
// The default first, the rules over it. The default is the branch NO
|
|
174
|
+
// rule chose, so it is normally the only speaker for its own name; where
|
|
175
|
+
// a rule also routes to it, the rule is the sharper sentence and wins.
|
|
176
|
+
if (typeof evidence?.default === 'string' && meaningful(evidence.defaultLabel)) {
|
|
177
|
+
observed.set(evidence.default, evidence.defaultLabel);
|
|
178
|
+
}
|
|
156
179
|
for (const rule of evidence?.rules ?? []) {
|
|
157
|
-
if (typeof rule.branch === 'string' &&
|
|
180
|
+
if (typeof rule.branch === 'string' && meaningful(rule.label)) {
|
|
158
181
|
observed.set(rule.branch, rule.label);
|
|
159
182
|
}
|
|
160
183
|
}
|
|
@@ -163,9 +186,10 @@ function meaningsRecorder(identity) {
|
|
|
163
186
|
return { recorder, observed };
|
|
164
187
|
}
|
|
165
188
|
exports.meaningsRecorder = meaningsRecorder;
|
|
166
|
-
/** Compose the final meanings: declared branches first,
|
|
167
|
-
*
|
|
168
|
-
*
|
|
189
|
+
/** Compose the final meanings: declared branches first, the labels this run
|
|
190
|
+
* observed (rule labels, and the default branch's own) winning where both
|
|
191
|
+
* speak — the declaration made beside the rules is the sharper sentence.
|
|
192
|
+
* Undefined when neither source produced anything — absent, never `{}`. */
|
|
169
193
|
function composeMeanings(identity, observed) {
|
|
170
194
|
const meanings = {};
|
|
171
195
|
for (const [branch, meaning] of identity.declared)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verdicts.js","sourceRoot":"","sources":["../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"verdicts.js","sourceRoot":"","sources":["../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":"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"}
|
|
@@ -57,6 +57,14 @@ export interface RunbookVerdictsOptions {
|
|
|
57
57
|
* in this run's decide() evidence refine them (and are the only source
|
|
58
58
|
* when the decider lives inside a dynamically generated fan-out branch,
|
|
59
59
|
* where build-time structure cannot see it).
|
|
60
|
+
*
|
|
61
|
+
* The DEFAULT branch is chosen by no rule, so no rule label describes it.
|
|
62
|
+
* Name it where the rules are named — `decide(scope, rules, { branch,
|
|
63
|
+
* label })` — and the label arrives here on the same evidence:
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* decide(scope, rules, { branch: 'protected', label: 'No rule fired — asset stays protected' });
|
|
67
|
+
* ```
|
|
60
68
|
*/
|
|
61
69
|
readonly decider: string;
|
|
62
70
|
/** Cap on `verdicts` rows AND the rendered table — ONE number for both
|
|
@@ -23,10 +23,21 @@
|
|
|
23
23
|
* GENERATED MEANINGS, never hand-restated: `verdict_meanings` is composed
|
|
24
24
|
* from (a) the named decider's declared branches in the chart's own structure
|
|
25
25
|
* (branch description, falling back to branch name), overlaid by (b) the rule
|
|
26
|
-
* LABELS this run's decide() evidence carried — the rule speaking for itself
|
|
26
|
+
* LABELS this run's decide() evidence carried — the rule speaking for itself —
|
|
27
|
+
* and (c) the DEFAULT branch's declared label, carried by the same evidence.
|
|
27
28
|
* A decider inside a dynamically generated fan-out branch is invisible to (a)
|
|
28
29
|
* by construction (the branch chart does not exist at build time); (b) still
|
|
29
30
|
* covers every verdict an executed rule produced.
|
|
31
|
+
*
|
|
32
|
+
* (c) exists because the default branch is chosen by NO rule — it fires
|
|
33
|
+
* exactly when every rule failed, so it appears in no `rules[]` entry and (b)
|
|
34
|
+
* can never name it. In a generated fan-out branch, where (a) is blind too, it
|
|
35
|
+
* was the one verdict the rowset could show and the meanings map could not
|
|
36
|
+
* explain. `decide(scope, rules, { branch, label })` (footprintjs ≥ 9.16.1) puts
|
|
37
|
+
* that label on the decision evidence, so it arrives here the way every other
|
|
38
|
+
* label does: harvested from the run, never declared at this boundary. There is
|
|
39
|
+
* deliberately no caller-supplied meanings map — a map a caller can write is a
|
|
40
|
+
* map that can describe rules that never ran.
|
|
30
41
|
*/
|
|
31
42
|
import type { CombinedRecorder } from 'footprintjs';
|
|
32
43
|
import type { FlowChart } from 'footprintjs';
|
|
@@ -80,7 +91,8 @@ export interface MeaningsHarvest {
|
|
|
80
91
|
* A tiny flow recorder capturing the named decider's decide() evidence as it
|
|
81
92
|
* fires — collected DURING the traversal, never reconstructed after. Rule
|
|
82
93
|
* labels are harvested for every rule the evidence lists (matched or not):
|
|
83
|
-
* a rule that was evaluated has spoken its label, whichever branch won.
|
|
94
|
+
* a rule that was evaluated has spoken its label, whichever branch won. The
|
|
95
|
+
* DEFAULT branch's label rides the same evidence and is harvested with them.
|
|
84
96
|
*
|
|
85
97
|
* A decider inside a subflow (including a generated fan-out branch) reports
|
|
86
98
|
* itself PATH-PREFIXED (`per-subject~0/Protection posture`), so the match is
|
|
@@ -89,7 +101,8 @@ export interface MeaningsHarvest {
|
|
|
89
101
|
* marker opaque here.
|
|
90
102
|
*/
|
|
91
103
|
export declare function meaningsRecorder(identity: DeciderIdentity): MeaningsHarvest;
|
|
92
|
-
/** Compose the final meanings: declared branches first,
|
|
93
|
-
*
|
|
94
|
-
*
|
|
104
|
+
/** Compose the final meanings: declared branches first, the labels this run
|
|
105
|
+
* observed (rule labels, and the default branch's own) winning where both
|
|
106
|
+
* speak — the declaration made beside the rules is the sharper sentence.
|
|
107
|
+
* Undefined when neither source produced anything — absent, never `{}`. */
|
|
95
108
|
export declare function composeMeanings(identity: DeciderIdentity, observed: ReadonlyMap<string, string>): Record<string, string> | undefined;
|
|
@@ -23,10 +23,21 @@
|
|
|
23
23
|
* GENERATED MEANINGS, never hand-restated: `verdict_meanings` is composed
|
|
24
24
|
* from (a) the named decider's declared branches in the chart's own structure
|
|
25
25
|
* (branch description, falling back to branch name), overlaid by (b) the rule
|
|
26
|
-
* LABELS this run's decide() evidence carried — the rule speaking for itself
|
|
26
|
+
* LABELS this run's decide() evidence carried — the rule speaking for itself —
|
|
27
|
+
* and (c) the DEFAULT branch's declared label, carried by the same evidence.
|
|
27
28
|
* A decider inside a dynamically generated fan-out branch is invisible to (a)
|
|
28
29
|
* by construction (the branch chart does not exist at build time); (b) still
|
|
29
30
|
* covers every verdict an executed rule produced.
|
|
31
|
+
*
|
|
32
|
+
* (c) exists because the default branch is chosen by NO rule — it fires
|
|
33
|
+
* exactly when every rule failed, so it appears in no `rules[]` entry and (b)
|
|
34
|
+
* can never name it. In a generated fan-out branch, where (a) is blind too, it
|
|
35
|
+
* was the one verdict the rowset could show and the meanings map could not
|
|
36
|
+
* explain. `decide(scope, rules, { branch, label })` (footprintjs ≥ 9.16.1) puts
|
|
37
|
+
* that label on the decision evidence, so it arrives here the way every other
|
|
38
|
+
* label does: harvested from the run, never declared at this boundary. There is
|
|
39
|
+
* deliberately no caller-supplied meanings map — a map a caller can write is a
|
|
40
|
+
* map that can describe rules that never ran.
|
|
30
41
|
*/
|
|
31
42
|
/** Default cap on `verdicts` rows and the rendered table. */
|
|
32
43
|
export const DEFAULT_MAX_ROWS = 50;
|
|
@@ -126,11 +137,17 @@ export function resolveDecider(chart, decider) {
|
|
|
126
137
|
}
|
|
127
138
|
return { spellings, declared };
|
|
128
139
|
}
|
|
140
|
+
/** A label is a meaning only when it says something — a blank one is recorded
|
|
141
|
+
* as no meaning at all, never as a verdict that means the empty string. */
|
|
142
|
+
function meaningful(label) {
|
|
143
|
+
return typeof label === 'string' && label.length > 0;
|
|
144
|
+
}
|
|
129
145
|
/**
|
|
130
146
|
* A tiny flow recorder capturing the named decider's decide() evidence as it
|
|
131
147
|
* fires — collected DURING the traversal, never reconstructed after. Rule
|
|
132
148
|
* labels are harvested for every rule the evidence lists (matched or not):
|
|
133
|
-
* a rule that was evaluated has spoken its label, whichever branch won.
|
|
149
|
+
* a rule that was evaluated has spoken its label, whichever branch won. The
|
|
150
|
+
* DEFAULT branch's label rides the same evidence and is harvested with them.
|
|
134
151
|
*
|
|
135
152
|
* A decider inside a subflow (including a generated fan-out branch) reports
|
|
136
153
|
* itself PATH-PREFIXED (`per-subject~0/Protection posture`), so the match is
|
|
@@ -147,8 +164,14 @@ export function meaningsRecorder(identity) {
|
|
|
147
164
|
if (!identity.spellings.has(lastSegment(event.decider)))
|
|
148
165
|
return;
|
|
149
166
|
const evidence = event.evidence;
|
|
167
|
+
// The default first, the rules over it. The default is the branch NO
|
|
168
|
+
// rule chose, so it is normally the only speaker for its own name; where
|
|
169
|
+
// a rule also routes to it, the rule is the sharper sentence and wins.
|
|
170
|
+
if (typeof evidence?.default === 'string' && meaningful(evidence.defaultLabel)) {
|
|
171
|
+
observed.set(evidence.default, evidence.defaultLabel);
|
|
172
|
+
}
|
|
150
173
|
for (const rule of evidence?.rules ?? []) {
|
|
151
|
-
if (typeof rule.branch === 'string' &&
|
|
174
|
+
if (typeof rule.branch === 'string' && meaningful(rule.label)) {
|
|
152
175
|
observed.set(rule.branch, rule.label);
|
|
153
176
|
}
|
|
154
177
|
}
|
|
@@ -156,9 +179,10 @@ export function meaningsRecorder(identity) {
|
|
|
156
179
|
};
|
|
157
180
|
return { recorder, observed };
|
|
158
181
|
}
|
|
159
|
-
/** Compose the final meanings: declared branches first,
|
|
160
|
-
*
|
|
161
|
-
*
|
|
182
|
+
/** Compose the final meanings: declared branches first, the labels this run
|
|
183
|
+
* observed (rule labels, and the default branch's own) winning where both
|
|
184
|
+
* speak — the declaration made beside the rules is the sharper sentence.
|
|
185
|
+
* Undefined when neither source produced anything — absent, never `{}`. */
|
|
162
186
|
export function composeMeanings(identity, observed) {
|
|
163
187
|
const meanings = {};
|
|
164
188
|
for (const [branch, meaning] of identity.declared)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verdicts.js","sourceRoot":"","sources":["../../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"verdicts.js","sourceRoot":"","sources":["../../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":"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"}
|
|
@@ -57,6 +57,14 @@ export interface RunbookVerdictsOptions {
|
|
|
57
57
|
* in this run's decide() evidence refine them (and are the only source
|
|
58
58
|
* when the decider lives inside a dynamically generated fan-out branch,
|
|
59
59
|
* where build-time structure cannot see it).
|
|
60
|
+
*
|
|
61
|
+
* The DEFAULT branch is chosen by no rule, so no rule label describes it.
|
|
62
|
+
* Name it where the rules are named — `decide(scope, rules, { branch,
|
|
63
|
+
* label })` — and the label arrives here on the same evidence:
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* decide(scope, rules, { branch: 'protected', label: 'No rule fired — asset stays protected' });
|
|
67
|
+
* ```
|
|
60
68
|
*/
|
|
61
69
|
readonly decider: string;
|
|
62
70
|
/** Cap on `verdicts` rows AND the rendered table — ONE number for both
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/runbook/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,SAAS,CAAC;AAElE;;sCAEsC;AACtC,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;6BACyB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB;IACrC
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/runbook/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,10 +23,21 @@
|
|
|
23
23
|
* GENERATED MEANINGS, never hand-restated: `verdict_meanings` is composed
|
|
24
24
|
* from (a) the named decider's declared branches in the chart's own structure
|
|
25
25
|
* (branch description, falling back to branch name), overlaid by (b) the rule
|
|
26
|
-
* LABELS this run's decide() evidence carried — the rule speaking for itself
|
|
26
|
+
* LABELS this run's decide() evidence carried — the rule speaking for itself —
|
|
27
|
+
* and (c) the DEFAULT branch's declared label, carried by the same evidence.
|
|
27
28
|
* A decider inside a dynamically generated fan-out branch is invisible to (a)
|
|
28
29
|
* by construction (the branch chart does not exist at build time); (b) still
|
|
29
30
|
* covers every verdict an executed rule produced.
|
|
31
|
+
*
|
|
32
|
+
* (c) exists because the default branch is chosen by NO rule — it fires
|
|
33
|
+
* exactly when every rule failed, so it appears in no `rules[]` entry and (b)
|
|
34
|
+
* can never name it. In a generated fan-out branch, where (a) is blind too, it
|
|
35
|
+
* was the one verdict the rowset could show and the meanings map could not
|
|
36
|
+
* explain. `decide(scope, rules, { branch, label })` (footprintjs ≥ 9.16.1) puts
|
|
37
|
+
* that label on the decision evidence, so it arrives here the way every other
|
|
38
|
+
* label does: harvested from the run, never declared at this boundary. There is
|
|
39
|
+
* deliberately no caller-supplied meanings map — a map a caller can write is a
|
|
40
|
+
* map that can describe rules that never ran.
|
|
30
41
|
*/
|
|
31
42
|
import type { CombinedRecorder } from 'footprintjs';
|
|
32
43
|
import type { FlowChart } from 'footprintjs';
|
|
@@ -80,7 +91,8 @@ export interface MeaningsHarvest {
|
|
|
80
91
|
* A tiny flow recorder capturing the named decider's decide() evidence as it
|
|
81
92
|
* fires — collected DURING the traversal, never reconstructed after. Rule
|
|
82
93
|
* labels are harvested for every rule the evidence lists (matched or not):
|
|
83
|
-
* a rule that was evaluated has spoken its label, whichever branch won.
|
|
94
|
+
* a rule that was evaluated has spoken its label, whichever branch won. The
|
|
95
|
+
* DEFAULT branch's label rides the same evidence and is harvested with them.
|
|
84
96
|
*
|
|
85
97
|
* A decider inside a subflow (including a generated fan-out branch) reports
|
|
86
98
|
* itself PATH-PREFIXED (`per-subject~0/Protection posture`), so the match is
|
|
@@ -89,8 +101,9 @@ export interface MeaningsHarvest {
|
|
|
89
101
|
* marker opaque here.
|
|
90
102
|
*/
|
|
91
103
|
export declare function meaningsRecorder(identity: DeciderIdentity): MeaningsHarvest;
|
|
92
|
-
/** Compose the final meanings: declared branches first,
|
|
93
|
-
*
|
|
94
|
-
*
|
|
104
|
+
/** Compose the final meanings: declared branches first, the labels this run
|
|
105
|
+
* observed (rule labels, and the default branch's own) winning where both
|
|
106
|
+
* speak — the declaration made beside the rules is the sharper sentence.
|
|
107
|
+
* Undefined when neither source produced anything — absent, never `{}`. */
|
|
95
108
|
export declare function composeMeanings(identity: DeciderIdentity, observed: ReadonlyMap<string, string>): Record<string, string> | undefined;
|
|
96
109
|
//# sourceMappingURL=verdicts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verdicts.d.ts","sourceRoot":"","sources":["../../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"verdicts.d.ts","sourceRoot":"","sources":["../../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;qEACqE;AACrE,eAAO,MAAM,mBAAmB,QAGoB,CAAC;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,QAMwC,CAAC;AAEvE;;yEAEyE;AACzE,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAE3C;4EAC4E;AAC5E,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,UAAU,EAAE,CAUpF;AAWD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,CAStE;AAgBD;oEACoE;AACpE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,CAyBjF;AAED,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChD;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,eAAe,CA4B3E;AAED;;;4EAG4E;AAC5E,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAKpC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentfootprint",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.82.0",
|
|
4
4
|
"description": "The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -326,7 +326,7 @@
|
|
|
326
326
|
"@modelcontextprotocol/sdk": "*",
|
|
327
327
|
"@opentelemetry/api": "*",
|
|
328
328
|
"@yarflam/potion-base-8m": "*",
|
|
329
|
-
"footprintjs": "^9.
|
|
329
|
+
"footprintjs": "^9.16.1",
|
|
330
330
|
"google-auth-library": "*",
|
|
331
331
|
"ioredis": "*",
|
|
332
332
|
"jose": "*",
|
|
@@ -423,7 +423,7 @@
|
|
|
423
423
|
"esbuild": "^0.28.0",
|
|
424
424
|
"eslint": "^8.44.0",
|
|
425
425
|
"eslint-config-prettier": "^6.15.0",
|
|
426
|
-
"footprintjs": "^9.
|
|
426
|
+
"footprintjs": "^9.16.1",
|
|
427
427
|
"google-auth-library": "^11.0.1",
|
|
428
428
|
"jose": "^6.2.7",
|
|
429
429
|
"openai": "^7.4.0",
|