@zanii/blackbox 0.8.0 → 0.10.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/README.md +12 -0
- package/dist/cli.js +3 -1
- package/dist/client/index.d.ts +14 -1
- package/dist/client/index.js +10 -0
- package/dist/policy/index.d.ts +3 -0
- package/dist/policy/index.js +49 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,6 +83,18 @@ verifyAnswerCredential(credential, lines, bodies, packs); // { ok, problems }: t
|
|
|
83
83
|
|
|
84
84
|
The same checks run offline as pure functions: `toolHallucinations`, `groundingFindings`, `factsIn`, `loadReferencePacks`, `answerRisk`, `detectorAccuracy`.
|
|
85
85
|
|
|
86
|
+
## New in 0.10.0
|
|
87
|
+
|
|
88
|
+
- `setTenantPolicy` / `tenantPolicy` / `removeTenantPolicy`: a tenant's own rules, hot on the gateway. `setProviderKey` / `providerKeys` / `removeProviderKey`: a tenant's own model keys, sealed. `cancelRun(sessionId, runId)`: stop one run (calls carrying `x-blackbox-run`).
|
|
89
|
+
- `loadPolicy` refuses an `args_match` that JavaScript and Python would read differently (`portableRegexError`).
|
|
90
|
+
- `blackbox sessions --outcome success|failure|abandoned`.
|
|
91
|
+
- Works with gateway v0.12: alert routing to PagerDuty, Teams, Alertmanager and Slack; the key passphrase from Vault or AWS KMS.
|
|
92
|
+
|
|
93
|
+
## New in 0.9.0
|
|
94
|
+
|
|
95
|
+
- `client(...).createKey(tenantId, {scope: "approve", expires_in_days})`: a key that only decides approvals (and labels findings), and keys that expire.
|
|
96
|
+
- Works with gateway v0.11: Idempotency-Key kept in the database (shared by every instance), the admin audit log anchored on the ledger and naming the signed-in person, and `BLACKBOX_APPROVALS_NEED_PERSON`.
|
|
97
|
+
|
|
86
98
|
## New in 0.8.0
|
|
87
99
|
|
|
88
100
|
- `blackbox drain --close` (and `drainSpools({close: true})` / `drain_spools(close=True)`): after shipping what a stopped agent left behind, each session is closed as `abandoned`.
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ const USAGE = `usage (spec/cli.md):
|
|
|
15
15
|
blackbox verify <bundle.json> [--proofs] [--bodies <dir>]
|
|
16
16
|
blackbox export <session_id> --out <bundle.json> [--bodies <dir>]
|
|
17
17
|
blackbox export-all --from <date> --to <date> --out <file.ndjson>
|
|
18
|
-
blackbox sessions [--limit N] [--label L] [--open | --closed] [--before <session_id>] [--from <date>] [--to <date>] [--json]
|
|
18
|
+
blackbox sessions [--limit N] [--label L] [--open | --closed] [--outcome success|failure|abandoned] [--before <session_id>] [--from <date>] [--to <date>] [--json]
|
|
19
19
|
blackbox sessions <session_id> [--json]
|
|
20
20
|
blackbox summary <session_id>
|
|
21
21
|
blackbox incident <session_id> [--lang en|ar]
|
|
@@ -388,6 +388,8 @@ async function sessionsCommand(rest) {
|
|
|
388
388
|
q.set("from", f.from);
|
|
389
389
|
if (f.to !== undefined)
|
|
390
390
|
q.set("to", f.to);
|
|
391
|
+
if (f.outcome !== undefined)
|
|
392
|
+
q.set("outcome", f.outcome);
|
|
391
393
|
if (f.open === "1")
|
|
392
394
|
q.set("status", "open");
|
|
393
395
|
if (f.closed === "1")
|
package/dist/client/index.d.ts
CHANGED
|
@@ -102,6 +102,10 @@ export declare function client(options: ClientOptions): {
|
|
|
102
102
|
emergency: (id: string, o?: {
|
|
103
103
|
note?: string;
|
|
104
104
|
}) => Promise<Json>;
|
|
105
|
+
/** Refuses the session's later calls that name this run (x-blackbox-run), spec/control.md §3a. */
|
|
106
|
+
cancelRun: (id: string, runId: string, o?: {
|
|
107
|
+
note?: string;
|
|
108
|
+
}) => Promise<Json>;
|
|
105
109
|
/** Hands the controls over (spec/authority.md): `human` takes over; `agent`/`supervised` hand back. */
|
|
106
110
|
authority: (id: string, to: "human" | "agent" | "supervised", o?: {
|
|
107
111
|
reason?: string;
|
|
@@ -139,8 +143,17 @@ export declare function client(options: ClientOptions): {
|
|
|
139
143
|
disabled?: boolean;
|
|
140
144
|
}) => Promise<Json>;
|
|
141
145
|
createKey: (tenantId: string, o?: {
|
|
142
|
-
scope?: "full" | "read";
|
|
146
|
+
scope?: "full" | "read" | "approve";
|
|
147
|
+
expires_in_days?: number;
|
|
143
148
|
}) => Promise<Json>;
|
|
144
149
|
revokeKey: (tenantId: string, keyId: string) => Promise<Json>;
|
|
150
|
+
/** Operator only: a tenant's own rules (spec/policy.md §7a). */
|
|
151
|
+
tenantPolicy: (tenantId: string) => Promise<Json>;
|
|
152
|
+
setTenantPolicy: (tenantId: string, rules: object[]) => Promise<Json>;
|
|
153
|
+
removeTenantPolicy: (tenantId: string) => Promise<Json>;
|
|
154
|
+
/** Operator only: a tenant's own provider keys, sealed, never shown (spec/tenancy.md §5a). */
|
|
155
|
+
providerKeys: (tenantId: string) => Promise<Json>;
|
|
156
|
+
setProviderKey: (tenantId: string, provider: string, apiKey: string) => Promise<Json>;
|
|
157
|
+
removeProviderKey: (tenantId: string, provider: string) => Promise<Json>;
|
|
145
158
|
};
|
|
146
159
|
export type Client = ReturnType<typeof client>;
|
package/dist/client/index.js
CHANGED
|
@@ -187,6 +187,8 @@ export function client(options) {
|
|
|
187
187
|
pause: (id, o = {}) => json("POST", ses(id, "pause"), compact(o)),
|
|
188
188
|
/** The emergency stop (squawk 7700, spec/control.md). */
|
|
189
189
|
emergency: (id, o = {}) => json("POST", ses(id, "emergency"), compact(o)),
|
|
190
|
+
/** Refuses the session's later calls that name this run (x-blackbox-run), spec/control.md §3a. */
|
|
191
|
+
cancelRun: (id, runId, o = {}) => json("POST", ses(id, `runs/${encodeURIComponent(runId)}/cancel`), compact(o), true),
|
|
190
192
|
/** Hands the controls over (spec/authority.md): `human` takes over; `agent`/`supervised` hand back. */
|
|
191
193
|
authority: (id, to, o = {}) => json("POST", ses(id, "authority"), compact({ to, ...o })),
|
|
192
194
|
/** The pending approvals (spec/approvals.md). */
|
|
@@ -205,6 +207,14 @@ export function client(options) {
|
|
|
205
207
|
updateTenant: (tenantId, o) => json("PATCH", `/v1/tenants/${encodeURIComponent(tenantId)}`, compact(o)),
|
|
206
208
|
createKey: (tenantId, o = {}) => json("POST", `/v1/tenants/${encodeURIComponent(tenantId)}/keys`, compact(o)),
|
|
207
209
|
revokeKey: (tenantId, keyId) => json("POST", `/v1/tenants/${encodeURIComponent(tenantId)}/keys/${encodeURIComponent(keyId)}/revoke`, {}, true),
|
|
210
|
+
/** Operator only: a tenant's own rules (spec/policy.md §7a). */
|
|
211
|
+
tenantPolicy: (tenantId) => json("GET", `/v1/tenants/${encodeURIComponent(tenantId)}/policy`),
|
|
212
|
+
setTenantPolicy: (tenantId, rules) => json("PUT", `/v1/tenants/${encodeURIComponent(tenantId)}/policy`, { policy: { rules } }, true),
|
|
213
|
+
removeTenantPolicy: (tenantId) => json("DELETE", `/v1/tenants/${encodeURIComponent(tenantId)}/policy`, undefined, true),
|
|
214
|
+
/** Operator only: a tenant's own provider keys, sealed, never shown (spec/tenancy.md §5a). */
|
|
215
|
+
providerKeys: (tenantId) => json("GET", `/v1/tenants/${encodeURIComponent(tenantId)}/providers`),
|
|
216
|
+
setProviderKey: (tenantId, provider, apiKey) => json("PUT", `/v1/tenants/${encodeURIComponent(tenantId)}/providers/${encodeURIComponent(provider)}`, { api_key: apiKey }, true),
|
|
217
|
+
removeProviderKey: (tenantId, provider) => json("DELETE", `/v1/tenants/${encodeURIComponent(tenantId)}/providers/${encodeURIComponent(provider)}`, undefined, true),
|
|
208
218
|
};
|
|
209
219
|
return api;
|
|
210
220
|
}
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -59,6 +59,9 @@ interface Compiled {
|
|
|
59
59
|
args: RegExp | null;
|
|
60
60
|
}
|
|
61
61
|
/** Parses a policy file; throws on anything invalid (the server refuses to start). */
|
|
62
|
+
/** spec/policy.md §1 (L6.5.4): null when `args_match` uses only syntax JavaScript and Python
|
|
63
|
+
* share, else what doesn't (vectors: spec/vectors/policy-regex.json). */
|
|
64
|
+
export declare function portableRegexError(p: string): string | null;
|
|
62
65
|
export declare function loadPolicy(bytes: Uint8Array): Policy;
|
|
63
66
|
export declare function compilePolicy(policy: Policy): Compiled[];
|
|
64
67
|
/** spec/api.md: the session's environment, from its `session.open` (the first line). */
|
package/dist/policy/index.js
CHANGED
|
@@ -58,6 +58,52 @@ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
|
58
58
|
/** A tool pattern: `*` is any run of characters, the rest is literal, anchored at both ends. */
|
|
59
59
|
const glob = (pattern) => new RegExp(`^${pattern.split("*").map(escapeRe).join(".*")}$`);
|
|
60
60
|
/** Parses a policy file; throws on anything invalid (the server refuses to start). */
|
|
61
|
+
/** spec/policy.md §1 (L6.5.4): null when `args_match` uses only syntax JavaScript and Python
|
|
62
|
+
* share, else what doesn't (vectors: spec/vectors/policy-regex.json). */
|
|
63
|
+
export function portableRegexError(p) {
|
|
64
|
+
const hex = (s) => /^[0-9A-Fa-f]+$/.test(s);
|
|
65
|
+
let inClass = false;
|
|
66
|
+
for (let i = 0; i < p.length; i++) {
|
|
67
|
+
const c = p[i];
|
|
68
|
+
if (c === "\\") {
|
|
69
|
+
const n = p[i + 1];
|
|
70
|
+
if (n === undefined)
|
|
71
|
+
return "a trailing \\";
|
|
72
|
+
if (n === "x" && !(p.length >= i + 4 && hex(p.slice(i + 2, i + 4))))
|
|
73
|
+
return "\\x needs 2 hex digits";
|
|
74
|
+
if (n === "u" && !(p.length >= i + 6 && hex(p.slice(i + 2, i + 6))))
|
|
75
|
+
return "\\u needs 4 hex digits";
|
|
76
|
+
if (/[A-Za-z]/.test(n) && !"dDwWsSbBnrtfvxu".includes(n))
|
|
77
|
+
return `\\${n}`;
|
|
78
|
+
i++;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (inClass) {
|
|
82
|
+
if (c === "]")
|
|
83
|
+
inClass = false;
|
|
84
|
+
else if (c === "[" && p[i + 1] === ":")
|
|
85
|
+
return "[:";
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (c === "[") {
|
|
89
|
+
if (p[i + 1] === ":")
|
|
90
|
+
return "[:";
|
|
91
|
+
const first = p[i + 1] === "^" ? i + 2 : i + 1;
|
|
92
|
+
if (p[first] === "]")
|
|
93
|
+
return "an empty class";
|
|
94
|
+
inClass = true;
|
|
95
|
+
i = first - 1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (c === "(" && p[i + 1] === "?" && !":=!".includes(p[i + 2] ?? ""))
|
|
99
|
+
return "(? other than (?: (?= (?!";
|
|
100
|
+
if ("*+?}".includes(c) && p[i + 1] === "+" && !(c === "?" && "*+?}".includes(p[i - 1] ?? "")))
|
|
101
|
+
return "a possessive quantifier";
|
|
102
|
+
if (c === "{" && p[i + 1] === ",")
|
|
103
|
+
return "{,";
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
61
107
|
export function loadPolicy(bytes) {
|
|
62
108
|
const doc = JSON.parse(new TextDecoder().decode(bytes));
|
|
63
109
|
if (doc.version !== 1 || !Array.isArray(doc.rules))
|
|
@@ -77,6 +123,9 @@ export function loadPolicy(bytes) {
|
|
|
77
123
|
if (r.args_match !== undefined) {
|
|
78
124
|
if (typeof r.args_match !== "string" || r.args_match.length > 200)
|
|
79
125
|
throw new Error(`policy ${r.id}: args_match must be a regular expression of ≤ 200 characters`);
|
|
126
|
+
const unportable = portableRegexError(r.args_match);
|
|
127
|
+
if (unportable)
|
|
128
|
+
throw new Error(`policy ${r.id}: args_match uses syntax JavaScript and Python don't share (${unportable})`);
|
|
80
129
|
new RegExp(r.args_match); // throws on bad syntax
|
|
81
130
|
}
|
|
82
131
|
if (r.audit !== undefined && typeof r.audit !== "boolean")
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.10.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// The SDK version, on its own so the CLI can print it without loading the whole SDK.
|
|
2
|
-
export const VERSION = "0.
|
|
2
|
+
export const VERSION = "0.10.0";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zanii/blackbox",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.10.0",
|
|
5
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
6
|
"keywords": [
|
|
7
7
|
"ai-agents",
|