@codespar/cli 0.18.1 → 0.20.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 (558) 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/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/surface.js +31 -13
  7. package/dist/surface.js.map +1 -1
  8. package/package.json +1 -1
  9. package/templates/bills-agent/README.md +2 -2
  10. package/templates/bills-agent/agents/bills-agent/.env.example +2 -2
  11. package/templates/bills-agent/agents/bills-agent/README.md +25 -18
  12. package/templates/bills-agent/agents/bills-agent/SYSTEM_PROMPT.md +13 -3
  13. package/templates/bills-agent/agents/bills-agent/agent.yaml +2 -1
  14. package/templates/bills-agent/agents/bills-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
  15. package/templates/bills-agent/agents/bills-agent/evals/adversarial/false-authority.transcript.jsonl +1 -1
  16. package/templates/bills-agent/agents/bills-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
  17. package/templates/bills-agent/agents/bills-agent/mandate.example.json +1 -1
  18. package/templates/bills-agent/agents/bills-agent/runbook.md +1 -1
  19. package/templates/bills-agent/agents/bills-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +1 -1
  20. package/templates/bills-agent/agents/bills-agent/scenarios/cap-exceeded.transcript.jsonl +1 -1
  21. package/templates/bills-agent/agents/bills-agent/scenarios/escalated-above-threshold.transcript.jsonl +4 -4
  22. package/templates/bills-agent/agents/bills-agent/scenarios/happy-path.transcript.jsonl +1 -1
  23. package/templates/bills-agent/agents/bills-agent/scenarios/mandate-revoked.json +1 -1
  24. package/templates/bills-agent/agents/bills-agent/scenarios/mandate-revoked.transcript.jsonl +4 -4
  25. package/templates/bills-agent/agents/bills-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
  26. package/templates/bills-agent/agents/bills-agent/src/bills.ts +1 -1
  27. package/templates/bills-agent/agents/bills-agent/src/kit.ts +15 -28
  28. package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +42 -15
  29. package/templates/bills-agent/agents/bills-agent/src/modules/pix-out.ts +21 -3
  30. package/templates/bills-agent/agents/bills-agent/src/strings.ts +68 -0
  31. package/templates/bills-agent/agents/bills-agent/test/cli.test.ts +130 -2
  32. package/templates/bills-agent/agents/bills-agent/test/consent-money.test.ts +25 -0
  33. package/templates/bills-agent/agents/bills-agent/test/dates-and-approval.test.ts +98 -0
  34. package/templates/bills-agent/agents/bills-agent/test/eval-runs.test.ts +81 -0
  35. package/templates/bills-agent/agents/bills-agent/test/fixtures/excursao-300.transcript.jsonl +2 -2
  36. package/templates/bills-agent/agents/bills-agent/test/fixtures/material-400.transcript.jsonl +1 -1
  37. package/templates/bills-agent/agents/bills-agent/test/locale.test.ts +161 -0
  38. package/templates/bills-agent/agents/bills-agent/test/mandate-state.test.ts +108 -0
  39. package/templates/bills-agent/agents/bills-agent/test/refused-before-draft.test.ts +81 -0
  40. package/templates/bills-agent/agents/bills-agent/test/replay.test.ts +76 -0
  41. package/templates/bills-agent/agents/bills-agent/test/reply-and-outcome.test.ts +84 -0
  42. package/templates/bills-agent/agents/bills-agent/test/reply-language.test.ts +95 -0
  43. package/templates/bills-agent/agents/bills-agent/test/rerun-and-missing-transcript.test.ts +120 -0
  44. package/templates/bills-agent/agents/bills-agent/test/rerun-world-events.test.ts +55 -0
  45. package/templates/bills-agent/agents/bills-agent/tools.json +1 -1
  46. package/templates/bills-agent/packages/agent-core/README.md +21 -2
  47. package/templates/bills-agent/packages/agent-core/package.json +1 -1
  48. package/templates/bills-agent/packages/agent-core/src/agent.ts +33 -6
  49. package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  50. package/templates/bills-agent/packages/agent-core/src/api/rail.ts +11 -5
  51. package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  52. package/templates/bills-agent/packages/agent-core/src/bundle.ts +2 -0
  53. package/templates/bills-agent/packages/agent-core/src/channels.ts +18 -8
  54. package/templates/bills-agent/packages/agent-core/src/check.ts +25 -3
  55. package/templates/bills-agent/packages/agent-core/src/engine.ts +90 -9
  56. package/templates/bills-agent/packages/agent-core/src/escalate.ts +10 -0
  57. package/templates/bills-agent/packages/agent-core/src/index.ts +3 -0
  58. package/templates/bills-agent/packages/agent-core/src/language.ts +52 -0
  59. package/templates/bills-agent/packages/agent-core/src/locale.ts +88 -0
  60. package/templates/bills-agent/packages/agent-core/src/manifest.ts +4 -1
  61. package/templates/bills-agent/packages/agent-core/src/rail.ts +29 -1
  62. package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  63. package/templates/bills-agent/packages/agent-core/src/secrets.ts +53 -8
  64. package/templates/bills-agent/packages/agent-core/src/state-machine.ts +14 -0
  65. package/templates/bills-agent/packages/agent-core/src/strings.ts +175 -0
  66. package/templates/bills-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  67. package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  68. package/templates/bills-agent/packages/agent-core/src/types.ts +15 -2
  69. package/templates/bills-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  70. package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  71. package/templates/bills-agent/packages/agent-core/test/engine.test.ts +23 -0
  72. package/templates/bills-agent/packages/agent-core/test/helpers.ts +1 -1
  73. package/templates/bills-agent/packages/agent-core/test/language.test.ts +40 -0
  74. package/templates/bills-agent/packages/agent-core/test/locale.test.ts +137 -0
  75. package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  76. package/templates/bills-agent/packages/agent-core/test/manifest.test.ts +2 -2
  77. package/templates/bills-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  78. package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  79. package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +49 -0
  80. package/templates/bills-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  81. package/templates/bills-agent/packages/agent-core/test/secrets.test.ts +40 -1
  82. package/templates/bills-agent/packages/agent-runtime/README.md +31 -4
  83. package/templates/bills-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  84. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  85. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  86. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  87. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  88. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  89. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  90. package/templates/bills-agent/packages/agent-runtime/src/cli.ts +30 -4
  91. package/templates/bills-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  92. package/templates/bills-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  93. package/templates/bills-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  94. package/templates/bills-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  95. package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  96. package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  97. package/templates/bills-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  98. package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  99. package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  100. package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  101. package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  102. package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  103. package/templates/bills-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  104. package/templates/bills-agent/packages/agent-runtime/src/env.ts +64 -0
  105. package/templates/bills-agent/packages/agent-runtime/src/index.ts +2 -0
  106. package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +81 -10
  107. package/templates/bills-agent/packages/agent-runtime/src/kit.ts +34 -12
  108. package/templates/bills-agent/packages/agent-runtime/src/outcome.ts +118 -0
  109. package/templates/bills-agent/packages/agent-runtime/src/poll.ts +7 -7
  110. package/templates/bills-agent/packages/agent-runtime/src/runs.ts +51 -0
  111. package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  112. package/templates/bills-agent/packages/agent-runtime/src/setup.ts +75 -5
  113. package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +71 -33
  114. package/templates/bills-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  115. package/templates/bills-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  116. package/templates/bills-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  117. package/templates/bills-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  118. package/templates/bills-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  119. package/templates/bills-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  120. package/templates/bills-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  121. package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  122. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  123. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  124. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
  125. package/templates/checkout-agent/README.md +2 -2
  126. package/templates/checkout-agent/agents/checkout-agent/.env.example +6 -4
  127. package/templates/checkout-agent/agents/checkout-agent/README.md +16 -8
  128. package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +17 -7
  129. package/templates/checkout-agent/agents/checkout-agent/agent.yaml +4 -3
  130. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +2 -2
  131. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +48 -4
  132. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +1 -1
  133. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +1 -1
  134. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +1 -1
  135. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +1 -1
  136. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
  137. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +1 -1
  138. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +1 -1
  139. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +1 -1
  140. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +1 -1
  141. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
  142. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +1 -1
  143. package/templates/checkout-agent/agents/checkout-agent/package.json +1 -1
  144. package/templates/checkout-agent/agents/checkout-agent/runbook.md +4 -4
  145. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +1 -1
  146. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +2 -2
  147. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +2 -2
  148. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +2 -2
  149. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +1 -1
  150. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +2 -2
  151. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +1 -1
  152. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +1 -1
  153. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +2 -2
  154. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +1 -1
  155. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +3 -3
  156. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +1 -1
  157. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +2 -2
  158. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +1 -1
  159. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
  160. package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +7 -7
  161. package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +47 -43
  162. package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +20 -12
  163. package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +4 -4
  164. package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +9 -6
  165. package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +44 -41
  166. package/templates/checkout-agent/agents/checkout-agent/src/strings.ts +179 -0
  167. package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +4 -1
  168. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +1 -1
  169. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +2 -2
  170. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +1 -1
  171. package/templates/checkout-agent/agents/checkout-agent/test/payable-instrument.test.ts +58 -0
  172. package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +2 -2
  173. package/templates/checkout-agent/agents/checkout-agent/test/rerun-unpaid.test.ts +47 -0
  174. package/templates/checkout-agent/agents/checkout-agent/test/scenario-reach-and-seconds.test.ts +55 -0
  175. package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +26 -10
  176. package/templates/checkout-agent/agents/checkout-agent/tools.json +1 -1
  177. package/templates/checkout-agent/packages/agent-core/README.md +21 -2
  178. package/templates/checkout-agent/packages/agent-core/package.json +1 -1
  179. package/templates/checkout-agent/packages/agent-core/src/agent.ts +33 -6
  180. package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  181. package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +11 -5
  182. package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  183. package/templates/checkout-agent/packages/agent-core/src/bundle.ts +2 -0
  184. package/templates/checkout-agent/packages/agent-core/src/channels.ts +18 -8
  185. package/templates/checkout-agent/packages/agent-core/src/check.ts +25 -3
  186. package/templates/checkout-agent/packages/agent-core/src/engine.ts +90 -9
  187. package/templates/checkout-agent/packages/agent-core/src/escalate.ts +10 -0
  188. package/templates/checkout-agent/packages/agent-core/src/index.ts +3 -0
  189. package/templates/checkout-agent/packages/agent-core/src/language.ts +52 -0
  190. package/templates/checkout-agent/packages/agent-core/src/locale.ts +88 -0
  191. package/templates/checkout-agent/packages/agent-core/src/manifest.ts +4 -1
  192. package/templates/checkout-agent/packages/agent-core/src/rail.ts +29 -1
  193. package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  194. package/templates/checkout-agent/packages/agent-core/src/secrets.ts +53 -8
  195. package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +14 -0
  196. package/templates/checkout-agent/packages/agent-core/src/strings.ts +175 -0
  197. package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  198. package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  199. package/templates/checkout-agent/packages/agent-core/src/types.ts +15 -2
  200. package/templates/checkout-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  201. package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  202. package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +23 -0
  203. package/templates/checkout-agent/packages/agent-core/test/helpers.ts +1 -1
  204. package/templates/checkout-agent/packages/agent-core/test/language.test.ts +40 -0
  205. package/templates/checkout-agent/packages/agent-core/test/locale.test.ts +137 -0
  206. package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  207. package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +2 -2
  208. package/templates/checkout-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  209. package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  210. package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +49 -0
  211. package/templates/checkout-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  212. package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +40 -1
  213. package/templates/checkout-agent/packages/agent-runtime/README.md +31 -4
  214. package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  215. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  216. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  217. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  218. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  219. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  220. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  221. package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +30 -4
  222. package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  223. package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  224. package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  225. package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  226. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  227. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  228. package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  229. package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  230. package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  231. package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  232. package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  233. package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  234. package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  235. package/templates/checkout-agent/packages/agent-runtime/src/env.ts +64 -0
  236. package/templates/checkout-agent/packages/agent-runtime/src/index.ts +2 -0
  237. package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +81 -10
  238. package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +34 -12
  239. package/templates/checkout-agent/packages/agent-runtime/src/outcome.ts +118 -0
  240. package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +7 -7
  241. package/templates/checkout-agent/packages/agent-runtime/src/runs.ts +51 -0
  242. package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  243. package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +75 -5
  244. package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +71 -33
  245. package/templates/checkout-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  246. package/templates/checkout-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  247. package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  248. package/templates/checkout-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  249. package/templates/checkout-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  250. package/templates/checkout-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  251. package/templates/checkout-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  252. package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  253. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  254. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  255. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
  256. package/templates/collections-agent/README.md +2 -2
  257. package/templates/collections-agent/agents/collections-agent/.env.example +6 -3
  258. package/templates/collections-agent/agents/collections-agent/README.md +14 -9
  259. package/templates/collections-agent/agents/collections-agent/SYSTEM_PROMPT.md +16 -5
  260. package/templates/collections-agent/agents/collections-agent/agent.yaml +4 -3
  261. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +21 -4
  262. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +49 -5
  263. package/templates/collections-agent/agents/collections-agent/mandate.example.json +1 -1
  264. package/templates/collections-agent/agents/collections-agent/runbook.md +3 -3
  265. package/templates/collections-agent/agents/collections-agent/src/agreements.ts +1 -1
  266. package/templates/collections-agent/agents/collections-agent/src/envelope.ts +15 -13
  267. package/templates/collections-agent/agents/collections-agent/src/kit.ts +38 -35
  268. package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +16 -8
  269. package/templates/collections-agent/agents/collections-agent/src/strings.ts +91 -0
  270. package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +2 -2
  271. package/templates/collections-agent/agents/collections-agent/test/check.test.ts +4 -1
  272. package/templates/collections-agent/agents/collections-agent/test/cli.test.ts +1 -1
  273. package/templates/collections-agent/agents/collections-agent/test/envelope.test.ts +2 -2
  274. package/templates/collections-agent/agents/collections-agent/test/fixtures/accept-1103.transcript.jsonl +1 -1
  275. package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +1 -1
  276. package/templates/collections-agent/agents/collections-agent/test/payable-instrument.test.ts +56 -0
  277. package/templates/collections-agent/agents/collections-agent/test/rerun-clock.test.ts +54 -0
  278. package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +69 -2
  279. package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +16 -3
  280. package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +16 -4
  281. package/templates/collections-agent/agents/collections-agent/tools.json +2 -2
  282. package/templates/collections-agent/packages/agent-core/README.md +21 -2
  283. package/templates/collections-agent/packages/agent-core/package.json +1 -1
  284. package/templates/collections-agent/packages/agent-core/src/agent.ts +33 -6
  285. package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  286. package/templates/collections-agent/packages/agent-core/src/api/rail.ts +11 -5
  287. package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  288. package/templates/collections-agent/packages/agent-core/src/bundle.ts +2 -0
  289. package/templates/collections-agent/packages/agent-core/src/channels.ts +18 -8
  290. package/templates/collections-agent/packages/agent-core/src/check.ts +25 -3
  291. package/templates/collections-agent/packages/agent-core/src/engine.ts +90 -9
  292. package/templates/collections-agent/packages/agent-core/src/escalate.ts +10 -0
  293. package/templates/collections-agent/packages/agent-core/src/index.ts +3 -0
  294. package/templates/collections-agent/packages/agent-core/src/language.ts +52 -0
  295. package/templates/collections-agent/packages/agent-core/src/locale.ts +88 -0
  296. package/templates/collections-agent/packages/agent-core/src/manifest.ts +4 -1
  297. package/templates/collections-agent/packages/agent-core/src/rail.ts +29 -1
  298. package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  299. package/templates/collections-agent/packages/agent-core/src/secrets.ts +53 -8
  300. package/templates/collections-agent/packages/agent-core/src/state-machine.ts +14 -0
  301. package/templates/collections-agent/packages/agent-core/src/strings.ts +175 -0
  302. package/templates/collections-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  303. package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  304. package/templates/collections-agent/packages/agent-core/src/types.ts +15 -2
  305. package/templates/collections-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  306. package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  307. package/templates/collections-agent/packages/agent-core/test/engine.test.ts +23 -0
  308. package/templates/collections-agent/packages/agent-core/test/helpers.ts +1 -1
  309. package/templates/collections-agent/packages/agent-core/test/language.test.ts +40 -0
  310. package/templates/collections-agent/packages/agent-core/test/locale.test.ts +137 -0
  311. package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  312. package/templates/collections-agent/packages/agent-core/test/manifest.test.ts +2 -2
  313. package/templates/collections-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  314. package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  315. package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +49 -0
  316. package/templates/collections-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  317. package/templates/collections-agent/packages/agent-core/test/secrets.test.ts +40 -1
  318. package/templates/collections-agent/packages/agent-runtime/README.md +31 -4
  319. package/templates/collections-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  320. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  321. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  322. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  323. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  324. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  325. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  326. package/templates/collections-agent/packages/agent-runtime/src/cli.ts +30 -4
  327. package/templates/collections-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  328. package/templates/collections-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  329. package/templates/collections-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  330. package/templates/collections-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  331. package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  332. package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  333. package/templates/collections-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  334. package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  335. package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  336. package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  337. package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  338. package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  339. package/templates/collections-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  340. package/templates/collections-agent/packages/agent-runtime/src/env.ts +64 -0
  341. package/templates/collections-agent/packages/agent-runtime/src/index.ts +2 -0
  342. package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +81 -10
  343. package/templates/collections-agent/packages/agent-runtime/src/kit.ts +34 -12
  344. package/templates/collections-agent/packages/agent-runtime/src/outcome.ts +118 -0
  345. package/templates/collections-agent/packages/agent-runtime/src/poll.ts +7 -7
  346. package/templates/collections-agent/packages/agent-runtime/src/runs.ts +51 -0
  347. package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  348. package/templates/collections-agent/packages/agent-runtime/src/setup.ts +75 -5
  349. package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +71 -33
  350. package/templates/collections-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  351. package/templates/collections-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  352. package/templates/collections-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  353. package/templates/collections-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  354. package/templates/collections-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  355. package/templates/collections-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  356. package/templates/collections-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  357. package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  358. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  359. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  360. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
  361. package/templates/hello-agent/README.md +2 -2
  362. package/templates/hello-agent/agents/hello-agent/README.md +6 -0
  363. package/templates/hello-agent/agents/hello-agent/SYSTEM_PROMPT.md +27 -13
  364. package/templates/hello-agent/agents/hello-agent/agent.yaml +2 -1
  365. package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
  366. package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
  367. package/templates/hello-agent/agents/hello-agent/mandate.example.json +1 -1
  368. package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.json +1 -1
  369. package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.transcript.jsonl +1 -1
  370. package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.json +1 -1
  371. package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
  372. package/templates/hello-agent/agents/hello-agent/src/bills.ts +9 -5
  373. package/templates/hello-agent/agents/hello-agent/src/kit.ts +4 -1
  374. package/templates/hello-agent/agents/hello-agent/tools.json +1 -1
  375. package/templates/hello-agent/packages/agent-core/README.md +21 -2
  376. package/templates/hello-agent/packages/agent-core/package.json +1 -1
  377. package/templates/hello-agent/packages/agent-core/src/agent.ts +33 -6
  378. package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  379. package/templates/hello-agent/packages/agent-core/src/api/rail.ts +11 -5
  380. package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  381. package/templates/hello-agent/packages/agent-core/src/bundle.ts +2 -0
  382. package/templates/hello-agent/packages/agent-core/src/channels.ts +18 -8
  383. package/templates/hello-agent/packages/agent-core/src/check.ts +25 -3
  384. package/templates/hello-agent/packages/agent-core/src/engine.ts +90 -9
  385. package/templates/hello-agent/packages/agent-core/src/escalate.ts +10 -0
  386. package/templates/hello-agent/packages/agent-core/src/index.ts +3 -0
  387. package/templates/hello-agent/packages/agent-core/src/language.ts +52 -0
  388. package/templates/hello-agent/packages/agent-core/src/locale.ts +88 -0
  389. package/templates/hello-agent/packages/agent-core/src/manifest.ts +4 -1
  390. package/templates/hello-agent/packages/agent-core/src/rail.ts +29 -1
  391. package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  392. package/templates/hello-agent/packages/agent-core/src/secrets.ts +53 -8
  393. package/templates/hello-agent/packages/agent-core/src/state-machine.ts +14 -0
  394. package/templates/hello-agent/packages/agent-core/src/strings.ts +175 -0
  395. package/templates/hello-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  396. package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  397. package/templates/hello-agent/packages/agent-core/src/types.ts +15 -2
  398. package/templates/hello-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  399. package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  400. package/templates/hello-agent/packages/agent-core/test/engine.test.ts +23 -0
  401. package/templates/hello-agent/packages/agent-core/test/helpers.ts +1 -1
  402. package/templates/hello-agent/packages/agent-core/test/language.test.ts +40 -0
  403. package/templates/hello-agent/packages/agent-core/test/locale.test.ts +137 -0
  404. package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  405. package/templates/hello-agent/packages/agent-core/test/manifest.test.ts +2 -2
  406. package/templates/hello-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  407. package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  408. package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +49 -0
  409. package/templates/hello-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  410. package/templates/hello-agent/packages/agent-core/test/secrets.test.ts +40 -1
  411. package/templates/hello-agent/packages/agent-runtime/README.md +31 -4
  412. package/templates/hello-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  413. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  414. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  415. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  416. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  417. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  418. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  419. package/templates/hello-agent/packages/agent-runtime/src/cli.ts +30 -4
  420. package/templates/hello-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  421. package/templates/hello-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  422. package/templates/hello-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  423. package/templates/hello-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  424. package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  425. package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  426. package/templates/hello-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  427. package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  428. package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  429. package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  430. package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  431. package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  432. package/templates/hello-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  433. package/templates/hello-agent/packages/agent-runtime/src/env.ts +64 -0
  434. package/templates/hello-agent/packages/agent-runtime/src/index.ts +2 -0
  435. package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +81 -10
  436. package/templates/hello-agent/packages/agent-runtime/src/kit.ts +34 -12
  437. package/templates/hello-agent/packages/agent-runtime/src/outcome.ts +118 -0
  438. package/templates/hello-agent/packages/agent-runtime/src/poll.ts +7 -7
  439. package/templates/hello-agent/packages/agent-runtime/src/runs.ts +51 -0
  440. package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  441. package/templates/hello-agent/packages/agent-runtime/src/setup.ts +75 -5
  442. package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +71 -33
  443. package/templates/hello-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  444. package/templates/hello-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  445. package/templates/hello-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  446. package/templates/hello-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  447. package/templates/hello-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  448. package/templates/hello-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  449. package/templates/hello-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  450. package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  451. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  452. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  453. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
  454. package/templates/kits.lock.json +12 -12
  455. package/templates/supplier-payments-agent/README.md +2 -2
  456. package/templates/supplier-payments-agent/agents/supplier-payments-agent/.env.example +2 -2
  457. package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +12 -7
  458. package/templates/supplier-payments-agent/agents/supplier-payments-agent/SYSTEM_PROMPT.md +14 -3
  459. package/templates/supplier-payments-agent/agents/supplier-payments-agent/agent.yaml +2 -1
  460. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
  461. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.transcript.jsonl +1 -1
  462. package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
  463. package/templates/supplier-payments-agent/agents/supplier-payments-agent/mandate.example.json +3 -3
  464. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +1 -1
  465. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.transcript.jsonl +1 -1
  466. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.transcript.jsonl +5 -5
  467. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.transcript.jsonl +1 -1
  468. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.transcript.jsonl +5 -5
  469. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.json +1 -1
  470. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.transcript.jsonl +1 -1
  471. package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
  472. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +37 -26
  473. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +41 -14
  474. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/pix-out.ts +12 -3
  475. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/payables.ts +8 -8
  476. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +75 -10
  477. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/locale.test.ts +101 -0
  478. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/run-outcome.test.ts +127 -0
  479. package/templates/supplier-payments-agent/agents/supplier-payments-agent/tools.json +1 -1
  480. package/templates/supplier-payments-agent/packages/agent-core/README.md +21 -2
  481. package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
  482. package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +33 -6
  483. package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
  484. package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +11 -5
  485. package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
  486. package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +2 -0
  487. package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +18 -8
  488. package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -3
  489. package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +90 -9
  490. package/templates/supplier-payments-agent/packages/agent-core/src/escalate.ts +10 -0
  491. package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +3 -0
  492. package/templates/supplier-payments-agent/packages/agent-core/src/language.ts +52 -0
  493. package/templates/supplier-payments-agent/packages/agent-core/src/locale.ts +88 -0
  494. package/templates/supplier-payments-agent/packages/agent-core/src/manifest.ts +4 -1
  495. package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +29 -1
  496. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +30 -2
  497. package/templates/supplier-payments-agent/packages/agent-core/src/secrets.ts +53 -8
  498. package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +14 -0
  499. package/templates/supplier-payments-agent/packages/agent-core/src/strings.ts +175 -0
  500. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
  501. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +7 -4
  502. package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +15 -2
  503. package/templates/supplier-payments-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
  504. package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
  505. package/templates/supplier-payments-agent/packages/agent-core/test/engine.test.ts +23 -0
  506. package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +1 -1
  507. package/templates/supplier-payments-agent/packages/agent-core/test/language.test.ts +40 -0
  508. package/templates/supplier-payments-agent/packages/agent-core/test/locale.test.ts +137 -0
  509. package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
  510. package/templates/supplier-payments-agent/packages/agent-core/test/manifest.test.ts +2 -2
  511. package/templates/supplier-payments-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
  512. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
  513. package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +49 -0
  514. package/templates/supplier-payments-agent/packages/agent-core/test/recorded-turns.ts +29 -0
  515. package/templates/supplier-payments-agent/packages/agent-core/test/secrets.test.ts +40 -1
  516. package/templates/supplier-payments-agent/packages/agent-runtime/README.md +31 -4
  517. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
  518. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
  519. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
  520. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
  521. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
  522. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
  523. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
  524. package/templates/supplier-payments-agent/packages/agent-runtime/src/cli.ts +30 -4
  525. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/check.ts +18 -2
  526. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
  527. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
  528. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
  529. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
  530. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
  531. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
  532. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
  533. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
  534. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +48 -17
  535. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
  536. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
  537. package/templates/supplier-payments-agent/packages/agent-runtime/src/default-kit.ts +52 -22
  538. package/templates/supplier-payments-agent/packages/agent-runtime/src/env.ts +64 -0
  539. package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +2 -0
  540. package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +81 -10
  541. package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +34 -12
  542. package/templates/supplier-payments-agent/packages/agent-runtime/src/outcome.ts +118 -0
  543. package/templates/supplier-payments-agent/packages/agent-runtime/src/poll.ts +7 -7
  544. package/templates/supplier-payments-agent/packages/agent-runtime/src/runs.ts +51 -0
  545. package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +55 -8
  546. package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +75 -5
  547. package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +71 -33
  548. package/templates/supplier-payments-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
  549. package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
  550. package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
  551. package/templates/supplier-payments-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
  552. package/templates/supplier-payments-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
  553. package/templates/supplier-payments-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
  554. package/templates/supplier-payments-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
  555. package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
  556. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
  557. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
  558. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
