@codespar/cli 0.14.0 → 0.16.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 +44 -17
- package/dist/commands/audit-replay.js +267 -0
- package/dist/commands/audit-replay.js.map +1 -0
- package/dist/commands/init.js +28 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.js +2 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/mandate-verify.js +75 -27
- package/dist/commands/mandate-verify.js.map +1 -1
- package/dist/config.js +17 -1
- package/dist/config.js.map +1 -1
- package/dist/did.js +262 -41
- package/dist/did.js.map +1 -1
- package/dist/index.js +33 -7
- package/dist/index.js.map +1 -1
- package/dist/surface.js +4 -1
- package/dist/surface.js.map +1 -1
- package/package.json +1 -1
- package/templates/bills-agent/README.md +8 -5
- package/templates/bills-agent/agents/bills-agent/.env.example +3 -3
- 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 +62 -22
- package/templates/bills-agent/agents/bills-agent/SYSTEM_PROMPT.md +1 -1
- package/templates/bills-agent/agents/bills-agent/agent.yaml +2 -2
- package/templates/bills-agent/agents/bills-agent/package.json +14 -11
- package/templates/bills-agent/agents/bills-agent/src/kit.ts +193 -0
- package/templates/bills-agent/agents/bills-agent/test/adversarial.test.ts +8 -7
- package/templates/bills-agent/agents/bills-agent/test/check.test.ts +19 -2
- package/templates/bills-agent/agents/bills-agent/test/cli.test.ts +72 -24
- package/templates/bills-agent/agents/bills-agent/test/fixtures/excursao-300.transcript.jsonl +2 -0
- package/templates/bills-agent/agents/bills-agent/test/fixtures/material-400.transcript.jsonl +2 -0
- package/templates/bills-agent/agents/bills-agent/test/inspect.test.ts +124 -0
- package/templates/bills-agent/agents/bills-agent/test/provider.test.ts +25 -0
- package/templates/bills-agent/agents/bills-agent/test/scenarios.test.ts +10 -9
- package/templates/bills-agent/agents/bills-agent/tsconfig.json +0 -1
- package/templates/bills-agent/package.json +5 -2
- package/templates/bills-agent/packages/agent-core/README.md +2 -0
- package/templates/bills-agent/packages/agent-core/package.json +1 -1
- package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +27 -6
- package/templates/bills-agent/packages/agent-core/src/api/rail.ts +16 -0
- package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +20 -7
- package/templates/bills-agent/packages/agent-core/src/approval.ts +16 -2
- package/templates/bills-agent/packages/agent-core/src/bundle.ts +50 -4
- package/templates/bills-agent/packages/agent-core/src/channels.ts +140 -0
- package/templates/bills-agent/packages/agent-core/src/check.ts +90 -8
- package/templates/bills-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/bills-agent/packages/agent-core/src/engine.ts +119 -18
- package/templates/bills-agent/packages/agent-core/src/hash.ts +13 -0
- package/templates/bills-agent/packages/agent-core/src/index.ts +3 -0
- package/templates/bills-agent/packages/agent-core/src/manifest.ts +1 -1
- package/templates/bills-agent/packages/agent-core/src/rail.ts +16 -2
- package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +401 -0
- package/templates/bills-agent/packages/agent-core/src/state-machine.ts +3 -1
- package/templates/bills-agent/packages/agent-core/src/stubs/charge-rail.ts +2 -0
- package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +19 -2
- package/templates/bills-agent/packages/agent-core/src/types.ts +28 -0
- package/templates/bills-agent/packages/agent-core/test/approval.test.ts +36 -2
- package/templates/bills-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/bills-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/bills-agent/packages/agent-core/test/engine.test.ts +73 -1
- package/templates/bills-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/bills-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/bills-agent/packages/agent-core/test/helpers.ts +1 -1
- package/templates/bills-agent/packages/agent-core/test/manifest.test.ts +2 -2
- package/templates/bills-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +51 -0
- package/templates/bills-agent/packages/agent-runtime/README.md +68 -0
- package/templates/bills-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/bills-agent/packages/agent-runtime/package.json +23 -0
- package/templates/bills-agent/{agents/bills-agent → packages/agent-runtime}/src/adversarial.ts +36 -44
- package/templates/bills-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +124 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
- package/templates/bills-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +245 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
- package/templates/bills-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/bills-agent/packages/agent-runtime/src/index.ts +12 -0
- package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +797 -0
- package/templates/bills-agent/packages/agent-runtime/src/kit.ts +206 -0
- package/templates/{collections-agent/agents/collections-agent → bills-agent/packages/agent-runtime}/src/poll.ts +22 -11
- package/templates/{collections-agent/agents/collections-agent → bills-agent/packages/agent-runtime}/src/scenarios.ts +50 -34
- package/templates/bills-agent/{agents/bills-agent → packages/agent-runtime}/src/setup.ts +88 -66
- package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +154 -0
- package/templates/{collections-agent/agents/collections-agent/channels/webhook/index.ts → bills-agent/packages/agent-runtime/src/webhook.ts} +1 -1
- package/templates/bills-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
- package/templates/bills-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/bills-agent/vitest.config.ts +1 -1
- package/templates/collections-agent/README.md +8 -5
- package/templates/collections-agent/agents/collections-agent/.env.example +14 -0
- package/templates/collections-agent/agents/collections-agent/AGENTS.md +6 -4
- package/templates/collections-agent/agents/collections-agent/CLAUDE.md +6 -4
- package/templates/collections-agent/agents/collections-agent/README.md +184 -16
- package/templates/collections-agent/agents/collections-agent/agent.yaml +3 -3
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +111 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042.json +11 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1103.json +11 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +23 -0
- package/templates/collections-agent/agents/collections-agent/package.json +12 -10
- package/templates/collections-agent/agents/collections-agent/runbook.md +52 -1
- package/templates/collections-agent/agents/collections-agent/src/kit.ts +265 -0
- package/templates/collections-agent/agents/collections-agent/test/adversarial.test.ts +9 -8
- package/templates/collections-agent/agents/collections-agent/test/check.test.ts +43 -1
- package/templates/collections-agent/agents/collections-agent/test/cli.test.ts +56 -18
- package/templates/collections-agent/agents/collections-agent/test/fixtures/agreed-1042-awaiting-payer.transcript.jsonl +4 -0
- package/templates/collections-agent/agents/collections-agent/test/scenarios.test.ts +11 -10
- package/templates/collections-agent/agents/collections-agent/test/webhook.test.ts +4 -7
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +271 -0
- package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +213 -0
- package/templates/collections-agent/agents/collections-agent/tsconfig.json +0 -1
- package/templates/collections-agent/package.json +3 -1
- package/templates/collections-agent/packages/agent-core/README.md +2 -0
- package/templates/collections-agent/packages/agent-core/package.json +1 -1
- package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +27 -6
- package/templates/collections-agent/packages/agent-core/src/api/rail.ts +16 -0
- package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +20 -7
- package/templates/collections-agent/packages/agent-core/src/approval.ts +16 -2
- package/templates/collections-agent/packages/agent-core/src/bundle.ts +50 -4
- package/templates/collections-agent/packages/agent-core/src/channels.ts +140 -0
- package/templates/collections-agent/packages/agent-core/src/check.ts +90 -8
- package/templates/collections-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/collections-agent/packages/agent-core/src/engine.ts +119 -18
- package/templates/collections-agent/packages/agent-core/src/hash.ts +13 -0
- package/templates/collections-agent/packages/agent-core/src/index.ts +3 -0
- package/templates/collections-agent/packages/agent-core/src/manifest.ts +1 -1
- package/templates/collections-agent/packages/agent-core/src/rail.ts +16 -2
- package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +401 -0
- package/templates/collections-agent/packages/agent-core/src/state-machine.ts +3 -1
- package/templates/collections-agent/packages/agent-core/src/stubs/charge-rail.ts +2 -0
- package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +19 -2
- package/templates/collections-agent/packages/agent-core/src/types.ts +28 -0
- package/templates/collections-agent/packages/agent-core/test/approval.test.ts +36 -2
- package/templates/collections-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/collections-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/collections-agent/packages/agent-core/test/engine.test.ts +73 -1
- package/templates/collections-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/collections-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/collections-agent/packages/agent-core/test/helpers.ts +1 -1
- package/templates/collections-agent/packages/agent-core/test/manifest.test.ts +2 -2
- package/templates/collections-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +51 -0
- package/templates/collections-agent/packages/agent-runtime/README.md +68 -0
- package/templates/collections-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/collections-agent/packages/agent-runtime/package.json +23 -0
- package/templates/collections-agent/{agents/collections-agent → packages/agent-runtime}/src/adversarial.ts +29 -50
- package/templates/collections-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +124 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
- package/templates/collections-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +245 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
- package/templates/collections-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/collections-agent/packages/agent-runtime/src/index.ts +12 -0
- package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +797 -0
- package/templates/collections-agent/packages/agent-runtime/src/kit.ts +206 -0
- package/templates/collections-agent/packages/agent-runtime/src/poll.ts +105 -0
- package/templates/{bills-agent/agents/bills-agent → collections-agent/packages/agent-runtime}/src/scenarios.ts +98 -26
- package/templates/collections-agent/packages/agent-runtime/src/setup.ts +229 -0
- package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +154 -0
- package/templates/collections-agent/packages/agent-runtime/src/webhook.ts +131 -0
- package/templates/collections-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
- package/templates/collections-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/collections-agent/vitest.config.ts +1 -1
- package/templates/hello-agent/LICENSE +21 -0
- package/templates/hello-agent/README.md +23 -0
- package/templates/hello-agent/_gitignore +10 -0
- package/templates/hello-agent/agents/hello-agent/.env.example +5 -0
- package/templates/hello-agent/agents/hello-agent/AGENTS.md +15 -0
- package/templates/hello-agent/agents/hello-agent/CLAUDE.md +15 -0
- package/templates/hello-agent/agents/hello-agent/README.md +33 -0
- package/templates/hello-agent/agents/hello-agent/SYSTEM_PROMPT.md +19 -0
- package/templates/hello-agent/agents/hello-agent/agent.yaml +19 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/beneficiary-swap.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/false-authority.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/false-authority.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/fractioning.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/fractioning.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/model-total.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/model-total.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/webhook-replay.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/eval.yaml +7 -0
- package/templates/hello-agent/agents/hello-agent/guardrails.json +8 -0
- package/templates/hello-agent/agents/hello-agent/mandate.example.json +17 -0
- package/templates/hello-agent/agents/hello-agent/package.json +19 -0
- package/templates/hello-agent/agents/hello-agent/runbook.md +10 -0
- package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.json +6 -0
- package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.json +6 -0
- package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/src/bills.ts +23 -0
- package/templates/hello-agent/agents/hello-agent/src/kit.ts +33 -0
- package/templates/hello-agent/agents/hello-agent/tools.json +11 -0
- package/templates/hello-agent/agents/hello-agent/tsconfig.json +9 -0
- package/templates/hello-agent/package.json +30 -0
- package/templates/hello-agent/packages/agent-core/README.md +21 -0
- package/templates/hello-agent/packages/agent-core/package.json +24 -0
- package/templates/hello-agent/packages/agent-core/src/agent.ts +125 -0
- package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +175 -0
- package/templates/hello-agent/packages/agent-core/src/api/client.ts +53 -0
- package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +58 -0
- package/templates/hello-agent/packages/agent-core/src/api/rail.ts +117 -0
- package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +58 -0
- package/templates/hello-agent/packages/agent-core/src/approval.ts +123 -0
- package/templates/hello-agent/packages/agent-core/src/bundle.ts +159 -0
- package/templates/hello-agent/packages/agent-core/src/channels.ts +140 -0
- package/templates/hello-agent/packages/agent-core/src/check.ts +253 -0
- package/templates/hello-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/hello-agent/packages/agent-core/src/engine.ts +885 -0
- package/templates/hello-agent/packages/agent-core/src/escalate.ts +85 -0
- package/templates/hello-agent/packages/agent-core/src/events.ts +40 -0
- package/templates/hello-agent/packages/agent-core/src/guardrails.ts +45 -0
- package/templates/hello-agent/packages/agent-core/src/hash.ts +62 -0
- package/templates/hello-agent/packages/agent-core/src/ids.ts +10 -0
- package/templates/hello-agent/packages/agent-core/src/index.ts +32 -0
- package/templates/hello-agent/packages/agent-core/src/mandate.ts +100 -0
- package/templates/hello-agent/packages/agent-core/src/manifest.ts +76 -0
- package/templates/hello-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
- package/templates/hello-agent/packages/agent-core/src/providers/replay.ts +63 -0
- package/templates/hello-agent/packages/agent-core/src/providers/types.ts +39 -0
- package/templates/hello-agent/packages/agent-core/src/rail.ts +101 -0
- package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +401 -0
- package/templates/hello-agent/packages/agent-core/src/revocation.ts +30 -0
- package/templates/hello-agent/packages/agent-core/src/secrets.ts +30 -0
- package/templates/hello-agent/packages/agent-core/src/state/store.ts +307 -0
- package/templates/hello-agent/packages/agent-core/src/state-machine.ts +148 -0
- package/templates/hello-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
- package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +57 -0
- package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +141 -0
- package/templates/hello-agent/packages/agent-core/src/tools.ts +55 -0
- package/templates/hello-agent/packages/agent-core/src/types.ts +134 -0
- package/templates/hello-agent/packages/agent-core/test/approval.test.ts +117 -0
- package/templates/hello-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/hello-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/hello-agent/packages/agent-core/test/engine.test.ts +596 -0
- package/templates/hello-agent/packages/agent-core/test/escalate.test.ts +42 -0
- package/templates/hello-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/hello-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/hello-agent/packages/agent-core/test/helpers.ts +139 -0
- package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +153 -0
- package/templates/hello-agent/packages/agent-core/test/manifest.test.ts +45 -0
- package/templates/hello-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +316 -0
- package/templates/hello-agent/packages/agent-core/test/secrets.test.ts +28 -0
- package/templates/hello-agent/packages/agent-core/test/state-machine.test.ts +67 -0
- package/templates/hello-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
- package/templates/hello-agent/packages/agent-core/tsconfig.json +10 -0
- package/templates/hello-agent/packages/agent-runtime/README.md +68 -0
- package/templates/hello-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/hello-agent/packages/agent-runtime/package.json +23 -0
- package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +169 -0
- package/templates/hello-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +124 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
- package/templates/hello-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +245 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
- package/templates/hello-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/hello-agent/packages/agent-runtime/src/index.ts +12 -0
- package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +797 -0
- package/templates/hello-agent/packages/agent-runtime/src/kit.ts +206 -0
- package/templates/hello-agent/packages/agent-runtime/src/poll.ts +105 -0
- package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +268 -0
- package/templates/hello-agent/packages/agent-runtime/src/setup.ts +229 -0
- package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +154 -0
- package/templates/hello-agent/packages/agent-runtime/src/webhook.ts +131 -0
- package/templates/hello-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
- package/templates/hello-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/hello-agent/tsconfig.base.json +16 -0
- package/templates/hello-agent/vitest.config.ts +12 -0
- package/templates/kits.lock.json +60 -12
- package/templates/supplier-payments-agent/LICENSE +21 -0
- package/templates/supplier-payments-agent/README.md +23 -0
- package/templates/supplier-payments-agent/_gitignore +10 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/.env.example +7 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +21 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +21 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +118 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/SYSTEM_PROMPT.md +29 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/agent.yaml +26 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/batch-tampering.json +12 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/batch-tampering.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap-mandate.json +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap.json +10 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.json +13 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.json +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/fractioning.json +15 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/fractioning.transcript.jsonl +6 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/model-total.json +10 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/model-total.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.json +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/webhook-replay.json +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/eval.yaml +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/guardrails.json +15 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/mandate.example.json +70 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/package.json +26 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/runbook.md +19 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.json +14 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.json +12 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.json +20 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.transcript.jsonl +7 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.json +19 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.json +21 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.transcript.jsonl +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.json +21 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.json +12 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +176 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +345 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/pix-out.ts +118 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/payables.ts +76 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/adversarial.test.ts +53 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +384 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/check.test.ts +109 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/scenarios.test.ts +81 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/tools.json +53 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/tsconfig.json +9 -0
- package/templates/supplier-payments-agent/package.json +36 -0
- package/templates/supplier-payments-agent/packages/agent-core/README.md +21 -0
- package/templates/supplier-payments-agent/packages/agent-core/package.json +24 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +125 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +175 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +53 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +58 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +117 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +58 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +123 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +159 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +140 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +253 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +885 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/escalate.ts +85 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/events.ts +40 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/guardrails.ts +45 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +62 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/ids.ts +10 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +32 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/mandate.ts +100 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/manifest.ts +76 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/providers/replay.ts +63 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/providers/types.ts +39 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +101 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +401 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +30 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/secrets.ts +30 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/state/store.ts +307 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +148 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +57 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +141 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +55 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +134 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/approval.test.ts +117 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/engine.test.ts +596 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/escalate.test.ts +42 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +139 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +153 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/manifest.test.ts +45 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +316 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/secrets.test.ts +28 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/state-machine.test.ts +67 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
- package/templates/supplier-payments-agent/packages/agent-core/tsconfig.json +10 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/README.md +68 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/package.json +23 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +169 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +124 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +245 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +12 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +797 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +206 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/poll.ts +105 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +268 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +229 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +154 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/webhook.ts +131 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/supplier-payments-agent/tsconfig.base.json +16 -0
- package/templates/supplier-payments-agent/vitest.config.ts +12 -0
- package/templates/bills-agent/agents/bills-agent/channels/terminal/index.ts +0 -108
- package/templates/bills-agent/agents/bills-agent/src/commands/approve.ts +0 -9
- package/templates/bills-agent/agents/bills-agent/src/commands/check.ts +0 -13
- package/templates/bills-agent/agents/bills-agent/src/commands/consent.ts +0 -26
- package/templates/bills-agent/agents/bills-agent/src/commands/decide.ts +0 -45
- package/templates/bills-agent/agents/bills-agent/src/commands/deny.ts +0 -7
- package/templates/bills-agent/agents/bills-agent/src/commands/eval.ts +0 -37
- package/templates/bills-agent/agents/bills-agent/src/commands/reconcile.ts +0 -38
- package/templates/bills-agent/agents/bills-agent/src/commands/rerun.ts +0 -65
- package/templates/bills-agent/agents/bills-agent/src/commands/resume.ts +0 -30
- package/templates/bills-agent/agents/bills-agent/src/main.ts +0 -185
- package/templates/collections-agent/agents/collections-agent/channels/terminal/index.ts +0 -178
- package/templates/collections-agent/agents/collections-agent/src/commands/approve.ts +0 -10
- package/templates/collections-agent/agents/collections-agent/src/commands/check.ts +0 -13
- package/templates/collections-agent/agents/collections-agent/src/commands/decide.ts +0 -48
- package/templates/collections-agent/agents/collections-agent/src/commands/deny.ts +0 -7
- package/templates/collections-agent/agents/collections-agent/src/commands/eval.ts +0 -39
- package/templates/collections-agent/agents/collections-agent/src/commands/poll.ts +0 -35
- package/templates/collections-agent/agents/collections-agent/src/commands/reconcile.ts +0 -38
- package/templates/collections-agent/agents/collections-agent/src/commands/rerun.ts +0 -55
- package/templates/collections-agent/agents/collections-agent/src/commands/resume.ts +0 -30
- package/templates/collections-agent/agents/collections-agent/src/commands/webhook.ts +0 -36
- package/templates/collections-agent/agents/collections-agent/src/main.ts +0 -193
- package/templates/collections-agent/agents/collections-agent/src/setup.ts +0 -250
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The official backend, without Meta.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here reaches the network. What is driven is the half where a
|
|
5
|
+
* mistake is silent: the signature check (a wrong one that passes is an open
|
|
6
|
+
* webhook), the webhook parse (a shape misread is a message never answered),
|
|
7
|
+
* the verification handshake, and the request builder. The send itself is one
|
|
8
|
+
* `fetch` over a request these functions built, and the one test that touches
|
|
9
|
+
* `deliver` injects a fetch that asserts what it was handed — so a regression
|
|
10
|
+
* that started calling Meta would fail here instead of dialling out.
|
|
11
|
+
*
|
|
12
|
+
* The shapes come from Meta's published documentation. Nobody on this repo
|
|
13
|
+
* has a Business account, so none of this has been run against Meta, and the
|
|
14
|
+
* README says so in those words rather than implying otherwise.
|
|
15
|
+
*/
|
|
16
|
+
import { createHmac } from "node:crypto";
|
|
17
|
+
import { describe, expect, it } from "vitest";
|
|
18
|
+
import {
|
|
19
|
+
buildSendRequest,
|
|
20
|
+
isMetaBaseUrl,
|
|
21
|
+
loadCloudApiConfig,
|
|
22
|
+
parseInbound,
|
|
23
|
+
verifyChallenge,
|
|
24
|
+
verifyWebhookSignature,
|
|
25
|
+
WhatsAppCloudApi,
|
|
26
|
+
type CloudApiConfig,
|
|
27
|
+
} from "../src/channels/whatsapp/cloud-api.js";
|
|
28
|
+
|
|
29
|
+
const CONFIG: CloudApiConfig = {
|
|
30
|
+
baseUrl: "https://graph.facebook.com",
|
|
31
|
+
phoneNumberId: "111222333",
|
|
32
|
+
accessToken: "test-token-not-a-credential",
|
|
33
|
+
verifyToken: "verify-me",
|
|
34
|
+
appSecret: "app-secret-for-the-test",
|
|
35
|
+
apiVersion: "v21.0",
|
|
36
|
+
webhookPort: 3111,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const sign = (body: string, secret = CONFIG.appSecret) => `sha256=${createHmac("sha256", secret).update(body, "utf8").digest("hex")}`;
|
|
40
|
+
|
|
41
|
+
describe("credentials are absent by default", () => {
|
|
42
|
+
it("names every variable that is missing rather than saying 'misconfigured'", () => {
|
|
43
|
+
const { config, missing } = loadCloudApiConfig({});
|
|
44
|
+
expect(config).toBeUndefined();
|
|
45
|
+
expect(missing).toEqual(["WHATSAPP_PHONE_NUMBER_ID", "WHATSAPP_ACCESS_TOKEN", "WHATSAPP_VERIFY_TOKEN", "WHATSAPP_APP_SECRET"]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("treats an empty value as absent: a commented-out .env.example must not half-configure a channel", () => {
|
|
49
|
+
const { config, missing } = loadCloudApiConfig({ WHATSAPP_PHONE_NUMBER_ID: " ", WHATSAPP_ACCESS_TOKEN: "", WHATSAPP_VERIFY_TOKEN: "v", WHATSAPP_APP_SECRET: "s" });
|
|
50
|
+
expect(config).toBeUndefined();
|
|
51
|
+
expect(missing).toEqual(["WHATSAPP_PHONE_NUMBER_ID", "WHATSAPP_ACCESS_TOKEN"]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("defaults the version, the port and Meta's own host, and nothing else", () => {
|
|
55
|
+
const { config } = loadCloudApiConfig({ WHATSAPP_PHONE_NUMBER_ID: "1", WHATSAPP_ACCESS_TOKEN: "2", WHATSAPP_VERIFY_TOKEN: "3", WHATSAPP_APP_SECRET: "4" });
|
|
56
|
+
expect(config?.apiVersion).toBe("v21.0");
|
|
57
|
+
expect(config?.webhookPort).toBe(3111);
|
|
58
|
+
expect(config?.baseUrl).toBe("https://graph.facebook.com");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("live is decided by the base URL, and only by it", () => {
|
|
63
|
+
it("is Meta's host and nothing that merely looks like it", () => {
|
|
64
|
+
expect(isMetaBaseUrl("https://graph.facebook.com")).toBe(true);
|
|
65
|
+
expect(isMetaBaseUrl("https://graph.facebook.com/")).toBe(true);
|
|
66
|
+
expect(isMetaBaseUrl("http://127.0.0.1:4290")).toBe(false);
|
|
67
|
+
expect(isMetaBaseUrl("https://graph.facebook.com.evil.test")).toBe(false);
|
|
68
|
+
expect(isMetaBaseUrl("not a url")).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("names itself `emulator` and reports `live: false` off Meta, which is what the evidence builder refuses on", () => {
|
|
72
|
+
const options = { conversation: { contact: "+5511987654321" }, say: () => undefined, fetchImpl: async () => new Response("{}") };
|
|
73
|
+
const emulator = new WhatsAppCloudApi({ ...options, config: { ...CONFIG, baseUrl: "http://127.0.0.1:4290" } });
|
|
74
|
+
expect(emulator.live).toBe(false);
|
|
75
|
+
expect(emulator.name).toBe("emulator");
|
|
76
|
+
const meta = new WhatsAppCloudApi({ ...options, config: CONFIG });
|
|
77
|
+
expect(meta.live).toBe(true);
|
|
78
|
+
expect(meta.name).toBe("cloud-api");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("builds the send against whichever base URL it was given, with the path unchanged", () => {
|
|
82
|
+
const request = buildSendRequest({ ...CONFIG, baseUrl: "http://127.0.0.1:4290", apiVersion: "v22.0" }, "+5511987654321", { kind: "text", text: "oi" });
|
|
83
|
+
if ("unsupported" in request) throw new Error("expected a request");
|
|
84
|
+
expect(request.url).toBe("http://127.0.0.1:4290/v22.0/111222333/messages");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("a delivery is trusted only when it is signed", () => {
|
|
89
|
+
const body = JSON.stringify({ object: "whatsapp_business_account" });
|
|
90
|
+
|
|
91
|
+
it("accepts the app secret's own signature over the RAW body", () => {
|
|
92
|
+
expect(verifyWebhookSignature(CONFIG.appSecret, sign(body), body)).toEqual({ ok: true });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("refuses a body that was re-serialized: the signature is over bytes", () => {
|
|
96
|
+
const reserialized = JSON.stringify(JSON.parse(body), null, 2);
|
|
97
|
+
expect(verifyWebhookSignature(CONFIG.appSecret, sign(body), reserialized).ok).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("refuses another secret, a missing header and a malformed one", () => {
|
|
101
|
+
expect(verifyWebhookSignature(CONFIG.appSecret, sign(body, "someone-else"), body).ok).toBe(false);
|
|
102
|
+
expect(verifyWebhookSignature(CONFIG.appSecret, undefined, body)).toEqual({ ok: false, reason: "missing X-Hub-Signature-256" });
|
|
103
|
+
expect(verifyWebhookSignature(CONFIG.appSecret, "sha1=abc", body).ok).toBe(false);
|
|
104
|
+
expect(verifyWebhookSignature(CONFIG.appSecret, "nonsense", body).ok).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("the registration handshake", () => {
|
|
109
|
+
it("echoes the challenge for the right verify token", () => {
|
|
110
|
+
expect(verifyChallenge(CONFIG, { "hub.mode": "subscribe", "hub.verify_token": "verify-me", "hub.challenge": "12345" })).toEqual({ status: 200, body: "12345" });
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("refuses a wrong token and a wrong mode", () => {
|
|
114
|
+
expect(verifyChallenge(CONFIG, { "hub.mode": "subscribe", "hub.verify_token": "nope", "hub.challenge": "1" }).status).toBe(403);
|
|
115
|
+
expect(verifyChallenge(CONFIG, { "hub.mode": "unsubscribe", "hub.verify_token": "verify-me" }).status).toBe(400);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("reading what Meta delivers", () => {
|
|
120
|
+
const delivery = {
|
|
121
|
+
object: "whatsapp_business_account",
|
|
122
|
+
entry: [
|
|
123
|
+
{
|
|
124
|
+
changes: [
|
|
125
|
+
{
|
|
126
|
+
field: "messages",
|
|
127
|
+
value: {
|
|
128
|
+
messaging_product: "whatsapp",
|
|
129
|
+
metadata: { phone_number_id: "111222333" },
|
|
130
|
+
messages: [
|
|
131
|
+
{ id: "wamid.HBgNNTUxMTk4NzY1NDMyMRUCABIYEjND", from: "5511987654321", timestamp: "1758643200", type: "text", text: { body: "fechado, pago à vista" } },
|
|
132
|
+
{ id: "wamid.SECOND", from: "5511987654321", timestamp: "1758643260", type: "image", image: { id: "media_1" } },
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
it("reads the text messages and puts the plus back on the number", () => {
|
|
142
|
+
const { messages } = parseInbound(delivery);
|
|
143
|
+
expect(messages).toEqual([{ id: "wamid.HBgNNTUxMTk4NzY1NDMyMRUCABIYEjND", from: "+5511987654321", text: "fechado, pago à vista", timestamp: 1758643200 }]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("names what it dropped instead of turning it into an empty message", () => {
|
|
147
|
+
expect(parseInbound(delivery).ignored).toEqual([{ id: "wamid.SECOND", type: "image" }]);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("reads a delivery that carries only status updates as no messages", () => {
|
|
151
|
+
expect(parseInbound({ entry: [{ changes: [{ value: { statuses: [{ id: "wamid.X", status: "delivered" }] } }] }] }).messages).toEqual([]);
|
|
152
|
+
expect(parseInbound({}).messages).toEqual([]);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("the request that would go to Meta", () => {
|
|
157
|
+
it("builds the documented /messages call for a text", () => {
|
|
158
|
+
const request = buildSendRequest(CONFIG, "+5511987654321", { kind: "text", text: "Oi, Joana!" });
|
|
159
|
+
expect("unsupported" in request).toBe(false);
|
|
160
|
+
if ("unsupported" in request) return;
|
|
161
|
+
expect(request.url).toBe("https://graph.facebook.com/v21.0/111222333/messages");
|
|
162
|
+
expect(request.headers["authorization"]).toBe("Bearer test-token-not-a-credential");
|
|
163
|
+
expect(JSON.parse(request.body)).toEqual({ messaging_product: "whatsapp", recipient_type: "individual", to: "5511987654321", type: "text", text: { preview_url: false, body: "Oi, Joana!" } });
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("sends a copy-and-paste as its own plain text message: a code inside a paragraph cannot be tapped", () => {
|
|
167
|
+
const request = buildSendRequest(CONFIG, "+5511987654321", { kind: "instrument", instrument: "pix_copy_paste", value: "00020126580014br.gov.bcb.pix" });
|
|
168
|
+
if ("unsupported" in request) throw new Error("expected a request");
|
|
169
|
+
expect(JSON.parse(request.body).text.body).toBe("00020126580014br.gov.bcb.pix");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("builds a template with its body parameters", () => {
|
|
173
|
+
const request = buildSendRequest(CONFIG, "+5511987654321", { kind: "template", template: "cobranca_lembrete", language: "pt_BR", variables: ["Joana", "1042"] });
|
|
174
|
+
if ("unsupported" in request) throw new Error("expected a request");
|
|
175
|
+
expect(JSON.parse(request.body).template).toEqual({
|
|
176
|
+
name: "cobranca_lembrete",
|
|
177
|
+
language: { code: "pt_BR" },
|
|
178
|
+
components: [{ type: "body", parameters: [{ type: "text", text: "Joana" }, { type: "text", text: "1042" }] }],
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("says outright that it cannot send an image, instead of pretending", () => {
|
|
183
|
+
expect(buildSendRequest(CONFIG, "+5511987654321", { kind: "media", media: "qr", data: "00020126" })).toEqual({ unsupported: "media_upload_unimplemented" });
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("deliver, with the network replaced by an assertion", () => {
|
|
188
|
+
it("posts the request the builder produced and returns the provider's message id", async () => {
|
|
189
|
+
const calls: Array<{ url: string; init: RequestInit | undefined }> = [];
|
|
190
|
+
const backend = new WhatsAppCloudApi({
|
|
191
|
+
config: CONFIG,
|
|
192
|
+
conversation: { contact: "+5511987654321" },
|
|
193
|
+
say: () => undefined,
|
|
194
|
+
fetchImpl: async (url, init) => {
|
|
195
|
+
calls.push({ url: String(url), init });
|
|
196
|
+
return new Response(JSON.stringify({ messages: [{ id: "wamid.SENT" }] }), { status: 200 });
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
const sent = await backend.deliver("+5511987654321", { kind: "text", text: "Oi!" });
|
|
200
|
+
expect(sent).toEqual({ id: "wamid.SENT", state: "sent" });
|
|
201
|
+
expect(calls).toHaveLength(1);
|
|
202
|
+
expect(calls[0]!.url).toBe("https://graph.facebook.com/v21.0/111222333/messages");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("refuses an image without reaching for the network at all", async () => {
|
|
206
|
+
let called = false;
|
|
207
|
+
const backend = new WhatsAppCloudApi({
|
|
208
|
+
config: CONFIG,
|
|
209
|
+
conversation: { contact: "+5511987654321" },
|
|
210
|
+
say: () => undefined,
|
|
211
|
+
fetchImpl: async () => {
|
|
212
|
+
called = true;
|
|
213
|
+
return new Response("{}", { status: 200 });
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
const sent = await backend.deliver("+5511987654321", { kind: "media", media: "qr", data: "00020126" });
|
|
217
|
+
expect(sent.refused?.rule).toBe("media_upload_unimplemented");
|
|
218
|
+
expect(called).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("reports a provider refusal as a failure and never as a send", async () => {
|
|
222
|
+
const backend = new WhatsAppCloudApi({
|
|
223
|
+
config: CONFIG,
|
|
224
|
+
conversation: { contact: "+5511987654321" },
|
|
225
|
+
say: () => undefined,
|
|
226
|
+
fetchImpl: async () => new Response(JSON.stringify({ error: { message: "Invalid parameter" } }), { status: 400 }),
|
|
227
|
+
});
|
|
228
|
+
const sent = await backend.deliver("+5511987654321", { kind: "text", text: "Oi!" });
|
|
229
|
+
expect(sent.state).toBe("failed");
|
|
230
|
+
expect(sent.refused?.rule).toBe("provider_refused");
|
|
231
|
+
});
|
|
232
|
+
});
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the emulator does, measured rather than assumed — and what it does NOT
|
|
3
|
+
* do, which is the more useful half.
|
|
4
|
+
*
|
|
5
|
+
* These run against `dyvit-wa-sim` (https://github.com/fabianocruz/whatsapp-simulator,
|
|
6
|
+
* MIT) at the version `scripts/whatsapp-emulator.mjs` pins. They SKIP when it is
|
|
7
|
+
* not listening, so `npm test` is green on a machine that never started it; the
|
|
8
|
+
* CI starts it, and `npm run whatsapp:gate` fails loudly rather than skipping,
|
|
9
|
+
* so nothing important hides behind a skip.
|
|
10
|
+
*
|
|
11
|
+
* Two kinds of assertion live here and they are not the same thing.
|
|
12
|
+
*
|
|
13
|
+
* What the emulator gets RIGHT, which our adapter depends on: the Cloud API
|
|
14
|
+
* response shape, the signed webhook, the controllable clock. If one of these
|
|
15
|
+
* breaks, our channel breaks.
|
|
16
|
+
*
|
|
17
|
+
* What the emulator does NOT do, which our channel has to cover itself. Those
|
|
18
|
+
* assertions are written to FAIL the day the emulator closes the gap — which
|
|
19
|
+
* is what we want, because the pin means it can only change when we move it,
|
|
20
|
+
* and the failure is the notification. They are a spec for the repo's owner,
|
|
21
|
+
* not a complaint: `docs/OPEN_QUESTIONS.md` §46 carries the same list in
|
|
22
|
+
* prose with the payloads.
|
|
23
|
+
*/
|
|
24
|
+
import { describe, expect, it } from "vitest";
|
|
25
|
+
import { EmulatorDriver } from "../src/channels/whatsapp/emulator.js";
|
|
26
|
+
import { buildSendRequest, type CloudApiConfig } from "../src/channels/whatsapp/cloud-api.js";
|
|
27
|
+
|
|
28
|
+
const URL_BASE = process.env["WHATSAPP_SIM_URL"] ?? "http://127.0.0.1:4290";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Probed at MODULE level, not in a `beforeAll`: `describe.skipIf` is read when
|
|
32
|
+
* the file is collected, which is before any hook has run, so a flag set in a
|
|
33
|
+
* hook would skip every case on a machine where the emulator is up.
|
|
34
|
+
*/
|
|
35
|
+
const up = await (async () => {
|
|
36
|
+
try {
|
|
37
|
+
return (await fetch(`${URL_BASE}/health`, { signal: AbortSignal.timeout(2000) })).ok;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
})();
|
|
42
|
+
if (!up) process.stderr.write(`[whatsapp] no emulator at ${URL_BASE}; its integration cases are skipped. Start it with \`npm run whatsapp:emulator\`.\n`);
|
|
43
|
+
|
|
44
|
+
/** A fresh phone-number id per case, because the emulator keys a conversation on it. */
|
|
45
|
+
const pnid = () => `9${String(Math.floor(Math.random() * 1e11)).padStart(11, "0")}`;
|
|
46
|
+
const TO = "5511987654321";
|
|
47
|
+
|
|
48
|
+
const config = (phoneNumberId: string): CloudApiConfig => ({
|
|
49
|
+
baseUrl: URL_BASE,
|
|
50
|
+
phoneNumberId,
|
|
51
|
+
accessToken: "emulator",
|
|
52
|
+
verifyToken: "emulator",
|
|
53
|
+
appSecret: "dev",
|
|
54
|
+
apiVersion: "v22.0",
|
|
55
|
+
webhookPort: 0,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
async function send(phoneNumberId: string, body: Parameters<typeof buildSendRequest>[2]) {
|
|
59
|
+
const request = buildSendRequest(config(phoneNumberId), `+${TO}`, body);
|
|
60
|
+
if ("unsupported" in request) return { status: 0, unsupported: request.unsupported, body: null as unknown };
|
|
61
|
+
const response = await fetch(request.url, { method: request.method, headers: request.headers, body: request.body });
|
|
62
|
+
return { status: response.status, unsupported: null, body: (await response.json()) as Record<string, unknown> };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function raw(path: string, init?: RequestInit) {
|
|
66
|
+
const response = await fetch(`${URL_BASE}${path}`, init);
|
|
67
|
+
return { status: response.status, body: (await response.json().catch(() => null)) as Record<string, unknown> | null };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe.skipIf(!up)("what our adapter depends on, and the emulator provides", () => {
|
|
71
|
+
it("answers our text send with the Cloud API's own response shape", async () => {
|
|
72
|
+
const id = pnid();
|
|
73
|
+
const result = await send(id, { kind: "text", text: "Oi, Joana!" });
|
|
74
|
+
expect(result.status).toBe(200);
|
|
75
|
+
expect(result.body).toMatchObject({ messaging_product: "whatsapp" });
|
|
76
|
+
const messages = (result.body as { messages: Array<{ id: string; message_status: string }> }).messages;
|
|
77
|
+
expect(messages[0]!.id).toMatch(/^wamid\./);
|
|
78
|
+
expect(messages[0]!.message_status).toBe("accepted");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("takes the Pix copy-and-paste as a plain text message: the string that pays goes through unchanged", async () => {
|
|
82
|
+
const brcode = "00020126580014br.gov.bcb.pix0136stub-chg_abc5204000053039865802BR5909CODESPAR6009SAO PAULO62070503***6304STUB";
|
|
83
|
+
const result = await send(pnid(), { kind: "instrument", instrument: "pix_copy_paste", value: brcode });
|
|
84
|
+
expect(result.status).toBe(200);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("takes a template with its body parameters", async () => {
|
|
88
|
+
const result = await send(pnid(), { kind: "template", template: "cobranca_lembrete", language: "pt_BR", variables: ["Joana"] });
|
|
89
|
+
expect(result.status).toBe(200);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("has a conversation clock we can pin and move, which is the only way a 24h window ever closes in a replay", async () => {
|
|
93
|
+
const driver = new EmulatorDriver(URL_BASE);
|
|
94
|
+
const pinned = (await driver.pin(new Date("2026-09-23T17:00:00.000Z"))) as { now: string };
|
|
95
|
+
expect(pinned.now).toBe("2026-09-23T17:00:00.000Z");
|
|
96
|
+
const moved = (await driver.advanceHours(26)) as { now: string };
|
|
97
|
+
expect(Date.parse(moved.now) - Date.parse(pinned.now)).toBeGreaterThanOrEqual(26 * 3600 * 1000);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("delivers an inbound message as a webhook our own parser reads", async () => {
|
|
101
|
+
const id = pnid();
|
|
102
|
+
const driver = new EmulatorDriver(URL_BASE);
|
|
103
|
+
await driver.inbound({ phoneNumberId: id, from: `+${TO}`, text: "oi, recebi a mensagem" });
|
|
104
|
+
const { body } = await raw("/_sim/webhooks");
|
|
105
|
+
const deliveries = (body as { deliveries: Array<{ body: unknown }> }).deliveries;
|
|
106
|
+
const mine = deliveries.map((d) => d.body).filter((b) => JSON.stringify(b).includes(id));
|
|
107
|
+
expect(mine.length).toBeGreaterThan(0);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The gaps. Each one is the exact payload that failed, so the assertion doubles
|
|
113
|
+
* as the report. They are written against the PINNED sha: a green run here
|
|
114
|
+
* means the gap is still open, and a red one means it was closed and our own
|
|
115
|
+
* cover for it can be reconsidered.
|
|
116
|
+
*/
|
|
117
|
+
describe.skipIf(!up)("what the emulator does not do, measured", () => {
|
|
118
|
+
it("GAP: accepts a free-form message outside the 24h window that Meta refuses with 131047 — and it already knows", async () => {
|
|
119
|
+
const id = pnid();
|
|
120
|
+
const driver = new EmulatorDriver(URL_BASE);
|
|
121
|
+
await driver.pin(new Date("2026-09-23T17:00:00.000Z"));
|
|
122
|
+
await driver.inbound({ phoneNumberId: id, from: `+${TO}`, text: "oi" });
|
|
123
|
+
expect((await send(id, { kind: "text", text: "dentro da janela" })).status).toBe(200);
|
|
124
|
+
|
|
125
|
+
await driver.advanceHours(26);
|
|
126
|
+
const outside = await send(id, { kind: "text", text: "Recebemos, acordo quitado." });
|
|
127
|
+
// The real Cloud API answers 400 with error 131047 and sends nothing.
|
|
128
|
+
expect(outside.status).toBe(200);
|
|
129
|
+
|
|
130
|
+
// And this is what makes it a one-line fix rather than a feature: the
|
|
131
|
+
// pricing engine has ALREADY decided the message is invalid there.
|
|
132
|
+
const state = (await raw(`/_sim/state?key=${id}:${TO}`)).body as {
|
|
133
|
+
messages: Array<{ id: string }>;
|
|
134
|
+
priced: { byMessageId: Record<string, { reasonCode: string }> };
|
|
135
|
+
};
|
|
136
|
+
const last = state.messages[state.messages.length - 1]!;
|
|
137
|
+
expect(state.priced.byMessageId[last.id]!.reasonCode).toBe("INVALID_NON_TEMPLATE_OUTSIDE_CSW");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("GAP: has no way to redeliver or reorder a webhook, so duplicate and out-of-order delivery cannot be driven", async () => {
|
|
141
|
+
for (const path of ["/_sim/replay", "/_sim/webhooks/replay", "/_sim/redeliver"]) {
|
|
142
|
+
const { status } = await raw(path, { method: "POST", headers: { "content-type": "application/json" }, body: "{}" });
|
|
143
|
+
expect(status, path).toBe(404);
|
|
144
|
+
}
|
|
145
|
+
// `GET /_sim/webhooks` is a read-only log; nothing re-dispatches from it.
|
|
146
|
+
const { status, body } = await raw("/_sim/webhooks");
|
|
147
|
+
expect(status).toBe(200);
|
|
148
|
+
expect(Array.isArray((body as { deliveries: unknown[] }).deliveries)).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("GAP: an inbound interactive reply degrades to an empty text message, so a debtor cannot tap a button", async () => {
|
|
152
|
+
const id = pnid();
|
|
153
|
+
const { status, body } = await raw("/_sim/inbound", {
|
|
154
|
+
method: "POST",
|
|
155
|
+
headers: { "content-type": "application/json" },
|
|
156
|
+
body: JSON.stringify({
|
|
157
|
+
phone_number_id: id,
|
|
158
|
+
from: `+${TO}`,
|
|
159
|
+
type: "interactive",
|
|
160
|
+
interactive: { type: "button_reply", button_reply: { id: "avista", title: "À vista" } },
|
|
161
|
+
}),
|
|
162
|
+
});
|
|
163
|
+
expect(status).toBe(200);
|
|
164
|
+
const message = (body as { message: { contentType: string; bodyPreview: string } }).message;
|
|
165
|
+
// The interactive payload is dropped: outbound buttons work, the reply to one does not exist.
|
|
166
|
+
expect(message.contentType).toBe("text");
|
|
167
|
+
expect(message.bodyPreview).toBe("");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("GAP: only `sent` and `delivered` are ever emitted, so a channel cannot observe a read receipt or a failure", async () => {
|
|
171
|
+
const id = pnid();
|
|
172
|
+
await send(id, { kind: "text", text: "uma mensagem" });
|
|
173
|
+
const { body } = await raw("/_sim/webhooks");
|
|
174
|
+
const statuses = (body as { deliveries: Array<{ body: unknown }> }).deliveries
|
|
175
|
+
.map((d) => d.body as { entry?: Array<{ id?: string; changes?: Array<{ value?: { statuses?: Array<{ status: string }> } }> }> })
|
|
176
|
+
.filter((b) => b.entry?.[0]?.id === id)
|
|
177
|
+
.flatMap((b) => b.entry?.[0]?.changes?.[0]?.value?.statuses ?? [])
|
|
178
|
+
.map((s) => s.status);
|
|
179
|
+
expect(statuses).toEqual(["sent", "delivered"]);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("GAP: a `+` on one side and none on the other splits one person into two conversations", async () => {
|
|
183
|
+
const id = pnid();
|
|
184
|
+
const driver = new EmulatorDriver(URL_BASE);
|
|
185
|
+
// Our driver strips the `+` precisely to avoid this; done by hand here, it splits.
|
|
186
|
+
await raw("/_sim/inbound", {
|
|
187
|
+
method: "POST",
|
|
188
|
+
headers: { "content-type": "application/json" },
|
|
189
|
+
body: JSON.stringify({ phone_number_id: id, from: `+${TO}`, text: "oi" }),
|
|
190
|
+
});
|
|
191
|
+
await send(id, { kind: "text", text: "resposta" });
|
|
192
|
+
const miss = await raw(`/_sim/state?key=${id}:not-a-conversation`);
|
|
193
|
+
const keys = (miss.body as { keys: string[] }).keys;
|
|
194
|
+
expect(keys).toContain(`${id}:+${TO}`);
|
|
195
|
+
expect(keys).toContain(`${id}:${TO}`);
|
|
196
|
+
|
|
197
|
+
// With our driver, which strips it, there is one conversation and the window opens.
|
|
198
|
+
const same = pnid();
|
|
199
|
+
await driver.inbound({ phoneNumberId: same, from: `+${TO}`, text: "oi" });
|
|
200
|
+
await send(same, { kind: "text", text: "resposta" });
|
|
201
|
+
const keysAfter = ((await raw(`/_sim/state?key=${same}:not-a-conversation`)).body as { keys: string[] }).keys;
|
|
202
|
+
expect(keysAfter.filter((k) => k.startsWith(`${same}:`))).toEqual([`${same}:${TO}`]);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision 19a: `attestation.evidence` on the consent submit, built from what
|
|
3
|
+
* a conversation channel can honestly say it observed (ent#1615).
|
|
4
|
+
*
|
|
5
|
+
* The first test is the important one and it asserts a REFUSAL. A simulated
|
|
6
|
+
* conversation was observed by nobody, and signing a counter as a provider
|
|
7
|
+
* message id would put a made-up fact inside a record whose whole value is
|
|
8
|
+
* that it is observed. The seam is wired; the simulator declines to use it.
|
|
9
|
+
*
|
|
10
|
+
* The rest hold the shape to the API's own rule, so a change here fails in
|
|
11
|
+
* the repo instead of failing as a 400 at the consent submit.
|
|
12
|
+
*/
|
|
13
|
+
import { describe, expect, it } from "vitest";
|
|
14
|
+
import { evidenceFor, WHATSAPP_EVIDENCE_KEYS } from "../src/channels/whatsapp/evidence.js";
|
|
15
|
+
import type { InboundMessage } from "../src/channels/types.js";
|
|
16
|
+
|
|
17
|
+
const AT = (iso: string) => Math.floor(Date.parse(iso) / 1000);
|
|
18
|
+
const NOW = () => new Date("2026-09-23T17:00:00Z");
|
|
19
|
+
const REAL: InboundMessage = {
|
|
20
|
+
id: "wamid.HBgNNTUxMTk4NzY1NDMyMRUCABIYEjND",
|
|
21
|
+
from: "+5511987654321",
|
|
22
|
+
text: "sim, autorizo",
|
|
23
|
+
timestamp: AT("2026-09-23T17:00:00Z"),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe("what an emulated conversation may attest", () => {
|
|
27
|
+
it("refuses, and says why in a sentence somebody can act on", () => {
|
|
28
|
+
const result = evidenceFor({ live: false, message: { ...REAL, id: "wamid.sim.9f1c" }, now: NOW });
|
|
29
|
+
expect(result.attestable).toBe(false);
|
|
30
|
+
if (result.attestable) return;
|
|
31
|
+
expect(result.reason).toContain("emulator");
|
|
32
|
+
expect(result.reason).toContain("no phone was involved");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("refuses even when the emulated message wears a provider-shaped id, because the id is not what decides", () => {
|
|
36
|
+
expect(evidenceFor({ live: false, message: REAL, now: NOW }).attestable).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("what a live conversation attests", () => {
|
|
41
|
+
it("builds the submit's attestation with the four keys the whatsapp channel may carry", () => {
|
|
42
|
+
const result = evidenceFor({ live: true, message: REAL, sessionId: "conv_1042", reference: "acordo-1042", now: NOW });
|
|
43
|
+
expect(result.attestable).toBe(true);
|
|
44
|
+
if (!result.attestable) return;
|
|
45
|
+
expect(result.attestation).toEqual({
|
|
46
|
+
method: "partner_session",
|
|
47
|
+
asserted_at: AT("2026-09-23T17:00:00Z"),
|
|
48
|
+
reference: "acordo-1042",
|
|
49
|
+
evidence: {
|
|
50
|
+
channel: "whatsapp",
|
|
51
|
+
contact: "+5511987654321",
|
|
52
|
+
message_id: "wamid.HBgNNTUxMTk4NzY1NDMyMRUCABIYEjND",
|
|
53
|
+
session_id: "conv_1042",
|
|
54
|
+
provider_ts: AT("2026-09-23T17:00:00Z"),
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("carries the contact in the clear, because the API hashes it under a key a partner cannot compute", () => {
|
|
60
|
+
const result = evidenceFor({ live: true, message: REAL, now: NOW });
|
|
61
|
+
if (!result.attestable) throw new Error("expected evidence");
|
|
62
|
+
expect(result.attestation.evidence.contact).toBe("+5511987654321");
|
|
63
|
+
expect(result.attestation.evidence).not.toHaveProperty("contact_hash");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("never carries an ip, a user agent, a device id or a geo: on this channel each of those was inferred", () => {
|
|
67
|
+
const result = evidenceFor({ live: true, message: REAL, now: NOW });
|
|
68
|
+
if (!result.attestable) throw new Error("expected evidence");
|
|
69
|
+
const keys = Object.keys(result.attestation.evidence).filter((k) => k !== "channel");
|
|
70
|
+
expect(keys.every((k) => (WHATSAPP_EVIDENCE_KEYS as readonly string[]).includes(k))).toBe(true);
|
|
71
|
+
for (const forbidden of ["ip", "user_agent", "device_id_hash", "geo"]) expect(result.attestation.evidence).not.toHaveProperty(forbidden);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("separates the partner's clock from the provider's: asserted_at is ours, provider_ts is theirs", () => {
|
|
75
|
+
const result = evidenceFor({ live: true, message: { ...REAL, timestamp: AT("2026-09-23T15:00:00Z") }, now: () => new Date("2026-09-23T17:00:05Z") });
|
|
76
|
+
if (!result.attestable) throw new Error("expected evidence");
|
|
77
|
+
expect(result.attestation.asserted_at).toBe(AT("2026-09-23T17:00:05Z"));
|
|
78
|
+
expect(result.attestation.evidence.provider_ts).toBe(AT("2026-09-23T15:00:00Z"));
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("refuses a reference with a dot: the canonical signing string joins segments, and a wamid goes in message_id", () => {
|
|
82
|
+
const result = evidenceFor({ live: true, message: REAL, reference: "wamid.HBgN", now: NOW });
|
|
83
|
+
expect(result.attestable).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("refuses a message id or a session id the API's opaque-id shape would reject", () => {
|
|
87
|
+
expect(evidenceFor({ live: true, message: { ...REAL, id: "wamid with spaces" }, now: NOW }).attestable).toBe(false);
|
|
88
|
+
expect(evidenceFor({ live: true, message: REAL, sessionId: "conv 1042", now: NOW }).attestable).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("refuses a message the provider gave no timestamp for", () => {
|
|
92
|
+
expect(evidenceFor({ live: true, message: { ...REAL, timestamp: 0 }, now: NOW }).attestable).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|