agentfootprint 9.82.0 → 9.83.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 (70) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/CLAUDE.md +1 -0
  3. package/README.md +15 -15
  4. package/ai-instructions/claude-code/SKILL.md +1 -1
  5. package/dist/core/Agent.js +33 -1
  6. package/dist/core/Agent.js.map +1 -1
  7. package/dist/core/agent/evidence/evidenceIndex.js +96 -11
  8. package/dist/core/agent/evidence/evidenceIndex.js.map +1 -1
  9. package/dist/core/agent/evidence/gate.js +66 -6
  10. package/dist/core/agent/evidence/gate.js.map +1 -1
  11. package/dist/core/agent/stages/route.js +71 -12
  12. package/dist/core/agent/stages/route.js.map +1 -1
  13. package/dist/esm/core/Agent.d.ts +4 -0
  14. package/dist/esm/core/Agent.js +33 -1
  15. package/dist/esm/core/Agent.js.map +1 -1
  16. package/dist/esm/core/agent/evidence/evidenceIndex.d.ts +91 -5
  17. package/dist/esm/core/agent/evidence/evidenceIndex.js +96 -11
  18. package/dist/esm/core/agent/evidence/evidenceIndex.js.map +1 -1
  19. package/dist/esm/core/agent/evidence/gate.d.ts +27 -0
  20. package/dist/esm/core/agent/evidence/gate.js +66 -6
  21. package/dist/esm/core/agent/evidence/gate.js.map +1 -1
  22. package/dist/esm/core/agent/evidence/types.d.ts +12 -0
  23. package/dist/esm/core/agent/stages/route.d.ts +6 -1
  24. package/dist/esm/core/agent/stages/route.js +69 -10
  25. package/dist/esm/core/agent/stages/route.js.map +1 -1
  26. package/dist/esm/core/agent/types.d.ts +66 -0
  27. package/dist/esm/index.d.ts +1 -0
  28. package/dist/esm/index.js +8 -0
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/integrity/disposition/lifecycle.d.ts +17 -1
  31. package/dist/esm/integrity/disposition/lifecycle.js +26 -1
  32. package/dist/esm/integrity/disposition/lifecycle.js.map +1 -1
  33. package/dist/esm/integrity/finding/types.d.ts +12 -2
  34. package/dist/esm/integrity/finding/types.js.map +1 -1
  35. package/dist/esm/integrity/prior-turn-evidence/check.d.ts +145 -0
  36. package/dist/esm/integrity/prior-turn-evidence/check.js +200 -0
  37. package/dist/esm/integrity/prior-turn-evidence/check.js.map +1 -0
  38. package/dist/esm/lib/trace-toolpack/traceToolpack.js +3 -2
  39. package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
  40. package/dist/index.js +12 -3
  41. package/dist/index.js.map +1 -1
  42. package/dist/integrity/disposition/lifecycle.js +26 -1
  43. package/dist/integrity/disposition/lifecycle.js.map +1 -1
  44. package/dist/integrity/finding/types.js.map +1 -1
  45. package/dist/integrity/prior-turn-evidence/check.js +204 -0
  46. package/dist/integrity/prior-turn-evidence/check.js.map +1 -0
  47. package/dist/lib/trace-toolpack/traceToolpack.js +3 -2
  48. package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
  49. package/dist/types/core/Agent.d.ts +4 -0
  50. package/dist/types/core/Agent.d.ts.map +1 -1
  51. package/dist/types/core/agent/evidence/evidenceIndex.d.ts +91 -5
  52. package/dist/types/core/agent/evidence/evidenceIndex.d.ts.map +1 -1
  53. package/dist/types/core/agent/evidence/gate.d.ts +27 -0
  54. package/dist/types/core/agent/evidence/gate.d.ts.map +1 -1
  55. package/dist/types/core/agent/evidence/types.d.ts +12 -0
  56. package/dist/types/core/agent/evidence/types.d.ts.map +1 -1
  57. package/dist/types/core/agent/stages/route.d.ts +6 -1
  58. package/dist/types/core/agent/stages/route.d.ts.map +1 -1
  59. package/dist/types/core/agent/types.d.ts +66 -0
  60. package/dist/types/core/agent/types.d.ts.map +1 -1
  61. package/dist/types/index.d.ts +1 -0
  62. package/dist/types/index.d.ts.map +1 -1
  63. package/dist/types/integrity/disposition/lifecycle.d.ts +17 -1
  64. package/dist/types/integrity/disposition/lifecycle.d.ts.map +1 -1
  65. package/dist/types/integrity/finding/types.d.ts +12 -2
  66. package/dist/types/integrity/finding/types.d.ts.map +1 -1
  67. package/dist/types/integrity/prior-turn-evidence/check.d.ts +146 -0
  68. package/dist/types/integrity/prior-turn-evidence/check.d.ts.map +1 -0
  69. package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
  70. package/package.json +2 -2
@@ -18,8 +18,18 @@
18
18
  *
19
19
  * ## What counts as evidence, and what deliberately does not
20
20
  *
