@codespar/cli 0.17.0 → 0.18.1

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 (482) hide show
  1. package/README.md +1 -0
  2. package/dist/surface.js +53 -1
  3. package/dist/surface.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/bills-agent/README.md +1 -1
  6. package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
  7. package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
  8. package/templates/bills-agent/agents/bills-agent/README.md +6 -5
  9. package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +5 -4
  10. package/templates/bills-agent/packages/agent-core/README.md +14 -1
  11. package/templates/bills-agent/packages/agent-core/package.json +1 -1
  12. package/templates/bills-agent/packages/agent-core/src/agent.ts +39 -1
  13. package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  14. package/templates/bills-agent/packages/agent-core/src/api/client.ts +28 -5
  15. package/templates/bills-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  16. package/templates/bills-agent/packages/agent-core/src/api/rail.ts +62 -26
  17. package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  18. package/templates/bills-agent/packages/agent-core/src/approval.ts +14 -2
  19. package/templates/bills-agent/packages/agent-core/src/bundle.ts +35 -3
  20. package/templates/bills-agent/packages/agent-core/src/channels.ts +49 -3
  21. package/templates/bills-agent/packages/agent-core/src/check.ts +25 -2
  22. package/templates/bills-agent/packages/agent-core/src/engine.ts +235 -46
  23. package/templates/bills-agent/packages/agent-core/src/hash.ts +57 -1
  24. package/templates/bills-agent/packages/agent-core/src/index.ts +2 -0
  25. package/templates/bills-agent/packages/agent-core/src/quote.ts +50 -0
  26. package/templates/bills-agent/packages/agent-core/src/rail.ts +89 -4
  27. package/templates/bills-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  28. package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  29. package/templates/bills-agent/packages/agent-core/src/revocation.ts +1 -1
  30. package/templates/bills-agent/packages/agent-core/src/state-machine.ts +10 -1
  31. package/templates/bills-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  32. package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  33. package/templates/bills-agent/packages/agent-core/src/tools.ts +17 -6
  34. package/templates/bills-agent/packages/agent-core/src/types.ts +54 -1
  35. package/templates/bills-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  36. package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  37. package/templates/bills-agent/packages/agent-core/test/composition.test.ts +204 -0
  38. package/templates/bills-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  39. package/templates/bills-agent/packages/agent-core/test/helpers.ts +4 -1
  40. package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  41. package/templates/bills-agent/packages/agent-core/test/quote.test.ts +198 -0
  42. package/templates/bills-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  43. package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  44. package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +103 -3
  45. package/templates/bills-agent/packages/agent-runtime/README.md +12 -0
  46. package/templates/bills-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  47. package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  48. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  49. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  50. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  51. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  52. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  53. package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  54. package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  55. package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  56. package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  57. package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  58. package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  59. package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  60. package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  61. package/templates/bills-agent/packages/agent-runtime/src/index.ts +1 -0
  62. package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +5 -1
  63. package/templates/bills-agent/packages/agent-runtime/src/kit.ts +25 -2
  64. package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  65. package/templates/bills-agent/packages/agent-runtime/src/setup.ts +13 -3
  66. package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +127 -18
  67. package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  68. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  69. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  70. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  71. package/templates/checkout-agent/LICENSE +21 -0
  72. package/templates/checkout-agent/README.md +28 -0
  73. package/templates/checkout-agent/_gitignore +10 -0
  74. package/templates/checkout-agent/agents/checkout-agent/.env.example +5 -0
  75. package/templates/checkout-agent/agents/checkout-agent/AGENTS.md +20 -0
  76. package/templates/checkout-agent/agents/checkout-agent/CLAUDE.md +20 -0
  77. package/templates/checkout-agent/agents/checkout-agent/README.md +94 -0
  78. package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +36 -0
  79. package/templates/checkout-agent/agents/checkout-agent/agent.yaml +30 -0
  80. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +28 -0
  81. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-beatriz.json +11 -0
  82. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina-retomada.json +10 -0
  83. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina.json +11 -0
  84. package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +49 -0
  85. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +12 -0
  86. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +11 -0
  87. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +5 -0
  88. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +13 -0
  89. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.transcript.jsonl +4 -0
  90. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.json +26 -0
  91. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
  92. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.json +11 -0
  93. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.transcript.jsonl +5 -0
  94. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.json +12 -0
  95. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +5 -0
  96. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.json +13 -0
  97. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.transcript.jsonl +5 -0
  98. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +15 -0
  99. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +7 -0
  100. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +12 -0
  101. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.transcript.jsonl +5 -0
  102. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.json +13 -0
  103. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +4 -0
  104. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.json +16 -0
  105. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.transcript.jsonl +9 -0
  106. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.json +15 -0
  107. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +8 -0
  108. package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/webhook-replay.json +11 -0
  109. package/templates/checkout-agent/agents/checkout-agent/evals/eval.yaml +11 -0
  110. package/templates/checkout-agent/agents/checkout-agent/guardrails.json +25 -0
  111. package/templates/checkout-agent/agents/checkout-agent/mandate.example.json +28 -0
  112. package/templates/checkout-agent/agents/checkout-agent/package.json +33 -0
  113. package/templates/checkout-agent/agents/checkout-agent/runbook.md +19 -0
  114. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.json +42 -0
  115. package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +4 -0
  116. package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.json +32 -0
  117. package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.transcript.jsonl +4 -0
  118. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.json +61 -0
  119. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +9 -0
  120. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +61 -0
  121. package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +9 -0
  122. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +36 -0
  123. package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +6 -0
  124. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.json +23 -0
  125. package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +4 -0
  126. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.json +72 -0
  127. package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +9 -0
  128. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.json +15 -0
  129. package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +6 -0
  130. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.json +23 -0
  131. package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +3 -0
  132. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.json +42 -0
  133. package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +12 -0
  134. package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.json +14 -0
  135. package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.transcript.jsonl +9 -0
  136. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +34 -0
  137. package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +7 -0
  138. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.json +33 -0
  139. package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +4 -0
  140. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.json +32 -0
  141. package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +4 -0
  142. package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +92 -0
  143. package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +338 -0
  144. package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +179 -0
  145. package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +374 -0
  146. package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +220 -0
  147. package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +297 -0
  148. package/templates/checkout-agent/agents/checkout-agent/test/adversarial.test.ts +117 -0
  149. package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +67 -0
  150. package/templates/checkout-agent/agents/checkout-agent/test/cli.test.ts +169 -0
  151. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +5 -0
  152. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +6 -0
  153. package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +2 -0
  154. package/templates/checkout-agent/agents/checkout-agent/test/nfse.test.ts +170 -0
  155. package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +138 -0
  156. package/templates/checkout-agent/agents/checkout-agent/test/scenarios.test.ts +113 -0
  157. package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +171 -0
  158. package/templates/checkout-agent/agents/checkout-agent/tools.json +68 -0
  159. package/templates/checkout-agent/agents/checkout-agent/tsconfig.json +10 -0
  160. package/templates/checkout-agent/package.json +38 -0
  161. package/templates/checkout-agent/packages/agent-core/README.md +34 -0
  162. package/templates/checkout-agent/packages/agent-core/package.json +24 -0
  163. package/templates/checkout-agent/packages/agent-core/src/agent.ts +163 -0
  164. package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +195 -0
  165. package/templates/checkout-agent/packages/agent-core/src/api/client.ts +76 -0
  166. package/templates/checkout-agent/packages/agent-core/src/api/mandate-status.ts +74 -0
  167. package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +153 -0
  168. package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +43 -0
  169. package/templates/checkout-agent/packages/agent-core/src/approval.ts +135 -0
  170. package/templates/checkout-agent/packages/agent-core/src/bundle.ts +191 -0
  171. package/templates/checkout-agent/packages/agent-core/src/channels.ts +186 -0
  172. package/templates/checkout-agent/packages/agent-core/src/check.ts +276 -0
  173. package/templates/checkout-agent/packages/agent-core/src/clock.ts +42 -0
  174. package/templates/checkout-agent/packages/agent-core/src/engine.ts +1074 -0
  175. package/templates/checkout-agent/packages/agent-core/src/escalate.ts +85 -0
  176. package/templates/checkout-agent/packages/agent-core/src/events.ts +40 -0
  177. package/templates/checkout-agent/packages/agent-core/src/guardrails.ts +45 -0
  178. package/templates/checkout-agent/packages/agent-core/src/hash.ts +118 -0
  179. package/templates/checkout-agent/packages/agent-core/src/ids.ts +10 -0
  180. package/templates/checkout-agent/packages/agent-core/src/index.ts +34 -0
  181. package/templates/checkout-agent/packages/agent-core/src/mandate.ts +100 -0
  182. package/templates/checkout-agent/packages/agent-core/src/manifest.ts +76 -0
  183. package/templates/checkout-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
  184. package/templates/checkout-agent/packages/agent-core/src/providers/replay.ts +63 -0
  185. package/templates/checkout-agent/packages/agent-core/src/providers/types.ts +39 -0
  186. package/templates/checkout-agent/packages/agent-core/src/quote.ts +50 -0
  187. package/templates/checkout-agent/packages/agent-core/src/rail.ts +186 -0
  188. package/templates/checkout-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  189. package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +577 -0
  190. package/templates/checkout-agent/packages/agent-core/src/revocation.ts +30 -0
  191. package/templates/checkout-agent/packages/agent-core/src/secrets.ts +30 -0
  192. package/templates/checkout-agent/packages/agent-core/src/state/store.ts +307 -0
  193. package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +157 -0
  194. package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
  195. package/templates/checkout-agent/packages/agent-core/src/stubs/mandate-status.ts +58 -0
  196. package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +209 -0
  197. package/templates/checkout-agent/packages/agent-core/src/tools.ts +66 -0
  198. package/templates/checkout-agent/packages/agent-core/src/types.ts +187 -0
  199. package/templates/checkout-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  200. package/templates/checkout-agent/packages/agent-core/test/approval.test.ts +117 -0
  201. package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  202. package/templates/checkout-agent/packages/agent-core/test/bundle.test.ts +85 -0
  203. package/templates/checkout-agent/packages/agent-core/test/clock.test.ts +25 -0
  204. package/templates/checkout-agent/packages/agent-core/test/composition.test.ts +204 -0
  205. package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +596 -0
  206. package/templates/checkout-agent/packages/agent-core/test/escalate.test.ts +42 -0
  207. package/templates/checkout-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  208. package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
  209. package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
  210. package/templates/checkout-agent/packages/agent-core/test/helpers.ts +142 -0
  211. package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +229 -0
  212. package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +45 -0
  213. package/templates/checkout-agent/packages/agent-core/test/quote.test.ts +198 -0
  214. package/templates/checkout-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  215. package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  216. package/templates/checkout-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
  217. package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +416 -0
  218. package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +28 -0
  219. package/templates/checkout-agent/packages/agent-core/test/state-machine.test.ts +67 -0
  220. package/templates/checkout-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
  221. package/templates/checkout-agent/packages/agent-core/tsconfig.json +10 -0
  222. package/templates/checkout-agent/packages/agent-runtime/README.md +80 -0
  223. package/templates/checkout-agent/packages/agent-runtime/bin.mjs +32 -0
  224. package/templates/checkout-agent/packages/agent-runtime/package.json +23 -0
  225. package/templates/checkout-agent/packages/agent-runtime/src/adversarial.ts +199 -0
  226. package/templates/checkout-agent/packages/agent-runtime/src/agent.ts +68 -0
  227. package/templates/checkout-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
  228. package/templates/checkout-agent/packages/agent-runtime/src/channels/index.ts +72 -0
  229. package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
  230. package/templates/checkout-agent/packages/agent-runtime/src/channels/types.ts +173 -0
  231. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +459 -0
  232. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +216 -0
  233. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
  234. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +350 -0
  235. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +180 -0
  236. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
  237. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +120 -0
  238. package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +127 -0
  239. package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +98 -0
  240. package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +15 -0
  241. package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
  242. package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
  243. package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
  244. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +367 -0
  245. package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +145 -0
  246. package/templates/checkout-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
  247. package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
  248. package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +49 -0
  249. package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +134 -0
  250. package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +248 -0
  251. package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +330 -0
  252. package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +40 -0
  253. package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +83 -0
  254. package/templates/checkout-agent/packages/agent-runtime/src/index.ts +13 -0
  255. package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +801 -0
  256. package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +229 -0
  257. package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +105 -0
  258. package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +274 -0
  259. package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +239 -0
  260. package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +263 -0
  261. package/templates/checkout-agent/packages/agent-runtime/src/webhook.ts +131 -0
  262. package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
  263. package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +329 -0
  264. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +277 -0
  265. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
  266. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
  267. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +167 -0
  268. package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  269. package/templates/checkout-agent/packages/agent-runtime/tsconfig.json +9 -0
  270. package/templates/checkout-agent/scripts/whatsapp-emulator.mjs +84 -0
  271. package/templates/checkout-agent/tsconfig.base.json +16 -0
  272. package/templates/checkout-agent/vitest.config.ts +12 -0
  273. package/templates/collections-agent/README.md +6 -1
  274. package/templates/collections-agent/agents/collections-agent/README.md +12 -8
  275. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +8 -0
  276. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042-retomada.json +10 -0
  277. package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +27 -1
  278. package/templates/collections-agent/agents/collections-agent/src/kit.ts +2 -0
  279. package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +12 -2
  280. package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +55 -0
  281. package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +2 -0
  282. package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +12 -6
  283. package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +148 -0
  284. package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +5 -0
  285. package/templates/collections-agent/package.json +1 -0
  286. package/templates/collections-agent/packages/agent-core/README.md +14 -1
  287. package/templates/collections-agent/packages/agent-core/package.json +1 -1
  288. package/templates/collections-agent/packages/agent-core/src/agent.ts +39 -1
  289. package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  290. package/templates/collections-agent/packages/agent-core/src/api/client.ts +28 -5
  291. package/templates/collections-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  292. package/templates/collections-agent/packages/agent-core/src/api/rail.ts +62 -26
  293. package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  294. package/templates/collections-agent/packages/agent-core/src/approval.ts +14 -2
  295. package/templates/collections-agent/packages/agent-core/src/bundle.ts +35 -3
  296. package/templates/collections-agent/packages/agent-core/src/channels.ts +49 -3
  297. package/templates/collections-agent/packages/agent-core/src/check.ts +25 -2
  298. package/templates/collections-agent/packages/agent-core/src/engine.ts +235 -46
  299. package/templates/collections-agent/packages/agent-core/src/hash.ts +57 -1
  300. package/templates/collections-agent/packages/agent-core/src/index.ts +2 -0
  301. package/templates/collections-agent/packages/agent-core/src/quote.ts +50 -0
  302. package/templates/collections-agent/packages/agent-core/src/rail.ts +89 -4
  303. package/templates/collections-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  304. package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  305. package/templates/collections-agent/packages/agent-core/src/revocation.ts +1 -1
  306. package/templates/collections-agent/packages/agent-core/src/state-machine.ts +10 -1
  307. package/templates/collections-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  308. package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  309. package/templates/collections-agent/packages/agent-core/src/tools.ts +17 -6
  310. package/templates/collections-agent/packages/agent-core/src/types.ts +54 -1
  311. package/templates/collections-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  312. package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  313. package/templates/collections-agent/packages/agent-core/test/composition.test.ts +204 -0
  314. package/templates/collections-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  315. package/templates/collections-agent/packages/agent-core/test/helpers.ts +4 -1
  316. package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  317. package/templates/collections-agent/packages/agent-core/test/quote.test.ts +198 -0
  318. package/templates/collections-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  319. package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  320. package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +103 -3
  321. package/templates/collections-agent/packages/agent-runtime/README.md +12 -0
  322. package/templates/collections-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  323. package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  324. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  325. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  326. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  327. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  328. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  329. package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  330. package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  331. package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  332. package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  333. package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  334. package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  335. package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  336. package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  337. package/templates/collections-agent/packages/agent-runtime/src/index.ts +1 -0
  338. package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +5 -1
  339. package/templates/collections-agent/packages/agent-runtime/src/kit.ts +25 -2
  340. package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  341. package/templates/collections-agent/packages/agent-runtime/src/setup.ts +13 -3
  342. package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +127 -18
  343. package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  344. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  345. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  346. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  347. package/templates/collections-agent/scripts/whatsapp-emulator.mjs +84 -0
  348. package/templates/hello-agent/README.md +1 -1
  349. package/templates/hello-agent/packages/agent-core/README.md +14 -1
  350. package/templates/hello-agent/packages/agent-core/package.json +1 -1
  351. package/templates/hello-agent/packages/agent-core/src/agent.ts +39 -1
  352. package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  353. package/templates/hello-agent/packages/agent-core/src/api/client.ts +28 -5
  354. package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  355. package/templates/hello-agent/packages/agent-core/src/api/rail.ts +62 -26
  356. package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  357. package/templates/hello-agent/packages/agent-core/src/approval.ts +14 -2
  358. package/templates/hello-agent/packages/agent-core/src/bundle.ts +35 -3
  359. package/templates/hello-agent/packages/agent-core/src/channels.ts +49 -3
  360. package/templates/hello-agent/packages/agent-core/src/check.ts +25 -2
  361. package/templates/hello-agent/packages/agent-core/src/engine.ts +235 -46
  362. package/templates/hello-agent/packages/agent-core/src/hash.ts +57 -1
  363. package/templates/hello-agent/packages/agent-core/src/index.ts +2 -0
  364. package/templates/hello-agent/packages/agent-core/src/quote.ts +50 -0
  365. package/templates/hello-agent/packages/agent-core/src/rail.ts +89 -4
  366. package/templates/hello-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  367. package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  368. package/templates/hello-agent/packages/agent-core/src/revocation.ts +1 -1
  369. package/templates/hello-agent/packages/agent-core/src/state-machine.ts +10 -1
  370. package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  371. package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  372. package/templates/hello-agent/packages/agent-core/src/tools.ts +17 -6
  373. package/templates/hello-agent/packages/agent-core/src/types.ts +54 -1
  374. package/templates/hello-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  375. package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  376. package/templates/hello-agent/packages/agent-core/test/composition.test.ts +204 -0
  377. package/templates/hello-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  378. package/templates/hello-agent/packages/agent-core/test/helpers.ts +4 -1
  379. package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  380. package/templates/hello-agent/packages/agent-core/test/quote.test.ts +198 -0
  381. package/templates/hello-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  382. package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  383. package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +103 -3
  384. package/templates/hello-agent/packages/agent-runtime/README.md +12 -0
  385. package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  386. package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  387. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  388. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  389. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  390. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  391. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  392. package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  393. package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  394. package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  395. package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  396. package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  397. package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  398. package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  399. package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  400. package/templates/hello-agent/packages/agent-runtime/src/index.ts +1 -0
  401. package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +5 -1
  402. package/templates/hello-agent/packages/agent-runtime/src/kit.ts +25 -2
  403. package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  404. package/templates/hello-agent/packages/agent-runtime/src/setup.ts +13 -3
  405. package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +127 -18
  406. package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  407. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  408. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  409. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  410. package/templates/kits.lock.json +29 -6
  411. package/templates/supplier-payments-agent/README.md +1 -1
  412. package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +1 -1
  413. package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +1 -1
  414. package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +11 -9
  415. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +1 -1
  416. package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +67 -20
  417. package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +504 -5
  418. package/templates/supplier-payments-agent/packages/agent-core/README.md +14 -1
  419. package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
  420. package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +39 -1
  421. package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
  422. package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +28 -5
  423. package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
  424. package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +62 -26
  425. package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
  426. package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +14 -2
  427. package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +35 -3
  428. package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +49 -3
  429. package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -2
  430. package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +235 -46
  431. package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +57 -1
  432. package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +2 -0
  433. package/templates/supplier-payments-agent/packages/agent-core/src/quote.ts +50 -0
  434. package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +89 -4
  435. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-chain.ts +270 -0
  436. package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +194 -18
  437. package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +1 -1
  438. package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +10 -1
  439. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
  440. package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +100 -32
  441. package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +17 -6
  442. package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +54 -1
  443. package/templates/supplier-payments-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
  444. package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
  445. package/templates/supplier-payments-agent/packages/agent-core/test/composition.test.ts +204 -0
  446. package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
  447. package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +4 -1
  448. package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
  449. package/templates/supplier-payments-agent/packages/agent-core/test/quote.test.ts +198 -0
  450. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
  451. package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
  452. package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +103 -3
  453. package/templates/supplier-payments-agent/packages/agent-runtime/README.md +12 -0
  454. package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +32 -2
  455. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +53 -4
  456. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
  457. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
  458. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
  459. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
  460. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
  461. package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
  462. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
  463. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
  464. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
  465. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
  466. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +6 -3
  467. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
  468. package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
  469. package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +1 -0
  470. package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +5 -1
  471. package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +25 -2
  472. package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +8 -2
  473. package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +13 -3
  474. package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +127 -18
  475. package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
  476. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
  477. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
  478. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
  479. package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  480. package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  481. package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
  482. package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