@@ -10,9 +10,10 @@ import { existsSync, mkdtempSync, readdirSync, readFileSync } from "node:fs";
10
10
  import { tmpdir } from "node:os";
11
11
  import { basename, join, resolve } from "node:path";
12
12
  import { z } from "zod";
13
- import type { ApprovalMode, Execution } from "@codespar/agent-core";
13
+ import type { ApprovalMode, Execution, Locale, StubChargeRailOptions, StubRailOptions } from "@codespar/agent-core";
14
14
  import type { Agent } from "./agent.js";
15
15
  import type { RailKind } from "./kit.js";
16
+ import { draftRefusals } from "./outcome.js";
16
17
  import { setup, type Setup } from "./setup.js";
17
18
  import { announceOutcome, followUp, handleExecution } from "./terminal.js";
18
19
 
@@ -84,14 +85,33 @@ export interface ScenarioRun {
84
85
  run_id: string;
85
86
  bundle_dir: string;
86
87
  replies: string[];
87
- executions: Array<{ id: string; state: string; reason: string | null; trigger: string | null; total: number; trail: string[]; charge_ids: string[]; receipt_ids: string[] }>;
88
+ executions: Array<{
89
+ id: string;
90
+ state: string;
91
+ reason: string | null;
92
+ trigger: string | null;
93
+ total: number;
94
+ trail: string[];
95
+ charge_ids: string[];
96
+ receipt_ids: string[];
97
+ /**
98
+ * A receivable that settled: whether a payable instrument (a Pix or a boleto line) was SEEN before anything paid it.
99
+ * `null` for anything that is not a settled receivable. `false` means the order settled on something nobody could
100
+ * have paid, which is how a failed issuance read as a closed cycle on staging (OPEN_QUESTIONS §63).
101
+ */
102
+ payable_seen: boolean | null;
103
+ }>;
88
104
  refused_before_draft: number;
89
105
  receipts: number;
90
106
  settled_total: number;
91
107
  charges_issued: number;
92
108
  debtor_messages: number;
93
- /** Seconds from the first issuance to the last terminal state (wall clock on the API; the fake clock's ticks on the stub). */
94
- cycle_seconds: number;
109
+ /**
110
+ * Seconds from the first issuance to the last terminal state (wall clock on the API; the fake clock's ticks on the stub).
111
+ * `null` when there was no real cycle to time: nothing was issued, nothing closed, or a receivable settled with no
112
+ * payable instrument seen first.
113
+ */
114
+ cycle_seconds: number | null;
95
115
  payer_calls: string[];
96
116
  }
