motebit 1.4.2 → 1.4.3

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.
Files changed (2) hide show
  1. package/dist/index.js +807 -514
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -982,6 +982,7 @@ var init_audience = __esm({
982
982
  "task:submit",
983
983
  "admin:query",
984
984
  "proposal",
985
+ "receipts:read",
985
986
  "account:balance",
986
987
  "account:deposit",
987
988
  "account:withdraw",
@@ -8061,6 +8062,7 @@ async function* runTurnStreaming(deps, userMessage, options) {
8061
8062
  let iteration = 0;
8062
8063
  let toolCallsSucceeded = 0;
8063
8064
  let toolCallsBlocked = 0;
8065
+ let toolCallsDenied = 0;
8064
8066
  let toolCallsFailed = 0;
8065
8067
  const toolResultsLog = [];
8066
8068
  let lastToolName = "";
@@ -8154,6 +8156,7 @@ async function* runTurnStreaming(deps, userMessage, options) {
8154
8156
  const decision = deps.policyGate.validate(toolDef, toolCall.args, turnCtx);
8155
8157
  if (!decision.allowed) {
8156
8158
  toolCallsBlocked++;
8159
+ toolCallsDenied++;
8157
8160
  yield {
8158
8161
  type: "tool_status",
8159
8162
  name: toolCall.name,
@@ -8591,6 +8594,7 @@ ${correction}`;
8591
8594
  iterations: iteration,
8592
8595
  toolCallsSucceeded,
8593
8596
  toolCallsBlocked,
8597
+ toolCallsDenied,
8594
8598
  toolCallsFailed,
8595
8599
  ...turnCtx && turnCtx.costAccumulated > 0 ? { totalTokens: turnCtx.costAccumulated } : {}
8596
8600
  }
@@ -8638,6 +8642,7 @@ var dist_exports2 = {};
8638
8642
  __export(dist_exports2, {
8639
8643
  ADJUDICATOR_VOTE_SUITE: () => ADJUDICATOR_VOTE_SUITE,
8640
8644
  AGENT_SETTLEMENT_ANCHOR_SUITE: () => AGENT_SETTLEMENT_ANCHOR_SUITE,
8645
+ APPROVAL_DECISION_SUITE: () => APPROVAL_DECISION_SUITE,
8641
8646
  BALANCE_WAIVER_SUITE: () => BALANCE_WAIVER_SUITE,
8642
8647
  COLLABORATIVE_RECEIPT_SUITE: () => COLLABORATIVE_RECEIPT_SUITE,
8643
8648
  COMPUTER_SESSION_RECEIPT_SUITE: () => COMPUTER_SESSION_RECEIPT_SUITE,
@@ -8709,6 +8714,7 @@ __export(dist_exports2, {
8709
8714
  resolveTreeHashVersion: () => resolveTreeHashVersion,
8710
8715
  sha256: () => sha2562,
8711
8716
  signAdjudicatorVote: () => signAdjudicatorVote,
8717
+ signApprovalDecision: () => signApprovalDecision,
8712
8718
  signBalanceWaiver: () => signBalanceWaiver,
8713
8719
  signBySuite: () => signBySuite,
8714
8720
  signCertAsDelegate: () => signCertAsDelegate,
@@ -8747,6 +8753,7 @@ __export(dist_exports2, {
8747
8753
  verify: () => verify,
8748
8754
  verifyAdjudicatorVote: () => verifyAdjudicatorVote,
8749
8755
  verifyAgentSettlementAnchor: () => verifyAgentSettlementAnchor,
8756
+ verifyApprovalDecision: () => verifyApprovalDecision,
8750
8757
  verifyBalanceWaiver: () => verifyBalanceWaiver,
8751
8758
  verifyBySuite: () => verifyBySuite,
8752
8759
  verifyCollaborativeReceipt: () => verifyCollaborativeReceipt,
@@ -11970,387 +11977,6 @@ async function verifyMerkleInclusion(leaf, index, siblings, layerSizes, expected
11970
11977
  }
11971
11978
  return toHex(current) === expectedRoot;
11972
11979
  }
11973
- function signatureWithoutValue(sig) {
11974
- return { suite: sig.suite, public_key: sig.public_key };
11975
- }
11976
- function canonicalizeSkillManifestBytes(manifest, body) {
11977
- const motebitForCanonical = manifest.motebit.signature ? {
11978
- ...manifest.motebit,
11979
- signature: signatureWithoutValue(manifest.motebit.signature)
11980
- } : manifest.motebit;
11981
- const manifestForCanonical = { ...manifest, motebit: motebitForCanonical };
11982
- const canonical = canonicalJson(manifestForCanonical);
11983
- const manifestBytes = new TextEncoder().encode(canonical);
11984
- const out = new Uint8Array(manifestBytes.length + 1 + body.length);
11985
- out.set(manifestBytes, 0);
11986
- out[manifestBytes.length] = 10;
11987
- out.set(body, manifestBytes.length + 1);
11988
- return out;
11989
- }
11990
- function canonicalizeSkillEnvelopeBytes(envelope) {
11991
- const envelopeForCanonical = {
11992
- ...envelope,
11993
- signature: signatureWithoutValue(envelope.signature)
11994
- };
11995
- const canonical = canonicalJson(envelopeForCanonical);
11996
- return new TextEncoder().encode(canonical);
11997
- }
11998
- async function signSkillManifest(unsigned, privateKey, publicKey, body) {
11999
- const publicKeyHex = bytesToLowerHex(publicKey);
12000
- const unsignedWithSig = {
12001
- ...unsigned,
12002
- motebit: {
12003
- ...unsigned.motebit,
12004
- signature: {
12005
- suite: SKILL_SIGNATURE_SUITE,
12006
- public_key: publicKeyHex,
12007
- value: ""
12008
- // placeholder; stripped by canonicalize
12009
- }
12010
- }
12011
- };
12012
- const message2 = canonicalizeSkillManifestBytes(unsignedWithSig, body);
12013
- const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message2, privateKey);
12014
- return {
12015
- ...unsigned,
12016
- motebit: {
12017
- ...unsigned.motebit,
12018
- signature: {
12019
- suite: SKILL_SIGNATURE_SUITE,
12020
- public_key: publicKeyHex,
12021
- value: toBase64Url(sig)
12022
- }
12023
- }
12024
- };
12025
- }
12026
- async function signSkillEnvelope(unsigned, privateKey, publicKey) {
12027
- const publicKeyHex = bytesToLowerHex(publicKey);
12028
- const unsignedWithSig = {
12029
- ...unsigned,
12030
- signature: {
12031
- suite: SKILL_SIGNATURE_SUITE,
12032
- public_key: publicKeyHex,
12033
- value: ""
12034
- // placeholder; stripped by canonicalize
12035
- }
12036
- };
12037
- const message2 = canonicalizeSkillEnvelopeBytes(unsignedWithSig);
12038
- const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message2, privateKey);
12039
- return {
12040
- ...unsigned,
12041
- signature: {
12042
- suite: SKILL_SIGNATURE_SUITE,
12043
- public_key: publicKeyHex,
12044
- value: toBase64Url(sig)
12045
- }
12046
- };
12047
- }
12048
- async function verifySkillManifest(manifest, body, publicKey) {
12049
- return (await verifySkillManifestDetailed(manifest, body, publicKey)).valid;
12050
- }
12051
- async function verifySkillManifestDetailed(manifest, body, publicKey) {
12052
- const sig = manifest.motebit.signature;
12053
- if (!sig) return { valid: false, reason: "no_signature" };
12054
- if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
12055
- const publicKeyHex = bytesToLowerHex(publicKey);
12056
- if (sig.public_key.toLowerCase() !== publicKeyHex) {
12057
- return { valid: false, reason: "bad_public_key" };
12058
- }
12059
- let sigBytes;
12060
- try {
12061
- sigBytes = fromBase64Url(sig.value);
12062
- } catch {
12063
- return { valid: false, reason: "bad_signature_value" };
12064
- }
12065
- const message2 = canonicalizeSkillManifestBytes(manifest, body);
12066
- const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message2, sigBytes, publicKey);
12067
- return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
12068
- }
12069
- async function verifySkillEnvelope(envelope, publicKey) {
12070
- return (await verifySkillEnvelopeDetailed(envelope, publicKey)).valid;
12071
- }
12072
- async function verifySkillEnvelopeDetailed(envelope, publicKey) {
12073
- const sig = envelope.signature;
12074
- if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
12075
- const publicKeyHex = bytesToLowerHex(publicKey);
12076
- if (sig.public_key.toLowerCase() !== publicKeyHex) {
12077
- return { valid: false, reason: "bad_public_key" };
12078
- }
12079
- let sigBytes;
12080
- try {
12081
- sigBytes = fromBase64Url(sig.value);
12082
- } catch {
12083
- return { valid: false, reason: "bad_signature_value" };
12084
- }
12085
- const message2 = canonicalizeSkillEnvelopeBytes(envelope);
12086
- const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message2, sigBytes, publicKey);
12087
- return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
12088
- }
12089
- function bytesToLowerHex(bytes) {
12090
- let hex = "";
12091
- for (const b3 of bytes) {
12092
- hex += b3.toString(16).padStart(2, "0");
12093
- }
12094
- return hex;
12095
- }
12096
- function decodeSkillSignaturePublicKey(sig) {
12097
- return hexToBytes5(sig.public_key);
12098
- }
12099
- function verifyHardwareAttestationClaim(claim, expectedIdentityPublicKeyHex, verifiers, deviceCheckContext) {
12100
- const platform = claim.platform;
12101
- const errors = [];
12102
- switch (platform) {
12103
- case "secure_enclave":
12104
- return verifySecureEnclaveClaim(claim, expectedIdentityPublicKeyHex);
12105
- case "software":
12106
- errors.push({
12107
- message: "platform `software` is a no-hardware sentinel; no verification channel"
12108
- });
12109
- return { valid: false, platform: "software", errors };
12110
- case "device_check":
12111
- if (verifiers?.deviceCheck) {
12112
- return dispatchInjected(
12113
- platform,
12114
- verifiers.deviceCheck(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
12115
- );
12116
- }
12117
- errors.push({
12118
- message: `platform \`${platform}\` verifier not wired \u2014 pass { deviceCheck: deviceCheckVerifier(...) } from @motebit/crypto-appattest to enable`
12119
- });
12120
- return { valid: false, platform, errors };
12121
- case "tpm":
12122
- if (verifiers?.tpm) {
12123
- return dispatchInjected(
12124
- platform,
12125
- verifiers.tpm(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
12126
- );
12127
- }
12128
- errors.push({
12129
- message: `platform \`${platform}\` verifier not wired \u2014 pass { tpm: ... } via the verifiers parameter to enable`
12130
- });
12131
- return { valid: false, platform, errors };
12132
- case "play_integrity":
12133
- if (verifiers?.playIntegrity) {
12134
- return dispatchInjected(
12135
- platform,
12136
- verifiers.playIntegrity(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
12137
- );
12138
- }
12139
- errors.push({
12140
- message: `platform \`${platform}\` verifier not wired \u2014 pass { playIntegrity: ... } via the verifiers parameter to enable`
12141
- });
12142
- return { valid: false, platform, errors };
12143
- case "webauthn":
12144
- if (verifiers?.webauthn) {
12145
- return dispatchInjected(
12146
- platform,
12147
- verifiers.webauthn(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
12148
- );
12149
- }
12150
- errors.push({
12151
- message: `platform \`${platform}\` verifier not wired \u2014 pass { webauthn: webauthnVerifier(...) } from @motebit/crypto-webauthn to enable`
12152
- });
12153
- return { valid: false, platform, errors };
12154
- case "android_keystore":
12155
- if (verifiers?.androidKeystore) {
12156
- return dispatchInjected(
12157
- platform,
12158
- verifiers.androidKeystore(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
12159
- );
12160
- }
12161
- errors.push({
12162
- message: `platform \`${platform}\` verifier not wired \u2014 pass { androidKeystore: androidKeystoreVerifier(...) } from @motebit/crypto-android-keystore to enable`
12163
- });
12164
- return { valid: false, platform, errors };
12165
- default:
12166
- errors.push({
12167
- message: `unknown platform \`${claim.platform}\` \u2014 not in the declared enum`
12168
- });
12169
- return { valid: false, platform: null, errors };
12170
- }
12171
- }
12172
- async function dispatchInjected(platform, result) {
12173
- const awaited = await Promise.resolve(result);
12174
- return {
12175
- valid: awaited.valid,
12176
- platform,
12177
- errors: awaited.errors ?? []
12178
- };
12179
- }
12180
- function verifySecureEnclaveClaim(claim, expectedIdentityPublicKeyHex) {
12181
- const errors = [];
12182
- const platform = "secure_enclave";
12183
- if (claim.key_exported === true) {
12184
- }
12185
- if (!claim.attestation_receipt) {
12186
- errors.push({
12187
- message: "secure_enclave claim missing `attestation_receipt`"
12188
- });
12189
- return { valid: false, platform, errors };
12190
- }
12191
- const parts = claim.attestation_receipt.split(".");
12192
- if (parts.length !== 2) {
12193
- errors.push({
12194
- message: `attestation_receipt must be 2 base64url parts separated by '.'; got ${parts.length}`
12195
- });
12196
- return { valid: false, platform, errors };
12197
- }
12198
- const [bodyB64, sigB64] = parts;
12199
- let bodyBytes;
12200
- let sigBytes;
12201
- try {
12202
- bodyBytes = fromBase64Url(bodyB64);
12203
- sigBytes = fromBase64Url(sigB64);
12204
- } catch (err2) {
12205
- errors.push({
12206
- message: `base64url decode failed: ${err2 instanceof Error ? err2.message : String(err2)}`
12207
- });
12208
- return { valid: false, platform, errors };
12209
- }
12210
- let bodyJson;
12211
- try {
12212
- bodyJson = JSON.parse(new TextDecoder().decode(bodyBytes));
12213
- } catch (err2) {
12214
- errors.push({
12215
- message: `body JSON parse failed: ${err2 instanceof Error ? err2.message : String(err2)}`
12216
- });
12217
- return { valid: false, platform, errors };
12218
- }
12219
- const bodyCheck = parseSecureEnclaveBody(bodyJson);
12220
- if (bodyCheck.kind === "error") {
12221
- errors.push({ message: bodyCheck.reason });
12222
- return { valid: false, platform, errors };
12223
- }
12224
- const body = bodyCheck.body;
12225
- if (body.version !== "1") {
12226
- errors.push({
12227
- message: `unsupported body version '${body.version}' (expected '1')`
12228
- });
12229
- return { valid: false, platform, errors };
12230
- }
12231
- if (body.algorithm !== "ecdsa-p256-sha256") {
12232
- errors.push({
12233
- message: `unsupported body algorithm '${body.algorithm}' (expected 'ecdsa-p256-sha256')`
12234
- });
12235
- return { valid: false, platform, errors };
12236
- }
12237
- let sigValid;
12238
- try {
12239
- sigValid = verifyP256EcdsaSha256(body.se_public_key, bodyBytes, sigBytes);
12240
- } catch (err2) {
12241
- errors.push({
12242
- message: `p-256 verification crashed: ${err2 instanceof Error ? err2.message : String(err2)}`
12243
- });
12244
- return { valid: false, platform, errors };
12245
- }
12246
- if (!sigValid) {
12247
- errors.push({
12248
- message: "p-256 signature does not verify against body + se_public_key"
12249
- });
12250
- return { valid: false, platform, errors };
12251
- }
12252
- if (body.identity_public_key.toLowerCase() !== expectedIdentityPublicKeyHex.toLowerCase()) {
12253
- errors.push({
12254
- message: `identity_public_key mismatch: body names ${body.identity_public_key.slice(0, 16)}\u2026, expected ${expectedIdentityPublicKeyHex.slice(0, 16)}\u2026`
12255
- });
12256
- return { valid: false, platform, errors };
12257
- }
12258
- return {
12259
- valid: true,
12260
- platform,
12261
- se_public_key: body.se_public_key,
12262
- attested_at: body.attested_at,
12263
- errors: []
12264
- };
12265
- }
12266
- function parseSecureEnclaveBody(raw) {
12267
- if (raw === null || typeof raw !== "object") {
12268
- return { kind: "error", reason: "body is not a JSON object" };
12269
- }
12270
- const r2 = raw;
12271
- const fields = [
12272
- "version",
12273
- "algorithm",
12274
- "motebit_id",
12275
- "device_id",
12276
- "identity_public_key",
12277
- "se_public_key",
12278
- "attested_at"
12279
- ];
12280
- for (const f6 of fields) {
12281
- if (!(f6 in r2)) {
12282
- return { kind: "error", reason: `body missing required field '${f6}'` };
12283
- }
12284
- }
12285
- if (typeof r2.version !== "string" || typeof r2.algorithm !== "string" || typeof r2.motebit_id !== "string" || typeof r2.device_id !== "string" || typeof r2.identity_public_key !== "string" || typeof r2.se_public_key !== "string" || typeof r2.attested_at !== "number") {
12286
- return { kind: "error", reason: "body field types invalid" };
12287
- }
12288
- return {
12289
- kind: "ok",
12290
- body: {
12291
- version: r2.version,
12292
- algorithm: r2.algorithm,
12293
- motebit_id: r2.motebit_id,
12294
- device_id: r2.device_id,
12295
- identity_public_key: r2.identity_public_key,
12296
- se_public_key: r2.se_public_key,
12297
- attested_at: r2.attested_at
12298
- }
12299
- };
12300
- }
12301
- function encodeSecureEnclaveReceiptForTest(bodyBytes, sigBytes) {
12302
- return `${toBase64Url2(bodyBytes)}.${toBase64Url2(sigBytes)}`;
12303
- }
12304
- function canonicalSecureEnclaveBodyForTest(body) {
12305
- const full = {
12306
- version: "1",
12307
- algorithm: "ecdsa-p256-sha256",
12308
- ...body
12309
- };
12310
- return new TextEncoder().encode(canonicalJson(full));
12311
- }
12312
- async function mintSecureEnclaveReceiptForTest(input) {
12313
- const { p256: p2562 } = await Promise.resolve().then(() => (init_nist(), nist_exports));
12314
- const privateKey = p2562.utils.randomSecretKey();
12315
- const sePublicKey = p2562.getPublicKey(privateKey, true);
12316
- const sePublicKeyHex = bytesToHexLocal(sePublicKey);
12317
- const bodyBytes = canonicalSecureEnclaveBodyForTest({
12318
- motebit_id: input.motebit_id,
12319
- device_id: input.device_id,
12320
- identity_public_key: input.identity_public_key,
12321
- se_public_key: sePublicKeyHex,
12322
- attested_at: input.attested_at
12323
- });
12324
- const digest = await sha256Local(bodyBytes);
12325
- const sigBytes = p2562.sign(digest, privateKey, { prehash: false, format: "der" });
12326
- const receipt = encodeSecureEnclaveReceiptForTest(bodyBytes, sigBytes);
12327
- return {
12328
- claim: {
12329
- platform: "secure_enclave",
12330
- key_exported: false,
12331
- attestation_receipt: receipt
12332
- },
12333
- sePublicKeyHex
12334
- };
12335
- }
12336
- function bytesToHexLocal(bytes) {
12337
- let hex = "";
12338
- for (let i3 = 0; i3 < bytes.length; i3++) {
12339
- hex += bytes[i3].toString(16).padStart(2, "0");
12340
- }
12341
- return hex;
12342
- }
12343
- async function sha256Local(bytes) {
12344
- const buf = await crypto.subtle.digest("SHA-256", bytes);
12345
- return new Uint8Array(buf);
12346
- }
12347
- function toBase64Url2(bytes) {
12348
- let binary = "";
12349
- for (let i3 = 0; i3 < bytes.length; i3++) {
12350
- binary += String.fromCharCode(bytes[i3]);
12351
- }
12352
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
12353
- }
12354
11980
  function isReceiptDebugEnabled() {
12355
11981
  const g3 = globalThis;
12356
11982
  if (g3.__motebit_debug_receipt_bytes === true) return true;
@@ -12677,6 +12303,27 @@ async function verifyAdjudicatorVote(vote, peerPublicKey) {
12677
12303
  return false;
12678
12304
  }
12679
12305
  }
12306
+ async function signApprovalDecision(decision, approverPrivateKey, publicKey) {
12307
+ const withKey = publicKey ? { ...decision, public_key: bytesToHex4(publicKey) } : decision;
12308
+ const body = { ...withKey, suite: APPROVAL_DECISION_SUITE };
12309
+ const canonical = canonicalJson(body);
12310
+ const message2 = new TextEncoder().encode(canonical);
12311
+ const sig = await signBySuite(APPROVAL_DECISION_SUITE, message2, approverPrivateKey);
12312
+ const signed = { ...body, signature: toBase64Url(sig) };
12313
+ return Object.freeze(signed);
12314
+ }
12315
+ async function verifyApprovalDecision(decision, approverPublicKey) {
12316
+ if (decision.suite !== APPROVAL_DECISION_SUITE) return false;
12317
+ const { signature, ...body } = decision;
12318
+ const canonical = canonicalJson(body);
12319
+ const message2 = new TextEncoder().encode(canonical);
12320
+ try {
12321
+ const sig = fromBase64Url(signature);
12322
+ return await verifyBySuite(decision.suite, message2, sig, approverPublicKey);
12323
+ } catch {
12324
+ return false;
12325
+ }
12326
+ }
12680
12327
  async function signDisputeResolution(resolution, adjudicatorPrivateKey) {
12681
12328
  const body = { ...resolution, suite: DISPUTE_RESOLUTION_SUITE };
12682
12329
  const canonical = canonicalJson(body);
@@ -13227,6 +12874,387 @@ async function signCredentialBundle(bundle, privateKey) {
13227
12874
  );
13228
12875
  return { ...withHash, signature: toBase64Url(sig) };
13229
12876
  }
12877
+ function signatureWithoutValue(sig) {
12878
+ return { suite: sig.suite, public_key: sig.public_key };
12879
+ }
12880
+ function canonicalizeSkillManifestBytes(manifest, body) {
12881
+ const motebitForCanonical = manifest.motebit.signature ? {
12882
+ ...manifest.motebit,
12883
+ signature: signatureWithoutValue(manifest.motebit.signature)
12884
+ } : manifest.motebit;
12885
+ const manifestForCanonical = { ...manifest, motebit: motebitForCanonical };
12886
+ const canonical = canonicalJson(manifestForCanonical);
12887
+ const manifestBytes = new TextEncoder().encode(canonical);
12888
+ const out = new Uint8Array(manifestBytes.length + 1 + body.length);
12889
+ out.set(manifestBytes, 0);
12890
+ out[manifestBytes.length] = 10;
12891
+ out.set(body, manifestBytes.length + 1);
12892
+ return out;
12893
+ }
12894
+ function canonicalizeSkillEnvelopeBytes(envelope) {
12895
+ const envelopeForCanonical = {
12896
+ ...envelope,
12897
+ signature: signatureWithoutValue(envelope.signature)
12898
+ };
12899
+ const canonical = canonicalJson(envelopeForCanonical);
12900
+ return new TextEncoder().encode(canonical);
12901
+ }
12902
+ async function signSkillManifest(unsigned, privateKey, publicKey, body) {
12903
+ const publicKeyHex = bytesToLowerHex(publicKey);
12904
+ const unsignedWithSig = {
12905
+ ...unsigned,
12906
+ motebit: {
12907
+ ...unsigned.motebit,
12908
+ signature: {
12909
+ suite: SKILL_SIGNATURE_SUITE,
12910
+ public_key: publicKeyHex,
12911
+ value: ""
12912
+ // placeholder; stripped by canonicalize
12913
+ }
12914
+ }
12915
+ };
12916
+ const message2 = canonicalizeSkillManifestBytes(unsignedWithSig, body);
12917
+ const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message2, privateKey);
12918
+ return {
12919
+ ...unsigned,
12920
+ motebit: {
12921
+ ...unsigned.motebit,
12922
+ signature: {
12923
+ suite: SKILL_SIGNATURE_SUITE,
12924
+ public_key: publicKeyHex,
12925
+ value: toBase64Url(sig)
12926
+ }
12927
+ }
12928
+ };
12929
+ }
12930
+ async function signSkillEnvelope(unsigned, privateKey, publicKey) {
12931
+ const publicKeyHex = bytesToLowerHex(publicKey);
12932
+ const unsignedWithSig = {
12933
+ ...unsigned,
12934
+ signature: {
12935
+ suite: SKILL_SIGNATURE_SUITE,
12936
+ public_key: publicKeyHex,
12937
+ value: ""
12938
+ // placeholder; stripped by canonicalize
12939
+ }
12940
+ };
12941
+ const message2 = canonicalizeSkillEnvelopeBytes(unsignedWithSig);
12942
+ const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message2, privateKey);
12943
+ return {
12944
+ ...unsigned,
12945
+ signature: {
12946
+ suite: SKILL_SIGNATURE_SUITE,
12947
+ public_key: publicKeyHex,
12948
+ value: toBase64Url(sig)
12949
+ }
12950
+ };
12951
+ }
12952
+ async function verifySkillManifest(manifest, body, publicKey) {
12953
+ return (await verifySkillManifestDetailed(manifest, body, publicKey)).valid;
12954
+ }
12955
+ async function verifySkillManifestDetailed(manifest, body, publicKey) {
12956
+ const sig = manifest.motebit.signature;
12957
+ if (!sig) return { valid: false, reason: "no_signature" };
12958
+ if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
12959
+ const publicKeyHex = bytesToLowerHex(publicKey);
12960
+ if (sig.public_key.toLowerCase() !== publicKeyHex) {
12961
+ return { valid: false, reason: "bad_public_key" };
12962
+ }
12963
+ let sigBytes;
12964
+ try {
12965
+ sigBytes = fromBase64Url(sig.value);
12966
+ } catch {
12967
+ return { valid: false, reason: "bad_signature_value" };
12968
+ }
12969
+ const message2 = canonicalizeSkillManifestBytes(manifest, body);
12970
+ const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message2, sigBytes, publicKey);
12971
+ return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
12972
+ }
12973
+ async function verifySkillEnvelope(envelope, publicKey) {
12974
+ return (await verifySkillEnvelopeDetailed(envelope, publicKey)).valid;
12975
+ }
12976
+ async function verifySkillEnvelopeDetailed(envelope, publicKey) {
12977
+ const sig = envelope.signature;
12978
+ if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
12979
+ const publicKeyHex = bytesToLowerHex(publicKey);
12980
+ if (sig.public_key.toLowerCase() !== publicKeyHex) {
12981
+ return { valid: false, reason: "bad_public_key" };
12982
+ }
12983
+ let sigBytes;
12984
+ try {
12985
+ sigBytes = fromBase64Url(sig.value);
12986
+ } catch {
12987
+ return { valid: false, reason: "bad_signature_value" };
12988
+ }
12989
+ const message2 = canonicalizeSkillEnvelopeBytes(envelope);
12990
+ const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message2, sigBytes, publicKey);
12991
+ return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
12992
+ }
12993
+ function bytesToLowerHex(bytes) {
12994
+ let hex = "";
12995
+ for (const b3 of bytes) {
12996
+ hex += b3.toString(16).padStart(2, "0");
12997
+ }
12998
+ return hex;
12999
+ }
13000
+ function decodeSkillSignaturePublicKey(sig) {
13001
+ return hexToBytes5(sig.public_key);
13002
+ }
13003
+ function verifyHardwareAttestationClaim(claim, expectedIdentityPublicKeyHex, verifiers, deviceCheckContext) {
13004
+ const platform = claim.platform;
13005
+ const errors = [];
13006
+ switch (platform) {
13007
+ case "secure_enclave":
13008
+ return verifySecureEnclaveClaim(claim, expectedIdentityPublicKeyHex);
13009
+ case "software":
13010
+ errors.push({
13011
+ message: "platform `software` is a no-hardware sentinel; no verification channel"
13012
+ });
13013
+ return { valid: false, platform: "software", errors };
13014
+ case "device_check":
13015
+ if (verifiers?.deviceCheck) {
13016
+ return dispatchInjected(
13017
+ platform,
13018
+ verifiers.deviceCheck(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
13019
+ );
13020
+ }
13021
+ errors.push({
13022
+ message: `platform \`${platform}\` verifier not wired \u2014 pass { deviceCheck: deviceCheckVerifier(...) } from @motebit/crypto-appattest to enable`
13023
+ });
13024
+ return { valid: false, platform, errors };
13025
+ case "tpm":
13026
+ if (verifiers?.tpm) {
13027
+ return dispatchInjected(
13028
+ platform,
13029
+ verifiers.tpm(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
13030
+ );
13031
+ }
13032
+ errors.push({
13033
+ message: `platform \`${platform}\` verifier not wired \u2014 pass { tpm: ... } via the verifiers parameter to enable`
13034
+ });
13035
+ return { valid: false, platform, errors };
13036
+ case "play_integrity":
13037
+ if (verifiers?.playIntegrity) {
13038
+ return dispatchInjected(
13039
+ platform,
13040
+ verifiers.playIntegrity(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
13041
+ );
13042
+ }
13043
+ errors.push({
13044
+ message: `platform \`${platform}\` verifier not wired \u2014 pass { playIntegrity: ... } via the verifiers parameter to enable`
13045
+ });
13046
+ return { valid: false, platform, errors };
13047
+ case "webauthn":
13048
+ if (verifiers?.webauthn) {
13049
+ return dispatchInjected(
13050
+ platform,
13051
+ verifiers.webauthn(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
13052
+ );
13053
+ }
13054
+ errors.push({
13055
+ message: `platform \`${platform}\` verifier not wired \u2014 pass { webauthn: webauthnVerifier(...) } from @motebit/crypto-webauthn to enable`
13056
+ });
13057
+ return { valid: false, platform, errors };
13058
+ case "android_keystore":
13059
+ if (verifiers?.androidKeystore) {
13060
+ return dispatchInjected(
13061
+ platform,
13062
+ verifiers.androidKeystore(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
13063
+ );
13064
+ }
13065
+ errors.push({
13066
+ message: `platform \`${platform}\` verifier not wired \u2014 pass { androidKeystore: androidKeystoreVerifier(...) } from @motebit/crypto-android-keystore to enable`
13067
+ });
13068
+ return { valid: false, platform, errors };
13069
+ default:
13070
+ errors.push({
13071
+ message: `unknown platform \`${claim.platform}\` \u2014 not in the declared enum`
13072
+ });
13073
+ return { valid: false, platform: null, errors };
13074
+ }
13075
+ }
13076
+ async function dispatchInjected(platform, result) {
13077
+ const awaited = await Promise.resolve(result);
13078
+ return {
13079
+ valid: awaited.valid,
13080
+ platform,
13081
+ errors: awaited.errors ?? []
13082
+ };
13083
+ }
13084
+ function verifySecureEnclaveClaim(claim, expectedIdentityPublicKeyHex) {
13085
+ const errors = [];
13086
+ const platform = "secure_enclave";
13087
+ if (claim.key_exported === true) {
13088
+ }
13089
+ if (!claim.attestation_receipt) {
13090
+ errors.push({
13091
+ message: "secure_enclave claim missing `attestation_receipt`"
13092
+ });
13093
+ return { valid: false, platform, errors };
13094
+ }
13095
+ const parts = claim.attestation_receipt.split(".");
13096
+ if (parts.length !== 2) {
13097
+ errors.push({
13098
+ message: `attestation_receipt must be 2 base64url parts separated by '.'; got ${parts.length}`
13099
+ });
13100
+ return { valid: false, platform, errors };
13101
+ }
13102
+ const [bodyB64, sigB64] = parts;
13103
+ let bodyBytes;
13104
+ let sigBytes;
13105
+ try {
13106
+ bodyBytes = fromBase64Url(bodyB64);
13107
+ sigBytes = fromBase64Url(sigB64);
13108
+ } catch (err2) {
13109
+ errors.push({
13110
+ message: `base64url decode failed: ${err2 instanceof Error ? err2.message : String(err2)}`
13111
+ });
13112
+ return { valid: false, platform, errors };
13113
+ }
13114
+ let bodyJson;
13115
+ try {
13116
+ bodyJson = JSON.parse(new TextDecoder().decode(bodyBytes));
13117
+ } catch (err2) {
13118
+ errors.push({
13119
+ message: `body JSON parse failed: ${err2 instanceof Error ? err2.message : String(err2)}`
13120
+ });
13121
+ return { valid: false, platform, errors };
13122
+ }
13123
+ const bodyCheck = parseSecureEnclaveBody(bodyJson);
13124
+ if (bodyCheck.kind === "error") {
13125
+ errors.push({ message: bodyCheck.reason });
13126
+ return { valid: false, platform, errors };
13127
+ }
13128
+ const body = bodyCheck.body;
13129
+ if (body.version !== "1") {
13130
+ errors.push({
13131
+ message: `unsupported body version '${body.version}' (expected '1')`
13132
+ });
13133
+ return { valid: false, platform, errors };
13134
+ }
13135
+ if (body.algorithm !== "ecdsa-p256-sha256") {
13136
+ errors.push({
13137
+ message: `unsupported body algorithm '${body.algorithm}' (expected 'ecdsa-p256-sha256')`
13138
+ });
13139
+ return { valid: false, platform, errors };
13140
+ }
13141
+ let sigValid;
13142
+ try {
13143
+ sigValid = verifyP256EcdsaSha256(body.se_public_key, bodyBytes, sigBytes);
13144
+ } catch (err2) {
13145
+ errors.push({
13146
+ message: `p-256 verification crashed: ${err2 instanceof Error ? err2.message : String(err2)}`
13147
+ });
13148
+ return { valid: false, platform, errors };
13149
+ }
13150
+ if (!sigValid) {
13151
+ errors.push({
13152
+ message: "p-256 signature does not verify against body + se_public_key"
13153
+ });
13154
+ return { valid: false, platform, errors };
13155
+ }
13156
+ if (body.identity_public_key.toLowerCase() !== expectedIdentityPublicKeyHex.toLowerCase()) {
13157
+ errors.push({
13158
+ message: `identity_public_key mismatch: body names ${body.identity_public_key.slice(0, 16)}\u2026, expected ${expectedIdentityPublicKeyHex.slice(0, 16)}\u2026`
13159
+ });
13160
+ return { valid: false, platform, errors };
13161
+ }
13162
+ return {
13163
+ valid: true,
13164
+ platform,
13165
+ se_public_key: body.se_public_key,
13166
+ attested_at: body.attested_at,
13167
+ errors: []
13168
+ };
13169
+ }
13170
+ function parseSecureEnclaveBody(raw) {
13171
+ if (raw === null || typeof raw !== "object") {
13172
+ return { kind: "error", reason: "body is not a JSON object" };
13173
+ }
13174
+ const r2 = raw;
13175
+ const fields = [
13176
+ "version",
13177
+ "algorithm",
13178
+ "motebit_id",
13179
+ "device_id",
13180
+ "identity_public_key",
13181
+ "se_public_key",
13182
+ "attested_at"
13183
+ ];
13184
+ for (const f6 of fields) {
13185
+ if (!(f6 in r2)) {
13186
+ return { kind: "error", reason: `body missing required field '${f6}'` };
13187
+ }
13188
+ }
13189
+ if (typeof r2.version !== "string" || typeof r2.algorithm !== "string" || typeof r2.motebit_id !== "string" || typeof r2.device_id !== "string" || typeof r2.identity_public_key !== "string" || typeof r2.se_public_key !== "string" || typeof r2.attested_at !== "number") {
13190
+ return { kind: "error", reason: "body field types invalid" };
13191
+ }
13192
+ return {
13193
+ kind: "ok",
13194
+ body: {
13195
+ version: r2.version,
13196
+ algorithm: r2.algorithm,
13197
+ motebit_id: r2.motebit_id,
13198
+ device_id: r2.device_id,
13199
+ identity_public_key: r2.identity_public_key,
13200
+ se_public_key: r2.se_public_key,
13201
+ attested_at: r2.attested_at
13202
+ }
13203
+ };
13204
+ }
13205
+ function encodeSecureEnclaveReceiptForTest(bodyBytes, sigBytes) {
13206
+ return `${toBase64Url2(bodyBytes)}.${toBase64Url2(sigBytes)}`;
13207
+ }
13208
+ function canonicalSecureEnclaveBodyForTest(body) {
13209
+ const full = {
13210
+ version: "1",
13211
+ algorithm: "ecdsa-p256-sha256",
13212
+ ...body
13213
+ };
13214
+ return new TextEncoder().encode(canonicalJson(full));
13215
+ }
13216
+ async function mintSecureEnclaveReceiptForTest(input) {
13217
+ const { p256: p2562 } = await Promise.resolve().then(() => (init_nist(), nist_exports));
13218
+ const privateKey = p2562.utils.randomSecretKey();
13219
+ const sePublicKey = p2562.getPublicKey(privateKey, true);
13220
+ const sePublicKeyHex = bytesToHexLocal(sePublicKey);
13221
+ const bodyBytes = canonicalSecureEnclaveBodyForTest({
13222
+ motebit_id: input.motebit_id,
13223
+ device_id: input.device_id,
13224
+ identity_public_key: input.identity_public_key,
13225
+ se_public_key: sePublicKeyHex,
13226
+ attested_at: input.attested_at
13227
+ });
13228
+ const digest = await sha256Local(bodyBytes);
13229
+ const sigBytes = p2562.sign(digest, privateKey, { prehash: false, format: "der" });
13230
+ const receipt = encodeSecureEnclaveReceiptForTest(bodyBytes, sigBytes);
13231
+ return {
13232
+ claim: {
13233
+ platform: "secure_enclave",
13234
+ key_exported: false,
13235
+ attestation_receipt: receipt
13236
+ },
13237
+ sePublicKeyHex
13238
+ };
13239
+ }
13240
+ function bytesToHexLocal(bytes) {
13241
+ let hex = "";
13242
+ for (let i3 = 0; i3 < bytes.length; i3++) {
13243
+ hex += bytes[i3].toString(16).padStart(2, "0");
13244
+ }
13245
+ return hex;
13246
+ }
13247
+ async function sha256Local(bytes) {
13248
+ const buf = await crypto.subtle.digest("SHA-256", bytes);
13249
+ return new Uint8Array(buf);
13250
+ }
13251
+ function toBase64Url2(bytes) {
13252
+ let binary = "";
13253
+ for (let i3 = 0; i3 < bytes.length; i3++) {
13254
+ binary += String.fromCharCode(bytes[i3]);
13255
+ }
13256
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
13257
+ }
13230
13258
  function buildVerificationMethod(publicKey) {
13231
13259
  const did = publicKeyToDidKey(publicKey);
13232
13260
  const fragment = did.slice("did:key:".length);
@@ -14508,6 +14536,9 @@ function detectArtifactType(artifact) {
14508
14536
  if ("credentialSubject" in obj && "issuer" in obj && "proof" in obj) {
14509
14537
  return "credential";
14510
14538
  }
14539
+ if ("invocation_id" in obj && "tool_name" in obj && "motebit_id" in obj && "signature" in obj) {
14540
+ return "tool-invocation";
14541
+ }
14511
14542
  if ("task_id" in obj && "motebit_id" in obj && "signature" in obj && "prompt_hash" in obj) {
14512
14543
  return "receipt";
14513
14544
  }
@@ -14887,23 +14918,26 @@ async function verifyIdentityBindingAnchored(identity, signingKeyHex, atTimestam
14887
14918
  async function verifyReceiptSignature(receipt, publicKey) {
14888
14919
  const { signature, ...body } = receipt;
14889
14920
  if (!signature || signature.trim() === "") {
14890
- return { valid: false, error: "Receipt signature is empty" };
14921
+ return { valid: false, error: "\xA711.2 violation: receipt signature is empty" };
14891
14922
  }
14892
14923
  let sig;
14893
14924
  try {
14894
14925
  sig = fromBase64Url2(signature);
14895
14926
  } catch {
14896
- return { valid: false, error: "Receipt signature is not valid base64url" };
14927
+ return { valid: false, error: "\xA711.2 violation: receipt signature is not valid base64url" };
14897
14928
  }
14898
14929
  if (sig.length !== 64) {
14899
- return { valid: false, error: `Receipt signature must be 64 bytes, got ${sig.length}` };
14930
+ return {
14931
+ valid: false,
14932
+ error: `\xA711.2 violation: receipt signature must be 64 bytes, got ${sig.length}`
14933
+ };
14900
14934
  }
14901
14935
  const canonical = canonicalJson2(body);
14902
14936
  const message2 = new TextEncoder().encode(canonical);
14903
14937
  const valid = await verifyBySuite("motebit-jcs-ed25519-b64-v1", message2, sig, publicKey);
14904
14938
  return { valid };
14905
14939
  }
14906
- async function verifyReceipt(receipt) {
14940
+ async function verifyReceipt(receipt, options) {
14907
14941
  let publicKey = null;
14908
14942
  let signerDid;
14909
14943
  if (receipt.public_key) {
@@ -14924,26 +14958,43 @@ async function verifyReceipt(receipt) {
14924
14958
  type: "receipt",
14925
14959
  valid: false,
14926
14960
  receipt,
14927
- errors: [{ message: "No embedded public_key \u2014 cannot verify without known keys" }],
14961
+ errors: [
14962
+ {
14963
+ message: "\xA711.3 violation: No embedded public_key \u2014 cannot verify without known keys"
14964
+ }
14965
+ ],
14928
14966
  ...delegations2.length > 0 ? { delegations: delegations2 } : {}
14929
14967
  };
14930
14968
  }
14931
14969
  const sigResult = await verifyReceiptSignature(receipt, publicKey);
14932
14970
  const errors = [];
14933
14971
  if (!sigResult.valid) {
14934
- errors.push({ message: sigResult.error ?? "Receipt signature verification failed" });
14972
+ errors.push({
14973
+ message: sigResult.error ?? "\xA711.2 violation: Ed25519 signature did not verify"
14974
+ });
14935
14975
  }
14936
14976
  const delegations = await verifyReceiptDelegations(receipt);
14937
14977
  const delegationErrors = delegations.filter((d4) => !d4.valid);
14938
14978
  for (const d4 of delegationErrors) {
14939
14979
  errors.push({
14940
- message: `Delegation ${d4.receipt?.task_id ?? "unknown"}: verification failed`,
14980
+ message: `\xA711.5 violation: delegation ${d4.receipt?.task_id ?? "unknown"} verification failed`,
14941
14981
  path: `delegation_receipts`
14942
14982
  });
14943
14983
  }
14984
+ let resultHashOk = true;
14985
+ if (options?.strictHashBinding) {
14986
+ const expected = await hash(new TextEncoder().encode(receipt.result));
14987
+ resultHashOk = expected === receipt.result_hash;
14988
+ if (!resultHashOk) {
14989
+ errors.push({
14990
+ message: "result_hash does not equal hex(SHA-256(result)) \u2014 receipt is not self-consistent (strict mode)",
14991
+ path: "result_hash"
14992
+ });
14993
+ }
14994
+ }
14944
14995
  return {
14945
14996
  type: "receipt",
14946
- valid: sigResult.valid && delegationErrors.length === 0,
14997
+ valid: sigResult.valid && delegationErrors.length === 0 && resultHashOk,
14947
14998
  receipt,
14948
14999
  signer: signerDid,
14949
15000
  keySource: "embedded",
@@ -14951,6 +15002,39 @@ async function verifyReceipt(receipt) {
14951
15002
  ...errors.length > 0 ? { errors } : {}
14952
15003
  };
14953
15004
  }
15005
+ async function verifyToolInvocation(receipt) {
15006
+ let publicKey = null;
15007
+ let signerDid;
15008
+ if (receipt.public_key) {
15009
+ try {
15010
+ publicKey = hexToBytes6(receipt.public_key);
15011
+ if (publicKey.length === 32) {
15012
+ signerDid = publicKeyToDidKey2(publicKey);
15013
+ } else {
15014
+ publicKey = null;
15015
+ }
15016
+ } catch {
15017
+ publicKey = null;
15018
+ }
15019
+ }
15020
+ if (!publicKey) {
15021
+ return {
15022
+ type: "tool-invocation",
15023
+ valid: false,
15024
+ toolInvocation: receipt,
15025
+ errors: [{ message: "No embedded public_key \u2014 cannot verify without known keys" }]
15026
+ };
15027
+ }
15028
+ const valid = await verifyToolInvocationReceipt(receipt, publicKey);
15029
+ return {
15030
+ type: "tool-invocation",
15031
+ valid,
15032
+ toolInvocation: receipt,
15033
+ signer: signerDid,
15034
+ keySource: "embedded",
15035
+ ...valid ? {} : { errors: [{ message: "Ed25519 signature did not verify" }] }
15036
+ };
15037
+ }
14954
15038
  async function verifyReceiptDelegations(receipt) {
14955
15039
  if (!receipt.delegation_receipts || receipt.delegation_receipts.length === 0) {
14956
15040
  return [];
@@ -15190,23 +15274,16 @@ async function verifyPresentation(vp, clockSkewSeconds = DEFAULT_CLOCK_SKEW_SECO
15190
15274
  async function verify(artifact, options) {
15191
15275
  const detected = detectArtifactType(artifact);
15192
15276
  if (detected === null) {
15193
- const fallbackType = options?.expectedType ?? "identity";
15277
+ const expected = options?.expectedType;
15194
15278
  return {
15195
- type: fallbackType,
15279
+ type: "unknown",
15196
15280
  valid: false,
15197
- ...fallbackType === "identity" ? { identity: null } : {},
15198
- ...fallbackType === "receipt" ? { receipt: null } : {},
15199
- ...fallbackType === "credential" ? { credential: null } : {},
15200
- ...fallbackType === "presentation" ? { presentation: null } : {},
15201
- ...fallbackType === "skill" ? {
15202
- envelope: null,
15203
- steps: {
15204
- envelope: { valid: false, reason: "wrong_suite" },
15205
- body_hash: null,
15206
- files: []
15281
+ reason: "unrecognized_artifact_type",
15282
+ errors: [
15283
+ {
15284
+ message: expected ? `Unrecognized artifact format (expected "${expected}")` : "Unrecognized artifact format"
15207
15285
  }
15208
- } : {},
15209
- errors: [{ message: "Unrecognized artifact format" }]
15286
+ ]
15210
15287
  };
15211
15288
  }
15212
15289
  if (options?.expectedType && options.expectedType !== detected) {
@@ -15215,6 +15292,7 @@ async function verify(artifact, options) {
15215
15292
  valid: false,
15216
15293
  ...detected === "identity" ? { identity: null } : {},
15217
15294
  ...detected === "receipt" ? { receipt: null } : {},
15295
+ ...detected === "tool-invocation" ? { toolInvocation: null } : {},
15218
15296
  ...detected === "credential" ? { credential: null } : {},
15219
15297
  ...detected === "presentation" ? { presentation: null } : {},
15220
15298
  ...detected === "skill" ? {
@@ -15237,6 +15315,7 @@ async function verify(artifact, options) {
15237
15315
  type: detected,
15238
15316
  valid: false,
15239
15317
  ...detected === "receipt" ? { receipt: null } : {},
15318
+ ...detected === "tool-invocation" ? { toolInvocation: null } : {},
15240
15319
  ...detected === "credential" ? { credential: null } : {},
15241
15320
  ...detected === "presentation" ? { presentation: null } : {},
15242
15321
  ...detected === "skill" ? {
@@ -15255,7 +15334,9 @@ async function verify(artifact, options) {
15255
15334
  case "identity":
15256
15335
  return verifyIdentity(resolved);
15257
15336
  case "receipt":
15258
- return verifyReceipt(resolved);
15337
+ return verifyReceipt(resolved, options);
15338
+ case "tool-invocation":
15339
+ return verifyToolInvocation(resolved);
15259
15340
  case "credential":
15260
15341
  return verifyCredential(
15261
15342
  resolved,
@@ -15272,7 +15353,7 @@ async function verify(artifact, options) {
15272
15353
  return verifySkillEnvelopeArtifact(resolved);
15273
15354
  }
15274
15355
  }
15275
- var __defProp2, __getOwnPropNames2, __esm2, __export2, hasHexBuiltin, hexes, asciis, oidNist, init_utils, HashMD, SHA256_IV, SHA384_IV, SHA512_IV, init_md, U32_MASK64, _32n, shrSH, shrSL, rotrSH, rotrSL, rotrBH, rotrBL, add3L, add3H, add4L, add4H, add5L, add5H, init_u64, SHA256_K, SHA256_W, SHA2_32B, _SHA256, K512, SHA512_Kh, SHA512_Kl, SHA512_W_H, SHA512_W_L, SHA2_64B, _SHA512, _SHA384, sha256, sha512, sha384, init_sha2, abytes3, anumber2, bytesToHex3, concatBytes3, hexToBytes3, isBytes3, randomBytes3, _0n, _1n, isPosBig, bitMask, init_utils2, _0n2, _1n2, _2n, _3n, _4n, _5n, _7n, _8n, _9n, _16n, FIELD_FIELDS, FIELD_SQRT, _Field, init_modular, _0n3, _1n3, pointPrecomputes, pointWindowSizes, wNAF2, init_curve, init_fft, os2ip, _DST_scalar, init_hash_to_curve, validateSigners, validateCommitmentsNum, AggErr, init_frost, _DST_scalarBytes, init_oprf, _HMAC, hmac, init_hmac, divNearest, DERErr, DER, _0n4, _1n4, _2n2, _3n2, _4n2, init_weierstrass, nist_exports, p256_CURVE, p384_CURVE, p521_CURVE, p256_Point, p256, p256_hasher, p256_oprf, p256_FROST, p384_Point, p384, p384_hasher, p384_oprf, p521_Point, p521, p521_hasher, p521_oprf, init_nist, ed25519_CURVE, P, N, Gx, Gy, _a, _d, h, L, captureTrace, err, isBig, isStr, isBytes, abytes, u8n, u8fr, padh, bytesToHex, C, _ch, hexToBytes, cr, subtle, concatBytes, randomBytes, big, assertRange, M, P_MASK, modP, modN, invert, callHash, checkDigest, apoint, B256, Point, G, I, numTo32bLE, bytesToNumberLE, pow2, pow_2_252_3, RM1, uvRatio, modL_LE, sha512a, hash2extK, getExtendedPublicKeyAsync, getPublicKeyAsync, hashFinishA, _sign, signAsync, defaultVerifyOpts, _verify, verifyAsync, hashes, randomSecretKey, keygenAsync, W, scalarBits, pwindows, pwindowSize, precompute, Gpows, ctneg, wNAF, SIGNED_TOKEN_SUITE, BASE58_ALPHABET, NODE_TAG_V2, LEAF_TAG_V2, SKILL_SIGNATURE_SUITE, EXECUTION_RECEIPT_SUITE, TOOL_INVOCATION_RECEIPT_SUITE, COMPUTER_SESSION_RECEIPT_SUITE, DELEGATION_TOKEN_SUITE, ADJUDICATOR_VOTE_SUITE, DISPUTE_RESOLUTION_SUITE, DISPUTE_REQUEST_SUITE, DISPUTE_EVIDENCE_SUITE, DISPUTE_APPEAL_SUITE, CONSOLIDATION_RECEIPT_SUITE, BALANCE_WAIVER_SUITE, SETTLEMENT_RECORD_SUITE, FEDERATION_SETTLEMENT_RECORD_SUITE, KEY_SUCCESSION_SUITE, GUARDIAN_REVOCATION_SUITE, COLLABORATIVE_RECEIPT_SUITE, DEVICE_REGISTRATION_SUITE, DEVICE_REGISTRATION_MAX_AGE_MS, VC_TYPE_GRADIENT2, VC_TYPE_REPUTATION2, VC_TYPE_TRUST2, ONE_HOUR_MS, CONTENT_ARTIFACT_SUITE, CREDENTIAL_ANCHOR_SUITE, REVOCATION_ANCHOR_SUITE, AGENT_SETTLEMENT_ANCHOR_SUITE, FEDERATION_SETTLEMENT_ANCHOR_SUITE, DELETION_CERTIFICATE_SUITE, WITNESS_OMISSION_DISPUTE_WINDOW_MS, EMPTY_FEDERATION_GRAPH_ANCHOR_ROOT, REASON_TABLE, WITNESS_OMISSION_DISPUTE_SUITE, FEDERATION_HEARTBEAT_SUITE, HEARTBEAT_FRESHNESS_WINDOW_MS, BASE58_ALPHABET2, IDENTITY_FILE_SUITE, SIG_PREFIX, SIG_SUFFIX, DEFAULT_CLOCK_SKEW_SECONDS;
15356
+ var __defProp2, __getOwnPropNames2, __esm2, __export2, hasHexBuiltin, hexes, asciis, oidNist, init_utils, HashMD, SHA256_IV, SHA384_IV, SHA512_IV, init_md, U32_MASK64, _32n, shrSH, shrSL, rotrSH, rotrSL, rotrBH, rotrBL, add3L, add3H, add4L, add4H, add5L, add5H, init_u64, SHA256_K, SHA256_W, SHA2_32B, _SHA256, K512, SHA512_Kh, SHA512_Kl, SHA512_W_H, SHA512_W_L, SHA2_64B, _SHA512, _SHA384, sha256, sha512, sha384, init_sha2, abytes3, anumber2, bytesToHex3, concatBytes3, hexToBytes3, isBytes3, randomBytes3, _0n, _1n, isPosBig, bitMask, init_utils2, _0n2, _1n2, _2n, _3n, _4n, _5n, _7n, _8n, _9n, _16n, FIELD_FIELDS, FIELD_SQRT, _Field, init_modular, _0n3, _1n3, pointPrecomputes, pointWindowSizes, wNAF2, init_curve, init_fft, os2ip, _DST_scalar, init_hash_to_curve, validateSigners, validateCommitmentsNum, AggErr, init_frost, _DST_scalarBytes, init_oprf, _HMAC, hmac, init_hmac, divNearest, DERErr, DER, _0n4, _1n4, _2n2, _3n2, _4n2, init_weierstrass, nist_exports, p256_CURVE, p384_CURVE, p521_CURVE, p256_Point, p256, p256_hasher, p256_oprf, p256_FROST, p384_Point, p384, p384_hasher, p384_oprf, p521_Point, p521, p521_hasher, p521_oprf, init_nist, ed25519_CURVE, P, N, Gx, Gy, _a, _d, h, L, captureTrace, err, isBig, isStr, isBytes, abytes, u8n, u8fr, padh, bytesToHex, C, _ch, hexToBytes, cr, subtle, concatBytes, randomBytes, big, assertRange, M, P_MASK, modP, modN, invert, callHash, checkDigest, apoint, B256, Point, G, I, numTo32bLE, bytesToNumberLE, pow2, pow_2_252_3, RM1, uvRatio, modL_LE, sha512a, hash2extK, getExtendedPublicKeyAsync, getPublicKeyAsync, hashFinishA, _sign, signAsync, defaultVerifyOpts, _verify, verifyAsync, hashes, randomSecretKey, keygenAsync, W, scalarBits, pwindows, pwindowSize, precompute, Gpows, ctneg, wNAF, SIGNED_TOKEN_SUITE, BASE58_ALPHABET, NODE_TAG_V2, LEAF_TAG_V2, EXECUTION_RECEIPT_SUITE, TOOL_INVOCATION_RECEIPT_SUITE, COMPUTER_SESSION_RECEIPT_SUITE, DELEGATION_TOKEN_SUITE, ADJUDICATOR_VOTE_SUITE, DISPUTE_RESOLUTION_SUITE, DISPUTE_REQUEST_SUITE, DISPUTE_EVIDENCE_SUITE, DISPUTE_APPEAL_SUITE, APPROVAL_DECISION_SUITE, CONSOLIDATION_RECEIPT_SUITE, BALANCE_WAIVER_SUITE, SETTLEMENT_RECORD_SUITE, FEDERATION_SETTLEMENT_RECORD_SUITE, KEY_SUCCESSION_SUITE, GUARDIAN_REVOCATION_SUITE, COLLABORATIVE_RECEIPT_SUITE, DEVICE_REGISTRATION_SUITE, DEVICE_REGISTRATION_MAX_AGE_MS, SKILL_SIGNATURE_SUITE, VC_TYPE_GRADIENT2, VC_TYPE_REPUTATION2, VC_TYPE_TRUST2, ONE_HOUR_MS, CONTENT_ARTIFACT_SUITE, CREDENTIAL_ANCHOR_SUITE, REVOCATION_ANCHOR_SUITE, AGENT_SETTLEMENT_ANCHOR_SUITE, FEDERATION_SETTLEMENT_ANCHOR_SUITE, DELETION_CERTIFICATE_SUITE, WITNESS_OMISSION_DISPUTE_WINDOW_MS, EMPTY_FEDERATION_GRAPH_ANCHOR_ROOT, REASON_TABLE, WITNESS_OMISSION_DISPUTE_SUITE, FEDERATION_HEARTBEAT_SUITE, HEARTBEAT_FRESHNESS_WINDOW_MS, BASE58_ALPHABET2, IDENTITY_FILE_SUITE, SIG_PREFIX, SIG_SUFFIX, DEFAULT_CLOCK_SKEW_SECONDS;
15276
15357
  var init_dist5 = __esm({
15277
15358
  "../../packages/crypto/dist/index.js"() {
15278
15359
  "use strict";
@@ -17108,7 +17189,6 @@ var init_dist5 = __esm({
17108
17189
  BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
17109
17190
  NODE_TAG_V2 = new Uint8Array([1]);
17110
17191
  LEAF_TAG_V2 = new Uint8Array([0]);
17111
- SKILL_SIGNATURE_SUITE = "motebit-jcs-ed25519-b64-v1";
17112
17192
  EXECUTION_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
17113
17193
  TOOL_INVOCATION_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
17114
17194
  COMPUTER_SESSION_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
@@ -17118,6 +17198,7 @@ var init_dist5 = __esm({
17118
17198
  DISPUTE_REQUEST_SUITE = "motebit-jcs-ed25519-b64-v1";
17119
17199
  DISPUTE_EVIDENCE_SUITE = "motebit-jcs-ed25519-b64-v1";
17120
17200
  DISPUTE_APPEAL_SUITE = "motebit-jcs-ed25519-b64-v1";
17201
+ APPROVAL_DECISION_SUITE = "motebit-jcs-ed25519-b64-v1";
17121
17202
  CONSOLIDATION_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
17122
17203
  BALANCE_WAIVER_SUITE = "motebit-jcs-ed25519-b64-v1";
17123
17204
  SETTLEMENT_RECORD_SUITE = "motebit-jcs-ed25519-b64-v1";
@@ -17127,6 +17208,7 @@ var init_dist5 = __esm({
17127
17208
  COLLABORATIVE_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
17128
17209
  DEVICE_REGISTRATION_SUITE = "motebit-jcs-ed25519-b64-v1";
17129
17210
  DEVICE_REGISTRATION_MAX_AGE_MS = 5 * 60 * 1e3;
17211
+ SKILL_SIGNATURE_SUITE = "motebit-jcs-ed25519-b64-v1";
17130
17212
  VC_TYPE_GRADIENT2 = "AgentGradientCredential";
17131
17213
  VC_TYPE_REPUTATION2 = "AgentReputationCredential";
17132
17214
  VC_TYPE_TRUST2 = "AgentTrustCredential";
@@ -18641,7 +18723,7 @@ var init_config2 = __esm({
18641
18723
  "src/config.ts"() {
18642
18724
  "use strict";
18643
18725
  init_esm_shims();
18644
- VERSION = true ? "1.4.2" : "0.0.0-dev";
18726
+ VERSION = true ? "1.4.3" : "0.0.0-dev";
18645
18727
  CONFIG_DIR = process.env["MOTEBIT_CONFIG_DIR"] ?? path2.join(os.homedir(), ".motebit");
18646
18728
  CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
18647
18729
  RELAY_DIR = path2.join(CONFIG_DIR, "relay");
@@ -40565,7 +40647,9 @@ var init_streaming = __esm({
40565
40647
  args: chunk.args,
40566
40648
  userMessage,
40567
40649
  runId,
40568
- quorum: chunk.quorum
40650
+ quorum: chunk.quorum,
40651
+ riskLevel: chunk.risk_level,
40652
+ requestedAt: Date.now()
40569
40653
  };
40570
40654
  const approvalStore = this.deps.getApprovalStore();
40571
40655
  if (chunk.quorum && approvalStore) {
@@ -40617,6 +40701,7 @@ var init_streaming = __esm({
40617
40701
  this.deps.pushStateUpdate({ processing: 0.9, attention: 0.8 });
40618
40702
  this.deps.setSpeaking(true);
40619
40703
  try {
40704
+ await this.signAndEmitApprovalDecision(pending, approved);
40620
40705
  if (approved) {
40621
40706
  yield { type: "tool_status", name: pending.toolName, status: "calling" };
40622
40707
  const toolRegistry = this.deps.getToolRegistry();
@@ -40662,6 +40747,55 @@ var init_streaming = __esm({
40662
40747
  this._isProcessing = false;
40663
40748
  }
40664
40749
  }
40750
+ /**
40751
+ * Build, sign, and emit the human-consent decision for a resolved approval.
40752
+ * Signed by the APPROVER's device key — consent is the approver's own
40753
+ * assertion, mirroring how the worker signs its own refusal. Commits to
40754
+ * `args_hash` (never raw args), the gated `approval_id` (= tool_call_id), the
40755
+ * verdict, and the requested/resolved timestamps, so a third party can verify
40756
+ * offline that this approver consented to THIS call at THIS time.
40757
+ *
40758
+ * Best-effort and fail-open on the AUDIT side only: if the signing material
40759
+ * is absent (no device key wired) or signing throws, the resume still
40760
+ * proceeds — the decision artifact is an audit record layered on top of the
40761
+ * already-enforced gate, not the gate itself. Mirrors `onToolInvocation`.
40762
+ */
40763
+ async signAndEmitApprovalDecision(pending, approved) {
40764
+ const sink = this.deps.onApprovalDecision;
40765
+ if (!sink)
40766
+ return;
40767
+ const deviceId = this.deps.getDeviceId?.() ?? null;
40768
+ const privateKey = this.deps.getSigningPrivateKey?.() ?? null;
40769
+ const publicKey = this.deps.getSigningPublicKey?.() ?? null;
40770
+ if (!deviceId || !privateKey || !this.deps.motebitId)
40771
+ return;
40772
+ try {
40773
+ const argsHash = await hashToolPayload(pending.args);
40774
+ const unsigned = {
40775
+ approval_id: pending.toolCallId,
40776
+ motebit_id: this.deps.motebitId,
40777
+ device_id: deviceId,
40778
+ tool_name: pending.toolName,
40779
+ args_hash: argsHash,
40780
+ risk_level: pending.riskLevel ?? 0,
40781
+ verdict: approved ? "approved" : "denied",
40782
+ requested_at: pending.requestedAt,
40783
+ resolved_at: Date.now(),
40784
+ ...approved ? {} : { denied_reason: "User denied this tool call." },
40785
+ ...pending.runId != null ? { run_id: pending.runId } : {}
40786
+ };
40787
+ const signed = await signApprovalDecision(unsigned, privateKey, publicKey ?? void 0);
40788
+ try {
40789
+ sink(signed);
40790
+ } catch (err2) {
40791
+ const msg = err2 instanceof Error ? err2.message : String(err2);
40792
+ console.warn(`[streaming] onApprovalDecision sink threw: ${msg}`);
40793
+ }
40794
+ } catch (err2) {
40795
+ const msg = err2 instanceof Error ? err2.message : String(err2);
40796
+ console.warn(`[streaming] failed to sign approval decision: ${msg}`);
40797
+ }
40798
+ }
40665
40799
  /**
40666
40800
  * Record a single approval vote for multi-party (quorum) approval.
40667
40801
  * - If no quorum is configured, delegates to resumeAfterApproval (backward compat).
@@ -42105,6 +42239,8 @@ async function* handleAgentTask(deps, task, privateKey, deviceId, publicKey, opt
42105
42239
  const toolsUsed = [];
42106
42240
  let memoriesFormed = 0;
42107
42241
  let status = "completed";
42242
+ let toolCallsSucceeded = 0;
42243
+ let toolCallsDenied = 0;
42108
42244
  try {
42109
42245
  const stream = deps.sendMessageStreaming(task.prompt, void 0, {
42110
42246
  delegationScope: options?.delegatedScope
@@ -42124,6 +42260,8 @@ async function* handleAgentTask(deps, task, privateKey, deviceId, publicKey, opt
42124
42260
  } else if (chunk.type === "result") {
42125
42261
  responseText = chunk.result.response;
42126
42262
  memoriesFormed = chunk.result.memoriesFormed.length;
42263
+ toolCallsSucceeded = chunk.result.toolCallsSucceeded;
42264
+ toolCallsDenied = chunk.result.toolCallsDenied ?? 0;
42127
42265
  }
42128
42266
  yield chunk;
42129
42267
  }
@@ -42134,6 +42272,10 @@ async function* handleAgentTask(deps, task, privateKey, deviceId, publicKey, opt
42134
42272
  clearTimeout(timeout);
42135
42273
  deps.restoreConversationContext(savedCtx);
42136
42274
  }
42275
+ if (status === "completed" && toolCallsSucceeded === 0 && toolCallsDenied > 0) {
42276
+ status = "denied";
42277
+ responseText = `Task refused by governance: ${toolCallsDenied} action(s) exceeded this motebit's policy (deny_above / denylist / delegated scope) and no permitted action completed.` + (responseText ? ` Model note: ${responseText}` : "");
42278
+ }
42137
42279
  const delegationReceipts = [];
42138
42280
  for (const adapter of deps.getMcpAdapters()) {
42139
42281
  if (adapter.getAndResetDelegationReceipts) {
@@ -42612,6 +42754,8 @@ var init_motebit_runtime = __esm({
42612
42754
  * persist the payload beyond the call.
42613
42755
  */
42614
42756
  _onToolActivity;
42757
+ /** Surface sink for signed human-consent decisions (the "approve" band artifact). */
42758
+ _onApprovalDecision;
42615
42759
  /**
42616
42760
  * receipts-1 — in-memory buffer of recent signed
42617
42761
  * `ToolInvocationReceipt`s, capped at `RECEIPTS_BUFFER_CAP`.
@@ -42635,6 +42779,8 @@ var init_motebit_runtime = __esm({
42635
42779
  */
42636
42780
  _recentReceipts = [];
42637
42781
  static RECEIPTS_BUFFER_CAP = 50;
42782
+ /** In-session buffer of signed human-consent decisions (newest last), same cap + contract as `_recentReceipts`. */
42783
+ _recentApprovalDecisions = [];
42638
42784
  constructor(config, adapters) {
42639
42785
  assertSpeciesIntegrity();
42640
42786
  this.motebitId = config.motebitId;
@@ -42656,6 +42802,7 @@ var init_motebit_runtime = __esm({
42656
42802
  }
42657
42803
  };
42658
42804
  this._onToolActivity = config.onToolActivity ?? null;
42805
+ this._onApprovalDecision = config.onApprovalDecision ?? null;
42659
42806
  this.compactionThreshold = config.compactionThreshold ?? 1e3;
42660
42807
  this.mcpConfigs = config.mcpServers ?? [];
42661
42808
  this.taskRouter = config.taskRouter ? new TaskRouter(config.taskRouter) : null;
@@ -42957,10 +43104,36 @@ var init_motebit_runtime = __esm({
42957
43104
  getSigningPrivateKey: () => this._signingKeys?.privateKey ?? null,
42958
43105
  getSigningPublicKey: () => this._signingKeys?.publicKey ?? null,
42959
43106
  onToolInvocation: this._onToolInvocation ? (receipt) => this._onToolInvocation?.(receipt) : void 0,
42960
- onToolActivity: this._onToolActivity ? (event) => this._onToolActivity?.(event) : void 0
43107
+ onToolActivity: this._onToolActivity ? (event) => this._onToolActivity?.(event) : void 0,
43108
+ onApprovalDecision: (decision) => this.recordApprovalDecision(decision)
42961
43109
  });
42962
43110
  this.wireLoopDeps();
42963
43111
  }
43112
+ /**
43113
+ * Buffer a signed human-consent decision and forward it to the surface sink —
43114
+ * the exact shape as `_onToolInvocation`: push to the in-session buffer FIRST
43115
+ * (so `getRecentApprovalDecisions()` is correct regardless of whether a
43116
+ * surface listener throws), then fan out. Deliberately does NOT append an
43117
+ * event-log entry: the daemon/goal path already records its own
43118
+ * `ApprovalApproved`/`ApprovalDenied` goal-audit event (`apps/cli` scheduler),
43119
+ * so a runtime append would double-emit; and durable cross-restart archival of
43120
+ * the signed artifact is a separate, consumer-shaped concern (a dedicated
43121
+ * retrieval surface), deferred until a consumer forces its shape — the same
43122
+ * sequencing the delegation refusal path used for receipt retrieval.
43123
+ */
43124
+ recordApprovalDecision(decision) {
43125
+ this._recentApprovalDecisions.push(decision);
43126
+ if (this._recentApprovalDecisions.length > _MotebitRuntime.RECEIPTS_BUFFER_CAP) {
43127
+ this._recentApprovalDecisions.shift();
43128
+ }
43129
+ if (this._onApprovalDecision) {
43130
+ try {
43131
+ this._onApprovalDecision(decision);
43132
+ } catch (err2) {
43133
+ this._logger.warn(`[runtime] surface onApprovalDecision threw: ${err2 instanceof Error ? err2.message : String(err2)}`);
43134
+ }
43135
+ }
43136
+ }
42964
43137
  // === Lifecycle ===
42965
43138
  async init(target) {
42966
43139
  this._sessionStartedAt = this._clock?.() ?? Date.now();
@@ -44854,6 +45027,18 @@ var init_motebit_runtime = __esm({
44854
45027
  getRecentReceipts() {
44855
45028
  return this._recentReceipts;
44856
45029
  }
45030
+ /**
45031
+ * Recent signed human-consent decisions (`ApprovalDecision`), newest last,
45032
+ * capped at `RECEIPTS_BUFFER_CAP`. The "approve" governance band's
45033
+ * counterpart to `getRecentReceipts()`: each entry is the approver's signed,
45034
+ * offline-verifiable verdict over a gated tool call. Empty when no decisions
45035
+ * were rendered this session, or when no signing key was wired (fail-closed —
45036
+ * the runtime emits nothing rather than an unsigned decision). Durable
45037
+ * cross-restart archival is a deferred, consumer-shaped concern.
45038
+ */
45039
+ getRecentApprovalDecisions() {
45040
+ return this._recentApprovalDecisions;
45041
+ }
44857
45042
  /**
44858
45043
  * Per-session pixel-passthrough consent. Composed into the loop's
44859
45044
  * `projectForAi` pixel gate alongside provider mode and effective
@@ -74223,6 +74408,59 @@ var init_accounts = __esm({
74223
74408
  }
74224
74409
  });
74225
74410
 
74411
+ // ../../services/relay/dist/receipts-store.js
74412
+ function receiptTaskId(r2) {
74413
+ return r2.relay_task_id != null && r2.relay_task_id.length > 0 ? r2.relay_task_id : r2.task_id;
74414
+ }
74415
+ function persistReceiptChain(db, receipt, parentTaskId = null, depth = 0, receivedAt = Date.now()) {
74416
+ if (depth > MAX_RECEIPT_DEPTH) {
74417
+ logger13.warn("receipt.persist.depth_limit_exceeded", {
74418
+ motebitId: receipt.motebit_id,
74419
+ taskId: receiptTaskId(receipt),
74420
+ depth,
74421
+ maxDepth: MAX_RECEIPT_DEPTH
74422
+ });
74423
+ return;
74424
+ }
74425
+ const taskId = receiptTaskId(receipt);
74426
+ const receiptJson = canonicalJson(receipt);
74427
+ db.prepare(INSERT_SQL).run(receipt.motebit_id, taskId, parentTaskId, depth, receipt.status, receipt.suite, receipt.public_key ?? "", receipt.signature, receipt.invocation_origin ?? null, receiptJson, receivedAt);
74428
+ const children = receipt.delegation_receipts ?? [];
74429
+ for (const child of children) {
74430
+ persistReceiptChain(db, child, taskId, depth + 1, receivedAt);
74431
+ }
74432
+ }
74433
+ function getStoredReceiptJson(db, motebitId, taskId) {
74434
+ const row = db.prepare("SELECT receipt_json FROM relay_receipts WHERE motebit_id = ? AND task_id = ?").get(motebitId, taskId);
74435
+ return row?.receipt_json ?? null;
74436
+ }
74437
+ function listStoredReceipts(db, motebitId, limit = 50) {
74438
+ const capped = Math.max(1, Math.min(limit, 200));
74439
+ return db.prepare(`SELECT task_id, status, invocation_origin, received_at, receipt_json
74440
+ FROM relay_receipts
74441
+ WHERE motebit_id = ? AND depth = 0
74442
+ ORDER BY received_at DESC
74443
+ LIMIT ?`).all(motebitId, capped);
74444
+ }
74445
+ var logger13, MAX_RECEIPT_DEPTH, INSERT_SQL;
74446
+ var init_receipts_store = __esm({
74447
+ "../../services/relay/dist/receipts-store.js"() {
74448
+ "use strict";
74449
+ init_esm_shims();
74450
+ init_dist6();
74451
+ init_logger();
74452
+ logger13 = createLogger({ service: "receipts-store" });
74453
+ MAX_RECEIPT_DEPTH = 10;
74454
+ INSERT_SQL = `
74455
+ INSERT OR IGNORE INTO relay_receipts (
74456
+ motebit_id, task_id, parent_task_id, depth, status,
74457
+ suite, public_key, signature, invocation_origin,
74458
+ receipt_json, received_at
74459
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
74460
+ `;
74461
+ }
74462
+ });
74463
+
74226
74464
  // ../../services/relay/dist/agent-revocation.js
74227
74465
  async function buildSignedRevocationRecord(relayIdentity, input, now = Date.now()) {
74228
74466
  const payload = {
@@ -74603,6 +74841,8 @@ function registerAgentRoutes(deps) {
74603
74841
  agentAudience = "credentials";
74604
74842
  } else if (path19.includes("/presentation")) {
74605
74843
  agentAudience = "credentials:present";
74844
+ } else if (path19.includes("/receipts")) {
74845
+ agentAudience = "receipts:read";
74606
74846
  } else {
74607
74847
  agentAudience = "admin:query";
74608
74848
  }
@@ -74613,6 +74853,27 @@ function registerAgentRoutes(deps) {
74613
74853
  c3.set("callerMotebitId", claims.mid);
74614
74854
  await next();
74615
74855
  });
74856
+ app.get("/api/v1/agents/:motebitId/receipts", (c3) => {
74857
+ const motebitId = c3.req.param("motebitId");
74858
+ const caller = c3.get("callerMotebitId");
74859
+ if (caller !== void 0 && caller !== motebitId) {
74860
+ throw new HTTPException4(403, { message: "Cannot read another motebit's receipts" });
74861
+ }
74862
+ const limit = parseInt(c3.req.query("limit") ?? "50", 10) || 50;
74863
+ return c3.json({ receipts: listStoredReceipts(moteDb.db, motebitId, limit) });
74864
+ });
74865
+ app.get("/api/v1/agents/:motebitId/receipts/:taskId", (c3) => {
74866
+ const motebitId = c3.req.param("motebitId");
74867
+ const caller = c3.get("callerMotebitId");
74868
+ if (caller !== void 0 && caller !== motebitId) {
74869
+ throw new HTTPException4(403, { message: "Cannot read another motebit's receipts" });
74870
+ }
74871
+ const json = getStoredReceiptJson(moteDb.db, motebitId, c3.req.param("taskId"));
74872
+ if (json === null) {
74873
+ throw new HTTPException4(404, { message: "No such receipt" });
74874
+ }
74875
+ return c3.body(json, 200, { "content-type": "application/json" });
74876
+ });
74616
74877
  app.post("/api/v1/agents/bootstrap", async (c3) => {
74617
74878
  const body = await c3.req.json();
74618
74879
  if (!body.motebit_id || typeof body.motebit_id !== "string" || body.motebit_id.trim() === "") {
@@ -74757,7 +75018,7 @@ function registerAgentRoutes(deps) {
74757
75018
  }
74758
75019
  moteDb.db.prepare(`INSERT INTO relay_key_successions (motebit_id, old_public_key, new_public_key, timestamp, reason, old_key_signature, new_key_signature, recovery, guardian_signature)
74759
75020
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(motebitId, succession.old_public_key, succession.new_public_key, succession.timestamp, succession.reason ?? null, succession.old_key_signature ?? null, succession.new_key_signature, succession.recovery ? 1 : 0, succession.guardian_signature ?? null);
74760
- logger13.info("agent.key.succession_on_register", {
75021
+ logger14.info("agent.key.succession_on_register", {
74761
75022
  motebitId,
74762
75023
  oldKey: existingAgent.public_key.slice(0, 16) + "...",
74763
75024
  newKey: publicKey.slice(0, 16) + "..."
@@ -75050,7 +75311,7 @@ function registerAgentRoutes(deps) {
75050
75311
  note
75051
75312
  });
75052
75313
  insertRevocationRecord(moteDb.db, record);
75053
- logger13.info(revoked ? "agent.revoked" : "agent.reinstated", {
75314
+ logger14.info(revoked ? "agent.revoked" : "agent.reinstated", {
75054
75315
  motebit_id: motebitId,
75055
75316
  reason
75056
75317
  });
@@ -75122,7 +75383,7 @@ function registerAgentRoutes(deps) {
75122
75383
  return c3.json({ ok: true });
75123
75384
  });
75124
75385
  }
75125
- var logger13, SOLVENCY_TTL_MS;
75386
+ var logger14, SOLVENCY_TTL_MS;
75126
75387
  var init_agents = __esm({
75127
75388
  "../../services/relay/dist/agents.js"() {
75128
75389
  "use strict";
@@ -75135,10 +75396,11 @@ var init_agents = __esm({
75135
75396
  init_dist30();
75136
75397
  init_idempotency();
75137
75398
  init_accounts();
75399
+ init_receipts_store();
75138
75400
  init_dist();
75139
75401
  init_agent_revocation3();
75140
75402
  init_logger();
75141
- logger13 = createLogger({ service: "agents" });
75403
+ logger14 = createLogger({ service: "agents" });
75142
75404
  SOLVENCY_TTL_MS = 5 * 60 * 1e3;
75143
75405
  }
75144
75406
  });
@@ -75296,7 +75558,7 @@ async function insertRevocationEvent(db, relayIdentity, type, motebitId, opts) {
75296
75558
  db.prepare("INSERT INTO relay_revocation_events (event_id, type, motebit_id, credential_id, new_public_key, timestamp, signature) VALUES (?, ?, ?, ?, ?, ?, ?)").run(eventId, type, motebitId, opts?.credentialId ?? null, opts?.newPublicKey ?? null, recordedAt, signatureHex);
75297
75559
  if (revocationAnchorSubmitter && opts?.revokedPublicKey) {
75298
75560
  anchorRevocationOnChain(opts.revokedPublicKey, effectiveAt).catch((err2) => {
75299
- logger14.error("revocation.anchor_failed", {
75561
+ logger15.error("revocation.anchor_failed", {
75300
75562
  motebitId,
75301
75563
  type,
75302
75564
  error: err2 instanceof Error ? err2.message : String(err2)
@@ -75317,13 +75579,13 @@ async function anchorRevocationOnChain(revokedPublicKeyHex, timestamp) {
75317
75579
  return;
75318
75580
  const available = await revocationAnchorSubmitter.isAvailable();
75319
75581
  if (!available) {
75320
- logger14.warn("revocation.anchor_submitter_unavailable", {
75582
+ logger15.warn("revocation.anchor_submitter_unavailable", {
75321
75583
  publicKey: revokedPublicKeyHex.slice(0, 16) + "..."
75322
75584
  });
75323
75585
  return;
75324
75586
  }
75325
75587
  const { txHash } = await revocationAnchorSubmitter.submitRevocation(revokedPublicKeyHex, timestamp);
75326
- logger14.info("revocation.anchored_onchain", {
75588
+ logger15.info("revocation.anchored_onchain", {
75327
75589
  publicKey: revokedPublicKeyHex.slice(0, 16) + "...",
75328
75590
  txHash,
75329
75591
  timestamp
@@ -75341,7 +75603,7 @@ async function processIncomingRevocations(db, events, peerPublicKey) {
75341
75603
  const valid = await ed25519Verify(hexToBytes5(event.signature), encoder2.encode(payload), peerPublicKey);
75342
75604
  if (!valid) {
75343
75605
  rejected++;
75344
- logger14.warn("federation.revocation.invalid_signature", {
75606
+ logger15.warn("federation.revocation.invalid_signature", {
75345
75607
  type: event.type,
75346
75608
  motebitId: event.motebit_id
75347
75609
  });
@@ -75519,13 +75781,13 @@ async function sendHeartbeats(db, relayIdentity) {
75519
75781
  const newMissed = peer.missed_heartbeats + 1;
75520
75782
  if (newMissed >= HEARTBEAT_REMOVE_THRESHOLD) {
75521
75783
  db.prepare("UPDATE relay_peers SET missed_heartbeats = ?, state = 'removed' WHERE peer_relay_id = ?").run(newMissed, peer.peer_relay_id);
75522
- logger14.warn("federation.peer.suspended", { peerId: peer.peer_relay_id });
75784
+ logger15.warn("federation.peer.suspended", { peerId: peer.peer_relay_id });
75523
75785
  } else if (newMissed >= HEARTBEAT_SUSPEND_THRESHOLD) {
75524
75786
  db.prepare("UPDATE relay_peers SET missed_heartbeats = ?, state = 'suspended' WHERE peer_relay_id = ?").run(newMissed, peer.peer_relay_id);
75525
- logger14.warn("federation.peer.suspended", { peerId: peer.peer_relay_id });
75787
+ logger15.warn("federation.peer.suspended", { peerId: peer.peer_relay_id });
75526
75788
  } else {
75527
75789
  db.prepare("UPDATE relay_peers SET missed_heartbeats = ? WHERE peer_relay_id = ?").run(newMissed, peer.peer_relay_id);
75528
- logger14.warn("federation.heartbeat.missed", {
75790
+ logger15.warn("federation.heartbeat.missed", {
75529
75791
  peerId: peer.peer_relay_id,
75530
75792
  missed: newMissed
75531
75793
  });
@@ -75569,7 +75831,7 @@ async function processSettlementRetries(db, relayIdentity, onRetryExhausted, ret
75569
75831
  try {
75570
75832
  onRetryExhausted(retry);
75571
75833
  } catch (refundErr) {
75572
- logger14.warn("settlement.retry.refund_failed", {
75834
+ logger15.warn("settlement.retry.refund_failed", {
75573
75835
  retryId: retry.retry_id,
75574
75836
  taskId: retry.task_id,
75575
75837
  error: refundErr instanceof Error ? refundErr.message : String(refundErr)
@@ -75579,7 +75841,7 @@ async function processSettlementRetries(db, relayIdentity, onRetryExhausted, ret
75579
75841
  } else {
75580
75842
  const backoffMs = nextRetryDelay(newAttempts - 1, retryPolicy);
75581
75843
  const nextRetry = Date.now() + backoffMs;
75582
- logger14.info("settlement.retry.scheduled", {
75844
+ logger15.info("settlement.retry.scheduled", {
75583
75845
  retryId: retry.retry_id,
75584
75846
  taskId: retry.task_id,
75585
75847
  attempt: newAttempts,
@@ -75778,7 +76040,7 @@ function registerFederationRoutes(deps) {
75778
76040
  }
75779
76041
  const now = Date.now();
75780
76042
  db.prepare(`UPDATE relay_peers SET state = 'active', peered_at = ?, last_heartbeat_at = ?, nonce = NULL WHERE peer_relay_id = ?`).run(now, now, relay_id);
75781
- logger14.info("federation.peer.active", { peerId: relay_id });
76043
+ logger15.info("federation.peer.active", { peerId: relay_id });
75782
76044
  return c3.json({ status: "active", peered_at: now });
75783
76045
  });
75784
76046
  app.post("/federation/v1/peer/heartbeat", async (c3) => {
@@ -75812,19 +76074,19 @@ function registerFederationRoutes(deps) {
75812
76074
  const peerPubKey = hexToBytes5(peer.public_key);
75813
76075
  const result = await processIncomingRevocations(db, revocations, peerPubKey);
75814
76076
  if (result.rejected > 0) {
75815
- logger14.warn("federation.revocation.rejected", {
76077
+ logger15.warn("federation.revocation.rejected", {
75816
76078
  peerId: relay_id,
75817
76079
  rejected: result.rejected
75818
76080
  });
75819
76081
  }
75820
76082
  if (result.processed > 0) {
75821
- logger14.info("federation.revocation.processed", {
76083
+ logger15.info("federation.revocation.processed", {
75822
76084
  peerId: relay_id,
75823
76085
  processed: result.processed
75824
76086
  });
75825
76087
  }
75826
76088
  } catch (err2) {
75827
- logger14.warn("federation.revocation.error", {
76089
+ logger15.warn("federation.revocation.error", {
75828
76090
  peerId: relay_id,
75829
76091
  error: err2 instanceof Error ? err2.message : String(err2)
75830
76092
  });
@@ -75871,7 +76133,7 @@ function registerFederationRoutes(deps) {
75871
76133
  });
75872
76134
  }
75873
76135
  const witnessSignature = await signHorizonWitnessRequestBody(request.cert_body, relayIdentity.privateKey);
75874
- logger14.info("federation.horizon.witness.signed", {
76136
+ logger15.info("federation.horizon.witness.signed", {
75875
76137
  issuerId: request.issuer_id,
75876
76138
  storeId: request.cert_body.store_id,
75877
76139
  horizonTs: request.cert_body.horizon_ts
@@ -75925,7 +76187,7 @@ function registerFederationRoutes(deps) {
75925
76187
  });
75926
76188
  }
75927
76189
  persistWitnessOmissionDispute(db, dispute, disputeJson, "verified");
75928
- logger14.info("federation.horizon.dispute.verified", {
76190
+ logger15.info("federation.horizon.dispute.verified", {
75929
76191
  disputeId: dispute.dispute_id,
75930
76192
  certIssuer: dispute.cert_issuer,
75931
76193
  certSignature: dispute.cert_signature.slice(0, 16),
@@ -75968,7 +76230,7 @@ function registerFederationRoutes(deps) {
75968
76230
  const canonical = canonicalJson(bodyForVerify);
75969
76231
  const valid = await ed25519Verify(fromBase64Url(signature), new TextEncoder().encode(canonical), hexToBytes5(peer.public_key));
75970
76232
  if (!valid) {
75971
- logger14.warn("federation.vote_request.signature_invalid", {
76233
+ logger15.warn("federation.vote_request.signature_invalid", {
75972
76234
  kind: "signature_invalid",
75973
76235
  peerId: request.requester_id,
75974
76236
  disputeId: request.dispute_id
@@ -76002,7 +76264,7 @@ function registerFederationRoutes(deps) {
76002
76264
  vote: voteOutcome,
76003
76265
  rationale: voteRationale
76004
76266
  }, relayIdentity.privateKey);
76005
- logger14.info("federation.vote_request.signed", {
76267
+ logger15.info("federation.vote_request.signed", {
76006
76268
  disputeId: request.dispute_id,
76007
76269
  round: request.round,
76008
76270
  requesterId: request.requester_id,
@@ -76208,7 +76470,7 @@ function registerFederationRoutes(deps) {
76208
76470
  return c3.json(result);
76209
76471
  });
76210
76472
  }
76211
- var FEDERATION_SUITE, RELAY_SPEC_VERSION, logger14, REVOCATION_TTL_MS, revocationAnchorSubmitter, PBKDF2_ITERATIONS, AUTH_TAG_BYTES, FEDERATION_QUERY_TTL_MS, HEARTBEAT_SUSPEND_THRESHOLD, HEARTBEAT_REMOVE_THRESHOLD, FEDERATION_TIMESTAMP_DRIFT_MS;
76473
+ var FEDERATION_SUITE, RELAY_SPEC_VERSION, logger15, REVOCATION_TTL_MS, revocationAnchorSubmitter, PBKDF2_ITERATIONS, AUTH_TAG_BYTES, FEDERATION_QUERY_TTL_MS, HEARTBEAT_SUSPEND_THRESHOLD, HEARTBEAT_REMOVE_THRESHOLD, FEDERATION_TIMESTAMP_DRIFT_MS;
76212
76474
  var init_federation = __esm({
76213
76475
  "../../services/relay/dist/federation.js"() {
76214
76476
  "use strict";
@@ -76227,7 +76489,7 @@ var init_federation = __esm({
76227
76489
  init_dist30();
76228
76490
  FEDERATION_SUITE = "motebit-concat-ed25519-hex-v1";
76229
76491
  RELAY_SPEC_VERSION = "motebit/relay-federation@1.2";
76230
- logger14 = createLogger({ service: "relay", module: "federation" });
76492
+ logger15 = createLogger({ service: "relay", module: "federation" });
76231
76493
  REVOCATION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
76232
76494
  PBKDF2_ITERATIONS = 6e5;
76233
76495
  AUTH_TAG_BYTES = 16;
@@ -76510,7 +76772,7 @@ function buildCreditOnDepositCallback(db) {
76510
76772
  accountStore.credit(deposit.motebitId, microAmount, "deposit", `onchain-${deposit.txHash}-${deposit.logIndex}`, `USDC deposit from ${deposit.fromAddress} on ${deposit.chain} (block ${deposit.blockNumber})`);
76511
76773
  db.prepare("INSERT INTO relay_deposit_log (tx_hash, log_index, agent_id, from_address, amount, chain, block_number, credited_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(deposit.txHash, deposit.logIndex, deposit.motebitId, deposit.fromAddress, deposit.amountOnchain.toString(), deposit.chain, deposit.blockNumber.toString(), Date.now());
76512
76774
  });
76513
- logger15.info("deposit.detected", {
76775
+ logger16.info("deposit.detected", {
76514
76776
  agentId: deposit.motebitId,
76515
76777
  chain: deposit.chain,
76516
76778
  amount: microAmount,
@@ -76534,7 +76796,7 @@ async function detectDeposits2(config) {
76534
76796
  maxBlocksPerCycle: config.maxBlocksPerCycle,
76535
76797
  confirmations,
76536
76798
  onDeposit: buildCreditOnDepositCallback(config.db),
76537
- logger: logger15
76799
+ logger: logger16
76538
76800
  });
76539
76801
  }
76540
76802
  function startDepositDetector(config) {
@@ -76545,7 +76807,7 @@ function startDepositDetector(config) {
76545
76807
  const intervalMs = config.intervalMs ?? 15e3;
76546
76808
  const maxBlocksPerCycle = config.maxBlocksPerCycle ?? 1e3;
76547
76809
  if (!contractAddress || !config.rpc && !rpcUrl || confirmations === void 0) {
76548
- logger15.warn("deposit-detector.disabled", {
76810
+ logger16.warn("deposit-detector.disabled", {
76549
76811
  chain: config.chain,
76550
76812
  reason: !contractAddress ? "no USDC contract" : !rpcUrl ? "no RPC URL" : "no confirmation depth registered (add to CONFIRMATIONS_BY_CHAIN)"
76551
76813
  });
@@ -76556,7 +76818,7 @@ function startDepositDetector(config) {
76556
76818
  const rpc = config.rpc ?? new HttpJsonRpcEvmAdapter({ rpcUrl, fetch: config.fetch });
76557
76819
  const store = new SqliteDepositDetectorStore(config.db);
76558
76820
  const onDeposit = buildCreditOnDepositCallback(config.db);
76559
- logger15.info("deposit-detector.started", {
76821
+ logger16.info("deposit-detector.started", {
76560
76822
  chain: config.chain,
76561
76823
  intervalMs,
76562
76824
  maxBlocksPerCycle,
@@ -76573,16 +76835,16 @@ function startDepositDetector(config) {
76573
76835
  maxBlocksPerCycle,
76574
76836
  confirmations,
76575
76837
  onDeposit,
76576
- logger: logger15
76838
+ logger: logger16
76577
76839
  });
76578
76840
  if (credits > 0) {
76579
- logger15.info("deposit-detector.cycle", {
76841
+ logger16.info("deposit-detector.cycle", {
76580
76842
  chain: config.chain,
76581
76843
  creditsApplied: credits
76582
76844
  });
76583
76845
  }
76584
76846
  } catch (err2) {
76585
- logger15.warn("deposit-detector.cycle_failed", {
76847
+ logger16.warn("deposit-detector.cycle_failed", {
76586
76848
  chain: config.chain,
76587
76849
  error: err2 instanceof Error ? err2.message : String(err2)
76588
76850
  });
@@ -76591,7 +76853,7 @@ function startDepositDetector(config) {
76591
76853
  void tick();
76592
76854
  return setInterval(() => void tick(), intervalMs);
76593
76855
  }
76594
- var logger15, TRANSFER_TOPIC, USDC_CONTRACTS, DEFAULT_RPC_URLS, CONFIRMATIONS_BY_CHAIN, USDC_ONCHAIN_TO_MICRO, SqliteDepositDetectorStore;
76856
+ var logger16, TRANSFER_TOPIC, USDC_CONTRACTS, DEFAULT_RPC_URLS, CONFIRMATIONS_BY_CHAIN, USDC_ONCHAIN_TO_MICRO, SqliteDepositDetectorStore;
76595
76857
  var init_deposit_detector = __esm({
76596
76858
  "../../services/relay/dist/deposit-detector.js"() {
76597
76859
  "use strict";
@@ -76600,7 +76862,7 @@ var init_deposit_detector = __esm({
76600
76862
  init_dist38();
76601
76863
  init_account_store_sqlite();
76602
76864
  init_logger();
76603
- logger15 = createLogger({ service: "deposit-detector" });
76865
+ logger16 = createLogger({ service: "deposit-detector" });
76604
76866
  TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
76605
76867
  USDC_CONTRACTS = {
76606
76868
  "eip155:1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
@@ -77644,13 +77906,13 @@ var stripe_webhook_adapter_exports = {};
77644
77906
  __export(stripe_webhook_adapter_exports, {
77645
77907
  StripeSubscriptionEventAdapter: () => StripeSubscriptionEventAdapter
77646
77908
  });
77647
- var logger18, StripeSubscriptionEventAdapter;
77909
+ var logger19, StripeSubscriptionEventAdapter;
77648
77910
  var init_stripe_webhook_adapter = __esm({
77649
77911
  "../../services/relay/dist/webhooks/stripe-webhook-adapter.js"() {
77650
77912
  "use strict";
77651
77913
  init_esm_shims();
77652
77914
  init_logger();
77653
- logger18 = createLogger({ service: "relay", module: "stripe-webhook-adapter" });
77915
+ logger19 = createLogger({ service: "relay", module: "stripe-webhook-adapter" });
77654
77916
  StripeSubscriptionEventAdapter = class {
77655
77917
  stripe;
77656
77918
  webhookSecret;
@@ -77663,7 +77925,7 @@ var init_stripe_webhook_adapter = __esm({
77663
77925
  try {
77664
77926
  event = this.stripe.webhooks.constructEvent(rawBody, signature, this.webhookSecret);
77665
77927
  } catch (err2) {
77666
- logger18.warn("webhook.signature_failed", {
77928
+ logger19.warn("webhook.signature_failed", {
77667
77929
  error: err2 instanceof Error ? err2.message : String(err2)
77668
77930
  });
77669
77931
  return Promise.resolve(null);
@@ -92319,7 +92581,7 @@ async function reconcileTreasury(config) {
92319
92581
 
92320
92582
  // ../../services/relay/dist/treasury-reconciliation.js
92321
92583
  init_logger();
92322
- var logger16 = createLogger({ service: "treasury-reconciliation" });
92584
+ var logger17 = createLogger({ service: "treasury-reconciliation" });
92323
92585
  var DEFAULT_INTERVAL_MS = 15 * 6e4;
92324
92586
  var DEFAULT_CONFIRMATION_LAG_BUFFER_MS = 5 * 6e4;
92325
92587
  var SqliteTreasuryReconciliationStore = class {
@@ -92400,11 +92662,11 @@ function startTreasuryReconciliationLoop(config) {
92400
92662
  const generateReconciliationId = config.generateReconciliationId ?? randomUUID;
92401
92663
  const store = new SqliteTreasuryReconciliationStore(config.db);
92402
92664
  const loopLogger = {
92403
- info: (event, data) => logger16.info(event, data),
92404
- warn: (event, data) => logger16.warn(event, data),
92405
- error: (event, data) => logger16.error(event, data)
92665
+ info: (event, data) => logger17.info(event, data),
92666
+ warn: (event, data) => logger17.warn(event, data),
92667
+ error: (event, data) => logger17.error(event, data)
92406
92668
  };
92407
- logger16.info("treasury-reconciliation.started", {
92669
+ logger17.info("treasury-reconciliation.started", {
92408
92670
  chain: config.chain,
92409
92671
  treasuryAddress: config.treasuryAddress,
92410
92672
  usdcContractAddress: config.usdcContractAddress,
@@ -92427,7 +92689,7 @@ function startTreasuryReconciliationLoop(config) {
92427
92689
  logger: loopLogger
92428
92690
  });
92429
92691
  } catch (err2) {
92430
- logger16.error("treasury-reconciliation.cycle_uncaught", {
92692
+ logger17.error("treasury-reconciliation.cycle_uncaught", {
92431
92693
  chain: config.chain,
92432
92694
  error: err2 instanceof Error ? err2.message : String(err2)
92433
92695
  });
@@ -92443,7 +92705,7 @@ init_esm_shims();
92443
92705
  init_dist39();
92444
92706
  init_logger();
92445
92707
  import { randomUUID as randomUUID2 } from "crypto";
92446
- var logger17 = createLogger({ service: "solana-treasury-reconciliation" });
92708
+ var logger18 = createLogger({ service: "solana-treasury-reconciliation" });
92447
92709
  var DEFAULT_INTERVAL_MS2 = 15 * 6e4;
92448
92710
  var SqliteSolanaTreasuryReconciliationStore = class {
92449
92711
  db;
@@ -92489,11 +92751,11 @@ function startSolanaTreasuryReconciliationLoop(config) {
92489
92751
  }), chain, usdcMint);
92490
92752
  const store = new SqliteSolanaTreasuryReconciliationStore(config.db);
92491
92753
  const loopLogger = {
92492
- info: (event, data) => logger17.info(event, data),
92493
- warn: (event, data) => logger17.warn(event, data),
92494
- error: (event, data) => logger17.error(event, data)
92754
+ info: (event, data) => logger18.info(event, data),
92755
+ warn: (event, data) => logger18.warn(event, data),
92756
+ error: (event, data) => logger18.error(event, data)
92495
92757
  };
92496
- logger17.info("solana-treasury-reconciliation.started", {
92758
+ logger18.info("solana-treasury-reconciliation.started", {
92497
92759
  chain,
92498
92760
  treasuryAddress: reconciler.treasuryAddress,
92499
92761
  usdcMint,
@@ -92512,7 +92774,7 @@ function startSolanaTreasuryReconciliationLoop(config) {
92512
92774
  logger: loopLogger
92513
92775
  });
92514
92776
  } catch (err2) {
92515
- logger17.error("solana-treasury-reconciliation.cycle_uncaught", {
92777
+ logger18.error("solana-treasury-reconciliation.cycle_uncaught", {
92516
92778
  chain,
92517
92779
  error: err2 instanceof Error ? err2.message : String(err2)
92518
92780
  });
@@ -92778,7 +93040,7 @@ init_dist6();
92778
93040
  init_logger();
92779
93041
  init_accounts();
92780
93042
  import Stripe from "stripe";
92781
- var logger19 = createLogger({ service: "relay", module: "proxy-tokens" });
93043
+ var logger20 = createLogger({ service: "relay", module: "proxy-tokens" });
92782
93044
  var DEPOSIT_MODELS = [
92783
93045
  "claude-opus-4-6",
92784
93046
  "claude-sonnet-4-6",
@@ -92839,7 +93101,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
92839
93101
  const balance = account?.balance ?? 0;
92840
93102
  try {
92841
93103
  const token = await issueProxyToken(motebitId, balance, relayIdentity);
92842
- logger19.info("proxy-token.issued", {
93104
+ logger20.info("proxy-token.issued", {
92843
93105
  motebitId,
92844
93106
  balance: fromMicro(balance)
92845
93107
  });
@@ -92851,7 +93113,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
92851
93113
  expires_at: Date.now() + PROXY_TOKEN_TTL_MS
92852
93114
  });
92853
93115
  } catch (err2) {
92854
- logger19.error("proxy-token.failed", {
93116
+ logger20.error("proxy-token.failed", {
92855
93117
  motebitId,
92856
93118
  error: err2 instanceof Error ? err2.message : String(err2)
92857
93119
  });
@@ -92871,13 +93133,13 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
92871
93133
  }
92872
93134
  const newBalance = debitAccount(db, motebitId, body.amount, "fee", body.reference_id, body.description ?? "Cloud AI usage");
92873
93135
  if (newBalance === null) {
92874
- logger19.warn("proxy-debit.insufficient", {
93136
+ logger20.warn("proxy-debit.insufficient", {
92875
93137
  motebitId,
92876
93138
  amount: body.amount
92877
93139
  });
92878
93140
  return c3.json({ success: false, balance: 0 });
92879
93141
  }
92880
- logger19.info("proxy-debit.success", {
93142
+ logger20.info("proxy-debit.success", {
92881
93143
  motebitId,
92882
93144
  amount: body.amount,
92883
93145
  balanceAfter: newBalance
@@ -92916,7 +93178,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
92916
93178
  creditAccount(db, motebitId, toMicro(MONTHLY_CREDIT_USD), "deposit", refId, `Motebit Cloud subscription \u2014 $${MONTHLY_CREDIT_USD} credits`);
92917
93179
  }
92918
93180
  const account = getAccountBalance(db, motebitId);
92919
- logger19.info("session-status.activated", { motebitId, subscriptionId });
93181
+ logger20.info("session-status.activated", { motebitId, subscriptionId });
92920
93182
  return c3.json({
92921
93183
  status: "complete",
92922
93184
  motebit_id: motebitId,
@@ -92929,7 +93191,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
92929
93191
  return c3.json({ status: "expired" });
92930
93192
  return c3.json({ status: "open" });
92931
93193
  } catch (err2) {
92932
- logger19.error("session-status.failed", {
93194
+ logger20.error("session-status.failed", {
92933
93195
  sessionId,
92934
93196
  error: err2 instanceof Error ? err2.message : String(err2)
92935
93197
  });
@@ -92943,7 +93205,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
92943
93205
  }
92944
93206
  const priceId = process.env.STRIPE_CLOUD_PRICE_ID;
92945
93207
  if (!priceId) {
92946
- logger19.error("checkout.missing_price_id", {});
93208
+ logger20.error("checkout.missing_price_id", {});
92947
93209
  return c3.json({ error: "subscription checkout not configured" }, 500);
92948
93210
  }
92949
93211
  const existing = db.prepare("SELECT status FROM relay_subscriptions WHERE motebit_id = ? AND status = 'active'").get(body.motebit_id);
@@ -92968,13 +93230,13 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
92968
93230
  success_url: body.return_url ?? `${c3.req.url.split("/api")[0]}/?checkout_session_id={CHECKOUT_SESSION_ID}`,
92969
93231
  cancel_url: body.return_url ?? `${c3.req.url.split("/api")[0]}/`
92970
93232
  });
92971
- logger19.info("subscription.checkout.created", {
93233
+ logger20.info("subscription.checkout.created", {
92972
93234
  motebitId: body.motebit_id,
92973
93235
  sessionId: session.id
92974
93236
  });
92975
93237
  return c3.json({ checkout_url: session.url, session_id: session.id });
92976
93238
  } catch (err2) {
92977
- logger19.error("subscription.checkout.failed", {
93239
+ logger20.error("subscription.checkout.failed", {
92978
93240
  motebitId: body.motebit_id,
92979
93241
  error: err2 instanceof Error ? err2.message : String(err2)
92980
93242
  });
@@ -93028,7 +93290,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
93028
93290
  getOrCreateAccount(db, event.motebit_id);
93029
93291
  creditAccount(db, event.motebit_id, toMicro(MONTHLY_CREDIT_USD), "deposit", refId, `Motebit Cloud subscription \u2014 $${MONTHLY_CREDIT_USD} credits`);
93030
93292
  }
93031
- logger19.info("subscription.activated", {
93293
+ logger20.info("subscription.activated", {
93032
93294
  motebitId: event.motebit_id,
93033
93295
  subscriptionId: event.subscription_id,
93034
93296
  creditUsd: MONTHLY_CREDIT_USD
@@ -93044,7 +93306,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
93044
93306
  if (existingTxn != null)
93045
93307
  break;
93046
93308
  creditAccount(db, row.motebit_id, toMicro(MONTHLY_CREDIT_USD), "deposit", refId, `Motebit Cloud renewal \u2014 $${MONTHLY_CREDIT_USD} credits`);
93047
- logger19.info("subscription.renewed", {
93309
+ logger20.info("subscription.renewed", {
93048
93310
  motebitId: row.motebit_id,
93049
93311
  subscriptionId: event.subscription_id,
93050
93312
  invoiceId: event.invoice_id,
@@ -93057,19 +93319,19 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
93057
93319
  if (!row)
93058
93320
  break;
93059
93321
  db.prepare("UPDATE relay_subscriptions SET status = 'cancelled', updated_at = ? WHERE stripe_subscription_id = ?").run(Date.now(), event.subscription_id);
93060
- logger19.info("subscription.cancelled", {
93322
+ logger20.info("subscription.cancelled", {
93061
93323
  motebitId: row.motebit_id,
93062
93324
  subscriptionId: event.subscription_id
93063
93325
  });
93064
93326
  break;
93065
93327
  }
93066
93328
  case "ignored": {
93067
- logger19.debug("webhook.unhandled", { type: event.type });
93329
+ logger20.debug("webhook.unhandled", { type: event.type });
93068
93330
  break;
93069
93331
  }
93070
93332
  }
93071
93333
  } catch (err2) {
93072
- logger19.error("webhook.processing_failed", {
93334
+ logger20.error("webhook.processing_failed", {
93073
93335
  kind: event.kind,
93074
93336
  error: err2 instanceof Error ? err2.message : String(err2)
93075
93337
  });
@@ -93090,10 +93352,10 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
93090
93352
  const periodEndSec = updated.current_period_end;
93091
93353
  const periodEnd = typeof periodEndSec === "number" ? periodEndSec * 1e3 : null;
93092
93354
  db.prepare("UPDATE relay_subscriptions SET status = 'cancelling', current_period_end = ?, updated_at = ? WHERE motebit_id = ?").run(periodEnd, Date.now(), motebitId);
93093
- logger19.info("subscription.cancel_scheduled", { motebitId, activeUntil: periodEnd });
93355
+ logger20.info("subscription.cancel_scheduled", { motebitId, activeUntil: periodEnd });
93094
93356
  return c3.json({ status: "cancelling", active_until: periodEnd });
93095
93357
  } catch (err2) {
93096
- logger19.error("subscription.cancel_failed", {
93358
+ logger20.error("subscription.cancel_failed", {
93097
93359
  motebitId,
93098
93360
  error: err2 instanceof Error ? err2.message : String(err2)
93099
93361
  });
@@ -93112,10 +93374,10 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
93112
93374
  cancel_at_period_end: false
93113
93375
  });
93114
93376
  db.prepare("UPDATE relay_subscriptions SET status = 'active', updated_at = ? WHERE motebit_id = ?").run(Date.now(), motebitId);
93115
- logger19.info("subscription.resubscribed", { motebitId });
93377
+ logger20.info("subscription.resubscribed", { motebitId });
93116
93378
  return c3.json({ status: "active" });
93117
93379
  } catch (err2) {
93118
- logger19.error("subscription.resubscribe_failed", {
93380
+ logger20.error("subscription.resubscribe_failed", {
93119
93381
  motebitId,
93120
93382
  error: err2 instanceof Error ? err2.message : String(err2)
93121
93383
  });
@@ -93558,44 +93820,8 @@ function registerReceiptExchangeRoutes(app, config = {}) {
93558
93820
  };
93559
93821
  }
93560
93822
 
93561
- // ../../services/relay/dist/receipts-store.js
93562
- init_esm_shims();
93563
- init_dist6();
93564
- init_logger();
93565
- var logger20 = createLogger({ service: "receipts-store" });
93566
- var MAX_RECEIPT_DEPTH = 10;
93567
- var INSERT_SQL = `
93568
- INSERT OR IGNORE INTO relay_receipts (
93569
- motebit_id, task_id, parent_task_id, depth, status,
93570
- suite, public_key, signature, invocation_origin,
93571
- receipt_json, received_at
93572
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
93573
- `;
93574
- function receiptTaskId(r2) {
93575
- return r2.relay_task_id != null && r2.relay_task_id.length > 0 ? r2.relay_task_id : r2.task_id;
93576
- }
93577
- function persistReceiptChain(db, receipt, parentTaskId = null, depth = 0, receivedAt = Date.now()) {
93578
- if (depth > MAX_RECEIPT_DEPTH) {
93579
- logger20.warn("receipt.persist.depth_limit_exceeded", {
93580
- motebitId: receipt.motebit_id,
93581
- taskId: receiptTaskId(receipt),
93582
- depth,
93583
- maxDepth: MAX_RECEIPT_DEPTH
93584
- });
93585
- return;
93586
- }
93587
- const taskId = receiptTaskId(receipt);
93588
- const receiptJson = canonicalJson(receipt);
93589
- db.prepare(INSERT_SQL).run(receipt.motebit_id, taskId, parentTaskId, depth, receipt.status, receipt.suite, receipt.public_key ?? "", receipt.signature, receipt.invocation_origin ?? null, receiptJson, receivedAt);
93590
- const children = receipt.delegation_receipts ?? [];
93591
- for (const child of children) {
93592
- persistReceiptChain(db, child, taskId, depth + 1, receivedAt);
93593
- }
93594
- }
93595
- function getStoredReceiptJson(db, motebitId, taskId) {
93596
- const row = db.prepare("SELECT receipt_json FROM relay_receipts WHERE motebit_id = ? AND task_id = ?").get(motebitId, taskId);
93597
- return row?.receipt_json ?? null;
93598
- }
93823
+ // ../../services/relay/dist/index.js
93824
+ init_receipts_store();
93599
93825
 
93600
93826
  // ../../services/relay/dist/onramp.js
93601
93827
  init_esm_shims();
@@ -94121,6 +94347,7 @@ init_esm_shims();
94121
94347
  init_dist2();
94122
94348
  init_dist6();
94123
94349
  init_dist5();
94350
+ init_receipts_store();
94124
94351
  import { HTTPException as HTTPException10 } from "hono/http-exception";
94125
94352
  var MOTEBIT_RELAY_CLAIM_GENERATOR = "motebit-relay/0.5.2";
94126
94353
  function registerStateExportRoutes(deps) {
@@ -96508,6 +96735,68 @@ import { HTTPException as HTTPException18 } from "hono/http-exception";
96508
96735
  init_esm_shims();
96509
96736
  init_logger();
96510
96737
  var logger27 = createLogger({ service: "push-adapter" });
96738
+ var EXPO_PUSH_URL = "https://exp.host/--/api/v2/push/send";
96739
+ var ExpoPushAdapter = class {
96740
+ accessToken;
96741
+ _fetch;
96742
+ constructor(config) {
96743
+ this.accessToken = config?.accessToken;
96744
+ this._fetch = config?.fetch ?? globalThis.fetch;
96745
+ }
96746
+ isAvailable() {
96747
+ return true;
96748
+ }
96749
+ async send(pushToken, _platform, payload) {
96750
+ const headers = {
96751
+ "Content-Type": "application/json"
96752
+ };
96753
+ if (this.accessToken) {
96754
+ headers["Authorization"] = `Bearer ${this.accessToken}`;
96755
+ }
96756
+ const body = {
96757
+ to: pushToken,
96758
+ data: payload,
96759
+ priority: "high",
96760
+ channelId: "agent-tasks",
96761
+ _contentAvailable: true,
96762
+ sound: null
96763
+ };
96764
+ try {
96765
+ const res = await this._fetch(EXPO_PUSH_URL, {
96766
+ method: "POST",
96767
+ headers,
96768
+ body: JSON.stringify(body),
96769
+ signal: AbortSignal.timeout(1e4)
96770
+ });
96771
+ if (!res.ok) {
96772
+ const text = await res.text().catch(() => "");
96773
+ return { delivered: false, error: `HTTP ${res.status}: ${text}` };
96774
+ }
96775
+ const json = await res.json();
96776
+ if (json.data?.status === "error") {
96777
+ const errorType = json.data.details?.error;
96778
+ logger27.warn("push.send.error", {
96779
+ pushToken: pushToken.slice(0, 20) + "...",
96780
+ error: json.data.message,
96781
+ errorType
96782
+ });
96783
+ return {
96784
+ delivered: false,
96785
+ error: `${errorType ?? "unknown"}: ${json.data.message ?? ""}`
96786
+ };
96787
+ }
96788
+ logger27.info("push.send.ok", {
96789
+ motebitId: payload.motebit_id,
96790
+ pendingCount: payload.pending_count
96791
+ });
96792
+ return { delivered: true };
96793
+ } catch (err2) {
96794
+ const msg = err2 instanceof Error ? err2.message : String(err2);
96795
+ logger27.warn("push.send.failed", { error: msg });
96796
+ return { delivered: false, error: msg };
96797
+ }
96798
+ }
96799
+ };
96511
96800
  var pushRateLimit = /* @__PURE__ */ new Map();
96512
96801
  var PUSH_RATE_LIMIT_MS = 3e4;
96513
96802
  async function attemptPushWake(motebitId, deps) {
@@ -96542,6 +96831,7 @@ async function attemptPushWake(motebitId, deps) {
96542
96831
 
96543
96832
  // ../../services/relay/dist/tasks.js
96544
96833
  init_task_routing();
96834
+ init_receipts_store();
96545
96835
 
96546
96836
  // ../../services/relay/dist/multihop-depth.js
96547
96837
  init_esm_shims();
@@ -99516,6 +99806,8 @@ async function createSyncRelay(config) {
99516
99806
  const sweepInterval = startSweepLoop(moteDb.db, {}, () => getEmergencyFreeze());
99517
99807
  logger36.info("sweep.started", { intervalMs: 3e5 });
99518
99808
  const batchWithdrawalInterval = startBatchWithdrawalLoop(moteDb.db, railRegistry.list(), {}, () => getEmergencyFreeze());
99809
+ const pushAdapter = process.env.EXPO_ACCESS_TOKEN ? new ExpoPushAdapter({ accessToken: process.env.EXPO_ACCESS_TOKEN }) : void 0;
99810
+ logger36.info("relay.push_wake", { enabled: pushAdapter !== void 0 });
99519
99811
  await registerTaskRoutes({
99520
99812
  app,
99521
99813
  moteDb,
@@ -99535,7 +99827,8 @@ async function createSyncRelay(config) {
99535
99827
  isTokenBlacklisted,
99536
99828
  isAgentRevoked,
99537
99829
  platformFeeRate,
99538
- railRegistry
99830
+ railRegistry,
99831
+ pushAdapter
99539
99832
  });
99540
99833
  function getConnectionCount() {
99541
99834
  let count2 = 0;