@codespar/cli 0.17.0 → 0.18.1

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 (482) hide show
  1. package/README.md +1 -0
  2. package/dist/surface.js +53 -1
  3. package/dist/surface.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/bills-agent/README.md +1 -1
  6. package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
  7. package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
  8. package/templates/bills-agent/agents/bills-agent/README.md +6 -5
  9. package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +5 -4
  10. package/templates/bills-agent/packages/agent-core/README.md +14 -1
  11. package/templates/bills-agent/packages/agent-core/package.json +1 -1
  12. package/templates/bills-agent/packages/agent-core/src/agent.ts +39 -1
  13. package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  14. package/templates/bills-agent/packages/agent-core/src/api/client.ts +28 -5
  15. package/templates/bills-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  16. package/templates/bills-agent/packages/agent-core/src/api/rail.ts +62 -26
  17. package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  18. package/templates/bills-agent/packages/agent-core/src/approval.ts +14 -2
  19. package/templates/bills-agent/packages/agent-core/src/bundle.ts +35 -3
  20. package/templates/bills-agent/packages/agent-core/src/channels.ts +49 -3
  21. package/templates/bills-agent/packages/agent-core/src/check.ts +25 -2
  22. package/templates/bills-agent/packages/agent-core/src/engine.ts +235 -46
  23. package/templates/bills-agent/packages/agent-core/src/hash.ts +57 -1
  24. package/templates/bills-agent/packages/agent-core/src/index.ts +2 -0
  25. package/templates/bills-agent/packages/agent-core/src/quote.ts +50 -0
  26. package/templates/bills-agent/packages/agent-core/src/rail.ts +89 -4
  27. package/templates/bills-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  28. package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  29. package/templates/bills-agent/packages/agent-core/src/revocation.ts +1 -1
  30. package/templates/bills-agent/packages/agent-core/src/state-machine.ts +10 -1
  31. package/templates/bills-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  32. package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  33. package/templates/bills-agent/packages/agent-core/src/tools.ts +17 -6
  34. package/templates/bills-agent/packages/agent-core/src/types.ts +54 -1
  35. package/templates/bills-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  36. package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  37. package/templates/bills-agent/packages/agent-core/test/composition.test.ts +204 -0
  38. package/templates/bills-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  39. package/templates/bills-agent/packages/agent-core/test/helpers.ts +4 -1
  40. package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  41. package/templates/bills-agent/packages/agent-core/test/quote.test.ts +198 -0
  42. package/templates/bills-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  43. package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  44. package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +103 -3
  45. package/templates/bills-agent/packages/agent-runtime/README.md +12 -0
  46. package/templates/bills-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  47. package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  48. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  49. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  50. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  51. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  52. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  53. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  54. package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  55. package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  56. package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  57. package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  58. package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  59. package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  60. package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  61. package/templates/bills-agent/packages/agent-runtime/src/index.ts +1 -0
  62. package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +5 -1
  63. package/templates/bills-agent/packages/agent-runtime/src/kit.ts +25 -2
  64. package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  65. package/templates/bills-agent/packages/agent-runtime/src/setup.ts +13 -3
  66. package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +127 -18
  67. package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  68. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  69. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  70. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  71. package/templates/checkout-agent/LICENSE +21 -0
  72. package/templates/checkout-agent/README.md +28 -0
  73. package/templates/checkout-agent/_gitignore +10 -0
  74. package/templates/checkout-agent/agents/checkout-agent/.env.example +5 -0
  75. package/templates/checkout-agent/agents/checkout-agent/AGENTS.md +20 -0
  76. package/templates/checkout-agent/agents/checkout-agent/CLAUDE.md +20 -0
  77. package/templates/checkout-agent/agents/checkout-agent/README.md +94 -0
  78. package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +36 -0
  79. package/templates/checkout-agent/agents/checkout-agent/agent.yaml +30 -0
  80. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +28 -0
  81. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-beatriz.json +11 -0
  82. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina-retomada.json +10 -0
  83. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina.json +11 -0
  84. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +49 -0
  85. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +12 -0
  86. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +11 -0
  87. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +5 -0
  88. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +13 -0
  89. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.transcript.jsonl +4 -0
  90. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.json +26 -0
  91. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
  92. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.json +11 -0
  93. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.transcript.jsonl +5 -0
  94. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.json +12 -0
  95. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +5 -0
  96. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.json +13 -0
  97. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.transcript.jsonl +5 -0
  98. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +15 -0
  99. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +7 -0
  100. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +12 -0
  101. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.transcript.jsonl +5 -0
  102. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.json +13 -0
  103. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +4 -0
  104. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.json +16 -0
  105. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.transcript.jsonl +9 -0
  106. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.json +15 -0
  107. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +8 -0
  108. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/webhook-replay.json +11 -0
  109. package/templates/checkout-agent/agents/checkout-agent/evals/eval.yaml +11 -0
  110. package/templates/checkout-agent/agents/checkout-agent/guardrails.json +25 -0
  111. package/templates/checkout-agent/agents/checkout-agent/mandate.example.json +28 -0
  112. package/templates/checkout-agent/agents/checkout-agent/package.json +33 -0
  113. package/templates/checkout-agent/agents/checkout-agent/runbook.md +19 -0
  114. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.json +42 -0
  115. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +4 -0
  116. package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.json +32 -0
  117. package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.transcript.jsonl +4 -0
  118. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.json +61 -0
  119. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +9 -0
  120. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +61 -0
  121. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +9 -0
  122. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +36 -0
  123. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +6 -0
  124. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.json +23 -0
  125. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +4 -0
  126. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.json +72 -0
  127. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +9 -0
  128. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.json +15 -0
  129. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +6 -0
  130. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.json +23 -0
  131. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +3 -0
  132. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.json +42 -0
  133. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +12 -0
  134. package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.json +14 -0
  135. package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.transcript.jsonl +9 -0
  136. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +34 -0
  137. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +7 -0
  138. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.json +33 -0
  139. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +4 -0
  140. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.json +32 -0
  141. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +4 -0
  142. package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +92 -0
  143. package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +338 -0
  144. package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +179 -0
  145. package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +374 -0
  146. package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +220 -0
  147. package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +297 -0
  148. package/templates/checkout-agent/agents/checkout-agent/test/adversarial.test.ts +117 -0
  149. package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +67 -0
  150. package/templates/checkout-agent/agents/checkout-agent/test/cli.test.ts +169 -0
  151. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +5 -0
  152. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +6 -0
  153. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +2 -0
  154. package/templates/checkout-agent/agents/checkout-agent/test/nfse.test.ts +170 -0
  155. package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +138 -0
  156. package/templates/checkout-agent/agents/checkout-agent/test/scenarios.test.ts +113 -0
  157. package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +171 -0
  158. package/templates/checkout-agent/agents/checkout-agent/tools.json +68 -0
  159. package/templates/checkout-agent/agents/checkout-agent/tsconfig.json +10 -0
  160. package/templates/checkout-agent/package.json +38 -0
  161. package/templates/checkout-agent/packages/agent-core/README.md +34 -0
  162. package/templates/checkout-agent/packages/agent-core/package.json +24 -0
  163. package/templates/checkout-agent/packages/agent-core/src/agent.ts +163 -0
  164. package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +195 -0
  165. package/templates/checkout-agent/packages/agent-core/src/api/client.ts +76 -0
  166. package/templates/checkout-agent/packages/agent-core/src/api/mandate-status.ts +74 -0
  167. package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +153 -0
  168. package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +43 -0
  169. package/templates/checkout-agent/packages/agent-core/src/approval.ts +135 -0
  170. package/templates/checkout-agent/packages/agent-core/src/bundle.ts +191 -0
  171. package/templates/checkout-agent/packages/agent-core/src/channels.ts +186 -0
  172. package/templates/checkout-agent/packages/agent-core/src/check.ts +276 -0
  173. package/templates/checkout-agent/packages/agent-core/src/clock.ts +42 -0
  174. package/templates/checkout-agent/packages/agent-core/src/engine.ts +1074 -0
  175. package/templates/checkout-agent/packages/agent-core/src/escalate.ts +85 -0
  176. package/templates/checkout-agent/packages/agent-core/src/events.ts +40 -0
  177. package/templates/checkout-agent/packages/agent-core/src/guardrails.ts +45 -0
  178. package/templates/checkout-agent/packages/agent-core/src/hash.ts +118 -0
  179. package/templates/checkout-agent/packages/agent-core/src/ids.ts +10 -0
  180. package/templates/checkout-agent/packages/agent-core/src/index.ts +34 -0
  181. package/templates/checkout-agent/packages/agent-core/src/mandate.ts +100 -0
  182. package/templates/checkout-agent/packages/agent-core/src/manifest.ts +76 -0
  183. package/templates/checkout-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
  184. package/templates/checkout-agent/packages/agent-core/src/providers/replay.ts +63 -0
  185. package/templates/checkout-agent/packages/agent-core/src/providers/types.ts +39 -0
  186. package/templates/checkout-agent/packages/agent-core/src/quote.ts +50 -0
  187. package/templates/checkout-agent/packages/agent-core/src/rail.ts +186 -0
  188. package/templates/checkout-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  189. package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +577 -0
  190. package/templates/checkout-agent/packages/agent-core/src/revocation.ts +30 -0
  191. package/templates/checkout-agent/packages/agent-core/src/secrets.ts +30 -0
  192. package/templates/checkout-agent/packages/agent-core/src/state/store.ts +307 -0
  193. package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +157 -0
  194. package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
  195. package/templates/checkout-agent/packages/agent-core/src/stubs/mandate-status.ts +58 -0
  196. package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +209 -0
  197. package/templates/checkout-agent/packages/agent-core/src/tools.ts +66 -0
  198. package/templates/checkout-agent/packages/agent-core/src/types.ts +187 -0
  199. package/templates/checkout-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  200. package/templates/checkout-agent/packages/agent-core/test/approval.test.ts +117 -0
  201. package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  202. package/templates/checkout-agent/packages/agent-core/test/bundle.test.ts +85 -0
  203. package/templates/checkout-agent/packages/agent-core/test/clock.test.ts +25 -0
  204. package/templates/checkout-agent/packages/agent-core/test/composition.test.ts +204 -0
  205. package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +596 -0
  206. package/templates/checkout-agent/packages/agent-core/test/escalate.test.ts +42 -0
  207. package/templates/checkout-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  208. package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  209. package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  210. package/templates/checkout-agent/packages/agent-core/test/helpers.ts +142 -0
  211. package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +229 -0
  212. package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +45 -0
  213. package/templates/checkout-agent/packages/agent-core/test/quote.test.ts +198 -0
  214. package/templates/checkout-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  215. package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  216. package/templates/checkout-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  217. package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +416 -0
  218. package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +28 -0
  219. package/templates/checkout-agent/packages/agent-core/test/state-machine.test.ts +67 -0
  220. package/templates/checkout-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
  221. package/templates/checkout-agent/packages/agent-core/tsconfig.json +10 -0
  222. package/templates/checkout-agent/packages/agent-runtime/README.md +80 -0
  223. package/templates/checkout-agent/packages/agent-runtime/bin.mjs +32 -0
  224. package/templates/checkout-agent/packages/agent-runtime/package.json +23 -0
  225. package/templates/checkout-agent/packages/agent-runtime/src/adversarial.ts +199 -0
  226. package/templates/checkout-agent/packages/agent-runtime/src/agent.ts +68 -0
  227. package/templates/checkout-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  228. package/templates/checkout-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  229. package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  230. package/templates/checkout-agent/packages/agent-runtime/src/channels/types.ts +173 -0
  231. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +459 -0
  232. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +216 -0
  233. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  234. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +350 -0
  235. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +180 -0
  236. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  237. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +120 -0
  238. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +127 -0
  239. package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +98 -0
  240. package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  241. package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  242. package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  243. package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  244. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +367 -0
  245. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +145 -0
  246. package/templates/checkout-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  247. package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  248. package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +49 -0
  249. package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +134 -0
  250. package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +248 -0
  251. package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +330 -0
  252. package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +40 -0
  253. package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  254. package/templates/checkout-agent/packages/agent-runtime/src/index.ts +13 -0
  255. package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +801 -0
  256. package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +229 -0
  257. package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +105 -0
  258. package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +274 -0
  259. package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +239 -0
  260. package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +263 -0
  261. package/templates/checkout-agent/packages/agent-runtime/src/webhook.ts +131 -0
  262. package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  263. package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +329 -0
  264. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +277 -0
  265. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  266. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  267. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +167 -0
  268. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  269. package/templates/checkout-agent/packages/agent-runtime/tsconfig.json +9 -0
  270. package/templates/checkout-agent/scripts/whatsapp-emulator.mjs +84 -0
  271. package/templates/checkout-agent/tsconfig.base.json +16 -0
  272. package/templates/checkout-agent/vitest.config.ts +12 -0
  273. package/templates/collections-agent/README.md +6 -1
  274. package/templates/collections-agent/agents/collections-agent/README.md +12 -8
  275. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +8 -0
  276. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042-retomada.json +10 -0
  277. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +27 -1
  278. package/templates/collections-agent/agents/collections-agent/src/kit.ts +2 -0
  279. package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +12 -2
  280. package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +55 -0
  281. package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +2 -0
  282. package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +12 -6
  283. package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +148 -0
  284. package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +5 -0
  285. package/templates/collections-agent/package.json +1 -0
  286. package/templates/collections-agent/packages/agent-core/README.md +14 -1
  287. package/templates/collections-agent/packages/agent-core/package.json +1 -1
  288. package/templates/collections-agent/packages/agent-core/src/agent.ts +39 -1
  289. package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  290. package/templates/collections-agent/packages/agent-core/src/api/client.ts +28 -5
  291. package/templates/collections-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  292. package/templates/collections-agent/packages/agent-core/src/api/rail.ts +62 -26
  293. package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  294. package/templates/collections-agent/packages/agent-core/src/approval.ts +14 -2
  295. package/templates/collections-agent/packages/agent-core/src/bundle.ts +35 -3
  296. package/templates/collections-agent/packages/agent-core/src/channels.ts +49 -3
  297. package/templates/collections-agent/packages/agent-core/src/check.ts +25 -2
  298. package/templates/collections-agent/packages/agent-core/src/engine.ts +235 -46
  299. package/templates/collections-agent/packages/agent-core/src/hash.ts +57 -1
  300. package/templates/collections-agent/packages/agent-core/src/index.ts +2 -0
  301. package/templates/collections-agent/packages/agent-core/src/quote.ts +50 -0
  302. package/templates/collections-agent/packages/agent-core/src/rail.ts +89 -4
  303. package/templates/collections-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  304. package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  305. package/templates/collections-agent/packages/agent-core/src/revocation.ts +1 -1
  306. package/templates/collections-agent/packages/agent-core/src/state-machine.ts +10 -1
  307. package/templates/collections-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  308. package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  309. package/templates/collections-agent/packages/agent-core/src/tools.ts +17 -6
  310. package/templates/collections-agent/packages/agent-core/src/types.ts +54 -1
  311. package/templates/collections-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  312. package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  313. package/templates/collections-agent/packages/agent-core/test/composition.test.ts +204 -0
  314. package/templates/collections-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  315. package/templates/collections-agent/packages/agent-core/test/helpers.ts +4 -1
  316. package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  317. package/templates/collections-agent/packages/agent-core/test/quote.test.ts +198 -0
  318. package/templates/collections-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  319. package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  320. package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +103 -3
  321. package/templates/collections-agent/packages/agent-runtime/README.md +12 -0
  322. package/templates/collections-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  323. package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  324. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  325. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  326. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  327. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  328. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  329. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  330. package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  331. package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  332. package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  333. package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  334. package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  335. package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  336. package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  337. package/templates/collections-agent/packages/agent-runtime/src/index.ts +1 -0
  338. package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +5 -1
  339. package/templates/collections-agent/packages/agent-runtime/src/kit.ts +25 -2
  340. package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  341. package/templates/collections-agent/packages/agent-runtime/src/setup.ts +13 -3
  342. package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +127 -18
  343. package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  344. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  345. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  346. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  347. package/templates/collections-agent/scripts/whatsapp-emulator.mjs +84 -0
  348. package/templates/hello-agent/README.md +1 -1
  349. package/templates/hello-agent/packages/agent-core/README.md +14 -1
  350. package/templates/hello-agent/packages/agent-core/package.json +1 -1
  351. package/templates/hello-agent/packages/agent-core/src/agent.ts +39 -1
  352. package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  353. package/templates/hello-agent/packages/agent-core/src/api/client.ts +28 -5
  354. package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  355. package/templates/hello-agent/packages/agent-core/src/api/rail.ts +62 -26
  356. package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  357. package/templates/hello-agent/packages/agent-core/src/approval.ts +14 -2
  358. package/templates/hello-agent/packages/agent-core/src/bundle.ts +35 -3
  359. package/templates/hello-agent/packages/agent-core/src/channels.ts +49 -3
  360. package/templates/hello-agent/packages/agent-core/src/check.ts +25 -2
  361. package/templates/hello-agent/packages/agent-core/src/engine.ts +235 -46
  362. package/templates/hello-agent/packages/agent-core/src/hash.ts +57 -1
  363. package/templates/hello-agent/packages/agent-core/src/index.ts +2 -0
  364. package/templates/hello-agent/packages/agent-core/src/quote.ts +50 -0
  365. package/templates/hello-agent/packages/agent-core/src/rail.ts +89 -4
  366. package/templates/hello-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  367. package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  368. package/templates/hello-agent/packages/agent-core/src/revocation.ts +1 -1
  369. package/templates/hello-agent/packages/agent-core/src/state-machine.ts +10 -1
  370. package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  371. package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  372. package/templates/hello-agent/packages/agent-core/src/tools.ts +17 -6
  373. package/templates/hello-agent/packages/agent-core/src/types.ts +54 -1
  374. package/templates/hello-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  375. package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  376. package/templates/hello-agent/packages/agent-core/test/composition.test.ts +204 -0
  377. package/templates/hello-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  378. package/templates/hello-agent/packages/agent-core/test/helpers.ts +4 -1
  379. package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  380. package/templates/hello-agent/packages/agent-core/test/quote.test.ts +198 -0
  381. package/templates/hello-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  382. package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  383. package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +103 -3
  384. package/templates/hello-agent/packages/agent-runtime/README.md +12 -0
  385. package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  386. package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  387. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  388. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  389. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  390. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  391. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  392. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  393. package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  394. package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  395. package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  396. package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  397. package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  398. package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  399. package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  400. package/templates/hello-agent/packages/agent-runtime/src/index.ts +1 -0
  401. package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +5 -1
  402. package/templates/hello-agent/packages/agent-runtime/src/kit.ts +25 -2
  403. package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  404. package/templates/hello-agent/packages/agent-runtime/src/setup.ts +13 -3
  405. package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +127 -18
  406. package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  407. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  408. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  409. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  410. package/templates/kits.lock.json +29 -6
  411. package/templates/supplier-payments-agent/README.md +1 -1
  412. package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +1 -1
  413. package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +1 -1
  414. package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +11 -9
  415. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +1 -1
  416. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +67 -20
  417. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +504 -5
  418. package/templates/supplier-payments-agent/packages/agent-core/README.md +14 -1
  419. package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
  420. package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +39 -1
  421. package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  422. package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +28 -5
  423. package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  424. package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +62 -26
  425. package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  426. package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +14 -2
  427. package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +35 -3
  428. package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +49 -3
  429. package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -2
  430. package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +235 -46
  431. package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +57 -1
  432. package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +2 -0
  433. package/templates/supplier-payments-agent/packages/agent-core/src/quote.ts +50 -0
  434. package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +89 -4
  435. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  436. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  437. package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +1 -1
  438. package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +10 -1
  439. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  440. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  441. package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +17 -6
  442. package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +54 -1
  443. package/templates/supplier-payments-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  444. package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  445. package/templates/supplier-payments-agent/packages/agent-core/test/composition.test.ts +204 -0
  446. package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  447. package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +4 -1
  448. package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  449. package/templates/supplier-payments-agent/packages/agent-core/test/quote.test.ts +198 -0
  450. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  451. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  452. package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +103 -3
  453. package/templates/supplier-payments-agent/packages/agent-runtime/README.md +12 -0
  454. package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  455. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  456. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  457. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  458. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  459. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  460. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  461. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  462. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  463. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  464. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  465. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  466. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  467. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  468. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  469. package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +1 -0
  470. package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +5 -1
  471. package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +25 -2
  472. package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  473. package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +13 -3
  474. package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +127 -18
  475. package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  476. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  477. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  478. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  479. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  480. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  481. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  482. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Wires the core for one agent: manifest, guardrails, mandate, state, rail,
