@codespar/cli 0.14.0 → 0.16.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 (538) hide show
  1. package/README.md +44 -17
  2. package/dist/commands/audit-replay.js +267 -0
  3. package/dist/commands/audit-replay.js.map +1 -0
  4. package/dist/commands/init.js +28 -0
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/commands/login.js +2 -1
  7. package/dist/commands/login.js.map +1 -1
  8. package/dist/commands/mandate-verify.js +75 -27
  9. package/dist/commands/mandate-verify.js.map +1 -1
  10. package/dist/config.js +17 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/did.js +262 -41
  13. package/dist/did.js.map +1 -1
  14. package/dist/index.js +33 -7
  15. package/dist/index.js.map +1 -1
  16. package/dist/surface.js +4 -1
  17. package/dist/surface.js.map +1 -1
  18. package/package.json +1 -1
  19. package/templates/bills-agent/README.md +8 -5
  20. package/templates/bills-agent/agents/bills-agent/.env.example +3 -3
  21. package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
  22. package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
  23. package/templates/bills-agent/agents/bills-agent/README.md +62 -22
  24. package/templates/bills-agent/agents/bills-agent/SYSTEM_PROMPT.md +1 -1
  25. package/templates/bills-agent/agents/bills-agent/agent.yaml +2 -2
  26. package/templates/bills-agent/agents/bills-agent/package.json +14 -11
  27. package/templates/bills-agent/agents/bills-agent/src/kit.ts +193 -0
  28. package/templates/bills-agent/agents/bills-agent/test/adversarial.test.ts +8 -7
  29. package/templates/bills-agent/agents/bills-agent/test/check.test.ts +19 -2
  30. package/templates/bills-agent/agents/bills-agent/test/cli.test.ts +72 -24
  31. package/templates/bills-agent/agents/bills-agent/test/fixtures/excursao-300.transcript.jsonl +2 -0
  32. package/templates/bills-agent/agents/bills-agent/test/fixtures/material-400.transcript.jsonl +2 -0
  33. package/templates/bills-agent/agents/bills-agent/test/inspect.test.ts +124 -0
  34. package/templates/bills-agent/agents/bills-agent/test/provider.test.ts +25 -0
  35. package/templates/bills-agent/agents/bills-agent/test/scenarios.test.ts +10 -9
  36. package/templates/bills-agent/agents/bills-agent/tsconfig.json +0 -1
  37. package/templates/bills-agent/package.json +5 -2
  38. package/templates/bills-agent/packages/agent-core/README.md +2 -0
  39. package/templates/bills-agent/packages/agent-core/package.json +1 -1
  40. package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +27 -6
  41. package/templates/bills-agent/packages/agent-core/src/api/rail.ts +16 -0
  42. package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +20 -7
  43. package/templates/bills-agent/packages/agent-core/src/approval.ts +16 -2
  44. package/templates/bills-agent/packages/agent-core/src/bundle.ts +50 -4
  45. package/templates/bills-agent/packages/agent-core/src/channels.ts +140 -0
  46. package/templates/bills-agent/packages/agent-core/src/check.ts +90 -8
  47. package/templates/bills-agent/packages/agent-core/src/clock.ts +42 -0
  48. package/templates/bills-agent/packages/agent-core/src/engine.ts +119 -18
  49. package/templates/bills-agent/packages/agent-core/src/hash.ts +13 -0
  50. package/templates/bills-agent/packages/agent-core/src/index.ts +3 -0
  51. package/templates/bills-agent/packages/agent-core/src/manifest.ts +1 -1
  52. package/templates/bills-agent/packages/agent-core/src/rail.ts +16 -2
  53. package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +401 -0
  54. package/templates/bills-agent/packages/agent-core/src/state-machine.ts +3 -1
  55. package/templates/bills-agent/packages/agent-core/src/stubs/charge-rail.ts +2 -0
  56. package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +19 -2
  57. package/templates/bills-agent/packages/agent-core/src/types.ts +28 -0
  58. package/templates/bills-agent/packages/agent-core/test/approval.test.ts +36 -2
  59. package/templates/bills-agent/packages/agent-core/test/bundle.test.ts +85 -0
  60. package/templates/bills-agent/packages/agent-core/test/clock.test.ts +25 -0
  61. package/templates/bills-agent/packages/agent-core/test/engine.test.ts +73 -1
  62. package/templates/bills-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  63. package/templates/bills-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  64. package/templates/bills-agent/packages/agent-core/test/helpers.ts +1 -1
  65. package/templates/bills-agent/packages/agent-core/test/manifest.test.ts +2 -2
  66. package/templates/bills-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  67. package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +51 -0
  68. package/templates/bills-agent/packages/agent-runtime/README.md +68 -0
  69. package/templates/bills-agent/packages/agent-runtime/bin.mjs +32 -0
  70. package/templates/bills-agent/packages/agent-runtime/package.json +23 -0
  71. package/templates/bills-agent/{agents/bills-agent → packages/agent-runtime}/src/adversarial.ts +36 -44
  72. package/templates/bills-agent/packages/agent-runtime/src/agent.ts +68 -0
  73. package/templates/bills-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  74. package/templates/bills-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  75. package/templates/bills-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  76. package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +124 -0
  77. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
  78. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
  79. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  80. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
  81. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
  82. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  83. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
  84. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
  85. package/templates/bills-agent/packages/agent-runtime/src/cli.ts +98 -0
  86. package/templates/bills-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  87. package/templates/bills-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  88. package/templates/bills-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  89. package/templates/bills-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  90. package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
  91. package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
  92. package/templates/bills-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  93. package/templates/bills-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  94. package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
  95. package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
  96. package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +245 -0
  97. package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
  98. package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
  99. package/templates/bills-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  100. package/templates/bills-agent/packages/agent-runtime/src/index.ts +12 -0
  101. package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +797 -0
  102. package/templates/bills-agent/packages/agent-runtime/src/kit.ts +206 -0
  103. package/templates/{collections-agent/agents/collections-agent → bills-agent/packages/agent-runtime}/src/poll.ts +22 -11
  104. package/templates/{collections-agent/agents/collections-agent → bills-agent/packages/agent-runtime}/src/scenarios.ts +50 -34
  105. package/templates/bills-agent/{agents/bills-agent → packages/agent-runtime}/src/setup.ts +88 -66
  106. package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +154 -0
  107. package/templates/{collections-agent/agents/collections-agent/channels/webhook/index.ts → bills-agent/packages/agent-runtime/src/webhook.ts} +1 -1
  108. package/templates/bills-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  109. package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
  110. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
  111. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
  112. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  113. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  114. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
  115. package/templates/bills-agent/packages/agent-runtime/tsconfig.json +9 -0
  116. package/templates/bills-agent/vitest.config.ts +1 -1
  117. package/templates/collections-agent/README.md +8 -5
  118. package/templates/collections-agent/agents/collections-agent/.env.example +14 -0
  119. package/templates/collections-agent/agents/collections-agent/AGENTS.md +6 -4
  120. package/templates/collections-agent/agents/collections-agent/CLAUDE.md +6 -4
  121. package/templates/collections-agent/agents/collections-agent/README.md +184 -16
  122. package/templates/collections-agent/agents/collections-agent/agent.yaml +3 -3
  123. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +111 -0
  124. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042.json +11 -0
  125. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1103.json +11 -0
  126. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +23 -0
  127. package/templates/collections-agent/agents/collections-agent/package.json +12 -10
  128. package/templates/collections-agent/agents/collections-agent/runbook.md +52 -1
  129. package/templates/collections-agent/agents/collections-agent/src/kit.ts +265 -0
  130. package/templates/collections-agent/agents/collections-agent/test/adversarial.test.ts +9 -8
  131. package/templates/collections-agent/agents/collections-agent/test/check.test.ts +43 -1
  132. package/templates/collections-agent/agents/collections-agent/test/cli.test.ts +56 -18
  133. package/templates/collections-agent/agents/collections-agent/test/fixtures/agreed-1042-awaiting-payer.transcript.jsonl +4 -0
  134. package/templates/collections-agent/agents/collections-agent/test/scenarios.test.ts +11 -10
  135. package/templates/collections-agent/agents/collections-agent/test/webhook.test.ts +4 -7
  136. package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +271 -0
  137. package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +213 -0
  138. package/templates/collections-agent/agents/collections-agent/tsconfig.json +0 -1
  139. package/templates/collections-agent/package.json +3 -1
  140. package/templates/collections-agent/packages/agent-core/README.md +2 -0
  141. package/templates/collections-agent/packages/agent-core/package.json +1 -1
  142. package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +27 -6
  143. package/templates/collections-agent/packages/agent-core/src/api/rail.ts +16 -0
  144. package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +20 -7
  145. package/templates/collections-agent/packages/agent-core/src/approval.ts +16 -2
  146. package/templates/collections-agent/packages/agent-core/src/bundle.ts +50 -4
  147. package/templates/collections-agent/packages/agent-core/src/channels.ts +140 -0
  148. package/templates/collections-agent/packages/agent-core/src/check.ts +90 -8
  149. package/templates/collections-agent/packages/agent-core/src/clock.ts +42 -0
  150. package/templates/collections-agent/packages/agent-core/src/engine.ts +119 -18
  151. package/templates/collections-agent/packages/agent-core/src/hash.ts +13 -0
  152. package/templates/collections-agent/packages/agent-core/src/index.ts +3 -0
  153. package/templates/collections-agent/packages/agent-core/src/manifest.ts +1 -1
  154. package/templates/collections-agent/packages/agent-core/src/rail.ts +16 -2
  155. package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +401 -0
  156. package/templates/collections-agent/packages/agent-core/src/state-machine.ts +3 -1
  157. package/templates/collections-agent/packages/agent-core/src/stubs/charge-rail.ts +2 -0
  158. package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +19 -2
  159. package/templates/collections-agent/packages/agent-core/src/types.ts +28 -0
  160. package/templates/collections-agent/packages/agent-core/test/approval.test.ts +36 -2
  161. package/templates/collections-agent/packages/agent-core/test/bundle.test.ts +85 -0
  162. package/templates/collections-agent/packages/agent-core/test/clock.test.ts +25 -0
  163. package/templates/collections-agent/packages/agent-core/test/engine.test.ts +73 -1
  164. package/templates/collections-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  165. package/templates/collections-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  166. package/templates/collections-agent/packages/agent-core/test/helpers.ts +1 -1
  167. package/templates/collections-agent/packages/agent-core/test/manifest.test.ts +2 -2
  168. package/templates/collections-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  169. package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +51 -0
  170. package/templates/collections-agent/packages/agent-runtime/README.md +68 -0
  171. package/templates/collections-agent/packages/agent-runtime/bin.mjs +32 -0
  172. package/templates/collections-agent/packages/agent-runtime/package.json +23 -0
  173. package/templates/collections-agent/{agents/collections-agent → packages/agent-runtime}/src/adversarial.ts +29 -50
  174. package/templates/collections-agent/packages/agent-runtime/src/agent.ts +68 -0
  175. package/templates/collections-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  176. package/templates/collections-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  177. package/templates/collections-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  178. package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +124 -0
  179. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
  180. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
  181. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  182. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
  183. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
  184. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  185. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
  186. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
  187. package/templates/collections-agent/packages/agent-runtime/src/cli.ts +98 -0
  188. package/templates/collections-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  189. package/templates/collections-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  190. package/templates/collections-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  191. package/templates/collections-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  192. package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
  193. package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
  194. package/templates/collections-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  195. package/templates/collections-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  196. package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
  197. package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
  198. package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +245 -0
  199. package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
  200. package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
  201. package/templates/collections-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  202. package/templates/collections-agent/packages/agent-runtime/src/index.ts +12 -0
  203. package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +797 -0
  204. package/templates/collections-agent/packages/agent-runtime/src/kit.ts +206 -0
  205. package/templates/collections-agent/packages/agent-runtime/src/poll.ts +105 -0
  206. package/templates/{bills-agent/agents/bills-agent → collections-agent/packages/agent-runtime}/src/scenarios.ts +98 -26
  207. package/templates/collections-agent/packages/agent-runtime/src/setup.ts +229 -0
  208. package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +154 -0
  209. package/templates/collections-agent/packages/agent-runtime/src/webhook.ts +131 -0
  210. package/templates/collections-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  211. package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
  212. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
  213. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
  214. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  215. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  216. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
  217. package/templates/collections-agent/packages/agent-runtime/tsconfig.json +9 -0
  218. package/templates/collections-agent/vitest.config.ts +1 -1
  219. package/templates/hello-agent/LICENSE +21 -0
  220. package/templates/hello-agent/README.md +23 -0
  221. package/templates/hello-agent/_gitignore +10 -0
  222. package/templates/hello-agent/agents/hello-agent/.env.example +5 -0
  223. package/templates/hello-agent/agents/hello-agent/AGENTS.md +15 -0
  224. package/templates/hello-agent/agents/hello-agent/CLAUDE.md +15 -0
  225. package/templates/hello-agent/agents/hello-agent/README.md +33 -0
  226. package/templates/hello-agent/agents/hello-agent/SYSTEM_PROMPT.md +19 -0
  227. package/templates/hello-agent/agents/hello-agent/agent.yaml +19 -0
  228. package/templates/hello-agent/agents/hello-agent/evals/adversarial/beneficiary-swap.json +6 -0
  229. package/templates/hello-agent/agents/hello-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +2 -0
  230. package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.json +6 -0
  231. package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.transcript.jsonl +2 -0
  232. package/templates/hello-agent/agents/hello-agent/evals/adversarial/false-authority.json +6 -0
  233. package/templates/hello-agent/agents/hello-agent/evals/adversarial/false-authority.transcript.jsonl +2 -0
  234. package/templates/hello-agent/agents/hello-agent/evals/adversarial/fractioning.json +6 -0
  235. package/templates/hello-agent/agents/hello-agent/evals/adversarial/fractioning.transcript.jsonl +2 -0
  236. package/templates/hello-agent/agents/hello-agent/evals/adversarial/model-total.json +6 -0
  237. package/templates/hello-agent/agents/hello-agent/evals/adversarial/model-total.transcript.jsonl +2 -0
  238. package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.json +6 -0
  239. package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.transcript.jsonl +2 -0
  240. package/templates/hello-agent/agents/hello-agent/evals/adversarial/webhook-replay.json +6 -0
  241. package/templates/hello-agent/agents/hello-agent/evals/eval.yaml +7 -0
  242. package/templates/hello-agent/agents/hello-agent/guardrails.json +8 -0
  243. package/templates/hello-agent/agents/hello-agent/mandate.example.json +17 -0
  244. package/templates/hello-agent/agents/hello-agent/package.json +19 -0
  245. package/templates/hello-agent/agents/hello-agent/runbook.md +10 -0
  246. package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.json +6 -0
  247. package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.transcript.jsonl +2 -0
  248. package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.json +6 -0
  249. package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.transcript.jsonl +2 -0
  250. package/templates/hello-agent/agents/hello-agent/src/bills.ts +23 -0
  251. package/templates/hello-agent/agents/hello-agent/src/kit.ts +33 -0
  252. package/templates/hello-agent/agents/hello-agent/tools.json +11 -0
  253. package/templates/hello-agent/agents/hello-agent/tsconfig.json +9 -0
  254. package/templates/hello-agent/package.json +30 -0
  255. package/templates/hello-agent/packages/agent-core/README.md +21 -0
  256. package/templates/hello-agent/packages/agent-core/package.json +24 -0
  257. package/templates/hello-agent/packages/agent-core/src/agent.ts +125 -0
  258. package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +175 -0
  259. package/templates/hello-agent/packages/agent-core/src/api/client.ts +53 -0
  260. package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +58 -0
  261. package/templates/hello-agent/packages/agent-core/src/api/rail.ts +117 -0
  262. package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +58 -0
  263. package/templates/hello-agent/packages/agent-core/src/approval.ts +123 -0
  264. package/templates/hello-agent/packages/agent-core/src/bundle.ts +159 -0
  265. package/templates/hello-agent/packages/agent-core/src/channels.ts +140 -0
  266. package/templates/hello-agent/packages/agent-core/src/check.ts +253 -0
  267. package/templates/hello-agent/packages/agent-core/src/clock.ts +42 -0
  268. package/templates/hello-agent/packages/agent-core/src/engine.ts +885 -0
  269. package/templates/hello-agent/packages/agent-core/src/escalate.ts +85 -0
  270. package/templates/hello-agent/packages/agent-core/src/events.ts +40 -0
  271. package/templates/hello-agent/packages/agent-core/src/guardrails.ts +45 -0
  272. package/templates/hello-agent/packages/agent-core/src/hash.ts +62 -0
  273. package/templates/hello-agent/packages/agent-core/src/ids.ts +10 -0
  274. package/templates/hello-agent/packages/agent-core/src/index.ts +32 -0
  275. package/templates/hello-agent/packages/agent-core/src/mandate.ts +100 -0
  276. package/templates/hello-agent/packages/agent-core/src/manifest.ts +76 -0
  277. package/templates/hello-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
  278. package/templates/hello-agent/packages/agent-core/src/providers/replay.ts +63 -0
  279. package/templates/hello-agent/packages/agent-core/src/providers/types.ts +39 -0
  280. package/templates/hello-agent/packages/agent-core/src/rail.ts +101 -0
  281. package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +401 -0
  282. package/templates/hello-agent/packages/agent-core/src/revocation.ts +30 -0
  283. package/templates/hello-agent/packages/agent-core/src/secrets.ts +30 -0
  284. package/templates/hello-agent/packages/agent-core/src/state/store.ts +307 -0
  285. package/templates/hello-agent/packages/agent-core/src/state-machine.ts +148 -0
  286. package/templates/hello-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
  287. package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +57 -0
  288. package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +141 -0
  289. package/templates/hello-agent/packages/agent-core/src/tools.ts +55 -0
  290. package/templates/hello-agent/packages/agent-core/src/types.ts +134 -0
  291. package/templates/hello-agent/packages/agent-core/test/approval.test.ts +117 -0
  292. package/templates/hello-agent/packages/agent-core/test/bundle.test.ts +85 -0
  293. package/templates/hello-agent/packages/agent-core/test/clock.test.ts +25 -0
  294. package/templates/hello-agent/packages/agent-core/test/engine.test.ts +596 -0
  295. package/templates/hello-agent/packages/agent-core/test/escalate.test.ts +42 -0
  296. package/templates/hello-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  297. package/templates/hello-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  298. package/templates/hello-agent/packages/agent-core/test/helpers.ts +139 -0
  299. package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +153 -0
  300. package/templates/hello-agent/packages/agent-core/test/manifest.test.ts +45 -0
  301. package/templates/hello-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  302. package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +316 -0
  303. package/templates/hello-agent/packages/agent-core/test/secrets.test.ts +28 -0
  304. package/templates/hello-agent/packages/agent-core/test/state-machine.test.ts +67 -0
  305. package/templates/hello-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
  306. package/templates/hello-agent/packages/agent-core/tsconfig.json +10 -0
  307. package/templates/hello-agent/packages/agent-runtime/README.md +68 -0
  308. package/templates/hello-agent/packages/agent-runtime/bin.mjs +32 -0
  309. package/templates/hello-agent/packages/agent-runtime/package.json +23 -0
  310. package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +169 -0
  311. package/templates/hello-agent/packages/agent-runtime/src/agent.ts +68 -0
  312. package/templates/hello-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  313. package/templates/hello-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  314. package/templates/hello-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  315. package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +124 -0
  316. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
  317. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
  318. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  319. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
  320. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
  321. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  322. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
  323. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
  324. package/templates/hello-agent/packages/agent-runtime/src/cli.ts +98 -0
  325. package/templates/hello-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  326. package/templates/hello-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  327. package/templates/hello-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  328. package/templates/hello-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  329. package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
  330. package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
  331. package/templates/hello-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  332. package/templates/hello-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  333. package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
  334. package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
  335. package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +245 -0
  336. package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
  337. package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
  338. package/templates/hello-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  339. package/templates/hello-agent/packages/agent-runtime/src/index.ts +12 -0
  340. package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +797 -0
  341. package/templates/hello-agent/packages/agent-runtime/src/kit.ts +206 -0
  342. package/templates/hello-agent/packages/agent-runtime/src/poll.ts +105 -0
  343. package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +268 -0
  344. package/templates/hello-agent/packages/agent-runtime/src/setup.ts +229 -0
  345. package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +154 -0
  346. package/templates/hello-agent/packages/agent-runtime/src/webhook.ts +131 -0
  347. package/templates/hello-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  348. package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
  349. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
  350. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
  351. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  352. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  353. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
  354. package/templates/hello-agent/packages/agent-runtime/tsconfig.json +9 -0
  355. package/templates/hello-agent/tsconfig.base.json +16 -0
  356. package/templates/hello-agent/vitest.config.ts +12 -0
  357. package/templates/kits.lock.json +60 -12
  358. package/templates/supplier-payments-agent/LICENSE +21 -0
  359. package/templates/supplier-payments-agent/README.md +23 -0
  360. package/templates/supplier-payments-agent/_gitignore +10 -0
  361. package/templates/supplier-payments-agent/agents/supplier-payments-agent/.env.example +7 -0
  362. package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +21 -0
  363. package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +21 -0
  364. package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +118 -0
  365. package/templates/supplier-payments-agent/agents/supplier-payments-agent/SYSTEM_PROMPT.md +29 -0
  366. package/templates/supplier-payments-agent/agents/supplier-payments-agent/agent.yaml +26 -0
  367. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/batch-tampering.json +12 -0
  368. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/batch-tampering.transcript.jsonl +3 -0
  369. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap-mandate.json +9 -0
  370. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap.json +10 -0
  371. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +2 -0
  372. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.json +13 -0
  373. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
  374. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.json +9 -0
  375. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.transcript.jsonl +3 -0
  376. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/fractioning.json +15 -0
  377. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/fractioning.transcript.jsonl +6 -0
  378. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/model-total.json +10 -0
  379. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/model-total.transcript.jsonl +2 -0
  380. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.json +9 -0
  381. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.transcript.jsonl +2 -0
  382. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/webhook-replay.json +9 -0
  383. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/eval.yaml +9 -0
  384. package/templates/supplier-payments-agent/agents/supplier-payments-agent/guardrails.json +15 -0
  385. package/templates/supplier-payments-agent/agents/supplier-payments-agent/mandate.example.json +70 -0
  386. package/templates/supplier-payments-agent/agents/supplier-payments-agent/package.json +26 -0
  387. package/templates/supplier-payments-agent/agents/supplier-payments-agent/runbook.md +19 -0
  388. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.json +14 -0
  389. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +2 -0
  390. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.json +12 -0
  391. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.transcript.jsonl +2 -0
  392. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.json +20 -0
  393. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.transcript.jsonl +7 -0
  394. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.json +19 -0
  395. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.transcript.jsonl +3 -0
  396. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.json +21 -0
  397. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.transcript.jsonl +9 -0
  398. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.json +21 -0
  399. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.transcript.jsonl +3 -0
  400. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.json +12 -0
  401. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.transcript.jsonl +2 -0
  402. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +176 -0
  403. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +345 -0
  404. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/pix-out.ts +118 -0
  405. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/payables.ts +76 -0
  406. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/adversarial.test.ts +53 -0
  407. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +384 -0
  408. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/check.test.ts +109 -0
  409. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/scenarios.test.ts +81 -0
  410. package/templates/supplier-payments-agent/agents/supplier-payments-agent/tools.json +53 -0
  411. package/templates/supplier-payments-agent/agents/supplier-payments-agent/tsconfig.json +9 -0
  412. package/templates/supplier-payments-agent/package.json +36 -0
  413. package/templates/supplier-payments-agent/packages/agent-core/README.md +21 -0
  414. package/templates/supplier-payments-agent/packages/agent-core/package.json +24 -0
  415. package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +125 -0
  416. package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +175 -0
  417. package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +53 -0
  418. package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +58 -0
  419. package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +117 -0
  420. package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +58 -0
  421. package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +123 -0
  422. package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +159 -0
  423. package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +140 -0
  424. package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +253 -0
  425. package/templates/supplier-payments-agent/packages/agent-core/src/clock.ts +42 -0
  426. package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +885 -0
  427. package/templates/supplier-payments-agent/packages/agent-core/src/escalate.ts +85 -0
  428. package/templates/supplier-payments-agent/packages/agent-core/src/events.ts +40 -0
  429. package/templates/supplier-payments-agent/packages/agent-core/src/guardrails.ts +45 -0
  430. package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +62 -0
  431. package/templates/supplier-payments-agent/packages/agent-core/src/ids.ts +10 -0
  432. package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +32 -0
  433. package/templates/supplier-payments-agent/packages/agent-core/src/mandate.ts +100 -0
  434. package/templates/supplier-payments-agent/packages/agent-core/src/manifest.ts +76 -0
  435. package/templates/supplier-payments-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
  436. package/templates/supplier-payments-agent/packages/agent-core/src/providers/replay.ts +63 -0
  437. package/templates/supplier-payments-agent/packages/agent-core/src/providers/types.ts +39 -0
  438. package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +101 -0
  439. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +401 -0
  440. package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +30 -0
  441. package/templates/supplier-payments-agent/packages/agent-core/src/secrets.ts +30 -0
  442. package/templates/supplier-payments-agent/packages/agent-core/src/state/store.ts +307 -0
  443. package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +148 -0
  444. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
  445. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +57 -0
  446. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +141 -0
  447. package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +55 -0
  448. package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +134 -0
  449. package/templates/supplier-payments-agent/packages/agent-core/test/approval.test.ts +117 -0
  450. package/templates/supplier-payments-agent/packages/agent-core/test/bundle.test.ts +85 -0
  451. package/templates/supplier-payments-agent/packages/agent-core/test/clock.test.ts +25 -0
  452. package/templates/supplier-payments-agent/packages/agent-core/test/engine.test.ts +596 -0
  453. package/templates/supplier-payments-agent/packages/agent-core/test/escalate.test.ts +42 -0
  454. package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  455. package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  456. package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +139 -0
  457. package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +153 -0
  458. package/templates/supplier-payments-agent/packages/agent-core/test/manifest.test.ts +45 -0
  459. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  460. package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +316 -0
  461. package/templates/supplier-payments-agent/packages/agent-core/test/secrets.test.ts +28 -0
  462. package/templates/supplier-payments-agent/packages/agent-core/test/state-machine.test.ts +67 -0
  463. package/templates/supplier-payments-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
  464. package/templates/supplier-payments-agent/packages/agent-core/tsconfig.json +10 -0
  465. package/templates/supplier-payments-agent/packages/agent-runtime/README.md +68 -0
  466. package/templates/supplier-payments-agent/packages/agent-runtime/bin.mjs +32 -0
  467. package/templates/supplier-payments-agent/packages/agent-runtime/package.json +23 -0
  468. package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +169 -0
  469. package/templates/supplier-payments-agent/packages/agent-runtime/src/agent.ts +68 -0
  470. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  471. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  472. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  473. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +124 -0
  474. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
  475. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
  476. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  477. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
  478. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
  479. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  480. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
  481. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
  482. package/templates/supplier-payments-agent/packages/agent-runtime/src/cli.ts +98 -0
  483. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  484. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  485. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  486. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  487. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
  488. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
  489. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  490. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  491. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
  492. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
  493. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +245 -0
  494. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
  495. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
  496. package/templates/supplier-payments-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  497. package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +12 -0
  498. package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +797 -0
  499. package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +206 -0
  500. package/templates/supplier-payments-agent/packages/agent-runtime/src/poll.ts +105 -0
  501. package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +268 -0
  502. package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +229 -0
  503. package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +154 -0
  504. package/templates/supplier-payments-agent/packages/agent-runtime/src/webhook.ts +131 -0
  505. package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  506. package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
  507. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
  508. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
  509. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  510. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  511. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
  512. package/templates/supplier-payments-agent/packages/agent-runtime/tsconfig.json +9 -0
  513. package/templates/supplier-payments-agent/tsconfig.base.json +16 -0
  514. package/templates/supplier-payments-agent/vitest.config.ts +12 -0
  515. package/templates/bills-agent/agents/bills-agent/channels/terminal/index.ts +0 -108
  516. package/templates/bills-agent/agents/bills-agent/src/commands/approve.ts +0 -9
  517. package/templates/bills-agent/agents/bills-agent/src/commands/check.ts +0 -13
  518. package/templates/bills-agent/agents/bills-agent/src/commands/consent.ts +0 -26
  519. package/templates/bills-agent/agents/bills-agent/src/commands/decide.ts +0 -45
  520. package/templates/bills-agent/agents/bills-agent/src/commands/deny.ts +0 -7
  521. package/templates/bills-agent/agents/bills-agent/src/commands/eval.ts +0 -37
  522. package/templates/bills-agent/agents/bills-agent/src/commands/reconcile.ts +0 -38
  523. package/templates/bills-agent/agents/bills-agent/src/commands/rerun.ts +0 -65
  524. package/templates/bills-agent/agents/bills-agent/src/commands/resume.ts +0 -30
  525. package/templates/bills-agent/agents/bills-agent/src/main.ts +0 -185
  526. package/templates/collections-agent/agents/collections-agent/channels/terminal/index.ts +0 -178
  527. package/templates/collections-agent/agents/collections-agent/src/commands/approve.ts +0 -10
  528. package/templates/collections-agent/agents/collections-agent/src/commands/check.ts +0 -13
  529. package/templates/collections-agent/agents/collections-agent/src/commands/decide.ts +0 -48
  530. package/templates/collections-agent/agents/collections-agent/src/commands/deny.ts +0 -7
  531. package/templates/collections-agent/agents/collections-agent/src/commands/eval.ts +0 -39
  532. package/templates/collections-agent/agents/collections-agent/src/commands/poll.ts +0 -35
  533. package/templates/collections-agent/agents/collections-agent/src/commands/reconcile.ts +0 -38
  534. package/templates/collections-agent/agents/collections-agent/src/commands/rerun.ts +0 -55
  535. package/templates/collections-agent/agents/collections-agent/src/commands/resume.ts +0 -30
  536. package/templates/collections-agent/agents/collections-agent/src/commands/webhook.ts +0 -36
  537. package/templates/collections-agent/agents/collections-agent/src/main.ts +0 -193
  538. package/templates/collections-agent/agents/collections-agent/src/setup.ts +0 -250
