@codespar/cli 0.14.1 → 0.17.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 (534) 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/mandate-verify.js +98 -19
  7. package/dist/commands/mandate-verify.js.map +1 -1
  8. package/dist/index.js +17 -3
  9. package/dist/index.js.map +1 -1
  10. package/dist/mandate-codec.js +31 -20
  11. package/dist/mandate-codec.js.map +1 -1
  12. package/dist/surface.js +4 -1
  13. package/dist/surface.js.map +1 -1
  14. package/package.json +1 -1
  15. package/templates/bills-agent/README.md +8 -5
  16. package/templates/bills-agent/agents/bills-agent/.env.example +3 -3
  17. package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
  18. package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
  19. package/templates/bills-agent/agents/bills-agent/README.md +62 -22
  20. package/templates/bills-agent/agents/bills-agent/SYSTEM_PROMPT.md +1 -1
  21. package/templates/bills-agent/agents/bills-agent/agent.yaml +2 -2
  22. package/templates/bills-agent/agents/bills-agent/package.json +14 -11
  23. package/templates/bills-agent/agents/bills-agent/src/kit.ts +193 -0
  24. package/templates/bills-agent/agents/bills-agent/test/adversarial.test.ts +8 -7
  25. package/templates/bills-agent/agents/bills-agent/test/check.test.ts +19 -2
  26. package/templates/bills-agent/agents/bills-agent/test/cli.test.ts +72 -24
  27. package/templates/bills-agent/agents/bills-agent/test/fixtures/excursao-300.transcript.jsonl +2 -0
  28. package/templates/bills-agent/agents/bills-agent/test/fixtures/material-400.transcript.jsonl +2 -0
  29. package/templates/bills-agent/agents/bills-agent/test/inspect.test.ts +124 -0
  30. package/templates/bills-agent/agents/bills-agent/test/provider.test.ts +25 -0
  31. package/templates/bills-agent/agents/bills-agent/test/scenarios.test.ts +10 -9
  32. package/templates/bills-agent/agents/bills-agent/tsconfig.json +0 -1
  33. package/templates/bills-agent/package.json +5 -2
  34. package/templates/bills-agent/packages/agent-core/README.md +2 -0
  35. package/templates/bills-agent/packages/agent-core/package.json +1 -1
  36. package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +27 -6
  37. package/templates/bills-agent/packages/agent-core/src/api/rail.ts +16 -0
  38. package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +20 -7
  39. package/templates/bills-agent/packages/agent-core/src/approval.ts +16 -2
  40. package/templates/bills-agent/packages/agent-core/src/bundle.ts +50 -4
  41. package/templates/bills-agent/packages/agent-core/src/channels.ts +140 -0
  42. package/templates/bills-agent/packages/agent-core/src/check.ts +90 -8
  43. package/templates/bills-agent/packages/agent-core/src/clock.ts +42 -0
  44. package/templates/bills-agent/packages/agent-core/src/engine.ts +119 -18
  45. package/templates/bills-agent/packages/agent-core/src/hash.ts +13 -0
  46. package/templates/bills-agent/packages/agent-core/src/index.ts +3 -0
  47. package/templates/bills-agent/packages/agent-core/src/manifest.ts +1 -1
  48. package/templates/bills-agent/packages/agent-core/src/rail.ts +16 -2
  49. package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +401 -0
  50. package/templates/bills-agent/packages/agent-core/src/state-machine.ts +3 -1
  51. package/templates/bills-agent/packages/agent-core/src/stubs/charge-rail.ts +2 -0
  52. package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +19 -2
  53. package/templates/bills-agent/packages/agent-core/src/types.ts +28 -0
  54. package/templates/bills-agent/packages/agent-core/test/approval.test.ts +36 -2
  55. package/templates/bills-agent/packages/agent-core/test/bundle.test.ts +85 -0
  56. package/templates/bills-agent/packages/agent-core/test/clock.test.ts +25 -0
  57. package/templates/bills-agent/packages/agent-core/test/engine.test.ts +73 -1
  58. package/templates/bills-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  59. package/templates/bills-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  60. package/templates/bills-agent/packages/agent-core/test/helpers.ts +1 -1
  61. package/templates/bills-agent/packages/agent-core/test/manifest.test.ts +2 -2
  62. package/templates/bills-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  63. package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +51 -0
  64. package/templates/bills-agent/packages/agent-runtime/README.md +68 -0
  65. package/templates/bills-agent/packages/agent-runtime/bin.mjs +32 -0
  66. package/templates/bills-agent/packages/agent-runtime/package.json +23 -0
  67. package/templates/bills-agent/{agents/bills-agent → packages/agent-runtime}/src/adversarial.ts +36 -44
  68. package/templates/bills-agent/packages/agent-runtime/src/agent.ts +68 -0
  69. package/templates/bills-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  70. package/templates/bills-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  71. package/templates/bills-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  72. package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +124 -0
  73. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
  74. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
  75. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  76. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
  77. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
  78. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  79. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
  80. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
  81. package/templates/bills-agent/packages/agent-runtime/src/cli.ts +98 -0
  82. package/templates/bills-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  83. package/templates/bills-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  84. package/templates/bills-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  85. package/templates/bills-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  86. package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
  87. package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
  88. package/templates/bills-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  89. package/templates/bills-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  90. package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
  91. package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
  92. package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +245 -0
  93. package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
  94. package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
  95. package/templates/bills-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  96. package/templates/bills-agent/packages/agent-runtime/src/index.ts +12 -0
  97. package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +797 -0
  98. package/templates/bills-agent/packages/agent-runtime/src/kit.ts +206 -0
  99. package/templates/{collections-agent/agents/collections-agent → bills-agent/packages/agent-runtime}/src/poll.ts +22 -11
  100. package/templates/{collections-agent/agents/collections-agent → bills-agent/packages/agent-runtime}/src/scenarios.ts +50 -34
  101. package/templates/bills-agent/{agents/bills-agent → packages/agent-runtime}/src/setup.ts +88 -66
  102. package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +154 -0
  103. package/templates/{collections-agent/agents/collections-agent/channels/webhook/index.ts → bills-agent/packages/agent-runtime/src/webhook.ts} +1 -1
  104. package/templates/bills-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  105. package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
  106. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
  107. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
  108. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  109. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  110. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
  111. package/templates/bills-agent/packages/agent-runtime/tsconfig.json +9 -0
  112. package/templates/bills-agent/vitest.config.ts +1 -1
  113. package/templates/collections-agent/README.md +8 -5
  114. package/templates/collections-agent/agents/collections-agent/.env.example +14 -0
  115. package/templates/collections-agent/agents/collections-agent/AGENTS.md +6 -4
  116. package/templates/collections-agent/agents/collections-agent/CLAUDE.md +6 -4
  117. package/templates/collections-agent/agents/collections-agent/README.md +184 -16
  118. package/templates/collections-agent/agents/collections-agent/agent.yaml +3 -3
  119. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +111 -0
  120. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042.json +11 -0
  121. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1103.json +11 -0
  122. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +23 -0
  123. package/templates/collections-agent/agents/collections-agent/package.json +12 -10
  124. package/templates/collections-agent/agents/collections-agent/runbook.md +52 -1
  125. package/templates/collections-agent/agents/collections-agent/src/kit.ts +265 -0
  126. package/templates/collections-agent/agents/collections-agent/test/adversarial.test.ts +9 -8
  127. package/templates/collections-agent/agents/collections-agent/test/check.test.ts +43 -1
  128. package/templates/collections-agent/agents/collections-agent/test/cli.test.ts +56 -18
  129. package/templates/collections-agent/agents/collections-agent/test/fixtures/agreed-1042-awaiting-payer.transcript.jsonl +4 -0
  130. package/templates/collections-agent/agents/collections-agent/test/scenarios.test.ts +11 -10
  131. package/templates/collections-agent/agents/collections-agent/test/webhook.test.ts +4 -7
  132. package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +271 -0
  133. package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +213 -0
  134. package/templates/collections-agent/agents/collections-agent/tsconfig.json +0 -1
  135. package/templates/collections-agent/package.json +3 -1
  136. package/templates/collections-agent/packages/agent-core/README.md +2 -0
  137. package/templates/collections-agent/packages/agent-core/package.json +1 -1
  138. package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +27 -6
  139. package/templates/collections-agent/packages/agent-core/src/api/rail.ts +16 -0
  140. package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +20 -7
  141. package/templates/collections-agent/packages/agent-core/src/approval.ts +16 -2
  142. package/templates/collections-agent/packages/agent-core/src/bundle.ts +50 -4
  143. package/templates/collections-agent/packages/agent-core/src/channels.ts +140 -0
  144. package/templates/collections-agent/packages/agent-core/src/check.ts +90 -8
  145. package/templates/collections-agent/packages/agent-core/src/clock.ts +42 -0
  146. package/templates/collections-agent/packages/agent-core/src/engine.ts +119 -18
  147. package/templates/collections-agent/packages/agent-core/src/hash.ts +13 -0
  148. package/templates/collections-agent/packages/agent-core/src/index.ts +3 -0
  149. package/templates/collections-agent/packages/agent-core/src/manifest.ts +1 -1
  150. package/templates/collections-agent/packages/agent-core/src/rail.ts +16 -2
  151. package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +401 -0
  152. package/templates/collections-agent/packages/agent-core/src/state-machine.ts +3 -1
  153. package/templates/collections-agent/packages/agent-core/src/stubs/charge-rail.ts +2 -0
  154. package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +19 -2
  155. package/templates/collections-agent/packages/agent-core/src/types.ts +28 -0
  156. package/templates/collections-agent/packages/agent-core/test/approval.test.ts +36 -2
  157. package/templates/collections-agent/packages/agent-core/test/bundle.test.ts +85 -0
  158. package/templates/collections-agent/packages/agent-core/test/clock.test.ts +25 -0
  159. package/templates/collections-agent/packages/agent-core/test/engine.test.ts +73 -1
  160. package/templates/collections-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  161. package/templates/collections-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  162. package/templates/collections-agent/packages/agent-core/test/helpers.ts +1 -1
  163. package/templates/collections-agent/packages/agent-core/test/manifest.test.ts +2 -2
  164. package/templates/collections-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  165. package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +51 -0
  166. package/templates/collections-agent/packages/agent-runtime/README.md +68 -0
  167. package/templates/collections-agent/packages/agent-runtime/bin.mjs +32 -0
  168. package/templates/collections-agent/packages/agent-runtime/package.json +23 -0
  169. package/templates/collections-agent/{agents/collections-agent → packages/agent-runtime}/src/adversarial.ts +29 -50
  170. package/templates/collections-agent/packages/agent-runtime/src/agent.ts +68 -0
  171. package/templates/collections-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  172. package/templates/collections-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  173. package/templates/collections-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  174. package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +124 -0
  175. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
  176. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
  177. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  178. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
  179. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
  180. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  181. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
  182. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
  183. package/templates/collections-agent/packages/agent-runtime/src/cli.ts +98 -0
  184. package/templates/collections-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  185. package/templates/collections-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  186. package/templates/collections-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  187. package/templates/collections-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  188. package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
  189. package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
  190. package/templates/collections-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  191. package/templates/collections-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  192. package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
  193. package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
  194. package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +245 -0
  195. package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
  196. package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
  197. package/templates/collections-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  198. package/templates/collections-agent/packages/agent-runtime/src/index.ts +12 -0
  199. package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +797 -0
  200. package/templates/collections-agent/packages/agent-runtime/src/kit.ts +206 -0
  201. package/templates/collections-agent/packages/agent-runtime/src/poll.ts +105 -0
  202. package/templates/{bills-agent/agents/bills-agent → collections-agent/packages/agent-runtime}/src/scenarios.ts +98 -26
  203. package/templates/collections-agent/packages/agent-runtime/src/setup.ts +229 -0
  204. package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +154 -0
  205. package/templates/collections-agent/packages/agent-runtime/src/webhook.ts +131 -0
  206. package/templates/collections-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  207. package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
  208. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
  209. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
  210. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  211. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  212. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
  213. package/templates/collections-agent/packages/agent-runtime/tsconfig.json +9 -0
  214. package/templates/collections-agent/vitest.config.ts +1 -1
  215. package/templates/hello-agent/LICENSE +21 -0
  216. package/templates/hello-agent/README.md +23 -0
  217. package/templates/hello-agent/_gitignore +10 -0
  218. package/templates/hello-agent/agents/hello-agent/.env.example +5 -0
  219. package/templates/hello-agent/agents/hello-agent/AGENTS.md +15 -0
  220. package/templates/hello-agent/agents/hello-agent/CLAUDE.md +15 -0
  221. package/templates/hello-agent/agents/hello-agent/README.md +33 -0
  222. package/templates/hello-agent/agents/hello-agent/SYSTEM_PROMPT.md +19 -0
  223. package/templates/hello-agent/agents/hello-agent/agent.yaml +19 -0
  224. package/templates/hello-agent/agents/hello-agent/evals/adversarial/beneficiary-swap.json +6 -0
  225. package/templates/hello-agent/agents/hello-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +2 -0
  226. package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.json +6 -0
  227. package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.transcript.jsonl +2 -0
  228. package/templates/hello-agent/agents/hello-agent/evals/adversarial/false-authority.json +6 -0
  229. package/templates/hello-agent/agents/hello-agent/evals/adversarial/false-authority.transcript.jsonl +2 -0
  230. package/templates/hello-agent/agents/hello-agent/evals/adversarial/fractioning.json +6 -0
  231. package/templates/hello-agent/agents/hello-agent/evals/adversarial/fractioning.transcript.jsonl +2 -0
  232. package/templates/hello-agent/agents/hello-agent/evals/adversarial/model-total.json +6 -0
  233. package/templates/hello-agent/agents/hello-agent/evals/adversarial/model-total.transcript.jsonl +2 -0
  234. package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.json +6 -0
  235. package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.transcript.jsonl +2 -0
  236. package/templates/hello-agent/agents/hello-agent/evals/adversarial/webhook-replay.json +6 -0
  237. package/templates/hello-agent/agents/hello-agent/evals/eval.yaml +7 -0
  238. package/templates/hello-agent/agents/hello-agent/guardrails.json +8 -0
  239. package/templates/hello-agent/agents/hello-agent/mandate.example.json +17 -0
  240. package/templates/hello-agent/agents/hello-agent/package.json +19 -0
  241. package/templates/hello-agent/agents/hello-agent/runbook.md +10 -0
  242. package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.json +6 -0
  243. package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.transcript.jsonl +2 -0
  244. package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.json +6 -0
  245. package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.transcript.jsonl +2 -0
  246. package/templates/hello-agent/agents/hello-agent/src/bills.ts +23 -0
  247. package/templates/hello-agent/agents/hello-agent/src/kit.ts +33 -0
  248. package/templates/hello-agent/agents/hello-agent/tools.json +11 -0
  249. package/templates/hello-agent/agents/hello-agent/tsconfig.json +9 -0
  250. package/templates/hello-agent/package.json +30 -0
  251. package/templates/hello-agent/packages/agent-core/README.md +21 -0
  252. package/templates/hello-agent/packages/agent-core/package.json +24 -0
  253. package/templates/hello-agent/packages/agent-core/src/agent.ts +125 -0
  254. package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +175 -0
  255. package/templates/hello-agent/packages/agent-core/src/api/client.ts +53 -0
  256. package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +58 -0
  257. package/templates/hello-agent/packages/agent-core/src/api/rail.ts +117 -0
  258. package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +58 -0
  259. package/templates/hello-agent/packages/agent-core/src/approval.ts +123 -0
  260. package/templates/hello-agent/packages/agent-core/src/bundle.ts +159 -0
  261. package/templates/hello-agent/packages/agent-core/src/channels.ts +140 -0
  262. package/templates/hello-agent/packages/agent-core/src/check.ts +253 -0
  263. package/templates/hello-agent/packages/agent-core/src/clock.ts +42 -0
  264. package/templates/hello-agent/packages/agent-core/src/engine.ts +885 -0
  265. package/templates/hello-agent/packages/agent-core/src/escalate.ts +85 -0
  266. package/templates/hello-agent/packages/agent-core/src/events.ts +40 -0
  267. package/templates/hello-agent/packages/agent-core/src/guardrails.ts +45 -0
  268. package/templates/hello-agent/packages/agent-core/src/hash.ts +62 -0
  269. package/templates/hello-agent/packages/agent-core/src/ids.ts +10 -0
  270. package/templates/hello-agent/packages/agent-core/src/index.ts +32 -0
  271. package/templates/hello-agent/packages/agent-core/src/mandate.ts +100 -0
  272. package/templates/hello-agent/packages/agent-core/src/manifest.ts +76 -0
  273. package/templates/hello-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
  274. package/templates/hello-agent/packages/agent-core/src/providers/replay.ts +63 -0
  275. package/templates/hello-agent/packages/agent-core/src/providers/types.ts +39 -0
  276. package/templates/hello-agent/packages/agent-core/src/rail.ts +101 -0
  277. package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +401 -0
  278. package/templates/hello-agent/packages/agent-core/src/revocation.ts +30 -0
  279. package/templates/hello-agent/packages/agent-core/src/secrets.ts +30 -0
  280. package/templates/hello-agent/packages/agent-core/src/state/store.ts +307 -0
  281. package/templates/hello-agent/packages/agent-core/src/state-machine.ts +148 -0
  282. package/templates/hello-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
  283. package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +57 -0
  284. package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +141 -0
  285. package/templates/hello-agent/packages/agent-core/src/tools.ts +55 -0
  286. package/templates/hello-agent/packages/agent-core/src/types.ts +134 -0
  287. package/templates/hello-agent/packages/agent-core/test/approval.test.ts +117 -0
  288. package/templates/hello-agent/packages/agent-core/test/bundle.test.ts +85 -0
  289. package/templates/hello-agent/packages/agent-core/test/clock.test.ts +25 -0
  290. package/templates/hello-agent/packages/agent-core/test/engine.test.ts +596 -0
  291. package/templates/hello-agent/packages/agent-core/test/escalate.test.ts +42 -0
  292. package/templates/hello-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  293. package/templates/hello-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  294. package/templates/hello-agent/packages/agent-core/test/helpers.ts +139 -0
  295. package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +153 -0
  296. package/templates/hello-agent/packages/agent-core/test/manifest.test.ts +45 -0
  297. package/templates/hello-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  298. package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +316 -0
  299. package/templates/hello-agent/packages/agent-core/test/secrets.test.ts +28 -0
  300. package/templates/hello-agent/packages/agent-core/test/state-machine.test.ts +67 -0
  301. package/templates/hello-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
  302. package/templates/hello-agent/packages/agent-core/tsconfig.json +10 -0
  303. package/templates/hello-agent/packages/agent-runtime/README.md +68 -0
  304. package/templates/hello-agent/packages/agent-runtime/bin.mjs +32 -0
  305. package/templates/hello-agent/packages/agent-runtime/package.json +23 -0
  306. package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +169 -0
  307. package/templates/hello-agent/packages/agent-runtime/src/agent.ts +68 -0
  308. package/templates/hello-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  309. package/templates/hello-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  310. package/templates/hello-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  311. package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +124 -0
  312. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
  313. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
  314. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  315. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
  316. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
  317. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  318. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
  319. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
  320. package/templates/hello-agent/packages/agent-runtime/src/cli.ts +98 -0
  321. package/templates/hello-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  322. package/templates/hello-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  323. package/templates/hello-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  324. package/templates/hello-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  325. package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
  326. package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
  327. package/templates/hello-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  328. package/templates/hello-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  329. package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
  330. package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
  331. package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +245 -0
  332. package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
  333. package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
  334. package/templates/hello-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  335. package/templates/hello-agent/packages/agent-runtime/src/index.ts +12 -0
  336. package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +797 -0
  337. package/templates/hello-agent/packages/agent-runtime/src/kit.ts +206 -0
  338. package/templates/hello-agent/packages/agent-runtime/src/poll.ts +105 -0
  339. package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +268 -0
  340. package/templates/hello-agent/packages/agent-runtime/src/setup.ts +229 -0
  341. package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +154 -0
  342. package/templates/hello-agent/packages/agent-runtime/src/webhook.ts +131 -0
  343. package/templates/hello-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  344. package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
  345. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
  346. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
  347. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  348. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  349. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
  350. package/templates/hello-agent/packages/agent-runtime/tsconfig.json +9 -0
  351. package/templates/hello-agent/tsconfig.base.json +16 -0
  352. package/templates/hello-agent/vitest.config.ts +12 -0
  353. package/templates/kits.lock.json +60 -12
  354. package/templates/supplier-payments-agent/LICENSE +21 -0
  355. package/templates/supplier-payments-agent/README.md +23 -0
  356. package/templates/supplier-payments-agent/_gitignore +10 -0
  357. package/templates/supplier-payments-agent/agents/supplier-payments-agent/.env.example +7 -0
  358. package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +21 -0
  359. package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +21 -0
  360. package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +118 -0
  361. package/templates/supplier-payments-agent/agents/supplier-payments-agent/SYSTEM_PROMPT.md +29 -0
  362. package/templates/supplier-payments-agent/agents/supplier-payments-agent/agent.yaml +26 -0
  363. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/batch-tampering.json +12 -0
  364. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/batch-tampering.transcript.jsonl +3 -0
  365. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap-mandate.json +9 -0
  366. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap.json +10 -0
  367. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +2 -0
  368. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.json +13 -0
  369. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
  370. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.json +9 -0
  371. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.transcript.jsonl +3 -0
  372. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/fractioning.json +15 -0
  373. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/fractioning.transcript.jsonl +6 -0
  374. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/model-total.json +10 -0
  375. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/model-total.transcript.jsonl +2 -0
  376. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.json +9 -0
  377. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.transcript.jsonl +2 -0
  378. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/webhook-replay.json +9 -0
  379. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/eval.yaml +9 -0
  380. package/templates/supplier-payments-agent/agents/supplier-payments-agent/guardrails.json +15 -0
  381. package/templates/supplier-payments-agent/agents/supplier-payments-agent/mandate.example.json +70 -0
  382. package/templates/supplier-payments-agent/agents/supplier-payments-agent/package.json +26 -0
  383. package/templates/supplier-payments-agent/agents/supplier-payments-agent/runbook.md +19 -0
  384. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.json +14 -0
  385. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +2 -0
  386. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.json +12 -0
  387. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.transcript.jsonl +2 -0
  388. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.json +20 -0
  389. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.transcript.jsonl +7 -0
  390. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.json +19 -0
  391. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.transcript.jsonl +3 -0
  392. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.json +21 -0
  393. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.transcript.jsonl +9 -0
  394. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.json +21 -0
  395. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.transcript.jsonl +3 -0
  396. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.json +12 -0
  397. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.transcript.jsonl +2 -0
  398. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +176 -0
  399. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +345 -0
  400. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/pix-out.ts +118 -0
  401. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/payables.ts +76 -0
  402. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/adversarial.test.ts +53 -0
  403. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +384 -0
  404. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/check.test.ts +109 -0
  405. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/scenarios.test.ts +81 -0
  406. package/templates/supplier-payments-agent/agents/supplier-payments-agent/tools.json +53 -0
  407. package/templates/supplier-payments-agent/agents/supplier-payments-agent/tsconfig.json +9 -0
  408. package/templates/supplier-payments-agent/package.json +36 -0
  409. package/templates/supplier-payments-agent/packages/agent-core/README.md +21 -0
  410. package/templates/supplier-payments-agent/packages/agent-core/package.json +24 -0
  411. package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +125 -0
  412. package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +175 -0
  413. package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +53 -0
  414. package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +58 -0
  415. package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +117 -0
  416. package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +58 -0
  417. package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +123 -0
  418. package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +159 -0
  419. package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +140 -0
  420. package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +253 -0
  421. package/templates/supplier-payments-agent/packages/agent-core/src/clock.ts +42 -0
  422. package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +885 -0
  423. package/templates/supplier-payments-agent/packages/agent-core/src/escalate.ts +85 -0
  424. package/templates/supplier-payments-agent/packages/agent-core/src/events.ts +40 -0
  425. package/templates/supplier-payments-agent/packages/agent-core/src/guardrails.ts +45 -0
  426. package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +62 -0
  427. package/templates/supplier-payments-agent/packages/agent-core/src/ids.ts +10 -0
  428. package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +32 -0
  429. package/templates/supplier-payments-agent/packages/agent-core/src/mandate.ts +100 -0
  430. package/templates/supplier-payments-agent/packages/agent-core/src/manifest.ts +76 -0
  431. package/templates/supplier-payments-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
  432. package/templates/supplier-payments-agent/packages/agent-core/src/providers/replay.ts +63 -0
  433. package/templates/supplier-payments-agent/packages/agent-core/src/providers/types.ts +39 -0
  434. package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +101 -0
  435. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +401 -0
  436. package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +30 -0
  437. package/templates/supplier-payments-agent/packages/agent-core/src/secrets.ts +30 -0
  438. package/templates/supplier-payments-agent/packages/agent-core/src/state/store.ts +307 -0
  439. package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +148 -0
  440. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
  441. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +57 -0
  442. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +141 -0
  443. package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +55 -0
  444. package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +134 -0
  445. package/templates/supplier-payments-agent/packages/agent-core/test/approval.test.ts +117 -0
  446. package/templates/supplier-payments-agent/packages/agent-core/test/bundle.test.ts +85 -0
  447. package/templates/supplier-payments-agent/packages/agent-core/test/clock.test.ts +25 -0
  448. package/templates/supplier-payments-agent/packages/agent-core/test/engine.test.ts +596 -0
  449. package/templates/supplier-payments-agent/packages/agent-core/test/escalate.test.ts +42 -0
  450. package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  451. package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  452. package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +139 -0
  453. package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +153 -0
  454. package/templates/supplier-payments-agent/packages/agent-core/test/manifest.test.ts +45 -0
  455. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  456. package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +316 -0
  457. package/templates/supplier-payments-agent/packages/agent-core/test/secrets.test.ts +28 -0
  458. package/templates/supplier-payments-agent/packages/agent-core/test/state-machine.test.ts +67 -0
  459. package/templates/supplier-payments-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
  460. package/templates/supplier-payments-agent/packages/agent-core/tsconfig.json +10 -0
  461. package/templates/supplier-payments-agent/packages/agent-runtime/README.md +68 -0
  462. package/templates/supplier-payments-agent/packages/agent-runtime/bin.mjs +32 -0
  463. package/templates/supplier-payments-agent/packages/agent-runtime/package.json +23 -0
  464. package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +169 -0
  465. package/templates/supplier-payments-agent/packages/agent-runtime/src/agent.ts +68 -0
  466. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  467. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  468. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  469. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +124 -0
  470. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
  471. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
  472. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  473. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
  474. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
  475. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  476. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
  477. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
  478. package/templates/supplier-payments-agent/packages/agent-runtime/src/cli.ts +98 -0
  479. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  480. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  481. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  482. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  483. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
  484. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
  485. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  486. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  487. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
  488. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
  489. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +245 -0
  490. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
  491. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
  492. package/templates/supplier-payments-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  493. package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +12 -0
  494. package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +797 -0
  495. package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +206 -0
  496. package/templates/supplier-payments-agent/packages/agent-runtime/src/poll.ts +105 -0
  497. package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +268 -0
  498. package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +229 -0
  499. package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +154 -0
  500. package/templates/supplier-payments-agent/packages/agent-runtime/src/webhook.ts +131 -0
  501. package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  502. package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
  503. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
  504. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
  505. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  506. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  507. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
  508. package/templates/supplier-payments-agent/packages/agent-runtime/tsconfig.json +9 -0
  509. package/templates/supplier-payments-agent/tsconfig.base.json +16 -0
  510. package/templates/supplier-payments-agent/vitest.config.ts +12 -0
  511. package/templates/bills-agent/agents/bills-agent/channels/terminal/index.ts +0 -108
  512. package/templates/bills-agent/agents/bills-agent/src/commands/approve.ts +0 -9
  513. package/templates/bills-agent/agents/bills-agent/src/commands/check.ts +0 -13
  514. package/templates/bills-agent/agents/bills-agent/src/commands/consent.ts +0 -26
  515. package/templates/bills-agent/agents/bills-agent/src/commands/decide.ts +0 -45
  516. package/templates/bills-agent/agents/bills-agent/src/commands/deny.ts +0 -7
  517. package/templates/bills-agent/agents/bills-agent/src/commands/eval.ts +0 -37
  518. package/templates/bills-agent/agents/bills-agent/src/commands/reconcile.ts +0 -38
  519. package/templates/bills-agent/agents/bills-agent/src/commands/rerun.ts +0 -65
  520. package/templates/bills-agent/agents/bills-agent/src/commands/resume.ts +0 -30
  521. package/templates/bills-agent/agents/bills-agent/src/main.ts +0 -185
  522. package/templates/collections-agent/agents/collections-agent/channels/terminal/index.ts +0 -178
  523. package/templates/collections-agent/agents/collections-agent/src/commands/approve.ts +0 -10
  524. package/templates/collections-agent/agents/collections-agent/src/commands/check.ts +0 -13
  525. package/templates/collections-agent/agents/collections-agent/src/commands/decide.ts +0 -48
  526. package/templates/collections-agent/agents/collections-agent/src/commands/deny.ts +0 -7
  527. package/templates/collections-agent/agents/collections-agent/src/commands/eval.ts +0 -39
  528. package/templates/collections-agent/agents/collections-agent/src/commands/poll.ts +0 -35
  529. package/templates/collections-agent/agents/collections-agent/src/commands/reconcile.ts +0 -38
  530. package/templates/collections-agent/agents/collections-agent/src/commands/rerun.ts +0 -55
  531. package/templates/collections-agent/agents/collections-agent/src/commands/resume.ts +0 -30
  532. package/templates/collections-agent/agents/collections-agent/src/commands/webhook.ts +0 -36
  533. package/templates/collections-agent/agents/collections-agent/src/main.ts +0 -193
  534. package/templates/collections-agent/agents/collections-agent/src/setup.ts +0 -250
