@zanii/blackbox 0.2.0 → 0.4.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 +26 -1
- package/dist/a2a/index.d.ts +77 -0
- package/dist/a2a/index.js +305 -0
- package/dist/agents/index.d.ts +8 -0
- package/dist/analysis/accuracy.d.ts +24 -0
- package/dist/analysis/accuracy.js +45 -0
- package/dist/analysis/credential.d.ts +101 -0
- package/dist/analysis/credential.js +142 -0
- package/dist/analysis/faults.js +115 -0
- package/dist/analysis/grounding.d.ts +122 -0
- package/dist/analysis/grounding.js +445 -0
- package/dist/analysis/hallucination.d.ts +32 -0
- package/dist/analysis/hallucination.js +357 -0
- package/dist/analysis/index.d.ts +23 -0
- package/dist/analysis/index.js +93 -0
- package/dist/analysis/memory.d.ts +8 -0
- package/dist/analysis/memory.js +35 -8
- package/dist/analysis/reference.d.ts +49 -0
- package/dist/analysis/reference.js +164 -0
- package/dist/analysis/taxonomy.d.ts +18 -0
- package/dist/analysis/taxonomy.js +66 -0
- package/dist/approvals/index.d.ts +23 -0
- package/dist/approvals/index.js +48 -0
- package/dist/archive/index.d.ts +39 -0
- package/dist/archive/index.js +96 -0
- package/dist/archive/parquet.d.ts +2 -0
- package/dist/archive/parquet.js +185 -0
- package/dist/badge/index.d.ts +16 -0
- package/dist/badge/index.js +48 -0
- package/dist/bom/index.d.ts +14 -0
- package/dist/bom/index.js +152 -0
- package/dist/cli.js +114 -10
- package/dist/compliance/art12.d.ts +35 -0
- package/dist/compliance/art12.js +190 -0
- package/dist/compliance/index.d.ts +36 -2
- package/dist/compliance/index.js +78 -11
- package/dist/compliance/zanii.d.ts +29 -0
- package/dist/compliance/zanii.js +84 -0
- package/dist/constitution/index.d.ts +57 -0
- package/dist/constitution/index.js +131 -0
- package/dist/cv/index.d.ts +39 -0
- package/dist/cv/index.js +108 -0
- package/dist/disclosure/index.d.ts +31 -0
- package/dist/disclosure/index.js +113 -0
- package/dist/encryption/index.d.ts +9 -0
- package/dist/encryption/index.js +31 -0
- package/dist/evidence/index.d.ts +60 -0
- package/dist/evidence/index.js +151 -0
- package/dist/federation/index.d.ts +35 -0
- package/dist/federation/index.js +102 -0
- package/dist/finance/index.d.ts +126 -0
- package/dist/finance/index.js +320 -0
- package/dist/fleet/index.js +9 -0
- package/dist/gov/index.d.ts +108 -0
- package/dist/gov/index.js +225 -0
- package/dist/health/index.d.ts +120 -0
- package/dist/health/index.js +233 -0
- package/dist/index.d.ts +34 -5
- package/dist/index.js +34 -5
- package/dist/memory/index.d.ts +36 -0
- package/dist/memory/index.js +85 -0
- package/dist/occurrence/index.d.ts +11 -0
- package/dist/occurrence/index.js +18 -0
- package/dist/ocsf/index.d.ts +1 -1
- package/dist/ocsf/index.js +36 -3
- package/dist/otlp/index.d.ts +8 -1
- package/dist/otlp/index.js +258 -1
- package/dist/packs/index.js +44 -4
- package/dist/policy/delta.js +7 -1
- package/dist/policy/index.d.ts +40 -6
- package/dist/policy/index.js +186 -8
- package/dist/policy/zanii.d.ts +31 -0
- package/dist/policy/zanii.js +87 -0
- package/dist/pq/index.d.ts +23 -0
- package/dist/pq/index.js +104 -0
- package/dist/search/index.d.ts +23 -0
- package/dist/search/index.js +69 -0
- package/dist/session/index.d.ts +107 -1
- package/dist/session/index.js +189 -11
- package/dist/sla/index.d.ts +61 -0
- package/dist/sla/index.js +197 -0
- package/dist/succession/index.d.ts +50 -0
- package/dist/succession/index.js +123 -0
- package/dist/timestamp/index.d.ts +24 -0
- package/dist/timestamp/index.js +274 -0
- package/dist/tokens/index.d.ts +6 -0
- package/dist/tokens/index.js +46 -0
- package/dist/transparency/index.d.ts +188 -0
- package/dist/transparency/index.js +712 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/walls/index.d.ts +31 -0
- package/dist/walls/index.js +119 -0
- package/package.json +1 -1
package/dist/session/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Attestation } from "../attest/index.ts";
|
|
2
|
+
import { buildPayment, type screenCounterparty } from "../finance/index.ts";
|
|
2
3
|
export interface SessionOptions {
|
|
3
4
|
/** The gateway, e.g. http://127.0.0.1:8787 */
|
|
4
5
|
url: string;
|
|
@@ -33,6 +34,8 @@ export interface SessionOptions {
|
|
|
33
34
|
tenant?: string;
|
|
34
35
|
/** spec/authority.md: `supervised` from the start (risky actions wait for a second person). */
|
|
35
36
|
authority?: "agent" | "supervised";
|
|
37
|
+
/** spec/api.md: where it runs (`production`, `staging`, …); policy rules can match it. */
|
|
38
|
+
environment?: string;
|
|
36
39
|
/** spec/preflight.md: the equipment the run needs. A no-go returns a disabled session (and
|
|
37
40
|
* `stats.lastError` says why); optional items that are down show in `state().degraded`. */
|
|
38
41
|
preflight?: {
|
|
@@ -125,6 +128,8 @@ export interface LlmCallIds {
|
|
|
125
128
|
}
|
|
126
129
|
/** N4 (idea R6): the same parts always give the same event id (sha256, 128 bits). */
|
|
127
130
|
export declare function stableEventId(...parts: readonly string[]): string;
|
|
131
|
+
/** The image type of a screenshot, by its first bytes: PNG, JPEG or WebP. */
|
|
132
|
+
export declare function imageType(image: Uint8Array): "image/png" | "image/jpeg" | "image/webp" | null;
|
|
128
133
|
/** Opens (or joins) a session. Never throws: on failure it returns a disabled session and logs why. */
|
|
129
134
|
export declare function session(options: SessionOptions): Promise<BlackboxSession>;
|
|
130
135
|
export declare class BlackboxSession {
|
|
@@ -142,6 +147,10 @@ export declare class BlackboxSession {
|
|
|
142
147
|
private timers;
|
|
143
148
|
private closed;
|
|
144
149
|
private landings;
|
|
150
|
+
/** spec/approvals.md §2: approved tools with arguments, until their call is reported. */
|
|
151
|
+
private approved;
|
|
152
|
+
/** spec/agents.md §4: the memory chain's tip. */
|
|
153
|
+
private memoryLast;
|
|
145
154
|
/** A session that records nothing. `reason`, when opening failed, goes to `stats.lastError` (audit K1). */
|
|
146
155
|
static disabled(options: SessionOptions, reason?: string): BlackboxSession;
|
|
147
156
|
private readonly options;
|
|
@@ -152,8 +161,74 @@ export declare class BlackboxSession {
|
|
|
152
161
|
keepToken?: boolean);
|
|
153
162
|
event(type: string, name?: string, data?: Record<string, unknown>, options?: {
|
|
154
163
|
eventId?: string;
|
|
164
|
+
/** spec/sdk.md §1.1: an image the event's body is (see `screen`). */
|
|
165
|
+
attachment?: {
|
|
166
|
+
contentType: string;
|
|
167
|
+
bytes: Uint8Array;
|
|
168
|
+
};
|
|
155
169
|
}): void;
|
|
156
170
|
step(name: string, data?: Record<string, unknown>): void;
|
|
171
|
+
/**
|
|
172
|
+
* spec/gov.md §2: a decision about a person, under the rulebook `manifestHash`, the person named
|
|
173
|
+
* only by their subject tag. Returns the factors' nonce (keep it to disclose them in a dispute).
|
|
174
|
+
* Throws on a decision that can't be lawful evidence: no rulebook, a bad kind, no tag.
|
|
175
|
+
*/
|
|
176
|
+
decision(kind: string, o: {
|
|
177
|
+
manifestHash: string;
|
|
178
|
+
outcome: Record<string, unknown>;
|
|
179
|
+
subjectTag: string;
|
|
180
|
+
factors?: Record<string, unknown>;
|
|
181
|
+
appealBy?: string;
|
|
182
|
+
}): {
|
|
183
|
+
nonce: string;
|
|
184
|
+
};
|
|
185
|
+
/** spec/health.md §2: who accessed a patient's record (an episode tag, never the seed), and why. */
|
|
186
|
+
healthAccess(o: {
|
|
187
|
+
episodeTag: string;
|
|
188
|
+
actor: string;
|
|
189
|
+
purpose: string;
|
|
190
|
+
consentRef?: string;
|
|
191
|
+
details?: Record<string, unknown>;
|
|
192
|
+
}): {
|
|
193
|
+
nonce: string;
|
|
194
|
+
};
|
|
195
|
+
/** spec/health.md §2: a model's recommendation under a protocol. `hash` is what the clinician signs. */
|
|
196
|
+
recommendation(o: {
|
|
197
|
+
episodeTag: string;
|
|
198
|
+
modelId: string;
|
|
199
|
+
manifestHash: string;
|
|
200
|
+
outcome: Record<string, unknown>;
|
|
201
|
+
runtimeHash?: string;
|
|
202
|
+
deviation?: Record<string, unknown>;
|
|
203
|
+
}): {
|
|
204
|
+
nonce: string;
|
|
205
|
+
hash: string;
|
|
206
|
+
};
|
|
207
|
+
/** spec/health.md §2: a clinician's signed confirmation (clinicianConfirmation). Throws on a bad signature. */
|
|
208
|
+
clinicianConfirmation(episodeTag: string, payload: Record<string, unknown>): void;
|
|
209
|
+
/** spec/health.md §2: an emergency access, signed by the clinician (breakGlass). Loud on purpose. */
|
|
210
|
+
breakGlass(episodeTag: string, payload: Record<string, unknown>): void;
|
|
211
|
+
private signedHealth;
|
|
212
|
+
/** spec/finance.md §2: a payment, exact (minor units), with its settlement reference. Throws on a bad amount. */
|
|
213
|
+
payment(input: Parameters<typeof buildPayment>[0]): void;
|
|
214
|
+
/** spec/finance.md §3: a counterparty screening (screenCounterparty), before paying them. */
|
|
215
|
+
screening(result: ReturnType<typeof screenCounterparty>): void;
|
|
216
|
+
/** spec/finance.md §4: a tax filing the agent prepared. It never files; a Tax Agent does. */
|
|
217
|
+
ftaPrepared(o: {
|
|
218
|
+
filingType: string;
|
|
219
|
+
period: string;
|
|
220
|
+
filingRef?: string;
|
|
221
|
+
manifestHash?: string;
|
|
222
|
+
by?: string;
|
|
223
|
+
}): void;
|
|
224
|
+
/** spec/finance.md §4: the prepared filing handed to a licensed Tax Agent. */
|
|
225
|
+
ftaHandoff(o: {
|
|
226
|
+
filingType: string;
|
|
227
|
+
period: string;
|
|
228
|
+
toTaxAgent: string;
|
|
229
|
+
filingRef?: string;
|
|
230
|
+
by?: string;
|
|
231
|
+
}): void;
|
|
157
232
|
toolCall(name: string, args?: unknown): void;
|
|
158
233
|
toolResult(name: string, result?: {
|
|
159
234
|
ok?: boolean;
|
|
@@ -193,6 +268,15 @@ export declare class BlackboxSession {
|
|
|
193
268
|
summary?: string;
|
|
194
269
|
file_path?: string;
|
|
195
270
|
}): void;
|
|
271
|
+
/** spec/findings.md §11 (H3): pin the documents the agent read, by hash; the content never leaves.
|
|
272
|
+
* Each doc is `{id, uri?, version?}` with its `content` (hashed here) or its `sha256`. */
|
|
273
|
+
retrieved(docs: ReadonlyArray<{
|
|
274
|
+
id: string;
|
|
275
|
+
uri?: string;
|
|
276
|
+
version?: string;
|
|
277
|
+
content?: string | Uint8Array;
|
|
278
|
+
sha256?: string;
|
|
279
|
+
}>): void;
|
|
196
280
|
/** Stage 2 R3: the result of one of the customer's own checks (tests, a schema, a policy, a
|
|
197
281
|
* partial goal), mid-run or at the end. A pass then a fail is VERIFY_REGRESSION; a success whose last
|
|
198
282
|
* check failed is FALSE_SUCCESS (spec/findings.md §6). */
|
|
@@ -215,8 +299,30 @@ export declare class BlackboxSession {
|
|
|
215
299
|
nearMiss(description: string): void;
|
|
216
300
|
/** spec/attestation.md: re-runs a read-only command and records whether the output matches. */
|
|
217
301
|
attest(argv: string[], claimed: string, cwd?: string, claimedExit?: number): Attestation | undefined;
|
|
302
|
+
/**
|
|
303
|
+
* spec/sdk.md §1.1: a computer-use screenshot (PNG, JPEG or WebP, at most 2 MB), recorded as the
|
|
304
|
+
* body of a `screen` event, so its hash is in the chain. `action` says what the agent did or is
|
|
305
|
+
* about to do (≤ 500 characters). Mask what mustn't be kept before calling: the image isn't
|
|
306
|
+
* redacted. One that's too big or not an image is dropped, and the event says why.
|
|
307
|
+
*/
|
|
308
|
+
screen(image: Uint8Array, options?: {
|
|
309
|
+
action?: string;
|
|
310
|
+
width?: number;
|
|
311
|
+
height?: number;
|
|
312
|
+
}): void;
|
|
218
313
|
/** spec/agents.md §1: the agent's memory store wrote, revoked or returned memories. */
|
|
219
|
-
|
|
314
|
+
/** spec/agents.md §4: with `content`, the write also carries Zanii's memory entry (a salted
|
|
315
|
+
* commitment, chained after `prev`, else this session's last entry). Keep the salt it returns. */
|
|
316
|
+
memoryWrite(memoryId: string, summary?: string, opts?: {
|
|
317
|
+
content: string;
|
|
318
|
+
kind?: string;
|
|
319
|
+
prev?: Record<string, unknown>;
|
|
320
|
+
agent?: string;
|
|
321
|
+
tags?: string[];
|
|
322
|
+
}): {
|
|
323
|
+
entry: Record<string, unknown>;
|
|
324
|
+
salt: string;
|
|
325
|
+
} | undefined;
|
|
220
326
|
memoryRevoke(memoryId: string, reason?: string): void;
|
|
221
327
|
memoryRead(memoryIds: string[], query?: string): void;
|
|
222
328
|
/** Ships what's pending now. Resolves true when everything recorded is acknowledged. With
|
package/dist/session/index.js
CHANGED
|
@@ -6,7 +6,19 @@ import { request as httpRequest } from "node:http";
|
|
|
6
6
|
import { request as httpsRequest } from "node:https";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
+
import { argsHash } from "../approvals/index.js";
|
|
9
10
|
import { attest, isReadOnly } from "../attest/index.js";
|
|
11
|
+
import { buildPayment, ftaPayload, screeningPayload, } from "../finance/index.js";
|
|
12
|
+
import { decisionPayload } from "../gov/index.js";
|
|
13
|
+
import { accessPayload, checkEpisodeTag, recommendationPayload, verifyHealthSignature, } from "../health/index.js";
|
|
14
|
+
import { memoryEntry } from "../memory/index.js";
|
|
15
|
+
/** spec/preflight.md §1 (L3): with an `egress` item, the SDK checks it here, on the agent's host. */
|
|
16
|
+
async function withEgress(p) {
|
|
17
|
+
if (![...(p.require ?? []), ...(p.optional ?? [])].includes("egress"))
|
|
18
|
+
return p;
|
|
19
|
+
const { url, open } = await checkEgress({ timeoutMs: 3000 });
|
|
20
|
+
return { ...p, egress: { url, open } };
|
|
21
|
+
}
|
|
10
22
|
/** spec/data.md §6: a direct HTTPS request to `url` (default https://example.com). Never throws. */
|
|
11
23
|
export async function checkEgress(options = {}) {
|
|
12
24
|
const url = options.url ?? "https://example.com";
|
|
@@ -30,6 +42,20 @@ const MAX_DATA = 64 * 1024;
|
|
|
30
42
|
const PREVIEW = 4096;
|
|
31
43
|
const BATCH = 500;
|
|
32
44
|
const READ_CHUNK = 4 * 1024 * 1024;
|
|
45
|
+
/** spec/sdk.md §1.1: a screenshot's bytes at most; base64 in its spool line, it stays under READ_CHUNK. */
|
|
46
|
+
const MAX_ATTACHMENT = 2 * 1024 * 1024;
|
|
47
|
+
/** The image type of a screenshot, by its first bytes: PNG, JPEG or WebP. */
|
|
48
|
+
export function imageType(image) {
|
|
49
|
+
const b = Buffer.from(image.buffer, image.byteOffset, image.byteLength);
|
|
50
|
+
if (b.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])))
|
|
51
|
+
return "image/png";
|
|
52
|
+
if (b.subarray(0, 3).equals(Buffer.from([0xff, 0xd8, 0xff])))
|
|
53
|
+
return "image/jpeg";
|
|
54
|
+
if (b.subarray(0, 4).toString("latin1") === "RIFF" &&
|
|
55
|
+
b.subarray(8, 12).toString("latin1") === "WEBP")
|
|
56
|
+
return "image/webp";
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
33
59
|
const LOCK_WAIT_MS = 2_000;
|
|
34
60
|
const LOCK_STALE_MS = 10_000;
|
|
35
61
|
/** Opens (or joins) a session. Never throws: on failure it returns a disabled session and logs why. */
|
|
@@ -57,7 +83,8 @@ export async function session(options) {
|
|
|
57
83
|
...(o.replay ? { replay: o.replay } : {}),
|
|
58
84
|
...(o.tenant ? { tenant: o.tenant } : {}),
|
|
59
85
|
...(o.authority ? { authority: o.authority } : {}),
|
|
60
|
-
...(o.
|
|
86
|
+
...(o.environment ? { environment: o.environment } : {}),
|
|
87
|
+
...(o.preflight ? { preflight: await withEgress(o.preflight) } : {}),
|
|
61
88
|
...(o.drill ? { drill: o.drill } : {}),
|
|
62
89
|
sdk: true, // this SDK will report the session's model calls (L2.3.2)
|
|
63
90
|
});
|
|
@@ -89,6 +116,10 @@ export class BlackboxSession {
|
|
|
89
116
|
timers = [];
|
|
90
117
|
closed = false;
|
|
91
118
|
landings = 0;
|
|
119
|
+
/** spec/approvals.md §2: approved tools with arguments, until their call is reported. */
|
|
120
|
+
approved = new Map();
|
|
121
|
+
/** spec/agents.md §4: the memory chain's tip. */
|
|
122
|
+
memoryLast = null;
|
|
92
123
|
/** A session that records nothing. `reason`, when opening failed, goes to `stats.lastError` (audit K1). */
|
|
93
124
|
static disabled(options, reason) {
|
|
94
125
|
const s = new BlackboxSession(options, "", "", false);
|
|
@@ -170,6 +201,14 @@ export class BlackboxSession {
|
|
|
170
201
|
...(eventId === undefined ? {} : { event_id: eventId }),
|
|
171
202
|
ts: new Date().toISOString(),
|
|
172
203
|
...(kept === undefined ? {} : { data: kept }),
|
|
204
|
+
...(options.attachment
|
|
205
|
+
? {
|
|
206
|
+
attachment: {
|
|
207
|
+
content_type: options.attachment.contentType,
|
|
208
|
+
data: Buffer.from(options.attachment.bytes).toString("base64"),
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
: {}),
|
|
173
212
|
});
|
|
174
213
|
appendFileSync(this.spool, `${line}\n`);
|
|
175
214
|
this.nextSeq++;
|
|
@@ -186,8 +225,82 @@ export class BlackboxSession {
|
|
|
186
225
|
step(name, data) {
|
|
187
226
|
this.event("step", name, data);
|
|
188
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* spec/gov.md §2: a decision about a person, under the rulebook `manifestHash`, the person named
|
|
230
|
+
* only by their subject tag. Returns the factors' nonce (keep it to disclose them in a dispute).
|
|
231
|
+
* Throws on a decision that can't be lawful evidence: no rulebook, a bad kind, no tag.
|
|
232
|
+
*/
|
|
233
|
+
decision(kind, o) {
|
|
234
|
+
if (!/^sha256:[0-9a-f]{64}$/.test(o.subjectTag))
|
|
235
|
+
throw new Error("subjectTag must be a subject tag (subjectTag(did, authority))");
|
|
236
|
+
const { payload, nonce } = decisionPayload({
|
|
237
|
+
kind,
|
|
238
|
+
manifestHash: o.manifestHash,
|
|
239
|
+
outcome: o.outcome,
|
|
240
|
+
ts: new Date().toISOString(),
|
|
241
|
+
...(o.factors !== undefined ? { factors: o.factors } : {}),
|
|
242
|
+
...(o.appealBy !== undefined ? { appealBy: o.appealBy } : {}),
|
|
243
|
+
});
|
|
244
|
+
this.event("decision", kind, { decision: payload, subject_tag: o.subjectTag });
|
|
245
|
+
return { nonce };
|
|
246
|
+
}
|
|
247
|
+
/** spec/health.md §2: who accessed a patient's record (an episode tag, never the seed), and why. */
|
|
248
|
+
healthAccess(o) {
|
|
249
|
+
const tag = checkEpisodeTag(o.episodeTag);
|
|
250
|
+
const { payload, nonce } = accessPayload({ ...o, ts: new Date().toISOString() });
|
|
251
|
+
this.event("health.access", "access", { payload, episode_tag: tag });
|
|
252
|
+
return { nonce };
|
|
253
|
+
}
|
|
254
|
+
/** spec/health.md §2: a model's recommendation under a protocol. `hash` is what the clinician signs. */
|
|
255
|
+
recommendation(o) {
|
|
256
|
+
const tag = checkEpisodeTag(o.episodeTag);
|
|
257
|
+
const { payload, nonce, hash } = recommendationPayload({ ...o, ts: new Date().toISOString() });
|
|
258
|
+
this.event("health.recommendation", "recommendation", { payload, episode_tag: tag });
|
|
259
|
+
return { nonce, hash };
|
|
260
|
+
}
|
|
261
|
+
/** spec/health.md §2: a clinician's signed confirmation (clinicianConfirmation). Throws on a bad signature. */
|
|
262
|
+
clinicianConfirmation(episodeTag, payload) {
|
|
263
|
+
this.signedHealth(episodeTag, payload, "confirmation");
|
|
264
|
+
}
|
|
265
|
+
/** spec/health.md §2: an emergency access, signed by the clinician (breakGlass). Loud on purpose. */
|
|
266
|
+
breakGlass(episodeTag, payload) {
|
|
267
|
+
this.signedHealth(episodeTag, payload, "break_glass");
|
|
268
|
+
}
|
|
269
|
+
signedHealth(episodeTag, payload, kind) {
|
|
270
|
+
const tag = checkEpisodeTag(episodeTag);
|
|
271
|
+
if (payload.kind !== kind || !verifyHealthSignature(payload, tag))
|
|
272
|
+
throw new Error(`not a ${kind} signed by the clinician it names, for this episode`);
|
|
273
|
+
this.event(`health.${kind}`, kind, { payload, episode_tag: tag });
|
|
274
|
+
}
|
|
275
|
+
/** spec/finance.md §2: a payment, exact (minor units), with its settlement reference. Throws on a bad amount. */
|
|
276
|
+
payment(input) {
|
|
277
|
+
this.event("payment", input.rail, buildPayment(input));
|
|
278
|
+
}
|
|
279
|
+
/** spec/finance.md §3: a counterparty screening (screenCounterparty), before paying them. */
|
|
280
|
+
screening(result) {
|
|
281
|
+
this.event("kya.screening", result.did, screeningPayload(result, new Date().toISOString(), this.id));
|
|
282
|
+
}
|
|
283
|
+
/** spec/finance.md §4: a tax filing the agent prepared. It never files; a Tax Agent does. */
|
|
284
|
+
ftaPrepared(o) {
|
|
285
|
+
this.event("fta.prepared", o.filingType, ftaPayload({ ...o, action: "prepared", by: o.by ?? this.id }));
|
|
286
|
+
}
|
|
287
|
+
/** spec/finance.md §4: the prepared filing handed to a licensed Tax Agent. */
|
|
288
|
+
ftaHandoff(o) {
|
|
289
|
+
this.event("fta.handoff", o.filingType, ftaPayload({ ...o, action: "handoff", by: o.by ?? this.id }));
|
|
290
|
+
}
|
|
189
291
|
toolCall(name, args) {
|
|
190
|
-
|
|
292
|
+
// spec/approvals.md §2: the first call after an approval names it, with what it ran
|
|
293
|
+
const approvalId = this.approved.get(name);
|
|
294
|
+
if (approvalId !== undefined)
|
|
295
|
+
this.approved.delete(name);
|
|
296
|
+
this.event("tool.call", name, args === undefined && approvalId === undefined
|
|
297
|
+
? undefined
|
|
298
|
+
: {
|
|
299
|
+
...(args !== undefined ? { args } : {}),
|
|
300
|
+
...(approvalId !== undefined
|
|
301
|
+
? { approval_id: approvalId, args_sha256: argsHash(args ?? null) }
|
|
302
|
+
: {}),
|
|
303
|
+
});
|
|
191
304
|
}
|
|
192
305
|
toolResult(name, result) {
|
|
193
306
|
this.event("tool.result", name, result);
|
|
@@ -255,6 +368,30 @@ export class BlackboxSession {
|
|
|
255
368
|
contextChange(change) {
|
|
256
369
|
this.event("context.change", change.kind, { ...change });
|
|
257
370
|
}
|
|
371
|
+
/** spec/findings.md §11 (H3): pin the documents the agent read, by hash; the content never leaves.
|
|
372
|
+
* Each doc is `{id, uri?, version?}` with its `content` (hashed here) or its `sha256`. */
|
|
373
|
+
retrieved(docs) {
|
|
374
|
+
const out = [];
|
|
375
|
+
for (const d of docs.slice(0, 100)) {
|
|
376
|
+
if (typeof d?.id !== "string" || d.id.length < 1)
|
|
377
|
+
continue;
|
|
378
|
+
const bytes = typeof d.content === "string" ? Buffer.from(d.content) : d.content;
|
|
379
|
+
const sha256 = bytes
|
|
380
|
+
? `sha256:${createHash("sha256").update(bytes).digest("hex")}`
|
|
381
|
+
: d.sha256;
|
|
382
|
+
if (typeof sha256 !== "string" || !/^sha256:[0-9a-f]{64}$/.test(sha256))
|
|
383
|
+
continue;
|
|
384
|
+
out.push({
|
|
385
|
+
id: d.id.slice(0, 256),
|
|
386
|
+
...(typeof d.uri === "string" ? { uri: d.uri.slice(0, 2048) } : {}),
|
|
387
|
+
...(typeof d.version === "string" ? { version: d.version.slice(0, 128) } : {}),
|
|
388
|
+
sha256,
|
|
389
|
+
...(bytes ? { bytes: bytes.length } : {}),
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
if (out.length)
|
|
393
|
+
this.event("retrieved", undefined, { docs: out });
|
|
394
|
+
}
|
|
258
395
|
/** Stage 2 R3: the result of one of the customer's own checks (tests, a schema, a policy, a
|
|
259
396
|
* partial goal), mid-run or at the end. A pass then a fail is VERIFY_REGRESSION; a success whose last
|
|
260
397
|
* check failed is FALSE_SUCCESS (spec/findings.md §6). */
|
|
@@ -291,9 +428,47 @@ export class BlackboxSession {
|
|
|
291
428
|
this.event("attestation", argv[0], { ...result });
|
|
292
429
|
return result;
|
|
293
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* spec/sdk.md §1.1: a computer-use screenshot (PNG, JPEG or WebP, at most 2 MB), recorded as the
|
|
433
|
+
* body of a `screen` event, so its hash is in the chain. `action` says what the agent did or is
|
|
434
|
+
* about to do (≤ 500 characters). Mask what mustn't be kept before calling: the image isn't
|
|
435
|
+
* redacted. One that's too big or not an image is dropped, and the event says why.
|
|
436
|
+
*/
|
|
437
|
+
screen(image, options = {}) {
|
|
438
|
+
const data = {
|
|
439
|
+
...(options.action ? { action: options.action.slice(0, 500) } : {}),
|
|
440
|
+
...(Number.isSafeInteger(options.width) ? { width: options.width } : {}),
|
|
441
|
+
...(Number.isSafeInteger(options.height) ? { height: options.height } : {}),
|
|
442
|
+
};
|
|
443
|
+
const type = imageType(image);
|
|
444
|
+
if (!type || image.length > MAX_ATTACHMENT) {
|
|
445
|
+
this.event("screen", undefined, {
|
|
446
|
+
...data,
|
|
447
|
+
attachment_dropped: type
|
|
448
|
+
? `${image.length} bytes, over 2 MB`
|
|
449
|
+
: "not a PNG, JPEG or WebP image",
|
|
450
|
+
});
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
this.event("screen", undefined, data, { attachment: { contentType: type, bytes: image } });
|
|
454
|
+
}
|
|
294
455
|
/** spec/agents.md §1: the agent's memory store wrote, revoked or returned memories. */
|
|
295
|
-
|
|
296
|
-
|
|
456
|
+
/** spec/agents.md §4: with `content`, the write also carries Zanii's memory entry (a salted
|
|
457
|
+
* commitment, chained after `prev`, else this session's last entry). Keep the salt it returns. */
|
|
458
|
+
memoryWrite(memoryId, summary, opts) {
|
|
459
|
+
const data = { memory_id: memoryId, ...(summary ? { summary } : {}) };
|
|
460
|
+
let made;
|
|
461
|
+
if (opts) {
|
|
462
|
+
made = memoryEntry({
|
|
463
|
+
...opts,
|
|
464
|
+
ts: new Date().toISOString(),
|
|
465
|
+
prev: opts.prev ?? this.memoryLast,
|
|
466
|
+
});
|
|
467
|
+
this.memoryLast = made.payload;
|
|
468
|
+
data.entry = made.payload;
|
|
469
|
+
}
|
|
470
|
+
this.event("memory.write", undefined, data);
|
|
471
|
+
return made && { entry: made.payload, salt: made.salt };
|
|
297
472
|
}
|
|
298
473
|
memoryRevoke(memoryId, reason) {
|
|
299
474
|
this.event("memory.revoke", undefined, { memory_id: memoryId, ...(reason ? { reason } : {}) });
|
|
@@ -350,17 +525,20 @@ export class BlackboxSession {
|
|
|
350
525
|
* and waits (polling) for the answer. Never throws. Only `"approved"` means go ahead; anything
|
|
351
526
|
* else (`rejected`, `timeout`, or `error` when the gateway can't be asked) means don't. */
|
|
352
527
|
async requestApproval(tool, options = {}) {
|
|
353
|
-
|
|
528
|
+
const { status, id } = await this.ask("approvals", { tool, ...(options.args ? { args: options.args } : {}) }, options);
|
|
529
|
+
if (status === "approved" && options.args && id)
|
|
530
|
+
this.approved.set(tool, id);
|
|
531
|
+
return status;
|
|
354
532
|
}
|
|
355
533
|
/** N2 (idea C2): asks a person to let this session use a gateway tool its policy denies (the
|
|
356
534
|
* denial's `requires.tool`, e.g. `mcp__files__delete`). A yes is a standing grant for the
|
|
357
535
|
* session: retry the call. Answers like `requestApproval`. Never throws. */
|
|
358
536
|
async requestPermission(tool, options = {}) {
|
|
359
|
-
return this.ask("permissions", { tool }, options);
|
|
537
|
+
return (await this.ask("permissions", { tool }, options)).status;
|
|
360
538
|
}
|
|
361
539
|
async ask(route, body, options) {
|
|
362
540
|
if (!this.enabled)
|
|
363
|
-
return "error";
|
|
541
|
+
return { status: "error" };
|
|
364
542
|
try {
|
|
365
543
|
const r = await post(this.options.url, `/v1/sessions/${this.id}/${route}`, this.token, {
|
|
366
544
|
...body,
|
|
@@ -369,21 +547,21 @@ export class BlackboxSession {
|
|
|
369
547
|
const id = r.json.approval_id;
|
|
370
548
|
if (r.status !== 201 || typeof id !== "string") {
|
|
371
549
|
this.fail("rejected", `asking for an approval: the gateway answered ${r.status}`);
|
|
372
|
-
return "error";
|
|
550
|
+
return { status: "error" };
|
|
373
551
|
}
|
|
374
552
|
const deadline = Date.now() + (options.timeoutMs ?? 310_000);
|
|
375
553
|
while (Date.now() < deadline) {
|
|
376
554
|
const s = await send("GET", this.options.url, `/v1/sessions/${this.id}/approvals/${id}`, this.token);
|
|
377
555
|
const status = s.json.status;
|
|
378
556
|
if (status === "approved" || status === "rejected" || status === "timeout")
|
|
379
|
-
return status;
|
|
557
|
+
return { status, id };
|
|
380
558
|
await sleep(options.pollMs ?? 1_000);
|
|
381
559
|
}
|
|
382
|
-
return "timeout";
|
|
560
|
+
return { status: "timeout", id };
|
|
383
561
|
}
|
|
384
562
|
catch (error) {
|
|
385
563
|
this.fail("network", `asking for an approval: ${message(error)}`);
|
|
386
|
-
return "error";
|
|
564
|
+
return { status: "error" };
|
|
387
565
|
}
|
|
388
566
|
}
|
|
389
567
|
/** Audit S8: `await using s = await session(…)` closes the session when the scope ends. */
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
type Obj = Record<string, unknown>;
|
|
2
|
+
type Commitments = {
|
|
3
|
+
min_actions?: number;
|
|
4
|
+
max_actions?: number;
|
|
5
|
+
max_gap_ms?: number;
|
|
6
|
+
};
|
|
7
|
+
/** Zanii's SLA terms. `commitments` holds at least one of min_actions, max_actions, max_gap_ms. */
|
|
8
|
+
export declare function buildSlaBody(o: {
|
|
9
|
+
provider: string;
|
|
10
|
+
client: string;
|
|
11
|
+
scope: string;
|
|
12
|
+
window: {
|
|
13
|
+
start: string;
|
|
14
|
+
end: string;
|
|
15
|
+
};
|
|
16
|
+
commitments: Commitments;
|
|
17
|
+
price?: {
|
|
18
|
+
amount: string;
|
|
19
|
+
currency: string;
|
|
20
|
+
};
|
|
21
|
+
createdAt: string;
|
|
22
|
+
ref?: string;
|
|
23
|
+
}): Obj;
|
|
24
|
+
/** One party signs the terms. Collect the provider's and the client's. */
|
|
25
|
+
export declare const signSla: (body: Obj, did: string, privateKey: Uint8Array) => {
|
|
26
|
+
did: string;
|
|
27
|
+
sig: string;
|
|
28
|
+
};
|
|
29
|
+
export declare const assembleSla: (body: Obj, signatures: Array<{
|
|
30
|
+
did: string;
|
|
31
|
+
sig: string;
|
|
32
|
+
}>) => {
|
|
33
|
+
body: Obj;
|
|
34
|
+
signatures: {
|
|
35
|
+
did: string;
|
|
36
|
+
sig: string;
|
|
37
|
+
}[];
|
|
38
|
+
};
|
|
39
|
+
export declare const slaHash: (sla: Obj) => string;
|
|
40
|
+
/** Zanii's check: sane terms, and BOTH provider and client signed exactly these. */
|
|
41
|
+
export declare function verifySla(sla: unknown): {
|
|
42
|
+
ok: boolean;
|
|
43
|
+
reasons: string[];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Zanii's verdict over the provider's consecutive receipt chain for the window. Breaches:
|
|
47
|
+
* INVALID_RECEIPT, INCOMPLETE_RECORD (a seam: something recorded was left out), TOO_FEW_ACTIONS,
|
|
48
|
+
* TOO_MANY_ACTIONS, MAX_GAP_EXCEEDED. `asOf` (default the window's end) caps the trailing gap.
|
|
49
|
+
*/
|
|
50
|
+
export declare function assessCompliance(sla: Obj, receipts: readonly unknown[], asOf?: string): {
|
|
51
|
+
ok: boolean;
|
|
52
|
+
breaches: {
|
|
53
|
+
kind: string;
|
|
54
|
+
detail: string;
|
|
55
|
+
}[];
|
|
56
|
+
metrics: {
|
|
57
|
+
actions: number;
|
|
58
|
+
max_gap_ms: number | null;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
export {};
|