@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,35 @@
1
+ import { type Summary } from "../fleet/index.ts";
2
+ export interface DirectiveItem {
3
+ tenant: string;
4
+ summary: Summary;
5
+ /** The models the session's calls used, sorted. */
6
+ models: string[];
7
+ }
8
+ export interface Directive {
9
+ id: string;
10
+ applies_to: {
11
+ model: string;
12
+ } | {
13
+ tool: string;
14
+ };
15
+ pattern: string[];
16
+ signature: string;
17
+ sessions: number;
18
+ type_sessions: number;
19
+ affected_ppm: number;
20
+ failure_ppm: number;
21
+ tenants: number;
22
+ first_seen: string;
23
+ }
24
+ export interface DirectiveOptions {
25
+ k?: number;
26
+ minSessions?: number;
27
+ minAffectedPpm?: number;
28
+ }
29
+ /** spec/directives.md §1. */
30
+ export declare function directives(items: readonly DirectiveItem[], { k, minSessions, minAffectedPpm }?: DirectiveOptions): Directive[];
31
+ /** spec/directives.md §2: the directives for the types one fleet flies, with its own sessions. */
32
+ export declare function fleetDirectives(all: readonly Directive[], mine: readonly DirectiveItem[]): Array<Directive & {
33
+ your_sessions: string[];
34
+ your_type_sessions: number;
35
+ }>;
@@ -0,0 +1,80 @@
1
+ // Airworthiness directives (spec/directives.md): one failure pattern, on one model or tool, across
2
+ // fleets. Pure; mirrors sdks/python/src/zanii_blackbox/directives.py; pinned by
3
+ // spec/vectors/directives.json.
4
+ import { createHash } from "node:crypto";
5
+ import { signatureOf } from "../fleet/index.js";
6
+ const FAILED = new Set(["failure", "abandoned"]);
7
+ const typesOf = (item) => [
8
+ ...[...new Set(item.models)].sort().map((m) => `model:${m}`),
9
+ ...Object.keys(item.summary.tools)
10
+ .sort()
11
+ .map((t) => `tool:${t}`),
12
+ ];
13
+ /** spec/directives.md §1. */
14
+ export function directives(items, { k = 3, minSessions = 5, minAffectedPpm = 50_000 } = {}) {
15
+ const typeSessions = new Map();
16
+ const groups = new Map();
17
+ for (const item of items) {
18
+ const sig = signatureOf(item.summary);
19
+ for (const type of typesOf(item)) {
20
+ typeSessions.set(type, (typeSessions.get(type) ?? 0) + 1);
21
+ if (!sig)
22
+ continue;
23
+ const key = `${type}|${sig.pattern.join(",")}`;
24
+ const g = groups.get(key) ?? {
25
+ type,
26
+ pattern: sig.pattern,
27
+ signature: sig.signature,
28
+ tenants: new Set(),
29
+ sessions: 0,
30
+ failed: 0,
31
+ first: item.summary.started_at,
32
+ };
33
+ g.tenants.add(item.tenant);
34
+ g.sessions++;
35
+ if (FAILED.has(item.summary.outcome ?? ""))
36
+ g.failed++;
37
+ if (item.summary.started_at < g.first)
38
+ g.first = item.summary.started_at;
39
+ groups.set(key, g);
40
+ }
41
+ }
42
+ const out = [];
43
+ for (const [key, g] of groups) {
44
+ const total = typeSessions.get(g.type) ?? 0;
45
+ const affected = Math.floor((g.sessions * 1_000_000) / total);
46
+ if (g.tenants.size < k || g.sessions < minSessions || affected < minAffectedPpm)
47
+ continue;
48
+ const [kind, ...rest] = g.type.split(":");
49
+ const name = rest.join(":");
50
+ out.push({
51
+ id: `AD-${createHash("sha256").update(key).digest("hex").slice(0, 10)}`,
52
+ applies_to: kind === "model" ? { model: name } : { tool: name },
53
+ pattern: g.pattern,
54
+ signature: g.signature,
55
+ sessions: g.sessions,
56
+ type_sessions: total,
57
+ affected_ppm: affected,
58
+ failure_ppm: Math.floor((g.failed * 1_000_000) / g.sessions),
59
+ tenants: g.tenants.size,
60
+ first_seen: g.first,
61
+ });
62
+ }
63
+ return out.sort((a, b) => b.affected_ppm !== a.affected_ppm ? b.affected_ppm - a.affected_ppm : a.id < b.id ? -1 : 1);
64
+ }
65
+ /** spec/directives.md §2: the directives for the types one fleet flies, with its own sessions. */
66
+ export function fleetDirectives(all, mine) {
67
+ const out = [];
68
+ for (const d of all) {
69
+ const type = "model" in d.applies_to ? `model:${d.applies_to.model}` : `tool:${d.applies_to.tool}`;
70
+ const flying = mine.filter((i) => typesOf(i).includes(type));
71
+ if (flying.length === 0)
72
+ continue;
73
+ const hit = flying
74
+ .filter((i) => signatureOf(i.summary)?.signature === d.signature)
75
+ .map((i) => i.summary.session_id)
76
+ .sort();
77
+ out.push({ ...d, your_sessions: hit, your_type_sessions: flying.length });
78
+ }
79
+ return out;
80
+ }
@@ -0,0 +1,43 @@
1
+ export type Fault = {
2
+ kind: "provider_error";
3
+ at_call: number;
4
+ status: number;
5
+ } | {
6
+ kind: "latency";
7
+ at_call: number;
8
+ ms: number;
9
+ } | {
10
+ kind: "truncate";
11
+ at_call: number;
12
+ } | {
13
+ kind: "tool_error";
14
+ at_tool_call: number;
15
+ message?: string;
16
+ } | {
17
+ kind: "bad_data";
18
+ at_tool_call: number;
19
+ text: string;
20
+ };
21
+ /** spec/drills.md §1: the drill with defaults filled in, or an error message. */
22
+ export declare function checkDrill(value: unknown): {
23
+ faults: Fault[];
24
+ } | string;
25
+ /** spec/drills.md §3. */
26
+ export declare function drillReport(lines: readonly string[]): {
27
+ drill: false;
28
+ planned: never[];
29
+ injected: never[];
30
+ findings_after: never[];
31
+ recovered: boolean;
32
+ outcome: null;
33
+ } | {
34
+ drill: true;
35
+ planned: Fault[];
36
+ injected: {
37
+ seq: number;
38
+ kind: string;
39
+ }[];
40
+ findings_after: string[];
41
+ recovered: boolean;
42
+ outcome: string | null;
43
+ };
@@ -0,0 +1,101 @@
1
+ // Drills (spec/drills.md): scheduled failures, checked, and what the agent did about them. Pure;
2
+ // mirrors sdks/python/src/zanii_blackbox/drills.py; pinned by spec/vectors/drills.json.
3
+ const MODEL_KINDS = new Set(["provider_error", "latency", "truncate"]);
4
+ const TOOL_KINDS = new Set(["tool_error", "bad_data"]);
5
+ const int = (v, min, max) => Number.isSafeInteger(v) && v >= min && v <= max;
6
+ /** spec/drills.md §1: the drill with defaults filled in, or an error message. */
7
+ export function checkDrill(value) {
8
+ const faults = value?.faults;
9
+ if (typeof value !== "object" || value === null || Array.isArray(value) || !Array.isArray(faults))
10
+ return "drill must be {faults: [...]}";
11
+ if (faults.length === 0 || faults.length > 20)
12
+ return "drill.faults must hold 1-20 faults";
13
+ const out = [];
14
+ for (const [i, raw] of faults.entries()) {
15
+ const f = raw;
16
+ const at = `drill.faults[${i}]`;
17
+ if (typeof f !== "object" || f === null)
18
+ return `${at} must be an object`;
19
+ if (MODEL_KINDS.has(f.kind)) {
20
+ if (!int(f.at_call, 1, 1_000_000))
21
+ return `${at}.at_call must be an integer of at least 1`;
22
+ if (f.kind === "provider_error") {
23
+ const status = f.status ?? 500;
24
+ if (!(status === 429 || int(status, 500, 599)))
25
+ return `${at}.status must be 429 or 500-599`;
26
+ out.push({
27
+ kind: "provider_error",
28
+ at_call: f.at_call,
29
+ status: status,
30
+ });
31
+ }
32
+ else if (f.kind === "latency") {
33
+ if (!int(f.ms, 1, 60_000))
34
+ return `${at}.ms must be 1-60000`;
35
+ out.push({ kind: "latency", at_call: f.at_call, ms: f.ms });
36
+ }
37
+ else
38
+ out.push({ kind: "truncate", at_call: f.at_call });
39
+ }
40
+ else if (TOOL_KINDS.has(f.kind)) {
41
+ if (!int(f.at_tool_call, 1, 1_000_000))
42
+ return `${at}.at_tool_call must be an integer of at least 1`;
43
+ if (f.kind === "tool_error") {
44
+ if (f.message !== undefined && (typeof f.message !== "string" || f.message.length > 500))
45
+ return `${at}.message must be at most 500 characters`;
46
+ out.push({
47
+ kind: "tool_error",
48
+ at_tool_call: f.at_tool_call,
49
+ ...(f.message !== undefined ? { message: f.message } : {}),
50
+ });
51
+ }
52
+ else {
53
+ if (typeof f.text !== "string" || f.text.length > 2000)
54
+ return `${at}.text must be a string of at most 2000 characters`;
55
+ out.push({ kind: "bad_data", at_tool_call: f.at_tool_call, text: f.text });
56
+ }
57
+ }
58
+ else
59
+ return `${at}.kind must be provider_error, latency, truncate, tool_error or bad_data`;
60
+ }
61
+ return { faults: out };
62
+ }
63
+ /** spec/drills.md §3. */
64
+ export function drillReport(lines) {
65
+ const events = lines.map((l) => JSON.parse(l));
66
+ const drill = events[0]?.meta.drill?.faults;
67
+ if (events[0]?.kind !== "session.open" || !drill)
68
+ return {
69
+ drill: false,
70
+ planned: [],
71
+ injected: [],
72
+ findings_after: [],
73
+ recovered: false,
74
+ outcome: null,
75
+ };
76
+ const injected = events
77
+ .filter((e) => (e.kind === "llm.request" || e.kind === "tool.call") &&
78
+ typeof e.meta.injected === "object" &&
79
+ e.meta.injected !== null)
80
+ .map((e) => ({ seq: e.seq, kind: String(e.meta.injected.kind) }));
81
+ const first = injected[0]?.seq ?? Number.POSITIVE_INFINITY;
82
+ const lastInjected = Math.max(-1, ...events.filter((e) => e.meta.injected !== undefined).map((e) => e.seq));
83
+ const codes = [
84
+ ...new Set(events.filter((e) => e.kind === "finding" && e.seq > first).map((e) => String(e.meta.code))),
85
+ ].sort();
86
+ const recovered = events.some((e) => e.kind === "llm.response" &&
87
+ e.seq > lastInjected &&
88
+ e.meta.status === 200 &&
89
+ e.meta.injected === undefined &&
90
+ injected.length > 0);
91
+ const outcomes = events.filter((e) => e.kind === "outcome");
92
+ const outcome = outcomes.length ? String(outcomes.at(-1)?.meta.outcome) : null;
93
+ return {
94
+ drill: true,
95
+ planned: drill,
96
+ injected,
97
+ findings_after: codes,
98
+ recovered,
99
+ outcome,
100
+ };
101
+ }
@@ -0,0 +1,21 @@
1
+ import { type LoadedPrices } from "../cost/index.ts";
2
+ export interface DutyLimits {
3
+ max_minutes?: number;
4
+ max_llm_calls?: number;
5
+ max_context_pct?: number;
6
+ }
7
+ export interface DutyFinding {
8
+ code: "DUTY_LIMIT";
9
+ source: "duty";
10
+ severity: "warning";
11
+ ref: {
12
+ seq: number;
13
+ limit: "minutes" | "llm_calls" | "context_pct";
14
+ value: number;
15
+ max: number;
16
+ };
17
+ }
18
+ /** spec/duty.md §2: at most one DUTY_LIMIT per limit, where it was first crossed. */
19
+ export declare function dutyFindings(lines: readonly string[], prices?: LoadedPrices, defaults?: DutyLimits): DutyFinding[];
20
+ /** Checks a flight plan's `duty` (spec/duty.md §1): an error message, or undefined. */
21
+ export declare function checkDuty(duty: unknown): string | undefined;
@@ -0,0 +1,68 @@
1
+ // Duty-time limits (spec/duty.md): long sessions and full context windows, detected. Pure; mirrors
2
+ // sdks/python/src/zanii_blackbox/duty.py; pinned by spec/vectors/duty.json.
3
+ import { priceFor, promptTokens, sessionCost } from "../cost/index.js";
4
+ const KEYS = ["max_minutes", "max_llm_calls", "max_context_pct"];
5
+ /** spec/duty.md §1: the flight plan's limits win, limit by limit, over the server's defaults. */
6
+ function limitsOf(open, defaults) {
7
+ const plan = open?.meta.flight_plan?.duty;
8
+ const out = { max_minutes: 0, max_llm_calls: 0, max_context_pct: 0 };
9
+ for (const k of KEYS) {
10
+ const v = plan?.[k] ?? defaults[k];
11
+ if (Number.isSafeInteger(v) && v > 0)
12
+ out[k] = v;
13
+ }
14
+ return out;
15
+ }
16
+ /** spec/duty.md §2: at most one DUTY_LIMIT per limit, where it was first crossed. */
17
+ export function dutyFindings(lines, prices, defaults = {}) {
18
+ const events = lines.map((l) => JSON.parse(l));
19
+ const open = events[0]?.kind === "session.open" ? events[0] : undefined;
20
+ const limits = limitsOf(open, defaults);
21
+ const out = [];
22
+ const finding = (seq, limit, value, max) => out.push({
23
+ code: "DUTY_LIMIT",
24
+ source: "duty",
25
+ severity: "warning",
26
+ ref: { seq, limit, value, max },
27
+ });
28
+ if (limits.max_minutes && open) {
29
+ const start = Date.parse(open.ts);
30
+ const late = events.find((e) => Date.parse(e.ts) - start > limits.max_minutes * 60_000);
31
+ if (late)
32
+ finding(late.seq, "minutes", Math.floor((Date.parse(late.ts) - start) / 60_000), limits.max_minutes);
33
+ }
34
+ if (limits.max_llm_calls) {
35
+ const requests = events.filter((e) => e.kind === "llm.request");
36
+ const over = requests[limits.max_llm_calls];
37
+ if (over)
38
+ finding(over.seq, "llm_calls", limits.max_llm_calls + 1, limits.max_llm_calls);
39
+ }
40
+ if (limits.max_context_pct && prices) {
41
+ for (const c of sessionCost(lines, prices).calls) {
42
+ const window = c.model ? priceFor(c.model, prices.table)?.price.context_window : undefined;
43
+ if (!window)
44
+ continue;
45
+ const prompt = promptTokens(c.tokens);
46
+ const pct = Math.floor((prompt * 100) / window);
47
+ if (pct >= limits.max_context_pct) {
48
+ finding(c.seq, "context_pct", pct, limits.max_context_pct);
49
+ break;
50
+ }
51
+ }
52
+ }
53
+ return out;
54
+ }
55
+ /** Checks a flight plan's `duty` (spec/duty.md §1): an error message, or undefined. */
56
+ export function checkDuty(duty) {
57
+ if (typeof duty !== "object" || duty === null || Array.isArray(duty))
58
+ return "flight_plan.duty must be an object";
59
+ for (const [k, v] of Object.entries(duty)) {
60
+ if (!KEYS.includes(k))
61
+ return `flight_plan.duty has an unknown field: ${k}`;
62
+ if (!Number.isSafeInteger(v) ||
63
+ v < 1 ||
64
+ (k === "max_context_pct" && v > 100))
65
+ return `flight_plan.duty.${k} must be a positive integer${k === "max_context_pct" ? " of at most 100" : ""}`;
66
+ }
67
+ return undefined;
68
+ }
@@ -0,0 +1,141 @@
1
+ import { type LoadedPrices } from "../cost/index.ts";
2
+ import { type Bodies } from "../reconcile/record.ts";
3
+ export interface Summary {
4
+ session_id: string;
5
+ label: string | null;
6
+ tenant: string | null;
7
+ objective: string | null;
8
+ started_at: string;
9
+ last_event_at: string;
10
+ closed: boolean;
11
+ outcome: string | null;
12
+ events: number;
13
+ model_calls: number;
14
+ tool_calls: number;
15
+ tools: Record<string, number>;
16
+ spend_micro_usd: number;
17
+ findings: {
18
+ advisory: number;
19
+ caution: number;
20
+ warning: number;
21
+ };
22
+ finding_codes: Record<string, number>;
23
+ latest_findings: Array<{
24
+ seq: number;
25
+ code: string;
26
+ fault: string | null;
27
+ severity: string;
28
+ }>;
29
+ claims: {
30
+ satisfied: number;
31
+ failed: number;
32
+ unverified: number;
33
+ };
34
+ blocked_by: string | null;
35
+ near_misses: number;
36
+ }
37
+ export declare function sessionSummary(lines: readonly string[], bodies: Bodies, prices: LoadedPrices): Summary;
38
+ /** The incident report (spec/fleet.md §2) in Markdown, in English or Arabic. */
39
+ export declare function incidentReport(lines: readonly string[], bodies: Bodies, prices: LoadedPrices, lang: "en" | "ar"): string;
40
+ /** Per agent label (spec/fleet.md §2): outcomes, warnings, near misses, exceedances by code (FOQA),
41
+ * spend, cost per success. */
42
+ export declare function logbook(summaries: readonly Summary[]): {
43
+ labels: {
44
+ label: string;
45
+ sessions: number;
46
+ outcomes: {
47
+ success: number;
48
+ failure: number;
49
+ abandoned: number;
50
+ unlabelled: number;
51
+ };
52
+ findings_warning: number;
53
+ near_misses: number;
54
+ exceedances: {
55
+ [k: string]: number;
56
+ };
57
+ spend_micro_usd: number;
58
+ cost_per_success_micro_usd: number | null;
59
+ }[];
60
+ };
61
+ export interface Collision {
62
+ resource: string;
63
+ sessions: Array<{
64
+ session_id: string;
65
+ tool: string;
66
+ seq: number;
67
+ }>;
68
+ }
69
+ /** Changes to the same resource from two or more sessions (A7), by resource; each session's first change. */
70
+ export declare function collisions(records: ReadonlyArray<{
71
+ lines: readonly string[];
72
+ bodies: Bodies;
73
+ }>): Collision[];
74
+ export interface AirworthinessCriteria {
75
+ /** The newest closed sessions judged (default 20). */
76
+ window: number;
77
+ /** Fewer closed sessions than this: insufficient_data (default 5). */
78
+ min_sessions: number;
79
+ /** Successes over closed sessions, in parts per million (default 900,000 = 90%). */
80
+ min_success_ppm: number;
81
+ /** Warning findings allowed across the window (default 0). */
82
+ max_warnings: number;
83
+ }
84
+ export declare const DEFAULT_AIRWORTHINESS: AirworthinessCriteria;
85
+ /** Is this agent (label) fit to fly? (spec/fleet.md §5). `summaries` newest first. */
86
+ export declare function airworthiness(label: string, summaries: readonly Summary[], criteria?: AirworthinessCriteria): {
87
+ v: 1;
88
+ label: string;
89
+ criteria: AirworthinessCriteria;
90
+ sessions: string[];
91
+ successes: number;
92
+ success_ppm: number;
93
+ warnings: number;
94
+ near_misses: number;
95
+ verdict: string;
96
+ reasons: string[];
97
+ };
98
+ /** spec/signatures.md §1: the failure pattern and its id, or null. */
99
+ export declare function signatureOf(summary: Summary): {
100
+ signature: string;
101
+ pattern: string[];
102
+ } | null;
103
+ /** spec/signatures.md §2: groups seen at k or more distinct tenants; counts only. */
104
+ export declare function signatures(items: ReadonlyArray<{
105
+ tenant: string;
106
+ summary: Summary;
107
+ }>, k?: number): {
108
+ signature: string;
109
+ pattern: string[];
110
+ tenants: number;
111
+ sessions: number;
112
+ failures: number;
113
+ failure_ppm: number;
114
+ }[];
115
+ /** The did:key of an Ed25519 identity, from its 32-byte seed. */
116
+ export declare function didFromSeed(seed: Uint8Array): string;
117
+ /** Signs a certificate with an Ed25519 identity (the 32-byte seed): adds `signed: {by, sig}`. */
118
+ export declare function signCertificate<T extends Record<string, unknown>>(cert: T, did: string, seed: Uint8Array): T & {
119
+ signed: {
120
+ by: string;
121
+ sig: string;
122
+ };
123
+ };
124
+ /** Checks a signed certificate offline, from the signer's did:key alone. */
125
+ export declare function verifyCertificate(cert: Record<string, unknown>, expectedDid?: string): {
126
+ ok: boolean;
127
+ by: string | null;
128
+ reason?: string;
129
+ };
130
+ export interface FindingGroup {
131
+ code: string;
132
+ fault: string | null;
133
+ severity: string;
134
+ count: number;
135
+ first_seq: number;
136
+ last_seq: number;
137
+ first_at: string;
138
+ last_at: string;
139
+ }
140
+ /** The record's findings grouped by code: how many, and when first and last, in first-seen order. */
141
+ export declare function aggregateFindings(lines: readonly string[]): FindingGroup[];