@codespar/cli 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +1 -1
- package/templates/bills-agent/README.md +1 -1
- package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
- package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
- package/templates/bills-agent/agents/bills-agent/README.md +6 -5
- package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +5 -4
- package/templates/bills-agent/packages/agent-core/README.md +14 -1
- package/templates/bills-agent/packages/agent-core/package.json +1 -1
- package/templates/bills-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/bills-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/bills-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/bills-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/bills-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/bills-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/bills-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/bills-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/bills-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/bills-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/bills-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/bills-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/bills-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/bills-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/bills-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/bills-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/bills-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/bills-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/bills-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/bills-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/bills-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/bills-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/bills-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/bills-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/bills-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/bills-agent/packages/agent-runtime/README.md +12 -0
- package/templates/bills-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/bills-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/bills-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/bills-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/checkout-agent/LICENSE +21 -0
- package/templates/checkout-agent/README.md +28 -0
- package/templates/checkout-agent/_gitignore +10 -0
- package/templates/checkout-agent/agents/checkout-agent/.env.example +5 -0
- package/templates/checkout-agent/agents/checkout-agent/AGENTS.md +20 -0
- package/templates/checkout-agent/agents/checkout-agent/CLAUDE.md +20 -0
- package/templates/checkout-agent/agents/checkout-agent/README.md +94 -0
- package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +36 -0
- package/templates/checkout-agent/agents/checkout-agent/agent.yaml +30 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +28 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-beatriz.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina-retomada.json +10 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +49 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +12 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +13 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.json +26 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.json +12 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.json +13 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +15 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +7 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +12 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.json +13 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.json +16 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.json +15 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +8 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/webhook-replay.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/eval.yaml +11 -0
- package/templates/checkout-agent/agents/checkout-agent/guardrails.json +25 -0
- package/templates/checkout-agent/agents/checkout-agent/mandate.example.json +28 -0
- package/templates/checkout-agent/agents/checkout-agent/package.json +33 -0
- package/templates/checkout-agent/agents/checkout-agent/runbook.md +19 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.json +42 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.json +32 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.json +61 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +61 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +36 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +6 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.json +23 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.json +72 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.json +15 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +6 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.json +23 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +3 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.json +42 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +12 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.json +14 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +34 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +7 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.json +33 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.json +32 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +92 -0
- package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +338 -0
- package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +179 -0
- package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +374 -0
- package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +220 -0
- package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +297 -0
- package/templates/checkout-agent/agents/checkout-agent/test/adversarial.test.ts +117 -0
- package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +67 -0
- package/templates/checkout-agent/agents/checkout-agent/test/cli.test.ts +169 -0
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +6 -0
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +2 -0
- package/templates/checkout-agent/agents/checkout-agent/test/nfse.test.ts +170 -0
- package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +138 -0
- package/templates/checkout-agent/agents/checkout-agent/test/scenarios.test.ts +113 -0
- package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +171 -0
- package/templates/checkout-agent/agents/checkout-agent/tools.json +68 -0
- package/templates/checkout-agent/agents/checkout-agent/tsconfig.json +10 -0
- package/templates/checkout-agent/package.json +38 -0
- package/templates/checkout-agent/packages/agent-core/README.md +34 -0
- package/templates/checkout-agent/packages/agent-core/package.json +24 -0
- package/templates/checkout-agent/packages/agent-core/src/agent.ts +163 -0
- package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +195 -0
- package/templates/checkout-agent/packages/agent-core/src/api/client.ts +76 -0
- package/templates/checkout-agent/packages/agent-core/src/api/mandate-status.ts +74 -0
- package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +153 -0
- package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +43 -0
- package/templates/checkout-agent/packages/agent-core/src/approval.ts +135 -0
- package/templates/checkout-agent/packages/agent-core/src/bundle.ts +191 -0
- package/templates/checkout-agent/packages/agent-core/src/channels.ts +186 -0
- package/templates/checkout-agent/packages/agent-core/src/check.ts +276 -0
- package/templates/checkout-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/checkout-agent/packages/agent-core/src/engine.ts +1074 -0
- package/templates/checkout-agent/packages/agent-core/src/escalate.ts +85 -0
- package/templates/checkout-agent/packages/agent-core/src/events.ts +40 -0
- package/templates/checkout-agent/packages/agent-core/src/guardrails.ts +45 -0
- package/templates/checkout-agent/packages/agent-core/src/hash.ts +118 -0
- package/templates/checkout-agent/packages/agent-core/src/ids.ts +10 -0
- package/templates/checkout-agent/packages/agent-core/src/index.ts +34 -0
- package/templates/checkout-agent/packages/agent-core/src/mandate.ts +100 -0
- package/templates/checkout-agent/packages/agent-core/src/manifest.ts +76 -0
- package/templates/checkout-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
- package/templates/checkout-agent/packages/agent-core/src/providers/replay.ts +63 -0
- package/templates/checkout-agent/packages/agent-core/src/providers/types.ts +39 -0
- package/templates/checkout-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/checkout-agent/packages/agent-core/src/rail.ts +186 -0
- package/templates/checkout-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +577 -0
- package/templates/checkout-agent/packages/agent-core/src/revocation.ts +30 -0
- package/templates/checkout-agent/packages/agent-core/src/secrets.ts +30 -0
- package/templates/checkout-agent/packages/agent-core/src/state/store.ts +307 -0
- package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +157 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/mandate-status.ts +58 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +209 -0
- package/templates/checkout-agent/packages/agent-core/src/tools.ts +66 -0
- package/templates/checkout-agent/packages/agent-core/src/types.ts +187 -0
- package/templates/checkout-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/checkout-agent/packages/agent-core/test/approval.test.ts +117 -0
- package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/checkout-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/checkout-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/checkout-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +596 -0
- package/templates/checkout-agent/packages/agent-core/test/escalate.test.ts +42 -0
- package/templates/checkout-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/checkout-agent/packages/agent-core/test/helpers.ts +142 -0
- package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +229 -0
- package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +45 -0
- package/templates/checkout-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +416 -0
- package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +28 -0
- package/templates/checkout-agent/packages/agent-core/test/state-machine.test.ts +67 -0
- package/templates/checkout-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
- package/templates/checkout-agent/packages/agent-core/tsconfig.json +10 -0
- package/templates/checkout-agent/packages/agent-runtime/README.md +80 -0
- package/templates/checkout-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/checkout-agent/packages/agent-runtime/package.json +23 -0
- package/templates/checkout-agent/packages/agent-runtime/src/adversarial.ts +199 -0
- package/templates/checkout-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/types.ts +173 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +459 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +216 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +350 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +180 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +120 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +127 -0
- package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +367 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +145 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +49 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +134 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +248 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +330 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +40 -0
- package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/checkout-agent/packages/agent-runtime/src/index.ts +13 -0
- package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +801 -0
- package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +229 -0
- package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +105 -0
- package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +274 -0
- package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +239 -0
- package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +263 -0
- package/templates/checkout-agent/packages/agent-runtime/src/webhook.ts +131 -0
- package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +329 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +277 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +167 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/checkout-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/checkout-agent/scripts/whatsapp-emulator.mjs +84 -0
- package/templates/checkout-agent/tsconfig.base.json +16 -0
- package/templates/checkout-agent/vitest.config.ts +12 -0
- package/templates/collections-agent/README.md +6 -1
- package/templates/collections-agent/agents/collections-agent/README.md +12 -8
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +8 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042-retomada.json +10 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +27 -1
- package/templates/collections-agent/agents/collections-agent/src/kit.ts +2 -0
- package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +12 -2
- package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +55 -0
- package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +2 -0
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +12 -6
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +148 -0
- package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +5 -0
- package/templates/collections-agent/package.json +1 -0
- package/templates/collections-agent/packages/agent-core/README.md +14 -1
- package/templates/collections-agent/packages/agent-core/package.json +1 -1
- package/templates/collections-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/collections-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/collections-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/collections-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/collections-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/collections-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/collections-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/collections-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/collections-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/collections-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/collections-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/collections-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/collections-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/collections-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/collections-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/collections-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/collections-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/collections-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/collections-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/collections-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/collections-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/collections-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/collections-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/collections-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/collections-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/collections-agent/packages/agent-runtime/README.md +12 -0
- package/templates/collections-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/collections-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/collections-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/collections-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/collections-agent/scripts/whatsapp-emulator.mjs +84 -0
- package/templates/hello-agent/README.md +1 -1
- package/templates/hello-agent/packages/agent-core/README.md +14 -1
- package/templates/hello-agent/packages/agent-core/package.json +1 -1
- package/templates/hello-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/hello-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/hello-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/hello-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/hello-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/hello-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/hello-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/hello-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/hello-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/hello-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/hello-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/hello-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/hello-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/hello-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/hello-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/hello-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/hello-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/hello-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/hello-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/hello-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/hello-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/hello-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/hello-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/hello-agent/packages/agent-runtime/README.md +12 -0
- package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/hello-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/hello-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/hello-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/kits.lock.json +29 -6
- package/templates/supplier-payments-agent/README.md +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +11 -9
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +67 -20
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +504 -5
- package/templates/supplier-payments-agent/packages/agent-core/README.md +14 -1
- package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/supplier-payments-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/supplier-payments-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/supplier-payments-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/README.md +12 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* The CodeSpar sandbox rail. With the signed envelope the consent returned
|
|
3
3
|
* (`mandate.canonical` + `mandate.signature`) it spends through
|
|
4
4
|
* `POST /v1/consumer-payments/execute`; without it, by mandate id through
|
|
5
|
-
* `POST /v1/consumers/mandates/{id}/spend`.
|
|
6
|
-
*
|
|
5
|
+
* `POST /v1/consumers/mandates/{id}/spend`. Idempotence on both is OPT-IN
|
|
6
|
+
* and keyed on an explicit `attempt_id`, which every payment here carries: a
|
|
7
|
+
* spend without one is a fresh payment on every request (ent#1671, #1683).
|
|
8
|
+
* What a repeat of an attempt answers is in `lookup` below. The by-id route answered `bad_signature` on staging for a
|
|
7
9
|
* mandate carrying `periodic_cap` (OPEN_QUESTIONS §14), which is why the
|
|
8
10
|
* envelope is preferred when it exists. Receipts come back from
|
|
9
11
|
* `GET /v1/consumers/receipts/{id}`.
|
|
@@ -14,19 +16,19 @@
|
|
|
14
16
|
*/
|
|
15
17
|
import type { ApiClient } from "@codespar/sdk";
|
|
16
18
|
import type { Mandate } from "../mandate.js";
|
|
17
|
-
import
|
|
19
|
+
import { checkQuote } from "../quote.js";
|
|
20
|
+
import { checkSpendApproval, type PaymentRail, type RailLookup, type RailOutcome, type RailPayment, type RailReceipt } from "../rail.js";
|
|
18
21
|
import type { Actor } from "../types.js";
|
|
19
|
-
import { describeApiError, isUncertain } from "./client.js";
|
|
22
|
+
import { describeApiError, isUncertain, type SpendErrorCode } from "./client.js";
|
|
20
23
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
24
|
+
/** Another presentation of this attempt is claimed and has no recorded outcome yet: it may be moving money right now. */
|
|
25
|
+
const ATTEMPT_IN_FLIGHT: SpendErrorCode = "psp_attempt_in_flight";
|
|
26
|
+
/** This attempt failed, moved no money and was compensated; the API will never run this id again. */
|
|
27
|
+
const ATTEMPT_SPENT: SpendErrorCode = "psp_attempt_conflict";
|
|
28
|
+
/** This attempt id was already used for a DIFFERENT payment (checked before its state); nothing was held or sent. */
|
|
29
|
+
const ATTEMPT_ID_CONFLICT: SpendErrorCode = "attempt_id_conflict";
|
|
30
|
+
/** Another project of this organization holds this attempt id; opaque by design, nothing was held or sent. */
|
|
31
|
+
const ATTEMPT_ID_UNAVAILABLE: SpendErrorCode = "attempt_id_unavailable";
|
|
30
32
|
|
|
31
33
|
export class CodeSparRail implements PaymentRail {
|
|
32
34
|
readonly name = "codespar" as const;
|
|
@@ -37,6 +39,12 @@ export class CodeSparRail implements PaymentRail {
|
|
|
37
39
|
) {}
|
|
38
40
|
|
|
39
41
|
async pay(payment: RailPayment): Promise<RailOutcome> {
|
|
42
|
+
// Without the quote the sealed receipt names no payee (OPEN_QUESTIONS §18), and the API pays a quote that disagrees. Neither goes out.
|
|
43
|
+
const quoted = checkQuote(payment);
|
|
44
|
+
if (!quoted.ok) return { status: "failed", code: quoted.code, message: `${quoted.detail}; nothing was sent` };
|
|
45
|
+
// Without it the receipt seals no link to what was approved (OPEN_QUESTIONS §3), and the API refuses a malformed one with 400 anyway.
|
|
46
|
+
const approval = checkSpendApproval(payment);
|
|
47
|
+
if (!approval.ok) return { status: "failed", code: approval.code, message: `${approval.detail}; nothing was sent` };
|
|
40
48
|
try {
|
|
41
49
|
const outcome =
|
|
42
50
|
this.envelope?.canonical && this.envelope.signature
|
|
@@ -49,11 +57,13 @@ export class CodeSparRail implements PaymentRail {
|
|
|
49
57
|
agent_id: payment.agent_id,
|
|
50
58
|
payee: payment.payee,
|
|
51
59
|
attempt_id: payment.attempt_id,
|
|
60
|
+
quote: quoted.quote,
|
|
61
|
+
approval: approval.approval,
|
|
52
62
|
},
|
|
53
63
|
})
|
|
54
64
|
: await this.api.post("/v1/consumers/mandates/{id}/spend", {
|
|
55
65
|
path: { id: payment.mandate_id },
|
|
56
|
-
body: { amount_minor: payment.amount_minor, payee: payment.payee, agent_id: payment.agent_id, attempt_id: payment.attempt_id },
|
|
66
|
+
body: { amount_minor: payment.amount_minor, payee: payment.payee, agent_id: payment.agent_id, attempt_id: payment.attempt_id, quote: quoted.quote, approval: approval.approval },
|
|
57
67
|
});
|
|
58
68
|
return {
|
|
59
69
|
status: "settled",
|
|
@@ -61,24 +71,50 @@ export class CodeSparRail implements PaymentRail {
|
|
|
61
71
|
receipt_id: outcome.receipt?.id ?? null,
|
|
62
72
|
money_moved: outcome.payment.moneyMoved,
|
|
63
73
|
sandbox: !outcome.payment.moneyMoved,
|
|
74
|
+
...(outcome.idempotent_replay ? { replayed: true as const } : {}),
|
|
64
75
|
raw: outcome,
|
|
65
76
|
};
|
|
66
77
|
} catch (err) {
|
|
67
78
|
const failure = describeApiError(err);
|
|
79
|
+
// Not a refusal: the other presentation may already have reached the provider, so "nothing moved" would be a claim nobody can make.
|
|
80
|
+
if (failure.code === ATTEMPT_IN_FLIGHT) return { status: "uncertain", code: failure.code, message: failure.message };
|
|
68
81
|
if (isUncertain(failure)) return { status: "uncertain", code: failure.code, message: failure.message };
|
|
82
|
+
if (failure.code === ATTEMPT_SPENT) return { status: "failed", code: failure.code, message: failure.message, spent: true };
|
|
83
|
+
// Held, never spent: the id belongs to another payment or another project, so no generation may be derived past it.
|
|
84
|
+
if (failure.code === ATTEMPT_ID_CONFLICT) return { status: "failed", code: failure.code, message: failure.message, held: "conflict" };
|
|
85
|
+
if (failure.code === ATTEMPT_ID_UNAVAILABLE) return { status: "failed", code: failure.code, message: failure.message, held: "unavailable" };
|
|
69
86
|
return { status: "failed", code: failure.code, message: failure.message };
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
|
|
73
90
|
/**
|
|
74
|
-
* There is no read route for an attempt
|
|
75
|
-
* the same `attempt_id
|
|
76
|
-
*
|
|
77
|
-
*
|
|
91
|
+
* There is no read route for an attempt: the way to learn what became of
|
|
92
|
+
* one is to present the same `attempt_id`, with the same payment, again.
|
|
93
|
+
* Against the deployed API (ent#1671, #1683) that call never moves money
|
|
94
|
+
* for an attempt it already holds, and each answer maps to one reading:
|
|
95
|
+
*
|
|
96
|
+
* - settled: `200` with the ORIGINAL body verbatim (same `transactionId`,
|
|
97
|
+
* same stored `receipt.id`, which the receipt route resolves) plus
|
|
98
|
+
* `idempotent_replay: true`. Read as `settled`, exactly as the first
|
|
99
|
+
* answer, and marked `replayed`.
|
|
100
|
+
* - `psp_attempt_in_flight` (409): claimed, no outcome yet, on every rail.
|
|
101
|
+
* Read as `in_flight`; the next reconcile asks again with the SAME id.
|
|
102
|
+
* - `psp_attempt_uncertain` (409): pinned, dispatch outcome unknown. Read as
|
|
103
|
+
* `uncertain`, which leaves the execution open for a person.
|
|
104
|
+
* - `psp_attempt_conflict` (409): failed, compensated, no money. Read as
|
|
105
|
+
* `failed` and `spent`.
|
|
106
|
+
* - `attempt_id_conflict` / `attempt_id_unavailable` (409): the id is held for
|
|
107
|
+
* a different payment, or by another project. Read as `failed` and `held`,
|
|
108
|
+
* never `spent`: nothing was held or sent by this call. Neither can come from re-presenting the
|
|
109
|
+
* payment this execution sent, whose tuple is the one the id was claimed
|
|
110
|
+
* with, so seeing one here is a defect worth its readable failure.
|
|
111
|
+
* - an attempt the API never took: there is no record to answer from, so
|
|
112
|
+
* this call IS the payment. Reconcile runs only after the outbox row
|
|
113
|
+
* flipped to `sent`, so an attempt it looks up was presented once already.
|
|
78
114
|
*/
|
|
79
115
|
async lookup(_attemptId: string, payment: RailPayment): Promise<RailLookup> {
|
|
80
116
|
const outcome = await this.pay(payment);
|
|
81
|
-
if (outcome.status === "
|
|
117
|
+
if (outcome.status === "uncertain" && outcome.code === ATTEMPT_IN_FLIGHT) return { status: "in_flight" };
|
|
82
118
|
return outcome;
|
|
83
119
|
}
|
|
84
120
|
|
|
@@ -88,7 +124,9 @@ export class CodeSparRail implements PaymentRail {
|
|
|
88
124
|
return {
|
|
89
125
|
receipt_id: r.receipt_id,
|
|
90
126
|
state: r.state,
|
|
91
|
-
mandate: { id: r.mandate.id },
|
|
127
|
+
mandate: { id: r.mandate.id, sig_sha256: r.mandate.sig_sha256 },
|
|
128
|
+
chain_version: r.chain_version,
|
|
129
|
+
approval: r.approval ?? null,
|
|
92
130
|
payment: {
|
|
93
131
|
amount_minor: r.payment.amount_minor,
|
|
94
132
|
payee: r.quote?.payee ?? null,
|
|
@@ -99,12 +137,10 @@ export class CodeSparRail implements PaymentRail {
|
|
|
99
137
|
},
|
|
100
138
|
chain: r.chain,
|
|
101
139
|
receipt_sig: r.receipt_sig,
|
|
102
|
-
// ent#1633
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// same answer the columns hold and reads as `unsigned`.
|
|
107
|
-
...readEd25519Seal(r),
|
|
140
|
+
// `||`, not `??`: a deployment older than ent#1633 omits both fields,
|
|
141
|
+
// and an empty string is no signature either. Both read as `unsigned`.
|
|
142
|
+
receipt_sig_ed25519: r.receipt_sig_ed25519 || null,
|
|
143
|
+
receipt_sig_kid: r.receipt_sig_kid || null,
|
|
108
144
|
actor,
|
|
109
145
|
raw: r,
|
|
110
146
|
};
|
|
@@ -12,27 +12,12 @@
|
|
|
12
12
|
* `fetch`, see docs/OPEN_QUESTIONS.md section 31c. The key is checked for
|
|
13
13
|
* the `csk_test_` prefix before a client exists, like every other call.
|
|
14
14
|
*/
|
|
15
|
-
import { ApiClient } from "@codespar/sdk";
|
|
15
|
+
import { ApiClient, type ApiOperation, type ApiSuccess } from "@codespar/sdk";
|
|
16
16
|
import type { ApiFailure } from "./client.js";
|
|
17
17
|
import { createCodeSparClient, describeApiError } from "./client.js";
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
status: "paid";
|
|
22
|
-
local_status: string;
|
|
23
|
-
currency: string;
|
|
24
|
-
quoted_minor: number;
|
|
25
|
-
paid_minor: number;
|
|
26
|
-
payment: "full" | "partial" | "over";
|
|
27
|
-
paid_via: string;
|
|
28
|
-
wallet_id: string;
|
|
29
|
-
ledger_entry_id: string | null;
|
|
30
|
-
event: { id: string; type: string } | null;
|
|
31
|
-
simulated: boolean;
|
|
32
|
-
settled_against: string | null;
|
|
33
|
-
money_moved: false;
|
|
34
|
-
idempotent_replay: boolean;
|
|
35
|
-
}
|
|
19
|
+
/** What the payer route answers, as the SDK's generated OpenAPI types it. */
|
|
20
|
+
export type SandboxPaidState = ApiSuccess<ApiOperation<"/v1/test/charges/{chargeId}/pay", "post">>;
|
|
36
21
|
|
|
37
22
|
export type SandboxPayResult = { ok: true; state: SandboxPaidState } | { ok: false; failure: ApiFailure };
|
|
38
23
|
|
|
@@ -13,7 +13,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "n
|
|
|
13
13
|
import { dirname, join } from "node:path";
|
|
14
14
|
import { canonicalJson, hexEqual, hmacSha256Hex, itemsHash } from "./hash.js";
|
|
15
15
|
import type { Execution } from "./state-machine.js";
|
|
16
|
-
import type { Actor, ApprovalArtifact, EscalationTrigger, ExecutionBatch } from "./types.js";
|
|
16
|
+
import type { Actor, ApprovalArtifact, EscalationTrigger, ExecutionBatch, ExecutionComposition } from "./types.js";
|
|
17
17
|
import { newId } from "./ids.js";
|
|
18
18
|
|
|
19
19
|
export const APPROVAL_KEY_ID = "local-dev-stub";
|
|
@@ -78,6 +78,8 @@ export function createApprovalArtifact(signer: ApprovalSigner, input: CreateAppr
|
|
|
78
78
|
// null — on an execution that is not part of a batch, so the payload a
|
|
79
79
|
// bills-agent artifact signs is unchanged by this field existing.
|
|
80
80
|
...(execution.batch ? { batch: execution.batch } : {}),
|
|
81
|
+
// What the single amount was composed of, when it was composed of lines. Same rule as `batch`: omitted, never null.
|
|
82
|
+
...(execution.composition ? { composition: execution.composition } : {}),
|
|
81
83
|
...(input.escalation ? { escalation: input.escalation } : {}),
|
|
82
84
|
actor: input.actor,
|
|
83
85
|
};
|
|
@@ -89,7 +91,7 @@ export function createApprovalArtifact(signer: ApprovalSigner, input: CreateAppr
|
|
|
89
91
|
|
|
90
92
|
export type ApprovalCheck =
|
|
91
93
|
| { ok: true }
|
|
92
|
-
| { ok: false; problem: "signature_invalid" | "items_hash_mismatch" | "expired" | "wrong_execution" | "wrong_mandate" | "batch_mismatch" };
|
|
94
|
+
| { ok: false; problem: "signature_invalid" | "items_hash_mismatch" | "expired" | "wrong_execution" | "wrong_mandate" | "batch_mismatch" | "composition_mismatch" };
|
|
93
95
|
|
|
94
96
|
/** Two batch bindings are the same binding, or one is absent and so is the other. */
|
|
95
97
|
function sameBatch(a: ExecutionBatch | undefined, b: ExecutionBatch | undefined): boolean {
|
|
@@ -97,6 +99,12 @@ function sameBatch(a: ExecutionBatch | undefined, b: ExecutionBatch | undefined)
|
|
|
97
99
|
return a.ref === b.ref && a.batch_hash === b.batch_hash && a.index === b.index && a.count === b.count;
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
/** Two compositions are the same binding, or one is absent and so is the other. */
|
|
103
|
+
function sameComposition(a: ExecutionComposition | undefined, b: ExecutionComposition | undefined): boolean {
|
|
104
|
+
if (a === undefined || b === undefined) return a === b;
|
|
105
|
+
return a.ref === b.ref && a.composition_hash === b.composition_hash && a.line_count === b.line_count;
|
|
106
|
+
}
|
|
107
|
+
|
|
100
108
|
/**
|
|
101
109
|
* The rule that makes the artifact proof: before `executing`, recompute the
|
|
102
110
|
* hash of what is about to be executed and compare it with the artifact.
|
|
@@ -118,6 +126,10 @@ export function checkApprovalArtifact(
|
|
|
118
126
|
// line was one OF. An execution that gained, lost or moved its place in a
|
|
119
127
|
// batch after approval is not the one that was approved.
|
|
120
128
|
if (!sameBatch(artifact.batch, execution.batch)) return { ok: false, problem: "batch_mismatch" };
|
|
129
|
+
// The item's amount is attested above; this is what that amount was made of.
|
|
130
|
+
// A cart recomposed at a constant total leaves `items_hash` unchanged and
|
|
131
|
+
// this hash changed, and that is the only thing that sees it.
|
|
132
|
+
if (!sameComposition(artifact.composition, execution.composition)) return { ok: false, problem: "composition_mismatch" };
|
|
121
133
|
if (new Date(artifact.expires_at).getTime() <= now.getTime()) return { ok: false, problem: "expired" };
|
|
122
134
|
return { ok: true };
|
|
123
135
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { basename, join } from "node:path";
|
|
8
|
+
import { canonicalJson } from "./hash.js";
|
|
8
9
|
import type { Mandate } from "./mandate.js";
|
|
9
10
|
import type { RailReceipt } from "./rail.js";
|
|
10
11
|
import type { ApprovalArtifact } from "./types.js";
|
|
@@ -97,11 +98,18 @@ export class ProofBundle {
|
|
|
97
98
|
writeFileSync(join(this.dir, "mandate.snapshot.json"), JSON.stringify({ ...safe, beneficiaries: safe.beneficiaries.map((b) => ({ ...b, payee: maskPayee(b.payee) })), merchant_allowlist: safe.merchant_allowlist.map(maskPayee) }, null, 2) + "\n");
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
/**
|
|
101
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Returns the path INSIDE the bundle (`receipts/<id>.json`): a bundle travels, and an absolute path names the machine that wrote it.
|
|
103
|
+
*
|
|
104
|
+
* `approval_id` names the artifact in `approval.json` whose hashes the spend carried, so `npm run verify` holds the receipt's sealed
|
|
105
|
+
* approval link against THAT artifact, found by identity and never by looking for one whose hash happens to match. The copy stays
|
|
106
|
+
* masked: binding its body to the chain needs the unmasked read, which `verify --from-api` takes from the API and writes nowhere.
|
|
107
|
+
*/
|
|
108
|
+
receipt(receipt: RailReceipt, context: { approval_id?: string | undefined } = {}): string {
|
|
102
109
|
const relative = join("receipts", `${receipt.receipt_id}.json`);
|
|
103
110
|
const { raw: _raw, ...safe } = receipt;
|
|
104
|
-
|
|
111
|
+
const copy = { ...safe, payment: { ...safe.payment, payee: safe.payment.payee ? maskPayee(safe.payment.payee) : null }, ...(context.approval_id ? { approval_id: context.approval_id } : {}) };
|
|
112
|
+
writeFileSync(join(this.dir, relative), JSON.stringify(copy, null, 2) + "\n");
|
|
105
113
|
return relative;
|
|
106
114
|
}
|
|
107
115
|
|
|
@@ -149,6 +157,30 @@ function readdirSorted(dir: string): string[] {
|
|
|
149
157
|
.sort();
|
|
150
158
|
}
|
|
151
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Where the bundle's masked copy of a receipt says something the API's read of the same receipt does not. The copy is not what the
|
|
162
|
+
* signature covers, so a copy edited after the fact still verifies at the signature level; this is what catches it once the read is
|
|
163
|
+
* in hand. The payee is compared masked, the only form the copy holds. An empty list means the copy is a faithful mask of the read.
|
|
164
|
+
*/
|
|
165
|
+
export function copyDisagreesWithRead(copy: Record<string, unknown>, read: Record<string, unknown>): string[] {
|
|
166
|
+
const field = (o: unknown, k: string): unknown => (o && typeof o === "object" ? (o as Record<string, unknown>)[k] : undefined);
|
|
167
|
+
const readPayee = field(read["quote"], "payee");
|
|
168
|
+
const expected: Array<[string, unknown, unknown]> = [
|
|
169
|
+
["receipt_id", copy["receipt_id"], read["receipt_id"]],
|
|
170
|
+
["chain", copy["chain"], read["chain"]],
|
|
171
|
+
["mandate.id", field(copy["mandate"], "id"), field(read["mandate"], "id")],
|
|
172
|
+
["payment.amount_minor", field(copy["payment"], "amount_minor"), field(read["payment"], "amount_minor")],
|
|
173
|
+
["payment.attempt_id", field(copy["payment"], "attempt_id"), field(read["payment"], "attempt_id")],
|
|
174
|
+
["payment.money_moved", field(copy["payment"], "money_moved"), field(read["payment"], "money_moved")],
|
|
175
|
+
["payment.at", field(copy["payment"], "at"), field(read["payment"], "at")],
|
|
176
|
+
["payment.payee", field(copy["payment"], "payee"), typeof readPayee === "string" ? maskPayee(readPayee) : null],
|
|
177
|
+
];
|
|
178
|
+
// Written onto the copy since the rail reads them through the SDK's types (0.16.10); a copy from before carries neither, and is not faulted for it.
|
|
179
|
+
if ("chain_version" in copy) expected.push(["chain_version", copy["chain_version"], read["chain_version"]]);
|
|
180
|
+
if ("approval" in copy) expected.push(["approval", canonicalJson(copy["approval"] ?? null), canonicalJson(read["approval"] ?? null)]);
|
|
181
|
+
return expected.filter(([, a, b]) => a !== b).map(([name]) => name);
|
|
182
|
+
}
|
|
183
|
+
|
|
152
184
|
/** `escola@exemplo.com.br` -> `es***@exemplo.com.br`; `+5511999998888` -> `+55***8888`; a UUID keeps its first and last 4. */
|
|
153
185
|
export function maskPayee(payee: string): string {
|
|
154
186
|
if (payee === "*") return payee;
|
|
@@ -47,16 +47,22 @@ export const ConversationScriptSchema = z
|
|
|
47
47
|
* different one, which is the secrecy rule as code rather than as prose.
|
|
48
48
|
*/
|
|
49
49
|
subject: z.string().min(1).optional(),
|
|
50
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* The person's turns, in order: something they TYPED (`text`), or a quick
|
|
52
|
+
* reply they TAPPED (`reply`, the id of a button a declared template
|
|
53
|
+
* offers). Exactly one of the two.
|
|
54
|
+
*/
|
|
51
55
|
turns: z
|
|
52
56
|
.array(
|
|
53
57
|
z
|
|
54
58
|
.object({
|
|
55
|
-
text: z.string().min(1),
|
|
59
|
+
text: z.string().min(1).optional(),
|
|
60
|
+
reply: z.object({ id: z.string().min(1), title: z.string().min(1).optional() }).strict().optional(),
|
|
56
61
|
/** Seconds after the previous inbound turn, on the run's clock. Keeps a scripted run deterministic. */
|
|
57
62
|
after_seconds: z.number().int().nonnegative().default(0),
|
|
58
63
|
})
|
|
59
|
-
.strict()
|
|
64
|
+
.strict()
|
|
65
|
+
.refine((t) => (t.text === undefined) !== (t.reply === undefined), { message: "a turn is either text the person typed or a reply they tapped, exactly one" }),
|
|
60
66
|
)
|
|
61
67
|
.nonempty(),
|
|
62
68
|
})
|
|
@@ -86,6 +92,26 @@ export const TEMPLATE_REGISTRY_FILE = "templates.json";
|
|
|
86
92
|
* match is a `132000` from the Cloud API and there is no reason to learn that
|
|
87
93
|
* in production.
|
|
88
94
|
*/
|
|
95
|
+
/**
|
|
96
|
+
* A quick-reply button a template offers, and what tapping it MEANS.
|
|
97
|
+
*
|
|
98
|
+
* `intent` is the turn the model receives when the person taps it — written
|
|
99
|
+
* here, by whoever wrote the template, and never by the model. A tap carries
|
|
100
|
+
* an id and a title; the title is copy and can change, the id cannot, and the
|
|
101
|
+
* intent is what the id stands for. An id this registry does not declare is
|
|
102
|
+
* not a turn at all.
|
|
103
|
+
*/
|
|
104
|
+
export const QuickReplySchema = z
|
|
105
|
+
.object({
|
|
106
|
+
id: z.string().regex(/^[a-z0-9_]{1,64}$/, "a reply id is lower-case letters, digits and underscores"),
|
|
107
|
+
/** What the button says. Meta caps a quick reply at 20 characters. */
|
|
108
|
+
title: z.string().min(1).max(20),
|
|
109
|
+
intent: z.string().min(1),
|
|
110
|
+
})
|
|
111
|
+
.strict();
|
|
112
|
+
|
|
113
|
+
export type QuickReply = z.infer<typeof QuickReplySchema>;
|
|
114
|
+
|
|
89
115
|
export const WhatsAppTemplateSchema = z
|
|
90
116
|
.object({
|
|
91
117
|
/** Meta's own naming rule for a template. */
|
|
@@ -94,6 +120,15 @@ export const WhatsAppTemplateSchema = z
|
|
|
94
120
|
language: z.string().regex(/^[a-z]{2}(_[A-Z]{2})?$/, 'expected a language code, e.g. "pt_BR"'),
|
|
95
121
|
description: z.string().min(1),
|
|
96
122
|
body: z.string().min(1),
|
|
123
|
+
/** Quick replies the template carries (Meta allows up to three on a template). */
|
|
124
|
+
buttons: z.array(QuickReplySchema).max(3).optional(),
|
|
125
|
+
/**
|
|
126
|
+
* The template a poll sends when the outcome it must tell has no copy of
|
|
127
|
+
* its own: it says there is news and asks the person to answer, and states
|
|
128
|
+
* nothing about the outcome — so it can never be the wrong news. Exactly
|
|
129
|
+
* one per registry, taking no variables (`npm run check`).
|
|
130
|
+
*/
|
|
131
|
+
fallback: z.boolean().optional(),
|
|
97
132
|
})
|
|
98
133
|
.strict();
|
|
99
134
|
|
|
@@ -120,12 +155,23 @@ export const TemplateRegistrySchema = z
|
|
|
120
155
|
.strict()
|
|
121
156
|
.superRefine((registry, ctx) => {
|
|
122
157
|
const seen = new Set<string>();
|
|
158
|
+
const replyIds = new Set<string>();
|
|
123
159
|
for (const [i, template] of registry.templates.entries()) {
|
|
124
160
|
if (seen.has(template.name)) ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["templates", i, "name"], message: `${template.name} is declared twice` });
|
|
125
161
|
seen.add(template.name);
|
|
162
|
+
// A tap is resolved by its id alone, so one id means one intent across the whole registry.
|
|
163
|
+
for (const [j, button] of (template.buttons ?? []).entries()) {
|
|
164
|
+
if (replyIds.has(button.id)) ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["templates", i, "buttons", j, "id"], message: `reply id ${button.id} is declared twice` });
|
|
165
|
+
replyIds.add(button.id);
|
|
166
|
+
}
|
|
126
167
|
}
|
|
127
168
|
});
|
|
128
169
|
|
|
170
|
+
/** Every quick reply the registry declares, by id: the only taps that can become a turn. */
|
|
171
|
+
export function declaredReplies(templates: readonly WhatsAppTemplate[]): Map<string, QuickReply> {
|
|
172
|
+
return new Map(templates.flatMap((t) => t.buttons ?? []).map((b) => [b.id, b]));
|
|
173
|
+
}
|
|
174
|
+
|
|
129
175
|
export type TemplateRegistry = z.infer<typeof TemplateRegistrySchema>;
|
|
130
176
|
|
|
131
177
|
export function parseTemplateRegistry(text: string): TemplateRegistry {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { parse as parseYaml } from "yaml";
|
|
11
|
-
import { ConversationScriptSchema, TemplateRegistrySchema, TEMPLATE_REGISTRY_FILE, templateArity } from "./channels.js";
|
|
11
|
+
import { ConversationScriptSchema, TemplateRegistrySchema, TEMPLATE_REGISTRY_FILE, declaredReplies, templateArity, type TemplateRegistry } from "./channels.js";
|
|
12
12
|
import { GuardrailsSchema } from "./guardrails.js";
|
|
13
13
|
import { loadManifest, ManifestSchema, type LoadedManifest } from "./manifest.js";
|
|
14
14
|
import { MandateSchema } from "./mandate.js";
|
|
@@ -121,12 +121,35 @@ export function checkAgent(agentDir: string): CheckReport {
|
|
|
121
121
|
if (!manifest.channels.includes("whatsapp") && scripts.length > 0) {
|
|
122
122
|
error("channels_undeclared", `channels/whatsapp/ ships ${scripts.length} conversation(s) but agent.yaml does not declare the whatsapp channel`);
|
|
123
123
|
}
|
|
124
|
+
const registryPath = join(whatsappDir, TEMPLATE_REGISTRY_FILE);
|
|
125
|
+
let registry: TemplateRegistry | undefined;
|
|
126
|
+
if (existsSync(registryPath)) {
|
|
127
|
+
const parsed = TemplateRegistrySchema.safeParse(JSON.parse(readFileSync(registryPath, "utf8")));
|
|
128
|
+
if (parsed.success) registry = parsed.data;
|
|
129
|
+
}
|
|
130
|
+
const replies = declaredReplies(registry?.templates ?? []);
|
|
124
131
|
for (const file of scripts) {
|
|
125
132
|
const parsed = ConversationScriptSchema.safeParse(JSON.parse(readFileSync(join(whatsappDir, file), "utf8")));
|
|
126
133
|
if (!parsed.success) error("channels_script_invalid", `channels/whatsapp/${file}: ${parsed.error.message}`);
|
|
127
134
|
else if (parsed.data.name !== file.replace(/\.json$/, "")) error("channels_script_invalid", `channels/whatsapp/${file} declares name ${parsed.data.name}`);
|
|
135
|
+
else {
|
|
136
|
+
// A scripted tap must be one a declared template offers: the channel drops any other, so the script would test nothing.
|
|
137
|
+
for (const turn of parsed.data.turns) {
|
|
138
|
+
if (turn.reply && !replies.has(turn.reply.id)) error("channels_script_invalid", `channels/whatsapp/${file} taps ${turn.reply.id}, which no template in ${TEMPLATE_REGISTRY_FILE} offers`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// An agent on WhatsApp closes outcomes days later, outside the window, where
|
|
143
|
+
// only a template goes. One it has no copy for still has to reach the
|
|
144
|
+
// person, so the registry declares a fallback that states no outcome.
|
|
145
|
+
if (manifest.channels.includes("whatsapp")) {
|
|
146
|
+
if (!existsSync(registryPath)) error("channels_templates_missing", `agent.yaml declares the whatsapp channel and channels/whatsapp/${TEMPLATE_REGISTRY_FILE} does not exist`);
|
|
147
|
+
else if (registry) {
|
|
148
|
+
const fallbacks = registry.templates.filter((t) => t.fallback);
|
|
149
|
+
if (fallbacks.length !== 1) error("channels_templates_fallback", `channels/whatsapp/${TEMPLATE_REGISTRY_FILE} declares ${fallbacks.length} fallback template(s); it must declare exactly one, for an outcome the agent has no template for`);
|
|
150
|
+
else if (templateArity(fallbacks[0]!.body) !== 0) error("channels_templates_fallback", `the fallback template ${fallbacks[0]!.name} takes variables; it is sent when nothing specific can be said, so it takes none`);
|
|
151
|
+
}
|
|
128
152
|
}
|
|
129
|
-
const registryPath = join(whatsappDir, TEMPLATE_REGISTRY_FILE);
|
|
130
153
|
if (existsSync(registryPath)) {
|
|
131
154
|
const parsed = TemplateRegistrySchema.safeParse(JSON.parse(readFileSync(registryPath, "utf8")));
|
|
132
155
|
if (!parsed.success) error("channels_templates_invalid", `channels/whatsapp/${TEMPLATE_REGISTRY_FILE}: ${parsed.error.message}`);
|