settld 0.1.1 → 0.1.5

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 (441) hide show
  1. package/README.md +61 -3
  2. package/SETTLD_VERSION +1 -1
  3. package/bin/settld-mcp +2 -0
  4. package/bin/settld.js +13 -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 +140 -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 +337 -0
  36. package/docs/QUICKSTART_MCP_HOSTS.md +143 -0
  37. package/docs/QUICKSTART_PRODUCE.md +61 -0
  38. package/docs/QUICKSTART_RELEASE_VERIFY.md +39 -0
  39. package/docs/QUICKSTART_SDK.md +125 -0
  40. package/docs/QUICKSTART_SDK_PYTHON.md +111 -0
  41. package/docs/QUICKSTART_VERIFY.md +54 -0
  42. package/docs/QUICKSTART_X402_GATEWAY.md +317 -0
  43. package/docs/README.md +15 -0
  44. package/docs/RELEASE_CHECKLIST.md +156 -0
  45. package/docs/RELEASING.md +81 -0
  46. package/docs/REPO_SETTINGS.md +37 -0
  47. package/docs/RUNBOOK.md +86 -0
  48. package/docs/SKILLS.md +42 -0
  49. package/docs/SKILL_BUNDLE_FORMAT.md +48 -0
  50. package/docs/SLO.md +70 -0
  51. package/docs/SUMMARY.md +16 -0
  52. package/docs/SUPPORT.md +31 -0
  53. package/docs/THREAT_MODEL.md +36 -0
  54. package/docs/TRUST.md +59 -0
  55. package/docs/WORKFLOW.md +35 -0
  56. package/docs/X402_BATCH_SETTLEMENT.md +126 -0
  57. package/docs/blog/2026-02-14-your-ai-agent-just-spent-500-where-is-the-receipt.md +73 -0
  58. package/docs/examples/x402-provider-payout-registry.example.json +14 -0
  59. package/docs/gitbook/README.md +52 -0
  60. package/docs/gitbook/SETUP.md +25 -0
  61. package/docs/gitbook/SUMMARY.md +15 -0
  62. package/docs/gitbook/api-reference.md +73 -0
  63. package/docs/gitbook/closepacks.md +55 -0
  64. package/docs/gitbook/conformance.md +59 -0
  65. package/docs/gitbook/core-primitives.md +85 -0
  66. package/docs/gitbook/dispute-lifecycle.md +33 -0
  67. package/docs/gitbook/faq.md +21 -0
  68. package/docs/gitbook/guides.md +49 -0
  69. package/docs/gitbook/operations-runbook.md +36 -0
  70. package/docs/gitbook/quickstart.md +104 -0
  71. package/docs/gitbook/replay-and-audit.md +30 -0
  72. package/docs/gitbook/sdk-reference.md +35 -0
  73. package/docs/gitbook/security-model.md +58 -0
  74. package/docs/integrations/README.md +14 -0
  75. package/docs/integrations/github-actions-verify.yml +31 -0
  76. package/docs/integrations/github-actions.md +34 -0
  77. package/docs/integrations/openclaw/CLAWHUB_PUBLISH_CHECKLIST.md +65 -0
  78. package/docs/integrations/openclaw/settld-mcp-skill/SKILL.md +69 -0
  79. package/docs/integrations/openclaw/settld-mcp-skill/mcp-server.example.json +12 -0
  80. package/docs/kernel-compatible/capabilities.json +36 -0
  81. package/docs/marketing/agent-commerce-substrate.md +78 -0
  82. package/docs/marketing/hn-repost-2026-02-17.md +102 -0
  83. package/docs/marketing/show-hn-post.md +45 -0
  84. package/docs/ops/ARTIFACT_VERIFICATION_STATUS.md +43 -0
  85. package/docs/ops/BILLING_WEBHOOK_REPLAY.md +105 -0
  86. package/docs/ops/CI_FLAKE_BUDGET.md +31 -0
  87. package/docs/ops/GO_LIVE_GATE_S13.md +27 -0
  88. package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
  89. package/docs/ops/KERNEL_V0_SHIP_GATE.md +67 -0
  90. package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
  91. package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +28 -0
  92. package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +89 -0
  93. package/docs/ops/P0_BACKEND_PROGRESS.md +150 -0
  94. package/docs/ops/PAYMENTS_ALPHA_R5.md +105 -0
  95. package/docs/ops/PILOT_ONBOARDING_RUNBOOK.md +112 -0
  96. package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +103 -0
  97. package/docs/ops/R1_SLOS.md +66 -0
  98. package/docs/ops/RELEASE_SIGNING_INCIDENT.md +58 -0
  99. package/docs/ops/SELF_SERVE_LAUNCH_AUTOMATION.md +89 -0
  100. package/docs/ops/THROUGHPUT_DRILL_10X.md +48 -0
  101. package/docs/ops/TRUST_CONFIG_WIZARD.md +47 -0
  102. package/docs/ops/X402_PILOT_WEEKLY_METRICS.md +76 -0
  103. package/docs/ops/tool-call-disputes-holdback.md +52 -0
  104. package/docs/pilot-kit/PILOT_PACKAGE_SCORECARD_X402.md +46 -0
  105. package/docs/pilot-kit/README.md +29 -0
  106. package/docs/pilot-kit/architecture-one-pager.md +48 -0
  107. package/docs/pilot-kit/buyer-email.txt +19 -0
  108. package/docs/pilot-kit/buyer-one-pager.md +31 -0
  109. package/docs/pilot-kit/gtm-pilot-playbook.md +182 -0
  110. package/docs/pilot-kit/offline-verify.md +33 -0
  111. package/docs/pilot-kit/procurement-one-pager.md +50 -0
  112. package/docs/pilot-kit/rfp-clause.md +46 -0
  113. package/docs/pilot-kit/roi-calculator-template.csv +2 -0
  114. package/docs/pilot-kit/security-qa.md +153 -0
  115. package/docs/pilot-kit/security-summary.md +35 -0
  116. package/docs/plans/2026-02-13-mcp-spike-design.md +113 -0
  117. package/docs/spec/AcceptanceCriteria.v1.md +17 -0
  118. package/docs/spec/AcceptanceEvaluation.v1.md +10 -0
  119. package/docs/spec/AgentEvent.v1.md +47 -0
  120. package/docs/spec/AgentIdentity.v1.md +62 -0
  121. package/docs/spec/AgentPassport.v1.md +95 -0
  122. package/docs/spec/AgentReputation.v1.md +59 -0
  123. package/docs/spec/AgentReputation.v2.md +52 -0
  124. package/docs/spec/AgentRun.v1.md +47 -0
  125. package/docs/spec/AgentRunSettlement.v1.md +52 -0
  126. package/docs/spec/AgentWallet.v1.md +43 -0
  127. package/docs/spec/AgreementDelegation.v1.md +109 -0
  128. package/docs/spec/ArbitrationCase.v1.md +67 -0
  129. package/docs/spec/ArbitrationVerdict.v1.md +60 -0
  130. package/docs/spec/BundleHeadAttestation.v1.md +32 -0
  131. package/docs/spec/CANONICAL_JSON.md +31 -0
  132. package/docs/spec/CRYPTOGRAPHY.md +61 -0
  133. package/docs/spec/ClosePack.v1.md +49 -0
  134. package/docs/spec/ClosePackManifest.v1.md +24 -0
  135. package/docs/spec/DelegationGrant.v1.md +90 -0
  136. package/docs/spec/DisputeOpenEnvelope.v1.md +43 -0
  137. package/docs/spec/ERRORS.md +76 -0
  138. package/docs/spec/ESCROW_NETTING_INVARIANTS.md +71 -0
  139. package/docs/spec/EvidenceIndex.v1.md +20 -0
  140. package/docs/spec/ExecutionIntent.v1.md +90 -0
  141. package/docs/spec/FinancePackBundleManifest.v1.md +24 -0
  142. package/docs/spec/FundingHold.v1.md +60 -0
  143. package/docs/spec/GovernancePolicy.v1.md +34 -0
  144. package/docs/spec/GovernancePolicy.v2.md +30 -0
  145. package/docs/spec/INVARIANTS.md +389 -0
  146. package/docs/spec/InteractionDirectionMatrix.v1.md +30 -0
  147. package/docs/spec/InvoiceBundleManifest.v1.md +24 -0
  148. package/docs/spec/InvoiceClaim.v1.md +11 -0
  149. package/docs/spec/MONEY_RAIL_STATE_MACHINE.md +58 -0
  150. package/docs/spec/MarketplaceAcceptance.v2.md +46 -0
  151. package/docs/spec/MarketplaceOffer.v2.md +54 -0
  152. package/docs/spec/MeteringReport.v1.md +18 -0
  153. package/docs/spec/PRODUCER_ERRORS.md +42 -0
  154. package/docs/spec/PricingMatrix.v1.md +20 -0
  155. package/docs/spec/PricingMatrixSignatures.v1.md +30 -0
  156. package/docs/spec/PricingMatrixSignatures.v2.md +29 -0
  157. package/docs/spec/ProduceCliOutput.v1.md +46 -0
  158. package/docs/spec/ProofBundleManifest.v1.md +24 -0
  159. package/docs/spec/README.md +104 -0
  160. package/docs/spec/REFERENCE_IMPLEMENTATIONS.md +29 -0
  161. package/docs/spec/REFERENCE_VERIFIER_BEHAVIOR.md +68 -0
  162. package/docs/spec/REMOTE_SIGNER.md +66 -0
  163. package/docs/spec/ReleaseIndex.v1.md +32 -0
  164. package/docs/spec/ReleaseIndexSignatures.v1.md +17 -0
  165. package/docs/spec/ReleaseTrust.v1.md +13 -0
  166. package/docs/spec/ReleaseTrust.v2.md +26 -0
  167. package/docs/spec/RemoteSignerRequest.v1.md +21 -0
  168. package/docs/spec/RemoteSignerResponse.v1.md +16 -0
  169. package/docs/spec/ReputationEvent.v1.md +63 -0
  170. package/docs/spec/RevocationList.v1.md +28 -0
  171. package/docs/spec/SIGNER_PROVIDER_PLUGIN.md +32 -0
  172. package/docs/spec/STRICTNESS.md +68 -0
  173. package/docs/spec/SUPPLY_CHAIN.md +33 -0
  174. package/docs/spec/SettlementAdjustment.v1.md +45 -0
  175. package/docs/spec/SettlementDecisionRecord.v1.md +48 -0
  176. package/docs/spec/SettlementDecisionRecord.v2.md +51 -0
  177. package/docs/spec/SettlementDecisionReport.v1.md +44 -0
  178. package/docs/spec/SettlementKernel.v1.md +59 -0
  179. package/docs/spec/SettlementReceipt.v1.md +63 -0
  180. package/docs/spec/SlaDefinition.v1.md +24 -0
  181. package/docs/spec/SlaEvaluation.v1.md +12 -0
  182. package/docs/spec/THREAT_MODEL.md +113 -0
  183. package/docs/spec/TOOL_PROVENANCE.md +30 -0
  184. package/docs/spec/TRUST_ANCHORS.md +84 -0
  185. package/docs/spec/TenantSettings.v1.md +90 -0
  186. package/docs/spec/TenantSettings.v2.md +99 -0
  187. package/docs/spec/TimestampProof.v1.md +25 -0
  188. package/docs/spec/ToolCallAgreement.v1.md +34 -0
  189. package/docs/spec/ToolCallEvidence.v1.md +47 -0
  190. package/docs/spec/ToolManifest.v1.md +47 -0
  191. package/docs/spec/VERIFIER_ENVIRONMENT.md +38 -0
  192. package/docs/spec/VERSIONING.md +107 -0
  193. package/docs/spec/VerificationReport.v1.md +50 -0
  194. package/docs/spec/VerifyAboutOutput.v1.md +10 -0
  195. package/docs/spec/VerifyCliOutput.v1.md +28 -0
  196. package/docs/spec/WARNINGS.md +83 -0
  197. package/docs/spec/error-codes.v1.txt +285 -0
  198. package/docs/spec/examples/agreement_delegation_v1.example.json +21 -0
  199. package/docs/spec/examples/arbitration_case_v1.example.json +26 -0
  200. package/docs/spec/examples/arbitration_verdict_v1.example.json +32 -0
  201. package/docs/spec/examples/dispute_open_envelope_v1.example.json +18 -0
  202. package/docs/spec/examples/produce_cli_output_v1.example.json +32 -0
  203. package/docs/spec/examples/release_index_signature_v1.example.json +9 -0
  204. package/docs/spec/examples/release_index_signatures_v1.example.json +14 -0
  205. package/docs/spec/examples/release_index_v1.example.json +15 -0
  206. package/docs/spec/examples/release_trust_v1.example.json +7 -0
  207. package/docs/spec/examples/release_trust_v2.example.json +22 -0
  208. package/docs/spec/examples/remote_signer_request_v1.example.json +18 -0
  209. package/docs/spec/examples/remote_signer_response_v1.example.json +8 -0
  210. package/docs/spec/examples/reputation_event_v1.example.json +29 -0
  211. package/docs/spec/examples/verification_report_v1.example.json +24 -0
  212. package/docs/spec/examples/verify_about_output_v1.example.json +29 -0
  213. package/docs/spec/examples/verify_cli_output_v1.example.json +13 -0
  214. package/docs/spec/legacy/MarketplaceAcceptance.v1.md +48 -0
  215. package/docs/spec/legacy/MarketplaceOffer.v1.md +56 -0
  216. package/docs/spec/legacy/schemas/MarketplaceAcceptance.v1.schema.json +53 -0
  217. package/docs/spec/legacy/schemas/MarketplaceOffer.v1.schema.json +61 -0
  218. package/docs/spec/producer-error-codes.v1.txt +14 -0
  219. package/docs/spec/schemas/AcceptanceCriteria.v1.schema.json +24 -0
  220. package/docs/spec/schemas/AcceptanceEvaluation.v1.schema.json +26 -0
  221. package/docs/spec/schemas/AgentEvent.v1.schema.json +49 -0
  222. package/docs/spec/schemas/AgentIdentity.v1.schema.json +129 -0
  223. package/docs/spec/schemas/AgentPassport.v1.schema.json +112 -0
  224. package/docs/spec/schemas/AgentReputation.v1.schema.json +151 -0
  225. package/docs/spec/schemas/AgentReputation.v2.schema.json +120 -0
  226. package/docs/spec/schemas/AgentRun.v1.schema.json +71 -0
  227. package/docs/spec/schemas/AgentRunSettlement.v1.schema.json +75 -0
  228. package/docs/spec/schemas/AgentWallet.v1.schema.json +54 -0
  229. package/docs/spec/schemas/AgreementDelegation.v1.schema.json +50 -0
  230. package/docs/spec/schemas/ArbitrationCase.v1.schema.json +133 -0
  231. package/docs/spec/schemas/ArbitrationVerdict.v1.schema.json +149 -0
  232. package/docs/spec/schemas/BundleHeadAttestation.v1.schema.json +21 -0
  233. package/docs/spec/schemas/ClosePackManifest.v1.schema.json +38 -0
  234. package/docs/spec/schemas/DelegationGrant.v1.schema.json +102 -0
  235. package/docs/spec/schemas/DisputeOpenEnvelope.v1.schema.json +78 -0
  236. package/docs/spec/schemas/EvidenceIndex.v1.schema.json +41 -0
  237. package/docs/spec/schemas/ExecutionIntent.v1.schema.json +85 -0
  238. package/docs/spec/schemas/FinancePackBundleManifest.v1.schema.json +38 -0
  239. package/docs/spec/schemas/FundingHold.v1.schema.json +46 -0
  240. package/docs/spec/schemas/GovernancePolicy.v1.schema.json +45 -0
  241. package/docs/spec/schemas/GovernancePolicy.v2.schema.json +70 -0
  242. package/docs/spec/schemas/InteractionDirectionMatrix.v1.schema.json +43 -0
  243. package/docs/spec/schemas/InvoiceBundleManifest.v1.schema.json +38 -0
  244. package/docs/spec/schemas/InvoiceClaim.v1.schema.json +39 -0
  245. package/docs/spec/schemas/MarketplaceAcceptance.v2.schema.json +53 -0
  246. package/docs/spec/schemas/MarketplaceOffer.v2.schema.json +61 -0
  247. package/docs/spec/schemas/MeteringReport.v1.schema.json +45 -0
  248. package/docs/spec/schemas/PricingMatrix.v1.schema.json +24 -0
  249. package/docs/spec/schemas/PricingMatrixSignatures.v1.schema.json +24 -0
  250. package/docs/spec/schemas/PricingMatrixSignatures.v2.schema.json +24 -0
  251. package/docs/spec/schemas/ProduceCliOutput.v1.schema.json +107 -0
  252. package/docs/spec/schemas/ProofBundleManifest.v1.schema.json +37 -0
  253. package/docs/spec/schemas/PublicKeys.v1.schema.json +33 -0
  254. package/docs/spec/schemas/ReleaseIndex.v1.schema.json +45 -0
  255. package/docs/spec/schemas/ReleaseIndexSignature.v1.schema.json +16 -0
  256. package/docs/spec/schemas/ReleaseIndexSignatures.v1.schema.json +16 -0
  257. package/docs/spec/schemas/ReleaseTrust.v1.schema.json +15 -0
  258. package/docs/spec/schemas/ReleaseTrust.v2.schema.json +37 -0
  259. package/docs/spec/schemas/RemoteSignerPublicKeyResponse.v1.schema.json +14 -0
  260. package/docs/spec/schemas/RemoteSignerRequest.v1.schema.json +24 -0
  261. package/docs/spec/schemas/RemoteSignerResponse.v1.schema.json +10 -0
  262. package/docs/spec/schemas/RemoteSignerSignRequest.v1.schema.json +27 -0
  263. package/docs/spec/schemas/RemoteSignerSignResponse.v1.schema.json +16 -0
  264. package/docs/spec/schemas/ReputationEvent.v1.schema.json +164 -0
  265. package/docs/spec/schemas/RevocationList.v1.schema.json +51 -0
  266. package/docs/spec/schemas/SettlementAdjustment.v1.schema.json +44 -0
  267. package/docs/spec/schemas/SettlementDecisionRecord.v1.schema.json +66 -0
  268. package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +148 -0
  269. package/docs/spec/schemas/SettlementDecisionReport.v1.schema.json +61 -0
  270. package/docs/spec/schemas/SettlementReceipt.v1.schema.json +135 -0
  271. package/docs/spec/schemas/SlaDefinition.v1.schema.json +33 -0
  272. package/docs/spec/schemas/SlaEvaluation.v1.schema.json +26 -0
  273. package/docs/spec/schemas/TenantSettings.v1.schema.json +90 -0
  274. package/docs/spec/schemas/TenantSettings.v2.schema.json +161 -0
  275. package/docs/spec/schemas/TimestampProof.v1.schema.json +17 -0
  276. package/docs/spec/schemas/ToolCallAgreement.v1.schema.json +34 -0
  277. package/docs/spec/schemas/ToolCallEvidence.v1.schema.json +45 -0
  278. package/docs/spec/schemas/ToolManifest.v1.schema.json +54 -0
  279. package/docs/spec/schemas/VerificationReport.v1.schema.json +83 -0
  280. package/docs/spec/schemas/VerifyAboutOutput.v1.schema.json +54 -0
  281. package/docs/spec/schemas/VerifyCliOutput.v1.schema.json +75 -0
  282. package/docs/spec/schemas/VerifyReleaseOutput.v1.schema.json +47 -0
  283. package/docs/spec/x402-error-codes.v1.txt +21 -0
  284. package/docs/templates/buyer-email.txt +18 -0
  285. package/docs/templates/buyer-one-pager.md +24 -0
  286. package/package.json +40 -6
  287. package/scripts/acceptance/full-stack.mjs +734 -0
  288. package/scripts/acceptance/full-stack.sh +99 -0
  289. package/scripts/audit/build-audit-packet.mjs +242 -0
  290. package/scripts/backup-pg.sh +45 -0
  291. package/scripts/backup-restore/README.md +18 -0
  292. package/scripts/backup-restore/capture-state.mjs +130 -0
  293. package/scripts/backup-restore/client.mjs +97 -0
  294. package/scripts/backup-restore/seed-workload.mjs +235 -0
  295. package/scripts/backup-restore/verify-state.mjs +139 -0
  296. package/scripts/backup-restore-test.sh +217 -0
  297. package/scripts/chaos.js +221 -0
  298. package/scripts/ci/build-launch-cutover-packet.mjs +148 -0
  299. package/scripts/ci/build-self-serve-benchmark-report.mjs +122 -0
  300. package/scripts/ci/changelog-guard.mjs +145 -0
  301. package/scripts/ci/check-kernel-v0-launch-gate.mjs +233 -0
  302. package/scripts/ci/check-secret-hygiene.mjs +78 -0
  303. package/scripts/ci/check-version-consistency.mjs +42 -0
  304. package/scripts/ci/cli-pack-smoke.mjs +160 -0
  305. package/scripts/ci/flake-budget-guard.mjs +68 -0
  306. package/scripts/ci/generate-error-codes.mjs +54 -0
  307. package/scripts/ci/lib/lighthouse-tracker.mjs +90 -0
  308. package/scripts/ci/lib/self-serve-launch-gate.mjs +89 -0
  309. package/scripts/ci/npm-pack-smoke.mjs +454 -0
  310. package/scripts/ci/run-10x-throughput-drill.mjs +246 -0
  311. package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +325 -0
  312. package/scripts/ci/run-arbitration-workspace-browser-e2e.sh +22 -0
  313. package/scripts/ci/run-circle-sandbox-smoke.mjs +237 -0
  314. package/scripts/ci/run-go-live-gate.mjs +150 -0
  315. package/scripts/ci/run-kernel-v0-ship-gate.mjs +97 -0
  316. package/scripts/ci/run-mcp-host-smoke.mjs +275 -0
  317. package/scripts/ci/run-self-serve-launch-gate.mjs +56 -0
  318. package/scripts/ci/runtime-import-smoke.mjs +58 -0
  319. package/scripts/ci/update-lighthouse-tracker.mjs +112 -0
  320. package/scripts/closepack/lib.mjs +286 -0
  321. package/scripts/collect-debug.sh +263 -0
  322. package/scripts/demo/compositional-settlement-3hop.mjs +237 -0
  323. package/scripts/demo/delivery-robot/export-ui-fixture.mjs +188 -0
  324. package/scripts/demo/delivery-robot/generate.mjs +377 -0
  325. package/scripts/demo/kernel-agent-goes-shopping.mjs +202 -0
  326. package/scripts/demo/magic-link-first-green.mjs +118 -0
  327. package/scripts/demo/magic-link-kind-smoke.mjs +577 -0
  328. package/scripts/demo/mcp-paid-exa.mjs +1110 -0
  329. package/scripts/dev/billing-doctor.sh +145 -0
  330. package/scripts/dev/billing-smoke-prod.sh +219 -0
  331. package/scripts/dev/billing-webhook-replay.sh +161 -0
  332. package/scripts/dev/env.dev.example +29 -0
  333. package/scripts/dev/env.sh +37 -0
  334. package/scripts/dev/new-sdk-key.sh +81 -0
  335. package/scripts/dev/sdk-first-run.sh +21 -0
  336. package/scripts/dev/smoke-x402-gateway.sh +115 -0
  337. package/scripts/dev/start-api.sh +24 -0
  338. package/scripts/examples/produce-and-verify-jobproof.mjs +191 -0
  339. package/scripts/examples/sdk-first-paid-rfq.py +105 -0
  340. package/scripts/examples/sdk-first-verified-run.mjs +85 -0
  341. package/scripts/examples/sdk-first-verified-run.py +99 -0
  342. package/scripts/examples/sdk-tenant-analytics.mjs +103 -0
  343. package/scripts/examples/sdk-tenant-analytics.py +118 -0
  344. package/scripts/finance-pack/bundle.mjs +284 -0
  345. package/scripts/fixtures/generate-bundle-fixtures.mjs +877 -0
  346. package/scripts/governance/export.mjs +169 -0
  347. package/scripts/load/delivery-stress.k6.js +183 -0
  348. package/scripts/load/ingest-burst.k6.js +236 -0
  349. package/scripts/load/run-delivery-load.js +66 -0
  350. package/scripts/load/webhook-receiver.js +131 -0
  351. package/scripts/magic-link/migrate-run-records-to-db.mjs +35 -0
  352. package/scripts/mcp/probe.mjs +238 -0
  353. package/scripts/mcp/settld-mcp-http-gateway.mjs +178 -0
  354. package/scripts/mcp/settld-mcp-server.mjs +1201 -0
  355. package/scripts/openapi/write.mjs +13 -0
  356. package/scripts/ops/bootstrap-tenant-conformance.mjs +185 -0
  357. package/scripts/ops/build-x402-pilot-reliability-report.mjs +489 -0
  358. package/scripts/ops/check-x402-receipt-sample.mjs +181 -0
  359. package/scripts/ops/design-partner-run-packet.mjs +466 -0
  360. package/scripts/ops/hosted-baseline-evidence.mjs +681 -0
  361. package/scripts/ops/money-rails-chargeback-evidence.mjs +509 -0
  362. package/scripts/ops/money-rails-reconcile-evidence.mjs +180 -0
  363. package/scripts/ops/p0-seed-money-rail-operation.mjs +432 -0
  364. package/scripts/pilot/finance-pack.mjs +495 -0
  365. package/scripts/pilot/fixtures/robot-keypair.json +4 -0
  366. package/scripts/pilot/fixtures/server-signer.json +4 -0
  367. package/scripts/proof-bundle/job.mjs +109 -0
  368. package/scripts/proof-bundle/lib.mjs +92 -0
  369. package/scripts/proof-bundle/month.mjs +103 -0
  370. package/scripts/provider/conformance-run.mjs +159 -0
  371. package/scripts/provider/keys-generate.mjs +135 -0
  372. package/scripts/provider/publish.mjs +420 -0
  373. package/scripts/quickstart/x402.mjs +334 -0
  374. package/scripts/release/build-artifacts.mjs +181 -0
  375. package/scripts/release/generate-release-index.mjs +112 -0
  376. package/scripts/release/release-index-lib.mjs +232 -0
  377. package/scripts/release/sign-release-index.mjs +85 -0
  378. package/scripts/release/validate-release-assets.mjs +170 -0
  379. package/scripts/release/verify-release.mjs +261 -0
  380. package/scripts/restore-pg.sh +34 -0
  381. package/scripts/scaffold/create-settld-paid-tool.mjs +19 -0
  382. package/scripts/sdk/smoke-python.py +30 -0
  383. package/scripts/sdk/smoke.mjs +16 -0
  384. package/scripts/settlement/x402-batch-worker.mjs +1091 -0
  385. package/scripts/slo/check.mjs +178 -0
  386. package/scripts/smoke/k8s-smoke.mjs +214 -0
  387. package/scripts/spec/generate-protocol-vectors.mjs +931 -0
  388. package/scripts/test/check-no-generated-artifacts.sh +12 -0
  389. package/scripts/test/run.sh +45 -0
  390. package/scripts/trust/validate-trust-file.mjs +57 -0
  391. package/scripts/trust-config/rotate-settld-pay.mjs +277 -0
  392. package/scripts/trust-config/wizard.mjs +161 -0
  393. package/scripts/vendor-contract-test-lib.mjs +182 -0
  394. package/scripts/vendor-contract-test.mjs +55 -0
  395. package/scripts/vercel/build-mkdocs.sh +9 -0
  396. package/scripts/vercel/ignore-mkdocs.sh +25 -0
  397. package/scripts/vercel/install-mkdocs.sh +6 -0
  398. package/scripts/verify-pg.js +217 -0
  399. package/scripts/x402/receipt-verify.mjs +289 -0
  400. package/services/finance-sink/src/dedupe-store.js +29 -6
  401. package/services/receiver/src/dedupe-store.js +29 -5
  402. package/services/x402-gateway/Dockerfile +13 -0
  403. package/services/x402-gateway/README.md +58 -0
  404. package/services/x402-gateway/examples/upstream-mock.js +337 -0
  405. package/services/x402-gateway/src/server.js +947 -0
  406. package/src/api/app.js +32517 -16877
  407. package/src/api/maintenance.js +70 -0
  408. package/src/api/openapi.js +1130 -17
  409. package/src/api/persistence.js +272 -0
  410. package/src/api/server.js +81 -5
  411. package/src/api/store.js +1248 -6
  412. package/src/api/workers/deliveries.js +99 -4
  413. package/src/api/workers/insolvency-sweep.js +159 -0
  414. package/src/core/agent-card.js +69 -0
  415. package/src/core/agent-wallets.js +97 -0
  416. package/src/core/agreement-delegation.js +549 -0
  417. package/src/core/billing-plans.js +40 -6
  418. package/src/core/circle-reserve-adapter.js +845 -0
  419. package/src/core/maintenance-locks.js +1 -0
  420. package/src/core/paid-tool-manifest.js +318 -0
  421. package/src/core/provider-publish-conformance.js +525 -0
  422. package/src/core/provider-publish-proof.js +396 -0
  423. package/src/core/provider-quote-signature.js +170 -0
  424. package/src/core/settld-keys.js +112 -0
  425. package/src/core/settld-pay-token.js +344 -0
  426. package/src/core/settlement-kernel.js +213 -2
  427. package/src/core/settlement-verifier.js +335 -0
  428. package/src/core/tool-call-agreement.js +112 -0
  429. package/src/core/tool-call-evidence.js +144 -0
  430. package/src/core/tool-provider-signature.js +98 -0
  431. package/src/core/x402-escalation-override.js +258 -0
  432. package/src/core/x402-gate.js +118 -0
  433. package/src/core/x402-provider-refund-decision.js +220 -0
  434. package/src/core/x402-receipt-verifier.js +708 -0
  435. package/src/core/x402-reversal-command.js +251 -0
  436. package/src/core/x402-wallet-issuer-decision.js +252 -0
  437. package/src/core/zk-verifier.js +300 -0
  438. package/src/db/migrations/029_reputation_event_index.sql +54 -0
  439. package/src/db/migrations/030_artifacts_source_event_unique_job_only.sql +15 -0
  440. package/src/db/pg.js +18 -7
  441. package/src/db/store-pg.js +838 -72