97
117
 
@@ -127,6 +147,10 @@ export interface RunScenarioOptions {
127
147
  say?: (line: string) => void;
128
148
  tell?: (line: string) => void;
129
149
  waitSeconds?: number | undefined;
150
+ /** Test only: stub rail options merged over the scenario's, to script an issuer the packs do not (an error, an unpayable settlement). */
151
+ stubRail?: StubRailOptions | StubChargeRailOptions;
152
+ /** The locale of the lines the run prints. The pack's assertions are on states, never on wording, so it passes in either. */
153
+ locale?: Locale | undefined;
130
154
  }
131
155
 
132
156
  export async function runScenario(agent: Agent, scenario: Scenario, options: RunScenarioOptions): Promise<ScenarioRun> {
@@ -149,7 +173,8 @@ export async function runScenario(agent: Agent, scenario: Scenario, options: Run
149
173
  runsDir: options.runsDir ?? join(agent.dir, "runs"),
150
174
  stateDir,
151
175
  ...(rail === "stub" ? { now: tick } : {}),
152
- ...(scenario.stub_refuse_payees.length > 0 ? { stubRail: { refusePayees: scenario.stub_refuse_payees } } : {}),
176
+ ...(options.locale ? { locale: options.locale } : {}),
177
+ ...(scenario.stub_refuse_payees.length > 0 || options.stubRail ? { stubRail: { ...(scenario.stub_refuse_payees.length > 0 ? { refusePayees: scenario.stub_refuse_payees } : {}), ...(options.stubRail ?? {}) } } : {}),
153
178
  say,
154
179
  });
