@zanii/blackbox 0.0.0-stage → 0.2.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 (135) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +109 -2
  3. package/dist/agents/index.d.ts +34 -0
  4. package/dist/agents/index.js +73 -0
  5. package/dist/analysis/detectors.d.ts +36 -0
  6. package/dist/analysis/detectors.js +339 -0
  7. package/dist/analysis/faults.d.ts +9 -0
  8. package/dist/analysis/faults.js +250 -0
  9. package/dist/analysis/index.d.ts +68 -0
  10. package/dist/analysis/index.js +388 -0
  11. package/dist/analysis/landing.d.ts +25 -0
  12. package/dist/analysis/landing.js +225 -0
  13. package/dist/analysis/memory.d.ts +13 -0
  14. package/dist/analysis/memory.js +33 -0
  15. package/dist/analysis/waste.d.ts +29 -0
  16. package/dist/analysis/waste.js +79 -0
  17. package/dist/approvals/index.d.ts +11 -0
  18. package/dist/approvals/index.js +27 -0
  19. package/dist/approvals/warnings.d.ts +2 -0
  20. package/dist/approvals/warnings.js +28 -0
  21. package/dist/attest/index.d.ts +17 -0
  22. package/dist/attest/index.js +106 -0
  23. package/dist/authority/index.d.ts +24 -0
  24. package/dist/authority/index.js +77 -0
  25. package/dist/billing/index.d.ts +99 -0
  26. package/dist/billing/index.js +174 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +1057 -0
  29. package/dist/client/index.d.ts +146 -0
  30. package/dist/client/index.js +210 -0
  31. package/dist/compliance/index.d.ts +41 -0
  32. package/dist/compliance/index.js +96 -0
  33. package/dist/cost/index.d.ts +133 -0
  34. package/dist/cost/index.js +293 -0
  35. package/dist/data/index.d.ts +191 -0
  36. package/dist/data/index.js +762 -0
  37. package/dist/directives/index.d.ts +35 -0
  38. package/dist/directives/index.js +80 -0
  39. package/dist/drills/index.d.ts +43 -0
  40. package/dist/drills/index.js +101 -0
  41. package/dist/duty/index.d.ts +21 -0
  42. package/dist/duty/index.js +68 -0
  43. package/dist/fleet/index.d.ts +141 -0
  44. package/dist/fleet/index.js +454 -0
  45. package/dist/hooks/ai-sdk.d.ts +42 -0
  46. package/dist/hooks/ai-sdk.js +62 -0
  47. package/dist/hooks/claude-agent-sdk.d.ts +14 -0
  48. package/dist/hooks/claude-agent-sdk.js +70 -0
  49. package/dist/hooks/index.d.ts +7 -0
  50. package/dist/hooks/index.js +10 -0
  51. package/dist/hooks/langchain-agent.d.ts +69 -0
  52. package/dist/hooks/langchain-agent.js +163 -0
  53. package/dist/hooks/langchain.d.ts +41 -0
  54. package/dist/hooks/langchain.js +216 -0
  55. package/dist/hooks/langgraph-checkpoint.d.ts +12 -0
  56. package/dist/hooks/langgraph-checkpoint.js +73 -0
  57. package/dist/hooks/memory.d.ts +17 -0
  58. package/dist/hooks/memory.js +64 -0
  59. package/dist/hooks/openai-agents.d.ts +6 -0
  60. package/dist/hooks/openai-agents.js +40 -0
  61. package/dist/hooks/protect.d.ts +7 -0
  62. package/dist/hooks/protect.js +39 -0
  63. package/dist/hooks/providers.d.ts +16 -0
  64. package/dist/hooks/providers.js +149 -0
  65. package/dist/hooks/shared.d.ts +11 -0
  66. package/dist/hooks/shared.js +39 -0
  67. package/dist/index.d.ts +46 -0
  68. package/dist/index.js +48 -0
  69. package/dist/investigate/index.d.ts +66 -0
  70. package/dist/investigate/index.js +119 -0
  71. package/dist/mcp-server/index.d.ts +85 -0
  72. package/dist/mcp-server/index.js +216 -0
  73. package/dist/mcp-wrap/index.d.ts +17 -0
  74. package/dist/mcp-wrap/index.js +170 -0
  75. package/dist/money/index.d.ts +114 -0
  76. package/dist/money/index.js +622 -0
  77. package/dist/occurrence/index.d.ts +108 -0
  78. package/dist/occurrence/index.js +168 -0
  79. package/dist/ocsf/index.d.ts +22 -0
  80. package/dist/ocsf/index.js +168 -0
  81. package/dist/otlp/index.d.ts +24 -0
  82. package/dist/otlp/index.js +143 -0
  83. package/dist/packs/index.d.ts +48 -0
  84. package/dist/packs/index.js +343 -0
  85. package/dist/policy/delta.d.ts +11 -0
  86. package/dist/policy/delta.js +39 -0
  87. package/dist/policy/drafts.d.ts +34 -0
  88. package/dist/policy/drafts.js +129 -0
  89. package/dist/policy/index.d.ts +47 -0
  90. package/dist/policy/index.js +154 -0
  91. package/dist/precog/index.d.ts +96 -0
  92. package/dist/precog/index.js +167 -0
  93. package/dist/precog/intervention.d.ts +22 -0
  94. package/dist/precog/intervention.js +44 -0
  95. package/dist/precog/normal.d.ts +31 -0
  96. package/dist/precog/normal.js +89 -0
  97. package/dist/preflight/index.d.ts +11 -0
  98. package/dist/preflight/index.js +19 -0
  99. package/dist/ratings/index.d.ts +21 -0
  100. package/dist/ratings/index.js +48 -0
  101. package/dist/reconcile/claude-code.d.ts +19 -0
  102. package/dist/reconcile/claude-code.js +220 -0
  103. package/dist/reconcile/codex.d.ts +5 -0
  104. package/dist/reconcile/codex.js +191 -0
  105. package/dist/reconcile/index.d.ts +19 -0
  106. package/dist/reconcile/index.js +50 -0
  107. package/dist/reconcile/record.d.ts +49 -0
  108. package/dist/reconcile/record.js +225 -0
  109. package/dist/reconcile/shared.d.ts +65 -0
  110. package/dist/reconcile/shared.js +113 -0
  111. package/dist/replay/index.d.ts +11 -0
  112. package/dist/replay/index.js +64 -0
  113. package/dist/replay/repair.d.ts +10 -0
  114. package/dist/replay/repair.js +62 -0
  115. package/dist/session/drain.d.ts +13 -0
  116. package/dist/session/drain.js +35 -0
  117. package/dist/session/index.d.ts +275 -0
  118. package/dist/session/index.js +681 -0
  119. package/dist/undo/index.d.ts +45 -0
  120. package/dist/undo/index.js +212 -0
  121. package/dist/verify/anchor.d.ts +54 -0
  122. package/dist/verify/anchor.js +77 -0
  123. package/dist/verify/chain.d.ts +27 -0
  124. package/dist/verify/chain.js +105 -0
  125. package/dist/verify/envelope.d.ts +28 -0
  126. package/dist/verify/envelope.js +55 -0
  127. package/dist/verify/index.d.ts +3 -0
  128. package/dist/verify/index.js +3 -0
  129. package/dist/version.d.ts +1 -0
  130. package/dist/version.js +2 -0
  131. package/dist/weather/index.d.ts +24 -0
  132. package/dist/weather/index.js +45 -0
  133. package/dist/workspace-receipt/index.d.ts +15 -0
  134. package/dist/workspace-receipt/index.js +121 -0
  135. package/package.json +56 -3