21
- * • **Tool results** — the `role: 'tool'` turns in the conversation. This is
22
- * the whole corpus. A value the model read from a tool is grounded.
21
+ * • **Tool results** — the `role: 'tool'` turns in `scope.history` AS IT
22
+ * STANDS AT JUDGEMENT. A value the model read from a tool is grounded.
23
+ *
24
+ * Say that precisely, because "the whole conversation" is only true for an
25
+ * agent with no window strategy. `.window()` / `.compaction()` /
26
+ * `tokenBudget` REWRITE `scope.history` in place (window.ts, `scope.history
27
+ * = result.window`), so on those agents this corpus is the LIVE WINDOW and
28
+ * a result the window has dropped is not in it — the same value then reads
29
+ * as ungrounded. `keepLastToolResults` (default 2) is what keeps the most
30
+ * recent observations pinned; nothing pins the rest. Anything computed from
31
+ * this index therefore describes what the run can still SEE, never
32
+ * everything it ever read.
23
33
  * • **Object KEYS count.** A map keyed by WWN puts real identifiers in key
24
34
  * position, and the model saw them exactly as it saw the values.
25
35
  * • **Tool call ARGUMENTS do not count.** The model typed those. Grounding a
@@ -40,6 +50,42 @@
40
50
  * system-prompt content this turn was built from. A value the user supplied is
41
51
  * not a fabrication — the user gave it — and neither is one the app's own
42
52
  * prompt or skill body put in front of the model.
53
+ *
54
+ * ## WHEN a value was read, and why the corpus is still conversation-wide
55
+ *
56
+ * The corpus above answers "did the run read this?" and says nothing about
57
+ * WHEN. That gap has a measured cost: an agent answered a fresh question with
58
+ * ZERO tool calls, and the gate approved it because all seven values appeared
59
+ * in an inventory result fetched four turns earlier for a different question.
60
+ * Grounded, and four turns out of date.
61
+ *
62
+ * The fix is NOT to narrow the corpus to this turn. "And what about that
63
+ * disk?" legitimately leans on the previous turn's rows, and flagging those
64
+ * would make the gate cry wolf until somebody switched it off. So the corpus
65
+ * stays whole and every indexed form carries the TURN it was last served in
66
+ * — one number, stamped during the walk that was happening anyway.
67
+ *
68
+ * A TURN starts at each `role: 'user'` message the library did NOT author.
69
+ * `isLibraryAuthoredTurn` (frames.ts) is a statement about AUTHORSHIP, not
70
+ * about time — it exists to keep the exempt corpus from swallowing the gate's
71
+ * own corrections — and it is reused here for a different reason that happens
72
+ * to need the same predicate: a frame the library wrote is not a person asking
73
+ * something new. Excluding them is load-bearing rather than tidy. The evidence
74
+ * recheck appends a `role: 'user'` turn MID-RUN, so counting it as a boundary
75
+ * would push this turn's own tool results into the "earlier" bucket and file a
76
+ * notice against every revised answer.
77
+ *
78
+ * Turn 1 is the first user message still in the window; anything indexed
79
+ * before one (a history with no user turn at all) is turn 0, which is also
80
+ * `currentTurn`, so nothing can read as earlier and no notice can be filed
81
+ * from a conversation with no boundary in it.
82
+ *
83
+ * **The ordinals are WINDOW-RELATIVE**, and that is the honest bound: they
84
+ * count the user turns the run can still see, so under a window strategy
85
+ * "turn 2 of 4" may be the conversation's turn 9 of 13. The BOUNDARY is
86
+ * exact regardless — the current request is un-droppable by every window
87
+ * strategy (`'current-request'`, window/currentRequest.ts) — so "this turn
88
+ * versus earlier" holds; only the DISTANCE is a floor.
43
89
  */
44
90
  import { absenceEvidenceProjection } from '../coverage/index.js';
45
91
  import { isLibraryAuthoredTurn } from './frames.js';
@@ -55,11 +101,16 @@ function add(sink, raw) {
55
101
  if (norm === '')
56
102
  return;
57
103
  for (const form of lookupForms(norm)) {
104
+ // Re-stamping a form the index already holds costs no entry, so it is
105
+ // done before the budget is consulted: a truncated index should still
106
+ // hold the freshest turn for everything it did manage to index.
107
+ if (sink.values.has(form)) {
108
+ sink.values.set(form, sink.turn);
109
+ continue;
110
+ }
58
111
  if (sink.budget <= 0)
59
112
  return;
60
- if (sink.values.has(form))
61
- continue;
62
- sink.values.add(form);
113
+ sink.values.set(form, sink.turn);
63
114
  sink.budget -= 1;
64
115
  }
65
116
  }
@@ -124,21 +175,38 @@ function indexResult(content, sink) {
124
175
  addText(sink, content);
125
176
  }
126
177
  /**
127
- * Build the evidence corpus from a conversation: every `role: 'tool'` turn.
178
+ * Build the evidence corpus from the history AS IT STANDS: every `role: 'tool'`
179
+ * turn still in it, each indexed form stamped with the turn that served it.
128
180
  *
129
181
  * In a single-turn run these are exactly this turn's tool results. In a
130
182
  * continued conversation the earlier turns' results are in here too, and that
131
183
  * is deliberate: the model really did read them, and calling a value from turn
132
- * one a fabrication in turn two would be false.
184
+ * one a fabrication in turn two would be false. Which earlier turns are still
185
+ * here is the WINDOW's decision, not this function's — see the header.
133
186
  */
