@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 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")
@@ -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>;
@@ -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
  }
@@ -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). */
@@ -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.8.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.8.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.8.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",