@@ -0,0 +1,358 @@
1
+ /**
2
+ * The official backend: Meta's WhatsApp Cloud API, behind the same interface
3
+ * the simulator implements. An ADAPTER and nothing more — the rules, the
4
+ * session window and the conversation record are the channel's, above this.
5
+ *
6
+ * Three things about it are deliberate.
7
+ *
8
+ * The credentials are ABSENT by default and this backend refuses to open
9
+ * without them, by name. No fallback, no "development mode" that quietly
10
+ * talks to nobody: a channel that pretends to send is worse than one that
11
+ * says it cannot.
12
+ *
13
+ * Nothing here is called from a test or from the CI, ever. What the tests
14
+ * exercise are the four pure functions — the signature check, the webhook
15
+ * parse, the verification handshake and the request builder — which is
16
+ * exactly the part where a mistake is silent. The send itself is one `fetch`
17
+ * over a request those functions built.
18
+ *
19
+ * And the shapes are written against Meta's published documentation, not
20
+ * against a call we made: nobody on this repo has a Business account, so the
21
+ * first live run belongs to the developer who has one. The README says that
22
+ * in those words, and `docs/OPEN_QUESTIONS.md` names what a live run would
23
+ * settle.
24
+ */
25
+ import { createHmac, timingSafeEqual } from "node:crypto";
26
+ import { createServer, type Server } from "node:http";
27
+ import type { ChannelBackend, InboundMessage, OutboundBody, SentMessage } from "../types.js";
28
+
29
+ export interface CloudApiConfig {
30
+ /**
31
+ * Where the Graph API lives. Meta's own by default, and the local emulator's
32
+ * when the run is against the local emulator — which is the whole point of
33
+ * having it be a field: the two backends are THE SAME CODE with a different
34
+ * base URL, so the simulator exercises the adapter instead of standing in
35
+ * for it. A mock of our own would only ever agree with us.
36
+ */
37
+ baseUrl: string;
38
+ /** The number the business sends from, as Meta's dashboard names it. */
39
+ phoneNumberId: string;
40
+ accessToken: string;
41
+ /** Echoed back on the `GET` handshake when Meta registers the webhook. */
42
+ verifyToken: string;
43
+ /** The app secret that signs deliveries. Without it no delivery is trusted. */
44
+ appSecret: string;
45
+ apiVersion: string;
46
+ webhookPort: number;
47
+ }
48
+
49
+ /** Meta's own Graph host. Anything else is a stand-in, and the channel treats it as one. */
50
+ export const META_GRAPH_HOST = "graph.facebook.com";
51
+
52
+ /**
53
+ * Whether this base URL is Meta. It decides one thing that matters beyond
54
+ * logging: `live`, which is what the consent-evidence builder refuses on. An
55
+ * act observed by an emulator was observed by nobody.
56
+ */
57
+ export function isMetaBaseUrl(baseUrl: string): boolean {
58
+ try {
59
+ return new URL(baseUrl).hostname === META_GRAPH_HOST;
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ export const CLOUD_API_ENV = {
66
+ phoneNumberId: "WHATSAPP_PHONE_NUMBER_ID",
67
+ accessToken: "WHATSAPP_ACCESS_TOKEN",
68
+ verifyToken: "WHATSAPP_VERIFY_TOKEN",
69
+ appSecret: "WHATSAPP_APP_SECRET",
70
+ apiVersion: "WHATSAPP_API_VERSION",
71
+ webhookPort: "WHATSAPP_WEBHOOK_PORT",
72
+ baseUrl: "WHATSAPP_GRAPH_URL",
73
+ } as const;
74
+
75
+ export interface ConfigResult {
76
+ config?: CloudApiConfig;
77
+ /** The variables that are not set. A refusal names them rather than saying "misconfigured". */
78
+ missing: string[];
79
+ }
80
+
81
+ export function loadCloudApiConfig(env: NodeJS.ProcessEnv): ConfigResult {
82
+ const read = (name: string) => {
83
+ const v = env[name]?.trim();
84
+ return v ? v : undefined;
85
+ };
86
+ const phoneNumberId = read(CLOUD_API_ENV.phoneNumberId);
87
+ const accessToken = read(CLOUD_API_ENV.accessToken);
88
+ const verifyToken = read(CLOUD_API_ENV.verifyToken);
89
+ const appSecret = read(CLOUD_API_ENV.appSecret);
90
+ const missing = [
91
+ ...(phoneNumberId ? [] : [CLOUD_API_ENV.phoneNumberId]),
92
+ ...(accessToken ? [] : [CLOUD_API_ENV.accessToken]),
93
+ ...(verifyToken ? [] : [CLOUD_API_ENV.verifyToken]),
94
+ ...(appSecret ? [] : [CLOUD_API_ENV.appSecret]),
95
+ ];
96
+ if (!phoneNumberId || !accessToken || !verifyToken || !appSecret) return { missing };
97
+ return {
98
+ missing,
99
+ config: {
100
+ baseUrl: read(CLOUD_API_ENV.baseUrl) ?? `https://${META_GRAPH_HOST}`,
101
+ phoneNumberId,
102
+ accessToken,
103
+ verifyToken,
104
+ appSecret,
105
+ apiVersion: read(CLOUD_API_ENV.apiVersion) ?? "v21.0",
106
+ webhookPort: Number(read(CLOUD_API_ENV.webhookPort) ?? 3111),
107
+ },
108
+ };
109
+ }
110
+
111
+ /* ── the pure half: what the tests drive ─────────────────────── */
112
+
113
+ /**
114
+ * `X-Hub-Signature-256: sha256=<hex>`, HMAC-SHA256 of the RAW body under the
115
+ * app secret. Raw and not re-serialized: a body that round-tripped through
116
+ * `JSON.parse` is a different string, and the signature is over bytes.
117
+ */
118
+ export function verifyWebhookSignature(appSecret: string, header: string | undefined, rawBody: string): { ok: true } | { ok: false; reason: string } {
119
+ if (!header) return { ok: false, reason: "missing X-Hub-Signature-256" };
120
+ const [algorithm, signature] = header.split("=");
121
+ if (algorithm !== "sha256" || !signature) return { ok: false, reason: "malformed signature header" };
122
+ const expected = createHmac("sha256", appSecret).update(rawBody, "utf8").digest("hex");
123
+ if (expected.length !== signature.length) return { ok: false, reason: "signature mismatch" };
124
+ if (!timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(signature, "hex"))) return { ok: false, reason: "signature mismatch" };
125
+ return { ok: true };
126
+ }
127
+
128
+ /** The `GET` handshake Meta makes once, when the webhook URL is registered. */
129
+ export function verifyChallenge(config: CloudApiConfig, query: Record<string, string | undefined>): { status: number; body: string } {
130
+ if (query["hub.mode"] !== "subscribe") return { status: 400, body: "bad mode" };
131
+ if (query["hub.verify_token"] !== config.verifyToken) return { status: 403, body: "bad verify token" };
132
+ return { status: 200, body: query["hub.challenge"] ?? "" };
133
+ }
134
+
135
+ interface CloudApiEnvelope {
136
+ object?: string;
137
+ entry?: Array<{
138
+ changes?: Array<{
139
+ field?: string;
140
+ value?: {
141
+ messaging_product?: string;
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 }>;
145
+ };
146
+ }>;
147
+ }>;
148
+ }
149
+
150
+ /**
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.
156
+ */
157
+ export function parseInbound(payload: unknown): { messages: InboundMessage[]; ignored: Array<{ id: string; type: string }> } {
158
+ const envelope = payload as CloudApiEnvelope;
159
+ const messages: InboundMessage[] = [];
160
+ const ignored: Array<{ id: string; type: string }> = [];
161
+ for (const entry of envelope.entry ?? []) {
162
+ for (const change of entry.changes ?? []) {
163
+ for (const message of change.value?.messages ?? []) {
164
+ const id = message.id;
165
+ const from = message.from;
166
+ if (!id || !from) continue;
167
+ if (message.type !== "text" || !message.text?.body) {
168
+ ignored.push({ id, type: message.type ?? "unknown" });
169
+ continue;
170
+ }
171
+ messages.push({ id, from: from.startsWith("+") ? from : `+${from}`, text: message.text.body, timestamp: Number(message.timestamp ?? 0) });
172
+ }
173
+ }
174
+ }
175
+ return { messages, ignored };
176
+ }
177
+
178
+ export interface SendRequest {
179
+ url: string;
180
+ method: "POST";
181
+ headers: Record<string, string>;
182
+ body: string;
183
+ }
184
+
185
+ /**
186
+ * The `/messages` call, as Meta documents it. `to` loses its `+`: the Cloud
187
+ * API wants the number in international format without the plus.
188
+ *
189
+ * `media` has no branch here and that is the honest answer, not an omission.
190
+ * Sending an image means uploading it first (`POST /{version}/{phone-number-id}/media`,
191
+ * multipart) or handing Meta a public URL, and this repo hosts nothing and
192
+ * renders no PNG — `qrcode-terminal` draws characters. The channel therefore
193
+ * sends the copy-and-paste as its own message, which is the string that
194
+ * actually pays, and the QR is the simulator's. Named in the README and in
195
+ * `docs/OPEN_QUESTIONS.md`.
196
+ */
197
+ export function buildSendRequest(config: CloudApiConfig, to: string, body: OutboundBody): SendRequest | { unsupported: string } {
198
+ const base = {
199
+ url: `${config.baseUrl.replace(/\/$/, "")}/${config.apiVersion}/${config.phoneNumberId}/messages`,
200
+ method: "POST" as const,
201
+ headers: { authorization: `Bearer ${config.accessToken}`, "content-type": "application/json" },
202
+ };
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
+ const recipient = { messaging_product: "whatsapp", recipient_type: "individual", to: toGraphNumber(to) };
208
+ switch (body.kind) {
209
+ case "text":
210
+ return { ...base, body: JSON.stringify({ ...recipient, type: "text", text: { preview_url: false, body: body.text } }) };
211
+ case "instrument":
212
+ return { ...base, body: JSON.stringify({ ...recipient, type: "text", text: { preview_url: false, body: body.value } }) };
213
+ case "template":
214
+ return {
215
+ ...base,
216
+ body: JSON.stringify({
217
+ ...recipient,
218
+ type: "template",
219
+ template: {
220
+ name: body.template,
221
+ language: { code: body.language },
222
+ ...(body.variables.length ? { components: [{ type: "body", parameters: body.variables.map((text) => ({ type: "text", text })) }] } : {}),
223
+ },
224
+ }),
225
+ };
226
+ case "media":
227
+ return { unsupported: "media_upload_unimplemented" };
228
+ }
229
+ }
230
+
231
+ /* ── the impure half: one fetch over a request the above built ── */
232
+
233
+ /** `+5511987654321` -> `5511987654321`, the only form the Graph API takes. */
234
+ export function toGraphNumber(contact: string): string {
235
+ return contact.replace(/^\+/, "");
236
+ }
237
+
238
+ export interface CloudApiOptions {
239
+ config: CloudApiConfig;
240
+ conversation: { contact: string };
241
+ say: (line: string) => void;
242
+ /** Injected so a test can drive `deliver` without a network. The default is the platform's. */
243
+ fetchImpl?: typeof fetch;
244
+ }
245
+
246
+ export class WhatsAppCloudApi implements ChannelBackend {
247
+ readonly name: string;
248
+ /** True only against Meta. The emulator is a stand-in and says so. */
249
+ readonly live: boolean;
250
+ private server: Server | undefined;
251
+ private readonly queue: InboundMessage[] = [];
252
+ private waiting: ((m: InboundMessage | undefined) => void) | undefined;
253
+ private closed = false;
254
+
255
+ constructor(private readonly options: CloudApiOptions) {
256
+ this.live = isMetaBaseUrl(options.config.baseUrl);
257
+ this.name = this.live ? "cloud-api" : "emulator";
258
+ }
259
+
260
+ /** The port the receiver is listening on, which the emulator has to be told about. */
261
+ get webhookPort(): number {
262
+ return this.options.config.webhookPort;
263
+ }
264
+
265
+ async open(): Promise<void> {
266
+ const { config, say } = this.options;
267
+ this.server = createServer((req, res) => {
268
+ const url = new URL(req.url ?? "/", `http://127.0.0.1:${config.webhookPort}`);
269
+ if (req.method === "GET") {
270
+ const query = Object.fromEntries(url.searchParams.entries());
271
+ const answer = verifyChallenge(config, query);
272
+ res.writeHead(answer.status, { "content-type": "text/plain" }).end(answer.body);
273
+ return;
274
+ }
275
+ if (req.method !== "POST") {
276
+ res.writeHead(405).end();
277
+ return;
278
+ }
279
+ const chunks: Buffer[] = [];
280
+ req.on("data", (c: Buffer) => chunks.push(c));
281
+ req.on("end", () => {
282
+ const raw = Buffer.concat(chunks).toString("utf8");
283
+ const header = req.headers["x-hub-signature-256"];
284
+ const check = verifyWebhookSignature(config.appSecret, Array.isArray(header) ? header[0] : header, raw);
285
+ if (!check.ok) {
286
+ say(`[whatsapp] delivery refused: ${check.reason}`);
287
+ res.writeHead(401, { "content-type": "application/json" }).end(JSON.stringify({ error: "bad_signature" }));
288
+ return;
289
+ }
290
+ let parsed: { messages: InboundMessage[]; ignored: Array<{ id: string; type: string }> };
291
+ try {
292
+ parsed = parseInbound(JSON.parse(raw));
293
+ } catch {
294
+ res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: "invalid_json" }));
295
+ return;
296
+ }
297
+ for (const ignored of parsed.ignored) say(`[whatsapp] ignored ${ignored.type} message ${ignored.id}: this channel reads text`);
298
+ for (const message of parsed.messages) this.push(message);
299
+ // Always 200 once read: Meta retries on anything else, and a duplicate is not an error.
300
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ received: parsed.messages.length }));
301
+ });
302
+ });
303
+ await new Promise<void>((resolve) => this.server!.listen(config.webhookPort, resolve));
304
+ say(
305
+ this.live
306
+ ? `[whatsapp] cloud API backend: webhook on http://127.0.0.1:${config.webhookPort}/ — expose it and register it with Meta; deliveries are verified against ${CLOUD_API_ENV.appSecret}`
307
+ : `[whatsapp] emulator backend: sending to ${config.baseUrl}/${config.apiVersion}, receiving signed deliveries on http://127.0.0.1:${config.webhookPort}/`,
308
+ );
309
+ }
310
+
311
+ private push(message: InboundMessage): void {
312
+ if (this.waiting) {
313
+ const resolve = this.waiting;
314
+ this.waiting = undefined;
315
+ resolve(message);
316
+ return;
317
+ }
318
+ this.queue.push(message);
319
+ }
320
+
321
+ async next(): Promise<InboundMessage | undefined> {
322
+ if (this.closed) return undefined;
323
+ const queued = this.queue.shift();
324
+ if (queued) return queued;
325
+ return new Promise<InboundMessage | undefined>((resolve) => {
326
+ this.waiting = resolve;
327
+ });
328
+ }
329
+
330
+ async deliver(to: string, body: OutboundBody): Promise<SentMessage> {
331
+ const request = buildSendRequest(this.options.config, to, body);
332
+ if ("unsupported" in request) {
333
+ return { id: "", state: "failed", refused: { rule: request.unsupported, detail: "this backend cannot upload media: the copy-and-paste text is what pays, and the QR image is the simulator's" } };
334
+ }
335
+ const doFetch = this.options.fetchImpl ?? fetch;
336
+ const response = await doFetch(request.url, { method: request.method, headers: request.headers, body: request.body });
337
+ 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
+ }
341
+ let id = "";
342
+ try {
343
+ id = String((JSON.parse(text) as { messages?: Array<{ id?: string }> }).messages?.[0]?.id ?? "");
344
+ } catch {
345
+ /* a 2xx with an unreadable body: the message went, the id did not come back */
346
+ }
347
+ return { id, state: "sent" };
348
+ }
349
+
350
+ async close(): Promise<void> {
351
+ this.closed = true;
352
+ if (this.waiting) {
353
+ this.waiting(undefined);
354
+ this.waiting = undefined;
355
+ }
356
+ await new Promise<void>((resolve) => (this.server ? this.server.close(() => resolve()) : resolve()));
357
+ }
358
+ }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * The house simulator, which is not ours: `dyvit-wa-sim`, the local Cloud API
3
+ * emulator from https://github.com/fabianocruz/whatsapp-simulator (MIT).
4
+ *
5
+ * WHY A SEPARATE PROCESS INSTEAD OF A FAKE IN THIS REPO. A mock we wrote would
6
+ * agree with us by construction — it would accept the payloads we send because
7
+ * we wrote both sides, and the day Meta refused one we would find out in
8
+ * production. The emulator answers `POST /v{version}/{phone-number-id}/messages`
9
+ * with the Cloud API's own response shape and posts back the same signed
10
+ * `x-hub-signature-256` webhooks, so the `simulator` backend is THE SAME CODE
11
+ * as the official one with a different base URL. That is the thing being
12
+ * proved, and it is not something a mock can prove.
13
+ *
14
+ * It also has a clock we can move (`POST /_sim/clock`), which a replay
15
+ * finishing in seconds cannot otherwise have: a collection runs over days, and
16
+ * without moving the clock the 24-hour window never closes, so the one rule
17
+ * that decides whether a message may be sent at all is never exercised.
18
+ *
19
+ * WHAT THIS FILE IS: the `_sim/*` side only — the routes that belong to the
20
+ * emulator and have no counterpart at Meta. The sending and receiving are
21
+ * `WhatsAppCloudApi`, unchanged.
22
+ *
23
+ * The emulator is a separate process and is not a dependency of this workspace:
24
+ * it is a development tool nothing here imports. `scripts/whatsapp-emulator.mjs`
25
+ * fetches `@dyvit/whatsapp-simulator-cli` at a pinned version and runs it;
26
+ * `npm run whatsapp:emulator` is the command.
27
+ */
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";
31
+
32
+ export const EMULATOR_ENV = {
33
+ url: "WHATSAPP_SIM_URL",
34
+ appSecret: "WHATSAPP_SIM_APP_SECRET",
35
+ webhookPort: "WHATSAPP_SIM_WEBHOOK_PORT",
36
+ phoneNumberId: "WHATSAPP_SIM_PHONE_NUMBER_ID",
37
+ } as const;
38
+
39
+ export const EMULATOR_DEFAULTS = {
40
+ url: "http://127.0.0.1:4290",
41
+ /** Matches `--app-secret` in `npm run whatsapp:emulator`. A local development value, not a credential. */
42
+ appSecret: "dev",
43
+ webhookPort: 4399,
44
+ phoneNumberId: "900000000001",
45
+ apiVersion: "v22.0",
46
+ } as const;
47
+
48
+ export class EmulatorUnreachableError extends Error {
49
+ constructor(url: string, detail: string) {
50
+ super(
51
+ `the WhatsApp emulator is not answering at ${url} (${detail}).\n` +
52
+ ` Start it in another terminal: npm run whatsapp:emulator\n` +
53
+ ` It is dyvit-wa-sim, from https://github.com/fabianocruz/whatsapp-simulator — MIT, run from npm at a pinned version, no Meta account and no credential.`,
54
+ );
55
+ this.name = "EmulatorUnreachableError";
56
+ }
57
+ }
58
+
59
+ /** The `_sim/*` routes: what the emulator has and Meta does not. */
60
+ export class EmulatorDriver {
61
+ constructor(
62
+ private readonly url: string,
63
+ private readonly fetchImpl: typeof fetch = fetch,
64
+ ) {}
65
+
66
+ private async call(path: string, init?: RequestInit): Promise<unknown> {
67
+ let response: Response;
68
+ try {
69
+ response = await this.fetchImpl(`${this.url}${path}`, { ...init, signal: AbortSignal.timeout(5000) });
70
+ } catch (err) {
71
+ throw new EmulatorUnreachableError(this.url, err instanceof Error ? err.message : String(err));
72
+ }
73
+ const text = await response.text();
74
+ if (!response.ok) throw new Error(`emulator answered ${response.status} to ${path}: ${text.slice(0, 200)}`);
75
+ return text ? JSON.parse(text) : {};
76
+ }
77
+
78
+ private post(path: string, body: unknown): Promise<unknown> {
79
+ return this.call(path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
80
+ }
81
+
82
+ health(): Promise<unknown> {
83
+ return this.call("/health");
84
+ }
85
+
86
+ /** Pins the conversation clock, so a scripted run reads the same at 03:00 as at 15:00. */
87
+ pin(at: Date): Promise<unknown> {
88
+ return this.post("/_sim/clock", { now: at.toISOString() });
89
+ }
90
+
91
+ /** Moves the conversation clock forward. The only way to reach the far side of the 24-hour window. */
92
+ advanceHours(hours: number): Promise<unknown> {
93
+ return this.post("/_sim/clock", { advance_hours: hours });
94
+ }
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> {
98
+ return this.post("/_sim/inbound", {
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,
104
+ ...(options.sentAt ? { sent_at: options.sentAt.toISOString() } : {}),
105
+ });
106
+ }
107
+
108
+ /** The priced timeline, which is the emulator's own reason for existing. Read for the console, never asserted on. */
109
+ state(key: string): Promise<{ priced?: { total?: number; currency?: string } } | undefined> {
110
+ return this.call(`/_sim/state?key=${encodeURIComponent(key)}`).then(
111
+ (v) => v as { priced?: { total?: number; currency?: string } },
112
+ () => undefined,
113
+ );
114
+ }
115
+ }
116
+
117
+ export interface EmulatorBackendOptions extends CloudApiOptions {
118
+ driver: EmulatorDriver;
119
+ /** The person's turns. Absent means they are at the keyboard. */
120
+ script?: ConversationScript | undefined;
121
+ /** The instant the emulator's conversation clock is pinned to. */
122
+ pinAt?: Date | undefined;
123
+ /** Interactive only: reads the person's next line. */
124
+ ask?: ((question: string) => Promise<string>) | undefined;
125
+ /** Where the console draws the conversation as it goes. */
126
+ render: (line: string) => void;
127
+ }
128
+
129
+ /**
130
+ * The `simulator` backend: `WhatsAppCloudApi` pointed at the emulator, plus the
131
+ * one thing the Cloud API has no equivalent for — making the person write.
132
+ *
133
+ * Every outbound message goes through the same `deliver` the Meta backend uses,
134
+ * and every inbound one arrives as a signed webhook at the same receiver, so
135
+ * nothing here is a shortcut around the adapter.
136
+ */
137
+ export class WhatsAppEmulator implements ChannelBackend {
138
+ readonly name = "emulator";
139
+ readonly live = false;
140
+ private readonly api: WhatsAppCloudApi;
141
+ private turn = 0;
142
+ private closed = false;
143
+
144
+ constructor(private readonly options: EmulatorBackendOptions) {
145
+ const { driver: _driver, script: _script, pinAt: _pinAt, ask: _ask, render: _render, ...api } = options;
146
+ this.api = new WhatsAppCloudApi(api);
147
+ }
148
+
149
+ async open(): Promise<void> {
150
+ await this.api.open();
151
+ await this.options.driver.health();
152
+ if (this.options.pinAt) await this.options.driver.pin(this.options.pinAt);
153
+ this.options.render("");
154
+ this.options.render(` ┌─ WhatsApp (dyvit-wa-sim, emulador local da Cloud API — sem rede externa, sem conta Meta)`);
155
+ }
156
+
157
+ async next(): Promise<InboundMessage | undefined> {
158
+ 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
+ });
169
+ return this.api.next();
170
+ }
171
+
172
+ private async nextText(): Promise<string | undefined> {
173
+ const script = this.options.script;
174
+ if (script) {
175
+ const turn = script.turns[this.turn];
176
+ if (!turn) return undefined;
177
+ this.turn += 1;
178
+ // The person's own pause, on the emulator's clock. A conversation that
179
+ // happens over days is what closes the 24-hour window.
180
+ if (turn.after_seconds > 0) await this.options.driver.advanceHours(turn.after_seconds / 3600);
181
+ return turn.text;
182
+ }
183
+ if (!this.options.ask) return undefined;
184
+ let answer: string;
185
+ try {
186
+ answer = await this.options.ask(" │ voce> ");
187
+ } catch {
188
+ return undefined;
189
+ }
190
+ const text = answer.trim();
191
+ if (!text) return this.nextText();
192
+ if (["sair", "exit", "quit"].includes(text.toLowerCase())) return undefined;
193
+ return text;
194
+ }
195
+
196
+ deliver(to: string, body: OutboundBody): Promise<SentMessage> {
197
+ return this.api.deliver(to, body);
198
+ }
199
+
200
+ async close(): Promise<void> {
201
+ if (this.closed) return;
202
+ this.closed = true;
203
+ await this.api.close();
204
+ this.options.render(" └─ fim da conversa");
205
+ }
206
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Decision 19a, the part a channel can carry: `attestation.evidence` on
3
+ * `POST /v1/consents/{token}/submit` (codespar-enterprise ent#1615).
4
+ *
5
+ * In the sandbox a mandate is born at the terminal with
6
+ * `attestation: { method: "in_person" }` — the titular is at the keyboard and
7
+ * the partner is the process. In production the act happens in a
8
+ * CONVERSATION, so the method is `partner_session` and the submit may carry
9
+ * what the partner OBSERVED of it. This builds that object.
10
+ *
11
+ * The API's rule, restated here because it is the whole point: a WhatsApp
12
+ * conversation hands the partner a message and a sender, not a socket. The
13
+ * channel's allowed keys are therefore `contact`, `message_id`, `session_id`
14
+ * and `provider_ts`, and an `ip`, a `user_agent`, a `device_id_hash` or a
15
+ * `geo` on this channel would have been inferred rather than observed. The
16
+ * API refuses them (400 `attestation_evidence_invalid`), and so does this,
17
+ * one layer earlier, because a value inferred inside a signed record reads as
18
+ * observed and is worse than an absent one.
19
+ *
20
+ * WHY THE EMULATOR CANNOT PRODUCE ONE. Everything here is a claim about what a
21
+ * provider observed. A conversation carried by an emulator on the developer's
22
+ * own machine was observed by nobody: its message id was minted locally, its
23
+ * timestamp came from a clock we move ourselves, and no phone was involved.
24
+ * Signing that as evidence would be a false declaration — the same one the
25
+ * API's own schema warns about for a partner that declares `other` for a
26
+ * WhatsApp act. So `evidenceFor` returns a REFUSAL for any backend that is not
27
+ * live, with the reason on it, and the caller either submits without evidence
28
+ * or does not submit at all. That is not a gap in the seam; it is the seam
29
+ * working.
30
+ *
31
+ * `live` is not a flag anyone sets: it is whether the base URL is Meta's host.
32
+ * A run cannot claim to be live by being configured to say so.
33
+ */
34
+ import type { InboundMessage } from "../types.js";
35
+
36
+ /** The keys the API's `whatsapp` channel accepts. Mirrored from `CONSENT_EVIDENCE_CHANNEL_KEYS`. */
37
+ export const WHATSAPP_EVIDENCE_KEYS = ["contact", "message_id", "session_id", "provider_ts"] as const;
38
+
39
+ /** Opaque provider ids: a `wamid.` carries `.`, `:`, `=` and `+`, and all four are allowed. */
40
+ const OPAQUE_ID = /^[A-Za-z0-9_.:=+-]{1,120}$/;
41
+
42
+ /**
43
+ * The evidence as the PARTNER sends it. The wire carries `contact` in the
44
+ * clear and the API hashes it into `contact_hash` under the org's own key —
45
+ * a partner cannot compute that hash, and a partner-computed one would match
46
+ * nothing.
47
+ */
48
+ export interface WhatsAppConsentEvidence {
49
+ channel: "whatsapp";
50
+ contact?: string;
51
+ message_id?: string;
52
+ session_id?: string;
53
+ /** Unix seconds on the PROVIDER's clock, which is not the partner's. */
54
+ provider_ts?: number;
55
+ }
56
+
57
+ export interface ConsentAttestation {
58
+ method: "partner_session";
59
+ /** Unix seconds: when the PARTNER says the human authorized. */
60
+ asserted_at: number;
61
+ /** The partner's own record id. `^[A-Za-z0-9_-]{1,120}$` — no dots, so a `wamid` goes in `message_id`. */
62
+ reference?: string;
63
+ evidence: WhatsAppConsentEvidence;
64
+ }
65
+
66
+ export type EvidenceResult = { attestable: true; attestation: ConsentAttestation } | { attestable: false; reason: string };
67
+
68
+ export interface EvidenceInput {
69
+ /** Whether the backend that carried the message talks to a real provider. */
70
+ live: boolean;
71
+ /** The message in which the person said yes. */
72
+ message: InboundMessage;
73
+ /** The partner's id for this conversation, when it has one. */
74
+ sessionId?: string | undefined;
75
+ /** The partner's clock, for `asserted_at`. */
76
+ now: () => Date;
77
+ reference?: string | undefined;
78
+ }
79
+
80
+ export function evidenceFor(input: EvidenceInput): EvidenceResult {
81
+ if (!input.live) {
82
+ return {
83
+ attestable: false,
84
+ reason:
85
+ "this act was observed by an emulator on this machine, and a simulated observation is not evidence: " +
86
+ "the message id was minted locally, the timestamp came from a clock we move ourselves, and no phone was involved",
87
+ };
88
+ }
89
+ if (!OPAQUE_ID.test(input.message.id)) return { attestable: false, reason: `the provider message id does not fit the API's opaque-id shape: ${input.message.id.slice(0, 24)}` };
90
+ if (!Number.isInteger(input.message.timestamp) || input.message.timestamp <= 0) return { attestable: false, reason: "the provider gave no usable timestamp for the message" };
91
+ if (input.sessionId !== undefined && !OPAQUE_ID.test(input.sessionId)) return { attestable: false, reason: "the session id does not fit the API's opaque-id shape" };
92
+ if (input.reference !== undefined && !/^[A-Za-z0-9_-]{1,120}$/.test(input.reference)) return { attestable: false, reason: "the attestation reference must be [A-Za-z0-9_-]{1,120}" };
93
+
94
+ return {
95
+ attestable: true,
96
+ attestation: {
97
+ method: "partner_session",
98
+ asserted_at: Math.floor(input.now().getTime() / 1000),
99
+ ...(input.reference !== undefined ? { reference: input.reference } : {}),
100
+ evidence: {
101
+ channel: "whatsapp",
102
+ contact: input.message.from,
103
+ message_id: input.message.id,
104
+ ...(input.sessionId !== undefined ? { session_id: input.sessionId } : {}),
105
+ provider_ts: input.message.timestamp,
106
+ },
107
+ },
108
+ };
109
+ }