agentfootprint 9.77.0 → 9.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +189 -0
  2. package/CLAUDE.md +2 -1
  3. package/dist/artifacts/recordingArtifact.js +5 -1
  4. package/dist/artifacts/recordingArtifact.js.map +1 -1
  5. package/dist/core/Agent.js +50 -0
  6. package/dist/core/Agent.js.map +1 -1
  7. package/dist/core/agent/stages/callLLM.js +14 -0
  8. package/dist/core/agent/stages/callLLM.js.map +1 -1
  9. package/dist/core/agent/stages/toolCalls.js +107 -0
  10. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  11. package/dist/core/runbook/index.js +3 -1
  12. package/dist/core/runbook/index.js.map +1 -1
  13. package/dist/core/runbook/recording.js +178 -0
  14. package/dist/core/runbook/recording.js.map +1 -0
  15. package/dist/core/runbook/runbookAsTool.js +45 -2
  16. package/dist/core/runbook/runbookAsTool.js.map +1 -1
  17. package/dist/core/tools.js +9 -1
  18. package/dist/core/tools.js.map +1 -1
  19. package/dist/esm/artifacts/recordingArtifact.d.ts +9 -0
  20. package/dist/esm/artifacts/recordingArtifact.js +5 -1
  21. package/dist/esm/artifacts/recordingArtifact.js.map +1 -1
  22. package/dist/esm/core/Agent.d.ts +7 -0
  23. package/dist/esm/core/Agent.js +50 -0
  24. package/dist/esm/core/Agent.js.map +1 -1
  25. package/dist/esm/core/agent/stages/callLLM.d.ts +11 -0
  26. package/dist/esm/core/agent/stages/callLLM.js +14 -0
  27. package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
  28. package/dist/esm/core/agent/stages/toolCalls.d.ts +20 -0
  29. package/dist/esm/core/agent/stages/toolCalls.js +107 -0
  30. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  31. package/dist/esm/core/agent/types.d.ts +63 -0
  32. package/dist/esm/core/runbook/index.d.ts +2 -1
  33. package/dist/esm/core/runbook/index.js +1 -0
  34. package/dist/esm/core/runbook/index.js.map +1 -1
  35. package/dist/esm/core/runbook/recording.d.ts +123 -0
  36. package/dist/esm/core/runbook/recording.js +172 -0
  37. package/dist/esm/core/runbook/recording.js.map +1 -0
  38. package/dist/esm/core/runbook/runbookAsTool.d.ts +5 -1
  39. package/dist/esm/core/runbook/runbookAsTool.js +45 -2
  40. package/dist/esm/core/runbook/runbookAsTool.js.map +1 -1
  41. package/dist/esm/core/runbook/types.d.ts +88 -0
  42. package/dist/esm/core/tools.d.ts +63 -0
  43. package/dist/esm/core/tools.js +17 -0
  44. package/dist/esm/core/tools.js.map +1 -1
  45. package/dist/esm/index.d.ts +3 -1
  46. package/dist/esm/index.js +17 -1
  47. package/dist/esm/index.js.map +1 -1
  48. package/dist/esm/integrity/column-types/check.d.ts +156 -0
  49. package/dist/esm/integrity/column-types/check.js +363 -0
  50. package/dist/esm/integrity/column-types/check.js.map +1 -0
  51. package/dist/esm/integrity/column-types/types.d.ts +121 -0
  52. package/dist/esm/integrity/column-types/types.js +107 -0
  53. package/dist/esm/integrity/column-types/types.js.map +1 -0
  54. package/dist/esm/integrity/disposition/lifecycle.d.ts +16 -1
  55. package/dist/esm/integrity/disposition/lifecycle.js +30 -1
  56. package/dist/esm/integrity/disposition/lifecycle.js.map +1 -1
  57. package/dist/esm/integrity/finding/types.d.ts +14 -2
  58. package/dist/esm/integrity/finding/types.js.map +1 -1
  59. package/dist/esm/lib/mcp/toolExtras.d.ts +17 -2
  60. package/dist/esm/lib/mcp/toolExtras.js +5 -2
  61. package/dist/esm/lib/mcp/toolExtras.js.map +1 -1
  62. package/dist/esm/lib/trace-toolpack/traceToolpack.js +2 -0
  63. package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
  64. package/dist/index.js +23 -3
  65. package/dist/index.js.map +1 -1
  66. package/dist/integrity/column-types/check.js +368 -0
  67. package/dist/integrity/column-types/check.js.map +1 -0
  68. package/dist/integrity/column-types/types.js +112 -0
  69. package/dist/integrity/column-types/types.js.map +1 -0
  70. package/dist/integrity/disposition/lifecycle.js +30 -1
  71. package/dist/integrity/disposition/lifecycle.js.map +1 -1
  72. package/dist/integrity/finding/types.js.map +1 -1
  73. package/dist/lib/mcp/toolExtras.js +4 -1
  74. package/dist/lib/mcp/toolExtras.js.map +1 -1
  75. package/dist/lib/trace-toolpack/traceToolpack.js +2 -0
  76. package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
  77. package/dist/types/artifacts/recordingArtifact.d.ts +9 -0
  78. package/dist/types/artifacts/recordingArtifact.d.ts.map +1 -1
  79. package/dist/types/core/Agent.d.ts +7 -0
  80. package/dist/types/core/Agent.d.ts.map +1 -1
  81. package/dist/types/core/agent/stages/callLLM.d.ts +11 -0
  82. package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
  83. package/dist/types/core/agent/stages/toolCalls.d.ts +20 -0
  84. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  85. package/dist/types/core/agent/types.d.ts +63 -0
  86. package/dist/types/core/agent/types.d.ts.map +1 -1
  87. package/dist/types/core/runbook/index.d.ts +2 -1
  88. package/dist/types/core/runbook/index.d.ts.map +1 -1
  89. package/dist/types/core/runbook/recording.d.ts +124 -0
  90. package/dist/types/core/runbook/recording.d.ts.map +1 -0
  91. package/dist/types/core/runbook/runbookAsTool.d.ts +5 -1
  92. package/dist/types/core/runbook/runbookAsTool.d.ts.map +1 -1
  93. package/dist/types/core/runbook/types.d.ts +88 -0
  94. package/dist/types/core/runbook/types.d.ts.map +1 -1
  95. package/dist/types/core/tools.d.ts +63 -0
  96. package/dist/types/core/tools.d.ts.map +1 -1
  97. package/dist/types/index.d.ts +3 -1
  98. package/dist/types/index.d.ts.map +1 -1
  99. package/dist/types/integrity/column-types/check.d.ts +157 -0
  100. package/dist/types/integrity/column-types/check.d.ts.map +1 -0
  101. package/dist/types/integrity/column-types/types.d.ts +122 -0
  102. package/dist/types/integrity/column-types/types.d.ts.map +1 -0
  103. package/dist/types/integrity/disposition/lifecycle.d.ts +16 -1
  104. package/dist/types/integrity/disposition/lifecycle.d.ts.map +1 -1
  105. package/dist/types/integrity/finding/types.d.ts +14 -2
  106. package/dist/types/integrity/finding/types.d.ts.map +1 -1
  107. package/dist/types/lib/mcp/toolExtras.d.ts +17 -2
  108. package/dist/types/lib/mcp/toolExtras.d.ts.map +1 -1
  109. package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
  110. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,195 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.79.0] - 2026-08-30
