settld 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (483) hide show
  1. package/README.md +93 -3
  2. package/SETTLD_VERSION +1 -1
  3. package/bin/settld-mcp +2 -0
  4. package/bin/settld.js +71 -0
  5. package/conformance/kernel-v0/README.md +7 -0
  6. package/conformance/kernel-v0/run.mjs +292 -4
  7. package/docs/ACCESS.md +57 -0
  8. package/docs/ADOPTION_CHECKLIST.md +44 -0
  9. package/docs/ALERTS.md +198 -0
  10. package/docs/ARCHITECTURE.md +69 -0
  11. package/docs/ARCHITECTURE_FOUNDER_GUIDE.md +284 -0
  12. package/docs/ARTIFACTS.md +60 -0
  13. package/docs/CERTIFICATION_CHECKLIST.md +33 -0
  14. package/docs/CIRCLE_SANDBOX_E2E.md +152 -0
  15. package/docs/CONFIG.md +297 -0
  16. package/docs/CONTRACTS_APIS.md +23 -0
  17. package/docs/DEPRECATION.md +31 -0
  18. package/docs/DOMAIN_MODEL.md +92 -0
  19. package/docs/EVENT_ENVELOPE.md +53 -0
  20. package/docs/FINANCE_PACK_FORMAT.md +53 -0
  21. package/docs/INCIDENT_TAXONOMY.md +30 -0
  22. package/docs/JOB_STATE_MACHINE.md +66 -0
  23. package/docs/KERNEL_COMPATIBLE.md +60 -0
  24. package/docs/KERNEL_V0.md +40 -0
  25. package/docs/KEY_ROTATION.md +80 -0
  26. package/docs/LEDGER.md +82 -0
  27. package/docs/LIVENESS.md +76 -0
  28. package/docs/MVP_BUILD_ORDER.md +36 -0
  29. package/docs/ONCALL_PLAYBOOK.md +39 -0
  30. package/docs/OPERATIONS_SIGNING.md +20 -0
  31. package/docs/OVERVIEW.md +190 -0
  32. package/docs/PERF_BASELINE.md +85 -0
  33. package/docs/PRD.md +77 -0
  34. package/docs/QUICKSTART_KERNEL_V0.md +96 -0
  35. package/docs/QUICKSTART_MCP.md +377 -0
  36. package/docs/QUICKSTART_MCP_HOSTS.md +210 -0
  37. package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
  38. package/docs/QUICKSTART_PRODUCE.md +61 -0
  39. package/docs/QUICKSTART_PROFILES.md +198 -0
  40. package/docs/QUICKSTART_RELEASE_VERIFY.md +39 -0
  41. package/docs/QUICKSTART_SDK.md +125 -0
  42. package/docs/QUICKSTART_SDK_PYTHON.md +111 -0
  43. package/docs/QUICKSTART_VERIFY.md +54 -0
  44. package/docs/QUICKSTART_X402_GATEWAY.md +317 -0
  45. package/docs/README.md +33 -0
  46. package/docs/RELEASE_CHECKLIST.md +182 -0
  47. package/docs/RELEASING.md +82 -0
  48. package/docs/REPO_SETTINGS.md +37 -0
  49. package/docs/RUNBOOK.md +86 -0
  50. package/docs/SKILLS.md +42 -0
  51. package/docs/SKILL_BUNDLE_FORMAT.md +48 -0
  52. package/docs/SLO.md +131 -0
  53. package/docs/SUMMARY.md +17 -0
  54. package/docs/SUPPORT.md +31 -0
  55. package/docs/THREAT_MODEL.md +36 -0
  56. package/docs/TRUST.md +59 -0
  57. package/docs/WORKFLOW.md +35 -0
  58. package/docs/X402_BATCH_SETTLEMENT.md +126 -0
  59. package/docs/blog/2026-02-14-your-ai-agent-just-spent-500-where-is-the-receipt.md +73 -0
  60. package/docs/examples/x402-provider-payout-registry.example.json +14 -0
  61. package/docs/gitbook/README.md +64 -0
  62. package/docs/gitbook/SETUP.md +25 -0
  63. package/docs/gitbook/SUMMARY.md +15 -0
  64. package/docs/gitbook/api-reference.md +73 -0
  65. package/docs/gitbook/closepacks.md +55 -0
  66. package/docs/gitbook/conformance.md +59 -0
  67. package/docs/gitbook/core-primitives.md +85 -0
  68. package/docs/gitbook/dispute-lifecycle.md +33 -0
  69. package/docs/gitbook/faq.md +21 -0
  70. package/docs/gitbook/guides.md +49 -0
  71. package/docs/gitbook/operations-runbook.md +36 -0
  72. package/docs/gitbook/quickstart.md +103 -0
  73. package/docs/gitbook/replay-and-audit.md +30 -0
  74. package/docs/gitbook/sdk-reference.md +35 -0
  75. package/docs/gitbook/security-model.md +58 -0
  76. package/docs/integrations/README.md +15 -0
  77. package/docs/integrations/github-actions-verify.yml +31 -0
  78. package/docs/integrations/github-actions.md +34 -0
  79. package/docs/integrations/openclaw/CLAWHUB_PUBLISH_CHECKLIST.md +65 -0
  80. package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +95 -0
  81. package/docs/integrations/openclaw/settld-mcp-skill/SKILL.md +69 -0
  82. package/docs/integrations/openclaw/settld-mcp-skill/mcp-server.example.json +12 -0
  83. package/docs/kernel-compatible/capabilities.json +36 -0
  84. package/docs/marketing/agent-commerce-substrate.md +78 -0
  85. package/docs/marketing/hn-repost-2026-02-17.md +102 -0
  86. package/docs/marketing/show-hn-post.md +45 -0
  87. package/docs/ops/ARTIFACT_VERIFICATION_STATUS.md +43 -0
  88. package/docs/ops/BILLING_WEBHOOK_REPLAY.md +105 -0
  89. package/docs/ops/CI_FLAKE_BUDGET.md +31 -0
  90. package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
  91. package/docs/ops/GO_LIVE_GATE_S13.md +27 -0
  92. package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
  93. package/docs/ops/KERNEL_V0_SHIP_GATE.md +69 -0
  94. package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
  95. package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +30 -0
  96. package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +89 -0
  97. package/docs/ops/P0_BACKEND_PROGRESS.md +150 -0
  98. package/docs/ops/PAYMENTS_ALPHA_R5.md +105 -0
  99. package/docs/ops/PILOT_ONBOARDING_RUNBOOK.md +112 -0
  100. package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +140 -0
  101. package/docs/ops/R1_SLOS.md +66 -0
  102. package/docs/ops/RELEASE_SIGNING_INCIDENT.md +58 -0
  103. package/docs/ops/SELF_SERVE_LAUNCH_AUTOMATION.md +89 -0
  104. package/docs/ops/THROUGHPUT_DRILL_10X.md +48 -0
  105. package/docs/ops/TRUST_CONFIG_WIZARD.md +60 -0
  106. package/docs/ops/X402_PILOT_WEEKLY_METRICS.md +76 -0
  107. package/docs/ops/tool-call-disputes-holdback.md +52 -0
  108. package/docs/pilot-kit/PILOT_PACKAGE_SCORECARD_X402.md +46 -0
  109. package/docs/pilot-kit/README.md +29 -0
  110. package/docs/pilot-kit/architecture-one-pager.md +48 -0
  111. package/docs/pilot-kit/buyer-email.txt +19 -0
  112. package/docs/pilot-kit/buyer-one-pager.md +31 -0
  113. package/docs/pilot-kit/gtm-pilot-playbook.md +182 -0
  114. package/docs/pilot-kit/offline-verify.md +33 -0
  115. package/docs/pilot-kit/procurement-one-pager.md +50 -0
  116. package/docs/pilot-kit/rfp-clause.md +46 -0
  117. package/docs/pilot-kit/roi-calculator-template.csv +2 -0
  118. package/docs/pilot-kit/security-qa.md +153 -0
  119. package/docs/pilot-kit/security-summary.md +35 -0
  120. package/docs/plans/2026-02-13-mcp-spike-design.md +113 -0
  121. package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
  122. package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
  123. package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
  124. package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
  125. package/docs/spec/AcceptanceCriteria.v1.md +17 -0
  126. package/docs/spec/AcceptanceEvaluation.v1.md +10 -0
  127. package/docs/spec/AgentEvent.v1.md +47 -0
  128. package/docs/spec/AgentIdentity.v1.md +62 -0
  129. package/docs/spec/AgentPassport.v1.md +95 -0
  130. package/docs/spec/AgentReputation.v1.md +59 -0
  131. package/docs/spec/AgentReputation.v2.md +52 -0
  132. package/docs/spec/AgentRun.v1.md +47 -0
  133. package/docs/spec/AgentRunSettlement.v1.md +52 -0
  134. package/docs/spec/AgentWallet.v1.md +43 -0
  135. package/docs/spec/AgreementDelegation.v1.md +109 -0
  136. package/docs/spec/ArbitrationCase.v1.md +67 -0
  137. package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
  138. package/docs/spec/ArbitrationVerdict.v1.md +60 -0
  139. package/docs/spec/BundleHeadAttestation.v1.md +32 -0
  140. package/docs/spec/CANONICAL_JSON.md +31 -0
  141. package/docs/spec/CRYPTOGRAPHY.md +61 -0
  142. package/docs/spec/ClosePack.v1.md +49 -0
  143. package/docs/spec/ClosePackManifest.v1.md +24 -0
  144. package/docs/spec/DelegationGrant.v1.md +90 -0
  145. package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
  146. package/docs/spec/DisputeOpenEnvelope.v1.md +43 -0
  147. package/docs/spec/ERRORS.md +76 -0
  148. package/docs/spec/ESCROW_NETTING_INVARIANTS.md +71 -0
  149. package/docs/spec/EvidenceIndex.v1.md +20 -0
  150. package/docs/spec/ExecutionIntent.v1.md +90 -0
  151. package/docs/spec/FinancePackBundleManifest.v1.md +24 -0
  152. package/docs/spec/FundingHold.v1.md +60 -0
  153. package/docs/spec/GovernancePolicy.v1.md +34 -0
  154. package/docs/spec/GovernancePolicy.v2.md +30 -0
  155. package/docs/spec/INVARIANTS.md +389 -0
  156. package/docs/spec/InteractionDirectionMatrix.v1.md +30 -0
  157. package/docs/spec/InvoiceBundleManifest.v1.md +24 -0
  158. package/docs/spec/InvoiceClaim.v1.md +11 -0
  159. package/docs/spec/MONEY_RAIL_STATE_MACHINE.md +58 -0
  160. package/docs/spec/MarketplaceAcceptance.v2.md +46 -0
  161. package/docs/spec/MarketplaceOffer.v2.md +54 -0
  162. package/docs/spec/MeteringReport.v1.md +18 -0
  163. package/docs/spec/OperatorAction.v1.md +90 -0
  164. package/docs/spec/PRODUCER_ERRORS.md +42 -0
  165. package/docs/spec/PolicyDecision.v1.md +83 -0
  166. package/docs/spec/PricingMatrix.v1.md +20 -0
  167. package/docs/spec/PricingMatrixSignatures.v1.md +30 -0
  168. package/docs/spec/PricingMatrixSignatures.v2.md +29 -0
  169. package/docs/spec/ProduceCliOutput.v1.md +46 -0
  170. package/docs/spec/ProofBundleManifest.v1.md +24 -0
  171. package/docs/spec/README.md +109 -0
  172. package/docs/spec/REFERENCE_IMPLEMENTATIONS.md +29 -0
  173. package/docs/spec/REFERENCE_VERIFIER_BEHAVIOR.md +68 -0
  174. package/docs/spec/REMOTE_SIGNER.md +66 -0
  175. package/docs/spec/ReleaseIndex.v1.md +32 -0
  176. package/docs/spec/ReleaseIndexSignatures.v1.md +17 -0
  177. package/docs/spec/ReleaseTrust.v1.md +13 -0
  178. package/docs/spec/ReleaseTrust.v2.md +26 -0
  179. package/docs/spec/RemoteSignerRequest.v1.md +21 -0
  180. package/docs/spec/RemoteSignerResponse.v1.md +16 -0
  181. package/docs/spec/ReputationEvent.v1.md +63 -0
  182. package/docs/spec/RevocationList.v1.md +28 -0
  183. package/docs/spec/SIGNER_PROVIDER_PLUGIN.md +32 -0
  184. package/docs/spec/STRICTNESS.md +68 -0
  185. package/docs/spec/SUPPLY_CHAIN.md +33 -0
  186. package/docs/spec/SettlementAdjustment.v1.md +45 -0
  187. package/docs/spec/SettlementDecisionRecord.v1.md +48 -0
  188. package/docs/spec/SettlementDecisionRecord.v2.md +53 -0
  189. package/docs/spec/SettlementDecisionReport.v1.md +44 -0
  190. package/docs/spec/SettlementKernel.v1.md +59 -0
  191. package/docs/spec/SettlementReceipt.v1.md +63 -0
  192. package/docs/spec/SlaDefinition.v1.md +24 -0
  193. package/docs/spec/SlaEvaluation.v1.md +12 -0
  194. package/docs/spec/THREAT_MODEL.md +113 -0
  195. package/docs/spec/TOOL_PROVENANCE.md +30 -0
  196. package/docs/spec/TRUST_ANCHORS.md +84 -0
  197. package/docs/spec/TenantSettings.v1.md +90 -0
  198. package/docs/spec/TenantSettings.v2.md +99 -0
  199. package/docs/spec/TimestampProof.v1.md +25 -0
  200. package/docs/spec/ToolCallAgreement.v1.md +34 -0
  201. package/docs/spec/ToolCallEvidence.v1.md +47 -0
  202. package/docs/spec/ToolManifest.v1.md +47 -0
  203. package/docs/spec/VERIFIER_ENVIRONMENT.md +38 -0
  204. package/docs/spec/VERSIONING.md +107 -0
  205. package/docs/spec/VerificationReport.v1.md +50 -0
  206. package/docs/spec/VerifyAboutOutput.v1.md +10 -0
  207. package/docs/spec/VerifyCliOutput.v1.md +28 -0
  208. package/docs/spec/WARNINGS.md +83 -0
  209. package/docs/spec/error-codes.v1.txt +285 -0
  210. package/docs/spec/examples/agreement_delegation_v1.example.json +21 -0
  211. package/docs/spec/examples/arbitration_case_v1.example.json +26 -0
  212. package/docs/spec/examples/arbitration_verdict_v1.example.json +32 -0
  213. package/docs/spec/examples/dispute_open_envelope_v1.example.json +18 -0
  214. package/docs/spec/examples/produce_cli_output_v1.example.json +32 -0
  215. package/docs/spec/examples/release_index_signature_v1.example.json +9 -0
  216. package/docs/spec/examples/release_index_signatures_v1.example.json +14 -0
  217. package/docs/spec/examples/release_index_v1.example.json +15 -0
  218. package/docs/spec/examples/release_trust_v1.example.json +7 -0
  219. package/docs/spec/examples/release_trust_v2.example.json +22 -0
  220. package/docs/spec/examples/remote_signer_request_v1.example.json +18 -0
  221. package/docs/spec/examples/remote_signer_response_v1.example.json +8 -0
  222. package/docs/spec/examples/reputation_event_v1.example.json +29 -0
  223. package/docs/spec/examples/verification_report_v1.example.json +24 -0
  224. package/docs/spec/examples/verify_about_output_v1.example.json +29 -0
  225. package/docs/spec/examples/verify_cli_output_v1.example.json +13 -0
  226. package/docs/spec/legacy/MarketplaceAcceptance.v1.md +48 -0
  227. package/docs/spec/legacy/MarketplaceOffer.v1.md +56 -0
  228. package/docs/spec/legacy/schemas/MarketplaceAcceptance.v1.schema.json +53 -0
  229. package/docs/spec/legacy/schemas/MarketplaceOffer.v1.schema.json +61 -0
  230. package/docs/spec/producer-error-codes.v1.txt +14 -0
  231. package/docs/spec/schemas/AcceptanceCriteria.v1.schema.json +24 -0
  232. package/docs/spec/schemas/AcceptanceEvaluation.v1.schema.json +26 -0
  233. package/docs/spec/schemas/AgentEvent.v1.schema.json +49 -0
  234. package/docs/spec/schemas/AgentIdentity.v1.schema.json +129 -0
  235. package/docs/spec/schemas/AgentPassport.v1.schema.json +112 -0
  236. package/docs/spec/schemas/AgentReputation.v1.schema.json +151 -0
  237. package/docs/spec/schemas/AgentReputation.v2.schema.json +120 -0
  238. package/docs/spec/schemas/AgentRun.v1.schema.json +71 -0
  239. package/docs/spec/schemas/AgentRunSettlement.v1.schema.json +75 -0
  240. package/docs/spec/schemas/AgentWallet.v1.schema.json +54 -0
  241. package/docs/spec/schemas/AgreementDelegation.v1.schema.json +50 -0
  242. package/docs/spec/schemas/ArbitrationCase.v1.schema.json +133 -0
  243. package/docs/spec/schemas/ArbitrationVerdict.v1.schema.json +149 -0
  244. package/docs/spec/schemas/BundleHeadAttestation.v1.schema.json +21 -0
  245. package/docs/spec/schemas/ClosePackManifest.v1.schema.json +38 -0
  246. package/docs/spec/schemas/DelegationGrant.v1.schema.json +102 -0
  247. package/docs/spec/schemas/DisputeOpenEnvelope.v1.schema.json +78 -0
  248. package/docs/spec/schemas/EvidenceIndex.v1.schema.json +41 -0
  249. package/docs/spec/schemas/ExecutionIntent.v1.schema.json +85 -0
  250. package/docs/spec/schemas/FinancePackBundleManifest.v1.schema.json +38 -0
  251. package/docs/spec/schemas/FundingHold.v1.schema.json +46 -0
  252. package/docs/spec/schemas/GovernancePolicy.v1.schema.json +45 -0
  253. package/docs/spec/schemas/GovernancePolicy.v2.schema.json +70 -0
  254. package/docs/spec/schemas/InteractionDirectionMatrix.v1.schema.json +43 -0
  255. package/docs/spec/schemas/InvoiceBundleManifest.v1.schema.json +38 -0
  256. package/docs/spec/schemas/InvoiceClaim.v1.schema.json +39 -0
  257. package/docs/spec/schemas/MarketplaceAcceptance.v2.schema.json +53 -0
  258. package/docs/spec/schemas/MarketplaceOffer.v2.schema.json +61 -0
  259. package/docs/spec/schemas/MeteringReport.v1.schema.json +45 -0
  260. package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
  261. package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
  262. package/docs/spec/schemas/PricingMatrix.v1.schema.json +24 -0
  263. package/docs/spec/schemas/PricingMatrixSignatures.v1.schema.json +24 -0
  264. package/docs/spec/schemas/PricingMatrixSignatures.v2.schema.json +24 -0
  265. package/docs/spec/schemas/ProduceCliOutput.v1.schema.json +107 -0
  266. package/docs/spec/schemas/ProofBundleManifest.v1.schema.json +37 -0
  267. package/docs/spec/schemas/PublicKeys.v1.schema.json +33 -0
  268. package/docs/spec/schemas/ReleaseIndex.v1.schema.json +45 -0
  269. package/docs/spec/schemas/ReleaseIndexSignature.v1.schema.json +16 -0
  270. package/docs/spec/schemas/ReleaseIndexSignatures.v1.schema.json +16 -0
  271. package/docs/spec/schemas/ReleaseTrust.v1.schema.json +15 -0
  272. package/docs/spec/schemas/ReleaseTrust.v2.schema.json +37 -0
  273. package/docs/spec/schemas/RemoteSignerPublicKeyResponse.v1.schema.json +14 -0
  274. package/docs/spec/schemas/RemoteSignerRequest.v1.schema.json +24 -0
  275. package/docs/spec/schemas/RemoteSignerResponse.v1.schema.json +10 -0
  276. package/docs/spec/schemas/RemoteSignerSignRequest.v1.schema.json +27 -0
  277. package/docs/spec/schemas/RemoteSignerSignResponse.v1.schema.json +16 -0
  278. package/docs/spec/schemas/ReputationEvent.v1.schema.json +164 -0
  279. package/docs/spec/schemas/RevocationList.v1.schema.json +51 -0
  280. package/docs/spec/schemas/SettlementAdjustment.v1.schema.json +44 -0
  281. package/docs/spec/schemas/SettlementDecisionRecord.v1.schema.json +66 -0
  282. package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +149 -0
  283. package/docs/spec/schemas/SettlementDecisionReport.v1.schema.json +61 -0
  284. package/docs/spec/schemas/SettlementReceipt.v1.schema.json +135 -0
  285. package/docs/spec/schemas/SlaDefinition.v1.schema.json +33 -0
  286. package/docs/spec/schemas/SlaEvaluation.v1.schema.json +26 -0
  287. package/docs/spec/schemas/TenantSettings.v1.schema.json +90 -0
  288. package/docs/spec/schemas/TenantSettings.v2.schema.json +161 -0
  289. package/docs/spec/schemas/TimestampProof.v1.schema.json +17 -0
  290. package/docs/spec/schemas/ToolCallAgreement.v1.schema.json +34 -0
  291. package/docs/spec/schemas/ToolCallEvidence.v1.schema.json +45 -0
  292. package/docs/spec/schemas/ToolManifest.v1.schema.json +54 -0
  293. package/docs/spec/schemas/VerificationReport.v1.schema.json +83 -0
  294. package/docs/spec/schemas/VerifyAboutOutput.v1.schema.json +54 -0
  295. package/docs/spec/schemas/VerifyCliOutput.v1.schema.json +75 -0
  296. package/docs/spec/schemas/VerifyReleaseOutput.v1.schema.json +47 -0
  297. package/docs/spec/x402-error-codes.v1.txt +35 -0
  298. package/docs/templates/buyer-email.txt +18 -0
  299. package/docs/templates/buyer-one-pager.md +24 -0
  300. package/package.json +53 -6
  301. package/scripts/acceptance/full-stack.mjs +734 -0
  302. package/scripts/acceptance/full-stack.sh +99 -0
  303. package/scripts/audit/build-audit-packet.mjs +242 -0
  304. package/scripts/backup-pg.sh +45 -0
  305. package/scripts/backup-restore/README.md +18 -0
  306. package/scripts/backup-restore/capture-state.mjs +130 -0
  307. package/scripts/backup-restore/client.mjs +97 -0
  308. package/scripts/backup-restore/seed-workload.mjs +235 -0
  309. package/scripts/backup-restore/verify-state.mjs +139 -0
  310. package/scripts/backup-restore-test.sh +217 -0
  311. package/scripts/chaos.js +221 -0
  312. package/scripts/ci/build-launch-cutover-packet.mjs +304 -0
  313. package/scripts/ci/build-self-serve-benchmark-report.mjs +122 -0
  314. package/scripts/ci/changelog-guard.mjs +145 -0
  315. package/scripts/ci/check-kernel-v0-launch-gate.mjs +233 -0
  316. package/scripts/ci/check-secret-hygiene.mjs +78 -0
  317. package/scripts/ci/check-version-consistency.mjs +42 -0
  318. package/scripts/ci/cli-pack-smoke.mjs +160 -0
  319. package/scripts/ci/flake-budget-guard.mjs +68 -0
  320. package/scripts/ci/generate-error-codes.mjs +54 -0
  321. package/scripts/ci/lib/lighthouse-tracker.mjs +90 -0
  322. package/scripts/ci/lib/self-serve-launch-gate.mjs +89 -0
  323. package/scripts/ci/npm-pack-smoke.mjs +454 -0
  324. package/scripts/ci/run-10x-throughput-drill.mjs +318 -0
  325. package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +368 -0
  326. package/scripts/ci/run-arbitration-workspace-browser-e2e.sh +22 -0
  327. package/scripts/ci/run-circle-sandbox-smoke.mjs +237 -0
  328. package/scripts/ci/run-go-live-gate.mjs +150 -0
  329. package/scripts/ci/run-kernel-v0-ship-gate.mjs +97 -0
  330. package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
  331. package/scripts/ci/run-mcp-host-smoke.mjs +473 -0
  332. package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
  333. package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
  334. package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
  335. package/scripts/ci/run-production-cutover-gate.mjs +540 -0
  336. package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
  337. package/scripts/ci/run-release-promotion-guard.mjs +756 -0
  338. package/scripts/ci/run-self-serve-launch-gate.mjs +56 -0
  339. package/scripts/ci/runtime-import-smoke.mjs +58 -0
  340. package/scripts/ci/update-lighthouse-tracker.mjs +112 -0
  341. package/scripts/closepack/lib.mjs +286 -0
  342. package/scripts/collect-debug.sh +263 -0
  343. package/scripts/demo/compositional-settlement-3hop.mjs +237 -0
  344. package/scripts/demo/delivery-robot/export-ui-fixture.mjs +188 -0
  345. package/scripts/demo/delivery-robot/generate.mjs +377 -0
  346. package/scripts/demo/kernel-agent-goes-shopping.mjs +202 -0
  347. package/scripts/demo/magic-link-first-green.mjs +118 -0
  348. package/scripts/demo/magic-link-kind-smoke.mjs +577 -0
  349. package/scripts/demo/mcp-paid-exa.mjs +1110 -0
  350. package/scripts/dev/billing-doctor.sh +145 -0
  351. package/scripts/dev/billing-smoke-prod.sh +219 -0
  352. package/scripts/dev/billing-webhook-replay.sh +161 -0
  353. package/scripts/dev/env.dev.example +29 -0
  354. package/scripts/dev/env.sh +37 -0
  355. package/scripts/dev/new-sdk-key.sh +81 -0
  356. package/scripts/dev/sdk-first-run.sh +21 -0
  357. package/scripts/dev/smoke-x402-gateway.sh +115 -0
  358. package/scripts/dev/start-api.sh +24 -0
  359. package/scripts/doctor/mcp-host.mjs +120 -0
  360. package/scripts/examples/produce-and-verify-jobproof.mjs +191 -0
  361. package/scripts/examples/sdk-first-paid-rfq.py +105 -0
  362. package/scripts/examples/sdk-first-verified-run.mjs +85 -0
  363. package/scripts/examples/sdk-first-verified-run.py +99 -0
  364. package/scripts/examples/sdk-tenant-analytics.mjs +103 -0
  365. package/scripts/examples/sdk-tenant-analytics.py +118 -0
  366. package/scripts/finance-pack/bundle.mjs +284 -0
  367. package/scripts/fixtures/generate-bundle-fixtures.mjs +877 -0
  368. package/scripts/governance/export.mjs +169 -0
  369. package/scripts/load/delivery-stress.k6.js +183 -0
  370. package/scripts/load/ingest-burst.k6.js +236 -0
  371. package/scripts/load/run-delivery-load.js +66 -0
  372. package/scripts/load/webhook-receiver.js +131 -0
  373. package/scripts/magic-link/migrate-run-records-to-db.mjs +35 -0
  374. package/scripts/mcp/probe.mjs +238 -0
  375. package/scripts/mcp/settld-mcp-http-gateway.mjs +178 -0
  376. package/scripts/mcp/settld-mcp-server.mjs +1511 -0
  377. package/scripts/openapi/write.mjs +13 -0
  378. package/scripts/ops/bootstrap-tenant-conformance.mjs +185 -0
  379. package/scripts/ops/build-x402-pilot-reliability-report.mjs +489 -0
  380. package/scripts/ops/check-x402-receipt-sample.mjs +181 -0
  381. package/scripts/ops/design-partner-run-packet.mjs +466 -0
  382. package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
  383. package/scripts/ops/hosted-baseline-evidence.mjs +890 -0
  384. package/scripts/ops/money-rails-chargeback-evidence.mjs +509 -0
  385. package/scripts/ops/money-rails-reconcile-evidence.mjs +180 -0
  386. package/scripts/ops/p0-seed-money-rail-operation.mjs +432 -0
  387. package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
  388. package/scripts/pilot/finance-pack.mjs +495 -0
  389. package/scripts/pilot/fixtures/robot-keypair.json +4 -0
  390. package/scripts/pilot/fixtures/server-signer.json +4 -0
  391. package/scripts/policy/cli.mjs +600 -0
  392. package/scripts/profile/cli.mjs +1324 -0
  393. package/scripts/proof-bundle/job.mjs +109 -0
  394. package/scripts/proof-bundle/lib.mjs +92 -0
  395. package/scripts/proof-bundle/month.mjs +103 -0
  396. package/scripts/provider/conformance-run.mjs +159 -0
  397. package/scripts/provider/keys-generate.mjs +135 -0
  398. package/scripts/provider/publish.mjs +420 -0
  399. package/scripts/quickstart/x402.mjs +334 -0
  400. package/scripts/register-entity-secret.mjs +102 -0
  401. package/scripts/release/build-artifacts.mjs +181 -0
  402. package/scripts/release/generate-release-index.mjs +112 -0
  403. package/scripts/release/release-index-lib.mjs +232 -0
  404. package/scripts/release/sign-release-index.mjs +85 -0
  405. package/scripts/release/validate-release-assets.mjs +170 -0
  406. package/scripts/release/verify-release.mjs +261 -0
  407. package/scripts/restore-pg.sh +34 -0
  408. package/scripts/scaffold/create-settld-paid-tool.mjs +19 -0
  409. package/scripts/sdk/smoke-python.py +30 -0
  410. package/scripts/sdk/smoke.mjs +16 -0
  411. package/scripts/settlement/x402-batch-worker.mjs +1091 -0
  412. package/scripts/setup/circle-bootstrap.mjs +310 -0
  413. package/scripts/setup/host-config.mjs +617 -0
  414. package/scripts/setup/onboard.mjs +1337 -0
  415. package/scripts/setup/openclaw-onboard.mjs +423 -0
  416. package/scripts/setup/wizard.mjs +986 -0
  417. package/scripts/slo/check.mjs +239 -0
  418. package/scripts/smoke/k8s-smoke.mjs +214 -0
  419. package/scripts/spec/generate-protocol-vectors.mjs +1019 -0
  420. package/scripts/test/check-no-generated-artifacts.sh +12 -0
  421. package/scripts/test/run.sh +59 -0
  422. package/scripts/trust/validate-trust-file.mjs +57 -0
  423. package/scripts/trust-config/rotate-settld-pay.mjs +277 -0
  424. package/scripts/trust-config/wizard.mjs +161 -0
  425. package/scripts/vendor-contract-test-lib.mjs +182 -0
  426. package/scripts/vendor-contract-test.mjs +55 -0
  427. package/scripts/vercel/build-mkdocs.sh +9 -0
  428. package/scripts/vercel/ignore-mkdocs.sh +25 -0
  429. package/scripts/vercel/install-mkdocs.sh +6 -0
  430. package/scripts/verify-pg.js +217 -0
  431. package/scripts/x402/receipt-verify.mjs +289 -0
  432. package/services/finance-sink/src/dedupe-store.js +29 -6
  433. package/services/receiver/src/dedupe-store.js +29 -5
  434. package/services/x402-gateway/Dockerfile +13 -0
  435. package/services/x402-gateway/README.md +58 -0
  436. package/services/x402-gateway/examples/upstream-mock.js +337 -0
  437. package/services/x402-gateway/src/server.js +1058 -0
  438. package/src/api/app.js +34658 -16940
  439. package/src/api/maintenance.js +70 -0
  440. package/src/api/middleware/trust-kernel.js +114 -0
  441. package/src/api/openapi.js +1778 -70
  442. package/src/api/persistence.js +456 -0
  443. package/src/api/server.js +81 -5
  444. package/src/api/store.js +1581 -62
  445. package/src/api/workers/deliveries.js +99 -4
  446. package/src/api/workers/insolvency-sweep.js +159 -0
  447. package/src/core/agent-card.js +69 -0
  448. package/src/core/agent-wallets.js +231 -0
  449. package/src/core/agreement-delegation.js +549 -0
  450. package/src/core/billing-plans.js +40 -6
  451. package/src/core/circle-reserve-adapter.js +845 -0
  452. package/src/core/event-policy.js +21 -2
  453. package/src/core/maintenance-locks.js +1 -0
  454. package/src/core/operator-action.js +303 -0
  455. package/src/core/paid-tool-manifest.js +318 -0
  456. package/src/core/policy-decision.js +322 -0
  457. package/src/core/policy-packs.js +207 -0
  458. package/src/core/profile-fingerprint.js +27 -0
  459. package/src/core/profile-simulation-reasons.js +84 -0
  460. package/src/core/profile-templates.js +242 -0
  461. package/src/core/provider-publish-conformance.js +525 -0
  462. package/src/core/provider-publish-proof.js +396 -0
  463. package/src/core/provider-quote-signature.js +170 -0
  464. package/src/core/settld-keys.js +112 -0
  465. package/src/core/settld-pay-token.js +344 -0
  466. package/src/core/settlement-kernel.js +239 -2
  467. package/src/core/settlement-verifier.js +335 -0
  468. package/src/core/tool-call-agreement.js +112 -0
  469. package/src/core/tool-call-evidence.js +144 -0
  470. package/src/core/tool-provider-signature.js +98 -0
  471. package/src/core/wallet-assignment-resolver.js +129 -0
  472. package/src/core/wallet-provider-bootstrap.js +365 -0
  473. package/src/core/x402-escalation-override.js +258 -0
  474. package/src/core/x402-gate.js +118 -0
  475. package/src/core/x402-provider-refund-decision.js +220 -0
  476. package/src/core/x402-receipt-verifier.js +708 -0
  477. package/src/core/x402-reversal-command.js +251 -0
  478. package/src/core/x402-wallet-issuer-decision.js +252 -0
  479. package/src/core/zk-verifier.js +300 -0
  480. package/src/db/migrations/029_reputation_event_index.sql +54 -0
  481. package/src/db/migrations/030_artifacts_source_event_unique_job_only.sql +15 -0
  482. package/src/db/pg.js +18 -7
  483. package/src/db/store-pg.js +1508 -111
