agentfootprint 9.87.1 → 9.89.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 (143) hide show
  1. package/CHANGELOG.md +946 -0
  2. package/dist/core/Agent.js +32 -0
  3. package/dist/core/Agent.js.map +1 -1
  4. package/dist/core/LLMCall.js +7 -10
  5. package/dist/core/LLMCall.js.map +1 -1
  6. package/dist/core/agent/buildAgentMessageApiChart.js +2 -4
  7. package/dist/core/agent/buildAgentMessageApiChart.js.map +1 -1
  8. package/dist/core/agent/buildDynamicAgentChart.js +10 -0
  9. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
  10. package/dist/core/agent/buildMessageApiChart.js +2 -4
  11. package/dist/core/agent/buildMessageApiChart.js.map +1 -1
  12. package/dist/core/agent/composeRequest.js +134 -0
  13. package/dist/core/agent/composeRequest.js.map +1 -0
  14. package/dist/core/agent/stages/callLLM.js +62 -25
  15. package/dist/core/agent/stages/callLLM.js.map +1 -1
  16. package/dist/core/agent/stages/seed.js +12 -0
  17. package/dist/core/agent/stages/seed.js.map +1 -1
  18. package/dist/core/agent/stages/toolCalls.js +2 -4
  19. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  20. package/dist/core/flowchartAsTool.js.map +1 -1
  21. package/dist/esm/core/Agent.d.ts +3 -0
  22. package/dist/esm/core/Agent.js +32 -0
  23. package/dist/esm/core/Agent.js.map +1 -1
  24. package/dist/esm/core/LLMCall.js +7 -10
  25. package/dist/esm/core/LLMCall.js.map +1 -1
  26. package/dist/esm/core/agent/buildAgentMessageApiChart.js +2 -4
  27. package/dist/esm/core/agent/buildAgentMessageApiChart.js.map +1 -1
  28. package/dist/esm/core/agent/buildDynamicAgentChart.js +10 -0
  29. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
  30. package/dist/esm/core/agent/buildMessageApiChart.js +2 -4
  31. package/dist/esm/core/agent/buildMessageApiChart.js.map +1 -1
  32. package/dist/esm/core/agent/composeRequest.d.ts +121 -0
  33. package/dist/esm/core/agent/composeRequest.js +127 -0
  34. package/dist/esm/core/agent/composeRequest.js.map +1 -0
  35. package/dist/esm/core/agent/stages/callLLM.d.ts +28 -0
  36. package/dist/esm/core/agent/stages/callLLM.js +61 -24
  37. package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
  38. package/dist/esm/core/agent/stages/seed.d.ts +26 -0
  39. package/dist/esm/core/agent/stages/seed.js +12 -0
  40. package/dist/esm/core/agent/stages/seed.js.map +1 -1
  41. package/dist/esm/core/agent/stages/toolCalls.js +2 -4
  42. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  43. package/dist/esm/core/agent/types.d.ts +64 -0
  44. package/dist/esm/core/flowchartAsTool.d.ts +15 -7
  45. package/dist/esm/core/flowchartAsTool.js.map +1 -1
  46. package/dist/esm/index.d.ts +1 -0
  47. package/dist/esm/index.js +9 -0
  48. package/dist/esm/index.js.map +1 -1
  49. package/dist/esm/lib/context-bisect/llmEdgeWeigher.d.ts +8 -0
  50. package/dist/esm/lib/context-bisect/llmEdgeWeigher.js +0 -0
  51. package/dist/esm/lib/context-bisect/llmEdgeWeigher.js.map +1 -1
  52. package/dist/esm/lib/context-bisect/trajectory.js +3 -7
  53. package/dist/esm/lib/context-bisect/trajectory.js.map +1 -1
  54. package/dist/esm/lib/context-ledger/contextLedger.js +5 -12
  55. package/dist/esm/lib/context-ledger/contextLedger.js.map +1 -1
  56. package/dist/esm/lib/time-travel/epochs.d.ts +210 -0
  57. package/dist/esm/lib/time-travel/epochs.js +274 -0
  58. package/dist/esm/lib/time-travel/epochs.js.map +1 -0
  59. package/dist/esm/lib/time-travel/index.d.ts +10 -4
  60. package/dist/esm/lib/time-travel/index.js +10 -4
  61. package/dist/esm/lib/time-travel/index.js.map +1 -1
  62. package/dist/esm/lib/time-travel/keyedFold.d.ts +112 -0
  63. package/dist/esm/lib/time-travel/keyedFold.js +286 -0
  64. package/dist/esm/lib/time-travel/keyedFold.js.map +1 -0
  65. package/dist/esm/lib/time-travel/milestoneStops.d.ts +57 -36
  66. package/dist/esm/lib/time-travel/milestoneStops.js +75 -82
  67. package/dist/esm/lib/time-travel/milestoneStops.js.map +1 -1
  68. package/dist/esm/lib/time-travel/receipt.d.ts +496 -0
  69. package/dist/esm/lib/time-travel/receipt.js +434 -0
  70. package/dist/esm/lib/time-travel/receipt.js.map +1 -0
  71. package/dist/esm/lib/time-travel/servedView.d.ts +525 -0
  72. package/dist/esm/lib/time-travel/servedView.js +820 -0
  73. package/dist/esm/lib/time-travel/servedView.js.map +1 -0
  74. package/dist/esm/lib/time-travel/sha256.d.ts +24 -0
  75. package/dist/esm/lib/time-travel/sha256.js +103 -0
  76. package/dist/esm/lib/time-travel/sha256.js.map +1 -0
  77. package/dist/esm/recorders/observability/BoundaryRecorder.d.ts +21 -0
  78. package/dist/esm/recorders/observability/BoundaryRecorder.js +6 -0
  79. package/dist/esm/recorders/observability/BoundaryRecorder.js.map +1 -1
  80. package/dist/index.js +124 -103
  81. package/dist/index.js.map +1 -1
  82. package/dist/lib/context-bisect/llmEdgeWeigher.js +0 -0
  83. package/dist/lib/context-bisect/llmEdgeWeigher.js.map +1 -1
  84. package/dist/lib/context-bisect/trajectory.js +3 -7
  85. package/dist/lib/context-bisect/trajectory.js.map +1 -1
  86. package/dist/lib/context-ledger/contextLedger.js +5 -12
  87. package/dist/lib/context-ledger/contextLedger.js.map +1 -1
  88. package/dist/lib/time-travel/epochs.js +284 -0
  89. package/dist/lib/time-travel/epochs.js.map +1 -0
  90. package/dist/lib/time-travel/index.js +35 -5
  91. package/dist/lib/time-travel/index.js.map +1 -1
  92. package/dist/lib/time-travel/keyedFold.js +290 -0
  93. package/dist/lib/time-travel/keyedFold.js.map +1 -0
  94. package/dist/lib/time-travel/milestoneStops.js +74 -81
  95. package/dist/lib/time-travel/milestoneStops.js.map +1 -1
  96. package/dist/lib/time-travel/receipt.js +442 -0
  97. package/dist/lib/time-travel/receipt.js.map +1 -0
  98. package/dist/lib/time-travel/servedView.js +826 -0
  99. package/dist/lib/time-travel/servedView.js.map +1 -0
  100. package/dist/lib/time-travel/sha256.js +107 -0
  101. package/dist/lib/time-travel/sha256.js.map +1 -0
  102. package/dist/recorders/observability/BoundaryRecorder.js +6 -0
  103. package/dist/recorders/observability/BoundaryRecorder.js.map +1 -1
  104. package/dist/types/core/Agent.d.ts +3 -0
  105. package/dist/types/core/Agent.d.ts.map +1 -1
  106. package/dist/types/core/LLMCall.d.ts.map +1 -1
  107. package/dist/types/core/agent/buildAgentMessageApiChart.d.ts.map +1 -1
  108. package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
  109. package/dist/types/core/agent/buildMessageApiChart.d.ts.map +1 -1
  110. package/dist/types/core/agent/composeRequest.d.ts +122 -0
  111. package/dist/types/core/agent/composeRequest.d.ts.map +1 -0
  112. package/dist/types/core/agent/stages/callLLM.d.ts +28 -0
  113. package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
  114. package/dist/types/core/agent/stages/seed.d.ts +26 -0
  115. package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
  116. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  117. package/dist/types/core/agent/types.d.ts +64 -0
  118. package/dist/types/core/agent/types.d.ts.map +1 -1
  119. package/dist/types/core/flowchartAsTool.d.ts +15 -7
  120. package/dist/types/core/flowchartAsTool.d.ts.map +1 -1
  121. package/dist/types/index.d.ts +1 -0
  122. package/dist/types/index.d.ts.map +1 -1
  123. package/dist/types/lib/context-bisect/llmEdgeWeigher.d.ts +8 -0
  124. package/dist/types/lib/context-bisect/llmEdgeWeigher.d.ts.map +1 -1
  125. package/dist/types/lib/context-bisect/trajectory.d.ts.map +1 -1
  126. package/dist/types/lib/context-ledger/contextLedger.d.ts.map +1 -1
  127. package/dist/types/lib/time-travel/epochs.d.ts +211 -0
  128. package/dist/types/lib/time-travel/epochs.d.ts.map +1 -0
  129. package/dist/types/lib/time-travel/index.d.ts +10 -4
  130. package/dist/types/lib/time-travel/index.d.ts.map +1 -1
  131. package/dist/types/lib/time-travel/keyedFold.d.ts +113 -0
  132. package/dist/types/lib/time-travel/keyedFold.d.ts.map +1 -0
  133. package/dist/types/lib/time-travel/milestoneStops.d.ts +57 -36
  134. package/dist/types/lib/time-travel/milestoneStops.d.ts.map +1 -1
  135. package/dist/types/lib/time-travel/receipt.d.ts +497 -0
  136. package/dist/types/lib/time-travel/receipt.d.ts.map +1 -0
  137. package/dist/types/lib/time-travel/servedView.d.ts +526 -0
  138. package/dist/types/lib/time-travel/servedView.d.ts.map +1 -0
  139. package/dist/types/lib/time-travel/sha256.d.ts +25 -0
  140. package/dist/types/lib/time-travel/sha256.d.ts.map +1 -0
  141. package/dist/types/recorders/observability/BoundaryRecorder.d.ts +21 -0
  142. package/dist/types/recorders/observability/BoundaryRecorder.d.ts.map +1 -1
  143. package/package.json +3 -3