@@ -155,6 +155,24 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
155
155
  store.agentRuns.clear();
156
156
  if (!(store.arbitrationCases instanceof Map)) store.arbitrationCases = new Map();
157
157
  store.arbitrationCases.clear();
158
+ if (!(store.agreementDelegations instanceof Map)) store.agreementDelegations = new Map();
159
+ store.agreementDelegations.clear();
160
+ if (!(store.x402Gates instanceof Map)) store.x402Gates = new Map();
161
+ store.x402Gates.clear();
162
+ if (!(store.x402AgentLifecycles instanceof Map)) store.x402AgentLifecycles = new Map();
163
+ store.x402AgentLifecycles.clear();
164
+ if (!(store.x402Receipts instanceof Map)) store.x402Receipts = new Map();
165
+ store.x402Receipts.clear();
166
+ if (!(store.x402WalletPolicies instanceof Map)) store.x402WalletPolicies = new Map();
167
+ store.x402WalletPolicies.clear();
168
+ if (!(store.x402ZkVerificationKeys instanceof Map)) store.x402ZkVerificationKeys = new Map();
169
+ store.x402ZkVerificationKeys.clear();
170
+ if (!(store.x402ReversalEvents instanceof Map)) store.x402ReversalEvents = new Map();
171
+ store.x402ReversalEvents.clear();
172
+ if (!(store.x402ReversalNonceUsage instanceof Map)) store.x402ReversalNonceUsage = new Map();
173
+ store.x402ReversalNonceUsage.clear();
174
+ if (!(store.x402ReversalCommandUsage instanceof Map)) store.x402ReversalCommandUsage = new Map();
175
+ store.x402ReversalCommandUsage.clear();
158
176
  if (!(store.toolCallHolds instanceof Map)) store.toolCallHolds = new Map();