package/src/api/store.js CHANGED
@@ -18,23 +18,159 @@ import { computeFinanceAccountMapHash, validateFinanceAccountMapV1 } from "../co
18
18
  import { appendChainedEvent, createChainedEvent } from "../core/event-chain.js";
19
19
  import { normalizeBillingPlanId } from "../core/billing-plans.js";
20
20
 
21
+ const SERVER_SIGNER_FILENAME = "server-signer.json";
22
+ const SETTLD_PAY_KEYSET_STORE_FILENAME = "settld-pay-keyset-store.json";
23
+ const EMERGENCY_SCOPE_TYPE = Object.freeze({
24
+ TENANT: "tenant",
25
+ AGENT: "agent",
26
+ ADAPTER: "adapter"
27
+ });
28
+ const EMERGENCY_SCOPE_TYPES = new Set(Object.values(EMERGENCY_SCOPE_TYPE));
29
+ const EMERGENCY_CONTROL_TYPE = Object.freeze({
30
+ PAUSE: "pause",
31
+ QUARANTINE: "quarantine",
32
+ REVOKE: "revoke",
33
+ KILL_SWITCH: "kill-switch"
34
+ });
35
+ const EMERGENCY_CONTROL_TYPES = Object.values(EMERGENCY_CONTROL_TYPE);
36
+ const EMERGENCY_CONTROL_TYPES_SET = new Set(EMERGENCY_CONTROL_TYPES);
37
+ const EMERGENCY_ACTION = Object.freeze({
38
+ PAUSE: "pause",
39
+ QUARANTINE: "quarantine",
40
+ REVOKE: "revoke",
41
+ KILL_SWITCH: "kill-switch",
42
+ RESUME: "resume"
43
+ });
44
+ const EMERGENCY_ACTIONS = new Set(Object.values(EMERGENCY_ACTION));
45
+
46
+ function readJsonFileSafe(filePath) {
47
+ if (!fs.existsSync(filePath)) return null;
48
+ const raw = fs.readFileSync(filePath, "utf8");
49
+ return JSON.parse(raw);
50
+ }
51
+
52
+ function normalizeSettldPayPreviousRows(rows) {
53
+ const out = [];
54
+ const list = Array.isArray(rows) ? rows : [];
55
+ const seen = new Set();
56
+ for (const row of list) {
57
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
58
+ const publicKeyPem = typeof row.publicKeyPem === "string" ? row.publicKeyPem : "";
59
+ if (!publicKeyPem.trim()) continue;
60
+ const derivedKeyId = keyIdFromPublicKeyPem(publicKeyPem);
61
+ const keyId = typeof row.keyId === "string" && row.keyId.trim() !== "" ? row.keyId.trim() : derivedKeyId;
62
+ if (keyId !== derivedKeyId) throw new Error("invalid settld-pay-keyset-store.json: previous[].keyId mismatch");
63
+ if (seen.has(keyId)) continue;
64
+ seen.add(keyId);
65
+ const rotatedAt = typeof row.rotatedAt === "string" && row.rotatedAt.trim() !== "" ? row.rotatedAt.trim() : null;
66
+ out.push({ keyId, publicKeyPem, rotatedAt });
67
+ }
68
+ return out;
69
+ }
70
+
71
+ function normalizeSettldPayKeysetStore(payload) {
72
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
73
+ throw new Error("invalid settld-pay-keyset-store.json: object expected");
74
+ }
75
+ const active = payload.active;
76
+ if (!active || typeof active !== "object" || Array.isArray(active)) {
77
+ throw new Error("invalid settld-pay-keyset-store.json: active key missing");
78
+ }
79
+ const activePublicKeyPem = typeof active.publicKeyPem === "string" ? active.publicKeyPem : "";
80
+ const activePrivateKeyPem = typeof active.privateKeyPem === "string" ? active.privateKeyPem : "";
81
+ if (!activePublicKeyPem.trim() || !activePrivateKeyPem.trim()) {
82
+ throw new Error("invalid settld-pay-keyset-store.json: active keypair missing");
83
+ }
84
+ const activeDerivedKeyId = keyIdFromPublicKeyPem(activePublicKeyPem);
85
+ const activeKeyId = typeof active.keyId === "string" && active.keyId.trim() !== "" ? active.keyId.trim() : activeDerivedKeyId;
86
+ if (activeKeyId !== activeDerivedKeyId) throw new Error("invalid settld-pay-keyset-store.json: active.keyId mismatch");
87
+
88
+ const previous = normalizeSettldPayPreviousRows(payload.previous);
89
+ return {
90
+ active: {
91
+ keyId: activeKeyId,
92
+ publicKeyPem: activePublicKeyPem,
93
+ privateKeyPem: activePrivateKeyPem
94
+ },
95
+ previous
96
+ };
97
+ }
98
+
21
99
  function loadOrCreateServerSigner({ persistenceDir }) {
22
100
  if (!persistenceDir) {
23
101
  const { publicKeyPem, privateKeyPem } = createEd25519Keypair();
24
- return { publicKeyPem, privateKeyPem };
102
+ return { active: { publicKeyPem, privateKeyPem }, previous: [], source: "generated-ephemeral" };
25
103
  }
26
104
 
27
105
  fs.mkdirSync(persistenceDir, { recursive: true });
28
- const signerPath = path.join(persistenceDir, "server-signer.json");
106
+ const keysetStorePath = path.join(persistenceDir, SETTLD_PAY_KEYSET_STORE_FILENAME);
107
+ const signerPath = path.join(persistenceDir, SERVER_SIGNER_FILENAME);
108
+ if (fs.existsSync(keysetStorePath)) {
109
+ const parsed = readJsonFileSafe(keysetStorePath);
110
+ const normalized = normalizeSettldPayKeysetStore(parsed);
111
+ // Keep legacy signer file in sync for compatibility with existing tooling.
112
+ fs.writeFileSync(
113
+ signerPath,
114
+ JSON.stringify({ publicKeyPem: normalized.active.publicKeyPem, privateKeyPem: normalized.active.privateKeyPem }, null, 2),
115
+ "utf8"
116
+ );
117
+ return {
118
+ active: { publicKeyPem: normalized.active.publicKeyPem, privateKeyPem: normalized.active.privateKeyPem },
119
+ previous: normalized.previous,
120
+ source: "keyset-store"
121
+ };
122
+ }
123
+
29
124
  if (fs.existsSync(signerPath)) {
30
125
  const parsed = JSON.parse(fs.readFileSync(signerPath, "utf8"));
31
126
  if (!parsed?.publicKeyPem || !parsed?.privateKeyPem) throw new Error("invalid server-signer.json");
32
- return { publicKeyPem: parsed.publicKeyPem, privateKeyPem: parsed.privateKeyPem };
127
+ return {
128
+ active: { publicKeyPem: parsed.publicKeyPem, privateKeyPem: parsed.privateKeyPem },
129
+ previous: [],
130
+ source: "legacy-signer"
131
+ };
33
132
  }
34
133
 
35
134
  const { publicKeyPem, privateKeyPem } = createEd25519Keypair();
36
135
  fs.writeFileSync(signerPath, JSON.stringify({ publicKeyPem, privateKeyPem }, null, 2), "utf8");
37
- return { publicKeyPem, privateKeyPem };
136
+ return { active: { publicKeyPem, privateKeyPem }, previous: [], source: "generated-persistent" };
137
+ }
138
+
139
+ function normalizeEmergencyScopeType(value) {
140
+ const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
141
+ if (!EMERGENCY_SCOPE_TYPES.has(normalized)) throw new TypeError("scope.type must be tenant|agent|adapter");
142
+ return normalized;
143
+ }
144
+
145
+ function normalizeEmergencyScopeId(scopeType, value) {
146
+ if (scopeType === EMERGENCY_SCOPE_TYPE.TENANT) return null;
147
+ const normalized = typeof value === "string" ? value.trim() : "";
148
+ if (!normalized) throw new TypeError("scope.id is required for scope.type agent|adapter");
149
+ return normalized;
150
+ }
151
+
152
+ function normalizeEmergencyControlType(value, { allowNull = false } = {}) {
153
+ if (allowNull && (value === null || value === undefined || String(value).trim() === "")) return null;
154
+ const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
155
+ if (!EMERGENCY_CONTROL_TYPES_SET.has(normalized)) throw new TypeError("controlType must be pause|quarantine|revoke|kill-switch");
156
+ return normalized;
157
+ }
158
+
159
+ function normalizeEmergencyAction(value) {
160
+ const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
161
+ if (!EMERGENCY_ACTIONS.has(normalized)) throw new TypeError("action must be pause|quarantine|revoke|kill-switch|resume");
162
+ return normalized;
163
+ }
164
+
165
+ function normalizeEmergencyResumeControlTypes(value) {
166
+ const values = Array.isArray(value) ? value : value === null || value === undefined ? EMERGENCY_CONTROL_TYPES : [value];
167
+ const dedupe = new Set();
168
+ for (const item of values) {
169
+ dedupe.add(normalizeEmergencyControlType(item, { allowNull: false }));
170
+ }
171
+ const out = Array.from(dedupe.values());
172
+ out.sort((left, right) => left.localeCompare(right));
173
+ return out;
38
174
  }
