@voidly/session 1.1.0 → 1.2.1

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
@@ -21,13 +21,13 @@ is not in this package.
21
21
  ## Install
22
22
 
23
23
  ```bash
24
- npm install --ignore-scripts --save-exact @voidly/session@1.1.0
24
+ npm install --ignore-scripts --save-exact @voidly/session@1.2.1
25
25
  ```
26
26
 
27
27
  The package name is public. Check the exact version's registry metadata and its
28
28
  linked public source before installation; a source checkout does not prove that
29
- version has been published. If 1.1.0 is not released yet, use a locally reviewed
30
- tarball for development rather than substituting another version.
29
+ version has been published. If the requested version is unavailable, stop rather
30
+ than substituting another version. Locally reviewed tarballs are for development.
31
31
 
32
32
  ESM only. Node ≥ 18 or any runtime with WebCrypto, `fetch` and `TextEncoder`.
33
33
  Two exact runtime dependencies: `tweetnacl@1.0.3` and `tweetnacl-util@0.15.1`.
@@ -38,8 +38,8 @@ explicitly execute after installation.
38
38
  > **Building from a checkout instead?** Pack it yourself:
39
39
  >
40
40
  > ```bash
41
- > npm run build && npm pack # → voidly-session-1.1.0.tgz
42
- > npm install --ignore-scripts --save-exact /path/to/voidly-session-1.1.0.tgz
41
+ > npm run build && npm pack # → voidly-session-1.2.1.tgz
42
+ > npm install --ignore-scripts --save-exact /path/to/voidly-session-1.2.1.tgz
43
43
  > ```
44
44
  >
45
45
  > `npm run gate` inspects the actual packed bytes. A local build is not registry
@@ -48,7 +48,49 @@ explicitly execute after installation.
48
48
 
49
49
  ---
50
50
 