159
177
  store.toolCallHolds.clear();
160
178
  if (!(store.settlementAdjustments instanceof Map)) store.settlementAdjustments = new Map();
@@ -180,6 +198,84 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
180
198
  caseId: snap?.caseId ?? String(id)
181
199
  });
182
200
  }
201
+ if (type === "agreement_delegation") {
202
+ store.agreementDelegations.set(key, {
203
+ ...snap,
204
+ tenantId: snap?.tenantId ?? tenantId,
205
+ delegationId: snap?.delegationId ?? String(id)
206
+ });
207
+ }
208
+ if (type === "x402_gate") {
209
+ store.x402Gates.set(key, {
210
+ ...snap,
211
+ tenantId: snap?.tenantId ?? tenantId,
212
+ gateId: snap?.gateId ?? String(id)
213
+ });
214
+ }
215
+ if (type === "x402_agent_lifecycle") {
216
+ store.x402AgentLifecycles.set(key, {
217
+ ...snap,
218
+ tenantId: snap?.tenantId ?? tenantId,
219
+ agentId: snap?.agentId ?? String(id)
220
+ });
221
+ }
222
+ if (type === "x402_receipt") {
223
+ store.x402Receipts.set(key, {
224
+ ...snap,
225
+ tenantId: snap?.tenantId ?? tenantId,
226
+ receiptId: snap?.receiptId ?? String(id),
227
+ reversal: null,
228
+ reversalEvents: []
229
+ });
230
+ }
231
+ if (type === "x402_wallet_policy") {
232
+ const sponsorWalletRef = typeof snap?.sponsorWalletRef === "string" ? snap.sponsorWalletRef : null;
233
+ const policyRef = typeof snap?.policyRef === "string" ? snap.policyRef : null;
234
+ const policyVersion = parseSafeIntegerOrNull(snap?.policyVersion);
235
+ if (sponsorWalletRef && policyRef && policyVersion !== null && policyVersion > 0) {
236
+ store.x402WalletPolicies.set(makeScopedKey({ tenantId, id: `${sponsorWalletRef}::${policyRef}::${policyVersion}` }), {
237
+ ...snap,
238
+ tenantId: snap?.tenantId ?? tenantId,
239
+ sponsorWalletRef,
240
+ policyRef,
241
+ policyVersion
242
+ });
243
+ }
244
+ }
245
+ if (type === "x402_zk_verification_key") {
246
+ store.x402ZkVerificationKeys.set(key, {
247
+ ...snap,
248
+ tenantId: snap?.tenantId ?? tenantId,
249
+ verificationKeyId: snap?.verificationKeyId ?? String(id)
250
+ });
251
+ }
252
+ if (type === "x402_reversal_event") {
253
+ store.x402ReversalEvents.set(key, {
254
+ ...snap,
255
+ tenantId: snap?.tenantId ?? tenantId,
256
+ eventId: snap?.eventId ?? String(id)
257
+ });
258
+ }
259
+ if (type === "x402_reversal_nonce") {
260
+ const sponsorRef = typeof snap?.sponsorRef === "string" ? snap.sponsorRef : null;
261
+ const nonce = typeof snap?.nonce === "string" ? snap.nonce : null;
262
+ if (sponsorRef && nonce) {
263
+ const nonceKey = `${tenantId}\n${sponsorRef}\n${nonce}`;
264
+ store.x402ReversalNonceUsage.set(nonceKey, {
265
+ ...snap,
266
+ tenantId,
267
+ sponsorRef,
268
+ nonce
269
+ });
270
+ }
271
+ }
272
+ if (type === "x402_reversal_command") {
273
+ store.x402ReversalCommandUsage.set(key, {
274
+ ...snap,
275
+ tenantId: snap?.tenantId ?? tenantId,
276
+ commandId: snap?.commandId ?? String(id)
277
+ });
278
+ }
183
279
  if (type === "tool_call_hold") {
184
280
  store.toolCallHolds.set(key, {
185
281
  ...snap,
@@ -1149,6 +1245,341 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
1149
1245
  );
1150
1246
  }
1151
1247
 
1248
+ async function persistAgreementDelegation(client, { tenantId, delegationId, delegation }) {
1249
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1250
+ if (!delegation || typeof delegation !== "object" || Array.isArray(delegation)) {
1251
+ throw new TypeError("delegation is required");
1252
+ }
1253
+ const normalizedDelegationId =
1254
+ delegationId ? String(delegationId) : delegation.delegationId ? String(delegation.delegationId) : null;
1255
+ if (!normalizedDelegationId) throw new TypeError("delegationId is required");
1256
+
1257
+ const updatedAt = parseIsoOrNull(delegation.updatedAt) ?? new Date().toISOString();
1258
+ const normalizedDelegation = {
1259
+ ...delegation,
1260
+ tenantId,
1261
+ delegationId: normalizedDelegationId,
1262
+ updatedAt
1263
+ };
1264
+
1265
+ await client.query(
1266
+ `
1267
+ INSERT INTO snapshots (tenant_id, aggregate_type, aggregate_id, seq, at_chain_hash, snapshot_json, updated_at)
1268
+ VALUES ($1, 'agreement_delegation', $2, 0, NULL, $3, $4)
1269
+ ON CONFLICT (tenant_id, aggregate_type, aggregate_id) DO UPDATE SET
1270
+ snapshot_json = EXCLUDED.snapshot_json,
1271
+ updated_at = EXCLUDED.updated_at
1272
+ `,
1273
+ [tenantId, normalizedDelegationId, JSON.stringify(normalizedDelegation), updatedAt]
1274
+ );
1275
+ }
1276
+
1277
+ async function persistX402Gate(client, { tenantId, gateId, gate }) {
1278
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1279
+ if (!gate || typeof gate !== "object" || Array.isArray(gate)) {
1280
+ throw new TypeError("gate is required");
1281
+ }
1282
+ const normalizedGateId = gateId ? String(gateId) : gate.gateId ? String(gate.gateId) : gate.id ? String(gate.id) : null;
1283
+ if (!normalizedGateId) throw new TypeError("gateId is required");
1284
+
1285
+ const updatedAt = parseIsoOrNull(gate.updatedAt) ?? new Date().toISOString();
1286
+ const normalizedGate = {
1287
+ ...gate,
1288
+ tenantId,
1289
+ gateId: normalizedGateId,
1290
+ updatedAt
1291
+ };
1292
+
1293
+ await client.query(
1294
+ `
1295
+ INSERT INTO snapshots (tenant_id, aggregate_type, aggregate_id, seq, at_chain_hash, snapshot_json, updated_at)
1296
+ VALUES ($1, 'x402_gate', $2, 0, NULL, $3, $4)
1297
+ ON CONFLICT (tenant_id, aggregate_type, aggregate_id) DO UPDATE SET
1298
+ snapshot_json = EXCLUDED.snapshot_json,
1299
+ updated_at = EXCLUDED.updated_at
1300
+ `,
1301
+ [tenantId, normalizedGateId, JSON.stringify(normalizedGate), updatedAt]
1302
+ );
1303
+ }
1304
+
1305
+ async function persistX402AgentLifecycle(client, { tenantId, agentId, agentLifecycle }) {
1306
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1307
+ if (!agentLifecycle || typeof agentLifecycle !== "object" || Array.isArray(agentLifecycle)) {
1308
+ throw new TypeError("agentLifecycle is required");
1309
+ }
1310
+ const normalizedAgentId =
1311
+ agentId ? String(agentId) : agentLifecycle.agentId ? String(agentLifecycle.agentId) : null;
1312
+ if (!normalizedAgentId) throw new TypeError("agentId is required");
1313
+ const status = String(agentLifecycle.status ?? "").trim().toLowerCase();
1314
+ if (status !== "active" && status !== "frozen" && status !== "archived") {
1315
+ throw new TypeError("agentLifecycle.status must be active|frozen|archived");
1316
+ }
1317
+
1318
+ const updatedAt = parseIsoOrNull(agentLifecycle.updatedAt) ?? new Date().toISOString();
1319
+ const normalizedAgentLifecycle = {
1320
+ ...agentLifecycle,
1321
+ tenantId,
1322
+ agentId: normalizedAgentId,
1323
+ status,
1324
+ updatedAt
1325
+ };
1326
+
1327
+ await client.query(
1328
+ `
1329
+ INSERT INTO snapshots (tenant_id, aggregate_type, aggregate_id, seq, at_chain_hash, snapshot_json, updated_at)
1330
+ VALUES ($1, 'x402_agent_lifecycle', $2, 0, NULL, $3, $4)
1331
+ ON CONFLICT (tenant_id, aggregate_type, aggregate_id) DO UPDATE SET
1332
+ snapshot_json = EXCLUDED.snapshot_json,
1333
+ updated_at = EXCLUDED.updated_at
1334
+ `,
1335
+ [tenantId, normalizedAgentId, JSON.stringify(normalizedAgentLifecycle), updatedAt]
1336
+ );
1337
+ }
1338
+
1339
+ async function persistX402Receipt(client, { tenantId, receiptId, receipt }) {
1340
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1341
+ if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)) {
1342
+ throw new TypeError("receipt is required");
1343
+ }
1344
+ const normalizedReceiptId =
1345
+ receiptId ? String(receiptId) : receipt.receiptId ? String(receipt.receiptId) : null;
1346
+ if (!normalizedReceiptId) throw new TypeError("receiptId is required");
1347
+
1348
+ const updatedAt =
1349
+ parseIsoOrNull(receipt.createdAt) ??
1350
+ parseIsoOrNull(receipt.settledAt) ??
1351
+ parseIsoOrNull(receipt.updatedAt) ??
1352
+ new Date().toISOString();
1353
+ const normalizedReceipt = {
1354
+ ...receipt,
1355
+ tenantId,
1356
+ receiptId: normalizedReceiptId,
1357
+ reversal: null,
1358
+ reversalEvents: [],
1359
+ updatedAt
1360
+ };
1361
+
1362
+ const inserted = await client.query(
1363
+ `
1364
+ INSERT INTO snapshots (tenant_id, aggregate_type, aggregate_id, seq, at_chain_hash, snapshot_json, updated_at)
1365
+ VALUES ($1, 'x402_receipt', $2, 0, NULL, $3, $4)
1366
+ ON CONFLICT (tenant_id, aggregate_type, aggregate_id) DO NOTHING
1367
+ RETURNING aggregate_id
1368
+ `,
1369
+ [tenantId, normalizedReceiptId, JSON.stringify(normalizedReceipt), updatedAt]
1370
+ );
1371
+ if (inserted.rows.length > 0) return;
1372
+
1373
+ const existing = await client.query(
1374
+ `
1375
+ SELECT snapshot_json
1376
+ FROM snapshots
1377
+ WHERE tenant_id = $1 AND aggregate_type = 'x402_receipt' AND aggregate_id = $2
1378
+ LIMIT 1
1379
+ `,
1380
+ [tenantId, normalizedReceiptId]
1381
+ );
1382
+ if (!existing.rows.length) return;
1383
+
1384
+ const existingCanonical = canonicalJsonStringify(existing.rows[0]?.snapshot_json ?? null);
1385
+ const incomingCanonical = canonicalJsonStringify(normalizedReceipt);
1386
+ if (existingCanonical !== incomingCanonical) {
1387
+ const err = new Error("x402 receipt is immutable and cannot be changed");
1388
+ err.code = "X402_RECEIPT_IMMUTABLE";
1389
+ err.receiptId = normalizedReceiptId;
1390
+ throw err;
1391
+ }
1392
+ }
1393
+
1394
+ async function persistX402WalletPolicy(client, { tenantId, policy }) {
1395
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1396
+ if (!policy || typeof policy !== "object" || Array.isArray(policy)) {
1397
+ throw new TypeError("policy is required");
1398
+ }
1399
+ const sponsorWalletRef =
1400
+ policy.sponsorWalletRef && String(policy.sponsorWalletRef).trim() !== ""
1401
+ ? String(policy.sponsorWalletRef).trim()
1402
+ : null;
1403
+ const policyRef = policy.policyRef && String(policy.policyRef).trim() !== "" ? String(policy.policyRef).trim() : null;
1404
+ const policyVersion = parseSafeIntegerOrNull(policy.policyVersion);
1405
+ if (!sponsorWalletRef) throw new TypeError("policy.sponsorWalletRef is required");
1406
+ if (!policyRef) throw new TypeError("policy.policyRef is required");
1407
+ if (policyVersion === null || policyVersion <= 0) throw new TypeError("policy.policyVersion must be >= 1");
1408
+ const aggregateId = `${sponsorWalletRef}::${policyRef}::${policyVersion}`;
1409
+ const updatedAt = parseIsoOrNull(policy.updatedAt) ?? new Date().toISOString();
1410
+ const normalizedPolicy = {
1411
+ ...policy,
1412
+ tenantId,
1413
+ sponsorWalletRef,
1414
+ policyRef,
1415
+ policyVersion,
1416
+ updatedAt
1417
+ };
1418
+
1419
+ await client.query(
1420
+ `
1421
+ INSERT INTO snapshots (tenant_id, aggregate_type, aggregate_id, seq, at_chain_hash, snapshot_json, updated_at)
1422
+ VALUES ($1, 'x402_wallet_policy', $2, 0, NULL, $3, $4)
1423
+ ON CONFLICT (tenant_id, aggregate_type, aggregate_id) DO UPDATE SET
1424
+ snapshot_json = EXCLUDED.snapshot_json,
1425
+ updated_at = EXCLUDED.updated_at
1426
+ `,
1427
+ [tenantId, aggregateId, JSON.stringify(normalizedPolicy), updatedAt]
1428
+ );
1429
+ }
1430
+
1431
+ async function persistX402ZkVerificationKey(client, { tenantId, verificationKeyId, verificationKey }) {
1432
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1433
+ if (!verificationKey || typeof verificationKey !== "object" || Array.isArray(verificationKey)) {
1434
+ throw new TypeError("verificationKey is required");
1435
+ }
1436
+ const normalizedVerificationKeyId =
1437
+ verificationKeyId && String(verificationKeyId).trim() !== ""
1438
+ ? String(verificationKeyId).trim()
1439
+ : verificationKey.verificationKeyId && String(verificationKey.verificationKeyId).trim() !== ""
1440
+ ? String(verificationKey.verificationKeyId).trim()
1441
+ : null;
1442
+ if (!normalizedVerificationKeyId) throw new TypeError("verificationKeyId is required");
1443
+ const createdAt =
1444
+ parseIsoOrNull(verificationKey.createdAt) ??
1445
+ parseIsoOrNull(verificationKey.updatedAt) ??
1446
+ new Date().toISOString();
1447
+ const normalizedVerificationKey = {
1448
+ ...verificationKey,
1449
+ tenantId,
1450
+ verificationKeyId: normalizedVerificationKeyId,
1451
+ createdAt,
1452
+ updatedAt: createdAt
1453
+ };
1454
+
1455
+ const inserted = await client.query(
1456
+ `
1457
+ INSERT INTO snapshots (tenant_id, aggregate_type, aggregate_id, seq, at_chain_hash, snapshot_json, updated_at)
1458
+ VALUES ($1, 'x402_zk_verification_key', $2, 0, NULL, $3, $4)
1459
+ ON CONFLICT (tenant_id, aggregate_type, aggregate_id) DO NOTHING
1460
+ RETURNING aggregate_id
1461
+ `,
1462
+ [tenantId, normalizedVerificationKeyId, JSON.stringify(normalizedVerificationKey), createdAt]
1463
+ );
1464
+ if (inserted.rows.length > 0) return;
1465
+
1466
+ const existing = await client.query(
1467
+ `
1468
+ SELECT snapshot_json
1469
+ FROM snapshots
1470
+ WHERE tenant_id = $1 AND aggregate_type = 'x402_zk_verification_key' AND aggregate_id = $2
1471
+ LIMIT 1
1472
+ `,
1473
+ [tenantId, normalizedVerificationKeyId]
1474
+ );
1475
+ if (!existing.rows.length) return;
1476
+
1477
+ const existingCanonical = canonicalJsonStringify(existing.rows[0]?.snapshot_json ?? null);
1478
+ const incomingCanonical = canonicalJsonStringify(normalizedVerificationKey);
1479
+ if (existingCanonical !== incomingCanonical) {
1480
+ const err = new Error("x402 zk verification key is immutable and cannot be changed");
1481
+ err.code = "X402_ZK_VERIFICATION_KEY_IMMUTABLE";
1482
+ err.verificationKeyId = normalizedVerificationKeyId;
1483
+ throw err;
1484
+ }
1485
+ }
1486
+
1487
+ async function persistX402ReversalEvent(client, { tenantId, gateId, eventId, event }) {
1488
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1489
+ if (!event || typeof event !== "object" || Array.isArray(event)) {
1490
+ throw new TypeError("event is required");
1491
+ }
1492
+ const normalizedEventId = eventId ? String(eventId) : event.eventId ? String(event.eventId) : event.id ? String(event.id) : null;
1493
+ if (!normalizedEventId) throw new TypeError("eventId is required");
1494
+ const normalizedGateId = gateId ? String(gateId) : event.gateId ? String(event.gateId) : null;
1495
+ if (!normalizedGateId) throw new TypeError("gateId is required");
1496
+ const updatedAt = parseIsoOrNull(event.occurredAt ?? event.createdAt) ?? new Date().toISOString();
1497
+ const normalizedEvent = {
1498
+ ...event,
1499
+ tenantId,
1500
+ gateId: normalizedGateId,
1501
+ eventId: normalizedEventId,
1502
+ occurredAt: parseIsoOrNull(event.occurredAt) ?? updatedAt
1503
+ };
1504
+ await client.query(
1505
+ `
1506
+ INSERT INTO snapshots (tenant_id, aggregate_type, aggregate_id, seq, at_chain_hash, snapshot_json, updated_at)
1507
+ VALUES ($1, 'x402_reversal_event', $2, 0, NULL, $3, $4)
1508
+ ON CONFLICT (tenant_id, aggregate_type, aggregate_id) DO UPDATE SET
1509
+ snapshot_json = EXCLUDED.snapshot_json,
1510
+ updated_at = EXCLUDED.updated_at
1511
+ `,
1512
+ [tenantId, normalizedEventId, JSON.stringify(normalizedEvent), updatedAt]
1513
+ );
1514
+ }
1515
+
1516
+ async function persistX402ReversalNonceUsage(client, { tenantId, sponsorRef, nonce, usage }) {
1517
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1518
+ if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
1519
+ const normalizedSponsorRef =
1520
+ sponsorRef && String(sponsorRef).trim() !== ""
1521
+ ? String(sponsorRef).trim()
1522
+ : usage.sponsorRef && String(usage.sponsorRef).trim() !== ""
1523
+ ? String(usage.sponsorRef).trim()
1524
+ : null;
1525
+ const normalizedNonce =
1526
+ nonce && String(nonce).trim() !== ""
1527
+ ? String(nonce).trim()
1528
+ : usage.nonce && String(usage.nonce).trim() !== ""
1529
+ ? String(usage.nonce).trim()
1530
+ : null;
1531
+ if (!normalizedSponsorRef) throw new TypeError("sponsorRef is required");
1532
+ if (!normalizedNonce) throw new TypeError("nonce is required");
1533
+ const aggregateId = `${normalizedSponsorRef}::${normalizedNonce}`;
1534
+ const updatedAt = parseIsoOrNull(usage.usedAt) ?? new Date().toISOString();
1535
+ const normalizedUsage = {
1536
+ ...usage,
1537
+ tenantId,
1538
+ sponsorRef: normalizedSponsorRef,
1539
+ nonce: normalizedNonce,
1540
+ usedAt: parseIsoOrNull(usage.usedAt) ?? updatedAt
1541
+ };
1542
+ await client.query(
1543
+ `
1544
+ INSERT INTO snapshots (tenant_id, aggregate_type, aggregate_id, seq, at_chain_hash, snapshot_json, updated_at)
1545
+ VALUES ($1, 'x402_reversal_nonce', $2, 0, NULL, $3, $4)
1546
+ ON CONFLICT (tenant_id, aggregate_type, aggregate_id) DO UPDATE SET
1547
+ snapshot_json = EXCLUDED.snapshot_json,
1548
+ updated_at = EXCLUDED.updated_at
1549
+ `,
1550
+ [tenantId, aggregateId, JSON.stringify(normalizedUsage), updatedAt]
1551
+ );
1552
+ }
1553
+
1554
+ async function persistX402ReversalCommandUsage(client, { tenantId, commandId, usage }) {
1555
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1556
+ if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
1557
+ const normalizedCommandId =
1558
+ commandId && String(commandId).trim() !== ""
1559
+ ? String(commandId).trim()
1560
+ : usage.commandId && String(usage.commandId).trim() !== ""
1561
+ ? String(usage.commandId).trim()
1562
+ : null;
1563
+ if (!normalizedCommandId) throw new TypeError("commandId is required");
1564
+ const updatedAt = parseIsoOrNull(usage.usedAt) ?? new Date().toISOString();
1565
+ const normalizedUsage = {
1566
+ ...usage,
1567
+ tenantId,
1568
+ commandId: normalizedCommandId,
1569
+ usedAt: parseIsoOrNull(usage.usedAt) ?? updatedAt
1570
+ };
1571
+ await client.query(
1572
+ `
1573
+ INSERT INTO snapshots (tenant_id, aggregate_type, aggregate_id, seq, at_chain_hash, snapshot_json, updated_at)
1574
+ VALUES ($1, 'x402_reversal_command', $2, 0, NULL, $3, $4)
1575
+ ON CONFLICT (tenant_id, aggregate_type, aggregate_id) DO UPDATE SET
1576
+ snapshot_json = EXCLUDED.snapshot_json,
1577
+ updated_at = EXCLUDED.updated_at
1578
+ `,
1579
+ [tenantId, normalizedCommandId, JSON.stringify(normalizedUsage), updatedAt]
1580
+ );
1581
+ }
1582
+
1152
1583
  async function persistToolCallHold(client, { tenantId, holdHash, hold }) {
1153
1584
  tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
1154
1585
  if (!hold || typeof hold !== "object" || Array.isArray(hold)) {
@@ -1643,6 +2074,15 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
1643
2074
  "AGENT_RUN_EVENTS_APPENDED",
1644
2075
  "AGENT_RUN_SETTLEMENT_UPSERT",
1645
2076
  "ARBITRATION_CASE_UPSERT",
2077
+ "AGREEMENT_DELEGATION_UPSERT",
2078
+ "X402_GATE_UPSERT",
2079
+ "X402_AGENT_LIFECYCLE_UPSERT",
2080
+ "X402_RECEIPT_PUT",
2081
+ "X402_WALLET_POLICY_UPSERT",
2082
+ "X402_ZK_VERIFICATION_KEY_PUT",
2083
+ "X402_REVERSAL_EVENT_APPEND",
2084
+ "X402_REVERSAL_NONCE_PUT",
2085
+ "X402_REVERSAL_COMMAND_PUT",
1646
2086
  "TOOL_CALL_HOLD_UPSERT",
1647
2087
  "SETTLEMENT_ADJUSTMENT_PUT",
1648
2088
  "MARKETPLACE_RFQ_UPSERT",
@@ -2028,6 +2468,45 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
2028
2468
  );
2029
2469
  }