39
175
 
40
176
  export function createStore({ persistenceDir = null, serverSignerKeypair = null } = {}) {
@@ -49,8 +185,18 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
49
185
  throw new Error("invalid serverSignerKeypair");
50
186
  }
51
187
 
52
- const { publicKeyPem: serverPublicKeyPem, privateKeyPem: serverPrivateKeyPem } =
53
- resolvedServerSignerKeypair ?? loadOrCreateServerSigner({ persistenceDir });
188
+ const loadedServerSigner =
189
+ resolvedServerSignerKeypair === null
190
+ ? loadOrCreateServerSigner({ persistenceDir })
191
+ : {
192
+ active: {
193
+ publicKeyPem: resolvedServerSignerKeypair.publicKeyPem,
194
+ privateKeyPem: resolvedServerSignerKeypair.privateKeyPem
195
+ },
196
+ previous: [],
197
+ source: "explicit-override"
198
+ };
199
+ const { publicKeyPem: serverPublicKeyPem, privateKeyPem: serverPrivateKeyPem } = loadedServerSigner.active;
54
200
  const serverKeyId = keyIdFromPublicKeyPem(serverPublicKeyPem);
55
201
 
56
202
  function parseEvidenceRetentionMaxDaysByTenant() {
@@ -217,11 +363,27 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
217
363
  operators: new Map(), // `${tenantId}\n${operatorId}` -> snapshot
218
364
  operatorEvents: new Map(), // `${tenantId}\n${operatorId}` -> chained events
219
365
  agentIdentities: new Map(), // `${tenantId}\n${agentId}` -> AgentIdentity.v1 record
366
+ agentPassports: new Map(), // `${tenantId}\n${agentId}` -> AgentPassport.v1 record
220
367
  agentWallets: new Map(), // `${tenantId}\n${agentId}` -> AgentWallet.v1 record
221
368
  agentRuns: new Map(), // `${tenantId}\n${runId}` -> AgentRun.v1 snapshot
222
369
  agentRunEvents: new Map(), // `${tenantId}\n${runId}` -> AgentEvent.v1[]
223
370
  agentRunSettlements: new Map(), // `${tenantId}\n${runId}` -> AgentRunSettlement.v1
224
371
  arbitrationCases: new Map(), // `${tenantId}\n${caseId}` -> ArbitrationCase.v1 snapshot
372
+ agreementDelegations: new Map(), // `${tenantId}\n${delegationId}` -> AgreementDelegation.v1
373
+ x402Gates: new Map(), // `${tenantId}\n${gateId}` -> X402 gate record (internal API surface)
374
+ x402Receipts: new Map(), // `${tenantId}\n${receiptId}` -> immutable X402ReceiptRecord.v1 base record
375
+ x402WalletPolicies: new Map(), // `${tenantId}\n${sponsorWalletRef}::${policyRef}::${policyVersion}` -> X402WalletPolicy.v1
376
+ x402ZkVerificationKeys: new Map(), // `${tenantId}\n${verificationKeyId}` -> X402ZkVerificationKey.v1
377
+ x402ReversalEvents: new Map(), // `${tenantId}\n${eventId}` -> X402GateReversalEvent.v1
378
+ x402ReversalNonceUsage: new Map(), // `${tenantId}\n${sponsorRef}\n${nonce}` -> X402ReversalNonceUsage.v1
379
+ x402ReversalCommandUsage: new Map(), // `${tenantId}\n${commandId}` -> X402ReversalCommandUsage.v1
380
+ x402Escalations: new Map(), // `${tenantId}\n${escalationId}` -> X402AuthorizationEscalation.v1
381
+ x402EscalationEvents: new Map(), // `${tenantId}\n${eventId}` -> X402AuthorizationEscalationEvent.v1
382
+ x402EscalationOverrideUsage: new Map(), // `${tenantId}\n${overrideId}` -> X402EscalationOverrideUsage.v1
383
+ x402AgentLifecycles: new Map(), // `${tenantId}\n${agentId}` -> X402AgentLifecycle.v1
384
+ x402WebhookEndpoints: new Map(), // `${tenantId}\n${endpointId}` -> X402WebhookEndpoint.v1
385
+ emergencyControlEvents: new Map(), // `${tenantId}\n${eventId}` -> OpsEmergencyControlEvent.v1
386
+ emergencyControlState: new Map(), // `${tenantId}\n${scopeType}::${scopeId}::${controlType}` -> OpsEmergencyControlState.v1
225
387
  toolCallHolds: new Map(), // `${tenantId}\n${holdHash}` -> FundingHold.v1 snapshot
226
388
  settlementAdjustments: new Map(), // `${tenantId}\n${adjustmentId}` -> SettlementAdjustment.v1 snapshot
227
389
  moneyRailOperations: new Map(), // `${tenantId}\n${providerId}\n${operationId}` -> MoneyRailOperation.v1
@@ -230,12 +392,14 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
230
392
  financeReconciliationTriages: new Map(), // `${tenantId}\n${triageKey}` -> FinanceReconciliationTriage.v1
231
393
  marketplaceRfqs: new Map(), // `${tenantId}\n${rfqId}` -> MarketplaceRfq.v1
232
394
  marketplaceRfqBids: new Map(), // `${tenantId}\n${rfqId}` -> MarketplaceBid.v1[]
395
+ marketplaceProviderPublications: new Map(), // `${tenantId}\n${providerRef}` -> MarketplaceProviderPublication.v1
233
396
  tenantSettlementPolicies: new Map(), // `${tenantId}\n${policyId}\n${policyVersion}` -> TenantSettlementPolicy.v1
234
397
  tenantSettlementPolicyRollouts: new Map(), // `${tenantId}\nrollout` -> TenantSettlementPolicyRollout.v1
235
398
  contracts: new Map(), // `${tenantId}\n${contractId}` -> contract
236
399
  idempotency: new Map(),
237
400
  publicKeyByKeyId,
238
401
  serverSigner: { keyId: serverKeyId, publicKeyPem: serverPublicKeyPem, privateKeyPem: serverPrivateKeyPem },
402
+ settldPayFallbackKeys: loadedServerSigner.previous.map((row) => ({ keyId: row.keyId, publicKeyPem: row.publicKeyPem })),
239
403
  ledgerByTenant,
240
404
  // Back-compat: keep store.ledger and store.config as the default tenant's objects.
241
405
  ledger: ledgerByTenant.get(DEFAULT_TENANT_ID),
@@ -254,6 +418,7 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
254
418
  monthCloseCursor: 0,
255
419
  artifactCursor: 0,
256
420
  deliveryCursor: 0,
421
+ x402WinddownReversalCursor: 0,
257
422
  persistence: persistenceDir ? createFileTxLog({ dir: persistenceDir }) : null
258
423
  };
259
424
 
@@ -692,100 +857,1223 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
692
857
  updatedAt: nowAt
693
858
  };
694
859
 
