@codespar/cli 0.18.1 → 0.21.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 (560) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/login.js +52 -12
  3. package/dist/commands/login.js.map +1 -1
  4. package/dist/commands/payables.js +220 -0
  5. package/dist/commands/payables.js.map +1 -0
  6. package/dist/index.js +77 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/surface.js +31 -13
  9. package/dist/surface.js.map +1 -1
  10. package/package.json +2 -2
  11. package/templates/bills-agent/README.md +2 -2
  12. package/templates/bills-agent/agents/bills-agent/.env.example +2 -2
  13. package/templates/bills-agent/agents/bills-agent/README.md +25 -18
  14. package/templates/bills-agent/agents/bills-agent/SYSTEM_PROMPT.md +13 -3
  15. package/templates/bills-agent/agents/bills-agent/agent.yaml +2 -1
  16. package/templates/bills-agent/agents/bills-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
  17. package/templates/bills-agent/agents/bills-agent/evals/adversarial/false-authority.transcript.jsonl +1 -1
  18. package/templates/bills-agent/agents/bills-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
  19. package/templates/bills-agent/agents/bills-agent/mandate.example.json +1 -1
  20. package/templates/bills-agent/agents/bills-agent/runbook.md +1 -1
  21. package/templates/bills-agent/agents/bills-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +1 -1
  22. package/templates/bills-agent/agents/bills-agent/scenarios/cap-exceeded.transcript.jsonl +1 -1
  23. package/templates/bills-agent/agents/bills-agent/scenarios/escalated-above-threshold.transcript.jsonl +4 -4
  24. package/templates/bills-agent/agents/bills-agent/scenarios/happy-path.transcript.jsonl +1 -1
  25. package/templates/bills-agent/agents/bills-agent/scenarios/mandate-revoked.json +1 -1
  26. package/templates/bills-agent/agents/bills-agent/scenarios/mandate-revoked.transcript.jsonl +4 -4
  27. package/templates/bills-agent/agents/bills-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
  28. package/templates/bills-agent/agents/bills-agent/src/bills.ts +1 -1
  29. package/templates/bills-agent/agents/bills-agent/src/kit.ts +15 -28
  30. package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +42 -15
  31. package/templates/bills-agent/agents/bills-agent/src/modules/pix-out.ts +21 -3
  32. package/templates/bills-agent/agents/bills-agent/src/strings.ts +68 -0
  33. package/templates/bills-agent/agents/bills-agent/test/cli.test.ts +130 -2
  34. package/templates/bills-agent/agents/bills-agent/test/consent-money.test.ts +25 -0
  35. package/templates/bills-agent/agents/bills-agent/test/dates-and-approval.test.ts +98 -0
  36. package/templates/bills-agent/agents/bills-agent/test/eval-runs.test.ts +81 -0
  37. package/templates/bills-agent/agents/bills-agent/test/fixtures/excursao-300.transcript.jsonl +2 -2
  38. package/templates/bills-agent/agents/bills-agent/test/fixtures/material-400.transcript.jsonl +1 -1
  39. package/templates/bills-agent/agents/bills-agent/test/locale.test.ts +161 -0
  40. package/templates/bills-agent/agents/bills-agent/test/mandate-state.test.ts +108 -0
  41. package/templates/bills-agent/agents/bills-agent/test/refused-before-draft.test.ts +81 -0
  42. package/templates/bills-agent/agents/bills-agent/test/replay.test.ts +76 -0
  43. package/templates/bills-agent/agents/bills-agent/test/reply-and-outcome.test.ts +84 -0
  44. package/templates/bills-agent/agents/bills-agent/test/reply-language.test.ts +95 -0
  45. package/templates/bills-agent/agents/bills-agent/test/rerun-and-missing-transcript.test.ts +120 -0
  46. package/templates/bills-agent/agents/bills-agent/test/rerun-world-events.test.ts +55 -0
  47. package/templates/bills-agent/agents/bills-agent/tools.json +1 -1
  48. package/templates/bills-agent/packages/agent-core/README.md +21 -2
  49. package/templates/bills-agent/packages/agent-core/package.json +1 -1
  50. package/templates/bills-agent/packages/agent-core/src/agent.ts +33 -6
  51. package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  52. package/templates/bills-agent/packages/agent-core/src/api/rail.ts +11 -5
  53. package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  54. package/templates/bills-agent/packages/agent-core/src/bundle.ts +2 -0
  55. package/templates/bills-agent/packages/agent-core/src/channels.ts +18 -8
  56. package/templates/bills-agent/packages/agent-core/src/check.ts +25 -3
  57. package/templates/bills-agent/packages/agent-core/src/engine.ts +90 -9
  58. package/templates/bills-agent/packages/agent-core/src/escalate.ts +10 -0
  59. package/templates/bills-agent/packages/agent-core/src/index.ts +3 -0
  60. package/templates/bills-agent/packages/agent-core/src/language.ts +52 -0
  61. package/templates/bills-agent/packages/agent-core/src/locale.ts +88 -0
  62. package/templates/bills-agent/packages/agent-core/src/manifest.ts +4 -1
  63. package/templates/bills-agent/packages/agent-core/src/rail.ts +29 -1
  64. package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  65. package/templates/bills-agent/packages/agent-core/src/secrets.ts +53 -8
  66. package/templates/bills-agent/packages/agent-core/src/state-machine.ts +14 -0
  67. package/templates/bills-agent/packages/agent-core/src/strings.ts +175 -0
  68. package/templates/bills-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  69. package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  70. package/templates/bills-agent/packages/agent-core/src/types.ts +15 -2
  71. package/templates/bills-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  72. package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  73. package/templates/bills-agent/packages/agent-core/test/engine.test.ts +23 -0
  74. package/templates/bills-agent/packages/agent-core/test/helpers.ts +1 -1
  75. package/templates/bills-agent/packages/agent-core/test/language.test.ts +40 -0
  76. package/templates/bills-agent/packages/agent-core/test/locale.test.ts +137 -0
  77. package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  78. package/templates/bills-agent/packages/agent-core/test/manifest.test.ts +2 -2
  79. package/templates/bills-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  80. package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  81. package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +49 -0
  82. package/templates/bills-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  83. package/templates/bills-agent/packages/agent-core/test/secrets.test.ts +40 -1
  84. package/templates/bills-agent/packages/agent-runtime/README.md +31 -4
  85. package/templates/bills-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  86. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  87. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  88. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  89. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  90. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  91. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  92. package/templates/bills-agent/packages/agent-runtime/src/cli.ts +30 -4
  93. package/templates/bills-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  94. package/templates/bills-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  95. package/templates/bills-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  96. package/templates/bills-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  97. package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  98. package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  99. package/templates/bills-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  100. package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  101. package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  102. package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  103. package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  104. package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  105. package/templates/bills-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  106. package/templates/bills-agent/packages/agent-runtime/src/env.ts +64 -0
  107. package/templates/bills-agent/packages/agent-runtime/src/index.ts +2 -0
  108. package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +81 -10
  109. package/templates/bills-agent/packages/agent-runtime/src/kit.ts +34 -12
  110. package/templates/bills-agent/packages/agent-runtime/src/outcome.ts +118 -0
  111. package/templates/bills-agent/packages/agent-runtime/src/poll.ts +7 -7
  112. package/templates/bills-agent/packages/agent-runtime/src/runs.ts +51 -0
  113. package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  114. package/templates/bills-agent/packages/agent-runtime/src/setup.ts +75 -5
  115. package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +71 -33
  116. package/templates/bills-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  117. package/templates/bills-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  118. package/templates/bills-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  119. package/templates/bills-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  120. package/templates/bills-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  121. package/templates/bills-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  122. package/templates/bills-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  123. package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  124. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  125. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  126. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
  127. package/templates/checkout-agent/README.md +2 -2
  128. package/templates/checkout-agent/agents/checkout-agent/.env.example +6 -4
  129. package/templates/checkout-agent/agents/checkout-agent/README.md +16 -8
  130. package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +17 -7
  131. package/templates/checkout-agent/agents/checkout-agent/agent.yaml +4 -3
  132. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +2 -2
  133. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +48 -4
  134. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +1 -1
  135. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +1 -1
  136. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +1 -1
  137. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +1 -1
  138. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
  139. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +1 -1
  140. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +1 -1
  141. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +1 -1
  142. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +1 -1
  143. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
  144. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +1 -1
  145. package/templates/checkout-agent/agents/checkout-agent/package.json +1 -1
  146. package/templates/checkout-agent/agents/checkout-agent/runbook.md +4 -4
  147. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +1 -1
  148. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +2 -2
  149. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +2 -2
  150. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +2 -2
  151. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +1 -1
  152. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +2 -2
  153. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +1 -1
  154. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +1 -1
  155. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +2 -2
  156. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +1 -1
  157. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +3 -3
  158. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +1 -1
  159. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +2 -2
  160. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +1 -1
  161. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
  162. package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +7 -7
  163. package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +47 -43
  164. package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +20 -12
  165. package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +4 -4
  166. package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +9 -6
  167. package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +44 -41
  168. package/templates/checkout-agent/agents/checkout-agent/src/strings.ts +179 -0
  169. package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +4 -1
  170. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +1 -1
  171. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +2 -2
  172. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +1 -1
  173. package/templates/checkout-agent/agents/checkout-agent/test/payable-instrument.test.ts +58 -0
  174. package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +2 -2
  175. package/templates/checkout-agent/agents/checkout-agent/test/rerun-unpaid.test.ts +47 -0
  176. package/templates/checkout-agent/agents/checkout-agent/test/scenario-reach-and-seconds.test.ts +55 -0
  177. package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +26 -10
  178. package/templates/checkout-agent/agents/checkout-agent/tools.json +1 -1
  179. package/templates/checkout-agent/packages/agent-core/README.md +21 -2
  180. package/templates/checkout-agent/packages/agent-core/package.json +1 -1
  181. package/templates/checkout-agent/packages/agent-core/src/agent.ts +33 -6
  182. package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  183. package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +11 -5
  184. package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  185. package/templates/checkout-agent/packages/agent-core/src/bundle.ts +2 -0
  186. package/templates/checkout-agent/packages/agent-core/src/channels.ts +18 -8
  187. package/templates/checkout-agent/packages/agent-core/src/check.ts +25 -3
  188. package/templates/checkout-agent/packages/agent-core/src/engine.ts +90 -9
  189. package/templates/checkout-agent/packages/agent-core/src/escalate.ts +10 -0
  190. package/templates/checkout-agent/packages/agent-core/src/index.ts +3 -0
  191. package/templates/checkout-agent/packages/agent-core/src/language.ts +52 -0
  192. package/templates/checkout-agent/packages/agent-core/src/locale.ts +88 -0
  193. package/templates/checkout-agent/packages/agent-core/src/manifest.ts +4 -1
  194. package/templates/checkout-agent/packages/agent-core/src/rail.ts +29 -1
  195. package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  196. package/templates/checkout-agent/packages/agent-core/src/secrets.ts +53 -8
  197. package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +14 -0
  198. package/templates/checkout-agent/packages/agent-core/src/strings.ts +175 -0
  199. package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  200. package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  201. package/templates/checkout-agent/packages/agent-core/src/types.ts +15 -2
  202. package/templates/checkout-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  203. package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  204. package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +23 -0
  205. package/templates/checkout-agent/packages/agent-core/test/helpers.ts +1 -1
  206. package/templates/checkout-agent/packages/agent-core/test/language.test.ts +40 -0
  207. package/templates/checkout-agent/packages/agent-core/test/locale.test.ts +137 -0
  208. package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  209. package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +2 -2
  210. package/templates/checkout-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  211. package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  212. package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +49 -0
  213. package/templates/checkout-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  214. package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +40 -1
  215. package/templates/checkout-agent/packages/agent-runtime/README.md +31 -4
  216. package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  217. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  218. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  219. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  220. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  221. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  222. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  223. package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +30 -4
  224. package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  225. package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  226. package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  227. package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  228. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  229. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  230. package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  231. package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  232. package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  233. package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  234. package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  235. package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  236. package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  237. package/templates/checkout-agent/packages/agent-runtime/src/env.ts +64 -0
  238. package/templates/checkout-agent/packages/agent-runtime/src/index.ts +2 -0
  239. package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +81 -10
  240. package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +34 -12
  241. package/templates/checkout-agent/packages/agent-runtime/src/outcome.ts +118 -0
  242. package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +7 -7
  243. package/templates/checkout-agent/packages/agent-runtime/src/runs.ts +51 -0
  244. package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  245. package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +75 -5
  246. package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +71 -33
  247. package/templates/checkout-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  248. package/templates/checkout-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  249. package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  250. package/templates/checkout-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  251. package/templates/checkout-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  252. package/templates/checkout-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  253. package/templates/checkout-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  254. package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  255. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  256. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  257. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
  258. package/templates/collections-agent/README.md +2 -2
  259. package/templates/collections-agent/agents/collections-agent/.env.example +6 -3
  260. package/templates/collections-agent/agents/collections-agent/README.md +14 -9
  261. package/templates/collections-agent/agents/collections-agent/SYSTEM_PROMPT.md +16 -5
  262. package/templates/collections-agent/agents/collections-agent/agent.yaml +4 -3
  263. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +21 -4
  264. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +49 -5
  265. package/templates/collections-agent/agents/collections-agent/mandate.example.json +1 -1
  266. package/templates/collections-agent/agents/collections-agent/runbook.md +3 -3
  267. package/templates/collections-agent/agents/collections-agent/src/agreements.ts +1 -1
  268. package/templates/collections-agent/agents/collections-agent/src/envelope.ts +15 -13
  269. package/templates/collections-agent/agents/collections-agent/src/kit.ts +38 -35
  270. package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +16 -8
  271. package/templates/collections-agent/agents/collections-agent/src/strings.ts +91 -0
  272. package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +2 -2
  273. package/templates/collections-agent/agents/collections-agent/test/check.test.ts +4 -1
  274. package/templates/collections-agent/agents/collections-agent/test/cli.test.ts +1 -1
  275. package/templates/collections-agent/agents/collections-agent/test/envelope.test.ts +2 -2
  276. package/templates/collections-agent/agents/collections-agent/test/fixtures/accept-1103.transcript.jsonl +1 -1
  277. package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +1 -1
  278. package/templates/collections-agent/agents/collections-agent/test/payable-instrument.test.ts +56 -0
  279. package/templates/collections-agent/agents/collections-agent/test/rerun-clock.test.ts +54 -0
  280. package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +69 -2
  281. package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +16 -3
  282. package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +16 -4
  283. package/templates/collections-agent/agents/collections-agent/tools.json +2 -2
  284. package/templates/collections-agent/packages/agent-core/README.md +21 -2
  285. package/templates/collections-agent/packages/agent-core/package.json +1 -1
  286. package/templates/collections-agent/packages/agent-core/src/agent.ts +33 -6
  287. package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  288. package/templates/collections-agent/packages/agent-core/src/api/rail.ts +11 -5
  289. package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  290. package/templates/collections-agent/packages/agent-core/src/bundle.ts +2 -0
  291. package/templates/collections-agent/packages/agent-core/src/channels.ts +18 -8
  292. package/templates/collections-agent/packages/agent-core/src/check.ts +25 -3
  293. package/templates/collections-agent/packages/agent-core/src/engine.ts +90 -9
  294. package/templates/collections-agent/packages/agent-core/src/escalate.ts +10 -0
  295. package/templates/collections-agent/packages/agent-core/src/index.ts +3 -0
  296. package/templates/collections-agent/packages/agent-core/src/language.ts +52 -0
  297. package/templates/collections-agent/packages/agent-core/src/locale.ts +88 -0
  298. package/templates/collections-agent/packages/agent-core/src/manifest.ts +4 -1
  299. package/templates/collections-agent/packages/agent-core/src/rail.ts +29 -1
  300. package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  301. package/templates/collections-agent/packages/agent-core/src/secrets.ts +53 -8
  302. package/templates/collections-agent/packages/agent-core/src/state-machine.ts +14 -0
  303. package/templates/collections-agent/packages/agent-core/src/strings.ts +175 -0
  304. package/templates/collections-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  305. package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  306. package/templates/collections-agent/packages/agent-core/src/types.ts +15 -2
  307. package/templates/collections-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  308. package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  309. package/templates/collections-agent/packages/agent-core/test/engine.test.ts +23 -0
  310. package/templates/collections-agent/packages/agent-core/test/helpers.ts +1 -1
  311. package/templates/collections-agent/packages/agent-core/test/language.test.ts +40 -0
  312. package/templates/collections-agent/packages/agent-core/test/locale.test.ts +137 -0
  313. package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  314. package/templates/collections-agent/packages/agent-core/test/manifest.test.ts +2 -2
  315. package/templates/collections-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  316. package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  317. package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +49 -0
  318. package/templates/collections-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  319. package/templates/collections-agent/packages/agent-core/test/secrets.test.ts +40 -1
  320. package/templates/collections-agent/packages/agent-runtime/README.md +31 -4
  321. package/templates/collections-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  322. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  323. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  324. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  325. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  326. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  327. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  328. package/templates/collections-agent/packages/agent-runtime/src/cli.ts +30 -4
  329. package/templates/collections-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  330. package/templates/collections-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  331. package/templates/collections-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  332. package/templates/collections-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  333. package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  334. package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  335. package/templates/collections-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  336. package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  337. package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  338. package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  339. package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  340. package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  341. package/templates/collections-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  342. package/templates/collections-agent/packages/agent-runtime/src/env.ts +64 -0
  343. package/templates/collections-agent/packages/agent-runtime/src/index.ts +2 -0
  344. package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +81 -10
  345. package/templates/collections-agent/packages/agent-runtime/src/kit.ts +34 -12
  346. package/templates/collections-agent/packages/agent-runtime/src/outcome.ts +118 -0
  347. package/templates/collections-agent/packages/agent-runtime/src/poll.ts +7 -7
  348. package/templates/collections-agent/packages/agent-runtime/src/runs.ts +51 -0
  349. package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  350. package/templates/collections-agent/packages/agent-runtime/src/setup.ts +75 -5
  351. package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +71 -33
  352. package/templates/collections-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  353. package/templates/collections-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  354. package/templates/collections-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  355. package/templates/collections-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  356. package/templates/collections-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  357. package/templates/collections-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  358. package/templates/collections-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  359. package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  360. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  361. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  362. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
  363. package/templates/hello-agent/README.md +2 -2
  364. package/templates/hello-agent/agents/hello-agent/README.md +6 -0
  365. package/templates/hello-agent/agents/hello-agent/SYSTEM_PROMPT.md +27 -13
  366. package/templates/hello-agent/agents/hello-agent/agent.yaml +2 -1
  367. package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
  368. package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
  369. package/templates/hello-agent/agents/hello-agent/mandate.example.json +1 -1
  370. package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.json +1 -1
  371. package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.transcript.jsonl +1 -1
  372. package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.json +1 -1
  373. package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
  374. package/templates/hello-agent/agents/hello-agent/src/bills.ts +9 -5
  375. package/templates/hello-agent/agents/hello-agent/src/kit.ts +4 -1
  376. package/templates/hello-agent/agents/hello-agent/tools.json +1 -1
  377. package/templates/hello-agent/packages/agent-core/README.md +21 -2
  378. package/templates/hello-agent/packages/agent-core/package.json +1 -1
  379. package/templates/hello-agent/packages/agent-core/src/agent.ts +33 -6
  380. package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  381. package/templates/hello-agent/packages/agent-core/src/api/rail.ts +11 -5
  382. package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  383. package/templates/hello-agent/packages/agent-core/src/bundle.ts +2 -0
  384. package/templates/hello-agent/packages/agent-core/src/channels.ts +18 -8
  385. package/templates/hello-agent/packages/agent-core/src/check.ts +25 -3
  386. package/templates/hello-agent/packages/agent-core/src/engine.ts +90 -9
  387. package/templates/hello-agent/packages/agent-core/src/escalate.ts +10 -0
  388. package/templates/hello-agent/packages/agent-core/src/index.ts +3 -0
  389. package/templates/hello-agent/packages/agent-core/src/language.ts +52 -0
  390. package/templates/hello-agent/packages/agent-core/src/locale.ts +88 -0
  391. package/templates/hello-agent/packages/agent-core/src/manifest.ts +4 -1
  392. package/templates/hello-agent/packages/agent-core/src/rail.ts +29 -1
  393. package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  394. package/templates/hello-agent/packages/agent-core/src/secrets.ts +53 -8
  395. package/templates/hello-agent/packages/agent-core/src/state-machine.ts +14 -0
  396. package/templates/hello-agent/packages/agent-core/src/strings.ts +175 -0
  397. package/templates/hello-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  398. package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  399. package/templates/hello-agent/packages/agent-core/src/types.ts +15 -2
  400. package/templates/hello-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  401. package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  402. package/templates/hello-agent/packages/agent-core/test/engine.test.ts +23 -0
  403. package/templates/hello-agent/packages/agent-core/test/helpers.ts +1 -1
  404. package/templates/hello-agent/packages/agent-core/test/language.test.ts +40 -0
  405. package/templates/hello-agent/packages/agent-core/test/locale.test.ts +137 -0
  406. package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  407. package/templates/hello-agent/packages/agent-core/test/manifest.test.ts +2 -2
  408. package/templates/hello-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  409. package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  410. package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +49 -0
  411. package/templates/hello-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  412. package/templates/hello-agent/packages/agent-core/test/secrets.test.ts +40 -1
  413. package/templates/hello-agent/packages/agent-runtime/README.md +31 -4
  414. package/templates/hello-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  415. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  416. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  417. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  418. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  419. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  420. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  421. package/templates/hello-agent/packages/agent-runtime/src/cli.ts +30 -4
  422. package/templates/hello-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  423. package/templates/hello-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  424. package/templates/hello-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  425. package/templates/hello-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  426. package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  427. package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  428. package/templates/hello-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  429. package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  430. package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  431. package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  432. package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  433. package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  434. package/templates/hello-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  435. package/templates/hello-agent/packages/agent-runtime/src/env.ts +64 -0
  436. package/templates/hello-agent/packages/agent-runtime/src/index.ts +2 -0
  437. package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +81 -10
  438. package/templates/hello-agent/packages/agent-runtime/src/kit.ts +34 -12
  439. package/templates/hello-agent/packages/agent-runtime/src/outcome.ts +118 -0
  440. package/templates/hello-agent/packages/agent-runtime/src/poll.ts +7 -7
  441. package/templates/hello-agent/packages/agent-runtime/src/runs.ts +51 -0
  442. package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  443. package/templates/hello-agent/packages/agent-runtime/src/setup.ts +75 -5
  444. package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +71 -33
  445. package/templates/hello-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  446. package/templates/hello-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  447. package/templates/hello-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  448. package/templates/hello-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  449. package/templates/hello-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  450. package/templates/hello-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  451. package/templates/hello-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  452. package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  453. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  454. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  455. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
  456. package/templates/kits.lock.json +12 -12
  457. package/templates/supplier-payments-agent/README.md +2 -2
  458. package/templates/supplier-payments-agent/agents/supplier-payments-agent/.env.example +2 -2
  459. package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +12 -7
  460. package/templates/supplier-payments-agent/agents/supplier-payments-agent/SYSTEM_PROMPT.md +14 -3
  461. package/templates/supplier-payments-agent/agents/supplier-payments-agent/agent.yaml +2 -1
  462. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
  463. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.transcript.jsonl +1 -1
  464. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
  465. package/templates/supplier-payments-agent/agents/supplier-payments-agent/mandate.example.json +3 -3
  466. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +1 -1
  467. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.transcript.jsonl +1 -1
  468. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.transcript.jsonl +5 -5
  469. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.transcript.jsonl +1 -1
  470. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.transcript.jsonl +5 -5
  471. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.json +1 -1
  472. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.transcript.jsonl +1 -1
  473. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
  474. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +37 -26
  475. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +41 -14
  476. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/pix-out.ts +12 -3
  477. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/payables.ts +8 -8
  478. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +75 -10
  479. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/locale.test.ts +101 -0
  480. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/run-outcome.test.ts +127 -0
  481. package/templates/supplier-payments-agent/agents/supplier-payments-agent/tools.json +1 -1
  482. package/templates/supplier-payments-agent/packages/agent-core/README.md +21 -2
  483. package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
  484. package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +33 -6
  485. package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  486. package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +11 -5
  487. package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  488. package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +2 -0
  489. package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +18 -8
  490. package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -3
  491. package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +90 -9
  492. package/templates/supplier-payments-agent/packages/agent-core/src/escalate.ts +10 -0
  493. package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +3 -0
  494. package/templates/supplier-payments-agent/packages/agent-core/src/language.ts +52 -0
  495. package/templates/supplier-payments-agent/packages/agent-core/src/locale.ts +88 -0
  496. package/templates/supplier-payments-agent/packages/agent-core/src/manifest.ts +4 -1
  497. package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +29 -1
  498. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  499. package/templates/supplier-payments-agent/packages/agent-core/src/secrets.ts +53 -8
  500. package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +14 -0
  501. package/templates/supplier-payments-agent/packages/agent-core/src/strings.ts +175 -0
  502. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  503. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  504. package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +15 -2
  505. package/templates/supplier-payments-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  506. package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  507. package/templates/supplier-payments-agent/packages/agent-core/test/engine.test.ts +23 -0
  508. package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +1 -1
  509. package/templates/supplier-payments-agent/packages/agent-core/test/language.test.ts +40 -0
  510. package/templates/supplier-payments-agent/packages/agent-core/test/locale.test.ts +137 -0
  511. package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  512. package/templates/supplier-payments-agent/packages/agent-core/test/manifest.test.ts +2 -2
  513. package/templates/supplier-payments-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  514. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  515. package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +49 -0
  516. package/templates/supplier-payments-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  517. package/templates/supplier-payments-agent/packages/agent-core/test/secrets.test.ts +40 -1
  518. package/templates/supplier-payments-agent/packages/agent-runtime/README.md +31 -4
  519. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  520. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  521. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  522. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  523. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  524. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  525. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  526. package/templates/supplier-payments-agent/packages/agent-runtime/src/cli.ts +30 -4
  527. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  528. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  529. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  530. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  531. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  532. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  533. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  534. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  535. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  536. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  537. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  538. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  539. package/templates/supplier-payments-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  540. package/templates/supplier-payments-agent/packages/agent-runtime/src/env.ts +64 -0
  541. package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +2 -0
  542. package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +81 -10
  543. package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +34 -12
  544. package/templates/supplier-payments-agent/packages/agent-runtime/src/outcome.ts +118 -0
  545. package/templates/supplier-payments-agent/packages/agent-runtime/src/poll.ts +7 -7
  546. package/templates/supplier-payments-agent/packages/agent-runtime/src/runs.ts +51 -0
  547. package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  548. package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +75 -5
  549. package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +71 -33
  550. package/templates/supplier-payments-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  551. package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  552. package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  553. package/templates/supplier-payments-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  554. package/templates/supplier-payments-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  555. package/templates/supplier-payments-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  556. package/templates/supplier-payments-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  557. package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  558. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  559. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  560. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