155
180
  const stub = s.rail as { armUncertainOnce?: () => void };
@@ -186,7 +211,7 @@ export async function runScenario(agent: Agent, scenario: Scenario, options: Run
186
211
  });
187
212
  const result = await loop.turn(turn.input);
188
213
  replies.push(result.reply);
189
- refusedBeforeDraft += s.store.listEvents({ run_id: s.runId }).filter((e) => e.type === "execution.refused_before_draft").length - refusedBeforeDraft;
214
+ refusedBeforeDraft = draftRefusals(s.store, s.runId).length;
190
215
  if (turn.payer === "late") for (const e of s.engine.list({ state: "executing" })) if (!before.has(e.id)) late.push(e.id);
191
216
  }
192
217
  // A late payer pays after the conversation ended (the receivable was in flight while the world changed).
@@ -219,6 +244,8 @@ export async function runScenario(agent: Agent, scenario: Scenario, options: Run
219
244
  if (e.type === "sandbox_payer") payerCalls.push(String((e.payload as { detail: string }).detail));
220
245
  }
221
246
  const executions = s.engine.list().filter((e) => e.run_id === s.runId);
247
+ const payableSeen = new Map(executions.map((e) => [e.id, awaitsPayer ? payableSeenBeforePayment(e, events) : null]));
248
+ const cycleIsReal = firstIssued !== undefined && lastClosed !== undefined && [...payableSeen.values()].every((seen) => seen !== false);
222
249
  return {
223
250
  scenario: scenario.name,
224
251
  mode: options.mode,
@@ -235,13 +262,14 @@ export async function runScenario(agent: Agent, scenario: Scenario, options: Run
235
262
  trail: e.history.map((h) => h.to),
236
263
  charge_ids: e.outcomes.map((o) => o.transaction_id).filter((x): x is string => typeof x === "string"),
237
264
  receipt_ids: e.outcomes.filter((o) => o.receipt_id).map((o) => o.receipt_id as string),
265
+ payable_seen: payableSeen.get(e.id) ?? null,
238
266
  })),