2030
2470
 
2471
+ async function persistReputationEventIndexRow(client, { tenantId, artifactId, artifactHash, artifact }) {
2472
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
2473
+ if (!artifact || typeof artifact !== "object" || Array.isArray(artifact)) return;
2474
+ if (String(artifact.artifactType ?? artifact.schemaVersion ?? "") !== "ReputationEvent.v1") return;
2475
+
2476
+ const subject = artifact.subject && typeof artifact.subject === "object" && !Array.isArray(artifact.subject) ? artifact.subject : null;
2477
+ const sourceRef = artifact.sourceRef && typeof artifact.sourceRef === "object" && !Array.isArray(artifact.sourceRef) ? artifact.sourceRef : null;
2478
+ const agentId = subject?.agentId ? String(subject.agentId).trim() : "";
2479
+ if (!agentId) return;
2480
+
2481
+ const toolIdRaw = subject?.toolId ? String(subject.toolId).trim() : "";
2482
+ const toolId = toolIdRaw === "" ? null : toolIdRaw;
2483
+ const sourceKindRaw = sourceRef?.kind ? String(sourceRef.kind).trim().toLowerCase() : "";
2484
+ const sourceKind = sourceKindRaw === "" ? "unknown" : sourceKindRaw;
2485
+ const sourceHashRaw = sourceRef?.hash ? String(sourceRef.hash).trim().toLowerCase() : "";
2486
+ const sourceHash = sourceHashRaw === "" ? null : sourceHashRaw;
2487
+ const eventKindRaw = artifact?.eventKind ? String(artifact.eventKind).trim().toLowerCase() : "";
2488
+ const eventKind = eventKindRaw === "" ? "unknown" : eventKindRaw;
2489
+ const occurredAtParsed = Date.parse(String(artifact.occurredAt ?? ""));
2490
+ const occurredAt = Number.isFinite(occurredAtParsed) ? new Date(occurredAtParsed).toISOString() : new Date().toISOString();
2491
+
2492
+ await client.query(
2493
+ `
2494
+ INSERT INTO reputation_event_index (
2495
+ tenant_id, artifact_id, artifact_hash, subject_agent_id, subject_tool_id, occurred_at, event_kind, source_kind, source_hash
2496
+ ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
2497
+ ON CONFLICT (tenant_id, artifact_id) DO UPDATE SET
2498
+ artifact_hash = EXCLUDED.artifact_hash,
2499
+ subject_agent_id = EXCLUDED.subject_agent_id,
2500
+ subject_tool_id = EXCLUDED.subject_tool_id,
2501
+ occurred_at = EXCLUDED.occurred_at,
2502
+ event_kind = EXCLUDED.event_kind,
2503
+ source_kind = EXCLUDED.source_kind,
2504
+ source_hash = EXCLUDED.source_hash
2505
+ `,
2506
+ [tenantId, String(artifactId), String(artifactHash), agentId, toolId, occurredAt, eventKind, sourceKind, sourceHash]
2507
+ );
2508
+ }
2509
+
2031
2510
  async function persistArtifactRow(client, { tenantId, artifact }) {
2032
2511
  tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
2033
2512
  if (!artifact || typeof artifact !== "object") throw new TypeError("artifact is required");
@@ -2040,9 +2519,12 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
2040
2519
  const jobId = String(artifact.jobId ?? "");
2041
2520
  const sourceEventId = typeof artifact.sourceEventId === "string" && artifact.sourceEventId.trim() !== "" ? String(artifact.sourceEventId) : "";
2042
2521
 
2043
- // Invariant: for artifacts tied to a specific source event, there must be exactly one artifact per
2522
+ // Invariant: for artifacts tied to a specific *job* source event, there must be exactly one artifact per
2044
2523
  // (jobId + artifactType + sourceEventId). This prevents duplicate settlement-backed certificates.
2045
- if (sourceEventId) {
2524
+ //
2525
+ // Important: many non-job artifacts (month close statements, party statements, payout instructions, etc.) set a
2526
+ // sourceEventId but intentionally do not have a jobId. Do not apply this invariant to those artifacts.
2527
+ if (sourceEventId && jobId) {
2046
2528
  const existingBySource = await client.query(
2047
2529
  "SELECT artifact_id, artifact_hash FROM artifacts WHERE tenant_id = $1 AND job_id = $2 AND artifact_type = $3 AND source_event_id = $4 LIMIT 1",
2048
2530
  [tenantId, jobId, artifactType, sourceEventId]
@@ -2058,80 +2540,89 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
2058
2540
  err.gotArtifactHash = String(artifactHash);
2059
2541
  throw err;
2060
2542
  }
2543
+ await persistReputationEventIndexRow(client, { tenantId, artifactId: currentId, artifactHash: currentHash, artifact });
2061
2544
  return;
2062
2545
  }
2063
2546
  }
2064
2547
 
2065
- const existing = await client.query("SELECT artifact_hash FROM artifacts WHERE tenant_id = $1 AND artifact_id = $2", [tenantId, artifactId]);
2066
- if (existing.rows.length) {
2067
- const current = String(existing.rows[0].artifact_hash);
2068
- if (current !== String(artifactHash)) {
2069
- const err = new Error("artifactId already exists with a different hash");
2070
- err.code = "ARTIFACT_HASH_MISMATCH";
2071
- err.expectedArtifactHash = current;
2072
- err.gotArtifactHash = String(artifactHash);
2073
- throw err;
2074
- }
2075
- return;
2076
- }
2548
+ const existing = await client.query("SELECT artifact_hash FROM artifacts WHERE tenant_id = $1 AND artifact_id = $2", [tenantId, artifactId]);
2549
+ if (existing.rows.length) {
2550
+ const current = String(existing.rows[0].artifact_hash);
2551
+ if (current !== String(artifactHash)) {
2552
+ const err = new Error("artifactId already exists with a different hash");
2553
+ err.code = "ARTIFACT_HASH_MISMATCH";
2554
+ err.expectedArtifactHash = current;
2555
+ err.gotArtifactHash = String(artifactHash);
2556
+ throw err;
2557
+ }
2558
+ await persistReputationEventIndexRow(client, { tenantId, artifactId, artifactHash: current, artifact });
2559
+ return;
2560
+ }
2561
+
2562
+ // Avoid transaction-aborting unique-constraint errors (we run inside explicit transactions).
2563
+ // Handle all unique conflicts by doing nothing and then checking what already exists.
2564
+ const insertRes = await client.query(
2565
+ `
2566
+ INSERT INTO artifacts (tenant_id, artifact_id, artifact_type, job_id, at_chain_hash, source_event_id, artifact_hash, artifact_json)
2567
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8)
2568
+ ON CONFLICT DO NOTHING
2569
+ `,
2570
+ [
2571
+ tenantId,
2572
+ String(artifactId),
2573
+ artifactType,
2574
+ jobId,
2575
+ String(artifact.atChainHash ?? artifact.eventProof?.lastChainHash ?? ""),
2576
+ sourceEventId,
2577
+ String(artifactHash),
2578
+ JSON.stringify(artifact)
2579
+ ]
2580
+ );
2581
+ if (Number(insertRes?.rowCount ?? 0) > 0) {
2582
+ await persistReputationEventIndexRow(client, { tenantId, artifactId, artifactHash, artifact });
2583
+ return;
2584
+ }
2077
2585
 
2078
- try {
2079
- await client.query(
2080
- `
2081
- INSERT INTO artifacts (tenant_id, artifact_id, artifact_type, job_id, at_chain_hash, source_event_id, artifact_hash, artifact_json)
2082
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8)
2083
- `,
2084
- [
2085
- tenantId,
2086
- String(artifactId),
2087
- artifactType,
2088
- jobId,
2089
- String(artifact.atChainHash ?? artifact.eventProof?.lastChainHash ?? ""),
2090
- sourceEventId,
2091
- String(artifactHash),
2092
- JSON.stringify(artifact)
2093
- ]
2094
- );
2095
- } catch (err) {
2096
- // Under concurrency, two workers may attempt to persist the same artifact at the same time. Treat this as
2097
- // idempotent when the existing row's hash matches, and retryable otherwise.
2098
- if (err?.code === "23505") {
2099
- if (sourceEventId) {
2100
- const bySource = await client.query(
2101
- "SELECT artifact_id, artifact_hash FROM artifacts WHERE tenant_id = $1 AND job_id = $2 AND artifact_type = $3 AND source_event_id = $4 LIMIT 1",
2102
- [tenantId, jobId, artifactType, sourceEventId]
2103
- );
2104
- if (bySource.rows.length) {
2105
- const currentId = String(bySource.rows[0].artifact_id);
2106
- const currentHash = String(bySource.rows[0].artifact_hash);
2107
- if (currentHash === String(artifactHash)) return;
2108
- const conflict = new Error("artifact already exists for this job/type/sourceEventId with a different hash");
2109
- conflict.code = "ARTIFACT_SOURCE_EVENT_CONFLICT";
2110
- conflict.existingArtifactId = currentId;
2111
- conflict.existingArtifactHash = currentHash;
2112
- conflict.gotArtifactHash = String(artifactHash);
2113
- throw conflict;
2114
- }
2115
- }
2586
+ // Someone else inserted a conflicting row under a unique constraint. Determine if it's idempotent.
2587
+ const byId = await client.query("SELECT artifact_hash FROM artifacts WHERE tenant_id = $1 AND artifact_id = $2", [tenantId, artifactId]);
2588
+ if (byId.rows.length) {
2589
+ const current = String(byId.rows[0].artifact_hash);
2590
+ if (current === String(artifactHash)) {
2591
+ await persistReputationEventIndexRow(client, { tenantId, artifactId, artifactHash: current, artifact });
2592
+ return;
2593
+ }
2594
+ const mismatch = new Error("artifactId already exists with a different hash");
2595
+ mismatch.code = "ARTIFACT_HASH_MISMATCH";
2596
+ mismatch.expectedArtifactHash = current;
2597
+ mismatch.gotArtifactHash = String(artifactHash);
2598
+ throw mismatch;
2599
+ }
2116
2600
 
2117
- const byId = await client.query("SELECT artifact_hash FROM artifacts WHERE tenant_id = $1 AND artifact_id = $2", [tenantId, artifactId]);
2118
- if (byId.rows.length) {
2119
- const current = String(byId.rows[0].artifact_hash);
2120
- if (current === String(artifactHash)) return;
2121
- const mismatch = new Error("artifactId already exists with a different hash");
2122
- mismatch.code = "ARTIFACT_HASH_MISMATCH";
2123
- mismatch.expectedArtifactHash = current;
2124
- mismatch.gotArtifactHash = String(artifactHash);
2125
- throw mismatch;
2126
- }
2601
+ if (sourceEventId && jobId) {
2602
+ const bySource = await client.query(
2603
+ "SELECT artifact_id, artifact_hash FROM artifacts WHERE tenant_id = $1 AND job_id = $2 AND artifact_type = $3 AND source_event_id = $4 LIMIT 1",
2604
+ [tenantId, jobId, artifactType, sourceEventId]
2605
+ );
2606
+ if (bySource.rows.length) {
2607
+ const currentId = String(bySource.rows[0].artifact_id);
2608
+ const currentHash = String(bySource.rows[0].artifact_hash);
2609
+ if (currentHash === String(artifactHash)) {
2610
+ await persistReputationEventIndexRow(client, { tenantId, artifactId: currentId, artifactHash: currentHash, artifact });
2611
+ return;
2612
+ }
2613
+ const conflict = new Error("artifact already exists for this job/type/sourceEventId with a different hash");
2614
+ conflict.code = "ARTIFACT_SOURCE_EVENT_CONFLICT";
2615
+ conflict.existingArtifactId = currentId;
2616
+ conflict.existingArtifactHash = currentHash;
2617
+ conflict.gotArtifactHash = String(artifactHash);
2618
+ throw conflict;
2619
+ }
2620
+ }
2127
2621
 
2128
- const raced = new Error("artifact insert raced with another transaction");
2129
- raced.code = "ARTIFACT_INSERT_RACE";
2130
- throw raced;
2131
- }
2132
- throw err;
2133
- }
2134
- }
2622
+ const raced = new Error("artifact insert raced with another transaction");
2623
+ raced.code = "ARTIFACT_INSERT_RACE";
2624
+ throw raced;
2625
+ }
2135
2626
 
