@codespar/cli 0.18.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commands/login.js +52 -12
- package/dist/commands/login.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/surface.js +31 -13
- package/dist/surface.js.map +1 -1
- package/package.json +1 -1
- package/templates/bills-agent/README.md +2 -2
- package/templates/bills-agent/agents/bills-agent/.env.example +2 -2
- package/templates/bills-agent/agents/bills-agent/README.md +25 -18
- package/templates/bills-agent/agents/bills-agent/SYSTEM_PROMPT.md +13 -3
- package/templates/bills-agent/agents/bills-agent/agent.yaml +2 -1
- package/templates/bills-agent/agents/bills-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
- package/templates/bills-agent/agents/bills-agent/evals/adversarial/false-authority.transcript.jsonl +1 -1
- package/templates/bills-agent/agents/bills-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
- package/templates/bills-agent/agents/bills-agent/mandate.example.json +1 -1
- package/templates/bills-agent/agents/bills-agent/runbook.md +1 -1
- package/templates/bills-agent/agents/bills-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +1 -1
- package/templates/bills-agent/agents/bills-agent/scenarios/cap-exceeded.transcript.jsonl +1 -1
- package/templates/bills-agent/agents/bills-agent/scenarios/escalated-above-threshold.transcript.jsonl +4 -4
- package/templates/bills-agent/agents/bills-agent/scenarios/happy-path.transcript.jsonl +1 -1
- package/templates/bills-agent/agents/bills-agent/scenarios/mandate-revoked.json +1 -1
- package/templates/bills-agent/agents/bills-agent/scenarios/mandate-revoked.transcript.jsonl +4 -4
- package/templates/bills-agent/agents/bills-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
- package/templates/bills-agent/agents/bills-agent/src/bills.ts +1 -1
- package/templates/bills-agent/agents/bills-agent/src/kit.ts +15 -28
- package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +42 -15
- package/templates/bills-agent/agents/bills-agent/src/modules/pix-out.ts +21 -3
- package/templates/bills-agent/agents/bills-agent/src/strings.ts +68 -0
- package/templates/bills-agent/agents/bills-agent/test/cli.test.ts +130 -2
- package/templates/bills-agent/agents/bills-agent/test/consent-money.test.ts +25 -0
- package/templates/bills-agent/agents/bills-agent/test/dates-and-approval.test.ts +98 -0
- package/templates/bills-agent/agents/bills-agent/test/eval-runs.test.ts +81 -0
- package/templates/bills-agent/agents/bills-agent/test/fixtures/excursao-300.transcript.jsonl +2 -2
- package/templates/bills-agent/agents/bills-agent/test/fixtures/material-400.transcript.jsonl +1 -1
- package/templates/bills-agent/agents/bills-agent/test/locale.test.ts +161 -0
- package/templates/bills-agent/agents/bills-agent/test/mandate-state.test.ts +108 -0
- package/templates/bills-agent/agents/bills-agent/test/refused-before-draft.test.ts +81 -0
- package/templates/bills-agent/agents/bills-agent/test/replay.test.ts +76 -0
- package/templates/bills-agent/agents/bills-agent/test/reply-and-outcome.test.ts +84 -0
- package/templates/bills-agent/agents/bills-agent/test/reply-language.test.ts +95 -0
- package/templates/bills-agent/agents/bills-agent/test/rerun-and-missing-transcript.test.ts +120 -0
- package/templates/bills-agent/agents/bills-agent/test/rerun-world-events.test.ts +55 -0
- package/templates/bills-agent/agents/bills-agent/tools.json +1 -1
- package/templates/bills-agent/packages/agent-core/README.md +21 -2
- package/templates/bills-agent/packages/agent-core/package.json +1 -1
- package/templates/bills-agent/packages/agent-core/src/agent.ts +33 -6
- package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
- package/templates/bills-agent/packages/agent-core/src/api/rail.ts +11 -5
- package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
- package/templates/bills-agent/packages/agent-core/src/bundle.ts +2 -0
- package/templates/bills-agent/packages/agent-core/src/channels.ts +18 -8
- package/templates/bills-agent/packages/agent-core/src/check.ts +25 -3
- package/templates/bills-agent/packages/agent-core/src/engine.ts +90 -9
- package/templates/bills-agent/packages/agent-core/src/escalate.ts +10 -0
- package/templates/bills-agent/packages/agent-core/src/index.ts +3 -0
- package/templates/bills-agent/packages/agent-core/src/language.ts +52 -0
- package/templates/bills-agent/packages/agent-core/src/locale.ts +88 -0
- package/templates/bills-agent/packages/agent-core/src/manifest.ts +4 -1
- package/templates/bills-agent/packages/agent-core/src/rail.ts +29 -1
- package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +30 -2
- package/templates/bills-agent/packages/agent-core/src/secrets.ts +53 -8
- package/templates/bills-agent/packages/agent-core/src/state-machine.ts +14 -0
- package/templates/bills-agent/packages/agent-core/src/strings.ts +175 -0
- package/templates/bills-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
- package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +7 -4
- package/templates/bills-agent/packages/agent-core/src/types.ts +15 -2
- package/templates/bills-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
- package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
- package/templates/bills-agent/packages/agent-core/test/engine.test.ts +23 -0
- package/templates/bills-agent/packages/agent-core/test/helpers.ts +1 -1
- package/templates/bills-agent/packages/agent-core/test/language.test.ts +40 -0
- package/templates/bills-agent/packages/agent-core/test/locale.test.ts +137 -0
- package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
- package/templates/bills-agent/packages/agent-core/test/manifest.test.ts +2 -2
- package/templates/bills-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
- package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
- package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +49 -0
- package/templates/bills-agent/packages/agent-core/test/recorded-turns.ts +29 -0
- package/templates/bills-agent/packages/agent-core/test/secrets.test.ts +40 -1
- package/templates/bills-agent/packages/agent-runtime/README.md +31 -4
- package/templates/bills-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
- package/templates/bills-agent/packages/agent-runtime/src/cli.ts +30 -4
- package/templates/bills-agent/packages/agent-runtime/src/commands/check.ts +18 -2
- package/templates/bills-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
- package/templates/bills-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
- package/templates/bills-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
- package/templates/bills-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
- package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
- package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
- package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +48 -17
- package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
- package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
- package/templates/bills-agent/packages/agent-runtime/src/default-kit.ts +52 -22
- package/templates/bills-agent/packages/agent-runtime/src/env.ts +64 -0
- package/templates/bills-agent/packages/agent-runtime/src/index.ts +2 -0
- package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +81 -10
- package/templates/bills-agent/packages/agent-runtime/src/kit.ts +34 -12
- package/templates/bills-agent/packages/agent-runtime/src/outcome.ts +118 -0
- package/templates/bills-agent/packages/agent-runtime/src/poll.ts +7 -7
- package/templates/bills-agent/packages/agent-runtime/src/runs.ts +51 -0
- package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +55 -8
- package/templates/bills-agent/packages/agent-runtime/src/setup.ts +75 -5
- package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +71 -33
- package/templates/bills-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
- package/templates/bills-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
- package/templates/bills-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
- package/templates/bills-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
- package/templates/bills-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
- package/templates/bills-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
- package/templates/bills-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
- package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
- package/templates/checkout-agent/README.md +2 -2
- package/templates/checkout-agent/agents/checkout-agent/.env.example +6 -4
- package/templates/checkout-agent/agents/checkout-agent/README.md +16 -8
- package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +17 -7
- package/templates/checkout-agent/agents/checkout-agent/agent.yaml +4 -3
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +2 -2
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +48 -4
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/package.json +1 -1
- package/templates/checkout-agent/agents/checkout-agent/runbook.md +4 -4
- package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +2 -2
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +2 -2
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +2 -2
- package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +1 -1
- package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +2 -2
- package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +2 -2
- package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +3 -3
- package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +1 -1
- package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +2 -2
- package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +7 -7
- package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +47 -43
- package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +20 -12
- package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +4 -4
- package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +9 -6
- package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +44 -41
- package/templates/checkout-agent/agents/checkout-agent/src/strings.ts +179 -0
- package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +4 -1
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +2 -2
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +1 -1
- package/templates/checkout-agent/agents/checkout-agent/test/payable-instrument.test.ts +58 -0
- package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +2 -2
- package/templates/checkout-agent/agents/checkout-agent/test/rerun-unpaid.test.ts +47 -0
- package/templates/checkout-agent/agents/checkout-agent/test/scenario-reach-and-seconds.test.ts +55 -0
- package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +26 -10
- package/templates/checkout-agent/agents/checkout-agent/tools.json +1 -1
- package/templates/checkout-agent/packages/agent-core/README.md +21 -2
- package/templates/checkout-agent/packages/agent-core/package.json +1 -1
- package/templates/checkout-agent/packages/agent-core/src/agent.ts +33 -6
- package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
- package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +11 -5
- package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
- package/templates/checkout-agent/packages/agent-core/src/bundle.ts +2 -0
- package/templates/checkout-agent/packages/agent-core/src/channels.ts +18 -8
- package/templates/checkout-agent/packages/agent-core/src/check.ts +25 -3
- package/templates/checkout-agent/packages/agent-core/src/engine.ts +90 -9
- package/templates/checkout-agent/packages/agent-core/src/escalate.ts +10 -0
- package/templates/checkout-agent/packages/agent-core/src/index.ts +3 -0
- package/templates/checkout-agent/packages/agent-core/src/language.ts +52 -0
- package/templates/checkout-agent/packages/agent-core/src/locale.ts +88 -0
- package/templates/checkout-agent/packages/agent-core/src/manifest.ts +4 -1
- package/templates/checkout-agent/packages/agent-core/src/rail.ts +29 -1
- package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +30 -2
- package/templates/checkout-agent/packages/agent-core/src/secrets.ts +53 -8
- package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +14 -0
- package/templates/checkout-agent/packages/agent-core/src/strings.ts +175 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
- package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +7 -4
- package/templates/checkout-agent/packages/agent-core/src/types.ts +15 -2
- package/templates/checkout-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
- package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
- package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +23 -0
- package/templates/checkout-agent/packages/agent-core/test/helpers.ts +1 -1
- package/templates/checkout-agent/packages/agent-core/test/language.test.ts +40 -0
- package/templates/checkout-agent/packages/agent-core/test/locale.test.ts +137 -0
- package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
- package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +2 -2
- package/templates/checkout-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
- package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +49 -0
- package/templates/checkout-agent/packages/agent-core/test/recorded-turns.ts +29 -0
- package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +40 -1
- package/templates/checkout-agent/packages/agent-runtime/README.md +31 -4
- package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
- package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +30 -4
- package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +18 -2
- package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
- package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
- package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
- package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
- package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
- package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
- package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
- package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
- package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +48 -17
- package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
- package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
- package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +52 -22
- package/templates/checkout-agent/packages/agent-runtime/src/env.ts +64 -0
- package/templates/checkout-agent/packages/agent-runtime/src/index.ts +2 -0
- package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +81 -10
- package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +34 -12
- package/templates/checkout-agent/packages/agent-runtime/src/outcome.ts +118 -0
- package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +7 -7
- package/templates/checkout-agent/packages/agent-runtime/src/runs.ts +51 -0
- package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +55 -8
- package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +75 -5
- package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +71 -33
- package/templates/checkout-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
- package/templates/checkout-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
- package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
- package/templates/checkout-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
- package/templates/checkout-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
- package/templates/checkout-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
- package/templates/checkout-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
- package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
- package/templates/collections-agent/README.md +2 -2
- package/templates/collections-agent/agents/collections-agent/.env.example +6 -3
- package/templates/collections-agent/agents/collections-agent/README.md +14 -9
- package/templates/collections-agent/agents/collections-agent/SYSTEM_PROMPT.md +16 -5
- package/templates/collections-agent/agents/collections-agent/agent.yaml +4 -3
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +21 -4
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +49 -5
- package/templates/collections-agent/agents/collections-agent/mandate.example.json +1 -1
- package/templates/collections-agent/agents/collections-agent/runbook.md +3 -3
- package/templates/collections-agent/agents/collections-agent/src/agreements.ts +1 -1
- package/templates/collections-agent/agents/collections-agent/src/envelope.ts +15 -13
- package/templates/collections-agent/agents/collections-agent/src/kit.ts +38 -35
- package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +16 -8
- package/templates/collections-agent/agents/collections-agent/src/strings.ts +91 -0
- package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +2 -2
- package/templates/collections-agent/agents/collections-agent/test/check.test.ts +4 -1
- package/templates/collections-agent/agents/collections-agent/test/cli.test.ts +1 -1
- package/templates/collections-agent/agents/collections-agent/test/envelope.test.ts +2 -2
- package/templates/collections-agent/agents/collections-agent/test/fixtures/accept-1103.transcript.jsonl +1 -1
- package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +1 -1
- package/templates/collections-agent/agents/collections-agent/test/payable-instrument.test.ts +56 -0
- package/templates/collections-agent/agents/collections-agent/test/rerun-clock.test.ts +54 -0
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +69 -2
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +16 -3
- package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +16 -4
- package/templates/collections-agent/agents/collections-agent/tools.json +2 -2
- package/templates/collections-agent/packages/agent-core/README.md +21 -2
- package/templates/collections-agent/packages/agent-core/package.json +1 -1
- package/templates/collections-agent/packages/agent-core/src/agent.ts +33 -6
- package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
- package/templates/collections-agent/packages/agent-core/src/api/rail.ts +11 -5
- package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
- package/templates/collections-agent/packages/agent-core/src/bundle.ts +2 -0
- package/templates/collections-agent/packages/agent-core/src/channels.ts +18 -8
- package/templates/collections-agent/packages/agent-core/src/check.ts +25 -3
- package/templates/collections-agent/packages/agent-core/src/engine.ts +90 -9
- package/templates/collections-agent/packages/agent-core/src/escalate.ts +10 -0
- package/templates/collections-agent/packages/agent-core/src/index.ts +3 -0
- package/templates/collections-agent/packages/agent-core/src/language.ts +52 -0
- package/templates/collections-agent/packages/agent-core/src/locale.ts +88 -0
- package/templates/collections-agent/packages/agent-core/src/manifest.ts +4 -1
- package/templates/collections-agent/packages/agent-core/src/rail.ts +29 -1
- package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +30 -2
- package/templates/collections-agent/packages/agent-core/src/secrets.ts +53 -8
- package/templates/collections-agent/packages/agent-core/src/state-machine.ts +14 -0
- package/templates/collections-agent/packages/agent-core/src/strings.ts +175 -0
- package/templates/collections-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
- package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +7 -4
- package/templates/collections-agent/packages/agent-core/src/types.ts +15 -2
- package/templates/collections-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
- package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
- package/templates/collections-agent/packages/agent-core/test/engine.test.ts +23 -0
- package/templates/collections-agent/packages/agent-core/test/helpers.ts +1 -1
- package/templates/collections-agent/packages/agent-core/test/language.test.ts +40 -0
- package/templates/collections-agent/packages/agent-core/test/locale.test.ts +137 -0
- package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
- package/templates/collections-agent/packages/agent-core/test/manifest.test.ts +2 -2
- package/templates/collections-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
- package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
- package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +49 -0
- package/templates/collections-agent/packages/agent-core/test/recorded-turns.ts +29 -0
- package/templates/collections-agent/packages/agent-core/test/secrets.test.ts +40 -1
- package/templates/collections-agent/packages/agent-runtime/README.md +31 -4
- package/templates/collections-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
- package/templates/collections-agent/packages/agent-runtime/src/cli.ts +30 -4
- package/templates/collections-agent/packages/agent-runtime/src/commands/check.ts +18 -2
- package/templates/collections-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
- package/templates/collections-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
- package/templates/collections-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
- package/templates/collections-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
- package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
- package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
- package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +48 -17
- package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
- package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
- package/templates/collections-agent/packages/agent-runtime/src/default-kit.ts +52 -22
- package/templates/collections-agent/packages/agent-runtime/src/env.ts +64 -0
- package/templates/collections-agent/packages/agent-runtime/src/index.ts +2 -0
- package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +81 -10
- package/templates/collections-agent/packages/agent-runtime/src/kit.ts +34 -12
- package/templates/collections-agent/packages/agent-runtime/src/outcome.ts +118 -0
- package/templates/collections-agent/packages/agent-runtime/src/poll.ts +7 -7
- package/templates/collections-agent/packages/agent-runtime/src/runs.ts +51 -0
- package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +55 -8
- package/templates/collections-agent/packages/agent-runtime/src/setup.ts +75 -5
- package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +71 -33
- package/templates/collections-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
- package/templates/collections-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
- package/templates/collections-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
- package/templates/collections-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
- package/templates/collections-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
- package/templates/collections-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
- package/templates/collections-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
- package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
- package/templates/hello-agent/README.md +2 -2
- package/templates/hello-agent/agents/hello-agent/README.md +6 -0
- package/templates/hello-agent/agents/hello-agent/SYSTEM_PROMPT.md +27 -13
- package/templates/hello-agent/agents/hello-agent/agent.yaml +2 -1
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
- package/templates/hello-agent/agents/hello-agent/mandate.example.json +1 -1
- package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.json +1 -1
- package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.transcript.jsonl +1 -1
- package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.json +1 -1
- package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
- package/templates/hello-agent/agents/hello-agent/src/bills.ts +9 -5
- package/templates/hello-agent/agents/hello-agent/src/kit.ts +4 -1
- package/templates/hello-agent/agents/hello-agent/tools.json +1 -1
- package/templates/hello-agent/packages/agent-core/README.md +21 -2
- package/templates/hello-agent/packages/agent-core/package.json +1 -1
- package/templates/hello-agent/packages/agent-core/src/agent.ts +33 -6
- package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
- package/templates/hello-agent/packages/agent-core/src/api/rail.ts +11 -5
- package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
- package/templates/hello-agent/packages/agent-core/src/bundle.ts +2 -0
- package/templates/hello-agent/packages/agent-core/src/channels.ts +18 -8
- package/templates/hello-agent/packages/agent-core/src/check.ts +25 -3
- package/templates/hello-agent/packages/agent-core/src/engine.ts +90 -9
- package/templates/hello-agent/packages/agent-core/src/escalate.ts +10 -0
- package/templates/hello-agent/packages/agent-core/src/index.ts +3 -0
- package/templates/hello-agent/packages/agent-core/src/language.ts +52 -0
- package/templates/hello-agent/packages/agent-core/src/locale.ts +88 -0
- package/templates/hello-agent/packages/agent-core/src/manifest.ts +4 -1
- package/templates/hello-agent/packages/agent-core/src/rail.ts +29 -1
- package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +30 -2
- package/templates/hello-agent/packages/agent-core/src/secrets.ts +53 -8
- package/templates/hello-agent/packages/agent-core/src/state-machine.ts +14 -0
- package/templates/hello-agent/packages/agent-core/src/strings.ts +175 -0
- package/templates/hello-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
- package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +7 -4
- package/templates/hello-agent/packages/agent-core/src/types.ts +15 -2
- package/templates/hello-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
- package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
- package/templates/hello-agent/packages/agent-core/test/engine.test.ts +23 -0
- package/templates/hello-agent/packages/agent-core/test/helpers.ts +1 -1
- package/templates/hello-agent/packages/agent-core/test/language.test.ts +40 -0
- package/templates/hello-agent/packages/agent-core/test/locale.test.ts +137 -0
- package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
- package/templates/hello-agent/packages/agent-core/test/manifest.test.ts +2 -2
- package/templates/hello-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
- package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
- package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +49 -0
- package/templates/hello-agent/packages/agent-core/test/recorded-turns.ts +29 -0
- package/templates/hello-agent/packages/agent-core/test/secrets.test.ts +40 -1
- package/templates/hello-agent/packages/agent-runtime/README.md +31 -4
- package/templates/hello-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
- package/templates/hello-agent/packages/agent-runtime/src/cli.ts +30 -4
- package/templates/hello-agent/packages/agent-runtime/src/commands/check.ts +18 -2
- package/templates/hello-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
- package/templates/hello-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
- package/templates/hello-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
- package/templates/hello-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
- package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
- package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
- package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +48 -17
- package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
- package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
- package/templates/hello-agent/packages/agent-runtime/src/default-kit.ts +52 -22
- package/templates/hello-agent/packages/agent-runtime/src/env.ts +64 -0
- package/templates/hello-agent/packages/agent-runtime/src/index.ts +2 -0
- package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +81 -10
- package/templates/hello-agent/packages/agent-runtime/src/kit.ts +34 -12
- package/templates/hello-agent/packages/agent-runtime/src/outcome.ts +118 -0
- package/templates/hello-agent/packages/agent-runtime/src/poll.ts +7 -7
- package/templates/hello-agent/packages/agent-runtime/src/runs.ts +51 -0
- package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +55 -8
- package/templates/hello-agent/packages/agent-runtime/src/setup.ts +75 -5
- package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +71 -33
- package/templates/hello-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
- package/templates/hello-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
- package/templates/hello-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
- package/templates/hello-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
- package/templates/hello-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
- package/templates/hello-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
- package/templates/hello-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
- package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
- package/templates/kits.lock.json +12 -12
- package/templates/supplier-payments-agent/README.md +2 -2
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/.env.example +2 -2
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +12 -7
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/SYSTEM_PROMPT.md +14 -3
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/agent.yaml +2 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.transcript.jsonl +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.transcript.jsonl +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.transcript.jsonl +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/mandate.example.json +3 -3
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.transcript.jsonl +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.transcript.jsonl +5 -5
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.transcript.jsonl +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.transcript.jsonl +5 -5
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.json +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.transcript.jsonl +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.transcript.jsonl +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +37 -26
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +41 -14
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/pix-out.ts +12 -3
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/payables.ts +8 -8
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +75 -10
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/locale.test.ts +101 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/run-outcome.test.ts +127 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/tools.json +1 -1
- package/templates/supplier-payments-agent/packages/agent-core/README.md +21 -2
- package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +33 -6
- package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +15 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +11 -5
- package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +36 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +2 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +18 -8
- package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +90 -9
- package/templates/supplier-payments-agent/packages/agent-core/src/escalate.ts +10 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +3 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/language.ts +52 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/locale.ts +88 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/manifest.ts +4 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +29 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +30 -2
- package/templates/supplier-payments-agent/packages/agent-core/src/secrets.ts +53 -8
- package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +14 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/strings.ts +175 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/charge-rail.ts +15 -2
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +7 -4
- package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +15 -2
- package/templates/supplier-payments-agent/packages/agent-core/test/actor-on-the-wire.test.ts +144 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +2 -2
- package/templates/supplier-payments-agent/packages/agent-core/test/engine.test.ts +23 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +1 -1
- package/templates/supplier-payments-agent/packages/agent-core/test/language.test.ts +40 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/locale.test.ts +137 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +2 -2
- package/templates/supplier-payments-agent/packages/agent-core/test/manifest.test.ts +2 -2
- package/templates/supplier-payments-agent/packages/agent-core/test/rail-errors.test.ts +74 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +32 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +49 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/recorded-turns.ts +29 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/secrets.test.ts +40 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/README.md +31 -4
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/rules.ts +4 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +11 -5
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +43 -21
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +3 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +8 -12
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -4
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +12 -9
- package/templates/supplier-payments-agent/packages/agent-runtime/src/cli.ts +30 -4
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/check.ts +18 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/decide.ts +13 -4
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/eval.ts +7 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/inspect.ts +11 -13
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +32 -24
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +12 -5
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/rerun.ts +83 -10
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +2 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +10 -5
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +48 -17
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +37 -12
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +11 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/default-kit.ts +52 -22
- package/templates/supplier-payments-agent/packages/agent-runtime/src/env.ts +64 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +2 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +81 -10
- package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +34 -12
- package/templates/supplier-payments-agent/packages/agent-runtime/src/outcome.ts +118 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/poll.ts +7 -7
- package/templates/supplier-payments-agent/packages/agent-runtime/src/runs.ts +51 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +55 -8
- package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +75 -5
- package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +71 -33
- package/templates/supplier-payments-agent/packages/agent-runtime/test/env-names.test.ts +56 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect-mandate-status.test.ts +80 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect.test.ts +63 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/test/outcome.test.ts +72 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/poll-payer.test.ts +64 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/rerun-not-replayed.test.ts +46 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/transition-line.test.ts +29 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +99 -6
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +8 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +77 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +27 -1
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
*/
|
|
54
54
|
import { createPublicKey, verify as verifyDetached } from "node:crypto";
|
|
55
55
|
import { itemsHash } from "./hash.js";
|
|
56
|
-
import { readChainRecipe, readSealedApproval, recomputeChain, sameApprovalHash, type SealedApproval } from "./receipt-chain.js";
|
|
56
|
+
import { readChainRecipe, readSealedApproval, recomputeChain, sameApprovalHash, type ChainRecipe, type SealedApproval } from "./receipt-chain.js";
|
|
57
57
|
import type { ExecutionItem } from "./types.js";
|
|
58
58
|
|
|
59
59
|
/** The domain tag. A receipt names the version it was sealed under through
|
|
@@ -123,6 +123,16 @@ export interface ChainCheck {
|
|
|
123
123
|
reason: string;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Whether the sealed payment was a sandbox one and whether money moved, read off a body that recomputed to the signed chain. A field
|
|
128
|
+
* is `null` when the recipe's payment link for that version does not seal it: a value the chain does not cover is not reported as one
|
|
129
|
+
* it does. Only these two, so a verdict never carries the rest of the read (the tenant's read also carries `mandate.sig`).
|
|
130
|
+
*/
|
|
131
|
+
export interface SealedPayment {
|
|
132
|
+
sandbox: boolean | null;
|
|
133
|
+
money_moved: boolean | null;
|
|
134
|
+
}
|
|
135
|
+
|
|
126
136
|
/** What the sealed approval link was held against. Present on a v4 chain that recomputed. */
|
|
127
137
|
export interface ApprovalLinkCheck {
|
|
128
138
|
/** `matched`: the artifact's hashes are the sealed ones. `mismatch`: they are not. `not_compared`: no artifact was given. */
|
|
@@ -161,6 +171,8 @@ export interface ReceiptVerification {
|
|
|
161
171
|
/** The body check, when one was attempted: only `verifyReceiptRead` attempts it, and only on a signature that verified. */
|
|
162
172
|
chain_check?: ChainCheck;
|
|
163
173
|
approval_check?: ApprovalLinkCheck;
|
|
174
|
+
/** Present exactly when the body recomputed to the signed chain. */
|
|
175
|
+
sealed_payment?: SealedPayment;
|
|
164
176
|
}
|
|
165
177
|
|
|
166
178
|
/** One published key, RFC 8037 shape, as the JWKS serves it. */
|
|
@@ -531,12 +543,13 @@ export function verifyReceiptRead(read: unknown, keyDocument: unknown, options:
|
|
|
531
543
|
};
|
|
532
544
|
}
|
|
533
545
|
const chainCheck: ChainCheck = { status: "recomputed", version: recomputed.version, recomputed: recomputed.chain, reason: "body_recomputes" };
|
|
546
|
+
const sealedPayment = sealedPaymentOf(read, recipe.recipe, recomputed.version);
|
|
534
547
|
const bound = `the body recomputes to the signed chain under the published recipe (v${recomputed.version})`;
|
|
535
548
|
|
|
536
549
|
const sealed = readSealedApproval(read);
|
|
537
550
|
if (!sealed) {
|
|
538
551
|
// Unreachable through the published recipe (v4 seals the link, and a read without it fails the recomputation above), kept so a future version is not misread.
|
|
539
|
-
return { ...signature, reason: "body_recomputes", message: `receipt ${signature.receipt_id} was sealed by CodeSpar and ${bound}; it seals no approval link`, chain_check: chainCheck };
|
|
552
|
+
return { ...signature, reason: "body_recomputes", message: `receipt ${signature.receipt_id} was sealed by CodeSpar and ${bound}; it seals no approval link`, chain_check: chainCheck, sealed_payment: sealedPayment };
|
|
540
553
|
}
|
|
541
554
|
const approval = options.approval;
|
|
542
555
|
if (!approval) {
|
|
@@ -546,6 +559,7 @@ export function verifyReceiptRead(read: unknown, keyDocument: unknown, options:
|
|
|
546
559
|
message: `receipt ${signature.receipt_id} was sealed by CodeSpar and ${bound}. It seals the approval ${sealed.items_hash}${sealed.batch_hash ? ` (batch ${sealed.batch_hash})` : ""}; no approval artifact was given to hold it against`,
|
|
547
560
|
chain_check: chainCheck,
|
|
548
561
|
approval_check: { status: "not_compared", sealed, artifact: null, reason: "no_artifact" },
|
|
562
|
+
sealed_payment: sealedPayment,
|
|
549
563
|
};
|
|
550
564
|
}
|
|
551
565
|
|
|
@@ -557,6 +571,7 @@ export function verifyReceiptRead(read: unknown, keyDocument: unknown, options:
|
|
|
557
571
|
message: `receipt ${signature.receipt_id} was sealed by CodeSpar and ${bound}, and ${why}: this payment was not sealed against the list artifact ${approval.approval_id} approved`,
|
|
558
572
|
chain_check: chainCheck,
|
|
559
573
|
approval_check: { status: "mismatch", sealed, artifact, reason },
|
|
574
|
+
sealed_payment: sealedPayment,
|
|
560
575
|
});
|
|
561
576
|
if (approval.items && itemsHash(approval.items) !== approval.items_hash) {
|
|
562
577
|
return mismatch("artifact_items_hash_inconsistent", `the artifact's items_hash ${approval.items_hash} is not the hash of the items it lists (${itemsHash(approval.items)})`);
|
|
@@ -573,5 +588,18 @@ export function verifyReceiptRead(read: unknown, keyDocument: unknown, options:
|
|
|
573
588
|
message: `receipt ${signature.receipt_id} was sealed by CodeSpar, ${bound}, and it was paid against the list artifact ${approval.approval_id} approved (${sealed.items_hash}${sealed.batch_hash ? `, batch ${sealed.batch_hash}` : ""}). WHO approved it is in the artifact, under a local key, and nowhere in the seal`,
|
|
574
589
|
chain_check: chainCheck,
|
|
575
590
|
approval_check: { status: "matched", sealed, artifact, reason: "approval_matches" },
|
|
591
|
+
sealed_payment: sealedPayment,
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function sealedPaymentOf(read: Record<string, unknown>, recipe: ChainRecipe, version: number): SealedPayment {
|
|
596
|
+
const spec = recipe.links.find((l) => l.from === "payment");
|
|
597
|
+
const fields = spec?.fields ?? spec?.fields_by_version?.[String(version)] ?? [];
|
|
598
|
+
const payment = read["payment"];
|
|
599
|
+
const pick = (name: string): boolean | null => {
|
|
600
|
+
if (!fields.includes(name) && !fields.includes(`${name}${recipe.optional_marker}`)) return null;
|
|
601
|
+
const value = isRecord(payment) ? payment[name] : undefined;
|
|
602
|
+
return typeof value === "boolean" ? value : null;
|
|
576
603
|
};
|
|
604
|
+
return { sandbox: pick("sandbox"), money_moved: pick("money_moved") };
|
|
577
605
|
}
|
|
@@ -1,26 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sandbox by construction: a key outside the `csk_test_` pattern fails
|
|
3
3
|
* before any network call. Nothing in this repository ever holds a live key.
|
|
4
|
+
*
|
|
5
|
+
* Three different mistakes land here and each has its own fix, so each has
|
|
6
|
+
* its own message (#50): no key at all (the `.env` was never copied, or sits
|
|
7
|
+
* where the agent does not read it), the `.env.example` placeholder left in
|
|
8
|
+
* place, and a key that is not a test key. Telling a newcomer who has no key
|
|
9
|
+
* that their key is LIVE sends them looking for the wrong thing.
|
|
4
10
|
*/
|
|
5
11
|
export const TEST_KEY_PREFIX = "csk_test_";
|
|
6
12
|
|
|
13
|
+
/** The value `.env.example` ships with. It has the test prefix and is nobody's key. */
|
|
14
|
+
export const CODESPAR_KEY_PLACEHOLDER = "csk_test_your_key_here";
|
|
15
|
+
|
|
16
|
+
/** Why a `CODESPAR_API_KEY` cannot be used: absent or empty, the placeholder, or not a test key. */
|
|
17
|
+
export type TestKeyProblem = "missing" | "placeholder" | "not_test";
|
|
18
|
+
|
|
19
|
+
export function testKeyProblem(key: string | undefined): TestKeyProblem | undefined {
|
|
20
|
+
const value = key?.trim() ?? "";
|
|
21
|
+
if (value === "") return "missing";
|
|
22
|
+
if (value === CODESPAR_KEY_PLACEHOLDER) return "placeholder";
|
|
23
|
+
if (!value.startsWith(TEST_KEY_PREFIX) || value.length <= TEST_KEY_PREFIX.length) return "not_test";
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `envFile` is the `.env` the agent actually reads, as the runner resolves it
|
|
29
|
+
* (`agents/<agent>/.env`, relative to where the command ran). The core does
|
|
30
|
+
* not know which agent it runs for, so without it the message names the
|
|
31
|
+
* pattern instead of the path.
|
|
32
|
+
*/
|
|
33
|
+
export function testKeyMessage(problem: TestKeyProblem, envFile?: string): string {
|
|
34
|
+
const file = envFile ?? "agents/<agent>/.env";
|
|
35
|
+
switch (problem) {
|
|
36
|
+
case "missing":
|
|
37
|
+
return (
|
|
38
|
+
`CODESPAR_API_KEY is not set: copy ${file}.example to ${file} and paste a ${TEST_KEY_PREFIX} key from https://codespar.dev/auth/signup. ` +
|
|
39
|
+
`${file} is the file this agent reads; a .env anywhere else, the repository root included, is not read.`
|
|
40
|
+
);
|
|
41
|
+
case "placeholder":
|
|
42
|
+
return `CODESPAR_API_KEY is still the placeholder from .env.example (${CODESPAR_KEY_PLACEHOLDER}): replace it in ${file} with your own ${TEST_KEY_PREFIX} key from https://codespar.dev/auth/signup.`;
|
|
43
|
+
case "not_test":
|
|
44
|
+
return (
|
|
45
|
+
`CODESPAR_API_KEY must be a test key (prefix "${TEST_KEY_PREFIX}"). ` +
|
|
46
|
+
`This kit runs in the sandbox only; a live key is refused before any call is made. It is read from the environment, or from ${file}.`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
7
51
|
export class NotATestKeyError extends Error {
|
|
8
|
-
constructor(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
);
|
|
52
|
+
constructor(
|
|
53
|
+
readonly problem: TestKeyProblem = "not_test",
|
|
54
|
+
readonly envFile?: string,
|
|
55
|
+
) {
|
|
56
|
+
super(testKeyMessage(problem, envFile));
|
|
13
57
|
this.name = "NotATestKeyError";
|
|
14
58
|
}
|
|
15
59
|
}
|
|
16
60
|
|
|
17
61
|
export function isTestKey(key: string | undefined): key is string {
|
|
18
|
-
return
|
|
62
|
+
return testKeyProblem(key) === undefined;
|
|
19
63
|
}
|
|
20
64
|
|
|
21
65
|
export function assertTestKey(key: string | undefined): string {
|
|
22
|
-
|
|
23
|
-
|
|
66
|
+
const problem = testKeyProblem(key);
|
|
67
|
+
if (problem) throw new NotATestKeyError(problem);
|
|
68
|
+
return key!.trim();
|
|
24
69
|
}
|
|
25
70
|
|
|
26
71
|
/** For logs and bundles: the prefix and the last four characters, nothing else. */
|
|
@@ -62,6 +62,13 @@ export interface TransitionRecord {
|
|
|
62
62
|
actor: Actor;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/** The code and message a rail answered for one attempt, as the rail gave them. */
|
|
66
|
+
export interface RailAnswer {
|
|
67
|
+
attempt_id: string;
|
|
68
|
+
code: string;
|
|
69
|
+
message: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
export interface Execution<S extends ExecutionState = ExecutionState> {
|
|
66
73
|
id: string;
|
|
67
74
|
run_id: string;
|
|
@@ -97,6 +104,13 @@ export interface Execution<S extends ExecutionState = ExecutionState> {
|
|
|
97
104
|
reason?: ExecutionReason;
|
|
98
105
|
detail?: string;
|
|
99
106
|
outcomes: ItemOutcome[];
|
|
107
|
+
/**
|
|
108
|
+
* What the rail answered, verbatim, for attempts whose outcome it left
|
|
109
|
+
* UNKNOWN (a timeout, a 5xx such as `provider_error`): no outcome exists for
|
|
110
|
+
* them, so their code and message would otherwise live only in the event
|
|
111
|
+
* log. An entry whose attempt later gains an outcome is superseded by it.
|
|
112
|
+
*/
|
|
113
|
+
uncertain_answers?: RailAnswer[];
|
|
100
114
|
history: TransitionRecord[];
|
|
101
115
|
created_at: string;
|
|
102
116
|
updated_at: string;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The strings every agent shares, in each locale: the approval path's lines
|
|
3
|
+
* (the batch gesture, the per-line question's hint, the waits), the words of
|
|
4
|
+
* an execution's description, how a payable instrument is shown, and the
|
|
5
|
+
* operator's lines on the WhatsApp channel. A kit's own words are in its
|
|
6
|
+
* kit's `strings`.
|
|
7
|
+
*
|
|
8
|
+
* DISPLAY ONLY. What a person may ANSWER is not here and does not depend on
|
|
9
|
+
* the locale: `[s/N]` takes s, sim, y and yes in both, and the batch gesture
|
|
10
|
+
* takes todas / all, todas exceto / all except, nenhuma / none in both
|
|
11
|
+
* (`parseBatchGesture`). A prompt shown in English and answered in Portuguese
|
|
12
|
+
* decides exactly what the same answer decides under a Portuguese prompt.
|
|
13
|
+
*/
|
|
14
|
+
import type { LocaleTable } from "./locale.js";
|
|
15
|
+
|
|
16
|
+
const pt = {
|
|
17
|
+
// How an execution reads on the console.
|
|
18
|
+
execution: "execução",
|
|
19
|
+
totalByCore: "total (calculado pelo core)",
|
|
20
|
+
escalatedBy: "escalado por",
|
|
21
|
+
blocked: (reasons: string, notAuthorized: string) => `bloqueado: ${reasons} — ${notAuthorized}; não há o que aprovar`,
|
|
22
|
+
replayedSettlement: "já estava pago: a API respondeu com um pagamento anterior desta mesma tentativa; esta execução não moveu dinheiro",
|
|
23
|
+
|
|
24
|
+
// The approval path at the keyboard.
|
|
25
|
+
onlyDeny: " (o único desfecho possível é negar; aperte Enter)",
|
|
26
|
+
leftAwaiting: " deixado em awaiting_approval (rode de novo com --approve ou --deny)",
|
|
27
|
+
afterLooks: (rounds: number, seconds: number, timedOut: boolean) =>
|
|
28
|
+
` após ${rounds} consulta(s), ${seconds}s${timedOut ? " (tempo esgotado; `npm run poll` continua de onde parou)" : ""}`,
|
|
29
|
+
banner: (name: string, version: string, mode: string, rail: string, mandateWord: string, mandateId: string) => `${name} ${version} — approval: ${mode} — trilho: ${rail} — ${mandateWord} ${mandateId}`,
|
|
30
|
+
bundleAt: (runId: string, dir: string) => `run ${runId} — bundle em ${dir}`,
|
|
31
|
+
|
|
32
|
+
// The batch gesture: one question for a whole list.
|
|
33
|
+
batchHeader: (ref: string, label: string, count: number, total: string, hash: string) => ` lote ${ref} — ${label}: ${count} linha(s), total ${total}, batch_hash ${hash}…`,
|
|
34
|
+
batchAlreadySettled: " (já paga; não roda de novo)",
|
|
35
|
+
batchInProgress: " (em andamento; não roda de novo)",
|
|
36
|
+
batchAttemptConflict: " (tentativa presa a outro pagamento; não roda de novo)",
|
|
37
|
+
batchQuestion: " Aprovar a lista? [todas / todas exceto 3,7 / nenhuma] ",
|
|
38
|
+
batchUnreadable: (count: number) => ` não entendi; responda todas, todas exceto <números de 1 a ${count}> ou nenhuma`,
|
|
39
|
+
batchApprovedAll: " -> lista aprovada inteira",
|
|
40
|
+
batchDeniedAll: " -> lista negada inteira",
|
|
41
|
+
batchApprovedExcept: (positions: string) => ` -> lista aprovada exceto ${positions}`,
|
|
42
|
+
|
|
43
|
+
// A payable instrument, on the terminal and on WhatsApp.
|
|
44
|
+
instalmentOf: (instalment: number, count: number) => `Parcela ${instalment}/${count}: `,
|
|
45
|
+
dueOn: (date: string) => `, vence ${date}`,
|
|
46
|
+
chargeRef: (chargeId: string) => ` — cobrança ${chargeId}`,
|
|
47
|
+
pixCopyPaste: "Pix copia e cola:",
|
|
48
|
+
boletoLine: "Ou pelo boleto, linha digitável:",
|
|
49
|
+
|
|
50
|
+
// The collection-hours rule: the envelope refuses to draft outside it, the channel refuses to speak.
|
|
51
|
+
outsideCollectionHours: (window: string, timezone: string, clock: string) => `fora do horário de cobrança (${window} ${timezone}); agora são ${clock}`,
|
|
52
|
+
|
|
53
|
+
// The scenario runner.
|
|
54
|
+
scenarioReply: (text: string) => `agente: ${text}`,
|
|
55
|
+
scenarioOk: (cycleSeconds: number | null | undefined, dir: string) =>
|
|
56
|
+
`== ok — ${cycleSeconds === undefined ? "" : cycleSeconds === null ? "sem ciclo medido — " : `${cycleSeconds}s — `}bundle em ${dir}`,
|
|
57
|
+
|
|
58
|
+
// The WhatsApp channel, on the operator's console. None of these reach the person.
|
|
59
|
+
waBanner: (name: string, version: string, backend: string, mode: string, rail: string, mandateWord: string, mandateId: string) =>
|
|
60
|
+
`${name} ${version} — canal: whatsapp (${backend}) — approval: ${mode} — trilho: ${rail} — ${mandateWord} ${mandateId}`,
|
|
61
|
+
waTapIgnored: (rule: string, detail: string) => ` [whatsapp] toque ignorado (${rule}): ${detail}`,
|
|
62
|
+
waRefused: (rule: string, detail: string) => ` [whatsapp] recusado (${rule}): ${detail}`,
|
|
63
|
+
waRefusedByBackend: (rule: string, detail: string) => ` [whatsapp] recusado pelo backend (${rule}): ${detail}`,
|
|
64
|
+
waDeliveryFailed: (messageId: string, codes: string, told: string | undefined) =>
|
|
65
|
+
` [operador] ENTREGA FALHOU da mensagem ${messageId} (${codes})${told ? ` — era o aviso de ${told}: a pessoa NÃO foi avisada` : ""}`,
|
|
66
|
+
waShop: "loja:",
|
|
67
|
+
waTap: (title: string) => `[toque: ${title}]`,
|
|
68
|
+
waTapIgnoredMark: (rule: string) => ` (ignorado: ${rule})`,
|
|
69
|
+
waNotSent: (rule: string) => `(não enviada: ${rule})`,
|
|
70
|
+
waCopyPaste: "[copia e cola]",
|
|
71
|
+
waQrImage: "[imagem: QR Pix]",
|
|
72
|
+
waYou: " │ você> ",
|
|
73
|
+
waEmulatorOpen: " ┌─ WhatsApp (dyvit-wa-sim, emulador local da Cloud API — sem rede externa, sem conta Meta)",
|
|
74
|
+
waEmulatorClosed: " └─ fim da conversa",
|
|
75
|
+
waConversationSummary: (log: string, received: number | undefined, sent: number, refused: number) =>
|
|
76
|
+
`conversa em ${log} — ${received === undefined ? "" : `${received} recebida(s), `}${sent} enviada(s)${refused ? `, ${refused} recusada(s)` : ""}`,
|
|
77
|
+
waSimulatedCost: (total: string, currency: string) => `custo simulado desta conversa nas regras da Meta: ${total} ${currency} (conta do emulador, não nossa)`,
|
|
78
|
+
|
|
79
|
+
// `poll --channel whatsapp`.
|
|
80
|
+
pollNoSubject: (conversation: string) =>
|
|
81
|
+
`a conversa ${conversation} não declara subject: não dá para dizer quais execuções são dela, e mandar o desfecho de outra pessoa nesta conversa é exatamente o que a regra de sigilo proíbe`,
|
|
82
|
+
pollNothingWaiting: (conversation: string) => `nada aguardando pagador em ${conversation}`,
|
|
83
|
+
pollLocaleConflict: (conversation: string, recorded: string, asked: string) =>
|
|
84
|
+
`a conversa ${conversation} foi registrada em ${recorded}; um poll continua a conversa no idioma em que ela começou, e --locale ${asked} a mudaria no meio`,
|
|
85
|
+
pollNoBundle: (runIds: string) => `nenhum bundle encontrado para ${runIds}: a conversa não pode ser retomada sem o registro dela`,
|
|
86
|
+
pollBanner: (name: string, version: string, backend: string, conversation: string, openMinutes: number | undefined) =>
|
|
87
|
+
`${name} ${version} — poll no canal whatsapp (${backend}) — conversa ${conversation} — janela de 24h ${openMinutes === undefined ? "FECHADA: só template aprovado" : `ABERTA (${openMinutes} min restantes): mensagem livre`}`,
|
|
88
|
+
pollToldByText: "avisado por mensagem livre",
|
|
89
|
+
pollToldByTemplate: (template: string, fallback: boolean) => `avisado por template ${template}${fallback ? " (reserva: o kit não tem cópia para este desfecho)" : ""}`,
|
|
90
|
+
pollNotTold: (reason: string, detail: string | undefined) => `não avisado (${reason}${detail ? `: ${detail}` : ""})`,
|
|
91
|
+
pollNoTemplateForOutcome: (outcome: string) => `a janela de 24h está fechada e o agente não declara template para ${outcome}, nem um template de reserva`,
|
|
92
|
+
pollTemplateNotDeclared: (template: string) => `o kit pediu o template ${template}, que channels/whatsapp/templates.json não declara`,
|
|
93
|
+
pollProviderRefused: "o provedor não aceitou a mensagem",
|
|
94
|
+
|
|
95
|
+
// What a run did, counted from its executions: printed after the reply, whatever the reply says.
|
|
96
|
+
runOutcome: (settled: number, failed: number, declined: number, alreadyPaid: number, open: number) =>
|
|
97
|
+
settled + failed + declined + alreadyPaid + open === 0
|
|
98
|
+
? "resultado deste run: nenhuma execução; nada foi pago"
|
|
99
|
+
: `resultado deste run: ${settled} liquidada(s), ${failed} com falha ou recusada(s), ${declined} negada(s) ou expirada(s), ${alreadyPaid} pulada(s) por já paga(s), ${open} em aberto`,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type CoreStrings = typeof pt;
|
|
103
|
+
|
|
104
|
+
const en: CoreStrings = {
|
|
105
|
+
execution: "execution",
|
|
106
|
+
totalByCore: "total (computed by the core)",
|
|
107
|
+
escalatedBy: "escalated by",
|
|
108
|
+
blocked: (reasons, notAuthorized) => `blocked: ${reasons} — ${notAuthorized}; there is nothing to approve`,
|
|
109
|
+
replayedSettlement: "already paid: the API answered with an earlier payment of this same attempt; this execution moved no money",
|
|
110
|
+
|
|
111
|
+
onlyDeny: " (the only possible outcome is to deny; press Enter)",
|
|
112
|
+
leftAwaiting: " left in awaiting_approval (run again with --approve or --deny)",
|
|
113
|
+
afterLooks: (rounds, seconds, timedOut) => ` after ${rounds} look(s), ${seconds}s${timedOut ? " (timed out; `npm run poll` picks up where this stopped)" : ""}`,
|
|
114
|
+
banner: (name, version, mode, rail, mandateWord, mandateId) => `${name} ${version} — approval: ${mode} — rail: ${rail} — ${mandateWord} ${mandateId}`,
|
|
115
|
+
bundleAt: (runId, dir) => `run ${runId} — bundle in ${dir}`,
|
|
116
|
+
|
|
117
|
+
batchHeader: (ref, label, count, total, hash) => ` batch ${ref} — ${label}: ${count} line(s), total ${total}, batch_hash ${hash}…`,
|
|
118
|
+
batchAlreadySettled: " (already paid; does not run again)",
|
|
119
|
+
batchInProgress: " (in progress; does not run again)",
|
|
120
|
+
batchAttemptConflict: " (attempt bound to another payment; does not run again)",
|
|
121
|
+
batchQuestion: " Approve the list? [all / all except 3,7 / none] ",
|
|
122
|
+
batchUnreadable: (count) => ` I did not understand; answer all, all except <numbers from 1 to ${count}> or none`,
|
|
123
|
+
batchApprovedAll: " -> list approved in full",
|
|
124
|
+
batchDeniedAll: " -> list denied in full",
|
|
125
|
+
batchApprovedExcept: (positions) => ` -> list approved except ${positions}`,
|
|
126
|
+
|
|
127
|
+
instalmentOf: (instalment, count) => `Instalment ${instalment}/${count}: `,
|
|
128
|
+
dueOn: (date) => `, due ${date}`,
|
|
129
|
+
chargeRef: (chargeId) => ` — charge ${chargeId}`,
|
|
130
|
+
pixCopyPaste: "Pix copy and paste:",
|
|
131
|
+
boletoLine: "Or pay the boleto with this line:",
|
|
132
|
+
|
|
133
|
+
outsideCollectionHours: (window, timezone, clock) => `outside collection hours (${window} ${timezone}); it is ${clock} now`,
|
|
134
|
+
|
|
135
|
+
scenarioReply: (text) => `agent: ${text}`,
|
|
136
|
+
scenarioOk: (cycleSeconds, dir) => `== ok — ${cycleSeconds === undefined ? "" : cycleSeconds === null ? "no cycle measured — " : `${cycleSeconds}s — `}bundle in ${dir}`,
|
|
137
|
+
|
|
138
|
+
waBanner: (name, version, backend, mode, rail, mandateWord, mandateId) => `${name} ${version} — channel: whatsapp (${backend}) — approval: ${mode} — rail: ${rail} — ${mandateWord} ${mandateId}`,
|
|
139
|
+
waTapIgnored: (rule, detail) => ` [whatsapp] tap ignored (${rule}): ${detail}`,
|
|
140
|
+
waRefused: (rule, detail) => ` [whatsapp] refused (${rule}): ${detail}`,
|
|
141
|
+
waRefusedByBackend: (rule, detail) => ` [whatsapp] refused by the backend (${rule}): ${detail}`,
|
|
142
|
+
waDeliveryFailed: (messageId, codes, told) => ` [operator] DELIVERY FAILED for message ${messageId} (${codes})${told ? ` — it told ${told}: the person was NOT told` : ""}`,
|
|
143
|
+
waShop: "shop:",
|
|
144
|
+
waTap: (title) => `[tap: ${title}]`,
|
|
145
|
+
waTapIgnoredMark: (rule) => ` (ignored: ${rule})`,
|
|
146
|
+
waNotSent: (rule) => `(not sent: ${rule})`,
|
|
147
|
+
waCopyPaste: "[copy and paste]",
|
|
148
|
+
waQrImage: "[image: Pix QR]",
|
|
149
|
+
waYou: " │ you> ",
|
|
150
|
+
waEmulatorOpen: " ┌─ WhatsApp (dyvit-wa-sim, a local Cloud API emulator — no external network, no Meta account)",
|
|
151
|
+
waEmulatorClosed: " └─ end of conversation",
|
|
152
|
+
waConversationSummary: (log, received, sent, refused) => `conversation in ${log} — ${received === undefined ? "" : `${received} received, `}${sent} sent${refused ? `, ${refused} refused` : ""}`,
|
|
153
|
+
waSimulatedCost: (total, currency) => `simulated cost of this conversation under Meta's rules: ${total} ${currency} (the emulator's figure, not ours)`,
|
|
154
|
+
|
|
155
|
+
pollNoSubject: (conversation) =>
|
|
156
|
+
`conversation ${conversation} declares no subject: there is no telling which executions are its own, and sending somebody else's outcome into this conversation is exactly what the secrecy rule forbids`,
|
|
157
|
+
pollNothingWaiting: (conversation) => `nothing waiting for a payer in ${conversation}`,
|
|
158
|
+
pollLocaleConflict: (conversation, recorded, asked) => `conversation ${conversation} was recorded in ${recorded}; a poll continues a conversation in the language it started in, and --locale ${asked} would switch it midway`,
|
|
159
|
+
pollNoBundle: (runIds) => `no bundle found for ${runIds}: the conversation cannot be resumed without its record`,
|
|
160
|
+
pollBanner: (name, version, backend, conversation, openMinutes) =>
|
|
161
|
+
`${name} ${version} — poll on the whatsapp channel (${backend}) — conversation ${conversation} — 24h window ${openMinutes === undefined ? "SHUT: approved template only" : `OPEN (${openMinutes} min left): free-form message`}`,
|
|
162
|
+
pollToldByText: "told by free-form message",
|
|
163
|
+
pollToldByTemplate: (template, fallback) => `told by template ${template}${fallback ? " (fallback: the kit has no copy for this outcome)" : ""}`,
|
|
164
|
+
pollNotTold: (reason, detail) => `not told (${reason}${detail ? `: ${detail}` : ""})`,
|
|
165
|
+
pollNoTemplateForOutcome: (outcome) => `the 24h window is shut and the agent declares no template for ${outcome}, nor a fallback template`,
|
|
166
|
+
pollTemplateNotDeclared: (template) => `the kit asked for template ${template}, which channels/whatsapp/templates.json does not declare`,
|
|
167
|
+
pollProviderRefused: "the provider did not accept the message",
|
|
168
|
+
|
|
169
|
+
runOutcome: (settled, failed, declined, alreadyPaid, open) =>
|
|
170
|
+
settled + failed + declined + alreadyPaid + open === 0
|
|
171
|
+
? "result of this run: no execution; nothing was paid"
|
|
172
|
+
: `result of this run: ${settled} settled, ${failed} failed or refused, ${declined} denied or expired, ${alreadyPaid} skipped as already paid, ${open} open`,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const CORE_STRINGS: LocaleTable<CoreStrings> = { "pt-BR": pt, en };
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* "the charge was issued" finds it again on `resume`.
|
|
7
7
|
*
|
|
8
8
|
* The fixture payer lives in the lookups: the first look after issuance
|
|
9
|
-
* finds the instrument registered (a
|
|
9
|
+
* finds the instrument registered (a cobrança com vencimento answers
|
|
10
10
|
* PROCESSING at create and PENDING once the clearing house has it), the
|
|
11
11
|
* second look finds what the payer did: paid (default), let it expire, or
|
|
12
12
|
* nothing yet. With a real key the scenario runner calls the sandbox pay
|
|
@@ -29,6 +29,13 @@ export interface StubChargeRailOptions {
|
|
|
29
29
|
uncertainOnce?: string[];
|
|
30
30
|
/** Test hook: called after the issuance is persisted and before the outcome is returned. */
|
|
31
31
|
afterDispatch?: (attemptId: string) => void;
|
|
32
|
+
/** Debtor documents whose registration the issuer ends in `ERROR` at the first look: no instrument, ever (staging, 2026-09-27, OPEN_QUESTIONS §63). */
|
|
33
|
+
issuerErrorPayees?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Debtor documents whose charge never becomes payable and is settled anyway at the second look: what the API's test
|
|
36
|
+
* pay route did to a charge in `ERROR` on staging (ent#1816). Here so the scenario gate can be shown refusing it.
|
|
37
|
+
*/
|
|
38
|
+
settlesUnpayablePayees?: string[];
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
interface PayerState {
|
|
@@ -100,8 +107,14 @@ export class StubChargeRail implements PaymentRail {
|
|
|
100
107
|
const looks = state.looks + 1;
|
|
101
108
|
this.savePayerState(attemptId, { ...state, looks });
|
|
102
109
|
|
|
110
|
+
if (this.options.issuerErrorPayees?.includes(payment.payee)) {
|
|
111
|
+
const failed: RailOutcome = { status: "failed", code: "charge_issuer_error", message: `stub: the issuer ended charge ${outcome.transaction_id}'s registration in ERROR: no Pix, no boleto, nothing a payer can pay`, raw: { stub: true } };
|
|
112
|
+
this.replace(attemptId, payment, failed);
|
|
113
|
+
return failed;
|
|
114
|
+
}
|
|
115
|
+
if (this.options.settlesUnpayablePayees?.includes(payment.payee) && looks === 1) return this.update(attemptId, payment, "PROCESSING", false);
|
|
103
116
|
// First look: the clearing house registered the instrument. It is payable from here.
|
|
104
|
-
if (looks === 1 || !outcome.instrument.payable) {
|
|
117
|
+
if ((looks === 1 || !outcome.instrument.payable) && !this.options.settlesUnpayablePayees?.includes(payment.payee)) {
|
|
105
118
|
return this.update(attemptId, payment, "PENDING", true);
|
|
106
119
|
}
|
|
107
120
|
if (state.fate === "pays") {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { canonicalJson, sha256Hex } from "../hash.js";
|
|
14
14
|
import { checkQuote } from "../quote.js";
|
|
15
|
-
import { checkSpendApproval, type PaymentRail, type RailLookup, type RailOutcome, type RailPayment, type RailReceipt } from "../rail.js";
|
|
15
|
+
import { checkSpendApproval, wireActorOf, type PaymentRail, type RailLookup, type RailOutcome, type RailPayment, type RailReceipt, type WireActor } from "../rail.js";
|
|
16
16
|
import type { StateStore } from "../state/store.js";
|
|
17
17
|
import type { Actor } from "../types.js";
|
|
18
18
|
|
|
@@ -98,9 +98,11 @@ export class StubRail implements PaymentRail {
|
|
|
98
98
|
if (!quoted.ok) return { status: "failed", code: quoted.code, message: `${quoted.detail}; nothing was sent` };
|
|
99
99
|
const approval = checkSpendApproval(payment);
|
|
100
100
|
if (!approval.ok) return { status: "failed", code: approval.code, message: `${approval.detail}; nothing was sent` };
|
|
101
|
-
const { actor
|
|
101
|
+
const { actor, ...asked } = payment;
|
|
102
|
+
// Like the API: the actor the spend declared is recorded with it and read back on the receipt, and is not part of the attempt tuple.
|
|
103
|
+
const request = { ...asked, sealed_actor: wireActorOf(actor) };
|
|
102
104
|
const existing = this.store.stubRailGet(payment.attempt_id);
|
|
103
|
-
if (existing) return this.repeat(payment.attempt_id, existing.request as Omit<RailPayment, "actor">, existing.outcome as Recorded,
|
|
105
|
+
if (existing) return this.repeat(payment.attempt_id, existing.request as Omit<RailPayment, "actor">, existing.outcome as Recorded, asked);
|
|
104
106
|
|
|
105
107
|
const uncertainPayee = this.options.uncertainPayees?.includes(payment.payee) === true && !this.uncertainAnswered.has(payment.attempt_id);
|
|
106
108
|
if (this.uncertainArmed || this.uncertainPending.has(payment.attempt_id) || uncertainPayee) {
|
|
@@ -169,7 +171,7 @@ export class StubRail implements PaymentRail {
|
|
|
169
171
|
async receipt(receiptId: string, actor: Actor): Promise<RailReceipt | undefined> {
|
|
170
172
|
const sealed = this.store.stubRailFindByReceipt(receiptId);
|
|
171
173
|
if (!sealed) return undefined;
|
|
172
|
-
const req = sealed.request as Omit<RailPayment, "actor"
|
|
174
|
+
const req = sealed.request as Omit<RailPayment, "actor"> & { sealed_actor?: WireActor };
|
|
173
175
|
const out = sealed.outcome as Extract<RailOutcome, { status: "settled" }>;
|
|
174
176
|
// Like the API: the payee on the receipt is the one the quote sealed, and none when the spend carried no quote.
|
|
175
177
|
const body = {
|
|
@@ -192,6 +194,7 @@ export class StubRail implements PaymentRail {
|
|
|
192
194
|
receipt_sig_ed25519: null,
|
|
193
195
|
receipt_sig_kid: null,
|
|
194
196
|
actor,
|
|
197
|
+
sealed_actor: req.sealed_actor ?? null,
|
|
195
198
|
raw: { stub: true, transaction_id: out.transaction_id },
|
|
196
199
|
};
|
|
197
200
|
}
|
|
@@ -122,6 +122,11 @@ export type ExecutionReason =
|
|
|
122
122
|
| "awaiting_settlement"
|
|
123
123
|
| "charge_expired"
|
|
124
124
|
| "charge_cancelled"
|
|
125
|
+
/**
|
|
126
|
+
* The issuer ended the receivable's registration in an error state (`ERROR`): no Pix, no boleto, nothing a payer can
|
|
127
|
+
* pay, and nothing will be. Terminal, and nothing was collected. Measured on staging on 2026-09-27 (OPEN_QUESTIONS §63).
|
|
128
|
+
*/
|
|
129
|
+
| "charge_issuer_error"
|
|
125
130
|
/**
|
|
126
131
|
* An ISSUED receivable whose read now answers that its reference matches more than one charge. Terminal for the
|
|
127
132
|
* execution, but not "nothing moved": the charge exists and may still be paid. Reconcile it by the charge id; never
|
|
@@ -166,16 +171,24 @@ export interface ItemOutcome {
|
|
|
166
171
|
transaction_id?: string;
|
|
167
172
|
/** The rail's code on a failed outcome (`charge_expired`, `charge_cancelled`, a provider code). */
|
|
168
173
|
code?: string;
|
|
174
|
+
/** The rail's own message on a failed outcome, verbatim: for the CodeSpar rail, the API's `error.message`. */
|
|
175
|
+
message?: string;
|
|
169
176
|
/** A failed outcome because the attempt id is held for another payment or another project (`RailOutcome.held`). */
|
|
170
177
|
held?: "conflict" | "unavailable";
|
|
171
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* A settled outcome the rail answered, at dispatch, from its record of an
|
|
180
|
+
* earlier presentation (`RailOutcome.replayed`): the first time THIS
|
|
181
|
+
* execution presented the attempt, it was already paid, so this execution
|
|
182
|
+
* moved nothing for it. Never set by reconcile, whose lookup re-presents an
|
|
183
|
+
* attempt this execution already sent and cannot tell whose payment it reads.
|
|
184
|
+
*/
|
|
172
185
|
replayed?: true;
|
|
173
186
|
error?: string;
|
|
174
187
|
/** What the payer is shown for an accepted receivable, as the rail handed it back. Presentation only; nothing here decides money. */
|
|
175
188
|
instrument?: ChargeInstrument;
|
|
176
189
|
}
|
|
177
190
|
|
|
178
|
-
/** The payable legs of a receivable. Null until the issuer registers the instrument (a
|
|
191
|
+
/** The payable legs of a receivable. Null until the issuer registers the instrument (a cobrança com vencimento answers PROCESSING first). */
|
|
179
192
|
export interface ChargeInstrument {
|
|
180
193
|
payable: boolean;
|
|
181
194
|
pix_copy_paste: string | null;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OPEN_QUESTIONS §2: every spend carries the kit's `actor` on the wire, in the
|
|
3
|
+
* API's `PaymentActor` shape (@codespar/sdk 0.16.10); the receipt reads it
|
|
4
|
+
* back; the engine compares what came back with what was sent, and a
|
|
5
|
+
* different actor, or none, is an event in the bundle.
|
|
6
|
+
*/
|
|
7
|
+
import type { ApiClient } from "@codespar/sdk";
|
|
8
|
+
import { describe, expect, it, vi } from "vitest";
|
|
9
|
+
import { CodeSparRail } from "../src/api/rail.js";
|
|
10
|
+
import { copyDisagreesWithRead } from "../src/bundle.js";
|
|
11
|
+
import { sameWireActor, wireActorOf, type PaymentRail, type RailPayment, type WireActor } from "../src/rail.js";
|
|
12
|
+
import type { Actor } from "../src/types.js";
|
|
13
|
+
import { ESCOLA, harness } from "./helpers.js";
|
|
14
|
+
|
|
15
|
+
const approver = { id: "usr_demo", channel: "terminal" };
|
|
16
|
+
const agentActor: Actor = { type: "agent", agent: "bills-agent@0.1.0", on_behalf_of: "usr_demo" };
|
|
17
|
+
const ITEMS_HASH = `sha256:${"1".repeat(64)}`;
|
|
18
|
+
|
|
19
|
+
function payment(over: Partial<RailPayment> = {}): RailPayment {
|
|
20
|
+
return {
|
|
21
|
+
attempt_id: "att_actor_0",
|
|
22
|
+
mandate_id: "mdt_test_0001",
|
|
23
|
+
amount_minor: 185000,
|
|
24
|
+
currency: "BRL",
|
|
25
|
+
payee: ESCOLA,
|
|
26
|
+
purpose: "contas do mes",
|
|
27
|
+
agent_id: "bills-agent",
|
|
28
|
+
quote: { seller: "Escola Aurora", resource: "outubro", price_minor: 185000, payee: ESCOLA, at: "2026-09-23T18:00:00.000Z" },
|
|
29
|
+
approval: { items_hash: ITEMS_HASH },
|
|
30
|
+
actor: agentActor,
|
|
31
|
+
...over,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function fakeApi(receiptBody?: unknown) {
|
|
36
|
+
const post = vi.fn(async (_path: string, _init: { body: Record<string, unknown> }) => ({ payment: { transactionId: "tx_1", moneyMoved: false }, receipt: { id: "rcpt_1" } }));
|
|
37
|
+
return { api: { post, get: vi.fn(async () => receiptBody) } as unknown as ApiClient, post };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The stub rail, but the receipt it hands back records whatever actor the test says. */
|
|
41
|
+
function recordingActor(sealed: WireActor | null) {
|
|
42
|
+
return (inner: PaymentRail): PaymentRail => ({
|
|
43
|
+
name: inner.name,
|
|
44
|
+
pay: (p) => inner.pay(p),
|
|
45
|
+
lookup: (id, p, tx) => inner.lookup(id, p, tx),
|
|
46
|
+
receipt: async (id, a) => {
|
|
47
|
+
const r = await inner.receipt(id, a);
|
|
48
|
+
return r && { ...r, sealed_actor: sealed };
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe("the actor, in the API's shape", () => {
|
|
54
|
+
it("an agent is its identifier and the consumer it acts for; a person is an id and the channel they acted from", () => {
|
|
55
|
+
expect(wireActorOf(agentActor)).toEqual({ type: "agent", id: "bills-agent@0.1.0", on_behalf_of: "usr_demo" });
|
|
56
|
+
expect(wireActorOf({ type: "human", id: "usr_ana", channel: "whatsapp" })).toEqual({ type: "human", id: "usr_ana", channel: "whatsapp" });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("the same actor is the same on every field, and the channel counts", () => {
|
|
60
|
+
const person: WireActor = { type: "human", id: "usr_ana", channel: "whatsapp" };
|
|
61
|
+
expect(sameWireActor(person, { ...person })).toBe(true);
|
|
62
|
+
expect(sameWireActor(person, { ...person, channel: "dashboard" })).toBe(false);
|
|
63
|
+
expect(sameWireActor(wireActorOf(agentActor), { type: "agent", id: "bills-agent@0.1.0", on_behalf_of: "usr_other" })).toBe(false);
|
|
64
|
+
expect(sameWireActor(wireActorOf(agentActor), person)).toBe(false);
|
|
65
|
+
expect(sameWireActor(null, wireActorOf(agentActor))).toBe(false);
|
|
66
|
+
expect(sameWireActor(null, null)).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("the CodeSpar rail sends it and reads it back", () => {
|
|
71
|
+
it("both spend routes carry the actor, not only agent_id", async () => {
|
|
72
|
+
const byEnvelope = fakeApi();
|
|
73
|
+
await new CodeSparRail(byEnvelope.api, { canonical: { any: "envelope" } as never, signature: "a".repeat(64) }).pay(payment());
|
|
74
|
+
expect(byEnvelope.post.mock.calls[0]![0]).toBe("/v1/consumer-payments/execute");
|
|
75
|
+
expect(byEnvelope.post.mock.calls[0]![1].body["actor"]).toEqual({ type: "agent", id: "bills-agent@0.1.0", on_behalf_of: "usr_demo" });
|
|
76
|
+
const byId = fakeApi();
|
|
77
|
+
await new CodeSparRail(byId.api).pay(payment({ actor: { type: "human", id: "usr_ana", channel: "whatsapp" } }));
|
|
78
|
+
expect(byId.post.mock.calls[0]![0]).toBe("/v1/consumers/mandates/{id}/spend");
|
|
79
|
+
expect(byId.post.mock.calls[0]![1].body["actor"]).toEqual({ type: "human", id: "usr_ana", channel: "whatsapp" });
|
|
80
|
+
// agent_id still goes: it names who the mandate was signed for, which is an authority and not the event.
|
|
81
|
+
expect(byId.post.mock.calls[0]![1].body["agent_id"]).toBe("bills-agent");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("the receipt read gives back the actor the API recorded, and null when it recorded none", async () => {
|
|
85
|
+
const read = {
|
|
86
|
+
receipt_id: "rcpt_1",
|
|
87
|
+
state: "paid",
|
|
88
|
+
chain_version: 4,
|
|
89
|
+
actor: { type: "agent", id: "bills-agent@0.1.0", on_behalf_of: "usr_demo" },
|
|
90
|
+
mandate: { id: "cm_1", nonce: "n", scope: "s", currency: "BRL", sig: "x", sig_sha256: "d".repeat(64) },
|
|
91
|
+
quote: { seller: "Escola Aurora", resource: "outubro", price_minor: 185000, payee: ESCOLA, session_id: null, sig: "q", at: "2026-09-23T18:00:00.000Z" },
|
|
92
|
+
approval: { items_hash: ITEMS_HASH, batch_hash: null },
|
|
93
|
+
payment: { rail: "pix-consent", provider: "pix", tx_id: "tx_1", amount_minor: 185000, amount_atomic: null, sandbox: true, attempt_id: "att_actor_0", money_moved: false, at: "2026-09-23T18:00:01.000Z" },
|
|
94
|
+
delivery: null,
|
|
95
|
+
chain: "c".repeat(64),
|
|
96
|
+
receipt_sig: "s",
|
|
97
|
+
exceptions: [],
|
|
98
|
+
};
|
|
99
|
+
const receipt = await new CodeSparRail(fakeApi(read).api).receipt("rcpt_1", agentActor);
|
|
100
|
+
expect(receipt?.sealed_actor).toEqual({ type: "agent", id: "bills-agent@0.1.0", on_behalf_of: "usr_demo" });
|
|
101
|
+
// The local stamp stays the kit's own: the two are compared, never merged.
|
|
102
|
+
expect(receipt?.actor).toEqual(agentActor);
|
|
103
|
+
expect((await new CodeSparRail(fakeApi({ ...read, actor: null }).api).receipt("rcpt_1", agentActor))?.sealed_actor).toBeNull();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("the engine compares what came back with what was sent", () => {
|
|
108
|
+
const run = async (wrap?: (inner: PaymentRail) => PaymentRail) => {
|
|
109
|
+
const h = harness({ mode: "human", ...(wrap ? { wrapRail: wrap } : {}) });
|
|
110
|
+
const d = await h.engine.draft({ items: [{ payee: "escola", amount: 185000 }] });
|
|
111
|
+
if (!d.ok) throw new Error("refused");
|
|
112
|
+
const done = await h.engine.execute(h.engine.approve(d.execution.id, approver).id);
|
|
113
|
+
return { h, done };
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
it("the same actor back: nothing to say, and the bundle's receipt copy carries both the stamp and what the API recorded", async () => {
|
|
117
|
+
const { h, done } = await run();
|
|
118
|
+
expect(done.state).toBe("settled");
|
|
119
|
+
expect(h.bundle.readEvents().some((e) => e["type"] === "receipt.actor_mismatch")).toBe(false);
|
|
120
|
+
const copy = h.bundle.readReceipt(h.bundle.listReceipts()[0]!)!;
|
|
121
|
+
expect(copy["sealed_actor"]).toEqual(wireActorOf(h.engine.agentActor));
|
|
122
|
+
expect(copy["actor"]).toEqual(h.engine.agentActor);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
for (const [name, sealed] of [
|
|
126
|
+
["another actor", { type: "human", id: "usr_intruso", channel: "dashboard" } as WireActor],
|
|
127
|
+
["no actor at all", null],
|
|
128
|
+
] as const) {
|
|
129
|
+
it(`a receipt that records ${name} is a receipt.actor_mismatch event; the payment stands`, async () => {
|
|
130
|
+
const { h, done } = await run(recordingActor(sealed));
|
|
131
|
+
expect(done.state).toBe("settled");
|
|
132
|
+
const mismatch = h.bundle.readEvents().find((e) => e["type"] === "receipt.actor_mismatch");
|
|
133
|
+
expect(mismatch?.["payload"]).toMatchObject({ sealed_actor: sealed, sent_actor: wireActorOf(h.engine.agentActor) });
|
|
134
|
+
expect(mismatch?.["actor"]).toBeDefined();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
it("verify's copy check flags a copy whose recorded actor is not the read's", () => {
|
|
139
|
+
const read = { receipt_id: "r", chain: "c", mandate: { id: "m" }, payment: { amount_minor: 1, attempt_id: "a", money_moved: false, at: "t" }, actor: { type: "agent", id: "x", on_behalf_of: "u" } };
|
|
140
|
+
const copy = { receipt_id: "r", chain: "c", mandate: { id: "m" }, payment: { amount_minor: 1, attempt_id: "a", money_moved: false, at: "t", payee: null }, sealed_actor: { type: "agent", id: "y", on_behalf_of: "u" } };
|
|
141
|
+
expect(copyDisagreesWithRead(copy, read)).toEqual(["sealed_actor"]);
|
|
142
|
+
expect(copyDisagreesWithRead({ ...copy, sealed_actor: read.actor }, read)).toEqual([]);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -65,8 +65,8 @@ beforeEach(() => {
|
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
function rail(): CodeSparRail {
|
|
68
|
-
//
|
|
69
|
-
return new CodeSparRail(createCodeSparClient({ apiKey: "
|
|
68
|
+
// Not the placeholder, which the guard refuses (#50); the underscore keeps it below the secret scan's key shape.
|
|
69
|
+
return new CodeSparRail(createCodeSparClient({ apiKey: "csk_test_unit_0000", baseUrl, timeoutMs: 5_000 }));
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function refusal(code: string, details?: Record<string, unknown>): Answer {
|