@codespar/cli 0.17.0 → 0.18.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 (480) hide show
  1. package/README.md +1 -0
  2. package/package.json +1 -1
  3. package/templates/bills-agent/README.md +1 -1
  4. package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
  5. package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
  6. package/templates/bills-agent/agents/bills-agent/README.md +6 -5
  7. package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +5 -4
  8. package/templates/bills-agent/packages/agent-core/README.md +14 -1
  9. package/templates/bills-agent/packages/agent-core/package.json +1 -1
  10. package/templates/bills-agent/packages/agent-core/src/agent.ts +39 -1
  11. package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  12. package/templates/bills-agent/packages/agent-core/src/api/client.ts +28 -5
  13. package/templates/bills-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  14. package/templates/bills-agent/packages/agent-core/src/api/rail.ts +62 -26
  15. package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  16. package/templates/bills-agent/packages/agent-core/src/approval.ts +14 -2
  17. package/templates/bills-agent/packages/agent-core/src/bundle.ts +35 -3
  18. package/templates/bills-agent/packages/agent-core/src/channels.ts +49 -3
  19. package/templates/bills-agent/packages/agent-core/src/check.ts +25 -2
  20. package/templates/bills-agent/packages/agent-core/src/engine.ts +235 -46
  21. package/templates/bills-agent/packages/agent-core/src/hash.ts +57 -1
  22. package/templates/bills-agent/packages/agent-core/src/index.ts +2 -0
  23. package/templates/bills-agent/packages/agent-core/src/quote.ts +50 -0
  24. package/templates/bills-agent/packages/agent-core/src/rail.ts +89 -4
  25. package/templates/bills-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  26. package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  27. package/templates/bills-agent/packages/agent-core/src/revocation.ts +1 -1
  28. package/templates/bills-agent/packages/agent-core/src/state-machine.ts +10 -1
  29. package/templates/bills-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  30. package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  31. package/templates/bills-agent/packages/agent-core/src/tools.ts +17 -6
  32. package/templates/bills-agent/packages/agent-core/src/types.ts +54 -1
  33. package/templates/bills-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  34. package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  35. package/templates/bills-agent/packages/agent-core/test/composition.test.ts +204 -0
  36. package/templates/bills-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  37. package/templates/bills-agent/packages/agent-core/test/helpers.ts +4 -1
  38. package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  39. package/templates/bills-agent/packages/agent-core/test/quote.test.ts +198 -0
  40. package/templates/bills-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  41. package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  42. package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +103 -3
  43. package/templates/bills-agent/packages/agent-runtime/README.md +12 -0
  44. package/templates/bills-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  45. package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  46. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  47. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  48. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  49. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  50. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  51. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  52. package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  53. package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  54. package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  55. package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  56. package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  57. package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  58. package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  59. package/templates/bills-agent/packages/agent-runtime/src/index.ts +1 -0
  60. package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +5 -1
  61. package/templates/bills-agent/packages/agent-runtime/src/kit.ts +25 -2
  62. package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  63. package/templates/bills-agent/packages/agent-runtime/src/setup.ts +13 -3
  64. package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +127 -18
  65. package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  66. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  67. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  68. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  69. package/templates/checkout-agent/LICENSE +21 -0
  70. package/templates/checkout-agent/README.md +28 -0
  71. package/templates/checkout-agent/_gitignore +10 -0
  72. package/templates/checkout-agent/agents/checkout-agent/.env.example +5 -0
  73. package/templates/checkout-agent/agents/checkout-agent/AGENTS.md +20 -0
  74. package/templates/checkout-agent/agents/checkout-agent/CLAUDE.md +20 -0
  75. package/templates/checkout-agent/agents/checkout-agent/README.md +94 -0
  76. package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +36 -0
  77. package/templates/checkout-agent/agents/checkout-agent/agent.yaml +30 -0
  78. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +28 -0
  79. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-beatriz.json +11 -0
  80. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina-retomada.json +10 -0
  81. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina.json +11 -0
  82. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +49 -0
  83. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +12 -0
  84. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +11 -0
  85. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +5 -0
  86. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +13 -0
  87. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.transcript.jsonl +4 -0
  88. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.json +26 -0
  89. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
  90. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.json +11 -0
  91. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.transcript.jsonl +5 -0
  92. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.json +12 -0
  93. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +5 -0
  94. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.json +13 -0
  95. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.transcript.jsonl +5 -0
  96. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +15 -0
  97. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +7 -0
  98. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +12 -0
  99. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.transcript.jsonl +5 -0
  100. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.json +13 -0
  101. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +4 -0
  102. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.json +16 -0
  103. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.transcript.jsonl +9 -0
  104. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.json +15 -0
  105. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +8 -0
  106. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/webhook-replay.json +11 -0
  107. package/templates/checkout-agent/agents/checkout-agent/evals/eval.yaml +11 -0
  108. package/templates/checkout-agent/agents/checkout-agent/guardrails.json +25 -0
  109. package/templates/checkout-agent/agents/checkout-agent/mandate.example.json +28 -0
  110. package/templates/checkout-agent/agents/checkout-agent/package.json +33 -0
  111. package/templates/checkout-agent/agents/checkout-agent/runbook.md +19 -0
  112. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.json +42 -0
  113. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +4 -0
  114. package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.json +32 -0
  115. package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.transcript.jsonl +4 -0
  116. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.json +61 -0
  117. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +9 -0
  118. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +61 -0
  119. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +9 -0
  120. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +36 -0
  121. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +6 -0
  122. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.json +23 -0
  123. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +4 -0
  124. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.json +72 -0
  125. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +9 -0
  126. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.json +15 -0
  127. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +6 -0
  128. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.json +23 -0
  129. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +3 -0
  130. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.json +42 -0
  131. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +12 -0
  132. package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.json +14 -0
  133. package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.transcript.jsonl +9 -0
  134. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +34 -0
  135. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +7 -0
  136. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.json +33 -0
  137. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +4 -0
  138. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.json +32 -0
  139. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +4 -0
  140. package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +92 -0
  141. package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +338 -0
  142. package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +179 -0
  143. package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +374 -0
  144. package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +220 -0
  145. package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +297 -0
  146. package/templates/checkout-agent/agents/checkout-agent/test/adversarial.test.ts +117 -0
  147. package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +67 -0
  148. package/templates/checkout-agent/agents/checkout-agent/test/cli.test.ts +169 -0
  149. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +5 -0
  150. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +6 -0
  151. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +2 -0
  152. package/templates/checkout-agent/agents/checkout-agent/test/nfse.test.ts +170 -0
  153. package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +138 -0
  154. package/templates/checkout-agent/agents/checkout-agent/test/scenarios.test.ts +113 -0
  155. package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +171 -0
  156. package/templates/checkout-agent/agents/checkout-agent/tools.json +68 -0
  157. package/templates/checkout-agent/agents/checkout-agent/tsconfig.json +10 -0
  158. package/templates/checkout-agent/package.json +38 -0
  159. package/templates/checkout-agent/packages/agent-core/README.md +34 -0
  160. package/templates/checkout-agent/packages/agent-core/package.json +24 -0
  161. package/templates/checkout-agent/packages/agent-core/src/agent.ts +163 -0
  162. package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +195 -0
  163. package/templates/checkout-agent/packages/agent-core/src/api/client.ts +76 -0
  164. package/templates/checkout-agent/packages/agent-core/src/api/mandate-status.ts +74 -0
  165. package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +153 -0
  166. package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +43 -0
  167. package/templates/checkout-agent/packages/agent-core/src/approval.ts +135 -0
  168. package/templates/checkout-agent/packages/agent-core/src/bundle.ts +191 -0
  169. package/templates/checkout-agent/packages/agent-core/src/channels.ts +186 -0
  170. package/templates/checkout-agent/packages/agent-core/src/check.ts +276 -0
  171. package/templates/checkout-agent/packages/agent-core/src/clock.ts +42 -0
  172. package/templates/checkout-agent/packages/agent-core/src/engine.ts +1074 -0
  173. package/templates/checkout-agent/packages/agent-core/src/escalate.ts +85 -0
  174. package/templates/checkout-agent/packages/agent-core/src/events.ts +40 -0
  175. package/templates/checkout-agent/packages/agent-core/src/guardrails.ts +45 -0
  176. package/templates/checkout-agent/packages/agent-core/src/hash.ts +118 -0
  177. package/templates/checkout-agent/packages/agent-core/src/ids.ts +10 -0
  178. package/templates/checkout-agent/packages/agent-core/src/index.ts +34 -0
  179. package/templates/checkout-agent/packages/agent-core/src/mandate.ts +100 -0
  180. package/templates/checkout-agent/packages/agent-core/src/manifest.ts +76 -0
  181. package/templates/checkout-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
  182. package/templates/checkout-agent/packages/agent-core/src/providers/replay.ts +63 -0
  183. package/templates/checkout-agent/packages/agent-core/src/providers/types.ts +39 -0
  184. package/templates/checkout-agent/packages/agent-core/src/quote.ts +50 -0
  185. package/templates/checkout-agent/packages/agent-core/src/rail.ts +186 -0
  186. package/templates/checkout-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  187. package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +577 -0
  188. package/templates/checkout-agent/packages/agent-core/src/revocation.ts +30 -0
  189. package/templates/checkout-agent/packages/agent-core/src/secrets.ts +30 -0
  190. package/templates/checkout-agent/packages/agent-core/src/state/store.ts +307 -0
  191. package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +157 -0
  192. package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
  193. package/templates/checkout-agent/packages/agent-core/src/stubs/mandate-status.ts +58 -0
  194. package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +209 -0
  195. package/templates/checkout-agent/packages/agent-core/src/tools.ts +66 -0
  196. package/templates/checkout-agent/packages/agent-core/src/types.ts +187 -0
  197. package/templates/checkout-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  198. package/templates/checkout-agent/packages/agent-core/test/approval.test.ts +117 -0
  199. package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  200. package/templates/checkout-agent/packages/agent-core/test/bundle.test.ts +85 -0
  201. package/templates/checkout-agent/packages/agent-core/test/clock.test.ts +25 -0
  202. package/templates/checkout-agent/packages/agent-core/test/composition.test.ts +204 -0
  203. package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +596 -0
  204. package/templates/checkout-agent/packages/agent-core/test/escalate.test.ts +42 -0
  205. package/templates/checkout-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  206. package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  207. package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  208. package/templates/checkout-agent/packages/agent-core/test/helpers.ts +142 -0
  209. package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +229 -0
  210. package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +45 -0
  211. package/templates/checkout-agent/packages/agent-core/test/quote.test.ts +198 -0
  212. package/templates/checkout-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  213. package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  214. package/templates/checkout-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  215. package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +416 -0
  216. package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +28 -0
  217. package/templates/checkout-agent/packages/agent-core/test/state-machine.test.ts +67 -0
  218. package/templates/checkout-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
  219. package/templates/checkout-agent/packages/agent-core/tsconfig.json +10 -0
  220. package/templates/checkout-agent/packages/agent-runtime/README.md +80 -0
  221. package/templates/checkout-agent/packages/agent-runtime/bin.mjs +32 -0
  222. package/templates/checkout-agent/packages/agent-runtime/package.json +23 -0
  223. package/templates/checkout-agent/packages/agent-runtime/src/adversarial.ts +199 -0
  224. package/templates/checkout-agent/packages/agent-runtime/src/agent.ts +68 -0
  225. package/templates/checkout-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  226. package/templates/checkout-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  227. package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  228. package/templates/checkout-agent/packages/agent-runtime/src/channels/types.ts +173 -0
  229. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +459 -0
  230. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +216 -0
  231. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  232. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +350 -0
  233. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +180 -0
  234. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  235. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +120 -0
  236. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +127 -0
  237. package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +98 -0
  238. package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  239. package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  240. package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  241. package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  242. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +367 -0
  243. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +145 -0
  244. package/templates/checkout-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  245. package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  246. package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +49 -0
  247. package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +134 -0
  248. package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +248 -0
  249. package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +330 -0
  250. package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +40 -0
  251. package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  252. package/templates/checkout-agent/packages/agent-runtime/src/index.ts +13 -0
  253. package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +801 -0
  254. package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +229 -0
  255. package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +105 -0
  256. package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +274 -0
  257. package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +239 -0
  258. package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +263 -0
  259. package/templates/checkout-agent/packages/agent-runtime/src/webhook.ts +131 -0
  260. package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  261. package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +329 -0
  262. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +277 -0
  263. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  264. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  265. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +167 -0
  266. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  267. package/templates/checkout-agent/packages/agent-runtime/tsconfig.json +9 -0
  268. package/templates/checkout-agent/scripts/whatsapp-emulator.mjs +84 -0
  269. package/templates/checkout-agent/tsconfig.base.json +16 -0
  270. package/templates/checkout-agent/vitest.config.ts +12 -0
  271. package/templates/collections-agent/README.md +6 -1
  272. package/templates/collections-agent/agents/collections-agent/README.md +12 -8
  273. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +8 -0
  274. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042-retomada.json +10 -0
  275. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +27 -1
  276. package/templates/collections-agent/agents/collections-agent/src/kit.ts +2 -0
  277. package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +12 -2
  278. package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +55 -0
  279. package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +2 -0
  280. package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +12 -6
  281. package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +148 -0
  282. package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +5 -0
  283. package/templates/collections-agent/package.json +1 -0
  284. package/templates/collections-agent/packages/agent-core/README.md +14 -1
  285. package/templates/collections-agent/packages/agent-core/package.json +1 -1
  286. package/templates/collections-agent/packages/agent-core/src/agent.ts +39 -1
  287. package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  288. package/templates/collections-agent/packages/agent-core/src/api/client.ts +28 -5
  289. package/templates/collections-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  290. package/templates/collections-agent/packages/agent-core/src/api/rail.ts +62 -26
  291. package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  292. package/templates/collections-agent/packages/agent-core/src/approval.ts +14 -2
  293. package/templates/collections-agent/packages/agent-core/src/bundle.ts +35 -3
  294. package/templates/collections-agent/packages/agent-core/src/channels.ts +49 -3
  295. package/templates/collections-agent/packages/agent-core/src/check.ts +25 -2
  296. package/templates/collections-agent/packages/agent-core/src/engine.ts +235 -46
  297. package/templates/collections-agent/packages/agent-core/src/hash.ts +57 -1
  298. package/templates/collections-agent/packages/agent-core/src/index.ts +2 -0
  299. package/templates/collections-agent/packages/agent-core/src/quote.ts +50 -0
  300. package/templates/collections-agent/packages/agent-core/src/rail.ts +89 -4
  301. package/templates/collections-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  302. package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  303. package/templates/collections-agent/packages/agent-core/src/revocation.ts +1 -1
  304. package/templates/collections-agent/packages/agent-core/src/state-machine.ts +10 -1
  305. package/templates/collections-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  306. package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  307. package/templates/collections-agent/packages/agent-core/src/tools.ts +17 -6
  308. package/templates/collections-agent/packages/agent-core/src/types.ts +54 -1
  309. package/templates/collections-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  310. package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  311. package/templates/collections-agent/packages/agent-core/test/composition.test.ts +204 -0
  312. package/templates/collections-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  313. package/templates/collections-agent/packages/agent-core/test/helpers.ts +4 -1
  314. package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  315. package/templates/collections-agent/packages/agent-core/test/quote.test.ts +198 -0
  316. package/templates/collections-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  317. package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  318. package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +103 -3
  319. package/templates/collections-agent/packages/agent-runtime/README.md +12 -0
  320. package/templates/collections-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  321. package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  322. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  323. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  324. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  325. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  326. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  327. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  328. package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  329. package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  330. package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  331. package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  332. package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  333. package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  334. package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  335. package/templates/collections-agent/packages/agent-runtime/src/index.ts +1 -0
  336. package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +5 -1
  337. package/templates/collections-agent/packages/agent-runtime/src/kit.ts +25 -2
  338. package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  339. package/templates/collections-agent/packages/agent-runtime/src/setup.ts +13 -3
  340. package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +127 -18
  341. package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  342. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  343. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  344. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  345. package/templates/collections-agent/scripts/whatsapp-emulator.mjs +84 -0
  346. package/templates/hello-agent/README.md +1 -1
  347. package/templates/hello-agent/packages/agent-core/README.md +14 -1
  348. package/templates/hello-agent/packages/agent-core/package.json +1 -1
  349. package/templates/hello-agent/packages/agent-core/src/agent.ts +39 -1
  350. package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  351. package/templates/hello-agent/packages/agent-core/src/api/client.ts +28 -5
  352. package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  353. package/templates/hello-agent/packages/agent-core/src/api/rail.ts +62 -26
  354. package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  355. package/templates/hello-agent/packages/agent-core/src/approval.ts +14 -2
  356. package/templates/hello-agent/packages/agent-core/src/bundle.ts +35 -3
  357. package/templates/hello-agent/packages/agent-core/src/channels.ts +49 -3
  358. package/templates/hello-agent/packages/agent-core/src/check.ts +25 -2
  359. package/templates/hello-agent/packages/agent-core/src/engine.ts +235 -46
  360. package/templates/hello-agent/packages/agent-core/src/hash.ts +57 -1
  361. package/templates/hello-agent/packages/agent-core/src/index.ts +2 -0
  362. package/templates/hello-agent/packages/agent-core/src/quote.ts +50 -0
  363. package/templates/hello-agent/packages/agent-core/src/rail.ts +89 -4
  364. package/templates/hello-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  365. package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  366. package/templates/hello-agent/packages/agent-core/src/revocation.ts +1 -1
  367. package/templates/hello-agent/packages/agent-core/src/state-machine.ts +10 -1
  368. package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  369. package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  370. package/templates/hello-agent/packages/agent-core/src/tools.ts +17 -6
  371. package/templates/hello-agent/packages/agent-core/src/types.ts +54 -1
  372. package/templates/hello-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  373. package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  374. package/templates/hello-agent/packages/agent-core/test/composition.test.ts +204 -0
  375. package/templates/hello-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  376. package/templates/hello-agent/packages/agent-core/test/helpers.ts +4 -1
  377. package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  378. package/templates/hello-agent/packages/agent-core/test/quote.test.ts +198 -0
  379. package/templates/hello-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  380. package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  381. package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +103 -3
  382. package/templates/hello-agent/packages/agent-runtime/README.md +12 -0
  383. package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  384. package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  385. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  386. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  387. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  388. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  389. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  390. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  391. package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  392. package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  393. package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  394. package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  395. package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  396. package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  397. package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  398. package/templates/hello-agent/packages/agent-runtime/src/index.ts +1 -0
  399. package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +5 -1
  400. package/templates/hello-agent/packages/agent-runtime/src/kit.ts +25 -2
  401. package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  402. package/templates/hello-agent/packages/agent-runtime/src/setup.ts +13 -3
  403. package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +127 -18
  404. package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  405. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  406. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  407. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  408. package/templates/kits.lock.json +29 -6
  409. package/templates/supplier-payments-agent/README.md +1 -1
  410. package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +1 -1
  411. package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +1 -1
  412. package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +11 -9
  413. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +1 -1
  414. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +67 -20
  415. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +504 -5
  416. package/templates/supplier-payments-agent/packages/agent-core/README.md +14 -1
  417. package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
  418. package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +39 -1
  419. package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  420. package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +28 -5
  421. package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  422. package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +62 -26
  423. package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  424. package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +14 -2
  425. package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +35 -3
  426. package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +49 -3
  427. package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -2
  428. package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +235 -46
  429. package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +57 -1
  430. package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +2 -0
  431. package/templates/supplier-payments-agent/packages/agent-core/src/quote.ts +50 -0
  432. package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +89 -4
  433. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  434. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  435. package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +1 -1
  436. package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +10 -1
  437. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  438. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  439. package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +17 -6
  440. package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +54 -1
  441. package/templates/supplier-payments-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  442. package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  443. package/templates/supplier-payments-agent/packages/agent-core/test/composition.test.ts +204 -0
  444. package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  445. package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +4 -1
  446. package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  447. package/templates/supplier-payments-agent/packages/agent-core/test/quote.test.ts +198 -0
  448. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  449. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  450. package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +103 -3
  451. package/templates/supplier-payments-agent/packages/agent-runtime/README.md +12 -0
  452. package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  453. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  454. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  455. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  456. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  457. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  458. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  459. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  460. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  461. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  462. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  463. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  464. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  465. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  466. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  467. package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +1 -0
  468. package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +5 -1
  469. package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +25 -2
  470. package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  471. package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +13 -3
  472. package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +127 -18
  473. package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  474. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  475. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  476. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  477. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  478. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  479. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  480. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