134
187
  export function evidenceFromHistory(history) {
135
- const sink = { values: new Set(), budget: MAX_INDEX_TOKENS };
188
+ const sink = { values: new Map(), budget: MAX_INDEX_TOKENS, turn: 0 };
189
+ let toolResultsThisTurn = 0;
136
190
  for (const msg of history) {
191
+ // A turn boundary — the person asked something new. The library's own
192
+ // corrections are `role: 'user'` too and are NOT boundaries; see the
193
+ // header for the bug that distinction prevents.
194
+ if (msg.role === 'user' && !isLibraryAuthoredTurn(msg.content)) {
195
+ sink.turn += 1;
196
+ toolResultsThisTurn = 0;
197
+ continue;
198
+ }
137
199
  if (msg.role !== 'tool')
138
200
  continue;
201
+ toolResultsThisTurn += 1;
139
202
  indexResult(msg.content, sink);
140
203
  }
141
- return { values: sink.values, truncated: sink.budget <= 0 };
204
+ return {
205
+ values: sink.values,
206
+ truncated: sink.budget <= 0,
207
+ currentTurn: sink.turn,
208
+ toolResultsThisTurn,
209
+ };
142
210
  }
143
211
  /**
144
212
  * Build the exempt corpus: everything the RUN put in front of the model that
@@ -150,9 +218,26 @@ export function evidenceFromHistory(history) {
150
218
  * summarised or redacted contributes what it has. That direction is the safe
151
219
  * one: a missing exemption can only cost a false flag on a value the app
152
220
  * supplied, and the caller can name it in `exempt`.
221
+ *
222
+ * STATED REACH, because `systemPromptInjections` is broader than "the app's
223
+ * own prompt": it also carries `.memory()` recall and RAG passages. A value
224
+ * that reached the model through memory or retrieval is therefore EXEMPT from
225
+ * grounding — never flagged, and never counted as tool evidence of any turn,
226
+ * so it is invisible to the recency read too. That is the correct call for the
227
+ * fabrication question (the run really did put those words in front of the
228
+ * model) and a real blind spot for the recency one, and it is deliberately NOT
229
+ * fixed here: recall is its own seam, with its own record
230
+ * (`retrievalEvidence_<id>`, `agentfootprint.memory.attached`), and giving a
231
+ * recalled passage a turn stamp means deciding whose turn it belongs to — the
232
+ * one it was written in or the one it was recalled in — which is a question
233
+ * this file has no standing to answer.
153
234
  */