@@ -2,15 +2,18 @@
2
2
  * The terminal channel: what `codespar-agent start` opens. No account, no
3
3
  * server. It shows what the core decided, asks the person when an execution
4
4
  * is awaiting approval, and prints where the sealed outcome landed. The
5
- * words are the agent's (`kit.labels`, `kit.describeExecution`); the order
6
- * of the gates is the runner's and is the same for every agent.
5
+ * words are the agent's (`kit.strings`, `kit.describeExecution`) and the
6
+ * shared ones (`CORE_STRINGS`), in the run's locale; the order of the gates
7
+ * is the runner's and is the same for every agent, and so is what an answer
8
+ * decides: the parsers below read both languages whatever the locale.
7
9
  */
8
10
  import { createInterface } from "node:readline/promises";
9
11
  import { stdin, stderr, stdout } from "node:process";
10
12
  import { relative } from "node:path";
11
- import type { AgentRuntime, BatchGesture, BatchPresentation, ChargeInstrument, Execution } from "@codespar/agent-core";
13
+ import { formatBRL, railErrorOf, type AgentRuntime, type BatchGesture, type BatchPresentation, type ChargeInstrument, type Execution, type NotRunLine } from "@codespar/agent-core";
12
14
  import { pollUntilClosed, type PollResult } from "./poll.js";