package/README.md CHANGED
@@ -156,6 +156,7 @@ one-line description per template.
156
156
  | `multi-tenant` | Next.js + OpenAI — one API key, N tenants |
157
157
  <!-- kit-templates:start -->
158
158
  | `bills-agent` | Starter kit — The consumer delegates the month's bills to an agent that pays under a signed mandate: per-payment cap, monthly cap, named payees, expiry. Every payment returns a receipt. |
159
+ | `checkout-agent` | Starter kit — The merchant's agent that sells in the conversation: the customer builds a cart the code prices, the attendant (or the sales policy) confirms the order, one bolepix is issued with an idempotency key, the QR and the copy-and-paste go to the customer, and the order closes on commerce.charge.paid. |
159
160
  | `collections-agent` | Starter kit — The merchant's agent that collects: agrees terms with the payer inside a negotiation envelope, issues one bolepix per instalment with an idempotency key, presents the QR and the copy-and-paste in the conversation, and closes the cycle on commerce.charge.paid or commerce.charge.expired. |
160
161
  | `hello-agent` | Starter kit — The worked example of the codespar-agent-builder skill: a read-only agent that reads the month's bills and cannot pay. Built from the five files, the packs and one kit module. |
161
162
  | `supplier-payments-agent` | Starter kit — A company delegates its suppliers, commissions and payroll to an agent that pays them in batches under one signed mandate. A batch is a loop of executions: one refusal does not stop the others, and re-running it pays nobody twice. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codespar/cli",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "CodeSpar CLI. The agentic OS for money movement in Latin America, from your terminal: authenticate, browse servers, mint mandates, execute tools, manage sessions, stream logs.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  # {{name}}