154
235
  export function exemptFromRun(args) {
155
- const sink = { values: new Set(), budget: MAX_INDEX_TOKENS };
236
+ // The same accumulator, walked with no turn boundaries: an exemption is a
237
+ // fact about WHO supplied a value, and the turn it arrived in changes
238
+ // nothing about that. The keys are lifted into a Set at the end so the
239
+ // grounding decision keeps the exact membership type it always took.
240
+ const sink = { values: new Map(), budget: MAX_INDEX_TOKENS, turn: 0 };
156
241
  if (args.userMessage)
157
242
  addText(sink, args.userMessage);
158
243
  for (const msg of args.history) {
@@ -175,6 +260,6 @@ export function exemptFromRun(args) {
175
260
  else if (rec.contentSummary)
176
261
  addText(sink, rec.contentSummary);
177
262
  }
178
- return sink.values;
263
+ return new Set(sink.values.keys());
179
264
  }
180
265
  //# sourceMappingURL=evidenceIndex.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"evidenceIndex.js","sourceRoot":"","sources":["../../../../../src/core/agent/evidence/evidenceIndex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAIH,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEvE;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAoBjC,SAAS,GAAG,CAAC,IAAU,EAAE,GAAW;IAClC,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO;IACxB,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO;QAC7B,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,IAAU,EAAE,IAAY;IACvC,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO;QAC7B,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED,0DAA0D;AAC1D,SAAS,IAAI,CAAC,IAAa,EAAE,IAAU;IACrC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IACpE,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,wEAAwE;QACxE,uDAAuD;QACvD,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChB,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACpB,OAAO;IACT,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtF,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACxB,OAAO;IACT,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,KAAK,MAAM,EAAE,IAAI,IAAI;YAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;YAC3E,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACf,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;AACH,CAAC;AAED,mFAAmF;AACnF,SAAS,WAAW,CAAC,OAAe,EAAE,IAAU;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,sEAAsE;YACtE,uEAAuE;YACvE,sEAAsE;YACtE,oDAAoD;YACpD,uEAAuE;YACvE,iEAAiE;YACjE,0CAA0C;YAC1C,MAAM,UAAU,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;YACrD,IAAI,CAAC,UAAU,IAAI,MAAM,EAAE,IAAI,CAAC,CAAC;YACjC,OAAO;QACT,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;YACrE,sEAAsE;YACtE,uEAAuE;YACvE,4BAA4B;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACzB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA8B;IAChE,MAAM,IAAI,GAAS,EAAE,MAAM,EAAE,IAAI,GAAG,EAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3E,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM;YAAE,SAAS;QAClC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,IAI7B;IACC,MAAM,IAAI,GAAS,EAAE,MAAM,EAAE,IAAI,GAAG,EAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3E,IAAI,IAAI,CAAC,WAAW;QAAE,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAC/B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QAC3D,sEAAsE;QACtE,yEAAyE;QACzE,wEAAwE;QACxE,6BAA6B;QAC7B,IAAI,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QACjD,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,sBAAsB,IAAI,EAAE,EAAE,CAAC;QACpD,IAAI,GAAG,CAAC,UAAU;YAAE,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;QAClD,wEAAwE;QACxE,wEAAwE;QACxE,iCAAiC;aAC5B,IAAI,GAAG,CAAC,cAAc;YAAE,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC;AACrB,CAAC"}
1
+ {"version":3,"file":"evidenceIndex.js","sourceRoot":"","sources":["../../../../../src/core/agent/evidence/evidenceIndex.ts"],"names":[],"mappings":"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"}
@@ -42,6 +42,13 @@
42
42
  * unexamined. A missed fabrication is a miss; a false accusation costs a real
43
43
  * turn and can refuse a good answer, so the bias points the way it does.
44
44
  *
45
+ * **And it does not ask WHEN.** A value read four turns ago for a different
46
+ * question is as grounded here as one read a second ago — which is a measured
47
+ * failure, not a hypothetical (see `../../../integrity/prior-turn-evidence`).
48
+ * That fact is reported beside this check, never folded into it: the gate's
49
+ * job is "was this read at all", and answering a second question through the
50
+ * same verdict would make one dial control two decisions.
51
+ *
45
52
  * ## Why the check is DETERMINISTIC
46
53
  *
47
54
  * No model call, no embedding, no judge. The library's thesis is that
@@ -69,6 +76,13 @@ export declare function resolveEvidenceGate(opts?: NamesAndNumbersOptions): Reso
69
76
  *
70
77
  * `exempt` is checked BEFORE the evidence: a value the user supplied is not a
71
78
  * fabrication whether or not a tool ever echoed it back.
79
+ *
80
+ * The same pass reports WHEN each grounded value was read (`grounding`) — the
81
+ * time axis the corpus gained in 9.83.0. Three buckets, and an exempt value is
82
+ * in none of them: exemption is a statement about who SUPPLIED a value, and a
83
+ * value the app or the person put in front of the model was never something
84
+ * the run had to go and look up. Counting one as tool evidence — of any turn —
85
+ * would answer a question nobody asked.
72
86
  */
73
87
  export declare function checkAnswer(answer: string, args: {
74
88
  readonly gate: ResolvedEvidenceGate;
@@ -92,6 +106,19 @@ export declare function describeValues(values: readonly UnsupportedValue[]): str
92
106
  * compaction frame and the schema frame follow, and a rule with an exception
93
107
  * is not a rule.
94
108
  *
109
+ * ## The scope this sentence used to claim, and no longer does (9.83.0)
110
+ *
111
+ * Both this frame and {@link evidenceRefusalSentence} said the flagged values
112
+ * "appear in NO tool result FROM THIS TURN". The index they are written from
113
+ * has never been turn-scoped — it walks every `role: 'tool'` turn in
114
+ * `scope.history` — so the sentence named a boundary the check did not honour,
115
+ * in the two places the claim is read by a model and by an operator. It was
116
+ * also needlessly weak: a flagged value appears in no tool result the run can
117
+ * point at AT ALL, which is a stronger and true thing to say. So the words
118
+ * changed to the check's real reach. The recency fact the old wording gestured
119
+ * at is now reported where it can be measured — `prior-turn-evidence`, off by
120
+ * default — instead of asserted here where it could not be.
121
+ *
95
122
  * `stagedRefs` (optional) is the staged-refs join computed by the recheck
96
123
  * stage: when this turn holds placed artifact tickets a served `wants` tool
97
124
  * can spend, the frame names the refs and the spender by their declared names
@@ -42,6 +42,13 @@
42
42
  * unexamined. A missed fabrication is a miss; a false accusation costs a real
43
43
  * turn and can refuse a good answer, so the bias points the way it does.
44
44
  *
45
+ * **And it does not ask WHEN.** A value read four turns ago for a different
46
+ * question is as grounded here as one read a second ago — which is a measured
47
+ * failure, not a hypothetical (see `../../../integrity/prior-turn-evidence`).
48
+ * That fact is reported beside this check, never folded into it: the gate's
49
+ * job is "was this read at all", and answering a second question through the
50
+ * same verdict would make one dial control two decisions.
51
+ *
45
52
  * ## Why the check is DETERMINISTIC
46
53
  *
47
54
  * No model call, no embedding, no judge. The library's thesis is that
@@ -148,20 +155,58 @@ function anchor(re) {
148
155
  *
149
156
  * `exempt` is checked BEFORE the evidence: a value the user supplied is not a
150
157
  * fabrication whether or not a tool ever echoed it back.
158
+ *
159
+ * The same pass reports WHEN each grounded value was read (`grounding`) — the
160
+ * time axis the corpus gained in 9.83.0. Three buckets, and an exempt value is
161
+ * in none of them: exemption is a statement about who SUPPLIED a value, and a
162
+ * value the app or the person put in front of the model was never something
163
+ * the run had to go and look up. Counting one as tool evidence — of any turn —
164
+ * would answer a question nobody asked.
151
165
  */
152
166
  export function checkAnswer(answer, args) {
153
167
  const candidates = extractCandidates(answer, args.gate);
154
168
  const unsupported = [];
169
+ let fromThisTurn = 0;
170
+ let fromPriorTurns = 0;
171
+ let latestPriorTurn;
155
172
  for (const candidate of candidates) {
156
173
  const forms = lookupForms(candidate.value);
157
- const known = forms.some((f) => args.exempt.has(f) || args.evidence.values.has(f));
158
- if (!known)
174
+ if (forms.some((f) => args.exempt.has(f)))
175
+ continue;
176
+ // The NEWEST turn any spelling of this value was served in. Newest,
177
+ // because the question is whether this turn could have supplied it — an
178
+ // identifier echoed back by a lookup this turn is this turn's, whatever
179
+ // else served it three turns ago.
180
+ let turn;
181
+ for (const form of forms) {
182
+ const seen = args.evidence.values.get(form);
183
+ if (seen !== undefined && (turn === undefined || seen > turn))
184
+ turn = seen;
185
+ }
186
+ if (turn === undefined) {
159
187
  unsupported.push({ value: clip(candidate.value), shape: candidate.shape });
188
+ continue;
189
+ }
190
+ if (turn >= args.evidence.currentTurn) {
191
+ fromThisTurn += 1;
192
+ continue;
193
+ }
194
+ fromPriorTurns += 1;
195
+ if (latestPriorTurn === undefined || turn > latestPriorTurn)
196
+ latestPriorTurn = turn;
160
197
  }
161
198
  return {
162
199
  unsupported,
163
200
  candidates: candidates.length,
164
201
  evidenceTruncated: args.evidence.truncated,
202
+ grounding: {
203
+ fromThisTurn,
204
+ fromPriorTurns,
205
+ ...(latestPriorTurn !== undefined && { latestPriorTurn }),
206
+ currentTurn: args.evidence.currentTurn,
207
+ toolResultsThisTurn: args.evidence.toolResultsThisTurn,
208
+ indexTruncated: args.evidence.truncated,
209
+ },
165
210
  };
166
211
  }
167
212
  /** Render the flagged values for a human or a model: `` `x` (shape) ``. */
@@ -186,6 +231,19 @@ export function describeValues(values) {
186
231
  * compaction frame and the schema frame follow, and a rule with an exception
187
232
  * is not a rule.
188
233
  *
234
+ * ## The scope this sentence used to claim, and no longer does (9.83.0)
235
+ *
236
+ * Both this frame and {@link evidenceRefusalSentence} said the flagged values
237
+ * "appear in NO tool result FROM THIS TURN". The index they are written from
238
+ * has never been turn-scoped — it walks every `role: 'tool'` turn in
239
+ * `scope.history` — so the sentence named a boundary the check did not honour,
240
+ * in the two places the claim is read by a model and by an operator. It was
241
+ * also needlessly weak: a flagged value appears in no tool result the run can
242
+ * point at AT ALL, which is a stronger and true thing to say. So the words
243
+ * changed to the check's real reach. The recency fact the old wording gestured
244
+ * at is now reported where it can be measured — `prior-turn-evidence`, off by
245
+ * default — instead of asserted here where it could not be.
246
+ *
189
247
  * `stagedRefs` (optional) is the staged-refs join computed by the recheck
190
248
  * stage: when this turn holds placed artifact tickets a served `wants` tool
191
249
  * can spend, the frame names the refs and the spender by their declared names
@@ -197,7 +255,7 @@ export function describeValues(values) {
197
255
  */
198
256
  export function buildEvidenceCorrection(failedAnswer, values, stagedRefs) {
199
257
  const frame = `${EVIDENCE_CHECK_FRAME_PREFIX} — the answer above states values that appear in NO tool ` +
200
- `result from this turn, so they were not read from the data. Reply again using only names ` +
258
+ `result this run read, so they were not read from the data. Reply again using only names ` +
201
259
  `and numbers a tool actually returned. If you need one of these values, call the tool that ` +
202
260
  `provides it.` +
203
261
  (stagedRefs === undefined ? '' : stagedRefsTeachingClause(stagedRefs)) +
@@ -220,14 +278,16 @@ export function buildEvidenceCorrection(failedAnswer, values, stagedRefs) {
220
278
  */
221
279
  export function evidenceRefusalSentence(values, posture, revised) {
222
280
  const head = posture === 'rails'
223
- ? `[agentfootprint] this answer was NOT returned: ${values.length} value(s) in it appear in no tool result from this turn`
224
- : `[agentfootprint] this answer states ${values.length} value(s) that appear in no tool result from this turn`;
281
+ ? `[agentfootprint] this answer was NOT returned: ${values.length} value(s) in it appear in no tool result this run read`
282
+ : `[agentfootprint] this answer states ${values.length} value(s) that appear in no tool result this run read`;
225
283
  return (`${head} — ${describeValues(values)}. ` +
226
284
  (revised ? 'The model was asked once to correct them and they survived the revision. ' : '') +
227
285
  'What would satisfy the check: every name and number in the answer appears in a tool ' +
228
286
  'result (or in the message you sent). Call a tool that returns these values, declare their ' +
229
287
  'shape via `shapes` if they are legitimate and the extractor mis-read them, or accept the ' +
230
288
  "answer with `posture: 'assist'`. This check catches INVENTED values only — it cannot " +
231
- 'tell you whether a claim built from real values is true.');
289
+ 'tell you whether a claim built from real values is true, and it does not ask WHEN a value ' +
290
+ 'was read (`noticePriorTurnEvidence` reports that). What it reads is the live window: a ' +
291
+ 'result a window strategy has already dropped is not in it.');
232
292
  }
233
293
  //# sourceMappingURL=gate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../../../src/core/agent/evidence/gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../../../src/core/agent/evidence/gate.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@
6
6
  * Role: core/ layer. Nothing here runs; it is the contract.
7
7
  * Emits: N/A.
8
8
  */
9
+ import type { AnswerGroundingReading } from '../../../integrity/prior-turn-evidence/check.js';
9
10
  /**
10
11
  * How hard the check pushes back. **Same three words as the skill-graph
11
12
  * routing dial, deliberately** — one posture vocabulary across the library —
@@ -138,4 +139,15 @@ export interface EvidenceVerdict {
138
139
  * accusation.
139
140
  */
140
141
  readonly evidenceTruncated: boolean;
142
+ /**
143
+ * WHEN the answer's grounded values were read (9.83.0) — the same pass,
144
+ * one time axis.
145
+ *
146
+ * Always computed, because it is three counters over a loop that was
147
+ * already running. It is only READ when the operator armed
148
+ * `AgentOptions.noticePriorTurnEvidence`, which is what turns it into a
149
+ * `prior-turn-evidence` finding; nothing else in the gate consults it and
150
+ * no posture changes because of it.
151
+ */
152
+ readonly grounding: AnswerGroundingReading;
141
153
  }
@@ -75,4 +75,9 @@ hasWrapUp?: boolean,
75
75
  * Both absent → the claim seam never runs and not one line changes. */
76
76
  claims?: readonly DeclaredClaim[], integrityLedger?: {
77
77
  current: DispositionLedger | undefined;
78
- }): (scope: TypedScope<AgentState>) => RouteBranch | Promise<RouteBranch>;
78
+ },
79
+ /** `AgentOptions.noticePriorTurnEvidence` (9.83.0) — the claim seam's
80
+ * recency read. Needs the evidence gate to be armed too (it owns the
81
+ * extractor), so absent OR gate-less → not one row, not one event, and
82
+ * the decider a pre-9.83.0 chart was handed. */
83
+ noticePriorTurnEvidence?: boolean): (scope: TypedScope<AgentState>) => RouteBranch | Promise<RouteBranch>;
@@ -19,6 +19,8 @@ import { judgeAnswer, recordOutputAttempt, } from '../outputEnforcement.js';
19
19
  import { pointerOf, remainingStepsOf, stepInProgress, } from '../../../lib/injection-engine/skillSteps.js';
20
20
  import { checkAnswer, evidenceRefusalSentence, MAX_REPORTED_VALUES } from '../evidence/gate.js';
21
21
  import { evidenceFromHistory, exemptFromRun } from '../evidence/evidenceIndex.js';
22
+ import { priorTurnEvidenceOf } from '../../../integrity/prior-turn-evidence/check.js';
23
+ import { fileIntegrityFindings } from '../integrityFindings.js';
22
24
  import { unsupportedClaimsOf } from '../../../integrity/unsupported-claim/check.js';
23
25
  import { contextErrorIdentity } from '../../../integrity/finding/types.js';
24
26
  /** The base decision, with the sentence that explains it. Split out so the
@@ -299,7 +301,10 @@ function judgeUnfinishedSteps(scope, stepPlanFor, earlyStop) {
299
301
  * • otherwise → `'flagged'`: the answer ships unchanged with the fact on the
300
302
  * record and one warning naming the values.
301
303
  */
302
- function judgeEvidence(scope, gate, earlyStop) {
304
+ function judgeEvidence(scope, gate, earlyStop,
305
+ /** `AgentOptions.noticePriorTurnEvidence` (9.83.0). Absent → the recency
306
+ * read is computed and never looked at, and no row is ever filed. */
307
+ noticePriorTurnEvidence, integrityLedger) {
303
308
  if (gate === undefined)
304
309
  return undefined;
305
310
  const answer = scope.llmLatestContent ?? '';
@@ -307,8 +312,12 @@ function judgeEvidence(scope, gate, earlyStop) {
307
312
  const afterRevision = scope.evidenceRevisionSpent === true;
308
313
  // An empty answer states nothing. `stoppedEarly` already reports why it is
309
314
  // empty; adding "and it cited nothing" would be noise about a non-answer.
310
- if (answer === '')
315
+ if (answer === '') {
316
+ // …but the recency row still has to say it could not run. An armed check
317
+ // that files nothing is indistinguishable from an unhooked one.
318
+ noteRecency(noticePriorTurnEvidence, integrityLedger, 'unreachable');
311
319
  return undefined;
320
+ }
312
321
  const history = scope.history;
313
322
  const verdict = checkAnswer(answer, {
314
323
  gate,
@@ -319,6 +328,19 @@ function judgeEvidence(scope, gate, earlyStop) {
319
328
  systemPromptInjections: scope.systemPromptInjections,
320
329
  }),
321
330
  });
331
+ // ── THE CLAIM SEAM'S RECENCY READ (9.83.0) — `prior-turn-evidence` ──
332
+ // Judged on EVERY outcome, clean or flagged, and before the branch below
333
+ // returns: "grounded" and "grounded, but every value is four turns old"
334
+ // are different facts about the same answer, and an answer that is about
335
+ // to be revised for a fabrication can still be resting on stale evidence
336
+ // for everything else it says. Detection only — it never chooses a branch,
337
+ // so the revise/refuse/flag decision below is byte-for-byte the one every
338
+ // release since 9.35.0 made.
339
+ if (noticePriorTurnEvidence === true) {
340
+ const recency = priorTurnEvidenceOf(verdict.grounding, iteration);
341
+ integrityLedger?.current?.note('prior-turn-evidence', 'claim', recency.disposition, recency.disposition === 'checked-fail' ? Date.now() : undefined);
342
+ fileIntegrityFindings(scope, recency.findings, iteration);
343
+ }
322
344
  if (verdict.unsupported.length === 0) {
323
345
  typedEmit(scope, 'agentfootprint.agent.evidence_checked', {
324
346
  iteration,
@@ -377,6 +399,25 @@ function judgeEvidence(scope, gate, earlyStop) {
377
399
  }
378
400
  return undefined;
379
401
  }
402
+ /**
403
+ * File the recency check's disposition WITHOUT reading anything (9.83.0) —
404
+ * `noteClaims`' precedent, one judge over, and for the identical reason.
405
+ *
406
+ * An armed check that leaves its row UNTOUCHED is what `assertAlive` reads as
407
+ * wiring rot, and in `integrityPosture: 'dev'` that is a `CheckerDeadError` on
408
+ * a perfectly healthy run. Three terminal exits reach a caller without the
409
+ * evidence gate ever producing a grounding reading — an empty answer, a
410
+ * middleware denial, and an answer the output schema rejected — so each says
411
+ * so in the ledger rather than going quiet.
412
+ *
413
+ * Value-conditional on the dial: an unarmed check already carries its
414
+ * `not-applicable` row from registration and must not be noted twice.
415
+ */
416
+ function noteRecency(armed, ledger, disposition) {
417
+ if (armed !== true)
418
+ return;
419
+ ledger?.current?.note('prior-turn-evidence', 'claim', disposition);
420
+ }
380
421
  /** The `route_decided` rationale for an evidence recheck. */
381
422
  function evidenceRecheckRationale(scope) {
382
423
  const pending = scope.evidenceUnsupported;
@@ -467,7 +508,12 @@ export function buildRouteDeciderStage(messageMiddleware, enforcement, stepPlanF
467
508
  hasWrapUp = false,
468
509
  /** The declared claim contract (9.61.0) and the run's disposition ledger.
469
510
  * Both absent → the claim seam never runs and not one line changes. */
470
- claims, integrityLedger) {
511
+ claims, integrityLedger,
512
+ /** `AgentOptions.noticePriorTurnEvidence` (9.83.0) — the claim seam's
513
+ * recency read. Needs the evidence gate to be armed too (it owns the
514
+ * extractor), so absent OR gate-less → not one row, not one event, and
515
+ * the decider a pre-9.83.0 chart was handed. */
516
+ noticePriorTurnEvidence) {
471
517
  const chain = messageMiddleware ?? [];
472
518
  if (chain.length === 0 &&
473
519
  enforcement === undefined &&
@@ -476,9 +522,9 @@ claims, integrityLedger) {
476
522
  return hasWrapUp ? buildSimpleDecider(true) : routeDeciderStage;
477
523
  }
478
524
  if (enforcement !== undefined)
479
- return buildEnforcingDecider(chain, enforcement, stepPlanFor, evidence, hasWrapUp, claims, integrityLedger);
525
+ return buildEnforcingDecider(chain, enforcement, stepPlanFor, evidence, hasWrapUp, claims, integrityLedger, noticePriorTurnEvidence);
480
526
  if (stepPlanFor !== undefined || evidence !== undefined)
481
- return buildJudgingDecider(chain, stepPlanFor, evidence, hasWrapUp);
527
+ return buildJudgingDecider(chain, stepPlanFor, evidence, hasWrapUp, noticePriorTurnEvidence, integrityLedger);
482
528
  return async (scope) => {
483
529
  const { chosen, rationale, earlyStop } = decideBranch(scope);
484
530
  // ── The out-of-budget wrap-up (9.56.0) ─────────────────────────────
@@ -622,7 +668,7 @@ function readValidatedAnswer(scope) {
622
668
  return undefined;
623
669
  }
624
670
  }
625
- function buildJudgingDecider(chain, stepPlanFor, evidence, hasWrapUp) {
671
+ function buildJudgingDecider(chain, stepPlanFor, evidence, hasWrapUp, noticePriorTurnEvidence, integrityLedger) {
626
672
  return async (scope) => {
627
673
  const { chosen, rationale, earlyStop } = decideBranch(scope);
628
674
  if (chosen !== 'final') {
@@ -657,13 +703,19 @@ function buildJudgingDecider(chain, stepPlanFor, evidence, hasWrapUp) {
657
703
  }
658
704
  scope.llmLatestContent = verdict.content;
659
705
  }
706
+ // A withheld answer is judged by nothing below, so the recency row says so
707
+ // here rather than sitting untouched (see `noteRecency`).
708
+ if (denied)
709
+ noteRecency(noticePriorTurnEvidence, integrityLedger, 'not-applicable');
660
710
  if (!denied && judgeUnfinishedSteps(scope, stepPlanFor, earlyStop) === 'step-nudge') {
661
711
  emitRouteDecided(scope, 'step-nudge', stepNudgeRationale(scope));
662
712
  return 'step-nudge';
663
713
  }
664
714
  // AFTER the step judge: an answer about to be nudged is not the final
665
715
  // answer, and grounding it would pay for a turn that is being replaced.
666
- if (!denied && judgeEvidence(scope, evidence, earlyStop) === 'evidence-recheck') {
716
+ if (!denied &&
717
+ judgeEvidence(scope, evidence, earlyStop, noticePriorTurnEvidence, integrityLedger) ===
718
+ 'evidence-recheck') {
667
719
  emitRouteDecided(scope, 'evidence-recheck', evidenceRecheckRationale(scope));
668
720
  return 'evidence-recheck';
669
721
  }
@@ -678,7 +730,7 @@ function buildJudgingDecider(chain, stepPlanFor, evidence, hasWrapUp) {
678
730
  * announce the branch — so `route_decided` names the branch the run actually
679
731
  * took rather than the one it was heading for.
680
732
  */
681
- function buildEnforcingDecider(chain, enforcement, stepPlanFor, evidence, hasWrapUp, claims, integrityLedger) {
733
+ function buildEnforcingDecider(chain, enforcement, stepPlanFor, evidence, hasWrapUp, claims, integrityLedger, noticePriorTurnEvidence) {
682
734
  return async (scope) => {
683
735
  const base = decideBranch(scope);
684
736
  if (base.chosen !== 'final') {
@@ -730,6 +782,10 @@ function buildEnforcingDecider(chain, enforcement, stepPlanFor, evidence, hasWra
730
782
  // The claim seam never runs on a denied answer — nobody receives it —
731
783
  // but the run must still SAY that, per declared claim.
732
784
  noteClaims(claims, integrityLedger, 'not-applicable');
785
+ // Same exit, same reasoning, for the recency read: an answer nobody
786
+ // receives has no provenance worth reporting, and that is a rule rather
787
+ // than a failure to reach one.
788
+ noteRecency(noticePriorTurnEvidence, integrityLedger, 'not-applicable');
733
789
  emitRouteDecided(scope, 'final', base.rationale);
734
790
  settleWrapUp(scope, base.earlyStop, false);
735
791
  return 'final';
@@ -751,7 +807,8 @@ function buildEnforcingDecider(chain, enforcement, stepPlanFor, evidence, hasWra
751
807
  // The evidence gate is last of the three judges: it grounds the answer
752
808
  // the schema accepted and the procedure finished — the one that is
753
809
  // really about to be handed back.
754
- if (judgeEvidence(scope, evidence, base.earlyStop) === 'evidence-recheck') {
810
+ if (judgeEvidence(scope, evidence, base.earlyStop, noticePriorTurnEvidence, integrityLedger) ===
811
+ 'evidence-recheck') {
755
812
  emitRouteDecided(scope, 'evidence-recheck', evidenceRecheckRationale(scope));
756
813
  return 'evidence-recheck';
757
814
  }
@@ -822,7 +879,9 @@ function buildEnforcingDecider(chain, enforcement, stepPlanFor, evidence, hasWra
822
879
  // failed its own contract and the caller is being told so; grounding the
823
880
  // values inside a shape the app has declared invalid would file a second
824
881
  // verdict about a string nobody is going to use, and under `guard` it
825
- // would buy a revision for an answer that cannot pass anyway.
882
+ // would buy a revision for an answer that cannot pass anyway. The recency
883
+ // row says exactly that — out of scope by rule, never silence.
884
+ noteRecency(noticePriorTurnEvidence, integrityLedger, 'not-applicable');
826
885
  emitRouteDecided(scope, 'final', brokenByChain !== undefined
827
886
  ? `final answer satisfied the output schema until the output rule '${brokenByChain}' ` +
828
887
  `rewrote it (${failure.stage}) — not re-asked, because the model's answer was fine`