@codespar/cli 0.16.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 (486) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/mandate-verify.js +98 -19
  3. package/dist/commands/mandate-verify.js.map +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/mandate-codec.js +31 -20
  7. package/dist/mandate-codec.js.map +1 -1
  8. package/package.json +1 -1
  9. package/templates/bills-agent/README.md +1 -1
  10. package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
  11. package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
  12. package/templates/bills-agent/agents/bills-agent/README.md +6 -5
  13. package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +5 -4
  14. package/templates/bills-agent/packages/agent-core/README.md +14 -1
  15. package/templates/bills-agent/packages/agent-core/package.json +1 -1
  16. package/templates/bills-agent/packages/agent-core/src/agent.ts +39 -1
  17. package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  18. package/templates/bills-agent/packages/agent-core/src/api/client.ts +28 -5
  19. package/templates/bills-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  20. package/templates/bills-agent/packages/agent-core/src/api/rail.ts +62 -26
  21. package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  22. package/templates/bills-agent/packages/agent-core/src/approval.ts +14 -2
  23. package/templates/bills-agent/packages/agent-core/src/bundle.ts +35 -3
  24. package/templates/bills-agent/packages/agent-core/src/channels.ts +49 -3
  25. package/templates/bills-agent/packages/agent-core/src/check.ts +25 -2
  26. package/templates/bills-agent/packages/agent-core/src/engine.ts +235 -46
  27. package/templates/bills-agent/packages/agent-core/src/hash.ts +57 -1
  28. package/templates/bills-agent/packages/agent-core/src/index.ts +2 -0
  29. package/templates/bills-agent/packages/agent-core/src/quote.ts +50 -0
  30. package/templates/bills-agent/packages/agent-core/src/rail.ts +89 -4
  31. package/templates/bills-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  32. package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  33. package/templates/bills-agent/packages/agent-core/src/revocation.ts +1 -1
  34. package/templates/bills-agent/packages/agent-core/src/state-machine.ts +10 -1
  35. package/templates/bills-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  36. package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  37. package/templates/bills-agent/packages/agent-core/src/tools.ts +17 -6
  38. package/templates/bills-agent/packages/agent-core/src/types.ts +54 -1
  39. package/templates/bills-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  40. package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  41. package/templates/bills-agent/packages/agent-core/test/composition.test.ts +204 -0
  42. package/templates/bills-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  43. package/templates/bills-agent/packages/agent-core/test/helpers.ts +4 -1
  44. package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  45. package/templates/bills-agent/packages/agent-core/test/quote.test.ts +198 -0
  46. package/templates/bills-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  47. package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  48. package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +103 -3
  49. package/templates/bills-agent/packages/agent-runtime/README.md +12 -0
  50. package/templates/bills-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  51. package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  52. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  53. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  54. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  55. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  56. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  57. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  58. package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  59. package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  60. package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  61. package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  62. package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  63. package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  64. package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  65. package/templates/bills-agent/packages/agent-runtime/src/index.ts +1 -0
  66. package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +5 -1
  67. package/templates/bills-agent/packages/agent-runtime/src/kit.ts +25 -2
  68. package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  69. package/templates/bills-agent/packages/agent-runtime/src/setup.ts +13 -3
  70. package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +127 -18
  71. package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  72. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  73. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  74. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  75. package/templates/checkout-agent/LICENSE +21 -0
  76. package/templates/checkout-agent/README.md +28 -0
  77. package/templates/checkout-agent/_gitignore +10 -0
  78. package/templates/checkout-agent/agents/checkout-agent/.env.example +5 -0
  79. package/templates/checkout-agent/agents/checkout-agent/AGENTS.md +20 -0
  80. package/templates/checkout-agent/agents/checkout-agent/CLAUDE.md +20 -0
  81. package/templates/checkout-agent/agents/checkout-agent/README.md +94 -0
  82. package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +36 -0
  83. package/templates/checkout-agent/agents/checkout-agent/agent.yaml +30 -0
  84. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +28 -0
  85. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-beatriz.json +11 -0
  86. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina-retomada.json +10 -0
  87. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina.json +11 -0
  88. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +49 -0
  89. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +12 -0
  90. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +11 -0
  91. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +5 -0
  92. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +13 -0
  93. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.transcript.jsonl +4 -0
  94. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.json +26 -0
  95. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
  96. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.json +11 -0
  97. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.transcript.jsonl +5 -0
  98. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.json +12 -0
  99. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +5 -0
  100. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.json +13 -0
  101. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.transcript.jsonl +5 -0
  102. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +15 -0
  103. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +7 -0
  104. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +12 -0
  105. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.transcript.jsonl +5 -0
  106. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.json +13 -0
  107. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +4 -0
  108. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.json +16 -0
  109. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.transcript.jsonl +9 -0
  110. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.json +15 -0
  111. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +8 -0
  112. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/webhook-replay.json +11 -0
  113. package/templates/checkout-agent/agents/checkout-agent/evals/eval.yaml +11 -0
  114. package/templates/checkout-agent/agents/checkout-agent/guardrails.json +25 -0
  115. package/templates/checkout-agent/agents/checkout-agent/mandate.example.json +28 -0
  116. package/templates/checkout-agent/agents/checkout-agent/package.json +33 -0
  117. package/templates/checkout-agent/agents/checkout-agent/runbook.md +19 -0
  118. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.json +42 -0
  119. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +4 -0
  120. package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.json +32 -0
  121. package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.transcript.jsonl +4 -0
  122. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.json +61 -0
  123. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +9 -0
  124. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +61 -0
  125. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +9 -0
  126. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +36 -0
  127. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +6 -0
  128. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.json +23 -0
  129. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +4 -0
  130. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.json +72 -0
  131. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +9 -0
  132. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.json +15 -0
  133. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +6 -0
  134. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.json +23 -0
  135. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +3 -0
  136. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.json +42 -0
  137. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +12 -0
  138. package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.json +14 -0
  139. package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.transcript.jsonl +9 -0
  140. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +34 -0
  141. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +7 -0
  142. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.json +33 -0
  143. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +4 -0
  144. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.json +32 -0
  145. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +4 -0
  146. package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +92 -0
  147. package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +338 -0
  148. package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +179 -0
  149. package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +374 -0
  150. package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +220 -0
  151. package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +297 -0
  152. package/templates/checkout-agent/agents/checkout-agent/test/adversarial.test.ts +117 -0
  153. package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +67 -0
  154. package/templates/checkout-agent/agents/checkout-agent/test/cli.test.ts +169 -0
  155. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +5 -0
  156. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +6 -0
  157. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +2 -0
  158. package/templates/checkout-agent/agents/checkout-agent/test/nfse.test.ts +170 -0
  159. package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +138 -0
  160. package/templates/checkout-agent/agents/checkout-agent/test/scenarios.test.ts +113 -0
  161. package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +171 -0
  162. package/templates/checkout-agent/agents/checkout-agent/tools.json +68 -0
  163. package/templates/checkout-agent/agents/checkout-agent/tsconfig.json +10 -0
  164. package/templates/checkout-agent/package.json +38 -0
  165. package/templates/checkout-agent/packages/agent-core/README.md +34 -0
  166. package/templates/checkout-agent/packages/agent-core/package.json +24 -0
  167. package/templates/checkout-agent/packages/agent-core/src/agent.ts +163 -0
  168. package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +195 -0
  169. package/templates/checkout-agent/packages/agent-core/src/api/client.ts +76 -0
  170. package/templates/checkout-agent/packages/agent-core/src/api/mandate-status.ts +74 -0
  171. package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +153 -0
  172. package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +43 -0
  173. package/templates/checkout-agent/packages/agent-core/src/approval.ts +135 -0
  174. package/templates/checkout-agent/packages/agent-core/src/bundle.ts +191 -0
  175. package/templates/checkout-agent/packages/agent-core/src/channels.ts +186 -0
  176. package/templates/checkout-agent/packages/agent-core/src/check.ts +276 -0
  177. package/templates/checkout-agent/packages/agent-core/src/clock.ts +42 -0
  178. package/templates/checkout-agent/packages/agent-core/src/engine.ts +1074 -0
  179. package/templates/checkout-agent/packages/agent-core/src/escalate.ts +85 -0
  180. package/templates/checkout-agent/packages/agent-core/src/events.ts +40 -0
  181. package/templates/checkout-agent/packages/agent-core/src/guardrails.ts +45 -0
  182. package/templates/checkout-agent/packages/agent-core/src/hash.ts +118 -0
  183. package/templates/checkout-agent/packages/agent-core/src/ids.ts +10 -0
  184. package/templates/checkout-agent/packages/agent-core/src/index.ts +34 -0
  185. package/templates/checkout-agent/packages/agent-core/src/mandate.ts +100 -0
  186. package/templates/checkout-agent/packages/agent-core/src/manifest.ts +76 -0
  187. package/templates/checkout-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
  188. package/templates/checkout-agent/packages/agent-core/src/providers/replay.ts +63 -0
  189. package/templates/checkout-agent/packages/agent-core/src/providers/types.ts +39 -0
  190. package/templates/checkout-agent/packages/agent-core/src/quote.ts +50 -0
  191. package/templates/checkout-agent/packages/agent-core/src/rail.ts +186 -0
  192. package/templates/checkout-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  193. package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +577 -0
  194. package/templates/checkout-agent/packages/agent-core/src/revocation.ts +30 -0
  195. package/templates/checkout-agent/packages/agent-core/src/secrets.ts +30 -0
  196. package/templates/checkout-agent/packages/agent-core/src/state/store.ts +307 -0
  197. package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +157 -0
  198. package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
  199. package/templates/checkout-agent/packages/agent-core/src/stubs/mandate-status.ts +58 -0
  200. package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +209 -0
  201. package/templates/checkout-agent/packages/agent-core/src/tools.ts +66 -0
  202. package/templates/checkout-agent/packages/agent-core/src/types.ts +187 -0
  203. package/templates/checkout-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  204. package/templates/checkout-agent/packages/agent-core/test/approval.test.ts +117 -0
  205. package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  206. package/templates/checkout-agent/packages/agent-core/test/bundle.test.ts +85 -0
  207. package/templates/checkout-agent/packages/agent-core/test/clock.test.ts +25 -0
  208. package/templates/checkout-agent/packages/agent-core/test/composition.test.ts +204 -0
  209. package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +596 -0
  210. package/templates/checkout-agent/packages/agent-core/test/escalate.test.ts +42 -0
  211. package/templates/checkout-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  212. package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  213. package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  214. package/templates/checkout-agent/packages/agent-core/test/helpers.ts +142 -0
  215. package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +229 -0
  216. package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +45 -0
  217. package/templates/checkout-agent/packages/agent-core/test/quote.test.ts +198 -0
  218. package/templates/checkout-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  219. package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  220. package/templates/checkout-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  221. package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +416 -0
  222. package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +28 -0
  223. package/templates/checkout-agent/packages/agent-core/test/state-machine.test.ts +67 -0
  224. package/templates/checkout-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
  225. package/templates/checkout-agent/packages/agent-core/tsconfig.json +10 -0
  226. package/templates/checkout-agent/packages/agent-runtime/README.md +80 -0
  227. package/templates/checkout-agent/packages/agent-runtime/bin.mjs +32 -0
  228. package/templates/checkout-agent/packages/agent-runtime/package.json +23 -0
  229. package/templates/checkout-agent/packages/agent-runtime/src/adversarial.ts +199 -0
  230. package/templates/checkout-agent/packages/agent-runtime/src/agent.ts +68 -0
  231. package/templates/checkout-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  232. package/templates/checkout-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  233. package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  234. package/templates/checkout-agent/packages/agent-runtime/src/channels/types.ts +173 -0
  235. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +459 -0
  236. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +216 -0
  237. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  238. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +350 -0
  239. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +180 -0
  240. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  241. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +120 -0
  242. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +127 -0
  243. package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +98 -0
  244. package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  245. package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  246. package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  247. package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  248. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +367 -0
  249. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +145 -0
  250. package/templates/checkout-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  251. package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  252. package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +49 -0
  253. package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +134 -0
  254. package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +248 -0
  255. package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +330 -0
  256. package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +40 -0
  257. package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  258. package/templates/checkout-agent/packages/agent-runtime/src/index.ts +13 -0
  259. package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +801 -0
  260. package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +229 -0
  261. package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +105 -0
  262. package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +274 -0
  263. package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +239 -0
  264. package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +263 -0
  265. package/templates/checkout-agent/packages/agent-runtime/src/webhook.ts +131 -0
  266. package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  267. package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +329 -0
  268. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +277 -0
  269. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  270. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  271. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +167 -0
  272. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  273. package/templates/checkout-agent/packages/agent-runtime/tsconfig.json +9 -0
  274. package/templates/checkout-agent/scripts/whatsapp-emulator.mjs +84 -0
  275. package/templates/checkout-agent/tsconfig.base.json +16 -0
  276. package/templates/checkout-agent/vitest.config.ts +12 -0
  277. package/templates/collections-agent/README.md +6 -1
  278. package/templates/collections-agent/agents/collections-agent/README.md +12 -8
  279. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +8 -0
  280. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042-retomada.json +10 -0
  281. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +27 -1
  282. package/templates/collections-agent/agents/collections-agent/src/kit.ts +2 -0
  283. package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +12 -2
  284. package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +55 -0
  285. package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +2 -0
  286. package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +12 -6
  287. package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +148 -0
  288. package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +5 -0
  289. package/templates/collections-agent/package.json +1 -0
  290. package/templates/collections-agent/packages/agent-core/README.md +14 -1
  291. package/templates/collections-agent/packages/agent-core/package.json +1 -1
  292. package/templates/collections-agent/packages/agent-core/src/agent.ts +39 -1
  293. package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  294. package/templates/collections-agent/packages/agent-core/src/api/client.ts +28 -5
  295. package/templates/collections-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  296. package/templates/collections-agent/packages/agent-core/src/api/rail.ts +62 -26
  297. package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  298. package/templates/collections-agent/packages/agent-core/src/approval.ts +14 -2
  299. package/templates/collections-agent/packages/agent-core/src/bundle.ts +35 -3
  300. package/templates/collections-agent/packages/agent-core/src/channels.ts +49 -3
  301. package/templates/collections-agent/packages/agent-core/src/check.ts +25 -2
  302. package/templates/collections-agent/packages/agent-core/src/engine.ts +235 -46
  303. package/templates/collections-agent/packages/agent-core/src/hash.ts +57 -1
  304. package/templates/collections-agent/packages/agent-core/src/index.ts +2 -0
  305. package/templates/collections-agent/packages/agent-core/src/quote.ts +50 -0
  306. package/templates/collections-agent/packages/agent-core/src/rail.ts +89 -4
  307. package/templates/collections-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  308. package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  309. package/templates/collections-agent/packages/agent-core/src/revocation.ts +1 -1
  310. package/templates/collections-agent/packages/agent-core/src/state-machine.ts +10 -1
  311. package/templates/collections-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  312. package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  313. package/templates/collections-agent/packages/agent-core/src/tools.ts +17 -6
  314. package/templates/collections-agent/packages/agent-core/src/types.ts +54 -1
  315. package/templates/collections-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  316. package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  317. package/templates/collections-agent/packages/agent-core/test/composition.test.ts +204 -0
  318. package/templates/collections-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  319. package/templates/collections-agent/packages/agent-core/test/helpers.ts +4 -1
  320. package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  321. package/templates/collections-agent/packages/agent-core/test/quote.test.ts +198 -0
  322. package/templates/collections-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  323. package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  324. package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +103 -3
  325. package/templates/collections-agent/packages/agent-runtime/README.md +12 -0
  326. package/templates/collections-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  327. package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  328. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  329. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  330. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  331. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  332. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  333. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  334. package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  335. package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  336. package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  337. package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  338. package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  339. package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  340. package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  341. package/templates/collections-agent/packages/agent-runtime/src/index.ts +1 -0
  342. package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +5 -1
  343. package/templates/collections-agent/packages/agent-runtime/src/kit.ts +25 -2
  344. package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  345. package/templates/collections-agent/packages/agent-runtime/src/setup.ts +13 -3
  346. package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +127 -18
  347. package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  348. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  349. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  350. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  351. package/templates/collections-agent/scripts/whatsapp-emulator.mjs +84 -0
  352. package/templates/hello-agent/README.md +1 -1
  353. package/templates/hello-agent/packages/agent-core/README.md +14 -1
  354. package/templates/hello-agent/packages/agent-core/package.json +1 -1
  355. package/templates/hello-agent/packages/agent-core/src/agent.ts +39 -1
  356. package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  357. package/templates/hello-agent/packages/agent-core/src/api/client.ts +28 -5
  358. package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  359. package/templates/hello-agent/packages/agent-core/src/api/rail.ts +62 -26
  360. package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  361. package/templates/hello-agent/packages/agent-core/src/approval.ts +14 -2
  362. package/templates/hello-agent/packages/agent-core/src/bundle.ts +35 -3
  363. package/templates/hello-agent/packages/agent-core/src/channels.ts +49 -3
  364. package/templates/hello-agent/packages/agent-core/src/check.ts +25 -2
  365. package/templates/hello-agent/packages/agent-core/src/engine.ts +235 -46
  366. package/templates/hello-agent/packages/agent-core/src/hash.ts +57 -1
  367. package/templates/hello-agent/packages/agent-core/src/index.ts +2 -0
  368. package/templates/hello-agent/packages/agent-core/src/quote.ts +50 -0
  369. package/templates/hello-agent/packages/agent-core/src/rail.ts +89 -4
  370. package/templates/hello-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  371. package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  372. package/templates/hello-agent/packages/agent-core/src/revocation.ts +1 -1
  373. package/templates/hello-agent/packages/agent-core/src/state-machine.ts +10 -1
  374. package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  375. package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  376. package/templates/hello-agent/packages/agent-core/src/tools.ts +17 -6
  377. package/templates/hello-agent/packages/agent-core/src/types.ts +54 -1
  378. package/templates/hello-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  379. package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  380. package/templates/hello-agent/packages/agent-core/test/composition.test.ts +204 -0
  381. package/templates/hello-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  382. package/templates/hello-agent/packages/agent-core/test/helpers.ts +4 -1
  383. package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  384. package/templates/hello-agent/packages/agent-core/test/quote.test.ts +198 -0
  385. package/templates/hello-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  386. package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  387. package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +103 -3
  388. package/templates/hello-agent/packages/agent-runtime/README.md +12 -0
  389. package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  390. package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  391. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  392. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  393. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  394. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  395. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  396. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  397. package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  398. package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  399. package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  400. package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  401. package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  402. package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  403. package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  404. package/templates/hello-agent/packages/agent-runtime/src/index.ts +1 -0
  405. package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +5 -1
  406. package/templates/hello-agent/packages/agent-runtime/src/kit.ts +25 -2
  407. package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  408. package/templates/hello-agent/packages/agent-runtime/src/setup.ts +13 -3
  409. package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +127 -18
  410. package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  411. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  412. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  413. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  414. package/templates/kits.lock.json +29 -6
  415. package/templates/supplier-payments-agent/README.md +1 -1
  416. package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +1 -1
  417. package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +1 -1
  418. package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +11 -9
  419. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +1 -1
  420. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +67 -20
  421. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +504 -5
  422. package/templates/supplier-payments-agent/packages/agent-core/README.md +14 -1
  423. package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
  424. package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +39 -1
  425. package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  426. package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +28 -5
  427. package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  428. package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +62 -26
  429. package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  430. package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +14 -2
  431. package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +35 -3
  432. package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +49 -3
  433. package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -2
  434. package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +235 -46
  435. package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +57 -1
  436. package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +2 -0
  437. package/templates/supplier-payments-agent/packages/agent-core/src/quote.ts +50 -0
  438. package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +89 -4
  439. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  440. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  441. package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +1 -1
  442. package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +10 -1
  443. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  444. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  445. package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +17 -6
  446. package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +54 -1
  447. package/templates/supplier-payments-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  448. package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  449. package/templates/supplier-payments-agent/packages/agent-core/test/composition.test.ts +204 -0
  450. package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  451. package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +4 -1
  452. package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  453. package/templates/supplier-payments-agent/packages/agent-core/test/quote.test.ts +198 -0
  454. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  455. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  456. package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +103 -3
  457. package/templates/supplier-payments-agent/packages/agent-runtime/README.md +12 -0
  458. package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  459. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  460. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  461. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  462. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  463. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  464. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  465. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  466. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  467. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  468. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  469. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  470. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  471. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  472. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  473. package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +1 -0
  474. package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +5 -1
  475. package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +25 -2
  476. package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  477. package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +13 -3
  478. package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +127 -18
  479. package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  480. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  481. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  482. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  483. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  484. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  485. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  486. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