@@ -0,0 +1,293 @@
1
+ // The cost engine (spec/cost.md): integer micro-USD from a versioned price table, AED display.
2
+ // Mirrors sdks/python/src/zanii_blackbox/cost.py.
3
+ import { createHash } from "node:crypto";
4
+ const CLASSES = ["input", "output", "cache_read", "cache_write"];
5
+ const EXTRA = ["cache_write_1h", "input_audio", "output_audio"];
6
+ const OPTIONAL_PRICES = [
7
+ "context_window",
8
+ "cache_write_1h",
9
+ "input_audio",
10
+ "output_audio",
11
+ "web_search_per_1k",
12
+ ];
13
+ /** Parses and checks a price table; its sha256 is of the exact file bytes. */
14
+ export function loadPrices(bytes) {
15
+ const table = JSON.parse(new TextDecoder().decode(bytes));
16
+ if (table.currency !== "USD" ||
17
+ table.unit !== "micro_usd_per_mtok" ||
18
+ typeof table.models !== "object")
19
+ throw new Error("not a v1 price table (USD, micro_usd_per_mtok)");
20
+ const whole = (model, field, v) => {
21
+ if (!Number.isSafeInteger(v) || v < 0)
22
+ throw new Error(`price table: ${model}.${field} must be an integer ≥ 0`);
23
+ };
24
+ for (const [model, p] of Object.entries(table.models)) {
25
+ for (const c of CLASSES)
26
+ whole(model, c, p[c]);
27
+ for (const c of OPTIONAL_PRICES)
28
+ if (p[c] !== undefined)
29
+ whole(model, c, p[c]);
30
+ for (const [i, t] of (p.context_tiers ?? []).entries()) {
31
+ whole(model, `context_tiers[${i}].above`, t.above);
32
+ for (const c of CLASSES)
33
+ if (t[c] !== undefined)
34
+ whole(model, `context_tiers[${i}].${c}`, t[c]);
35
+ }
36
+ for (const [name, t] of Object.entries(p.service_tiers ?? {}))
37
+ for (const c of CLASSES)
38
+ if (t[c] !== undefined)
39
+ whole(model, `service_tiers.${name}.${c}`, t[c]);
40
+ if (p.status !== undefined && p.status !== "deprecated" && p.status !== "beta")
41
+ throw new Error(`price table: ${model}.status must be deprecated or beta`);
42
+ }
43
+ return { table, sha256: `sha256:${createHash("sha256").update(bytes).digest("hex")}` };
44
+ }
45
+ /** The exact model, else the longest table key the model starts with; null when there's none. */
46
+ export function priceFor(model, table,
47
+ /** The configured provider id: `<provider>/<model>` keys are tried first (spec/cost.md §1). */
48
+ provider) {
49
+ if (provider) {
50
+ const scoped = lookup(`${provider}/${model}`, table);
51
+ if (scoped)
52
+ return scoped;
53
+ }
54
+ const found = lookup(model, table);
55
+ if (found)
56
+ return found;
57
+ // spec/cost.md §1: a cloud's model id (Bedrock's `us.anthropic.claude-…`) as the vendor's own
58
+ const plain = model.replace(GEO, "").replace(VENDOR, "");
59
+ return plain !== model ? lookup(plain, table) : null;
60
+ }
61
+ const GEO = /^(?:us|eu|apac|au|ca|jp|global|us-gov)\./;
62
+ const VENDOR = /^(?:anthropic|amazon|meta|mistral|cohere|ai21|deepseek|openai|qwen|writer|google)\./;
63
+ function lookup(model, table) {
64
+ const exact = table.models[model];
65
+ if (exact)
66
+ return { key: model, price: exact };
67
+ let best = null;
68
+ for (const key of Object.keys(table.models))
69
+ if (model.startsWith(key) && (best === null || key.length > best.length))
70
+ best = key;
71
+ return best === null ? null : { key: best, price: table.models[best] };
72
+ }
73
+ /** Token classes from a recorded, flattened `meta.usage` (spec/cost.md §2). */
74
+ export function tokensOf(usage) {
75
+ const n = (k) => {
76
+ const v = usage[k];
77
+ return typeof v === "number" && Number.isSafeInteger(v) && v > 0 ? v : 0;
78
+ };
79
+ if ("promptTokenCount" in usage) {
80
+ // Gemini (spec/providers.md §5): the prompt count includes the cached part; thoughts are output.
81
+ const cached = n("cachedContentTokenCount");
82
+ const audioIn = n("promptTokensDetails_AUDIO");
83
+ const audioOut = n("candidatesTokensDetails_AUDIO");
84
+ return extras({
85
+ input: Math.max(0, n("promptTokenCount") - cached - audioIn) + n("toolUsePromptTokenCount"),
86
+ output: Math.max(0, n("candidatesTokenCount") - audioOut) + n("thoughtsTokenCount"),
87
+ cache_read: cached,
88
+ cache_write: 0,
89
+ }, { input_audio: audioIn, output_audio: audioOut });
90
+ }
91
+ if ("inputTokens" in usage)
92
+ // Bedrock Converse (spec/providers.md §5)
93
+ return {
94
+ input: n("inputTokens"),
95
+ output: n("outputTokens"),
96
+ cache_read: n("cacheReadInputTokens"),
97
+ cache_write: n("cacheWriteInputTokens"),
98
+ };
99
+ if ("prompt_tokens" in usage) {
100
+ // prompt_tokens includes both the cached part and the part written to the cache (B1)
101
+ const cached = n("prompt_tokens_details_cached_tokens");
102
+ const written = n("prompt_tokens_details_cache_write_tokens");
103
+ const audioIn = n("prompt_tokens_details_audio_tokens");
104
+ const audioOut = n("completion_tokens_details_audio_tokens");
105
+ return extras({
106
+ input: Math.max(0, n("prompt_tokens") - cached - written - audioIn),
107
+ output: Math.max(0, n("completion_tokens") - audioOut),
108
+ cache_read: cached,
109
+ cache_write: written,
110
+ }, { input_audio: audioIn, output_audio: audioOut });
111
+ }
112
+ if (Object.keys(usage).some((k) => k.startsWith("input_tokens_details_") || k.startsWith("output_tokens_details_"))) {
113
+ const cached = n("input_tokens_details_cached_tokens");
114
+ const written = n("input_tokens_details_cache_write_tokens");
115
+ const audioIn = n("input_tokens_details_audio_tokens");
116
+ const audioOut = n("output_tokens_details_audio_tokens");
117
+ return extras({
118
+ input: Math.max(0, n("input_tokens") - cached - written - audioIn),
119
+ output: Math.max(0, n("output_tokens") - audioOut),
120
+ cache_read: cached,
121
+ cache_write: written,
122
+ }, { input_audio: audioIn, output_audio: audioOut });
123
+ }
124
+ // Anthropic: cache_creation_input_tokens is both lifetimes; the 1-hour part is priced apart
125
+ const hour = n("cache_creation_ephemeral_1h_input_tokens");
126
+ return extras({
127
+ input: n("input_tokens"),
128
+ output: n("output_tokens"),
129
+ cache_read: n("cache_read_input_tokens"),
130
+ cache_write: Math.max(0, n("cache_creation_input_tokens") - hour),
131
+ }, { cache_write_1h: hour });
132
+ }
133
+ /** Adds the optional classes that are non-zero, so a record without them prices as before. */
134
+ function extras(base, more) {
135
+ const out = { ...base };
136
+ for (const c of EXTRA)
137
+ if ((more[c] ?? 0) > 0)
138
+ out[c] = more[c];
139
+ return out;
140
+ }
141
+ /** Requests a call made to a provider's server tools (Anthropic web search). */
142
+ export function requestsOf(usage) {
143
+ const v = usage.server_tool_use_web_search_requests;
144
+ return typeof v === "number" && Number.isSafeInteger(v) && v > 0 ? { web_search: v } : undefined;
145
+ }
146
+ /** A call's prompt, in tokens: what a context tier is decided on. */
147
+ export function promptTokens(t) {
148
+ return t.input + t.cache_read + t.cache_write + (t.cache_write_1h ?? 0) + (t.input_audio ?? 0);
149
+ }
150
+ /** The prices a call pays: a context tier when its prompt is over one, then its service tier. */
151
+ export function effectivePrice(price, tokens, serviceTier) {
152
+ let out = price;
153
+ const tier = {};
154
+ const size = promptTokens(tokens);
155
+ const context = [...(price.context_tiers ?? [])]
156
+ .sort((a, b) => b.above - a.above)
157
+ .find((t) => size > t.above);
158
+ if (context) {
159
+ out = { ...out, ...pick(context) };
160
+ tier.context_above = context.above;
161
+ }
162
+ const service = serviceTier ? price.service_tiers?.[serviceTier] : undefined;
163
+ if (service && serviceTier) {
164
+ out = { ...out, ...pick(service) };
165
+ tier.service = serviceTier;
166
+ }
167
+ return Object.keys(tier).length ? { price: out, tier } : { price: out };
168
+ }
169
+ function pick(t) {
170
+ const o = {};
171
+ for (const c of CLASSES)
172
+ if (t[c] !== undefined)
173
+ o[c] = t[c];
174
+ return o;
175
+ }
176
+ /** round_half_up((tokens × price + requests × price per 1k × 1000) / 1e6), integers only. */
177
+ export function callMicroUsd(tokens, price, requests) {
178
+ let sum = 0;
179
+ for (const c of CLASSES)
180
+ sum += tokens[c] * price[c];
181
+ sum += (tokens.cache_write_1h ?? 0) * (price.cache_write_1h ?? price.cache_write);
182
+ sum += (tokens.input_audio ?? 0) * (price.input_audio ?? price.input);
183
+ sum += (tokens.output_audio ?? 0) * (price.output_audio ?? price.output);
184
+ sum += (requests?.web_search ?? 0) * (price.web_search_per_1k ?? 0) * 1000;
185
+ if (!Number.isSafeInteger(sum))
186
+ throw new RangeError("cost overflow: tokens × price beyond 2^53");
187
+ return Math.floor((sum + 500_000) / 1_000_000);
188
+ }
189
+ /** One call's cost as the cost report prices it: the table (provider-scoped, tiered), else the
190
+ * provider's own reported cost, else null. The spend and duty checks use the same. */
191
+ export function priceCall(call, table) {
192
+ const tokens = tokensOf(call.usage);
193
+ const requests = requestsOf(call.usage);
194
+ const match = call.model === null ? null : priceFor(call.model, table, call.provider);
195
+ if (match) {
196
+ const { price, tier } = effectivePrice(match.price, tokens, call.serviceTier);
197
+ return {
198
+ key: match.key,
199
+ tokens,
200
+ micro_usd: callMicroUsd(tokens, price, requests),
201
+ ...(requests ? { requests } : {}),
202
+ ...(tier ? { tier } : {}),
203
+ };
204
+ }
205
+ if (typeof call.reportedMicroUsd === "number")
206
+ return {
207
+ key: null,
208
+ tokens,
209
+ micro_usd: call.reportedMicroUsd,
210
+ ...(requests ? { requests } : {}),
211
+ source: "reported",
212
+ };
213
+ return { key: null, tokens, micro_usd: null, ...(requests ? { requests } : {}) };
214
+ }
215
+ /** fils at the CBUAE peg (3.6725 AED / USD), rounded half up. */
216
+ export function toAedFils(microUsd) {
217
+ return Math.floor((microUsd * 36725 + 50_000_000) / 100_000_000);
218
+ }
219
+ export function formatAed(fils) {
220
+ const sign = fils < 0 ? "-" : "";
221
+ const abs = Math.abs(fils);
222
+ return `${sign}AED ${Math.floor(abs / 100)}.${String(abs % 100).padStart(2, "0")}`;
223
+ }
224
+ /** The session's cost report from its recorded lines. */
225
+ export function sessionCost(lines, prices) {
226
+ const calls = [];
227
+ const unknown = new Set();
228
+ const total = { input: 0, output: 0, cache_read: 0, cache_write: 0 };
229
+ let micro = 0;
230
+ const replayed = new Set(); // requests served from a cassette: no provider was paid (spec/cost.md)
231
+ const providers = new Map(); // request seq → the configured provider id
232
+ let webSearch = 0;
233
+ let pinnedSha; // session.open's meta.prices_sha256 (L3.1.5)
234
+ for (const line of lines) {
235
+ const e = JSON.parse(line);
236
+ if (e.kind === "session.open")
237
+ pinnedSha = e.meta.prices_sha256;
238
+ if (e.kind === "llm.request") {
239
+ const r = e.meta.replay;
240
+ if (Number.isSafeInteger(r?.take))
241
+ replayed.add(e.seq);
242
+ if (typeof e.meta.provider === "string")
243
+ providers.set(e.seq, e.meta.provider);
244
+ continue;
245
+ }
246
+ if (e.kind !== "llm.response" && e.kind !== "llm.incomplete")
247
+ continue;
248
+ if (replayed.has(e.meta.request_seq))
249
+ continue;
250
+ const usage = e.meta.usage;
251
+ if (typeof usage !== "object" || usage === null)
252
+ continue;
253
+ const model = typeof e.meta.model === "string" ? e.meta.model : null;
254
+ const priced = priceCall({
255
+ model,
256
+ usage: usage,
257
+ provider: providers.get(e.meta.request_seq) ?? null,
258
+ serviceTier: typeof e.meta.service_tier === "string" ? e.meta.service_tier : null,
259
+ reportedMicroUsd: Number.isSafeInteger(e.meta.reported_cost_micro_usd)
260
+ ? e.meta.reported_cost_micro_usd
261
+ : null,
262
+ }, prices.table);
263
+ const { key: _key, ...call } = priced;
264
+ for (const c of CLASSES)
265
+ total[c] += call.tokens[c];
266
+ for (const c of EXTRA)
267
+ if (call.tokens[c] !== undefined)
268
+ total[c] = (total[c] ?? 0) + call.tokens[c];
269
+ webSearch += call.requests?.web_search ?? 0;
270
+ if (call.micro_usd === null)
271
+ unknown.add(model ?? "(no model)");
272
+ else
273
+ micro += call.micro_usd;
274
+ calls.push({ seq: e.seq, model, ...call });
275
+ }
276
+ return {
277
+ v: 1,
278
+ price_table: {
279
+ version: prices.table.version,
280
+ sha256: prices.sha256,
281
+ ...(typeof pinnedSha === "string" ? { pinned: pinnedSha === prices.sha256 } : {}),
282
+ },
283
+ total_micro_usd: micro,
284
+ total_aed_fils: toAedFils(micro),
285
+ calls_priced: calls.filter((c) => c.micro_usd !== null).length,
286
+ calls_unpriced: calls.filter((c) => c.micro_usd === null).length,
287
+ unknown_models: [...unknown].sort(),
288
+ tokens: total,
289
+ calls,
290
+ ...(replayed.size ? { calls_replayed: replayed.size } : {}),
291
+ ...(webSearch ? { requests: { web_search: webSearch } } : {}),
292
+ };
293
+ }
@@ -0,0 +1,191 @@
1
+ export interface Identifier {
2
+ id: string;
3
+ title: string;
4
+ pattern: string;
5
+ normalize: "digits" | "lower" | "upper" | "exact";
6
+ keep_last?: number;
7
+ check?: "luhn";
8
+ personal: boolean;
9
+ }
10
+ /** spec/data/identifiers-v1.json: always on. */
11
+ export declare const CORE_IDENTIFIERS: readonly Identifier[];
12
+ /** spec/data/core-v1.json: evidence defaults for common writes (§5). */
13
+ export declare const CORE_EVIDENCE: ReadonlyArray<{
14
+ tool: string;
15
+ field: string;
16
+ }>;
17
+ /**
18
+ * §6: the canary values as one identifier, matched literally. Longer values first, so a value that
19
+ * contains another wins. ASCII punctuation is escaped the same way both languages read it.
20
+ */
21
+ export declare function canaryIdentifier(values: readonly string[]): Identifier;
22
+ export declare function normalizeValue(value: string, how: Identifier["normalize"]): string;
23
+ export declare function luhn(digits: string): boolean;
24
+ export interface Found {
25
+ id: string;
26
+ value: string;
27
+ }
28
+ /** §1: each distinct (id, normalized value) in text order, at most 200. Earlier identifiers win a span. */
29
+ export declare function dataScan(text: string, identifiers: readonly Identifier[]): Found[];
30
+ /** Every match counted (not only distinct ones), for `data.classes`. */
31
+ export declare function dataClasses(text: string, identifiers: readonly Identifier[]): Record<string, number>;
32
+ /** §2: the tenant's fingerprint of one identifier value. */
33
+ export declare function dataFingerprint(key: Uint8Array | string, tenant: string, id: string, value: string): string;
34
+ /** §2: the `data` meta of a body, or undefined when it holds no identifier. */
35
+ export declare function dataMeta(text: string, identifiers: readonly Identifier[], key: Uint8Array | string, tenant: string): {
36
+ classes: Record<string, number>;
37
+ fps: Array<{
38
+ id: string;
39
+ fp: string;
40
+ }>;
41
+ } | undefined;
42
+ export interface Lineage {
43
+ subjects: Array<{
44
+ fp: string;
45
+ id: string;
46
+ first: {
47
+ seq: number;
48
+ kind: string;
49
+ from: string;
50
+ };
51
+ sent: Array<{
52
+ seq: number;
53
+ to: string;
54
+ region?: string;
55
+ }>;
56
+ }>;
57
+ destinations: Array<{
58
+ to: string;
59
+ region?: string;
60
+ classes: Record<string, number>;
61
+ subjects: number;
62
+ }>;
63
+ }
64
+ /** §3: where each subject's data came from and went in one session. */
65
+ export declare function lineage(lines: readonly string[]): Lineage;
66
+ export interface Topology {
67
+ nodes: Array<{
68
+ id: string;
69
+ kind: string;
70
+ region?: string;
71
+ sessions: number;
72
+ }>;
73
+ edges: Array<{
74
+ from: string;
75
+ to: string;
76
+ region?: string;
77
+ classes: Record<string, number>;
78
+ subjects: number;
79
+ sessions: number;
80
+ flags: Array<"outside_region" | "region_unknown">;
81
+ }>;
82
+ }
83
+ /**
84
+ * §3a: many sessions' lineage as one map: where data is read from (nodes on the left) and sent to,
85
+ * with how many subjects and sessions each flow carried. With `dataRegion`, a flow to a model in
86
+ * another region is flagged `outside_region`, one to a model of no known region `region_unknown`.
87
+ */
88
+ export declare function dataTopology(sessions: ReadonlyArray<{
89
+ session_id: string;
90
+ lines: readonly string[];
91
+ }>, dataRegion?: string | null): Topology;
92
+ export interface DataRules {
93
+ identifiers: readonly Identifier[];
94
+ residency: boolean;
95
+ restricted: ReadonlyArray<{
96
+ identifiers: readonly string[];
97
+ allow: readonly string[];
98
+ }>;
99
+ evidence: ReadonlyArray<{
100
+ tool: string;
101
+ field: string;
102
+ }>;
103
+ }
104
+ export interface DataFinding {
105
+ code: "DATA_LEFT_REGION" | "DATA_REGION_UNKNOWN" | "DATA_TO_UNAPPROVED_DESTINATION" | "DATA_CANARY_TRIPPED" | "CONSENT_WITHDRAWN_USE" | "EGRESS_OPEN" | "TOOL_WITNESS_INVALID" | "UNEVIDENCED_SIDE_EFFECT";
106
+ severity: "advisory" | "caution" | "warning";
107
+ ref: Record<string, string | number>;
108
+ }
109
+ /** §4, §6, §7, §9: findings over a session's events. */
110
+ export declare function dataFindings(lines: readonly string[], rules: Pick<DataRules, "identifiers" | "residency" | "restricted">, dataRegion: string | null): DataFinding[];
111
+ export declare const hasEvidence: (value: unknown, field: string) => boolean;
112
+ /** §5: successful results of state-changing tools with no evidence of their own. */
113
+ export declare function unevidenced(lines: readonly string[], body: (hash: string) => Uint8Array | undefined, rules: DataRules["evidence"]): DataFinding[];
114
+ /** §9: a tool signs what it returns, so the result is the tool's word, not the agent's. */
115
+ export declare function toolWitness(keypair: {
116
+ did?: string;
117
+ privateKey: Uint8Array;
118
+ }): {
119
+ did: string;
120
+ sign(result: unknown): {
121
+ result: unknown;
122
+ witness: {
123
+ did: string;
124
+ sig: string;
125
+ };
126
+ };
127
+ };
128
+ /** §9: does `witness` sign `result`? */
129
+ export declare function verifyToolWitness(result: unknown, witness: {
130
+ did?: unknown;
131
+ sig?: unknown;
132
+ }): boolean;
133
+ /** §9: the witness an MCP result or an SDK tool.result body carries, checked; undefined if none. */
134
+ export declare function witnessOf(bodyJson: unknown): {
135
+ did: string;
136
+ ok: boolean;
137
+ } | undefined;
138
+ export interface Connector {
139
+ v: 1;
140
+ id: string;
141
+ format: "csv" | "jsonl";
142
+ fields: {
143
+ at: string;
144
+ action: string;
145
+ object?: string;
146
+ evidence?: string;
147
+ amount?: string;
148
+ actor?: string;
149
+ };
150
+ actions: Record<string, string>;
151
+ /** Which tool arguments hold the object and the amount the call acted on. */
152
+ arguments?: {
153
+ object?: string;
154
+ amount?: string;
155
+ };
156
+ window_seconds?: number;
157
+ }
158
+ export interface AuditRow {
159
+ row: number;
160
+ at: number;
161
+ action: string;
162
+ object?: string;
163
+ evidence?: string;
164
+ amount?: string;
165
+ actor?: string;
166
+ }
167
+ /** §10: why a connector is invalid, or undefined. */
168
+ export declare function checkAuditConnector(value: unknown): string | undefined;
169
+ /** RFC 4180: a header row, then rows; quoted fields may hold commas, quotes ("") and newlines. */
170
+ export declare function parseCsv(text: string): string[][];
171
+ /** §10: the rows of an audit-log export, in file order; rows without a time or action are skipped. */
172
+ export declare function parseAuditLog(text: string, connector: Connector): AuditRow[];
173
+ export interface SystemReport {
174
+ connector: string;
175
+ calls: number;
176
+ rows: number;
177
+ matched: Array<{
178
+ seq: number;
179
+ row: number;
180
+ by: "evidence" | "action";
181
+ }>;
182
+ findings: Array<{
183
+ code: "AGENT_ACTION_NOT_IN_SYSTEM" | "SYSTEM_CHANGE_NOT_IN_RECORD" | "SYSTEM_RECORD_MISMATCH";
184
+ ref: {
185
+ seq?: number;
186
+ row?: number;
187
+ };
188
+ }>;
189
+ }
190
+ /** §10: the session's successful mapped tool calls against the system's own log. */
191
+ export declare function reconcileSystem(lines: readonly string[], body: (hash: string) => Uint8Array | undefined, rows: readonly AuditRow[], connector: Connector, evidence: DataRules["evidence"]): SystemReport;