agentfootprint 6.16.0 → 6.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/observability/audit.js +559 -0
- package/dist/adapters/observability/audit.js.map +1 -0
- package/dist/adapters/observability/otel.js +545 -56
- package/dist/adapters/observability/otel.js.map +1 -1
- package/dist/adapters/observability/xray.js +97 -13
- package/dist/adapters/observability/xray.js.map +1 -1
- package/dist/esm/adapters/observability/audit.js +554 -0
- package/dist/esm/adapters/observability/audit.js.map +1 -0
- package/dist/esm/adapters/observability/otel.js +545 -56
- package/dist/esm/adapters/observability/otel.js.map +1 -1
- package/dist/esm/adapters/observability/xray.js +97 -13
- package/dist/esm/adapters/observability/xray.js.map +1 -1
- package/dist/esm/lib/canonicalJson.js +125 -0
- package/dist/esm/lib/canonicalJson.js.map +1 -0
- package/dist/esm/observability-providers.js +5 -0
- package/dist/esm/observability-providers.js.map +1 -1
- package/dist/lib/canonicalJson.js +129 -0
- package/dist/lib/canonicalJson.js.map +1 -0
- package/dist/observability-providers.js +13 -1
- package/dist/observability-providers.js.map +1 -1
- package/dist/types/adapters/observability/audit.d.ts +255 -0
- package/dist/types/adapters/observability/audit.d.ts.map +1 -0
- package/dist/types/adapters/observability/otel.d.ts +143 -20
- package/dist/types/adapters/observability/otel.d.ts.map +1 -1
- package/dist/types/adapters/observability/xray.d.ts +7 -1
- package/dist/types/adapters/observability/xray.d.ts.map +1 -1
- package/dist/types/lib/canonicalJson.d.ts +57 -0
- package/dist/types/lib/canonicalJson.d.ts.map +1 -0
- package/dist/types/observability-providers.d.ts +3 -1
- package/dist/types/observability-providers.d.ts.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* auditExport — tamper-evident audit bundle (backlog #20, compliance
|
|
4
|
+
* wedge item 2; pairs with #19's `otelObservability` GenAI spans).
|
|
5
|
+
*
|
|
6
|
+
* Consumes the typed `agentfootprint.*` event stream and accumulates an
|
|
7
|
+
* append-only, HASH-CHAINED record log: every record carries the SHA-256
|
|
8
|
+
* of its own canonical serialization plus the hash of the previous
|
|
9
|
+
* record. Flipping a single byte anywhere in an exported bundle makes
|
|
10
|
+
* `verifyAuditBundle` name the exact record that broke — the
|
|
11
|
+
* record-keeping shape EU AI Act Art. 12 asks for (events the system
|
|
12
|
+
* logged, in order, demonstrably unmodified since capture).
|
|
13
|
+
*
|
|
14
|
+
* Pattern: Observability strategy (one purpose — chain accumulation)
|
|
15
|
+
* + pure offline verifier.
|
|
16
|
+
* Role: Outer ring (Hexagonal). Attach via
|
|
17
|
+
* `agent.enable.observability({ strategy: auditExport() })`.
|
|
18
|
+
* Emits: nothing — terminal sink.
|
|
19
|
+
*
|
|
20
|
+
* ## What lands in the chain
|
|
21
|
+
*
|
|
22
|
+
* One record per typed event, in dispatch order: decisions
|
|
23
|
+
* (`agent.route_decided`, `composition.route_decided` incl. decide()
|
|
24
|
+
* evidence), tool calls (`stream.tool_start/_end`), validation
|
|
25
|
+
* rejections (#9), permission verdicts and halts, credential lifecycle,
|
|
26
|
+
* costs, errors, skill/memory/context activity. Each new `meta.runId`
|
|
27
|
+
* is anchored by a GENESIS record (`audit.genesis`) carrying the runId,
|
|
28
|
+
* the agent identity, and library versions — runs chain back-to-back in
|
|
29
|
+
* one log, so silently DROPPING a whole run breaks the chain too.
|
|
30
|
+
*
|
|
31
|
+
* High-volume content deltas (`stream.token`, `stream.thinking_delta`)
|
|
32
|
+
* are excluded by default (`includeTokenEvents: true` to include).
|
|
33
|
+
*
|
|
34
|
+
* ## Record / bundle schema
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* AuditRecord = { seq, timestamp, eventType, payload, meta, prevHash, hash }
|
|
38
|
+
* hash = SHA-256 hex over canonicalJson(record minus `hash`)
|
|
39
|
+
* prevHash = previous record's `hash` (ZERO_HASH at chain start)
|
|
40
|
+
* AuditBundle = { header, records, finalHash }
|
|
41
|
+
* header = { format, hashAlgorithm, canonicalization, chainHead,
|
|
42
|
+
* firstSeq, recordCount, exportedAt, library }
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Canonicalization is `afp-cjson/1` (see `lib/canonicalJson.ts` — those
|
|
46
|
+
* rules ARE the contract; the header names them so independent
|
|
47
|
+
* verifiers can re-implement byte-exactly).
|
|
48
|
+
*
|
|
49
|
+
* ## Persistence + long runs
|
|
50
|
+
*
|
|
51
|
+
* Persistence is the CONSUMER's job — the bundle is plain JSON
|
|
52
|
+
* (`JSON.stringify(strategy.bundle())`, store anywhere). For long runs,
|
|
53
|
+
* `drain()` returns the records accumulated since the last drain while
|
|
54
|
+
* keeping the chain intact ACROSS drains: each segment's
|
|
55
|
+
* `header.chainHead` equals the previous segment's `finalHash`, so
|
|
56
|
+
* `verifyAuditBundle([seg1, seg2, ...])` re-verifies the concatenation
|
|
57
|
+
* end-to-end.
|
|
58
|
+
*
|
|
59
|
+
* ## PII discipline (mirrors #19's otelObservability)
|
|
60
|
+
*
|
|
61
|
+
* Payloads enter records through a bounding layer — by default
|
|
62
|
+
* (`payloadMode: 'bounded'`) record payloads NEVER carry raw runtime
|
|
63
|
+
* values that can echo PII:
|
|
64
|
+
*
|
|
65
|
+
* - tool args → `'[keys: …]'` (top-level key NAMES only)
|
|
66
|
+
* - tool results → `'[type: …]'` (typeof only)
|
|
67
|
+
* - userPrompt / LLM content / thinking blocks / history
|
|
68
|
+
* → `'[N chars]'` / `'[N messages]'` markers
|
|
69
|
+
* - content PREVIEWS (`contentSummary` on context/memory events,
|
|
70
|
+
* `rawContent`, `resultSummary`, `droppedSummaries`) → markers
|
|
71
|
+
* (for short content a preview IS the content; `contentHash`
|
|
72
|
+
* stays — it links identical content without echoing it)
|
|
73
|
+
* - error MESSAGE strings (`error`, `errorMessage`, `lastError`,
|
|
74
|
+
* `rawOutput`) → `'[N chars]'` (messages can echo values)
|
|
75
|
+
* - free-form Records (`questionPayload`, `resumeInput`, risk/eval
|
|
76
|
+
* `evidence`, memory `scoreEvidence`) → `'[keys: …]'`
|
|
77
|
+
*
|
|
78
|
+
* Everything else is embedded as the registry payload (sanitized:
|
|
79
|
+
* strings capped at 256 chars, lists at 32 items, cycles broken) —
|
|
80
|
+
* those payloads are bounded by construction: identifiers, counts,
|
|
81
|
+
* enums, decide() evidence (engine-bounded + redaction-aware),
|
|
82
|
+
* validation issues (paths/TYPES per #9), credential events (no
|
|
83
|
+
* secrets by contract).
|
|
84
|
+
*
|
|
85
|
+
* `payloadMode: 'verbatim'` embeds full payloads (still
|
|
86
|
+
* JSON-sanitized). For Art. 12 completeness on an access-controlled
|
|
87
|
+
* store that is often the point — but the bundle then carries prompts,
|
|
88
|
+
* tool args/results and model output. Treat it as PII-bearing, and
|
|
89
|
+
* remember the Agent sets NO footprintjs RedactionPolicy by default
|
|
90
|
+
* (policies you do set redact the emit channel UPSTREAM of this
|
|
91
|
+
* strategy, so redacted events arrive here already redacted).
|
|
92
|
+
*
|
|
93
|
+
* ## Tamper-EVIDENT, not tamper-PROOF (honest threat model)
|
|
94
|
+
*
|
|
95
|
+
* The chain proves INTERNAL consistency: any partial modification —
|
|
96
|
+
* edit, insert, delete, reorder, drop-a-run — is detected and named.
|
|
97
|
+
* It does NOT prove provenance: an adversary holding the only copy can
|
|
98
|
+
* recompute every hash from the mutation onward and present a
|
|
99
|
+
* self-consistent forgery. For non-repudiation, anchor `finalHash`
|
|
100
|
+
* externally as part of your retention process (write-once/WORM store,
|
|
101
|
+
* signed log, RFC 3161 timestamping, or simply a second party) — then
|
|
102
|
+
* a whole-suffix recomputation no longer matches the anchor.
|
|
103
|
+
*
|
|
104
|
+
* ## Runtime requirements
|
|
105
|
+
*
|
|
106
|
+
* Hashing uses `node:crypto` (`createHash('sha256')`) — zero new
|
|
107
|
+
* dependencies, imported lazily at first use (same gating as the
|
|
108
|
+
* optional vendor SDKs in this folder, so merely importing this module
|
|
109
|
+
* stays browser-safe). `auditExport` and `verifyAuditBundle` therefore
|
|
110
|
+
* run anywhere `node:crypto` exists: Node ≥ 20, Bun, Deno,
|
|
111
|
+
* edge runtimes with Node compat (e.g. Cloudflare `nodejs_compat`).
|
|
112
|
+
* In a browser there is no SYNC SHA-256 (WebCrypto is async-only), so
|
|
113
|
+
* both throw a descriptive error — verify server-side, or re-implement
|
|
114
|
+
* verification from the documented contract (it is pure: recompute
|
|
115
|
+
* SHA-256 over `afp-cjson/1` canonicalization and walk the chain).
|
|
116
|
+
*
|
|
117
|
+
* @example Capture → export → verify
|
|
118
|
+
* ```ts
|
|
119
|
+
* import { auditExport, verifyAuditBundle } from 'agentfootprint/observability-providers';
|
|
120
|
+
*
|
|
121
|
+
* const audit = auditExport({ agent: 'loan-officer' });
|
|
122
|
+
* const stop = agent.enable.observability({ strategy: audit });
|
|
123
|
+
* await agent.run({ message: 'assess application A-17' });
|
|
124
|
+
* stop();
|
|
125
|
+
*
|
|
126
|
+
* const bundle = audit.bundle(); // JSON-serializable
|
|
127
|
+
* await fs.writeFile('run.audit.json', JSON.stringify(bundle));
|
|
128
|
+
*
|
|
129
|
+
* const check = verifyAuditBundle(bundle); // offline — no agent needed
|
|
130
|
+
* // check.valid === true; tamper with one byte → { valid: false, brokenAt: <seq> }
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134
|
+
exports.verifyAuditBundle = exports.auditExport = exports.AUDIT_BUNDLE_FORMAT = exports.AUDIT_GENESIS_EVENT_TYPE = exports.AUDIT_ZERO_HASH = void 0;
|
|
135
|
+
const canonicalJson_js_1 = require("../../lib/canonicalJson.js");
|
|
136
|
+
const lazyRequire_js_1 = require("../../lib/lazyRequire.js");
|
|
137
|
+
// ─── Public types ─────────────────────────────────────────────────────
|
|
138
|
+
/** SHA-256 of "nothing" — the `prevHash` of the first record in a
|
|
139
|
+
* chain and the `chainHead` of a chain's first segment. */
|
|
140
|
+
exports.AUDIT_ZERO_HASH = '0'.repeat(64);
|
|
141
|
+
/** `eventType` of the per-run genesis record. Deliberately OUTSIDE the
|
|
142
|
+
* `agentfootprint.*` registry namespace — it is a chain-level record,
|
|
143
|
+
* not a dispatched event (#20 ships zero new typed events). */
|
|
144
|
+
exports.AUDIT_GENESIS_EVENT_TYPE = 'audit.genesis';
|
|
145
|
+
/** Format identifier carried on every bundle header. */
|
|
146
|
+
exports.AUDIT_BUNDLE_FORMAT = 'agentfootprint.audit/1';
|
|
147
|
+
let cryptoModule;
|
|
148
|
+
function resolveCrypto() {
|
|
149
|
+
if (cryptoModule)
|
|
150
|
+
return cryptoModule;
|
|
151
|
+
try {
|
|
152
|
+
cryptoModule = (0, lazyRequire_js_1.lazyRequire)('node:crypto');
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
throw new Error('auditExport/verifyAuditBundle require `node:crypto` for synchronous SHA-256 ' +
|
|
156
|
+
'(available in Node ≥ 20, Bun, Deno, and edge runtimes with Node compat). ' +
|
|
157
|
+
'Browsers expose only async WebCrypto — capture/verify audit bundles server-side.');
|
|
158
|
+
}
|
|
159
|
+
if (typeof cryptoModule.createHash !== 'function') {
|
|
160
|
+
throw new Error('auditExport: `node:crypto` resolved but `createHash` is missing.');
|
|
161
|
+
}
|
|
162
|
+
return cryptoModule;
|
|
163
|
+
}
|
|
164
|
+
function sha256Hex(text) {
|
|
165
|
+
return resolveCrypto().createHash('sha256').update(text, 'utf8').digest('hex');
|
|
166
|
+
}
|
|
167
|
+
/** Best-effort library version for header + genesis.
|
|
168
|
+
*
|
|
169
|
+
* Tries the package self-reference first (`exports["./package.json"]`
|
|
170
|
+
* — resolves from source and the CJS build), then relative paths to
|
|
171
|
+
* the root manifest (the ESM build sits under `dist/esm/`, whose
|
|
172
|
+
* type-stamp `package.json` has no `name`, which breaks Node's
|
|
173
|
+
* self-reference scope; relative specifiers resolve from
|
|
174
|
+
* `lib/lazyRequire` — `../../` from `src//dist/lib`, `../../../`
|
|
175
|
+
* from `dist/esm/lib`). The `name` guard rejects any manifest that
|
|
176
|
+
* isn't actually ours. 'unknown' where nothing resolves (bundlers). */
|
|
177
|
+
function libraryVersion() {
|
|
178
|
+
for (const specifier of [
|
|
179
|
+
'agentfootprint/package.json',
|
|
180
|
+
'../../package.json',
|
|
181
|
+
'../../../package.json',
|
|
182
|
+
]) {
|
|
183
|
+
try {
|
|
184
|
+
const pkg = (0, lazyRequire_js_1.lazyRequire)(specifier);
|
|
185
|
+
if (pkg.name === 'agentfootprint' && typeof pkg.version === 'string')
|
|
186
|
+
return pkg.version;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
/* try the next candidate */
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return 'unknown';
|
|
193
|
+
}
|
|
194
|
+
// ─── JSON sanitization (both modes) ──────────────────────────────────
|
|
195
|
+
/** Caps mirror otel.ts's bounding discipline (defense-in-depth — the
|
|
196
|
+
* registry payloads are mostly bounded upstream). */
|
|
197
|
+
const MAX_STRING_CHARS = 256;
|
|
198
|
+
const MAX_LIST_ITEMS = 32;
|
|
199
|
+
const MAX_DEPTH = 8;
|
|
200
|
+
/**
|
|
201
|
+
* Convert any value to a JSON-safe, size-bounded tree:
|
|
202
|
+
* strings capped, arrays capped with an overflow marker, cycles
|
|
203
|
+
* broken, depth capped, Date → ISO, bigint → string, non-finite →
|
|
204
|
+
* null, functions/symbols/undefined dropped (objects) or null
|
|
205
|
+
* (arrays). Output always canonicalizes without throwing.
|
|
206
|
+
*/
|
|
207
|
+
function sanitizeJson(value, depth = 0, seen = new Set()) {
|
|
208
|
+
if (value === null)
|
|
209
|
+
return null;
|
|
210
|
+
switch (typeof value) {
|
|
211
|
+
case 'string':
|
|
212
|
+
return value.length > MAX_STRING_CHARS ? `${value.slice(0, MAX_STRING_CHARS - 1)}…` : value;
|
|
213
|
+
case 'number':
|
|
214
|
+
return Number.isFinite(value) ? value : null;
|
|
215
|
+
case 'boolean':
|
|
216
|
+
return value;
|
|
217
|
+
case 'bigint':
|
|
218
|
+
return String(value);
|
|
219
|
+
case 'undefined':
|
|
220
|
+
case 'function':
|
|
221
|
+
case 'symbol':
|
|
222
|
+
return undefined;
|
|
223
|
+
default:
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
const obj = value;
|
|
227
|
+
if (obj instanceof Date)
|
|
228
|
+
return obj.toISOString();
|
|
229
|
+
if (depth >= MAX_DEPTH)
|
|
230
|
+
return '[truncated: depth]';
|
|
231
|
+
if (seen.has(obj))
|
|
232
|
+
return '[circular]';
|
|
233
|
+
seen.add(obj);
|
|
234
|
+
try {
|
|
235
|
+
if (Array.isArray(obj)) {
|
|
236
|
+
const capped = obj.slice(0, MAX_LIST_ITEMS).map((item) => {
|
|
237
|
+
const sanitized = sanitizeJson(item, depth + 1, seen);
|
|
238
|
+
return sanitized === undefined ? null : sanitized;
|
|
239
|
+
});
|
|
240
|
+
if (obj.length > MAX_LIST_ITEMS)
|
|
241
|
+
capped.push(`…+${obj.length - MAX_LIST_ITEMS} more`);
|
|
242
|
+
return capped;
|
|
243
|
+
}
|
|
244
|
+
const out = {};
|
|
245
|
+
for (const [key, entry] of Object.entries(obj)) {
|
|
246
|
+
const sanitized = sanitizeJson(entry, depth + 1, seen);
|
|
247
|
+
if (sanitized !== undefined)
|
|
248
|
+
out[key] = sanitized;
|
|
249
|
+
}
|
|
250
|
+
return out;
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
seen.delete(obj);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// ─── Bounding layer (payloadMode: 'bounded') ─────────────────────────
|
|
257
|
+
/** `'[23 chars]'` — length evidence without content. */
|
|
258
|
+
function charsMarker(value) {
|
|
259
|
+
return typeof value === 'string' ? `[${value.length} chars]` : `[type: ${typeofOf(value)}]`;
|
|
260
|
+
}
|
|
261
|
+
/** `'[keys: a, b, c]'` — shape evidence for free-form Records. */
|
|
262
|
+
function keysMarker(value) {
|
|
263
|
+
if (value === null || typeof value !== 'object')
|
|
264
|
+
return `[type: ${typeofOf(value)}]`;
|
|
265
|
+
const keys = Object.keys(value).slice(0, MAX_LIST_ITEMS);
|
|
266
|
+
return `[keys: ${keys.join(', ')}]`;
|
|
267
|
+
}
|
|
268
|
+
function typeofOf(value) {
|
|
269
|
+
return value === null ? 'null' : typeof value;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Content-bearing fields per event type → bounded replacement.
|
|
273
|
+
* Only fields that can echo raw runtime values appear here; everything
|
|
274
|
+
* else passes through `sanitizeJson` untouched (see module docs for
|
|
275
|
+
* the per-domain verbatim/summarized table).
|
|
276
|
+
*/
|
|
277
|
+
const BOUND_FIELDS = {
|
|
278
|
+
'agentfootprint.agent.turn_start': { userPrompt: charsMarker },
|
|
279
|
+
'agentfootprint.agent.turn_end': { finalContent: charsMarker },
|
|
280
|
+
'agentfootprint.agent.iteration_end': {
|
|
281
|
+
history: (v) => (Array.isArray(v) ? `[${v.length} messages]` : charsMarker(v)),
|
|
282
|
+
},
|
|
283
|
+
'agentfootprint.agent.output_schema_validation_failed': { rawOutput: charsMarker },
|
|
284
|
+
'agentfootprint.stream.llm_end': { content: charsMarker },
|
|
285
|
+
'agentfootprint.stream.token': { content: charsMarker },
|
|
286
|
+
'agentfootprint.stream.thinking_delta': { content: charsMarker },
|
|
287
|
+
'agentfootprint.stream.thinking_end': {
|
|
288
|
+
blocks: (v) => (Array.isArray(v) ? `[${v.length} blocks]` : charsMarker(v)),
|
|
289
|
+
},
|
|
290
|
+
'agentfootprint.stream.tool_start': { args: keysMarker },
|
|
291
|
+
'agentfootprint.stream.tool_end': { result: (v) => `[type: ${typeofOf(v)}]` },
|
|
292
|
+
// contentSummary is a raw-content PREVIEW (not a redacted summary) —
|
|
293
|
+
// for short content it IS the content, so it is bounded like content.
|
|
294
|
+
// `contentHash` stays verbatim: it links identical injections across
|
|
295
|
+
// records without echoing a byte of them.
|
|
296
|
+
'agentfootprint.context.injected': { rawContent: charsMarker, contentSummary: charsMarker },
|
|
297
|
+
'agentfootprint.context.slot_composed': {
|
|
298
|
+
droppedSummaries: (v) => (Array.isArray(v) ? `[${v.length} summaries]` : charsMarker(v)),
|
|
299
|
+
},
|
|
300
|
+
'agentfootprint.composition.merge_end': { resultSummary: charsMarker },
|
|
301
|
+
'agentfootprint.memory.attached': { contentSummary: charsMarker },
|
|
302
|
+
'agentfootprint.memory.written': { contentSummary: charsMarker },
|
|
303
|
+
'agentfootprint.pause.request': { questionPayload: keysMarker },
|
|
304
|
+
'agentfootprint.pause.resume': { resumeInput: keysMarker },
|
|
305
|
+
'agentfootprint.risk.flagged': { evidence: keysMarker },
|
|
306
|
+
'agentfootprint.eval.score': { evidence: keysMarker },
|
|
307
|
+
'agentfootprint.memory.strategy_applied': { scoreEvidence: keysMarker },
|
|
308
|
+
};
|
|
309
|
+
/** Error-MESSAGE field names — bounded in EVERY event (messages can
|
|
310
|
+
* echo runtime values; mirrors #19's "stage + scope only" rule). The
|
|
311
|
+
* classifier fields (`errorName`, `errorKind`) stay verbatim. */
|
|
312
|
+
const ERROR_MESSAGE_FIELDS = new Set(['error', 'errorMessage', 'lastError']);
|
|
313
|
+
function boundPayload(eventType, payload) {
|
|
314
|
+
if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
315
|
+
return sanitizeJson(payload);
|
|
316
|
+
}
|
|
317
|
+
const fieldRules = BOUND_FIELDS[eventType];
|
|
318
|
+
const out = {};
|
|
319
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
320
|
+
const rule = fieldRules?.[key];
|
|
321
|
+
if (rule !== undefined) {
|
|
322
|
+
out[key] = rule(value);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (ERROR_MESSAGE_FIELDS.has(key) && typeof value === 'string') {
|
|
326
|
+
out[key] = charsMarker(value);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const sanitized = sanitizeJson(value);
|
|
330
|
+
if (sanitized !== undefined)
|
|
331
|
+
out[key] = sanitized;
|
|
332
|
+
}
|
|
333
|
+
return out;
|
|
334
|
+
}
|
|
335
|
+
// ─── Strategy factory ────────────────────────────────────────────────
|
|
336
|
+
/** Excluded by default — high-volume content deltas. */
|
|
337
|
+
const TOKEN_EVENT_TYPES = new Set([
|
|
338
|
+
'agentfootprint.stream.token',
|
|
339
|
+
'agentfootprint.stream.thinking_delta',
|
|
340
|
+
]);
|
|
341
|
+
function auditExport(opts = {}) {
|
|
342
|
+
const payloadMode = opts.payloadMode ?? 'bounded';
|
|
343
|
+
const includeTokenEvents = opts.includeTokenEvents === true;
|
|
344
|
+
const version = libraryVersion();
|
|
345
|
+
// Chain state — survives drains; one chain per strategy instance.
|
|
346
|
+
let nextSeq = 0;
|
|
347
|
+
let lastHash = exports.AUDIT_ZERO_HASH;
|
|
348
|
+
let segmentHead = exports.AUDIT_ZERO_HASH;
|
|
349
|
+
let segmentFirstSeq = 0;
|
|
350
|
+
let retained = [];
|
|
351
|
+
const seenRunIds = new Set();
|
|
352
|
+
let stopped = false;
|
|
353
|
+
function append(eventType, timestamp, payload, meta) {
|
|
354
|
+
const preimage = { seq: nextSeq, timestamp, eventType, payload, meta, prevHash: lastHash };
|
|
355
|
+
const hash = sha256Hex((0, canonicalJson_js_1.canonicalJson)(preimage));
|
|
356
|
+
retained.push({ ...preimage, hash });
|
|
357
|
+
lastHash = hash;
|
|
358
|
+
nextSeq += 1;
|
|
359
|
+
}
|
|
360
|
+
function appendGenesis(runId, timestamp) {
|
|
361
|
+
append(exports.AUDIT_GENESIS_EVENT_TYPE, timestamp, {
|
|
362
|
+
runId,
|
|
363
|
+
...(opts.agent !== undefined && { agent: opts.agent }),
|
|
364
|
+
library: { name: 'agentfootprint', version },
|
|
365
|
+
...(opts.versions !== undefined && { versions: sanitizeJson(opts.versions) }),
|
|
366
|
+
payloadMode,
|
|
367
|
+
}, { runId });
|
|
368
|
+
}
|
|
369
|
+
function makeBundle() {
|
|
370
|
+
return {
|
|
371
|
+
header: {
|
|
372
|
+
format: exports.AUDIT_BUNDLE_FORMAT,
|
|
373
|
+
hashAlgorithm: 'sha-256',
|
|
374
|
+
canonicalization: canonicalJson_js_1.CANONICAL_JSON_VERSION,
|
|
375
|
+
chainHead: segmentHead,
|
|
376
|
+
firstSeq: segmentFirstSeq,
|
|
377
|
+
recordCount: retained.length,
|
|
378
|
+
exportedAt: Date.now(),
|
|
379
|
+
library: { name: 'agentfootprint', version },
|
|
380
|
+
},
|
|
381
|
+
records: [...retained],
|
|
382
|
+
finalHash: lastHash,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
name: 'audit',
|
|
387
|
+
capabilities: { events: true },
|
|
388
|
+
/** Attach-time check (the `validate()` contract) — fail fast where
|
|
389
|
+
* `node:crypto` is unavailable instead of on the first event. */
|
|
390
|
+
validate() {
|
|
391
|
+
resolveCrypto();
|
|
392
|
+
},
|
|
393
|
+
exportEvent(event) {
|
|
394
|
+
if (stopped)
|
|
395
|
+
return;
|
|
396
|
+
if (!includeTokenEvents && TOKEN_EVENT_TYPES.has(event.type))
|
|
397
|
+
return;
|
|
398
|
+
// Real dispatcher envelopes carry meta (bridge/eventMeta.ts).
|
|
399
|
+
// Hand-fed events without it are still recorded — an audit sink
|
|
400
|
+
// must never silently drop — under the 'unattributed' run anchor.
|
|
401
|
+
const meta = event.meta;
|
|
402
|
+
const runId = typeof meta?.runId === 'string' ? meta.runId : 'unattributed';
|
|
403
|
+
const timestamp = typeof meta?.wallClockMs === 'number' ? meta.wallClockMs : Date.now();
|
|
404
|
+
if (!seenRunIds.has(runId)) {
|
|
405
|
+
seenRunIds.add(runId);
|
|
406
|
+
appendGenesis(runId, timestamp);
|
|
407
|
+
}
|
|
408
|
+
// wallClockMs lives on `timestamp`; the rest of the meta rides
|
|
409
|
+
// along sanitized (runtimeStageId, paths, turn/iter indices, …).
|
|
410
|
+
const { wallClockMs: _lifted, ...metaRest } = meta ?? {};
|
|
411
|
+
void _lifted;
|
|
412
|
+
const recordMeta = {
|
|
413
|
+
...sanitizeJson(metaRest),
|
|
414
|
+
runId,
|
|
415
|
+
};
|
|
416
|
+
const payload = payloadMode === 'bounded'
|
|
417
|
+
? boundPayload(event.type, event.payload)
|
|
418
|
+
: sanitizeJson(event.payload);
|
|
419
|
+
append(event.type, timestamp, payload, recordMeta);
|
|
420
|
+
},
|
|
421
|
+
flush() {
|
|
422
|
+
// In-memory sink — nothing to flush. Export via bundle()/drain().
|
|
423
|
+
},
|
|
424
|
+
stop() {
|
|
425
|
+
// Stop OBSERVING; never destroy collected evidence — consumers
|
|
426
|
+
// drain/bundle after stop.
|
|
427
|
+
stopped = true;
|
|
428
|
+
},
|
|
429
|
+
bundle() {
|
|
430
|
+
return makeBundle();
|
|
431
|
+
},
|
|
432
|
+
drain() {
|
|
433
|
+
const segment = makeBundle();
|
|
434
|
+
segmentHead = lastHash;
|
|
435
|
+
segmentFirstSeq = nextSeq;
|
|
436
|
+
retained = [];
|
|
437
|
+
return segment;
|
|
438
|
+
},
|
|
439
|
+
recordCount() {
|
|
440
|
+
return retained.length;
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
exports.auditExport = auditExport;
|
|
445
|
+
// ─── Offline verification (pure — no agent, no strategy) ─────────────
|
|
446
|
+
/**
|
|
447
|
+
* Recompute the hash chain of a bundle (or of consecutive drained
|
|
448
|
+
* segments, in order) and report the exact record where integrity
|
|
449
|
+
* breaks. Pure function over JSON data — runs offline, long after the
|
|
450
|
+
* run, with no agent and no strategy instance.
|
|
451
|
+
*
|
|
452
|
+
* Checks, in order, per segment:
|
|
453
|
+
* 1. header format / algorithm / canonicalization are supported
|
|
454
|
+
* 2. `recordCount` matches `records.length`
|
|
455
|
+
* 3. segment continuity (`chainHead`/`firstSeq` extend the previous
|
|
456
|
+
* segment's `finalHash`/seq range)
|
|
457
|
+
* 4. per record: `seq` is contiguous, `prevHash` links the previous
|
|
458
|
+
* record, and SHA-256 over the canonical preimage (the record
|
|
459
|
+
* minus `hash` — so ADDED fields are caught too) matches `hash`
|
|
460
|
+
* 5. `finalHash` equals the last record's hash
|
|
461
|
+
*/
|
|
462
|
+
function verifyAuditBundle(input) {
|
|
463
|
+
const segments = Array.isArray(input)
|
|
464
|
+
? input
|
|
465
|
+
: [input];
|
|
466
|
+
let recordsChecked = 0;
|
|
467
|
+
let expectedChainHead;
|
|
468
|
+
let expectedFirstSeq;
|
|
469
|
+
const fail = (reason, brokenAt) => ({
|
|
470
|
+
valid: false,
|
|
471
|
+
recordsChecked,
|
|
472
|
+
...(brokenAt !== undefined && { brokenAt }),
|
|
473
|
+
reason,
|
|
474
|
+
});
|
|
475
|
+
if (segments.length === 0)
|
|
476
|
+
return fail('no bundles supplied');
|
|
477
|
+
for (let s = 0; s < segments.length; s++) {
|
|
478
|
+
const segment = segments[s];
|
|
479
|
+
const where = segments.length > 1 ? ` (segment ${s})` : '';
|
|
480
|
+
if (segment === undefined || typeof segment !== 'object') {
|
|
481
|
+
return fail(`bundle is not an object${where}`);
|
|
482
|
+
}
|
|
483
|
+
const { header, records, finalHash } = segment;
|
|
484
|
+
if (header === undefined || !Array.isArray(records) || typeof finalHash !== 'string') {
|
|
485
|
+
return fail(`bundle missing header/records/finalHash${where}`);
|
|
486
|
+
}
|
|
487
|
+
if (header.format !== exports.AUDIT_BUNDLE_FORMAT) {
|
|
488
|
+
return fail(`unsupported format '${String(header.format)}'${where}`);
|
|
489
|
+
}
|
|
490
|
+
if (header.hashAlgorithm !== 'sha-256') {
|
|
491
|
+
return fail(`unsupported hashAlgorithm '${String(header.hashAlgorithm)}'${where}`);
|
|
492
|
+
}
|
|
493
|
+
if (header.canonicalization !== canonicalJson_js_1.CANONICAL_JSON_VERSION) {
|
|
494
|
+
return fail(`unsupported canonicalization '${String(header.canonicalization)}'${where}`);
|
|
495
|
+
}
|
|
496
|
+
if (header.recordCount !== records.length) {
|
|
497
|
+
return fail(`recordCount ${String(header.recordCount)} does not match ${records.length} records${where}`);
|
|
498
|
+
}
|
|
499
|
+
if (expectedChainHead !== undefined && header.chainHead !== expectedChainHead) {
|
|
500
|
+
return fail(`segment discontinuity — chainHead does not match previous segment's finalHash${where}`, header.firstSeq);
|
|
501
|
+
}
|
|
502
|
+
if (expectedFirstSeq !== undefined && header.firstSeq !== expectedFirstSeq) {
|
|
503
|
+
return fail(`segment discontinuity — firstSeq ${String(header.firstSeq)}, expected ${expectedFirstSeq}${where}`, expectedFirstSeq);
|
|
504
|
+
}
|
|
505
|
+
// Head-truncation invariant (adversarial-review finding): a segment
|
|
506
|
+
// claiming to be the chain START (firstSeq 0) MUST anchor on the zero
|
|
507
|
+
// hash, and a segment anchored on the zero hash MUST claim seq 0 —
|
|
508
|
+
// both directions. Without this, an attacker could drop the first K
|
|
509
|
+
// records (genesis included — agent identity, versions, payload-mode
|
|
510
|
+
// declaration) and forge `chainHead` from the survivor's prevHash;
|
|
511
|
+
// `finalHash` is untouched by that attack, so external finalHash
|
|
512
|
+
// anchoring alone would NOT catch it. With the invariant, a head-
|
|
513
|
+
// truncated forgery must declare firstSeq > 0 — visibly NOT a chain
|
|
514
|
+
// start — and auditors reject leading segments missing seq 0.
|
|
515
|
+
if (header.firstSeq === 0 && header.chainHead !== exports.AUDIT_ZERO_HASH) {
|
|
516
|
+
return fail(`head-truncation guard — firstSeq 0 requires chainHead to be the zero hash${where}`, 0);
|
|
517
|
+
}
|
|
518
|
+
if (header.chainHead === exports.AUDIT_ZERO_HASH && header.firstSeq !== 0) {
|
|
519
|
+
return fail(`head-truncation guard — zero-hash chainHead requires firstSeq 0, got ${String(header.firstSeq)}${where}`, header.firstSeq);
|
|
520
|
+
}
|
|
521
|
+
let prevHash = header.chainHead;
|
|
522
|
+
for (let i = 0; i < records.length; i++) {
|
|
523
|
+
const record = records[i];
|
|
524
|
+
const expectedSeq = header.firstSeq + i;
|
|
525
|
+
if (record === null || typeof record !== 'object') {
|
|
526
|
+
return fail(`record at seq ${expectedSeq} is not an object`, expectedSeq);
|
|
527
|
+
}
|
|
528
|
+
if (record.seq !== expectedSeq) {
|
|
529
|
+
return fail(`sequence mismatch — record carries seq ${String(record.seq)}, expected ${expectedSeq}`, expectedSeq);
|
|
530
|
+
}
|
|
531
|
+
if (record.prevHash !== prevHash) {
|
|
532
|
+
return fail(`chain broken — prevHash does not match the preceding record's hash`, expectedSeq);
|
|
533
|
+
}
|
|
534
|
+
let recomputed;
|
|
535
|
+
try {
|
|
536
|
+
const { hash: _stored, ...preimage } = record;
|
|
537
|
+
void _stored;
|
|
538
|
+
recomputed = sha256Hex((0, canonicalJson_js_1.canonicalJson)(preimage));
|
|
539
|
+
}
|
|
540
|
+
catch (err) {
|
|
541
|
+
return fail(`record not canonicalizable (${err instanceof Error ? err.message : String(err)})`, expectedSeq);
|
|
542
|
+
}
|
|
543
|
+
if (recomputed !== record.hash) {
|
|
544
|
+
return fail(`hash mismatch — record content does not match its hash (tampered or corrupted)`, expectedSeq);
|
|
545
|
+
}
|
|
546
|
+
prevHash = record.hash;
|
|
547
|
+
recordsChecked += 1;
|
|
548
|
+
}
|
|
549
|
+
if (finalHash !== prevHash) {
|
|
550
|
+
const lastSeq = header.firstSeq + records.length - 1;
|
|
551
|
+
return fail(`finalHash does not match the last record's hash${where}`, records.length > 0 ? lastSeq : header.firstSeq);
|
|
552
|
+
}
|
|
553
|
+
expectedChainHead = finalHash;
|
|
554
|
+
expectedFirstSeq = header.firstSeq + records.length;
|
|
555
|
+
}
|
|
556
|
+
return { valid: true, recordsChecked };
|
|
557
|
+
}
|
|
558
|
+
exports.verifyAuditBundle = verifyAuditBundle;
|
|
559
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/adapters/observability/audit.ts"],"names":[],"mappings":"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"}
|