@@ -0,0 +1,68 @@
1
+ {
2
+ "meta_tools": [
3
+ {
4
+ "name": "codespar_charge",
5
+ "effect": "charge",
6
+ "description": "The order and its charge. action=create turns the cart into an ORDER for the customer you are talking to: the code prices it from the catalog, checks it against the sales policy (price, discount, margin floor, ticket, service hours) and, depending on the approval mode, asks the attendant or confirms it alone. Nothing is issued by create. action=issue sends the confirmed order's charge: ONE bolepix (cobranca com vencimento, due today: the customer pays by Pix or by boleto) for the cart's total, with an idempotency key, so asking twice returns the same charge; the code presents the QR and the copy-and-paste. If the cart changed after the order was confirmed, issue sends it back to the attendant instead. action=status reads the order. You never pass an amount or a document: the code takes the total from the cart and the customer's name and document from the store's customer book.",
7
+ "input_schema": {
8
+ "type": "object",
9
+ "properties": {
10
+ "action": { "type": "string", "enum": ["create", "issue", "status"], "description": "create (the default) places the order; issue sends its charge; status reads it." },
11
+ "cart_id": { "type": "string", "description": "Optional. The cart (cart-1). Omitted: the conversation's current cart." },
12
+ "customer": { "type": "string", "description": "action=create: the alias of the customer you are talking to, their first name in lowercase (marina). Never a document, a name typed for somebody else, or a Pix key." },
13
+ "payment": { "type": "string", "enum": ["bolepix"], "description": "Optional. The only instrument offered: bolepix." },
14
+ "total_minor": { "type": "integer", "description": "Optional. The total you told the customer. The code recomputes it; if yours differs, the order is refused and you present the cart's total again." },
15
+ "execution_id": { "type": "string", "description": "Optional, issue/status: the order to act on. Omitted: the current cart's order." }
16
+ },
17
+ "required": ["action"]
18
+ }
19
+ }
20
+ ],
21
+ "local_tools": [
22
+ {
23
+ "name": "list_catalog",
24
+ "effect": "read",
25
+ "description": "The store's catalog (sku, title, category, list price, availability, stock where limited) and how it sells: service hours, the instrument, and the discount rule in words. Read-only. Costs, discount ceilings, coupon codes and customers are not returned.",
26
+ "input_schema": { "type": "object", "properties": {} }
27
+ },
28
+ {
29
+ "name": "cart_view",
30
+ "effect": "read",
31
+ "description": "The cart as the code priced it: lines, totals, coupon, validation_issues, cart_hash, and the order it belongs to, if any. Read-only.",
32
+ "input_schema": {
33
+ "type": "object",
34
+ "properties": {
35
+ "cart_id": { "type": "string", "description": "Optional. Omitted: the conversation's current cart." }
36
+ }
37
+ }
38
+ },
39
+ {
40
+ "name": "cart_update",
41
+ "effect": "state",
42
+ "description": "REPLACES the whole cart with the lines you pass and answers the cart re-priced by the code. There is no add or remove: send the complete list every time; an empty list empties the cart. A line is a sku and a quantity, plus a negotiated discount in percent when the customer asked for one; there is no price field anywhere, prices come from the catalog. A coupon is the code the customer gave you; the code checks it against the store's table. Every answer carries validation_issues [{code, field, message}] (sku_unknown, item_unavailable, quantity_invalid, quantity_above_stock, coupon_unknown, coupon_not_applicable, price_not_caller_input): a line or a coupon named there is NOT in the cart. If the cart already became an order that is still open, the order follows the new cart and goes back to the attendant.",
43
+ "input_schema": {
44
+ "type": "object",
45
+ "properties": {
46
+ "cart_id": { "type": "string", "description": "Optional. Omitted: the conversation's current cart, or a new one if none is open." },
47
+ "lines": {
48
+ "type": "array",
49
+ "description": "The complete cart, one entry per sku.",
50
+ "items": {
51
+ "type": "object",
52
+ "properties": {
53
+ "sku": { "type": "string", "description": "A sku from list_catalog." },
54
+ "quantity": { "type": "integer", "minimum": 1 },
55
+ "discount_pct": { "type": "number", "minimum": 0, "maximum": 100, "description": "Optional. A negotiated discount on this line, in percent. The code refuses what the store's policy does not allow." }
56
+ },
57
+ "required": ["sku", "quantity"],
58
+ "additionalProperties": false
59
+ }
60
+ },
61
+ "coupon": { "type": "string", "description": "Optional. The coupon code the customer gave." },
62
+ "order_discount_pct": { "type": "number", "minimum": 0, "maximum": 100, "description": "Optional. A negotiated discount on the whole order, in percent. Does not add to a coupon." }
63
+ },
64
+ "required": ["lines"]
65
+ }
66
+ }
67
+ ]
68
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "."
5
+ },
6
+ "include": [
7
+ "src/**/*.ts",
8
+ "test/**/*.ts"
9
+ ]
10
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "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.",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "workspaces": [
9
+ "packages/agent-core",
10
+ "packages/agent-runtime",
11
+ "agents/checkout-agent"
12
+ ],
13
+ "scripts": {
14
+ "start": "npm start --workspace=agents/checkout-agent --",
15
+ "check": "npm run check --workspaces --if-present --",
16
+ "test": "vitest run --pool=forks --maxWorkers=1",
17
+ "typecheck": "tsc --noEmit -p packages/agent-core/tsconfig.json && tsc --noEmit -p packages/agent-runtime/tsconfig.json && tsc --noEmit -p agents/checkout-agent/tsconfig.json",
18
+ "whatsapp:emulator": "node scripts/whatsapp-emulator.mjs",
19
+ "approve": "npm run approve --workspace=agents/checkout-agent --",
20
+ "deny": "npm run deny --workspace=agents/checkout-agent --",
21
+ "eval": "npm run eval --workspace=agents/checkout-agent --",
22
+ "inspect": "npm run inspect --workspace=agents/checkout-agent --",
23
+ "poll": "npm run poll --workspace=agents/checkout-agent --",
24
+ "reconcile": "npm run reconcile --workspace=agents/checkout-agent --",
25
+ "rerun": "npm run rerun --workspace=agents/checkout-agent --",
26
+ "resume": "npm run resume --workspace=agents/checkout-agent --",
27
+ "webhook": "npm run webhook --workspace=agents/checkout-agent --"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "22.20.4",
31
+ "tsx": "4.20.5",
32
+ "typescript": "5.9.2",
33
+ "vitest": "3.2.4"
34
+ },
35
+ "engines": {
36
+ "node": ">=22.13"
37
+ }
38
+ }
@@ -0,0 +1,34 @@
1
+ # @codespar/agent-core
2
+
3
+ The primitives every CodeSpar starter-kit agent inherits (spec v5.1.1, section 4): the execution state machine, the approval artifact, the `agent.yaml` manifest, `escalate_above`, `actor`, local state in SQLite, the proof bundle, the rail contract and the two providers.
4
+
5
+ This package is the decisions. The RUNNER around them — the terminal channel, `codespar-agent start|consent|approve|deny|resume|rerun|reconcile|poll|webhook|check|eval`, the setup that wires this package to an agent directory, and the scenario and adversarial runners — is [`@codespar/agent-runtime`](../agent-runtime), which depends on this one and is what an agent's `package.json` scripts call (#13). Nothing here knows an agent's name.
6
+
7
+ ## The state machine, and what the types do and do not prove
8
+
9
+ `transition(execution, to, input)` accepts only a `to` that the table in `state-machine.ts` lists for `execution.state`. On a **narrowed** value (`Execution<"drafted">`, `Execution<"executing">`, ...) a transition outside the table is a compile error, and `test/transitions.type-test.ts` keeps that true with `@ts-expect-error` lines that `tsc` refuses to leave unused. An **un-narrowed** `Execution` (the union, which is what a row read back from SQLite is) compiles for any target; the engine narrows with casts at its call sites. So the guard that always holds is the runtime one: `transition()` throws `IllegalTransitionError` on any pair the table does not list, whatever the static type said. The type test proves the table is closed; the runtime check enforces it.
10
+
11
+ ## The one policy, three gates
12
+
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
+
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
+
28
+ ## Reconcile never re-dispatches
29
+
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.
31
+
32
+ ## Stubs
33
+
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.
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@codespar/agent-core",
3
+ "version": "0.1.0",
4
+ "description": "The primitives every CodeSpar starter-kit agent inherits: the execution state machine, the approval artifact, the agent.yaml manifest, escalate_above, actor, local state and providers.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./src/index.ts",
8
+ "types": "./src/index.ts",
9
+ "exports": {
10
+ ".": "./src/index.ts",
11
+ "./providers/anthropic": "./src/providers/anthropic.ts",
12
+ "./providers/replay": "./src/providers/replay.ts"
13
+ },
14
+ "scripts": {
15
+ "typecheck": "tsc --noEmit -p tsconfig.json",
16
+ "test": "vitest run --pool=forks --maxWorkers=1 --root ../.. packages/agent-core"
17
+ },
18
+ "dependencies": {
19
+ "@anthropic-ai/sdk": "0.128.0",
20
+ "@codespar/sdk": "0.16.10",
21
+ "yaml": "2.9.1",
22
+ "zod": "3.25.76"
23
+ }
24
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * The tool-use loop. The provider says what the model wants; this file
3
+ * dispatches only the tools `tools.json` names, records every step in the
4
+ * transcript, and hands payment tools to the engine, which is the only
5
+ * thing that can move an execution past `drafted`.
6
+ */
7
+ import type { ProofBundle } from "./bundle.js";
8
+ import type { ExecutionEngine } from "./engine.js";
9
+ import type { AgentRuntime, ConversationMessage, ToolCall, ToolSpec } from "./providers/types.js";
10
+ import { isReply } from "./providers/types.js";
11
+ import type { Execution } from "./state-machine.js";
12
+ import type { ToolsFile } from "./tools.js";
13
+ import { allowedToolNames, toolSpecs } from "./tools.js";
14
+
15
+ export interface ToolContext {
16
+ engine: ExecutionEngine;
17
+ /** Fires when a payment tool created an execution; the channel decides what to do with it (ask, or run). */
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[];
55
+ }
56
+
57
+ export type ToolHandler = (input: Record<string, unknown>, ctx: ToolContext) => Promise<unknown>;
58
+
59
+ export interface AgentLoopOptions {
60
+ runtime: AgentRuntime;
61
+ tools: ToolsFile;
62
+ handlers: Record<string, ToolHandler>;
63
+ system: string;
64
+ bundle: ProofBundle;
65
+ engine: ExecutionEngine;
66
+ onExecution: ToolContext["onExecution"];
67
+ onBatch?: ToolContext["onBatch"] | undefined;
68
+ /** A hard stop on tool round-trips per turn, so a looping model cannot run forever. */
69
+ maxSteps?: number;
70
+ clock?: () => Date;
71
+ }
72
+
73
+ export interface TurnResult {
74
+ reply: string;
75
+ tool_calls: Array<{ name: string; refused: boolean }>;
76
+ executions: Execution[];
77
+ }
78
+
79
+ export class AgentLoop {
80
+ private readonly messages: ConversationMessage[] = [];
81
+ private readonly allowed: Set<string>;
82
+ private readonly specs: ToolSpec[];
83
+ private readonly clock: () => Date;
84
+
85
+ constructor(private readonly options: AgentLoopOptions) {
86
+ this.allowed = allowedToolNames(options.tools);
87
+ this.specs = toolSpecs(options.tools);
88
+ this.clock = options.clock ?? (() => new Date());
89
+ }
90
+
91
+ get history(): readonly ConversationMessage[] {
92
+ return this.messages;
93
+ }
94
+
95
+ async turn(userText: string): Promise<TurnResult> {
96
+ const { runtime, bundle } = this.options;
97
+ const maxSteps = this.options.maxSteps ?? 8;
98
+ const calls: TurnResult["tool_calls"] = [];
99
+ const executions: Execution[] = [];
100
+
101
+ this.messages.push({ role: "user", content: userText });
102
+ bundle.transcript({ at: this.now(), kind: "user", text: userText });
103
+
104
+ for (let step = 0; step < maxSteps; step += 1) {
105
+ const out = await runtime.step({ system: this.options.system, messages: this.messages }, this.specs);
106
+ if (isReply(out)) {
107
+ this.messages.push({ role: "assistant", content: out.text });
108
+ bundle.transcript({ at: this.now(), kind: "assistant_step", reply: out.text });
109
+ bundle.transcript({ at: this.now(), kind: "assistant", text: out.text });
110
+ return { reply: out.text, tool_calls: calls, executions };
111
+ }
112
+
113
+ this.messages.push({ role: "assistant", content: "", tool_calls: out });
114
+ bundle.transcript({ at: this.now(), kind: "assistant_step", tool_calls: out });
115
+
116
+ for (const call of out) {
117
+ const result = await this.dispatch(call, executions);
118
+ calls.push({ name: call.name, refused: result.refused });
119
+ this.messages.push({ role: "tool", tool_call_id: call.id, name: call.name, content: JSON.stringify(result.content), ...(result.refused ? { is_error: true } : {}) });
120
+ bundle.transcript({ at: this.now(), kind: "tool_result", tool_call_id: call.id, name: call.name, refused: result.refused, content: result.content });
121
+ }
122
+ }
123
+
124
+ const text = "I stopped: too many tool steps in one turn. Nothing was paid beyond what the trail shows.";
125
+ this.messages.push({ role: "assistant", content: text });
126
+ bundle.transcript({ at: this.now(), kind: "assistant", text, truncated: true });
127
+ return { reply: text, tool_calls: calls, executions };
128
+ }
129
+
130
+ private async dispatch(call: ToolCall, executions: Execution[]): Promise<{ refused: boolean; content: unknown }> {
131
+ const { bundle, handlers, engine, onExecution, onBatch } = this.options;
132
+ bundle.transcript({ at: this.now(), kind: "tool_call", tool_call_id: call.id, name: call.name, input: call.input });
133
+
134
+ if (!this.allowed.has(call.name)) {
135
+ // Section 15, closed tools: a tool outside tools.json is refused, and the refusal is in the trail.
136
+ bundle.event({ at: this.now(), type: "tool.refused", tool: call.name, reason: "tool_not_allowed", actor: engine.agentActor });
137
+ return { refused: true, content: { error: "tool_not_allowed", message: `${call.name} is not in this agent's tools.json` } };
138
+ }
139
+ const handler = handlers[call.name];
140
+ if (!handler) return { refused: true, content: { error: "tool_not_implemented", message: `${call.name} has no handler` } };
141
+
142
+ try {
143
+ const content = await handler(call.input, {
144
+ engine,
145
+ onExecution: async (execution) => {
146
+ const settledOrNot = await onExecution(execution);
147
+ executions.push(settledOrNot);
148
+ return settledOrNot;
149
+ },
150
+ ...(onBatch ? { onBatch } : {}),
151
+ });
152
+ return { refused: false, content };
153
+ } catch (err) {
154
+ const message = err instanceof Error ? err.message : String(err);
155
+ bundle.event({ at: this.now(), type: "tool.error", tool: call.name, message, actor: engine.agentActor });
156
+ return { refused: true, content: { error: "tool_failed", message } };
157
+ }
158
+ }
159
+
160
+ private now(): string {
161
+ return this.clock().toISOString();
162
+ }
163
+ }
@@ -0,0 +1,195 @@
1
+ /**
2
+ * The CodeSpar receivable rail: the merchant COLLECTS. `pay()` issues a
3
+ * cobranca com vencimento through `POST /v1/charges` (the REST form of the
4
+ * `codespar_charge` meta-tool, action=create): `method: "boleto"` plus a
5
+ * `due_date` is what makes it ONE receivable the payer settles either by
6
+ * boleto or by Pix; `idempotency_key` is REQUIRED on that create and is our
7
+ * attempt id, so a retry returns the same charge instead of a second
8
+ * receivable the same debtor could pay twice. An immediate Pix
9
+ * (`method: "pix"`) is deliberately not used: the API does not replay its
10
+ * create on a key and does not read it back, so it cannot close a loop.
11
+ *
12
+ * `lookup()` is `GET /v1/charges/{id}`, which accepts our idempotency key as
13
+ * the id; the kit polls it to close the cycle when no webhook can reach a
14
+ * terminal. Branching is on `local_status` and the normalized `status`, never
15
+ * on prose. Amounts cross the wire in MAJOR units (`amount: 1080.5`), which
16
+ * is what `coerceMetaChargeArgs` reads; the answer's `amount_minor` is
17
+ * compared with what was asked and a mismatch is a failure, not a charge.
18
+ *
19
+ * There is no receipt object for a receivable; the paid charge as the API
20
+ * reports it is what the bundle keeps, marked `kind: "charge"` and unsealed.
21
+ */
22
+ import type { ApiClient, ApiOperation, ApiSuccess } from "@codespar/sdk";
23
+ import type { PaymentRail, RailLookup, RailOutcome, RailPayment, RailReceipt } from "../rail.js";
24
+ import type { Actor, ChargeInstrument } from "../types.js";
25
+ import { describeApiError, isUncertain, type ApiErrorCode } from "./client.js";
26
+
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;
55
+ }
56
+
57
+ export function instrumentOf(view: ChargeView): ChargeInstrument {
58
+ return {
59
+ payable: view.payable,
60
+ status: view.status,
61
+ pix_copy_paste: view.pix_copy_paste,
62
+ boleto_bank_line: view.boleto_bank_line,
63
+ boleto_bar_code: view.boleto_bar_code,
64
+ due_date: view.due_date,
65
+ };
66
+ }
67
+
68
+ /** What a charge view means for the attempt. The row's own status wins over the issuer's normalized one where they name a terminal state. */
69
+ export function outcomeOf(view: ChargeView): RailOutcome | { status: "in_flight" } {
70
+ if (view.issuance_unconfirmed || !view.id) return { status: "in_flight" };
71
+ const id = view.id;
72
+ if (view.local_status === "settled" || view.status === "CONFIRMED" || view.settlement === "confirmed") {
73
+ return { status: "settled", transaction_id: id, receipt_id: id, money_moved: false, sandbox: true, raw: view };
74
+ }
75
+ if (view.local_status === "expired" || view.status === "EXPIRED") {
76
+ return { status: "failed", code: "charge_expired", message: `charge ${id} reached its due date unpaid`, raw: view };
77
+ }
78
+ if (view.status === "CANCELLED" || view.local_status === "cancelled") {
79
+ return { status: "failed", code: "charge_cancelled", message: `charge ${id} was withdrawn`, raw: view };
80
+ }
81
+ return { status: "accepted", transaction_id: id, instrument: instrumentOf(view), sandbox: true, raw: view };
82
+ }
83
+
84
+ export class CodeSparChargeRail implements PaymentRail {
85
+ readonly name = "codespar-charge" as const;
86
+
87
+ constructor(private readonly api: ApiClient) {}
88
+
89
+ async pay(payment: RailPayment): Promise<RailOutcome> {
90
+ if (!payment.due_date) return { status: "failed", code: "due_date_required", message: "a receivable needs a due_date; the immediate Pix charge cannot be read back or replayed" };
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" };
92
+ let view: ChargeView;
93
+ try {
94
+ view = await this.api.post("/v1/charges", {
95
+ body: {
96
+ consumer_id: payment.consumer_id,
97
+ amount: payment.amount_minor / 100,
98
+ currency: payment.currency,
99
+ method: "boleto",
100
+ description: payment.description ?? payment.purpose,
101
+ buyer: { name: payment.beneficiary ?? payment.payee, document: payment.payee },
102
+ due_date: payment.due_date,
103
+ idempotency_key: payment.attempt_id,
104
+ },
105
+ });
106
+ } catch (err) {
107
+ const failure = describeApiError(err);
108
+ if (isUncertain(failure) || failure.code === ISSUANCE_UNCONFIRMED) return { status: "uncertain", code: failure.code, message: failure.message };
109
+ return { status: "failed", code: failure.code, message: failure.message };
110
+ }
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" };
112
+ if (view.amount_minor !== payment.amount_minor) {
113
+ // The API understood another amount than the one approved. The receivable exists; withdraw it rather than leave a debt nobody approved.
114
+ const withdrawn = await this.withdraw(view.id);
115
+ return { status: "failed", code: "amount_mismatch", message: `the API issued ${view.amount_minor} for a request of ${payment.amount_minor} (charge ${view.id}); ${withdrawn ? "withdrawn" : "withdrawal refused, an operator must cancel it"}` };
116
+ }
117
+ const outcome = outcomeOf(view);
118
+ // `in_flight` is excluded above (`issuance_unconfirmed` / no id); the create answers a state, never "still running".
119
+ return outcome.status === "in_flight" ? { status: "uncertain", code: ISSUANCE_UNCONFIRMED, message: "the create answered no readable state" } : outcome;
120
+ }
121
+
122
+ /**
123
+ * By the charge id when the create handed one back; by our idempotency key only when it did not (the create's answer was lost).
124
+ * The route's doc says the read accepts the caller's key as the id; on staging (2026-09-23) it answered `charge_not_found` for
125
+ * the key and the charge for the id, so the id comes first and the key is the fallback, not the other way round.
126
+ */
127
+ async lookup(attemptId: string, _payment: RailPayment, transactionId?: string): Promise<RailLookup> {
128
+ const byId = transactionId ? await this.read(transactionId) : { kind: "missing" as const };
129
+ if (byId.kind === "view") return outcomeOf(byId.view);
130
+ if (byId.kind === "in_flight") return { status: "in_flight" };
131
+ if (byId.kind === "failed" || byId.kind === "uncertain") return byId.outcome;
132
+ const byKey = await this.read(attemptId);
133
+ if (byKey.kind === "view") return outcomeOf(byKey.view);
134
+ if (byKey.kind === "in_flight") return { status: "in_flight" };
135
+ if (byKey.kind === "failed" || byKey.kind === "uncertain") return byKey.outcome;
136
+ return undefined;
137
+ }
138
+
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> {
145
+ try {
146
+ const view = await this.api.get("/v1/charges/{chargeId}", { path: { chargeId: chargeRef } });
147
+ return { kind: "view", view };
148
+ } catch (err) {
149
+ const failure = describeApiError(err);
150
+ if (failure.status === 404) return { kind: "missing" };
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
+ }
159
+ return { kind: "uncertain", outcome: { status: "uncertain", code: failure.code, message: failure.message } };
160
+ }
161
+ }
162
+
163
+ async receipt(chargeId: string, actor: Actor): Promise<RailReceipt | undefined> {
164
+ try {
165
+ const view = await this.api.get("/v1/charges/{chargeId}", { path: { chargeId } });
166
+ return {
167
+ receipt_id: chargeId,
168
+ kind: "charge",
169
+ state: view.local_status === "settled" ? "paid" : view.local_status,
170
+ mandate: { id: "n/a" },
171
+ // The read names no buyer, so the payee is not on this record; the execution's item names the debtor.
172
+ payment: { amount_minor: view.amount_minor, payee: null, attempt_id: chargeId, money_moved: false, sandbox: true, at: settledAt(view) ?? new Date().toISOString() },
173
+ chain: null,
174
+ receipt_sig: null,
175
+ receipt_sig_ed25519: null,
176
+ receipt_sig_kid: null,
177
+ actor,
178
+ raw: view,
179
+ };
180
+ } catch (err) {
181
+ const failure = describeApiError(err);
182
+ if (failure.status === 404) return undefined;
183
+ throw err;
184
+ }
185
+ }
186
+
187
+ private async withdraw(chargeId: string): Promise<boolean> {
188
+ try {
189
+ await this.api.post("/v1/charges/{chargeId}/cancel", { path: { chargeId } });
190
+ return true;
191
+ } catch {
192
+ return false;
193
+ }
194
+ }
195
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The one place the kit talks to the CodeSpar API. The key is checked for
3
+ * the `csk_test_` prefix before the client is built, so no live key ever
4
+ * reaches the network from this repository.
5
+ */
6
+ import { ApiClient, CodesparApiError, TimeoutError, type ApiOperation, type ApiResponse } from "@codespar/sdk";
7
+ import { assertTestKey } from "../secrets.js";
8
+
9
+ export const DEFAULT_BASE_URL = "https://api.codespar.dev";
10
+
11
+ export interface CodeSparClientOptions {
12
+ apiKey: string | undefined;
13
+ baseUrl?: string | undefined;
14
+ projectId?: string | undefined;
15
+ timeoutMs?: number;
16
+ }
17
+
18
+ export function createCodeSparClient(options: CodeSparClientOptions): ApiClient {
19
+ const apiKey = assertTestKey(options.apiKey);
20
+ return new ApiClient({
21
+ baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
22
+ apiKey,
23
+ ...(options.projectId ? { projectId: options.projectId } : {}),
24
+ timeout: options.timeoutMs ?? 30_000,
25
+ });
26
+ }
27
+
28
+ export interface ApiFailure {
29
+ status: number;
30
+ code: string;
31
+ message: string;
32
+ body?: unknown;
33
+ }
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
+
58
+ /** Normalise the SDK's two error classes into one readable shape without leaking the body into logs. */
59
+ export function describeApiError(err: unknown): ApiFailure {
60
+ if (err instanceof TimeoutError) return { status: 0, code: "timeout", message: `request timed out after ${err.timeoutMs}ms` };
61
+ if (err instanceof CodesparApiError) {
62
+ const envelope = errorEnvelope(err.body);
63
+ return {
64
+ status: err.status,
65
+ code: err.code ?? envelope.code ?? (err.status === 0 ? "network" : `http_${err.status}`),
66
+ message: envelope.message ?? err.message,
67
+ body: err.body,
68
+ };
69
+ }
70
+ return { status: 0, code: "unknown", message: err instanceof Error ? err.message : String(err) };
71
+ }
72
+
73
+ /** 5xx, timeouts and `psp_dispatch_uncertain` mean "the money may have moved". */
74
+ export function isUncertain(failure: ApiFailure): boolean {
75
+ return failure.status === 0 || failure.status >= 500 || UNCERTAIN_CODES.some((code) => code === failure.code);
76
+ }