@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
@@ -29,9 +29,46 @@ export interface InboundMessage {
29
29
  id: string;
30
30
  /** Who wrote it, E.164 on WhatsApp. */
31
31
  from: string;
32
+ /**
33
+ * What the person wrote. For a TAP (`reply` set) the backend puts the
34
+ * button's title here, and the channel replaces it with the intent the
35
+ * template that offered the button declared — the model never reads a
36
+ * title as if the person had typed it.
37
+ */
32
38
  text: string;
33
39
  /** Unix seconds on the PROVIDER's clock, which is not this process's clock. */
34
40
  timestamp: number;
41
+ /** Set when the person tapped a quick reply instead of typing. */
42
+ reply?: InboundReply;
43
+ }
44
+
45
+ /** A tapped quick reply, as the provider reports it: which button, and on which message when it says. */
46
+ export interface InboundReply {
47
+ /** `button_reply` / `list_reply` (an interactive message), `button` (a template's quick reply). */
48
+ type: "button_reply" | "list_reply" | "button";
49
+ id: string;
50
+ title: string;
51
+ /** The message the button was on (`context.id`), when the provider sends it. */
52
+ context_id?: string;
53
+ }
54
+
55
+ /**
56
+ * What the provider reports about a message the agent sent, after it left.
57
+ * `failed` carries Meta's errors (131026: not a WhatsApp number, or it blocked
58
+ * the business). Delivered by webhook, at any time, to whoever is listening.
59
+ */
60
+ export interface StatusUpdate {
61
+ message_id: string;
62
+ status: DeliveryState;
63
+ /** Unix seconds, the provider's clock. */
64
+ timestamp: number;
65
+ errors: Array<{ code?: number; title?: string; details?: string }>;
66
+ }
67
+
68
+ /** The execution an outbound message tells the outcome of: what a failed delivery of it means. */
69
+ export interface OutcomeTag {
70
+ execution_id: string;
71
+ state: string;
35
72
  }
36
73
 
37
74
  /**
@@ -50,10 +87,11 @@ export interface InboundMessage {
50
87
  * and which the rail did, and only the first are checked for a document.
51
88
  */
52
89
  export type OutboundBody =
53
- | { kind: "text"; text: string }
90
+ | { kind: "text"; text: string; about?: OutcomeTag }
54
91
  | { kind: "media"; media: "qr"; data: string; caption?: string }
55
92
  | { kind: "instrument"; instrument: "pix_copy_paste" | "boleto_bank_line"; value: string }
56
- | { kind: "template"; template: string; language: string; variables: string[] };
93
+ /** `buttons` are the declaration's, attached by the channel; a caller never composes them. */
94
+ | { kind: "template"; template: string; language: string; variables: string[]; about?: OutcomeTag; buttons?: Array<{ id: string; title: string }> };
57
95
 