2
2
 
3
3
  Scaffolded by `codespar init --template bills-agent` from the `bills-agent` starter kit
4
- (https://github.com/codespar/agent-starter-kits at `8f130b7cdc30510c929e6f7ebb07184f763e6ce4`, agent 0.1.0).
4
+ (https://github.com/codespar/agent-starter-kits at `b766575306a71c9135c0d70e23d591e6523dc2f8`, agent 0.1.0).
5
5
 
6
6
  The consumer delegates the month's bills to an agent that pays under a signed mandate: per-payment cap, monthly cap, named payees, expiry. Every payment returns a receipt.
7
7
 
@@ -10,7 +10,7 @@ You are editing a reference agent of the CodeSpar Agent Starter Kits. These rule
10
10
  6. **Sandbox by construction.** Only `csk_test_` keys. Never commit `.env`, `.codespar/` or `runs/`. The pre-commit secret scan and the CI both refuse a key-shaped string.
11
11
  7. **Before you say a task is done:** `npm run check`, `npm run eval` (adversarial suite plus scenarios, on the replay provider, no model needed) and `npm test` at the repository root. All green, or it is not done.
12
12
  8. **Use the CodeSpar MCP and skills** (`@codespar/mcp@0.5.8`, pinned in `agent.yaml`) to learn the real API. When the spec in `docs/spec-v5.1.1.md` and the API diverge, follow the API and log it in `docs/OPEN_QUESTIONS.md`.
13
- 9. **Say which signature you mean.** A receipt sealed since the API added Ed25519 carries a signature anybody can check against the published key set, with no credential — that is what `npm run verify -- <receipt-file>` does. "Verifiable by a third party" is true of THAT and of nothing else here: a receipt sealed before the change carries no Ed25519 signature and never will, and the approval artifact is still HMAC with a local development key. Write the phrase only where `Ed25519` is written too; `npm run check` fails otherwise.
13
+ 9. **Say which signature you mean.** A receipt sealed since the API added Ed25519 carries a signature anybody can check against the published key set, with no credential — that is what `npm run verify -- <receipt-file>` does. "Verifiable by a third party" is true of THAT and of nothing else here: a receipt sealed before the change carries no Ed25519 signature and never will, and the approval artifact is still HMAC with a local development key: what reaches a third party is its hash, sealed into a v4 receipt, which proves what was approved and never who. Write the phrase only where `Ed25519` is written too; `npm run check` fails otherwise.
14
14
  10. **No telemetry.** Nothing in this repository phones home.
15
15
 
16
16
  `AGENTS.md` and `CLAUDE.md` are the same file; the CI fails if they diverge. Edit both or neither.
@@ -10,7 +10,7 @@ You are editing a reference agent of the CodeSpar Agent Starter Kits. These rule
10
10
  6. **Sandbox by construction.** Only `csk_test_` keys. Never commit `.env`, `.codespar/` or `runs/`. The pre-commit secret scan and the CI both refuse a key-shaped string.
11
11
  7. **Before you say a task is done:** `npm run check`, `npm run eval` (adversarial suite plus scenarios, on the replay provider, no model needed) and `npm test` at the repository root. All green, or it is not done.
12
12
  8. **Use the CodeSpar MCP and skills** (`@codespar/mcp@0.5.8`, pinned in `agent.yaml`) to learn the real API. When the spec in `docs/spec-v5.1.1.md` and the API diverge, follow the API and log it in `docs/OPEN_QUESTIONS.md`.
13
- 9. **Say which signature you mean.** A receipt sealed since the API added Ed25519 carries a signature anybody can check against the published key set, with no credential — that is what `npm run verify -- <receipt-file>` does. "Verifiable by a third party" is true of THAT and of nothing else here: a receipt sealed before the change carries no Ed25519 signature and never will, and the approval artifact is still HMAC with a local development key. Write the phrase only where `Ed25519` is written too; `npm run check` fails otherwise.
13
+ 9. **Say which signature you mean.** A receipt sealed since the API added Ed25519 carries a signature anybody can check against the published key set, with no credential — that is what `npm run verify -- <receipt-file>` does. "Verifiable by a third party" is true of THAT and of nothing else here: a receipt sealed before the change carries no Ed25519 signature and never will, and the approval artifact is still HMAC with a local development key: what reaches a third party is its hash, sealed into a v4 receipt, which proves what was approved and never who. Write the phrase only where `Ed25519` is written too; `npm run check` fails otherwise.
14
14
  10. **No telemetry.** Nothing in this repository phones home.
15
15
 
16
16
  `AGENTS.md` and `CLAUDE.md` are the same file; the CI fails if they diverge. Edit both or neither.
@@ -70,7 +70,7 @@ Not in this kit yet: WhatsApp, batch payouts.
70
70
  | `npm run resume` | After a crash: dispatches only what the outbox proves was never sent, reconciles the rest from the rail, expires what went stale. Never pays twice. |
71
71
  | `npm run rerun <run-id>` | Replays a recorded run with no network and checks the state sequence matches. |
72
72
  | `npm run inspect <run-id> [--json] [--html <file>]` | The proof bundle of that run read back as a timeline: who proposed what, who approved it and when (with the `items_hash` and the escalation trigger when one fired), under which version of the mandate, every state transition with its actor, which call went out under which idempotency key, what the rail answered, and which receipts came back. `--json` puts the whole report on stdout and nothing else; `--html` writes one self-contained page that opens from disk with nothing fetched. Payees are masked the way the bundle masks them, and the conversation is reported as counts, not text. |
73
- | `npm run verify -- <receipt-file> [--json] [--keys <file>] [--url <url>]` | Checks a receipt's Ed25519 signature against CodeSpar's published key set. No key and no agent needed: it runs on a receipt file copied to another machine. The exit code is the verdict — 0 verified, 1 tampered, 3 unsigned, 4 unknown key, 5 the key set could not be read, 6 not a receipt. |
73
+ | `npm run verify -- <receipt-file> [--json] [--keys <file>] [--url <url>] [--approval <file>] [--from-api]` | Checks a receipt's Ed25519 signature against CodeSpar's published key set, then recomputes its chain from the API's receipt read under the published recipe, then holds the sealed approval hashes against the approval artifact. The read needs no key and no agent; a run's masked copy proves the signature only, unless `--from-api` reads the receipt with this agent's key (nothing is written). The exit code is the verdict — 0 verified, 1 tampered, 3 unsigned, 4 unknown key, 5 the key set could not be read, 6 not a receipt, 7 signature only (the body was not bound), 8 the body is not the sealed one, 9 the sealed approval is not the artifact's. |
74
74
  | `npm run reconcile` | Compares local state with the rail. Closes an `executing` execution only from a recorded rail outcome; what the rail has not answered yet stays `executing` with an `execution.uncertain` event, for a human. Never dispatches. |
75
75
  | `npm run consent -- --yes` | Runs a new consent for a mandate (test key, partner surface); without `--yes` it asks at the keyboard. The signed envelope is stored in `.codespar/mandate.json`, mode 0600. The first thing to run after `.env`: `npm start -- --input` needs it. |
76
76
 
@@ -91,7 +91,7 @@ transcript.jsonl the conversation and the tool calls
91
91
  approval.json the approval artifacts of the run (section 4.2 of the spec)
92
92
  mandate.snapshot.json the mandate as it was, keys masked
93
93
  events.jsonl every transition and every rail event, each with its actor
94
- receipts/ the receipts the rail returned, each stamped with the actor
94
+ receipts/ the receipts the rail returned, each stamped with the actor; the payee is the one the receipt sealed
95
95
  run.json mode, rail, mandate id and the version it ran under
96
96
  ```
97
97
 
@@ -111,14 +111,15 @@ The receipt copies carry both of the API's seals. `receipt_sig` is the HMAC, whi
111
111
  npm run verify -- runs/<run-id>/receipts/<receipt-id>.json
112
112
  ```
113
113
 
114
- That reads the public key set from `/.well-known/codespar-receipt-keys.json`, picks the key the receipt names, rebuilds `codespar-receipt:v1:<receipt_id>:<chain>` and checks it with stock `node:crypto` — no key, no API key, no CodeSpar call that could be refused. `--keys <file>` uses a saved copy of the key set instead and touches no network at all; `--json` puts the verdict on stdout. `--url` names another deployment's key set: the default is production, and every deployment publishes its own key under the same `kid`, so a sandbox receipt checked against the production keys reads `tampered`. The signature covers the receipt id and the chain, so the masking above does not disturb it, and the file verifies on a machine that has never seen this repository. A receipt sealed before the API had the capability answers `unsigned`, which is not a failure: it has no Ed25519 signature and never will, and its HMAC seal is unaffected.
114
+ That reads the public key set from `/.well-known/codespar-receipt-keys.json`, picks the key the receipt names, rebuilds `codespar-receipt:v1:<receipt_id>:<chain>` and checks it with stock `node:crypto` — no key, no API key, no CodeSpar call that could be refused. `--keys <file>` uses a saved copy of the key set instead and touches no network at all; `--json` puts the verdict on stdout. `--url` names another deployment's key set: the default is production. The signature covers the receipt id and the chain, so the masking above does not disturb it, and the file verifies on a machine that has never seen this repository; on its own the copy answers `signature_only`, because it masks the payee the chain sealed. `npm run verify -- runs/<run-id>/receipts/<receipt-id>.json --from-api` reads the unmasked receipt from the API with this agent's key, recomputes the chain from it, and holds its sealed approval hashes against the artifact the copy names in `approval.json`: `verified` then means this payment was made against the list that artifact approved. Nothing unmasked is written. A receipt sealed before the API had the capability answers `unsigned`, which is not a failure: it has no Ed25519 signature and never will, and its HMAC seal is unaffected. A receipt sealed before chain v4 answers `signature_only`: its chain seals the mandate's own signature, which the verifier does not take. [`docs/OPEN_QUESTIONS.md`](../../docs/OPEN_QUESTIONS.md) §47.
115
115
 
116
116
 
117
117
  ## Limits and stubs
118
118
 
119
119
  - The receipt carries two signatures. The HMAC one, under the consumer secret CodeSpar holds, proves the payment to whoever runs this agent, because verifying it means holding the key that also mints it. The Ed25519 one, sealed by CodeSpar's platform issuer key since the API added it, proves it to anybody with `npm run verify`. A receipt sealed before that carries no Ed25519 signature and never will — there is no backfill, and signing an old receipt with today's key would attest to what the database says now, not to what happened then.
120
- - The approval artifact is signed by HMAC with a **local development key** (`.codespar/approval.key`). This is a stub: the CodeSpar API does not sign approval lists today. It proves what was approved to whoever runs the agent.
121
- - Revocation is checked against the API. With a test key, the core reads `GET /v1/mandates/{id}` before every `executing` and executes on `status: active` only: `paused` → `denied` (`mandate_paused`), `revoked` → `denied` (`mandate_revoked`), `expired` → `expired`, and a read that does not answer (timeout, 5xx, 404, an unreadable body) → `denied` (`mandate_status_unavailable`), never "assume active". `npx -y @codespar/cli@0.14.0 mandate revoke <id>` is the switch. Without a key (the CI, the scenarios, `rerun`) the same check answers from a **local stub** (`packages/agent-core/src/stubs/mandate-status.ts`), which is also where the organization kill switch (`org pauseAll`) lives, since the API does not expose one yet.
120
+ - The receipt names the payee because every spend presents the approved payment as a `quote`; without one the API seals no payee anywhere in the chain. The API only records a quote that disagrees with what settled and pays anyway, so the kit refuses the disagreement before the call, and a receipt that seals a different payee, or none, stops the run with `ReceiptSealMismatchError` instead of a warning. [`docs/OPEN_QUESTIONS.md`](../../docs/OPEN_QUESTIONS.md) §18.
121
+ - The approval artifact is signed by HMAC with a **local development key** (`.codespar/approval.key`). This is a stub: the CodeSpar API does not sign approval lists today, so WHO approved is proved to whoever runs the agent and nobody else. WHAT was approved reaches further: every spend carries the artifact's `items_hash` (and a batch line its `batch_hash`), the API seals them into the receipt's chain (v4), and `npm run verify` holds that sealed link against the artifact. [`docs/OPEN_QUESTIONS.md`](../../docs/OPEN_QUESTIONS.md) §3.
122
+ - Revocation is checked against the API. With a test key, the core reads `GET /v1/mandates/{id}` before every `executing` and executes on `status: active` only: `paused` → `denied` (`mandate_paused`), `revoked` → `denied` (`mandate_revoked`), `expired` → `expired`, and a read that does not answer (timeout, 5xx, 404, an unreadable body) → `denied` (`mandate_status_unavailable`), never "assume active". `npx -y @codespar/cli@0.14.0 mandate revoke <id>` is the switch. The same read carries the organization kill switch (ent#1648): `org_paused: true` → `denied` (`org_paused`) whatever `status` says, and a read without `org_paused` is `mandate_status_unavailable`, never "assume running". If the switch is pressed after the read, the API refuses the spend itself with 403 `org_paused` and the execution closes `failed` (`org_paused`); nothing moved. The kit never resumes an organization: that is a dashboard action by a verified admin. Without a key (the CI, the scenarios, `rerun`) the same check answers from a **local stub** (`packages/agent-core/src/stubs/mandate-status.ts`), where `pauseAll` stands in for the switch.
122
123
  - The `actor` of every call is carried locally on every event, approval and receipt copy. The API has no `actor` field on the wire today; the spend carries `agent_id`, which the mandate binds.
123
124
  - A `consumer_id` with an approved account does not leave the registry. Synthetic onboarding stops at document verification, which is the correct behaviour.
124
125
  - CodeSpar does not host or run this agent. The repository delivers it; whoever runs it, runs it.
@@ -72,7 +72,7 @@ export async function runEmbeddedConsent(options: ConsentOptions): Promise<Manda
72
72
 
73
73
  // The consumer's "browser" is this terminal: the submit is the same call the hosted page makes,
74
74
  // plus the attestation the partner surface requires. Sandbox rails accept a placeholder token.
75
- const submitted = (await api.request("post", "/v1/consents/{token}/submit" as never, {
75
+ const submitted = await api.post("/v1/consents/{token}/submit", {
76
76
  path: { token: init.token },
77
77
  body: {
78
78
  consumer_id: consumerId,
@@ -81,9 +81,10 @@ export async function runEmbeddedConsent(options: ConsentOptions): Promise<Manda
81
81
  display_label: "bills-agent (sandbox)",
82
82
  attestation: { method: "in_person", asserted_at: Math.floor(now().getTime() / 1000), reference: "terminal" },
83
83
  },
84
- } as never)) as { mandate_id: string; mandate: Record<string, unknown>; signature: string };
84
+ });
85
85
 
86
86
  const signed = submitted.mandate;
87
+ const allowlist = signed["merchant_allowlist"];
87
88
  const mandate = MandateSchema.parse({
88
89
  id: submitted.mandate_id,
89
90
  version: 1,
@@ -95,8 +96,8 @@ export async function runEmbeddedConsent(options: ConsentOptions): Promise<Manda
95
96
  per_tx_cap_minor: Number(signed["per_tx_cap_minor"]),
96
97
  ...(signed["periodic_cap"] ? { periodic_cap: signed["periodic_cap"] } : {}),
97
98
  merchant_pin_kind: String(signed["merchant_pin_kind"]),
98
- merchant_allowlist: signed["merchant_allowlist"],
99
- beneficiaries: example.beneficiaries.filter((b) => (signed["merchant_allowlist"] as string[]).includes(b.payee)),
99
+ merchant_allowlist: allowlist,
100
+ beneficiaries: example.beneficiaries.filter((b) => Array.isArray(allowlist) && allowlist.includes(b.payee)),
100
101
  status: "active",
101
102
  expires_at: new Date(Number(signed["expires_at"]) * 1000).toISOString(),
102
103
  signature: submitted.signature,
@@ -12,10 +12,23 @@ This package is the decisions. The RUNNER around them — the terminal channel,
12
12
 
13
13
  `ExecutionEngine.policy()` runs at draft, at a human's approval and again immediately before `executing`, inside the transaction that writes the outbox row. Mandate status, allowlist, per-transaction cap, the window cap (counting settled, in flight AND awaiting executions as reservations), `escalate_above` and the artifact's `items_hash` are all re-checked at the last gate. A person's approval satisfies `escalate_above` and nothing else.
14
14
 
15
+ ## What the artifact binds besides the items
16
+
17
+ `items_hash` covers the execution's items: payee, amount, currency and a receivable's due date. Two optional bindings cover what those fields cannot see, and each is **omitted, never null**, when it does not apply, so an artifact without them signs the same bytes it signed before they existed (`test/composition.test.ts` pins that against an artifact hashed on `main` before `composition` was added):
18
+
19
+ - `batch` — the set a line was approved inside (`batch_hash` over the ordered lines, the line's `index`, the `count`). One execution per line, N lines.
20
+ - `composition` — what ONE amount is made of (`{ ref, composition_hash, line_count }`, with `compositionHash` over the resolved lines). One execution, N lines behind it: a sale is one charge, so a cart of three lines is one item whose amount is the order's total. Two units at 100 and one unit at 200 are the same item and the same `items_hash`; they are not the same `composition_hash`. `compositionHash` uses `itemsHash`'s canonicalisation over `ref`, `quantity`, `unit_amount`, `amount` and `currency`, not `itemsHash` itself, because ten units at a 10% discount and nine at list price have the same line amount.
21
+
22
+ `checkApprovalArtifact` compares both, and the last gate sends a mismatch back to `awaiting_approval` with `items_hash_mismatch`, like a changed list. `ExecutionEngine.restate()` is how an OPEN execution (`awaiting_approval`, `approved`) follows the proposal behind it — a customer who changes the cart after the order was approved — without a transition: items, total, `items_hash` and composition are replaced, the state, the artifact and the idempotency key are kept, and the last gate does the refusing. The payees of a restatement cannot change.
23
+
24
+ ## What of the artifact reaches the receipt
25
+
26
+ Every spend carries `approval: { items_hash, batch_hash? }`, the artifact's own hashes (`spendApprovalOf`), and the API seals them into the receipt's chain as a link of their own (chain v4, ent#1670). Both rails refuse a spend without them before any call. `composition` is not sent: the API takes the two hashes and nothing else, and only charges carry a composition today. `receipt-chain.ts` recomputes a chain from the receipt read under the `chain_recipe` the key document publishes (RFC 8785 implemented locally, `node:crypto` only), and `verifyReceiptRead` holds the signature, the body and the sealed approval link against the artifact, in that order. It proves WHAT was approved to anybody holding the read; WHO approved stays in the artifact, under the local key. `docs/OPEN_QUESTIONS.md` §3 and §47.
27
+
15
28
  ## Reconcile never re-dispatches
16
29
 
17
30
  An execution left in `executing` is looked up on the rail, attempt by attempt. A recorded settled or failed outcome closes it; `in_flight`, `uncertain` or unknown leaves it in `executing` with `reason: rail_uncertain` for a human. The single exception is an outbox row still `pending` (the state changed, no call was ever made): those attempts go out once, under the same ids.
18
31
 
19
32
  ## Stubs
20
33
 
21
- `stubs/mandate-status.ts` (the section 4.7 status source for runs without a key: revocation, pause and the organization kill switch, over the local state.db) and `stubs/rail.ts` (the sandbox rail) are stand-ins for the CI, the scenarios and `rerun`, marked as such. With a test key the engine reads the mandate status from the API instead (`api/mandate-status.ts`, `GET /v1/mandates/{id}`), fail-closed: anything but `active` refuses `executing`, and a read that does not answer is `mandate_status_unavailable`, never "assume active". The approval artifact is signed with a local development key (`approval.ts`); the API does not sign approval lists.
34
+ `stubs/mandate-status.ts` (the section 4.7 status source for runs without a key: revocation, pause and the organization kill switch, over the local state.db) and `stubs/rail.ts` (the sandbox rail) are stand-ins for the CI, the scenarios and `rerun`, marked as such. With a test key the engine reads the mandate status from the API instead (`api/mandate-status.ts`, `GET /v1/mandates/{id}`), fail-closed: anything but `active` refuses `executing`, `org_paused: true` (the organization kill switch, ent#1648) refuses it whatever `status` says, and a read that does not answer, or answers without `org_paused`, is `mandate_status_unavailable`, never "assume active". The approval artifact is signed with a local development key (`approval.ts`); the API does not sign approval lists.
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@anthropic-ai/sdk": "0.128.0",
20
- "@codespar/sdk": "0.16.6",
20
+ "@codespar/sdk": "0.16.10",
21
21
  "yaml": "2.9.1",
22
22
  "zod": "3.25.76"
23
23
  }
@@ -16,6 +16,42 @@ export interface ToolContext {
16
16
  engine: ExecutionEngine;
17
17
  /** Fires when a payment tool created an execution; the channel decides what to do with it (ask, or run). */
18
18
  onExecution: (execution: Execution) => Promise<Execution>;
19
+ /**
20
+ * Fires ONCE for a batch, before its first line is drafted, when the channel
21
+ * can take one gesture for the whole list. Absent on a channel that decides
22
+ * line by line (the one-shot, the scenario runner), and then nothing changes:
23
+ * each line reaches `onExecution` and is decided there. The gesture never
24
+ * approves anything itself; every line is still drafted, still reaches
25
+ * `onExecution`, and still mints its own artifact.
26
+ */
27
+ onBatch?: (batch: BatchPresentation) => Promise<BatchGesture | undefined>;
28
+ }
29
+
30
+ /** What a person sees when a whole batch is put in front of them: the list, its total, and the hash of the list. */
31
+ export interface BatchPresentation {
32
+ ref: string;
33
+ label: string;
34
+ /** `batchHash` of the ordered lines. The gesture is a decision about THIS hash and no other. */
35
+ batch_hash: string;
36
+ count: number;
37
+ total_minor: number;
38
+ /** The total as the kit writes money. */
39
+ total: string;
40
+ lines: Array<{
41
+ index: number;
42
+ beneficiary: string;
43
+ amount_minor: number;
44
+ amount: string;
45
+ /** A line a previous run already covers is shown, and does not run again whatever the gesture says. */
46
+ status: "open" | "already_settled" | "in_progress" | "attempt_id_conflict";
47
+ }>;
48
+ }
49
+
50
+ /** The decision a person took on a presented batch: which positions they approved and which they vetoed. */
51
+ export interface BatchGesture {
52
+ batch_hash: string;
53
+ approved: number[];
54
+ vetoed: number[];
19
55
  }
20
56
 
21
57
  export type ToolHandler = (input: Record<string, unknown>, ctx: ToolContext) => Promise<unknown>;
@@ -28,6 +64,7 @@ export interface AgentLoopOptions {
28
64
  bundle: ProofBundle;
29
65
  engine: ExecutionEngine;
30
66
  onExecution: ToolContext["onExecution"];
67
+ onBatch?: ToolContext["onBatch"] | undefined;
31
68
  /** A hard stop on tool round-trips per turn, so a looping model cannot run forever. */
32
69
  maxSteps?: number;
33
70
  clock?: () => Date;
@@ -91,7 +128,7 @@ export class AgentLoop {
91
128
  }
92
129
 
93
130
  private async dispatch(call: ToolCall, executions: Execution[]): Promise<{ refused: boolean; content: unknown }> {
94
- const { bundle, handlers, engine, onExecution } = this.options;
131
+ const { bundle, handlers, engine, onExecution, onBatch } = this.options;
95
132
  bundle.transcript({ at: this.now(), kind: "tool_call", tool_call_id: call.id, name: call.name, input: call.input });
96
133
 
97
134
  if (!this.allowed.has(call.name)) {
@@ -110,6 +147,7 @@ export class AgentLoop {
110
147
  executions.push(settledOrNot);
111
148
  return settledOrNot;
112
149
  },
150
+ ...(onBatch ? { onBatch } : {}),
113
151
  });
114
152
  return { refused: false, content };
115
153
  } catch (err) {
@@ -19,30 +19,39 @@
19
19
  * There is no receipt object for a receivable; the paid charge as the API
20
20
  * reports it is what the bundle keeps, marked `kind: "charge"` and unsealed.
21
21
  */
22
- import type { ApiClient } from "@codespar/sdk";
22
+ import type { ApiClient, ApiOperation, ApiSuccess } from "@codespar/sdk";
23
23
  import type { PaymentRail, RailLookup, RailOutcome, RailPayment, RailReceipt } from "../rail.js";
24
24
  import type { Actor, ChargeInstrument } from "../types.js";
25
- import { describeApiError, isUncertain } from "./client.js";
25
+ import { describeApiError, isUncertain, type ApiErrorCode } from "./client.js";
26
26
 
27
- /** The `/v1/charges` answer, as the SDK types it. */
28
- export interface ChargeView {
29
- id: string | null;
30
- status: string;
31
- local_status: string;
32
- status_conflict: boolean;
33
- method: string;
34
- currency: string;
35
- amount: number;
36
- amount_minor: number;
37
- due_date: string | null;
38
- payable: boolean;
39
- boleto_bar_code: string | null;
40
- boleto_bank_line: string | null;
41
- pix_copy_paste: string | null;
42
- credit_correlation_armed: boolean;
43
- payment_in_flight: boolean;
44
- settlement: "confirmed" | "pending" | "unconfirmable" | null;
45
- issuance_unconfirmed: boolean;
27
+ /** The `/v1/charges/{chargeId}` answer, as the SDK's generated OpenAPI types it. The create answers the same shape, and the compiler holds them together. */
28
+ export type ChargeView = ApiSuccess<ApiOperation<"/v1/charges/{chargeId}", "get">>;
29
+
30
+ /**
31
+ * Documented as the read's 409. The create does not document it, and the
32
+ * create's error path below still treats it as uncertain, which is the safe
33
+ * reading of a code the route is not known to send (OPEN_QUESTIONS §8).
34
+ */
35
+ const ISSUANCE_UNCONFIRMED: ApiErrorCode<ApiOperation<"/v1/charges/{chargeId}", "get">> = "issuance_unconfirmed";
36
+
37
+ /** The read's other 409: the reference matched more than one charge. Terminal for that reference; the API answers by the charge id. */
38
+ const REFERENCE_AMBIGUOUS: ApiErrorCode<ApiOperation<"/v1/charges/{chargeId}", "get">> = "charge_reference_ambiguous";
39
+
40
+ type ChargeRead =
41
+ | { kind: "view"; view: ChargeView }
42
+ | { kind: "missing" }
43
+ | { kind: "in_flight" }
44
+ | { kind: "failed"; outcome: Extract<RailOutcome, { status: "failed" }> }
45
+ | { kind: "uncertain"; outcome: Extract<RailOutcome, { status: "uncertain" }> };
46
+
47
+ /**
48
+ * When the charge settled, if the answer says so. `settled_at` is a column of
49
+ * the charge row, but the SDK's type for the read does not carry it, so this
50
+ * is the one field here read outside the type (OPEN_QUESTIONS §8).
51
+ */
52
+ function settledAt(view: ChargeView): string | undefined {
53
+ const value: unknown = (view as Record<string, unknown>)["settled_at"];
54
+ return typeof value === "string" && value !== "" ? value : undefined;
46
55
  }
47
56
 
48
57
  export function instrumentOf(view: ChargeView): ChargeInstrument {
@@ -82,7 +91,7 @@ export class CodeSparChargeRail implements PaymentRail {
82
91
  if (!payment.consumer_id) return { status: "failed", code: "consumer_id_required", message: "a receivable settles into the principal's account; the policy names no consumer_id" };
83
92
  let view: ChargeView;
84
93
  try {
85
- view = (await this.api.post("/v1/charges", {
94
+ view = await this.api.post("/v1/charges", {
86
95
  body: {
87
96
  consumer_id: payment.consumer_id,
88
97
  amount: payment.amount_minor / 100,
@@ -93,13 +102,13 @@ export class CodeSparChargeRail implements PaymentRail {
93
102
  due_date: payment.due_date,
94
103
  idempotency_key: payment.attempt_id,
95
104
  },
96
- })) as ChargeView;
105
+ });
97
106
  } catch (err) {
98
107
  const failure = describeApiError(err);
99
- if (isUncertain(failure) || failure.code === "issuance_unconfirmed") return { status: "uncertain", code: failure.code, message: failure.message };
108
+ if (isUncertain(failure) || failure.code === ISSUANCE_UNCONFIRMED) return { status: "uncertain", code: failure.code, message: failure.message };
100
109
  return { status: "failed", code: failure.code, message: failure.message };
101
110
  }
102
- if (view.issuance_unconfirmed || !view.id) return { status: "uncertain", code: "issuance_unconfirmed", message: "the issuer's answer to the create was lost; the key is reserved and a later read resolves it" };
111
+ if (view.issuance_unconfirmed || !view.id) return { status: "uncertain", code: ISSUANCE_UNCONFIRMED, message: "the issuer's answer to the create was lost; the key is reserved and a later read resolves it" };
103
112
  if (view.amount_minor !== payment.amount_minor) {
104
113
  // The API understood another amount than the one approved. The receivable exists; withdraw it rather than leave a debt nobody approved.
105
114
  const withdrawn = await this.withdraw(view.id);
@@ -107,7 +116,7 @@ export class CodeSparChargeRail implements PaymentRail {
107
116
  }
108
117
  const outcome = outcomeOf(view);
109
118
  // `in_flight` is excluded above (`issuance_unconfirmed` / no id); the create answers a state, never "still running".
110
- return outcome.status === "in_flight" ? { status: "uncertain", code: "issuance_unconfirmed", message: "the create answered no readable state" } : outcome;
119
+ return outcome.status === "in_flight" ? { status: "uncertain", code: ISSUANCE_UNCONFIRMED, message: "the create answered no readable state" } : outcome;
111
120
  }
112
121
 
113
122
  /**
@@ -119,37 +128,48 @@ export class CodeSparChargeRail implements PaymentRail {
119
128
  const byId = transactionId ? await this.read(transactionId) : { kind: "missing" as const };
120
129
  if (byId.kind === "view") return outcomeOf(byId.view);
121
130
  if (byId.kind === "in_flight") return { status: "in_flight" };
122
- if (byId.kind === "uncertain") return byId.outcome;
131
+ if (byId.kind === "failed" || byId.kind === "uncertain") return byId.outcome;
123
132
  const byKey = await this.read(attemptId);
124
133
  if (byKey.kind === "view") return outcomeOf(byKey.view);
125
134
  if (byKey.kind === "in_flight") return { status: "in_flight" };
126
- if (byKey.kind === "uncertain") return byKey.outcome;
135
+ if (byKey.kind === "failed" || byKey.kind === "uncertain") return byKey.outcome;
127
136
  return undefined;
128
137
  }
129
138
 
130
- private async read(chargeRef: string): Promise<{ kind: "view"; view: ChargeView } | { kind: "missing" } | { kind: "in_flight" } | { kind: "uncertain"; outcome: Extract<RailOutcome, { status: "uncertain" }> }> {
139
+ /**
140
+ * A 409 is branched on its code, never on the status alone: only `issuance_unconfirmed` means "still issuing".
141
+ * `charge_reference_ambiguous` is terminal for the reference and fails explicitly instead of being polled forever,
142
+ * and a 409 whose code this kit does not read is a failure too.
143
+ */
144
+ private async read(chargeRef: string): Promise<ChargeRead> {
131
145
  try {
132
- const view = (await this.api.get("/v1/charges/{chargeId}", { path: { chargeId: chargeRef } })) as ChargeView;
146
+ const view = await this.api.get("/v1/charges/{chargeId}", { path: { chargeId: chargeRef } });
133
147
  return { kind: "view", view };
134
148
  } catch (err) {
135
149
  const failure = describeApiError(err);
136
150
  if (failure.status === 404) return { kind: "missing" };
137
- if (failure.status === 409) return { kind: "in_flight" };
151
+ if (failure.status === 409) {
152
+ if (failure.code === ISSUANCE_UNCONFIRMED) return { kind: "in_flight" };
153
+ const message =
154
+ failure.code === REFERENCE_AMBIGUOUS
155
+ ? `the reference ${chargeRef} matches more than one charge, and the API answers for the charge id only; which one is this attempt's is not assumed, so reconcile it by the charge id`
156
+ : `GET /v1/charges/${chargeRef} answered 409 with a code this kit does not read (${failure.code}); it is not read as a charge still issuing`;
157
+ return { kind: "failed", outcome: { status: "failed", code: failure.code, message } };
158
+ }
138
159
  return { kind: "uncertain", outcome: { status: "uncertain", code: failure.code, message: failure.message } };
139
160
  }
140
161
  }
141
162
 
142
163
  async receipt(chargeId: string, actor: Actor): Promise<RailReceipt | undefined> {
143
164
  try {
144
- const view = (await this.api.get("/v1/charges/{chargeId}", { path: { chargeId } })) as ChargeView;
145
- const raw = view as ChargeView & { settled_at?: string };
165
+ const view = await this.api.get("/v1/charges/{chargeId}", { path: { chargeId } });
146
166
  return {
147
167
  receipt_id: chargeId,
148
168
  kind: "charge",
149
169
  state: view.local_status === "settled" ? "paid" : view.local_status,
150
170
  mandate: { id: "n/a" },
151
171
  // The read names no buyer, so the payee is not on this record; the execution's item names the debtor.
152
- payment: { amount_minor: view.amount_minor, payee: null, attempt_id: chargeId, money_moved: false, sandbox: true, at: raw.settled_at ?? new Date().toISOString() },
172
+ payment: { amount_minor: view.amount_minor, payee: null, attempt_id: chargeId, money_moved: false, sandbox: true, at: settledAt(view) ?? new Date().toISOString() },
153
173
  chain: null,
154
174
  receipt_sig: null,
155
175
  receipt_sig_ed25519: null,
@@ -3,7 +3,7 @@
3
3
  * the `csk_test_` prefix before the client is built, so no live key ever
4
4
  * reaches the network from this repository.
5
5
  */
6
- import { ApiClient, CodesparApiError, TimeoutError } from "@codespar/sdk";
6
+ import { ApiClient, CodesparApiError, TimeoutError, type ApiOperation, type ApiResponse } from "@codespar/sdk";
7
7
  import { assertTestKey } from "../secrets.js";
8
8
 
9
9
  export const DEFAULT_BASE_URL = "https://api.codespar.dev";
@@ -32,15 +32,38 @@ export interface ApiFailure {
32
32
  body?: unknown;
33
33
  }
34
34
 
35
+ /**
36
+ * The `error.code` values an operation documents on its non-2xx answers, read
37
+ * from the SDK's generated types. A code the kit branches on is declared
38
+ * against this, so a code the API stops documenting fails `tsc`.
39
+ */
40
+ export type ApiErrorCode<Op> = Extract<ApiResponse<Op>, { ok: false }>["data"] extends infer D ? (D extends { error: { code: infer C } } ? C : never) : never;
41
+
42
+ /** The codes both spend routes document; the rail takes either. */
43
+ export type SpendErrorCode = ApiErrorCode<ApiOperation<"/v1/consumers/mandates/{id}/spend", "post">> & ApiErrorCode<ApiOperation<"/v1/consumer-payments/execute", "post">>;
44
+
45
+ const UNCERTAIN_CODES: readonly SpendErrorCode[] = ["psp_dispatch_uncertain", "psp_attempt_uncertain"];
46
+
47
+ /** The thrown error's body is `unknown` to the SDK, whatever the route documents; this reads `{ error: { code, message } }` off it without a cast. */
48
+ function errorEnvelope(body: unknown): { code?: string; message?: string } {
49
+ if (!body || typeof body !== "object" || !("error" in body)) return {};
50
+ const error = body.error;
51
+ if (!error || typeof error !== "object") return {};
52
+ return {
53
+ ...("code" in error && typeof error.code === "string" ? { code: error.code } : {}),
54
+ ...("message" in error && typeof error.message === "string" ? { message: error.message } : {}),
55
+ };
56
+ }
57
+
35
58
  /** Normalise the SDK's two error classes into one readable shape without leaking the body into logs. */
36
59
  export function describeApiError(err: unknown): ApiFailure {
37
60
  if (err instanceof TimeoutError) return { status: 0, code: "timeout", message: `request timed out after ${err.timeoutMs}ms` };
38
61
  if (err instanceof CodesparApiError) {
39
- const body = err.body as { error?: { code?: string; message?: string } } | undefined;
62
+ const envelope = errorEnvelope(err.body);
40
63
  return {
41
64
  status: err.status,
42
- code: err.code ?? body?.error?.code ?? (err.status === 0 ? "network" : `http_${err.status}`),
43
- message: body?.error?.message ?? err.message,
65
+ code: err.code ?? envelope.code ?? (err.status === 0 ? "network" : `http_${err.status}`),
66
+ message: envelope.message ?? err.message,
44
67
  body: err.body,
45
68
  };
46
69
  }
@@ -49,5 +72,5 @@ export function describeApiError(err: unknown): ApiFailure {
49
72
 
50
73
  /** 5xx, timeouts and `psp_dispatch_uncertain` mean "the money may have moved". */
51
74
  export function isUncertain(failure: ApiFailure): boolean {
52
- return failure.status === 0 || failure.status >= 500 || failure.code === "psp_dispatch_uncertain" || failure.code === "psp_attempt_uncertain";
75
+ return failure.status === 0 || failure.status >= 500 || UNCERTAIN_CODES.some((code) => code === failure.code);
53
76
  }
@@ -6,26 +6,36 @@
6
6
  * the same `csk_test_` client every other call of the kit uses.
7
7
  *
8
8
  * Fail-closed: this source never throws and never assumes. A transport
9
- * failure, a timeout, a 404, a 5xx, an unreadable body or a status outside
10
- * `active | paused | revoked | expired` is reported as `unknown`, and the
11
- * engine executes nothing on `unknown`.
9
+ * failure, a timeout, a 404, a 5xx, an unreadable body, a status outside
10
+ * `active | paused | revoked | expired` or an `org_paused` that is not a
11
+ * boolean is reported as `unknown`, and the engine executes nothing on
12
+ * `unknown`.
12
13
  *
13
- * The organization kill switch (`org pauseAll`) has no API surface, so
14
- * `org_paused` is `false` here: the API cannot say it, and this source does
15
- * not invent it.
14
+ * The organization kill switch (ent#1648) answers on the same read:
15
+ * `org_paused` next to `status`, which stays the mandate's own. A missing
16
+ * `org_paused` is `unknown`, never "not paused": a deployment that cannot
17
+ * say whether the organization stopped every spend has not said it did not.
16
18
  */
17
- import type { ApiClient } from "@codespar/sdk";
19
+ import type { ApiOperation, ApiSuccess, ApiClient } from "@codespar/sdk";
18
20
  import { z } from "zod";
19
21
  import type { MandateStatusReport, MandateStatusSource } from "../revocation.js";
20
22
  import { describeApiError } from "./client.js";
21
23
 
22
- /** The two fields of the 14-field projection the gate reads; the rest is passed through untouched. */
24
+ /** `GET /v1/mandates/{id}` as the SDK's generated OpenAPI types it. */
25
+ type MandateRead = ApiSuccess<ApiOperation<"/v1/mandates/{id}", "get">>;
26
+
27
+ /**
28
+ * The three fields the gate reads; the rest is passed through untouched.
29
+ * Bound to the SDK's type, so a field the API renames or retypes breaks the
30
+ * compile here instead of reading as absent at run time.
31
+ */
23
32
  const MandateReadSchema = z
24
33
  .object({
25
34
  status: z.enum(["active", "paused", "revoked", "expired"]),
26
35
  expires_at: z.string(),
36
+ org_paused: z.boolean(),
27
37
  })
28
- .passthrough();
38
+ .passthrough() satisfies z.ZodType<Pick<MandateRead, "status" | "expires_at" | "org_paused">>;
29
39
 
30
40
  export class ApiMandateStatusSource implements MandateStatusSource {
31
41
  constructor(
@@ -35,7 +45,7 @@ export class ApiMandateStatusSource implements MandateStatusSource {
35
45
 
36
46
  async check(mandateId: string): Promise<MandateStatusReport> {
37
47
  const base = { mandate_id: mandateId, org_paused: false, checked_at: this.clock().toISOString(), source: "api" as const };
38
- let body: unknown;
48
+ let body: MandateRead;
39
49
  try {
40
50
  body = await this.api.get("/v1/mandates/{id}", { path: { id: mandateId } });
41
51
  } catch (err) {
@@ -46,11 +56,17 @@ export class ApiMandateStatusSource implements MandateStatusSource {
46
56
  : `GET /v1/mandates/${mandateId} did not answer (${failure.code}): ${failure.message}`;
47
57
  return { ...base, status: "unknown", detail };
48
58
  }
59
+ // The type says what the API promises; the parse checks that this answer kept the promise.
49
60
  const parsed = MandateReadSchema.safeParse(body);
50
61
  if (!parsed.success) {
51
- const status = body && typeof body === "object" && "status" in body ? String((body as { status: unknown }).status) : "missing";
52
- return { ...base, status: "unknown", detail: `GET /v1/mandates/${mandateId} answered a status this kit does not read (${status})` };
62
+ const unread = parsed.error.issues.map((issue) => issue.path.join(".") || "body");
63
+ const status = body && typeof body === "object" && "status" in body ? String(body.status) : "missing";
64
+ const detail = unread.includes("org_paused")
65
+ ? `GET /v1/mandates/${mandateId} did not say whether the organization is paused (org_paused unreadable)`
66
+ : `GET /v1/mandates/${mandateId} answered a status this kit does not read (${status})`;
67
+ return { ...base, status: "unknown", detail };
53
68
  }
69
+ if (parsed.data.org_paused) return { ...base, org_paused: true, status: parsed.data.status };
54
70
  const expiresAt = new Date(parsed.data.expires_at).getTime();
55
71
  const expired = Number.isFinite(expiresAt) && expiresAt <= this.clock().getTime();
56
72
  return { ...base, status: expired && parsed.data.status === "active" ? "expired" : parsed.data.status };