@@ -0,0 +1,401 @@
1
+ /**
2
+ * `receipt-verification`: the half of a receipt a party OUTSIDE CodeSpar can
3
+ * check.
4
+ *
5
+ * A receipt carries two signatures and they prove different things to
6
+ * different people.
7
+ *
8
+ * `receipt_sig` HMAC over the chain, under the consumer's secret.
9
+ * Verifying it means holding the key that also MINTS
10
+ * it, so it proves the payment to whoever runs the
11
+ * agent and to nobody else.
12
+ * `receipt_sig_ed25519` Ed25519 over `codespar-receipt:v1:<id>:<chain>`,
13
+ * made with CodeSpar's platform issuer key, whose
14
+ * PUBLIC half is served with no credential at
15
+ * `/.well-known/codespar-receipt-keys.json`. That one
16
+ * proves the payment to an accountant, a marketplace
17
+ * reconciling a payout, or a court — parties that
18
+ * have no relationship with CodeSpar at all.
19
+ *
20
+ * This module checks the second. It imports `node:crypto` and nothing else: no
21
+ * CodeSpar SDK, no key material, no token, no network of its own (the key
22
+ * document is passed in, or fetched by a function the caller supplies). A
23
+ * third party can read the forty lines that matter and reimplement them.
24
+ *
25
+ * WHAT A `verified` VERDICT PROVES, exactly: CodeSpar sealed a receipt with
26
+ * THIS id and THIS chain. The chain is a SHA-256 over the four links of the
27
+ * Control Record, so the seller, the amount and the payee are inside it — but
28
+ * recomputing that digest from the receipt body needs the canonical link
29
+ * shapes, which CodeSpar does not publish today. Binding the body to the chain
30
+ * is therefore not something this module can promise, and it does not pretend
31
+ * to: see `docs/OPEN_QUESTIONS.md` §47. It is also why the signature survives
32
+ * the proof bundle's masking — the bundle masks the payee, and the signature
33
+ * covers the id and the digest, not the copy in front of you.
34
+ *
35
+ * WHICH KEY SET. `kid` is `<did>#<n>` and the DID is the platform's, so it is
36
+ * the same string on every deployment while the KEY behind it is not: a
37
+ * sandbox receipt checked against the production key set finds a key of that
38
+ * name, fails to verify, and reads `tampered`. The verifier therefore points
39
+ * at ONE deployment's `/.well-known/codespar-receipt-keys.json` — the default
40
+ * is production, and `--url` names another. Nothing in the two documents tells
41
+ * them apart today; `docs/OPEN_QUESTIONS.md` §47 says so and names the ask.
42
+ *
43
+ * The signed string is the enterprise's `receiptSigningString`
44
+ * (`packages/api/src/receipt-signature.ts`, ent#1633), reproduced here rather
45
+ * than imported, because a verifier that has to install the signer's code
46
+ * proves nothing about the signer.
47
+ */
48
+ import { createPublicKey, verify as verifyDetached } from "node:crypto";
49
+
50
+ /** The domain tag. A receipt names the version it was sealed under through
51
+ * this literal, so a future `v2` does not invalidate a `v1` signature. */
52
+ export const RECEIPT_SIGNATURE_DOMAIN = "codespar-receipt:v1";
53
+
54
+ /** The unauthenticated key set of the production API. `--url` points the
55
+ * verifier at another deployment (staging), `--keys` at a file. */
56
+ export const DEFAULT_RECEIPT_KEYS_URL = "https://api.codespar.dev/.well-known/codespar-receipt-keys.json";
57
+
58
+ /** The key set of a deployment, from its base URL. */
59
+ export function receiptKeysUrl(baseUrl: string): string {
60
+ return new URL("/.well-known/codespar-receipt-keys.json", baseUrl).toString();
61
+ }
62
+
63
+ /** The exact bytes the Ed25519 signature covers. Every segment is fixed-shape:
64
+ * the domain is a literal, the id is `rcpt_` + 22 url-safe characters, and the
65
+ * chain is 64 hex. No free text reaches this string. */
66
+ export function receiptSigningString(receiptId: string, chain: string): string {
67
+ return `${RECEIPT_SIGNATURE_DOMAIN}:${receiptId}:${chain}`;
68
+ }
69
+
70
+ /**
71
+ * The six honest answers. Five of them are not "invalid", and keeping them
72
+ * apart is the point of the module:
73
+ *
74
+ * `verified` the signature is CodeSpar's, over this id and this chain.
75
+ * `tampered` a signature is there and it does not match. A refusal.
76
+ * `unsigned` no signature on this copy. NOT a failure: every receipt
77
+ * sealed before CodeSpar added Ed25519 has none and never
78
+ * will, because signing them today with today's key would
79
+ * attest to what the database says now, not to what happened.
80
+ * `unknown_key` the `kid` is not in the key set. Retired keys stay
81
+ * published precisely so old signatures keep verifying, so a
82
+ * missing kid means another deployment, or a revoked key.
83
+ * `unreachable` no usable key set. UNKNOWN — never "invalid".
84
+ * `malformed` the file is not a receipt this module can read. Distinct
85
+ * from `unsigned` on purpose: answering "unsigned" for an
86
+ * arbitrary JSON file would state that it is a receipt from
87
+ * before the capability existed, which is a claim about a
88
+ * file nobody checked.
89
+ */
90
+ export type ReceiptVerdict = "verified" | "tampered" | "unsigned" | "unknown_key" | "unreachable" | "malformed";
91
+
92
+ export interface ReceiptVerification {
93
+ verdict: ReceiptVerdict;
94
+ /** The receipt this is about, when the document names one. */
95
+ receipt_id: string | null;
96
+ /** The key the receipt names, when it names one. */
97
+ kid: string | null;
98
+ /** `active` or `retired`, as the key set publishes it. A retired key signs
99
+ * nothing new and verifies everything it already signed. */
100
+ key_status: string | null;
101
+ /** The bytes that were verified, so a reader can reproduce the check by hand. */
102
+ signing_string: string | null;
103
+ /** Where the keys came from, or `null` when no key was needed. */
104
+ keys_from: string | null;
105
+ /** A stable machine code under the verdict. */
106
+ reason: string;
107
+ /** One sentence for a person. Never a stack trace. */
108
+ message: string;
109
+ }
110
+
111
+ /** One published key, RFC 8037 shape, as the JWKS serves it. */
112
+ export interface PublishedReceiptKey {
113
+ kid: string;
114
+ kty: string;
115
+ crv: string;
116
+ /** base64url of the raw 32-byte public key. */
117
+ x: string;
118
+ alg?: string;
119
+ use?: string;
120
+ status?: string;
121
+ created_at?: string;
122
+ retired_at?: string | null;
123
+ }
124
+
125
+ export interface ReceiptKeyDocument {
126
+ issuer?: string;
127
+ algorithm?: string;
128
+ signing_string?: string;
129
+ keys: PublishedReceiptKey[];
130
+ }
131
+
132
+ /** The receipt fields this module reads. Everything else in the JSON is none
133
+ * of its business — including the HMAC seal, which needs a secret it must
134
+ * never be given. */
135
+ export interface SignedReceiptFields {
136
+ receipt_id: string;
137
+ chain: string | null;
138
+ receipt_sig_ed25519: string | null;
139
+ receipt_sig_kid: string | null;
140
+ }
141
+
142
+ /** The caller's fetcher. Injected so the module has no network of its own and
143
+ * a unit test needs none. */
144
+ export type KeyDocumentFetcher = (url: string) => Promise<unknown>;
145
+
146
+ export interface KeySource {
147
+ /** A key set already in hand — a file, a fixture, a document fetched once
148
+ * and reused across a folder of receipts. */
149
+ document?: unknown;
150
+ /** Where to fetch it from, with the function that does the fetching. */
151
+ url?: string;
152
+ fetch?: KeyDocumentFetcher;
153
+ /** What to call the origin in the report. Defaults to the url, or `file`. */
154
+ label?: string;
155
+ }
156
+
157
+ /* ── Reading the documents ───────────────────────────────────── */
158
+
159
+ function isRecord(value: unknown): value is Record<string, unknown> {
160
+ return typeof value === "object" && value !== null && !Array.isArray(value);
161
+ }
162
+
163
+ function stringOrNull(value: unknown): string | null {
164
+ return typeof value === "string" && value.length > 0 ? value : null;
165
+ }
166
+
167
+ /** base64url, strictly: Node's decoder accepts standard base64 and skips
168
+ * characters it does not know, so a round trip is what proves the string was
169
+ * the encoding it claims to be. */
170
+ function decodeBase64Url(value: string, expectedBytes: number): Buffer | undefined {
171
+ if (!/^[A-Za-z0-9_-]+$/.test(value)) return undefined;
172
+ const buf = Buffer.from(value, "base64url");
173
+ if (buf.length !== expectedBytes || buf.toString("base64url") !== value) return undefined;
174
+ return buf;
175
+ }
176
+
177
+ export type ReceiptRead = { ok: true; fields: SignedReceiptFields } | { ok: false; reason: string; message: string; receipt_id: string | null };
178
+
179
+ /**
180
+ * Read the four fields out of a receipt JSON, or say why it cannot be read.
181
+ *
182
+ * A signature without its `kid`, or a `kid` without its signature, is a
183
+ * MALFORMED document and not an unsigned one. The API writes both columns or
184
+ * neither, so half a pair means the file was edited — and "unsigned" is the
185
+ * benign verdict, which is exactly the one an edit must not be able to reach
186
+ * by deletion.
187
+ */
188
+ export function readSignedReceipt(input: unknown): ReceiptRead {
189
+ if (!isRecord(input)) {
190
+ return { ok: false, reason: "not_an_object", receipt_id: null, message: "this file is not a JSON object, so it is not a receipt" };
191
+ }
192
+ const receiptId = stringOrNull(input["receipt_id"]);
193
+ if (receiptId === null) {
194
+ return { ok: false, reason: "receipt_id_missing", receipt_id: null, message: "this JSON carries no `receipt_id`, so it is not a CodeSpar receipt" };
195
+ }
196
+ const sigRaw = input["receipt_sig_ed25519"];
197
+ const kidRaw = input["receipt_sig_kid"];
198
+ const sig = stringOrNull(sigRaw);
199
+ const kid = stringOrNull(kidRaw);
200
+ const sigAbsent = sigRaw === undefined || sigRaw === null;
201
+ const kidAbsent = kidRaw === undefined || kidRaw === null;
202
+ if (!sigAbsent && sig === null) {
203
+ return { ok: false, reason: "signature_not_a_string", receipt_id: receiptId, message: `receipt ${receiptId} carries a \`receipt_sig_ed25519\` that is not a string` };
204
+ }
205
+ if (!kidAbsent && kid === null) {
206
+ return { ok: false, reason: "kid_not_a_string", receipt_id: receiptId, message: `receipt ${receiptId} carries a \`receipt_sig_kid\` that is not a string` };
207
+ }
208
+ if (sig !== null && kid === null) {
209
+ return { ok: false, reason: "kid_missing", receipt_id: receiptId, message: `receipt ${receiptId} carries a signature and no \`receipt_sig_kid\`, so there is no way to know which key to check it against; a receipt sealed before Ed25519 carries neither` };
210
+ }
211
+ if (kid !== null && sig === null) {
212
+ return { ok: false, reason: "signature_missing", receipt_id: receiptId, message: `receipt ${receiptId} names the key \`${kid}\` and carries no \`receipt_sig_ed25519\` to check against it` };
213
+ }
214
+ const chain = stringOrNull(input["chain"]);
215
+ if (sig !== null && chain === null) {
216
+ return { ok: false, reason: "chain_missing", receipt_id: receiptId, message: `receipt ${receiptId} carries a signature and no \`chain\`, so the signed string cannot be rebuilt` };
217
+ }
218
+ return { ok: true, fields: { receipt_id: receiptId, chain, receipt_sig_ed25519: sig, receipt_sig_kid: kid } };
219
+ }
220
+
221
+ export type KeyDocumentRead = { ok: true; document: ReceiptKeyDocument } | { ok: false; reason: string; message: string };
222
+
223
+ /** Read a JWKS, or say why it is not one. Anything unusable is `unreachable`
224
+ * territory: a key set we cannot read leaves us knowing nothing about the
225
+ * signature, which is not the same as knowing it is bad. */
226
+ export function readKeyDocument(input: unknown): KeyDocumentRead {
227
+ if (!isRecord(input)) return { ok: false, reason: "key_document_unreadable", message: "the key set is not a JSON object" };
228
+ const keys = input["keys"];
229
+ if (!Array.isArray(keys)) return { ok: false, reason: "key_document_unreadable", message: "the key set carries no `keys` array" };
230
+ const parsed: PublishedReceiptKey[] = [];
231
+ for (const entry of keys) {
232
+ if (!isRecord(entry)) continue;
233
+ const kid = stringOrNull(entry["kid"]);
234
+ const x = stringOrNull(entry["x"]);
235
+ const kty = stringOrNull(entry["kty"]);
236
+ const crv = stringOrNull(entry["crv"]);
237
+ if (kid === null || x === null || kty === null || crv === null) continue;
238
+ parsed.push({
239
+ kid,
240
+ kty,
241
+ crv,
242
+ x,
243
+ ...(stringOrNull(entry["alg"]) !== null ? { alg: entry["alg"] as string } : {}),
244
+ ...(stringOrNull(entry["status"]) !== null ? { status: entry["status"] as string } : {}),
245
+ });
246
+ }
247
+ const document: ReceiptKeyDocument = {
248
+ keys: parsed,
249
+ ...(stringOrNull(input["issuer"]) !== null ? { issuer: input["issuer"] as string } : {}),
250
+ ...(stringOrNull(input["signing_string"]) !== null ? { signing_string: input["signing_string"] as string } : {}),
251
+ };
252
+ return { ok: true, document };
253
+ }
254
+
255
+ /* ── The check ───────────────────────────────────────────────── */
256
+
257
+ /**
258
+ * Verify a receipt against a key set already in hand. Synchronous, offline,
259
+ * and the whole of the cryptography: everything else in this file is reading
260
+ * JSON carefully.
261
+ */
262
+ export function verifyReceiptWithKeys(receipt: unknown, keyDocument: unknown, label = "file"): ReceiptVerification {
263
+ const read = readSignedReceipt(receipt);
264
+ if (!read.ok) {
265
+ return { verdict: "malformed", receipt_id: read.receipt_id, kid: null, key_status: null, signing_string: null, keys_from: null, reason: read.reason, message: read.message };
266
+ }
267
+ const { receipt_id: receiptId, chain, receipt_sig_ed25519: signature, receipt_sig_kid: kid } = read.fields;
268
+
269
+ if (signature === null || kid === null || chain === null) {
270
+ return {
271
+ verdict: "unsigned",
272
+ receipt_id: receiptId,
273
+ kid: null,
274
+ key_status: null,
275
+ signing_string: null,
276
+ keys_from: null,
277
+ reason: "no_ed25519_signature",
278
+ message: `receipt ${receiptId} carries no Ed25519 signature. Receipts sealed before CodeSpar added one carry none and never will; its HMAC seal is unaffected and still proves the payment to whoever runs the agent`,
279
+ };
280
+ }
281
+
282
+ const signingString = receiptSigningString(receiptId, chain);
283
+ const doc = readKeyDocument(keyDocument);
284
+ if (!doc.ok) {
285
+ return { verdict: "unreachable", receipt_id: receiptId, kid, key_status: null, signing_string: signingString, keys_from: label, reason: doc.reason, message: `${doc.message}, so nothing is proved and nothing is disproved about receipt ${receiptId}` };
286
+ }
287
+
288
+ const key = doc.document.keys.find((k) => k.kid === kid);
289
+ if (!key) {
290
+ return {
291
+ verdict: "unknown_key",
292
+ receipt_id: receiptId,
293
+ kid,
294
+ key_status: null,
295
+ signing_string: signingString,
296
+ keys_from: label,
297
+ reason: "kid_not_published",
298
+ message: `the key \`${kid}\` is not among the ${doc.document.keys.length} published at ${label}. CodeSpar keeps retired keys published so old signatures go on verifying, so a missing kid is a key from another deployment, or one that was revoked`,
299
+ };
300
+ }
301
+ const keyStatus = key.status ?? null;
302
+
303
+ if (key.kty !== "OKP" || key.crv !== "Ed25519") {
304
+ return { verdict: "unreachable", receipt_id: receiptId, kid, key_status: keyStatus, signing_string: signingString, keys_from: label, reason: "key_unusable", message: `the published key \`${kid}\` is ${key.kty}/${key.crv} and not OKP/Ed25519, so this module cannot check the signature with it` };
305
+ }
306
+ const publicKey = decodeBase64Url(key.x, 32);
307
+ if (!publicKey) {
308
+ return { verdict: "unreachable", receipt_id: receiptId, kid, key_status: keyStatus, signing_string: signingString, keys_from: label, reason: "key_unusable", message: `the published key \`${kid}\` does not carry a 32-byte base64url public key, so this module cannot check the signature with it` };
309
+ }
310
+ const signatureBytes = decodeBase64Url(signature, 64);
311
+ if (!signatureBytes) {
312
+ return { verdict: "tampered", receipt_id: receiptId, kid, key_status: keyStatus, signing_string: signingString, keys_from: label, reason: "signature_malformed", message: `receipt ${receiptId} carries a \`receipt_sig_ed25519\` that is not a 64-byte base64url Ed25519 signature, so it is not the one CodeSpar wrote` };
313
+ }
314
+
315
+ let matches: boolean;
316
+ try {
317
+ matches = verifyDetached(null, Buffer.from(signingString, "utf8"), createPublicKey({ key: { kty: "OKP", crv: "Ed25519", x: key.x }, format: "jwk" }), signatureBytes);
318
+ } catch (err) {
319
+ return { verdict: "unreachable", receipt_id: receiptId, kid, key_status: keyStatus, signing_string: signingString, keys_from: label, reason: "key_unusable", message: `the published key \`${kid}\` could not be used: ${err instanceof Error ? err.message : String(err)}` };
320
+ }
321
+
322
+ if (!matches) {
323
+ return {
324
+ verdict: "tampered",
325
+ receipt_id: receiptId,
326
+ kid,
327
+ key_status: keyStatus,
328
+ signing_string: signingString,
329
+ keys_from: label,
330
+ reason: "signature_does_not_match",
331
+ message: `receipt ${receiptId} does not match its signature: the key \`${kid}\` did not sign this receipt id and this chain. The id or the chain was changed after the receipt was sealed, the signature was copied from another receipt, or this key set is not the one the deployment that sealed it publishes — a sandbox receipt checked against the production keys reads exactly like this, because every deployment publishes its own key under the same \`kid\``,
332
+ };
333
+ }
334
+ return {
335
+ verdict: "verified",
336
+ receipt_id: receiptId,
337
+ kid,
338
+ key_status: keyStatus,
339
+ signing_string: signingString,
340
+ keys_from: label,
341
+ reason: "signature_matches",
342
+ message: `receipt ${receiptId} was sealed by CodeSpar: the ${keyStatus ?? "published"} key \`${kid}\` signed this receipt id and this chain`,
343
+ };
344
+ }
345
+
346
+ /**
347
+ * Verify a receipt, fetching the key set when one is not in hand.
348
+ *
349
+ * The fetch is the caller's function, so this module opens no socket and a
350
+ * unit test needs no network. A fetch that fails is `unreachable` and never
351
+ * `tampered`: not knowing is not the same as knowing it is bad, and a
352
+ * verifier that conflated the two would call every receipt invalid the day
353
+ * its DNS broke.
354
+ */
355
+ export async function verifyReceipt(receipt: unknown, source: KeySource = {}): Promise<ReceiptVerification> {
356
+ const read = readSignedReceipt(receipt);
357
+ if (!read.ok) {
358
+ return { verdict: "malformed", receipt_id: read.receipt_id, kid: null, key_status: null, signing_string: null, keys_from: null, reason: read.reason, message: read.message };
359
+ }
360
+ // An unsigned receipt needs no key, so it is answered before the network is
361
+ // touched: fetching a key set to check a signature that is not there would
362
+ // turn an offline answer into an outage.
363
+ if (read.fields.receipt_sig_ed25519 === null) return verifyReceiptWithKeys(receipt, { keys: [] }, source.label ?? "none");
364
+
365
+ if (source.document !== undefined) return verifyReceiptWithKeys(receipt, source.document, source.label ?? "file");
366
+
367
+ const url = source.url ?? DEFAULT_RECEIPT_KEYS_URL;
368
+ const label = source.label ?? url;
369
+ if (!source.fetch) {
370
+ return { verdict: "unreachable", receipt_id: read.fields.receipt_id, kid: read.fields.receipt_sig_kid, key_status: null, signing_string: null, keys_from: label, reason: "no_fetcher", message: "no key set was given and no way to fetch one: pass a document, or a fetch function" };
371
+ }
372
+ let fetched: unknown;
373
+ try {
374
+ fetched = await source.fetch(url);
375
+ } catch (err) {
376
+ return {
377
+ verdict: "unreachable",
378
+ receipt_id: read.fields.receipt_id,
379
+ kid: read.fields.receipt_sig_kid,
380
+ key_status: null,
381
+ signing_string: null,
382
+ keys_from: label,
383
+ reason: "fetch_failed",
384
+ message: `the published key set could not be read from ${label}: ${err instanceof Error ? err.message : String(err)}. Nothing is proved and nothing is disproved; the keys can also be saved to a file and passed in`,
385
+ };
386
+ }
387
+ return verifyReceiptWithKeys(receipt, fetched, label);
388
+ }
389
+
390
+ /** The exit code of `codespar-agent verify`, one per verdict, so a script can
391
+ * branch on the answer instead of on "did it fail". `unsigned` is not 0: it is
392
+ * not a failure, and it is not a verification either. 2 is the usage error the
393
+ * other commands already use. */
394
+ export const VERDICT_EXIT_CODES: Record<ReceiptVerdict, number> = {
395
+ verified: 0,
396
+ tampered: 1,
397
+ unsigned: 3,
398
+ unknown_key: 4,
399
+ unreachable: 5,
400
+ malformed: 6,
401
+ };
@@ -6,7 +6,7 @@
6
6
  * does not compile; the runtime check below refuses the same pair when the