51
- ## Start without a wallet: exercise the installed SDK
51
+ ## One instruction: complete and save a private proof
52
+
53
+ Open [Voidpay Proofs](https://voidly.ai/pay/proofs), authorize one private result,
54
+ and copy its agent instruction. After reviewing and installing this exact package,
55
+ the agent runs one command with the small handoff supplied through standard input:
56
+
57
+ ```bash
58
+ node node_modules/@voidly/session/dist/proofsCli.mjs complete
59
+ ```
60
+
61
+ The command performs the offline self-test, obtains fresh public exercise data,
62
+ checks the provider, submits the result, verifies its signed receipt and generates
63
+ the exact saved event's artwork locally. It creates new `proof-artwork.svg` and
64
+ `proof-receipt.json` files inside a unique private directory in the current
65
+ workspace. Its compact JSON output includes absolute paths; if export fails it
66
+ includes the SVG itself, so the agent can display and attach the art. A failed local export does not undo a saved
67
+ proof: use the returned SVG instead of creating a replacement run. PNG conversion
68
+ is optional with an already available local converter, never an online service.
69
+
70
+ The handoff contains a **limited completion credential**. Anyone holding it can
71
+ finish that one approved proof and retrieve its result during the allowed window.
72
+ It cannot list, manage, publish or delete your collection, access a wallet, or pay.
73
+ Supply it only through a real subprocess standard-input channel—not arguments,
74
+ environment variables, URLs, npm commands, shell history or a temporary input file.
75
+ The CLI sends it only to three fixed first-party completion endpoints. Provider
76
+ and issuer requests receive no credential. It does not read ambient credentials.
77
+
78
+ The browser may close after copying succeeds. Authorization lasts one hour; first
79
+ activation must happen within 50 minutes to leave the full ten-minute exercise
80
+ window. Successful results can be recovered with the same handoff for one hour
81
+ after completion. Retry the **same instruction** after an uncertain response.
82
+ At most three server-verifier attempts are allowed; expiry, cancellation or
83
+ exhaustion requires a fresh approval on the website. The saved record remains
84
+ available in the owner's collection for its stated retention period.
85
+
86
+ Nothing is published by this command. Return to the website to share or join the
87
+ board explicitly. A previously published record can be recovered without changing
88
+ its visibility. This is participation, not remote proof of package installation,
89
+ agent identity, a unique person, payment, earnings or impact. The installation may
90
+ be temporary in a hosted agent workspace. Respect the agent's execution policy;
91
+ some environments cannot install packages, supply safe stdin or display SVG files.
92
+
93
+ ## Credential-free SDK exercises (unchanged)
52
94
 
53
95
  The website flow is released separately from this package. Follow the current
54
96
  page's availability state; installing the SDK alone does not enable saving.
package/dist/proofs.mjs CHANGED
@@ -328,20 +328,29 @@ async function verifyPublicExercise(exercise, index, manifest, nowMs = Date.now(
328
328
  return { schema: PUBLIC_RESULT_SCHEMA, task: parsed.task, challenge_id: parsed.challenge_id, challenge: parsed.challenge, manifest_digest_sha256, challenge_response_sha256, checks: { provider_pair_listed: true, manifest_verified: true, wrong_pin_refused: true } };
329
329
  }
330
330
  async function readFixedJson(url, maxBytes, fetchImpl) {
331
- let controller = new AbortController(), timer, reader, deadline = new Promise((_, reject) => {
331
+ let controller = new AbortController(), timer, reader, response, deadlineAt = performance.now() + 8e3, deadline = new Promise((_, reject) => {
332
332
  timer = setTimeout(() => {
333
333
  controller.abort(), reject(new PublicExerciseError("upstream_unavailable"));
334
334
  }, 8e3);
335
335
  });
336
336
  try {
337
- let response = await Promise.race([fetchImpl(url, { method: "GET", headers: { accept: "application/json" }, redirect: "error", credentials: "omit", cache: "no-store", referrerPolicy: "no-referrer", signal: controller.signal }), deadline]);
338
- requireCondition(!response.redirected && (response.url === "" || response.url === url), "upstream_redirect"), requireCondition(response.ok && response.body, "upstream_unavailable"), requireCondition(/^application\/json(?:\s*;|$)/i.test(response.headers.get("content-type") ?? ""), "upstream_not_json");
337
+ let fetched = Promise.resolve(fetchImpl(url, { method: "GET", headers: { accept: "application/json" }, redirect: "error", credentials: "omit", cache: "no-store", referrerPolicy: "no-referrer", signal: controller.signal })).then((value) => {
338
+ if (controller.signal.aborted) throw value.body && !value.body.locked && value.body.cancel().catch(() => {
339
+ }), new PublicExerciseError("upstream_unavailable");
340
+ return value;
341
+ });
342
+ response = await Promise.race([fetched, deadline]), requireCondition(performance.now() < deadlineAt, "upstream_unavailable"), requireCondition(!response.redirected && (response.url === "" || response.url === url), "upstream_redirect"), requireCondition(response.ok && response.body, "upstream_unavailable"), requireCondition(/^application\/json(?:\s*;|$)/i.test(response.headers.get("content-type") ?? ""), "upstream_not_json");
339
343
  let contentLength = response.headers.get("content-length");
340
344
  requireCondition(contentLength === null || /^\d+$/.test(contentLength) && Number(contentLength) <= maxBytes, "upstream_too_large"), reader = response.body.getReader();
341
- let chunks = [], size = 0;
345
+ let chunks = [], size = 0, reads = 0, emptyReads = 0;
342
346
  for (; ; ) {
347
+ requireCondition(performance.now() < deadlineAt, "upstream_unavailable"), requireCondition(++reads <= 2048, "upstream_too_large");
343
348
  let part = await Promise.race([reader.read(), deadline]);
344
- if (part.done) break;
349
+ if (requireCondition(performance.now() < deadlineAt, "upstream_unavailable"), part.done) break;
350
+ if (part.value.byteLength === 0) {
351
+ requireCondition(++emptyReads <= 128, "upstream_too_large");
352
+ continue;
353
+ }
345
354
  size += part.value.byteLength, requireCondition(size <= maxBytes, "upstream_too_large"), chunks.push(part.value);
346
355
  }
347
356
  let bytes = new Uint8Array(size), offset = 0;
@@ -354,8 +363,9 @@ async function readFixedJson(url, maxBytes, fetchImpl) {
354
363
  } catch (error) {
355
364
  throw error instanceof PublicExerciseError ? error : new PublicExerciseError("upstream_unavailable");
356
365
  } finally {
357
- clearTimeout(timer), controller.abort(), reader && (reader.cancel().catch(() => {
358
- }), reader.releaseLock());
366
+ clearTimeout(timer), controller.abort(), reader ? (reader.cancel().catch(() => {
367
+ }), reader.releaseLock()) : response?.body && !response.body.locked && response.body.cancel().catch(() => {
368
+ });
359
369
  }
360
370
  }
361
371
  async function runPublicExercise(text, options = {}) {
@@ -0,0 +1,111 @@
1
+ // @voidly/session — GENERATED by scripts/build-types.mjs. Do not edit.
2
+ //
3
+ // The transitive closure of what src/proofsAuto.ts exports, flattened into one
4
+ // module. Server declarations are absent because nothing here reaches them.
5
+
6
+ export declare const AUTOMATIC_BASE = "https://api.voidly.ai/v4/proofs/collections";
7
+ export declare const AUTOMATIC_BOUNDARY: Readonly<{
8
+ decision: "STOP";
9
+ wallet_required: false;
10
+ payment_action_allowed: false;
11
+ authorization_created: false;
12
+ transaction_broadcast: false;
13
+ settlement_observed: false;
14
+ reward_offered: false;
15
+ reward_amount: "0";
16
+ reward_asset: null;
17
+ customer_identity_verified: false;
18
+ commercial_transaction: false;
19
+ credited_to_traction_metrics: false;
20
+ unique_person_verified: false;
21
+ impact_verified: false;
22
+ }>;
23
+ export declare const AUTOMATIC_HANDOFF_MAX_BYTES = 1024;
24
+ export declare const AUTOMATIC_HANDOFF_SCHEMA: "voidpay.proof-collection.agent-handoff/v4";
25
+ export declare const AUTOMATIC_ISSUER = "https://api.voidly.ai/v2/proofs/collections/issuer";
26
+ export declare const AUTOMATIC_RECEIPT_DOMAIN = "voidpay-proof-collection-receipt/v4\n";
27
+ export declare const AUTOMATIC_RUN_BINDING_DOMAIN = "voidpay-proof-collection-run-binding/v4\n";
28
+ export declare const AUTOMATIC_SCOPE = "public-provider-discovery-and-owner-preauthorized-private-save-only";
29
+ export declare const AUTOMATIC_TASK: Readonly<{
30
+ id: "sessions-auto-save-01";
31
+ version: 1;
32
+ acceptance_rule_sha256: "b58e042e088740892856e96f4c8eee079bd94819c518e8e486ef250184ffb230";
33
+ }>;
34
+ export type AutomaticHandoff = {
35
+ schema: typeof AUTOMATIC_HANDOFF_SCHEMA;
36
+ run_id: string;
37
+ completion_capability: string;
38
+ };
39
+ export declare class AutomaticProofError extends Error {
40
+ readonly code: AutomaticProofErrorCode;
41
+ constructor(code: AutomaticProofErrorCode);
42
+ }
43
+ export type AutomaticProofErrorCode = "handoff_invalid" | "self_test_failed" | "response_invalid" | "issuer_invalid" | "receipt_invalid" | "completion_unavailable" | "completion_uncertain" | "permission_refused" | "run_expired_or_revoked" | "rate_limited" | "attempts_exhausted" | "provider_check_failed";
44
+ export type AutomaticReceipt = {
45
+ statement: {
46
+ schema: "voidpay.proof-collection.receipt/v4";
47
+ issuer: string;
48
+ key_id: string;
49
+ event_id: string;
50
+ run_binding_sha256: string;
51
+ exercise_binding_sha256: string;
52
+ artwork: {
53
+ recipe: "contours-v1";
54
+ seed: string;
55
+ };
56
+ task: typeof AUTOMATIC_TASK;
57
+ computation_task: typeof SESSIONS_PROOFS_TASK;
58
+ checked_at: string;
59
+ completed_at: string;
60
+ outcome: "PASS";
61
+ checks: {
62
+ server: Record<typeof SERVER_CHECKS[number], true>;
63
+ claimant: Record<typeof CLAIMANT_CHECKS[number], true>;
64
+ };
65
+ provider: {
66
+ provider_did: string;
67
+ manifest_url: string;
68
+ };
69
+ scope: typeof AUTOMATIC_SCOPE;
70
+ boundary: typeof AUTOMATIC_BOUNDARY;
71
+ };
72
+ signature_hex: string;
73
+ };
74
+ declare const CLAIMANT_CHECKS: readonly [
75
+ "manifest_digest_matches",
76
+ "challenge_response_matches"
77
+ ];
78
+ export declare function completeAutomaticProof(text: string, options?: {
79
+ readonly fetchImpl?: typeof fetch;
80
+ readonly nowMs?: number;
81
+ }): Promise<VerifiedAutomaticProof>;
82
+ export declare function parseAutomaticHandoff(text: string): AutomaticHandoff;
83
+ declare const SERVER_CHECKS: readonly [
84
+ "owner_preauthorized",
85
+ "challenge_unexpired",
86
+ "provider_pair_listed",
87
+ "manifest_verified",
88
+ "wrong_pin_refused"
89
+ ];
90
+ declare const SESSIONS_PROOFS_TASK: Readonly<{
91
+ id: "sessions-public-check-01";
92
+ version: 1;
93
+ acceptance_rule_sha256: "1be9d67c2f8caffa45fd985b7454371838c29e4fadbd8dadc2a4e6663b0f7bc5";
94
+ }>;
95
+ export type VerifiedAutomaticProof = {
96
+ schema: "voidly.session.automatic-proof/v1";
97
+ ok: true;
98
+ saved_proof: true;
99
+ event_id: string;
100
+ visibility: "private" | "public";
101
+ already_completed: boolean;
102
+ receipt: AutomaticReceipt;
103
+ artwork: {
104
+ recipe: "contours-v1";
105
+ seed: string;
106
+ svg: string;
107
+ sha256: string;
108
+ };
109
+ return_url: "https://voidly.ai/pay/proofs";
110
+ payment_boundary: "STOP";
111
+ };
@@ -0,0 +1,554 @@
1
+ import nacl3 from "tweetnacl";
2
+ import nacl from "tweetnacl";
3
+ import __naclUtil0 from "tweetnacl-util";
4
+ const { decodeBase64 } = __naclUtil0;
5
+ var MAX_WINDOW_MS = 3600 * 1e3, MAX_CLOCK_SKEW_MS = 30 * 1e3;
6
+ function canonicalize(value) {
7
+ if (value == null) return "null";
8
+ if (typeof value == "boolean") return value ? "true" : "false";
9
+ if (typeof value == "number") {
10
+ if (!Number.isFinite(value) || !Number.isInteger(value)) throw new Error("canonicalize: only finite integers supported");
11
+ return value.toString(10);
12
+ }
13
+ if (typeof value == "bigint") return value.toString(10);
14
+ if (typeof value == "string") return JSON.stringify(value);
15
+ if (Array.isArray(value)) return "[" + value.map(canonicalize).join(",") + "]";
16
+ if (typeof value == "object") {
17
+ let obj = value;
18
+ return "{" + Object.keys(obj).filter((k) => obj[k] !== null && obj[k] !== void 0).sort().map((k) => JSON.stringify(k) + ":" + canonicalize(obj[k])).join(",") + "}";
19
+ }
20
+ throw new Error(`canonicalize: unsupported type ${typeof value}`);
21
+ }
22
+ function canonicalBytes(value) {
23
+ return new TextEncoder().encode(canonicalize(value));
24
+ }
25
+ var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
26
+ function toBase58(bytes) {
27
+ let result = "", num = BigInt("0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join(""));
28
+ for (; num > 0n; ) {
29
+ let remainder = num % 58n;
30
+ num = num / 58n, result = BASE58_ALPHABET[Number(remainder)] + result;
31
+ }
32
+ for (let byte of bytes) if (byte === 0) result = "1" + result;
33
+ else break;
34
+ return result || "1";
35
+ }
36
+ function deriveDidFromSigningKey(signingPublicKey) {
37
+ return `did:voidly:${toBase58(signingPublicKey.slice(0, 16))}`;
38
+ }
39
+ async function sha256Bytes(bytes) {
40
+ let buf = await crypto.subtle.digest("SHA-256", bytes);
41
+ return new Uint8Array(buf);
42
+ }
43
+ async function sha256Hex(bytes) {
44
+ let digest = await sha256Bytes(bytes);
45
+ return Array.from(digest).map((b) => b.toString(16).padStart(2, "0")).join("");
46
+ }
47
+ var FRAME_OFFER_HASH_BYTES = 32, FRAME_BODY_NONCE_BYTES = 24, FRAME_HEADER_LENGTH = FRAME_OFFER_HASH_BYTES + FRAME_BODY_NONCE_BYTES + 4, MIN_FRAME_BUCKET = 512;
48
+ var SECRETBOX_TAG_BYTES = 16, CAPSULE_SALT_BYTES = 32;
49
+ function base64Length(bytes) {
50
+ return 4 * Math.ceil(bytes / 3);
51
+ }
52
+ var SESSION_MAX_BODY_BYTES = 32 * 1024, SESSION_WORST_CASE_ENVELOPE_BYTES = 4972, MAX_FRAME_BUCKET_BYTES = (() => {
53
+ let best = 0;
54
+ for (let bucket = MIN_FRAME_BUCKET; bucket <= SESSION_MAX_BODY_BYTES; bucket *= 2) base64Length(bucket + SECRETBOX_TAG_BYTES) + SESSION_WORST_CASE_ENVELOPE_BYTES <= SESSION_MAX_BODY_BYTES && (best = bucket);
55
+ if (best === 0) throw new Error("wireBudget: the transport ceiling cannot carry the minimum frame bucket");
56
+ return best;
57
+ })(), MAX_CAPSULE_BODY_BYTES = MAX_FRAME_BUCKET_BYTES + SECRETBOX_TAG_BYTES, MAX_CAPSULE_BODY_BASE64_LENGTH = base64Length(MAX_CAPSULE_BODY_BYTES), MAX_SEALED_PAYLOAD_BYTES = MAX_FRAME_BUCKET_BYTES - FRAME_HEADER_LENGTH, SESSION_EVIDENCE_HEADROOM_BYTES = SESSION_MAX_BODY_BYTES - MAX_CAPSULE_BODY_BASE64_LENGTH - SESSION_WORST_CASE_ENVELOPE_BYTES;
58
+ var TASK_BRIEF_SCHEMA = "voidly-task-brief/v1";
59
+ var TASK_RESULT_SCHEMA = "voidly-task-result/v1";
60
+ var MAX_OFFER_TTL_MS = 1440 * 60 * 1e3, MAX_GRANT_TTL_MS = 1440 * 60 * 1e3;
61
+ var SESSION_RAIL_MIN_CONFIRMATIONS = 12, SESSION_RAIL_BLOCK_TIME_MS = 2e3, MIN_GRANT_TTL_MS = SESSION_RAIL_MIN_CONFIRMATIONS * SESSION_RAIL_BLOCK_TIME_MS + MAX_CLOCK_SKEW_MS, MAX_SERVICE_REF_LENGTH = 128;
62
+ var BRIEF_SALT_BASE64_LENGTH = base64Length(CAPSULE_SALT_BYTES), canonicalPayloadOverhead = (schema, field) => canonicalBytes({ schema, [field]: "", salt_base64: "A".repeat(BRIEF_SALT_BASE64_LENGTH) }).length, MAX_BRIEF_LENGTH = MAX_SEALED_PAYLOAD_BYTES - Math.max(canonicalPayloadOverhead(TASK_BRIEF_SCHEMA, "brief"), canonicalPayloadOverhead(TASK_RESULT_SCHEMA, "result"));
63
+ var MAX_RECOVERY_TTL_MS = 10080 * 60 * 1e3;
64
+ var DID_RE = /^did:voidly:[A-Za-z0-9._-]{1,64}$/;
65
+ function hasOnlyKeys(raw, allowed) {
66
+ for (let k of Object.keys(raw)) if (!allowed.includes(k)) return false;
67
+ return true;
68
+ }
69
+ function isBase64Key32(value, decode) {
70
+ if (typeof value != "string" || value.length === 0) return false;
71
+ try {
72
+ return decode(value).length === 32;
73
+ } catch {
74
+ return false;
75
+ }
76
+ }
77
+ var CAIP2_RE = /^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$/, CAIP10_RE = /^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}:[-.%a-zA-Z0-9]{1,128}$/, CAIP19_RE = /^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}\/[-a-z0-9]{3,8}:[-.%a-zA-Z0-9]{1,128}(\/[-.%a-zA-Z0-9]{1,78})?$/, POSITIVE_DECIMAL_RE = /^[1-9][0-9]{0,77}$/;
78
+ function isCaip2(s) {
79
+ return typeof s == "string" && CAIP2_RE.test(s);
80
+ }
81
+ function isCaip10(s) {
82
+ return typeof s == "string" && CAIP10_RE.test(s);
83
+ }
84
+ function isCaip19(s) {
85
+ return typeof s == "string" && CAIP19_RE.test(s);
86
+ }
87
+ function isPositiveDecimalString(s) {
88
+ return typeof s == "string" && POSITIVE_DECIMAL_RE.test(s);
89
+ }
90
+ import __naclUtil1 from "tweetnacl-util";
91
+ const { decodeBase64: decodeBase642 } = __naclUtil1;
92
+ var SESSION_HIRE_SCHEMA = "voidly-session-hire/v1";
93
+ var AUTHORIZATION_ENTRY_POINTS = ["transfer_with_authorization", "receive_with_authorization"];
94
+ import nacl2 from "tweetnacl";
95
+ import __naclUtil2 from "tweetnacl-util";
96
+ const { decodeBase64: decodeBase643 } = __naclUtil2;
97
+ var MINTED = /* @__PURE__ */ new WeakMap();
98
+ function containerCopy(value) {
99
+ if (value === null || typeof value != "object") return null;
100
+ if (Array.isArray(value)) {
101
+ let source = value, sink = [];
102
+ for (let key of Object.keys(source)) sink[key] = source[key];
103
+ return sink;
104
+ }
105
+ return { ...value };
106
+ }
107
+ function rebuildDocumentDeep(document) {
108
+ let top = containerCopy(document);
109
+ if (top === null) return document;
110
+ let made = /* @__PURE__ */ new Map();
111
+ made.set(document, top);
112
+ let pending2 = [top];
113
+ for (; pending2.length > 0; ) {
114
+ let frame = pending2.pop();
115
+ if (frame === void 0) break;
116
+ for (let key of Object.keys(frame)) {
117
+ let child = frame[key];
118
+ if (child === null || typeof child != "object") continue;
119
+ let already = made.get(child);
120
+ if (already !== void 0) {
121
+ frame[key] = already;
122
+ continue;
123
+ }
124
+ let copy = containerCopy(child);
125
+ copy !== null && (made.set(child, copy), frame[key] = copy, pending2.push(copy));
126
+ }
127
+ }
128
+ return top;
129
+ }
130
+ function freezeManifestDeep(m) {
131
+ let seen = /* @__PURE__ */ new Set(), pending2 = [m];
132
+ for (; pending2.length > 0; ) {
133
+ let node = pending2.pop();
134
+ if (node === null || typeof node != "object") continue;
135
+ let container = node;
136
+ if (!seen.has(container)) {
137
+ seen.add(container), Object.freeze(container);
138
+ for (let key of Reflect.ownKeys(container)) {
139
+ if (typeof key == "symbol") continue;
140
+ let d = Object.getOwnPropertyDescriptor(container, key);
141
+ d !== void 0 && "value" in d && pending2.push(d.value);
142
+ }
143
+ }
144
+ }
145
+ return m;
146
+ }
147
+ function mintVerifiedProvider(manifest) {
148
+ let document = freezeManifestDeep(rebuildDocumentDeep(manifest)), provider = Object.freeze({ manifest: document });
149
+ return MINTED.set(provider, document), provider;
150
+ }
151
+ var PROVIDER_MANIFEST_SCHEMA = "voidly.session.provider.manifest/v1", PROVIDER_MANIFEST_KEYS = ["schema", "provider_did", "signing_public_key_base64", "encryption_public_key_base64", "attestor_public_key_base64", "accept_url", "hire_message_schema", "worker_base_url", "hint_url", "relays", "grant_ttl_ms", "acceptance_ttl_ms", "services", "payment_buys", "notes", "signature_base64"];
152
+ function manifestSigningBytes(m) {
153
+ return canonicalBytes(m);
154
+ }
155
+ function isNonEmptyString(v, max = 2048) {
156
+ return typeof v == "string" && v.length > 0 && v.length <= max;
157
+ }
158
+ function isPositiveInt(v) {
159
+ return typeof v == "number" && Number.isInteger(v) && v > 0;
160
+ }
161
+ function verifyManifest(raw, expectedProviderDid) {
162
+ if (typeof raw != "object" || raw === null || Array.isArray(raw)) return { ok: false, reason: "manifest_not_object" };
163
+ let r = { ...raw };
164
+ if (r.schema !== PROVIDER_MANIFEST_SCHEMA) return { ok: false, reason: "manifest_schema_mismatch" };
165
+ if (!hasOnlyKeys(r, PROVIDER_MANIFEST_KEYS)) return { ok: false, reason: "manifest_unexpected_field" };
166
+ if (r.signature_base64 === void 0 || r.signature_base64 === null) return { ok: false, reason: "manifest_signature_missing" };
167
+ if (typeof r.signature_base64 != "string" || r.signature_base64.length === 0) return { ok: false, reason: "manifest_signature_malformed" };
168
+ let signature;
169
+ try {
170
+ signature = decodeBase643(r.signature_base64);
171
+ } catch {
172
+ return { ok: false, reason: "manifest_signature_malformed" };
173
+ }
174
+ if (signature.length !== nacl2.sign.signatureLength) return { ok: false, reason: "manifest_signature_malformed" };
175
+ if (!isBase64Key32(r.signing_public_key_base64, decodeBase643)) return { ok: false, reason: "manifest_field_malformed" };
176
+ if (!isBase64Key32(r.encryption_public_key_base64, decodeBase643)) return { ok: false, reason: "manifest_field_malformed" };
177
+ if (!isBase64Key32(r.attestor_public_key_base64, decodeBase643)) return { ok: false, reason: "manifest_field_malformed" };
178
+ if (typeof r.provider_did != "string" || !DID_RE.test(r.provider_did)) return { ok: false, reason: "manifest_field_malformed" };
179
+ if (!isNonEmptyString(r.accept_url) || !isNonEmptyString(r.worker_base_url)) return { ok: false, reason: "manifest_field_malformed" };
180
+ let rawHintUrl = r.hint_url;
181
+ if (rawHintUrl !== void 0 && !isNonEmptyString(rawHintUrl)) return { ok: false, reason: "manifest_field_malformed" };
182
+ let hintUrl = rawHintUrl === void 0 ? void 0 : rawHintUrl, rawRelays = r.relays, relays;
183
+ if (rawRelays !== void 0) {
184
+ if (typeof rawRelays != "object" || rawRelays === null || Array.isArray(rawRelays)) return { ok: false, reason: "manifest_field_malformed" };
185
+ let rel = { ...rawRelays };
186
+ if (!hasOnlyKeys(rel, ["provider_submits", "accepted_entry_points"])) return { ok: false, reason: "manifest_field_malformed" };
187
+ if (typeof rel.provider_submits != "boolean") return { ok: false, reason: "manifest_field_malformed" };
188
+ let rawDoors = rel.accepted_entry_points;
189
+ if (!Array.isArray(rawDoors) || rawDoors.length === 0 || rawDoors.length > AUTHORIZATION_ENTRY_POINTS.length) return { ok: false, reason: "manifest_field_malformed" };
190
+ let doors = [];
191
+ for (let i = 0; i < rawDoors.length; i += 1) {
192
+ let door = rawDoors[i];
193
+ if (typeof door != "string" || !AUTHORIZATION_ENTRY_POINTS.includes(door)) return { ok: false, reason: "manifest_field_malformed" };
194
+ if (doors.includes(door)) return { ok: false, reason: "manifest_field_malformed" };
195
+ doors.push(door);
196
+ }
197
+ relays = { provider_submits: rel.provider_submits, accepted_entry_points: doors };
198
+ }
199
+ if (r.hire_message_schema !== SESSION_HIRE_SCHEMA) return { ok: false, reason: "manifest_field_malformed" };
200
+ if (r.payment_buys !== "an attempt, not an outcome") return { ok: false, reason: "manifest_field_malformed" };
201
+ if (!isPositiveInt(r.acceptance_ttl_ms)) return { ok: false, reason: "manifest_field_malformed" };
202
+ let ttl = r.grant_ttl_ms;
203
+ if (typeof ttl != "object" || ttl === null || Array.isArray(ttl)) return { ok: false, reason: "manifest_field_malformed" };
204
+ let ttlSnap = { ...ttl };
205
+ if (!hasOnlyKeys(ttlSnap, ["min", "max"])) return { ok: false, reason: "manifest_field_malformed" };
206
+ if (!isPositiveInt(ttlSnap.min) || !isPositiveInt(ttlSnap.max)) return { ok: false, reason: "manifest_field_malformed" };
207
+ if (ttlSnap.min > ttlSnap.max || ttlSnap.max > MAX_GRANT_TTL_MS) return { ok: false, reason: "manifest_field_malformed" };
208
+ let rawNotes = r.notes;
209
+ if (!Array.isArray(rawNotes)) return { ok: false, reason: "manifest_field_malformed" };
210
+ let notes = [];
211
+ for (let i = 0; i < rawNotes.length; i += 1) {
212
+ let note = rawNotes[i];
213
+ if (!isNonEmptyString(note, 4096)) return { ok: false, reason: "manifest_field_malformed" };
214
+ notes.push(note);
215
+ }
216
+ let rawServices = r.services;
217
+ if (!Array.isArray(rawServices) || rawServices.length === 0) return { ok: false, reason: "manifest_field_malformed" };
218
+ let services = [];
219
+ for (let i = 0; i < rawServices.length; i += 1) {
220
+ let entry = rawServices[i];
221
+ if (typeof entry != "object" || entry === null || Array.isArray(entry)) return { ok: false, reason: "manifest_field_malformed" };
222
+ let s = { ...entry };
223
+ if (!hasOnlyKeys(s, ["ref", "description", "price"])) return { ok: false, reason: "manifest_field_malformed" };
224
+ if (!isNonEmptyString(s.ref, MAX_SERVICE_REF_LENGTH) || typeof s.description != "string") return { ok: false, reason: "manifest_field_malformed" };
225
+ let price = s.price;
226
+ if (typeof price != "object" || price === null || Array.isArray(price)) return { ok: false, reason: "manifest_field_malformed" };
227
+ let p = { ...price };
228
+ if (!hasOnlyKeys(p, ["chain", "asset", "payee_account", "min_amount", "max_amount"])) return { ok: false, reason: "manifest_field_malformed" };
229
+ if (typeof p.chain != "string" || !isCaip2(p.chain)) return { ok: false, reason: "manifest_field_malformed" };
230
+ if (typeof p.asset != "string" || !isCaip19(p.asset)) return { ok: false, reason: "manifest_field_malformed" };
231
+ if (typeof p.payee_account != "string" || !isCaip10(p.payee_account)) return { ok: false, reason: "manifest_field_malformed" };
232
+ if (typeof p.min_amount != "string" || !isPositiveDecimalString(p.min_amount)) return { ok: false, reason: "manifest_field_malformed" };
233
+ if (typeof p.max_amount != "string" || !isPositiveDecimalString(p.max_amount)) return { ok: false, reason: "manifest_field_malformed" };
234
+ services.push({ ref: s.ref, description: s.description, price: { chain: p.chain, asset: p.asset, payee_account: p.payee_account, min_amount: p.min_amount, max_amount: p.max_amount } });
235
+ }
236
+ let signingKey = decodeBase643(r.signing_public_key_base64), derived = null;
237
+ try {
238
+ derived = deriveDidFromSigningKey(signingKey);
239
+ } catch {
240
+ derived = null;
241
+ }
242
+ if (derived === null || derived !== r.provider_did) return { ok: false, reason: "manifest_did_not_derived" };
243
+ let body = { schema: PROVIDER_MANIFEST_SCHEMA, provider_did: r.provider_did, signing_public_key_base64: r.signing_public_key_base64, encryption_public_key_base64: r.encryption_public_key_base64, attestor_public_key_base64: r.attestor_public_key_base64, accept_url: r.accept_url, hire_message_schema: SESSION_HIRE_SCHEMA, worker_base_url: r.worker_base_url, ...hintUrl !== void 0 ? { hint_url: hintUrl } : {}, ...relays !== void 0 ? { relays } : {}, grant_ttl_ms: { min: ttlSnap.min, max: ttlSnap.max }, acceptance_ttl_ms: r.acceptance_ttl_ms, services, payment_buys: "an attempt, not an outcome", notes }, verified = false;
244
+ try {
245
+ verified = nacl2.sign.detached.verify(manifestSigningBytes(body), signature, signingKey);
246
+ } catch {
247
+ verified = false;
248
+ }
249
+ return verified ? expectedProviderDid !== void 0 && expectedProviderDid !== body.provider_did ? { ok: false, reason: "manifest_did_not_pinned" } : { ok: true, manifest: { ...body, signature_base64: r.signature_base64 } } : { ok: false, reason: "manifest_signature_invalid" };
250
+ }
251
+ function verifyProvider(raw, expectedProviderDid) {
252
+ if (typeof expectedProviderDid != "string" || !DID_RE.test(expectedProviderDid)) return { ok: false, reason: "manifest_pin_not_a_did" };
253
+ let verdict = verifyManifest(raw, expectedProviderDid);
254
+ if (!verdict.ok) return { ok: false, reason: verdict.reason };
255
+ let refs = /* @__PURE__ */ new Set();
256
+ for (let offering of verdict.manifest.services) {
257
+ if (refs.has(offering.ref)) return { ok: false, reason: "manifest_service_ref_duplicated" };
258
+ refs.add(offering.ref);
259
+ }
260
+ return { ok: true, provider: mintVerifiedProvider(verdict.manifest) };
261
+ }
262
+ function automaticCanonicalJson(value) {
263
+ if (value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isSafeInteger(value)) return JSON.stringify(value);
264
+ if (Array.isArray(value)) return `[${value.map(automaticCanonicalJson).join(",")}]`;
265
+ if (value !== null && typeof value == "object" && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) {
266
+ let record = value;
267
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${automaticCanonicalJson(record[key])}`).join(",")}}`;
268
+ }
269
+ throw new TypeError("Unsupported automatic collection JSON value");
270
+ }
271
+ var PROOFS_FIXTURE = { schema: "voidly.session.provider.manifest/v1", provider_did: "did:voidly:E41eeJuRTVxE7411zGGF9H", signing_public_key_base64: "abJvEa/votvZLFZzFKEhwNxyBoIqBRYgvL6mrNysGFI=", encryption_public_key_base64: "CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCws=", attestor_public_key_base64: "ExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExM=", accept_url: "https://fixture.example.test/session/accept", hire_message_schema: "voidly-session-hire/v1", worker_base_url: "https://fixture.example.test", grant_ttl_ms: { min: 3e5, max: 216e5 }, acceptance_ttl_ms: 3e5, services: [{ ref: "sessions.public.fixture/v1", description: "Offline signature verification fixture only.", price: { chain: "eip155:8453", asset: "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", payee_account: "eip155:8453:0x2222222222222222222222222222222222222222", min_amount: "1", max_amount: "1" } }], payment_buys: "an attempt, not an outcome", notes: ["Public offline fixture. Never contact these example endpoints or use this manifest for a payment."], signature_base64: "1gE/tewhX6oLaymE2i1w2vrR5gzZv3WTz5EzIECoetx4Dma+aVTAZDyzSLDHeHi/fc57mO7Sd6+3cR/WjfcJDA==" }, PROOFS_FIXTURE_DIGEST = "ccf7933183585166962b3244e19df341b6011895daa4fd43337011bc94b0846a";
272
+ var TITLE = "Your proof artwork", DESCRIPTION = "Code-generated artwork for a saved proof. Decorative, not a signed receipt or evidence of identity, payment, or impact.", PALETTES = [[[184, 166, 237], [237, 227, 206]], [[158, 190, 214], [224, 227, 238]], [[219, 185, 171], [194, 176, 230]]];
273
+ function generator(id) {
274
+ let a = parseInt(id.slice(0, 8), 16) | 0, b = parseInt(id.slice(8, 16), 16) | 0, c = parseInt(id.slice(16, 24), 16) | 0, d = parseInt(id.slice(24, 32), 16) | 0;
275
+ function next() {
276
+ let value = (a + b | 0) + d | 0;
277
+ return d = d + 1 | 0, a = b ^ b >>> 9, b = c + (c << 3) | 0, c = (c << 21 | c >>> 11) + value | 0, (value >>> 0) / 4294967296;
278
+ }
279
+ for (let index = 0; index < 16; index++) next();
280
+ return next;
281
+ }
282
+ function proofArtworkSvg(id) {
283
+ if (typeof id != "string" || !/^[0-9a-f]{32}$/.test(id)) throw new Error("Artwork requires a valid saved proof event ID.");
284
+ let random = generator(id), palette = PALETTES[Math.floor(random() * PALETTES.length)], rotation = random() * Math.PI * 2, phase = random() * Math.PI * 2, lobes = 2 + Math.floor(random() * 4), twist = (random() - 0.5) * 4.8, amplitude = 0.07 + random() * 0.12, stretch = 0.58 + random() * 0.4, driftX = (random() - 0.5) * 54, driftY = (random() - 0.5) * 54, aperture = 5 + random() * 23, power = 0.75 + random() * 0.75, paths = [];
285
+ for (let ring = 0; ring < 38; ring++) {
286
+ let progress = ring / 37, radius = aperture + progress * (102 - aperture), turn = rotation + twist * (1 - progress) ** 2, points = [];
287
+ for (let sample = 0; sample < 112; sample++) {
288
+ let angle = sample / 112 * Math.PI * 2, ripple = 1 + Math.sin(angle * lobes + phase + progress) * amplitude + Math.cos(angle * 2 - phase) * 0.025, cos = Math.cos(angle), sin = Math.sin(angle), x = Math.sign(cos) * Math.abs(cos) ** power * radius * ripple, y = Math.sign(sin) * Math.abs(sin) ** power * radius * ripple * stretch, px = 160 + Math.cos(turn) * x - Math.sin(turn) * y + driftX * (1 - progress), py = 160 + Math.sin(turn) * x + Math.cos(turn) * y + driftY * (1 - progress);
289
+ points.push(`${sample ? "L" : "M"}${px.toFixed(2)} ${py.toFixed(2)}`);
290
+ }
291
+ let color = palette[0].map((value, index) => Math.round(value + (palette[1][index] - value) * progress));
292
+ paths.push(`<path d="${points.join("")}Z" fill="none" stroke="rgb(${color.join(",")})" stroke-width="${ring % 6 === 0 ? 1.4 : 0.9}"/>`);
293
+ }
294
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" viewBox="0 0 320 320" role="img" aria-label="${TITLE}"><title>${TITLE}</title><desc>${DESCRIPTION}</desc><rect width="320" height="320" fill="#050506"/>${paths.join("")}</svg>`;
295
+ }
296
+ var PUBLIC_EXERCISE_SCHEMA = "voidpay.proof-collection.public-exercise/v3", PUBLIC_RESULT_SCHEMA = "voidpay.proof-collection.public-result/v3", PUBLIC_RESPONSE_SCHEMA = "voidpay.proof-collection.public-response/v3", SESSIONS_PROOFS_TASK = Object.freeze({ id: "sessions-public-check-01", version: 1, acceptance_rule_sha256: "1be9d67c2f8caffa45fd985b7454371838c29e4fadbd8dadc2a4e6663b0f7bc5" }), SESSIONS_PROOFS_PROVIDER = Object.freeze({ provider_did: "did:voidly:6rGTFa5apSnKNF14bGXZfu", manifest_url: "https://intelligence.voidly.ai:8443/.well-known/voidly-session-provider.json", index_url: "https://api.voidly.ai/v1/session/providers" }), PUBLIC_EXERCISE_MAX_BYTES = 8192, PUBLIC_EXERCISE_TTL_MS = 6e5, WRONG_PIN = "did:voidly:2222222222222222", HEX32 = /^[0-9a-f]{32}$/, HEX64 = /^[0-9a-f]{64}$/, PublicExerciseError = class extends Error {
297
+ constructor(code) {
298
+ super(code), this.name = "PublicExerciseError", this.code = code;
299
+ }
300
+ };
301
+ function requireCondition(value, code) {
302
+ if (!value) throw new PublicExerciseError(code);
303
+ }
304
+ function plain(value) {
305
+ return value !== null && typeof value == "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
306
+ }
307
+ function exact(value, keys) {
308
+ return plain(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
309
+ }
310
+ function same(value, expected) {
311
+ return exact(value, Object.keys(expected)) && Object.entries(expected).every(([key, field]) => value[key] === field);
312
+ }
313
+ function iso(value) {
314
+ return typeof value == "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString() === value;
315
+ }
316
+ function parsePublicExercise(text, nowMs = Date.now()) {
317
+ requireCondition(typeof text == "string" && new TextEncoder().encode(text).length <= PUBLIC_EXERCISE_MAX_BYTES, "exercise_invalid");
318
+ let input;
319
+ try {
320
+ input = JSON.parse(text);
321
+ } catch {
322
+ throw new PublicExerciseError("exercise_invalid");
323
+ }
324
+ return requireCondition(exact(input, ["schema", "task", "challenge_id", "challenge", "issued_at", "expires_at", "provider"]), "exercise_invalid"), requireCondition(input.schema === PUBLIC_EXERCISE_SCHEMA && same(input.task, SESSIONS_PROOFS_TASK) && same(input.provider, SESSIONS_PROOFS_PROVIDER) && typeof input.challenge_id == "string" && HEX32.test(input.challenge_id) && typeof input.challenge == "string" && HEX64.test(input.challenge) && iso(input.issued_at) && iso(input.expires_at), "exercise_invalid"), requireCondition(Number.isFinite(nowMs) && Date.parse(input.expires_at) - Date.parse(input.issued_at) === PUBLIC_EXERCISE_TTL_MS && Date.parse(input.issued_at) <= nowMs + 3e4, "exercise_invalid"), requireCondition(Date.parse(input.expires_at) > nowMs, "exercise_expired"), { schema: PUBLIC_EXERCISE_SCHEMA, task: SESSIONS_PROOFS_TASK, challenge_id: input.challenge_id, challenge: input.challenge, issued_at: input.issued_at, expires_at: input.expires_at, provider: SESSIONS_PROOFS_PROVIDER };
325
+ }
326
+ async function runSessionsSelfTest() {
327
+ let found = verifyProvider(PROOFS_FIXTURE, PROOFS_FIXTURE.provider_did), wrong = verifyProvider(PROOFS_FIXTURE, WRONG_PIN), altered = verifyProvider({ ...PROOFS_FIXTURE, accept_url: "https://changed.example.test/accept" }, PROOFS_FIXTURE.provider_did), digest = found.ok ? await sha256Hex(canonicalBytes(found.provider.manifest)) : null, checks = { signed_manifest_verified: found.ok, wrong_pin_refused: !wrong.ok && wrong.reason === "manifest_did_not_pinned", changed_manifest_refused: !altered.ok && altered.reason === "manifest_signature_invalid", canonical_digest_matches: digest === PROOFS_FIXTURE_DIGEST };
328
+ return { schema: "voidly.session.self-test/v1", ok: Object.values(checks).every(Boolean), mode: "offline", checks, network_requests: 0, saved_proof: false };
329
+ }
330
+ async function verifyPublicExercise(exercise, index, manifest, nowMs = Date.now()) {
331
+ let parsed = parsePublicExercise(JSON.stringify(exercise), nowMs);
332
+ requireCondition(plain(index) && Array.isArray(index.providers) && index.providers.length <= 1e3 && index.providers.some((entry) => plain(entry) && entry.provider_did === SESSIONS_PROOFS_PROVIDER.provider_did && entry.manifest_url === SESSIONS_PROOFS_PROVIDER.manifest_url), "provider_not_listed");
333
+ let verified = verifyProvider(manifest, SESSIONS_PROOFS_PROVIDER.provider_did);
334
+ requireCondition(verified.ok, "manifest_invalid");
335
+ let wrong = verifyProvider(verified.provider.manifest, WRONG_PIN);
336
+ requireCondition(!wrong.ok && wrong.reason === "manifest_did_not_pinned", "wrong_pin_not_refused");
337
+ let manifest_digest_sha256 = await sha256Hex(canonicalBytes(verified.provider.manifest)), challenge_response_sha256 = await sha256Hex(canonicalBytes({ schema: PUBLIC_RESPONSE_SCHEMA, task: parsed.task, challenge_id: parsed.challenge_id, challenge: parsed.challenge, manifest_digest_sha256 }));
338
+ return { schema: PUBLIC_RESULT_SCHEMA, task: parsed.task, challenge_id: parsed.challenge_id, challenge: parsed.challenge, manifest_digest_sha256, challenge_response_sha256, checks: { provider_pair_listed: true, manifest_verified: true, wrong_pin_refused: true } };
339
+ }
340
+ async function readFixedJson(url, maxBytes, fetchImpl) {
341
+ let controller = new AbortController(), timer, reader, response, deadlineAt = performance.now() + 8e3, deadline = new Promise((_, reject) => {
342
+ timer = setTimeout(() => {
343
+ controller.abort(), reject(new PublicExerciseError("upstream_unavailable"));
344
+ }, 8e3);
345
+ });
346
+ try {
347
+ let fetched = Promise.resolve(fetchImpl(url, { method: "GET", headers: { accept: "application/json" }, redirect: "error", credentials: "omit", cache: "no-store", referrerPolicy: "no-referrer", signal: controller.signal })).then((value) => {
348
+ if (controller.signal.aborted) throw value.body && !value.body.locked && value.body.cancel().catch(() => {
349
+ }), new PublicExerciseError("upstream_unavailable");
350
+ return value;
351
+ });
352
+ response = await Promise.race([fetched, deadline]), requireCondition(performance.now() < deadlineAt, "upstream_unavailable"), requireCondition(!response.redirected && (response.url === "" || response.url === url), "upstream_redirect"), requireCondition(response.ok && response.body, "upstream_unavailable"), requireCondition(/^application\/json(?:\s*;|$)/i.test(response.headers.get("content-type") ?? ""), "upstream_not_json");
353
+ let contentLength = response.headers.get("content-length");
354
+ requireCondition(contentLength === null || /^\d+$/.test(contentLength) && Number(contentLength) <= maxBytes, "upstream_too_large"), reader = response.body.getReader();
355
+ let chunks = [], size = 0, reads = 0, emptyReads = 0;
356
+ for (; ; ) {
357
+ requireCondition(performance.now() < deadlineAt, "upstream_unavailable"), requireCondition(++reads <= 2048, "upstream_too_large");
358
+ let part = await Promise.race([reader.read(), deadline]);
359
+ if (requireCondition(performance.now() < deadlineAt, "upstream_unavailable"), part.done) break;
360
+ if (part.value.byteLength === 0) {
361
+ requireCondition(++emptyReads <= 128, "upstream_too_large");
362
+ continue;
363
+ }
364
+ size += part.value.byteLength, requireCondition(size <= maxBytes, "upstream_too_large"), chunks.push(part.value);
365
+ }
366
+ let bytes = new Uint8Array(size), offset = 0;
367
+ for (let chunk of chunks) bytes.set(chunk, offset), offset += chunk.byteLength;
368
+ try {
369
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
370
+ } catch {
371
+ throw new PublicExerciseError("upstream_not_json");
372
+ }
373
+ } catch (error) {
374
+ throw error instanceof PublicExerciseError ? error : new PublicExerciseError("upstream_unavailable");
375
+ } finally {
376
+ clearTimeout(timer), controller.abort(), reader ? (reader.cancel().catch(() => {
377
+ }), reader.releaseLock()) : response?.body && !response.body.locked && response.body.cancel().catch(() => {
378
+ });
379
+ }
380
+ }
381
+ async function runPublicExercise(text, options = {}) {
382
+ let nowMs = options.nowMs ?? Date.now(), exercise = parsePublicExercise(text, nowMs), fetchImpl = options.fetchImpl ?? fetch, index = await readFixedJson(SESSIONS_PROOFS_PROVIDER.index_url, 262144, fetchImpl), manifest = await readFixedJson(SESSIONS_PROOFS_PROVIDER.manifest_url, 65536, fetchImpl);
383
+ return verifyPublicExercise(exercise, index, manifest, options.nowMs ?? Date.now());
384
+ }
385
+ var AUTOMATIC_HANDOFF_SCHEMA = "voidpay.proof-collection.agent-handoff/v4", AUTOMATIC_TASK = Object.freeze({ id: "sessions-auto-save-01", version: 1, acceptance_rule_sha256: "b58e042e088740892856e96f4c8eee079bd94819c518e8e486ef250184ffb230" }), AUTOMATIC_BASE = "https://api.voidly.ai/v4/proofs/collections", AUTOMATIC_ISSUER = "https://api.voidly.ai/v2/proofs/collections/issuer", AUTOMATIC_RECEIPT_DOMAIN = `voidpay-proof-collection-receipt/v4
386
+ `, AUTOMATIC_RUN_BINDING_DOMAIN = `voidpay-proof-collection-run-binding/v4
387
+ `, AUTOMATIC_SCOPE = "public-provider-discovery-and-owner-preauthorized-private-save-only", AUTOMATIC_HANDOFF_MAX_BYTES = 1024, AUTOMATIC_BOUNDARY = Object.freeze({ decision: "STOP", wallet_required: false, payment_action_allowed: false, authorization_created: false, transaction_broadcast: false, settlement_observed: false, reward_offered: false, reward_amount: "0", reward_asset: null, customer_identity_verified: false, commercial_transaction: false, credited_to_traction_metrics: false, unique_person_verified: false, impact_verified: false }), SERVER_CHECKS = ["owner_preauthorized", "challenge_unexpired", "provider_pair_listed", "manifest_verified", "wrong_pin_refused"], CLAIMANT_CHECKS = ["manifest_digest_matches", "challenge_response_matches"], HEX322 = /^[0-9a-f]{32}$/, HEX642 = /^[0-9a-f]{64}$/, RETENTION_MS = 15552e6, MAX_RESPONSE_BYTES = 32768, MAX_ATTEMPTS = 3, AutomaticProofError = class extends Error {
388
+ constructor(code) {
389
+ super(code);
390
+ this.code = code;
391
+ this.name = "AutomaticProofError";
392
+ }
393
+ }, AutomaticHttpError = class extends AutomaticProofError {
394
+ constructor(code, status) {
395
+ super(code);
396
+ this.status = status;
397
+ }
398
+ };
399
+ function requireCondition2(value, code) {
400
+ if (!value) throw new AutomaticProofError(code);
401
+ }
402
+ function exact2(value, keys) {
403
+ return value !== null && typeof value == "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null) && Object.keys(value).length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
404
+ }
405
+ function same2(value, expected) {
406
+ return exact2(value, Object.keys(expected)) && JSON.stringify(value, Object.keys(expected).sort()) === JSON.stringify(expected, Object.keys(expected).sort());
407
+ }
408
+ function iso2(value) {
409
+ return typeof value == "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString() === value;
410
+ }
411
+ function unhex(value) {
412
+ return Uint8Array.from(value.match(/../g) ?? [], (pair) => Number.parseInt(pair, 16));
413
+ }
414
+ function parseAutomaticHandoff(text) {
415
+ requireCondition2(typeof text == "string" && new TextEncoder().encode(text).length <= AUTOMATIC_HANDOFF_MAX_BYTES, "handoff_invalid");
416
+ let value;
417
+ try {
418
+ value = JSON.parse(text);
419
+ } catch {
420
+ throw new AutomaticProofError("handoff_invalid");
421
+ }
422
+ return requireCondition2(exact2(value, ["schema", "run_id", "completion_capability"]) && value.schema === AUTOMATIC_HANDOFF_SCHEMA && typeof value.run_id == "string" && HEX322.test(value.run_id) && typeof value.completion_capability == "string" && HEX642.test(value.completion_capability), "handoff_invalid"), { schema: AUTOMATIC_HANDOFF_SCHEMA, run_id: value.run_id, completion_capability: value.completion_capability };
423
+ }
424
+ async function requestJson(kind, handoff, body, fetchImpl) {
425
+ let url = kind === "issuer" ? AUTOMATIC_ISSUER : `${AUTOMATIC_BASE}/${kind}`, controller = new AbortController(), timer, reader, response, deadlineAt = performance.now() + (kind === "submit" ? 2e4 : 1e4), deadline = new Promise((_, reject) => {
426
+ timer = setTimeout(() => {
427
+ controller.abort(), reject(new AutomaticProofError("completion_unavailable"));
428
+ }, kind === "submit" ? 2e4 : 1e4);
429
+ });
430
+ try {
431
+ let headers = { accept: "application/json" };
432
+ kind !== "issuer" && (headers["content-type"] = "application/json", headers.authorization = `Bearer ${handoff.completion_capability}`);
433
+ let fetched = Promise.resolve(fetchImpl(url, { method: kind === "issuer" ? "GET" : "POST", headers, ...kind === "issuer" ? {} : { body: JSON.stringify(body) }, redirect: "error", credentials: "omit", cache: "no-store", referrerPolicy: "no-referrer", signal: controller.signal })).then((value2) => {
434
+ if (controller.signal.aborted) throw value2.body && !value2.body.locked && value2.body.cancel().catch(() => {
435
+ }), new AutomaticProofError("completion_unavailable");
436
+ return value2;
437
+ });
438
+ response = await Promise.race([fetched, deadline]), requireCondition2(performance.now() < deadlineAt, "completion_unavailable"), requireCondition2(!response.redirected && response.type !== "opaqueredirect" && (response.url === "" || response.url === url) && response.status !== 0 && !(response.status >= 300 && response.status < 400), "response_invalid"), requireCondition2(/^application\/json(?:\s*;|$)/i.test(response.headers.get("content-type") ?? "") && response.body, "response_invalid");
439
+ let length = response.headers.get("content-length");
440
+ requireCondition2(length === null || /^\d+$/.test(length) && Number(length) <= MAX_RESPONSE_BYTES, "response_invalid"), reader = response.body.getReader();
441
+ let chunks = [], size = 0, reads = 0, emptyReads = 0;
442
+ for (; ; ) {
443
+ requireCondition2(performance.now() < deadlineAt, "completion_unavailable"), requireCondition2(++reads <= 2048, "response_invalid");
444
+ let next = await Promise.race([reader.read(), deadline]);
445
+ if (requireCondition2(performance.now() < deadlineAt, "completion_unavailable"), next.done) break;
446
+ if (next.value.byteLength === 0) {
447
+ requireCondition2(++emptyReads <= 128, "response_invalid");
448
+ continue;
449
+ }
450
+ size += next.value.byteLength, requireCondition2(size <= MAX_RESPONSE_BYTES, "response_invalid"), chunks.push(next.value);
451
+ }
452
+ let bytes = new Uint8Array(size), offset = 0;
453
+ for (let chunk of chunks) bytes.set(chunk, offset), offset += chunk.byteLength;
454
+ let value;
455
+ try {
456
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
457
+ } catch {
458
+ throw new AutomaticProofError("response_invalid");
459
+ }
460
+ if (requireCondition2(!JSON.stringify(value).includes(handoff.completion_capability), "response_invalid"), !response.ok) {
461
+ requireCondition2(exact2(value, ["schema", "ok", "error"]) && value.schema === "voidpay.proof-collection.error/v4" && value.ok === false && exact2(value.error, ["code"]) && typeof value.error.code == "string", "response_invalid");
462
+ let code = response.status === 401 || response.status === 403 ? "permission_refused" : response.status === 404 || response.status === 410 ? "run_expired_or_revoked" : response.status === 429 ? value.error.code === "verifier_attempts_exhausted" ? "attempts_exhausted" : "rate_limited" : "completion_unavailable";
463
+ throw new AutomaticHttpError(code, response.status);
464
+ }
465
+ return value;
466
+ } catch (error) {
467
+ throw error instanceof AutomaticProofError ? error : new AutomaticProofError("completion_unavailable");
468
+ } finally {
469
+ clearTimeout(timer), controller.abort(), reader ? (reader.cancel().catch(() => {
470
+ }), reader.releaseLock()) : response?.body && !response.body.locked && response.body.cancel().catch(() => {
471
+ });
472
+ }
473
+ }
474
+ async function issuerKeys(handoff, fetchImpl) {
475
+ let value = await requestJson("issuer", handoff, void 0, fetchImpl);
476
+ requireCondition2(exact2(value, ["schema", "issuer", "keys", "unknown_key_policy"]) && value.schema === "voidpay.proof-collection.issuer/v2" && value.issuer === AUTOMATIC_ISSUER && value.unknown_key_policy === "untrusted; never trust an embedded receipt key" && Array.isArray(value.keys) && value.keys.length > 0 && value.keys.length <= 8, "issuer_invalid");
477
+ let keys = /* @__PURE__ */ new Map();
478
+ for (let key of value.keys) requireCondition2(exact2(key, ["issuer", "key_id", "algorithm", "public_key_hex", "status"]) && key.issuer === AUTOMATIC_ISSUER && key.algorithm === "Ed25519" && key.status === "active" && typeof key.key_id == "string" && HEX642.test(key.key_id) && typeof key.public_key_hex == "string" && HEX642.test(key.public_key_hex) && !keys.has(key.key_id) && await sha256Hex(unhex(key.public_key_hex)) === key.key_id, "issuer_invalid"), keys.set(key.key_id, key.public_key_hex);
479
+ return keys;
480
+ }
481
+ async function verifyComplete(value, handoff, fetchImpl, now) {
482
+ requireCondition2(exact2(value, ["schema", "ok", "status", "run_id", "exercise", "record", "already_completed"]) && value.schema === "voidpay.proof-collection.result/v4" && value.ok === true && value.status === "complete" && value.run_id === handoff.run_id && typeof value.already_completed == "boolean", "response_invalid");
483
+ let record = value.record;
484
+ requireCondition2(exact2(record, ["event_id", "visibility", "public_url", "expires_at", "receipt"]) && typeof record.event_id == "string" && HEX322.test(record.event_id) && iso2(record.expires_at) && (record.visibility === "private" || record.visibility === "public") && (value.already_completed || record.visibility === "private") && record.public_url === (record.visibility === "private" ? null : `https://api.voidly.ai/v2/proofs/collections/public/${record.event_id}`) && exact2(record.receipt, ["statement", "signature_hex"]), "receipt_invalid");
485
+ let receipt = record.receipt, s = receipt.statement;
486
+ requireCondition2(exact2(s, ["schema", "issuer", "key_id", "event_id", "run_binding_sha256", "exercise_binding_sha256", "artwork", "task", "computation_task", "checked_at", "completed_at", "outcome", "checks", "provider", "scope", "boundary"]) && s.schema === "voidpay.proof-collection.receipt/v4" && s.issuer === AUTOMATIC_ISSUER && s.event_id === record.event_id && typeof s.key_id == "string" && HEX642.test(s.key_id) && typeof s.run_binding_sha256 == "string" && HEX642.test(s.run_binding_sha256) && typeof s.exercise_binding_sha256 == "string" && HEX642.test(s.exercise_binding_sha256) && typeof receipt.signature_hex == "string" && /^[0-9a-f]{128}$/.test(receipt.signature_hex) && same2(s.task, AUTOMATIC_TASK) && same2(s.computation_task, SESSIONS_PROOFS_TASK) && s.scope === AUTOMATIC_SCOPE && same2(s.boundary, AUTOMATIC_BOUNDARY) && s.outcome === "PASS" && iso2(s.checked_at) && iso2(s.completed_at) && s.checked_at <= s.completed_at && Date.parse(s.completed_at) <= now + 3e4 && Date.parse(record.expires_at) === Date.parse(s.completed_at) + RETENTION_MS && Date.parse(record.expires_at) > now && same2(s.provider, { provider_did: SESSIONS_PROOFS_PROVIDER.provider_did, manifest_url: SESSIONS_PROOFS_PROVIDER.manifest_url }) && same2(s.artwork, { recipe: "contours-v1", seed: record.event_id }) && exact2(s.checks, ["server", "claimant"]) && exact2(s.checks.server, SERVER_CHECKS) && exact2(s.checks.claimant, CLAIMANT_CHECKS) && [...Object.values(s.checks.server), ...Object.values(s.checks.claimant)].every((check) => check === true), "receipt_invalid");
487
+ let exercise;
488
+ try {
489
+ exercise = parsePublicExercise(JSON.stringify(value.exercise), Date.parse(s.checked_at));
490
+ } catch {
491
+ throw new AutomaticProofError("receipt_invalid");
492
+ }
493
+ requireCondition2(exercise.challenge_id === handoff.run_id && Date.parse(exercise.issued_at) <= Date.parse(s.checked_at) && Date.parse(exercise.expires_at) > Date.parse(s.completed_at) && await sha256Hex(new TextEncoder().encode(AUTOMATIC_RUN_BINDING_DOMAIN + handoff.run_id)) === s.run_binding_sha256 && await sha256Hex(new TextEncoder().encode(automaticCanonicalJson(exercise))) === s.exercise_binding_sha256, "receipt_invalid");
494
+ let key = (await issuerKeys(handoff, fetchImpl)).get(s.key_id);
495
+ requireCondition2(key && nacl3.sign.detached.verify(new TextEncoder().encode(AUTOMATIC_RECEIPT_DOMAIN + automaticCanonicalJson(s)), unhex(receipt.signature_hex), unhex(key)), "receipt_invalid");
496
+ let svg = proofArtworkSvg(record.event_id), result = { schema: "voidly.session.automatic-proof/v1", ok: true, saved_proof: true, event_id: record.event_id, visibility: record.visibility, already_completed: value.already_completed, receipt: JSON.parse(JSON.stringify(receipt)), artwork: { recipe: "contours-v1", seed: record.event_id, svg, sha256: await sha256Hex(new TextEncoder().encode(svg)) }, return_url: "https://voidly.ai/pay/proofs", payment_boundary: "STOP" };
497
+ return requireCondition2(!JSON.stringify(result).includes(handoff.completion_capability), "response_invalid"), result;
498
+ }
499
+ function pending(value, runId) {
500
+ return exact2(value, ["schema", "ok", "status", "run_id", "record"]) && value.schema === "voidpay.proof-collection.result/v4" && value.ok === true && value.status === "pending" && value.run_id === runId && value.record === null;
501
+ }
502
+ function transient(error) {
503
+ return error instanceof AutomaticHttpError ? error.status === 409 || error.status >= 500 : error instanceof AutomaticProofError && error.code === "completion_unavailable";
504
+ }
505
+ async function completeAutomaticProof(text, options = {}) {
506
+ let handoff = parseAutomaticHandoff(text), fetchImpl = options.fetchImpl ?? fetch, now = () => options.nowMs ?? Date.now();
507
+ requireCondition2((await runSessionsSelfTest()).ok, "self_test_failed");
508
+ let mayHaveSaved = false;
509
+ try {
510
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) try {
511
+ let existing = await requestJson("result", handoff, { run_id: handoff.run_id }, fetchImpl);
512
+ if (!pending(existing, handoff.run_id)) return await verifyComplete(existing, handoff, fetchImpl, now());
513
+ let fresh = await requestJson("exercise", handoff, { run_id: handoff.run_id }, fetchImpl);
514
+ if (exact2(fresh, ["schema", "ok", "status", "run_id", "exercise", "record", "already_completed"])) return await verifyComplete(fresh, handoff, fetchImpl, now());
515
+ requireCondition2(exact2(fresh, ["schema", "ok", "action", "run_id", "exercise"]) && fresh.schema === "voidpay.proof-collection.response/v4" && fresh.ok === true && fresh.action === "exercise" && fresh.run_id === handoff.run_id, "response_invalid");
516
+ let exercise = parsePublicExercise(JSON.stringify(fresh.exercise), now());
517
+ requireCondition2(exercise.challenge_id === handoff.run_id, "response_invalid");
518
+ let result = await runPublicExercise(JSON.stringify(exercise), { fetchImpl, nowMs: options.nowMs });
519
+ mayHaveSaved = true;
520
+ let completed = await requestJson("submit", handoff, { run_id: handoff.run_id, result }, fetchImpl);
521
+ return await verifyComplete(completed, handoff, fetchImpl, now());
522
+ } catch (error) {
523
+ if (error instanceof PublicExerciseError) {
524
+ if (error.code !== "upstream_unavailable") throw new AutomaticProofError("provider_check_failed");
525
+ error = new AutomaticProofError("completion_unavailable");
526
+ }
527
+ if (!transient(error)) throw error;
528
+ attempt < MAX_ATTEMPTS - 1 && await new Promise((resolve) => setTimeout(resolve, 1e3));
529
+ }
530
+ if (mayHaveSaved) try {
531
+ let existing = await requestJson("result", handoff, { run_id: handoff.run_id }, fetchImpl);
532
+ if (!pending(existing, handoff.run_id)) return await verifyComplete(existing, handoff, fetchImpl, now());
533
+ } catch (error) {
534
+ if (!transient(error)) throw error;
535
+ }
536
+ throw new AutomaticProofError(mayHaveSaved ? "completion_uncertain" : "completion_unavailable");
537
+ } finally {
538
+ handoff.completion_capability = "";
539
+ }
540
+ }
541
+ export {
542
+ AUTOMATIC_BASE,
543
+ AUTOMATIC_BOUNDARY,
544
+ AUTOMATIC_HANDOFF_MAX_BYTES,
545
+ AUTOMATIC_HANDOFF_SCHEMA,
546
+ AUTOMATIC_ISSUER,
547
+ AUTOMATIC_RECEIPT_DOMAIN,
548
+ AUTOMATIC_RUN_BINDING_DOMAIN,
549
+ AUTOMATIC_SCOPE,
550
+ AUTOMATIC_TASK,
551
+ AutomaticProofError,
552
+ completeAutomaticProof,
553
+ parseAutomaticHandoff
554
+ };
@@ -109,9 +109,9 @@ function rebuildDocumentDeep(document) {
109
109
  if (top === null) return document;
110
110
  let made = /* @__PURE__ */ new Map();
111
111
  made.set(document, top);
112
- let pending = [top];
113
- for (; pending.length > 0; ) {
114
- let frame = pending.pop();
112
+ let pending2 = [top];
113
+ for (; pending2.length > 0; ) {
114
+ let frame = pending2.pop();
115
115
  if (frame === void 0) break;
116
116
  for (let key of Object.keys(frame)) {
117
117
  let child = frame[key];
@@ -122,15 +122,15 @@ function rebuildDocumentDeep(document) {
122
122
  continue;
123
123
  }
124
124
  let copy = containerCopy(child);
125
- copy !== null && (made.set(child, copy), frame[key] = copy, pending.push(copy));
125
+ copy !== null && (made.set(child, copy), frame[key] = copy, pending2.push(copy));
126
126
  }
127
127
  }
128
128
  return top;
129
129
  }
130
130
  function freezeManifestDeep(m) {
131
- let seen = /* @__PURE__ */ new Set(), pending = [m];
132
- for (; pending.length > 0; ) {
133
- let node = pending.pop();
131
+ let seen = /* @__PURE__ */ new Set(), pending2 = [m];
132
+ for (; pending2.length > 0; ) {
133
+ let node = pending2.pop();
134
134
  if (node === null || typeof node != "object") continue;
135
135
  let container = node;
136
136
  if (!seen.has(container)) {
@@ -138,7 +138,7 @@ function freezeManifestDeep(m) {
138
138
  for (let key of Reflect.ownKeys(container)) {
139
139
  if (typeof key == "symbol") continue;
140
140
  let d = Object.getOwnPropertyDescriptor(container, key);
141
- d !== void 0 && "value" in d && pending.push(d.value);
141
+ d !== void 0 && "value" in d && pending2.push(d.value);
142
142
  }
143
143
  }
144
144
  }
@@ -329,20 +329,29 @@ async function verifyPublicExercise(exercise, index, manifest, nowMs = Date.now(
329
329
  return { schema: PUBLIC_RESULT_SCHEMA, task: parsed.task, challenge_id: parsed.challenge_id, challenge: parsed.challenge, manifest_digest_sha256, challenge_response_sha256, checks: { provider_pair_listed: true, manifest_verified: true, wrong_pin_refused: true } };
330
330
  }
331
331
  async function readFixedJson(url, maxBytes, fetchImpl) {
332
- let controller = new AbortController(), timer, reader, deadline = new Promise((_, reject) => {
332
+ let controller = new AbortController(), timer, reader, response, deadlineAt = performance.now() + 8e3, deadline = new Promise((_, reject) => {
333
333
  timer = setTimeout(() => {
334
334
  controller.abort(), reject(new PublicExerciseError("upstream_unavailable"));
335
335
  }, 8e3);
336
336
  });
337
337
  try {
338
- let response = await Promise.race([fetchImpl(url, { method: "GET", headers: { accept: "application/json" }, redirect: "error", credentials: "omit", cache: "no-store", referrerPolicy: "no-referrer", signal: controller.signal }), deadline]);
339
- requireCondition(!response.redirected && (response.url === "" || response.url === url), "upstream_redirect"), requireCondition(response.ok && response.body, "upstream_unavailable"), requireCondition(/^application\/json(?:\s*;|$)/i.test(response.headers.get("content-type") ?? ""), "upstream_not_json");
338
+ let fetched = Promise.resolve(fetchImpl(url, { method: "GET", headers: { accept: "application/json" }, redirect: "error", credentials: "omit", cache: "no-store", referrerPolicy: "no-referrer", signal: controller.signal })).then((value) => {
339
+ if (controller.signal.aborted) throw value.body && !value.body.locked && value.body.cancel().catch(() => {
340
+ }), new PublicExerciseError("upstream_unavailable");
341
+ return value;
342
+ });
343
+ response = await Promise.race([fetched, deadline]), requireCondition(performance.now() < deadlineAt, "upstream_unavailable"), requireCondition(!response.redirected && (response.url === "" || response.url === url), "upstream_redirect"), requireCondition(response.ok && response.body, "upstream_unavailable"), requireCondition(/^application\/json(?:\s*;|$)/i.test(response.headers.get("content-type") ?? ""), "upstream_not_json");
340
344
  let contentLength = response.headers.get("content-length");
341
345
  requireCondition(contentLength === null || /^\d+$/.test(contentLength) && Number(contentLength) <= maxBytes, "upstream_too_large"), reader = response.body.getReader();
342
- let chunks = [], size = 0;
346
+ let chunks = [], size = 0, reads = 0, emptyReads = 0;
343
347
  for (; ; ) {
348
+ requireCondition(performance.now() < deadlineAt, "upstream_unavailable"), requireCondition(++reads <= 2048, "upstream_too_large");
344
349
  let part = await Promise.race([reader.read(), deadline]);
345
- if (part.done) break;
350
+ if (requireCondition(performance.now() < deadlineAt, "upstream_unavailable"), part.done) break;
351
+ if (part.value.byteLength === 0) {
352
+ requireCondition(++emptyReads <= 128, "upstream_too_large");
353
+ continue;
354
+ }
346
355
  size += part.value.byteLength, requireCondition(size <= maxBytes, "upstream_too_large"), chunks.push(part.value);
347
356
  }
348
357
  let bytes = new Uint8Array(size), offset = 0;
@@ -355,22 +364,214 @@ async function readFixedJson(url, maxBytes, fetchImpl) {
355
364
  } catch (error) {
356
365
  throw error instanceof PublicExerciseError ? error : new PublicExerciseError("upstream_unavailable");
357
366
  } finally {
358
- clearTimeout(timer), controller.abort(), reader && (reader.cancel().catch(() => {
359
- }), reader.releaseLock());
367
+ clearTimeout(timer), controller.abort(), reader ? (reader.cancel().catch(() => {
368
+ }), reader.releaseLock()) : response?.body && !response.body.locked && response.body.cancel().catch(() => {
369
+ });
360
370
  }
361
371
  }
362
372
  async function runPublicExercise(text, options = {}) {
363
373
  let nowMs = options.nowMs ?? Date.now(), exercise = parsePublicExercise(text, nowMs), fetchImpl = options.fetchImpl ?? fetch, index = await readFixedJson(SESSIONS_PROOFS_PROVIDER.index_url, 262144, fetchImpl), manifest = await readFixedJson(SESSIONS_PROOFS_PROVIDER.manifest_url, 65536, fetchImpl);
364
374
  return verifyPublicExercise(exercise, index, manifest, options.nowMs ?? Date.now());
365
375
  }
366
- async function input() {
376
+ import nacl3 from "tweetnacl";
377
+ function automaticCanonicalJson(value) {
378
+ if (value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isSafeInteger(value)) return JSON.stringify(value);
379
+ if (Array.isArray(value)) return `[${value.map(automaticCanonicalJson).join(",")}]`;
380
+ if (value !== null && typeof value == "object" && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) {
381
+ let record = value;
382
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${automaticCanonicalJson(record[key])}`).join(",")}}`;
383
+ }
384
+ throw new TypeError("Unsupported automatic collection JSON value");
385
+ }
386
+ var AUTOMATIC_HANDOFF_SCHEMA = "voidpay.proof-collection.agent-handoff/v4", AUTOMATIC_TASK = Object.freeze({ id: "sessions-auto-save-01", version: 1, acceptance_rule_sha256: "b58e042e088740892856e96f4c8eee079bd94819c518e8e486ef250184ffb230" }), AUTOMATIC_BASE = "https://api.voidly.ai/v4/proofs/collections", AUTOMATIC_ISSUER = "https://api.voidly.ai/v2/proofs/collections/issuer", AUTOMATIC_RECEIPT_DOMAIN = `voidpay-proof-collection-receipt/v4
387
+ `, AUTOMATIC_RUN_BINDING_DOMAIN = `voidpay-proof-collection-run-binding/v4
388
+ `, AUTOMATIC_SCOPE = "public-provider-discovery-and-owner-preauthorized-private-save-only", AUTOMATIC_HANDOFF_MAX_BYTES = 1024, AUTOMATIC_BOUNDARY = Object.freeze({ decision: "STOP", wallet_required: false, payment_action_allowed: false, authorization_created: false, transaction_broadcast: false, settlement_observed: false, reward_offered: false, reward_amount: "0", reward_asset: null, customer_identity_verified: false, commercial_transaction: false, credited_to_traction_metrics: false, unique_person_verified: false, impact_verified: false }), SERVER_CHECKS = ["owner_preauthorized", "challenge_unexpired", "provider_pair_listed", "manifest_verified", "wrong_pin_refused"], CLAIMANT_CHECKS = ["manifest_digest_matches", "challenge_response_matches"], HEX322 = /^[0-9a-f]{32}$/, HEX642 = /^[0-9a-f]{64}$/, RETENTION_MS = 15552e6, MAX_RESPONSE_BYTES = 32768, MAX_ATTEMPTS = 3, AutomaticProofError = class extends Error {
389
+ constructor(code) {
390
+ super(code);
391
+ this.code = code;
392
+ this.name = "AutomaticProofError";
393
+ }
394
+ }, AutomaticHttpError = class extends AutomaticProofError {
395
+ constructor(code, status) {
396
+ super(code);
397
+ this.status = status;
398
+ }
399
+ };
400
+ function requireCondition2(value, code) {
401
+ if (!value) throw new AutomaticProofError(code);
402
+ }
403
+ function exact2(value, keys) {
404
+ return value !== null && typeof value == "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null) && Object.keys(value).length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
405
+ }
406
+ function same2(value, expected) {
407
+ return exact2(value, Object.keys(expected)) && JSON.stringify(value, Object.keys(expected).sort()) === JSON.stringify(expected, Object.keys(expected).sort());
408
+ }
409
+ function iso2(value) {
410
+ return typeof value == "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString() === value;
411
+ }
412
+ function unhex(value) {
413
+ return Uint8Array.from(value.match(/../g) ?? [], (pair) => Number.parseInt(pair, 16));
414
+ }
415
+ function parseAutomaticHandoff(text) {
416
+ requireCondition2(typeof text == "string" && new TextEncoder().encode(text).length <= AUTOMATIC_HANDOFF_MAX_BYTES, "handoff_invalid");
417
+ let value;
418
+ try {
419
+ value = JSON.parse(text);
420
+ } catch {
421
+ throw new AutomaticProofError("handoff_invalid");
422
+ }
423
+ return requireCondition2(exact2(value, ["schema", "run_id", "completion_capability"]) && value.schema === AUTOMATIC_HANDOFF_SCHEMA && typeof value.run_id == "string" && HEX322.test(value.run_id) && typeof value.completion_capability == "string" && HEX642.test(value.completion_capability), "handoff_invalid"), { schema: AUTOMATIC_HANDOFF_SCHEMA, run_id: value.run_id, completion_capability: value.completion_capability };
424
+ }
425
+ async function requestJson(kind, handoff, body, fetchImpl) {
426
+ let url = kind === "issuer" ? AUTOMATIC_ISSUER : `${AUTOMATIC_BASE}/${kind}`, controller = new AbortController(), timer, reader, response, deadlineAt = performance.now() + (kind === "submit" ? 2e4 : 1e4), deadline = new Promise((_, reject) => {
427
+ timer = setTimeout(() => {
428
+ controller.abort(), reject(new AutomaticProofError("completion_unavailable"));
429
+ }, kind === "submit" ? 2e4 : 1e4);
430
+ });
431
+ try {
432
+ let headers = { accept: "application/json" };
433
+ kind !== "issuer" && (headers["content-type"] = "application/json", headers.authorization = `Bearer ${handoff.completion_capability}`);
434
+ let fetched = Promise.resolve(fetchImpl(url, { method: kind === "issuer" ? "GET" : "POST", headers, ...kind === "issuer" ? {} : { body: JSON.stringify(body) }, redirect: "error", credentials: "omit", cache: "no-store", referrerPolicy: "no-referrer", signal: controller.signal })).then((value2) => {
435
+ if (controller.signal.aborted) throw value2.body && !value2.body.locked && value2.body.cancel().catch(() => {
436
+ }), new AutomaticProofError("completion_unavailable");
437
+ return value2;
438
+ });
439
+ response = await Promise.race([fetched, deadline]), requireCondition2(performance.now() < deadlineAt, "completion_unavailable"), requireCondition2(!response.redirected && response.type !== "opaqueredirect" && (response.url === "" || response.url === url) && response.status !== 0 && !(response.status >= 300 && response.status < 400), "response_invalid"), requireCondition2(/^application\/json(?:\s*;|$)/i.test(response.headers.get("content-type") ?? "") && response.body, "response_invalid");
440
+ let length = response.headers.get("content-length");
441
+ requireCondition2(length === null || /^\d+$/.test(length) && Number(length) <= MAX_RESPONSE_BYTES, "response_invalid"), reader = response.body.getReader();
442
+ let chunks = [], size = 0, reads = 0, emptyReads = 0;
443
+ for (; ; ) {
444
+ requireCondition2(performance.now() < deadlineAt, "completion_unavailable"), requireCondition2(++reads <= 2048, "response_invalid");
445
+ let next = await Promise.race([reader.read(), deadline]);
446
+ if (requireCondition2(performance.now() < deadlineAt, "completion_unavailable"), next.done) break;
447
+ if (next.value.byteLength === 0) {
448
+ requireCondition2(++emptyReads <= 128, "response_invalid");
449
+ continue;
450
+ }
451
+ size += next.value.byteLength, requireCondition2(size <= MAX_RESPONSE_BYTES, "response_invalid"), chunks.push(next.value);
452
+ }
453
+ let bytes = new Uint8Array(size), offset = 0;
454
+ for (let chunk of chunks) bytes.set(chunk, offset), offset += chunk.byteLength;
455
+ let value;
456
+ try {
457
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
458
+ } catch {
459
+ throw new AutomaticProofError("response_invalid");
460
+ }
461
+ if (requireCondition2(!JSON.stringify(value).includes(handoff.completion_capability), "response_invalid"), !response.ok) {
462
+ requireCondition2(exact2(value, ["schema", "ok", "error"]) && value.schema === "voidpay.proof-collection.error/v4" && value.ok === false && exact2(value.error, ["code"]) && typeof value.error.code == "string", "response_invalid");
463
+ let code = response.status === 401 || response.status === 403 ? "permission_refused" : response.status === 404 || response.status === 410 ? "run_expired_or_revoked" : response.status === 429 ? value.error.code === "verifier_attempts_exhausted" ? "attempts_exhausted" : "rate_limited" : "completion_unavailable";
464
+ throw new AutomaticHttpError(code, response.status);
465
+ }
466
+ return value;
467
+ } catch (error) {
468
+ throw error instanceof AutomaticProofError ? error : new AutomaticProofError("completion_unavailable");
469
+ } finally {
470
+ clearTimeout(timer), controller.abort(), reader ? (reader.cancel().catch(() => {
471
+ }), reader.releaseLock()) : response?.body && !response.body.locked && response.body.cancel().catch(() => {
472
+ });
473
+ }
474
+ }
475
+ async function issuerKeys(handoff, fetchImpl) {
476
+ let value = await requestJson("issuer", handoff, void 0, fetchImpl);
477
+ requireCondition2(exact2(value, ["schema", "issuer", "keys", "unknown_key_policy"]) && value.schema === "voidpay.proof-collection.issuer/v2" && value.issuer === AUTOMATIC_ISSUER && value.unknown_key_policy === "untrusted; never trust an embedded receipt key" && Array.isArray(value.keys) && value.keys.length > 0 && value.keys.length <= 8, "issuer_invalid");
478
+ let keys = /* @__PURE__ */ new Map();
479
+ for (let key of value.keys) requireCondition2(exact2(key, ["issuer", "key_id", "algorithm", "public_key_hex", "status"]) && key.issuer === AUTOMATIC_ISSUER && key.algorithm === "Ed25519" && key.status === "active" && typeof key.key_id == "string" && HEX642.test(key.key_id) && typeof key.public_key_hex == "string" && HEX642.test(key.public_key_hex) && !keys.has(key.key_id) && await sha256Hex(unhex(key.public_key_hex)) === key.key_id, "issuer_invalid"), keys.set(key.key_id, key.public_key_hex);
480
+ return keys;
481
+ }
482
+ async function verifyComplete(value, handoff, fetchImpl, now) {
483
+ requireCondition2(exact2(value, ["schema", "ok", "status", "run_id", "exercise", "record", "already_completed"]) && value.schema === "voidpay.proof-collection.result/v4" && value.ok === true && value.status === "complete" && value.run_id === handoff.run_id && typeof value.already_completed == "boolean", "response_invalid");
484
+ let record = value.record;
485
+ requireCondition2(exact2(record, ["event_id", "visibility", "public_url", "expires_at", "receipt"]) && typeof record.event_id == "string" && HEX322.test(record.event_id) && iso2(record.expires_at) && (record.visibility === "private" || record.visibility === "public") && (value.already_completed || record.visibility === "private") && record.public_url === (record.visibility === "private" ? null : `https://api.voidly.ai/v2/proofs/collections/public/${record.event_id}`) && exact2(record.receipt, ["statement", "signature_hex"]), "receipt_invalid");
486
+ let receipt = record.receipt, s = receipt.statement;
487
+ requireCondition2(exact2(s, ["schema", "issuer", "key_id", "event_id", "run_binding_sha256", "exercise_binding_sha256", "artwork", "task", "computation_task", "checked_at", "completed_at", "outcome", "checks", "provider", "scope", "boundary"]) && s.schema === "voidpay.proof-collection.receipt/v4" && s.issuer === AUTOMATIC_ISSUER && s.event_id === record.event_id && typeof s.key_id == "string" && HEX642.test(s.key_id) && typeof s.run_binding_sha256 == "string" && HEX642.test(s.run_binding_sha256) && typeof s.exercise_binding_sha256 == "string" && HEX642.test(s.exercise_binding_sha256) && typeof receipt.signature_hex == "string" && /^[0-9a-f]{128}$/.test(receipt.signature_hex) && same2(s.task, AUTOMATIC_TASK) && same2(s.computation_task, SESSIONS_PROOFS_TASK) && s.scope === AUTOMATIC_SCOPE && same2(s.boundary, AUTOMATIC_BOUNDARY) && s.outcome === "PASS" && iso2(s.checked_at) && iso2(s.completed_at) && s.checked_at <= s.completed_at && Date.parse(s.completed_at) <= now + 3e4 && Date.parse(record.expires_at) === Date.parse(s.completed_at) + RETENTION_MS && Date.parse(record.expires_at) > now && same2(s.provider, { provider_did: SESSIONS_PROOFS_PROVIDER.provider_did, manifest_url: SESSIONS_PROOFS_PROVIDER.manifest_url }) && same2(s.artwork, { recipe: "contours-v1", seed: record.event_id }) && exact2(s.checks, ["server", "claimant"]) && exact2(s.checks.server, SERVER_CHECKS) && exact2(s.checks.claimant, CLAIMANT_CHECKS) && [...Object.values(s.checks.server), ...Object.values(s.checks.claimant)].every((check) => check === true), "receipt_invalid");
488
+ let exercise;
489
+ try {
490
+ exercise = parsePublicExercise(JSON.stringify(value.exercise), Date.parse(s.checked_at));
491
+ } catch {
492
+ throw new AutomaticProofError("receipt_invalid");
493
+ }
494
+ requireCondition2(exercise.challenge_id === handoff.run_id && Date.parse(exercise.issued_at) <= Date.parse(s.checked_at) && Date.parse(exercise.expires_at) > Date.parse(s.completed_at) && await sha256Hex(new TextEncoder().encode(AUTOMATIC_RUN_BINDING_DOMAIN + handoff.run_id)) === s.run_binding_sha256 && await sha256Hex(new TextEncoder().encode(automaticCanonicalJson(exercise))) === s.exercise_binding_sha256, "receipt_invalid");
495
+ let key = (await issuerKeys(handoff, fetchImpl)).get(s.key_id);
496
+ requireCondition2(key && nacl3.sign.detached.verify(new TextEncoder().encode(AUTOMATIC_RECEIPT_DOMAIN + automaticCanonicalJson(s)), unhex(receipt.signature_hex), unhex(key)), "receipt_invalid");
497
+ let svg = proofArtworkSvg(record.event_id), result = { schema: "voidly.session.automatic-proof/v1", ok: true, saved_proof: true, event_id: record.event_id, visibility: record.visibility, already_completed: value.already_completed, receipt: JSON.parse(JSON.stringify(receipt)), artwork: { recipe: "contours-v1", seed: record.event_id, svg, sha256: await sha256Hex(new TextEncoder().encode(svg)) }, return_url: "https://voidly.ai/pay/proofs", payment_boundary: "STOP" };
498
+ return requireCondition2(!JSON.stringify(result).includes(handoff.completion_capability), "response_invalid"), result;
499
+ }
500
+ function pending(value, runId) {
501
+ return exact2(value, ["schema", "ok", "status", "run_id", "record"]) && value.schema === "voidpay.proof-collection.result/v4" && value.ok === true && value.status === "pending" && value.run_id === runId && value.record === null;
502
+ }
503
+ function transient(error) {
504
+ return error instanceof AutomaticHttpError ? error.status === 409 || error.status >= 500 : error instanceof AutomaticProofError && error.code === "completion_unavailable";
505
+ }
506
+ async function completeAutomaticProof(text, options = {}) {
507
+ let handoff = parseAutomaticHandoff(text), fetchImpl = options.fetchImpl ?? fetch, now = () => options.nowMs ?? Date.now();
508
+ requireCondition2((await runSessionsSelfTest()).ok, "self_test_failed");
509
+ let mayHaveSaved = false;
510
+ try {
511
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) try {
512
+ let existing = await requestJson("result", handoff, { run_id: handoff.run_id }, fetchImpl);
513
+ if (!pending(existing, handoff.run_id)) return await verifyComplete(existing, handoff, fetchImpl, now());
514
+ let fresh = await requestJson("exercise", handoff, { run_id: handoff.run_id }, fetchImpl);
515
+ if (exact2(fresh, ["schema", "ok", "status", "run_id", "exercise", "record", "already_completed"])) return await verifyComplete(fresh, handoff, fetchImpl, now());
516
+ requireCondition2(exact2(fresh, ["schema", "ok", "action", "run_id", "exercise"]) && fresh.schema === "voidpay.proof-collection.response/v4" && fresh.ok === true && fresh.action === "exercise" && fresh.run_id === handoff.run_id, "response_invalid");
517
+ let exercise = parsePublicExercise(JSON.stringify(fresh.exercise), now());
518
+ requireCondition2(exercise.challenge_id === handoff.run_id, "response_invalid");
519
+ let result = await runPublicExercise(JSON.stringify(exercise), { fetchImpl, nowMs: options.nowMs });
520
+ mayHaveSaved = true;
521
+ let completed = await requestJson("submit", handoff, { run_id: handoff.run_id, result }, fetchImpl);
522
+ return await verifyComplete(completed, handoff, fetchImpl, now());
523
+ } catch (error) {
524
+ if (error instanceof PublicExerciseError) {
525
+ if (error.code !== "upstream_unavailable") throw new AutomaticProofError("provider_check_failed");
526
+ error = new AutomaticProofError("completion_unavailable");
527
+ }
528
+ if (!transient(error)) throw error;
529
+ attempt < MAX_ATTEMPTS - 1 && await new Promise((resolve) => setTimeout(resolve, 1e3));
530
+ }
531
+ if (mayHaveSaved) try {
532
+ let existing = await requestJson("result", handoff, { run_id: handoff.run_id }, fetchImpl);
533
+ if (!pending(existing, handoff.run_id)) return await verifyComplete(existing, handoff, fetchImpl, now());
534
+ } catch (error) {
535
+ if (!transient(error)) throw error;
536
+ }
537
+ throw new AutomaticProofError(mayHaveSaved ? "completion_uncertain" : "completion_unavailable");
538
+ } finally {
539
+ handoff.completion_capability = "";
540
+ }
541
+ }
542
+ async function exportProofArtifacts(eventId, verifiedReceipt) {
543
+ let artworkPath = null, receiptPath = null;
544
+ try {
545
+ if (!/^[0-9a-f]{32}$/.test(eventId)) throw new Error("event_invalid");
546
+ let svg = proofArtworkSvg(eventId), receipt = JSON.stringify(verifiedReceipt, null, 2) + `
547
+ `, fs = await import("node:fs/promises"), { constants } = await import("node:fs"), { join } = await import("node:path");
548
+ if (!Number.isInteger(constants.O_NOFOLLOW) || constants.O_NOFOLLOW === 0) throw new Error("no_follow_unavailable");
549
+ let directory = await fs.mkdtemp(join(process.cwd(), "voidpay-proof-"));
550
+ await fs.chmod(directory, 448);
551
+ let stat = await fs.lstat(directory);
552
+ if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error("directory_invalid");
553
+ let create = async (name, bytes) => {
554
+ let path = join(directory, name), handle = await fs.open(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
555
+ try {
556
+ await handle.writeFile(bytes, "utf8"), await handle.sync();
557
+ } finally {
558
+ await handle.close();
559
+ }
560
+ return path;
561
+ };
562
+ return receiptPath = await create("proof-receipt.json", receipt), artworkPath = await create("proof-artwork.svg", svg), { ok: true, artwork_path: artworkPath, receipt_path: receiptPath, error: null };
563
+ } catch {
564
+ return { ok: false, artwork_path: artworkPath, receipt_path: receiptPath, error: "local_export_failed" };
565
+ }
566
+ }
567
+ async function input(maxBytes = PUBLIC_EXERCISE_MAX_BYTES) {
367
568
  return new Promise((resolve, reject) => {
368
569
  let chunks = [], size = 0, cleanup = () => {
369
570
  clearTimeout(timer), process.stdin.removeListener("data", onData), process.stdin.removeListener("end", onEnd), process.stdin.removeListener("error", onError), process.stdin.pause();
370
571
  }, fail = () => {
371
572
  cleanup(), reject(new Error("input_invalid"));
372
573
  }, onData = (chunk) => {
373
- size += chunk.byteLength, size > PUBLIC_EXERCISE_MAX_BYTES ? fail() : chunks.push(chunk);
574
+ size += chunk.byteLength, size > maxBytes ? fail() : chunks.push(chunk);
374
575
  }, onEnd = () => {
375
576
  cleanup();
376
577
  try {
@@ -387,15 +588,27 @@ async function main() {
387
588
  if (major < 20 || major === 20 && minor < 3) return process.stderr.write(`This CLI requires Node.js 20.3 or newer. No check was run.
388
589
  `), 1;
389
590
  let args = process.argv.slice(2);
591
+ if (args.length === 1 && args[0] === "complete") try {
592
+ let result = await completeAutomaticProof(await input(AUTOMATIC_HANDOFF_MAX_BYTES)), artifacts = await exportProofArtifacts(result.event_id, result.receipt), { artwork, receipt, ...summary } = result;
593
+ return process.stdout.write(JSON.stringify({ artifacts, outcome: "PASS", checks_passed: 7, receipt_verified: true, ...summary, receipt, artwork: { recipe: artwork.recipe, seed: artwork.seed, sha256: artwork.sha256, ...artifacts.ok ? {} : { svg: artwork.svg } } }, null, 2) + `
594
+ `), process.stderr.write(`PASS \u2014 seven checks verified. ${result.already_completed ? "Your existing proof was recovered" : "Your proof was saved"}. ${result.visibility === "private" ? "Saved privately; nothing published by this command." : "This record was already made public separately."}
595
+ `), artifacts.ok || process.stderr.write(`The proof is saved, but local export failed. Use the returned SVG to save or display the art; do not start another proof.
596
+ `), 0;
597
+ } catch (error) {
598
+ let code = error instanceof AutomaticProofError ? error.code : "completion_unavailable", next = code === "run_expired_or_revoked" || code === "attempts_exhausted" || code === "permission_refused" ? "This instruction cannot continue. Check your records at https://voidly.ai/pay/proofs before approving a new run." : "A submission may already be saved; retry the SAME instruction or check https://voidly.ai/pay/proofs. Never create a replacement run just to retry.";
599
+ return process.stderr.write(`Automatic proof stopped: ${code}. Completion was not verified. ${next}
600
+ `), 1;
601
+ }
390
602
  if (args.length === 2 && args[0] === "artwork" && /^[0-9a-f]{32}$/.test(args[1])) return process.stdout.write(proofArtworkSvg(args[1]) + `
391
603
  `), process.stderr.write(`Decorative artwork only. The supplied identifier was not checked with a server.
392
604
  `), 0;
393
- if (args.length !== 1 || !["self-test", "public-check", "--help"].includes(args[0])) return process.stderr.write(`Usage: voidly-session self-test | public-check | artwork <saved-event-id> | --help
605
+ if (args.length !== 1 || !["self-test", "public-check", "--help"].includes(args[0])) return process.stderr.write(`Usage: voidly-session self-test | public-check | complete | artwork <saved-event-id> | --help
394
606
  `), 1;
395
607
  if (args[0] === "--help") return process.stdout.write(`self-test: offline SDK verification; no network.
396
608
  public-check: public exercise JSON on stdin; two fixed public GETs; result JSON on stdout.
609
+ complete: one-proof completion credential on stdin; fixed first-party requests; saves one preauthorized private PASS, verifies its signed receipt and creates new local artwork/receipt files.
397
610
  artwork <saved-event-id>: decorative SVG on stdout, generated locally; no savedness verification.
398
- These commands do not save proofs, publish or perform payment. Review the installed package before running it.
611
+ Only complete saves proofs. No command publishes or performs payment. Review the installed package before running it.
399
612
  `), 0;
400
613
  try {
401
614
  if (args[0] === "self-test") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidly/session",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "The voidpay session client. Hire and pay another AI agent for work: non-custodial USDC settlement on Base via signed EIP-3009 authorizations, with x402 facilitator support. The rail never holds, custodies or submits funds — it only verifies.",
5
5
  "keywords": [
6
6
  "agent-payments",
@@ -46,6 +46,11 @@
46
46
  "import": "./dist/proofs.mjs",
47
47
  "default": "./dist/proofs.mjs"
48
48
  },
49
+ "./proofs-auto": {
50
+ "types": "./dist/proofsAuto.d.ts",
51
+ "import": "./dist/proofsAuto.mjs",
52
+ "default": "./dist/proofsAuto.mjs"
53
+ },
49
54
  "./package.json": "./package.json"
50
55
  },
51
56
  "bin": {