@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
@@ -1,31 +1,66 @@
1
1
  /**
2
- * `codespar-agent verify <receipt-file> [--json] [--keys <file>] [--url <url>]`:
2
+ * `codespar-agent verify <receipt-file> [--json] [--keys <file> | --url <url>] [--approval <file> [--approval-id <id>]] [--from-api]`:
3
3
  * the wave-5 gate — someone outside verifies a receipt without access to
4
- * CodeSpar.
4
+ * CodeSpar — and, since ent#1670, the body and the approval behind it.
5
5
  *
6
- * It takes a receipt JSON, fetches the public key set over plain HTTPS (or
7
- * reads a copy off disk, for a machine with no network), and checks the
8
- * Ed25519 signature with stock `node:crypto`. No CodeSpar key, no API key, no
9
- * SDK call: the verifier holds nothing, and that is what makes the answer
10
- * worth anything. It is the one command here that does NOT need an agent —
11
- * `cli.ts` dispatches it before it looks for an `agent.yaml` — because the
12
- * file it reads has usually been copied off the machine that produced it.
6
+ * Three checks, in order, each one only on the answer of the one before:
7
+ *
8
+ * 1. The Ed25519 signature over `codespar-receipt:v1:<id>:<chain>`, against
9
+ * the public key set, fetched over plain HTTPS or read off disk.
10
+ * 2. The chain, recomputed from the API's receipt READ under the
11
+ * `chain_recipe` the SAME key document publishes, held against the
12
+ * signed one: this is what binds the payee, the amount and the approval
13
+ * to the signature.
14
+ * 3. For a v4 chain, the sealed approval link held against the local
15
+ * approval artifact: "this payment was made against the list H".
16
+ *
17
+ * The receipt file is either the read itself — what a tenant hands a third
18
+ * party, payee unmasked; checked with no credential at all — or a copy from a
19
+ * run's `runs/<run-id>/receipts/`, which masks the payee and carries none of
20
+ * the links. A copy reaches check 2 only with `--from-api`: the tenant's
21
+ * `CODESPAR_API_KEY` (the environment, or the `.env` of the agent the copy
22
+ * sits in) reads the receipt from the API, in memory, and nothing unmasked is
23
+ * written anywhere. Without it a copy answers `signature_only`, which is the
24
+ * truth about what a masked copy can prove. The approval artifact of a copy
25
+ * is the one its `approval_id` names in the run's own `approval.json`.
26
+ *
27
+ * It is the one command here that does NOT need an agent — `cli.ts`
28
+ * dispatches it before it looks for an `agent.yaml` — because the file it
29
+ * reads has usually been copied off the machine that produced it.
13
30
  *
14
31
  * `--json` follows the rule of section 14.5: machine data on stdout, valid
15
32
  * JSON and nothing else, human sentences on stderr. The exit code is the
16
33
  * verdict (`VERDICT_EXIT_CODES`), so a script branches on WHICH answer it got
17
34
  * rather than on "did it fail" — an unsigned receipt is not a failure and must
18
- * not exit 0 either.
35
+ * not exit 0 either, and neither is a signature whose body was not bound.
19
36
  */
20
- import { readFileSync } from "node:fs";
21
- import { resolve } from "node:path";
37
+ import { existsSync, readFileSync } from "node:fs";
38
+ import { basename, dirname, join, resolve } from "node:path";
22
39
  import { stderr, stdout } from "node:process";
23
- import { DEFAULT_RECEIPT_KEYS_URL, VERDICT_EXIT_CODES, verifyReceipt, type KeySource, type ReceiptVerification } from "@codespar/agent-core";
40
+ import {
41
+ DEFAULT_BASE_URL,
42
+ DEFAULT_RECEIPT_KEYS_URL,
43
+ VERDICT_EXIT_CODES,
44
+ copyDisagreesWithRead,
45
+ createCodeSparClient,
46
+ describeApiError,
47
+ isReceiptRead,
48
+ readSignedReceipt,
49
+ receiptKeysUrl,
50
+ signatureOnly,
51
+ verifyReceiptRead,
52
+ verifyReceiptWithKeys,
53
+ type ApprovalClaim,
54
+ type ExecutionItem,
55
+ type ReceiptVerification,
56
+ } from "@codespar/agent-core";
57
+ import { findAgentDir } from "../agent.js";
58
+ import { readDotEnv } from "../setup.js";
24
59
 