7
7
  * types were bypassed (a cast, a row read back from SQLite).
8
8
  */
9
- import type { Actor, ApprovalMode, EscalationTrigger, ExecutionItem, ExecutionReason, ItemOutcome, MandateRef } from "./types.js";
9
+ import type { Actor, ApprovalMode, EscalationTrigger, ExecutionBatch, ExecutionItem, ExecutionReason, ItemOutcome, MandateRef } from "./types.js";
10
10
 
11
11
  export type ExecutionState =
12
12
  | "drafted"
@@ -76,6 +76,8 @@ export interface Execution<S extends ExecutionState = ExecutionState> {
76
76
  /** What the model claimed the total was, when it claimed one. Recorded, never used. */
77
77
  model_claimed_total?: number;
78
78
  items_hash: string;
79
+ /** Present when this execution is one line of a batch: the set it was presented inside, and where in it. */
80
+ batch?: ExecutionBatch;
79
81
  mandate: MandateRef;
80
82
  /** Written once, before the rail is called. Retrying reuses it. */
81
83
  idempotency_key: string;
@@ -139,6 +139,8 @@ export class StubChargeRail implements PaymentRail {
139
139
  payment: { amount_minor: req.amount_minor, payee: req.payee, attempt_id: req.attempt_id, money_moved: false, sandbox: true, at: raw.paid_at ?? sealed.at },
140
140
  chain: null,
141
141
  receipt_sig: null,
142
+ receipt_sig_ed25519: null,
143
+ receipt_sig_kid: null,
142
144
  actor,
143
145
  raw: { stub: true, charge_id: out.transaction_id, simulated: true, settled_against: "sandbox_fixture" },
144
146
  };
@@ -17,6 +17,14 @@ export interface StubRailOptions {
17
17
  refusePayees?: string[];
18
18
  /** Attempt ids the stub answers `uncertain` for, once. */
19
19
  uncertainOnce?: string[];
20
+ /**
21
+ * Payees whose first presentation answers `uncertain`, the way a timeout
22
+ * does: the provider still took the attempt, so a later lookup finds it.
23
+ * The payee twin of `refusePayees`, for scripting an unknown outcome in the
24
+ * MIDDLE of a multi-item execution, where the attempt ids are not known
25
+ * until the execution exists.
26
+ */
27
+ uncertainPayees?: string[];
20
28
  /** Test hook: called after the attempt is persisted and before the outcome is returned. */
21
29
  afterDispatch?: (attemptId: string) => void;
22
30
  /** Attempt ids whose first lookup answers `in_flight` and whose second lookup finds the provider finished (settled), with no pay() in between. */
@@ -40,6 +48,8 @@ export class StubRail implements PaymentRail {
40
48
  private readonly inFlightSeen = new Set<string>();
41
49
  /** Attempts that answered `uncertain` because the stub was armed: the "provider" did take them, and a later lookup finds them finished. */
42
50
  private readonly lateAttempts = new Set<string>();
51
+ /** Attempts that already gave their one `uncertain`, so a re-presentation behaves normally. */
52
+ private readonly uncertainAnswered = new Set<string>();
43
53
  /** How many times pay() reached the point of persisting a NEW attempt. */
44
54
  payCount = 0;
45
55
 
@@ -52,8 +62,10 @@ export class StubRail implements PaymentRail {
52
62
  const existing = this.store.stubRailGet(payment.attempt_id);
53
63
  if (existing) return existing.outcome as RailOutcome;
54
64
 
55
- if (this.uncertainArmed || this.uncertainPending.has(payment.attempt_id)) {
56
- if (this.uncertainArmed) this.lateAttempts.add(payment.attempt_id);
65
+ const uncertainPayee = this.options.uncertainPayees?.includes(payment.payee) === true && !this.uncertainAnswered.has(payment.attempt_id);
66
+ if (this.uncertainArmed || this.uncertainPending.has(payment.attempt_id) || uncertainPayee) {
67
+ if (this.uncertainArmed || uncertainPayee) this.lateAttempts.add(payment.attempt_id);
68
+ this.uncertainAnswered.add(payment.attempt_id);
57
69
  this.uncertainArmed = false;
58
70
  this.uncertainPending.delete(payment.attempt_id);
59
71
  return { status: "uncertain", code: "psp_dispatch_uncertain", message: "stub: outcome unknown on first presentation" };
@@ -117,6 +129,11 @@ export class StubRail implements PaymentRail {
117
129
  ...body,
118
130
  chain,
119
131
  receipt_sig: `stub:${sha256Hex(`sig:${chain}`).slice(0, 32)}`,
132
+ // The stub holds no CodeSpar key and must not look as though it did:
133
+ // `npm run verify` answers `unsigned` on a stub receipt, which is the
134
+ // truth about it.
135
+ receipt_sig_ed25519: null,
136
+ receipt_sig_kid: null,
120
137
  actor,
121
138
  raw: { stub: true, transaction_id: out.transaction_id },
122
139
  };
@@ -26,6 +26,32 @@ export interface ExecutionItem {
26
26
  due_date?: string;
27
27
  }
28
28
 
29
+ /**
30
+ * The SET a line belongs to, when the line is one execution of a batch.
31
+ *
32
+ * A batch of N lines is N executions, each attested by its own `items_hash`.
33
+ * That leaves the list itself unattested: a person who approved four lines
34
+ * while the agent ran three leaves a bundle that does not say a fourth
35
+ * existed. `batch_hash` binds the set — computed once over the ordered lines
36
+ * at the moment the list is presented for approval, and carried by every
37
+ * artifact of that batch together with the line's own position in it.
38
+ *
39
+ * Absent on an execution that is not part of a batch, and an artifact
40
+ * without it is the artifact of section 4.2 unchanged, byte for byte: the
41
+ * field is omitted rather than null, so the canonical payload it signs is
42
+ * the same payload it signed before this existed.
43
+ */
44
+ export interface ExecutionBatch {
45
+ /** The batch's reference, as the kit names it (`folha-2026-10`). */
46
+ ref: string;
47
+ /** `batchHash` over the batch's ordered lines, as they were presented. */
48
+ batch_hash: string;
49
+ /** This line's position in that ordered list, counting from 0. */
50
+ index: number;
51
+ /** How many lines the presented list held. */
52
+ count: number;
53
+ }
54
+
29
55
  /** The trigger of section 4.4 that sent a `mandate` execution to a human. */
30
56
  export type EscalationTrigger = "amount" | "new_beneficiary" | "outside_hours";
31
57
 
@@ -74,6 +100,8 @@ export interface ApprovalArtifact {
74
100
  mandate: MandateRef;
75
101
  items: ExecutionItem[];
76
102
  items_hash: string;
103
+ /** Present when this execution is one line of a batch: what binds the SET this line was approved inside. */
104
+ batch?: ExecutionBatch;
77
105
  /** Present when a section 4.4 trigger sent the execution to a human first. */
78
106
  escalation?: { trigger: EscalationTrigger; detail: string };
79
107
  actor: Actor;
@@ -1,8 +1,8 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { checkApprovalArtifact, createApprovalArtifact, hmacSigner } from "../src/approval.js";
3
- import { itemsHash } from "../src/hash.js";
3
+ import { batchHash, itemsHash } from "../src/hash.js";
4
4
  import type { Execution } from "../src/state-machine.js";
5
- import type { Actor, ExecutionItem } from "../src/types.js";
5
+ import type { Actor, ExecutionBatch, ExecutionItem } from "../src/types.js";
6
6
 
7
7
  const actor: Actor = { type: "human", id: "usr_1", channel: "terminal" };
8
8
  const signer = hmacSigner("test", Buffer.alloc(32, 7));
@@ -30,6 +30,7 @@ function execution(items: ExecutionItem[]): Execution<"awaiting_approval"> {
30
30
  }
31
31
 
32
32
  const escola: ExecutionItem = { alias: "escola", beneficiary: "Escola", payee: "escola@exemplo.com.br", amount: 185000, currency: "BRL" };
33
+ const mercado: ExecutionItem = { alias: "mercado", beneficiary: "Mercado", payee: "+5511999990001", amount: 42000, currency: "BRL" };
33
34
 
34
35
  describe("section 4.2: approval artifact", () => {
35
36
  it("has the schema of the spec and an HMAC signature that verifies", () => {
@@ -69,6 +70,39 @@ describe("section 4.2: approval artifact", () => {
69
70
  expect(checkApprovalArtifact(signer, artifact, { ...exe, mandate: { id: "mdt_1", version: 4 } }, now)).toEqual({ ok: false, problem: "wrong_mandate" });
70
71
  });
71
72
 
73
+ it("carries the batch this line belongs to, and the whole of it is signed", () => {
74
+ const batch: ExecutionBatch = { ref: "folha-2026-10", batch_hash: batchHash([escola, mercado]), index: 0, count: 2 };
75
+ const exe = { ...execution([escola]), batch };
76
+ const artifact = createApprovalArtifact(signer, { execution: exe, approver: { type: "person", id: "usr_1", channel: "terminal" }, actor, now });
77
+ expect(artifact.batch).toEqual(batch);
78
+ expect(checkApprovalArtifact(signer, artifact, exe, now)).toEqual({ ok: true });
79
+ // Moving the line inside the list, re-pricing the list, or renaming it:
80
+ // each is a different set, and the signature covers all of it.
81
+ for (const changed of [{ ...batch, index: 1 }, { ...batch, count: 3 }, { ...batch, ref: "folha-2026-11" }, { ...batch, batch_hash: batchHash([escola]) }]) {
82
+ expect(checkApprovalArtifact(signer, { ...artifact, batch: changed }, exe, now)).toEqual({ ok: false, problem: "signature_invalid" });
83
+ expect(checkApprovalArtifact(signer, artifact, { ...exe, batch: changed }, now)).toEqual({ ok: false, problem: "batch_mismatch" });
84
+ }
85
+ });
86
+
87
+ it("an artifact with no batch signs the payload it signed before batches existed", () => {
88
+ const exe = execution([escola]);
89
+ const artifact = createApprovalArtifact(signer, { execution: exe, approver: { type: "person", id: "usr_1", channel: "terminal" }, actor, now });
90
+ // Omitted, never null: a bills-agent run has no batch and must not grow a key.
91
+ expect("batch" in artifact).toBe(false);
92
+ expect(checkApprovalArtifact(signer, artifact, exe, now)).toEqual({ ok: true });
93
+ // An execution that gained a batch after the artifact was signed is not the one that was approved.
94
+ const joined = { ...exe, batch: { ref: "folha-2026-10", batch_hash: batchHash([escola]), index: 0, count: 1 } };
95
+ expect(checkApprovalArtifact(signer, artifact, joined, now)).toEqual({ ok: false, problem: "batch_mismatch" });
96
+ });
97
+
98
+ it("the batch hash IS the items hash of the whole ordered list, so a reader can recompute it", () => {
99
+ expect(batchHash([escola, mercado])).toBe(itemsHash([escola, mercado]));
100
+ // Order is part of it: `index` names a position, and a position only means something in a fixed order.
101
+ expect(batchHash([mercado, escola])).not.toBe(batchHash([escola, mercado]));
102
+ // A line dropped is a different set, which is the whole point.
103
+ expect(batchHash([escola])).not.toBe(batchHash([escola, mercado]));
104
+ });
105
+
72
106
  it("records the escalation trigger when there was one", () => {
73
107
  const exe = execution([escola]);
74
108
  const artifact = createApprovalArtifact(signer, {
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Section 11: what the proof bundle has to carry for `npm run inspect` to
3
+ * answer "why could the agent pay this" from the folder alone — the rail's
4
+ * request and its answer per attempt, and a receipt path that travels with
5
+ * the bundle instead of naming the machine that wrote it.
6
+ */
7
+ import { readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { describe, expect, it } from "vitest";
10
+ import { maskPayee } from "../src/bundle.js";
11
+ import { harness } from "./helpers.js";
12
+
13
+ const approver = { id: "usr_demo", channel: "terminal" };
14
+
15
+ async function settledRun() {
16
+ const h = harness({ mode: "human" });
17
+ const draft = await h.engine.draft({ items: [{ payee: "escola", amount: 185000, description: "outubro" }] });
18
+ if (!draft.ok) throw new Error("refused");
19
+ const approved = h.engine.approve(draft.execution.id, approver);
20
+ const settled = await h.engine.execute(approved.id);
21
+ return { h, settled };
22
+ }
23
+
24
+ describe("the proof bundle records the rail call and its answer", () => {
25
+ it("rail.dispatch names the idempotency key that makes a retry the same payment", async () => {
26
+ const { h, settled } = await settledRun();
27
+ const dispatch = h.bundle.readEvents().find((e) => e["type"] === "rail.dispatch");
28
+ expect(dispatch).toBeDefined();
29
+ const payload = dispatch!["payload"] as Record<string, unknown>;
30
+ expect(payload["idempotency_key"]).toBe(settled.idempotency_key);
31
+ expect(payload["attempt_id"]).toBe(settled.outcomes[0]?.attempt_id);
32
+ expect(payload["rail"]).toBe("stub");
33
+ });
34
+
35
+ it("rail.outcome carries what the rail answered: transaction, receipt and whether money moved", async () => {
36
+ const { h, settled } = await settledRun();
37
+ const outcome = h.bundle.readEvents().find((e) => e["type"] === "rail.outcome");
38
+ const payload = outcome!["payload"] as Record<string, unknown>;
39
+ expect(payload["status"]).toBe("settled");
40
+ expect(payload["receipt_id"]).toBe(settled.outcomes[0]?.receipt_id);
41
+ expect(payload["transaction_id"]).toBe(settled.outcomes[0]?.transaction_id);
42
+ expect(payload["money_moved"]).toBe(false);
43
+ expect(payload["sandbox"]).toBe(true);
44
+ // Never the provider's echo: `raw` may carry payer data the bundle does not promise to mask.
45
+ expect(payload["raw"]).toBeUndefined();
46
+ });
47
+
48
+ it("a refused attempt records the rail's own code and message", async () => {
49
+ const h = harness({ mode: "human", rail: { refusePayees: ["escola@exemplo.com.br"] } });
50
+ const draft = await h.engine.draft({ items: [{ payee: "escola", amount: 185000 }] });
51
+ if (!draft.ok) throw new Error("refused");
52
+ const approved = h.engine.approve(draft.execution.id, approver);
53
+ await h.engine.execute(approved.id);
54
+ const outcome = h.bundle.readEvents().find((e) => e["type"] === "rail.outcome");
55
+ const payload = outcome!["payload"] as Record<string, unknown>;
56
+ expect(payload["status"]).toBe("failed");
57
+ expect(payload["code"]).toBeTypeOf("string");
58
+ expect(payload["message"]).toBeTypeOf("string");
59
+ });
60
+
61
+ it("receipt.saved names the file inside the bundle, not the machine that wrote it", async () => {
62
+ const { h } = await settledRun();
63
+ const saved = h.bundle.readEvents().find((e) => e["type"] === "receipt.saved");
64
+ const path = (saved!["payload"] as { path: string }).path;
65
+ expect(path).toMatch(/^receipts\//);
66
+ expect(path).not.toContain(h.bundle.dir);
67
+ const receipt = JSON.parse(readFileSync(join(h.bundle.dir, path), "utf8")) as { payment: { payee: string } };
68
+ expect(receipt.payment.payee).toBe(maskPayee("escola@exemplo.com.br"));
69
+ });
70
+
71
+ it("the mandate snapshot carries the version the approval artifact names", async () => {
72
+ const { h, settled } = await settledRun();
73
+ h.bundle.mandateSnapshot(h.engine.mandate);
74
+ const snapshot = JSON.parse(readFileSync(join(h.bundle.dir, "mandate.snapshot.json"), "utf8")) as { version: number; signature?: string };
75
+ expect(snapshot.version).toBe(settled.mandate.version);
76
+ expect(snapshot.signature).toBeUndefined();
77
+ expect(h.bundle.readApprovals()[0]?.mandate.version).toBe(snapshot.version);
78
+ });
79
+
80
+ it("maskPayee is idempotent, so a reader may apply it to a value the bundle already masked", () => {
81
+ for (const raw of ["escola@exemplo.com.br", "+5511999990001", "12345678909", "*"]) {
82
+ expect(maskPayee(maskPayee(raw))).toBe(maskPayee(raw));
83
+ }
84
+ });
85
+ });