11
+
12
+ ### Added
13
+
14
+ - **`walk: { recording }` — file the runbook's inner chart as a recording, so
15
+ the walk can actually be DRAWN.** A consumer wired the flow components of
16
+ the lens family to a runbook's answer and could not mount anything, and they
17
+ were right not to try. `runbookAsTool` files its walk as
18
+ `recording/chart-walk`, whose payload is a **row projection** — 129 rows of
19
+ `{step, type, depth, stage, stage_id, runtime_stage_id, subflow, text}`. A
20
+ step graph cannot be inferred from sentences about steps; a consumer handed
21
+ those rows can only correctly REFUSE to guess at the edges. The one piece
22
+ that makes a walk drawable is `structure`, the chart's build-time graph —
23
+ which a finished run does not leave behind and no snapshot carries.
24
+
25
+ Everything needed was already in the file, a few lines apart: the bridge
26
+ builds a fresh inner executor and ATTACHES RECORDERS (the run was recorded,
27
+ it simply was not filed), `recordingPutInput` was already the mint for the
28
+ agent's own run, `mintWalk` was already the guarded side effect that files
29
+ and never fails the answer, and `WalkDescriptor` was already where the spine
30
+ states what it filed. This connects them.
31
+
32
+ Declare `walk: { recording: true }` (or `{ label, maxBytes }`) and the inner
33
+ chart's own **`{ snapshot, events, structure }`** — the `recordRun` contract
34
+ exactly, the shape `observeRecording()` mounts — is filed under the existing
35
+ kind `recording/run`, and its ref rides the SAME spine descriptor as
36
+ `result.walk.recording_ref` beside `recording_kind`, `recording_bytes` and
37
+ `recording_note`. The wire ops that already redeem the walk redeem this with
38
+ **zero new operations**.
39
+
40
+ **OPT-IN, and that is the honest default.** A walk carries *sentences about*
41
+ what happened and no payload from it — values are off by construction
42
+ (`narrative({ includeValues: false })`). A recording is the run: shared
43
+ state, the whole commit log, every attached recorder's data — **whatever the
44
+ chart wrote**. Filing one is a materially bigger promise, so it is declared,
45
+ never begun on an operator's behalf. Unset, nothing extra runs — no second
46
+ snapshot, no bytes measured, no store call — and the envelope is
47
+ byte-identical to 9.78.0, pinned by a test that asserts no `recording_*` key
48
+ exists at all. A reader who never asked for a recording does not even get a
49
+ sentence explaining its absence.
50
+
51
+ **Redaction means the same for both artifacts.** The recording's snapshot is
52
+ read from the REDACTED MIRROR (`getSnapshot({ redact: true })`), never the
53
+ raw working memory — so the `redact` policy that scrubs the walk scrubs the
54
+ recording by the same rule at the same moment, and a redacted key travels as
55
+ `REDACTED` rather than vanishing (a reader sees that a value existed and was
56
+ scrubbed). With no policy configured the flag is a documented no-op.
57
+
58
+ **Size has a declared failure mode, and it is a refusal.** Over
59
+ `walk.recording.maxBytes` (default `DEFAULT_RECORDING_MAX_BYTES` =
60
+ 5,000,000 — a walk is tens of KB, this package's own measured `recordRun`
61
+ bundle was 2.76 MB, a fleet sweep is unbounded) the recording is **not filed
62
+ and not truncated**, and `recording_note` names what it measured, the
63
+ ceiling it broke, and the option that raises it. The asymmetry with the
64
+ walk's row cap is the point: walk rows are independently meaningful so a
65
+ projection of them is still true, but `{ snapshot, events, structure }` is
66
+ one bundle — half a commit log under a whole chart draws a picture nobody
67
+ can check.
68
+
69
+ **The absence is always SPOKEN.** No store, an over-size refusal, an
70
+ unserializable snapshot, a store that threw — each costs the REF and lands a
71
+ named reason in `recording_note`, following `mintWalk`'s own law. A missing
72
+ ref with no sentence would leave a reader guessing, which is the one thing
73
+ the spine exists to prevent.
74
+
75
+ **`events` is empty by construction, and says so.** It is the typed
76
+ *agentfootprint* stream, fired by an agent turn; what ran here is a
77
+ footprintjs chart on its own executor, which fires none. All three keys are
78
+ present (that is what a viewer reads), the empty array is the honest count,
79
+ and the note states it so nobody reads it as a dropped stream — the walk's
80
+ own story rides `snapshot`, where the narrative recorder's data already
81
+ lives.
82
+
83
+ - **`recordingPutInput` accepts `toolCallId`** — stamped on `origin.toolCallId`
84
+ the way `chartWalkPutInput` already did. A walk and the recording it projects
85
+ are two views of ONE tool call, so they carry the same join key, and either
86
+ joins back to the call the model made. Absent for an agent's own run
87
+ recording, which is a whole turn and belongs to no single call.
88
+
89
+ ## [9.78.0] - 2026-08-30
90
+
91
+ ### Added
92
+
93
+ - **`Tool.resultColumns` + `checkColumnTypes` — the column-type contract: a
94
+ tool declares what its rows contain, and the library checks the rows against
95
+ it at the boundary.** Three recorded failures, and they are one shape — *a
96
+ number became something else, and nothing noticed at the seam*:
97
+
98
+ 1. A mapping report wrote `str(m.get("logical_unit_number") or "")`. **LUN 0
99
+ is falsy**, so LUN 0 was stored as an EMPTY STRING on 2,094 mappings, and
100
+ a host group missing the LUN an initiator probes first became
101
+ indistinguishable from one that had it.
102
+ 2. A capacity view rendered `round(mib / 1024, 1)`, so an 8 MiB disk came out
103
+ as `0.0 GB` — which reads as NO DISK, a provisioning failure, during a
104
+ live incident.
105
+ 3. A whole family of tools returned their numbers as quoted strings
106
+ (`"1240"`), which silently blanked every chart, because nothing downstream
107
+ could tell a measure from a label.
108
+
109
+ Every rail passed, honestly, in all three: nothing errored, nothing was
110
+ ungrounded. The library already lets a tool declare what its result IS
111
+ (`resultKind`, 9.70.0); it did not let a tool declare what its result
112
+ CONTAINS, so a rowset had nothing to be wrong against — and every consumer
113
+ downstream was left SNIFFING types out of the data, where one stray `''`
114
+ demotes a numeric column to text in silence.
115
+
116
+ `resultColumns` is the sibling declaration: a column-name → type map, on
117
+ `Tool` beside `resultKind`. Types are `number` / `string` / `boolean` /
118
+ `date` — **the vocabulary this ecosystem's rowset consumers already sniff
119
+ their way to**, not a new one. The one word deliberately left behind is
120
+ `'unknown'`: a sniffer needs it ("I could not tell"), a declaration has no
121
+ use for it. A column maps to a bare word or to `{ type, nullable }` (the
122
+ `CostBudget` two-spellings pattern, normalized once).
123
+
124
+ **THE CEILING**, exported as `COLUMN_TYPE_CEILING` and quoted verbatim into
125
+ every finding, the `EMPTY_LOOKUP_CEILING` law: *"This judges TYPE, never
126
+ MEANING — it can see that a column declared `number` holds a string, and it
127
+ can never see that the string should have been 0, or that a 0.0 should have
128
+ been an 8; a column whose every value has its declared type passes here and
129
+ can still be wrong."* Failures 1 and 3 are caught. **Failure 2 is not, and
130
+ never will be** — `0.0` is a perfectly good number — and the check says so
131
+ out loud rather than letting a green row imply otherwise.
132
+
133
+ **TWO finding kinds, because the field bug turned on the difference.** New
134
+ `ContextErrorKind`s at the **write seam**: **`column-type-mismatch`** (the
135
+ column is THERE and holds the wrong thing) and **`missing-column`** (the
136
+ declared column is in NONE of the rows). *"The value is not what it should
137
+ be"* sends a person to the mapping code; *"the column was never delivered"*
138
+ sends them to the query. A checker that said only "something is off with
139
+ logical_unit_number" would have helped with neither. Each finding names the
140
+ column, the offending value quoted, the rows affected of the rows read, and
141
+ the tool.
142
+
143
+ **OPEN, never closed.** A declaration is a promise about what it NAMES — an
144
+ unlisted column is allowed and never judged. A closed schema would punish the
145
+ wrong party the day a backend adds a column, and it is the rule the
146
+ neighbouring boundary (`toolArgsValidation`) already keeps.
147
+
148
+ **`nullable`, and what "no value" means.** `null`, `undefined` and a key not
149
+ set on a row are one idea with three spellings, and by default all three are
150
+ violations; `nullable: true` legitimizes them and every finding about an
151
+ absence names that one-word fix in its own message. `nullable` is a promise
152
+ about VALUES, not about the column's existence: a declared column in no row
153
+ at all is `missing-column` regardless.
154
+
155
+ **The dial: `AgentOptions.checkColumnTypes`, default `'off'`** —
156
+ `'off'` | `'warn'` | `'enforce'`. **The three words are borrowed, not
157
+ minted:** this boundary is the MIRROR of `toolArgsValidation` (arguments in,
158
+ against `inputSchema`; rows out, against `resultColumns`), and two validators
159
+ at one seam grading themselves in different vocabularies would be a worse
160
+ defect than either could catch — so there is no new `assist`/`guard`/`rails`
161
+ trio here and no new `observe`/`warn`/`refuse` one either. `'warn'` files
162
+ findings and the model reads the rows **exactly** as the tool returned them.
163
+ `'enforce'` REFUSES in the library's own refusal idiom — the `resultCeiling`
164
+ teaching sentence ("…Fix the tool so the column holds what it declares, or
165
+ change the declaration. No data was returned."), the whole payload on every
166
+ channel, delivered status `'invalid'`, never a thrown stack trace.
167
+
168
+ **What it refuses to judge** (`readRowset`): a result is read only when it is
169
+ an ARRAY OF PLAIN OBJECTS with at least one row. Prose, a `null`, a bespoke
170
+ `{ rows: [...] }` wrapper, a claim ticket — and the **zero-row** result,
171
+ which has no columns to be wrong about and is `empty-lookup`'s subject next
172
+ door — all file an explicit `not-applicable` ROW and no finding. Filing
173
+ `missing-column` for every declared column of an empty answer would turn one
174
+ honest emptiness into a pile of false accusations.
175
+
176
+ **Armed by two halves**: the dial off `'off'` **and** at least one tool
177
+ declaring `resultColumns`. Absent either, the run is byte-identical — no
178
+ finding, no event, nothing on the wire, and a declaring tool with the dial
179
+ off runs byte-for-byte the run it ran before the declaration existed. The one
180
+ visible difference is the two registered rows in the disposition report,
181
+ filed `not-applicable`: registered-but-unarmed is a ROW, never silence.
182
+
183
+ **Travels MCP `_meta`** like the library's other tool declarations, both
184
+ directions — a remote catalogue is exactly where a numeric column arriving as
185
+ text goes unnoticed, and leaving the declaration behind would arm the check
186
+ for local tools while leaving every MCP tool a second-class citizen of it. A
187
+ malformed declaration from a foreign server is warned about once and dropped;
188
+ the tool still registers.
189
+
190
+ Exports: `COLUMN_TYPE_CEILING`, `COLUMN_TYPES`, `readRowset`,
191
+ `assertResultColumns`, and the types `ColumnType`, `ColumnDeclaration`,
192
+ `ToolResultColumns`, `ColumnCheckMode`, `ColumnViolation`, `RowsetReading`.
193
+ Docs: `docs-next` → Monitor → Column Types, which also names the three
194
+ existing consumers this declaration feeds (chart axis pickers that sniff, the
195
+ panel deciding table-vs-chart by inference, and `compute` staging rows blind)
196
+ — none of those integrations are built here, they are named so the next
197
+ person does not add an eighth sniffer.
198
+
10
199
  ## [9.77.0] - 2026-08-29
11
200
 
12
201
  ### 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) + `walk {cap}` (default 500; over-cap ⇒ control-flow projection, declared) + `composedOf` (drift-checked at agent BUILD) + kept `recorders`/`keepRecord`/`keepRecordLimit`/`redact`; reserved state keys `verdicts`/`coverage`/`report` (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 |
20
+ | turning a written operational procedure (a runbook, a triage playbook) into ONE agent tool whose every answer is EVIDENCE — coverage folded up from the inner tools it calls, rule name+version, verdict rows with GENERATED meanings, and the recorded walk as an artifact ticket, never bytes | `runbookAsTool` — dials: `procedure` (factory, invoked per call with `ctx.tools`) + `resultKind` (`'verdict/*'` arms the rowset projection; anything else ships spine + the chart's `report`) + `rules {name, version}` (default absent ⇒ `rule_version: 'undeclared'`) + `verdicts {decider, maxRows}` (default 50) + `walk {cap, 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
  | 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 |
22
22
  | a tool returning numbers WITH the caveats that make them honest — interval/aggregation grain, is-it-a-counter, when the world was measured, which ground was NOT covered — as typed data the model reads compactly and the record keeps whole | `semantic()` + `tools.semantics_declared` (model sees `semanticsForModel` projection; `coverage` field absorbed by the coverage()/absent() channel) | `src/lib/semantics/` | 9.53.0 |
23
23
  | a build gate that refuses a triage/inventory tool that forgot its caveats, by tool name and field name | `checkSemantics` + `defineTool({ resultClass })` (`'triage'`/`'inventory'`, the closed set) + bin `agentfootprint-check-semantics` | `src/lib/semantics/check.ts` | 9.53.0 |
@@ -52,6 +52,7 @@ not in this table, search `src/index.ts` for the nearest noun before writing cod
52
52
  | the model inventing an id or a reading that no tool ever returned — a deterministic fabrication detector on the final answer, with a posture for how hard it pushes back | `.namesAndNumbersFromEvidence()` + `posture` (`'assist'` record+flag, the default / `'guard'` one revision then ship flagged / `'rails'` refuse instead) + `shapes` + `exempt` + `minDigits` (default 4) + `nudge` (default off) | `src/core/agent/evidence/` | 9.35.0 |
53
53
  | counting a HUMAN-VERIFIED value as ground for the choice-seam check — the person clicked a row, the app verified the cells against the artifact, and the id the model takes from that selection is not fabricated; the source label travels onto the record | `externalGrounds` + `ExternalGround` + `external_ground_used` | `src/integrity/unsupported-argument/check.ts` | 9.72.0 |
54
54
  | a lookup tool that answers "nothing found" for EVERY id because a filter broke, and an agent reporting that absence as fact — the run itself produced the id, the lookup came back empty, and that PAIR is worth a look | `noticeEmptyLookups` + `readLookupResult` + `EMPTY_LOOKUP_CEILING` (dial default OFF and needs a tool declaring `argumentsFrom` too — two halves; kind `empty-lookup` at seam `write`, always `advisory: true`; empty = a zero-length ARRAY or an `absent()` envelope, every other shape files `not-applicable` and no finding; the ceiling is quoted verbatim into every message) | `src/integrity/empty-lookup/` | 9.77.0 |
55
+ | a tool's ROWS quietly disagreeing with what the tool promised — a LUN 0 stored as `""` because it is falsy, a numeric column arriving as quoted strings, a declared column present in no row at all | `Tool.resultColumns` + `checkColumnTypes` + `COLUMN_TYPE_CEILING` + `readRowset` (dial `'off'` (default) / `'warn'` (file findings, model reads the rows unchanged) / `'enforce'` (refuse the rows, `resultCeiling`'s teaching-sentence idiom, delivered status `'invalid'`) — the `toolArgsValidation` trio, this seam's mirror; needs a tool declaring `resultColumns` too — two halves; types `number`/`string`/`boolean`/`date`, bare word or `{type, nullable}`, deliberately NO `'unknown'`; OPEN — unlisted columns allowed and unjudged; TWO kinds at seam `write` — `column-type-mismatch` (there, wrong type) vs `missing-column` (declared, in no row); rowset = an ARRAY OF PLAIN OBJECTS with ≥1 row, everything else incl. the ZERO-ROW result files `not-applicable` (empty is `empty-lookup`'s subject); travels MCP `_meta`; the ceiling is quoted verbatim into every message) | `src/integrity/column-types/` | 9.78.0 |
55
56
  | a model head-mathing a total from tool-result numbers while a compute tool sits unused on the wire — a LATE line each iteration naming the staged refs and the `wants` tool that spends them (recency working FOR the instruction), plus the revise correction naming the same route | `nudge: true` (default off) + `stagedRefsNudgeLine` + `findStagedRefs` + `grounding_nudged` | `src/core/agent/stagedRefs.ts` | 9.75.0 |