25
- const USAGE = "usage: npm run verify <receipt-file> [--json] [--keys <key-set.json>] [--url <https://.../.well-known/codespar-receipt-keys.json>]";
60
+ const USAGE =
61
+ "usage: npm run verify <receipt-file> [--json] [--keys <key-set.json> | --url <https://.../.well-known/codespar-receipt-keys.json>] [--approval <approval.json> [--approval-id <apr_...>]] [--from-api]";
26
62
 
27
- /** The fetcher the module is given. The only network in this feature, and it
28
- * lives here rather than in the core so a unit test injects its own. */
63
+ /** The fetcher the key set comes through: the one unauthenticated call in this command. */
29
64
  async function fetchKeyDocument(url: string): Promise<unknown> {
30
65
  const controller = new AbortController();
31
66
  const timer = setTimeout(() => controller.abort(), 10_000);
@@ -38,69 +73,226 @@ async function fetchKeyDocument(url: string): Promise<unknown> {
38
73
  }
39
74
  }
40
75
 
76
+ interface Args {
77
+ json: boolean;
78
+ keysFile?: string;
79
+ url?: string;
80
+ approvalFile?: string;
81
+ approvalId?: string;
82
+ fromApi: boolean;
83
+ receiptFile?: string;
84
+ }
85
+
86
+ const VALUED: readonly string[] = ["--keys", "--url", "--approval", "--approval-id"];
87
+
41
88
  export async function verify(argv: string[]): Promise<number> {
42
89
  const say = (line: string) => stderr.write(line + "\n");
43
- let json = false;
44
- let keysFile: string | undefined;
45
- let url: string | undefined;
46
- let receiptFile: string | undefined;
90
+ const args: Args = { json: false, fromApi: false };
47
91
 
48
92
  for (let i = 0; i < argv.length; i += 1) {
49
93
  const a = argv[i]!;
50
- if (a === "--json") json = true;
51
- else if (a === "--keys" || a === "--url") {
94
+ if (a === "--json") args.json = true;
95
+ else if (a === "--from-api") args.fromApi = true;
96
+ else if (VALUED.includes(a)) {
52
97
  const value = argv[i + 1];
53
98
  if (value === undefined || value.startsWith("--")) {
54
99
  say(`${a} needs a value\n${USAGE}`);
55
100
  return 2;
56
101
  }
57
- if (a === "--keys") keysFile = value;
58
- else url = value;
102
+ if (a === "--keys") args.keysFile = value;
103
+ else if (a === "--url") args.url = value;
104
+ else if (a === "--approval") args.approvalFile = value;
105
+ else args.approvalId = value;
59
106
  i += 1;
60
107
  } else if (a === "--help" || a === "-h") {
61
108
  say(USAGE);
62
109
  say(`the key set is public and needs no credential; its default is ${DEFAULT_RECEIPT_KEYS_URL}`);
63
110
  say("--keys reads a saved copy of that document instead, so the check runs with no network at all");
111
+ say("the receipt file is the API's read (GET /v1/consumers/receipts/{id}), whose body is recomputed against the signed chain, or a run's copy, which proves the signature only");
112
+ say("--from-api reads a run's copy from the API with CODESPAR_API_KEY (in memory, nothing written), so its body and its approval are checked too; the key set then defaults to that deployment's");
113
+ say("--approval holds a v4 receipt's sealed approval link against an approval artifact (a run's copy uses its own approval.json)");
64
114
  return 0;
65
115
  } else if (a.startsWith("-")) {
66
116
  say(`unknown argument ${a}\n${USAGE}`);
67
117
  return 2;
68
- } else if (receiptFile === undefined) receiptFile = a;
118
+ } else if (args.receiptFile === undefined) args.receiptFile = a;
69
119
  else {
70
- say(`verify takes one receipt file (got ${receiptFile} and ${a})\n${USAGE}`);
120
+ say(`verify takes one receipt file (got ${args.receiptFile} and ${a})\n${USAGE}`);
71
121
  return 2;
72
122
  }
73
123
  }
74
124
 
75
- if (!receiptFile) {
125
+ if (!args.receiptFile) {
76
126
  say(USAGE);
77
127
  say("the receipt is the JSON the API returns from GET /v1/consumers/receipts/{id}, or a copy from a run's runs/<run-id>/receipts/");
78
128
  return 2;
79
129
  }
80
- if (keysFile && url) {
130
+ if (args.keysFile && args.url) {
81
131
  say(`--keys and --url name two different key sets; pass one\n${USAGE}`);
82
132
  return 2;
83
133
  }
134
+ if (args.approvalId && !args.approvalFile) {
135
+ say(`--approval-id picks one artifact out of --approval; pass the file too\n${USAGE}`);
136
+ return 2;
137
+ }
84
138
 
85
- const receipt = readJson(receiptFile, say);
139
+ const receipt = readJson(args.receiptFile, say);
86
140
  if (receipt === undefined) return 2;
87
141
 
88
- let source: KeySource;
89
- if (keysFile) {
90
- const document = readJson(keysFile, say);
91
- if (document === undefined) return 2;
92
- source = { document, label: resolve(keysFile) };
93
- } else {
94
- source = { url: url ?? DEFAULT_RECEIPT_KEYS_URL, fetch: fetchKeyDocument };
142
+ // The read and the key set must come from ONE deployment: the recipe that
143
+ // binds the body is the key document's, and a staging read checked under
144
+ // production's document is the §47 trap again.
145
+ const apiBase = process.env["CODESPAR_API_URL"]?.trim() || DEFAULT_BASE_URL;
146
+ if (args.fromApi && args.url && new URL(args.url).origin !== new URL(apiBase).origin) {
147
+ say(`--from-api reads the receipt from ${new URL(apiBase).origin} and --url names the key set of ${new URL(args.url).origin}; the read and the key set must come from the same deployment\n${USAGE}`);
148
+ return 2;
149
+ }
150
+
151
+ let approval: ApprovalClaim | undefined;
152
+ if (args.approvalFile) {
153
+ const claim = readApprovalClaim(args.approvalFile, args.approvalId, say);
154
+ if (!claim) return 2;
155
+ approval = claim;
156
+ }
157
+
158
+ let keysDocument: unknown;
159
+ if (args.keysFile) {
160
+ keysDocument = readJson(args.keysFile, say);
161
+ if (keysDocument === undefined) return 2;
95
162
  }
96
163
 
97
- const report = await verifyReceipt(receipt, source);
98
- if (json) stdout.write(JSON.stringify(report) + "\n");
164
+ const report = await check(receipt, args, apiBase, approval, keysDocument);
165
+ if (args.json) stdout.write(JSON.stringify(report) + "\n");
99
166
  else stdout.write(render(report));
100
167
  say(report.message);
101
168
  return VERDICT_EXIT_CODES[report.verdict];
102
169
  }
103
170
 
171
+ async function check(receipt: unknown, args: Args, apiBase: string, approval: ApprovalClaim | undefined, keysDocument: unknown): Promise<ReceiptVerification> {
172
+ // Answered before the network is touched, as before: an unsigned or
173
+ // malformed file needs no key set, and fetching one to check a signature
174
+ // that is not there would turn an offline answer into an outage.
175
+ const signed = readSignedReceipt(receipt);
176
+ if (!signed.ok || signed.fields.receipt_sig_ed25519 === null) return verifyReceiptWithKeys(receipt, { keys: [] }, "none");
177
+
178
+ let document: unknown;
179
+ let label: string;
180
+ if (args.keysFile) {
181
+ label = resolve(args.keysFile);
182
+ document = keysDocument;
183
+ } else {
184
+ label = args.url ?? (args.fromApi ? receiptKeysUrl(apiBase) : DEFAULT_RECEIPT_KEYS_URL);
185
+ try {
186
+ document = await fetchKeyDocument(label);
187
+ } catch (err) {
188
+ return unreachable(
189
+ signed.fields.receipt_id,
190
+ label,
191
+ `the published key set could not be read from ${label}: ${err instanceof Error ? err.message : String(err)}. Nothing is proved and nothing is disproved; the keys can also be saved to a file and passed in`,
192
+ );
193
+ }
194
+ }
195
+
196
+ if (isReceiptRead(receipt)) return verifyReceiptRead(receipt, document, { label, ...(approval ? { approval } : {}) });
197
+
198
+ // A run's copy: the signature is checked on the copy itself, as it always was.
199
+ const signature = verifyReceiptWithKeys(receipt, document, label);
200
+ if (signature.verdict !== "verified") return signature;
201
+ if (!args.fromApi) {
202
+ return signatureOnly(signature, "read_required", "this is a run's copy, which masks the payee and carries none of the chain's links. Recompute from the API's read: run this again with --from-api (the tenant's key, read-only, nothing written), or verify the read itself");
203
+ }
204
+
205
+ const read = await readFromApi(signed.fields.receipt_id, args.receiptFile!, apiBase);
206
+ if (!read.ok) return signatureOnly(signature, "read_unavailable", `the API's read of it could not be fetched (${read.detail}), so nothing is proved and nothing is disproved about the body`);
207
+ const differs = copyDisagreesWithRead(receipt as Record<string, unknown>, read.body);
208
+ if (differs.includes("chain")) {
209
+ // The copy's chain carries a signature that checked above, so a read with another chain is a re-seal, not an edit.
210
+ return signatureOnly(signature, "read_resealed", "the API's read carries a different chain than this copy: the receipt was re-sealed after the copy was written (a delivery or metering update). Verify the read itself");
211
+ }
212
+ if (differs.length > 0) {
213
+ return {
214
+ ...signature,
215
+ verdict: "chain_mismatch",
216
+ reason: "copy_differs_from_read",
217
+ message: `receipt ${signature.receipt_id}: this copy says something the sealed receipt does not (${differs.join(", ")}). The signature covers the id and the chain, not the copy's body, so the copy was edited after it was written`,
218
+ chain_check: { status: "mismatch", version: null, recomputed: null, reason: "copy_differs_from_read" },
219
+ };
220
+ }
221
+ const claim = approval ?? approvalOfCopy(receipt as Record<string, unknown>, args.receiptFile!);
222
+ return verifyReceiptRead(read.body, document, { label, ...(claim ? { approval: claim } : {}) });
223
+ }
224
+
225
+ function unreachable(receiptId: string, label: string, message: string): ReceiptVerification {
226
+ return { verdict: "unreachable", receipt_id: receiptId, kid: null, key_status: null, signing_string: null, keys_from: label, reason: "fetch_failed", message };
227
+ }
228
+
229
+ /**
230
+ * The receipt as the API reads it, for a copy, with the tenant's key. The key
231
+ * is the environment's, or the `.env` of the agent the copy sits in — the
232
+ * same place `npm start` reads it from — and only a `csk_test_` key is
233
+ * accepted, like everywhere else in this repository.
234
+ */
235
+ async function readFromApi(receiptId: string, receiptFile: string, apiBase: string): Promise<{ ok: true; body: Record<string, unknown> } | { ok: false; detail: string }> {
236
+ const agentDir = findAgentDir(dirname(resolve(receiptFile)));
237
+ if (agentDir) readDotEnv(agentDir);
238
+ try {
239
+ const api = createCodeSparClient({ apiKey: process.env["CODESPAR_API_KEY"], baseUrl: apiBase, projectId: process.env["CODESPAR_PROJECT_ID"], timeoutMs: 15_000 });
240
+ const body: unknown = await api.get("/v1/consumers/receipts/{id}", { path: { id: receiptId } });
241
+ if (!isReceiptRead(body)) return { ok: false, detail: "the API answered something that is not a receipt read" };
242
+ return { ok: true, body };
243
+ } catch (err) {
244
+ const failure = describeApiError(err);
245
+ return { ok: false, detail: `${failure.code}: ${failure.message}` };
246
+ }
247
+ }
248
+
249
+ /**
250
+ * The artifact a run's copy names by `approval_id`, from the same run's
251
+ * `approval.json`. Found by identity: picking whichever artifact matched the
252
+ * sealed hash would make a mismatch impossible to see.
253
+ */
254
+ function approvalOfCopy(copy: Record<string, unknown>, receiptFile: string): ApprovalClaim | undefined {
255
+ const id = copy["approval_id"];
256
+ const receipts = dirname(resolve(receiptFile));
257
+ const path = join(dirname(receipts), "approval.json");
258
+ if (typeof id !== "string" || basename(receipts) !== "receipts" || !existsSync(path)) return undefined;
259
+ try {
260
+ return pickArtifact(JSON.parse(readFileSync(path, "utf8")) as unknown, id);
261
+ } catch {
262
+ return undefined;
263
+ }
264
+ }
265
+
266
+ function isRecord(value: unknown): value is Record<string, unknown> {
267
+ return typeof value === "object" && value !== null && !Array.isArray(value);
268
+ }
269
+
270
+ /** One artifact out of an `approval.json` (a list) or a single artifact file, as the part the approval link is held against. */
271
+ function pickArtifact(input: unknown, id: string | undefined): ApprovalClaim | undefined {
272
+ const candidates = (Array.isArray(input) ? input : [input]).filter((a): a is Record<string, unknown> => isRecord(a) && typeof a["items_hash"] === "string" && typeof a["approval_id"] === "string");
273
+ const chosen = id === undefined ? (candidates.length === 1 ? candidates[0] : undefined) : candidates.find((a) => a["approval_id"] === id);
274
+ if (!chosen) return undefined;
275
+ const batch = chosen["batch"];
276
+ return {
277
+ approval_id: chosen["approval_id"] as string,
278
+ items_hash: chosen["items_hash"] as string,
279
+ ...(Array.isArray(chosen["items"]) ? { items: chosen["items"] as ExecutionItem[] } : {}),
280
+ ...(isRecord(batch) && typeof batch["batch_hash"] === "string" ? { batch: { batch_hash: batch["batch_hash"] } } : {}),
281
+ };
282
+ }
283
+
284
+ /** `--approval`: `undefined` is a usage error, already said. */
285
+ function readApprovalClaim(path: string, id: string | undefined, say: (line: string) => void): ApprovalClaim | undefined {
286
+ const input = readJson(path, say);
287
+ if (input === undefined) return undefined;
288
+ const claim = pickArtifact(input, id);
289
+ if (!claim) {
290
+ const count = Array.isArray(input) ? input.length : 1;
291
+ say(id ? `${resolve(path)} holds no approval artifact ${id}` : `${resolve(path)} holds ${count} approval artifacts, not one; name the one this payment carried with --approval-id`);
292
+ }
293
+ return claim;
294
+ }
295
+
104
296
  /** A file that is missing or is not JSON is a refusal a person can act on, never a stack trace. */
105
297
  function readJson(path: string, say: (line: string) => void): unknown {
106
298
  let text: string;
@@ -124,6 +316,15 @@ function render(report: ReceiptVerification): string {
124
316
  if (report.kid) lines.push(` key ${report.kid}${report.key_status ? ` (${report.key_status})` : ""}`);
125
317
  if (report.keys_from) lines.push(` key set ${report.keys_from}`);
126
318
  if (report.signing_string) lines.push(` signed ${report.signing_string}`);
319
+ if (report.chain_check) {
320
+ const c = report.chain_check;
321
+ lines.push(` body ${c.status}${c.version !== null ? ` (chain v${c.version})` : ""}${c.recomputed && c.status === "mismatch" ? `, recomputes to ${c.recomputed}` : ""}`);
322
+ }
323
+ if (report.approval_check) {
324
+ const a = report.approval_check;
325
+ lines.push(` approval ${a.status}: sealed ${a.sealed.items_hash}${a.sealed.batch_hash ? ` batch ${a.sealed.batch_hash}` : ""}`);
326
+ if (a.artifact) lines.push(` artifact ${a.artifact.approval_id} ${a.artifact.items_hash}${a.artifact.batch_hash ? ` batch ${a.artifact.batch_hash}` : ""}`);
327
+ }
127
328
  lines.push(` reason ${report.reason}`);
128
329
  return lines.join("\n") + "\n";
129
330
  }
@@ -8,7 +8,7 @@
8
8
  import { stderr, stdout } from "node:process";
9
9
  import { envName, type Agent } from "../agent.js";
10
10
  import { setup } from "../setup.js";
11
- import { announceOutcome } from "../terminal.js";
11
+ import { announceOutcome, followUp } from "../terminal.js";
12
12
  import { startWebhookServer } from "../webhook.js";
13
13
 
14
14
  export async function webhook(agent: Agent, argv: string[]): Promise<number> {
@@ -21,8 +21,9 @@ export async function webhook(agent: Agent, argv: string[]): Promise<number> {
21
21
  {
22
22
  engine: s.engine,
23
23
  secret,
24
- onClosed: (execution) => {
24
+ onClosed: async (execution) => {
25
25
  announceOutcome(execution, s, (l) => void stdout.write(l + "\n"));
26
+ await followUp(execution, s, say);
26
27
  },
27
28
  },
28
29
  say,
@@ -10,3 +10,4 @@ export * from "./scenarios.js";
10
10
  export * from "./adversarial.js";
11
11
  export * from "./inspect.js";
12
12
  export { main } from "./cli.js";
13
+ export { pollWhatsApp, type PollWhatsAppOptions } from "./commands/poll-whatsapp.js";
@@ -18,7 +18,7 @@
18
18
  * text: a debtor's own words are in `transcript.jsonl` and are not this
19
19
  * command's to re-publish.
20
20
  */
21
- import { maskPayee, type ApprovalArtifact, type ExecutionBatch, type ProofBundle } from "@codespar/agent-core";
21
+ import { maskPayee, type ApprovalArtifact, type ExecutionBatch, type ExecutionComposition, type ProofBundle } from "@codespar/agent-core";
22
22
  import { formatMinor } from "./default-kit.js";
23
23
 
24
24
  export interface InspectItem {
@@ -87,6 +87,8 @@ export interface InspectExecution {
87
87
  items_hash: string;
88
88
  /** The set binding as the ARTIFACT carries it, which is the signed copy. */
89
89
  batch: ExecutionBatch | null;
90
+ /** What the single amount was composed of (a cart), as the artifact signed it. */
91
+ composition?: ExecutionComposition;
90
92
  mandate: { id: string; version: number };
91
93
  escalation: { trigger: string; detail: string } | null;
92
94
  } | null;
@@ -453,6 +455,7 @@ export function assembleTimeline(bundle: ProofBundle): InspectReport {
453
455
  expires_at: artifact.expires_at,
454
456
  items_hash: artifact.items_hash,
455
457
  batch: batchBinding(artifact.batch),
458
+ ...(artifact.composition ? { composition: artifact.composition } : {}),
456
459
  mandate: artifact.mandate,
457
460
  escalation: artifact.escalation ? { trigger: artifact.escalation.trigger, detail: redact(artifact.escalation.detail) ?? "" } : null,
458
461
  };
@@ -575,6 +578,7 @@ function executionLines(execution: InspectExecution): Array<{ at: string; label:
575
578
  if (approval) {
576
579
  const wrapped = [`${approval.approval_id} · items_hash ${approval.items_hash}`, `under mandate ${approval.mandate.id} v${approval.mandate.version} · the artifact expires ${clock(approval.expires_at)}`];
577
580
  if (approval.batch) wrapped.push(`line ${approval.batch.index + 1} of ${approval.batch.count} of batch ${approval.batch.ref} · batch_hash ${approval.batch.batch_hash}`);
581
+ if (approval.composition) wrapped.push(`composed of ${approval.composition.line_count} line(s) of ${approval.composition.ref} · composition_hash ${approval.composition.composition_hash}`);
578
582
  if (approval.escalation) wrapped.push(`escalated by ${approval.escalation.trigger}: ${approval.escalation.detail}`);
579
583
  lines.push({ at: approval.approved_at, label: "approved", text: approverLabel(approval.approver), wrapped });
580
584
  }
@@ -157,8 +157,19 @@ export interface AgentKit {
157
157
  usage(manifest: LoadedManifest): string;
158
158
  buildRail(ctx: RailContext): RailBuild;
159
159
  handlers(setup: Setup): Record<string, ToolHandler>;
160
- /** The deterministic policy the core runs as its `policyExtension`. */
161
- policyExtension?(ctx: { agentDir: string; manifest: LoadedManifest; guardrails: Guardrails }): PolicyExtension | undefined;
160
+ /**
161
+ * Whether the channel dispatches an execution the moment it is approved.
162
+ * Default true. False when approving and issuing are two moments of the
163
+ * agent's own flow — a sale: the attendant (or the policy) confirms the
164
+ * order, and the charge goes out when the customer asks for it. The
165
+ * approved execution then waits, and the agent's own tool hands it to
166
+ * `engine.execute`, which runs the last gate like any other caller: a cart
167
+ * changed in between is caught there. An approved execution nobody issues
168
+ * expires on the approval TTL.
169
+ */
170
+ executeOnApproval?: boolean;
171
+ /** The deterministic policy the core runs as its `policyExtension`. `store` is the run's state.db, for a policy that judges durable state of the agent's own (a cart). */
172
+ policyExtension?(ctx: { agentDir: string; manifest: LoadedManifest; guardrails: Guardrails; store: StateStore }): PolicyExtension | undefined;
162
173
  /** The console lines that describe one execution. */
163
174
  describeExecution(execution: Execution, setup: Setup): string[];
164
175
  /** The `--json` body of a one-shot. */
@@ -184,6 +195,18 @@ export interface AgentKit {
184
195
  * who was not told was not told.
185
196
  */
186
197
  outcomeTemplate?(execution: Execution): { template: string; variables: string[] } | undefined;
198
+ /**
199
+ * Work that FOLLOWS an outcome and is not part of it: a sale's service
200
+ * invoice once the order is `settled`. Runs after `announceOutcome`,
201
+ * wherever an outcome is announced (the terminal, the scenario runner,
202
+ * `poll`, `webhook`, the WhatsApp poll), and is awaited there. It can never
203
+ * move the execution it follows: a throw is reported on the operator's
204
+ * console and the outcome stands, because a paid order stays paid whatever
205
+ * happens after it.
206
+ */
207
+ followUp?(execution: Execution, setup: Setup, say: (line: string) => void): Promise<void>;
208
+ /** `resume` only: the follow-ups a crash left half-done. The same rule as the core's outbox: what provably never left may go; what may have left is reported, never sent again. */
209
+ resumeFollowUps?(setup: Setup, say: (line: string) => void): Promise<void>;
187
210
  /** Runs before a one-shot or an interactive session, when the mandate may have to be born first. Returns false to stop the run. */
188
211
  ensureMandate?(ctx: EnsureMandateContext): Promise<boolean>;
189
212
  /** `codespar-agent consent`. Absent means the agent has no consent step. */
@@ -14,7 +14,7 @@ import type { ApprovalMode, Execution } from "@codespar/agent-core";
14
14
  import type { Agent } from "./agent.js";
15
15
  import type { RailKind } from "./kit.js";
16
16
  import { setup, type Setup } from "./setup.js";
17
- import { announceOutcome, handleExecution } from "./terminal.js";
17
+ import { announceOutcome, followUp, handleExecution } from "./terminal.js";
18
18
 
19
19
  const TurnSchema = z
20
20
  .object({
@@ -204,7 +204,13 @@ export async function runScenario(agent: Agent, scenario: Scenario, options: Run
204
204
  for (const stuck of s.engine.list({ state: "executing" })) await s.engine.reconcile(stuck.id);
205
205
  }
206
206
  // Whatever closed after the conversation is told to the counterparty once, as the poll or the webhook would.
207
- if (awaitsPayer) for (const e of s.engine.list()) if (e.run_id === s.runId) announceOutcome(e, s, tell);
207
+ if (awaitsPayer) {
208
+ for (const e of s.engine.list()) {
209
+ if (e.run_id !== s.runId) continue;
210
+ announceOutcome(e, s, tell);
211
+ await followUp(e, s, say);
212
+ }
213
+ }
208
214
 
209
215
  const events = s.store.listEvents({ run_id: s.runId });
210
216
  for (const e of events) {
@@ -21,6 +21,7 @@ import {
21
21
  resolveFixedClock,
22
22
  type AgentRuntime,
23
23
  type ApprovalMode,
24
+ type ConversationScript,
24
25
  type Execution,
25
26
  type Guardrails,
26
27
  type LoadedManifest,
@@ -29,6 +30,7 @@ import {
29
30
  type PaymentRail,
30
31
  type StubChargeRailOptions,
31
32
  type StubRailOptions,
33
+ type ToolContext,
32
34
  type ToolHandler,
33
35
  } from "@codespar/agent-core";
34
36
  import { AnthropicRuntime } from "@codespar/agent-core/providers/anthropic";
@@ -95,7 +97,15 @@ export interface Setup {
95
97
  system: string;
96
98
  handlers: Record<string, ToolHandler>;
97
99
  tools: ReturnType<typeof loadToolsFile>;
98
- makeLoop(runtime: AgentRuntime, onExecution: (execution: Execution) => Promise<Execution>): AgentLoop;
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;
99
109
  makeRuntime(): AgentRuntime;
100
110
  close(): void;
101
111
  }
@@ -166,7 +176,7 @@ export function setup(agent: Agent, options: SetupOptions = {}): Setup {
166
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.
167
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() });
168
178
 
169
- const policyExtension = agent.kit.policyExtension?.({ agentDir: agent.dir, manifest, guardrails });
179
+ const policyExtension = agent.kit.policyExtension?.({ agentDir: agent.dir, manifest, guardrails, store });
170
180
  const engine = new ExecutionEngine({
171
181
  store,
172
182
  rail,
@@ -214,7 +224,7 @@ export function setup(agent: Agent, options: SetupOptions = {}): Setup {
214
224
  handlers: {},
215
225
  tools,
216
226
  makeRuntime,
217
- makeLoop: (runtime, onExecution) => new AgentLoop({ runtime, tools, handlers: s.handlers, system, bundle, engine, onExecution, ...(now ? { clock: now } : {}) }),
227
+ makeLoop: (runtime, onExecution, onBatch) => new AgentLoop({ runtime, tools, handlers: s.handlers, system, bundle, engine, onExecution, ...(onBatch ? { onBatch } : {}), ...(now ? { clock: now } : {}) }),
218
228
  close: () => store.close(),
219
229
  };
220
230
  s.handlers = agent.kit.handlers(s);