2136
2627
  async function insertDeliveryRow(client, { tenantId, delivery }) {
2137
2628
  tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
@@ -2415,6 +2906,60 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
2415
2906
  const tenantId = normalizeTenantId(op.tenantId ?? op.arbitrationCase?.tenantId ?? DEFAULT_TENANT_ID);
2416
2907
  await persistArbitrationCase(client, { tenantId, caseId: op.caseId, arbitrationCase: op.arbitrationCase });
2417
2908
  }
2909
+ if (op.kind === "AGREEMENT_DELEGATION_UPSERT") {
2910
+ const tenantId = normalizeTenantId(op.tenantId ?? op.delegation?.tenantId ?? DEFAULT_TENANT_ID);
2911
+ await persistAgreementDelegation(client, { tenantId, delegationId: op.delegationId, delegation: op.delegation });
2912
+ }
2913
+ if (op.kind === "X402_GATE_UPSERT") {
2914
+ const tenantId = normalizeTenantId(op.tenantId ?? op.gate?.tenantId ?? DEFAULT_TENANT_ID);
2915
+ await persistX402Gate(client, { tenantId, gateId: op.gateId, gate: op.gate });
2916
+ }
2917
+ if (op.kind === "X402_AGENT_LIFECYCLE_UPSERT") {
2918
+ const tenantId = normalizeTenantId(op.tenantId ?? op.agentLifecycle?.tenantId ?? DEFAULT_TENANT_ID);
2919
+ await persistX402AgentLifecycle(client, { tenantId, agentId: op.agentId, agentLifecycle: op.agentLifecycle });
2920
+ }
2921
+ if (op.kind === "X402_RECEIPT_PUT") {
2922
+ const tenantId = normalizeTenantId(op.tenantId ?? op.receipt?.tenantId ?? DEFAULT_TENANT_ID);
2923
+ await persistX402Receipt(client, { tenantId, receiptId: op.receiptId, receipt: op.receipt });
2924
+ }
2925
+ if (op.kind === "X402_WALLET_POLICY_UPSERT") {
2926
+ const tenantId = normalizeTenantId(op.tenantId ?? op.policy?.tenantId ?? DEFAULT_TENANT_ID);
2927
+ await persistX402WalletPolicy(client, { tenantId, policy: op.policy });
2928
+ }
2929
+ if (op.kind === "X402_ZK_VERIFICATION_KEY_PUT") {
2930
+ const tenantId = normalizeTenantId(op.tenantId ?? op.verificationKey?.tenantId ?? DEFAULT_TENANT_ID);
2931
+ await persistX402ZkVerificationKey(client, {
2932
+ tenantId,
2933
+ verificationKeyId: op.verificationKeyId,
2934
+ verificationKey: op.verificationKey
2935
+ });
2936
+ }
2937
+ if (op.kind === "X402_REVERSAL_EVENT_APPEND") {
2938
+ const tenantId = normalizeTenantId(op.tenantId ?? op.event?.tenantId ?? DEFAULT_TENANT_ID);
2939
+ await persistX402ReversalEvent(client, {
2940
+ tenantId,
2941
+ gateId: op.gateId,
2942
+ eventId: op.eventId,
2943
+ event: op.event
2944
+ });
2945
+ }
2946
+ if (op.kind === "X402_REVERSAL_NONCE_PUT") {
2947
+ const tenantId = normalizeTenantId(op.tenantId ?? op.usage?.tenantId ?? DEFAULT_TENANT_ID);
2948
+ await persistX402ReversalNonceUsage(client, {
2949
+ tenantId,
2950
+ sponsorRef: op.sponsorRef,
2951
+ nonce: op.nonce,
2952
+ usage: op.usage
2953
+ });
2954
+ }
2955
+ if (op.kind === "X402_REVERSAL_COMMAND_PUT") {
2956
+ const tenantId = normalizeTenantId(op.tenantId ?? op.usage?.tenantId ?? DEFAULT_TENANT_ID);
2957
+ await persistX402ReversalCommandUsage(client, {
2958
+ tenantId,
2959
+ commandId: op.commandId,
2960
+ usage: op.usage
2961
+ });
2962
+ }
2418
2963
  if (op.kind === "TOOL_CALL_HOLD_UPSERT") {
2419
2964
  const tenantId = normalizeTenantId(op.tenantId ?? op.hold?.tenantId ?? DEFAULT_TENANT_ID);
2420
2965
  await persistToolCallHold(client, { tenantId, holdHash: op.holdHash, hold: op.hold });
@@ -2758,6 +3303,88 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
2758
3303
  }