@@ -0,0 +1,801 @@
1
+ /**
2
+ * Section 11: the proof bundle read back as a timeline. A run folder answers
3
+ * "why could the agent pay this", but only to somebody willing to join six
4
+ * files by hand. `inspect` does the join: who proposed what, who approved it
5
+ * and when, under which version of the mandate, every state transition with
6
+ * its actor, which call went out and what the rail answered, and which
7
+ * receipts came back.
8
+ *
9
+ * It reads the bundle and nothing else — no state.db, no network, no kit — so
10
+ * it works on any agent's run, and on a bundle somebody copied to a laptop
11
+ * that has never seen the agent.
12
+ *
13
+ * Masking is the bundle's: `maskPayee` is what wrote the mandate snapshot and
14
+ * the receipts, and it is applied again here to the payees the event log and
15
+ * the approval artifact still hold raw. It is idempotent on its own output, so
16
+ * a value the bundle already masked passes through unchanged. Nothing here
17
+ * un-masks anything, and the conversation is reported as counts, never as
18
+ * text: a debtor's own words are in `transcript.jsonl` and are not this
19
+ * command's to re-publish.
20
+ */
21
+ import { maskPayee, type ApprovalArtifact, type ExecutionBatch, type ExecutionComposition, type ProofBundle } from "@codespar/agent-core";
22
+ import { formatMinor } from "./default-kit.js";
23
+
24
+ export interface InspectItem {
25
+ beneficiary: string;
26
+ /** Masked exactly as the bundle masks it. */
27
+ payee: string;
28
+ amount_minor: number;
29
+ currency: string;
30
+ description: string | null;
31
+ due_date: string | null;
32
+ }
33
+
34
+ export interface InspectTransition {
35
+ at: string;
36
+ from: string | null;
37
+ to: string;
38
+ actor: string;
39
+ reason: string | null;
40
+ detail: string | null;
41
+ }
42
+
43
+ export interface InspectAttempt {
44
+ attempt_id: string;
45
+ idempotency_key: string | null;
46
+ payee: string | null;
47
+ amount_minor: number | null;
48
+ rail: string | null;
49
+ dispatched_at: string | null;
50
+ /** What the rail said when it was called, and ONLY that: a later settlement does not rewrite the answer it gave. */
51
+ answer: {
52
+ at: string;
53
+ status: string;
54
+ code: string | null;
55
+ message: string | null;
56
+ transaction_id: string | null;
57
+ receipt_id: string | null;
58
+ money_moved: boolean | null;
59
+ sandbox: boolean | null;
60
+ } | null;
61
+ /** The attempt's outcome as last known, from the call, a later look or a settlement event. */
62
+ status: string | null;
63
+ transaction_id: string | null;
64
+ receipt_id: string | null;
65
+ /** What a later look at the same attempt found. `reconcile` never re-sends; it reads. */
66
+ reconciles: Array<{ at: string; found: string }>;
67
+ /** The payable legs of a receivable, as the issuer last registered them. */
68
+ instrument: Array<{ at: string; status: string; payable: boolean; due_date: string | null }>;
69
+ }
70
+
71
+ export interface InspectExecution {
72
+ execution_id: string;
73
+ final_state: string;
74
+ mode: string | null;
75
+ proposed_at: string | null;
76
+ items: InspectItem[];
77
+ total_minor: number | null;
78
+ /** What the model claimed the total was. Recorded, never used to pay. */
79
+ model_claimed_total: number | null;
80
+ /** The batch this execution is one line of, when it is one: which list, and where in it. */
81
+ batch: ExecutionBatch | null;
82
+ approval: {
83
+ approval_id: string;
84
+ approver: ApprovalArtifact["approver"];
85
+ approved_at: string;
86
+ expires_at: string;
87
+ items_hash: string;
88
+ /** The set binding as the ARTIFACT carries it, which is the signed copy. */
89
+ batch: ExecutionBatch | null;
90
+ /** What the single amount was composed of (a cart), as the artifact signed it. */
91
+ composition?: ExecutionComposition;
92
+ mandate: { id: string; version: number };
93
+ escalation: { trigger: string; detail: string } | null;
94
+ } | null;
95
+ transitions: InspectTransition[];
96
+ attempts: InspectAttempt[];
97
+ receipts: string[];
98
+ /** Lines that are neither a transition nor a rail call: an uncertain outcome, a resumed dispatch, a total the core overrode. */
99
+ notes: Array<{ at: string; type: string; detail: string }>;
100
+ }
101
+
102
+ export interface InspectReceipt {
103
+ receipt_id: string;
104
+ file: string;
105
+ state: string | null;
106
+ amount_minor: number | null;
107
+ payee: string | null;
108
+ money_moved: boolean | null;
109
+ sandbox: boolean | null;
110
+ at: string | null;
111
+ }
112
+
113
+ /**
114
+ * One batch the run touched, assembled from the `batch_hash` its executions
115
+ * carry. This is what lets a reader see "3 of 4 lines" instead of three
116
+ * timelines with nothing to compare against: `count` is what the list held
117
+ * when it was presented for approval, and everything below is measured
118
+ * against it.
119
+ *
120
+ * Keyed by (ref, batch_hash) and not by ref alone. A bundle that holds two
121
+ * lists under one ref is a bundle where the list changed, and collapsing
122
+ * them into one header would hide exactly the thing `batch_hash` exists to
123
+ * show.
124
+ */
125
+ export interface InspectBatch {
126
+ ref: string;
127
+ batch_hash: string;
128
+ /** Lines the list held when it was presented. */
129
+ count: number;
130
+ /** The lines this bundle holds an execution for, by their position in the list. */
131
+ lines: Array<{ index: number; execution_id: string; final_state: string; attested: boolean }>;
132
+ /** How many of the lines carry an approval artifact. The "3" of "3 of 4". */
133
+ attested: number;
134
+ /** Positions of the presented list this bundle holds no execution for at all. */
135
+ missing: number[];
136
+ }
137
+
138
+ export interface InspectReport {
139
+ run: {
140
+ run_id: string;
141
+ agent: string | null;
142
+ mode: string | null;
143
+ rail: string | null;
144
+ mandate_id: string | null;
145
+ mandate_version: number | null;
146
+ started_at: string | null;
147
+ bundle_dir: string;
148
+ };
149
+ mandate: {
150
+ id: string;
151
+ version: number;
152
+ status: string;
153
+ expires_at: string;
154
+ currency: string;
155
+ per_tx_cap_minor: number;
156
+ cap_minor: number;
157
+ periodic_cap: { window: string; cap_minor: number } | null;
158
+ beneficiaries: Array<{ alias: string; name: string; payee: string }>;
159
+ } | null;
160
+ executions: InspectExecution[];
161
+ /** The batches this run's executions belong to. Empty for a run that ran no batch. */
162
+ batches: InspectBatch[];
163
+ /** Events the run holds that belong to no execution: a proposal refused before it was drafted, a tool outside `tools.json`. */
164
+ run_events: Array<{ at: string; type: string; detail: string }>;
165
+ receipts: InspectReceipt[];
166
+ conversation: { turns: number; tool_calls: number; tools: string[]; refused_tools: string[] };
167
+ /** Section 11: `verify.json` is the output of `codespar audit replay`, which is not a registered CLI command. */
168
+ verify: { present: boolean; note: string };
169
+ }
170
+
171
+ export const VERIFY_NOTE =
172
+ "absent by design: verify.json is the output of `codespar audit replay`, which is not a registered command of @codespar/cli, and the spec forbids a second implementation of the hash-chain check (sections 11 and 14.5)";
173
+
174
+ type Event = Record<string, unknown>;
175
+
176
+ function str(value: unknown): string | null {
177
+ return typeof value === "string" ? value : null;
178
+ }
179
+
180
+ function num(value: unknown): number | null {
181
+ return typeof value === "number" ? value : null;
182
+ }
183
+
184
+ function bool(value: unknown): boolean | null {
185
+ return typeof value === "boolean" ? value : null;
186
+ }
187
+
188
+ function payload(event: Event): Record<string, unknown> {
189
+ const p = event["payload"];
190
+ return p !== null && typeof p === "object" ? (p as Record<string, unknown>) : {};
191
+ }
192
+
193
+ /** `agent bills-agent@0.1.0 for usr_x`, `usr_x (person, terminal)`: who acted, in one phrase. */
194
+ export function actorLabel(actor: unknown): string {
195
+ if (actor === null || typeof actor !== "object") return "unknown";
196
+ const a = actor as Record<string, unknown>;
197
+ if (a["type"] === "agent") return `agent ${String(a["agent"])} for ${String(a["on_behalf_of"])}`;
198
+ if (a["type"] === "human") return `${String(a["id"])} (person, ${String(a["channel"])})`;
199
+ if (a["type"] === "person") return `${String(a["id"])} (person, ${String(a["channel"])})`;
200
+ if (a["type"] === "mandate") return `the mandate ${String(a["id"])}`;
201
+ return "unknown";
202
+ }
203
+
204
+ function maskMaybe(value: unknown): string | null {
205
+ const s = str(value);
206
+ return s === null ? null : maskPayee(s);
207
+ }
208
+
209
+ /**
210
+ * A payee key also turns up inside free text: an escalation detail names the
211
+ * payee it escalated on, a refusal names the one it refused. Masking the
212
+ * `payee` field alone would leave the same key readable one line below, so
213
+ * every raw payee the bundle holds is replaced by its mask wherever it
214
+ * appears. Longest first, or a key that is a prefix of another masks it twice.
215
+ */
216
+ export function redactorFor(rawPayees: Iterable<string>): (value: string | null) => string | null {
217
+ const pairs = [...new Set([...rawPayees].filter((p) => p.length > 0 && p !== "*"))]
218
+ .sort((a, b) => b.length - a.length)
219
+ .map((raw) => [raw, maskPayee(raw)] as const)
220
+ .filter(([raw, masked]) => raw !== masked);
221
+ if (!pairs.length) return (value) => value;
222
+ return (value) => {
223
+ if (value === null) return null;
224
+ let out = value;
225
+ for (const [raw, masked] of pairs) out = out.split(raw).join(masked);
226
+ return out;
227
+ };
228
+ }
229
+
230
+ /** Every raw payee this bundle holds: the drafted items, the rail calls, the settlement events and the approval artifacts. */
231
+ function rawPayeesOf(events: Event[], approvals: ApprovalArtifact[]): string[] {
232
+ const out: string[] = [];
233
+ const push = (value: unknown) => {
234
+ const s = str(value);
235
+ if (s) out.push(s);
236
+ };
237
+ for (const event of events) {
238
+ const p = payload(event);
239
+ push(p["payee"]);
240
+ if (Array.isArray(p["items"])) for (const i of p["items"] as Array<Record<string, unknown>>) push(i?.["payee"]);
241
+ }
242
+ for (const artifact of approvals) for (const i of artifact.items) push(i.payee);
243
+ return out;
244
+ }
245
+
246
+ /**
247
+ * A batch binding as the bundle holds it, refusing anything that is not one.
248
+ * A bundle is a file somebody may have edited, so "line 5 of 3" is read as no
249
+ * binding at all rather than rendered as a count a reader would then trust.
250
+ */
251
+ function batchBinding(raw: unknown): ExecutionBatch | null {
252
+ if (raw === null || typeof raw !== "object") return null;
253
+ const b = raw as Record<string, unknown>;
254
+ const ref = str(b["ref"]);
255
+ const hash = str(b["batch_hash"]);
256
+ const index = num(b["index"]);
257
+ const count = num(b["count"]);
258
+ if (!ref || !hash || index === null || count === null) return null;
259
+ if (!Number.isInteger(index) || !Number.isInteger(count) || count < 1 || index < 0 || index >= count) return null;
260
+ return { ref, batch_hash: hash, index, count };
261
+ }
262
+
263
+ function item(raw: unknown, currency: string): InspectItem {
264
+ const i = (raw ?? {}) as Record<string, unknown>;
265
+ return {
266
+ beneficiary: str(i["beneficiary"]) ?? str(i["alias"]) ?? "unknown",
267
+ payee: maskMaybe(i["payee"]) ?? "unknown",
268
+ amount_minor: num(i["amount"]) ?? 0,
269
+ currency: str(i["currency"]) ?? currency,
270
+ description: str(i["description"]),
271
+ due_date: str(i["due_date"]),
272
+ };
273
+ }
274
+
275
+ function attemptOf(attempts: Map<string, InspectAttempt>, attemptId: string): InspectAttempt {
276
+ const existing = attempts.get(attemptId);
277
+ if (existing) return existing;
278
+ const fresh: InspectAttempt = {
279
+ attempt_id: attemptId,
280
+ idempotency_key: null,
281
+ payee: null,
282
+ amount_minor: null,
283
+ rail: null,
284
+ dispatched_at: null,
285
+ answer: null,
286
+ status: null,
287
+ transaction_id: null,
288
+ receipt_id: null,
289
+ reconciles: [],
290
+ instrument: [],
291
+ };
292
+ attempts.set(attemptId, fresh);
293
+ return fresh;
294
+ }
295
+
296
+ /**
297
+ * The bundle, joined. Every field comes from a file in the run folder; a run
298
+ * that is missing one is reported with nulls rather than refused, because an
299
+ * interrupted run is exactly when somebody wants to look at it.
300
+ */
301
+ export function assembleTimeline(bundle: ProofBundle): InspectReport {
302
+ const meta = bundle.readMeta() ?? {};
303
+ const events = bundle.readEvents();
304
+ const approvals = bundle.readApprovals();
305
+ const snapshot = bundle.readMandateSnapshot();
306
+ const currency = str(snapshot?.["currency"]) ?? "BRL";
307
+
308
+ const redact = redactorFor(rawPayeesOf(events, approvals));
309
+ const executions = new Map<string, InspectExecution>();
310
+ const attemptsBy = new Map<string, Map<string, InspectAttempt>>();
311
+ const runEvents: InspectReport["run_events"] = [];
312
+
313
+ const executionOf = (id: string): InspectExecution => {
314
+ const existing = executions.get(id);
315
+ if (existing) return existing;
316
+ const fresh: InspectExecution = {
317
+ execution_id: id,
318
+ final_state: "unknown",
319
+ mode: null,
320
+ proposed_at: null,
321
+ items: [],
322
+ total_minor: null,
323
+ model_claimed_total: null,
324
+ batch: null,
325
+ approval: null,
326
+ transitions: [],
327
+ attempts: [],
328
+ receipts: [],
329
+ notes: [],
330
+ };
331
+ executions.set(id, fresh);
332
+ attemptsBy.set(id, new Map());
333
+ return fresh;
334
+ };
335
+
336
+ for (const event of events) {
337
+ const type = str(event["type"]) ?? "unknown";
338
+ const at = str(event["at"]) ?? "";
339
+ const executionId = str(event["execution_id"]);
340
+ const p = payload(event);
341
+
342
+ if (!executionId) {
343
+ const detail = str(p["detail"]) ?? str(p["reason"]) ?? str(event["reason"]) ?? str(event["tool"]) ?? str(event["message"]) ?? "";
344
+ const tool = str(event["tool"]);
345
+ runEvents.push({ at, type, detail: redact(tool ? `${tool}: ${detail || String(event["reason"] ?? "")}` : detail) ?? "" });
346
+ continue;
347
+ }
348
+
349
+ const execution = executionOf(executionId);
350
+ const attempts = attemptsBy.get(executionId)!;
351
+
352
+ switch (type) {
353
+ case "execution.drafted": {
354
+ execution.proposed_at = at;
355
+ execution.items = Array.isArray(p["items"]) ? (p["items"] as unknown[]).map((i) => item(i, currency)) : [];
356
+ execution.total_minor = num(p["total"]);
357
+ execution.model_claimed_total = num(p["model_claimed_total"]);
358
+ execution.mode = str(p["mode"]);
359
+ execution.batch = batchBinding(p["batch"]);
360
+ break;
361
+ }
362
+ case "execution.transition": {
363
+ const to = str(p["to"]) ?? "unknown";
364
+ execution.transitions.push({ at: str(p["at"]) ?? at, from: str(p["from"]), to, actor: actorLabel(p["actor"] ?? event["actor"]), reason: str(p["reason"]), detail: redact(str(p["detail"])) });
365
+ execution.final_state = to;
366
+ break;
367
+ }
368
+ case "approval.created":
369
+ // The artifact itself is `approval.json`; the event is only the stamp that says when it was written.
370
+ break;
371
+ case "rail.dispatch": {
372
+ const attemptId = str(p["attempt_id"]);
373
+ if (!attemptId) break;
374
+ const attempt = attemptOf(attempts, attemptId);
375
+ attempt.dispatched_at = at;
376
+ attempt.payee = maskMaybe(p["payee"]);
377
+ attempt.amount_minor = num(p["amount"]);
378
+ attempt.rail = str(p["rail"]);
379
+ attempt.idempotency_key = str(p["idempotency_key"]);
380
+ break;
381
+ }
382
+ case "rail.outcome": {
383
+ const attemptId = str(p["attempt_id"]);
384
+ if (!attemptId) break;
385
+ const attempt = attemptOf(attempts, attemptId);
386
+ attempt.answer = {
387
+ at,
388
+ status: str(p["status"]) ?? "unknown",
389
+ code: str(p["code"]),
390
+ message: redact(str(p["message"])),
391
+ transaction_id: str(p["transaction_id"]),
392
+ receipt_id: str(p["receipt_id"]),
393
+ money_moved: bool(p["money_moved"]),
394
+ sandbox: bool(p["sandbox"]),
395
+ };
396
+ attempt.status = attempt.answer.status;
397
+ attempt.transaction_id = attempt.answer.transaction_id ?? attempt.transaction_id;
398
+ attempt.receipt_id = attempt.answer.receipt_id ?? attempt.receipt_id;
399
+ break;
400
+ }
401
+ case "rail.uncertain": {
402
+ const attemptId = str(p["attempt_id"]);
403
+ const attempt = attemptId ? attemptOf(attempts, attemptId) : undefined;
404
+ if (attempt?.answer) {
405
+ attempt.answer.message = redact(str(p["message"])) ?? attempt.answer.message;
406
+ attempt.answer.code = str(p["code"]) ?? attempt.answer.code;
407
+ }
408
+ break;
409
+ }
410
+ case "rail.reconcile": {
411
+ const attemptId = str(p["attempt_id"]);
412
+ if (!attemptId) break;
413
+ const found = str(p["found"]) ?? "unknown";
414
+ const attempt = attemptOf(attempts, attemptId);
415
+ attempt.reconciles.push({ at, found });
416
+ if (found !== "absent" && found !== "unknown") attempt.status = found;
417
+ break;
418
+ }
419
+ case "charge.instrument": {
420
+ const attemptId = str(p["attempt_id"]);
421
+ if (!attemptId) break;
422
+ const attempt = attemptOf(attempts, attemptId);
423
+ attempt.transaction_id = str(p["charge_id"]) ?? attempt.transaction_id;
424
+ attempt.instrument.push({ at, status: str(p["status"]) ?? "unknown", payable: bool(p["payable"]) ?? false, due_date: str(p["due_date"]) });
425
+ break;
426
+ }
427
+ case "receipt.saved": {
428
+ const receiptId = str(p["receipt_id"]);
429
+ if (receiptId && !execution.receipts.includes(receiptId)) execution.receipts.push(receiptId);
430
+ break;
431
+ }
432
+ default: {
433
+ const attemptId = str(p["attempt_id"]);
434
+ if (type.startsWith("commerce.")) {
435
+ // The rail's own settlement event, which may be the ONLY place a receipt id appears (a webhook closed the attempt).
436
+ const receiptId = str(p["receipt_id"]);
437
+ if (attemptId) {
438
+ const attempt = attemptOf(attempts, attemptId);
439
+ attempt.receipt_id = receiptId ?? attempt.receipt_id;
440
+ }
441
+ execution.notes.push({ at, type, detail: [attemptId ? `attempt ${attemptId}` : "", receiptId ? `receipt ${receiptId}` : ""].filter(Boolean).join(" · ") });
442
+ break;
443
+ }
444
+ execution.notes.push({ at, type, detail: redact(str(p["detail"]) ?? str(p["message"]) ?? str(p["reason"]) ?? "") ?? "" });
445
+ }
446
+ }
447
+ }
448
+
449
+ for (const artifact of approvals) {
450
+ const execution = executionOf(artifact.execution_id);
451
+ execution.approval = {
452
+ approval_id: artifact.approval_id,
453
+ approver: artifact.approver,
454
+ approved_at: artifact.approved_at,
455
+ expires_at: artifact.expires_at,
456
+ items_hash: artifact.items_hash,
457
+ batch: batchBinding(artifact.batch),
458
+ ...(artifact.composition ? { composition: artifact.composition } : {}),
459
+ mandate: artifact.mandate,
460
+ escalation: artifact.escalation ? { trigger: artifact.escalation.trigger, detail: redact(artifact.escalation.detail) ?? "" } : null,
461
+ };
462
+ // A run whose event log was truncated still knows what was approved.
463
+ if (execution.items.length === 0) execution.items = artifact.items.map((i) => item(i, currency));
464
+ if (!execution.batch) execution.batch = batchBinding(artifact.batch);
465
+ }
466
+
467
+ for (const [id, attempts] of attemptsBy) executionOf(id).attempts = [...attempts.values()];
468
+
469
+ const receipts: InspectReceipt[] = bundle.listReceipts().map((file) => {
470
+ const raw = bundle.readReceipt(file) ?? {};
471
+ const payment = (raw["payment"] ?? {}) as Record<string, unknown>;
472
+ return {
473
+ receipt_id: str(raw["receipt_id"]) ?? file.replace(/\.json$/, ""),
474
+ file: `receipts/${file}`,
475
+ state: str(raw["state"]),
476
+ amount_minor: num(payment["amount_minor"]),
477
+ payee: maskMaybe(payment["payee"]),
478
+ money_moved: bool(payment["money_moved"]),
479
+ sandbox: bool(payment["sandbox"]),
480
+ at: str(payment["at"]),
481
+ };
482
+ });
483
+
484
+ const transcript = bundle.readTranscript();
485
+ const toolCalls = transcript.filter((l) => l.kind === "tool_call");
486
+ const refusedTools = transcript.filter((l) => l.kind === "tool_result" && l["refused"] === true).map((l) => String(l["name"]));
487
+
488
+ return {
489
+ run: {
490
+ run_id: bundle.runId,
491
+ agent: str(meta["agent"]),
492
+ mode: str(meta["mode"]),
493
+ rail: str(meta["rail"]),
494
+ mandate_id: str(meta["mandate_id"]),
495
+ mandate_version: num(meta["mandate_version"]) ?? num(snapshot?.["version"]),
496
+ started_at: str(meta["started_at"]),
497
+ bundle_dir: bundle.dir,
498
+ },
499
+ mandate: snapshot
500
+ ? {
501
+ id: str(snapshot["id"]) ?? "unknown",
502
+ version: num(snapshot["version"]) ?? 0,
503
+ status: str(snapshot["status"]) ?? "unknown",
504
+ expires_at: str(snapshot["expires_at"]) ?? "unknown",
505
+ currency,
506
+ per_tx_cap_minor: num(snapshot["per_tx_cap_minor"]) ?? 0,
507
+ cap_minor: num(snapshot["cap_minor"]) ?? 0,
508
+ periodic_cap: snapshot["periodic_cap"] && typeof snapshot["periodic_cap"] === "object" ? { window: String((snapshot["periodic_cap"] as Record<string, unknown>)["window"]), cap_minor: Number((snapshot["periodic_cap"] as Record<string, unknown>)["cap_minor"]) } : null,
509
+ beneficiaries: Array.isArray(snapshot["beneficiaries"])
510
+ ? (snapshot["beneficiaries"] as Array<Record<string, unknown>>).map((b) => ({ alias: String(b["alias"]), name: String(b["name"]), payee: maskMaybe(b["payee"]) ?? "unknown" }))
511
+ : [],
512
+ }
513
+ : null,
514
+ executions: [...executions.values()],
515
+ batches: assembleBatches([...executions.values()]),
516
+ run_events: runEvents,
517
+ receipts,
518
+ conversation: {
519
+ turns: transcript.filter((l) => l.kind === "user").length,
520
+ tool_calls: toolCalls.length,
521
+ tools: [...new Set(toolCalls.map((l) => String(l["name"])))],
522
+ refused_tools: [...new Set(refusedTools)],
523
+ },
524
+ verify: { present: bundle.hasVerify(), note: VERIFY_NOTE },
525
+ };
526
+ }
527
+
528
+ /**
529
+ * The batches of a run, from the bindings its executions carry. A line is
530
+ * `attested` when the bundle holds its approval artifact: a line a person
531
+ * DENIED has an execution and no artifact, and a line dropped before it ran
532
+ * has neither, so the two are counted apart and both are visible.
533
+ */
534
+ function assembleBatches(executions: InspectExecution[]): InspectBatch[] {
535
+ const batches = new Map<string, InspectBatch>();
536
+ for (const execution of executions) {
537
+ const binding = execution.batch;
538
+ if (!binding) continue;
539
+ const key = `${binding.ref}\u0000${binding.batch_hash}`;
540
+ const entry = batches.get(key) ?? { ref: binding.ref, batch_hash: binding.batch_hash, count: binding.count, lines: [], attested: 0, missing: [] };
541
+ entry.lines.push({ index: binding.index, execution_id: execution.execution_id, final_state: execution.final_state, attested: execution.approval !== null });
542
+ batches.set(key, entry);
543
+ }
544
+ for (const entry of batches.values()) {
545
+ entry.lines.sort((a, b) => a.index - b.index);
546
+ entry.attested = entry.lines.filter((l) => l.attested).length;
547
+ const held = new Set(entry.lines.map((l) => l.index));
548
+ entry.missing = [...Array(entry.count).keys()].filter((i) => !held.has(i));
549
+ }
550
+ return [...batches.values()];
551
+ }
552
+
553
+ // ---- the terminal rendering --------------------------------------------
554
+
555
+ function clock(at: string | null): string {
556
+ if (!at) return "--:--:--";
557
+ const m = /T(\d{2}:\d{2}:\d{2})/.exec(at);
558
+ return m?.[1] ?? at;
559
+ }
560
+
561
+ function approverLabel(approver: ApprovalArtifact["approver"]): string {
562
+ return approver.type === "person" ? `${approver.id} (person, ${approver.channel})` : `the mandate ${approver.id}`;
563
+ }
564
+
565
+ /** One ordered list of lines per execution: the proposal, the decision, every transition, the call and its answer. */
566
+ function executionLines(execution: InspectExecution): Array<{ at: string; label: string; text: string; wrapped: string[] }> {
567
+ const lines: Array<{ at: string; label: string; text: string; wrapped: string[] }> = [];
568
+
569
+ if (execution.proposed_at) {
570
+ const wrapped = execution.items.map((i) => `${i.beneficiary} <${i.payee}> ${formatMinor(i.amount_minor)}${i.description ? ` ${i.description}` : ""}${i.due_date ? ` due ${i.due_date}` : ""}`);
571
+ if (execution.total_minor !== null) {
572
+ wrapped.push(`total ${formatMinor(execution.total_minor)} computed by the core${execution.model_claimed_total !== null ? `; the model said ${formatMinor(execution.model_claimed_total)}` : ""}`);
573
+ }
574
+ lines.push({ at: execution.proposed_at, label: "proposed", text: `${execution.items.length} item(s)${execution.mode ? ` in ${execution.mode} mode` : ""}`, wrapped });
575
+ }
576
+
577
+ const approval = execution.approval;
578
+ if (approval) {
579
+ const wrapped = [`${approval.approval_id} · items_hash ${approval.items_hash}`, `under mandate ${approval.mandate.id} v${approval.mandate.version} · the artifact expires ${clock(approval.expires_at)}`];
580
+ if (approval.batch) wrapped.push(`line ${approval.batch.index + 1} of ${approval.batch.count} of batch ${approval.batch.ref} · batch_hash ${approval.batch.batch_hash}`);
581
+ if (approval.composition) wrapped.push(`composed of ${approval.composition.line_count} line(s) of ${approval.composition.ref} · composition_hash ${approval.composition.composition_hash}`);
582
+ if (approval.escalation) wrapped.push(`escalated by ${approval.escalation.trigger}: ${approval.escalation.detail}`);
583
+ lines.push({ at: approval.approved_at, label: "approved", text: approverLabel(approval.approver), wrapped });
584
+ }
585
+
586
+ for (const t of execution.transitions) {
587
+ const suffix = [t.reason ? `reason ${t.reason}` : "", t.detail ?? ""].filter(Boolean).join(" · ");
588
+ lines.push({ at: t.at, label: "state", text: `${t.from ?? "-"} -> ${t.to} by ${t.actor}`, wrapped: suffix ? [suffix] : [] });
589
+ }
590
+
591
+ for (const a of execution.attempts) {
592
+ if (a.dispatched_at) {
593
+ const wrapped = a.idempotency_key ? [`idempotency_key ${a.idempotency_key}`] : [];
594
+ lines.push({ at: a.dispatched_at, label: "call out", text: `${a.attempt_id} -> ${a.rail ?? "rail"}${a.amount_minor !== null ? ` ${formatMinor(a.amount_minor)}` : ""}${a.payee ? ` to ${a.payee}` : ""}`, wrapped });
595
+ }
596
+ const answer = a.answer;
597
+ if (answer) {
598
+ const bits = [answer.transaction_id ? `tx ${answer.transaction_id}` : "", answer.receipt_id ? `receipt ${answer.receipt_id}` : "", answer.money_moved !== null ? `money_moved ${answer.money_moved}` : "", answer.sandbox ? "sandbox" : ""].filter(Boolean);
599
+ const why = [answer.code, answer.message].filter(Boolean).join(": ");
600
+ lines.push({ at: answer.at, label: "rail says", text: `${answer.status}${why ? ` ${why}` : ""}`, wrapped: bits.length ? [bits.join(" · ")] : [] });
601
+ }
602
+ for (const i of a.instrument) lines.push({ at: i.at, label: "instrument", text: `${a.attempt_id} ${i.status}${i.payable ? ", payable" : ", not payable yet"}${i.due_date ? `, due ${i.due_date}` : ""}`, wrapped: [] });
603
+ for (const r of a.reconciles) lines.push({ at: r.at, label: "look again", text: `${a.attempt_id} the rail says ${r.found}`, wrapped: [] });
604
+ }
605
+
606
+ for (const n of execution.notes) lines.push({ at: n.at, label: n.type.startsWith("commerce.") ? "rail event" : "note", text: `${n.type}${n.detail ? ` ${n.detail}` : ""}`, wrapped: [] });
607
+
608
+ return lines.sort((a, b) => (a.at === b.at ? 0 : a.at < b.at ? -1 : 1));
609
+ }
610
+
611
+ /**
612
+ * The batch header, in the words a reader needs: how many lines the approved
613
+ * list held, how many of them this bundle attests, and which positions it
614
+ * holds nothing for. "3 of 4 lines attested" is the sentence; the rest of the
615
+ * block says which 3 and which 4th.
616
+ */
617
+ export function batchSummary(batch: InspectBatch): string {
618
+ const bits = [`${batch.attested} of ${batch.count} line(s) attested`];
619
+ if (batch.lines.length !== batch.attested) bits.push(`${batch.lines.length} with an execution`);
620
+ if (batch.missing.length) bits.push(`no execution for line(s) ${batch.missing.map((i) => i + 1).join(", ")}`);
621
+ return bits.join(" · ");
622
+ }
623
+
624
+ /** One row per position of the presented list, present in the bundle or not. */
625
+ export function batchLineRows(batch: InspectBatch): string[] {
626
+ const byIndex = new Map(batch.lines.map((l) => [l.index, l]));
627
+ return [...Array(batch.count).keys()].map((i) => {
628
+ const line = byIndex.get(i);
629
+ const where = `line ${i + 1} of ${batch.count}`;
630
+ if (!line) return `${where} — no execution in this bundle: approved as part of this list, and nothing here says it ran`;
631
+ return `${where} ${line.execution_id} ${line.final_state} ${line.attested ? "approval artifact present" : "no approval artifact"}`;
632
+ });
633
+ }
634
+
635
+ /** `execution exe_x — settled` gains ` · folha-2026-10 line 2 of 3` when it is one line of a batch. */
636
+ function executionHeading(execution: InspectExecution): string {
637
+ const b = execution.batch;
638
+ return `execution ${execution.execution_id} — ${execution.final_state}${b ? ` · ${b.ref} line ${b.index + 1} of ${b.count}` : ""}`;
639
+ }
640
+
641
+ export function renderText(report: InspectReport): string {
642
+ const out: string[] = [];
643
+ const r = report.run;
644
+ out.push(`${r.run_id}${r.agent ? ` — ${r.agent}` : ""}`);
645
+ out.push(` mode ${r.mode ?? "?"} · rail ${r.rail ?? "?"} · mandate ${r.mandate_id ?? "?"}${r.mandate_version !== null ? ` v${r.mandate_version}` : ""}${r.started_at ? ` · started ${r.started_at}` : ""}`);
646
+ const m = report.mandate;
647
+ if (m) {
648
+ const caps = [`per payment ${formatMinor(m.per_tx_cap_minor)}`, m.periodic_cap ? `per ${m.periodic_cap.window} ${formatMinor(m.periodic_cap.cap_minor)}` : "", `lifetime ${formatMinor(m.cap_minor)}`].filter(Boolean);
649
+ out.push(` mandate ${m.status}, expires ${m.expires_at} · ${caps.join(" · ")}`);
650
+ if (m.beneficiaries.length) out.push(` named payees: ${m.beneficiaries.map((b) => `${b.name} <${b.payee}>`).join(", ")}`);
651
+ }
652
+
653
+ for (const batch of report.batches) {
654
+ out.push("");
655
+ out.push(`batch ${batch.ref} — ${batchSummary(batch)}`);
656
+ out.push(` batch_hash ${batch.batch_hash}`);
657
+ for (const row of batchLineRows(batch)) out.push(` ${row}`);
658
+ }
659
+
660
+ for (const execution of report.executions) {
661
+ out.push("");
662
+ out.push(executionHeading(execution));
663
+ for (const line of executionLines(execution)) {
664
+ out.push(` ${clock(line.at)} ${line.label.padEnd(10)} ${line.text}`);
665
+ for (const w of line.wrapped) out.push(` ${" ".repeat(8)} ${" ".repeat(10)} ${w}`);
666
+ }
667
+ }
668
+
669
+ if (report.run_events.length) {
670
+ out.push("");
671
+ out.push("outside any execution");
672
+ for (const e of report.run_events) out.push(` ${clock(e.at)} ${e.type.padEnd(10)} ${e.detail}`);
673
+ }
674
+
675
+ out.push("");
676
+ out.push(`receipts (${report.receipts.length})`);
677
+ for (const receipt of report.receipts) {
678
+ const bits = [receipt.state ?? "?", receipt.amount_minor !== null ? formatMinor(receipt.amount_minor) : "", receipt.payee ?? "", receipt.sandbox ? "sandbox" : "", receipt.money_moved === false ? "no money moved" : ""].filter(Boolean);
679
+ out.push(` ${receipt.receipt_id} ${bits.join(" ")}`);
680
+ out.push(` ${receipt.file}`);
681
+ }
682
+ if (!report.receipts.length) out.push(" none");
683
+
684
+ out.push("");
685
+ const c = report.conversation;
686
+ out.push(`conversation: ${c.turns} turn(s), ${c.tool_calls} tool call(s)${c.tools.length ? ` (${c.tools.join(", ")})` : ""}${c.refused_tools.length ? ` · refused: ${c.refused_tools.join(", ")}` : ""}`);
687
+ out.push(`verify.json: ${report.verify.present ? "present" : report.verify.note}`);
688
+ return out.join("\n") + "\n";
689
+ }
690
+
691
+ // ---- the static HTML rendering ------------------------------------------
692
+
693
+ function escapeHtml(value: string): string {
694
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
695
+ }
696
+
697
+ /**
698
+ * One file, no network: the CSS is inline, there is no script, and nothing is
699
+ * fetched. A bundle inspected on an air-gapped machine renders the same as on
700
+ * a connected one, which is the whole point of a proof you can hand over.
701
+ */
702
+ export function renderHtml(report: InspectReport): string {
703
+ const r = report.run;
704
+ const m = report.mandate;
705
+ const esc = escapeHtml;
706
+
707
+ const header = [
708
+ `<h1>${esc(r.run_id)}</h1>`,
709
+ `<p class="lede">${esc(r.agent ?? "unknown agent")} · mode <b>${esc(r.mode ?? "?")}</b> · rail <b>${esc(r.rail ?? "?")}</b> · mandate <b>${esc(r.mandate_id ?? "?")}</b>${r.mandate_version !== null ? ` v${r.mandate_version}` : ""}${r.started_at ? ` · started ${esc(r.started_at)}` : ""}</p>`,
710
+ m
711
+ ? `<p class="lede">mandate ${esc(m.status)}, expires ${esc(m.expires_at)} · per payment ${esc(formatMinor(m.per_tx_cap_minor))}${m.periodic_cap ? ` · per ${esc(m.periodic_cap.window)} ${esc(formatMinor(m.periodic_cap.cap_minor))}` : ""} · lifetime ${esc(formatMinor(m.cap_minor))}</p>`
712
+ : "",
713
+ m && m.beneficiaries.length ? `<p class="lede">named payees: ${m.beneficiaries.map((b) => `${esc(b.name)} <code>${esc(b.payee)}</code>`).join(", ")}</p>` : "",
714
+ ].join("\n");
715
+
716
+ // The batch header comes before the timelines it is a header FOR: a reader
717
+ // must know the list held four lines before reading the three that ran.
718
+ const batches = report.batches
719
+ .map((batch) => {
720
+ const rows = batchLineRows(batch)
721
+ .map((row) => `<tr><td>${esc(row)}</td></tr>`)
722
+ .join("\n");
723
+ return `<section><h2>batch ${esc(batch.ref)} <span class="state">${esc(batchSummary(batch))}</span></h2><p class="lede">batch_hash <code>${esc(batch.batch_hash)}</code></p><table>${rows}</table></section>`;
724
+ })
725
+ .join("\n");
726
+
727
+ const executions = report.executions
728
+ .map((execution) => {
729
+ const rows = executionLines(execution)
730
+ .map((line) => {
731
+ const extra = line.wrapped.map((w) => `<div class="sub">${esc(w)}</div>`).join("");
732
+ return `<tr><td class="t">${esc(clock(line.at))}</td><td class="k"><span class="tag tag-${esc(line.label.replace(/\s+/g, "-"))}">${esc(line.label)}</span></td><td>${esc(line.text)}${extra}</td></tr>`;
733
+ })
734
+ .join("\n");
735
+ const b = execution.batch;
736
+ const where = b ? `<span class="state">${esc(`${b.ref} line ${b.index + 1} of ${b.count}`)}</span>` : "";
737
+ return `<section><h2>execution ${esc(execution.execution_id)} <span class="state state-${esc(execution.final_state)}">${esc(execution.final_state)}</span> ${where}</h2><table>${rows}</table></section>`;
738
+ })
739
+ .join("\n");
740
+
741
+ const runEvents = report.run_events.length
742
+ ? `<section><h2>outside any execution</h2><table>${report.run_events.map((e) => `<tr><td class="t">${esc(clock(e.at))}</td><td class="k">${esc(e.type)}</td><td>${esc(e.detail)}</td></tr>`).join("")}</table></section>`
743
+ : "";
744
+
745
+ const receipts = report.receipts.length
746
+ ? `<table>${report.receipts
747
+ .map((receipt) => {
748
+ const bits = [receipt.state ?? "?", receipt.amount_minor !== null ? formatMinor(receipt.amount_minor) : "", receipt.payee ?? "", receipt.sandbox ? "sandbox" : "", receipt.money_moved === false ? "no money moved" : ""].filter(Boolean).join(" · ");
749
+ return `<tr><td class="k"><code>${esc(receipt.receipt_id)}</code></td><td>${esc(bits)}<div class="sub">${esc(receipt.file)}</div></td></tr>`;
750
+ })
751
+ .join("")}</table>`
752
+ : "<p class=\"lede\">none</p>";
753
+
754
+ const c = report.conversation;
755
+ const footer = `<p class="lede">conversation: ${c.turns} turn(s), ${c.tool_calls} tool call(s)${c.tools.length ? ` (${esc(c.tools.join(", "))})` : ""}${c.refused_tools.length ? ` · refused: ${esc(c.refused_tools.join(", "))}` : ""}</p><p class="lede">verify.json: ${report.verify.present ? "present" : esc(report.verify.note)}</p><p class="lede">Payees are masked the way the proof bundle masks them. The conversation is reported as counts; its text stays in transcript.jsonl.</p>`;
756
+
757
+ return `<!doctype html>
758
+ <html lang="en">
759
+ <head>
760
+ <meta charset="utf-8">
761
+ <meta name="viewport" content="width=device-width, initial-scale=1">
762
+ <title>${esc(r.run_id)} — CodeSpar proof bundle</title>
763
+ <style>
764
+ :root { color-scheme: light dark; --bg: #fbfbfa; --fg: #1a1a18; --muted: #6b6b63; --line: #e2e2dc; --card: #ffffff; --accent: #2f5d50; }
765
+ @media (prefers-color-scheme: dark) { :root { --bg: #16171a; --fg: #e8e8e4; --muted: #9a9a92; --line: #2c2d31; --card: #1d1e22; --accent: #7fc3ae; } }
766
+ * { box-sizing: border-box; }
767
+ body { margin: 0; padding: 32px 16px 64px; background: var(--bg); color: var(--fg); font: 15px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }
768
+ main { max-width: 980px; margin: 0 auto; }
769
+ h1 { font-size: 20px; margin: 0 0 4px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; word-break: break-all; }
770
+ h2 { font-size: 15px; margin: 0 0 10px; font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; word-break: break-all; }
771
+ h3 { font-size: 15px; margin: 28px 0 10px; }
772
+ .lede { color: var(--muted); margin: 2px 0; font-size: 13px; }
773
+ section { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 16px; margin: 20px 0; }
774
+ table { width: 100%; border-collapse: collapse; }
775
+ td { padding: 5px 8px 5px 0; vertical-align: top; border-top: 1px solid var(--line); font-size: 13px; }
776
+ tr:first-child td { border-top: 0; }
777
+ td.t { width: 78px; color: var(--muted); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; white-space: nowrap; }
778
+ td.k { width: 120px; white-space: nowrap; }
779
+ .sub { color: var(--muted); font-size: 12px; margin-top: 2px; word-break: break-word; }
780
+ code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; overflow-wrap: anywhere; }
781
+ .tag { display: inline-block; padding: 1px 7px; border-radius: 999px; border: 1px solid var(--line); font-size: 11px; color: var(--muted); }
782
+ .tag-proposed, .tag-approved { border-color: var(--accent); color: var(--accent); }
783
+ .state { display: inline-block; padding: 1px 8px; border-radius: 999px; border: 1px solid var(--line); font-size: 11px; font-family: ui-sans-serif, system-ui, sans-serif; color: var(--muted); }
784
+ .state-settled { border-color: var(--accent); color: var(--accent); }
785
+ .state-denied, .state-failed, .state-expired { border-color: #b4552f; color: #b4552f; }
786
+ @media (max-width: 620px) { td.t, td.k { width: auto; white-space: normal; } body { padding: 20px 12px 48px; } }
787
+ </style>
788
+ </head>
789
+ <body>
790
+ <main>
791
+ ${header}
792
+ ${batches}
793
+ ${executions}
794
+ ${runEvents}
795
+ <section><h2>receipts (${report.receipts.length})</h2>${receipts}</section>
796
+ ${footer}
797
+ </main>
798
+ </body>
799
+ </html>
800
+ `;
801
+ }