@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.
- package/LICENSE +202 -0
- package/README.md +109 -2
- package/dist/agents/index.d.ts +34 -0
- package/dist/agents/index.js +73 -0
- package/dist/analysis/detectors.d.ts +36 -0
- package/dist/analysis/detectors.js +339 -0
- package/dist/analysis/faults.d.ts +9 -0
- package/dist/analysis/faults.js +250 -0
- package/dist/analysis/index.d.ts +68 -0
- package/dist/analysis/index.js +388 -0
- package/dist/analysis/landing.d.ts +25 -0
- package/dist/analysis/landing.js +225 -0
- package/dist/analysis/memory.d.ts +13 -0
- package/dist/analysis/memory.js +33 -0
- package/dist/analysis/waste.d.ts +29 -0
- package/dist/analysis/waste.js +79 -0
- package/dist/approvals/index.d.ts +11 -0
- package/dist/approvals/index.js +27 -0
- package/dist/approvals/warnings.d.ts +2 -0
- package/dist/approvals/warnings.js +28 -0
- package/dist/attest/index.d.ts +17 -0
- package/dist/attest/index.js +106 -0
- package/dist/authority/index.d.ts +24 -0
- package/dist/authority/index.js +77 -0
- package/dist/billing/index.d.ts +99 -0
- package/dist/billing/index.js +174 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1057 -0
- package/dist/client/index.d.ts +146 -0
- package/dist/client/index.js +210 -0
- package/dist/compliance/index.d.ts +41 -0
- package/dist/compliance/index.js +96 -0
- package/dist/cost/index.d.ts +133 -0
- package/dist/cost/index.js +293 -0
- package/dist/data/index.d.ts +191 -0
- package/dist/data/index.js +762 -0
- package/dist/directives/index.d.ts +35 -0
- package/dist/directives/index.js +80 -0
- package/dist/drills/index.d.ts +43 -0
- package/dist/drills/index.js +101 -0
- package/dist/duty/index.d.ts +21 -0
- package/dist/duty/index.js +68 -0
- package/dist/fleet/index.d.ts +141 -0
- package/dist/fleet/index.js +454 -0
- package/dist/hooks/ai-sdk.d.ts +42 -0
- package/dist/hooks/ai-sdk.js +62 -0
- package/dist/hooks/claude-agent-sdk.d.ts +14 -0
- package/dist/hooks/claude-agent-sdk.js +70 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.js +10 -0
- package/dist/hooks/langchain-agent.d.ts +69 -0
- package/dist/hooks/langchain-agent.js +163 -0
- package/dist/hooks/langchain.d.ts +41 -0
- package/dist/hooks/langchain.js +216 -0
- package/dist/hooks/langgraph-checkpoint.d.ts +12 -0
- package/dist/hooks/langgraph-checkpoint.js +73 -0
- package/dist/hooks/memory.d.ts +17 -0
- package/dist/hooks/memory.js +64 -0
- package/dist/hooks/openai-agents.d.ts +6 -0
- package/dist/hooks/openai-agents.js +40 -0
- package/dist/hooks/protect.d.ts +7 -0
- package/dist/hooks/protect.js +39 -0
- package/dist/hooks/providers.d.ts +16 -0
- package/dist/hooks/providers.js +149 -0
- package/dist/hooks/shared.d.ts +11 -0
- package/dist/hooks/shared.js +39 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +48 -0
- package/dist/investigate/index.d.ts +66 -0
- package/dist/investigate/index.js +119 -0
- package/dist/mcp-server/index.d.ts +85 -0
- package/dist/mcp-server/index.js +216 -0
- package/dist/mcp-wrap/index.d.ts +17 -0
- package/dist/mcp-wrap/index.js +170 -0
- package/dist/money/index.d.ts +114 -0
- package/dist/money/index.js +622 -0
- package/dist/occurrence/index.d.ts +108 -0
- package/dist/occurrence/index.js +168 -0
- package/dist/ocsf/index.d.ts +22 -0
- package/dist/ocsf/index.js +168 -0
- package/dist/otlp/index.d.ts +24 -0
- package/dist/otlp/index.js +143 -0
- package/dist/packs/index.d.ts +48 -0
- package/dist/packs/index.js +343 -0
- package/dist/policy/delta.d.ts +11 -0
- package/dist/policy/delta.js +39 -0
- package/dist/policy/drafts.d.ts +34 -0
- package/dist/policy/drafts.js +129 -0
- package/dist/policy/index.d.ts +47 -0
- package/dist/policy/index.js +154 -0
- package/dist/precog/index.d.ts +96 -0
- package/dist/precog/index.js +167 -0
- package/dist/precog/intervention.d.ts +22 -0
- package/dist/precog/intervention.js +44 -0
- package/dist/precog/normal.d.ts +31 -0
- package/dist/precog/normal.js +89 -0
- package/dist/preflight/index.d.ts +11 -0
- package/dist/preflight/index.js +19 -0
- package/dist/ratings/index.d.ts +21 -0
- package/dist/ratings/index.js +48 -0
- package/dist/reconcile/claude-code.d.ts +19 -0
- package/dist/reconcile/claude-code.js +220 -0
- package/dist/reconcile/codex.d.ts +5 -0
- package/dist/reconcile/codex.js +191 -0
- package/dist/reconcile/index.d.ts +19 -0
- package/dist/reconcile/index.js +50 -0
- package/dist/reconcile/record.d.ts +49 -0
- package/dist/reconcile/record.js +225 -0
- package/dist/reconcile/shared.d.ts +65 -0
- package/dist/reconcile/shared.js +113 -0
- package/dist/replay/index.d.ts +11 -0
- package/dist/replay/index.js +64 -0
- package/dist/replay/repair.d.ts +10 -0
- package/dist/replay/repair.js +62 -0
- package/dist/session/drain.d.ts +13 -0
- package/dist/session/drain.js +35 -0
- package/dist/session/index.d.ts +275 -0
- package/dist/session/index.js +681 -0
- package/dist/undo/index.d.ts +45 -0
- package/dist/undo/index.js +212 -0
- package/dist/verify/anchor.d.ts +54 -0
- package/dist/verify/anchor.js +77 -0
- package/dist/verify/chain.d.ts +27 -0
- package/dist/verify/chain.js +105 -0
- package/dist/verify/envelope.d.ts +28 -0
- package/dist/verify/envelope.js +55 -0
- package/dist/verify/index.d.ts +3 -0
- package/dist/verify/index.js +3 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/weather/index.d.ts +24 -0
- package/dist/weather/index.js +45 -0
- package/dist/workspace-receipt/index.d.ts +15 -0
- package/dist/workspace-receipt/index.js +121 -0
- package/package.json +56 -3
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Weather (spec/weather.md): how each provider and model is doing, from the gateway's own calls.
|
|
2
|
+
// Pure; mirrors sdks/python/src/zanii_blackbox/weather.py; pinned by spec/vectors/weather.json.
|
|
3
|
+
const MIN5 = 5 * 60_000;
|
|
4
|
+
const MIN15 = 15 * 60_000;
|
|
5
|
+
function windowOf(samples) {
|
|
6
|
+
const calls = samples.length;
|
|
7
|
+
const errors = samples.filter((s) => !s.ok).length;
|
|
8
|
+
const ms = samples.map((s) => s.ms).sort((a, b) => a - b);
|
|
9
|
+
return {
|
|
10
|
+
calls,
|
|
11
|
+
errors,
|
|
12
|
+
error_ppm: calls ? Math.floor((errors * 1_000_000) / calls) : 0,
|
|
13
|
+
// nearest rank, in integers: ceil(0.95 × calls) without floating-point drift
|
|
14
|
+
p95_ms: calls ? ms[Math.floor((95 * calls + 99) / 100) - 1] : 0,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** spec/weather.md §2. */
|
|
18
|
+
export function weather(samples, nowMs, { slowMs = 30_000 } = {}) {
|
|
19
|
+
const groups = new Map();
|
|
20
|
+
for (const s of samples) {
|
|
21
|
+
if (nowMs - s.at_ms > MIN15 || s.at_ms > nowMs)
|
|
22
|
+
continue;
|
|
23
|
+
const key = JSON.stringify([s.provider, s.model]);
|
|
24
|
+
const list = groups.get(key);
|
|
25
|
+
if (list)
|
|
26
|
+
list.push(s);
|
|
27
|
+
else
|
|
28
|
+
groups.set(key, [s]);
|
|
29
|
+
}
|
|
30
|
+
const out = [];
|
|
31
|
+
for (const [key, list] of groups) {
|
|
32
|
+
const [provider, model] = JSON.parse(key);
|
|
33
|
+
const last5 = windowOf(list.filter((s) => nowMs - s.at_ms <= MIN5));
|
|
34
|
+
const status = last5.calls < 3
|
|
35
|
+
? "clear"
|
|
36
|
+
: last5.error_ppm >= 500_000
|
|
37
|
+
? "down"
|
|
38
|
+
: last5.error_ppm >= 100_000 || last5.p95_ms >= slowMs
|
|
39
|
+
? "degraded"
|
|
40
|
+
: "clear";
|
|
41
|
+
out.push({ provider, model, last_5m: last5, last_15m: windowOf(list), status });
|
|
42
|
+
}
|
|
43
|
+
const cmp = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
44
|
+
return out.sort((a, b) => cmp(a.provider, b.provider) || cmp(a.model ?? "", b.model ?? ""));
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type Tree = {
|
|
2
|
+
tree: string;
|
|
3
|
+
} | {
|
|
4
|
+
reason: "not_a_git_repo" | "git_unavailable" | "git_failed";
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* The workspace's content as one git tree hash: tracked and untracked files (minus .gitignore'd
|
|
8
|
+
* ones), built in a temporary index so the user's staging area is never touched.
|
|
9
|
+
* ponytail: `git add -A` hashes every changed file, so a huge dirty tree costs seconds per tool call.
|
|
10
|
+
*/
|
|
11
|
+
export declare function workspaceTree(cwd: string): Tree;
|
|
12
|
+
/** The hook's JSON input (from Claude Code on stdin) → the receipt posted to the gateway. */
|
|
13
|
+
export declare function receiptFor(hook: Record<string, unknown>, tree: Tree): Record<string, unknown> | null;
|
|
14
|
+
/** Posts one receipt. Resolves with null on 204, else the reason; never rejects. */
|
|
15
|
+
export declare function postReceipt(gatewayUrl: string, token: string, receipt: Record<string, unknown>): Promise<string | null>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Workspace receipts (T1.3, spec/reconcile.md §7): a Claude Code PreToolUse / PostToolUse hook that
|
|
2
|
+
// records the git tree hash of the workspace before and after every tool call.
|
|
3
|
+
// Mirrors sdks/python/src/zanii_blackbox/workspace_receipt.py.
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
6
|
+
import { request as httpRequest } from "node:http";
|
|
7
|
+
import { request as httpsRequest } from "node:https";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join, resolve } from "node:path";
|
|
10
|
+
const GIT_TIMEOUT_MS = 10_000;
|
|
11
|
+
const HTTP_TIMEOUT_MS = 5_000;
|
|
12
|
+
/**
|
|
13
|
+
* The workspace's content as one git tree hash: tracked and untracked files (minus .gitignore'd
|
|
14
|
+
* ones), built in a temporary index so the user's staging area is never touched.
|
|
15
|
+
* ponytail: `git add -A` hashes every changed file, so a huge dirty tree costs seconds per tool call.
|
|
16
|
+
*/
|
|
17
|
+
export function workspaceTree(cwd) {
|
|
18
|
+
const git = (args, env = process.env) => spawnSync("git", args, {
|
|
19
|
+
cwd,
|
|
20
|
+
env,
|
|
21
|
+
encoding: "utf8",
|
|
22
|
+
timeout: GIT_TIMEOUT_MS,
|
|
23
|
+
windowsHide: true,
|
|
24
|
+
});
|
|
25
|
+
const top = git(["rev-parse", "--show-toplevel", "--absolute-git-dir", "--git-path", "objects"]);
|
|
26
|
+
if (top.error)
|
|
27
|
+
return { reason: "git_unavailable" };
|
|
28
|
+
if (top.status !== 0)
|
|
29
|
+
return { reason: "not_a_git_repo" };
|
|
30
|
+
const [toplevel, gitDir, objects] = top.stdout.trim().split(/\r?\n/);
|
|
31
|
+
if (!toplevel || !gitDir || !objects)
|
|
32
|
+
return { reason: "git_failed" };
|
|
33
|
+
const scratch = mkdtempSync(join(tmpdir(), "bbx-receipt-"));
|
|
34
|
+
try {
|
|
35
|
+
const index = join(scratch, "index");
|
|
36
|
+
// Starting from the real index lets git skip files it already hashed.
|
|
37
|
+
if (existsSync(join(gitDir, "index")))
|
|
38
|
+
copyFileSync(join(gitDir, "index"), index);
|
|
39
|
+
// New blobs go to a scratch object store (the repository's is an alternate, so what's already
|
|
40
|
+
// there is found), and vanish with it: the receipt leaves no loose objects behind (L2.1.3).
|
|
41
|
+
const store = join(scratch, "objects");
|
|
42
|
+
mkdirSync(store);
|
|
43
|
+
const env = {
|
|
44
|
+
...process.env,
|
|
45
|
+
GIT_INDEX_FILE: index,
|
|
46
|
+
GIT_OBJECT_DIRECTORY: store,
|
|
47
|
+
GIT_ALTERNATE_OBJECT_DIRECTORIES: resolve(cwd, objects),
|
|
48
|
+
};
|
|
49
|
+
const add = spawnSync("git", ["add", "-A", "--", "."], {
|
|
50
|
+
cwd: toplevel,
|
|
51
|
+
env,
|
|
52
|
+
encoding: "utf8",
|
|
53
|
+
timeout: GIT_TIMEOUT_MS,
|
|
54
|
+
windowsHide: true,
|
|
55
|
+
});
|
|
56
|
+
if (add.status !== 0)
|
|
57
|
+
return { reason: "git_failed" };
|
|
58
|
+
const tree = spawnSync("git", ["write-tree"], {
|
|
59
|
+
cwd: toplevel,
|
|
60
|
+
env,
|
|
61
|
+
encoding: "utf8",
|
|
62
|
+
timeout: GIT_TIMEOUT_MS,
|
|
63
|
+
windowsHide: true,
|
|
64
|
+
});
|
|
65
|
+
const hash = tree.stdout.trim();
|
|
66
|
+
return tree.status === 0 && /^([0-9a-f]{40}|[0-9a-f]{64})$/.test(hash)
|
|
67
|
+
? { tree: hash }
|
|
68
|
+
: { reason: "git_failed" };
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** The hook's JSON input (from Claude Code on stdin) → the receipt posted to the gateway. */
|
|
75
|
+
export function receiptFor(hook, tree) {
|
|
76
|
+
const phase = hook.hook_event_name === "PreToolUse"
|
|
77
|
+
? "pre"
|
|
78
|
+
: hook.hook_event_name === "PostToolUse"
|
|
79
|
+
? "post"
|
|
80
|
+
: null;
|
|
81
|
+
if (!phase || typeof hook.tool_use_id !== "string" || hook.tool_use_id === "")
|
|
82
|
+
return null;
|
|
83
|
+
return {
|
|
84
|
+
phase,
|
|
85
|
+
tool_use_id: hook.tool_use_id,
|
|
86
|
+
...(typeof hook.tool_name === "string" ? { tool: hook.tool_name.slice(0, 256) } : {}),
|
|
87
|
+
...(typeof hook.session_id === "string"
|
|
88
|
+
? { agent_session: hook.session_id.slice(0, 256) }
|
|
89
|
+
: {}),
|
|
90
|
+
...("tree" in tree ? { tree: tree.tree } : { tree: null, reason: tree.reason }),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Posts one receipt. Resolves with null on 204, else the reason; never rejects. */
|
|
94
|
+
export function postReceipt(gatewayUrl, token, receipt) {
|
|
95
|
+
return new Promise((resolve) => {
|
|
96
|
+
let url;
|
|
97
|
+
try {
|
|
98
|
+
url = new URL(`${gatewayUrl.replace(/\/$/, "")}/v1/workspace-receipts`);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return resolve("BLACKBOX_URL is not a URL");
|
|
102
|
+
}
|
|
103
|
+
const body = JSON.stringify(receipt);
|
|
104
|
+
const req = (url.protocol === "https:" ? httpsRequest : httpRequest)(url, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
timeout: HTTP_TIMEOUT_MS,
|
|
107
|
+
headers: {
|
|
108
|
+
authorization: `Bearer ${token}`,
|
|
109
|
+
"content-type": "application/json",
|
|
110
|
+
"content-length": Buffer.byteLength(body),
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
req.on("response", (res) => {
|
|
114
|
+
res.resume();
|
|
115
|
+
resolve(res.statusCode === 204 ? null : `the gateway answered ${res.statusCode}`);
|
|
116
|
+
});
|
|
117
|
+
req.on("timeout", () => req.destroy(new Error("timed out")));
|
|
118
|
+
req.on("error", (error) => resolve(`the gateway could not be reached (${error.message})`));
|
|
119
|
+
req.end(body);
|
|
120
|
+
});
|
|
121
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,59 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zanii/blackbox",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"license": "Apache-2.0",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"description": "The flight recorder for AI agents: sessions, a zero-loss spool, framework hooks, approvals, and offline verification of the gateway's hash-chained record.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"ai-agents",
|
|
8
|
+
"observability",
|
|
9
|
+
"audit",
|
|
10
|
+
"flight-recorder",
|
|
11
|
+
"llm",
|
|
12
|
+
"mcp",
|
|
13
|
+
"langgraph",
|
|
14
|
+
"openai-agents",
|
|
15
|
+
"claude",
|
|
16
|
+
"tamper-evident",
|
|
17
|
+
"compliance"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://zanii.agency",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"email": "info@zanii.agency"
|
|
22
|
+
},
|
|
23
|
+
"author": "Zanii <info@zanii.agency>",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=24"
|
|
27
|
+
},
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"bin": {
|
|
35
|
+
"blackbox": "./dist/cli.js"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@zanii/core": "^0.5.1",
|
|
44
|
+
"@zanii/sdk": "^0.5.1"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.283",
|
|
48
|
+
"@langchain/core": "1.2.13",
|
|
49
|
+
"@langchain/langgraph": "1.4.18",
|
|
50
|
+
"@openai/agents": "0.18.0",
|
|
51
|
+
"langchain": "1.5.14",
|
|
52
|
+
"zod": "^4.6.5"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsc -p tsconfig.build.json",
|
|
56
|
+
"typecheck": "tsc -p .",
|
|
57
|
+
"test": "node --test --test-timeout=30000 \"src/**/*.test.ts\""
|
|
58
|
+
}
|
|
6
59
|
}
|