56
57
  | a tool answering "I looked and found nothing", routably | `absent` + `looked_for` + `checked`/`not_checked`/`cannot_cover` + `tryInstead` (delivered status `'absent'`, routable by `onToolStatus`) | `src/core/agent/coverage/absent.ts` | 9.43.0 |
57
58
  | stating what a clean answer does NOT rule out | `coverage` + the declaration `{ checked, not_checked?, cannot_cover? }` | `src/core/agent/coverage/ledger.ts` | 9.43.0 |
@@ -84,12 +84,16 @@ function recordingPutInput(recording, facts = {}) {
84
84
  if (text === '') {
85
85
  throw new UnserializableRecordingError('it serializes to nothing');
86
86
  }
87
+ const origin = {
88
+ ...(facts.runId !== undefined && { runId: facts.runId }),
89
+ ...(facts.toolCallId !== undefined && { toolCallId: facts.toolCallId }),
90
+ };
87
91
  return {
88
92
  kind: exports.RECORDING_ARTIFACT_KIND,
89
93
  mediaType: exports.RECORDING_MEDIA_TYPE,
90
94
  data: text,
91
95
  label: facts.label ?? (facts.runId !== undefined ? `run ${facts.runId}` : 'run recording'),
92
- ...(facts.runId !== undefined && { origin: { runId: facts.runId } }),
96
+ ...(Object.keys(origin).length > 0 && { origin }),
93
97
  };
94
98
  }