58
96
  export interface SentMessage {
59
97
  /** The provider's id when it accepted one, else the id the backend minted. */
@@ -85,6 +123,8 @@ export interface ChannelBackend {
85
123
  next(): Promise<InboundMessage | undefined>;
86
124
  /** Hands the message to the provider. Rules have already run: a backend never re-decides policy. */
87
125
  deliver(to: string, body: OutboundBody): Promise<SentMessage>;
126
+ /** Where the status webhooks go. A backend that cannot observe statuses never calls it. */
127
+ onStatus?(listener: (status: StatusUpdate) => void): void;
88
128
  close(): Promise<void>;
89
129
  }
90
130
 
@@ -105,10 +145,12 @@ export interface Channel {
105
145
 
106
146
  export interface ChannelLogLine {
107
147
  at: string;
108
- direction: "in" | "out";
148
+ /** `status`: what the provider reported about an outbound message, after it left. */
149
+ direction: "in" | "out" | "status";
109
150
  /** The contact, MASKED. The raw value never reaches a file. */
110
151
  contact: string;
111
- kind: OutboundBody["kind"];
152
+ /** `reply`: an inbound tap. `status`: a delivery status. */
153
+ kind: OutboundBody["kind"] | "reply" | "status";
112
154
  /** The provider's id, or ours when nothing was sent. */
113
155
  message_id: string;
114
156
  state: DeliveryState;
@@ -121,4 +163,11 @@ export interface ChannelLogLine {
121
163
  */
122
164
  provider_timestamp?: number;
123
165
  refused?: { rule: string; detail: string };
166
+ /** Outbound: the execution whose outcome this message tells. Status: the same, looked up by message id. */
167
+ about?: OutcomeTag;
168
+ /** Outbound template: the reply ids it offered. Inbound tap: the reply tapped. */
169
+ offered?: string[];
170
+ reply?: { type: InboundReply["type"]; id: string; title: string };
171
+ /** Status `failed`: Meta's errors, as reported. */
172
+ errors?: StatusUpdate["errors"];
124
173
  }
@@ -24,7 +24,7 @@
24
24
  */
25
25
  import { createHmac, timingSafeEqual } from "node:crypto";
26
26
  import { createServer, type Server } from "node:http";
27
- import type { ChannelBackend, InboundMessage, OutboundBody, SentMessage } from "../types.js";
27
+ import type { ChannelBackend, DeliveryState, InboundMessage, InboundReply, OutboundBody, SentMessage, StatusUpdate } from "../types.js";
28
28
 
29
29
  export interface CloudApiConfig {
30
30
  /**
@@ -140,23 +140,36 @@ interface CloudApiEnvelope {
140
140
  value?: {
141
141
  messaging_product?: string;
142
142
  metadata?: { phone_number_id?: string };
143
- messages?: Array<{ id?: string; from?: string; timestamp?: string; type?: string; text?: { body?: string } }>;
144
- statuses?: Array<{ id?: string; status?: string }>;
143
+ messages?: Array<{
144
+ id?: string;
145
+ from?: string;
146
+ timestamp?: string;
147
+ type?: string;
148
+ text?: { body?: string };
149
+ interactive?: { type?: string; button_reply?: { id?: string; title?: string }; list_reply?: { id?: string; title?: string } };
150
+ button?: { payload?: string; text?: string };
151
+ context?: { id?: string };
152
+ }>;
153
+ statuses?: Array<{ id?: string; status?: string; timestamp?: string; errors?: Array<{ code?: number; title?: string; message?: string; error_data?: { details?: string } }> }>;
145
154
  };
146
155
  }>;
147
156
  }>;
148
157
  }
149
158
 
150
159
  /**
151
- * The text messages inside one delivery. A delivery carries several entries,
152
- * each with several changes, each with several messages — and also status
153
- * updates, which are not messages. Anything that is not a `text` is dropped
154
- * with its id named: this channel reads words, and an image or a location the
155
- * agent cannot read must not be silently treated as an empty message.
160
+ * What one delivery carries. A delivery holds several entries, each with
161
+ * several changes, each with messages and with STATUS updates about messages
162
+ * we sent. A message is read when it is text, or a tapped quick reply (an
163
+ * interactive `button_reply` / `list_reply`, or a template's `button`), which
164
+ * keeps its id — the channel decides whether that id means anything. Anything
165
+ * else (an image, a location) is dropped with its id named: this channel
166
+ * reads words and taps, and an unreadable message must not become an empty
167
+ * turn.
156
168
  */
157
- export function parseInbound(payload: unknown): { messages: InboundMessage[]; ignored: Array<{ id: string; type: string }> } {
169
+ export function parseInbound(payload: unknown): { messages: InboundMessage[]; statuses: StatusUpdate[]; ignored: Array<{ id: string; type: string }> } {
158
170
  const envelope = payload as CloudApiEnvelope;
159
171
  const messages: InboundMessage[] = [];
172
+ const statuses: StatusUpdate[] = [];
160
173
  const ignored: Array<{ id: string; type: string }> = [];
161
174
  for (const entry of envelope.entry ?? []) {
162
175
  for (const change of entry.changes ?? []) {
@@ -164,15 +177,51 @@ export function parseInbound(payload: unknown): { messages: InboundMessage[]; ig
164
177
  const id = message.id;
165
178
  const from = message.from;
166
179
  if (!id || !from) continue;
180
+ const base = { id, from: from.startsWith("+") ? from : `+${from}`, timestamp: Number(message.timestamp ?? 0) };
181
+ const reply = replyOf(message);
182
+ if (reply) {
183
+ messages.push({ ...base, text: reply.title, reply: { ...reply, ...(message.context?.id ? { context_id: message.context.id } : {}) } });
184
+ continue;
185
+ }
167
186
  if (message.type !== "text" || !message.text?.body) {
168
- ignored.push({ id, type: message.type ?? "unknown" });
187
+ ignored.push({ id, type: message.type === "interactive" ? `interactive:${message.interactive?.type ?? "unknown"}` : message.type ?? "unknown" });
169
188
  continue;
170
189
  }
171
- messages.push({ id, from: from.startsWith("+") ? from : `+${from}`, text: message.text.body, timestamp: Number(message.timestamp ?? 0) });
190
+ messages.push({ ...base, text: message.text.body });
191
+ }
192
+ for (const status of change.value?.statuses ?? []) {
193
+ if (!status.id || !isDeliveryState(status.status)) continue;
194
+ statuses.push({
195
+ message_id: status.id,
196
+ status: status.status,
197
+ timestamp: Number(status.timestamp ?? 0),
198
+ errors: (status.errors ?? []).map((e) => ({
199
+ ...(typeof e.code === "number" ? { code: e.code } : {}),
200
+ ...(e.title ?? e.message ? { title: e.title ?? e.message } : {}),
201
+ ...(e.error_data?.details ? { details: e.error_data.details } : {}),
202
+ })),
203
+ });
172
204
  }
173
205
  }
174
206
  }
175
- return { messages, ignored };
207
+ return { messages, statuses, ignored };
208
+ }
209
+
210
+ type CloudMessage = NonNullable<NonNullable<NonNullable<NonNullable<CloudApiEnvelope["entry"]>[number]["changes"]>[number]["value"]>["messages"]>[number];
211
+
212
+ function replyOf(message: CloudMessage): Omit<InboundReply, "context_id"> | undefined {
213
+ if (message.type === "interactive") {
214
+ const kind = message.interactive?.type;
215
+ const picked = kind === "button_reply" ? message.interactive?.button_reply : kind === "list_reply" ? message.interactive?.list_reply : undefined;
216
+ if ((kind === "button_reply" || kind === "list_reply") && picked?.id) return { type: kind, id: picked.id, title: picked.title ?? picked.id };
217
+ return undefined;
218
+ }
219
+ if (message.type === "button" && message.button?.payload) return { type: "button", id: message.button.payload, title: message.button.text ?? message.button.payload };
220
+ return undefined;
221
+ }
222
+
223
+ function isDeliveryState(value: unknown): value is DeliveryState {
224
+ return value === "sent" || value === "delivered" || value === "read" || value === "failed";
176
225
  }
177
226
 
178
227
  export interface SendRequest {
@@ -200,10 +249,6 @@ export function buildSendRequest(config: CloudApiConfig, to: string, body: Outbo
200
249
  method: "POST" as const,
201
250
  headers: { authorization: `Bearer ${config.accessToken}`, "content-type": "application/json" },
202
251
  };
203
- // `to` loses its `+`: the Cloud API documents the number in international
204
- // format without one, and the emulator keys its conversation on the literal
205
- // string, so a `+` here and none on the inbound side splits one person into
206
- // two conversations (measured; docs/OPEN_QUESTIONS.md §46c).
207
252
  const recipient = { messaging_product: "whatsapp", recipient_type: "individual", to: toGraphNumber(to) };
208
253
  switch (body.kind) {
209
254
  case "text":
@@ -219,7 +264,7 @@ export function buildSendRequest(config: CloudApiConfig, to: string, body: Outbo
219
264
  template: {
220
265
  name: body.template,
221
266
  language: { code: body.language },
222
- ...(body.variables.length ? { components: [{ type: "body", parameters: body.variables.map((text) => ({ type: "text", text })) }] } : {}),
267
+ ...templateComponents(body),
223
268
  },
224
269
  }),
225
270
  };
@@ -228,6 +273,49 @@ export function buildSendRequest(config: CloudApiConfig, to: string, body: Outbo
228
273
  }
229
274
  }
230
275
 
276
+ /**
277
+ * A template's parameters as Meta takes them: the body's variables, and one
278
+ * `quick_reply` button component per declared reply, whose `payload` is the
279
+ * reply id — what comes back as `button.payload` when the person taps it.
280
+ */
281
+ function templateComponents(body: Extract<OutboundBody, { kind: "template" }>): { components?: unknown[] } {
282
+ const components: unknown[] = [];
283
+ if (body.variables.length) components.push({ type: "body", parameters: body.variables.map((text) => ({ type: "text", text })) });
284
+ for (const [index, button] of (body.buttons ?? []).entries()) components.push({ type: "button", sub_type: "quick_reply", index: String(index), parameters: [{ type: "payload", payload: button.id }] });
285
+ return components.length ? { components } : {};
286
+ }
287
+
288
+ /** Meta's error for a free-form message outside the 24 hours after the person's last one. */
289
+ export const WINDOW_CLOSED_ERROR = 131047;
290
+
291
+ /**
292
+ * What a non-2xx from `/messages` means, read from Meta's error body.
293
+ *
294
+ * One code gets its own rule: 131047 is the provider saying the 24-hour
295
+ * window is shut, which is the SAME rule the channel enforces before a send
296
+ * and is answered the same way — with a template. It reaches here only when
297
+ * the two clocks disagree (ours read the window open, the provider's read it
298
+ * shut), which at the boundary is a race and not a bug, so a caller has to be
299
+ * able to tell it from a refusal it cannot do anything about.
300
+ */
301
+ export function providerRefusal(status: number, body: string): { rule: string; detail: string } {
302
+ let error: { code?: unknown; message?: unknown; error_data?: { details?: unknown } } | undefined;
303
+ try {
304
+ error = (JSON.parse(body) as { error?: typeof error }).error;
305
+ } catch {
306
+ /* an error without Meta's envelope: the status is all there is */
307
+ }
308
+ const details = typeof error?.error_data?.details === "string" ? error.error_data.details : undefined;
309
+ if (error?.code === WINDOW_CLOSED_ERROR) {
310
+ return {
311
+ rule: "session_window_closed",
312
+ detail: `the provider refused the free-form message (${status}/${WINDOW_CLOSED_ERROR}): its 24h window is shut${details ? ` — ${details}` : ""}`,
313
+ };
314
+ }
315
+ const code = typeof error?.code === "number" ? `/${error.code}` : "";
316
+ return { rule: "provider_refused", detail: `${status}${code} from the Cloud API${details ? ` — ${details}` : ""}` };
317
+ }
318
+
231
319
  /* ── the impure half: one fetch over a request the above built ── */
232
320
 
233
321
  /** `+5511987654321` -> `5511987654321`, the only form the Graph API takes. */
@@ -249,7 +337,10 @@ export class WhatsAppCloudApi implements ChannelBackend {
249
337
  readonly live: boolean;
250
338
  private server: Server | undefined;
251
339
  private readonly queue: InboundMessage[] = [];
340
+ /** Message ids already handed on. Meta delivers at-least-once, so the same message can arrive twice. */
341
+ private readonly seen = new Set<string>();
252
342
  private waiting: ((m: InboundMessage | undefined) => void) | undefined;
343
+ private statusListener: ((status: StatusUpdate) => void) | undefined;
253
344
  private closed = false;
254
345
 
255
346
  constructor(private readonly options: CloudApiOptions) {
@@ -287,15 +378,16 @@ export class WhatsAppCloudApi implements ChannelBackend {
287
378
  res.writeHead(401, { "content-type": "application/json" }).end(JSON.stringify({ error: "bad_signature" }));
288
379
  return;
289
380
  }
290
- let parsed: { messages: InboundMessage[]; ignored: Array<{ id: string; type: string }> };
381
+ let parsed: ReturnType<typeof parseInbound>;
291
382
  try {
292
383
  parsed = parseInbound(JSON.parse(raw));
293
384
  } catch {
294
385
  res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: "invalid_json" }));
295
386
  return;
296
387
  }
297
- for (const ignored of parsed.ignored) say(`[whatsapp] ignored ${ignored.type} message ${ignored.id}: this channel reads text`);
388
+ for (const ignored of parsed.ignored) say(`[whatsapp] ignored ${ignored.type} message ${ignored.id}: this channel reads text and taps`);
298
389
  for (const message of parsed.messages) this.push(message);
390
+ for (const status of parsed.statuses) this.statusListener?.(status);
299
391
  // Always 200 once read: Meta retries on anything else, and a duplicate is not an error.
300
392
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ received: parsed.messages.length }));
301
393
  });