2759
3304
  };
2760
3305
 
3306
+ store.sumWalletPolicySpendCentsForDay = async function sumWalletPolicySpendCentsForDay({
3307
+ tenantId = DEFAULT_TENANT_ID,
3308
+ agentId,
3309
+ dayStartIso,
3310
+ dayEndIso
3311
+ } = {}) {
3312
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
3313
+ assertNonEmptyString(agentId, "agentId");
3314
+ assertNonEmptyString(dayStartIso, "dayStartIso");
3315
+ assertNonEmptyString(dayEndIso, "dayEndIso");
3316
+ const startMs = Date.parse(dayStartIso);
3317
+ const endMs = Date.parse(dayEndIso);
3318
+ if (!Number.isFinite(startMs)) throw new TypeError("dayStartIso must be an ISO date string");
3319
+ if (!Number.isFinite(endMs)) throw new TypeError("dayEndIso must be an ISO date string");
3320
+ if (!(endMs > startMs)) throw new TypeError("dayEndIso must be after dayStartIso");
3321
+
3322
+ let runSum = 0;
3323
+ try {
3324
+ const res = await pool.query(
3325
+ `
3326
+ SELECT COALESCE(SUM(amount_cents), 0)::bigint AS c
3327
+ FROM agent_run_settlements
3328
+ WHERE tenant_id = $1
3329
+ AND payer_agent_id = $2
3330
+ AND locked_at >= $3
3331
+ AND locked_at < $4
3332
+ `,
3333
+ [tenantId, String(agentId), String(dayStartIso), String(dayEndIso)]
3334
+ );
3335
+ const n = Number(res.rows[0]?.c ?? 0);
3336
+ runSum = Number.isSafeInteger(n) && n >= 0 ? n : 0;
3337
+ } catch (err) {
3338
+ if (err?.code !== "42P01") throw err;
3339
+ for (const row of store.agentRunSettlements.values()) {
3340
+ if (!row || typeof row !== "object") continue;
3341
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
3342
+ if (String(row.payerAgentId ?? "") !== String(agentId)) continue;
3343
+ const lockedAt = row.lockedAt ?? null;
3344
+ const lockedMs = typeof lockedAt === "string" ? Date.parse(lockedAt) : NaN;
3345
+ if (!Number.isFinite(lockedMs)) continue;
3346
+ if (lockedMs < startMs || lockedMs >= endMs) continue;
3347
+ const amountCents = Number(row.amountCents ?? 0);
3348
+ if (!Number.isSafeInteger(amountCents) || amountCents <= 0) continue;
3349
+ runSum += amountCents;
3350
+ }
3351
+ }
3352
+
3353
+ let holdSum = 0;
3354
+ try {
3355
+ const res = await pool.query(
3356
+ `
3357
+ SELECT COALESCE(SUM((snapshot_json->>'heldAmountCents')::bigint), 0)::bigint AS c
3358
+ FROM snapshots
3359
+ WHERE tenant_id = $1
3360
+ AND aggregate_type = 'tool_call_hold'
3361
+ AND snapshot_json->>'payerAgentId' = $2
3362
+ AND (snapshot_json->>'createdAt')::timestamptz >= $3::timestamptz
3363
+ AND (snapshot_json->>'createdAt')::timestamptz < $4::timestamptz
3364
+ `,
3365
+ [tenantId, String(agentId), String(dayStartIso), String(dayEndIso)]
3366
+ );
3367
+ const n = Number(res.rows[0]?.c ?? 0);
3368
+ holdSum = Number.isSafeInteger(n) && n >= 0 ? n : 0;
3369
+ } catch (err) {
3370
+ if (err?.code !== "42P01") throw err;
3371
+ for (const row of store.toolCallHolds.values()) {
3372
+ if (!row || typeof row !== "object") continue;
3373
+ if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
3374
+ if (String(row.payerAgentId ?? "") !== String(agentId)) continue;
3375
+ const createdAt = row.createdAt ?? null;
3376
+ const createdMs = typeof createdAt === "string" ? Date.parse(createdAt) : NaN;
3377
+ if (!Number.isFinite(createdMs)) continue;
3378
+ if (createdMs < startMs || createdMs >= endMs) continue;
3379
+ const heldAmountCents = Number(row.heldAmountCents ?? 0);
3380
+ if (!Number.isSafeInteger(heldAmountCents) || heldAmountCents <= 0) continue;
3381
+ holdSum += heldAmountCents;
3382
+ }
3383
+ }
3384
+
3385
+ return runSum + holdSum;
3386
+ };
3387
+
2761
3388
  function arbitrationCaseSnapshotRowToRecord(row) {
2762
3389
  const arbitrationCase = row?.snapshot_json ?? null;
2763
3390
  if (!arbitrationCase || typeof arbitrationCase !== "object" || Array.isArray(arbitrationCase)) return null;
@@ -4734,6 +5361,58 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
4734
5361
  return res.rows.map((r) => r.artifact_json);
4735
5362
  };