95
99
  exports.recordingPutInput = recordingPutInput;
@@ -1 +1 @@
1
- {"version":3,"file":"recordingArtifact.js","sourceRoot":"","sources":["../../src/artifacts/recordingArtifact.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recordingArtifact.js","sourceRoot":"","sources":["../../src/artifacts/recordingArtifact.ts"],"names":[],"mappings":"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"}
@@ -315,6 +315,13 @@ class Agent extends RunnerBase_js_1.RunnerBase {
315
315
  /** See AgentOptions.noticeEmptyLookups (9.77.0). Default false — absent is
316
316
  * byte-identical, save for the registered not-applicable ledger row. */
317
317
  noticeEmptyLookups = false;
318
+ /** Set at chart build: whether any tool in the FULL declared catalog
319
+ * declared `resultColumns` (9.78.0) — the other half of the column-type
320
+ * contract's arming. */
321
+ integrityColumnsPresent = false;
322
+ /** See AgentOptions.checkColumnTypes (9.78.0). Default 'off' — absent is
323
+ * byte-identical, save for the registered not-applicable ledger rows. */
324
+ checkColumnTypes = 'off';
318
325
  /** See AgentOptions.externalGrounds (9.72.0). Absent = door closed,
319
326
  * byte-identical behavior. */
320
327
  externalGrounds;
@@ -675,6 +682,22 @@ class Agent extends RunnerBase_js_1.RunnerBase {
675
682
  }
676
683
  this.noticeEmptyLookups = opts.noticeEmptyLookups;
677
684
  }
685
+ // The column-type contract's dial (9.78.0) — refused at construction for
686
+ // the same reason as the postures above, and with one more: `'enforce'`
687
+ // REFUSES tool results, so a misspelling silently downgraded to off would
688
+ // leave an operator believing a boundary is held that nothing is holding.
689
+ if (opts.checkColumnTypes !== undefined) {
690
+ if (opts.checkColumnTypes !== 'off' &&
691
+ opts.checkColumnTypes !== 'warn' &&
692
+ opts.checkColumnTypes !== 'enforce') {
693
+ throw new Error(`Agent: checkColumnTypes must be 'off', 'warn' or 'enforce', got ` +
694
+ `${JSON.stringify(opts.checkColumnTypes)}. It reads each tool's declared ` +
695
+ `\`resultColumns\` and judges the rows it returns: 'warn' files findings and ` +
696
+ `changes nothing the model reads; 'enforce' refuses the rows and hands the model a ` +
697
+ `teaching sentence instead. Omit it (or pass 'off') and nothing is ever measured.`);
698
+ }
699
+ this.checkColumnTypes = opts.checkColumnTypes;
700
+ }
678
701
  // The claim-check seam (9.21.0). One store per agent, attached at
679
702
  // construction — idempotent by shape: there is no second door to attach a
680
703
  // competing one through, so "one per agent" is a fact of the type rather
@@ -2362,6 +2385,10 @@ class Agent extends RunnerBase_js_1.RunnerBase {
2362
2385
  // TWO HALVES (9.77.0): the operator's dial AND a declaration to arm
2363
2386
  // on. Either alone leaves a registered `not-applicable` row.
2364
2387
  emptyLookup: this.noticeEmptyLookups && this.integrityDanglingPresent,
2388
+ // TWO HALVES (9.78.0), the same law: the operator's dial off `'off'`
2389
+ // AND a tool declaring `resultColumns`. Either alone leaves two
2390
+ // registered `not-applicable` rows.
2391
+ columnTypes: this.checkColumnTypes !== 'off' && this.integrityColumnsPresent,
2365
2392
  }, this.integrityPosture);
