@codespar/cli 0.14.1 → 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/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/surface.js +4 -1
- package/dist/surface.js.map +1 -1
- package/package.json +1 -1
- package/templates/bills-agent/README.md +8 -5
- package/templates/bills-agent/agents/bills-agent/.env.example +3 -3
- package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
- package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
- package/templates/bills-agent/agents/bills-agent/README.md +62 -22
- package/templates/bills-agent/agents/bills-agent/SYSTEM_PROMPT.md +1 -1
- package/templates/bills-agent/agents/bills-agent/agent.yaml +2 -2
- package/templates/bills-agent/agents/bills-agent/package.json +14 -11
- package/templates/bills-agent/agents/bills-agent/src/kit.ts +193 -0
- package/templates/bills-agent/agents/bills-agent/test/adversarial.test.ts +8 -7
- package/templates/bills-agent/agents/bills-agent/test/check.test.ts +19 -2
- package/templates/bills-agent/agents/bills-agent/test/cli.test.ts +72 -24
- package/templates/bills-agent/agents/bills-agent/test/fixtures/excursao-300.transcript.jsonl +2 -0
- package/templates/bills-agent/agents/bills-agent/test/fixtures/material-400.transcript.jsonl +2 -0
- package/templates/bills-agent/agents/bills-agent/test/inspect.test.ts +124 -0
- package/templates/bills-agent/agents/bills-agent/test/provider.test.ts +25 -0
- package/templates/bills-agent/agents/bills-agent/test/scenarios.test.ts +10 -9
- package/templates/bills-agent/agents/bills-agent/tsconfig.json +0 -1
- package/templates/bills-agent/package.json +5 -2
- package/templates/bills-agent/packages/agent-core/README.md +2 -0
- package/templates/bills-agent/packages/agent-core/package.json +1 -1
- package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +27 -6
- package/templates/bills-agent/packages/agent-core/src/api/rail.ts +16 -0
- package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +20 -7
- package/templates/bills-agent/packages/agent-core/src/approval.ts +16 -2
- package/templates/bills-agent/packages/agent-core/src/bundle.ts +50 -4
- package/templates/bills-agent/packages/agent-core/src/channels.ts +140 -0
- package/templates/bills-agent/packages/agent-core/src/check.ts +90 -8
- package/templates/bills-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/bills-agent/packages/agent-core/src/engine.ts +119 -18
- package/templates/bills-agent/packages/agent-core/src/hash.ts +13 -0
- package/templates/bills-agent/packages/agent-core/src/index.ts +3 -0
- package/templates/bills-agent/packages/agent-core/src/manifest.ts +1 -1
- package/templates/bills-agent/packages/agent-core/src/rail.ts +16 -2
- package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +401 -0
- package/templates/bills-agent/packages/agent-core/src/state-machine.ts +3 -1
- package/templates/bills-agent/packages/agent-core/src/stubs/charge-rail.ts +2 -0
- package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +19 -2
- package/templates/bills-agent/packages/agent-core/src/types.ts +28 -0
- package/templates/bills-agent/packages/agent-core/test/approval.test.ts +36 -2
- package/templates/bills-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/bills-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/bills-agent/packages/agent-core/test/engine.test.ts +73 -1
- package/templates/bills-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/bills-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/bills-agent/packages/agent-core/test/helpers.ts +1 -1
- package/templates/bills-agent/packages/agent-core/test/manifest.test.ts +2 -2
- package/templates/bills-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +51 -0
- package/templates/bills-agent/packages/agent-runtime/README.md +68 -0
- package/templates/bills-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/bills-agent/packages/agent-runtime/package.json +23 -0
- package/templates/bills-agent/{agents/bills-agent → packages/agent-runtime}/src/adversarial.ts +36 -44
- package/templates/bills-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +124 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
- package/templates/bills-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +245 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
- package/templates/bills-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/bills-agent/packages/agent-runtime/src/index.ts +12 -0
- package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +797 -0
- package/templates/bills-agent/packages/agent-runtime/src/kit.ts +206 -0
- package/templates/{collections-agent/agents/collections-agent → bills-agent/packages/agent-runtime}/src/poll.ts +22 -11
- package/templates/{collections-agent/agents/collections-agent → bills-agent/packages/agent-runtime}/src/scenarios.ts +50 -34
- package/templates/bills-agent/{agents/bills-agent → packages/agent-runtime}/src/setup.ts +88 -66
- package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +154 -0
- package/templates/{collections-agent/agents/collections-agent/channels/webhook/index.ts → bills-agent/packages/agent-runtime/src/webhook.ts} +1 -1
- package/templates/bills-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
- package/templates/bills-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/bills-agent/vitest.config.ts +1 -1
- package/templates/collections-agent/README.md +8 -5
- package/templates/collections-agent/agents/collections-agent/.env.example +14 -0
- package/templates/collections-agent/agents/collections-agent/AGENTS.md +6 -4
- package/templates/collections-agent/agents/collections-agent/CLAUDE.md +6 -4
- package/templates/collections-agent/agents/collections-agent/README.md +184 -16
- package/templates/collections-agent/agents/collections-agent/agent.yaml +3 -3
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +111 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042.json +11 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1103.json +11 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +23 -0
- package/templates/collections-agent/agents/collections-agent/package.json +12 -10
- package/templates/collections-agent/agents/collections-agent/runbook.md +52 -1
- package/templates/collections-agent/agents/collections-agent/src/kit.ts +265 -0
- package/templates/collections-agent/agents/collections-agent/test/adversarial.test.ts +9 -8
- package/templates/collections-agent/agents/collections-agent/test/check.test.ts +43 -1
- package/templates/collections-agent/agents/collections-agent/test/cli.test.ts +56 -18
- package/templates/collections-agent/agents/collections-agent/test/fixtures/agreed-1042-awaiting-payer.transcript.jsonl +4 -0
- package/templates/collections-agent/agents/collections-agent/test/scenarios.test.ts +11 -10
- package/templates/collections-agent/agents/collections-agent/test/webhook.test.ts +4 -7
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +271 -0
- package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +213 -0
- package/templates/collections-agent/agents/collections-agent/tsconfig.json +0 -1
- package/templates/collections-agent/package.json +3 -1
- package/templates/collections-agent/packages/agent-core/README.md +2 -0
- package/templates/collections-agent/packages/agent-core/package.json +1 -1
- package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +27 -6
- package/templates/collections-agent/packages/agent-core/src/api/rail.ts +16 -0
- package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +20 -7
- package/templates/collections-agent/packages/agent-core/src/approval.ts +16 -2
- package/templates/collections-agent/packages/agent-core/src/bundle.ts +50 -4
- package/templates/collections-agent/packages/agent-core/src/channels.ts +140 -0
- package/templates/collections-agent/packages/agent-core/src/check.ts +90 -8
- package/templates/collections-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/collections-agent/packages/agent-core/src/engine.ts +119 -18
- package/templates/collections-agent/packages/agent-core/src/hash.ts +13 -0
- package/templates/collections-agent/packages/agent-core/src/index.ts +3 -0
- package/templates/collections-agent/packages/agent-core/src/manifest.ts +1 -1
- package/templates/collections-agent/packages/agent-core/src/rail.ts +16 -2
- package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +401 -0
- package/templates/collections-agent/packages/agent-core/src/state-machine.ts +3 -1
- package/templates/collections-agent/packages/agent-core/src/stubs/charge-rail.ts +2 -0
- package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +19 -2
- package/templates/collections-agent/packages/agent-core/src/types.ts +28 -0
- package/templates/collections-agent/packages/agent-core/test/approval.test.ts +36 -2
- package/templates/collections-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/collections-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/collections-agent/packages/agent-core/test/engine.test.ts +73 -1
- package/templates/collections-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/collections-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/collections-agent/packages/agent-core/test/helpers.ts +1 -1
- package/templates/collections-agent/packages/agent-core/test/manifest.test.ts +2 -2
- package/templates/collections-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +51 -0
- package/templates/collections-agent/packages/agent-runtime/README.md +68 -0
- package/templates/collections-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/collections-agent/packages/agent-runtime/package.json +23 -0
- package/templates/collections-agent/{agents/collections-agent → packages/agent-runtime}/src/adversarial.ts +29 -50
- package/templates/collections-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +124 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
- package/templates/collections-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +245 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
- package/templates/collections-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/collections-agent/packages/agent-runtime/src/index.ts +12 -0
- package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +797 -0
- package/templates/collections-agent/packages/agent-runtime/src/kit.ts +206 -0
- package/templates/collections-agent/packages/agent-runtime/src/poll.ts +105 -0
- package/templates/{bills-agent/agents/bills-agent → collections-agent/packages/agent-runtime}/src/scenarios.ts +98 -26
- package/templates/collections-agent/packages/agent-runtime/src/setup.ts +229 -0
- package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +154 -0
- package/templates/collections-agent/packages/agent-runtime/src/webhook.ts +131 -0
- package/templates/collections-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
- package/templates/collections-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/collections-agent/vitest.config.ts +1 -1
- package/templates/hello-agent/LICENSE +21 -0
- package/templates/hello-agent/README.md +23 -0
- package/templates/hello-agent/_gitignore +10 -0
- package/templates/hello-agent/agents/hello-agent/.env.example +5 -0
- package/templates/hello-agent/agents/hello-agent/AGENTS.md +15 -0
- package/templates/hello-agent/agents/hello-agent/CLAUDE.md +15 -0
- package/templates/hello-agent/agents/hello-agent/README.md +33 -0
- package/templates/hello-agent/agents/hello-agent/SYSTEM_PROMPT.md +19 -0
- package/templates/hello-agent/agents/hello-agent/agent.yaml +19 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/beneficiary-swap.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/exfiltration.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/false-authority.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/false-authority.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/fractioning.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/fractioning.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/model-total.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/model-total.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/prompt-injection.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/evals/adversarial/webhook-replay.json +6 -0
- package/templates/hello-agent/agents/hello-agent/evals/eval.yaml +7 -0
- package/templates/hello-agent/agents/hello-agent/guardrails.json +8 -0
- package/templates/hello-agent/agents/hello-agent/mandate.example.json +17 -0
- package/templates/hello-agent/agents/hello-agent/package.json +19 -0
- package/templates/hello-agent/agents/hello-agent/runbook.md +10 -0
- package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.json +6 -0
- package/templates/hello-agent/agents/hello-agent/scenarios/happy-path.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.json +6 -0
- package/templates/hello-agent/agents/hello-agent/scenarios/prompt-injection.transcript.jsonl +2 -0
- package/templates/hello-agent/agents/hello-agent/src/bills.ts +23 -0
- package/templates/hello-agent/agents/hello-agent/src/kit.ts +33 -0
- package/templates/hello-agent/agents/hello-agent/tools.json +11 -0
- package/templates/hello-agent/agents/hello-agent/tsconfig.json +9 -0
- package/templates/hello-agent/package.json +30 -0
- package/templates/hello-agent/packages/agent-core/README.md +21 -0
- package/templates/hello-agent/packages/agent-core/package.json +24 -0
- package/templates/hello-agent/packages/agent-core/src/agent.ts +125 -0
- package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +175 -0
- package/templates/hello-agent/packages/agent-core/src/api/client.ts +53 -0
- package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +58 -0
- package/templates/hello-agent/packages/agent-core/src/api/rail.ts +117 -0
- package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +58 -0
- package/templates/hello-agent/packages/agent-core/src/approval.ts +123 -0
- package/templates/hello-agent/packages/agent-core/src/bundle.ts +159 -0
- package/templates/hello-agent/packages/agent-core/src/channels.ts +140 -0
- package/templates/hello-agent/packages/agent-core/src/check.ts +253 -0
- package/templates/hello-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/hello-agent/packages/agent-core/src/engine.ts +885 -0
- package/templates/hello-agent/packages/agent-core/src/escalate.ts +85 -0
- package/templates/hello-agent/packages/agent-core/src/events.ts +40 -0
- package/templates/hello-agent/packages/agent-core/src/guardrails.ts +45 -0
- package/templates/hello-agent/packages/agent-core/src/hash.ts +62 -0
- package/templates/hello-agent/packages/agent-core/src/ids.ts +10 -0
- package/templates/hello-agent/packages/agent-core/src/index.ts +32 -0
- package/templates/hello-agent/packages/agent-core/src/mandate.ts +100 -0
- package/templates/hello-agent/packages/agent-core/src/manifest.ts +76 -0
- package/templates/hello-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
- package/templates/hello-agent/packages/agent-core/src/providers/replay.ts +63 -0
- package/templates/hello-agent/packages/agent-core/src/providers/types.ts +39 -0
- package/templates/hello-agent/packages/agent-core/src/rail.ts +101 -0
- package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +401 -0
- package/templates/hello-agent/packages/agent-core/src/revocation.ts +30 -0
- package/templates/hello-agent/packages/agent-core/src/secrets.ts +30 -0
- package/templates/hello-agent/packages/agent-core/src/state/store.ts +307 -0
- package/templates/hello-agent/packages/agent-core/src/state-machine.ts +148 -0
- package/templates/hello-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
- package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +57 -0
- package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +141 -0
- package/templates/hello-agent/packages/agent-core/src/tools.ts +55 -0
- package/templates/hello-agent/packages/agent-core/src/types.ts +134 -0
- package/templates/hello-agent/packages/agent-core/test/approval.test.ts +117 -0
- package/templates/hello-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/hello-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/hello-agent/packages/agent-core/test/engine.test.ts +596 -0
- package/templates/hello-agent/packages/agent-core/test/escalate.test.ts +42 -0
- package/templates/hello-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/hello-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/hello-agent/packages/agent-core/test/helpers.ts +139 -0
- package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +153 -0
- package/templates/hello-agent/packages/agent-core/test/manifest.test.ts +45 -0
- package/templates/hello-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +316 -0
- package/templates/hello-agent/packages/agent-core/test/secrets.test.ts +28 -0
- package/templates/hello-agent/packages/agent-core/test/state-machine.test.ts +67 -0
- package/templates/hello-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
- package/templates/hello-agent/packages/agent-core/tsconfig.json +10 -0
- package/templates/hello-agent/packages/agent-runtime/README.md +68 -0
- package/templates/hello-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/hello-agent/packages/agent-runtime/package.json +23 -0
- package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +169 -0
- package/templates/hello-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +124 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
- package/templates/hello-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +245 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
- package/templates/hello-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/hello-agent/packages/agent-runtime/src/index.ts +12 -0
- package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +797 -0
- package/templates/hello-agent/packages/agent-runtime/src/kit.ts +206 -0
- package/templates/hello-agent/packages/agent-runtime/src/poll.ts +105 -0
- package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +268 -0
- package/templates/hello-agent/packages/agent-runtime/src/setup.ts +229 -0
- package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +154 -0
- package/templates/hello-agent/packages/agent-runtime/src/webhook.ts +131 -0
- package/templates/hello-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
- package/templates/hello-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/hello-agent/tsconfig.base.json +16 -0
- package/templates/hello-agent/vitest.config.ts +12 -0
- package/templates/kits.lock.json +60 -12
- package/templates/supplier-payments-agent/LICENSE +21 -0
- package/templates/supplier-payments-agent/README.md +23 -0
- package/templates/supplier-payments-agent/_gitignore +10 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/.env.example +7 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +21 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +21 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +118 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/SYSTEM_PROMPT.md +29 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/agent.yaml +26 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/batch-tampering.json +12 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/batch-tampering.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap-mandate.json +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap.json +10 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.json +13 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.json +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/false-authority.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/fractioning.json +15 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/fractioning.transcript.jsonl +6 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/model-total.json +10 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/model-total.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.json +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/prompt-injection.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/adversarial/webhook-replay.json +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/evals/eval.yaml +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/guardrails.json +15 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/mandate.example.json +70 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/package.json +26 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/runbook.md +19 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.json +14 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.json +12 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/cap-exceeded.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.json +20 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/escalated-above-threshold.transcript.jsonl +7 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.json +19 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/happy-path.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.json +21 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/mandate-revoked.transcript.jsonl +9 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.json +21 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/partial-batch-failure.transcript.jsonl +3 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.json +12 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/scenarios/prompt-injection.transcript.jsonl +2 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +176 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +345 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/pix-out.ts +118 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/payables.ts +76 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/adversarial.test.ts +53 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +384 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/check.test.ts +109 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/scenarios.test.ts +81 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/tools.json +53 -0
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/tsconfig.json +9 -0
- package/templates/supplier-payments-agent/package.json +36 -0
- package/templates/supplier-payments-agent/packages/agent-core/README.md +21 -0
- package/templates/supplier-payments-agent/packages/agent-core/package.json +24 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +125 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +175 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +53 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +58 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +117 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +58 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +123 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +159 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +140 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +253 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +885 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/escalate.ts +85 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/events.ts +40 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/guardrails.ts +45 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +62 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/ids.ts +10 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +32 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/mandate.ts +100 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/manifest.ts +76 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/providers/replay.ts +63 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/providers/types.ts +39 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +101 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +401 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +30 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/secrets.ts +30 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/state/store.ts +307 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +148 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +57 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +141 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +55 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +134 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/approval.test.ts +117 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/engine.test.ts +596 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/escalate.test.ts +42 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +139 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +153 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/manifest.test.ts +45 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +316 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/secrets.test.ts +28 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/state-machine.test.ts +67 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
- package/templates/supplier-payments-agent/packages/agent-core/tsconfig.json +10 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/README.md +68 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/package.json +23 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +169 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +124 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +358 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +206 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +225 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +143 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +113 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +113 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +317 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +144 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +43 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +131 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +245 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +129 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +39 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +12 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +797 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +206 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/poll.ts +105 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +268 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +229 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +154 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/webhook.ts +131 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +127 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +232 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +204 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +135 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/supplier-payments-agent/tsconfig.base.json +16 -0
- package/templates/supplier-payments-agent/vitest.config.ts +12 -0
- package/templates/bills-agent/agents/bills-agent/channels/terminal/index.ts +0 -108
- package/templates/bills-agent/agents/bills-agent/src/commands/approve.ts +0 -9
- package/templates/bills-agent/agents/bills-agent/src/commands/check.ts +0 -13
- package/templates/bills-agent/agents/bills-agent/src/commands/consent.ts +0 -26
- package/templates/bills-agent/agents/bills-agent/src/commands/decide.ts +0 -45
- package/templates/bills-agent/agents/bills-agent/src/commands/deny.ts +0 -7
- package/templates/bills-agent/agents/bills-agent/src/commands/eval.ts +0 -37
- package/templates/bills-agent/agents/bills-agent/src/commands/reconcile.ts +0 -38
- package/templates/bills-agent/agents/bills-agent/src/commands/rerun.ts +0 -65
- package/templates/bills-agent/agents/bills-agent/src/commands/resume.ts +0 -30
- package/templates/bills-agent/agents/bills-agent/src/main.ts +0 -185
- package/templates/collections-agent/agents/collections-agent/channels/terminal/index.ts +0 -178
- package/templates/collections-agent/agents/collections-agent/src/commands/approve.ts +0 -10
- package/templates/collections-agent/agents/collections-agent/src/commands/check.ts +0 -13
- package/templates/collections-agent/agents/collections-agent/src/commands/decide.ts +0 -48
- package/templates/collections-agent/agents/collections-agent/src/commands/deny.ts +0 -7
- package/templates/collections-agent/agents/collections-agent/src/commands/eval.ts +0 -39
- package/templates/collections-agent/agents/collections-agent/src/commands/poll.ts +0 -35
- package/templates/collections-agent/agents/collections-agent/src/commands/reconcile.ts +0 -38
- package/templates/collections-agent/agents/collections-agent/src/commands/rerun.ts +0 -55
- package/templates/collections-agent/agents/collections-agent/src/commands/resume.ts +0 -30
- package/templates/collections-agent/agents/collections-agent/src/commands/webhook.ts +0 -36
- package/templates/collections-agent/agents/collections-agent/src/main.ts +0 -193
- package/templates/collections-agent/agents/collections-agent/src/setup.ts +0 -250
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `receipt-verification`: the half of a receipt a party OUTSIDE CodeSpar can
|
|
3
|
+
* check.
|
|
4
|
+
*
|
|
5
|
+
* A receipt carries two signatures and they prove different things to
|
|
6
|
+
* different people.
|
|
7
|
+
*
|
|
8
|
+
* `receipt_sig` HMAC over the chain, under the consumer's secret.
|
|
9
|
+
* Verifying it means holding the key that also MINTS
|
|
10
|
+
* it, so it proves the payment to whoever runs the
|
|
11
|
+
* agent and to nobody else.
|
|
12
|
+
* `receipt_sig_ed25519` Ed25519 over `codespar-receipt:v1:<id>:<chain>`,
|
|
13
|
+
* made with CodeSpar's platform issuer key, whose
|
|
14
|
+
* PUBLIC half is served with no credential at
|
|
15
|
+
* `/.well-known/codespar-receipt-keys.json`. That one
|
|
16
|
+
* proves the payment to an accountant, a marketplace
|
|
17
|
+
* reconciling a payout, or a court — parties that
|
|
18
|
+
* have no relationship with CodeSpar at all.
|
|
19
|
+
*
|
|
20
|
+
* This module checks the second. It imports `node:crypto` and nothing else: no
|
|
21
|
+
* CodeSpar SDK, no key material, no token, no network of its own (the key
|
|
22
|
+
* document is passed in, or fetched by a function the caller supplies). A
|
|
23
|
+
* third party can read the forty lines that matter and reimplement them.
|
|
24
|
+
*
|
|
25
|
+
* WHAT A `verified` VERDICT PROVES, exactly: CodeSpar sealed a receipt with
|
|
26
|
+
* THIS id and THIS chain. The chain is a SHA-256 over the four links of the
|
|
27
|
+
* Control Record, so the seller, the amount and the payee are inside it — but
|
|
28
|
+
* recomputing that digest from the receipt body needs the canonical link
|
|
29
|
+
* shapes, which CodeSpar does not publish today. Binding the body to the chain
|
|
30
|
+
* is therefore not something this module can promise, and it does not pretend
|
|
31
|
+
* to: see `docs/OPEN_QUESTIONS.md` §47. It is also why the signature survives
|
|
32
|
+
* the proof bundle's masking — the bundle masks the payee, and the signature
|
|
33
|
+
* covers the id and the digest, not the copy in front of you.
|
|
34
|
+
*
|
|
35
|
+
* WHICH KEY SET. `kid` is `<did>#<n>` and the DID is the platform's, so it is
|
|
36
|
+
* the same string on every deployment while the KEY behind it is not: a
|
|
37
|
+
* sandbox receipt checked against the production key set finds a key of that
|
|
38
|
+
* name, fails to verify, and reads `tampered`. The verifier therefore points
|
|
39
|
+
* at ONE deployment's `/.well-known/codespar-receipt-keys.json` — the default
|
|
40
|
+
* is production, and `--url` names another. Nothing in the two documents tells
|
|
41
|
+
* them apart today; `docs/OPEN_QUESTIONS.md` §47 says so and names the ask.
|
|
42
|
+
*
|
|
43
|
+
* The signed string is the enterprise's `receiptSigningString`
|
|
44
|
+
* (`packages/api/src/receipt-signature.ts`, ent#1633), reproduced here rather
|
|
45
|
+
* than imported, because a verifier that has to install the signer's code
|
|
46
|
+
* proves nothing about the signer.
|
|
47
|
+
*/
|
|
48
|
+
import { createPublicKey, verify as verifyDetached } from "node:crypto";
|
|
49
|
+
|
|
50
|
+
/** The domain tag. A receipt names the version it was sealed under through
|
|
51
|
+
* this literal, so a future `v2` does not invalidate a `v1` signature. */
|
|
52
|
+
export const RECEIPT_SIGNATURE_DOMAIN = "codespar-receipt:v1";
|
|
53
|
+
|
|
54
|
+
/** The unauthenticated key set of the production API. `--url` points the
|
|
55
|
+
* verifier at another deployment (staging), `--keys` at a file. */
|
|
56
|
+
export const DEFAULT_RECEIPT_KEYS_URL = "https://api.codespar.dev/.well-known/codespar-receipt-keys.json";
|
|
57
|
+
|
|
58
|
+
/** The key set of a deployment, from its base URL. */
|
|
59
|
+
export function receiptKeysUrl(baseUrl: string): string {
|
|
60
|
+
return new URL("/.well-known/codespar-receipt-keys.json", baseUrl).toString();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The exact bytes the Ed25519 signature covers. Every segment is fixed-shape:
|
|
64
|
+
* the domain is a literal, the id is `rcpt_` + 22 url-safe characters, and the
|
|
65
|
+
* chain is 64 hex. No free text reaches this string. */
|
|
66
|
+
export function receiptSigningString(receiptId: string, chain: string): string {
|
|
67
|
+
return `${RECEIPT_SIGNATURE_DOMAIN}:${receiptId}:${chain}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The six honest answers. Five of them are not "invalid", and keeping them
|
|
72
|
+
* apart is the point of the module:
|
|
73
|
+
*
|
|
74
|
+
* `verified` the signature is CodeSpar's, over this id and this chain.
|
|
75
|
+
* `tampered` a signature is there and it does not match. A refusal.
|
|
76
|
+
* `unsigned` no signature on this copy. NOT a failure: every receipt
|
|
77
|
+
* sealed before CodeSpar added Ed25519 has none and never
|
|
78
|
+
* will, because signing them today with today's key would
|
|
79
|
+
* attest to what the database says now, not to what happened.
|
|
80
|
+
* `unknown_key` the `kid` is not in the key set. Retired keys stay
|
|
81
|
+
* published precisely so old signatures keep verifying, so a
|
|
82
|
+
* missing kid means another deployment, or a revoked key.
|
|
83
|
+
* `unreachable` no usable key set. UNKNOWN — never "invalid".
|
|
84
|
+
* `malformed` the file is not a receipt this module can read. Distinct
|
|
85
|
+
* from `unsigned` on purpose: answering "unsigned" for an
|
|
86
|
+
* arbitrary JSON file would state that it is a receipt from
|
|
87
|
+
* before the capability existed, which is a claim about a
|
|
88
|
+
* file nobody checked.
|
|
89
|
+
*/
|
|
90
|
+
export type ReceiptVerdict = "verified" | "tampered" | "unsigned" | "unknown_key" | "unreachable" | "malformed";
|
|
91
|
+
|
|
92
|
+
export interface ReceiptVerification {
|
|
93
|
+
verdict: ReceiptVerdict;
|
|
94
|
+
/** The receipt this is about, when the document names one. */
|
|
95
|
+
receipt_id: string | null;
|
|
96
|
+
/** The key the receipt names, when it names one. */
|
|
97
|
+
kid: string | null;
|
|
98
|
+
/** `active` or `retired`, as the key set publishes it. A retired key signs
|
|
99
|
+
* nothing new and verifies everything it already signed. */
|
|
100
|
+
key_status: string | null;
|
|
101
|
+
/** The bytes that were verified, so a reader can reproduce the check by hand. */
|
|
102
|
+
signing_string: string | null;
|
|
103
|
+
/** Where the keys came from, or `null` when no key was needed. */
|
|
104
|
+
keys_from: string | null;
|
|
105
|
+
/** A stable machine code under the verdict. */
|
|
106
|
+
reason: string;
|
|
107
|
+
/** One sentence for a person. Never a stack trace. */
|
|
108
|
+
message: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** One published key, RFC 8037 shape, as the JWKS serves it. */
|
|
112
|
+
export interface PublishedReceiptKey {
|
|
113
|
+
kid: string;
|
|
114
|
+
kty: string;
|
|
115
|
+
crv: string;
|
|
116
|
+
/** base64url of the raw 32-byte public key. */
|
|
117
|
+
x: string;
|
|
118
|
+
alg?: string;
|
|
119
|
+
use?: string;
|
|
120
|
+
status?: string;
|
|
121
|
+
created_at?: string;
|
|
122
|
+
retired_at?: string | null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface ReceiptKeyDocument {
|
|
126
|
+
issuer?: string;
|
|
127
|
+
algorithm?: string;
|
|
128
|
+
signing_string?: string;
|
|
129
|
+
keys: PublishedReceiptKey[];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The receipt fields this module reads. Everything else in the JSON is none
|
|
133
|
+
* of its business — including the HMAC seal, which needs a secret it must
|
|
134
|
+
* never be given. */
|
|
135
|
+
export interface SignedReceiptFields {
|
|
136
|
+
receipt_id: string;
|
|
137
|
+
chain: string | null;
|
|
138
|
+
receipt_sig_ed25519: string | null;
|
|
139
|
+
receipt_sig_kid: string | null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** The caller's fetcher. Injected so the module has no network of its own and
|
|
143
|
+
* a unit test needs none. */
|
|
144
|
+
export type KeyDocumentFetcher = (url: string) => Promise<unknown>;
|
|
145
|
+
|
|
146
|
+
export interface KeySource {
|
|
147
|
+
/** A key set already in hand — a file, a fixture, a document fetched once
|
|
148
|
+
* and reused across a folder of receipts. */
|
|
149
|
+
document?: unknown;
|
|
150
|
+
/** Where to fetch it from, with the function that does the fetching. */
|
|
151
|
+
url?: string;
|
|
152
|
+
fetch?: KeyDocumentFetcher;
|
|
153
|
+
/** What to call the origin in the report. Defaults to the url, or `file`. */
|
|
154
|
+
label?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* ── Reading the documents ───────────────────────────────────── */
|
|
158
|
+
|
|
159
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
160
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function stringOrNull(value: unknown): string | null {
|
|
164
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** base64url, strictly: Node's decoder accepts standard base64 and skips
|
|
168
|
+
* characters it does not know, so a round trip is what proves the string was
|
|
169
|
+
* the encoding it claims to be. */
|
|
170
|
+
function decodeBase64Url(value: string, expectedBytes: number): Buffer | undefined {
|
|
171
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value)) return undefined;
|
|
172
|
+
const buf = Buffer.from(value, "base64url");
|
|
173
|
+
if (buf.length !== expectedBytes || buf.toString("base64url") !== value) return undefined;
|
|
174
|
+
return buf;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export type ReceiptRead = { ok: true; fields: SignedReceiptFields } | { ok: false; reason: string; message: string; receipt_id: string | null };
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Read the four fields out of a receipt JSON, or say why it cannot be read.
|
|
181
|
+
*
|
|
182
|
+
* A signature without its `kid`, or a `kid` without its signature, is a
|
|
183
|
+
* MALFORMED document and not an unsigned one. The API writes both columns or
|
|
184
|
+
* neither, so half a pair means the file was edited — and "unsigned" is the
|
|
185
|
+
* benign verdict, which is exactly the one an edit must not be able to reach
|
|
186
|
+
* by deletion.
|
|
187
|
+
*/
|
|
188
|
+
export function readSignedReceipt(input: unknown): ReceiptRead {
|
|
189
|
+
if (!isRecord(input)) {
|
|
190
|
+
return { ok: false, reason: "not_an_object", receipt_id: null, message: "this file is not a JSON object, so it is not a receipt" };
|
|
191
|
+
}
|
|
192
|
+
const receiptId = stringOrNull(input["receipt_id"]);
|
|
193
|
+
if (receiptId === null) {
|
|
194
|
+
return { ok: false, reason: "receipt_id_missing", receipt_id: null, message: "this JSON carries no `receipt_id`, so it is not a CodeSpar receipt" };
|
|
195
|
+
}
|
|
196
|
+
const sigRaw = input["receipt_sig_ed25519"];
|
|
197
|
+
const kidRaw = input["receipt_sig_kid"];
|
|
198
|
+
const sig = stringOrNull(sigRaw);
|
|
199
|
+
const kid = stringOrNull(kidRaw);
|
|
200
|
+
const sigAbsent = sigRaw === undefined || sigRaw === null;
|
|
201
|
+
const kidAbsent = kidRaw === undefined || kidRaw === null;
|
|
202
|
+
if (!sigAbsent && sig === null) {
|
|
203
|
+
return { ok: false, reason: "signature_not_a_string", receipt_id: receiptId, message: `receipt ${receiptId} carries a \`receipt_sig_ed25519\` that is not a string` };
|
|
204
|
+
}
|
|
205
|
+
if (!kidAbsent && kid === null) {
|
|
206
|
+
return { ok: false, reason: "kid_not_a_string", receipt_id: receiptId, message: `receipt ${receiptId} carries a \`receipt_sig_kid\` that is not a string` };
|
|
207
|
+
}
|
|
208
|
+
if (sig !== null && kid === null) {
|
|
209
|
+
return { ok: false, reason: "kid_missing", receipt_id: receiptId, message: `receipt ${receiptId} carries a signature and no \`receipt_sig_kid\`, so there is no way to know which key to check it against; a receipt sealed before Ed25519 carries neither` };
|
|
210
|
+
}
|
|
211
|
+
if (kid !== null && sig === null) {
|
|
212
|
+
return { ok: false, reason: "signature_missing", receipt_id: receiptId, message: `receipt ${receiptId} names the key \`${kid}\` and carries no \`receipt_sig_ed25519\` to check against it` };
|
|
213
|
+
}
|
|
214
|
+
const chain = stringOrNull(input["chain"]);
|
|
215
|
+
if (sig !== null && chain === null) {
|
|
216
|
+
return { ok: false, reason: "chain_missing", receipt_id: receiptId, message: `receipt ${receiptId} carries a signature and no \`chain\`, so the signed string cannot be rebuilt` };
|
|
217
|
+
}
|
|
218
|
+
return { ok: true, fields: { receipt_id: receiptId, chain, receipt_sig_ed25519: sig, receipt_sig_kid: kid } };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export type KeyDocumentRead = { ok: true; document: ReceiptKeyDocument } | { ok: false; reason: string; message: string };
|
|
222
|
+
|
|
223
|
+
/** Read a JWKS, or say why it is not one. Anything unusable is `unreachable`
|
|
224
|
+
* territory: a key set we cannot read leaves us knowing nothing about the
|
|
225
|
+
* signature, which is not the same as knowing it is bad. */
|
|
226
|
+
export function readKeyDocument(input: unknown): KeyDocumentRead {
|
|
227
|
+
if (!isRecord(input)) return { ok: false, reason: "key_document_unreadable", message: "the key set is not a JSON object" };
|
|
228
|
+
const keys = input["keys"];
|
|
229
|
+
if (!Array.isArray(keys)) return { ok: false, reason: "key_document_unreadable", message: "the key set carries no `keys` array" };
|
|
230
|
+
const parsed: PublishedReceiptKey[] = [];
|
|
231
|
+
for (const entry of keys) {
|
|
232
|
+
if (!isRecord(entry)) continue;
|
|
233
|
+
const kid = stringOrNull(entry["kid"]);
|
|
234
|
+
const x = stringOrNull(entry["x"]);
|
|
235
|
+
const kty = stringOrNull(entry["kty"]);
|
|
236
|
+
const crv = stringOrNull(entry["crv"]);
|
|
237
|
+
if (kid === null || x === null || kty === null || crv === null) continue;
|
|
238
|
+
parsed.push({
|
|
239
|
+
kid,
|
|
240
|
+
kty,
|
|
241
|
+
crv,
|
|
242
|
+
x,
|
|
243
|
+
...(stringOrNull(entry["alg"]) !== null ? { alg: entry["alg"] as string } : {}),
|
|
244
|
+
...(stringOrNull(entry["status"]) !== null ? { status: entry["status"] as string } : {}),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const document: ReceiptKeyDocument = {
|
|
248
|
+
keys: parsed,
|
|
249
|
+
...(stringOrNull(input["issuer"]) !== null ? { issuer: input["issuer"] as string } : {}),
|
|
250
|
+
...(stringOrNull(input["signing_string"]) !== null ? { signing_string: input["signing_string"] as string } : {}),
|
|
251
|
+
};
|
|
252
|
+
return { ok: true, document };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* ── The check ───────────────────────────────────────────────── */
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Verify a receipt against a key set already in hand. Synchronous, offline,
|
|
259
|
+
* and the whole of the cryptography: everything else in this file is reading
|
|
260
|
+
* JSON carefully.
|
|
261
|
+
*/
|
|
262
|
+
export function verifyReceiptWithKeys(receipt: unknown, keyDocument: unknown, label = "file"): ReceiptVerification {
|
|
263
|
+
const read = readSignedReceipt(receipt);
|
|
264
|
+
if (!read.ok) {
|
|
265
|
+
return { verdict: "malformed", receipt_id: read.receipt_id, kid: null, key_status: null, signing_string: null, keys_from: null, reason: read.reason, message: read.message };
|
|
266
|
+
}
|
|
267
|
+
const { receipt_id: receiptId, chain, receipt_sig_ed25519: signature, receipt_sig_kid: kid } = read.fields;
|
|
268
|
+
|
|
269
|
+
if (signature === null || kid === null || chain === null) {
|
|
270
|
+
return {
|
|
271
|
+
verdict: "unsigned",
|
|
272
|
+
receipt_id: receiptId,
|
|
273
|
+
kid: null,
|
|
274
|
+
key_status: null,
|
|
275
|
+
signing_string: null,
|
|
276
|
+
keys_from: null,
|
|
277
|
+
reason: "no_ed25519_signature",
|
|
278
|
+
message: `receipt ${receiptId} carries no Ed25519 signature. Receipts sealed before CodeSpar added one carry none and never will; its HMAC seal is unaffected and still proves the payment to whoever runs the agent`,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const signingString = receiptSigningString(receiptId, chain);
|
|
283
|
+
const doc = readKeyDocument(keyDocument);
|
|
284
|
+
if (!doc.ok) {
|
|
285
|
+
return { verdict: "unreachable", receipt_id: receiptId, kid, key_status: null, signing_string: signingString, keys_from: label, reason: doc.reason, message: `${doc.message}, so nothing is proved and nothing is disproved about receipt ${receiptId}` };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const key = doc.document.keys.find((k) => k.kid === kid);
|
|
289
|
+
if (!key) {
|
|
290
|
+
return {
|
|
291
|
+
verdict: "unknown_key",
|
|
292
|
+
receipt_id: receiptId,
|
|
293
|
+
kid,
|
|
294
|
+
key_status: null,
|
|
295
|
+
signing_string: signingString,
|
|
296
|
+
keys_from: label,
|
|
297
|
+
reason: "kid_not_published",
|
|
298
|
+
message: `the key \`${kid}\` is not among the ${doc.document.keys.length} published at ${label}. CodeSpar keeps retired keys published so old signatures go on verifying, so a missing kid is a key from another deployment, or one that was revoked`,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const keyStatus = key.status ?? null;
|
|
302
|
+
|
|
303
|
+
if (key.kty !== "OKP" || key.crv !== "Ed25519") {
|
|
304
|
+
return { verdict: "unreachable", receipt_id: receiptId, kid, key_status: keyStatus, signing_string: signingString, keys_from: label, reason: "key_unusable", message: `the published key \`${kid}\` is ${key.kty}/${key.crv} and not OKP/Ed25519, so this module cannot check the signature with it` };
|
|
305
|
+
}
|
|
306
|
+
const publicKey = decodeBase64Url(key.x, 32);
|
|
307
|
+
if (!publicKey) {
|
|
308
|
+
return { verdict: "unreachable", receipt_id: receiptId, kid, key_status: keyStatus, signing_string: signingString, keys_from: label, reason: "key_unusable", message: `the published key \`${kid}\` does not carry a 32-byte base64url public key, so this module cannot check the signature with it` };
|
|
309
|
+
}
|
|
310
|
+
const signatureBytes = decodeBase64Url(signature, 64);
|
|
311
|
+
if (!signatureBytes) {
|
|
312
|
+
return { verdict: "tampered", receipt_id: receiptId, kid, key_status: keyStatus, signing_string: signingString, keys_from: label, reason: "signature_malformed", message: `receipt ${receiptId} carries a \`receipt_sig_ed25519\` that is not a 64-byte base64url Ed25519 signature, so it is not the one CodeSpar wrote` };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
let matches: boolean;
|
|
316
|
+
try {
|
|
317
|
+
matches = verifyDetached(null, Buffer.from(signingString, "utf8"), createPublicKey({ key: { kty: "OKP", crv: "Ed25519", x: key.x }, format: "jwk" }), signatureBytes);
|
|
318
|
+
} catch (err) {
|
|
319
|
+
return { verdict: "unreachable", receipt_id: receiptId, kid, key_status: keyStatus, signing_string: signingString, keys_from: label, reason: "key_unusable", message: `the published key \`${kid}\` could not be used: ${err instanceof Error ? err.message : String(err)}` };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!matches) {
|
|
323
|
+
return {
|
|
324
|
+
verdict: "tampered",
|
|
325
|
+
receipt_id: receiptId,
|
|
326
|
+
kid,
|
|
327
|
+
key_status: keyStatus,
|
|
328
|
+
signing_string: signingString,
|
|
329
|
+
keys_from: label,
|
|
330
|
+
reason: "signature_does_not_match",
|
|
331
|
+
message: `receipt ${receiptId} does not match its signature: the key \`${kid}\` did not sign this receipt id and this chain. The id or the chain was changed after the receipt was sealed, the signature was copied from another receipt, or this key set is not the one the deployment that sealed it publishes — a sandbox receipt checked against the production keys reads exactly like this, because every deployment publishes its own key under the same \`kid\``,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
verdict: "verified",
|
|
336
|
+
receipt_id: receiptId,
|
|
337
|
+
kid,
|
|
338
|
+
key_status: keyStatus,
|
|
339
|
+
signing_string: signingString,
|
|
340
|
+
keys_from: label,
|
|
341
|
+
reason: "signature_matches",
|
|
342
|
+
message: `receipt ${receiptId} was sealed by CodeSpar: the ${keyStatus ?? "published"} key \`${kid}\` signed this receipt id and this chain`,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Verify a receipt, fetching the key set when one is not in hand.
|
|
348
|
+
*
|
|
349
|
+
* The fetch is the caller's function, so this module opens no socket and a
|
|
350
|
+
* unit test needs no network. A fetch that fails is `unreachable` and never
|
|
351
|
+
* `tampered`: not knowing is not the same as knowing it is bad, and a
|
|
352
|
+
* verifier that conflated the two would call every receipt invalid the day
|
|
353
|
+
* its DNS broke.
|
|
354
|
+
*/
|
|
355
|
+
export async function verifyReceipt(receipt: unknown, source: KeySource = {}): Promise<ReceiptVerification> {
|
|
356
|
+
const read = readSignedReceipt(receipt);
|
|
357
|
+
if (!read.ok) {
|
|
358
|
+
return { verdict: "malformed", receipt_id: read.receipt_id, kid: null, key_status: null, signing_string: null, keys_from: null, reason: read.reason, message: read.message };
|
|
359
|
+
}
|
|
360
|
+
// An unsigned receipt needs no key, so it is answered before the network is
|
|
361
|
+
// touched: fetching a key set to check a signature that is not there would
|
|
362
|
+
// turn an offline answer into an outage.
|
|
363
|
+
if (read.fields.receipt_sig_ed25519 === null) return verifyReceiptWithKeys(receipt, { keys: [] }, source.label ?? "none");
|
|
364
|
+
|
|
365
|
+
if (source.document !== undefined) return verifyReceiptWithKeys(receipt, source.document, source.label ?? "file");
|
|
366
|
+
|
|
367
|
+
const url = source.url ?? DEFAULT_RECEIPT_KEYS_URL;
|
|
368
|
+
const label = source.label ?? url;
|
|
369
|
+
if (!source.fetch) {
|
|
370
|
+
return { verdict: "unreachable", receipt_id: read.fields.receipt_id, kid: read.fields.receipt_sig_kid, key_status: null, signing_string: null, keys_from: label, reason: "no_fetcher", message: "no key set was given and no way to fetch one: pass a document, or a fetch function" };
|
|
371
|
+
}
|
|
372
|
+
let fetched: unknown;
|
|
373
|
+
try {
|
|
374
|
+
fetched = await source.fetch(url);
|
|
375
|
+
} catch (err) {
|
|
376
|
+
return {
|
|
377
|
+
verdict: "unreachable",
|
|
378
|
+
receipt_id: read.fields.receipt_id,
|
|
379
|
+
kid: read.fields.receipt_sig_kid,
|
|
380
|
+
key_status: null,
|
|
381
|
+
signing_string: null,
|
|
382
|
+
keys_from: label,
|
|
383
|
+
reason: "fetch_failed",
|
|
384
|
+
message: `the published key set could not be read from ${label}: ${err instanceof Error ? err.message : String(err)}. Nothing is proved and nothing is disproved; the keys can also be saved to a file and passed in`,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return verifyReceiptWithKeys(receipt, fetched, label);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** The exit code of `codespar-agent verify`, one per verdict, so a script can
|
|
391
|
+
* branch on the answer instead of on "did it fail". `unsigned` is not 0: it is
|
|
392
|
+
* not a failure, and it is not a verification either. 2 is the usage error the
|
|
393
|
+
* other commands already use. */
|
|
394
|
+
export const VERDICT_EXIT_CODES: Record<ReceiptVerdict, number> = {
|
|
395
|
+
verified: 0,
|
|
396
|
+
tampered: 1,
|
|
397
|
+
unsigned: 3,
|
|
398
|
+
unknown_key: 4,
|
|
399
|
+
unreachable: 5,
|
|
400
|
+
malformed: 6,
|
|
401
|
+
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* does not compile; the runtime check below refuses the same pair when the
|
|
7
7
|
* types were bypassed (a cast, a row read back from SQLite).
|
|
8
8
|
*/
|
|
9
|
-
import type { Actor, ApprovalMode, EscalationTrigger, ExecutionItem, ExecutionReason, ItemOutcome, MandateRef } from "./types.js";
|
|
9
|
+
import type { Actor, ApprovalMode, EscalationTrigger, ExecutionBatch, ExecutionItem, ExecutionReason, ItemOutcome, MandateRef } from "./types.js";
|
|
10
10
|
|
|
11
11
|
export type ExecutionState =
|
|
12
12
|
| "drafted"
|
|
@@ -76,6 +76,8 @@ export interface Execution<S extends ExecutionState = ExecutionState> {
|
|
|
76
76
|
/** What the model claimed the total was, when it claimed one. Recorded, never used. */
|
|
77
77
|
model_claimed_total?: number;
|
|
78
78
|
items_hash: string;
|
|
79
|
+
/** Present when this execution is one line of a batch: the set it was presented inside, and where in it. */
|
|
80
|
+
batch?: ExecutionBatch;
|
|
79
81
|
mandate: MandateRef;
|
|
80
82
|
/** Written once, before the rail is called. Retrying reuses it. */
|
|
81
83
|
idempotency_key: string;
|
|
@@ -139,6 +139,8 @@ export class StubChargeRail implements PaymentRail {
|
|
|
139
139
|
payment: { amount_minor: req.amount_minor, payee: req.payee, attempt_id: req.attempt_id, money_moved: false, sandbox: true, at: raw.paid_at ?? sealed.at },
|
|
140
140
|
chain: null,
|
|
141
141
|
receipt_sig: null,
|
|
142
|
+
receipt_sig_ed25519: null,
|
|
143
|
+
receipt_sig_kid: null,
|
|
142
144
|
actor,
|
|
143
145
|
raw: { stub: true, charge_id: out.transaction_id, simulated: true, settled_against: "sandbox_fixture" },
|
|
144
146
|
};
|
|
@@ -17,6 +17,14 @@ export interface StubRailOptions {
|
|
|
17
17
|
refusePayees?: string[];
|
|
18
18
|
/** Attempt ids the stub answers `uncertain` for, once. */
|
|
19
19
|
uncertainOnce?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Payees whose first presentation answers `uncertain`, the way a timeout
|
|
22
|
+
* does: the provider still took the attempt, so a later lookup finds it.
|
|
23
|
+
* The payee twin of `refusePayees`, for scripting an unknown outcome in the
|
|
24
|
+
* MIDDLE of a multi-item execution, where the attempt ids are not known
|
|
25
|
+
* until the execution exists.
|
|
26
|
+
*/
|
|
27
|
+
uncertainPayees?: string[];
|
|
20
28
|
/** Test hook: called after the attempt is persisted and before the outcome is returned. */
|
|
21
29
|
afterDispatch?: (attemptId: string) => void;
|
|
22
30
|
/** Attempt ids whose first lookup answers `in_flight` and whose second lookup finds the provider finished (settled), with no pay() in between. */
|
|
@@ -40,6 +48,8 @@ export class StubRail implements PaymentRail {
|
|
|
40
48
|
private readonly inFlightSeen = new Set<string>();
|
|
41
49
|
/** Attempts that answered `uncertain` because the stub was armed: the "provider" did take them, and a later lookup finds them finished. */
|
|
42
50
|
private readonly lateAttempts = new Set<string>();
|
|
51
|
+
/** Attempts that already gave their one `uncertain`, so a re-presentation behaves normally. */
|
|
52
|
+
private readonly uncertainAnswered = new Set<string>();
|
|
43
53
|
/** How many times pay() reached the point of persisting a NEW attempt. */
|
|
44
54
|
payCount = 0;
|
|
45
55
|
|
|
@@ -52,8 +62,10 @@ export class StubRail implements PaymentRail {
|
|
|
52
62
|
const existing = this.store.stubRailGet(payment.attempt_id);
|
|
53
63
|
if (existing) return existing.outcome as RailOutcome;
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
65
|
+
const uncertainPayee = this.options.uncertainPayees?.includes(payment.payee) === true && !this.uncertainAnswered.has(payment.attempt_id);
|
|
66
|
+
if (this.uncertainArmed || this.uncertainPending.has(payment.attempt_id) || uncertainPayee) {
|
|
67
|
+
if (this.uncertainArmed || uncertainPayee) this.lateAttempts.add(payment.attempt_id);
|
|
68
|
+
this.uncertainAnswered.add(payment.attempt_id);
|
|
57
69
|
this.uncertainArmed = false;
|
|
58
70
|
this.uncertainPending.delete(payment.attempt_id);
|
|
59
71
|
return { status: "uncertain", code: "psp_dispatch_uncertain", message: "stub: outcome unknown on first presentation" };
|
|
@@ -117,6 +129,11 @@ export class StubRail implements PaymentRail {
|
|
|
117
129
|
...body,
|
|
118
130
|
chain,
|
|
119
131
|
receipt_sig: `stub:${sha256Hex(`sig:${chain}`).slice(0, 32)}`,
|
|
132
|
+
// The stub holds no CodeSpar key and must not look as though it did:
|
|
133
|
+
// `npm run verify` answers `unsigned` on a stub receipt, which is the
|
|
134
|
+
// truth about it.
|
|
135
|
+
receipt_sig_ed25519: null,
|
|
136
|
+
receipt_sig_kid: null,
|
|
120
137
|
actor,
|
|
121
138
|
raw: { stub: true, transaction_id: out.transaction_id },
|
|
122
139
|
};
|
|
@@ -26,6 +26,32 @@ export interface ExecutionItem {
|
|
|
26
26
|
due_date?: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* The SET a line belongs to, when the line is one execution of a batch.
|
|
31
|
+
*
|
|
32
|
+
* A batch of N lines is N executions, each attested by its own `items_hash`.
|
|
33
|
+
* That leaves the list itself unattested: a person who approved four lines
|
|
34
|
+
* while the agent ran three leaves a bundle that does not say a fourth
|
|
35
|
+
* existed. `batch_hash` binds the set — computed once over the ordered lines
|
|
36
|
+
* at the moment the list is presented for approval, and carried by every
|
|
37
|
+
* artifact of that batch together with the line's own position in it.
|
|
38
|
+
*
|
|
39
|
+
* Absent on an execution that is not part of a batch, and an artifact
|
|
40
|
+
* without it is the artifact of section 4.2 unchanged, byte for byte: the
|
|
41
|
+
* field is omitted rather than null, so the canonical payload it signs is
|
|
42
|
+
* the same payload it signed before this existed.
|
|
43
|
+
*/
|
|
44
|
+
export interface ExecutionBatch {
|
|
45
|
+
/** The batch's reference, as the kit names it (`folha-2026-10`). */
|
|
46
|
+
ref: string;
|
|
47
|
+
/** `batchHash` over the batch's ordered lines, as they were presented. */
|
|
48
|
+
batch_hash: string;
|
|
49
|
+
/** This line's position in that ordered list, counting from 0. */
|
|
50
|
+
index: number;
|
|
51
|
+
/** How many lines the presented list held. */
|
|
52
|
+
count: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
29
55
|
/** The trigger of section 4.4 that sent a `mandate` execution to a human. */
|
|
30
56
|
export type EscalationTrigger = "amount" | "new_beneficiary" | "outside_hours";
|
|
31
57
|
|
|
@@ -74,6 +100,8 @@ export interface ApprovalArtifact {
|
|
|
74
100
|
mandate: MandateRef;
|
|
75
101
|
items: ExecutionItem[];
|
|
76
102
|
items_hash: string;
|
|
103
|
+
/** Present when this execution is one line of a batch: what binds the SET this line was approved inside. */
|
|
104
|
+
batch?: ExecutionBatch;
|
|
77
105
|
/** Present when a section 4.4 trigger sent the execution to a human first. */
|
|
78
106
|
escalation?: { trigger: EscalationTrigger; detail: string };
|
|
79
107
|
actor: Actor;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { checkApprovalArtifact, createApprovalArtifact, hmacSigner } from "../src/approval.js";
|
|
3
|
-
import { itemsHash } from "../src/hash.js";
|
|
3
|
+
import { batchHash, itemsHash } from "../src/hash.js";
|
|
4
4
|
import type { Execution } from "../src/state-machine.js";
|
|
5
|
-
import type { Actor, ExecutionItem } from "../src/types.js";
|
|
5
|
+
import type { Actor, ExecutionBatch, ExecutionItem } from "../src/types.js";
|
|
6
6
|
|
|
7
7
|
const actor: Actor = { type: "human", id: "usr_1", channel: "terminal" };
|
|
8
8
|
const signer = hmacSigner("test", Buffer.alloc(32, 7));
|
|
@@ -30,6 +30,7 @@ function execution(items: ExecutionItem[]): Execution<"awaiting_approval"> {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const escola: ExecutionItem = { alias: "escola", beneficiary: "Escola", payee: "escola@exemplo.com.br", amount: 185000, currency: "BRL" };
|
|
33
|
+
const mercado: ExecutionItem = { alias: "mercado", beneficiary: "Mercado", payee: "+5511999990001", amount: 42000, currency: "BRL" };
|
|
33
34
|
|
|
34
35
|
describe("section 4.2: approval artifact", () => {
|
|
35
36
|
it("has the schema of the spec and an HMAC signature that verifies", () => {
|
|
@@ -69,6 +70,39 @@ describe("section 4.2: approval artifact", () => {
|
|
|
69
70
|
expect(checkApprovalArtifact(signer, artifact, { ...exe, mandate: { id: "mdt_1", version: 4 } }, now)).toEqual({ ok: false, problem: "wrong_mandate" });
|
|
70
71
|
});
|
|
71
72
|
|
|
73
|
+
it("carries the batch this line belongs to, and the whole of it is signed", () => {
|
|
74
|
+
const batch: ExecutionBatch = { ref: "folha-2026-10", batch_hash: batchHash([escola, mercado]), index: 0, count: 2 };
|
|
75
|
+
const exe = { ...execution([escola]), batch };
|
|
76
|
+
const artifact = createApprovalArtifact(signer, { execution: exe, approver: { type: "person", id: "usr_1", channel: "terminal" }, actor, now });
|
|
77
|
+
expect(artifact.batch).toEqual(batch);
|
|
78
|
+
expect(checkApprovalArtifact(signer, artifact, exe, now)).toEqual({ ok: true });
|
|
79
|
+
// Moving the line inside the list, re-pricing the list, or renaming it:
|
|
80
|
+
// each is a different set, and the signature covers all of it.
|
|
81
|
+
for (const changed of [{ ...batch, index: 1 }, { ...batch, count: 3 }, { ...batch, ref: "folha-2026-11" }, { ...batch, batch_hash: batchHash([escola]) }]) {
|
|
82
|
+
expect(checkApprovalArtifact(signer, { ...artifact, batch: changed }, exe, now)).toEqual({ ok: false, problem: "signature_invalid" });
|
|
83
|
+
expect(checkApprovalArtifact(signer, artifact, { ...exe, batch: changed }, now)).toEqual({ ok: false, problem: "batch_mismatch" });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("an artifact with no batch signs the payload it signed before batches existed", () => {
|
|
88
|
+
const exe = execution([escola]);
|
|
89
|
+
const artifact = createApprovalArtifact(signer, { execution: exe, approver: { type: "person", id: "usr_1", channel: "terminal" }, actor, now });
|
|
90
|
+
// Omitted, never null: a bills-agent run has no batch and must not grow a key.
|
|
91
|
+
expect("batch" in artifact).toBe(false);
|
|
92
|
+
expect(checkApprovalArtifact(signer, artifact, exe, now)).toEqual({ ok: true });
|
|
93
|
+
// An execution that gained a batch after the artifact was signed is not the one that was approved.
|
|
94
|
+
const joined = { ...exe, batch: { ref: "folha-2026-10", batch_hash: batchHash([escola]), index: 0, count: 1 } };
|
|
95
|
+
expect(checkApprovalArtifact(signer, artifact, joined, now)).toEqual({ ok: false, problem: "batch_mismatch" });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("the batch hash IS the items hash of the whole ordered list, so a reader can recompute it", () => {
|
|
99
|
+
expect(batchHash([escola, mercado])).toBe(itemsHash([escola, mercado]));
|
|
100
|
+
// Order is part of it: `index` names a position, and a position only means something in a fixed order.
|
|
101
|
+
expect(batchHash([mercado, escola])).not.toBe(batchHash([escola, mercado]));
|
|
102
|
+
// A line dropped is a different set, which is the whole point.
|
|
103
|
+
expect(batchHash([escola])).not.toBe(batchHash([escola, mercado]));
|
|
104
|
+
});
|
|
105
|
+
|
|
72
106
|
it("records the escalation trigger when there was one", () => {
|
|
73
107
|
const exe = execution([escola]);
|
|
74
108
|
const artifact = createApprovalArtifact(signer, {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 11: what the proof bundle has to carry for `npm run inspect` to
|
|
3
|
+
* answer "why could the agent pay this" from the folder alone — the rail's
|
|
4
|
+
* request and its answer per attempt, and a receipt path that travels with
|
|
5
|
+
* the bundle instead of naming the machine that wrote it.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { describe, expect, it } from "vitest";
|
|
10
|
+
import { maskPayee } from "../src/bundle.js";
|
|
11
|
+
import { harness } from "./helpers.js";
|
|
12
|
+
|
|
13
|
+
const approver = { id: "usr_demo", channel: "terminal" };
|
|
14
|
+
|
|
15
|
+
async function settledRun() {
|
|
16
|
+
const h = harness({ mode: "human" });
|
|
17
|
+
const draft = await h.engine.draft({ items: [{ payee: "escola", amount: 185000, description: "outubro" }] });
|
|
18
|
+
if (!draft.ok) throw new Error("refused");
|
|
19
|
+
const approved = h.engine.approve(draft.execution.id, approver);
|
|
20
|
+
const settled = await h.engine.execute(approved.id);
|
|
21
|
+
return { h, settled };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe("the proof bundle records the rail call and its answer", () => {
|
|
25
|
+
it("rail.dispatch names the idempotency key that makes a retry the same payment", async () => {
|
|
26
|
+
const { h, settled } = await settledRun();
|
|
27
|
+
const dispatch = h.bundle.readEvents().find((e) => e["type"] === "rail.dispatch");
|
|
28
|
+
expect(dispatch).toBeDefined();
|
|
29
|
+
const payload = dispatch!["payload"] as Record<string, unknown>;
|
|
30
|
+
expect(payload["idempotency_key"]).toBe(settled.idempotency_key);
|
|
31
|
+
expect(payload["attempt_id"]).toBe(settled.outcomes[0]?.attempt_id);
|
|
32
|
+
expect(payload["rail"]).toBe("stub");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("rail.outcome carries what the rail answered: transaction, receipt and whether money moved", async () => {
|
|
36
|
+
const { h, settled } = await settledRun();
|
|
37
|
+
const outcome = h.bundle.readEvents().find((e) => e["type"] === "rail.outcome");
|
|
38
|
+
const payload = outcome!["payload"] as Record<string, unknown>;
|
|
39
|
+
expect(payload["status"]).toBe("settled");
|
|
40
|
+
expect(payload["receipt_id"]).toBe(settled.outcomes[0]?.receipt_id);
|
|
41
|
+
expect(payload["transaction_id"]).toBe(settled.outcomes[0]?.transaction_id);
|
|
42
|
+
expect(payload["money_moved"]).toBe(false);
|
|
43
|
+
expect(payload["sandbox"]).toBe(true);
|
|
44
|
+
// Never the provider's echo: `raw` may carry payer data the bundle does not promise to mask.
|
|
45
|
+
expect(payload["raw"]).toBeUndefined();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("a refused attempt records the rail's own code and message", async () => {
|
|
49
|
+
const h = harness({ mode: "human", rail: { refusePayees: ["escola@exemplo.com.br"] } });
|
|
50
|
+
const draft = await h.engine.draft({ items: [{ payee: "escola", amount: 185000 }] });
|
|
51
|
+
if (!draft.ok) throw new Error("refused");
|
|
52
|
+
const approved = h.engine.approve(draft.execution.id, approver);
|
|
53
|
+
await h.engine.execute(approved.id);
|
|
54
|
+
const outcome = h.bundle.readEvents().find((e) => e["type"] === "rail.outcome");
|
|
55
|
+
const payload = outcome!["payload"] as Record<string, unknown>;
|
|
56
|
+
expect(payload["status"]).toBe("failed");
|
|
57
|
+
expect(payload["code"]).toBeTypeOf("string");
|
|
58
|
+
expect(payload["message"]).toBeTypeOf("string");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("receipt.saved names the file inside the bundle, not the machine that wrote it", async () => {
|
|
62
|
+
const { h } = await settledRun();
|
|
63
|
+
const saved = h.bundle.readEvents().find((e) => e["type"] === "receipt.saved");
|
|
64
|
+
const path = (saved!["payload"] as { path: string }).path;
|
|
65
|
+
expect(path).toMatch(/^receipts\//);
|
|
66
|
+
expect(path).not.toContain(h.bundle.dir);
|
|
67
|
+
const receipt = JSON.parse(readFileSync(join(h.bundle.dir, path), "utf8")) as { payment: { payee: string } };
|
|
68
|
+
expect(receipt.payment.payee).toBe(maskPayee("escola@exemplo.com.br"));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("the mandate snapshot carries the version the approval artifact names", async () => {
|
|
72
|
+
const { h, settled } = await settledRun();
|
|
73
|
+
h.bundle.mandateSnapshot(h.engine.mandate);
|
|
74
|
+
const snapshot = JSON.parse(readFileSync(join(h.bundle.dir, "mandate.snapshot.json"), "utf8")) as { version: number; signature?: string };
|
|
75
|
+
expect(snapshot.version).toBe(settled.mandate.version);
|
|
76
|
+
expect(snapshot.signature).toBeUndefined();
|
|
77
|
+
expect(h.bundle.readApprovals()[0]?.mandate.version).toBe(snapshot.version);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("maskPayee is idempotent, so a reader may apply it to a value the bundle already masked", () => {
|
|
81
|
+
for (const raw of ["escola@exemplo.com.br", "+5511999990001", "12345678909", "*"]) {
|
|
82
|
+
expect(maskPayee(maskPayee(raw))).toBe(maskPayee(raw));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|