4736
5363
 
5364
+ store.listReputationEvents = async function listReputationEvents({
5365
+ tenantId = DEFAULT_TENANT_ID,
5366
+ agentId,
5367
+ toolId = null,
5368
+ occurredAtGte = null,
5369
+ occurredAtLte = null,
5370
+ limit = 1000,
5371
+ offset = 0
5372
+ } = {}) {
5373
+ tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
5374
+ assertNonEmptyString(agentId, "agentId");
5375
+ if (toolId !== null && toolId !== undefined) assertNonEmptyString(toolId, "toolId");
5376
+ if (occurredAtGte !== null && occurredAtGte !== undefined) assertNonEmptyString(occurredAtGte, "occurredAtGte");
5377
+ if (occurredAtLte !== null && occurredAtLte !== undefined) assertNonEmptyString(occurredAtLte, "occurredAtLte");
5378
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
5379
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
5380
+
5381
+ const params = [tenantId, String(agentId)];
5382
+ const where = ["idx.tenant_id = $1", "idx.subject_agent_id = $2"];
5383
+ if (toolId !== null && toolId !== undefined) {
5384
+ params.push(String(toolId));
5385
+ where.push(`idx.subject_tool_id = $${params.length}`);
5386
+ }
5387
+ if (occurredAtGte !== null && occurredAtGte !== undefined) {
5388
+ params.push(String(occurredAtGte));
5389
+ where.push(`idx.occurred_at >= $${params.length}::timestamptz`);
5390
+ }
5391
+ if (occurredAtLte !== null && occurredAtLte !== undefined) {
5392
+ params.push(String(occurredAtLte));
5393
+ where.push(`idx.occurred_at <= $${params.length}::timestamptz`);
5394
+ }
5395
+ const safeLimit = Math.min(5000, limit);
5396
+ params.push(safeLimit);
5397
+ params.push(offset);
5398
+
5399
+ const res = await pool.query(
5400
+ `
5401
+ SELECT a.artifact_json
5402
+ FROM reputation_event_index idx
5403
+ INNER JOIN artifacts a
5404
+ ON a.tenant_id = idx.tenant_id
5405
+ AND a.artifact_id = idx.artifact_id
5406
+ WHERE ${where.join(" AND ")}
5407
+ ORDER BY idx.occurred_at ASC, idx.artifact_id ASC
5408
+ LIMIT $${params.length - 1}
5409
+ OFFSET $${params.length}
5410
+ `,
5411
+ params
5412
+ );
5413
+ return res.rows.map((row) => row.artifact_json);
5414
+ };
5415
+
4737
5416
  store.createDelivery = async function createDelivery({ tenantId = DEFAULT_TENANT_ID, delivery }) {
4738
5417
  tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
4739
5418
  if (!delivery || typeof delivery !== "object") throw new TypeError("delivery is required");
@@ -5596,7 +6275,10 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
5596
6275
  `,
5597
6276
  [tenantId, periodStart, periodEnd]
5598
6277
  );
5599
- const jobIds = settledIdsRes.rows.map((r) => String(r.aggregate_id)).filter((id) => id && id.trim() !== "");
6278
+ const jobIds = settledIdsRes.rows
6279
+ .map((r) => String(r.aggregate_id))
6280
+ .filter((id) => id && id.trim() !== "")
6281
+ .sort((a, b) => a.localeCompare(b));
5600
6282
 
5601
6283
  const jobs = [];
5602
6284
  if (jobIds.length) {
@@ -5604,7 +6286,11 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
5604
6286
  "SELECT aggregate_id, snapshot_json FROM snapshots WHERE tenant_id = $1 AND aggregate_type = 'job' AND aggregate_id = ANY($2::text[])",
5605
6287
  [tenantId, jobIds]
5606
6288
  );
5607
- for (const r of snapRes.rows) {
6289
+ // Determinism: DB may return rows in arbitrary order for ANY($2), but artifacts must be hash-stable.
6290
+ const ordered = snapRes.rows
6291
+ .slice()
6292
+ .sort((a, b) => String(a.aggregate_id ?? "").localeCompare(String(b.aggregate_id ?? "")));
6293
+ for (const r of ordered) {
5608
6294
  jobs.push(r.snapshot_json);
5609
6295
  }
5610
6296
  }
@@ -6395,13 +7081,93 @@ export async function createPgStore({ databaseUrl, schema = "public", dropSchema
6395
7081
  return { processed, worker };
6396
7082
  }
6397
7083
 
7084
+ async function processNoopOutboxTopic({ topic, worker, maxMessages = Number.MAX_SAFE_INTEGER } = {}) {
7085
+ if (typeof topic !== "string" || topic.trim() === "") throw new TypeError("topic is required");
7086
+ if (typeof worker !== "string" || worker.trim() === "") throw new TypeError("worker is required");
7087
+ if (!Number.isSafeInteger(maxMessages) || maxMessages < 0) throw new TypeError("maxMessages must be a non-negative safe integer");
7088
+
7089
+ const processed = [];
7090
+
7091
+ // These topics are informational today (no worker consumes them in pg mode).
7092
+ // Drain them deterministically so ops / hosted-baseline checks don't wedge.
7093
+ while (processed.length < maxMessages) {
7094
+ const claimed = await store.claimOutbox({ topic, maxMessages: Math.min(1000, maxMessages - processed.length), worker });
7095
+ if (!claimed.length) break;
7096
+ const ids = claimed.map((r) => r.id);
7097
+ await store.markOutboxProcessed({ ids, lastError: "ok:noop" });
7098
+ for (const id of ids) processed.push({ id, status: "noop" });
7099
+ }
7100
+
7101
+ return { processed, worker };
7102
+ }
7103
+
6398
7104
  store.processOutbox = async function processOutbox({ maxMessages = 1000 } = {}) {
6399
7105
  const ledger = await processLedgerOutbox({ maxMessages });
6400
7106
  const notifications = await processNotificationsOutbox({ maxMessages });
6401
7107
  const correlations = await processCorrelationsOutbox({ maxMessages });
7108
+ const jobStatusChanged = await processNoopOutboxTopic({ topic: "JOB_STATUS_CHANGED", worker: "job_status_changed_v0", maxMessages });
7109
+ const jobSettled = await processNoopOutboxTopic({ topic: "JOB_SETTLED", worker: "job_settled_v0", maxMessages });
6402
7110
  const monthClose = await processMonthCloseOutbox({ maxMessages });
6403
7111
  const financePack = await processFinancePackOutbox({ maxMessages });
6404
- return { ledger, notifications, correlations, monthClose, financePack };
7112
+ return { ledger, notifications, correlations, jobStatusChanged, jobSettled, monthClose, financePack };
7113
+ };
7114
+
7115
+ // Read-only ops debugging helper (used by /ops/debug/outbox).
7116
+ // Intentionally narrow: surfaces enough to diagnose stuck/DLQ outbox without direct DB access.
7117
+ store.listOutboxDebug = async function listOutboxDebug({
7118
+ topic = null,
7119
+ tenantId = null,
7120
+ includeProcessed = false,
7121
+ state = null,
7122
+ limit = 50
7123
+ } = {}) {
7124
+ const safeLimit = Number.isSafeInteger(Number(limit)) ? Number(limit) : 50;
7125
+ if (safeLimit <= 0 || safeLimit > 500) throw new TypeError("limit must be a safe integer between 1 and 500");
7126
+ const t = typeof topic === "string" && topic.trim() ? topic.trim() : null;
7127
+ const tenant = typeof tenantId === "string" && tenantId.trim() ? normalizeTenantId(tenantId) : null;
7128
+ const normalizedState = typeof state === "string" && state.trim() ? state.trim().toLowerCase() : null;
7129
+ if (
7130
+ normalizedState !== null &&
7131
+ normalizedState !== "pending" &&
7132
+ normalizedState !== "processed" &&
7133
+ normalizedState !== "dlq" &&
7134
+ normalizedState !== "all"
7135
+ ) {
7136
+ throw new TypeError("state must be one of pending|processed|dlq|all");
7137
+ }
7138
+
7139
+ return await withTx({ statementTimeoutMs: workerStatementTimeoutMs }, async (client) => {
7140
+ const params = [];
7141
+ let where = `WHERE 1=1`;
7142
+ if (normalizedState === "pending") {
7143
+ where += ` AND processed_at IS NULL`;
7144
+ } else if (normalizedState === "processed") {
7145
+ where += ` AND processed_at IS NOT NULL AND (last_error IS NULL OR last_error NOT LIKE 'DLQ:%')`;
7146
+ } else if (normalizedState === "dlq") {
7147
+ where += ` AND processed_at IS NOT NULL AND last_error LIKE 'DLQ:%'`;
7148
+ } else if (normalizedState !== "all" && !includeProcessed) {
7149
+ // Backward compatibility when state is omitted.
7150
+ where += ` AND processed_at IS NULL`;
7151
+ }
7152
+ if (t) {
7153
+ params.push(String(t));
7154
+ where += ` AND topic = $${params.length}`;
7155
+ }
7156
+ if (tenant) {
7157
+ params.push(String(tenant));
7158
+ where += ` AND tenant_id = $${params.length}`;
7159
+ }
7160
+ params.push(safeLimit);
7161
+ const sql = `
7162
+ SELECT id, tenant_id, topic, aggregate_type, aggregate_id, attempts, claimed_at, processed_at, last_error, payload_json
7163
+ FROM outbox
7164
+ ${where}
7165
+ ORDER BY id DESC
7166
+ LIMIT $${params.length}
7167
+ `;
7168
+ const res = await client.query(sql, params);
7169
+ return res.rows;
7170
+ });
6405
7171
  };
6406
7172
 
6407
7173
  store.close = async function close() {