695
- const ops = [
696
- { kind: "AGENT_IDENTITY_UPSERT", tenantId, agentIdentity: record },
697
- { kind: "PUBLIC_KEY_PUT", keyId: String(keyId), publicKeyPem: String(publicKeyPem) }
698
- ];
699
- await store.commitTx({ at: nowAt, ops, audit });
700
- return store.getAgentIdentity({ tenantId, agentId: String(agentId) });
860
+ const ops = [
861
+ { kind: "AGENT_IDENTITY_UPSERT", tenantId, agentIdentity: record },
862
+ { kind: "PUBLIC_KEY_PUT", keyId: String(keyId), publicKeyPem: String(publicKeyPem) }
863
+ ];
864
+ await store.commitTx({ at: nowAt, ops, audit });
865
+ return store.getAgentIdentity({ tenantId, agentId: String(agentId) });
866
+ };
867
+
868
+ store.getAgentIdentity = async function getAgentIdentity({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
869
+ tenantId = normalizeTenantId(tenantId);
870
+ if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
871
+ const key = makeScopedKey({ tenantId, id: String(agentId) });
872
+ return store.agentIdentities.get(key) ?? null;
873
+ };
874
+
875
+ store.listAgentIdentities = async function listAgentIdentities({ tenantId = DEFAULT_TENANT_ID, status = null, limit = 200, offset = 0 } = {}) {
876
+ tenantId = normalizeTenantId(tenantId);
877
+ if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
878
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
879
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
880
+ const statusFilter = status ? String(status).trim().toLowerCase() : null;
881
+ const safeLimit = Math.min(1000, limit);
882
+ const safeOffset = offset;
883
+ const out = [];
884
+ for (const row of store.agentIdentities.values()) {
885
+ if (!row || typeof row !== "object") continue;
886
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
887
+ if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
888
+ out.push(row);
889
+ }
890
+ out.sort((left, right) => String(left.agentId ?? "").localeCompare(String(right.agentId ?? "")));
891
+ return out.slice(safeOffset, safeOffset + safeLimit);
892
+ };
893
+
894
+ store.putAgentPassport = async function putAgentPassport({ tenantId = DEFAULT_TENANT_ID, agentPassport, audit = null } = {}) {
895
+ tenantId = normalizeTenantId(tenantId);
896
+ if (!agentPassport || typeof agentPassport !== "object" || Array.isArray(agentPassport)) {
897
+ throw new TypeError("agentPassport is required");
898
+ }
899
+ const agentId = typeof agentPassport.agentId === "string" ? agentPassport.agentId.trim() : "";
900
+ if (!agentId) throw new TypeError("agentPassport.agentId is required");
901
+ const status = typeof agentPassport.status === "string" ? agentPassport.status.trim().toLowerCase() : "";
902
+ if (status !== "active" && status !== "suspended" && status !== "revoked") {
903
+ throw new TypeError("agentPassport.status must be active|suspended|revoked");
904
+ }
905
+ const at = agentPassport.updatedAt ?? agentPassport.createdAt ?? new Date().toISOString();
906
+ await store.commitTx({
907
+ at,
908
+ ops: [
909
+ {
910
+ kind: "AGENT_PASSPORT_UPSERT",
911
+ tenantId,
912
+ agentId,
913
+ agentPassport: { ...agentPassport, tenantId, agentId, status }
914
+ }
915
+ ],
916
+ audit
917
+ });
918
+ return store.agentPassports.get(makeScopedKey({ tenantId, id: agentId })) ?? null;
919
+ };
920
+
921
+ store.getAgentPassport = async function getAgentPassport({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
922
+ tenantId = normalizeTenantId(tenantId);
923
+ if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
924
+ return store.agentPassports.get(makeScopedKey({ tenantId, id: String(agentId) })) ?? null;
925
+ };
926
+
927
+ store.getAgentWallet = async function getAgentWallet({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
928
+ tenantId = normalizeTenantId(tenantId);
929
+ if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
930
+ return store.agentWallets.get(makeScopedKey({ tenantId, id: String(agentId) })) ?? null;
931
+ };
932
+
933
+ store.putAgentWallet = async function putAgentWallet({ tenantId = DEFAULT_TENANT_ID, wallet } = {}) {
934
+ tenantId = normalizeTenantId(tenantId);
935
+ if (!wallet || typeof wallet !== "object" || Array.isArray(wallet)) throw new TypeError("wallet is required");
936
+ const agentId = wallet.agentId ?? null;
937
+ if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("wallet.agentId is required");
938
+ const key = makeScopedKey({ tenantId, id: String(agentId) });
939
+ await store.commitTx({ at: wallet.updatedAt ?? new Date().toISOString(), ops: [{ kind: "AGENT_WALLET_UPSERT", tenantId, wallet: { ...wallet, tenantId, agentId } }] });
940
+ return store.agentWallets.get(key) ?? null;
941
+ };
942
+
943
+ store.getAgentRun = async function getAgentRun({ tenantId = DEFAULT_TENANT_ID, runId } = {}) {
944
+ tenantId = normalizeTenantId(tenantId);
945
+ if (typeof runId !== "string" || runId.trim() === "") throw new TypeError("runId is required");
946
+ return store.agentRuns.get(makeScopedKey({ tenantId, id: String(runId) })) ?? null;
947
+ };
948
+
949
+ store.listAgentRuns = async function listAgentRuns({ tenantId = DEFAULT_TENANT_ID, agentId = null, status = null, limit = 200, offset = 0 } = {}) {
950
+ tenantId = normalizeTenantId(tenantId);
951
+ if (agentId !== null && (typeof agentId !== "string" || agentId.trim() === "")) throw new TypeError("agentId must be null or a non-empty string");
952
+ if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
953
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
954
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
955
+
956
+ const statusFilter = status ? String(status).trim().toLowerCase() : null;
957
+ const safeLimit = Math.min(1000, limit);
958
+ const safeOffset = offset;
959
+ const out = [];
960
+ for (const row of store.agentRuns.values()) {
961
+ if (!row || typeof row !== "object") continue;
962
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
963
+ if (agentId !== null && String(row.agentId ?? "") !== String(agentId)) continue;
964
+ if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
965
+ out.push(row);
966
+ }
967
+ out.sort((left, right) => String(left.runId ?? "").localeCompare(String(right.runId ?? "")));
968
+ return out.slice(safeOffset, safeOffset + safeLimit);
969
+ };
970
+
971
+ store.getAgentRunEvents = async function getAgentRunEvents({ tenantId = DEFAULT_TENANT_ID, runId } = {}) {
972
+ tenantId = normalizeTenantId(tenantId);
973
+ if (typeof runId !== "string" || runId.trim() === "") throw new TypeError("runId is required");
974
+ return store.agentRunEvents.get(makeScopedKey({ tenantId, id: String(runId) })) ?? [];
975
+ };
976
+
977
+ store.getAgentRunSettlement = async function getAgentRunSettlement({ tenantId = DEFAULT_TENANT_ID, runId } = {}) {
978
+ tenantId = normalizeTenantId(tenantId);
979
+ if (typeof runId !== "string" || runId.trim() === "") throw new TypeError("runId is required");
980
+ return store.agentRunSettlements.get(makeScopedKey({ tenantId, id: String(runId) })) ?? null;
981
+ };
982
+
983
+ store.sumWalletPolicySpendCentsForDay = async function sumWalletPolicySpendCentsForDay({
984
+ tenantId = DEFAULT_TENANT_ID,
985
+ agentId,
986
+ dayStartIso,
987
+ dayEndIso
988
+ } = {}) {
989
+ tenantId = normalizeTenantId(tenantId);
990
+ if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
991
+ if (typeof dayStartIso !== "string" || dayStartIso.trim() === "" || !Number.isFinite(Date.parse(dayStartIso))) {
992
+ throw new TypeError("dayStartIso must be an ISO date string");
993
+ }
994
+ if (typeof dayEndIso !== "string" || dayEndIso.trim() === "" || !Number.isFinite(Date.parse(dayEndIso))) {
995
+ throw new TypeError("dayEndIso must be an ISO date string");
996
+ }
997
+ const startMs = Date.parse(dayStartIso);
998
+ const endMs = Date.parse(dayEndIso);
999
+ if (!(endMs > startMs)) throw new TypeError("dayEndIso must be after dayStartIso");
1000
+
1001
+ let total = 0;
1002
+
1003
+ // Agent run settlements lock escrow for the full settlement amount.
1004
+ for (const row of store.agentRunSettlements.values()) {
1005
+ if (!row || typeof row !== "object") continue;
1006
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1007
+ if (String(row.payerAgentId ?? "") !== String(agentId)) continue;
1008
+ const lockedAt = row.lockedAt ?? null;
1009
+ const lockedMs = typeof lockedAt === "string" ? Date.parse(lockedAt) : NaN;
1010
+ if (!Number.isFinite(lockedMs) || lockedMs < startMs || lockedMs >= endMs) continue;
1011
+ const amountCents = Number(row.amountCents ?? 0);
1012
+ if (!Number.isSafeInteger(amountCents) || amountCents <= 0) continue;
1013
+ total += amountCents;
1014
+ }
1015
+
1016
+ // Tool-call holds lock escrow for the holdback amount only.
1017
+ for (const row of store.toolCallHolds.values()) {
1018
+ if (!row || typeof row !== "object") continue;
1019
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1020
+ if (String(row.payerAgentId ?? "") !== String(agentId)) continue;
1021
+ const createdAt = row.createdAt ?? null;
1022
+ const createdMs = typeof createdAt === "string" ? Date.parse(createdAt) : NaN;
1023
+ if (!Number.isFinite(createdMs) || createdMs < startMs || createdMs >= endMs) continue;
1024
+ const heldAmountCents = Number(row.heldAmountCents ?? 0);
1025
+ if (!Number.isSafeInteger(heldAmountCents) || heldAmountCents <= 0) continue;
1026
+ total += heldAmountCents;
1027
+ }
1028
+
1029
+ return total;
1030
+ };
1031
+
1032
+ store.getArbitrationCase = async function getArbitrationCase({ tenantId = DEFAULT_TENANT_ID, caseId } = {}) {
1033
+ tenantId = normalizeTenantId(tenantId);
1034
+ if (typeof caseId !== "string" || caseId.trim() === "") throw new TypeError("caseId is required");
1035
+ return store.arbitrationCases.get(makeScopedKey({ tenantId, id: String(caseId) })) ?? null;
1036
+ };
1037
+
1038
+ store.getAgreementDelegation = async function getAgreementDelegation({ tenantId = DEFAULT_TENANT_ID, delegationId } = {}) {
1039
+ tenantId = normalizeTenantId(tenantId);
1040
+ if (typeof delegationId !== "string" || delegationId.trim() === "") throw new TypeError("delegationId is required");
1041
+ return store.agreementDelegations.get(makeScopedKey({ tenantId, id: String(delegationId) })) ?? null;
1042
+ };
1043
+
1044
+ store.putAgreementDelegation = async function putAgreementDelegation({ tenantId = DEFAULT_TENANT_ID, delegation, audit = null } = {}) {
1045
+ tenantId = normalizeTenantId(tenantId);
1046
+ if (!delegation || typeof delegation !== "object" || Array.isArray(delegation)) throw new TypeError("delegation is required");
1047
+ const delegationId = delegation.delegationId ?? null;
1048
+ if (typeof delegationId !== "string" || delegationId.trim() === "") throw new TypeError("delegation.delegationId is required");
1049
+ const key = makeScopedKey({ tenantId, id: String(delegationId) });
1050
+ const at = delegation.updatedAt ?? new Date().toISOString();
1051
+ await store.commitTx({ at, ops: [{ kind: "AGREEMENT_DELEGATION_UPSERT", tenantId, delegationId, delegation: { ...delegation, tenantId, delegationId } }], audit });
1052
+ return store.agreementDelegations.get(key) ?? null;
1053
+ };
1054
+
1055
+ store.listAgreementDelegations = async function listAgreementDelegations({
1056
+ tenantId = DEFAULT_TENANT_ID,
1057
+ parentAgreementHash = null,
1058
+ childAgreementHash = null,
1059
+ status = null,
1060
+ limit = 200,
1061
+ offset = 0
1062
+ } = {}) {
1063
+ tenantId = normalizeTenantId(tenantId);
1064
+ if (parentAgreementHash !== null && (typeof parentAgreementHash !== "string" || parentAgreementHash.trim() === "")) {
1065
+ throw new TypeError("parentAgreementHash must be null or a non-empty string");
1066
+ }
1067
+ if (childAgreementHash !== null && (typeof childAgreementHash !== "string" || childAgreementHash.trim() === "")) {
1068
+ throw new TypeError("childAgreementHash must be null or a non-empty string");
1069
+ }
1070
+ if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
1071
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
1072
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
1073
+
1074
+ const statusFilter = status ? String(status).trim().toLowerCase() : null;
1075
+ const parentFilter = parentAgreementHash ? String(parentAgreementHash).trim().toLowerCase() : null;
1076
+ const childFilter = childAgreementHash ? String(childAgreementHash).trim().toLowerCase() : null;
1077
+ const safeLimit = Math.min(1000, limit);
1078
+ const safeOffset = offset;
1079
+ const out = [];
1080
+ for (const row of store.agreementDelegations.values()) {
1081
+ if (!row || typeof row !== "object") continue;
1082
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1083
+ if (parentFilter && String(row.parentAgreementHash ?? "").toLowerCase() !== parentFilter) continue;
1084
+ if (childFilter && String(row.childAgreementHash ?? "").toLowerCase() !== childFilter) continue;
1085
+ if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
1086
+ out.push(row);
1087
+ }
1088
+ out.sort((left, right) => String(left.delegationId ?? "").localeCompare(String(right.delegationId ?? "")));
1089
+ return out.slice(safeOffset, safeOffset + safeLimit);
1090
+ };
1091
+
1092
+ store.getX402Gate = async function getX402Gate({ tenantId = DEFAULT_TENANT_ID, gateId } = {}) {
1093
+ tenantId = normalizeTenantId(tenantId);
1094
+ if (typeof gateId !== "string" || gateId.trim() === "") throw new TypeError("gateId is required");
1095
+ return store.x402Gates.get(makeScopedKey({ tenantId, id: String(gateId) })) ?? null;
1096
+ };
1097
+
1098
+ store.putX402Gate = async function putX402Gate({ tenantId = DEFAULT_TENANT_ID, gate, audit = null } = {}) {
1099
+ tenantId = normalizeTenantId(tenantId);
1100
+ if (!gate || typeof gate !== "object" || Array.isArray(gate)) throw new TypeError("gate is required");
1101
+ const gateId = gate.gateId ?? gate.id ?? null;
1102
+ if (typeof gateId !== "string" || gateId.trim() === "") throw new TypeError("gate.gateId is required");
1103
+ const key = makeScopedKey({ tenantId, id: String(gateId) });
1104
+ const at = gate.updatedAt ?? gate.createdAt ?? new Date().toISOString();
1105
+ await store.commitTx({ at, ops: [{ kind: "X402_GATE_UPSERT", tenantId, gateId, gate: { ...gate, tenantId, gateId: String(gateId) } }], audit });
1106
+ return store.x402Gates.get(key) ?? null;
1107
+ };
1108
+
1109
+ store.putX402AgentLifecycle = async function putX402AgentLifecycle({ tenantId = DEFAULT_TENANT_ID, agentLifecycle, audit = null } = {}) {
1110
+ tenantId = normalizeTenantId(tenantId);
1111
+ if (!agentLifecycle || typeof agentLifecycle !== "object" || Array.isArray(agentLifecycle)) {
1112
+ throw new TypeError("agentLifecycle is required");
1113
+ }
1114
+ const agentId = typeof agentLifecycle.agentId === "string" ? agentLifecycle.agentId.trim() : "";
1115
+ if (!agentId) throw new TypeError("agentLifecycle.agentId is required");
1116
+ const status = typeof agentLifecycle.status === "string" ? agentLifecycle.status.trim().toLowerCase() : "";
1117
+ if (status !== "active" && status !== "frozen" && status !== "archived") {
1118
+ throw new TypeError("agentLifecycle.status must be active|frozen|archived");
1119
+ }
1120
+ const at = agentLifecycle.updatedAt ?? agentLifecycle.createdAt ?? new Date().toISOString();
1121
+ await store.commitTx({
1122
+ at,
1123
+ ops: [
1124
+ {
1125
+ kind: "X402_AGENT_LIFECYCLE_UPSERT",
1126
+ tenantId,
1127
+ agentId,
1128
+ agentLifecycle: { ...agentLifecycle, tenantId, agentId, status }
1129
+ }
1130
+ ],
1131
+ audit
1132
+ });
1133
+ return store.x402AgentLifecycles.get(makeScopedKey({ tenantId, id: agentId })) ?? null;
1134
+ };
1135
+
1136
+ store.getX402AgentLifecycle = async function getX402AgentLifecycle({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
1137
+ tenantId = normalizeTenantId(tenantId);
1138
+ if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
1139
+ return store.x402AgentLifecycles.get(makeScopedKey({ tenantId, id: String(agentId) })) ?? null;
1140
+ };
1141
+
1142
+ function x402WalletPolicyStoreKey({ tenantId, sponsorWalletRef, policyRef, policyVersion }) {
1143
+ const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1144
+ const normalizedSponsorWalletRef = typeof sponsorWalletRef === "string" ? sponsorWalletRef.trim() : "";
1145
+ const normalizedPolicyRef = typeof policyRef === "string" ? policyRef.trim() : "";
1146
+ const normalizedPolicyVersion = Number(policyVersion);
1147
+ if (!normalizedSponsorWalletRef) throw new TypeError("sponsorWalletRef is required");
1148
+ if (!normalizedPolicyRef) throw new TypeError("policyRef is required");
1149
+ if (!Number.isSafeInteger(normalizedPolicyVersion) || normalizedPolicyVersion <= 0) {
1150
+ throw new TypeError("policyVersion must be a positive safe integer");
1151
+ }
1152
+ return makeScopedKey({
1153
+ tenantId: normalizedTenantId,
1154
+ id: `${normalizedSponsorWalletRef}::${normalizedPolicyRef}::${normalizedPolicyVersion}`
1155
+ });
1156
+ }
1157
+
1158
+ function x402WebhookEndpointStoreKey({ tenantId, endpointId }) {
1159
+ const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1160
+ const normalizedEndpointId = typeof endpointId === "string" ? endpointId.trim() : "";
1161
+ if (!normalizedEndpointId) throw new TypeError("endpointId is required");
1162
+ return makeScopedKey({ tenantId: normalizedTenantId, id: normalizedEndpointId });
1163
+ }
1164
+
1165
+ function x402ZkVerificationKeyStoreKey({ tenantId, verificationKeyId }) {
1166
+ const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1167
+ const normalizedVerificationKeyId = typeof verificationKeyId === "string" ? verificationKeyId.trim() : "";
1168
+ if (!normalizedVerificationKeyId) throw new TypeError("verificationKeyId is required");
1169
+ return makeScopedKey({ tenantId: normalizedTenantId, id: normalizedVerificationKeyId });
1170
+ }
1171
+
1172
+ store.putX402WalletPolicy = async function putX402WalletPolicy({ tenantId = DEFAULT_TENANT_ID, policy, audit = null } = {}) {
1173
+ tenantId = normalizeTenantId(tenantId);
1174
+ if (!policy || typeof policy !== "object" || Array.isArray(policy)) throw new TypeError("policy is required");
1175
+ const sponsorWalletRef = typeof policy.sponsorWalletRef === "string" ? policy.sponsorWalletRef.trim() : "";
1176
+ const policyRef = typeof policy.policyRef === "string" ? policy.policyRef.trim() : "";
1177
+ const policyVersion = Number(policy.policyVersion);
1178
+ if (!sponsorWalletRef) throw new TypeError("policy.sponsorWalletRef is required");
1179
+ if (!policyRef) throw new TypeError("policy.policyRef is required");
1180
+ if (!Number.isSafeInteger(policyVersion) || policyVersion <= 0) throw new TypeError("policy.policyVersion must be a positive safe integer");
1181
+ const key = x402WalletPolicyStoreKey({ tenantId, sponsorWalletRef, policyRef, policyVersion });
1182
+ const at = policy.updatedAt ?? policy.createdAt ?? new Date().toISOString();
1183
+ await store.commitTx({
1184
+ at,
1185
+ ops: [
1186
+ {
1187
+ kind: "X402_WALLET_POLICY_UPSERT",
1188
+ tenantId,
1189
+ policy: { ...policy, tenantId, sponsorWalletRef, policyRef, policyVersion }
1190
+ }
1191
+ ],
1192
+ audit
1193
+ });
1194
+ return store.x402WalletPolicies.get(key) ?? null;
1195
+ };
1196
+
1197
+ store.getX402WalletPolicy = async function getX402WalletPolicy({
1198
+ tenantId = DEFAULT_TENANT_ID,
1199
+ sponsorWalletRef,
1200
+ policyRef,
1201
+ policyVersion
1202
+ } = {}) {
1203
+ tenantId = normalizeTenantId(tenantId);
1204
+ const key = x402WalletPolicyStoreKey({ tenantId, sponsorWalletRef, policyRef, policyVersion });
1205
+ return store.x402WalletPolicies.get(key) ?? null;
1206
+ };
1207
+
1208
+ store.listX402WalletPolicies = async function listX402WalletPolicies({
1209
+ tenantId = DEFAULT_TENANT_ID,
1210
+ sponsorWalletRef = null,
1211
+ sponsorRef = null,
1212
+ policyRef = null,
1213
+ status = null,
1214
+ limit = 200,
1215
+ offset = 0
1216
+ } = {}) {
1217
+ tenantId = normalizeTenantId(tenantId);
1218
+ if (sponsorWalletRef !== null && (typeof sponsorWalletRef !== "string" || sponsorWalletRef.trim() === "")) {
1219
+ throw new TypeError("sponsorWalletRef must be null or a non-empty string");
1220
+ }
1221
+ if (sponsorRef !== null && (typeof sponsorRef !== "string" || sponsorRef.trim() === "")) {
1222
+ throw new TypeError("sponsorRef must be null or a non-empty string");
1223
+ }
1224
+ if (policyRef !== null && (typeof policyRef !== "string" || policyRef.trim() === "")) {
1225
+ throw new TypeError("policyRef must be null or a non-empty string");
1226
+ }
1227
+ if (status !== null && (typeof status !== "string" || status.trim() === "")) {
1228
+ throw new TypeError("status must be null or a non-empty string");
1229
+ }
1230
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
1231
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
1232
+
1233
+ const sponsorWalletFilter = sponsorWalletRef ? sponsorWalletRef.trim() : null;
1234
+ const sponsorFilter = sponsorRef ? sponsorRef.trim() : null;
1235
+ const policyFilter = policyRef ? policyRef.trim() : null;
1236
+ const statusFilter = status ? status.trim().toLowerCase() : null;
1237
+ const out = [];
1238
+ for (const row of store.x402WalletPolicies.values()) {
1239
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
1240
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1241
+ if (sponsorWalletFilter && String(row.sponsorWalletRef ?? "") !== sponsorWalletFilter) continue;
1242
+ if (sponsorFilter && String(row.sponsorRef ?? "") !== sponsorFilter) continue;
1243
+ if (policyFilter && String(row.policyRef ?? "") !== policyFilter) continue;
1244
+ if (statusFilter && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
1245
+ out.push(row);
1246
+ }
1247
+ out.sort((left, right) => {
1248
+ const leftAt = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
1249
+ const rightAt = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
1250
+ if (Number.isFinite(leftAt) && Number.isFinite(rightAt) && rightAt !== leftAt) return rightAt - leftAt;
1251
+ const sponsorOrder = String(left?.sponsorWalletRef ?? "").localeCompare(String(right?.sponsorWalletRef ?? ""));
1252
+ if (sponsorOrder !== 0) return sponsorOrder;
1253
+ const policyOrder = String(left?.policyRef ?? "").localeCompare(String(right?.policyRef ?? ""));
1254
+ if (policyOrder !== 0) return policyOrder;
1255
+ return Number(right?.policyVersion ?? 0) - Number(left?.policyVersion ?? 0);
1256
+ });
1257
+ return out.slice(offset, offset + Math.min(1000, limit));
1258
+ };
1259
+
1260
+ store.putX402ZkVerificationKey = async function putX402ZkVerificationKey({
1261
+ tenantId = DEFAULT_TENANT_ID,
1262
+ verificationKey,
1263
+ audit = null
1264
+ } = {}) {
1265
+ tenantId = normalizeTenantId(tenantId);
1266
+ if (!verificationKey || typeof verificationKey !== "object" || Array.isArray(verificationKey)) {
1267
+ throw new TypeError("verificationKey is required");
1268
+ }
1269
+ const verificationKeyId = typeof verificationKey.verificationKeyId === "string" ? verificationKey.verificationKeyId.trim() : "";
1270
+ if (!verificationKeyId) throw new TypeError("verificationKey.verificationKeyId is required");
1271
+ const key = x402ZkVerificationKeyStoreKey({ tenantId, verificationKeyId });
1272
+ const at = verificationKey.updatedAt ?? verificationKey.createdAt ?? new Date().toISOString();
1273
+ await store.commitTx({
1274
+ at,
1275
+ ops: [
1276
+ {
1277
+ kind: "X402_ZK_VERIFICATION_KEY_PUT",
1278
+ tenantId,
1279
+ verificationKeyId,
1280
+ verificationKey: { ...verificationKey, tenantId, verificationKeyId }
1281
+ }
1282
+ ],
1283
+ audit
1284
+ });
1285
+ return store.x402ZkVerificationKeys.get(key) ?? null;
1286
+ };
1287
+
1288
+ store.getX402ZkVerificationKey = async function getX402ZkVerificationKey({
1289
+ tenantId = DEFAULT_TENANT_ID,
1290
+ verificationKeyId
1291
+ } = {}) {
1292
+ tenantId = normalizeTenantId(tenantId);
1293
+ return store.x402ZkVerificationKeys.get(x402ZkVerificationKeyStoreKey({ tenantId, verificationKeyId })) ?? null;
1294
+ };
1295
+
1296
+ store.listX402ZkVerificationKeys = async function listX402ZkVerificationKeys({
1297
+ tenantId = DEFAULT_TENANT_ID,
1298
+ protocol = null,
1299
+ providerRef = null,
1300
+ limit = 200,
1301
+ offset = 0
1302
+ } = {}) {
1303
+ tenantId = normalizeTenantId(tenantId);
1304
+ if (protocol !== null && (typeof protocol !== "string" || protocol.trim() === "")) {
1305
+ throw new TypeError("protocol must be null or a non-empty string");
1306
+ }
1307
+ if (providerRef !== null && (typeof providerRef !== "string" || providerRef.trim() === "")) {
1308
+ throw new TypeError("providerRef must be null or a non-empty string");
1309
+ }
1310
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
1311
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
1312
+ const protocolFilter = protocol ? protocol.trim().toLowerCase() : null;
1313
+ const providerRefFilter = providerRef ? providerRef.trim() : null;
1314
+ const out = [];
1315
+ for (const row of store.x402ZkVerificationKeys.values()) {
1316
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
1317
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1318
+ if (protocolFilter && String(row.protocol ?? "").trim().toLowerCase() !== protocolFilter) continue;
1319
+ if (providerRefFilter && String(row.providerRef ?? "") !== providerRefFilter) continue;
1320
+ out.push(row);
1321
+ }
1322
+ out.sort((left, right) => {
1323
+ const leftAt = Number.isFinite(Date.parse(String(left?.createdAt ?? ""))) ? Date.parse(String(left.createdAt)) : Number.NaN;
1324
+ const rightAt = Number.isFinite(Date.parse(String(right?.createdAt ?? ""))) ? Date.parse(String(right.createdAt)) : Number.NaN;
1325
+ if (Number.isFinite(leftAt) && Number.isFinite(rightAt) && rightAt !== leftAt) return rightAt - leftAt;
1326
+ return String(left?.verificationKeyId ?? "").localeCompare(String(right?.verificationKeyId ?? ""));
1327
+ });
1328
+ return out.slice(offset, offset + Math.min(1000, limit));
1329
+ };
1330
+
1331
+ store.putX402WebhookEndpoint = async function putX402WebhookEndpoint({ tenantId = DEFAULT_TENANT_ID, endpoint, audit = null } = {}) {
1332
+ tenantId = normalizeTenantId(tenantId);
1333
+ if (!endpoint || typeof endpoint !== "object" || Array.isArray(endpoint)) throw new TypeError("endpoint is required");
1334
+ const endpointId = typeof endpoint.endpointId === "string" ? endpoint.endpointId.trim() : "";
1335
+ if (!endpointId) throw new TypeError("endpoint.endpointId is required");
1336
+ const at = endpoint.updatedAt ?? endpoint.createdAt ?? new Date().toISOString();
1337
+ await store.commitTx({
1338
+ at,
1339
+ ops: [{ kind: "X402_WEBHOOK_ENDPOINT_UPSERT", tenantId, endpointId, endpoint: { ...endpoint, tenantId, endpointId } }],
1340
+ audit
1341
+ });
1342
+ return store.x402WebhookEndpoints.get(x402WebhookEndpointStoreKey({ tenantId, endpointId })) ?? null;
1343
+ };
1344
+
1345
+ store.getX402WebhookEndpoint = async function getX402WebhookEndpoint({ tenantId = DEFAULT_TENANT_ID, endpointId } = {}) {
1346
+ tenantId = normalizeTenantId(tenantId);
1347
+ return store.x402WebhookEndpoints.get(x402WebhookEndpointStoreKey({ tenantId, endpointId })) ?? null;
1348
+ };
1349
+
1350
+ store.listX402WebhookEndpoints = async function listX402WebhookEndpoints({
1351
+ tenantId = DEFAULT_TENANT_ID,
1352
+ endpointId = null,
1353
+ destinationId = null,
1354
+ status = null,
1355
+ event = null,
1356
+ limit = 200,
1357
+ offset = 0
1358
+ } = {}) {
1359
+ tenantId = normalizeTenantId(tenantId);
1360
+ if (endpointId !== null && (typeof endpointId !== "string" || endpointId.trim() === "")) {
1361
+ throw new TypeError("endpointId must be null or a non-empty string");
1362
+ }
1363
+ if (destinationId !== null && (typeof destinationId !== "string" || destinationId.trim() === "")) {
1364
+ throw new TypeError("destinationId must be null or a non-empty string");
1365
+ }
1366
+ if (status !== null && (typeof status !== "string" || status.trim() === "")) {
1367
+ throw new TypeError("status must be null or a non-empty string");
1368
+ }
1369
+ if (event !== null && (typeof event !== "string" || event.trim() === "")) {
1370
+ throw new TypeError("event must be null or a non-empty string");
1371
+ }
1372
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
1373
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
1374
+
1375
+ const endpointFilter = endpointId ? endpointId.trim() : null;
1376
+ const destinationFilter = destinationId ? destinationId.trim() : null;
1377
+ const statusFilter = status ? status.trim().toLowerCase() : null;
1378
+ const eventFilter = event ? event.trim().toLowerCase() : null;
1379
+ const out = [];
1380
+ for (const row of store.x402WebhookEndpoints.values()) {
1381
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
1382
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1383
+ if (endpointFilter && String(row.endpointId ?? "") !== endpointFilter) continue;
1384
+ if (destinationFilter && String(row.destinationId ?? "") !== destinationFilter) continue;
1385
+ if (statusFilter && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
1386
+ if (eventFilter) {
1387
+ const events = Array.isArray(row.events) ? row.events.map((value) => String(value).trim().toLowerCase()) : [];
1388
+ if (!events.includes(eventFilter)) continue;
1389
+ }
1390
+ out.push(row);
1391
+ }
1392
+ out.sort((left, right) => {
1393
+ const leftAt = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
1394
+ const rightAt = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
1395
+ if (Number.isFinite(leftAt) && Number.isFinite(rightAt) && rightAt !== leftAt) return rightAt - leftAt;
1396
+ return String(left?.endpointId ?? "").localeCompare(String(right?.endpointId ?? ""));
1397
+ });
1398
+ return out.slice(offset, offset + Math.min(1000, limit));
1399
+ };
1400
+
1401
+ store.recordX402WebhookEndpointDeliveryResult = async function recordX402WebhookEndpointDeliveryResult({
1402
+ tenantId = DEFAULT_TENANT_ID,
1403
+ destinationId,
1404
+ deliveredAt = new Date().toISOString(),
1405
+ success,
1406
+ failureReason = null,
1407
+ statusCode = null,
1408
+ autoDisableThreshold = null,
1409
+ audit = null
1410
+ } = {}) {
1411
+ tenantId = normalizeTenantId(tenantId);
1412
+ if (typeof destinationId !== "string" || destinationId.trim() === "") throw new TypeError("destinationId is required");
1413
+ const matched = await store.listX402WebhookEndpoints({ tenantId, destinationId: destinationId.trim(), limit: 1, offset: 0 });
1414
+ const endpoint = Array.isArray(matched) && matched.length > 0 ? matched[0] : null;
1415
+ if (!endpoint) return null;
1416
+ if (String(endpoint.status ?? "").toLowerCase() === "revoked") return endpoint;
1417
+ const thresholdRaw = autoDisableThreshold ?? store.x402WebhookAutoDisableFailures ?? 10;
1418
+ const threshold = Number.isSafeInteger(Number(thresholdRaw)) && Number(thresholdRaw) > 0 ? Number(thresholdRaw) : 10;
1419
+ const next = {
1420
+ ...endpoint,
1421
+ updatedAt: deliveredAt,
1422
+ lastDeliveryAt: deliveredAt
1423
+ };
1424
+ if (success) {
1425
+ next.consecutiveFailures = 0;
1426
+ next.lastFailureReason = null;
1427
+ next.lastFailureAt = null;
1428
+ next.lastFailureStatusCode = null;
1429
+ } else {
1430
+ const prevFailures = Number.isSafeInteger(Number(endpoint.consecutiveFailures)) ? Number(endpoint.consecutiveFailures) : 0;
1431
+ next.consecutiveFailures = prevFailures + 1;
1432
+ next.lastFailureReason = failureReason ? String(failureReason) : "delivery_failed";
1433
+ next.lastFailureAt = deliveredAt;
1434
+ next.lastFailureStatusCode = Number.isSafeInteger(Number(statusCode)) ? Number(statusCode) : null;
1435
+ if (String(endpoint.status ?? "").toLowerCase() === "active" && next.consecutiveFailures >= threshold) {
1436
+ next.status = "disabled";
1437
+ next.disabledAt = deliveredAt;
1438
+ }
1439
+ }
1440
+ return store.putX402WebhookEndpoint({ tenantId, endpoint: next, audit });
1441
+ };
1442
+
1443
+ function x402ReversalNonceStoreKey({ tenantId, sponsorRef, nonce }) {
1444
+ const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1445
+ const normalizedSponsorRef = typeof sponsorRef === "string" ? sponsorRef.trim() : "";
1446
+ const normalizedNonce = typeof nonce === "string" ? nonce.trim() : "";
1447
+ if (!normalizedSponsorRef) throw new TypeError("sponsorRef is required");
1448
+ if (!normalizedNonce) throw new TypeError("nonce is required");
1449
+ return `${normalizedTenantId}\n${normalizedSponsorRef}\n${normalizedNonce}`;
1450
+ }
1451
+
1452
+ store.putX402ReversalEvent = async function putX402ReversalEvent({ tenantId = DEFAULT_TENANT_ID, gateId, event, audit = null } = {}) {
1453
+ tenantId = normalizeTenantId(tenantId);
1454
+ if (typeof gateId !== "string" || gateId.trim() === "") throw new TypeError("gateId is required");
1455
+ if (!event || typeof event !== "object" || Array.isArray(event)) throw new TypeError("event is required");
1456
+ const eventId = event.eventId ?? event.id ?? null;
1457
+ if (typeof eventId !== "string" || eventId.trim() === "") throw new TypeError("event.eventId is required");
1458
+ const at = event.occurredAt ?? event.createdAt ?? new Date().toISOString();
1459
+ await store.commitTx({
1460
+ at,
1461
+ ops: [{ kind: "X402_REVERSAL_EVENT_APPEND", tenantId, gateId: String(gateId), eventId: String(eventId), event: { ...event, tenantId, gateId: String(gateId), eventId: String(eventId) } }],
1462
+ audit
1463
+ });
1464
+ return store.x402ReversalEvents.get(makeScopedKey({ tenantId, id: String(eventId) })) ?? null;
1465
+ };
1466
+
1467
+ store.getX402ReversalEvent = async function getX402ReversalEvent({ tenantId = DEFAULT_TENANT_ID, eventId } = {}) {
1468
+ tenantId = normalizeTenantId(tenantId);
1469
+ if (typeof eventId !== "string" || eventId.trim() === "") throw new TypeError("eventId is required");
1470
+ return store.x402ReversalEvents.get(makeScopedKey({ tenantId, id: String(eventId) })) ?? null;
1471
+ };
1472
+
1473
+ store.listX402ReversalEvents = async function listX402ReversalEvents({
1474
+ tenantId = DEFAULT_TENANT_ID,
1475
+ gateId = null,
1476
+ receiptId = null,
1477
+ action = null,
1478
+ from = null,
1479
+ to = null,
1480
+ limit = 200,
1481
+ offset = 0
1482
+ } = {}) {
1483
+ tenantId = normalizeTenantId(tenantId);
1484
+ if (gateId !== null && (typeof gateId !== "string" || gateId.trim() === "")) throw new TypeError("gateId must be null or a non-empty string");
1485
+ if (receiptId !== null && (typeof receiptId !== "string" || receiptId.trim() === "")) throw new TypeError("receiptId must be null or a non-empty string");
1486
+ if (action !== null && (typeof action !== "string" || action.trim() === "")) throw new TypeError("action must be null or a non-empty string");
1487
+ if (from !== null && !Number.isFinite(Date.parse(String(from)))) throw new TypeError("from must be null or an ISO date-time");
1488
+ if (to !== null && !Number.isFinite(Date.parse(String(to)))) throw new TypeError("to must be null or an ISO date-time");
1489
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
1490
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
1491
+
1492
+ const normalizedGateId = gateId ? gateId.trim() : null;
1493
+ const normalizedReceiptId = receiptId ? receiptId.trim() : null;
1494
+ const normalizedAction = action ? action.trim().toLowerCase() : null;
1495
+ const fromMs = from ? Date.parse(String(from)) : null;
1496
+ const toMs = to ? Date.parse(String(to)) : null;
1497
+
1498
+ const out = [];
1499
+ for (const row of store.x402ReversalEvents.values()) {
1500
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
1501
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1502
+ if (normalizedGateId && String(row.gateId ?? "") !== normalizedGateId) continue;
1503
+ if (normalizedReceiptId && String(row.receiptId ?? "") !== normalizedReceiptId) continue;
1504
+ if (normalizedAction && String(row.action ?? "").toLowerCase() !== normalizedAction) continue;
1505
+ const occurredAtMs = Number.isFinite(Date.parse(String(row.occurredAt ?? ""))) ? Date.parse(String(row.occurredAt)) : Number.NaN;
1506
+ if (fromMs !== null && (!Number.isFinite(occurredAtMs) || occurredAtMs < fromMs)) continue;
1507
+ if (toMs !== null && (!Number.isFinite(occurredAtMs) || occurredAtMs > toMs)) continue;
1508
+ out.push(row);
1509
+ }
1510
+ out.sort((left, right) => {
1511
+ const leftMs = Number.isFinite(Date.parse(String(left?.occurredAt ?? ""))) ? Date.parse(String(left.occurredAt)) : Number.NaN;
1512
+ const rightMs = Number.isFinite(Date.parse(String(right?.occurredAt ?? ""))) ? Date.parse(String(right.occurredAt)) : Number.NaN;
1513
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return rightMs - leftMs;
1514
+ return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
1515
+ });
1516
+ return out.slice(offset, offset + Math.min(1000, limit));
1517
+ };
1518
+
1519
+ store.putX402ReversalNonceUsage = async function putX402ReversalNonceUsage({ tenantId = DEFAULT_TENANT_ID, usage, audit = null } = {}) {
1520
+ tenantId = normalizeTenantId(tenantId);
1521
+ if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
1522
+ const sponsorRef = typeof usage.sponsorRef === "string" ? usage.sponsorRef.trim() : "";
1523
+ const nonce = typeof usage.nonce === "string" ? usage.nonce.trim() : "";
1524
+ if (!sponsorRef) throw new TypeError("usage.sponsorRef is required");
1525
+ if (!nonce) throw new TypeError("usage.nonce is required");
1526
+ const at = usage.usedAt ?? new Date().toISOString();
1527
+ await store.commitTx({
1528
+ at,
1529
+ ops: [{ kind: "X402_REVERSAL_NONCE_PUT", tenantId, sponsorRef, nonce, usage: { ...usage, tenantId, sponsorRef, nonce } }],
1530
+ audit
1531
+ });
1532
+ return store.x402ReversalNonceUsage.get(x402ReversalNonceStoreKey({ tenantId, sponsorRef, nonce })) ?? null;
1533
+ };
1534
+
1535
+ store.getX402ReversalNonceUsage = async function getX402ReversalNonceUsage({ tenantId = DEFAULT_TENANT_ID, sponsorRef, nonce } = {}) {
1536
+ tenantId = normalizeTenantId(tenantId);
1537
+ return store.x402ReversalNonceUsage.get(x402ReversalNonceStoreKey({ tenantId, sponsorRef, nonce })) ?? null;
1538
+ };
1539
+
1540
+ store.putX402ReversalCommandUsage = async function putX402ReversalCommandUsage({ tenantId = DEFAULT_TENANT_ID, usage, audit = null } = {}) {
1541
+ tenantId = normalizeTenantId(tenantId);
1542
+ if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
1543
+ const commandId = typeof usage.commandId === "string" ? usage.commandId.trim() : "";
1544
+ if (!commandId) throw new TypeError("usage.commandId is required");
1545
+ const at = usage.usedAt ?? new Date().toISOString();
1546
+ await store.commitTx({
1547
+ at,
1548
+ ops: [{ kind: "X402_REVERSAL_COMMAND_PUT", tenantId, commandId, usage: { ...usage, tenantId, commandId } }],
1549
+ audit
1550
+ });
1551
+ return store.x402ReversalCommandUsage.get(makeScopedKey({ tenantId, id: commandId })) ?? null;
701
1552
  };
702
1553
 
703
- store.getAgentIdentity = async function getAgentIdentity({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
1554
+ store.getX402ReversalCommandUsage = async function getX402ReversalCommandUsage({ tenantId = DEFAULT_TENANT_ID, commandId } = {}) {
704
1555
  tenantId = normalizeTenantId(tenantId);
705
- if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
706
- const key = makeScopedKey({ tenantId, id: String(agentId) });
707
- return store.agentIdentities.get(key) ?? null;
1556
+ if (typeof commandId !== "string" || commandId.trim() === "") throw new TypeError("commandId is required");
1557
+ return store.x402ReversalCommandUsage.get(makeScopedKey({ tenantId, id: commandId.trim() })) ?? null;
708
1558
  };
709
1559
 
710
- store.listAgentIdentities = async function listAgentIdentities({ tenantId = DEFAULT_TENANT_ID, status = null, limit = 200, offset = 0 } = {}) {
1560
+ store.putX402Escalation = async function putX402Escalation({ tenantId = DEFAULT_TENANT_ID, escalation, audit = null } = {}) {
1561
+ tenantId = normalizeTenantId(tenantId);
1562
+ if (!escalation || typeof escalation !== "object" || Array.isArray(escalation)) throw new TypeError("escalation is required");
1563
+ const escalationId = typeof escalation.escalationId === "string" ? escalation.escalationId.trim() : "";
1564
+ if (!escalationId) throw new TypeError("escalation.escalationId is required");
1565
+ const at = escalation.updatedAt ?? escalation.createdAt ?? new Date().toISOString();
1566
+ await store.commitTx({
1567
+ at,
1568
+ ops: [{ kind: "X402_ESCALATION_UPSERT", tenantId, escalationId, escalation: { ...escalation, tenantId, escalationId } }],
1569
+ audit
1570
+ });
1571
+ return store.x402Escalations.get(makeScopedKey({ tenantId, id: escalationId })) ?? null;
1572
+ };
1573
+
1574
+ store.getX402Escalation = async function getX402Escalation({ tenantId = DEFAULT_TENANT_ID, escalationId } = {}) {
1575
+ tenantId = normalizeTenantId(tenantId);
1576
+ if (typeof escalationId !== "string" || escalationId.trim() === "") throw new TypeError("escalationId is required");
1577
+ return store.x402Escalations.get(makeScopedKey({ tenantId, id: escalationId.trim() })) ?? null;
1578
+ };
1579
+
1580
+ store.listX402Escalations = async function listX402Escalations({
1581
+ tenantId = DEFAULT_TENANT_ID,
1582
+ gateId = null,
1583
+ agentId = null,
1584
+ status = null,
1585
+ limit = 200,
1586
+ offset = 0
1587
+ } = {}) {
711
1588
  tenantId = normalizeTenantId(tenantId);
1589
+ if (gateId !== null && (typeof gateId !== "string" || gateId.trim() === "")) throw new TypeError("gateId must be null or a non-empty string");
1590
+ if (agentId !== null && (typeof agentId !== "string" || agentId.trim() === "")) throw new TypeError("agentId must be null or a non-empty string");
712
1591
  if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
713
1592
  if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
714
1593
  if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
715
- const statusFilter = status ? String(status).trim().toLowerCase() : null;
716
- const safeLimit = Math.min(1000, limit);
717
- const safeOffset = offset;
1594
+
1595
+ const normalizedGateId = gateId ? gateId.trim() : null;
1596
+ const normalizedAgentId = agentId ? agentId.trim() : null;
1597
+ const normalizedStatus = status ? status.trim().toLowerCase() : null;
718
1598
  const out = [];
719
- for (const row of store.agentIdentities.values()) {
720
- if (!row || typeof row !== "object") continue;
1599
+ for (const row of store.x402Escalations.values()) {
1600
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
721
1601
  if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
722
- if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
1602
+ if (normalizedGateId && String(row.gateId ?? "") !== normalizedGateId) continue;
1603
+ if (
1604
+ normalizedAgentId &&
1605
+ String(
1606
+ row.requesterAgentId ??
1607
+ row.payerAgentId ??
1608
+ null
1609
+ ).trim() !== normalizedAgentId
1610
+ ) {
1611
+ continue;
1612
+ }
1613
+ if (normalizedStatus && String(row.status ?? "").toLowerCase() !== normalizedStatus) continue;
723
1614
  out.push(row);
724
1615
  }
725
- out.sort((left, right) => String(left.agentId ?? "").localeCompare(String(right.agentId ?? "")));
726
- return out.slice(safeOffset, safeOffset + safeLimit);
1616
+ out.sort((left, right) => {
1617
+ const leftMs = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
1618
+ const rightMs = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
1619
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return rightMs - leftMs;
1620
+ return String(left?.escalationId ?? "").localeCompare(String(right?.escalationId ?? ""));
1621
+ });
1622
+ return out.slice(offset, offset + Math.min(1000, limit));
727
1623
  };
728
1624
 
729
- store.getAgentWallet = async function getAgentWallet({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
1625
+ store.appendX402EscalationEvent = async function appendX402EscalationEvent({ tenantId = DEFAULT_TENANT_ID, event, audit = null } = {}) {
730
1626
  tenantId = normalizeTenantId(tenantId);
731
- if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
732
- return store.agentWallets.get(makeScopedKey({ tenantId, id: String(agentId) })) ?? null;
1627
+ if (!event || typeof event !== "object" || Array.isArray(event)) throw new TypeError("event is required");
1628
+ const eventId = typeof event.eventId === "string" ? event.eventId.trim() : "";
1629
+ const escalationId = typeof event.escalationId === "string" ? event.escalationId.trim() : "";
1630
+ if (!eventId) throw new TypeError("event.eventId is required");
1631
+ if (!escalationId) throw new TypeError("event.escalationId is required");
1632
+ const at = event.occurredAt ?? event.createdAt ?? new Date().toISOString();
1633
+ await store.commitTx({
1634
+ at,
1635
+ ops: [{ kind: "X402_ESCALATION_EVENT_APPEND", tenantId, eventId, escalationId, event: { ...event, tenantId, eventId, escalationId } }],
1636
+ audit
1637
+ });
1638
+ return store.x402EscalationEvents.get(makeScopedKey({ tenantId, id: eventId })) ?? null;
733
1639
  };
734
1640
 
735
- store.putAgentWallet = async function putAgentWallet({ tenantId = DEFAULT_TENANT_ID, wallet } = {}) {
1641
+ store.listX402EscalationEvents = async function listX402EscalationEvents({
1642
+ tenantId = DEFAULT_TENANT_ID,
1643
+ escalationId = null,
1644
+ limit = 200,
1645
+ offset = 0
1646
+ } = {}) {
736
1647
  tenantId = normalizeTenantId(tenantId);
737
- if (!wallet || typeof wallet !== "object" || Array.isArray(wallet)) throw new TypeError("wallet is required");
738
- const agentId = wallet.agentId ?? null;
739
- if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("wallet.agentId is required");
740
- const key = makeScopedKey({ tenantId, id: String(agentId) });
741
- await store.commitTx({ at: wallet.updatedAt ?? new Date().toISOString(), ops: [{ kind: "AGENT_WALLET_UPSERT", tenantId, wallet: { ...wallet, tenantId, agentId } }] });
742
- return store.agentWallets.get(key) ?? null;
1648
+ if (escalationId !== null && (typeof escalationId !== "string" || escalationId.trim() === "")) {
1649
+ throw new TypeError("escalationId must be null or a non-empty string");
1650
+ }
1651
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
1652
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
1653
+ const normalizedEscalationId = escalationId ? escalationId.trim() : null;
1654
+ const out = [];
1655
+ for (const row of store.x402EscalationEvents.values()) {
1656
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
1657
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1658
+ if (normalizedEscalationId && String(row.escalationId ?? "") !== normalizedEscalationId) continue;
1659
+ out.push(row);
1660
+ }
1661
+ out.sort((left, right) => {
1662
+ const leftMs = Number.isFinite(Date.parse(String(left?.occurredAt ?? ""))) ? Date.parse(String(left.occurredAt)) : Number.NaN;
1663
+ const rightMs = Number.isFinite(Date.parse(String(right?.occurredAt ?? ""))) ? Date.parse(String(right.occurredAt)) : Number.NaN;
1664
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return leftMs - rightMs;
1665
+ return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
1666
+ });
1667
+ return out.slice(offset, offset + Math.min(1000, limit));
743
1668
  };
744
1669
 
745
- store.getAgentRun = async function getAgentRun({ tenantId = DEFAULT_TENANT_ID, runId } = {}) {
1670
+ store.putX402EscalationOverrideUsage = async function putX402EscalationOverrideUsage({ tenantId = DEFAULT_TENANT_ID, usage, audit = null } = {}) {
746
1671
  tenantId = normalizeTenantId(tenantId);
747
- if (typeof runId !== "string" || runId.trim() === "") throw new TypeError("runId is required");
748
- return store.agentRuns.get(makeScopedKey({ tenantId, id: String(runId) })) ?? null;
1672
+ if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
1673
+ const overrideId = typeof usage.overrideId === "string" ? usage.overrideId.trim() : "";
1674
+ if (!overrideId) throw new TypeError("usage.overrideId is required");
1675
+ const at = usage.usedAt ?? new Date().toISOString();
1676
+ await store.commitTx({
1677
+ at,
1678
+ ops: [{ kind: "X402_ESCALATION_OVERRIDE_USAGE_PUT", tenantId, overrideId, usage: { ...usage, tenantId, overrideId } }],
1679
+ audit
1680
+ });
1681
+ return store.x402EscalationOverrideUsage.get(makeScopedKey({ tenantId, id: overrideId })) ?? null;
749
1682
  };
750
1683
 
751
- store.listAgentRuns = async function listAgentRuns({ tenantId = DEFAULT_TENANT_ID, agentId = null, status = null, limit = 200, offset = 0 } = {}) {
1684
+ store.getX402EscalationOverrideUsage = async function getX402EscalationOverrideUsage({
1685
+ tenantId = DEFAULT_TENANT_ID,
1686
+ overrideId
1687
+ } = {}) {
752
1688
  tenantId = normalizeTenantId(tenantId);
753
- if (agentId !== null && (typeof agentId !== "string" || agentId.trim() === "")) throw new TypeError("agentId must be null or a non-empty string");
754
- if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
755
- if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
756
- if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
1689
+ if (typeof overrideId !== "string" || overrideId.trim() === "") throw new TypeError("overrideId is required");
1690
+ return store.x402EscalationOverrideUsage.get(makeScopedKey({ tenantId, id: overrideId.trim() })) ?? null;
1691
+ };
757
1692
 
758
- const statusFilter = status ? String(status).trim().toLowerCase() : null;
759
- const safeLimit = Math.min(1000, limit);
760
- const safeOffset = offset;
1693
+ function x402ReceiptStoreKey({ tenantId, receiptId }) {
1694
+ const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1695
+ const normalizedReceiptId = typeof receiptId === "string" ? receiptId.trim() : "";
1696
+ if (!normalizedReceiptId) throw new TypeError("receiptId is required");
1697
+ return makeScopedKey({ tenantId: normalizedTenantId, id: normalizedReceiptId });
1698
+ }
1699
+
1700
+ function listX402ReversalEventsForReceiptSync({ tenantId = DEFAULT_TENANT_ID, receiptId } = {}) {
1701
+ const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1702
+ const normalizedReceiptId = typeof receiptId === "string" ? receiptId.trim() : "";
1703
+ if (!normalizedReceiptId) return [];
761
1704
  const out = [];
762
- for (const row of store.agentRuns.values()) {
763
- if (!row || typeof row !== "object") continue;
764
- if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
765
- if (agentId !== null && String(row.agentId ?? "") !== String(agentId)) continue;
766
- if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
1705
+ for (const row of store.x402ReversalEvents.values()) {
1706
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
1707
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== normalizedTenantId) continue;
1708
+ if (String(row.receiptId ?? "") !== normalizedReceiptId) continue;
767
1709
  out.push(row);
768
1710
  }
769
- out.sort((left, right) => String(left.runId ?? "").localeCompare(String(right.runId ?? "")));
770
- return out.slice(safeOffset, safeOffset + safeLimit);
1711
+ out.sort((left, right) => {
1712
+ const leftMs = Number.isFinite(Date.parse(String(left?.occurredAt ?? ""))) ? Date.parse(String(left.occurredAt)) : Number.NaN;
1713
+ const rightMs = Number.isFinite(Date.parse(String(right?.occurredAt ?? ""))) ? Date.parse(String(right.occurredAt)) : Number.NaN;
1714
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return leftMs - rightMs;
1715
+ return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
1716
+ });
1717
+ return out;
1718
+ }
1719
+
1720
+ function toX402ReceiptRecord({ tenantId, gate, settlement: settlementInput = null, includeReversalContext = true } = {}) {
1721
+ if (!gate || typeof gate !== "object" || Array.isArray(gate)) return null;
1722
+ const runId = typeof gate.runId === "string" && gate.runId.trim() !== "" ? gate.runId.trim() : null;
1723
+ const settlement =
1724
+ settlementInput && typeof settlementInput === "object" && !Array.isArray(settlementInput)
1725
+ ? settlementInput
1726
+ : runId && store.agentRunSettlements instanceof Map
1727
+ ? store.agentRunSettlements.get(makeScopedKey({ tenantId, id: runId })) ?? null
1728
+ : null;
1729
+ const decisionTrace =
1730
+ settlement?.decisionTrace && typeof settlement.decisionTrace === "object" && !Array.isArray(settlement.decisionTrace)
1731
+ ? settlement.decisionTrace
1732
+ : gate.decisionTrace && typeof gate.decisionTrace === "object" && !Array.isArray(gate.decisionTrace)
1733
+ ? gate.decisionTrace
1734
+ : null;
1735
+ const settlementReceipt =
1736
+ decisionTrace?.settlementReceipt && typeof decisionTrace.settlementReceipt === "object" && !Array.isArray(decisionTrace.settlementReceipt)
1737
+ ? decisionTrace.settlementReceipt
1738
+ : null;
1739
+ if (!settlementReceipt) return null;
1740
+ const receiptId = typeof settlementReceipt.receiptId === "string" ? settlementReceipt.receiptId.trim() : "";
1741
+ if (!receiptId) return null;
1742
+ const bindings =
1743
+ decisionTrace?.bindings && typeof decisionTrace.bindings === "object" && !Array.isArray(decisionTrace.bindings)
1744
+ ? decisionTrace.bindings
1745
+ : null;
1746
+ const verificationContext =
1747
+ gate?.verificationContext && typeof gate.verificationContext === "object" && !Array.isArray(gate.verificationContext)
1748
+ ? gate.verificationContext
1749
+ : gate?.decision?.verificationContext &&
1750
+ typeof gate.decision.verificationContext === "object" &&
1751
+ !Array.isArray(gate.decision.verificationContext)
1752
+ ? gate.decision.verificationContext
1753
+ : null;
1754
+ const sponsorRef =
1755
+ typeof bindings?.spendAuthorization?.sponsorRef === "string" && bindings.spendAuthorization.sponsorRef.trim() !== ""
1756
+ ? bindings.spendAuthorization.sponsorRef.trim()
1757
+ : null;
1758
+ const sponsorWalletRef =
1759
+ typeof bindings?.spendAuthorization?.sponsorWalletRef === "string" && bindings.spendAuthorization.sponsorWalletRef.trim() !== ""
1760
+ ? bindings.spendAuthorization.sponsorWalletRef.trim()
1761
+ : null;
1762
+ const agentKeyId =
1763
+ typeof bindings?.spendAuthorization?.agentKeyId === "string" && bindings.spendAuthorization.agentKeyId.trim() !== ""
1764
+ ? bindings.spendAuthorization.agentKeyId.trim()
1765
+ : null;
1766
+ const settledAt =
1767
+ typeof settlementReceipt.settledAt === "string" && settlementReceipt.settledAt.trim() !== ""
1768
+ ? settlementReceipt.settledAt.trim()
1769
+ : typeof gate.resolvedAt === "string" && gate.resolvedAt.trim() !== ""
1770
+ ? gate.resolvedAt.trim()
1771
+ : null;
1772
+ const base = {
1773
+ schemaVersion: "X402ReceiptRecord.v1",
1774
+ tenantId,
1775
+ receiptId,
1776
+ gateId: typeof gate.gateId === "string" ? gate.gateId : null,
1777
+ runId,
1778
+ payerAgentId: typeof gate.payerAgentId === "string" ? gate.payerAgentId : null,
1779
+ providerId: typeof gate.payeeAgentId === "string" ? gate.payeeAgentId : null,
1780
+ toolId:
1781
+ typeof gate.toolId === "string" && gate.toolId.trim() !== ""
1782
+ ? gate.toolId.trim()
1783
+ : typeof gate?.quote?.toolId === "string" && gate.quote.toolId.trim() !== ""
1784
+ ? gate.quote.toolId.trim()
1785
+ : null,
1786
+ sponsorRef,
1787
+ sponsorWalletRef,
1788
+ agentKeyId,
1789
+ settlementState:
1790
+ typeof settlementReceipt.status === "string" && settlementReceipt.status.trim() !== ""
1791
+ ? settlementReceipt.status.trim().toLowerCase()
1792
+ : typeof settlement?.status === "string" && settlement.status.trim() !== ""
1793
+ ? settlement.status.trim().toLowerCase()
1794
+ : null,
1795
+ verificationStatus:
1796
+ typeof decisionTrace?.verificationStatus === "string" && decisionTrace.verificationStatus.trim() !== ""
1797
+ ? decisionTrace.verificationStatus.trim().toLowerCase()
1798
+ : null,
1799
+ settledAt,
1800
+ createdAt:
1801
+ typeof settlementReceipt.createdAt === "string" && settlementReceipt.createdAt.trim() !== ""
1802
+ ? settlementReceipt.createdAt.trim()
1803
+ : null,
1804
+ updatedAt:
1805
+ typeof gate.updatedAt === "string" && gate.updatedAt.trim() !== ""
1806
+ ? gate.updatedAt.trim()
1807
+ : typeof settledAt === "string"
1808
+ ? settledAt
1809
+ : null,
1810
+ evidenceRefs: Array.isArray(gate.evidenceRefs) ? gate.evidenceRefs.slice() : [],
1811
+ verificationContext,
1812
+ bindings,
1813
+ providerSignature:
1814
+ gate?.providerSignature && typeof gate.providerSignature === "object" && !Array.isArray(gate.providerSignature)
1815
+ ? gate.providerSignature
1816
+ : null,
1817
+ providerQuoteSignature:
1818
+ gate?.providerQuoteSignature && typeof gate.providerQuoteSignature === "object" && !Array.isArray(gate.providerQuoteSignature)
1819
+ ? gate.providerQuoteSignature
1820
+ : null,
1821
+ providerQuotePayload:
1822
+ gate?.providerQuotePayload && typeof gate.providerQuotePayload === "object" && !Array.isArray(gate.providerQuotePayload)
1823
+ ? gate.providerQuotePayload
1824
+ : null,
1825
+ zkProof:
1826
+ gate?.zkProof && typeof gate.zkProof === "object" && !Array.isArray(gate.zkProof)
1827
+ ? gate.zkProof
1828
+ : null,
1829
+ decisionRecord:
1830
+ decisionTrace?.decisionRecord && typeof decisionTrace.decisionRecord === "object" && !Array.isArray(decisionTrace.decisionRecord)
1831
+ ? decisionTrace.decisionRecord
1832
+ : null,
1833
+ settlementReceipt
1834
+ };
1835
+ if (!includeReversalContext) return base;
1836
+ return {
1837
+ ...base,
1838
+ reversal:
1839
+ gate?.reversal && typeof gate.reversal === "object" && !Array.isArray(gate.reversal)
1840
+ ? gate.reversal
1841
+ : null,
1842
+ reversalEvents: listX402ReversalEventsForReceiptSync({ tenantId, receiptId })
1843
+ };
1844
+ }
1845
+
1846
+ function normalizeX402ReceiptRecordForStorage({ receipt } = {}) {
1847
+ if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)) return null;
1848
+ const stableUpdatedAt =
1849
+ typeof receipt.createdAt === "string" && receipt.createdAt.trim() !== ""
1850
+ ? receipt.createdAt
1851
+ : typeof receipt.settledAt === "string" && receipt.settledAt.trim() !== ""
1852
+ ? receipt.settledAt
1853
+ : typeof receipt.updatedAt === "string" && receipt.updatedAt.trim() !== ""
1854
+ ? receipt.updatedAt
1855
+ : new Date().toISOString();
1856
+ const normalized = {
1857
+ ...receipt,
1858
+ reversal: null,
1859
+ reversalEvents: [],
1860
+ updatedAt: stableUpdatedAt
1861
+ };
1862
+ return normalized;
1863
+ }
1864
+
1865
+ function projectX402ReceiptRecord({ tenantId, receipt } = {}) {
1866
+ if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)) return null;
1867
+ const receiptId = typeof receipt.receiptId === "string" ? receipt.receiptId.trim() : "";
1868
+ if (!receiptId) return null;
1869
+ const gateId = typeof receipt.gateId === "string" ? receipt.gateId.trim() : "";
1870
+ const gate =
1871
+ gateId && store.x402Gates instanceof Map
1872
+ ? store.x402Gates.get(makeScopedKey({ tenantId, id: gateId })) ?? null
1873
+ : null;
1874
+ const reversalEvents = listX402ReversalEventsForReceiptSync({ tenantId, receiptId });
1875
+ const latestEvent = reversalEvents.length > 0 ? reversalEvents[reversalEvents.length - 1] : null;
1876
+ const derivedState =
1877
+ typeof latestEvent?.settlementStatusAfter === "string" && latestEvent.settlementStatusAfter.trim() !== ""
1878
+ ? latestEvent.settlementStatusAfter.trim().toLowerCase()
1879
+ : null;
1880
+ const updatedAtCandidates = [receipt.updatedAt, latestEvent?.occurredAt]
1881
+ .map((value) => (typeof value === "string" && value.trim() !== "" && Number.isFinite(Date.parse(value)) ? new Date(Date.parse(value)).toISOString() : null))
1882
+ .filter(Boolean);
1883
+ const updatedAt = updatedAtCandidates.length > 0 ? updatedAtCandidates.sort((a, b) => Date.parse(a) - Date.parse(b))[updatedAtCandidates.length - 1] : null;
1884
+ return {
1885
+ ...receipt,
1886
+ settlementState: derivedState ?? receipt.settlementState ?? null,
1887
+ updatedAt: updatedAt ?? receipt.updatedAt ?? null,
1888
+ reversal:
1889
+ gate?.reversal && typeof gate.reversal === "object" && !Array.isArray(gate.reversal)
1890
+ ? gate.reversal
1891
+ : receipt?.reversal && typeof receipt.reversal === "object" && !Array.isArray(receipt.reversal)
1892
+ ? receipt.reversal
1893
+ : null,
1894
+ reversalEvents
1895
+ };
1896
+ }
1897
+
1898
+ function compareX402ReceiptRecords(left, right) {
1899
+ const leftMs = Number.isFinite(Date.parse(String(left?.settledAt ?? ""))) ? Date.parse(String(left.settledAt)) : Number.NaN;
1900
+ const rightMs = Number.isFinite(Date.parse(String(right?.settledAt ?? ""))) ? Date.parse(String(right.settledAt)) : Number.NaN;
1901
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return rightMs - leftMs;
1902
+ return String(left?.receiptId ?? "").localeCompare(String(right?.receiptId ?? ""));
1903
+ }
1904
+
1905
+ function encodeX402ReceiptCursor(record) {
1906
+ const settledAt = typeof record?.settledAt === "string" && record.settledAt.trim() !== "" ? new Date(Date.parse(record.settledAt)).toISOString() : null;
1907
+ const receiptId = typeof record?.receiptId === "string" && record.receiptId.trim() !== "" ? record.receiptId.trim() : null;
1908
+ if (!settledAt || !receiptId) return null;
1909
+ return Buffer.from(JSON.stringify({ settledAt, receiptId }), "utf8").toString("base64url");
1910
+ }
1911
+
1912
+ function decodeX402ReceiptCursor(raw) {
1913
+ if (raw === null || raw === undefined || String(raw).trim() === "") return null;
1914
+ let parsed;
1915
+ try {
1916
+ parsed = JSON.parse(Buffer.from(String(raw).trim(), "base64url").toString("utf8"));
1917
+ } catch {
1918
+ throw new TypeError("cursor must be base64url-encoded JSON");
1919
+ }
1920
+ const settledAt = typeof parsed?.settledAt === "string" && Number.isFinite(Date.parse(parsed.settledAt)) ? new Date(Date.parse(parsed.settledAt)).toISOString() : null;
1921
+ const receiptId = typeof parsed?.receiptId === "string" && parsed.receiptId.trim() !== "" ? parsed.receiptId.trim() : null;
1922
+ if (!settledAt || !receiptId) throw new TypeError("cursor is invalid");
1923
+ return { settledAt, receiptId };
1924
+ }
1925
+
1926
+ function isReceiptAfterCursor(record, cursor) {
1927
+ if (!cursor) return true;
1928
+ const recordMs = Number.isFinite(Date.parse(String(record?.settledAt ?? ""))) ? Date.parse(String(record.settledAt)) : Number.NaN;
1929
+ const cursorMs = Date.parse(cursor.settledAt);
1930
+ if (!Number.isFinite(recordMs)) return false;
1931
+ if (recordMs < cursorMs) return true;
1932
+ if (recordMs > cursorMs) return false;
1933
+ return String(record?.receiptId ?? "") > String(cursor.receiptId ?? "");
1934
+ }
1935
+
1936
+ function listX402ReceiptCandidates({ tenantId }) {
1937
+ const byReceiptId = new Map();
1938
+ if (store.x402Receipts instanceof Map) {
1939
+ for (const row of store.x402Receipts.values()) {
1940
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
1941
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1942
+ const receiptId = typeof row.receiptId === "string" ? row.receiptId.trim() : "";
1943
+ if (!receiptId) continue;
1944
+ byReceiptId.set(receiptId, projectX402ReceiptRecord({ tenantId, receipt: row }));
1945
+ }
1946
+ }
1947
+ if (byReceiptId.size === 0 && store.x402Gates instanceof Map) {
1948
+ for (const gate of store.x402Gates.values()) {
1949
+ if (!gate || typeof gate !== "object" || Array.isArray(gate)) continue;
1950
+ if (normalizeTenantId(gate.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
1951
+ const derived = toX402ReceiptRecord({ tenantId, gate, includeReversalContext: true });
1952
+ if (!derived) continue;
1953
+ const receiptId = typeof derived.receiptId === "string" ? derived.receiptId.trim() : "";
1954
+ if (!receiptId) continue;
1955
+ byReceiptId.set(receiptId, derived);
1956
+ }
1957
+ }
1958
+ return Array.from(byReceiptId.values()).filter(Boolean);
1959
+ }
1960
+
1961
+ store.deriveX402ReceiptRecord = function deriveX402ReceiptRecord({
1962
+ tenantId = DEFAULT_TENANT_ID,
1963
+ gate,
1964
+ settlement = null,
1965
+ includeReversalContext = false
1966
+ } = {}) {
1967
+ tenantId = normalizeTenantId(tenantId);
1968
+ const derived = toX402ReceiptRecord({ tenantId, gate, settlement, includeReversalContext });
1969
+ return normalizeX402ReceiptRecordForStorage({ receipt: derived });
771
1970
  };
772
1971
 
773
- store.getAgentRunEvents = async function getAgentRunEvents({ tenantId = DEFAULT_TENANT_ID, runId } = {}) {
1972
+ store.putX402Receipt = async function putX402Receipt({ tenantId = DEFAULT_TENANT_ID, receipt, audit = null } = {}) {
774
1973
  tenantId = normalizeTenantId(tenantId);
775
- if (typeof runId !== "string" || runId.trim() === "") throw new TypeError("runId is required");
776
- return store.agentRunEvents.get(makeScopedKey({ tenantId, id: String(runId) })) ?? [];
1974
+ const normalized = normalizeX402ReceiptRecordForStorage({ receipt });
1975
+ if (!normalized) throw new TypeError("receipt is required");
1976
+ const receiptId = typeof normalized.receiptId === "string" ? normalized.receiptId.trim() : "";
1977
+ if (!receiptId) throw new TypeError("receipt.receiptId is required");
1978
+ const key = x402ReceiptStoreKey({ tenantId, receiptId });
1979
+ const at =
1980
+ typeof normalized.updatedAt === "string" && normalized.updatedAt.trim() !== ""
1981
+ ? normalized.updatedAt
1982
+ : typeof normalized.createdAt === "string" && normalized.createdAt.trim() !== ""
1983
+ ? normalized.createdAt
1984
+ : new Date().toISOString();
1985
+ await store.commitTx({
1986
+ at,
1987
+ ops: [{ kind: "X402_RECEIPT_PUT", tenantId, receiptId, receipt: { ...normalized, tenantId, receiptId } }],
1988
+ audit
1989
+ });
1990
+ return store.x402Receipts.get(key) ?? null;
777
1991
  };
778
1992
 
779
- store.getAgentRunSettlement = async function getAgentRunSettlement({ tenantId = DEFAULT_TENANT_ID, runId } = {}) {
1993
+ store.getX402Receipt = async function getX402Receipt({ tenantId = DEFAULT_TENANT_ID, receiptId } = {}) {
780
1994
  tenantId = normalizeTenantId(tenantId);
781
- if (typeof runId !== "string" || runId.trim() === "") throw new TypeError("runId is required");
782
- return store.agentRunSettlements.get(makeScopedKey({ tenantId, id: String(runId) })) ?? null;
1995
+ if (typeof receiptId !== "string" || receiptId.trim() === "") throw new TypeError("receiptId is required");
1996
+ const wanted = receiptId.trim();
1997
+ const key = x402ReceiptStoreKey({ tenantId, receiptId: wanted });
1998
+ const stored = store.x402Receipts instanceof Map ? store.x402Receipts.get(key) ?? null : null;
1999
+ if (stored) return projectX402ReceiptRecord({ tenantId, receipt: stored });
2000
+ for (const gate of store.x402Gates.values()) {
2001
+ if (!gate || typeof gate !== "object" || Array.isArray(gate)) continue;
2002
+ if (normalizeTenantId(gate.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
2003
+ const receipt = toX402ReceiptRecord({ tenantId, gate, includeReversalContext: true });
2004
+ if (!receipt) continue;
2005
+ if (String(receipt.receiptId ?? "") === wanted) return receipt;
2006
+ }
2007
+ return null;
783
2008
  };
784
2009
 
785
- store.getArbitrationCase = async function getArbitrationCase({ tenantId = DEFAULT_TENANT_ID, caseId } = {}) {
2010
+ store.listX402ReceiptsPage = async function listX402ReceiptsPage({
2011
+ tenantId = DEFAULT_TENANT_ID,
2012
+ agentId = null,
2013
+ sponsorId = null,
2014
+ sponsorWalletRef = null,
2015
+ toolId = null,
2016
+ state = null,
2017
+ from = null,
2018
+ to = null,
2019
+ limit = 200,
2020
+ offset = 0,
2021
+ cursor = null
2022
+ } = {}) {
786
2023
  tenantId = normalizeTenantId(tenantId);
787
- if (typeof caseId !== "string" || caseId.trim() === "") throw new TypeError("caseId is required");
788
- return store.arbitrationCases.get(makeScopedKey({ tenantId, id: String(caseId) })) ?? null;
2024
+ if (agentId !== null && (typeof agentId !== "string" || agentId.trim() === "")) throw new TypeError("agentId must be null or a non-empty string");
2025
+ if (sponsorId !== null && (typeof sponsorId !== "string" || sponsorId.trim() === "")) throw new TypeError("sponsorId must be null or a non-empty string");
2026
+ if (sponsorWalletRef !== null && (typeof sponsorWalletRef !== "string" || sponsorWalletRef.trim() === "")) {
2027
+ throw new TypeError("sponsorWalletRef must be null or a non-empty string");
2028
+ }
2029
+ if (toolId !== null && (typeof toolId !== "string" || toolId.trim() === "")) throw new TypeError("toolId must be null or a non-empty string");
2030
+ if (state !== null && (typeof state !== "string" || state.trim() === "")) throw new TypeError("state must be null or a non-empty string");
2031
+ if (from !== null && !Number.isFinite(Date.parse(String(from)))) throw new TypeError("from must be null or an ISO date-time");
2032
+ if (to !== null && !Number.isFinite(Date.parse(String(to)))) throw new TypeError("to must be null or an ISO date-time");
2033
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
2034
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
2035
+
2036
+ const normalizedAgentId = agentId ? agentId.trim() : null;
2037
+ const normalizedSponsorId = sponsorId ? sponsorId.trim() : null;
2038
+ const normalizedSponsorWalletRef = sponsorWalletRef ? sponsorWalletRef.trim() : null;
2039
+ const normalizedToolId = toolId ? toolId.trim() : null;
2040
+ const normalizedState = state ? state.trim().toLowerCase() : null;
2041
+ const fromMs = from ? Date.parse(String(from)) : null;
2042
+ const toMs = to ? Date.parse(String(to)) : null;
2043
+ const decodedCursor = cursor === null ? null : decodeX402ReceiptCursor(cursor);
2044
+
2045
+ const all = listX402ReceiptCandidates({ tenantId });
2046
+ const filtered = all.filter((receipt) => {
2047
+ if (!receipt) return false;
2048
+ if (
2049
+ normalizedAgentId &&
2050
+ String(receipt.payerAgentId ?? "") !== normalizedAgentId &&
2051
+ String(receipt.providerId ?? "") !== normalizedAgentId &&
2052
+ String(receipt.agentKeyId ?? "") !== normalizedAgentId
2053
+ ) {
2054
+ return false;
2055
+ }
2056
+ if (normalizedSponsorId && String(receipt.sponsorRef ?? "") !== normalizedSponsorId) return false;
2057
+ if (normalizedSponsorWalletRef && String(receipt.sponsorWalletRef ?? "") !== normalizedSponsorWalletRef) return false;
2058
+ if (normalizedToolId && String(receipt.toolId ?? "") !== normalizedToolId) return false;
2059
+ if (normalizedState && String(receipt.settlementState ?? "").toLowerCase() !== normalizedState) return false;
2060
+ const settledAtMs = Number.isFinite(Date.parse(String(receipt.settledAt ?? ""))) ? Date.parse(String(receipt.settledAt)) : Number.NaN;
2061
+ if (fromMs !== null && (!Number.isFinite(settledAtMs) || settledAtMs < fromMs)) return false;
2062
+ if (toMs !== null && (!Number.isFinite(settledAtMs) || settledAtMs > toMs)) return false;
2063
+ return true;
2064
+ });
2065
+
2066
+ filtered.sort(compareX402ReceiptRecords);
2067
+ const cursorFiltered = decodedCursor ? filtered.filter((row) => isReceiptAfterCursor(row, decodedCursor)) : filtered;
2068
+ const paged = cursorFiltered.slice(offset, offset + Math.min(1000, limit));
2069
+ const hasMore = cursorFiltered.length > offset + paged.length;
2070
+ const nextCursor = hasMore && paged.length > 0 ? encodeX402ReceiptCursor(paged[paged.length - 1]) : null;
2071
+ return { receipts: paged, nextCursor };
2072
+ };
2073
+
2074
+ store.listX402Receipts = async function listX402Receipts(args = {}) {
2075
+ const page = await store.listX402ReceiptsPage(args);
2076
+ return page.receipts;
789
2077
  };
790
2078
 
791
2079
  store.listArbitrationCases = async function listArbitrationCases({
@@ -1299,6 +2587,55 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
1299
2587
  return all;
1300
2588
  };
1301
2589
 
2590
+ store.listReputationEvents = async function listReputationEvents({
2591
+ tenantId = DEFAULT_TENANT_ID,
2592
+ agentId,
2593
+ toolId = null,
2594
+ occurredAtGte = null,
2595
+ occurredAtLte = null,
2596
+ limit = 1000,
2597
+ offset = 0
2598
+ } = {}) {
2599
+ tenantId = normalizeTenantId(tenantId);
2600
+ if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
2601
+ if (toolId !== null && toolId !== undefined && (typeof toolId !== "string" || toolId.trim() === "")) {
2602
+ throw new TypeError("toolId must be null or a non-empty string");
2603
+ }
2604
+ if (occurredAtGte !== null && occurredAtGte !== undefined && !Number.isFinite(Date.parse(String(occurredAtGte)))) {
2605
+ throw new TypeError("occurredAtGte must be an ISO date-time");
2606
+ }
2607
+ if (occurredAtLte !== null && occurredAtLte !== undefined && !Number.isFinite(Date.parse(String(occurredAtLte)))) {
2608
+ throw new TypeError("occurredAtLte must be an ISO date-time");
2609
+ }
2610
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
2611
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
2612
+
2613
+ const minMs = occurredAtGte ? Date.parse(String(occurredAtGte)) : Number.NaN;
2614
+ const maxMs = occurredAtLte ? Date.parse(String(occurredAtLte)) : Number.NaN;
2615
+ const out = [];
2616
+ for (const art of store.artifacts.values()) {
2617
+ if (!art || typeof art !== "object" || Array.isArray(art)) continue;
2618
+ if (normalizeTenantId(art.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
2619
+ if (String(art.schemaVersion ?? "") !== "ReputationEvent.v1") continue;
2620
+ const subject = art.subject && typeof art.subject === "object" && !Array.isArray(art.subject) ? art.subject : null;
2621
+ if (!subject) continue;
2622
+ if (String(subject.agentId ?? "") !== String(agentId)) continue;
2623
+ if (toolId !== null && toolId !== undefined && String(subject.toolId ?? "") !== String(toolId)) continue;
2624
+ const occurredAtMs = Date.parse(String(art.occurredAt ?? ""));
2625
+ if (!Number.isFinite(occurredAtMs)) continue;
2626
+ if (Number.isFinite(minMs) && occurredAtMs < minMs) continue;
2627
+ if (Number.isFinite(maxMs) && occurredAtMs > maxMs) continue;
2628
+ out.push(art);
2629
+ }
2630
+ out.sort((left, right) => {
2631
+ const leftMs = Date.parse(String(left?.occurredAt ?? ""));
2632
+ const rightMs = Date.parse(String(right?.occurredAt ?? ""));
2633
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return leftMs - rightMs;
2634
+ return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
2635
+ });
2636
+ return out.slice(offset, offset + Math.min(5000, limit));
2637
+ };
2638
+
1302
2639
  store.getArtifact = async function getArtifact({ tenantId = DEFAULT_TENANT_ID, artifactId }) {
1303
2640
  tenantId = normalizeTenantId(tenantId);
1304
2641
  if (typeof artifactId !== "string" || artifactId.trim() === "") throw new TypeError("artifactId is required");
@@ -1767,6 +3104,188 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
1767
3104
  return all.slice(safeOffset, safeOffset + safeLimit);
1768
3105
  };
1769
3106
 
3107
+ store.appendEmergencyControlEvent = async function appendEmergencyControlEvent({ tenantId = DEFAULT_TENANT_ID, event, audit = null } = {}) {
3108
+ tenantId = normalizeTenantId(tenantId);
3109
+ if (!event || typeof event !== "object" || Array.isArray(event)) throw new TypeError("event is required");
3110
+
3111
+ const eventId = assertNonEmptyString(event.eventId ?? event.id ?? null, "event.eventId");
3112
+ const action = normalizeEmergencyAction(event.action ?? null);
3113
+ const scopeInput = event.scope && typeof event.scope === "object" && !Array.isArray(event.scope) ? event.scope : {};
3114
+ const scopeType = normalizeEmergencyScopeType(scopeInput.type ?? event.scopeType ?? EMERGENCY_SCOPE_TYPE.TENANT);
3115
+ const scopeId = normalizeEmergencyScopeId(scopeType, scopeInput.id ?? event.scopeId ?? null);
3116
+ const controlType =
3117
+ action === EMERGENCY_ACTION.RESUME
3118
+ ? normalizeEmergencyControlType(event.controlType ?? null, { allowNull: true })
3119
+ : normalizeEmergencyControlType(event.controlType ?? action, { allowNull: false });
3120
+ const resumeControlTypes =
3121
+ action === EMERGENCY_ACTION.RESUME
3122
+ ? normalizeEmergencyResumeControlTypes(event.resumeControlTypes ?? (controlType ? [controlType] : null))
3123
+ : [];
3124
+
3125
+ const nowAt = typeof store.nowIso === "function" ? store.nowIso() : new Date().toISOString();
3126
+ const effectiveAt =
3127
+ typeof event.effectiveAt === "string" && event.effectiveAt.trim() !== "" ? event.effectiveAt.trim() : nowAt;
3128
+ const createdAt = typeof event.createdAt === "string" && event.createdAt.trim() !== "" ? event.createdAt.trim() : nowAt;
3129
+ const normalizedEvent = {
3130
+ ...event,
3131
+ schemaVersion:
3132
+ typeof event.schemaVersion === "string" && event.schemaVersion.trim() !== ""
3133
+ ? event.schemaVersion.trim()
3134
+ : "OpsEmergencyControlEvent.v1",
3135
+ eventId,
3136
+ tenantId,
3137
+ action,
3138
+ controlType,
3139
+ resumeControlTypes,
3140
+ scope: { type: scopeType, id: scopeId },
3141
+ effectiveAt,
3142
+ createdAt
3143
+ };
3144
+
3145
+ await store.commitTx({ at: effectiveAt, ops: [{ kind: "EMERGENCY_CONTROL_EVENT_APPEND", tenantId, event: normalizedEvent }], audit });
3146
+ return store.emergencyControlEvents.get(makeScopedKey({ tenantId, id: eventId })) ?? normalizedEvent;
3147
+ };
3148
+
3149
+ store.listEmergencyControlEvents = async function listEmergencyControlEvents({
3150
+ tenantId = DEFAULT_TENANT_ID,
3151
+ action = null,
3152
+ scopeType = null,
3153
+ scopeId = null,
3154
+ controlType = null,
3155
+ limit = 200,
3156
+ offset = 0
3157
+ } = {}) {
3158
+ tenantId = normalizeTenantId(tenantId);
3159
+ if (action !== null) action = normalizeEmergencyAction(action);
3160
+ if (scopeType !== null) scopeType = normalizeEmergencyScopeType(scopeType);
3161
+ if (controlType !== null) controlType = normalizeEmergencyControlType(controlType, { allowNull: false });
3162
+ if (scopeId !== null && (typeof scopeId !== "string" || scopeId.trim() === "")) {
3163
+ throw new TypeError("scopeId must be null or a non-empty string");
3164
+ }
3165
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
3166
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
3167
+ const safeLimit = Math.min(1000, limit);
3168
+ const safeOffset = offset;
3169
+
3170
+ const out = [];
3171
+ for (const row of store.emergencyControlEvents.values()) {
3172
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
3173
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
3174
+ if (action !== null && String(row.action ?? "").toLowerCase() !== action) continue;
3175
+ const rowScope = row.scope && typeof row.scope === "object" && !Array.isArray(row.scope) ? row.scope : {};
3176
+ if (scopeType !== null && String(rowScope.type ?? "").toLowerCase() !== scopeType) continue;
3177
+ if (scopeId !== null && String(rowScope.id ?? "") !== scopeId) continue;
3178
+ if (controlType !== null && String(row.controlType ?? "").toLowerCase() !== controlType) continue;
3179
+ out.push(row);
3180
+ }
3181
+ out.sort((left, right) => {
3182
+ const leftMs = Number.isFinite(Date.parse(String(left?.effectiveAt ?? ""))) ? Date.parse(String(left.effectiveAt)) : Number.NaN;
3183
+ const rightMs = Number.isFinite(Date.parse(String(right?.effectiveAt ?? ""))) ? Date.parse(String(right.effectiveAt)) : Number.NaN;
3184
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && rightMs !== leftMs) return rightMs - leftMs;
3185
+ return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
3186
+ });
3187
+ return out.slice(safeOffset, safeOffset + safeLimit);
3188
+ };
3189
+
3190
+ store.listEmergencyControlState = async function listEmergencyControlState({
3191
+ tenantId = DEFAULT_TENANT_ID,
3192
+ active = null,
3193
+ scopeType = null,
3194
+ scopeId = null,
3195
+ controlType = null,
3196
+ limit = 200,
3197
+ offset = 0
3198
+ } = {}) {
3199
+ tenantId = normalizeTenantId(tenantId);
3200
+ if (active !== null && typeof active !== "boolean") throw new TypeError("active must be null or boolean");
3201
+ if (scopeType !== null) scopeType = normalizeEmergencyScopeType(scopeType);
3202
+ if (controlType !== null) controlType = normalizeEmergencyControlType(controlType, { allowNull: false });
3203
+ if (scopeId !== null && (typeof scopeId !== "string" || scopeId.trim() === "")) {
3204
+ throw new TypeError("scopeId must be null or a non-empty string");
3205
+ }
3206
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
3207
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
3208
+ const safeLimit = Math.min(1000, limit);
3209
+ const safeOffset = offset;
3210
+
3211
+ const out = [];
3212
+ for (const row of store.emergencyControlState.values()) {
3213
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
3214
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
3215
+ if (active !== null && Boolean(row.active) !== active) continue;
3216
+ if (scopeType !== null && String(row.scopeType ?? "").toLowerCase() !== scopeType) continue;
3217
+ if (scopeId !== null && String(row.scopeId ?? "") !== scopeId) continue;
3218
+ if (controlType !== null && String(row.controlType ?? "").toLowerCase() !== controlType) continue;
3219
+ out.push(row);
3220
+ }
3221
+ out.sort((left, right) => {
3222
+ const leftMs = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
3223
+ const rightMs = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
3224
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && rightMs !== leftMs) return rightMs - leftMs;
3225
+ const leftScope = `${String(left?.scopeType ?? "")}:${String(left?.scopeId ?? "")}`;
3226
+ const rightScope = `${String(right?.scopeType ?? "")}:${String(right?.scopeId ?? "")}`;
3227
+ const scopeOrder = leftScope.localeCompare(rightScope);
3228
+ if (scopeOrder !== 0) return scopeOrder;
3229
+ return String(left?.controlType ?? "").localeCompare(String(right?.controlType ?? ""));
3230
+ });
3231
+ return out.slice(safeOffset, safeOffset + safeLimit);
3232
+ };
3233
+
3234
+ store.findEmergencyControlBlock = async function findEmergencyControlBlock({
3235
+ tenantId = DEFAULT_TENANT_ID,
3236
+ controlTypes = null,
3237
+ agentIds = [],
3238
+ adapterIds = []
3239
+ } = {}) {
3240
+ tenantId = normalizeTenantId(tenantId);
3241
+ const allowedControlTypes = controlTypes === null ? null : new Set(normalizeEmergencyResumeControlTypes(controlTypes));
3242
+ const normalizedAgentIds = new Set((Array.isArray(agentIds) ? agentIds : []).map((value) => String(value ?? "").trim()).filter(Boolean));
3243
+ const normalizedAdapterIds = new Set((Array.isArray(adapterIds) ? adapterIds : []).map((value) => String(value ?? "").trim()).filter(Boolean));
3244
+ const controlPriority = new Map([
3245
+ [EMERGENCY_CONTROL_TYPE.KILL_SWITCH, 0],
3246
+ [EMERGENCY_CONTROL_TYPE.REVOKE, 1],
3247
+ [EMERGENCY_CONTROL_TYPE.QUARANTINE, 2],
3248
+ [EMERGENCY_CONTROL_TYPE.PAUSE, 3]
3249
+ ]);
3250
+ const scopePriority = new Map([
3251
+ [EMERGENCY_SCOPE_TYPE.AGENT, 0],
3252
+ [EMERGENCY_SCOPE_TYPE.ADAPTER, 1],
3253
+ [EMERGENCY_SCOPE_TYPE.TENANT, 2]
3254
+ ]);
3255
+
3256
+ const matches = [];
3257
+ for (const row of store.emergencyControlState.values()) {
3258
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
3259
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
3260
+ if (row.active !== true) continue;
3261
+ const rowControlType = normalizeEmergencyControlType(row.controlType ?? null, { allowNull: false });
3262
+ if (allowedControlTypes && !allowedControlTypes.has(rowControlType)) continue;
3263
+ const rowScopeType = normalizeEmergencyScopeType(row.scopeType ?? null);
3264
+ const rowScopeId = row.scopeId === null || row.scopeId === undefined ? null : String(row.scopeId);
3265
+ if (rowScopeType === EMERGENCY_SCOPE_TYPE.AGENT && !normalizedAgentIds.has(String(rowScopeId ?? ""))) continue;
3266
+ if (rowScopeType === EMERGENCY_SCOPE_TYPE.ADAPTER && !normalizedAdapterIds.has(String(rowScopeId ?? ""))) continue;
3267
+ matches.push({
3268
+ ...row,
3269
+ controlType: rowControlType,
3270
+ scopeType: rowScopeType,
3271
+ scopeId: rowScopeId
3272
+ });
3273
+ }
3274
+ matches.sort((left, right) => {
3275
+ const leftControl = controlPriority.get(left.controlType) ?? 100;
3276
+ const rightControl = controlPriority.get(right.controlType) ?? 100;
3277
+ if (leftControl !== rightControl) return leftControl - rightControl;
3278
+ const leftScope = scopePriority.get(left.scopeType) ?? 100;
3279
+ const rightScope = scopePriority.get(right.scopeType) ?? 100;
3280
+ if (leftScope !== rightScope) return leftScope - rightScope;
3281
+ const leftMs = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
3282
+ const rightMs = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
3283
+ if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && rightMs !== leftMs) return rightMs - leftMs;
3284
+ return String(left?.lastEventId ?? "").localeCompare(String(right?.lastEventId ?? ""));
3285
+ });
3286
+ return matches[0] ?? null;
3287
+ };
3288
+
1770
3289
  store.getSignerKey = async function getSignerKey({ tenantId = DEFAULT_TENANT_ID, keyId } = {}) {
1771
3290
  tenantId = normalizeTenantId(tenantId);
1772
3291
  if (typeof keyId !== "string" || keyId.trim() === "") throw new TypeError("keyId is required");