3
+ * status source, signer, bundle, provider. Everything the commands and the
4
+ * terminal channel share, whatever the agent is; what the agent brings is the
5
+ * rail, the tool handlers and the policy extension, through its kit.
6
+ */
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import {
10
+ AgentLoop,
11
+ ExecutionEngine,
12
+ LocalMandateStatusStub,
13
+ ProofBundle,
14
+ ReplayRuntime,
15
+ StateStore,
16
+ loadGuardrails,
17
+ loadManifest,
18
+ loadOrCreateLocalApprovalKey,
19
+ loadToolsFile,
20
+ newRunId,
21
+ resolveFixedClock,
22
+ type AgentRuntime,
23
+ type ApprovalMode,
24
+ type ConversationScript,
25
+ type Execution,
26
+ type Guardrails,
27
+ type LoadedManifest,
28
+ type Mandate,
29
+ type MandateStatusSource,
30
+ type PaymentRail,
31
+ type StubChargeRailOptions,
32
+ type StubRailOptions,
33
+ type ToolContext,
34
+ type ToolHandler,
35
+ } from "@codespar/agent-core";
36
+ import { AnthropicRuntime } from "@codespar/agent-core/providers/anthropic";
37
+ import type { ApiClient } from "@codespar/sdk";
38
+ import { envName, type Agent } from "./agent.js";
39
+ import type { AgentKit, RailKind, SandboxPayer, Settlement } from "./kit.js";
40
+
41
+ /** The `.env.example` placeholder counts as no key: a copied example must replay, not call Anthropic with a fake key. */
42
+ export const ANTHROPIC_KEY_PLACEHOLDER = "sk-ant-your_key_here";
43
+ export const CODESPAR_KEY_PLACEHOLDER = "csk_test_your_key_here";
44
+
45
+ export type ProviderKind = "anthropic" | "replay";
46
+
47
+ export function stateDirOf(agent: Agent): string {
48
+ return join(agent.dir, ".codespar");
49
+ }
50
+
51
+ export function mandatePathOf(agent: Agent): string {
52
+ return join(stateDirOf(agent), "mandate.json");
53
+ }
54
+
55
+ /** The runs folder in use: the agent's own, or the scratch one the restart test points at. */
56
+ export function runsDir(agent: Agent, env: NodeJS.ProcessEnv = process.env): string {
57
+ return env[envName(agent, "RUNS_DIR")] ?? join(agent.dir, "runs");
58
+ }
59
+
60
+ export interface SetupOptions {
61
+ mode?: ApprovalMode | undefined;
62
+ rail?: RailKind | undefined;
63
+ provider?: ProviderKind | undefined;
64
+ transcript?: string | undefined;
65
+ runId?: string | undefined;
66
+ runsDir?: string | undefined;
67
+ stateDir?: string | undefined;
68
+ now?: (() => Date) | undefined;
69
+ stubRail?: StubRailOptions | StubChargeRailOptions | undefined;
70
+ /** Use this mandate instead of the local or the example one (scenarios, tests). */
71
+ mandate?: Mandate | undefined;
72
+ env?: NodeJS.ProcessEnv;
73
+ say?: ((line: string) => void) | undefined;
74
+ }
75
+
76
+ export interface Setup {
77
+ agent: Agent;
78
+ kit: AgentKit;
79
+ settlement: Settlement;
80
+ manifest: LoadedManifest;
81
+ guardrails: Guardrails;
82
+ mode: ApprovalMode;
83
+ mandate: Mandate;
84
+ store: StateStore;
85
+ gate: LocalMandateStatusStub;
86
+ rail: PaymentRail;
87
+ railKind: RailKind;
88
+ api: ApiClient | undefined;
89
+ status: MandateStatusSource;
90
+ /** `await-payer` agents only: who plays the counterparty in the sandbox. */
91
+ payer: SandboxPayer | undefined;
92
+ /** Milliseconds between two looks at an issued receivable: 0 on the stub. */
93
+ pollIntervalMs: number;
94
+ bundle: ProofBundle;
95
+ runId: string;
96
+ engine: ExecutionEngine;
97
+ system: string;
98
+ handlers: Record<string, ToolHandler>;
99
+ tools: ReturnType<typeof loadToolsFile>;
100
+ /**
101
+ * The conversation this run is bound to, when a channel binds one (WhatsApp):
102
+ * its contact and its subject. Set by the channel's start, read by a kit
103
+ * whose rules depend on who the person is — a sale is charged to the
104
+ * customer the conversation is with, and to nobody else.
105
+ */
106
+ conversation?: ConversationScript | undefined;
107
+ /** `onBatch` is for a channel that takes one gesture per batch; without it every line is decided in `onExecution`. */
108
+ makeLoop(runtime: AgentRuntime, onExecution: (execution: Execution) => Promise<Execution>, onBatch?: ToolContext["onBatch"]): AgentLoop;
109
+ makeRuntime(): AgentRuntime;
110
+ close(): void;
111
+ }
112
+
113
+ export function readDotEnv(agentDir: string): void {
114
+ const path = join(agentDir, ".env");
115
+ if (!existsSync(path)) return;
116
+ for (const line of readFileSync(path, "utf8").split("\n")) {
117
+ const m = /^\s*([A-Z][A-Z0-9_]*)\s*=\s*(.*?)\s*$/.exec(line);
118
+ if (!m || !m[1]) continue;
119
+ const value = (m[2] ?? "").replace(/^["']|["']$/g, "");
120
+ if (process.env[m[1]] === undefined) process.env[m[1]] = value;
121
+ }
122
+ }
123
+
124
+ export function resolveProvider(env: NodeJS.ProcessEnv, requested: ProviderKind | undefined): ProviderKind {
125
+ if (requested) return requested;
126
+ const key = env["ANTHROPIC_API_KEY"]?.trim();
127
+ return key && key !== ANTHROPIC_KEY_PLACEHOLDER ? "anthropic" : "replay";
128
+ }
129
+
130
+ export function resolveRailKind(env: NodeJS.ProcessEnv, requested: RailKind | undefined): RailKind {
131
+ if (requested) return requested;
132
+ const key = env["CODESPAR_API_KEY"];
133
+ if (!key || key === CODESPAR_KEY_PLACEHOLDER) return "stub";
134
+ return "api";
135
+ }
136
+
137
+ export function setup(agent: Agent, options: SetupOptions = {}): Setup {
138
+ const env = options.env ?? process.env;
139
+ const say = options.say ?? ((line: string) => process.stderr.write(line + "\n"));
140
+ // `--now` on the one-shot, or CODESPAR_AGENT_NOW for every command: the clock the engine, the stubs, the gate and the loop read (#16).
141
+ const now = options.now ?? resolveFixedClock(undefined, env);
142
+ const manifest = loadManifest(join(agent.dir, "agent.yaml"));
143
+ const guardrails = loadGuardrails(manifest.resolvePath(manifest.manifest.guardrails));
144
+ const tools = loadToolsFile(manifest.resolvePath(manifest.manifest.tools));
145
+ const system = readFileSync(join(agent.dir, "SYSTEM_PROMPT.md"), "utf8");
146
+ const mode: ApprovalMode = options.mode ?? manifest.manifest.default_approval;
147
+ if (!manifest.manifest.approval.includes(mode)) throw new Error(`agent.yaml does not support approval: ${mode}`);
148
+
149
+ // <PREFIX>_STATE_DIR / <PREFIX>_RUNS_DIR exist for the restart test, which drives a child process over a scratch state.
150
+ const stateDir = options.stateDir ?? env[envName(agent, "STATE_DIR")] ?? stateDirOf(agent);
151
+ const runs = options.runsDir ?? runsDir(agent, env);
152
+ const store = new StateStore(join(stateDir, "state.db"));
153
+ const gate = new LocalMandateStatusStub(store, now);
154
+ const signer = loadOrCreateLocalApprovalKey(stateDir);
155
+
156
+ const railKind = resolveRailKind(env, options.rail);
157
+ const built = agent.kit.buildRail({
158
+ kind: railKind,
159
+ env,
160
+ agentDir: agent.dir,
161
+ stateDir,
162
+ manifest,
163
+ store,
164
+ gate,
165
+ now,
166
+ mandate: options.mandate,
167
+ stubRail: options.stubRail,
168
+ envVar: (name) => env[envName(agent, name)],
169
+ });
170
+ const { rail, mandate } = built;
171
+ const status = built.status ?? gate;
172
+
173
+ const runId = options.runId ?? newRunId(mode);
174
+ const bundle = new ProofBundle(runs, runId);
175
+ bundle.mandateSnapshot(mandate);
176
+ // Section 11: mode, rail and mandate id. The VERSION rides with the id, so a reader knows which signing of the mandate authorised the run without opening the snapshot.
177
+ bundle.meta({ run_id: runId, agent: `${manifest.manifest.name}@${manifest.manifest.version}`, mode, rail: railKind, mandate_id: mandate.id, mandate_version: mandate.version, started_at: (now ?? (() => new Date()))().toISOString() });
178
+
179
+ const policyExtension = agent.kit.policyExtension?.({ agentDir: agent.dir, manifest, guardrails, store });
180
+ const engine = new ExecutionEngine({
181
+ store,
182
+ rail,
183
+ status,
184
+ signer,
185
+ manifest: manifest.manifest,
186
+ guardrails: { ...guardrails, approval: mode },
187
+ mandate,
188
+ bundle,
189
+ mode,
190
+ runId,
191
+ onBehalfOf: mandate.consumer_id,
192
+ ...(policyExtension ? { policyExtension } : {}),
193
+ ...(now ? { clock: now } : {}),
194
+ });
195
+
196
+ const makeRuntime = (): AgentRuntime => {
197
+ const provider = resolveProvider(env, options.provider);
198
+ if (provider === "anthropic") return new AnthropicRuntime({ apiKey: env["ANTHROPIC_API_KEY"] });
199
+ if (!options.transcript) throw new Error("the replay provider needs a transcript (--transcript <file> or --scenario <name>)");
200
+ say(`[replay] no ANTHROPIC_API_KEY: replaying ${options.transcript}`);
201
+ return ReplayRuntime.fromFile(options.transcript);
202
+ };
203
+
204
+ const s: Setup = {
205
+ agent,
206
+ kit: agent.kit,
207
+ settlement: agent.settlement,
208
+ manifest,
209
+ guardrails,
210
+ mode,
211
+ mandate,
212
+ store,
213
+ gate,
214
+ rail,
215
+ railKind,
216
+ api: built.api,
217
+ status,
218
+ payer: built.payer,
219
+ pollIntervalMs: built.pollIntervalMs ?? 0,
220
+ bundle,
221
+ runId,
222
+ engine,
223
+ system,
224
+ handlers: {},
225
+ tools,
226
+ makeRuntime,
227
+ makeLoop: (runtime, onExecution, onBatch) => new AgentLoop({ runtime, tools, handlers: s.handlers, system, bundle, engine, onExecution, ...(onBatch ? { onBatch } : {}), ...(now ? { clock: now } : {}) }),
228
+ close: () => store.close(),
229
+ };
230
+ s.handlers = agent.kit.handlers(s);
231
+ return s;
232
+ }
233
+
234
+ export class NoMandateError extends Error {
235
+ constructor() {
236
+ super("no signed mandate yet: run `npm run consent -- --yes` first (or `npm start` without --input, which starts the consent when a test key is present)");
237
+ this.name = "NoMandateError";
238
+ }
239
+ }
@@ -0,0 +1,263 @@
1
+ /**
2
+ * The terminal channel: what `codespar-agent start` opens. No account, no
3
+ * server. It shows what the core decided, asks the person when an execution
4
+ * is awaiting approval, and prints where the sealed outcome landed. The
5
+ * words are the agent's (`kit.labels`, `kit.describeExecution`); the order
6
+ * of the gates is the runner's and is the same for every agent.
7
+ */
8
+ import { createInterface } from "node:readline/promises";
9
+ import { stdin, stderr, stdout } from "node:process";
10
+ import { relative } from "node:path";
11
+ import type { AgentRuntime, BatchGesture, BatchPresentation, ChargeInstrument, Execution } from "@codespar/agent-core";
12
+ import { pollUntilClosed, type PollResult } from "./poll.js";
13
+ import type { Setup } from "./setup.js";
14
+
15
+ export interface TerminalOptions {
16
+ setup: Setup;
17
+ approver: { id: string; channel: string };
18
+ /** Non-interactive decision for a one-shot: approve, deny, or leave it awaiting. */
19
+ decision?: "approve" | "deny" | "none";
20
+ /** `await-payer` only: seconds to wait for the payer after issuing. */
21
+ waitSeconds?: number | undefined;
22
+ /** `await-payer` only: let the sandbox payer act once the receivable is payable. */
23
+ simulatePayer?: boolean | undefined;
24
+ say?: ((line: string) => void) | undefined;
25
+ ask?: ((question: string) => Promise<string>) | undefined;
26
+ /**
27
+ * Where the lines for the counterparty go (the conversation); `say` is the
28
+ * operator's console. `about` is the execution whose OUTCOME the line tells,
29
+ * so a channel can tie a failed delivery of it back to that outcome.
30
+ */
31
+ tell?: ((line: string, about?: Execution) => void) | undefined;
32
+ /**
33
+ * How a payable receivable is put in front of the counterparty. Defaults to
34
+ * the kit's own, which writes lines to a console. A channel overrides it,
35
+ * because a QR on WhatsApp is an image and the copy-and-paste is its own
36
+ * message, and neither of those is a line of text.
37
+ */
38
+ presentInstrument?: ((execution: Execution, instalment: number, chargeId: string, instrument: ChargeInstrument, tell: (line: string) => void) => void | Promise<void>) | undefined;
39
+ /**
40
+ * The gestures taken on whole batches in this session, by batch ref. When a
41
+ * line of a batch arrives and its ref is here, the line is decided by the
42
+ * gesture instead of by a question; see `presentBatch`.
43
+ */
44
+ gestures?: BatchGestures | undefined;
45
+ }
46
+
47
+ /** One gesture per batch ref: the list it was taken on, and the positions vetoed. */
48
+ export type BatchGestures = Map<string, BatchGestureRecord>;
49
+ type BatchGestureRecord = { batch_hash: string; count: number; vetoed: Set<number> };
50
+
51
+ export function describeExecution(execution: Execution, setup: Setup): string[] {
52
+ return setup.kit.describeExecution(execution, setup);
53
+ }
54
+
55
+ /** The one message per outcome, recorded so a duplicate event never sends it twice. */
56
+ export function announceOutcome(execution: Execution, setup: Setup, tell: (line: string) => void): boolean {
57
+ return setup.kit.announceOutcome?.(execution, setup, tell) ?? false;
58
+ }
59
+
60
+ /** The kit's follow-up to an outcome. Never throws: what follows a paid order cannot un-pay it. */
61
+ export async function followUp(execution: Execution, setup: Setup, say: (line: string) => void): Promise<void> {
62
+ if (!setup.kit.followUp) return;
63
+ try {
64
+ await setup.kit.followUp(execution, setup, say);
65
+ } catch (err) {
66
+ say(` follow-up of ${execution.id} failed and changed nothing about it: ${err instanceof Error ? err.message : String(err)}`);
67
+ }
68
+ }
69
+
70
+ function receiptLines(execution: Execution, setup: Setup, say: (line: string) => void): void {
71
+ for (const outcome of execution.outcomes) {
72
+ if (outcome.receipt_id) say(` ${setup.kit.labels.receiptWord}: ${relative(process.cwd(), `${setup.bundle.dir}/receipts/${outcome.receipt_id}.json`)}`);
73
+ }
74
+ }
75
+
76
+ /** Decides an `awaiting_approval` execution and runs an `approved` one. Returns the final execution of this pass. */
77
+ export async function handleExecution(execution: Execution, options: TerminalOptions): Promise<Execution> {
78
+ const { setup, approver } = options;
79
+ const say = options.say ?? ((l: string) => stderr.write(l + "\n"));
80
+ const tell = options.tell ?? ((l: string) => stdout.write(l + "\n"));
81
+ const labels = setup.kit.labels;
82
+ const engine = setup.engine;
83
+
84
+ for (const line of describeExecution(execution, setup)) say(line);
85
+
86
+ let current = execution;
87
+ if (current.state === "awaiting_approval") {
88
+ const gesture = options.decision === undefined && current.batch ? options.gestures?.get(current.batch.ref) : undefined;
89
+ if (gesture) {
90
+ current = decideByGesture(current, gesture, setup, approver);
91
+ } else {
92
+ let decision = options.decision;
93
+ if (!decision) {
94
+ if (current.blocking_reasons.length > 0) say(" (o unico desfecho possivel e negar; aperte Enter)");
95
+ const ask = options.ask ?? defaultAsk;
96
+ const answer = (await ask(labels.approveQuestion)).trim().toLowerCase();
97
+ decision = answer === "s" || answer === "sim" || answer === "y" || answer === "yes" ? "approve" : "deny";
98
+ }
99
+ if (decision === "approve") current = engine.approve(current.id, approver);
100
+ else if (decision === "deny") current = engine.deny(current.id, approver);
101
+ else {
102
+ say(" deixado em awaiting_approval (rode de novo com --approve ou --deny)");
103
+ return current;
104
+ }
105
+ }
106
+ say(` -> ${current.state}${current.reason ? ` (${current.reason})` : ""}`);
107
+ }
108
+
109
+ // An agent that issues on request (`executeOnApproval: false`) leaves the approved execution for its own tool to issue.
110
+ if (current.state === "approved" && setup.kit.executeOnApproval !== false) {
111
+ current = await engine.execute(current.id);
112
+ say(` -> ${current.state}${current.reason ? ` (${current.reason})` : ""}`);
113
+ if (setup.settlement === "immediate") {
114
+ receiptLines(current, setup, say);
115
+ if (current.state === "executing") say(labels.uncertainDispatch);
116
+ }
117
+ }
118
+
119
+ if (setup.settlement === "await-payer") {
120
+ if (current.state === "executing" && current.reason === "awaiting_settlement") {
121
+ const result = await waitForPayer(current.id, options);
122
+ current = result.execution;
123
+ say(` -> ${current.state}${current.reason ? ` (${current.reason})` : ""} apos ${result.rounds} consulta(s), ${result.seconds}s${result.timed_out ? " (tempo esgotado; `npm run poll` continua de onde parou)" : ""}`);
124
+ receiptLines(current, setup, say);
125
+ } else if (current.state === "executing") {
126
+ say(labels.uncertainDispatch);
127
+ }
128
+ const told = current;
129
+ announceOutcome(told, setup, (line) => tell(line, told));
130
+ await followUp(current, setup, say);
131
+ }
132
+ return current;
133
+ }
134
+
135
+ /**
136
+ * A line of a batch the person already decided on as a list. The gesture
137
+ * approves or vetoes a POSITION in ONE list: a line that names another list
138
+ * under the same ref is not covered by it, and "todas" must not become a yes
139
+ * to a list nobody was shown. `approve` still runs the policy and still mints
140
+ * this line's own artifact, which carries the `batch_hash` the gesture was
141
+ * taken on — that is what makes the gesture attested rather than asserted.
142
+ */
143
+ function decideByGesture(execution: Execution, gesture: BatchGestureRecord, setup: Setup, approver: TerminalOptions["approver"]): Execution {
144
+ const engine = setup.engine;
145
+ const { batch_hash, index, count } = execution.batch!;
146
+ if (batch_hash !== gesture.batch_hash || count !== gesture.count) {
147
+ return engine.deny(execution.id, approver, `the list changed after the gesture: it was taken on ${gesture.count} line(s) hashing to ${gesture.batch_hash}, and this line belongs to ${count} hashing to ${batch_hash}`);
148
+ }
149
+ if (gesture.vetoed.has(index)) return engine.deny(execution.id, approver, `vetoed in the batch gesture (line ${index + 1} of ${count})`);
150
+ return engine.approve(execution.id, approver);
151
+ }
152
+
153
+ /**
154
+ * What the person answered to a whole list, or `undefined` when the answer
155
+ * cannot be read as one. Lines are numbered from 1, as they were shown.
156
+ * "todas" / "all", "todas exceto 3,7" / "all except 3,7", and "nenhuma" /
157
+ * "none" (also an empty answer: the default is no, as it is per line). A
158
+ * number outside the list is unreadable rather than ignored, because a typo
159
+ * that silently vetoed nothing would pay the line the person meant to stop.
160
+ */
161
+ export function parseBatchGesture(answer: string, count: number): { vetoed: number[] } | undefined {
162
+ const text = answer.trim().toLowerCase();
163
+ const all = Array.from({ length: count }, (_, i) => i);
164
+ if (text === "" || text === "nenhuma" || text === "none" || text === "n" || text === "nao") return { vetoed: all };
165
+ if (text === "todas" || text === "all") return { vetoed: [] };
166
+ const except = /^(?:todas|all)\s+(?:exceto|except)\s+([\d\s,]+)$/.exec(text);
167
+ if (!except) return undefined;
168
+ const numbers = except[1]!.split(/[\s,]+/).filter(Boolean).map(Number);
169
+ if (numbers.length === 0 || numbers.some((n) => !Number.isInteger(n) || n < 1 || n > count)) return undefined;
170
+ return { vetoed: [...new Set(numbers.map((n) => n - 1))].sort((a, b) => a - b) };
171
+ }
172
+
173
+ /**
174
+ * Section 3, for a list: the person sees every line, the total and the hash
175
+ * prefix, and answers once. What they answer is recorded in the bundle as a
176
+ * `batch.gesture` event and kept for the lines that follow; each line is then
177
+ * decided by `decideByGesture` when it arrives, and each one it approves mints
178
+ * its own artifact as before.
179
+ */
180
+ export async function presentBatch(batch: BatchPresentation, options: TerminalOptions & { gestures: BatchGestures }): Promise<BatchGesture> {
181
+ const { setup, approver } = options;
182
+ const say = options.say ?? ((l: string) => stderr.write(l + "\n"));
183
+ const ask = options.ask ?? defaultAsk;
184
+ say(` lote ${batch.ref} — ${batch.label}: ${batch.count} linha(s), total ${batch.total}, batch_hash ${batch.batch_hash.slice(0, 19)}…`);
185
+ for (const line of batch.lines) {
186
+ const note =
187
+ line.status === "already_settled"
188
+ ? " (ja paga; nao roda de novo)"
189
+ : line.status === "in_progress"
190
+ ? " (em andamento; nao roda de novo)"
191
+ : line.status === "attempt_id_conflict"
192
+ ? " (tentativa presa a outro pagamento; nao roda de novo)"
193
+ : "";
194
+ say(` ${line.index + 1}. ${line.beneficiary}: ${line.amount}${note}`);
195
+ }
196
+ let parsed: { vetoed: number[] } | undefined;
197
+ while (!parsed) {
198
+ parsed = parseBatchGesture(await ask(" Aprovar a lista? [todas / todas exceto 3,7 / nenhuma] "), batch.count);
199
+ if (!parsed) say(` nao entendi; responda todas, todas exceto <numeros de 1 a ${batch.count}> ou nenhuma`);
200
+ }
201
+ const vetoed = new Set(parsed.vetoed);
202
+ const gesture: BatchGesture = { batch_hash: batch.batch_hash, approved: batch.lines.map((l) => l.index).filter((i) => !vetoed.has(i)), vetoed: parsed.vetoed };
203
+ options.gestures.set(batch.ref, { batch_hash: batch.batch_hash, count: batch.count, vetoed });
204
+ setup.engine.note("batch.gesture", null, { batch_ref: batch.ref, batch_hash: batch.batch_hash, count: batch.count, total_minor: batch.total_minor, approved: gesture.approved, vetoed: gesture.vetoed, approver: { type: "human", id: approver.id, channel: approver.channel } });
205
+ say(` -> lista ${gesture.vetoed.length === 0 ? "aprovada inteira" : gesture.approved.length === 0 ? "negada inteira" : `aprovada exceto ${gesture.vetoed.map((i) => i + 1).join(", ")}`}`);
206
+ return gesture;
207
+ }
208
+
209
+ export async function waitForPayer(executionId: string, options: TerminalOptions): Promise<PollResult> {
210
+ const { setup } = options;
211
+ const say = options.say ?? ((l: string) => stderr.write(l + "\n"));
212
+ const tell = options.tell ?? ((l: string) => stdout.write(l + "\n"));
213
+ const waitSeconds = options.waitSeconds ?? (setup.railKind === "api" ? 60 : 0);
214
+ return pollUntilClosed(setup.engine, executionId, {
215
+ intervalMs: setup.pollIntervalMs,
216
+ timeoutMs: waitSeconds * 1000,
217
+ payer: options.simulatePayer ? setup.payer : undefined,
218
+ onInstrument: (e, n, id, instrument) => (options.presentInstrument ?? setup.kit.presentInstrument)?.(e, n, id, instrument, tell),
219
+ onWait: (_e, round) => {
220
+ const line = setup.kit.labels.waitingForPayer?.(round);
221
+ if (setup.railKind === "api" && line !== undefined && round % 5 === 0) say(line);
222
+ },
223
+ });
224
+ }
225
+
226
+ let rl: ReturnType<typeof createInterface> | undefined;
227
+
228
+ export function defaultAsk(question: string): Promise<string> {
229
+ rl ??= createInterface({ input: stdin, output: stderr });
230
+ return rl.question(question);
231
+ }
232
+
233
+ export function closeTerminal(): void {
234
+ rl?.close();
235
+ rl = undefined;
236
+ }
237
+
238
+ export async function interactive(options: TerminalOptions & { runtime: AgentRuntime }): Promise<void> {
239
+ const { setup } = options;
240
+ const say = options.say ?? ((l: string) => stderr.write(l + "\n"));
241
+ const labels = setup.kit.labels;
242
+ // Interactive only: a person at the keyboard can decide a list in one go. The one-shot and the scenario runner pass one decision to every line and never reach this.
243
+ const gestures: BatchGestures = new Map();
244
+ const withGestures = { ...options, gestures };
245
+ const loop = setup.makeLoop(options.runtime, (execution) => handleExecution(execution, withGestures), (batch) => presentBatch(batch, withGestures));
246
+ say(`${setup.manifest.manifest.name} ${setup.manifest.manifest.version} — approval: ${setup.mode} — trilho: ${setup.railKind} — ${labels.mandateWord} ${setup.mandate.id}`);
247
+ say(`run ${setup.runId} — bundle em ${relative(process.cwd(), setup.bundle.dir)}`);
248
+ say(labels.intro);
249
+ for (;;) {
250
+ let text: string;
251
+ try {
252
+ text = await defaultAsk(labels.prompt);
253
+ } catch {
254
+ break;
255
+ }
256
+ const trimmed = text.trim();
257
+ if (!trimmed) continue;
258
+ if (["sair", "exit", "quit"].includes(trimmed.toLowerCase())) break;
259
+ const result = await loop.turn(trimmed);
260
+ stdout.write(result.reply + "\n");
261
+ }
262
+ closeTerminal();
263
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * The webhook channel: the OTHER way the cycle closes. The CodeSpar API
3
+ * delivers `commerce.charge.*` to a trigger's URL; a terminal kit has no
4
+ * URL the API can reach, so the default is the poll (`poll.ts`) and this
5
+ * receiver is what a developer wires when they do have one.
6
+ *
7
+ * STUB, on purpose: it does not register a trigger (that is
8
+ * `POST /v1/triggers` with `events: ["commerce.charge.paid", ...]`, done
9
+ * once with the CLI or the dashboard), it does not expose itself to the
10
+ * internet, and it holds no key. What it does is the receiving contract:
11
+ *
12
+ * POST <url>
13
+ * Content-Type: application/json
14
+ * X-CodeSpar-Event: commerce.charge.paid
15
+ * X-CodeSpar-Event-Id: evt_...
16
+ * X-CodeSpar-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")>
17
+ * { "id": "evt_...", "type": "commerce.charge.paid", "source": "celcoin", "occurred_at": "...", "data": { "payment_id": "<charge id>", ... } }
18
+ *
19
+ * The body is handed to `engine.ingestExternalEvent` by the rail's charge id
20
+ * (`data.payment_id`), which the kit recorded when the attempt was accepted.
21
+ * Duplicates are dropped by event id, `paid` after `expired` (or the other
22
+ * way round) moves nothing, and the debtor is told once. With a trigger
23
+ * secret the signature is verified and a bad one is 401; without one, the
24
+ * receiver is unauthenticated and says so on start.
25
+ */
26
+ import { createHmac, timingSafeEqual } from "node:crypto";
27
+ import { createServer, type Server } from "node:http";
28
+ import type { Execution, ExecutionEngine } from "@codespar/agent-core";
29
+
30
+ export interface WebhookDelivery {
31
+ id: string;
32
+ type: string;
33
+ source?: string;
34
+ occurred_at?: string;
35
+ data?: Record<string, unknown>;
36
+ }
37
+
38
+ export interface WebhookHandlerOptions {
39
+ engine: ExecutionEngine;
40
+ /** The trigger's HMAC secret (`POST /v1/triggers` returns it once). Without it, no signature is checked. */
41
+ secret?: string | undefined;
42
+ /** Seconds a signature timestamp may be off. */
43
+ toleranceSeconds?: number;
44
+ clock?: () => Date;
45
+ /** What to do with an execution the delivery closed (tell the payer, fetch the record). */
46
+ onClosed?: (execution: Execution) => Promise<void> | void;
47
+ }
48
+
49
+ export interface WebhookResponse {
50
+ status: number;
51
+ body: Record<string, unknown>;
52
+ }
53
+
54
+ export function verifySignature(secret: string, header: string | undefined, body: string, now: Date, toleranceSeconds: number): { ok: true } | { ok: false; reason: string } {
55
+ if (!header) return { ok: false, reason: "missing X-CodeSpar-Signature" };
56
+ const parts = Object.fromEntries(header.split(",").map((p) => p.split("=") as [string, string]));
57
+ const t = parts["t"];
58
+ const v1 = parts["v1"];
59
+ if (!t || !v1) return { ok: false, reason: "malformed signature header" };
60
+ const skew = Math.abs(now.getTime() / 1000 - Number(t));
61
+ if (!Number.isFinite(skew) || skew > toleranceSeconds) return { ok: false, reason: "signature timestamp outside tolerance" };
62
+ const expected = createHmac("sha256", secret).update(`${t}.${body}`, "utf8").digest("hex");
63
+ if (expected.length !== v1.length || !timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(v1, "hex"))) return { ok: false, reason: "signature mismatch" };
64
+ return { ok: true };
65
+ }
66
+
67
+ export function chargeIdOf(delivery: WebhookDelivery): string | undefined {
68
+ const data = delivery.data ?? {};
69
+ for (const key of ["payment_id", "charge_id", "external_reference"]) {
70
+ const v = data[key];
71
+ if (typeof v === "string" && v) return v;
72
+ }
73
+ return undefined;
74
+ }
75
+
76
+ /** The pure handler: headers and raw body in, status and body out. The HTTP server below is a thin wrapper. */
77
+ export function createWebhookHandler(options: WebhookHandlerOptions) {
78
+ const clock = options.clock ?? (() => new Date());
79
+ return async (headers: Record<string, string | undefined>, rawBody: string): Promise<WebhookResponse> => {
80
+ if (options.secret) {
81
+ const check = verifySignature(options.secret, headers["x-codespar-signature"], rawBody, clock(), options.toleranceSeconds ?? 300);
82
+ if (!check.ok) return { status: 401, body: { error: "bad_signature", reason: check.reason } };
83
+ }
84
+ let delivery: WebhookDelivery;
85
+ try {
86
+ delivery = JSON.parse(rawBody) as WebhookDelivery;
87
+ } catch {
88
+ return { status: 400, body: { error: "invalid_json" } };
89
+ }
90
+ if (typeof delivery.id !== "string" || typeof delivery.type !== "string") return { status: 400, body: { error: "invalid_delivery", reason: "id and type are required" } };
91
+ if (!delivery.type.startsWith("commerce.charge.")) return { status: 200, body: { applied: false, reason: `event type ${delivery.type} is not a charge event` } };
92
+ const chargeId = chargeIdOf(delivery);
93
+ if (!chargeId) return { status: 200, body: { applied: false, reason: "delivery names no charge" } };
94
+ const before = options.engine.list({ state: "executing" }).map((e) => e.id);
95
+ const result = options.engine.ingestExternalEvent({ event_id: delivery.id, type: delivery.type, transaction_id: chargeId, ...(delivery.occurred_at ? { at: delivery.occurred_at } : {}) });
96
+ if (result.applied) {
97
+ for (const id of before) {
98
+ const after = options.engine.get(id);
99
+ if (after && after.state !== "executing") {
100
+ await options.engine.collectReceipts(after.id);
101
+ await options.onClosed?.(after);
102
+ }
103
+ }
104
+ }
105
+ // Always 2xx once the delivery was read: the API retries on anything else, and a duplicate or a late event is not an error.
106
+ return { status: 200, body: { applied: result.applied, reason: result.reason } };
107
+ };
108
+ }
109
+
110
+ export function startWebhookServer(port: number, options: WebhookHandlerOptions, say: (line: string) => void): Server {
111
+ const handle = createWebhookHandler(options);
112
+ const server = createServer((req, res) => {
113
+ if (req.method !== "POST") {
114
+ res.writeHead(405).end();
115
+ return;
116
+ }
117
+ const chunks: Buffer[] = [];
118
+ req.on("data", (c: Buffer) => chunks.push(c));
119
+ req.on("end", () => {
120
+ const headers = Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v]));
121
+ void handle(headers, Buffer.concat(chunks).toString("utf8")).then((out) => {
122
+ res.writeHead(out.status, { "content-type": "application/json" }).end(JSON.stringify(out.body));
123
+ });
124
+ });
125
+ });
126
+ server.listen(port, () => {
127
+ say(`webhook receiver on http://127.0.0.1:${port}/ (${options.secret ? "signature verified" : "UNAUTHENTICATED: set the trigger secret to verify deliveries"})`);
128
+ say("register the URL once: codespar triggers create --events commerce.charge.paid,commerce.charge.expired,commerce.charge.cancelled --url <public url>");
129
+ });
130
+ return server;
131
+ }