@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
|
@@ -8,14 +8,13 @@ import { mkdtempSync } from "node:fs";
|
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { describe, expect, it } from "vitest";
|
|
11
|
-
import { announceOutcome } from "
|
|
12
|
-
import {
|
|
13
|
-
import { RUNS_DIR, setup } from "../src/setup.js";
|
|
11
|
+
import { announceOutcome, createWebhookHandler, setup, verifySignature } from "@codespar/agent-runtime";
|
|
12
|
+
import { agent } from "../src/kit.js";
|
|
14
13
|
|
|
15
14
|
function issued(mode: "human" | "mandate" = "mandate") {
|
|
16
15
|
let clock = new Date("2026-09-23T18:00:00Z");
|
|
17
|
-
const s = setup({ mode, rail: "stub", provider: "replay", transcript: "unused", stateDir: mkdtempSync(join(tmpdir(), "collections-webhook-")), runsDir: mkdtempSync(join(tmpdir(), "collections-webhook-runs-")), now: () => (clock = new Date(clock.getTime() + 1000)), say: () => undefined });
|
|
18
|
-
s.payer
|
|
16
|
+
const s = setup(agent, { mode, rail: "stub", provider: "replay", transcript: "unused", stateDir: mkdtempSync(join(tmpdir(), "collections-webhook-")), runsDir: mkdtempSync(join(tmpdir(), "collections-webhook-runs-")), now: () => (clock = new Date(clock.getTime() + 1000)), say: () => undefined });
|
|
17
|
+
s.payer!.behave("never");
|
|
19
18
|
return s;
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -96,5 +95,3 @@ describe("channels/webhook", () => {
|
|
|
96
95
|
}
|
|
97
96
|
});
|
|
98
97
|
});
|
|
99
|
-
|
|
100
|
-
void RUNS_DIR;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The case the channel could not run before: agreed on Tuesday, paid on
|
|
3
|
+
* Friday.
|
|
4
|
+
*
|
|
5
|
+
* Every case that converses runs TWO processes over ONE state directory,
|
|
6
|
+
* because that is the shape in production — the run that agreed the terms has
|
|
7
|
+
* exited by the time the money lands, and what carries the cycle across is
|
|
8
|
+
* the record it left. Between them the CONVERSATION's clock moves
|
|
9
|
+
* (`POST /_sim/clock`), which is a different thing from the AGENT's clock
|
|
10
|
+
* (`--now`, #16): one is where the person's last message sits, the other is
|
|
11
|
+
* what the guardrails and the window comparison read.
|
|
12
|
+
*
|
|
13
|
+
* What is asserted is which CARRIER the confirmation went out on and that the
|
|
14
|
+
* cycle closed, never the wording. Read the template assertions precisely:
|
|
15
|
+
* the emulator prices the 24-hour window and does not enforce it
|
|
16
|
+
* (docs/OPEN_QUESTIONS.md §46a), so what passes here is OUR choice of
|
|
17
|
+
* carrier, made by the session window, and not the provider refusing the
|
|
18
|
+
* alternative.
|
|
19
|
+
*
|
|
20
|
+
* The conversing cases SKIP without the emulator, like the rest of the
|
|
21
|
+
* channel suite; `npm run whatsapp:gate` fails rather than skipping, so the
|
|
22
|
+
* thing that matters never hides behind one. The cases that need no
|
|
23
|
+
* conversation at all — the no-op, the flag that belongs to another channel —
|
|
24
|
+
* run everywhere.
|
|
25
|
+
*/
|
|
26
|
+
import { spawnSync } from "node:child_process";
|
|
27
|
+
import { existsSync, mkdtempSync, readFileSync, readdirSync } from "node:fs";
|
|
28
|
+
import { tmpdir } from "node:os";
|
|
29
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
30
|
+
import { DatabaseSync } from "node:sqlite";
|
|
31
|
+
import { describe, expect, it } from "vitest";
|
|
32
|
+
|
|
33
|
+
const AGENT_DIR = resolve(import.meta.dirname, "..");
|
|
34
|
+
const NODE = process.execPath;
|
|
35
|
+
const BIN = resolve(AGENT_DIR, "../../packages/agent-runtime/bin.mjs");
|
|
36
|
+
const EMULATOR = process.env["WHATSAPP_SIM_URL"] ?? "http://127.0.0.1:4290";
|
|
37
|
+
|
|
38
|
+
/** Tuesday, inside the collection hours the guardrails declare. */
|
|
39
|
+
const TUESDAY = "2026-09-23T14:00:00-03:00";
|
|
40
|
+
const ADVANCE_HOURS = 26;
|
|
41
|
+
/** The same instant plus the advance: 16:00 in Sao Paulo, still inside 08:00-20:00. */
|
|
42
|
+
const AFTER_WINDOW = new Date(new Date(TUESDAY).getTime() + ADVANCE_HOURS * 3600_000).toISOString();
|
|
43
|
+
/**
|
|
44
|
+
* The recording where the charge is issued and NOBODY has paid yet. The
|
|
45
|
+
* shipped happy-path one closes with "quitado" because its payer paid inside
|
|
46
|
+
* the turn; here that would be a lie, and it is the lie this whole command
|
|
47
|
+
* exists to stop telling.
|
|
48
|
+
*/
|
|
49
|
+
const AGREED = "test/fixtures/agreed-1042-awaiting-payer.transcript.jsonl";
|
|
50
|
+
|
|
51
|
+
interface ChannelLine {
|
|
52
|
+
direction: "in" | "out";
|
|
53
|
+
kind: string;
|
|
54
|
+
text?: string;
|
|
55
|
+
message_id: string;
|
|
56
|
+
provider_timestamp?: number;
|
|
57
|
+
refused?: { rule: string };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface Polled {
|
|
61
|
+
id: string;
|
|
62
|
+
state: string;
|
|
63
|
+
reason: string | null;
|
|
64
|
+
timed_out: boolean;
|
|
65
|
+
session_open: boolean;
|
|
66
|
+
delivery: { told: boolean; carrier?: string; template?: string; reason?: string };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface PollPayload {
|
|
70
|
+
polled: Polled[];
|
|
71
|
+
conversation: string;
|
|
72
|
+
channel: { backend: string; session_open: boolean; messages_out: number; refused: Array<{ rule: string }>; log: string } | null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function scratch(label: string) {
|
|
76
|
+
const stateDir = mkdtempSync(join(tmpdir(), `collections-wa-poll-${label}-`));
|
|
77
|
+
return { COLLECTIONS_STATE_DIR: stateDir, COLLECTIONS_RUNS_DIR: join(stateDir, "runs") };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function agent(args: string[], env: Record<string, string>) {
|
|
81
|
+
const result = spawnSync(NODE, [BIN, ...args], {
|
|
82
|
+
cwd: AGENT_DIR,
|
|
83
|
+
env: {
|
|
84
|
+
...process.env,
|
|
85
|
+
ANTHROPIC_API_KEY: "",
|
|
86
|
+
CODESPAR_API_KEY: "",
|
|
87
|
+
// A developer's own Meta credentials must not change what this suite does.
|
|
88
|
+
WHATSAPP_PHONE_NUMBER_ID: "",
|
|
89
|
+
WHATSAPP_ACCESS_TOKEN: "",
|
|
90
|
+
WHATSAPP_VERIFY_TOKEN: "",
|
|
91
|
+
WHATSAPP_APP_SECRET: "",
|
|
92
|
+
...env,
|
|
93
|
+
},
|
|
94
|
+
encoding: "utf8",
|
|
95
|
+
timeout: 90_000,
|
|
96
|
+
});
|
|
97
|
+
return { code: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Tuesday: the debtor agrees, the bolepix is issued, and the fixture payer is told to sit on it. */
|
|
101
|
+
function agree(env: Record<string, string>) {
|
|
102
|
+
return agent(
|
|
103
|
+
["start", "--channel", "whatsapp", "--conversation", "acordo-1042", "--scripted", "--mode", "mandate", "--transcript", AGREED, "--now", TUESDAY, "--json"],
|
|
104
|
+
{ ...env, COLLECTIONS_STUB_PAYER: "never" },
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function poll(env: Record<string, string>, extra: string[] = []) {
|
|
109
|
+
const out = agent(["poll", "--channel", "whatsapp", "--conversation", "acordo-1042", "--json", ...extra], env);
|
|
110
|
+
const lines = out.stdout.split("\n").filter(Boolean);
|
|
111
|
+
expect(lines).toHaveLength(1);
|
|
112
|
+
return { ...out, payload: JSON.parse(lines[0]!) as PollPayload };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function advanceConversationClock(hours: number) {
|
|
116
|
+
const response = await fetch(`${EMULATOR}/_sim/clock`, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: { "content-type": "application/json" },
|
|
119
|
+
body: JSON.stringify({ advance_hours: hours }),
|
|
120
|
+
signal: AbortSignal.timeout(5000),
|
|
121
|
+
});
|
|
122
|
+
expect(response.ok).toBe(true);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function conversationOf(logPath: string): ChannelLine[] {
|
|
126
|
+
const path = isAbsolute(logPath) ? logPath : join(AGENT_DIR, logPath);
|
|
127
|
+
expect(existsSync(path)).toBe(true);
|
|
128
|
+
return readFileSync(path, "utf8").split("\n").filter(Boolean).map((l) => JSON.parse(l) as ChannelLine);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function countEvents(env: Record<string, string>, type: string): number {
|
|
132
|
+
const db = new DatabaseSync(join(env["COLLECTIONS_STATE_DIR"]!, "state.db"));
|
|
133
|
+
try {
|
|
134
|
+
return (db.prepare("SELECT COUNT(*) AS n FROM events WHERE type = ?").get(type) as { n: number }).n;
|
|
135
|
+
} finally {
|
|
136
|
+
db.close();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const emulatorUp = await (async () => {
|
|
141
|
+
try {
|
|
142
|
+
return (await fetch(`${EMULATOR}/health`, { signal: AbortSignal.timeout(2000) })).ok;
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
})();
|
|
147
|
+
if (!emulatorUp) process.stderr.write(`[whatsapp] no emulator at ${EMULATOR}; the poll's conversing cases are skipped. Start it with \`npm run whatsapp:emulator\`.\n`);
|
|
148
|
+
|
|
149
|
+
describe.skipIf(!emulatorUp)("a charge paid after the 24-hour window closed", () => {
|
|
150
|
+
it("confirms by template, closes the cycle, and leaves one conversation rather than two", async () => {
|
|
151
|
+
const env = scratch("template");
|
|
152
|
+
|
|
153
|
+
const agreed = agree(env);
|
|
154
|
+
// Nobody has paid: the execution is still open and that is why the process exits 3.
|
|
155
|
+
expect(agreed.code).toBe(3);
|
|
156
|
+
const opened = JSON.parse(agreed.stdout.split("\n").filter(Boolean).pop()!) as { executions: Array<{ state: string; reason: string }>; channel: { log: string } };
|
|
157
|
+
expect(opened.executions[0]).toMatchObject({ state: "executing", reason: "awaiting_settlement" });
|
|
158
|
+
const before = conversationOf(opened.channel.log);
|
|
159
|
+
// The person's own message carries the provider's clock, which is the only
|
|
160
|
+
// thing the window can be counted from once this process is gone.
|
|
161
|
+
expect(before.filter((l) => l.direction === "in").every((l) => typeof l.provider_timestamp === "number")).toBe(true);
|
|
162
|
+
expect(before.some((l) => l.direction === "out" && /quitad/i.test(l.text ?? ""))).toBe(false);
|
|
163
|
+
|
|
164
|
+
await advanceConversationClock(ADVANCE_HOURS);
|
|
165
|
+
|
|
166
|
+
const polled = poll(env, ["--simulate-payer", "--now", AFTER_WINDOW]);
|
|
167
|
+
expect(polled.code).toBe(0);
|
|
168
|
+
expect(polled.payload.polled).toHaveLength(1);
|
|
169
|
+
expect(polled.payload.polled[0]).toMatchObject({
|
|
170
|
+
state: "settled",
|
|
171
|
+
timed_out: false,
|
|
172
|
+
session_open: false,
|
|
173
|
+
delivery: { told: true, carrier: "template", template: "acordo_quitado" },
|
|
174
|
+
});
|
|
175
|
+
expect(polled.payload.channel?.refused).toEqual([]);
|
|
176
|
+
// One message, and only one, went out with the window shut.
|
|
177
|
+
expect(polled.payload.channel?.messages_out).toBe(1);
|
|
178
|
+
|
|
179
|
+
// The confirmation was appended to the bundle of the run that opened the
|
|
180
|
+
// conversation, so it sits under the QR it confirms instead of in a second folder.
|
|
181
|
+
const after = conversationOf(polled.payload.channel!.log);
|
|
182
|
+
expect(after.slice(0, before.length)).toEqual(before);
|
|
183
|
+
const last = after[after.length - 1]!;
|
|
184
|
+
expect(last.kind).toBe("template");
|
|
185
|
+
expect(last.text).toContain("acordo_quitado");
|
|
186
|
+
expect(last.refused).toBeUndefined();
|
|
187
|
+
// It went through the provider's own surface, not around it.
|
|
188
|
+
expect(last.message_id).toMatch(/^wamid\./);
|
|
189
|
+
|
|
190
|
+
expect(countEvents(env, "message.debtor")).toBe(1);
|
|
191
|
+
expect(countEvents(env, "commerce.charge.paid")).toBe(1);
|
|
192
|
+
const runs = env["COLLECTIONS_RUNS_DIR"]!;
|
|
193
|
+
const receipts = readdirSync(runs).flatMap((d) => (existsSync(join(runs, d, "receipts")) ? readdirSync(join(runs, d, "receipts")) : []));
|
|
194
|
+
expect(receipts).toHaveLength(1);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("polled twice, tells the person once: the cursor that says so is in state.db, not in the process", async () => {
|
|
198
|
+
const env = scratch("twice");
|
|
199
|
+
expect(agree(env).code).toBe(3);
|
|
200
|
+
await advanceConversationClock(ADVANCE_HOURS);
|
|
201
|
+
|
|
202
|
+
const first = poll(env, ["--simulate-payer", "--now", AFTER_WINDOW]);
|
|
203
|
+
expect(first.payload.polled[0]!.delivery.told).toBe(true);
|
|
204
|
+
|
|
205
|
+
// The execution is terminal, so there is nothing left waiting for a payer
|
|
206
|
+
// and the second poll never opens the channel at all.
|
|
207
|
+
const second = poll(env, ["--simulate-payer", "--now", AFTER_WINDOW]);
|
|
208
|
+
expect(second.code).toBe(0);
|
|
209
|
+
expect(second.payload.polled).toEqual([]);
|
|
210
|
+
expect(second.payload.channel).toBeNull();
|
|
211
|
+
expect(countEvents(env, "message.debtor")).toBe(1);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("writes freely while the window is still open: the carrier follows the rule, not the command", async () => {
|
|
215
|
+
const env = scratch("open");
|
|
216
|
+
expect(agree(env).code).toBe(3);
|
|
217
|
+
// No clock move: the person wrote minutes ago.
|
|
218
|
+
const polled = poll(env, ["--simulate-payer", "--now", "2026-09-23T15:30:00-03:00"]);
|
|
219
|
+
expect(polled.code).toBe(0);
|
|
220
|
+
expect(polled.payload.polled[0]).toMatchObject({ state: "settled", session_open: true, delivery: { told: true, carrier: "text" } });
|
|
221
|
+
const last = conversationOf(polled.payload.channel!.log).at(-1)!;
|
|
222
|
+
expect(last.kind).toBe("text");
|
|
223
|
+
expect(last.text).toMatch(/quitad/i);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("a charge that expired gets the expiry template and not the paid one", async () => {
|
|
227
|
+
const env = scratch("expired");
|
|
228
|
+
expect(agree(env).code).toBe(3);
|
|
229
|
+
await advanceConversationClock(ADVANCE_HOURS);
|
|
230
|
+
|
|
231
|
+
// No `--simulate-payer`: nobody paid, and that is the point. `--payer
|
|
232
|
+
// expires` rewrites the fate of the receivable this state already holds.
|
|
233
|
+
const polled = poll(env, ["--payer", "expires", "--now", AFTER_WINDOW]);
|
|
234
|
+
expect(polled.payload.polled[0]).toMatchObject({
|
|
235
|
+
state: "failed",
|
|
236
|
+
reason: "charge_expired",
|
|
237
|
+
session_open: false,
|
|
238
|
+
delivery: { told: true, carrier: "template", template: "acordo_cobranca_vencida" },
|
|
239
|
+
});
|
|
240
|
+
const last = conversationOf(polled.payload.channel!.log).at(-1)!;
|
|
241
|
+
expect(last.text).toContain("acordo_cobranca_vencida");
|
|
242
|
+
expect(last.text).not.toContain("acordo_quitado");
|
|
243
|
+
// Nothing was paid, so nothing was sealed.
|
|
244
|
+
const runs = env["COLLECTIONS_RUNS_DIR"]!;
|
|
245
|
+
expect(readdirSync(runs).flatMap((d) => (existsSync(join(runs, d, "receipts")) ? readdirSync(join(runs, d, "receipts")) : []))).toHaveLength(0);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe("what the poll refuses to be asked, with no emulator in sight", () => {
|
|
250
|
+
it("nothing waiting for a payer is a clean no-op, not an error: a cron that fails on an empty queue gets turned off", () => {
|
|
251
|
+
const env = scratch("noop");
|
|
252
|
+
const out = poll(env, ["--now", TUESDAY]);
|
|
253
|
+
expect(out.code).toBe(0);
|
|
254
|
+
expect(out.payload).toEqual({ polled: [], conversation: "acordo-1042", channel: null });
|
|
255
|
+
// The channel was never opened, so a machine with no emulator running
|
|
256
|
+
// still gets a clean answer.
|
|
257
|
+
expect(out.stderr).not.toContain("emulator backend");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("refuses --conversation on the terminal channel rather than quietly ignoring it", () => {
|
|
261
|
+
const out = agent(["poll", "--conversation", "acordo-1042", "--json"], scratch("flag"));
|
|
262
|
+
expect(out.code).toBe(2);
|
|
263
|
+
expect(out.stderr).toContain("belong to --channel whatsapp");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("refuses a flag it does not know, so a typo is not a silently different run", () => {
|
|
267
|
+
const out = agent(["poll", "--chanel", "whatsapp"], scratch("typo"));
|
|
268
|
+
expect(out.code).toBe(2);
|
|
269
|
+
expect(out.stderr).toContain("unknown argument --chanel");
|
|
270
|
+
});
|
|
271
|
+
});
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The collections-agent on the channel it was written for.
|
|
3
|
+
*
|
|
4
|
+
* Every case here runs a real process: no key of either kind, and the
|
|
5
|
+
* WHATSAPP_* variables explicitly blanked, so a machine that happens to have
|
|
6
|
+
* Meta credentials in its shell runs the same test as the CI.
|
|
7
|
+
*
|
|
8
|
+
* The cases that actually converse need `dyvit-wa-sim` listening — the local
|
|
9
|
+
* Cloud API emulator the channel's `simulator` backend talks to — and SKIP
|
|
10
|
+
* when it is not, so `npm test` is green on a machine that never started it.
|
|
11
|
+
* The CI starts it, and `npm run whatsapp:gate` fails rather than skipping, so
|
|
12
|
+
* the thing that matters never hides behind a skip. The cases that check what
|
|
13
|
+
* the channel REFUSES to be asked need no emulator: those refusals happen
|
|
14
|
+
* before anything is opened, and they run everywhere.
|
|
15
|
+
*
|
|
16
|
+
* What is asserted is the final state and the shape of the conversation, never
|
|
17
|
+
* the wording — the copy-and-paste as its own message, the operator's question
|
|
18
|
+
* nowhere near the debtor, nothing at all outside the collection hours.
|
|
19
|
+
*/
|
|
20
|
+
import { spawnSync } from "node:child_process";
|
|
21
|
+
import { existsSync, mkdtempSync, readFileSync } from "node:fs";
|
|
22
|
+
import { tmpdir } from "node:os";
|
|
23
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
24
|
+
import { describe, expect, it } from "vitest";
|
|
25
|
+
|
|
26
|
+
const AGENT_DIR = resolve(import.meta.dirname, "..");
|
|
27
|
+
const NODE = process.execPath;
|
|
28
|
+
const BIN = resolve(AGENT_DIR, "../../packages/agent-runtime/bin.mjs");
|
|
29
|
+
|
|
30
|
+
interface ChannelLine {
|
|
31
|
+
direction: "in" | "out";
|
|
32
|
+
kind: string;
|
|
33
|
+
text?: string;
|
|
34
|
+
contact: string;
|
|
35
|
+
state: string;
|
|
36
|
+
message_id: string;
|
|
37
|
+
refused?: { rule: string };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface Payload {
|
|
41
|
+
executions: Array<{ state: string; charges: Array<{ charge_id: string | null }> }>;
|
|
42
|
+
receipts: string[];
|
|
43
|
+
channel: { backend: string; conversation: string; turns: number; messages_in: number; messages_out: number; refused: Array<{ rule: string }>; log: string };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function run(args: string[], extra: Record<string, string> = {}) {
|
|
47
|
+
const stateDir = mkdtempSync(join(tmpdir(), "collections-wa-"));
|
|
48
|
+
const result = spawnSync(NODE, [BIN, "start", ...args], {
|
|
49
|
+
cwd: AGENT_DIR,
|
|
50
|
+
env: {
|
|
51
|
+
...process.env,
|
|
52
|
+
ANTHROPIC_API_KEY: "",
|
|
53
|
+
CODESPAR_API_KEY: "",
|
|
54
|
+
// A developer's own Meta credentials must not change what this suite does.
|
|
55
|
+
WHATSAPP_PHONE_NUMBER_ID: "",
|
|
56
|
+
WHATSAPP_ACCESS_TOKEN: "",
|
|
57
|
+
WHATSAPP_VERIFY_TOKEN: "",
|
|
58
|
+
WHATSAPP_APP_SECRET: "",
|
|
59
|
+
COLLECTIONS_STATE_DIR: stateDir,
|
|
60
|
+
COLLECTIONS_RUNS_DIR: join(stateDir, "runs"),
|
|
61
|
+
...extra,
|
|
62
|
+
},
|
|
63
|
+
encoding: "utf8",
|
|
64
|
+
timeout: 90_000,
|
|
65
|
+
});
|
|
66
|
+
return { code: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function payloadOf(stdout: string): Payload {
|
|
70
|
+
const lines = stdout.split("\n").filter(Boolean);
|
|
71
|
+
expect(lines).toHaveLength(1);
|
|
72
|
+
return JSON.parse(lines[0]!) as Payload;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function conversationOf(payload: Payload): ChannelLine[] {
|
|
76
|
+
const path = isAbsolute(payload.channel.log) ? payload.channel.log : join(AGENT_DIR, payload.channel.log);
|
|
77
|
+
expect(existsSync(path)).toBe(true);
|
|
78
|
+
return readFileSync(path, "utf8").split("\n").filter(Boolean).map((l) => JSON.parse(l) as ChannelLine);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const EMULATOR = process.env["WHATSAPP_SIM_URL"] ?? "http://127.0.0.1:4290";
|
|
82
|
+
/** Probed at module level: `describe.skipIf` is read when the file is collected, before any hook runs. */
|
|
83
|
+
const emulatorUp = await (async () => {
|
|
84
|
+
try {
|
|
85
|
+
return (await fetch(`${EMULATOR}/health`, { signal: AbortSignal.timeout(2000) })).ok;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
})();
|
|
90
|
+
if (!emulatorUp) process.stderr.write(`[whatsapp] no emulator at ${EMULATOR}; the conversing cases are skipped. Start it with \`npm run whatsapp:emulator\`.\n`);
|
|
91
|
+
|
|
92
|
+
const INSIDE_HOURS = ["--now", "2026-09-23T14:00:00-03:00"];
|
|
93
|
+
const AFTER_HOURS = ["--now", "2026-09-23T22:30:00-03:00"];
|
|
94
|
+
const SCRIPTED = ["--channel", "whatsapp", "--conversation", "acordo-1042", "--scripted", "--simulate-payer", "--json"];
|
|
95
|
+
|
|
96
|
+
describe.skipIf(!emulatorUp)("the cycle closes over the WhatsApp channel", () => {
|
|
97
|
+
it("settles in mandate mode with nobody at a keyboard, and sends the QR with the copy-and-paste under it", () => {
|
|
98
|
+
const out = run([...SCRIPTED, "--mode", "mandate", ...INSIDE_HOURS]);
|
|
99
|
+
expect(out.code).toBe(0);
|
|
100
|
+
const payload = payloadOf(out.stdout);
|
|
101
|
+
expect(payload.executions.map((e) => e.state)).toEqual(["settled"]);
|
|
102
|
+
expect(payload.executions[0]!.charges).toHaveLength(1);
|
|
103
|
+
expect(payload.receipts).toHaveLength(1);
|
|
104
|
+
expect(payload.channel.backend).toBe("emulator");
|
|
105
|
+
// The one expected refusal: neither backend can upload the QR image.
|
|
106
|
+
expect(payload.channel.refused.map((r) => r.rule)).toEqual(["media_upload_unimplemented"]);
|
|
107
|
+
|
|
108
|
+
const conversation = conversationOf(payload);
|
|
109
|
+
const outbound = conversation.filter((l) => l.direction === "out");
|
|
110
|
+
const qr = outbound.findIndex((l) => l.kind === "media");
|
|
111
|
+
expect(qr).toBeGreaterThanOrEqual(0);
|
|
112
|
+
expect(outbound[qr + 1]!.kind).toBe("instrument");
|
|
113
|
+
expect(outbound[qr + 1]!.text).toMatch(/^00020126/);
|
|
114
|
+
expect(conversation.filter((l) => l.direction === "in")).toHaveLength(2);
|
|
115
|
+
// Every delivered message carries a provider id: it went through the Cloud API surface, not around it.
|
|
116
|
+
for (const line of outbound.filter((l) => !l.refused)) expect(line.message_id).toMatch(/^wamid\./);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("settles in human mode too, and the operator's question never reaches the debtor", () => {
|
|
120
|
+
const out = run([...SCRIPTED, "--mode", "human", "--approve", ...INSIDE_HOURS]);
|
|
121
|
+
expect(out.code).toBe(0);
|
|
122
|
+
const payload = payloadOf(out.stdout);
|
|
123
|
+
expect(payload.executions.map((e) => e.state)).toEqual(["settled"]);
|
|
124
|
+
// The question is the operator's and is on the console; nothing like it is in the conversation.
|
|
125
|
+
for (const line of conversationOf(payload)) expect(line.text ?? "").not.toMatch(/operador|Aprovar a emissao/i);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("writes the conversation into the bundle with the contact masked", () => {
|
|
129
|
+
const out = run([...SCRIPTED, "--mode", "mandate", ...INSIDE_HOURS]);
|
|
130
|
+
const conversation = conversationOf(payloadOf(out.stdout));
|
|
131
|
+
expect(conversation.length).toBeGreaterThan(0);
|
|
132
|
+
for (const line of conversation) {
|
|
133
|
+
expect(line.contact).not.toContain("987654321");
|
|
134
|
+
expect(line.contact).toContain("****");
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("tells the person once when the charge expires, and writes no record", () => {
|
|
139
|
+
const out = run(["--channel", "whatsapp", "--conversation", "acordo-1103", "--scripted", "--mode", "mandate", "--payer", "expires", "--json", ...INSIDE_HOURS]);
|
|
140
|
+
const payload = payloadOf(out.stdout);
|
|
141
|
+
expect(payload.executions.map((e) => e.state)).toEqual(["failed"]);
|
|
142
|
+
expect(payload.receipts).toEqual([]);
|
|
143
|
+
expect(payload.channel.conversation).toBe("acordo-1103");
|
|
144
|
+
// The QR still went out: it was payable, nobody paid it.
|
|
145
|
+
const outbound = conversationOf(payload).filter((l) => l.direction === "out");
|
|
146
|
+
expect(outbound.some((l) => l.kind === "media")).toBe(true);
|
|
147
|
+
// The kit's one-per-outcome message, sent once however many looks carried the event.
|
|
148
|
+
// The model's own reply also says the charge expired, which is the terminal's behaviour too.
|
|
149
|
+
expect(outbound.filter((l) => String(l.text ?? "").startsWith("A cobranca venceu sem pagamento"))).toHaveLength(1);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("refuses to say anything at all outside the collection hours, and issues nothing", () => {
|
|
153
|
+
const out = run([...SCRIPTED, "--mode", "mandate", ...AFTER_HOURS]);
|
|
154
|
+
const payload = payloadOf(out.stdout);
|
|
155
|
+
expect(payload.channel.messages_out).toBe(0);
|
|
156
|
+
// The emulator would have accepted every one of them: the collection hours are OUR rule, not WhatsApp's.
|
|
157
|
+
expect(payload.channel.refused.map((r) => r.rule)).toContain("collection_hours");
|
|
158
|
+
expect(payload.executions.flatMap((e) => e.charges)).toEqual([]);
|
|
159
|
+
expect(payload.receipts).toEqual([]);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("what the channel refuses to be asked", () => {
|
|
164
|
+
it("refuses the cloud-api backend by naming every credential this repo does not ship", () => {
|
|
165
|
+
const out = run(["--channel", "whatsapp", "--conversation", "acordo-1042", "--scripted", "--backend", "cloud-api", "--mode", "mandate", ...INSIDE_HOURS]);
|
|
166
|
+
expect(out.code).toBe(1);
|
|
167
|
+
for (const name of ["WHATSAPP_PHONE_NUMBER_ID", "WHATSAPP_ACCESS_TOKEN", "WHATSAPP_VERIFY_TOKEN", "WHATSAPP_APP_SECRET"]) {
|
|
168
|
+
expect(out.stderr).toContain(name);
|
|
169
|
+
}
|
|
170
|
+
expect(out.stderr).toContain("emulator");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("refuses --input on a channel: a conversation takes its turns from the conversation", () => {
|
|
174
|
+
const out = run(["--channel", "whatsapp", "--input", "oi", "--json"]);
|
|
175
|
+
expect(out.code).toBe(2);
|
|
176
|
+
expect(out.stderr).toContain("--input");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("refuses the channel flags on the terminal", () => {
|
|
180
|
+
expect(run(["--backend", "simulator", "--input", "oi"]).code).toBe(2);
|
|
181
|
+
expect(run(["--conversation", "acordo-1042", "--input", "oi"]).code).toBe(2);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("refuses a scripted human-mode run rather than waiting on a keyboard nobody is at", () => {
|
|
185
|
+
const out = run(["--channel", "whatsapp", "--conversation", "acordo-1042", "--scripted", "--mode", "human", "--json", ...INSIDE_HOURS]);
|
|
186
|
+
expect(out.code).toBe(2);
|
|
187
|
+
expect(out.stderr).toContain("--approve");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("asks which conversation when the agent ships more than one: which debtor is not a default", () => {
|
|
191
|
+
const out = run(["--channel", "whatsapp", "--scripted", "--json", ...INSIDE_HOURS]);
|
|
192
|
+
expect(out.code).toBe(2);
|
|
193
|
+
expect(out.stderr).toContain("--conversation is required");
|
|
194
|
+
expect(out.stderr).toContain("acordo-1103");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("refuses a conversation the agent does not ship", () => {
|
|
198
|
+
const out = run(["--channel", "whatsapp", "--scripted", "--conversation", "acordo-9999", "--json", ...INSIDE_HOURS]);
|
|
199
|
+
expect(out.code).toBe(2);
|
|
200
|
+
expect(out.stderr).toContain("acordo-1042");
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("the terminal is untouched", () => {
|
|
205
|
+
it("still runs the same one-shot with no channel flag", () => {
|
|
206
|
+
const out = run(["--input", "fechado, pago à vista", "--transcript", "test/fixtures/accept-1042.transcript.jsonl", "--approve", "--simulate-payer", "--json", ...INSIDE_HOURS]);
|
|
207
|
+
expect(out.code).toBe(0);
|
|
208
|
+
const payload = JSON.parse(out.stdout.split("\n").filter(Boolean)[0]!) as Payload & { channel?: unknown };
|
|
209
|
+
expect(payload.executions.map((e) => e.state)).toEqual(["settled"]);
|
|
210
|
+
// No channel key: a terminal run is not a channel run, and its JSON shape did not change.
|
|
211
|
+
expect(payload.channel).toBeUndefined();
|
|
212
|
+
});
|
|
213
|
+
});
|
|
@@ -7,16 +7,18 @@
|
|
|
7
7
|
"type": "module",
|
|
8
8
|
"workspaces": [
|
|
9
9
|
"packages/agent-core",
|
|
10
|
+
"packages/agent-runtime",
|
|
10
11
|
"agents/collections-agent"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
14
|
"start": "npm start --workspace=agents/collections-agent --",
|
|
14
15
|
"check": "npm run check --workspaces --if-present --",
|
|
15
16
|
"test": "vitest run --pool=forks --maxWorkers=1",
|
|
16
|
-
"typecheck": "tsc --noEmit -p packages/agent-core/tsconfig.json && tsc --noEmit -p agents/collections-agent/tsconfig.json",
|
|
17
|
+
"typecheck": "tsc --noEmit -p packages/agent-core/tsconfig.json && tsc --noEmit -p packages/agent-runtime/tsconfig.json && tsc --noEmit -p agents/collections-agent/tsconfig.json",
|
|
17
18
|
"approve": "npm run approve --workspace=agents/collections-agent --",
|
|
18
19
|
"deny": "npm run deny --workspace=agents/collections-agent --",
|
|
19
20
|
"eval": "npm run eval --workspace=agents/collections-agent --",
|
|
21
|
+
"inspect": "npm run inspect --workspace=agents/collections-agent --",
|
|
20
22
|
"poll": "npm run poll --workspace=agents/collections-agent --",
|
|
21
23
|
"reconcile": "npm run reconcile --workspace=agents/collections-agent --",
|
|
22
24
|
"rerun": "npm run rerun --workspace=agents/collections-agent --",
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The primitives every CodeSpar starter-kit agent inherits (spec v5.1.1, section 4): the execution state machine, the approval artifact, the `agent.yaml` manifest, `escalate_above`, `actor`, local state in SQLite, the proof bundle, the rail contract and the two providers.
|
|
4
4
|
|
|
5
|
+
This package is the decisions. The RUNNER around them — the terminal channel, `codespar-agent start|consent|approve|deny|resume|rerun|reconcile|poll|webhook|check|eval`, the setup that wires this package to an agent directory, and the scenario and adversarial runners — is [`@codespar/agent-runtime`](../agent-runtime), which depends on this one and is what an agent's `package.json` scripts call (#13). Nothing here knows an agent's name.
|
|
6
|
+
|
|
5
7
|
## The state machine, and what the types do and do not prove
|
|
6
8
|
|
|
7
9
|
`transition(execution, to, input)` accepts only a `to` that the table in `state-machine.ts` lists for `execution.state`. On a **narrowed** value (`Execution<"drafted">`, `Execution<"executing">`, ...) a transition outside the table is a compile error, and `test/transitions.type-test.ts` keeps that true with `@ts-expect-error` lines that `tsc` refuses to leave unused. An **un-narrowed** `Execution` (the union, which is what a row read back from SQLite is) compiles for any target; the engine narrows with casts at its call sites. So the guard that always holds is the runtime one: `transition()` throws `IllegalTransitionError` on any pair the table does not list, whatever the static type said. The type test proves the table is closed; the runtime check enforces it.
|
|
@@ -79,10 +79,12 @@ export class CodeSparChargeRail implements PaymentRail {
|
|
|
79
79
|
|
|
80
80
|
async pay(payment: RailPayment): Promise<RailOutcome> {
|
|
81
81
|
if (!payment.due_date) return { status: "failed", code: "due_date_required", message: "a receivable needs a due_date; the immediate Pix charge cannot be read back or replayed" };
|
|
82
|
+
if (!payment.consumer_id) return { status: "failed", code: "consumer_id_required", message: "a receivable settles into the principal's account; the policy names no consumer_id" };
|
|
82
83
|
let view: ChargeView;
|
|
83
84
|
try {
|
|
84
85
|
view = (await this.api.post("/v1/charges", {
|
|
85
86
|
body: {
|
|
87
|
+
consumer_id: payment.consumer_id,
|
|
86
88
|
amount: payment.amount_minor / 100,
|
|
87
89
|
currency: payment.currency,
|
|
88
90
|
method: "boleto",
|
|
@@ -108,15 +110,32 @@ export class CodeSparChargeRail implements PaymentRail {
|
|
|
108
110
|
return outcome.status === "in_flight" ? { status: "uncertain", code: "issuance_unconfirmed", message: "the create answered no readable state" } : outcome;
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
/**
|
|
114
|
+
* By the charge id when the create handed one back; by our idempotency key only when it did not (the create's answer was lost).
|
|
115
|
+
* The route's doc says the read accepts the caller's key as the id; on staging (2026-09-23) it answered `charge_not_found` for
|
|
116
|
+
* the key and the charge for the id, so the id comes first and the key is the fallback, not the other way round.
|
|
117
|
+
*/
|
|
118
|
+
async lookup(attemptId: string, _payment: RailPayment, transactionId?: string): Promise<RailLookup> {
|
|
119
|
+
const byId = transactionId ? await this.read(transactionId) : { kind: "missing" as const };
|
|
120
|
+
if (byId.kind === "view") return outcomeOf(byId.view);
|
|
121
|
+
if (byId.kind === "in_flight") return { status: "in_flight" };
|
|
122
|
+
if (byId.kind === "uncertain") return byId.outcome;
|
|
123
|
+
const byKey = await this.read(attemptId);
|
|
124
|
+
if (byKey.kind === "view") return outcomeOf(byKey.view);
|
|
125
|
+
if (byKey.kind === "in_flight") return { status: "in_flight" };
|
|
126
|
+
if (byKey.kind === "uncertain") return byKey.outcome;
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private async read(chargeRef: string): Promise<{ kind: "view"; view: ChargeView } | { kind: "missing" } | { kind: "in_flight" } | { kind: "uncertain"; outcome: Extract<RailOutcome, { status: "uncertain" }> }> {
|
|
112
131
|
try {
|
|
113
|
-
const view = (await this.api.get("/v1/charges/{chargeId}", { path: { chargeId:
|
|
114
|
-
return
|
|
132
|
+
const view = (await this.api.get("/v1/charges/{chargeId}", { path: { chargeId: chargeRef } })) as ChargeView;
|
|
133
|
+
return { kind: "view", view };
|
|
115
134
|
} catch (err) {
|
|
116
135
|
const failure = describeApiError(err);
|
|
117
|
-
if (failure.status === 404) return
|
|
118
|
-
if (failure.status === 409) return {
|
|
119
|
-
return { status: "uncertain", code: failure.code, message: failure.message };
|
|
136
|
+
if (failure.status === 404) return { kind: "missing" };
|
|
137
|
+
if (failure.status === 409) return { kind: "in_flight" };
|
|
138
|
+
return { kind: "uncertain", outcome: { status: "uncertain", code: failure.code, message: failure.message } };
|
|
120
139
|
}
|
|
121
140
|
}
|
|
122
141
|
|
|
@@ -133,6 +152,8 @@ export class CodeSparChargeRail implements PaymentRail {
|
|
|
133
152
|
payment: { amount_minor: view.amount_minor, payee: null, attempt_id: chargeId, money_moved: false, sandbox: true, at: raw.settled_at ?? new Date().toISOString() },
|
|
134
153
|
chain: null,
|
|
135
154
|
receipt_sig: null,
|
|
155
|
+
receipt_sig_ed25519: null,
|
|
156
|
+
receipt_sig_kid: null,
|
|
136
157
|
actor,
|
|
137
158
|
raw: view,
|
|
138
159
|
};
|
|
@@ -18,6 +18,16 @@ import type { PaymentRail, RailLookup, RailOutcome, RailPayment, RailReceipt } f
|
|
|
18
18
|
import type { Actor } from "../types.js";
|
|
19
19
|
import { describeApiError, isUncertain } from "./client.js";
|
|
20
20
|
|
|
21
|
+
/** The asymmetric seal as the API serves it, or nulls when this deployment
|
|
22
|
+
* does not serve one. Never throws on a shape it does not recognise: a
|
|
23
|
+
* receipt is evidence of a payment that already happened, and refusing to
|
|
24
|
+
* record it over an unexpected field would trade the record for the proof. */
|
|
25
|
+
function readEd25519Seal(body: unknown): { receipt_sig_ed25519: string | null; receipt_sig_kid: string | null } {
|
|
26
|
+
const r = body as { receipt_sig_ed25519?: unknown; receipt_sig_kid?: unknown };
|
|
27
|
+
const str = (v: unknown): string | null => (typeof v === "string" && v.length > 0 ? v : null);
|
|
28
|
+
return { receipt_sig_ed25519: str(r.receipt_sig_ed25519), receipt_sig_kid: str(r.receipt_sig_kid) };
|
|
29
|
+
}
|
|
30
|
+
|
|
21
31
|
export class CodeSparRail implements PaymentRail {
|
|
22
32
|
readonly name = "codespar" as const;
|
|
23
33
|
|
|
@@ -89,6 +99,12 @@ export class CodeSparRail implements PaymentRail {
|
|
|
89
99
|
},
|
|
90
100
|
chain: r.chain,
|
|
91
101
|
receipt_sig: r.receipt_sig,
|
|
102
|
+
// ent#1633 landed after `@codespar/sdk@0.16.6` was generated, so the
|
|
103
|
+
// typed response does not carry these two yet. Read off the body
|
|
104
|
+
// defensively rather than pinning a new SDK for two nullable strings:
|
|
105
|
+
// a deployment that predates the change answers null, which is the
|
|
106
|
+
// same answer the columns hold and reads as `unsigned`.
|
|
107
|
+
...readEd25519Seal(r),
|
|
92
108
|
actor,
|
|
93
109
|
raw: r,
|
|
94
110
|
};
|