agentfootprint 9.34.0 → 9.35.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 (138) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +3 -1
  3. package/ai-instructions/claude-code/SKILL.md +1 -1
  4. package/dist/conventions.js +14 -0
  5. package/dist/conventions.js.map +1 -1
  6. package/dist/core/Agent.js +89 -5
  7. package/dist/core/Agent.js.map +1 -1
  8. package/dist/core/agent/AgentBuilder.js +76 -1
  9. package/dist/core/agent/AgentBuilder.js.map +1 -1
  10. package/dist/core/agent/buildAgentChart.js +8 -0
  11. package/dist/core/agent/buildAgentChart.js.map +1 -1
  12. package/dist/core/agent/buildDynamicAgentChart.js +16 -0
  13. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
  14. package/dist/core/agent/buildToolRegistry.js.map +1 -1
  15. package/dist/core/agent/evidence/errors.js +55 -0
  16. package/dist/core/agent/evidence/errors.js.map +1 -0
  17. package/dist/core/agent/evidence/evidenceIndex.js +167 -0
  18. package/dist/core/agent/evidence/evidenceIndex.js.map +1 -0
  19. package/dist/core/agent/evidence/extract.js +145 -0
  20. package/dist/core/agent/evidence/extract.js.map +1 -0
  21. package/dist/core/agent/evidence/frames.js +44 -0
  22. package/dist/core/agent/evidence/frames.js.map +1 -0
  23. package/dist/core/agent/evidence/gate.js +206 -0
  24. package/dist/core/agent/evidence/gate.js.map +1 -0
  25. package/dist/core/agent/evidence/index.js +15 -0
  26. package/dist/core/agent/evidence/index.js.map +1 -0
  27. package/dist/core/agent/evidence/normalize.js +141 -0
  28. package/dist/core/agent/evidence/normalize.js.map +1 -0
  29. package/dist/core/agent/evidence/types.js +11 -0
  30. package/dist/core/agent/evidence/types.js.map +1 -0
  31. package/dist/core/agent/stages/evidenceRecheck.js +86 -0
  32. package/dist/core/agent/stages/evidenceRecheck.js.map +1 -0
  33. package/dist/core/agent/stages/route.js +152 -16
  34. package/dist/core/agent/stages/route.js.map +1 -1
  35. package/dist/core/agent/stages/seed.js +7 -0
  36. package/dist/core/agent/stages/seed.js.map +1 -1
  37. package/dist/esm/conventions.d.ts +7 -0
  38. package/dist/esm/conventions.js +14 -0
  39. package/dist/esm/conventions.js.map +1 -1
  40. package/dist/esm/core/Agent.d.ts +34 -1
  41. package/dist/esm/core/Agent.js +87 -3
  42. package/dist/esm/core/Agent.js.map +1 -1
  43. package/dist/esm/core/agent/AgentBuilder.d.ts +66 -0
  44. package/dist/esm/core/agent/AgentBuilder.js +76 -1
  45. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  46. package/dist/esm/core/agent/buildAgentChart.d.ts +21 -0
  47. package/dist/esm/core/agent/buildAgentChart.js +8 -0
  48. package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
  49. package/dist/esm/core/agent/buildDynamicAgentChart.js +16 -0
  50. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
  51. package/dist/esm/core/agent/buildToolRegistry.js +1 -1
  52. package/dist/esm/core/agent/buildToolRegistry.js.map +1 -1
  53. package/dist/esm/core/agent/evidence/errors.d.ts +55 -0
  54. package/dist/esm/core/agent/evidence/errors.js +51 -0
  55. package/dist/esm/core/agent/evidence/errors.js.map +1 -0
  56. package/dist/esm/core/agent/evidence/evidenceIndex.d.ts +73 -0
  57. package/dist/esm/core/agent/evidence/evidenceIndex.js +162 -0
  58. package/dist/esm/core/agent/evidence/evidenceIndex.js.map +1 -0
  59. package/dist/esm/core/agent/evidence/extract.d.ts +66 -0
  60. package/dist/esm/core/agent/evidence/extract.js +139 -0
  61. package/dist/esm/core/agent/evidence/extract.js.map +1 -0
  62. package/dist/esm/core/agent/evidence/frames.d.ts +36 -0
  63. package/dist/esm/core/agent/evidence/frames.js +40 -0
  64. package/dist/esm/core/agent/evidence/frames.js.map +1 -0
  65. package/dist/esm/core/agent/evidence/gate.d.ts +96 -0
  66. package/dist/esm/core/agent/evidence/gate.js +201 -0
  67. package/dist/esm/core/agent/evidence/gate.js.map +1 -0
  68. package/dist/esm/core/agent/evidence/index.d.ts +11 -0
  69. package/dist/esm/core/agent/evidence/index.js +11 -0
  70. package/dist/esm/core/agent/evidence/index.js.map +1 -0
  71. package/dist/esm/core/agent/evidence/normalize.d.ts +49 -0
  72. package/dist/esm/core/agent/evidence/normalize.js +134 -0
  73. package/dist/esm/core/agent/evidence/normalize.js.map +1 -0
  74. package/dist/esm/core/agent/evidence/types.d.ts +118 -0
  75. package/dist/esm/core/agent/evidence/types.js +10 -0
  76. package/dist/esm/core/agent/evidence/types.js.map +1 -0
  77. package/dist/esm/core/agent/stages/evidenceRecheck.d.ts +31 -0
  78. package/dist/esm/core/agent/stages/evidenceRecheck.js +82 -0
  79. package/dist/esm/core/agent/stages/evidenceRecheck.js.map +1 -0
  80. package/dist/esm/core/agent/stages/route.d.ts +3 -2
  81. package/dist/esm/core/agent/stages/route.js +152 -16
  82. package/dist/esm/core/agent/stages/route.js.map +1 -1
  83. package/dist/esm/core/agent/stages/seed.d.ts +8 -0
  84. package/dist/esm/core/agent/stages/seed.js +7 -0
  85. package/dist/esm/core/agent/stages/seed.js.map +1 -1
  86. package/dist/esm/core/agent/types.d.ts +44 -0
  87. package/dist/esm/events/payloads.d.ts +53 -2
  88. package/dist/esm/events/registry.d.ts +3 -1
  89. package/dist/esm/events/registry.js +2 -0
  90. package/dist/esm/events/registry.js.map +1 -1
  91. package/dist/esm/index.d.ts +2 -0
  92. package/dist/esm/index.js +10 -0
  93. package/dist/esm/index.js.map +1 -1
  94. package/dist/events/registry.js +2 -0
  95. package/dist/events/registry.js.map +1 -1
  96. package/dist/index.js +50 -38
  97. package/dist/index.js.map +1 -1
  98. package/dist/types/conventions.d.ts +7 -0
  99. package/dist/types/conventions.d.ts.map +1 -1
  100. package/dist/types/core/Agent.d.ts +34 -1
  101. package/dist/types/core/Agent.d.ts.map +1 -1
  102. package/dist/types/core/agent/AgentBuilder.d.ts +66 -0
  103. package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
  104. package/dist/types/core/agent/buildAgentChart.d.ts +21 -0
  105. package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
  106. package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
  107. package/dist/types/core/agent/buildToolRegistry.d.ts.map +1 -1
  108. package/dist/types/core/agent/evidence/errors.d.ts +56 -0
  109. package/dist/types/core/agent/evidence/errors.d.ts.map +1 -0
  110. package/dist/types/core/agent/evidence/evidenceIndex.d.ts +74 -0
  111. package/dist/types/core/agent/evidence/evidenceIndex.d.ts.map +1 -0
  112. package/dist/types/core/agent/evidence/extract.d.ts +67 -0
  113. package/dist/types/core/agent/evidence/extract.d.ts.map +1 -0
  114. package/dist/types/core/agent/evidence/frames.d.ts +37 -0
  115. package/dist/types/core/agent/evidence/frames.d.ts.map +1 -0
  116. package/dist/types/core/agent/evidence/gate.d.ts +97 -0
  117. package/dist/types/core/agent/evidence/gate.d.ts.map +1 -0
  118. package/dist/types/core/agent/evidence/index.d.ts +12 -0
  119. package/dist/types/core/agent/evidence/index.d.ts.map +1 -0
  120. package/dist/types/core/agent/evidence/normalize.d.ts +50 -0
  121. package/dist/types/core/agent/evidence/normalize.d.ts.map +1 -0
  122. package/dist/types/core/agent/evidence/types.d.ts +119 -0
  123. package/dist/types/core/agent/evidence/types.d.ts.map +1 -0
  124. package/dist/types/core/agent/stages/evidenceRecheck.d.ts +32 -0
  125. package/dist/types/core/agent/stages/evidenceRecheck.d.ts.map +1 -0
  126. package/dist/types/core/agent/stages/route.d.ts +3 -2
  127. package/dist/types/core/agent/stages/route.d.ts.map +1 -1
  128. package/dist/types/core/agent/stages/seed.d.ts +8 -0
  129. package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
  130. package/dist/types/core/agent/types.d.ts +44 -0
  131. package/dist/types/core/agent/types.d.ts.map +1 -1
  132. package/dist/types/events/payloads.d.ts +53 -2
  133. package/dist/types/events/payloads.d.ts.map +1 -1
  134. package/dist/types/events/registry.d.ts +3 -1
  135. package/dist/types/events/registry.d.ts.map +1 -1
  136. package/dist/types/index.d.ts +2 -0
  137. package/dist/types/index.d.ts.map +1 -1
  138. package/package.json +1 -1
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ /**
3
+ * evidenceIndex — what the run can PROVE it read, as a lookup set.
4
+ *
5
+ * Pattern: build once per judgement, ask many times (the same "one pass, then
6
+ * query" shape the slice layer uses).
7
+ * Role: core/ layer. The other half of `namesAndNumbersFromEvidence`.
8
+ * Emits: N/A.
9
+ *
10
+ * ## Why the walk is STRUCTURAL
11
+ *
12
+ * The obvious implementation is `allToolResults.includes(value)`. It is wrong
13
+ * in the direction that matters: a fabricated FCID `0xef0101` "appears" in a
14
+ * blob that contains `0xef01011` or `naa.0xef0101ab`, so the invented value
15
+ * reads as grounded and the check quietly passes everything. So a tool result
16
+ * that parses as JSON is WALKED — every key, every leaf — and each leaf is
17
+ * indexed both whole and tokenized. A result that is not JSON is tokenized as
18
+ * text. Either way the comparison is token-exact, never substring.
19
+ *
20
+ * ## What counts as evidence, and what deliberately does not
21
+ *
22
+ * • **Tool results** — the `role: 'tool'` turns in the conversation. This is
23
+ * the whole corpus. A value the model read from a tool is grounded.
24
+ * • **Object KEYS count.** A map keyed by WWN puts real identifiers in key
25
+ * position, and the model saw them exactly as it saw the values.
26
+ * • **Tool call ARGUMENTS do not count.** The model typed those. Grounding a
27
+ * value because the model passed it to a tool would let any invention
28
+ * launder itself through one failed lookup.
29
+ * • **The model's own earlier answers do not count**, for the same reason.
30
+ *
31
+ * The EXEMPT index is built from a different corpus with the same machinery:
32
+ * the user's own message, the conversation's user/system turns, and the
33
+ * system-prompt content this turn was built from. A value the user supplied is
34
+ * not a fabrication — the user gave it — and neither is one the app's own
35
+ * prompt or skill body put in front of the model.
36
+ */
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.exemptFromRun = exports.evidenceFromHistory = void 0;
39
+ const frames_js_1 = require("./frames.js");
40
+ const normalize_js_1 = require("./normalize.js");
41
+ /**
42
+ * Ceiling on indexed tokens. Generous — a 200 000-token corpus is roughly a
43
+ * 5 MB tool result — because the cost of hitting it is not "slower", it is
44
+ * "the gate stops accusing" (see {@link EvidenceCorpus.truncated}).
45
+ */
46
+ const MAX_INDEX_TOKENS = 200_000;
47
+ function add(sink, raw) {
48
+ const norm = (0, normalize_js_1.normalizeToken)(raw);
49
+ if (norm === '')
50
+ return;
51
+ for (const form of (0, normalize_js_1.lookupForms)(norm)) {
52
+ if (sink.budget <= 0)
53
+ return;
54
+ if (sink.values.has(form))
55
+ continue;
56
+ sink.values.add(form);
57
+ sink.budget -= 1;
58
+ }
59
+ }
60
+ function addText(sink, text) {
61
+ for (const token of (0, normalize_js_1.tokenize)(text)) {
62
+ if (sink.budget <= 0)
63
+ return;
64
+ add(sink, token);
65
+ }
66
+ }
67
+ /** Walk a parsed JSON value, indexing keys and leaves. */
68
+ function walk(node, sink) {
69
+ if (sink.budget <= 0 || node === null || node === undefined)
70
+ return;
71
+ if (typeof node === 'string') {
72
+ // Whole value first (a leaf may contain spaces and still be one value),
73
+ // then its tokens (`"fc1/3 is down"` carries `fc1/3`).
74
+ add(sink, node);
75
+ addText(sink, node);
76
+ return;
77
+ }
78
+ if (typeof node === 'number' || typeof node === 'boolean' || typeof node === 'bigint') {
79
+ add(sink, String(node));
80
+ return;
81
+ }
82
+ if (Array.isArray(node)) {
83
+ for (const el of node)
84
+ walk(el, sink);
85
+ return;
86
+ }
87
+ if (typeof node === 'object') {
88
+ for (const [key, value] of Object.entries(node)) {
89
+ add(sink, key);
90
+ walk(value, sink);
91
+ }
92
+ }
93
+ }
94
+ /** Index one tool result: structurally when it is JSON, as text when it is not. */
95
+ function indexResult(content, sink) {
96
+ const trimmed = content.trim();
97
+ if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
98
+ try {
99
+ walk(JSON.parse(trimmed), sink);
100
+ return;
101
+ }
102
+ catch {
103
+ // Not JSON after all (a truncated result, a log line that happens to
104
+ // start with a brace). Fall through to the text path rather than lose
105
+ // the evidence entirely — a tool result that cannot be parsed is still
106
+ // something the model read.
107
+ }
108
+ }
109
+ addText(sink, content);
110
+ }
111
+ /**
112
+ * Build the evidence corpus from a conversation: every `role: 'tool'` turn.
113
+ *
114
+ * In a single-turn run these are exactly this turn's tool results. In a
115
+ * continued conversation the earlier turns' results are in here too, and that
116
+ * is deliberate: the model really did read them, and calling a value from turn
117
+ * one a fabrication in turn two would be false.
118
+ */
119
+ function evidenceFromHistory(history) {
120
+ const sink = { values: new Set(), budget: MAX_INDEX_TOKENS };
121
+ for (const msg of history) {
122
+ if (msg.role !== 'tool')
123
+ continue;
124
+ indexResult(msg.content, sink);
125
+ }
126
+ return { values: sink.values, truncated: sink.budget <= 0 };
127
+ }
128
+ exports.evidenceFromHistory = evidenceFromHistory;
129
+ /**
130
+ * Build the exempt corpus: everything the RUN put in front of the model that
131
+ * the model did not invent — the user's message, the conversation's user and
132
+ * system turns, and the composed system prompt (base prompt, skill bodies,
133
+ * facts, retrieved passages).
134
+ *
135
+ * `rawContent` is optional on an {@link InjectionRecord} — a record that was
136
+ * summarised or redacted contributes what it has. That direction is the safe
137
+ * one: a missing exemption can only cost a false flag on a value the app
138
+ * supplied, and the caller can name it in `exempt`.
139
+ */
140
+ function exemptFromRun(args) {
141
+ const sink = { values: new Set(), budget: MAX_INDEX_TOKENS };
142
+ if (args.userMessage)
143
+ addText(sink, args.userMessage);
144
+ for (const msg of args.history) {
145
+ if (msg.role !== 'user' && msg.role !== 'system')
146
+ continue;
147
+ // …except the corrections this library wrote. They are `role: 'user'`
148
+ // turns that QUOTE the flagged values back to the model, so indexing one
149
+ // would exempt exactly what it challenged — the gate laundering its own
150
+ // accusation. See frames.ts.
151
+ if ((0, frames_js_1.isLibraryAuthoredTurn)(msg.content))
152
+ continue;
153
+ addText(sink, msg.content);
154
+ }
155
+ for (const rec of args.systemPromptInjections ?? []) {
156
+ if (rec.rawContent)
157
+ addText(sink, rec.rawContent);
158
+ // The summary is what a redacted record has instead. Indexing it cannot
159
+ // create a false exemption for a value nobody supplied — the summary is
160
+ // built from the content itself.
161
+ else if (rec.contentSummary)
162
+ addText(sink, rec.contentSummary);
163
+ }
164
+ return sink.values;
165
+ }
166
+ exports.exemptFromRun = exemptFromRun;
167
+ //# sourceMappingURL=evidenceIndex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidenceIndex.js","sourceRoot":"","sources":["../../../../src/core/agent/evidence/evidenceIndex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ /**
3
+ * extract — which tokens in an answer are DATA, and therefore have to be
4
+ * grounded in a tool result.
5
+ *
6
+ * Pattern: a pure classifier over normalized tokens (normalize.ts is the only
7
+ * import), so it can be unit-tested on strings with no agent, no
8
+ * chart and no model.
9
+ * Role: core/ layer. The hard half of `namesAndNumbersFromEvidence`.
10
+ * Emits: N/A.
11
+ *
12
+ * ## The rule, and why every part of it is there
13
+ *
14
+ * A naive "flag every number" extractor makes the feature worse than useless.
15
+ * It flags `24 hours`, `3 issues` and `first`, and under `guard` it then spends
16
+ * a real turn asking the model to justify the word "three" — which is the
17
+ * retry loop this library exists to remove. So the default is CONSERVATIVE: it
18
+ * would rather miss a fabricated value than accuse a correct answer.
19
+ *
20
+ * A token is a candidate only if:
21
+ *
22
+ * 1. **It contains a digit.** English prose is alphabetic. Requiring a digit
23
+ * is the single cheapest separator between "data someone read off a
24
+ * screen" and "a word". It is also the rule's biggest blind spot, stated
25
+ * plainly: a fabricated all-letters name (`esxi-host-alpha`) is invisible
26
+ * to the default and needs a declared shape.
27
+ * 2. **It is distinctive.** Either
28
+ * • an IDENTIFIER — digits mixed with letters or with structural
29
+ * punctuation (`:` `_` `-` `/` `.`), at least 4 characters:
30
+ * `0xef0101`, `fc1/3`, `21:00:00:24:ff:4a:12:03`, `UCSB-B200-M5`; or
31
+ * • a NUMBER with at least `minDigits` (default 4) digits: `41,200`,
32
+ * `18450`, `786432`.
33
+ * 3. **It is not prose wearing a number.** Three exclusions, each from a
34
+ * real sentence in the material:
35
+ * • a number with a short unit or word glued to it — `32G`, `100MB`,
36
+ * `47th`, `2h`, `48-port`, `$20/month` — is judged on its NUMBER
37
+ * alone, so `48-port switch` never trips while `41200iops` still
38
+ * does;
39
+ * • a bare `N/M` of one or two digits each — `24/7`, `1/2` — is a ratio
40
+ * in prose. A two-number port id is spelled the same way, so the
41
+ * conservative reading wins and a domain that needs it declares a
42
+ * shape;
43
+ * • anything the caller declared exempt.
44
+ *
45
+ * Declared shapes are tested FIRST and win: an app that says "this is what my
46
+ * identifiers look like" has better information than these heuristics.
47
+ */
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.extractCandidates = exports.classifyToken = exports.isDeclaredExempt = void 0;
50
+ const normalize_js_1 = require("./normalize.js");
51
+ /** Structural punctuation an identifier is allowed to be built from. */
52
+ const STRUCTURAL = /[:_\-/.]/;
53
+ /** At least one ASCII letter. */
54
+ const HAS_LETTER = /[a-z]/;
55
+ /**
56
+ * `<number><short tail>` — a quantity with its unit or its adjective stuck to
57
+ * it. The tail is capped at 5 letters because real units are short (`mbps`,
58
+ * `gbps`, `hours`) while an identifier's alphabetic run is usually not, and an
59
+ * optional `/word` covers `20/month` and `100mb/s`.
60
+ */
61
+ const NUMBER_WITH_TAIL = /^([-+]?\d+(?:\.\d+)?)[-/]?[a-z]{1,5}(?:\/[a-z]{1,5})?$/;
62
+ /** A plain number, already canonicalised by `normalizeToken`. */
63
+ const BARE_NUMBER = /^[-+]?\d+(?:\.\d+)?$/;
64
+ /** `24/7`, `1/2` — a ratio in prose, not an identifier. */
65
+ const PROSE_RATIO = /^\d{1,2}\/\d{1,2}$/;
66
+ /** Ceiling on how many tokens one answer contributes. A model that pastes a
67
+ * 10 MB table into its answer must not turn the gate into the run's cost. */
68
+ const MAX_ANSWER_TOKENS = 20_000;
69
+ /** True when the caller declared this token exempt. */
70
+ function isDeclaredExempt(token, gate) {
71
+ if (gate.exemptValues.has(token))
72
+ return true;
73
+ for (const p of gate.exemptPatterns)
74
+ if (p.test(token))
75
+ return true;
76
+ return false;
77
+ }
78
+ exports.isDeclaredExempt = isDeclaredExempt;
79
+ /**
80
+ * Classify ONE normalized token. Returns the candidate it produces, or
81
+ * `undefined` when the token is prose.
82
+ *
83
+ * Exported for the unit tests, which is the whole reason the classifier is a
84
+ * function over a string rather than a loop body.
85
+ */
86
+ function classifyToken(token, gate) {
87
+ if (token === '')
88
+ return undefined;
89
+ if (isDeclaredExempt(token, gate))
90
+ return undefined;
91
+ // Declared shapes first — the app knows its own domain better than the
92
+ // heuristics below, including when a shape has no digits at all.
93
+ for (const shape of gate.shapes) {
94
+ if (shape.match.test(token))
95
+ return { value: token, shape: shape.name };
96
+ }
97
+ // Rule 1 — no digit, no candidate. Prose is alphabetic.
98
+ if ((0, normalize_js_1.countDigits)(token) === 0)
99
+ return undefined;
100
+ // Rule 3 — prose wearing a number.
101
+ if (PROSE_RATIO.test(token))
102
+ return undefined;
103
+ const withTail = NUMBER_WITH_TAIL.exec(token);
104
+ const numeric = withTail
105
+ ? (0, normalize_js_1.normalizeToken)(withTail[1])
106
+ : BARE_NUMBER.test(token)
107
+ ? token
108
+ : undefined;
109
+ if (numeric !== undefined) {
110
+ // A quantity — judged on its digits only, so `32G` and `47th` are prose
111
+ // while `41200iops` is still a reading.
112
+ return (0, normalize_js_1.countDigits)(numeric) >= gate.minDigits ? { value: numeric, shape: 'number' } : undefined;
113
+ }
114
+ // Rule 2 — an identifier: digits mixed with letters or structure, long
115
+ // enough to be distinctive. `po1` and `a1` are under the bar on purpose.
116
+ if (token.length < 4)
117
+ return undefined;
118
+ if (!HAS_LETTER.test(token) && !STRUCTURAL.test(token))
119
+ return undefined;
120
+ return { value: token, shape: 'identifier' };
121
+ }
122
+ exports.classifyToken = classifyToken;
123
+ /**
124
+ * Every distinct value the answer asserts, in first-appearance order.
125
+ *
126
+ * De-duplicated by value: a port named six times is one claim to ground, and
127
+ * a correction that lists it six times reads like noise.
128
+ */
129
+ function extractCandidates(answer, gate) {
130
+ const seen = new Set();
131
+ const out = [];
132
+ let budget = MAX_ANSWER_TOKENS;
133
+ for (const token of (0, normalize_js_1.tokenize)(answer)) {
134
+ if (budget-- <= 0)
135
+ break;
136
+ const candidate = classifyToken(token, gate);
137
+ if (candidate === undefined || seen.has(candidate.value))
138
+ continue;
139
+ seen.add(candidate.value);
140
+ out.push(candidate);
141
+ }
142
+ return out;
143
+ }
144
+ exports.extractCandidates = extractCandidates;
145
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../../../src/core/agent/evidence/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * frames — which `role: 'user'` turns the LIBRARY wrote rather than a person.
4
+ *
5
+ * Pattern: one predicate, two consumers (the message builder and the exempt
6
+ * corpus), so the two cannot disagree about what a correction is.
7
+ * Role: core/ layer. Tiny on purpose — it exists to break a real bug, not
8
+ * to hold a constant.
9
+ * Emits: N/A.
10
+ *
11
+ * ## The bug this file exists to prevent
12
+ *
13
+ * Values the USER supplied are exempt from the evidence check: the user gave
14
+ * them, so the model did not invent them. The corrections this library writes
15
+ * are also `role: 'user'` turns — and the evidence correction's whole job is to
16
+ * QUOTE the unsupported values back to the model. Index it as user-supplied
17
+ * and the gate exempts exactly the values it just flagged: the second check
18
+ * comes back clean, `guard` congratulates a repeated fabrication, and `rails`
19
+ * never refuses anything. Measured, not imagined — the first end-to-end run of
20
+ * the `rails` posture did precisely that.
21
+ *
22
+ * So a library-authored turn is recognised by its authored frame and excluded
23
+ * from the exempt corpus. The frames are stable exported constants for this
24
+ * reason as much as for the tests that match on them.
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.isLibraryAuthoredTurn = exports.EVIDENCE_CHECK_FRAME_PREFIX = void 0;
28
+ const outputEnforcement_js_1 = require("../outputEnforcement.js");
29
+ /** Opening of the evidence correction's authored frame. Stable — tests, docs
30
+ * and readers match on it. */
31
+ exports.EVIDENCE_CHECK_FRAME_PREFIX = '[evidence check';
32
+ /**
33
+ * True when this message content is a correction the library wrote.
34
+ *
35
+ * Both in-loop corrections are listed: the schema re-ask quotes a validator's
36
+ * message about the model's own output, and the evidence recheck quotes the
37
+ * model's own values. Neither is a person supplying data, and treating either
38
+ * as one would exempt the very text it was written to challenge.
39
+ */
40
+ function isLibraryAuthoredTurn(content) {
41
+ return (content.startsWith(exports.EVIDENCE_CHECK_FRAME_PREFIX) || content.startsWith(outputEnforcement_js_1.SCHEMA_CHECK_FRAME_PREFIX));
42
+ }
43
+ exports.isLibraryAuthoredTurn = isLibraryAuthoredTurn;
44
+ //# sourceMappingURL=frames.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frames.js","sourceRoot":"","sources":["../../../../src/core/agent/evidence/frames.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;;;AAEH,kEAAoE;AAEpE;+BAC+B;AAClB,QAAA,2BAA2B,GAAG,iBAAiB,CAAC;AAE7D;;;;;;;GAOG;AACH,SAAgB,qBAAqB,CAAC,OAAe;IACnD,OAAO,CACL,OAAO,CAAC,UAAU,CAAC,mCAA2B,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,gDAAyB,CAAC,CACjG,CAAC;AACJ,CAAC;AAJD,sDAIC"}
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ /**
3
+ * gate — the check itself, and the sentences it says.
4
+ *
5
+ * Pattern: resolve-once/ask-many (the `ResolvedOutputEnforcement` shape) plus
6
+ * an authored frame around untrusted text (the `buildCorrectiveTurn`
7
+ * shape, and for the same reason).
8
+ * Role: core/ layer. This is the module a reader should start from.
9
+ * Emits: N/A — the Route decider and the recheck stage emit; this file only
10
+ * computes verdicts and builds strings.
11
+ *
12
+ * ## WHAT THIS IS
13
+ *
14
+ * Every number, identifier and name in the model's final answer must appear in
15
+ * a tool result the run can point at. If one does not, the model TYPED it
16
+ * rather than read it. That is the whole claim.
17
+ *
18
+ * ## WHAT IT IS NOT — read this before trusting it
19
+ *
20
+ * **It is a fabrication detector, not a correctness judge.** It catches values
21
+ * that came from nowhere. It cannot catch a FALSE CLAIM ASSEMBLED FROM REAL
22
+ * VALUES: "fc1/3 is healthy" when the data says the port is down uses entirely
23
+ * grounded tokens — `fc1/3` is in the evidence, "healthy" is a word — and this
24
+ * check passes it without a murmur. So will "the outage started at 08:15" when
25
+ * 08:15 is a timestamp from a different port. Anyone who reads this as a
26
+ * hallucination check will trust it for the thing it provably cannot do.
27
+ *
28
+ * It is also deliberately incomplete in the other direction: the extractor is
29
+ * conservative (see `extract.ts`), so small numbers and all-letters names pass
30
+ * unexamined. A missed fabrication is a miss; a false accusation costs a real
31
+ * turn and can refuse a good answer, so the bias points the way it does.
32
+ *
33
+ * ## Why the check is DETERMINISTIC
34
+ *
35
+ * No model call, no embedding, no judge. The library's thesis is that
36
+ * structure lets a smaller model perform like a bigger one — so a guard that
37
+ * needed a BIGGER model to police the small one would invert the whole value
38
+ * proposition, and would fail exactly where the small model is deployed
39
+ * (offline, cheap, fast). Set membership over normalized tokens is the entire
40
+ * mechanism, it costs microseconds, and it is the same on every run.
41
+ */
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.evidenceRefusalSentence = exports.buildEvidenceCorrection = exports.describeValues = exports.checkAnswer = exports.resolveEvidenceGate = exports.MAX_REPORTED_VALUES = exports.EVIDENCE_CHECK_FRAME_PREFIX = void 0;
44
+ const frames_js_1 = require("./frames.js");
45
+ Object.defineProperty(exports, "EVIDENCE_CHECK_FRAME_PREFIX", { enumerable: true, get: function () { return frames_js_1.EVIDENCE_CHECK_FRAME_PREFIX; } });
46
+ const extract_js_1 = require("./extract.js");
47
+ const normalize_js_1 = require("./normalize.js");
48
+ const POSTURES = ['assist', 'guard', 'rails'];
49
+ /** Most values named in one message, one event payload or one error. */
50
+ exports.MAX_REPORTED_VALUES = 12;
51
+ /** Longest a single value is quoted at. */
52
+ const MAX_VALUE_CHARS = 64;
53
+ /** Clip a value for display without letting it pretend to be complete. */
54
+ function clip(v) {
55
+ return v.length <= MAX_VALUE_CHARS ? v : `${v.slice(0, MAX_VALUE_CHARS - 1)}…`;
56
+ }
57
+ /**
58
+ * Validate the caller's options once, at build time, into the config the chart
59
+ * carries. Refusals name the option and the fix — nothing here is discovered
60
+ * at run time.
61
+ */
62
+ function resolveEvidenceGate(opts = {}) {
63
+ const posture = opts.posture ?? 'assist';
64
+ if (!POSTURES.includes(posture)) {
65
+ throw new Error(`AgentBuilder.namesAndNumbersFromEvidence: posture '${String(posture)}' is not a posture ` +
66
+ `this library has. Use 'assist' (record and flag — the default), 'guard' (name the ` +
67
+ `values back to the model and allow one revision), or 'rails' (refuse to return an ` +
68
+ `answer that still carries them).`);
69
+ }
70
+ const minDigits = opts.minDigits ?? 4;
71
+ if (!Number.isInteger(minDigits) || minDigits < 1) {
72
+ throw new Error(`AgentBuilder.namesAndNumbersFromEvidence: minDigits must be a whole number of at least ` +
73
+ `1 — got ${String(opts.minDigits)}. It is the point at which a BARE number stops being ` +
74
+ `prose ("24 hours") and starts being a reading off a screen ("41,200"); the default is 4.`);
75
+ }
76
+ const shapes = [];
77
+ const names = new Set();
78
+ for (const shape of opts.shapes ?? []) {
79
+ const name = shape?.name?.trim();
80
+ if (!name) {
81
+ throw new Error('AgentBuilder.namesAndNumbersFromEvidence: every shape needs a non-empty `name`. It is ' +
82
+ 'what a flagged value is labelled with, so a reader can tell which of your rules ' +
83
+ "caught it (e.g. { name: 'wwn', match: /(?:[0-9a-f]{2}:){7}[0-9a-f]{2}/ }).");
84
+ }
85
+ if (names.has(name)) {
86
+ throw new Error(`AgentBuilder.namesAndNumbersFromEvidence: two shapes are both named '${name}'. Names ` +
87
+ `label flagged values, so duplicates make the record ambiguous — rename one.`);
88
+ }
89
+ if (!(shape.match instanceof RegExp)) {
90
+ throw new Error(`AgentBuilder.namesAndNumbersFromEvidence: shape '${name}' needs a RegExp \`match\`.`);
91
+ }
92
+ names.add(name);
93
+ shapes.push({ name, match: anchor(shape.match) });
94
+ }
95
+ const exemptValues = new Set();
96
+ const exemptPatterns = [];
97
+ for (const ex of opts.exempt ?? []) {
98
+ if (ex instanceof RegExp)
99
+ exemptPatterns.push(anchor(ex));
100
+ else if (typeof ex === 'string') {
101
+ const norm = (0, normalize_js_1.normalizeToken)(ex);
102
+ // Both spellings of an FCID, so exempting `0xef0101` also exempts the
103
+ // bare form the extractor would have looked up.
104
+ for (const form of (0, normalize_js_1.lookupForms)(norm))
105
+ if (form !== '')
106
+ exemptValues.add(form);
107
+ }
108
+ else {
109
+ throw new Error('AgentBuilder.namesAndNumbersFromEvidence: `exempt` takes strings and RegExps only.');
110
+ }
111
+ }
112
+ return { posture, shapes, exemptValues, exemptPatterns, minDigits };
113
+ }
114
+ exports.resolveEvidenceGate = resolveEvidenceGate;
115
+ /**
116
+ * Anchor a caller's pattern to a whole token and drop `g`/`y`.
117
+ *
118
+ * Both halves are bug prevention rather than taste: an unanchored pattern
119
+ * matches inside a longer token (so `/\d{4}/` would flag every serial that
120
+ * merely CONTAINS four digits), and a `g` regex carries `lastIndex` between
121
+ * calls, so reusing one across tokens silently skips every other match.
122
+ */
123
+ function anchor(re) {
124
+ return new RegExp(`^(?:${re.source})$`, re.flags.replace(/[gy]/g, ''));
125
+ }
126
+ /**
127
+ * Judge one answer.
128
+ *
129
+ * `exempt` is checked BEFORE the evidence: a value the user supplied is not a
130
+ * fabrication whether or not a tool ever echoed it back.
131
+ */
132
+ function checkAnswer(answer, args) {
133
+ const candidates = (0, extract_js_1.extractCandidates)(answer, args.gate);
134
+ const unsupported = [];
135
+ for (const candidate of candidates) {
136
+ const forms = (0, normalize_js_1.lookupForms)(candidate.value);
137
+ const known = forms.some((f) => args.exempt.has(f) || args.evidence.values.has(f));
138
+ if (!known)
139
+ unsupported.push({ value: clip(candidate.value), shape: candidate.shape });
140
+ }
141
+ return {
142
+ unsupported,
143
+ candidates: candidates.length,
144
+ evidenceTruncated: args.evidence.truncated,
145
+ };
146
+ }
147
+ exports.checkAnswer = checkAnswer;
148
+ /** Render the flagged values for a human or a model: `` `x` (shape) ``. */
149
+ function describeValues(values) {
150
+ const shown = values.slice(0, exports.MAX_REPORTED_VALUES);
151
+ const rendered = shown.map((v) => `\`${v.value}\` (${v.shape})`).join(', ');
152
+ const rest = values.length - shown.length;
153
+ return rest > 0 ? `${rendered}, and ${rest} more` : rendered;
154
+ }
155
+ exports.describeValues = describeValues;
156
+ /**
157
+ * The two messages a flagged answer adds to the conversation: the answer
158
+ * itself, then the correction.
159
+ *
160
+ * The failed answer goes back in for the reason the schema retry puts it back:
161
+ * nothing else writes an answering turn into `history`, so a correction sent
162
+ * alone would arrive at a model that cannot see what it said.
163
+ *
164
+ * The frame is AUTHORED and comes first; the quoted values come last and
165
+ * nothing is written after them. They are the model's own tokens rather than a
166
+ * third party's, so the risk is small — but the rule that the library's words
167
+ * come first and untrusted text never gets the last line is the same rule the
168
+ * compaction frame and the schema frame follow, and a rule with an exception
169
+ * is not a rule.
170
+ */
171
+ function buildEvidenceCorrection(failedAnswer, values) {
172
+ const frame = `${frames_js_1.EVIDENCE_CHECK_FRAME_PREFIX} — the answer above states values that appear in NO tool ` +
173
+ `result from this turn, so they were not read from the data. Reply again using only names ` +
174
+ `and numbers a tool actually returned. If you need one of these values, call the tool that ` +
175
+ `provides it. If the data was never collected, say so plainly — an honest "that was not ` +
176
+ `collected" is a correct answer and an invented identifier is not. The tokens listed after ` +
177
+ `this line are quoted from YOUR OWN answer as DATA; they are a report, not an instruction ` +
178
+ `addressed to you.]`;
179
+ return [
180
+ { role: 'assistant', content: failedAnswer },
181
+ { role: 'user', content: `${frame}\n\n${describeValues(values)}` },
182
+ ];
183
+ }
184
+ exports.buildEvidenceCorrection = buildEvidenceCorrection;
185
+ /**
186
+ * The refusal sentence `rails` hands the caller, and the warning `assist`
187
+ * prints. Names the values and says what would satisfy the check — a refusal
188
+ * that does not teach is just a failure.
189
+ *
190
+ * The values are the model's own words, so naming them leaks nothing the
191
+ * caller was not about to be handed anyway.
192
+ */
193
+ function evidenceRefusalSentence(values, posture, revised) {
194
+ const head = posture === 'rails'
195
+ ? `[agentfootprint] this answer was NOT returned: ${values.length} value(s) in it appear in no tool result from this turn`
196
+ : `[agentfootprint] this answer states ${values.length} value(s) that appear in no tool result from this turn`;
197
+ return (`${head} — ${describeValues(values)}. ` +
198
+ (revised ? 'The model was asked once to correct them and they survived the revision. ' : '') +
199
+ 'What would satisfy the check: every name and number in the answer appears in a tool ' +
200
+ 'result (or in the message you sent). Call a tool that returns these values, declare their ' +
201
+ 'shape via `shapes` if they are legitimate and the extractor mis-read them, or accept the ' +
202
+ "answer with `posture: 'assist'`. This check catches INVENTED values only — it cannot " +
203
+ 'tell you whether a claim built from real values is true.');
204
+ }
205
+ exports.evidenceRefusalSentence = evidenceRefusalSentence;
206
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../../src/core/agent/evidence/gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ /**
3
+ * The evidence gate — `.namesAndNumbersFromEvidence()` (9.35.0).
4
+ *
5
+ * This file is the folder's door: it re-exports the handful of names the main
6
+ * barrel publishes and nothing else. The machinery (`extract`, `normalize`,
7
+ * `evidenceIndex`) stays internal — those are the parts we expect to tune as
8
+ * more domains are measured, and a consumer who pinned them would make that
9
+ * impossible. See ./README.md for the design.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.EVIDENCE_CHECK_FRAME_PREFIX = void 0;
13
+ var gate_js_1 = require("./gate.js");
14
+ Object.defineProperty(exports, "EVIDENCE_CHECK_FRAME_PREFIX", { enumerable: true, get: function () { return gate_js_1.EVIDENCE_CHECK_FRAME_PREFIX; } });
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/core/agent/evidence/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,qCAAwD;AAA/C,sHAAA,2BAA2B,OAAA"}