@@ -309,6 +401,13 @@ export class WhatsAppCloudApi implements ChannelBackend {
309
401
  }
310
402
 
311
403
  private push(message: InboundMessage): void {
404
+ // A redelivery is the same message, not a second turn: answering it twice
405
+ // would put two replies in front of the person for one thing they said.
406
+ if (this.seen.has(message.id)) {
407
+ this.options.say(`[whatsapp] duplicate delivery of ${message.id} dropped`);
408
+ return;
409
+ }
410
+ this.seen.add(message.id);
312
411
  if (this.waiting) {
313
412
  const resolve = this.waiting;
314
413
  this.waiting = undefined;
@@ -318,6 +417,10 @@ export class WhatsAppCloudApi implements ChannelBackend {
318
417
  this.queue.push(message);
319
418
  }
320
419
 
420
+ onStatus(listener: (status: StatusUpdate) => void): void {
421
+ this.statusListener = listener;
422
+ }
423
+
321
424
  async next(): Promise<InboundMessage | undefined> {
322
425
  if (this.closed) return undefined;
323
426
  const queued = this.queue.shift();
@@ -335,9 +438,7 @@ export class WhatsAppCloudApi implements ChannelBackend {
335
438
  const doFetch = this.options.fetchImpl ?? fetch;
336
439
  const response = await doFetch(request.url, { method: request.method, headers: request.headers, body: request.body });
337
440
  const text = await response.text();
338
- if (!response.ok) {
339
- return { id: "", state: "failed", refused: { rule: "provider_refused", detail: `${response.status} from the Cloud API` } };
340
- }
441
+ if (!response.ok) return { id: "", state: "failed", refused: providerRefusal(response.status, text) };
341
442
  let id = "";
342
443
  try {
343
444
  id = String((JSON.parse(text) as { messages?: Array<{ id?: string }> }).messages?.[0]?.id ?? "");
@@ -26,8 +26,8 @@
26
26
  * `npm run whatsapp:emulator` is the command.
27
27
  */
28
28
  import type { ConversationScript } from "@codespar/agent-core";
29
- import type { ChannelBackend, InboundMessage, OutboundBody, SentMessage } from "../types.js";
30
- import { toGraphNumber, WhatsAppCloudApi, type CloudApiOptions } from "./cloud-api.js";
29
+ import type { ChannelBackend, InboundMessage, OutboundBody, SentMessage, StatusUpdate } from "../types.js";
30
+ import { WhatsAppCloudApi, type CloudApiOptions } from "./cloud-api.js";
31
31
 
32
32
  export const EMULATOR_ENV = {
33
33
  url: "WHATSAPP_SIM_URL",
@@ -93,18 +93,23 @@ export class EmulatorDriver {
93
93
  return this.post("/_sim/clock", { advance_hours: hours });
94
94
  }
95
95
 
96
- /** The person writes. The emulator answers with a signed inbound webhook to our receiver. */
97
- inbound(options: { phoneNumberId: string; from: string; text: string; sentAt?: Date }): Promise<unknown> {
96
+ /** The person writes, or taps a quick reply. The emulator answers with a signed inbound webhook to our receiver. */
97
+ inbound(options: { phoneNumberId: string; from: string; text: string; sentAt?: Date } | { phoneNumberId: string; from: string; reply: { id: string; title: string }; sentAt?: Date }): Promise<unknown> {
98
98
  return this.post("/_sim/inbound", {
99
99
  phone_number_id: options.phoneNumberId,
100
- // Without the `+`, matching what `deliver` sends as `to`: the emulator keys
101
- // its conversation on the literal string, so the two forms are two people.
102
- from: toGraphNumber(options.from),
103
- text: options.text,
100
+ from: options.from,
101
+ ...("reply" in options
102
+ ? { type: "interactive", interactive: { type: "button_reply", button_reply: { id: options.reply.id, title: options.reply.title } } }
103
+ : { text: options.text }),
104
104
  ...(options.sentAt ? { sent_at: options.sentAt.toISOString() } : {}),
105
105
  });
106
106
  }
107
107
 
108
+ /** Reports a delivery status for a message the business sent, as the provider would: `read`, or `failed` with a reason. */
109
+ status(options: { status: "read" | "failed"; messageId: string; reason?: string }): Promise<unknown> {
110
+ return this.post("/_sim/status", { status: options.status, message_id: options.messageId, ...(options.reason ? { reason: options.reason } : {}) });
111
+ }
112
+
108
113
  /** The priced timeline, which is the emulator's own reason for existing. Read for the console, never asserted on. */
109
114
  state(key: string): Promise<{ priced?: { total?: number; currency?: string } } | undefined> {
110
115
  return this.call(`/_sim/state?key=${encodeURIComponent(key)}`).then(
@@ -156,20 +161,21 @@ export class WhatsAppEmulator implements ChannelBackend {
156
161
 
157
162
  async next(): Promise<InboundMessage | undefined> {
158
163
  if (this.closed) return undefined;
159
- const text = await this.nextText();
160
- if (text === undefined) return undefined;
161
- // The person writes THROUGH the emulator: it mints the message id and posts
162
- // the signed webhook our receiver verifies, so a scripted turn takes the
163
- // same path a real one would.
164
- await this.options.driver.inbound({
165
- phoneNumberId: this.options.config.phoneNumberId,
166
- from: this.options.conversation.contact,
167
- text,
168
- });
164
+ const turn = await this.nextTurn();
165
+ if (turn === undefined) return undefined;
166
+ // The person writes (or taps) THROUGH the emulator: it mints the message id
167
+ // and posts the signed webhook our receiver verifies, so a scripted turn
168
+ // takes the same path a real one would.
169
+ const who = { phoneNumberId: this.options.config.phoneNumberId, from: this.options.conversation.contact };
170
+ await this.options.driver.inbound(typeof turn === "string" ? { ...who, text: turn } : { ...who, reply: turn });
169
171
  return this.api.next();
170
172
  }
171
173
 
172
- private async nextText(): Promise<string | undefined> {
174
+ onStatus(listener: (status: StatusUpdate) => void): void {
175
+ this.api.onStatus(listener);
176
+ }
177
+
178
+ private async nextTurn(): Promise<string | { id: string; title: string } | undefined> {
173
179
  const script = this.options.script;
174
180
  if (script) {
175
181
  const turn = script.turns[this.turn];
@@ -178,8 +184,12 @@ export class WhatsAppEmulator implements ChannelBackend {
178
184
  // The person's own pause, on the emulator's clock. A conversation that
179
185
  // happens over days is what closes the 24-hour window.
180
186
  if (turn.after_seconds > 0) await this.options.driver.advanceHours(turn.after_seconds / 3600);
181
- return turn.text;
187
+ return turn.reply ? { id: turn.reply.id, title: turn.reply.title ?? turn.reply.id } : turn.text;
182
188
  }
189
+ return this.nextText();
190
+ }
191
+
192
+ private async nextText(): Promise<string | undefined> {
183
193
  if (!this.options.ask) return undefined;
184
194
  let answer: string;
185
195
  try {