@codespar/cli 0.16.0 → 0.18.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.
- package/README.md +1 -0
- package/dist/commands/mandate-verify.js +98 -19
- package/dist/commands/mandate-verify.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mandate-codec.js +31 -20
- package/dist/mandate-codec.js.map +1 -1
- package/package.json +1 -1
- package/templates/bills-agent/README.md +1 -1
- package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
- package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
- package/templates/bills-agent/agents/bills-agent/README.md +6 -5
- package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +5 -4
- package/templates/bills-agent/packages/agent-core/README.md +14 -1
- package/templates/bills-agent/packages/agent-core/package.json +1 -1
- package/templates/bills-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/bills-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/bills-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/bills-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/bills-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/bills-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/bills-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/bills-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/bills-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/bills-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/bills-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/bills-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/bills-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/bills-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/bills-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/bills-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/bills-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/bills-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/bills-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/bills-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/bills-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/bills-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/bills-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/bills-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/bills-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/bills-agent/packages/agent-runtime/README.md +12 -0
- package/templates/bills-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/bills-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/bills-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/bills-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/checkout-agent/LICENSE +21 -0
- package/templates/checkout-agent/README.md +28 -0
- package/templates/checkout-agent/_gitignore +10 -0
- package/templates/checkout-agent/agents/checkout-agent/.env.example +5 -0
- package/templates/checkout-agent/agents/checkout-agent/AGENTS.md +20 -0
- package/templates/checkout-agent/agents/checkout-agent/CLAUDE.md +20 -0
- package/templates/checkout-agent/agents/checkout-agent/README.md +94 -0
- package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +36 -0
- package/templates/checkout-agent/agents/checkout-agent/agent.yaml +30 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +28 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-beatriz.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina-retomada.json +10 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +49 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +12 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +13 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.json +26 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.json +12 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.json +13 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +15 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +7 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +12 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.json +13 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.json +16 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.json +15 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +8 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/webhook-replay.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/eval.yaml +11 -0
- package/templates/checkout-agent/agents/checkout-agent/guardrails.json +25 -0
- package/templates/checkout-agent/agents/checkout-agent/mandate.example.json +28 -0
- package/templates/checkout-agent/agents/checkout-agent/package.json +33 -0
- package/templates/checkout-agent/agents/checkout-agent/runbook.md +19 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.json +42 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.json +32 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.json +61 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +61 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +36 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +6 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.json +23 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.json +72 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.json +15 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +6 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.json +23 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +3 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.json +42 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +12 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.json +14 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +34 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +7 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.json +33 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.json +32 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +92 -0
- package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +338 -0
- package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +179 -0
- package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +374 -0
- package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +220 -0
- package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +297 -0
- package/templates/checkout-agent/agents/checkout-agent/test/adversarial.test.ts +117 -0
- package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +67 -0
- package/templates/checkout-agent/agents/checkout-agent/test/cli.test.ts +169 -0
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +6 -0
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +2 -0
- package/templates/checkout-agent/agents/checkout-agent/test/nfse.test.ts +170 -0
- package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +138 -0
- package/templates/checkout-agent/agents/checkout-agent/test/scenarios.test.ts +113 -0
- package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +171 -0
- package/templates/checkout-agent/agents/checkout-agent/tools.json +68 -0
- package/templates/checkout-agent/agents/checkout-agent/tsconfig.json +10 -0
- package/templates/checkout-agent/package.json +38 -0
- package/templates/checkout-agent/packages/agent-core/README.md +34 -0
- package/templates/checkout-agent/packages/agent-core/package.json +24 -0
- package/templates/checkout-agent/packages/agent-core/src/agent.ts +163 -0
- package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +195 -0
- package/templates/checkout-agent/packages/agent-core/src/api/client.ts +76 -0
- package/templates/checkout-agent/packages/agent-core/src/api/mandate-status.ts +74 -0
- package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +153 -0
- package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +43 -0
- package/templates/checkout-agent/packages/agent-core/src/approval.ts +135 -0
- package/templates/checkout-agent/packages/agent-core/src/bundle.ts +191 -0
- package/templates/checkout-agent/packages/agent-core/src/channels.ts +186 -0
- package/templates/checkout-agent/packages/agent-core/src/check.ts +276 -0
- package/templates/checkout-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/checkout-agent/packages/agent-core/src/engine.ts +1074 -0
- package/templates/checkout-agent/packages/agent-core/src/escalate.ts +85 -0
- package/templates/checkout-agent/packages/agent-core/src/events.ts +40 -0
- package/templates/checkout-agent/packages/agent-core/src/guardrails.ts +45 -0
- package/templates/checkout-agent/packages/agent-core/src/hash.ts +118 -0
- package/templates/checkout-agent/packages/agent-core/src/ids.ts +10 -0
- package/templates/checkout-agent/packages/agent-core/src/index.ts +34 -0
- package/templates/checkout-agent/packages/agent-core/src/mandate.ts +100 -0
- package/templates/checkout-agent/packages/agent-core/src/manifest.ts +76 -0
- package/templates/checkout-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
- package/templates/checkout-agent/packages/agent-core/src/providers/replay.ts +63 -0
- package/templates/checkout-agent/packages/agent-core/src/providers/types.ts +39 -0
- package/templates/checkout-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/checkout-agent/packages/agent-core/src/rail.ts +186 -0
- package/templates/checkout-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +577 -0
- package/templates/checkout-agent/packages/agent-core/src/revocation.ts +30 -0
- package/templates/checkout-agent/packages/agent-core/src/secrets.ts +30 -0
- package/templates/checkout-agent/packages/agent-core/src/state/store.ts +307 -0
- package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +157 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/mandate-status.ts +58 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +209 -0
- package/templates/checkout-agent/packages/agent-core/src/tools.ts +66 -0
- package/templates/checkout-agent/packages/agent-core/src/types.ts +187 -0
- package/templates/checkout-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/checkout-agent/packages/agent-core/test/approval.test.ts +117 -0
- package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/checkout-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/checkout-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/checkout-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +596 -0
- package/templates/checkout-agent/packages/agent-core/test/escalate.test.ts +42 -0
- package/templates/checkout-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/checkout-agent/packages/agent-core/test/helpers.ts +142 -0
- package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +229 -0
- package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +45 -0
- package/templates/checkout-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +416 -0
- package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +28 -0
- package/templates/checkout-agent/packages/agent-core/test/state-machine.test.ts +67 -0
- package/templates/checkout-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
- package/templates/checkout-agent/packages/agent-core/tsconfig.json +10 -0
- package/templates/checkout-agent/packages/agent-runtime/README.md +80 -0
- package/templates/checkout-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/checkout-agent/packages/agent-runtime/package.json +23 -0
- package/templates/checkout-agent/packages/agent-runtime/src/adversarial.ts +199 -0
- package/templates/checkout-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/types.ts +173 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +459 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +216 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +350 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +180 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +120 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +127 -0
- package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +367 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +145 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +49 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +134 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +248 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +330 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +40 -0
- package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/checkout-agent/packages/agent-runtime/src/index.ts +13 -0
- package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +801 -0
- package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +229 -0
- package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +105 -0
- package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +274 -0
- package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +239 -0
- package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +263 -0
- package/templates/checkout-agent/packages/agent-runtime/src/webhook.ts +131 -0
- package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +329 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +277 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +167 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/checkout-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/checkout-agent/scripts/whatsapp-emulator.mjs +84 -0
- package/templates/checkout-agent/tsconfig.base.json +16 -0
- package/templates/checkout-agent/vitest.config.ts +12 -0
- package/templates/collections-agent/README.md +6 -1
- package/templates/collections-agent/agents/collections-agent/README.md +12 -8
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +8 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042-retomada.json +10 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +27 -1
- package/templates/collections-agent/agents/collections-agent/src/kit.ts +2 -0
- package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +12 -2
- package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +55 -0
- package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +2 -0
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +12 -6
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +148 -0
- package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +5 -0
- package/templates/collections-agent/package.json +1 -0
- package/templates/collections-agent/packages/agent-core/README.md +14 -1
- package/templates/collections-agent/packages/agent-core/package.json +1 -1
- package/templates/collections-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/collections-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/collections-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/collections-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/collections-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/collections-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/collections-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/collections-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/collections-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/collections-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/collections-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/collections-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/collections-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/collections-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/collections-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/collections-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/collections-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/collections-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/collections-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/collections-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/collections-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/collections-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/collections-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/collections-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/collections-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/collections-agent/packages/agent-runtime/README.md +12 -0
- package/templates/collections-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/collections-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/collections-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/collections-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/collections-agent/scripts/whatsapp-emulator.mjs +84 -0
- package/templates/hello-agent/README.md +1 -1
- package/templates/hello-agent/packages/agent-core/README.md +14 -1
- package/templates/hello-agent/packages/agent-core/package.json +1 -1
- package/templates/hello-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/hello-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/hello-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/hello-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/hello-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/hello-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/hello-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/hello-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/hello-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/hello-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/hello-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/hello-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/hello-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/hello-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/hello-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/hello-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/hello-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/hello-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/hello-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/hello-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/hello-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/hello-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/hello-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/hello-agent/packages/agent-runtime/README.md +12 -0
- package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/hello-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/hello-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/hello-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/kits.lock.json +29 -6
- package/templates/supplier-payments-agent/README.md +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +11 -9
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +67 -20
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +504 -5
- package/templates/supplier-payments-agent/packages/agent-core/README.md +14 -1
- package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/supplier-payments-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/supplier-payments-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/supplier-payments-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/README.md +12 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §46's three product gaps, as the channel now answers them, with no network:
|
|
3
|
+
* a status webhook is read (a `failed` of an outcome's message is that
|
|
4
|
+
* outcome NOT told), a tapped quick reply becomes a turn only as the intent a
|
|
5
|
+
* declared template gave it, and the registry carries a fallback for an
|
|
6
|
+
* outcome the kit has no copy for. The same behaviour against the emulator is
|
|
7
|
+
* in the repository's `test/whatsapp-emulator.integration.test.ts`, and the
|
|
8
|
+
* `check` rule that requires the fallback, asked of the shipped agents, in
|
|
9
|
+
* `test/whatsapp-fallback-check.test.ts`: both read this repository's other
|
|
10
|
+
* agents and scripts, which a template made from one agent does not carry.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, expect, it } from "vitest";
|
|
13
|
+
import type { WhatsAppTemplate } from "@codespar/agent-core";
|
|
14
|
+
import { WhatsAppChannel } from "../src/channels/whatsapp/index.js";
|
|
15
|
+
import { buildSendRequest, parseInbound, type CloudApiConfig } from "../src/channels/whatsapp/cloud-api.js";
|
|
16
|
+
import type { ChannelBackend, ChannelLogLine, InboundMessage, OutboundBody, SentMessage, StatusUpdate } from "../src/channels/types.js";
|
|
17
|
+
|
|
18
|
+
const CONTACT = "+5511987654321";
|
|
19
|
+
const NOW = new Date("2026-09-23T17:00:00Z");
|
|
20
|
+
|
|
21
|
+
/** A backend the test drives: it queues what the person does and hands the channel the statuses the test reports. */
|
|
22
|
+
class Scripted implements ChannelBackend {
|
|
23
|
+
readonly name = "scripted";
|
|
24
|
+
readonly live = false;
|
|
25
|
+
readonly delivered: OutboundBody[] = [];
|
|
26
|
+
private listener: ((s: StatusUpdate) => void) | undefined;
|
|
27
|
+
constructor(private readonly inbox: InboundMessage[] = []) {}
|
|
28
|
+
async open(): Promise<void> {}
|
|
29
|
+
async next(): Promise<InboundMessage | undefined> {
|
|
30
|
+
return this.inbox.shift();
|
|
31
|
+
}
|
|
32
|
+
async deliver(_to: string, body: OutboundBody): Promise<SentMessage> {
|
|
33
|
+
this.delivered.push(body);
|
|
34
|
+
return { id: `wamid.test.${this.delivered.length}`, state: "sent" };
|
|
35
|
+
}
|
|
36
|
+
onStatus(listener: (s: StatusUpdate) => void): void {
|
|
37
|
+
this.listener = listener;
|
|
38
|
+
}
|
|
39
|
+
report(status: StatusUpdate): void {
|
|
40
|
+
this.listener?.(status);
|
|
41
|
+
}
|
|
42
|
+
async close(): Promise<void> {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const TEMPLATES: WhatsAppTemplate[] = [
|
|
46
|
+
{ name: "acordo_quitado", language: "pt_BR", description: "paid", body: "Oi! {{1}} quitado." },
|
|
47
|
+
{
|
|
48
|
+
name: "acordo_cobranca_vencida",
|
|
49
|
+
language: "pt_BR",
|
|
50
|
+
description: "expired",
|
|
51
|
+
body: "Oi! A cobranca do {{1}} venceu.",
|
|
52
|
+
buttons: [
|
|
53
|
+
{ id: "emitir_nova", title: "Emitir nova", intent: "quero que voce emita uma nova cobranca para o meu acordo" },
|
|
54
|
+
{ id: "agora_nao", title: "Agora nao", intent: "agora nao quero uma nova cobranca" },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{ name: "atendimento_atualizacao", language: "pt_BR", description: "fallback", body: "Oi! Temos uma atualizacao.", fallback: true },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
function channel(inbox: InboundMessage[] = []) {
|
|
61
|
+
const backend = new Scripted(inbox);
|
|
62
|
+
const said: string[] = [];
|
|
63
|
+
const failed: ChannelLogLine[] = [];
|
|
64
|
+
const ch = new WhatsAppChannel({ backend, conversation: { contact: CONTACT, subject: "acordo-1042" }, now: () => NOW, templates: TEMPLATES, say: (l) => said.push(l), onDeliveryFailed: (l) => failed.push(l) });
|
|
65
|
+
return { ch, backend, said, failed };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const tap = (id: string, extra: Partial<InboundMessage["reply"]> = {}): InboundMessage => ({ id: `wamid.in.${id}`, from: CONTACT, text: "Emitir nova", timestamp: 1790190000, reply: { type: "button_reply", id, title: "Emitir nova", ...extra } });
|
|
69
|
+
|
|
70
|
+
describe("1. a status webhook is read, and a failed outcome is an outcome not told", () => {
|
|
71
|
+
it("parses statuses, with Meta's errors, out of the same delivery that carries messages", () => {
|
|
72
|
+
const parsed = parseInbound({
|
|
73
|
+
entry: [{ changes: [{ value: { statuses: [{ id: "wamid.a", status: "failed", timestamp: "1790190000", errors: [{ code: 131026, title: "Message undeliverable", error_data: { details: "not on whatsapp" } }] }, { id: "wamid.b", status: "read", timestamp: "1790190001" }, { id: "wamid.c", status: "deleted" }] } }] }],
|
|
74
|
+
});
|
|
75
|
+
expect(parsed.statuses).toEqual([
|
|
76
|
+
{ message_id: "wamid.a", status: "failed", timestamp: 1790190000, errors: [{ code: 131026, title: "Message undeliverable", details: "not on whatsapp" }] },
|
|
77
|
+
{ message_id: "wamid.b", status: "read", timestamp: 1790190001, errors: [] },
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("a failed status of a message that told an outcome is recorded, reaches the operator and the outcome's owner, and names the outcome", async () => {
|
|
82
|
+
const { ch, backend, said, failed } = channel();
|
|
83
|
+
await ch.open();
|
|
84
|
+
(ch as unknown as { session: { observeInbound(t: number): void } }).session.observeInbound(Math.floor(NOW.getTime() / 1000));
|
|
85
|
+
const sent = await ch.send({ kind: "text", text: "Recebemos, acordo quitado.", about: { execution_id: "exe_1", state: "settled" } });
|
|
86
|
+
backend.report({ message_id: sent.id, status: "failed", timestamp: 1790190100, errors: [{ code: 131026, details: "not on whatsapp" }] });
|
|
87
|
+
expect(ch.deliveryOf(sent.id)).toBe("failed");
|
|
88
|
+
const status = ch.log().find((l) => l.direction === "status")!;
|
|
89
|
+
expect(status).toMatchObject({ kind: "status", message_id: sent.id, state: "failed", about: { execution_id: "exe_1", state: "settled" }, errors: [{ code: 131026, details: "not on whatsapp" }] });
|
|
90
|
+
expect(failed).toEqual([status]);
|
|
91
|
+
expect(said.join("\n")).toContain("a pessoa NAO foi avisada");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("a read is recorded and nothing more: not consent, not an acknowledgement, not a reply", async () => {
|
|
95
|
+
const { ch, backend, said, failed } = channel();
|
|
96
|
+
await ch.open();
|
|
97
|
+
(ch as unknown as { session: { observeInbound(t: number): void } }).session.observeInbound(Math.floor(NOW.getTime() / 1000));
|
|
98
|
+
const sent = await ch.send({ kind: "text", text: "Recebemos.", about: { execution_id: "exe_1", state: "settled" } });
|
|
99
|
+
backend.report({ message_id: sent.id, status: "read", timestamp: 1790190100, errors: [] });
|
|
100
|
+
expect(ch.log().filter((l) => l.direction === "status").map((l) => l.state)).toEqual(["read"]);
|
|
101
|
+
expect(failed).toEqual([]);
|
|
102
|
+
expect(said).toEqual([]);
|
|
103
|
+
// A read is not a turn either.
|
|
104
|
+
expect(await ch.next()).toBeUndefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("a failure the provider reports BEFORE the send answered (the emulator posts statuses synchronously) is still tied to its outcome", async () => {
|
|
108
|
+
class Eager extends Scripted {
|
|
109
|
+
override async deliver(to: string, body: OutboundBody): Promise<SentMessage> {
|
|
110
|
+
const sent = await super.deliver(to, body);
|
|
111
|
+
this.report({ message_id: sent.id, status: "failed", timestamp: 1, errors: [{ code: 131026 }] });
|
|
112
|
+
return sent;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const backend = new Eager();
|
|
116
|
+
const failed: ChannelLogLine[] = [];
|
|
117
|
+
const ch = new WhatsAppChannel({ backend, conversation: { contact: CONTACT }, now: () => NOW, templates: TEMPLATES, onDeliveryFailed: (l) => failed.push(l) });
|
|
118
|
+
await ch.open();
|
|
119
|
+
await ch.send({ kind: "template", template: "acordo_quitado", language: "pt_BR", variables: ["acordo-1042"], about: { execution_id: "exe_2", state: "settled" } });
|
|
120
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
121
|
+
expect(failed.map((l) => l.about)).toEqual([{ execution_id: "exe_2", state: "settled" }]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("a status about a message an EARLIER run sent is tied back to the outcome that run recorded", async () => {
|
|
125
|
+
const prior: ChannelLogLine = { at: NOW.toISOString(), direction: "out", contact: "+55*****4321", kind: "template", message_id: "wamid.old", state: "sent", about: { execution_id: "exe_old", state: "settled" } };
|
|
126
|
+
const backend = new Scripted();
|
|
127
|
+
const failed: ChannelLogLine[] = [];
|
|
128
|
+
const ch = new WhatsAppChannel({ backend, conversation: { contact: CONTACT }, now: () => NOW, templates: TEMPLATES, priorLines: [prior], onDeliveryFailed: (l) => failed.push(l) });
|
|
129
|
+
await ch.open();
|
|
130
|
+
backend.report({ message_id: "wamid.old", status: "failed", timestamp: 1, errors: [{ code: 131026 }] });
|
|
131
|
+
expect(failed[0]?.about).toEqual({ execution_id: "exe_old", state: "settled" });
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("2. a tapped quick reply is a turn, as the intent a declared template gave it", () => {
|
|
136
|
+
it("parses a button_reply, a list_reply and a template's quick-reply button, id and context kept", () => {
|
|
137
|
+
const msg = (m: Record<string, unknown>) => ({ entry: [{ changes: [{ value: { messages: [{ from: "5511987654321", timestamp: "1", ...m }] } }] }] });
|
|
138
|
+
expect(parseInbound(msg({ id: "a", type: "interactive", interactive: { type: "button_reply", button_reply: { id: "emitir_nova", title: "Emitir nova" } }, context: { id: "wamid.offer" } })).messages[0]?.reply).toEqual({ type: "button_reply", id: "emitir_nova", title: "Emitir nova", context_id: "wamid.offer" });
|
|
139
|
+
expect(parseInbound(msg({ id: "b", type: "interactive", interactive: { type: "list_reply", list_reply: { id: "3x", title: "3x", description: "tres" } } })).messages[0]?.reply).toEqual({ type: "list_reply", id: "3x", title: "3x" });
|
|
140
|
+
expect(parseInbound(msg({ id: "c", type: "button", button: { payload: "emitir_nova", text: "Emitir nova" } })).messages[0]?.reply).toEqual({ type: "button", id: "emitir_nova", title: "Emitir nova" });
|
|
141
|
+
// What this channel cannot read is still named, not turned into an empty message.
|
|
142
|
+
expect(parseInbound(msg({ id: "d", type: "image" })).ignored).toEqual([{ id: "d", type: "image" }]);
|
|
143
|
+
expect(parseInbound(msg({ id: "e", type: "interactive", interactive: { type: "nfm_reply" } })).ignored).toEqual([{ id: "e", type: "interactive:nfm_reply" }]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("a declared id is a turn whose text is the declared intent, never the title", async () => {
|
|
147
|
+
const { ch } = channel([tap("emitir_nova")]);
|
|
148
|
+
const turn = await ch.next();
|
|
149
|
+
expect(turn?.text).toBe("quero que voce emita uma nova cobranca para o meu acordo");
|
|
150
|
+
const line = ch.log().at(-1)!;
|
|
151
|
+
expect(line).toMatchObject({ direction: "in", kind: "reply", reply: { id: "emitir_nova", title: "Emitir nova" }, text: "quero que voce emita uma nova cobranca para o meu acordo" });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("an id no template declares is not a turn: recorded, told to the operator, and the next real turn is what comes back", async () => {
|
|
155
|
+
const typed: InboundMessage = { id: "wamid.in.text", from: CONTACT, text: "oi", timestamp: 1790190001 };
|
|
156
|
+
const { ch, said } = channel([tap("pagar_tudo", { title: "Pagar tudo" }), typed]);
|
|
157
|
+
const turn = await ch.next();
|
|
158
|
+
expect(turn?.text).toBe("oi");
|
|
159
|
+
expect(ch.log()[0]).toMatchObject({ kind: "reply", refused: { rule: "reply_not_offered" }, reply: { id: "pagar_tudo" } });
|
|
160
|
+
expect(said.join("\n")).toContain("reply_not_offered");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("a tap that names a message this conversation did not send with that reply is not a turn either", async () => {
|
|
164
|
+
const { ch } = channel([tap("emitir_nova", { context_id: "wamid.somebody_else" })]);
|
|
165
|
+
expect(await ch.next()).toBeUndefined();
|
|
166
|
+
expect(ch.log()[0]?.refused?.rule).toBe("reply_not_offered");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("a template goes out with the quick replies its declaration offers, and the log says which", async () => {
|
|
170
|
+
const { ch, backend } = channel();
|
|
171
|
+
await ch.send({ kind: "template", template: "acordo_cobranca_vencida", language: "pt_BR", variables: ["acordo-1042"] });
|
|
172
|
+
expect((backend.delivered[0] as { buttons?: unknown }).buttons).toEqual([{ id: "emitir_nova", title: "Emitir nova" }, { id: "agora_nao", title: "Agora nao" }]);
|
|
173
|
+
expect(ch.log()[0]?.offered).toEqual(["emitir_nova", "agora_nao"]);
|
|
174
|
+
const config: CloudApiConfig = { baseUrl: "http://sim", phoneNumberId: "1", accessToken: "t", verifyToken: "v", appSecret: "s", apiVersion: "v22.0", webhookPort: 0 };
|
|
175
|
+
const request = buildSendRequest(config, CONTACT, backend.delivered[0]!) as { body: string };
|
|
176
|
+
expect(JSON.parse(request.body).template.components).toEqual([
|
|
177
|
+
{ type: "body", parameters: [{ type: "text", text: "acordo-1042" }] },
|
|
178
|
+
{ type: "button", sub_type: "quick_reply", index: "0", parameters: [{ type: "payload", payload: "emitir_nova" }] },
|
|
179
|
+
{ type: "button", sub_type: "quick_reply", index: "1", parameters: [{ type: "payload", payload: "agora_nao" }] },
|
|
180
|
+
]);
|
|
181
|
+
});
|
|
182
|
+
});
|
package/templates/kits.lock.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": 1,
|
|
3
3
|
"kits_repo": "https://github.com/codespar/agent-starter-kits",
|
|
4
|
-
"kits_ref": "
|
|
5
|
-
"commit": "
|
|
4
|
+
"kits_ref": "b766575306a71c9135c0d70e23d591e6523dc2f8",
|
|
5
|
+
"commit": "b766575306a71c9135c0d70e23d591e6523dc2f8",
|
|
6
6
|
"vendored_packages": [
|
|
7
7
|
{
|
|
8
8
|
"name": "@codespar/agent-core",
|
|
@@ -32,7 +32,27 @@
|
|
|
32
32
|
"npm run consent -- --yes",
|
|
33
33
|
"npm start"
|
|
34
34
|
],
|
|
35
|
-
"hash": "sha256:
|
|
35
|
+
"hash": "sha256:bd44befa0b4626949ec753c8f472994e4fb2819b637cc92d1c7f943386e50ad2"
|
|
36
|
+
},
|
|
37
|
+
"checkout-agent": {
|
|
38
|
+
"source": "agents/checkout-agent",
|
|
39
|
+
"description": "The merchant's agent that sells in the conversation: the customer builds a cart the code prices, the attendant (or the sales policy) confirms the order, one bolepix is issued with an idempotency key, the QR and the copy-and-paste go to the customer, and the order closes on commerce.charge.paid.",
|
|
40
|
+
"version": "0.1.0",
|
|
41
|
+
"cli": "@codespar/cli@0.14.0",
|
|
42
|
+
"mcp": "@codespar/mcp@0.5.8",
|
|
43
|
+
"vendored": {
|
|
44
|
+
"@codespar/agent-core": "0.1.0",
|
|
45
|
+
"@codespar/agent-runtime": "0.1.0"
|
|
46
|
+
},
|
|
47
|
+
"root_scripts": {
|
|
48
|
+
"whatsapp:emulator": "node scripts/whatsapp-emulator.mjs"
|
|
49
|
+
},
|
|
50
|
+
"next_steps": [
|
|
51
|
+
"cp agents/checkout-agent/.env.example agents/checkout-agent/.env # then fill in your keys",
|
|
52
|
+
"npm install",
|
|
53
|
+
"npm start"
|
|
54
|
+
],
|
|
55
|
+
"hash": "sha256:6f3ce0270015c774e879304b18ee55d8a9592e58815b0921ec51f973879c50d0"
|
|
36
56
|
},
|
|
37
57
|
"collections-agent": {
|
|
38
58
|
"source": "agents/collections-agent",
|
|
@@ -44,12 +64,15 @@
|
|
|
44
64
|
"@codespar/agent-core": "0.1.0",
|
|
45
65
|
"@codespar/agent-runtime": "0.1.0"
|
|
46
66
|
},
|
|
67
|
+
"root_scripts": {
|
|
68
|
+
"whatsapp:emulator": "node scripts/whatsapp-emulator.mjs"
|
|
69
|
+
},
|
|
47
70
|
"next_steps": [
|
|
48
71
|
"cp agents/collections-agent/.env.example agents/collections-agent/.env # then fill in your keys",
|
|
49
72
|
"npm install",
|
|
50
73
|
"npm start"
|
|
51
74
|
],
|
|
52
|
-
"hash": "sha256:
|
|
75
|
+
"hash": "sha256:2461b06b9d985c430679dd6012ec050e38bb8ad6f5f636ef9af281e65d180fc5"
|
|
53
76
|
},
|
|
54
77
|
"hello-agent": {
|
|
55
78
|
"source": "agents/hello-agent",
|
|
@@ -66,7 +89,7 @@
|
|
|
66
89
|
"npm install",
|
|
67
90
|
"npm start"
|
|
68
91
|
],
|
|
69
|
-
"hash": "sha256:
|
|
92
|
+
"hash": "sha256:aeef951a3f3725cb11ac19b55cf11997d7621633514b32663c7f2f373c7758c2"
|
|
70
93
|
},
|
|
71
94
|
"supplier-payments-agent": {
|
|
72
95
|
"source": "agents/supplier-payments-agent",
|
|
@@ -83,7 +106,7 @@
|
|
|
83
106
|
"npm install",
|
|
84
107
|
"npm start"
|
|
85
108
|
],
|
|
86
|
-
"hash": "sha256:
|
|
109
|
+
"hash": "sha256:58a9b44a7a4aae130124fc45708237207fc91ad9165298c44b0eb48ef6172dc3"
|
|
87
110
|
}
|
|
88
111
|
}
|
|
89
112
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# {{name}}
|
|
2
2
|
|
|
3
3
|
Scaffolded by `codespar init --template supplier-payments-agent` from the `supplier-payments-agent` starter kit
|
|
4
|
-
(https://github.com/codespar/agent-starter-kits at `
|
|
4
|
+
(https://github.com/codespar/agent-starter-kits at `b766575306a71c9135c0d70e23d591e6523dc2f8`, agent 0.1.0).
|
|
5
5
|
|
|
6
6
|
A company delegates its suppliers, commissions and payroll to an agent that pays them in batches under one signed mandate. A batch is a loop of executions: one refusal does not stop the others, and re-running it pays nobody twice.
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ You are editing a reference agent of the CodeSpar Agent Starter Kits. These rule
|
|
|
15
15
|
11. **Sandbox by construction.** Only `csk_test_` keys. Never commit `.env`, `.codespar/` or `runs/`. The pre-commit secret scan and the CI both refuse a key-shaped string.
|
|
16
16
|
12. **Before you say a task is done:** `npm run check`, `npm run eval` (adversarial suite plus scenarios, on the replay provider, no model needed) and `npm test` at the repository root. All green, or it is not done.
|
|
17
17
|
13. **Use the CodeSpar MCP and skills** (`@codespar/mcp@0.5.8`, pinned in `agent.yaml`) to learn the real API. When the spec in `docs/spec-v5.1.1.md` and the API diverge, follow the API and log it in `docs/OPEN_QUESTIONS.md`.
|
|
18
|
-
14. **Say which signature you mean.** A receipt sealed since the API added Ed25519 carries a signature anybody can check against the published key set, with no credential — that is what `npm run verify -- <receipt-file>` does. "Verifiable by a third party" is true of THAT and of nothing else here: a receipt sealed before the change carries no Ed25519 signature and never will, and the approval artifact is still HMAC with a local development key. Write the phrase only where `Ed25519` is written too; `npm run check` fails otherwise.
|
|
18
|
+
14. **Say which signature you mean.** A receipt sealed since the API added Ed25519 carries a signature anybody can check against the published key set, with no credential — that is what `npm run verify -- <receipt-file>` does. "Verifiable by a third party" is true of THAT and of nothing else here: a receipt sealed before the change carries no Ed25519 signature and never will, and the approval artifact is still HMAC with a local development key: what reaches a third party is its hash, sealed into a v4 receipt, which proves what was approved and never who. Write the phrase only where `Ed25519` is written too; `npm run check` fails otherwise.
|
|
19
19
|
15. **No telemetry.** Nothing in this repository phones home.
|
|
20
20
|
|
|
21
21
|
`AGENTS.md` and `CLAUDE.md` are the same file; the CI fails if they diverge. Edit both or neither.
|
|
@@ -15,7 +15,7 @@ You are editing a reference agent of the CodeSpar Agent Starter Kits. These rule
|
|
|
15
15
|
11. **Sandbox by construction.** Only `csk_test_` keys. Never commit `.env`, `.codespar/` or `runs/`. The pre-commit secret scan and the CI both refuse a key-shaped string.
|
|
16
16
|
12. **Before you say a task is done:** `npm run check`, `npm run eval` (adversarial suite plus scenarios, on the replay provider, no model needed) and `npm test` at the repository root. All green, or it is not done.
|
|
17
17
|
13. **Use the CodeSpar MCP and skills** (`@codespar/mcp@0.5.8`, pinned in `agent.yaml`) to learn the real API. When the spec in `docs/spec-v5.1.1.md` and the API diverge, follow the API and log it in `docs/OPEN_QUESTIONS.md`.
|
|
18
|
-
14. **Say which signature you mean.** A receipt sealed since the API added Ed25519 carries a signature anybody can check against the published key set, with no credential — that is what `npm run verify -- <receipt-file>` does. "Verifiable by a third party" is true of THAT and of nothing else here: a receipt sealed before the change carries no Ed25519 signature and never will, and the approval artifact is still HMAC with a local development key. Write the phrase only where `Ed25519` is written too; `npm run check` fails otherwise.
|
|
18
|
+
14. **Say which signature you mean.** A receipt sealed since the API added Ed25519 carries a signature anybody can check against the published key set, with no credential — that is what `npm run verify -- <receipt-file>` does. "Verifiable by a third party" is true of THAT and of nothing else here: a receipt sealed before the change carries no Ed25519 signature and never will, and the approval artifact is still HMAC with a local development key: what reaches a third party is its hash, sealed into a v4 receipt, which proves what was approved and never who. Write the phrase only where `Ed25519` is written too; `npm run check` fails otherwise.
|
|
19
19
|
15. **No telemetry.** Nothing in this repository phones home.
|
|
20
20
|
|
|
21
21
|
`AGENTS.md` and `CLAUDE.md` are the same file; the CI fails if they diverge. Edit both or neither.
|
|
@@ -15,7 +15,7 @@ npm run start:supplier
|
|
|
15
15
|
> roda a folha de outubro
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Three lines, three
|
|
18
|
+
Three lines, one question ("3 linha(s), total R$ …, batch_hash sha256:…" — answer `todas`, `todas exceto 2` or `nenhuma`), three approval artifacts, three receipts under `runs/<run-id>/receipts/`. Run it again and every line answers `already_settled`.
|
|
19
19
|
|
|
20
20
|
- `ANTHROPIC_API_KEY` can stay empty: the agent then replays a recorded transcript. The old placeholder `sk-ant-your_key_here` counts as empty.
|
|
21
21
|
- One-shot form: `npm start -- --input "roda a folha de outubro" --approve --json`.
|
|
@@ -31,10 +31,11 @@ Both shapes deliver it, now that the core dispatches every attempt and names eve
|
|
|
31
31
|
| Property | Where it comes from |
|
|
32
32
|
|---|---|
|
|
33
33
|
| One refusal does not stop the others | The loop in `src/modules/batch-payout.ts` `continue`s and never breaks. Each line reaches its own terminal state. |
|
|
34
|
-
| One `attempt_id` per call | The core derives it from
|
|
35
|
-
| Repeating pays nobody twice | A durable claim pairs (mandate, batch, line) with the execution covering it. Settled is skipped, still-open is never re-opened, and only an execution that ended without moving money is retried. |
|
|
34
|
+
| One `attempt_id` per call | The core derives it from the line itself — mandate, `batch_hash`, position — so the same line of the same list is the same attempt on any machine, and the quote it presents carries no approval time that would make it a different payment. |
|
|
35
|
+
| Repeating pays nobody twice | A durable claim pairs (mandate, batch, line) with the execution covering it. Settled is skipped, still-open is never re-opened, and only an execution that ended without moving money is retried. Behind the claim, the API answers a repeated `attempt_id` from its record of that attempt (ent#1671): a settled line replays its original receipt (marked `replayed`), one in flight stays open, and only a line the provider refused is paid again, under the next generation of its id. A line whose id the API holds for a different payment is reported `attempt_id_conflict` and never re-drafted. |
|
|
36
36
|
| Each approved line stays attested | One approval artifact per line, each with its own `items_hash` bound to the mandate version. `runs/<run-id>/approval.json` holds them in approval order. |
|
|
37
37
|
| The approved list is bound as a set | The list is hashed once, before any line is drafted, and every artifact of the batch carries that `batch_hash` with the line's own position and the list's length. So a bundle of three artifacts says "1, 2 and 4 of 4" and not "three payments". A line the human denied has an execution and no artifact; a line that left the list after it was approved has neither, and the run that would drop it is refused before it drafts anything. |
|
|
38
|
+
| One gesture approves the list, with a veto per line | In the interactive terminal a batch is put in front of the operator once: every line, the total and the `batch_hash` prefix. `todas` approves the list, `todas exceto 3,7` vetoes those positions, `nenhuma` denies it. The gesture decides each line as it arrives and approves nothing by itself: every approved line still mints its own artifact carrying the `batch_hash` the gesture was taken on, and the gesture is a `batch.gesture` event in the bundle naming what was approved and vetoed. A vetoed line is a denied execution with no artifact and appears in the report's `denied`; a line that claims another list under the same ref is not covered by the gesture and is denied. The one-shot and the scenario runner still pass one decision to every line. |
|
|
38
39
|
|
|
39
40
|
The claim is taken **before** the channel can run the execution. Claiming afterwards would leave a settled payout unclaimed if the process died in between, and that is exactly the double payment. A crash the other way round leaves a claim on an open execution, which the next run reports as `in_progress` and refuses to duplicate — the operator closes it with `npm run approve`, `npm run resume` or `npm run reconcile`.
|
|
40
41
|
|
|
@@ -45,7 +46,7 @@ The claim is taken **before** the channel can run the execution. Claiming afterw
|
|
|
45
46
|
| The model proposes, the code executes | `codespar_pay` creates executions in `drafted`. Only `ExecutionEngine` in `@codespar/agent-core` checks mandate, caps, allowlist, `escalate_above` and `items_hash`, and only it reaches `executing`. |
|
|
46
47
|
| A batch cannot be fractioned | The lines come from `payables.ts`, never from the tool call. `codespar_pay` refuses `batch_ref` sent together with `items` or `total_minor`, so a model being steered cannot add a payee to a payroll or split one line into five. |
|
|
47
48
|
| Partial failure is visible | `scenarios/partial-batch-failure`: the rail declines one supplier, the other two settle, and the report names which line failed and why. |
|
|
48
|
-
| Two modes, one trail | `approval: human` (default): the operator approves
|
|
49
|
+
| Two modes, one trail | `approval: human` (default): the operator approves the list, with a veto per line. `approval: mandate`: the agent runs what the signed allowance covers and asks above `escalate_above`. Same code, same states, same receipts. |
|
|
49
50
|
| Readable refusal | Cap per payout, cap per month, payee outside the mandate, revoked mandate, outside hours: each names itself in the trail and in the chat. |
|
|
50
51
|
| Sandbox by construction | A key that does not start with `csk_test_` fails before any network call. |
|
|
51
52
|
|
|
@@ -76,7 +77,7 @@ Not in this kit: WhatsApp, `embedded-consent` (see above), scheduling a batch fo
|
|
|
76
77
|
| `npm run resume` | After a crash: dispatches only what the outbox proves was never sent, reconciles the rest from the rail, expires what went stale. Never pays twice. |
|
|
77
78
|
| `npm run rerun <run-id>` | Replays a recorded run with no network and checks the state sequence matches. |
|
|
78
79
|
| `npm run inspect <run-id> [--json] [--html <file>]` | Reads a run's proof bundle back as a timeline. On a batch it prints the header once — the `batch_hash`, how many lines the approved list held, how many are attested here — and then one timeline per line. `2 of 4 line(s) attested · 3 with an execution · no execution for line(s) 3` is what a batch that did not all run looks like when you read it back. |
|
|
79
|
-
| `npm run verify -- <receipt-file> [--json] [--keys <file>] [--url <url>]` | Checks a receipt's Ed25519 signature against CodeSpar's published key set.
|
|
80
|
+
| `npm run verify -- <receipt-file> [--json] [--keys <file>] [--url <url>] [--approval <file>] [--from-api]` | Checks a receipt's Ed25519 signature against CodeSpar's published key set, then recomputes its chain from the API's receipt read under the published recipe, then holds the sealed approval hashes against the approval artifact. The read needs no key and no agent; a run's masked copy proves the signature only, unless `--from-api` reads the receipt with this agent's key (nothing is written). The exit code is the verdict — 0 verified, 1 tampered, 3 unsigned, 4 unknown key, 5 the key set could not be read, 6 not a receipt, 7 signature only (the body was not bound), 8 the body is not the sealed one, 9 the sealed approval is not the artifact's. |
|
|
80
81
|
| `npm run reconcile` | Compares local state with the rail. Closes an `executing` execution only from a recorded rail outcome; what the rail has not answered stays `executing` with an `execution.uncertain` event, for a human. Never dispatches. |
|
|
81
82
|
|
|
82
83
|
## The proof bundle
|
|
@@ -88,7 +89,7 @@ transcript.jsonl the conversation and the tool calls
|
|
|
88
89
|
approval.json the approval artifacts of the run — for a batch, one per line, in approval order
|
|
89
90
|
mandate.snapshot.json the mandate as it was, keys masked
|
|
90
91
|
events.jsonl every transition and every rail event, each with its actor
|
|
91
|
-
receipts/ the receipts the rail returned, each stamped with the actor
|
|
92
|
+
receipts/ the receipts the rail returned, each stamped with the actor; the payee is the one the receipt sealed
|
|
92
93
|
run.json mode, rail, mandate id
|
|
93
94
|
```
|
|
94
95
|
|
|
@@ -100,13 +101,14 @@ The receipt copies carry both of the API's seals. `receipt_sig` is the HMAC, whi
|
|
|
100
101
|
npm run verify -- runs/<run-id>/receipts/<receipt-id>.json
|
|
101
102
|
```
|
|
102
103
|
|
|
103
|
-
That reads the public key set from `/.well-known/codespar-receipt-keys.json`, picks the key the receipt names, rebuilds `codespar-receipt:v1:<receipt_id>:<chain>` and checks it with stock `node:crypto` — no key, no API key, no CodeSpar call that could be refused. `--keys <file>` uses a saved copy of the key set instead and touches no network at all; `--json` puts the verdict on stdout. `--url` names another deployment's key set: the default is production
|
|
104
|
+
That reads the public key set from `/.well-known/codespar-receipt-keys.json`, picks the key the receipt names, rebuilds `codespar-receipt:v1:<receipt_id>:<chain>` and checks it with stock `node:crypto` — no key, no API key, no CodeSpar call that could be refused. `--keys <file>` uses a saved copy of the key set instead and touches no network at all; `--json` puts the verdict on stdout. `--url` names another deployment's key set: the default is production. The signature covers the receipt id and the chain, so the masking above does not disturb it, and the file verifies on a machine that has never seen this repository; on its own the copy answers `signature_only`, because it masks the payee the chain sealed. `npm run verify -- runs/<run-id>/receipts/<receipt-id>.json --from-api` reads the unmasked receipt from the API with this agent's key, recomputes the chain from it, and holds its sealed approval hashes against the artifact the copy names in `approval.json`: `verified` then means this payment was made against the list that artifact approved. Nothing unmasked is written. A receipt sealed before the API had the capability answers `unsigned`, which is not a failure: it has no Ed25519 signature and never will, and its HMAC seal is unaffected. A receipt sealed before chain v4 answers `signature_only`: its chain seals the mandate's own signature, which the verifier does not take. [`docs/OPEN_QUESTIONS.md`](../../docs/OPEN_QUESTIONS.md) §47.
|
|
104
105
|
|
|
105
106
|
## Limits and stubs
|
|
106
107
|
|
|
107
108
|
- The receipt carries two signatures. The HMAC one proves the payout to whoever runs this agent, because verifying it means holding the secret that also mints it. The Ed25519 one, sealed by CodeSpar's platform issuer key since the API added it, proves it to anybody with `npm run verify`. A receipt sealed before that carries no Ed25519 signature and never will — there is no backfill, and signing an old receipt with today's key would attest to what the database says now, not to what happened then.
|
|
108
|
-
- The
|
|
109
|
-
- The
|
|
109
|
+
- The receipt names the payee because every spend presents the approved line as a `quote`; without one the API seals no payee anywhere in the chain. The API only records a quote that disagrees with what settled and pays anyway, so the kit refuses the disagreement before the call, and a receipt that seals a different payee, or none, stops the run with `ReceiptSealMismatchError` instead of a warning. [`docs/OPEN_QUESTIONS.md`](../../docs/OPEN_QUESTIONS.md) §18.
|
|
110
|
+
- The approval artifact is signed by HMAC with a **local development key** (`.codespar/approval.key`). This is a stub: the CodeSpar API does not sign approval lists today, so WHO approved is proved to whoever runs the agent and nobody else. WHAT was approved reaches further: every spend carries the artifact's `items_hash` (and a batch line its `batch_hash`), the API seals them into the receipt's chain (v4), and `npm run verify` holds that sealed link against the artifact. [`docs/OPEN_QUESTIONS.md`](../../docs/OPEN_QUESTIONS.md) §3.
|
|
111
|
+
- The claim is **local**, in `.codespar/state.db`. Delete that file, or run the same batch from a second machine, and the claim knows nothing. What still holds is the API: the re-run presents the same attempt ids with the same quotes, and the API pays each of them at most once. That rests on the server honouring an explicit `attempt_id`, which it does since ent#1671; it does not make the second machine's report complete (a line still in flight there is `uncertain` until reconciled), and a list that changed in any line is a different list with different ids. See `docs/OPEN_QUESTIONS.md` §39c.
|
|
110
112
|
- The batch is not atomic and does not try to be. Lines settle independently, so a batch can end part paid. That is the point, and the report says which lines those are.
|
|
111
113
|
- `batch_hash` binds the list; it does not bind the list to the payables file it came from. A batch whose lines changed is refused on the next run of the SAME `batch_ref`, because that is when there is an approved set to contradict. A list edited before it was ever presented is simply the list, and the mandate's allowlist and caps are what stand between it and a payment.
|
|
112
114
|
- The hash is over the resolved payees, so re-signing the mandate with a different Pix key for a payee moves it too, and the next run of an already-approved batch is refused. That is correct — where the money goes did change — but the refusal cannot see WHY, so it names what it measured and offers both ways out rather than guessing.
|
|
@@ -62,7 +62,7 @@ const kit: AgentKit = {
|
|
|
62
62
|
npm start -- --scenario <name> run a scenario pack (${"see scenarios/"})
|
|
63
63
|
options: --mode human|mandate --provider anthropic|replay --transcript <file> --rail stub|api --user <id> --json
|
|
64
64
|
--now <ISO 8601> pin the run to that instant (escalation hours, timestamps); env CODESPAR_AGENT_NOW is the same thing
|
|
65
|
-
a batch runs one execution per line: in \`human\` the
|
|
65
|
+
a batch runs one execution per line: in \`human\` the terminal asks once for the list (todas / todas exceto 3,7 / nenhuma), and every artifact carries the batch_hash of the list it was one of`,
|
|
66
66
|
|
|
67
67
|
buildRail: (ctx) => {
|
|
68
68
|
if (ctx.kind === "api") {
|
package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts
CHANGED
|
@@ -28,24 +28,33 @@
|
|
|
28
28
|
* one refusal does not stop the others -> the loop `continue`s, never
|
|
29
29
|
* breaks, and never throws past
|
|
30
30
|
* the line it is on
|
|
31
|
-
* one attempt_id per call -> the core derives it from
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* one attempt_id per call -> the core derives it from the
|
|
32
|
+
* line itself: mandate, batch_hash,
|
|
33
|
+
* position (`batchAttemptId`)
|
|
34
|
+
* repeating pays nobody twice -> the per-line claim below, and
|
|
35
|
+
* behind it the API's record of
|
|
36
|
+
* that attempt id
|
|
34
37
|
* the approved list is bound as a set -> `batch_hash`, and the set claim
|
|
35
38
|
* that refuses a run whose list
|
|
36
39
|
* changed after it was approved
|
|
37
40
|
*
|
|
38
|
-
* The claim is
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
41
|
+
* The claim is the first line of defence on a re-run, and it is local. The
|
|
42
|
+
* claim pairs (mandate, batch, line) with the execution that covers it,
|
|
43
|
+
* durably, and the rules for reading one back are the same posture the
|
|
44
|
+
* enterprise money paths take: a line whose execution SETTLED is done, a line
|
|
45
|
+
* whose execution is still open is in progress and is never re-opened, and
|
|
46
|
+
* only a line whose execution ended without moving money is retried.
|
|
47
|
+
*
|
|
48
|
+
* The second line is the API, and it holds wherever the batch runs. A batch
|
|
49
|
+
* line's attempt id and quote come from the line and not from the execution,
|
|
50
|
+
* so a run on a machine with no claim at all presents the attempt the API
|
|
51
|
+
* already holds, and the API answers it from its record (ent#1671): settled
|
|
52
|
+
* lines replay with their original receipt, a line still in flight stays open
|
|
53
|
+
* here, and only a line the provider refused is paid again, under the next
|
|
54
|
+
* generation of its id. OPEN_QUESTIONS §39c says what that does and does not
|
|
55
|
+
* cover.
|
|
47
56
|
*/
|
|
48
|
-
import { batchHash, isTerminal, type Execution, type ExecutionItem, type ProposedItem, type ToolContext } from "@codespar/agent-core";
|
|
57
|
+
import { batchHash, isTerminal, type BatchGesture, type Execution, type ExecutionItem, type ProposedItem, type ToolContext } from "@codespar/agent-core";
|
|
49
58
|
import { batchTotal, formatBRL, type Batch, type PayableLine } from "../payables.js";
|
|
50
59
|
|
|
51
60
|
/** What happened to one line of the batch. `dispatch` is the part an operator reads first. */
|
|
@@ -59,8 +68,15 @@ export interface BatchLineReport {
|
|
|
59
68
|
execution_id: string | null;
|
|
60
69
|
state: string;
|
|
61
70
|
reason: string | null;
|
|
62
|
-
/**
|
|
63
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Whether this line's money moved, could not move, or was deliberately not
|
|
73
|
+
* attempted again. `attempt_id_conflict`: the API holds this line's attempt
|
|
74
|
+
* id for a DIFFERENT payment (another amount, payee, mandate, rail or
|
|
75
|
+
* quote). Nothing was held or sent, the id is not spent, and presenting it
|
|
76
|
+
* again answers the same, so the line is never re-drafted under it and never
|
|
77
|
+
* moved to another id: a person decides what that other payment is.
|
|
78
|
+
*/
|
|
79
|
+
dispatch: "settled" | "refused" | "awaiting_decision" | "uncertain" | "already_settled" | "in_progress" | "attempt_id_conflict";
|
|
64
80
|
}
|
|
65
81
|
|
|
66
82
|
/**
|
|
@@ -91,6 +107,10 @@ export interface BatchReport {
|
|
|
91
107
|
failed: string[];
|
|
92
108
|
/** Lines a previous run of this batch already covers. */
|
|
93
109
|
skipped: string[];
|
|
110
|
+
/** Lines a person denied, by alias: a decision, reported, and a subset of `failed`. */
|
|
111
|
+
denied: string[];
|
|
112
|
+
/** Present when the channel took ONE gesture for the whole list: what it approved and vetoed, by position. */
|
|
113
|
+
gesture?: BatchGesture;
|
|
94
114
|
total_minor: number;
|
|
95
115
|
total: string;
|
|
96
116
|
/** Present when the SET was refused: nothing was drafted, nothing was sent, no line moved. */
|
|
@@ -143,7 +163,7 @@ function presentedItems(batch: Batch, ctx: ToolContext): ExecutionItem[] {
|
|
|
143
163
|
* What a claim already held means for this line: skip it, or drop it and
|
|
144
164
|
* pay. `undefined` means nothing is held and the line is paid normally.
|
|
145
165
|
*/
|
|
146
|
-
function priorVerdict(prior: Execution | undefined): "already_settled" | "in_progress" | undefined {
|
|
166
|
+
function priorVerdict(prior: Execution | undefined): "already_settled" | "in_progress" | "attempt_id_conflict" | undefined {
|
|
147
167
|
// A claim naming an execution the store does not have is a claim taken by a
|
|
148
168
|
// run that died before it drafted. Nothing moved, so the line is open.
|
|
149
169
|
if (!prior) return undefined;
|
|
@@ -152,13 +172,20 @@ function priorVerdict(prior: Execution | undefined): "already_settled" | "in_pro
|
|
|
152
172
|
// for the same line is how a payee gets paid twice; the operator closes the
|
|
153
173
|
// first one with `npm run approve`, `npm run resume` or `npm run reconcile`.
|
|
154
174
|
if (!isTerminal(prior.state)) return "in_progress";
|
|
175
|
+
// Held for another payment: a retry presents the same derived id and gets the same refusal, so it is not retried.
|
|
176
|
+
if (heldForAnotherPayment(prior)) return "attempt_id_conflict";
|
|
155
177
|
// `denied`, `expired`, `failed`: the core refused it or the rail declined
|
|
156
178
|
// it, and in every one of those the money provably did not move. Retry.
|
|
157
179
|
return undefined;
|
|
158
180
|
}
|
|
159
181
|
|
|
182
|
+
function heldForAnotherPayment(execution: Execution): boolean {
|
|
183
|
+
return execution.state === "failed" && execution.outcomes.some((o) => o.held === "conflict");
|
|
184
|
+
}
|
|
185
|
+
|
|
160
186
|
function dispatchOf(execution: Execution): BatchLineReport["dispatch"] {
|
|
161
187
|
if (execution.state === "settled") return "settled";
|
|
188
|
+
if (heldForAnotherPayment(execution)) return "attempt_id_conflict";
|
|
162
189
|
if (execution.state === "awaiting_approval") return "awaiting_decision";
|
|
163
190
|
// Still `executing` after the channel ran it: the rail did not say. Never
|
|
164
191
|
// re-sent here; `npm run reconcile` is what closes it.
|
|
@@ -200,6 +227,24 @@ export async function runBatch(batch: Batch, ctx: ToolContext): Promise<BatchRep
|
|
|
200
227
|
);
|
|
201
228
|
}
|
|
202
229
|
|
|
230
|
+
// One gesture for the list, when the channel can take one (section 3, v5.3:
|
|
231
|
+
// "o humano aprova a lista, com veto por linha"). Asked after the set
|
|
232
|
+
// refusal, so a person is never asked about a list that is not going to run,
|
|
233
|
+
// and before any draft, so what they see is the list the hash covers. It
|
|
234
|
+
// decides nothing here: every line below is still drafted and still reaches
|
|
235
|
+
// `onExecution`, which is where the channel applies it line by line.
|
|
236
|
+
const gesture = ctx.onBatch
|
|
237
|
+
? await ctx.onBatch({
|
|
238
|
+
ref: batch.ref,
|
|
239
|
+
label: batch.label,
|
|
240
|
+
batch_hash: presented,
|
|
241
|
+
count: batch.lines.length,
|
|
242
|
+
total_minor: batchTotal(batch),
|
|
243
|
+
total: formatBRL(batchTotal(batch)),
|
|
244
|
+
lines: batch.lines.map((line, index) => ({ index, beneficiary: line.name, amount_minor: line.amount_minor, amount: formatBRL(line.amount_minor), status: lineStatus(batch, line, ctx) })),
|
|
245
|
+
})
|
|
246
|
+
: undefined;
|
|
247
|
+
|
|
203
248
|
for (const [index, line] of batch.lines.entries()) {
|
|
204
249
|
const key = claimKey(mandateId, batch.ref, line.alias);
|
|
205
250
|
const held = ctx.engine.claimed(key);
|
|
@@ -272,7 +317,7 @@ export async function runBatch(batch: Batch, ctx: ToolContext): Promise<BatchRep
|
|
|
272
317
|
}
|
|
273
318
|
}
|
|
274
319
|
|
|
275
|
-
return summarise(batch, presented, lines);
|
|
320
|
+
return summarise(batch, presented, lines, undefined, gesture);
|
|
276
321
|
}
|
|
277
322
|
|
|
278
323
|
/**
|
|
@@ -310,7 +355,7 @@ function setRefusal(batch: Batch, presented: string, ctx: ToolContext, setKey: s
|
|
|
310
355
|
};
|
|
311
356
|
}
|
|
312
357
|
|
|
313
|
-
function summarise(batch: Batch, presented: string, lines: BatchLineReport[], refused?: BatchSetRefusal): BatchReport {
|
|
358
|
+
function summarise(batch: Batch, presented: string, lines: BatchLineReport[], refused?: BatchSetRefusal, gesture?: BatchGesture): BatchReport {
|
|
314
359
|
const settledMinor = lines.filter((l) => l.dispatch === "settled").reduce((sum, l) => sum + l.amount_minor, 0);
|
|
315
360
|
const total = batchTotal(batch);
|
|
316
361
|
return {
|
|
@@ -321,11 +366,13 @@ function summarise(batch: Batch, presented: string, lines: BatchLineReport[], re
|
|
|
321
366
|
lines,
|
|
322
367
|
settled_minor: settledMinor,
|
|
323
368
|
settled: formatBRL(settledMinor),
|
|
324
|
-
failed: lines.filter((l) => l.dispatch === "refused" || l.dispatch === "uncertain").map((l) => l.alias),
|
|
369
|
+
failed: lines.filter((l) => l.dispatch === "refused" || l.dispatch === "uncertain" || l.dispatch === "attempt_id_conflict").map((l) => l.alias),
|
|
325
370
|
skipped: lines.filter((l) => l.dispatch === "already_settled" || l.dispatch === "in_progress").map((l) => l.alias),
|
|
371
|
+
denied: lines.filter((l) => l.state === "denied" && l.reason === "denied_by_approver").map((l) => l.alias),
|
|
326
372
|
total_minor: total,
|
|
327
373
|
total: formatBRL(total),
|
|
328
374
|
...(refused ? { refused } : {}),
|
|
375
|
+
...(gesture ? { gesture } : {}),
|
|
329
376
|
};
|
|
330
377
|
}
|
|
331
378
|
|
|
@@ -338,7 +385,7 @@ function describe(line: PayableLine, index: number): Pick<BatchLineReport, "inde
|
|
|
338
385
|
* same claim the loop reads, so what the model is told and what the loop
|
|
339
386
|
* would do cannot drift.
|
|
340
387
|
*/
|
|
341
|
-
export function lineStatus(batch: Batch, line: PayableLine, ctx: ToolContext): "open" | "already_settled" | "in_progress" {
|
|
388
|
+
export function lineStatus(batch: Batch, line: PayableLine, ctx: ToolContext): "open" | "already_settled" | "in_progress" | "attempt_id_conflict" {
|
|
342
389
|
const held = ctx.engine.claimed(claimKey(ctx.engine.mandate.id, batch.ref, line.alias));
|
|
343
390
|
if (!held) return "open";
|
|
344
391
|
return priorVerdict(ctx.engine.get(held)) ?? "open";
|