2366
2393
  }
2367
2394
  /**
@@ -3092,6 +3119,14 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3092
3119
  .filter(([, tool]) => tool.argumentsFrom !== undefined)
3093
3120
  .map(([name, tool]) => [name, tool.argumentsFrom]));
3094
3121
  this.integrityDanglingPresent = toolGrounding.size > 0;
3122
+ // The column-type contract's declared half (9.78.0) — harvested from the
3123
+ // SAME catalog and with the same ToolProvider caveat, so an MCP-carried
3124
+ // `resultColumns` (which rides `_meta`) arms the check exactly as a
3125
+ // locally-defined one does.
3126
+ // `flatMap` rather than filter-then-assert: the narrowing is real here,
3127
+ // so the twin harvest above's non-null assertion is not inherited.
3128
+ const toolColumns = new Map([...registryByName.entries()].flatMap(([name, tool]) => tool.resultColumns === undefined ? [] : [[name, tool.resultColumns]]));
3129
+ this.integrityColumnsPresent = toolColumns.size > 0;
3095
3130
  // The staged-refs join's other half (grounded numbers): `Tool.wants` by
3096
3131
  // tool name, harvested the same way and with the same ToolProvider caveat.
3097
3132
  // Consumed only by the evidence gate (the callLLM nudge and the recheck
@@ -3142,6 +3177,11 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3142
3177
  // runs where the result is. Value-conditional on BOTH halves, so an
3143
3178
  // agent that never asked for it reads no new key.
3144
3179
  ...(this.noticeEmptyLookups && toolGrounding.size > 0 && { noticeEmptyLookups: true }),
3180
+ // The column-type contract's arming (9.78.0), same job and same
3181
+ // value-conditional law: this stage owns only the "no declaring tool
3182
+ // was called this response" not-applicable notes.
3183
+ ...(this.checkColumnTypes !== 'off' &&
3184
+ toolColumns.size > 0 && { columnDeclaringTools: new Set(toolColumns.keys()) }),
3145
3185
  integrityLedger: this.integrityLedgerHolder,
3146
3186
  ...(this.reliabilityConfig !== undefined && { reliability: this.reliabilityConfig }),
3147
3187
  ...(this.outputSchemaParser !== undefined && {
@@ -3246,6 +3286,16 @@ class Agent extends RunnerBase_js_1.RunnerBase {
3246
3286
  emptyLookupGrounding: toolGrounding,
3247
3287
  integrityLedger: this.integrityLedgerHolder,
3248
3288
  }),
3289
+ // THE WRITE SEAM'S other check (9.78.0) — the column-type contract.
3290
+ // Value-conditional on both halves for the same reason, so an agent
3291
+ // that asked for neither hands the handler exactly the deps object it
3292
+ // always did.
3293
+ ...(this.checkColumnTypes !== 'off' &&
3294
+ toolColumns.size > 0 && {
3295
+ columnDeclarations: toolColumns,
3296
+ columnCheckMode: this.checkColumnTypes,
3297
+ integrityLedger: this.integrityLedgerHolder,
3298
+ }),
3249
3299
  ...(this.externalToolProvider && { externalToolProvider: this.externalToolProvider }),
3250
3300
  ...(this.externalToolProvider && { providerToolCache }),
3251
3301
  ...(permissionChecker && { permissionChecker }),