@@ -0,0 +1,442 @@
1
+ "use strict";
2
+ /**
3
+ * receipt — what the model was actually handed, recorded at the stop where it
4
+ * was handed it.
5
+ *
6
+ * Role: Fold. This module owns the receipt's SHAPE and the one function that
7
+ * mints one (`buildReceipt`); the agent's request assembly calls it and
8
+ * commits the result under the scope key `receipt`, so the record lands
9
+ * in the call-llm bundle that already exists. Nothing here executes,
10
+ * reads scope, or emits.
11
+ * Reads: its arguments.
12
+ * Emits: N/A.
13
+ *
14
+ * ── THE FIRST LAW: NO AUTHORITY OMISSIONS ──────────────────────────────────
15
+ * A receipt never names — and never counts — what a caller's ROLE was not
16
+ * allowed to see. Committed state is readable by the trace toolpack's
17
+ * debugging tools, so a receipt carrying `hiddenSkillIds`, or even "3 skills
18
+ * withheld", would turn a permission decision into a leak path: the thing the
19
+ * check exists to hide, restated one layer down where nobody is checking. A
20
+ * reader that needs those ids reads them from the fold (`hiddenSkillIds` is
21
+ * committed state, and reading it is governed where it is read), not from
22
+ * here.
23
+ *
24
+ * ATTENTION omissions are a different fact and MAY ride the receipt:
25
+ * `omittedForAttention` says a slot's budget dropped content. Nobody was
26
+ * refused anything — the request simply did not fit — and a reader chasing "why
27
+ * did it not know that?" needs to see it. Even there the summaries are hashed,
28
+ * never quoted, for the same reason everything else on a receipt is.
29
+ *
30
+ * Measured on 9.88.0, NO chart shape supplies it: a slot writes its drops to
31
+ * `slotCompositions` inside its own subflow and no boundary bubbles that record
32
+ * out, so the agent's request assembly has nothing to pass. The field is on the
33
+ * shape because `buildReceipt` is a pure exported mint a caller can hand the
34
+ * fact to, and because the law it obeys is worth stating once rather than the
35
+ * day the record starts crossing. What is NOT done is go looking for it: a
36
+ * tracked read of an always-absent key is a read edge the trace then has to
37
+ * explain.
38
+ *
39
+ * ── THE SECOND LAW: HASHES AND REFERENCES, NEVER BYTES ─────────────────────
40
+ * A receipt is a fingerprint, not a copy. It records that a piece of a given
41
+ * shape and size was in a given position, and nothing about what it said. The
42
+ * bytes are already governed — `recordSystemPrompt` is opt-in for exactly this
43
+ * reason, redaction patterns scrub the committed mirror, and a window strategy
44
+ * decides what survives. A receipt that carried content would quietly reopen
45
+ * all three.
46
+ *
47
+ * ── THE THIRD LAW: HASHES ARE SALTED WITH THE RUN ID ───────────────────────
48
+ * `hash = sha256(runId + '\u001f' + content)`, first 16 hex characters. The
49
+ * salt is what stops a short piece from being fingerprinted ACROSS runs: an
50
+ * unsalted hash of a one-line system prompt, a tool schema, or a two-word user
51
+ * turn is a dictionary lookup away from being read back, and the receipt is
52
+ * committed state that travels in recordings. Salted per run, the same
53
+ * sentence in two runs has two hashes, so the fingerprint answers "is this the
54
+ * same as THAT piece of THIS run?" — which is the only question the
55
+ * conformance law asks — and answers nothing else.
56
+ *
57
+ * Hashes are NOT redacted, and the salt is what makes shipping THEM safe — not
58
+ * an assumption that something scrubbed them. It says nothing about the rest of
59
+ * the recording, and this law must not be read as if it did.
60
+ *
61
+ * WHAT A RECORDING ACTUALLY CONTAINS. An agent run is not redacted:
62
+ * `Agent.create(...)` has no redaction door, so a secret in a system prompt is
63
+ * in the commit log and in `servedAt(k).system.text`, verbatim. Redaction in
64
+ * this library is EXECUTOR-level — `flowchartAsTool({ redact })` /
65
+ * `runbookAsTool({ redact })` set a policy on an inner run, and footprintjs
66
+ * scrubs at COMMIT time, so a redacted key never enters that inner commit log
67
+ * (the same snapshot's live `sharedState` is a different view and is not
68
+ * scrubbed). A snapshot taken with `redact: true` also omits `initialState`,
69
+ * so folding it reports `basis: 'log-only'` and `servedView.ts` raises
70
+ * `no-fold-base` rather than rebuilding a short view in silence. Treat a
71
+ * recording as the plaintext it is; the salt protects the fingerprints, and
72
+ * only the fingerprints.
73
+ *
74
+ * ── THE FOURTH LAW: THE RECEIPT IS MINTED AT THE PROVIDER PORT ─────────────
75
+ * Everything on a receipt describes the request AS HANDED TO
76
+ * `LLMProvider.complete` — the port, not the wire. Anything downstream of that
77
+ * boundary is outside what this record can witness: a decorated provider, a
78
+ * vendor adapter's own serializer, a consumer's hand-written `complete`, or the
79
+ * vendor's server-side defaults. `RECEIPT_BOUNDARY` states it in one sentence
80
+ * for a renderer to print, and `servedView.ts` · `SERVED_GAPS` names the two
81
+ * places a reader is most likely to mistake it for wire-level proof.
82
+ */
83
+ Object.defineProperty(exports, "__esModule", { value: true });
84
+ exports.buildReceipt = exports.messageDigestInput = exports.toolDigestInput = exports.stableJson = exports.receiptHash = exports.RECEIPT_HASH_CHARS = exports.FORCED_OUTPUT_TOOL_KEY = exports.RECEIPT_KEY = exports.RECEIPT_BOUNDARY = exports.UNSERIALIZABLE = void 0;
85
+ const sha256_js_1 = require("./sha256.js");
86
+ /**
87
+ * The separator between two fields inside one digest input — ASCII UNIT
88
+ * SEPARATOR, which no prompt, tool name or JSON payload can contain (`JSON.
89
+ * stringify` escapes it). Written as an escape rather than a literal control
90
+ * character so a reader can see it and a formatter cannot eat it.
91
+ *
92
+ * Without it a digest is a bare concatenation, and a bare concatenation is
93
+ * ambiguous: `role + content` cannot tell `'user' + 'x'` from `'use' + 'rx'`.
94
+ */
95
+ const SEP = '\u001F';
96
+ /** The separator between two tool calls inside one message's digest. */
97
+ const CALL_SEP = '\u001C';
98
+ /** The separator between the fields of ONE tool call. */
99
+ const CALL_FIELD_SEP = '\u001D';
100
+ /** The separator between the top-level parts of a message's digest. */
101
+ const FIELD_SEP = '\u001E';
102
+ /**
103
+ * What a digest input records in place of a value JSON cannot express — a
104
+ * cycle, a `BigInt`, anything `stableJson` refuses. It is a MARK, not an empty
105
+ * string: two unserializable values are recorded as unserializable rather than
106
+ * as identical, which is what stops the cache comparison claiming "the strategy
107
+ * changed nothing" about two requests it could not read.
108
+ */
109
+ exports.UNSERIALIZABLE = '\u001F<unserializable>';
110
+ /**
111
+ * The boundary every receipt field is true at, in one sentence — exported so a
112
+ * renderer prints the library's own wording instead of inferring a stronger
113
+ * claim from a `null`.
114
+ *
115
+ * WHY IT IS A CONSTANT AND NOT A COMMENT. A reader who sees
116
+ * `cache.transform: 'unchanged'` will conclude "this request was not rewritten"
117
+ * unless something on the screen says otherwise, and the person who reads the
118
+ * screen is rarely the person who read the source. Print it beside the record.
119
+ *
120
+ * ── THE MECHANISM, WHICH THE PRINTED SENTENCE NO LONGER NAMES (9.88.0) ─────
121
+ * `buildReceipt` is called from `stages/callLLM.ts` with the request about to
122
+ * be passed to `LLMProvider.complete` — the PORT, this library's last sight of
123
+ * it. Three things sit downstream of that call and none of them is on the
124
+ * record: a provider decorated by the consumer (`complete()` wrapping
125
+ * `complete()`), a vendor adapter's own serializer, and the vendor's
126
+ * server-side defaults. `test/lib/time-travel/receipt-conformance.test.ts`
127
+ * reproduces the first of those — a decorator that appends a system suffix and
128
+ * a ghost tool after the mint, with `cache.transform` still reading
129
+ * `'unchanged'`, correctly.
130
+ *
131
+ * The sentence below used to say all of that, and it was PRINTED beside a
132
+ * trace by renderers that append it to a gap. A printed sentence names no
133
+ * module, function or call: it says which fields, what they mean here, and
134
+ * what to do — the rule in `test/helpers/gapProseClaims.ts`. So the mechanism
135
+ * lives in this comment, where a maintainer reads it and review catches its
136
+ * rot, and the constant says only the thing a reader must not get wrong.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * import { RECEIPT_BOUNDARY, receiptAt } from 'agentfootprint';
141
+ *
142
+ * const receipt = receiptAt(agent.getSnapshot()!, 1)!;
143
+ * if (receipt.cache.transform === 'unchanged') {
144
+ * console.log(`the cache strategy changed nothing. ${RECEIPT_BOUNDARY}`);
145
+ * }
146
+ * ```
147
+ */
148
+ exports.RECEIPT_BOUNDARY = 'A receipt describes the request as this library last saw it. Whatever ' +
149
+ 'handled it after that could have changed it, and nothing on the receipt ' +
150
+ 'would show that.';
151
+ /** The scope key the receipt is committed under. */
152
+ exports.RECEIPT_KEY = 'receipt';
153
+ /** The scope key holding the name of the tool a `'tool-forced'` output
154
+ * strategy put on the wire. `seed` commits it; `servedView.ts` · `servedAt`
155
+ * reads it — so a rebuild can NAME the forced tool without reading the
156
+ * receipt it is being checked against. */
157
+ exports.FORCED_OUTPUT_TOOL_KEY = 'forcedOutputToolName';
158
+ /** How many hex characters of the digest a receipt keeps. 16 — 64 bits, which
159
+ * is far past collision range for the few dozen pieces of one run, and short
160
+ * enough to read in a terminal. */
161
+ exports.RECEIPT_HASH_CHARS = 16;
162
+ /**
163
+ * The receipt's hash: run-salted SHA-256, first {@link RECEIPT_HASH_CHARS} hex
164
+ * characters. Exported because it is half of the conformance law — a reader
165
+ * that rebuilds a served view proves the rebuild by hashing it the same way.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * import { receiptAt, receiptHash, servedAt } from 'agentfootprint';
170
+ *
171
+ * const view = servedAt(agent.getSnapshot()!, 1)!;
172
+ * const receipt = receiptAt(agent.getSnapshot()!, 1)!;
173
+ * receiptHash(receipt.basis.runId, view.system.text) === receipt.system.hash; // true
174
+ * ```
175
+ */
176
+ function receiptHash(runId, content) {
177
+ return (0, sha256_js_1.sha256Hex)(`${runId}${SEP}${content}`).slice(0, exports.RECEIPT_HASH_CHARS);
178
+ }
179
+ exports.receiptHash = receiptHash;
180
+ /**
181
+ * JSON with object keys in sorted order, at every depth — so two structurally
182
+ * equal values hash the same however they were built. Property order survives
183
+ * `structuredClone`, but it does NOT survive every path a value takes through
184
+ * a delta-encoded commit log and back, and a fingerprint that depends on
185
+ * insertion order is a fingerprint that reports a change nobody made.
186
+ *
187
+ * Returns `undefined` — never `''` — for anything JSON cannot express (a
188
+ * cycle, a `BigInt`, a value whose `toJSON` throws). It does not throw,
189
+ * because a receipt is bookkeeping and must never be able to stop a run; and
190
+ * it does not collapse, because `''` made every unreadable value equal to
191
+ * every other one. Two requests that both failed to serialize compared EQUAL,
192
+ * and the receipt wrote `transformHash: null` — "the cache strategy changed
193
+ * nothing" — about a pair it had not read. Callers substitute
194
+ * {@link UNSERIALIZABLE} where a digest needs bytes, and branch where a
195
+ * comparison needs truth.
196
+ */
197
+ function stableJson(value) {
198
+ const canonical = (input) => {
199
+ if (Array.isArray(input))
200
+ return input.map(canonical);
201
+ if (input !== null && typeof input === 'object') {
202
+ const source = input;
203
+ const out = {};
204
+ for (const key of Object.keys(source).sort()) {
205
+ const next = canonical(source[key]);
206
+ if (next === undefined)
207
+ continue;
208
+ out[key] = next;
209
+ }
210
+ return out;
211
+ }
212
+ return input;
213
+ };
214
+ try {
215
+ return JSON.stringify(canonical(value)) ?? undefined;
216
+ }
217
+ catch {
218
+ return undefined;
219
+ }
220
+ }
221
+ exports.stableJson = stableJson;
222
+ /** `stableJson`, with the unreadable marked rather than dropped — the form a
223
+ * digest input takes. */
224
+ function digestJson(value) {
225
+ return stableJson(value) ?? exports.UNSERIALIZABLE;
226
+ }
227
+ /**
228
+ * The bytes a tool schema's hash covers: the whole `LLMToolSchema` — `name`,
229
+ * `description`, `inputSchema` — as sorted-key JSON, so two schemas that are
230
+ * structurally the same hash the same however they were built, and with a
231
+ * schema JSON cannot express (a cycle, a `BigInt`) marked {@link UNSERIALIZABLE}
232
+ * rather than dropped or thrown on.
233
+ *
234
+ * WHAT TO PASS (9.89.0). The receipt hashes each tool AS HANDED TO THE PORT:
235
+ * `buildReceipt` is given the request's tool list, and that list is the
236
+ * `dynamicToolSchemas` the run committed, plus a forced answer tool when an
237
+ * output strategy adds one. `servedAt(k).tools.schemas[i]` is that committed
238
+ * list read back — the same shape, the same bytes — so a consumer holding a
239
+ * served view already holds the object this function takes. It does not take
240
+ * the served view, a `Tool`, or a `defineTool` definition: those carry an
241
+ * `execute`, `wants` and other fields the model never saw.
242
+ *
243
+ * WHY IT IS EXPORTED. A reader that rebuilds a served view proves the rebuild
244
+ * by hashing it the way the receipt did. `receiptHash` and `messageDigestInput`
245
+ * were exported in 9.88.0 for the system text, the pieces and the messages,
246
+ * and a consumer could verify all of them — and NOT the tools, because the
247
+ * serializer behind `schemaHashes` was internal. Its only options were to copy
248
+ * `stableJson` (a second owner of the rule, which drifts the day the digest
249
+ * gains a field, as the message digest did in 9.88.0) or to leave the schema
250
+ * rows unverified. This is the third digest half of the law, beside its two
251
+ * siblings, and the ONLY spelling of the schema rule: `buildReceipt` calls it
252
+ * too. `stableJson` stays off the root barrel for the same reason — a consumer
253
+ * composing `hash(stableJson(tool))` would be writing the rule a second time.
254
+ *
255
+ * @example verify every schema row of a receipt from outside
256
+ * ```ts
257
+ * import { receiptAt, receiptHash, servedAt, toolDigestInput } from 'agentfootprint';
258
+ *
259
+ * const snapshot = agent.getSnapshot()!;
260
+ * const view = servedAt(snapshot, 1)!;
261
+ * const receipt = receiptAt(snapshot, 1)!;
262
+ * for (const tool of view.tools.schemas) {
263
+ * receiptHash(receipt.basis.runId, toolDigestInput(tool)) ===
264
+ * receipt.tools.schemaHashes[tool.name]; // true, for every tool of every epoch
265
+ * }
266
+ * // A forced answer tool is in `schemaHashes` and NOT in `schemas` — its body
267
+ * // is a declared gap (`forced-tool-schema`), so there is no row to check.
268
+ * ```
269
+ */
270
+ function toolDigestInput(tool) {
271
+ return digestJson(tool);
272
+ }
273
+ exports.toolDigestInput = toolDigestInput;
274
+ /**
275
+ * The bytes a message's hash covers, each field behind a separator: its role,
276
+ * its text, its `toolCallId`, its `toolName`, the calls it asked for (id, name,
277
+ * arguments and `providerMeta`) and its `thinkingBlocks`.
278
+ *
279
+ * WHY `toolCallId` (9.88.0). It is the JOIN KEY — `tool_use_id` on Anthropic's
280
+ * wire, `tool_call_id` on OpenAI's — that pairs a tool result to the call that
281
+ * asked for it. Two parallel calls whose results happen to have identical text
282
+ * hashed the SAME without it, so a mis-pairing (the answer to `c1` filed under
283
+ * `c2`) was invisible to the law: exactly the defect a receipt exists to catch.
284
+ *
285
+ * WHY `toolName` TOO (9.88.0). On two shipped providers the id is not the join
286
+ * key at all. `adapters/llm/GeminiProvider.ts` · `toGeminiContents` pairs a
287
+ * `functionResponse` to its call BY NAME and says so in its own comment; the
288
+ * id is optional there and dropped when it is not a real one.
289
+ * `adapters/llm/OllamaProvider.ts` · `toOllamaMessages` puts `tool_name` on
290
+ * the wire and only falls back to the id. So on those providers a receipt that
291
+ * covered `toolCallId` alone still could not see the very mis-pairing the
292
+ * field was added for: two `role:'tool'` messages with identical text and
293
+ * SWAPPED names fingerprinted identically. It rides as its own field behind
294
+ * the separator, beside the id, so a name moving between two results changes
295
+ * both hashes and neither can absorb the other's bytes.
296
+ *
297
+ * WHY `thinkingBlocks` and `providerMeta` (9.88.0). `adapters/types.ts` is
298
+ * explicit that a signed thinking block MUST be echoed byte-exact or the API
299
+ * rejects the turn, and `providerMeta` round-trips vendor state (Gemini's
300
+ * `thoughtSignature`, without which the turn after a tool call is refused).
301
+ * Both are things the model receives; a fingerprint that omitted them said two
302
+ * requests were the same when one of them would be rejected. They enter as a
303
+ * `stableJson` FINGERPRINT, so nothing quotable is added — a signature is an
304
+ * opaque token, not content.
305
+ *
306
+ * Still deliberately NOT covered: `injectedBy` (stripped before the request
307
+ * exists) and `ephemeral` (a persistence flag, invisible to the model). Two
308
+ * messages that hash the same are the same thing said to the model.
309
+ */
310
+ function messageDigestInput(message) {
311
+ const calls = (message.toolCalls ?? []).map((call) => [
312
+ call.id,
313
+ call.name,
314
+ digestJson(call.args),
315
+ call.providerMeta === undefined ? '' : digestJson(call.providerMeta),
316
+ ].join(CALL_FIELD_SEP));
317
+ return [
318
+ message.role,
319
+ message.content,
320
+ message.toolCallId ?? '',
321
+ message.toolName ?? '',
322
+ calls.join(CALL_SEP),
323
+ message.thinkingBlocks === undefined || message.thinkingBlocks.length === 0
324
+ ? ''
325
+ : digestJson(message.thinkingBlocks),
326
+ ].join(FIELD_SEP);
327
+ }
328
+ exports.messageDigestInput = messageDigestInput;
329
+ /**
330
+ * The sampling knobs off the request the port was handed.
331
+ *
332
+ * Duck-typed and value-conditional: an agent that set no dial records no key,
333
+ * so `params` is `{}` rather than a row of `undefined`s claiming defaults the
334
+ * library never sent.
335
+ */
336
+ function paramsOf(request) {
337
+ if (request === null || typeof request !== 'object')
338
+ return {};
339
+ const req = request;
340
+ const stop = Array.isArray(req.stop) ? req.stop.filter((s) => typeof s === 'string') : undefined;
341
+ return {
342
+ ...(typeof req.temperature === 'number' && { temperature: req.temperature }),
343
+ ...(typeof req.maxTokens === 'number' && { maxTokens: req.maxTokens }),
344
+ ...(typeof req.thinking?.budget === 'number' && { thinkingBudget: req.thinking.budget }),
345
+ ...(stop !== undefined && stop.length > 0 && { stop }),
346
+ ...(typeof req.toolChoice?.type === 'string' && {
347
+ toolChoice: {
348
+ type: req.toolChoice.type,
349
+ ...(typeof req.toolChoice.name === 'string' && { name: req.toolChoice.name }),
350
+ },
351
+ }),
352
+ };
353
+ }
354
+ /**
355
+ * Mint the receipt for one composed request.
356
+ *
357
+ * Pure and total: every branch here is arithmetic over what it was handed, so
358
+ * a receipt cannot fail a call. The one judgement it makes is the cache
359
+ * comparison, and it makes it in three values rather than two: `'unchanged'`
360
+ * exactly when the strategy handed back a request that serializes identically
361
+ * to the one it was given, `'rewritten'` when it did not, and `'unknown'` when
362
+ * either request could not be serialized at all — because "I could not read
363
+ * them" is not the same claim as "they were the same".
364
+ *
365
+ * Every value it produces is true AT THE PROVIDER PORT and nowhere past it —
366
+ * see {@link RECEIPT_BOUNDARY}.
367
+ */
368
+ function buildReceipt(input) {
369
+ const hash = (content) => receiptHash(input.runId, content);
370
+ const schemaHashes = {};
371
+ for (const tool of input.tools)
372
+ schemaHashes[tool.name] = hash(toolDigestInput(tool));
373
+ // The cache verdict, three-valued. A pair the receipt could not read is
374
+ // 'unknown' — never 'unchanged', which is what `''` used to make it.
375
+ const base = stableJson(input.baseRequest);
376
+ const prepared = stableJson(input.preparedRequest);
377
+ const comparable = base !== undefined && prepared !== undefined;
378
+ const transform = !comparable
379
+ ? 'unknown'
380
+ : base === prepared
381
+ ? 'unchanged'
382
+ : 'rewritten';
383
+ return {
384
+ system: {
385
+ hash: hash(input.systemText),
386
+ chars: input.systemText.length,
387
+ pieces: input.systemPieces.map((piece) => ({
388
+ hash: hash(piece.text),
389
+ slot: piece.slot,
390
+ source: piece.source,
391
+ })),
392
+ },
393
+ messages: {
394
+ count: input.messages.length + input.requestOnly.length,
395
+ entries: input.messages.map((message) => ({
396
+ role: message.role,
397
+ hash: hash(messageDigestInput(message)),
398
+ ...(message.toolCallId !== undefined && { key: message.toolCallId }),
399
+ })),
400
+ requestOnly: input.requestOnly.map((line) => ({
401
+ role: line.message.role,
402
+ hash: hash(messageDigestInput(line.message)),
403
+ reason: line.reason,
404
+ })),
405
+ },
406
+ tools: {
407
+ names: input.tools.map((tool) => tool.name),
408
+ schemaHashes,
409
+ forced: input.forced,
410
+ withheld: input.withheld,
411
+ },
412
+ cache: {
413
+ transform,
414
+ transformHash: transform === 'rewritten' ? hash(prepared) : null,
415
+ markersApplied: (input.markersApplied ?? []).map((marker) => ({
416
+ field: marker.field,
417
+ boundaryIndex: marker.boundaryIndex,
418
+ ttl: marker.ttl,
419
+ })),
420
+ },
421
+ // The PREPARED request, not the base one: the port is handed what the cache
422
+ // strategy returned, and a strategy that rewrote maxTokens must not leave
423
+ // five receipt fields describing a request nobody sent. See
424
+ // {@link RECEIPT_BOUNDARY} — this is the boundary those fields are true at.
425
+ params: paramsOf(input.preparedRequest),
426
+ ...(input.omittedForAttention !== undefined &&
427
+ input.omittedForAttention.count > 0 && {
428
+ omittedForAttention: {
429
+ count: input.omittedForAttention.count,
430
+ hashes: input.omittedForAttention.summaries.map((summary) => hash(summary)),
431
+ },
432
+ }),
433
+ basis: {
434
+ epoch: input.epoch,
435
+ runId: input.runId,
436
+ model: input.model,
437
+ provider: input.provider,
438
+ },
439
+ };
440
+ }
441
+ exports.buildReceipt = buildReceipt;
442
+ //# sourceMappingURL=receipt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipt.js","sourceRoot":"","sources":["../../../src/lib/time-travel/receipt.ts"],"names":[],"mappings":"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"}