13
- import type { Setup } from "./setup.js";
15
+ import { draftRefusals, refusalLine, sayOutcome } from "./outcome.js";
16
+ import { inLocaleOf, type Setup } from "./setup.js";
14
17
 
15
18
  export interface TerminalOptions {
16
19
  setup: Setup;
@@ -69,7 +72,7 @@ export async function followUp(execution: Execution, setup: Setup, say: (line: s
69
72
 
70
73
  function receiptLines(execution: Execution, setup: Setup, say: (line: string) => void): void {
71
74
  for (const outcome of execution.outcomes) {
72
- if (outcome.receipt_id) say(` ${setup.kit.labels.receiptWord}: ${relative(process.cwd(), `${setup.bundle.dir}/receipts/${outcome.receipt_id}.json`)}`);
75
+ if (outcome.receipt_id) say(` ${setup.strings.receiptWord}: ${relative(process.cwd(), `${setup.bundle.dir}/receipts/${outcome.receipt_id}.json`)}`);
73
76
  }
74
77
  }
75
78
 
@@ -78,7 +81,8 @@ export async function handleExecution(execution: Execution, options: TerminalOpt
78
81
  const { setup, approver } = options;
79
82
  const say = options.say ?? ((l: string) => stderr.write(l + "\n"));
80
83
  const tell = options.tell ?? ((l: string) => stdout.write(l + "\n"));
81
- const labels = setup.kit.labels;
84
+ const words = setup.strings;
85
+ const text = setup.coreStrings;
82
86
  const engine = setup.engine;
83
87
 
84
88
  for (const line of describeExecution(execution, setup)) say(line);
@@ -91,42 +95,43 @@ export async function handleExecution(execution: Execution, options: TerminalOpt
91
95
  } else {
92
96
  let decision = options.decision;
93
97
  if (!decision) {
94
- if (current.blocking_reasons.length > 0) say(" (o unico desfecho possivel e negar; aperte Enter)");
98
+ if (current.blocking_reasons.length > 0) say(text.onlyDeny);
95
99
  const ask = options.ask ?? defaultAsk;
96
- const answer = (await ask(labels.approveQuestion)).trim().toLowerCase();
97
- decision = answer === "s" || answer === "sim" || answer === "y" || answer === "yes" ? "approve" : "deny";
100
+ decision = parseApproval(await ask(words.approveQuestion));
98
101
  }
99
102
  if (decision === "approve") current = engine.approve(current.id, approver);
100
103
  else if (decision === "deny") current = engine.deny(current.id, approver);
101
104
  else {
102
- say(" deixado em awaiting_approval (rode de novo com --approve ou --deny)");
105
+ say(text.leftAwaiting);
103
106
  return current;
104
107
  }
105
108
  }
106
- say(` -> ${current.state}${current.reason ? ` (${current.reason})` : ""}`);
109
+ say(transitionLine(current));
107
110
  }
108
111
 
109
112
  // An agent that issues on request (`executeOnApproval: false`) leaves the approved execution for its own tool to issue.
110
113
  if (current.state === "approved" && setup.kit.executeOnApproval !== false) {
111
114
  current = await engine.execute(current.id);
112
- say(` -> ${current.state}${current.reason ? ` (${current.reason})` : ""}`);
115
+ say(transitionLine(current));
113
116
  if (setup.settlement === "immediate") {
114
117
  receiptLines(current, setup, say);
115
- if (current.state === "executing") say(labels.uncertainDispatch);
118
+ if (current.state === "executing") say(words.uncertainDispatch);
116
119
  }
117
120
  }
118
121
 
119
122
  if (setup.settlement === "await-payer") {
123
+ // The counterparty is spoken to in the locale of the run that proposed this execution (`npm run approve` is a later command); the operator's lines stay in this one's.
124
+ const counterparty = inLocaleOf(setup, current);
120
125
  if (current.state === "executing" && current.reason === "awaiting_settlement") {
121
- const result = await waitForPayer(current.id, options);
126
+ const result = await waitForPayer(current.id, { ...options, setup: counterparty });
122
127
  current = result.execution;
123
- say(` -> ${current.state}${current.reason ? ` (${current.reason})` : ""} apos ${result.rounds} consulta(s), ${result.seconds}s${result.timed_out ? " (tempo esgotado; `npm run poll` continua de onde parou)" : ""}`);
128
+ say(`${transitionLine(current)}${text.afterLooks(result.rounds, result.seconds, result.timed_out)}`);
124
129
  receiptLines(current, setup, say);
125
130
  } else if (current.state === "executing") {
126
- say(labels.uncertainDispatch);
131
+ say(words.uncertainDispatch);
127
132
  }
128
133
  const told = current;
129
- announceOutcome(told, setup, (line) => tell(line, told));
134
+ announceOutcome(told, counterparty, (line) => tell(line, told));
130
135
  await followUp(current, setup, say);
131
136
  }
132
137
  return current;
@@ -150,6 +155,16 @@ function decideByGesture(execution: Execution, gesture: BatchGestureRecord, setu
150
155
  return engine.approve(execution.id, approver);
151
156
  }
152
157
 
158
+ /**
159
+ * What the person answered to the per-line question. Yes is s, sim, y or yes,
160
+ * in any locale; anything else is no, which is the default the `[s/N]` and
161
+ * `[y/N]` prompts show.
162
+ */
163
+ export function parseApproval(answer: string): "approve" | "deny" {
164
+ const text = answer.trim().toLowerCase();
165
+ return text === "s" || text === "sim" || text === "y" || text === "yes" ? "approve" : "deny";
166
+ }
167
+
153
168
  /**
154
169
  * What the person answered to a whole list, or `undefined` when the answer
155
170
  * cannot be read as one. Lines are numbered from 1, as they were shown.
@@ -181,28 +196,30 @@ export async function presentBatch(batch: BatchPresentation, options: TerminalOp
181
196
  const { setup, approver } = options;
182
197
  const say = options.say ?? ((l: string) => stderr.write(l + "\n"));
183
198
  const ask = options.ask ?? defaultAsk;
184
- say(` lote ${batch.ref} — ${batch.label}: ${batch.count} linha(s), total ${batch.total}, batch_hash ${batch.batch_hash.slice(0, 19)}…`);
199
+ const text = setup.coreStrings;
200
+ // The amounts are formatted here from their minor units, in the run's locale; the presentation's own strings are the model's.
201
+ say(text.batchHeader(batch.ref, batch.label, batch.count, formatBRL(batch.total_minor, setup.locale), batch.batch_hash.slice(0, 19)));
185
202
  for (const line of batch.lines) {
186
203
  const note =
187
204
  line.status === "already_settled"
188
- ? " (ja paga; nao roda de novo)"
205
+ ? text.batchAlreadySettled
189
206
  : line.status === "in_progress"
190
- ? " (em andamento; nao roda de novo)"
207
+ ? text.batchInProgress
191
208
  : line.status === "attempt_id_conflict"
192
- ? " (tentativa presa a outro pagamento; nao roda de novo)"
209
+ ? text.batchAttemptConflict
193
210
  : "";
194
- say(` ${line.index + 1}. ${line.beneficiary}: ${line.amount}${note}`);
211
+ say(` ${line.index + 1}. ${line.beneficiary}: ${formatBRL(line.amount_minor, setup.locale)}${note}`);
195
212
  }
196
213
  let parsed: { vetoed: number[] } | undefined;
197
214
  while (!parsed) {
198
- parsed = parseBatchGesture(await ask(" Aprovar a lista? [todas / todas exceto 3,7 / nenhuma] "), batch.count);
199
- if (!parsed) say(` nao entendi; responda todas, todas exceto <numeros de 1 a ${batch.count}> ou nenhuma`);
215
+ parsed = parseBatchGesture(await ask(text.batchQuestion), batch.count);
216
+ if (!parsed) say(text.batchUnreadable(batch.count));
200
217
  }
201
218
  const vetoed = new Set(parsed.vetoed);
202
219
  const gesture: BatchGesture = { batch_hash: batch.batch_hash, approved: batch.lines.map((l) => l.index).filter((i) => !vetoed.has(i)), vetoed: parsed.vetoed };
203
220
  options.gestures.set(batch.ref, { batch_hash: batch.batch_hash, count: batch.count, vetoed });
204
221
  setup.engine.note("batch.gesture", null, { batch_ref: batch.ref, batch_hash: batch.batch_hash, count: batch.count, total_minor: batch.total_minor, approved: gesture.approved, vetoed: gesture.vetoed, approver: { type: "human", id: approver.id, channel: approver.channel } });
205
- say(` -> lista ${gesture.vetoed.length === 0 ? "aprovada inteira" : gesture.approved.length === 0 ? "negada inteira" : `aprovada exceto ${gesture.vetoed.map((i) => i + 1).join(", ")}`}`);
222
+ say(gesture.vetoed.length === 0 ? text.batchApprovedAll : gesture.approved.length === 0 ? text.batchDeniedAll : text.batchApprovedExcept(gesture.vetoed.map((i) => i + 1).join(", ")));
206
223
  return gesture;
207
224
  }
208
225
 
@@ -215,9 +232,9 @@ export async function waitForPayer(executionId: string, options: TerminalOptions
215
232
  intervalMs: setup.pollIntervalMs,
216
233
  timeoutMs: waitSeconds * 1000,
217
234
  payer: options.simulatePayer ? setup.payer : undefined,
218
- onInstrument: (e, n, id, instrument) => (options.presentInstrument ?? setup.kit.presentInstrument)?.(e, n, id, instrument, tell),
235
+ onInstrument: (e, n, id, instrument) => (options.presentInstrument ?? setup.kit.presentInstrument)?.(e, n, id, instrument, tell, setup.locale),
219
236
  onWait: (_e, round) => {
220
- const line = setup.kit.labels.waitingForPayer?.(round);
237
+ const line = setup.strings.waitingForPayer?.(round);
221
238
  if (setup.railKind === "api" && line !== undefined && round % 5 === 0) say(line);
222
239
  },
223
240
  });
@@ -238,18 +255,20 @@ export function closeTerminal(): void {
238
255
  export async function interactive(options: TerminalOptions & { runtime: AgentRuntime }): Promise<void> {
239
256
  const { setup } = options;
240
257
  const say = options.say ?? ((l: string) => stderr.write(l + "\n"));
241
- const labels = setup.kit.labels;
258
+ const words = setup.strings;
242
259
  // Interactive only: a person at the keyboard can decide a list in one go. The one-shot and the scenario runner pass one decision to every line and never reach this.
243
260
  const gestures: BatchGestures = new Map();
244
261
  const withGestures = { ...options, gestures };
245
262
  const loop = setup.makeLoop(options.runtime, (execution) => handleExecution(execution, withGestures), (batch) => presentBatch(batch, withGestures));
246
- say(`${setup.manifest.manifest.name} ${setup.manifest.manifest.version} — approval: ${setup.mode} — trilho: ${setup.railKind} — ${labels.mandateWord} ${setup.mandate.id}`);
247
- say(`run ${setup.runId} — bundle em ${relative(process.cwd(), setup.bundle.dir)}`);
248
- say(labels.intro);
263
+ say(setup.coreStrings.banner(setup.manifest.manifest.name, setup.manifest.manifest.version, setup.mode, setup.railKind, words.mandateWord, setup.mandate.id));
264
+ say(setup.coreStrings.bundleAt(setup.runId, relative(process.cwd(), setup.bundle.dir)));
265
+ say(words.intro);
266
+ const notRun: NotRunLine[] = [];
267
+ let refusalsSaid = 0;
249
268
  for (;;) {
250
269
  let text: string;
251
270
  try {
252
- text = await defaultAsk(labels.prompt);
271
+ text = await defaultAsk(words.prompt);
253
272
  } catch {
254
273
  break;
255
274
  }
@@ -257,7 +276,26 @@ export async function interactive(options: TerminalOptions & { runtime: AgentRun
257
276
  if (!trimmed) continue;
258
277
  if (["sair", "exit", "quit"].includes(trimmed.toLowerCase())) break;
259
278
  const result = await loop.turn(trimmed);
260
- stdout.write(result.reply + "\n");
279
+ stdout.write(`${result.reply}\n`);
280
+ // The line counts the run so far, and is said only after a turn that paid, tried to, or skipped a line: a greeting or a read moved nothing.
281
+ notRun.push(...result.not_run);
282
+ // A request refused before a draft: said with the engine's own reason, once, in the turn that met it.
283
+ const refused = draftRefusals(setup.store, setup.runId).slice(refusalsSaid);
284
+ refusalsSaid += refused.length;
285
+ for (const refusal of refused) say(refusalLine(refusal));
286
+ if (result.executions.length > 0 || result.not_run.length > 0 || refused.length > 0) sayOutcome(setup, notRun, (line) => stdout.write(`${line}\n`));
261
287
  }
262
288
  closeTerminal();
263
289
  }
290
+
291
+ /**
292
+ * ` -> failed (rail_failed): insufficient_funds — wallet cannot reserve the requested amount`.
293
+ * The reason says which gate closed the execution; on a failure, or an answer
294
+ * that left an attempt unknown, the rail's own code and message follow it,
295
+ * verbatim, so the first failure explains itself (#50).
296
+ */
297
+ export function transitionLine(execution: Execution): string {
298
+ const failure = execution.state === "failed" || execution.state === "executing" ? railErrorOf(execution) : null;
299
+ const answer = failure ? `: ${failure.code}${failure.message && failure.message !== failure.code ? ` — ${failure.message}` : ""}` : "";
300
+ return ` -> ${execution.state}${execution.reason ? ` (${execution.reason})` : ""}${answer}`;
301
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The kit reads `CODESPAR_API_URL` and `CODESPAR_PROJECT_ID`; the CodeSpar CLI
3
+ * reads `CODESPAR_BASE_URL` and `CODESPAR_PROJECT`. A shell set up for one
4
+ * must not send the other to production in silence: the kit takes the CLI's
5
+ * names as aliases, and refuses to start when the two names disagree.
6
+ */
7
+ import { describe, expect, it } from "vitest";
8
+ import { EnvNamesDisagreeError, reconcileEnvNames } from "../src/env.js";
9
+
10
+ const STAGING = "https://api.staging.codespar.dev";
11
+
12
+ describe("reconcileEnvNames", () => {
13
+ it("fills the kit's names from the CLI's when only the CLI's are set", () => {
14
+ const env: NodeJS.ProcessEnv = { CODESPAR_BASE_URL: STAGING, CODESPAR_PROJECT: "prj_cli" };
15
+ reconcileEnvNames(env);
16
+ expect(env["CODESPAR_API_URL"]).toBe(STAGING);
17
+ expect(env["CODESPAR_PROJECT_ID"]).toBe("prj_cli");
18
+ });
19
+
20
+ it("fills the CLI's names from the kit's too, so a .env read later cannot reopen a pair the shell closed", () => {
21
+ const env: NodeJS.ProcessEnv = { CODESPAR_API_URL: STAGING, CODESPAR_PROJECT_ID: "prj_kit" };
22
+ reconcileEnvNames(env);
23
+ expect(env["CODESPAR_BASE_URL"]).toBe(STAGING);
24
+ expect(env["CODESPAR_PROJECT"]).toBe("prj_kit");
25
+ });
26
+
27
+ it("leaves the kit's names alone when the CLI's are unset or empty", () => {
28
+ const env: NodeJS.ProcessEnv = { CODESPAR_API_URL: STAGING, CODESPAR_BASE_URL: "", CODESPAR_PROJECT_ID: "prj_kit" };
29
+ reconcileEnvNames(env);
30
+ expect(env["CODESPAR_API_URL"]).toBe(STAGING);
31
+ expect(env["CODESPAR_PROJECT_ID"]).toBe("prj_kit");
32
+ });
33
+
34
+ it("treats an empty kit name as unset, as the CLI does with its own", () => {
35
+ const env: NodeJS.ProcessEnv = { CODESPAR_API_URL: "", CODESPAR_BASE_URL: STAGING };
36
+ reconcileEnvNames(env);
37
+ expect(env["CODESPAR_API_URL"]).toBe(STAGING);
38
+ });
39
+
40
+ it("accepts both names when they say the same thing, a trailing slash aside", () => {
41
+ const env: NodeJS.ProcessEnv = { CODESPAR_API_URL: STAGING, CODESPAR_BASE_URL: `${STAGING}/`, CODESPAR_PROJECT_ID: "prj_1", CODESPAR_PROJECT: "prj_1" };
42
+ expect(() => reconcileEnvNames(env)).not.toThrow();
43
+ expect(env["CODESPAR_API_URL"]).toBe(STAGING);
44
+ });
45
+
46
+ it("refuses two URLs that disagree, naming both", () => {
47
+ const env: NodeJS.ProcessEnv = { CODESPAR_API_URL: "https://api.codespar.dev", CODESPAR_BASE_URL: STAGING };
48
+ expect(() => reconcileEnvNames(env)).toThrow(EnvNamesDisagreeError);
49
+ expect(() => reconcileEnvNames(env)).toThrow(/CODESPAR_API_URL=https:\/\/api\.codespar\.dev.*CODESPAR_BASE_URL=https:\/\/api\.staging\.codespar\.dev/);
50
+ });
51
+
52
+ it("refuses two projects that disagree, naming both", () => {
53
+ const env: NodeJS.ProcessEnv = { CODESPAR_PROJECT_ID: "prj_kit", CODESPAR_PROJECT: "prj_cli" };
54
+ expect(() => reconcileEnvNames(env)).toThrow(/CODESPAR_PROJECT_ID=prj_kit.*CODESPAR_PROJECT=prj_cli/);
55
+ });
56
+ });
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The header of `inspect` and the mandate's status. The snapshot in the
3
+ * bundle is the local copy at the start of the run and says `active` for a
4
+ * mandate the API answered was revoked two lines below; the header says what
5
+ * a gate of the run heard, when that is the last word the run has.
6
+ */
7
+ import { mkdtempSync, writeFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ import { describe, expect, it } from "vitest";
11
+ import { ProofBundle } from "@codespar/agent-core";
12
+ import { assembleTimeline, renderHtml, renderText } from "../src/inspect.js";
13
+
14
+ const AGENT = { type: "agent", agent: "bills-agent@0.1.0", on_behalf_of: "usr_demo_titular" };
15
+ const SNAPSHOT = "active in the snapshot at the start of the run";
16
+
17
+ type Step = { execution: string | null; to?: string; reason?: string; detail?: string; at: string };
18
+
19
+ function bundleOf(steps: Step[]): ProofBundle {
20
+ const bundle = new ProofBundle(mkdtempSync(join(tmpdir(), "inspect-mandate-status-")), "run_status_mandate_abc123");
21
+ bundle.meta({ run_id: bundle.runId, agent: "bills-agent@0.1.0", mode: "mandate", rail: "api", mandate_id: "cm_fixture", mandate_version: 1, started_at: "2026-09-23T18:00:00.000Z" });
22
+ writeFileSync(join(bundle.dir, "mandate.snapshot.json"), JSON.stringify({ id: "cm_fixture", version: 1, status: "active", currency: "BRL", cap_minor: 7200000, per_tx_cap_minor: 250000, expires_at: "2027-09-23T00:00:00.000Z", beneficiaries: [] }));
23
+ steps.forEach((step, i) => {
24
+ const payload = { ...(step.to ? { from: "approved", to: step.to, at: step.at, actor: AGENT } : {}), ...(step.reason ? { reason: step.reason } : {}), ...(step.detail ? { detail: step.detail } : {}) };
25
+ bundle.event({ seq: i + 1, run_id: bundle.runId, execution_id: step.execution, type: step.to ? "execution.transition" : "execution.refused_before_draft", payload, at: step.at, actor: AGENT });
26
+ });
27
+ return bundle;
28
+ }
29
+
30
+ const header = (bundle: ProofBundle) => renderText(assembleTimeline(bundle)).split("\n")[2];
31
+
32
+ describe("inspect: the mandate's status in the header", () => {
33
+ it("is the snapshot's when no gate of the run heard otherwise", () => {
34
+ const bundle = bundleOf([
35
+ { execution: "exe_a", to: "executing", at: "2026-09-23T18:00:10.000Z" },
36
+ { execution: "exe_a", to: "settled", at: "2026-09-23T18:00:12.000Z" },
37
+ ]);
38
+ expect(assembleTimeline(bundle).mandate?.status_seen).toBeNull();
39
+ expect(header(bundle)).toMatch(/^ {2}mandate active, expires 2027-09-23T00:00:00.000Z · /);
40
+ });
41
+
42
+ it("is what the API answered at the gate, with the source, the time and what the snapshot had said", () => {
43
+ const bundle = bundleOf([{ execution: "exe_a", to: "denied", reason: "mandate_revoked", detail: "mandate cm_fixture was revoked (source: api)", at: "2026-09-23T18:00:13.000Z" }]);
44
+ const report = assembleTimeline(bundle);
45
+ expect(report.mandate).toMatchObject({ status: "active", status_seen: { status: "revoked", source: "api", at: "2026-09-23T18:00:13.000Z" } });
46
+ expect(header(bundle)).toContain(`mandate revoked (source: api, 18:00:13; ${SNAPSHOT}), expires `);
47
+ expect(header(bundle)).not.toContain("mandate active,");
48
+ // The page says the same thing as the terminal.
49
+ expect(renderHtml(report)).toContain(`mandate revoked (source: api, 18:00:13; ${SNAPSHOT}), expires `);
50
+ expect(renderHtml(report)).not.toContain("mandate active,");
51
+ });
52
+
53
+ it("reads a refusal before a draft the same way, and a paused mandate, and the stub", () => {
54
+ const refused = bundleOf([{ execution: null, reason: "mandate_revoked", detail: "mandate cm_fixture was revoked (source: stub)", at: "2026-09-23T18:02:00.000Z" }]);
55
+ expect(header(refused)).toContain(`mandate revoked (source: stub, 18:02:00; ${SNAPSHOT})`);
56
+ const paused = bundleOf([{ execution: "exe_a", to: "denied", reason: "mandate_paused", detail: "mandate cm_fixture is paused (source: api)", at: "2026-09-23T18:00:13.000Z" }]);
57
+ expect(header(paused)).toContain(`mandate paused (source: api, 18:00:13; ${SNAPSHOT})`);
58
+ });
59
+
60
+ it("an expiry the run's own clock found names the clock, not a source that never answered it", () => {
61
+ const bundle = bundleOf([{ execution: "exe_a", to: "expired", reason: "mandate_expired", detail: "mandate cm_fixture expired at 2027-09-23T00:00:00.000Z", at: "2027-09-23T00:00:05.000Z" }]);
62
+ expect(assembleTimeline(bundle).mandate?.status_seen).toMatchObject({ status: "expired", source: null });
63
+ expect(header(bundle)).toContain(`mandate expired (by the run's clock, 00:00:05; ${SNAPSHOT})`);
64
+ });
65
+
66
+ it("a payment that executed after a pause is the last word: the mandate was resumed, and the header is the snapshot's again", () => {
67
+ const bundle = bundleOf([
68
+ { execution: "exe_a", to: "denied", reason: "mandate_paused", detail: "mandate cm_fixture is paused (source: api)", at: "2026-09-23T18:00:13.000Z" },
69
+ { execution: "exe_b", to: "executing", at: "2026-09-23T18:05:00.000Z" },
70
+ { execution: "exe_b", to: "settled", at: "2026-09-23T18:05:02.000Z" },
71
+ ]);
72
+ expect(assembleTimeline(bundle).mandate?.status_seen).toBeNull();
73
+ expect(header(bundle)).toContain("mandate active, expires ");
74
+ });
75
+
76
+ it("a refusal that is not about the mandate's status leaves the header alone", () => {
77
+ const bundle = bundleOf([{ execution: "exe_a", to: "denied", reason: "cap_exceeded", detail: "300000 is above the cap per payment", at: "2026-09-23T18:00:13.000Z" }]);
78
+ expect(assembleTimeline(bundle).mandate?.status_seen).toBeNull();
79
+ });
80
+ });
@@ -9,7 +9,7 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  import { describe, expect, it } from "vitest";
11
11
  import { ProofBundle } from "@codespar/agent-core";
12
- import { assembleTimeline, redactorFor, renderHtml, renderText, VERIFY_NOTE } from "../src/inspect.js";
12
+ import { assembleTimeline, redactorFor, renderHtml, renderText, REPLAY_NOTE, VERIFY_NOTE } from "../src/inspect.js";
13
13
 
14
14
  const ESCOLA = "financeiro@escola-aurora.example.com.br";
15
15
  const MASKED = "fi***@escola-aurora.example.com.br";
@@ -150,7 +150,7 @@ describe("the timeline `inspect` assembles from a bundle", () => {
150
150
 
151
151
  it("lists the receipts that came back, and the events that belong to no execution", () => {
152
152
  const report = assembleTimeline(fixtureBundle());
153
- expect(report.receipts).toEqual([{ receipt_id: "rcpt_one", file: "receipts/rcpt_one.json", state: "paid", amount_minor: 185000, payee: MASKED, money_moved: false, sandbox: true, at: "2026-09-23T18:00:22.000Z" }]);
153
+ expect(report.receipts).toEqual([{ receipt_id: "rcpt_one", file: "receipts/rcpt_one.json", state: "paid", amount_minor: 185000, payee: MASKED, money_moved: false, sandbox: true, at: "2026-09-23T18:00:22.000Z", replayed: false }]);
154
154
  expect(report.executions.find((e) => e.execution_id === "exe_one")!.receipts).toEqual(["rcpt_one"]);
155
155
  expect(report.run_events.map((e) => e.type)).toEqual(["execution.refused_before_draft", "tool.refused"]);
156
156
  });
@@ -168,6 +168,67 @@ describe("the timeline `inspect` assembles from a bundle", () => {
168
168
  });
169
169
  });
170
170
 
171
+ /**
172
+ * Issue #60: the rail answered a dispatch from an earlier presentation of the
173
+ * same attempt. Settled, and paid by somebody else. An attempt closed by a
174
+ * later look is the other case: the look cannot say whose payment it read, so
175
+ * it is never counted as a replay.
176
+ */
177
+ function withReplays(): ProofBundle {
178
+ const bundle = fixtureBundle();
179
+ const run = "run_fixture_human_abc123";
180
+ for (const event of [
181
+ { run_id: run, execution_id: "exe_three", type: "execution.drafted", payload: { items: [{ alias: "escola", beneficiary: "Escola Aurora", payee: ESCOLA, amount: 185000, currency: "BRL" }], total: 185000, mode: "mandate", batch: { ref: "folha", batch_hash: "sha256:abc", index: 0, count: 2 } }, at: "2026-09-23T18:03:00.000Z", actor: AGENT },
182
+ { run_id: run, execution_id: "exe_three", type: "rail.dispatch", payload: { attempt_id: "ska_three", payee: ESCOLA, amount: 185000, rail: "stub", idempotency_key: "idk_three" }, at: "2026-09-23T18:03:01.000Z", actor: AGENT },
183
+ { run_id: run, execution_id: "exe_three", type: "rail.outcome", payload: { attempt_id: "ska_three", status: "settled", transaction_id: "tx_earlier", receipt_id: "rcpt_earlier", money_moved: false, sandbox: true, idempotent_replay: true }, at: "2026-09-23T18:03:02.000Z", actor: AGENT },
184
+ { run_id: run, execution_id: "exe_three", type: "execution.transition", payload: { from: "executing", to: "settled", at: "2026-09-23T18:03:03.000Z", actor: AGENT }, at: "2026-09-23T18:03:03.000Z", actor: AGENT },
185
+ { run_id: run, execution_id: "exe_four", type: "execution.drafted", payload: { items: [{ alias: "escola", beneficiary: "Escola Aurora", payee: ESCOLA, amount: 185000, currency: "BRL" }], total: 185000, mode: "mandate", batch: { ref: "folha", batch_hash: "sha256:abc", index: 1, count: 2 } }, at: "2026-09-23T18:04:00.000Z", actor: AGENT },
186
+ { run_id: run, execution_id: "exe_four", type: "rail.dispatch", payload: { attempt_id: "ska_four", payee: ESCOLA, amount: 185000, rail: "stub", idempotency_key: "idk_four" }, at: "2026-09-23T18:04:01.000Z", actor: AGENT },
187
+ { run_id: run, execution_id: "exe_four", type: "rail.outcome", payload: { attempt_id: "ska_four", status: "uncertain", code: "psp_dispatch_uncertain", message: "timeout" }, at: "2026-09-23T18:04:02.000Z", actor: AGENT },
188
+ { run_id: run, execution_id: "exe_four", type: "rail.reconcile", payload: { attempt_id: "ska_four", found: "settled" }, at: "2026-09-23T18:05:00.000Z", actor: AGENT },
189
+ { run_id: run, execution_id: "exe_four", type: "execution.transition", payload: { from: "executing", to: "settled", at: "2026-09-23T18:05:01.000Z", actor: AGENT }, at: "2026-09-23T18:05:01.000Z", actor: AGENT },
190
+ ]) {
191
+ bundle.event(event as Record<string, unknown>);
192
+ }
193
+ writeFileSync(join(bundle.dir, "receipts", "rcpt_earlier.json"), JSON.stringify({ receipt_id: "rcpt_earlier", state: "paid", payment: { amount_minor: 185000, payee: MASKED, money_moved: false, sandbox: true, at: "2026-09-23T17:00:00.000Z" }, actor: AGENT }));
194
+ return bundle;
195
+ }
196
+
197
+ describe("inspect says which settlements this run did not pay (#60)", () => {
198
+ it("a dispatch answered as a replay is settled and replayed; one closed by a later look is settled and not", () => {
199
+ const report = assembleTimeline(withReplays());
200
+ const byId = new Map(report.executions.map((e) => [e.execution_id, e]));
201
+ expect(byId.get("exe_one")!.replayed).toBe(false);
202
+ expect(byId.get("exe_three")).toMatchObject({ final_state: "settled", replayed: true });
203
+ expect(byId.get("exe_three")!.attempts[0]!.answer).toMatchObject({ status: "settled", receipt_id: "rcpt_earlier", idempotent_replay: true });
204
+ expect(byId.get("exe_four")).toMatchObject({ final_state: "settled", replayed: false });
205
+ expect(report.batches[0]!.lines.map((l) => [l.execution_id, l.replayed])).toEqual([
206
+ ["exe_three", true],
207
+ ["exe_four", false],
208
+ ]);
209
+ expect(report.receipts.map((r) => [r.receipt_id, r.replayed])).toEqual([
210
+ ["rcpt_earlier", true],
211
+ ["rcpt_one", false],
212
+ ]);
213
+ });
214
+
215
+ it("the text and the HTML say it where a reader looks: the rail line, the heading, the batch header and the receipt", () => {
216
+ const report = assembleTimeline(withReplays());
217
+ const text = renderText(report);
218
+ expect(text).toContain("execution exe_three — settled (replayed, not paid by this run)");
219
+ expect(text).toContain("rail says settled (replayed)");
220
+ expect(text).toContain(REPLAY_NOTE);
221
+ expect(text).toContain("1 replayed, paid before and not by this run");
222
+ expect(text).toContain("rcpt_earlier paid R$ 1.850,00");
223
+ expect(text).toMatch(/rcpt_earlier .*read back from a replay, not paid by this run/);
224
+ expect(text).not.toMatch(/rcpt_one .*read back from a replay/);
225
+ expect(text).toContain("execution exe_four — settled · folha");
226
+ const html = renderHtml(report);
227
+ expect(html).toContain("replayed, not paid by this run");
228
+ expect(html).toContain(REPLAY_NOTE);
229
+ });
230
+ });
231
+
171
232
  describe("masking: inspect never un-masks and never leaks a key it read raw", () => {
172
233
  it("no raw payee survives into the report, the text or the HTML", () => {
173
234
  const report = assembleTimeline(fixtureBundle());
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The count the terminal prints and the one-shot exits by, on the cases a
3
+ * process test cannot stage cheaply: which reasons read as declined, and
4
+ * which exit code wins.
5
+ */
6
+ import { describe, expect, it } from "vitest";
7
+ import { isDeclinedReason, type Execution, type NotRunLine } from "@codespar/agent-core";
8
+ import { outcomeExitCode, runOutcome } from "../src/outcome.js";
9
+
10
+ function execution(state: Execution["state"], over: Partial<Pick<Execution, "reason" | "outcomes">> = {}): Execution {
11
+ return { id: `exec_${Math.random().toString(36).slice(2)}`, state, outcomes: [], ...over } as unknown as Execution;
12
+ }
13
+
14
+ const outcome = (status: "settled" | "failed" | "accepted", over: Record<string, unknown> = {}) => ({ index: 0, attempt_id: "att", status, ...over }) as Execution["outcomes"][number];
15
+
16
+ describe("isDeclinedReason", () => {
17
+ it("is true of a charge nobody paid, a charge withdrawn and the organization's pause, and of nothing else", () => {
18
+ for (const reason of ["charge_expired", "charge_cancelled", "org_paused"]) expect(isDeclinedReason(reason)).toBe(true);
19
+ for (const reason of ["charge_issuer_error", "rail_failed", "psp_dispatch_failed", undefined, null]) expect(isDeclinedReason(reason)).toBe(false);
20
+ });
21
+ });
22
+
23
+ describe("runOutcome", () => {
24
+ it("counts a multi-item execution by payment: three paid and one failed", () => {
25
+ const e = execution("failed", { reason: "rail_failed", outcomes: [outcome("settled"), outcome("failed", { code: "psp_dispatch_failed" }), outcome("settled"), outcome("settled")] });
26
+ expect(runOutcome([e], [])).toEqual({ settled: 3, failed: 1, declined: 0, already_paid: 0, open: 0 });
27
+ });
28
+
29
+ it("counts a replayed settlement as already paid, attempt by attempt", () => {
30
+ const e = execution("settled", { outcomes: [outcome("settled", { replayed: true }), outcome("settled")] });
31
+ expect(runOutcome([e], [])).toMatchObject({ settled: 1, already_paid: 1 });
32
+ });
33
+
34
+ it("reads charge_expired, charge_cancelled and org_paused as declined, and charge_issuer_error as failed", () => {
35
+ const declined = ["charge_expired", "charge_cancelled", "org_paused"].map((code) => execution("failed", { reason: code as Execution["reason"], outcomes: [outcome("failed", { code })] }));
36
+ expect(runOutcome(declined, [])).toEqual({ settled: 0, failed: 0, declined: 3, already_paid: 0, open: 0 });
37
+ expect(runOutcome([execution("failed", { reason: "charge_issuer_error", outcomes: [outcome("failed", { code: "charge_issuer_error" })] })], [])).toMatchObject({ failed: 1, declined: 0 });
38
+ });
39
+
40
+ it("reads an execution closed with no attempt by its own reason", () => {
41
+ expect(runOutcome([execution("failed", { reason: "org_paused" }), execution("failed", { reason: "rail_failed" }), execution("denied"), execution("expired")], [])).toEqual({ settled: 0, failed: 1, declined: 3, already_paid: 0, open: 0 });
42
+ });
43
+
44
+ it("counts a line an earlier run holds as open, whatever that run's verdict, and a refused line as failed", () => {
45
+ const notRun: NotRunLine[] = [
46
+ { ref: "a", why: "in_progress", detail: "attempt_id_conflict" },
47
+ { ref: "b", why: "in_progress", detail: "in_progress" },
48
+ { ref: "c", why: "already_settled" },
49
+ { ref: "d", why: "refused", detail: "unreadable_line" },
50
+ ];
51
+ expect(runOutcome([], notRun)).toEqual({ settled: 0, failed: 1, declined: 0, already_paid: 1, open: 2 });
52
+ });
53
+ });
54
+
55
+ describe("outcomeExitCode", () => {
56
+ const executing = execution("executing");
57
+
58
+ it("1 wins over 3: something failed and something else was left executing", () => {
59
+ const failed = execution("failed", { reason: "rail_failed", outcomes: [outcome("failed")] });
60
+ const counted = runOutcome([failed, executing], []);
61
+ expect(counted).toMatchObject({ failed: 1, open: 1 });
62
+ expect(outcomeExitCode(counted, [failed, executing])).toBe(1);
63
+ });
64
+
65
+ it("3 when nothing failed and an execution of this run was left executing", () => {
66
+ expect(outcomeExitCode(runOutcome([executing], []), [executing])).toBe(3);
67
+ });
68
+
69
+ it("0 when a line is held by an earlier run and this run left nothing executing", () => {
70
+ expect(outcomeExitCode(runOutcome([], [{ ref: "a", why: "in_progress" }]), [])).toBe(0);
71
+ });
72
+ });
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The sandbox payer in the poll pays a receivable only once it is payable.
3
+ * It used to also pay "after a few looks", whatever it found, and on staging
4
+ * that paid a charge the issuer had ended in ERROR: the order read `settled`
5
+ * with no QR ever shown (OPEN_QUESTIONS §63, ent#1816). These drive the poll
6
+ * with a rail that stays unpayable for longer than the old patience.
7
+ */
8
+ import { describe, expect, it } from "vitest";
9
+ import type { ChargeInstrument, Execution, ExecutionEngine } from "@codespar/agent-core";
10
+ import { pollUntilClosed } from "../src/poll.js";
11
+
12
+ function instrument(payable: boolean): ChargeInstrument {
13
+ return { payable, status: payable ? "PENDING" : "PROCESSING", pix_copy_paste: payable ? "000201..." : null, boleto_bank_line: payable ? "2".repeat(47) : null, boleto_bar_code: null, due_date: "2026-09-30" };
14
+ }
15
+
16
+ /** An engine that answers one executing receivable, payable from look `payableFrom` on (never when undefined). */
17
+ function engineWith(payableFrom?: number) {
18
+ let looks = 0;
19
+ const execution = (): Execution =>
20
+ ({
21
+ id: "exe_1",
22
+ state: "executing",
23
+ outcomes: [{ index: 0, attempt_id: "att_1_0", status: "accepted", transaction_id: "chg_1", instrument: instrument(payableFrom !== undefined && looks >= payableFrom) }],
24
+ }) as unknown as Execution;
25
+ const engine = {
26
+ get: () => execution(),
27
+ reconcile: async () => ((looks += 1), execution()),
28
+ markShown: () => true,
29
+ note: () => undefined,
30
+ } as unknown as ExecutionEngine;
31
+ return { engine, looks: () => looks };
32
+ }
33
+
34
+ function payerSpy() {
35
+ const calls: string[] = [];
36
+ return {
37
+ calls,
38
+ payer: {
39
+ kind: "stub" as const,
40
+ pay: async (chargeId: string) => (calls.push(chargeId), { ok: true as const, detail: "paid" }),
41
+ behave: () => undefined,
42
+ },
43
+ };
44
+ }
45
+
46
+ describe("the poll's sandbox payer pays only what is payable", () => {
47
+ it("a receivable that never becomes payable is never paid, however many looks the wait allows", async () => {
48
+ const { engine, looks } = engineWith(undefined);
49
+ const { payer, calls } = payerSpy();
50
+ const result = await pollUntilClosed(engine, "exe_1", { intervalMs: 0, timeoutMs: 0, maxRounds: 12, payer });
51
+ expect(looks()).toBe(12);
52
+ expect(calls).toEqual([]);
53
+ expect(result.payer_calls).toEqual([]);
54
+ expect(result.timed_out).toBe(true);
55
+ });
56
+
57
+ it("the payer plays once, on the first look that finds it payable", async () => {
58
+ const { engine } = engineWith(7);
59
+ const { payer, calls } = payerSpy();
60
+ const result = await pollUntilClosed(engine, "exe_1", { intervalMs: 0, timeoutMs: 0, maxRounds: 12, payer });
61
+ expect(calls).toEqual(["chg_1"]);
62
+ expect(result.payer_calls).toEqual(["paid"]);
63
+ });
64
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `notReplayed` reads a bundle's events for what only the world can do. These
3
+ * are the events as the engine writes them, by hand, so the reading is held
4
+ * to the shape and not to whatever a scenario happens to produce.
5
+ */
6
+ import { describe, expect, it } from "vitest";
7
+ import { notReplayed } from "../src/commands/rerun.js";
8
+
9
+ const transition = (execution: string, to: string, reason?: string) => ({ type: "execution.transition", execution_id: execution, payload: { to, ...(reason ? { reason } : {}) } });
10
+ const outcome = (execution: string, status: string) => ({ type: "rail.outcome", execution_id: execution, payload: { status } });
11
+
12
+ describe("what a rerun does not replay", () => {
13
+ it("nothing, in a run that settled, failed on the rail or was denied by a person", () => {
14
+ expect(notReplayed([transition("a", "approved"), transition("a", "executing"), outcome("a", "settled"), transition("a", "settled")])).toEqual([]);
15
+ expect(notReplayed([transition("a", "executing"), outcome("a", "failed"), transition("a", "failed", "rail_failed")])).toEqual([]);
16
+ expect(notReplayed([transition("a", "awaiting_approval"), transition("a", "denied", "denied_by_approver")])).toEqual([]);
17
+ // A reason that is a key of every object is not one of the three this reads.
18
+ expect(notReplayed([transition("a", "denied", "constructor"), transition("b", "denied", "toString")])).toEqual([]);
19
+ // Left awaiting approval is the person not deciding, and a rerun does replay that.
20
+ expect(notReplayed([transition("a", "awaiting_approval")])).toEqual([]);
21
+ });
22
+
23
+ it("a mandate revoked or paused, and the organization's kill switch, at a gate or before a draft", () => {
24
+ expect(notReplayed([transition("a", "denied", "mandate_revoked")])).toEqual(["a mandate that was revoked"]);
25
+ expect(notReplayed([transition("a", "denied", "mandate_paused")])).toEqual(["a mandate that was paused"]);
26
+ expect(notReplayed([transition("a", "denied", "org_paused")])).toEqual(["an organization that paused every mandate"]);
27
+ expect(notReplayed([{ type: "execution.refused_before_draft", execution_id: null, payload: { reason: "mandate_revoked" } }])).toEqual(["a mandate that was revoked"]);
28
+ });
29
+
30
+ it("an uncertain answer from the rail, even one a later reconciliation settled", () => {
31
+ expect(notReplayed([transition("a", "executing"), outcome("a", "uncertain"), transition("a", "settled")])).toEqual(["an uncertain answer from the rail"]);
32
+ // Whichever of the engine's three events for it the bundle holds.
33
+ expect(notReplayed([{ type: "rail.uncertain", execution_id: "a", payload: {} }])).toEqual(["an uncertain answer from the rail"]);
34
+ expect(notReplayed([{ type: "execution.uncertain", execution_id: "a", payload: { detail: "attempt att_1: psp_dispatch_uncertain" } }])).toEqual(["an uncertain answer from the rail"]);
35
+ });
36
+
37
+ it("a charge issued and still out when the run ended, and not one that was paid", () => {
38
+ expect(notReplayed([transition("a", "executing"), outcome("a", "accepted")])).toEqual(["a payer who had not paid when it ended"]);
39
+ expect(notReplayed([transition("a", "executing"), outcome("a", "accepted"), transition("a", "settled")])).toEqual([]);
40
+ });
41
+
42
+ it("each thing once, in the order the run met them", () => {
43
+ const events = [transition("a", "executing"), outcome("a", "uncertain"), transition("b", "denied", "mandate_revoked"), { type: "execution.refused_before_draft", execution_id: null, payload: { reason: "mandate_revoked" } }];
44
+ expect(notReplayed(events)).toEqual(["an uncertain answer from the rail", "a mandate that was revoked"]);
45
+ });
46
+ });