@codespar/cli 0.14.1 → 0.17.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/mandate-verify.js +98 -19
- package/dist/commands/mandate-verify.js.map +1 -1
- package/dist/index.js +17 -3
- package/dist/index.js.map +1 -1
- package/dist/mandate-codec.js +31 -20
- package/dist/mandate-codec.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
package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The house simulator, which is not ours: `dyvit-wa-sim`, the local Cloud API
|
|
3
|
+
* emulator from https://github.com/fabianocruz/whatsapp-simulator (MIT).
|
|
4
|
+
*
|
|
5
|
+
* WHY A SEPARATE PROCESS INSTEAD OF A FAKE IN THIS REPO. A mock we wrote would
|
|
6
|
+
* agree with us by construction — it would accept the payloads we send because
|
|
7
|
+
* we wrote both sides, and the day Meta refused one we would find out in
|
|
8
|
+
* production. The emulator answers `POST /v{version}/{phone-number-id}/messages`
|
|
9
|
+
* with the Cloud API's own response shape and posts back the same signed
|
|
10
|
+
* `x-hub-signature-256` webhooks, so the `simulator` backend is THE SAME CODE
|
|
11
|
+
* as the official one with a different base URL. That is the thing being
|
|
12
|
+
* proved, and it is not something a mock can prove.
|
|
13
|
+
*
|
|
14
|
+
* It also has a clock we can move (`POST /_sim/clock`), which a replay
|
|
15
|
+
* finishing in seconds cannot otherwise have: a collection runs over days, and
|
|
16
|
+
* without moving the clock the 24-hour window never closes, so the one rule
|
|
17
|
+
* that decides whether a message may be sent at all is never exercised.
|
|
18
|
+
*
|
|
19
|
+
* WHAT THIS FILE IS: the `_sim/*` side only — the routes that belong to the
|
|
20
|
+
* emulator and have no counterpart at Meta. The sending and receiving are
|
|
21
|
+
* `WhatsAppCloudApi`, unchanged.
|
|
22
|
+
*
|
|
23
|
+
* The emulator is a separate process and is not a dependency of this workspace:
|
|
24
|
+
* it is a development tool nothing here imports. `scripts/whatsapp-emulator.mjs`
|
|
25
|
+
* fetches `@dyvit/whatsapp-simulator-cli` at a pinned version and runs it;
|
|
26
|
+
* `npm run whatsapp:emulator` is the command.
|
|
27
|
+
*/
|
|
28
|
+
import type { ConversationScript } from "@codespar/agent-core";
|
|
29
|
+
import type { ChannelBackend, InboundMessage, OutboundBody, SentMessage } from "../types.js";
|
|
30
|
+
import { toGraphNumber, WhatsAppCloudApi, type CloudApiOptions } from "./cloud-api.js";
|
|
31
|
+
|
|
32
|
+
export const EMULATOR_ENV = {
|
|
33
|
+
url: "WHATSAPP_SIM_URL",
|
|
34
|
+
appSecret: "WHATSAPP_SIM_APP_SECRET",
|
|
35
|
+
webhookPort: "WHATSAPP_SIM_WEBHOOK_PORT",
|
|
36
|
+
phoneNumberId: "WHATSAPP_SIM_PHONE_NUMBER_ID",
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
export const EMULATOR_DEFAULTS = {
|
|
40
|
+
url: "http://127.0.0.1:4290",
|
|
41
|
+
/** Matches `--app-secret` in `npm run whatsapp:emulator`. A local development value, not a credential. */
|
|
42
|
+
appSecret: "dev",
|
|
43
|
+
webhookPort: 4399,
|
|
44
|
+
phoneNumberId: "900000000001",
|
|
45
|
+
apiVersion: "v22.0",
|
|
46
|
+
} as const;
|
|
47
|
+
|
|
48
|
+
export class EmulatorUnreachableError extends Error {
|
|
49
|
+
constructor(url: string, detail: string) {
|
|
50
|
+
super(
|
|
51
|
+
`the WhatsApp emulator is not answering at ${url} (${detail}).\n` +
|
|
52
|
+
` Start it in another terminal: npm run whatsapp:emulator\n` +
|
|
53
|
+
` It is dyvit-wa-sim, from https://github.com/fabianocruz/whatsapp-simulator — MIT, run from npm at a pinned version, no Meta account and no credential.`,
|
|
54
|
+
);
|
|
55
|
+
this.name = "EmulatorUnreachableError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The `_sim/*` routes: what the emulator has and Meta does not. */
|
|
60
|
+
export class EmulatorDriver {
|
|
61
|
+
constructor(
|
|
62
|
+
private readonly url: string,
|
|
63
|
+
private readonly fetchImpl: typeof fetch = fetch,
|
|
64
|
+
) {}
|
|
65
|
+
|
|
66
|
+
private async call(path: string, init?: RequestInit): Promise<unknown> {
|
|
67
|
+
let response: Response;
|
|
68
|
+
try {
|
|
69
|
+
response = await this.fetchImpl(`${this.url}${path}`, { ...init, signal: AbortSignal.timeout(5000) });
|
|
70
|
+
} catch (err) {
|
|
71
|
+
throw new EmulatorUnreachableError(this.url, err instanceof Error ? err.message : String(err));
|
|
72
|
+
}
|
|
73
|
+
const text = await response.text();
|
|
74
|
+
if (!response.ok) throw new Error(`emulator answered ${response.status} to ${path}: ${text.slice(0, 200)}`);
|
|
75
|
+
return text ? JSON.parse(text) : {};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private post(path: string, body: unknown): Promise<unknown> {
|
|
79
|
+
return this.call(path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
health(): Promise<unknown> {
|
|
83
|
+
return this.call("/health");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Pins the conversation clock, so a scripted run reads the same at 03:00 as at 15:00. */
|
|
87
|
+
pin(at: Date): Promise<unknown> {
|
|
88
|
+
return this.post("/_sim/clock", { now: at.toISOString() });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Moves the conversation clock forward. The only way to reach the far side of the 24-hour window. */
|
|
92
|
+
advanceHours(hours: number): Promise<unknown> {
|
|
93
|
+
return this.post("/_sim/clock", { advance_hours: hours });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The person writes. The emulator answers with a signed inbound webhook to our receiver. */
|
|
97
|
+
inbound(options: { phoneNumberId: string; from: string; text: string; sentAt?: Date }): Promise<unknown> {
|
|
98
|
+
return this.post("/_sim/inbound", {
|
|
99
|
+
phone_number_id: options.phoneNumberId,
|
|
100
|
+
// Without the `+`, matching what `deliver` sends as `to`: the emulator keys
|
|
101
|
+
// its conversation on the literal string, so the two forms are two people.
|
|
102
|
+
from: toGraphNumber(options.from),
|
|
103
|
+
text: options.text,
|
|
104
|
+
...(options.sentAt ? { sent_at: options.sentAt.toISOString() } : {}),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The priced timeline, which is the emulator's own reason for existing. Read for the console, never asserted on. */
|
|
109
|
+
state(key: string): Promise<{ priced?: { total?: number; currency?: string } } | undefined> {
|
|
110
|
+
return this.call(`/_sim/state?key=${encodeURIComponent(key)}`).then(
|
|
111
|
+
(v) => v as { priced?: { total?: number; currency?: string } },
|
|
112
|
+
() => undefined,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface EmulatorBackendOptions extends CloudApiOptions {
|
|
118
|
+
driver: EmulatorDriver;
|
|
119
|
+
/** The person's turns. Absent means they are at the keyboard. */
|
|
120
|
+
script?: ConversationScript | undefined;
|
|
121
|
+
/** The instant the emulator's conversation clock is pinned to. */
|
|
122
|
+
pinAt?: Date | undefined;
|
|
123
|
+
/** Interactive only: reads the person's next line. */
|
|
124
|
+
ask?: ((question: string) => Promise<string>) | undefined;
|
|
125
|
+
/** Where the console draws the conversation as it goes. */
|
|
126
|
+
render: (line: string) => void;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The `simulator` backend: `WhatsAppCloudApi` pointed at the emulator, plus the
|
|
131
|
+
* one thing the Cloud API has no equivalent for — making the person write.
|
|
132
|
+
*
|
|
133
|
+
* Every outbound message goes through the same `deliver` the Meta backend uses,
|
|
134
|
+
* and every inbound one arrives as a signed webhook at the same receiver, so
|
|
135
|
+
* nothing here is a shortcut around the adapter.
|
|
136
|
+
*/
|
|
137
|
+
export class WhatsAppEmulator implements ChannelBackend {
|
|
138
|
+
readonly name = "emulator";
|
|
139
|
+
readonly live = false;
|
|
140
|
+
private readonly api: WhatsAppCloudApi;
|
|
141
|
+
private turn = 0;
|
|
142
|
+
private closed = false;
|
|
143
|
+
|
|
144
|
+
constructor(private readonly options: EmulatorBackendOptions) {
|
|
145
|
+
const { driver: _driver, script: _script, pinAt: _pinAt, ask: _ask, render: _render, ...api } = options;
|
|
146
|
+
this.api = new WhatsAppCloudApi(api);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async open(): Promise<void> {
|
|
150
|
+
await this.api.open();
|
|
151
|
+
await this.options.driver.health();
|
|
152
|
+
if (this.options.pinAt) await this.options.driver.pin(this.options.pinAt);
|
|
153
|
+
this.options.render("");
|
|
154
|
+
this.options.render(` ┌─ WhatsApp (dyvit-wa-sim, emulador local da Cloud API — sem rede externa, sem conta Meta)`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async next(): Promise<InboundMessage | undefined> {
|
|
158
|
+
if (this.closed) return undefined;
|
|
159
|
+
const text = await this.nextText();
|
|
160
|
+
if (text === undefined) return undefined;
|
|
161
|
+
// The person writes THROUGH the emulator: it mints the message id and posts
|
|
162
|
+
// the signed webhook our receiver verifies, so a scripted turn takes the
|
|
163
|
+
// same path a real one would.
|
|
164
|
+
await this.options.driver.inbound({
|
|
165
|
+
phoneNumberId: this.options.config.phoneNumberId,
|
|
166
|
+
from: this.options.conversation.contact,
|
|
167
|
+
text,
|
|
168
|
+
});
|
|
169
|
+
return this.api.next();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private async nextText(): Promise<string | undefined> {
|
|
173
|
+
const script = this.options.script;
|
|
174
|
+
if (script) {
|
|
175
|
+
const turn = script.turns[this.turn];
|
|
176
|
+
if (!turn) return undefined;
|
|
177
|
+
this.turn += 1;
|
|
178
|
+
// The person's own pause, on the emulator's clock. A conversation that
|
|
179
|
+
// happens over days is what closes the 24-hour window.
|
|
180
|
+
if (turn.after_seconds > 0) await this.options.driver.advanceHours(turn.after_seconds / 3600);
|
|
181
|
+
return turn.text;
|
|
182
|
+
}
|
|
183
|
+
if (!this.options.ask) return undefined;
|
|
184
|
+
let answer: string;
|
|
185
|
+
try {
|
|
186
|
+
answer = await this.options.ask(" │ voce> ");
|
|
187
|
+
} catch {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
const text = answer.trim();
|
|
191
|
+
if (!text) return this.nextText();
|
|
192
|
+
if (["sair", "exit", "quit"].includes(text.toLowerCase())) return undefined;
|
|
193
|
+
return text;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
deliver(to: string, body: OutboundBody): Promise<SentMessage> {
|
|
197
|
+
return this.api.deliver(to, body);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async close(): Promise<void> {
|
|
201
|
+
if (this.closed) return;
|
|
202
|
+
this.closed = true;
|
|
203
|
+
await this.api.close();
|
|
204
|
+
this.options.render(" └─ fim da conversa");
|
|
205
|
+
}
|
|
206
|
+
}
|
package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision 19a, the part a channel can carry: `attestation.evidence` on
|
|
3
|
+
* `POST /v1/consents/{token}/submit` (codespar-enterprise ent#1615).
|
|
4
|
+
*
|
|
5
|
+
* In the sandbox a mandate is born at the terminal with
|
|
6
|
+
* `attestation: { method: "in_person" }` — the titular is at the keyboard and
|
|
7
|
+
* the partner is the process. In production the act happens in a
|
|
8
|
+
* CONVERSATION, so the method is `partner_session` and the submit may carry
|
|
9
|
+
* what the partner OBSERVED of it. This builds that object.
|
|
10
|
+
*
|
|
11
|
+
* The API's rule, restated here because it is the whole point: a WhatsApp
|
|
12
|
+
* conversation hands the partner a message and a sender, not a socket. The
|
|
13
|
+
* channel's allowed keys are therefore `contact`, `message_id`, `session_id`
|
|
14
|
+
* and `provider_ts`, and an `ip`, a `user_agent`, a `device_id_hash` or a
|
|
15
|
+
* `geo` on this channel would have been inferred rather than observed. The
|
|
16
|
+
* API refuses them (400 `attestation_evidence_invalid`), and so does this,
|
|
17
|
+
* one layer earlier, because a value inferred inside a signed record reads as
|
|
18
|
+
* observed and is worse than an absent one.
|
|
19
|
+
*
|
|
20
|
+
* WHY THE EMULATOR CANNOT PRODUCE ONE. Everything here is a claim about what a
|
|
21
|
+
* provider observed. A conversation carried by an emulator on the developer's
|
|
22
|
+
* own machine was observed by nobody: its message id was minted locally, its
|
|
23
|
+
* timestamp came from a clock we move ourselves, and no phone was involved.
|
|
24
|
+
* Signing that as evidence would be a false declaration — the same one the
|
|
25
|
+
* API's own schema warns about for a partner that declares `other` for a
|
|
26
|
+
* WhatsApp act. So `evidenceFor` returns a REFUSAL for any backend that is not
|
|
27
|
+
* live, with the reason on it, and the caller either submits without evidence
|
|
28
|
+
* or does not submit at all. That is not a gap in the seam; it is the seam
|
|
29
|
+
* working.
|
|
30
|
+
*
|
|
31
|
+
* `live` is not a flag anyone sets: it is whether the base URL is Meta's host.
|
|
32
|
+
* A run cannot claim to be live by being configured to say so.
|
|
33
|
+
*/
|
|
34
|
+
import type { InboundMessage } from "../types.js";
|
|
35
|
+
|
|
36
|
+
/** The keys the API's `whatsapp` channel accepts. Mirrored from `CONSENT_EVIDENCE_CHANNEL_KEYS`. */
|
|
37
|
+
export const WHATSAPP_EVIDENCE_KEYS = ["contact", "message_id", "session_id", "provider_ts"] as const;
|
|
38
|
+
|
|
39
|
+
/** Opaque provider ids: a `wamid.` carries `.`, `:`, `=` and `+`, and all four are allowed. */
|
|
40
|
+
const OPAQUE_ID = /^[A-Za-z0-9_.:=+-]{1,120}$/;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The evidence as the PARTNER sends it. The wire carries `contact` in the
|
|
44
|
+
* clear and the API hashes it into `contact_hash` under the org's own key —
|
|
45
|
+
* a partner cannot compute that hash, and a partner-computed one would match
|
|
46
|
+
* nothing.
|
|
47
|
+
*/
|
|
48
|
+
export interface WhatsAppConsentEvidence {
|
|
49
|
+
channel: "whatsapp";
|
|
50
|
+
contact?: string;
|
|
51
|
+
message_id?: string;
|
|
52
|
+
session_id?: string;
|
|
53
|
+
/** Unix seconds on the PROVIDER's clock, which is not the partner's. */
|
|
54
|
+
provider_ts?: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ConsentAttestation {
|
|
58
|
+
method: "partner_session";
|
|
59
|
+
/** Unix seconds: when the PARTNER says the human authorized. */
|
|
60
|
+
asserted_at: number;
|
|
61
|
+
/** The partner's own record id. `^[A-Za-z0-9_-]{1,120}$` — no dots, so a `wamid` goes in `message_id`. */
|
|
62
|
+
reference?: string;
|
|
63
|
+
evidence: WhatsAppConsentEvidence;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type EvidenceResult = { attestable: true; attestation: ConsentAttestation } | { attestable: false; reason: string };
|
|
67
|
+
|
|
68
|
+
export interface EvidenceInput {
|
|
69
|
+
/** Whether the backend that carried the message talks to a real provider. */
|
|
70
|
+
live: boolean;
|
|
71
|
+
/** The message in which the person said yes. */
|
|
72
|
+
message: InboundMessage;
|
|
73
|
+
/** The partner's id for this conversation, when it has one. */
|
|
74
|
+
sessionId?: string | undefined;
|
|
75
|
+
/** The partner's clock, for `asserted_at`. */
|
|
76
|
+
now: () => Date;
|
|
77
|
+
reference?: string | undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function evidenceFor(input: EvidenceInput): EvidenceResult {
|
|
81
|
+
if (!input.live) {
|
|
82
|
+
return {
|
|
83
|
+
attestable: false,
|
|
84
|
+
reason:
|
|
85
|
+
"this act was observed by an emulator on this machine, and a simulated observation is not evidence: " +
|
|
86
|
+
"the message id was minted locally, the timestamp came from a clock we move ourselves, and no phone was involved",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (!OPAQUE_ID.test(input.message.id)) return { attestable: false, reason: `the provider message id does not fit the API's opaque-id shape: ${input.message.id.slice(0, 24)}` };
|
|
90
|
+
if (!Number.isInteger(input.message.timestamp) || input.message.timestamp <= 0) return { attestable: false, reason: "the provider gave no usable timestamp for the message" };
|
|
91
|
+
if (input.sessionId !== undefined && !OPAQUE_ID.test(input.sessionId)) return { attestable: false, reason: "the session id does not fit the API's opaque-id shape" };
|
|
92
|
+
if (input.reference !== undefined && !/^[A-Za-z0-9_-]{1,120}$/.test(input.reference)) return { attestable: false, reason: "the attestation reference must be [A-Za-z0-9_-]{1,120}" };
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
attestable: true,
|
|
96
|
+
attestation: {
|
|
97
|
+
method: "partner_session",
|
|
98
|
+
asserted_at: Math.floor(input.now().getTime() / 1000),
|
|
99
|
+
...(input.reference !== undefined ? { reference: input.reference } : {}),
|
|
100
|
+
evidence: {
|
|
101
|
+
channel: "whatsapp",
|
|
102
|
+
contact: input.message.from,
|
|
103
|
+
message_id: input.message.id,
|
|
104
|
+
...(input.sessionId !== undefined ? { session_id: input.sessionId } : {}),
|
|
105
|
+
provider_ts: input.message.timestamp,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WhatsApp channel: one adapter, two backends.
|
|
3
|
+
*
|
|
4
|
+
* Everything that decides what may be said lives HERE and not in a backend,
|
|
5
|
+
* which is what makes "the rules are code" true rather than aspirational.
|
|
6
|
+
* Picking the simulator or the official API changes who carries the bytes; it
|
|
7
|
+
* does not change the hours, the bound contact, the secrecy of the debt, the
|
|
8
|
+
* document rule or the session window. A test that tries to get around a rule
|
|
9
|
+
* by choosing a backend is in `test/whatsapp-rules.test.ts` and fails.
|
|
10
|
+
*
|
|
11
|
+
* It also keeps the two audiences apart. `send` reaches the person who owes.
|
|
12
|
+
* The operator's console and the approval question are wired to stderr by the
|
|
13
|
+
* caller and have no path to this object at all — not a check, a shape: there
|
|
14
|
+
* is no method here that an operator line could be handed to.
|
|
15
|
+
*/
|
|
16
|
+
import type { ProofBundle, WhatsAppTemplate } from "@codespar/agent-core";
|
|
17
|
+
import { checkOutbound, type HoursRule, type RuleContext } from "../rules.js";
|
|
18
|
+
import type { Channel, ChannelBackend, ChannelLogLine, Conversation, InboundMessage, OutboundBody, SentMessage } from "../types.js";
|
|
19
|
+
import { maskContact } from "../contact.js";
|
|
20
|
+
import { SessionWindow, type SessionState } from "./session.js";
|
|
21
|
+
|
|
22
|
+
export interface WhatsAppChannelOptions {
|
|
23
|
+
backend: ChannelBackend;
|
|
24
|
+
conversation: Conversation;
|
|
25
|
+
now: () => Date;
|
|
26
|
+
hours?: HoursRule | undefined;
|
|
27
|
+
knownSubjects?: readonly string[] | undefined;
|
|
28
|
+
/** The run's bundle, so the conversation is part of the proof. */
|
|
29
|
+
bundle?: ProofBundle | undefined;
|
|
30
|
+
/** Templates the agent declares it uses, from `channels/whatsapp/templates.json`. Meta's approval of them is not knowable from here. */
|
|
31
|
+
templates?: readonly WhatsAppTemplate[] | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Where the window stood when an earlier run left this conversation. A run
|
|
34
|
+
* that JOINS a conversation — a poll looking at a charge agreed yesterday —
|
|
35
|
+
* has no inbound message of its own to open it from, and without this it
|
|
36
|
+
* would read every conversation as shut.
|
|
37
|
+
*/
|
|
38
|
+
session?: SessionState | undefined;
|
|
39
|
+
/** The operator's console. Refusals are reported here, never to the conversation. */
|
|
40
|
+
say?: ((line: string) => void) | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Draws the conversation for whoever is watching the run. It is a VIEW of
|
|
43
|
+
* what went over the channel, written after the fact, and never a way to put
|
|
44
|
+
* something in front of the person — that is `send`, and it is the only one.
|
|
45
|
+
*/
|
|
46
|
+
render?: ((line: string) => void) | undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class WhatsAppChannel implements Channel {
|
|
50
|
+
readonly name = "whatsapp" as const;
|
|
51
|
+
readonly conversation: Conversation;
|
|
52
|
+
private readonly session: SessionWindow;
|
|
53
|
+
private readonly lines: ChannelLogLine[] = [];
|
|
54
|
+
private lastInbound: InboundMessage | undefined;
|
|
55
|
+
|
|
56
|
+
constructor(private readonly options: WhatsAppChannelOptions) {
|
|
57
|
+
this.conversation = options.conversation;
|
|
58
|
+
this.session = new SessionWindow(options.templates ?? [], options.session);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get backend(): string {
|
|
62
|
+
return this.options.backend.name;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Whether the backend talks to a real provider. What the consent-evidence seam turns on. */
|
|
66
|
+
get live(): boolean {
|
|
67
|
+
return this.options.backend.live;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The message the person last sent, for the caller that needs to attest to an act. */
|
|
71
|
+
get lastInboundMessage(): InboundMessage | undefined {
|
|
72
|
+
return this.lastInbound;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Whether a free-form message may go out right now. A caller that has one
|
|
77
|
+
* thing to say and two ways to say it asks HERE which one the provider
|
|
78
|
+
* would carry — the alternative is composing a message, watching the
|
|
79
|
+
* channel refuse it, and calling that a decision.
|
|
80
|
+
*/
|
|
81
|
+
get sessionOpen(): boolean {
|
|
82
|
+
return this.session.open(this.options.now());
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Seconds of free-form left, for the console line that explains the choice. */
|
|
86
|
+
get sessionRemainingSeconds(): number {
|
|
87
|
+
return this.session.remainingSeconds(this.options.now());
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** What the agent declared about a template it is about to send. The language is the registry's, never the sender's guess. */
|
|
91
|
+
declaredTemplate(name: string): WhatsAppTemplate | undefined {
|
|
92
|
+
return this.session.declared(name);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async open(): Promise<void> {
|
|
96
|
+
await this.options.backend.open();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async next(): Promise<InboundMessage | undefined> {
|
|
100
|
+
const message = await this.options.backend.next();
|
|
101
|
+
if (!message) return undefined;
|
|
102
|
+
this.lastInbound = message;
|
|
103
|
+
this.session.observeInbound(message.timestamp);
|
|
104
|
+
this.record({
|
|
105
|
+
at: this.options.now().toISOString(),
|
|
106
|
+
direction: "in",
|
|
107
|
+
contact: maskContact(message.from),
|
|
108
|
+
kind: "text",
|
|
109
|
+
message_id: message.id,
|
|
110
|
+
state: "delivered",
|
|
111
|
+
text: message.text,
|
|
112
|
+
// The provider's clock, which is the only one the 24-hour window is
|
|
113
|
+
// counted on. A later run reads the window back from here.
|
|
114
|
+
provider_timestamp: message.timestamp,
|
|
115
|
+
});
|
|
116
|
+
return message;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async send(body: OutboundBody): Promise<SentMessage> {
|
|
120
|
+
const to = this.conversation.contact;
|
|
121
|
+
const ctx: RuleContext = {
|
|
122
|
+
conversation: this.conversation,
|
|
123
|
+
hours: this.options.hours,
|
|
124
|
+
now: this.options.now,
|
|
125
|
+
knownSubjects: this.options.knownSubjects,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// The house rules first: a message the law refuses is not a message the provider should ever see.
|
|
129
|
+
const refusal = checkOutbound(to, body, ctx) ?? this.providerRefusal(body);
|
|
130
|
+
if (refusal) {
|
|
131
|
+
const sent: SentMessage = { id: "", state: "failed", refused: refusal };
|
|
132
|
+
this.logOutbound(body, sent);
|
|
133
|
+
this.options.say?.(` [whatsapp] recusado (${refusal.rule}): ${refusal.detail}`);
|
|
134
|
+
return sent;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const sent = await this.options.backend.deliver(to, body);
|
|
138
|
+
if (sent.refused) this.options.say?.(` [whatsapp] recusado pelo backend (${sent.refused.rule}): ${sent.refused.detail}`);
|
|
139
|
+
this.logOutbound(body, sent);
|
|
140
|
+
return sent;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
say(text: string): Promise<SentMessage> {
|
|
144
|
+
return this.send({ kind: "text", text });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async close(): Promise<void> {
|
|
148
|
+
await this.options.backend.close();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Every message of this conversation, in order. */
|
|
152
|
+
log(): ReadonlyArray<ChannelLogLine> {
|
|
153
|
+
return this.lines;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private logOutbound(body: OutboundBody, sent: SentMessage): void {
|
|
157
|
+
this.record({
|
|
158
|
+
at: this.options.now().toISOString(),
|
|
159
|
+
direction: "out",
|
|
160
|
+
contact: maskContact(this.conversation.contact),
|
|
161
|
+
kind: body.kind,
|
|
162
|
+
message_id: sent.id,
|
|
163
|
+
state: sent.state,
|
|
164
|
+
...(textOf(body) !== undefined ? { text: textOf(body)! } : {}),
|
|
165
|
+
...(sent.refused ? { refused: sent.refused } : {}),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* WhatsApp's own rule, enforced on both backends: outside the 24 hours
|
|
171
|
+
* after the person's last message only an approved template may go out.
|
|
172
|
+
* The simulator obeys it because a rule that only bites in production is a
|
|
173
|
+
* rule you meet in production.
|
|
174
|
+
*/
|
|
175
|
+
private providerRefusal(body: OutboundBody): { rule: string; detail: string } | undefined {
|
|
176
|
+
const open = this.session.open(this.options.now());
|
|
177
|
+
if (body.kind === "template") return this.session.refuse(body);
|
|
178
|
+
if (!open) {
|
|
179
|
+
return {
|
|
180
|
+
rule: "session_window_closed",
|
|
181
|
+
detail: "more than 24h since the person's last message: WhatsApp only carries an approved template now, not a free-form message",
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private record(line: ChannelLogLine): void {
|
|
188
|
+
this.lines.push(line);
|
|
189
|
+
this.options.bundle?.channel({ ...line, channel: "whatsapp", backend: this.backend });
|
|
190
|
+
this.options.render?.(` │ ${draw(line)}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** One console line for one message. The refusals are visible: a message nobody got is part of the conversation's story. */
|
|
195
|
+
function draw(line: ChannelLogLine): string {
|
|
196
|
+
const who = line.direction === "in" ? `${line.contact}:` : "loja:";
|
|
197
|
+
const what =
|
|
198
|
+
line.kind === "instrument"
|
|
199
|
+
? `[copia e cola] ${line.text ?? ""}`
|
|
200
|
+
: line.kind === "media"
|
|
201
|
+
? "[imagem: QR Pix]"
|
|
202
|
+
: line.kind === "template"
|
|
203
|
+
? line.text ?? "[template]"
|
|
204
|
+
: line.text ?? "";
|
|
205
|
+
return line.refused ? `${who} (nao enviada: ${line.refused.rule}) ${what}` : `${who} ${what}`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function textOf(body: OutboundBody): string | undefined {
|
|
209
|
+
switch (body.kind) {
|
|
210
|
+
case "text":
|
|
211
|
+
return body.text;
|
|
212
|
+
case "instrument":
|
|
213
|
+
return body.value;
|
|
214
|
+
case "media":
|
|
215
|
+
return body.caption;
|
|
216
|
+
case "template":
|
|
217
|
+
return `[template ${body.template}] ${body.variables.join(" | ")}`;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export * from "../contact.js";
|
|
222
|
+
export * from "./cloud-api.js";
|
|
223
|
+
export * from "./emulator.js";
|
|
224
|
+
export * from "./evidence.js";
|
|
225
|
+
export * from "./session.js";
|
package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opening the WhatsApp channel: wiring, and only wiring.
|
|
3
|
+
*
|
|
4
|
+
* It lives apart from the command that starts a conversation because a second
|
|
5
|
+
* command now JOINS one — `codespar-agent poll --channel whatsapp`, which
|
|
6
|
+
* comes back to a conversation the run that opened it has long since ended.
|
|
7
|
+
* Two commands wiring the same channel by hand is how a rule ends up enforced
|
|
8
|
+
* on one path and not the other, and the whole claim of `channels/` is that
|
|
9
|
+
* the rules are above the backend. So both go through here.
|
|
10
|
+
*
|
|
11
|
+
* The decisions are all elsewhere: the rules in `channels/rules.ts`, the
|
|
12
|
+
* session window in `session.ts`, the gates in `terminal.ts`.
|
|
13
|
+
*/
|
|
14
|
+
import type { ConversationScript, ProofBundle } from "@codespar/agent-core";
|
|
15
|
+
import type { Agent } from "../../agent.js";
|
|
16
|
+
import type { Setup } from "../../setup.js";
|
|
17
|
+
import { knownSubjects, loadTemplates } from "../index.js";
|
|
18
|
+
import type { ChannelBackend, Conversation } from "../types.js";
|
|
19
|
+
import { WhatsAppChannel } from "./index.js";
|
|
20
|
+
import { toGraphNumber, WhatsAppCloudApi, loadCloudApiConfig, type CloudApiConfig } from "./cloud-api.js";
|
|
21
|
+
import { EmulatorDriver, EMULATOR_DEFAULTS, EMULATOR_ENV, WhatsAppEmulator } from "./emulator.js";
|
|
22
|
+
import type { SessionState } from "./session.js";
|
|
23
|
+
|
|
24
|
+
export type WhatsAppBackendName = "simulator" | "cloud-api";
|
|
25
|
+
|
|
26
|
+
export interface OpenWhatsAppOptions {
|
|
27
|
+
agent: Agent;
|
|
28
|
+
setup: Setup;
|
|
29
|
+
conversation: Conversation;
|
|
30
|
+
backend: WhatsAppBackendName;
|
|
31
|
+
now: () => Date;
|
|
32
|
+
say: (line: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Where the conversation is recorded. Normally the run's own bundle; a poll
|
|
35
|
+
* hands the bundle of the run it is continuing, because a conversation is
|
|
36
|
+
* one record and the message that closes it belongs with the ones that
|
|
37
|
+
* opened it.
|
|
38
|
+
*/
|
|
39
|
+
bundle: ProofBundle;
|
|
40
|
+
/** The person's turns, when a script drives them. Absent and with no `ask`, nobody writes. */
|
|
41
|
+
script?: ConversationScript | undefined;
|
|
42
|
+
/** Interactive only: reads the person's next line. */
|
|
43
|
+
ask?: ((question: string) => Promise<string>) | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Pins the emulator's CONVERSATION clock. A run that starts a conversation
|
|
46
|
+
* pins it; a run that joins one must not, because rewinding the provider's
|
|
47
|
+
* clock would reopen a window that really is shut. The agent's own notion of
|
|
48
|
+
* now is `now` above and is a different thing (#16).
|
|
49
|
+
*/
|
|
50
|
+
pinAt?: Date | undefined;
|
|
51
|
+
/** Where the session window stood when an earlier run left this conversation. */
|
|
52
|
+
session?: SessionState | undefined;
|
|
53
|
+
/** Draws the conversation on the operator's console. */
|
|
54
|
+
render?: ((line: string) => void) | undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface OpenedWhatsApp {
|
|
58
|
+
channel: WhatsAppChannel;
|
|
59
|
+
/** The `_sim/*` side, when the backend is the emulator. Absent against Meta. */
|
|
60
|
+
driver?: EmulatorDriver;
|
|
61
|
+
/** How the emulator keys this conversation, for the priced timeline. */
|
|
62
|
+
sessionKey?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Builds the channel, or explains what is missing. Nothing is opened here:
|
|
67
|
+
* the caller opens it, because an emulator that is not running has to be
|
|
68
|
+
* reported differently depending on how far the command already got.
|
|
69
|
+
*/
|
|
70
|
+
export function buildWhatsApp(options: OpenWhatsAppOptions): OpenedWhatsApp | { refusal: string[] } {
|
|
71
|
+
const { agent, setup: s, say } = options;
|
|
72
|
+
let backend: ChannelBackend;
|
|
73
|
+
let driver: EmulatorDriver | undefined;
|
|
74
|
+
let sessionKey: string | undefined;
|
|
75
|
+
|
|
76
|
+
if (options.backend === "cloud-api") {
|
|
77
|
+
const { config, missing } = loadCloudApiConfig(process.env);
|
|
78
|
+
if (!config) {
|
|
79
|
+
return {
|
|
80
|
+
refusal: [
|
|
81
|
+
`the cloud-api backend needs credentials this repo ships none of: ${missing.join(", ")}.`,
|
|
82
|
+
`They live in the agent's .env (commented out in .env.example) and belong to a Meta Business account. The emulator needs none: drop --backend cloud-api.`,
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
say("[whatsapp] cloud-api backend: this repo has never run one against Meta. The shapes are written from the published documentation; the first live run is yours.");
|
|
87
|
+
backend = new WhatsAppCloudApi({ config, conversation: { contact: options.conversation.contact }, say });
|
|
88
|
+
} else {
|
|
89
|
+
// The SAME backend, pointed somewhere else. What the emulator adds is the
|
|
90
|
+
// `_sim/*` side: making the person write, and moving the conversation clock.
|
|
91
|
+
const env = process.env;
|
|
92
|
+
const url = env[EMULATOR_ENV.url]?.trim() || EMULATOR_DEFAULTS.url;
|
|
93
|
+
const config: CloudApiConfig = {
|
|
94
|
+
baseUrl: url,
|
|
95
|
+
phoneNumberId: env[EMULATOR_ENV.phoneNumberId]?.trim() || EMULATOR_DEFAULTS.phoneNumberId,
|
|
96
|
+
// Not credentials: the emulator has no auth, and these are the values
|
|
97
|
+
// `npm run whatsapp:emulator` starts it with.
|
|
98
|
+
accessToken: "emulator",
|
|
99
|
+
verifyToken: "emulator",
|
|
100
|
+
appSecret: env[EMULATOR_ENV.appSecret]?.trim() || EMULATOR_DEFAULTS.appSecret,
|
|
101
|
+
apiVersion: EMULATOR_DEFAULTS.apiVersion,
|
|
102
|
+
webhookPort: Number(env[EMULATOR_ENV.webhookPort]?.trim() || EMULATOR_DEFAULTS.webhookPort),
|
|
103
|
+
};
|
|
104
|
+
driver = new EmulatorDriver(url);
|
|
105
|
+
sessionKey = `${config.phoneNumberId}:${toGraphNumber(options.conversation.contact)}`;
|
|
106
|
+
backend = new WhatsAppEmulator({
|
|
107
|
+
config,
|
|
108
|
+
conversation: { contact: options.conversation.contact },
|
|
109
|
+
driver,
|
|
110
|
+
say,
|
|
111
|
+
render: options.render ?? say,
|
|
112
|
+
...(options.script ? { script: options.script } : {}),
|
|
113
|
+
...(options.ask ? { ask: options.ask } : {}),
|
|
114
|
+
...(options.pinAt ? { pinAt: options.pinAt } : {}),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The hours are the agent's, from `guardrails.envelope.collection_hours`. An agent that declares none gets no hours rule, which is correct: not every conversation is a collection.
|
|
119
|
+
const window = typeof s.guardrails.envelope?.["collection_hours"] === "string" ? (s.guardrails.envelope["collection_hours"] as string) : undefined;
|
|
120
|
+
|
|
121
|
+
const channel = new WhatsAppChannel({
|
|
122
|
+
backend,
|
|
123
|
+
conversation: options.conversation,
|
|
124
|
+
now: options.now,
|
|
125
|
+
...(window ? { hours: { window, timezone: s.guardrails.timezone } } : {}),
|
|
126
|
+
knownSubjects: knownSubjects(agent),
|
|
127
|
+
templates: loadTemplates(agent),
|
|
128
|
+
bundle: options.bundle,
|
|
129
|
+
say,
|
|
130
|
+
render: options.render ?? say,
|
|
131
|
+
...(options.session ? { session: options.session } : {}),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return { channel, ...(driver ? { driver } : {}), ...(sessionKey ? { sessionKey } : {}) };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The priced timeline the emulator computed: read for the console, never asserted on. */
|
|
138
|
+
export async function simulatedCost(driver: EmulatorDriver | undefined, sessionKey: string | undefined): Promise<{ total: number; currency: string } | undefined> {
|
|
139
|
+
if (!driver || !sessionKey) return undefined;
|
|
140
|
+
const state = (await driver.state(sessionKey).catch(() => undefined)) as { priced?: { total?: number; currency?: string } } | undefined;
|
|
141
|
+
if (typeof state?.priced?.total !== "number") return undefined;
|
|
142
|
+
return { total: state.priced.total, currency: state.priced.currency ?? "BRL" };
|
|
143
|
+
}
|