239
267
  refused_before_draft: refusedBeforeDraft,
240
268
  receipts: s.bundle.listReceipts().length,
241
269
  settled_total: executions.filter((e) => e.state === "settled").reduce((sum, e) => sum + e.total, 0),
242
270
  charges_issued: events.filter((e) => e.type === "rail.outcome" && (e.payload as { status: string }).status === "accepted").length,
243
271
  debtor_messages: events.filter((e) => e.type === "message.debtor").length,
244
- cycle_seconds: firstIssued !== undefined && lastClosed !== undefined ? Math.round(((lastClosed - firstIssued) / 1000) * 10) / 10 : 0,
272
+ cycle_seconds: cycleIsReal ? Math.round(((lastClosed! - firstIssued!) / 1000) * 10) / 10 : null,
245
273
  payer_calls: payerCalls,
246
274
  };
247
275
  } finally {
@@ -249,6 +277,20 @@ export async function runScenario(agent: Agent, scenario: Scenario, options: Run
249
277
  }
250
278
  }
251
279
 
280
+ /**
281
+ * For a receivable that settled: was a payable instrument (`charge.instrument` with `payable: true`) recorded for it BEFORE the
282
+ * first thing that paid it (the sandbox payer's call, `commerce.charge.paid`, or the transition to `settled`)? `null` for an
283
+ * execution that is not a settled receivable.
284
+ */
285
+ export function payableSeenBeforePayment(execution: Execution, events: ReadonlyArray<{ seq: number; execution_id: string | null; type: string; payload: unknown }>): boolean | null {
286
+ if (execution.state !== "settled") return null;
287
+ const own = events.filter((e) => e.execution_id === execution.id).sort((a, b) => a.seq - b.seq);
288
+ if (!own.some((e) => e.type === "rail.outcome" && (e.payload as { status?: string }).status === "accepted")) return null;
289
+ const paid = own.find((e) => e.type === "sandbox_payer" || e.type === "commerce.charge.paid" || (e.type === "execution.transition" && (e.payload as { to?: string }).to === "settled"));
290
+ if (!paid) return null;
291
+ return own.some((e) => e.seq < paid.seq && e.type === "charge.instrument" && (e.payload as { payable?: boolean }).payable === true);
292
+ }
293
+
252
294
  function applyHook(s: Setup, hook: "revoke_mandate" | "pause_all" | undefined): void {
253
295
  if (hook === "revoke_mandate") s.gate.revoke(s.mandate.id, "revoked mid-run by the scenario");
254
296
  if (hook === "pause_all") s.gate.pauseAll();
@@ -267,7 +309,12 @@ export function checkScenario(scenario: Scenario, run: ScenarioRun): ScenarioChe
267
309
  if (expect.settled_total !== undefined && run.settled_total !== expect.settled_total) failures.push(`settled_total ${run.settled_total} != ${expect.settled_total}`);
268
310
  if (expect.charges_issued !== undefined && run.charges_issued !== expect.charges_issued) failures.push(`charges_issued ${run.charges_issued} != ${expect.charges_issued}`);
269
311
  if (expect.debtor_messages !== undefined && run.debtor_messages !== expect.debtor_messages) failures.push(`debtor_messages ${run.debtor_messages} != ${expect.debtor_messages}`);
270
- if (expect.max_cycle_seconds !== undefined && run.rail === "api" && run.cycle_seconds > expect.max_cycle_seconds) failures.push(`cycle_seconds ${run.cycle_seconds} > ${expect.max_cycle_seconds}`);
312
+ if (expect.max_cycle_seconds !== undefined && run.rail === "api" && run.cycle_seconds !== null && run.cycle_seconds > expect.max_cycle_seconds) failures.push(`cycle_seconds ${run.cycle_seconds} > ${expect.max_cycle_seconds}`);
313
+ if (expect.max_cycle_seconds !== undefined && run.rail === "api" && run.cycle_seconds === null && !run.executions.some((e) => e.payable_seen === false)) failures.push("cycle_seconds: no cycle was measured");
314
+ }
315
+ // Every run, every rail: a receivable that settled on an instrument nobody saw is not a closed cycle, whatever state it reached.
316
+ for (const e of run.executions) {
317
+ if (e.payable_seen === false) failures.push(`no_payable_instrument: ${e.id} settled on ${e.charge_ids.join(", ") || "a receivable"} with no payable instrument seen before the payment`);
271
318
  }
272
319
  // Every run, every mode: nothing reaches executing without an approval artifact carrying its hash.
273
320
  return { ok: failures.length === 0, mode: run.mode, failures, run };
@@ -5,7 +5,7 @@
5
5
  * rail, the tool handlers and the policy extension, through its kit.
6
6
  */
7
7
  import { existsSync, readFileSync } from "node:fs";
8
- import { join } from "node:path";
8
+ import { isAbsolute, join, relative } from "node:path";
9
9
  import {
10
10
  AgentLoop,
11
11
  ExecutionEngine,
@@ -19,7 +19,13 @@ import {
19
19
  loadToolsFile,
20
20
  newRunId,
21
21
  resolveFixedClock,
22
+ resolveLocale,
23
+ isLocale,
24
+ parseLocale,
25
+ CORE_STRINGS,
22
26
  type AgentRuntime,
27
+ type CoreStrings,
28
+ type Locale,
23
29
  type ApprovalMode,
24
30
  type ConversationScript,
25
31
  type Execution,
@@ -32,15 +38,30 @@ import {
32
38
  type StubRailOptions,
33
39
  type ToolContext,
34
40
  type ToolHandler,
41
+ CODESPAR_KEY_PLACEHOLDER,
35
42
  } from "@codespar/agent-core";
36
43
  import { AnthropicRuntime } from "@codespar/agent-core/providers/anthropic";
37
44
  import type { ApiClient } from "@codespar/sdk";
38
45
  import { envName, type Agent } from "./agent.js";
39
- import type { AgentKit, RailKind, SandboxPayer, Settlement } from "./kit.js";
46
+ import type { AgentKit, KitStrings, RailKind, SandboxPayer, Settlement } from "./kit.js";
40
47
 
41
48
  /** The `.env.example` placeholder counts as no key: a copied example must replay, not call Anthropic with a fake key. */
42
49
  export const ANTHROPIC_KEY_PLACEHOLDER = "sk-ant-your_key_here";
43
- export const CODESPAR_KEY_PLACEHOLDER = "csk_test_your_key_here";
50
+ export { CODESPAR_KEY_PLACEHOLDER };
51
+
52
+ /**
53
+ * The `.env` an agent reads (`readDotEnv(agent.dir)`), as a person would type
54
+ * it from where they ran the command: relative when it is below, absolute when
55
+ * it is not. `npm run` moves a workspace script into the agent's directory, so
56
+ * where the PERSON ran it is `INIT_CWD`, which npm sets; `npm run consent` at
57
+ * the root names `agents/bills-agent/.env`, not `.env`. The key refusals name
58
+ * this path, never a guess (#50).
59
+ */
60
+ export function envFileOf(agentDir: string, cwd: string = process.env["INIT_CWD"] || process.cwd()): string {
61
+ const path = join(agentDir, ".env");
62
+ const rel = relative(cwd, path);
63
+ return rel === "" || rel.startsWith("..") || isAbsolute(rel) ? path : rel;
64
+ }
44
65
 
45
66
  export type ProviderKind = "anthropic" | "replay";
46
67
 
@@ -71,6 +92,8 @@ export interface SetupOptions {
71
92
  mandate?: Mandate | undefined;
72
93
  env?: NodeJS.ProcessEnv;
73
94
  say?: ((line: string) => void) | undefined;
95
+ /** `--locale`. Absent: the manifest's `locale`, else pt-BR. */
96
+ locale?: Locale | undefined;
74
97
  }
75
98
 
76
99
  export interface Setup {
@@ -97,6 +120,18 @@ export interface Setup {
97
120
  system: string;
98
121
  handlers: Record<string, ToolHandler>;
99
122
  tools: ReturnType<typeof loadToolsFile>;
123
+ /**
124
+ * The language of what the code prints in this run. Fixed at setup, and
125
+ * recorded in the bundle's `run.json`. The one command that changes it is
126
+ * `poll --channel whatsapp`, which sets it to the locale the conversation
127
+ * was recorded in before it says anything, so a conversation that started
128
+ * in one language is not told its outcome in the other.
129
+ */
130
+ locale: Locale;
131
+ /** The kit's words in `locale`. */
132
+ readonly strings: KitStrings;
133
+ /** The words every agent shares, in `locale`. */
134
+ readonly coreStrings: CoreStrings;
100
135
  /**
101
136
  * The conversation this run is bound to, when a channel binds one (WhatsApp):
102
137
  * its contact and its subject. Set by the channel's start, read by a kit
@@ -144,6 +179,7 @@ export function setup(agent: Agent, options: SetupOptions = {}): Setup {
144
179
  const tools = loadToolsFile(manifest.resolvePath(manifest.manifest.tools));
145
180
  const system = readFileSync(join(agent.dir, "SYSTEM_PROMPT.md"), "utf8");
146
181
  const mode: ApprovalMode = options.mode ?? manifest.manifest.default_approval;
182
+ const locale = resolveLocale(options.locale, manifest.manifest);
147
183
  if (!manifest.manifest.approval.includes(mode)) throw new Error(`agent.yaml does not support approval: ${mode}`);
148
184
 
149
185
  // <PREFIX>_STATE_DIR / <PREFIX>_RUNS_DIR exist for the restart test, which drives a child process over a scratch state.
@@ -174,9 +210,10 @@ export function setup(agent: Agent, options: SetupOptions = {}): Setup {
174
210
  const bundle = new ProofBundle(runs, runId);
175
211
  bundle.mandateSnapshot(mandate);
176
212
  // Section 11: mode, rail and mandate id. The VERSION rides with the id, so a reader knows which signing of the mandate authorised the run without opening the snapshot.
177
- bundle.meta({ run_id: runId, agent: `${manifest.manifest.name}@${manifest.manifest.version}`, mode, rail: railKind, mandate_id: mandate.id, mandate_version: mandate.version, started_at: (now ?? (() => new Date()))().toISOString() });
213
+ bundle.meta({ run_id: runId, agent: `${manifest.manifest.name}@${manifest.manifest.version}`, mode, rail: railKind, mandate_id: mandate.id, mandate_version: mandate.version, locale, started_at: (now ?? (() => new Date()))().toISOString() });
178
214
 
179
- const policyExtension = agent.kit.policyExtension?.({ agentDir: agent.dir, manifest, guardrails, store });
215
+ // Read at call time, because `poll --channel whatsapp` may move the run to the conversation's recorded locale after setup.
216
+ const policyExtension = agent.kit.policyExtension?.({ agentDir: agent.dir, manifest, guardrails, store, locale: () => s.locale });
180
217
  const engine = new ExecutionEngine({
181
218
  store,
182
219
  rail,
@@ -223,6 +260,13 @@ export function setup(agent: Agent, options: SetupOptions = {}): Setup {
223
260
  system,
224
261
  handlers: {},
225
262
  tools,
263
+ locale,
264
+ get strings() {
265
+ return agent.kit.strings[this.locale];
266
+ },
267
+ get coreStrings() {
268
+ return CORE_STRINGS[this.locale];
269
+ },
226
270
  makeRuntime,
227
271
  makeLoop: (runtime, onExecution, onBatch) => new AgentLoop({ runtime, tools, handlers: s.handlers, system, bundle, engine, onExecution, ...(onBatch ? { onBatch } : {}), ...(now ? { clock: now } : {}) }),
228
272
  close: () => store.close(),
@@ -231,6 +275,32 @@ export function setup(agent: Agent, options: SetupOptions = {}): Setup {
231
275
  return s;
232
276
  }
233
277
 
278
+ /** `--locale <pt-BR|en>` out of a command's argv, validated. `undefined` when absent. */
279
+ export function localeFlag(argv: readonly string[]): Locale | undefined {
280
+ const at = argv.indexOf("--locale");
281
+ if (at < 0) return undefined;
282
+ const value = argv[at + 1];
283
+ if (value === undefined) throw new Error("--locale needs a value");
284
+ return parseLocale(value);
285
+ }
286
+
287
+ /** The locale the run `runId` recorded in its bundle, if the bundle is here and records one. */
288
+ export function recordedLocale(agent: Agent, runId: string, env: NodeJS.ProcessEnv = process.env): Locale | undefined {
289
+ const locale = ProofBundle.open(runsDir(agent, env), runId)?.readMeta()?.["locale"];
290
+ return isLocale(locale) ? locale : undefined;
291
+ }
292
+
293
+ /**
294
+ * The same setup, speaking the locale the run that created `execution`
295
+ * recorded. For a command that tells an outcome days after the run that
296
+ * proposed it (`poll`, `webhook`): the counterparty is told in the language
297
+ * they were spoken to in, whatever this command's own locale is.
298
+ */
299
+ export function inLocaleOf(s: Setup, execution: Execution): Setup {
300
+ const locale = recordedLocale(s.agent, execution.run_id);
301
+ return locale === undefined || locale === s.locale ? s : (Object.create(s, { locale: { value: locale } }) as Setup);
302
+ }
303
+
234
304
  export class NoMandateError extends Error {
235
305
  constructor() {
236
306
  super("no signed mandate yet: run `npm run consent -- --yes` first (or `npm start` without --input, which starts the consent when a test key is present)");
@@ -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
+ });