@codespar/cli 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/commands/mandate-verify.js +98 -19
- package/dist/commands/mandate-verify.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mandate-codec.js +31 -20
- package/dist/mandate-codec.js.map +1 -1
- package/package.json +1 -1
- package/templates/bills-agent/README.md +1 -1
- package/templates/bills-agent/agents/bills-agent/AGENTS.md +1 -1
- package/templates/bills-agent/agents/bills-agent/CLAUDE.md +1 -1
- package/templates/bills-agent/agents/bills-agent/README.md +6 -5
- package/templates/bills-agent/agents/bills-agent/src/modules/embedded-consent.ts +5 -4
- package/templates/bills-agent/packages/agent-core/README.md +14 -1
- package/templates/bills-agent/packages/agent-core/package.json +1 -1
- package/templates/bills-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/bills-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/bills-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/bills-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/bills-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/bills-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/bills-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/bills-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/bills-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/bills-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/bills-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/bills-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/bills-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/bills-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/bills-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/bills-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/bills-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/bills-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/bills-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/bills-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/bills-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/bills-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/bills-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/bills-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/bills-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/bills-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/bills-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/bills-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/bills-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/bills-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/bills-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/bills-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/bills-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/bills-agent/packages/agent-runtime/README.md +12 -0
- package/templates/bills-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/bills-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/bills-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/bills-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/bills-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/bills-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/bills-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/bills-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/bills-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/bills-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/bills-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/bills-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/bills-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/bills-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/bills-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/bills-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/checkout-agent/LICENSE +21 -0
- package/templates/checkout-agent/README.md +28 -0
- package/templates/checkout-agent/_gitignore +10 -0
- package/templates/checkout-agent/agents/checkout-agent/.env.example +5 -0
- package/templates/checkout-agent/agents/checkout-agent/AGENTS.md +20 -0
- package/templates/checkout-agent/agents/checkout-agent/CLAUDE.md +20 -0
- package/templates/checkout-agent/agents/checkout-agent/README.md +94 -0
- package/templates/checkout-agent/agents/checkout-agent/SYSTEM_PROMPT.md +36 -0
- package/templates/checkout-agent/agents/checkout-agent/agent.yaml +30 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/README.md +28 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-beatriz.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina-retomada.json +10 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/pedido-marina.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/channels/whatsapp/templates.json +49 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap-human.json +12 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/beneficiary-swap.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.json +13 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/coupon-unknown.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.json +26 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/exfiltration.transcript.jsonl +3 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/false-authority.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.json +12 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/fractioning.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.json +13 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/model-total.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.json +15 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/payment-claimed.transcript.jsonl +7 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.json +12 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/price-injected.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.json +13 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/prompt-injection.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.json +16 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped-human.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.json +15 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/quantity-swapped.transcript.jsonl +8 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/adversarial/webhook-replay.json +11 -0
- package/templates/checkout-agent/agents/checkout-agent/evals/eval.yaml +11 -0
- package/templates/checkout-agent/agents/checkout-agent/guardrails.json +25 -0
- package/templates/checkout-agent/agents/checkout-agent/mandate.example.json +28 -0
- package/templates/checkout-agent/agents/checkout-agent/package.json +33 -0
- package/templates/checkout-agent/agents/checkout-agent/runbook.md +19 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.json +42 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/beneficiary-not-allowed.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.json +32 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cap-exceeded.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.json +61 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-recomposed.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.json +61 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/cart-replaced.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.json +36 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/charge-expired.transcript.jsonl +6 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.json +23 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/coupon-unknown.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.json +72 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/escalated-above-threshold.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.json +15 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/happy-path.transcript.jsonl +6 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.json +23 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/item-unavailable.transcript.jsonl +3 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.json +42 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/mandate-revoked.transcript.jsonl +12 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.json +14 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/nfse-failed.transcript.jsonl +9 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.json +34 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/payment-claimed.transcript.jsonl +7 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.json +33 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/price-injected.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.json +32 -0
- package/templates/checkout-agent/agents/checkout-agent/scenarios/prompt-injection.transcript.jsonl +4 -0
- package/templates/checkout-agent/agents/checkout-agent/src/catalog.ts +92 -0
- package/templates/checkout-agent/agents/checkout-agent/src/kit.ts +338 -0
- package/templates/checkout-agent/agents/checkout-agent/src/modules/bolepix-receivables.ts +179 -0
- package/templates/checkout-agent/agents/checkout-agent/src/modules/nfse-invoice.ts +374 -0
- package/templates/checkout-agent/agents/checkout-agent/src/modules/storefront-cart.ts +220 -0
- package/templates/checkout-agent/agents/checkout-agent/src/pricing.ts +297 -0
- package/templates/checkout-agent/agents/checkout-agent/test/adversarial.test.ts +117 -0
- package/templates/checkout-agent/agents/checkout-agent/test/check.test.ts +67 -0
- package/templates/checkout-agent/agents/checkout-agent/test/cli.test.ts +169 -0
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/order-marina.transcript.jsonl +5 -0
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/ordered-marina-awaiting-payer.transcript.jsonl +6 -0
- package/templates/checkout-agent/agents/checkout-agent/test/fixtures/tap-falar-agora-marina.transcript.jsonl +2 -0
- package/templates/checkout-agent/agents/checkout-agent/test/nfse.test.ts +170 -0
- package/templates/checkout-agent/agents/checkout-agent/test/pricing.test.ts +138 -0
- package/templates/checkout-agent/agents/checkout-agent/test/scenarios.test.ts +113 -0
- package/templates/checkout-agent/agents/checkout-agent/test/whatsapp.test.ts +171 -0
- package/templates/checkout-agent/agents/checkout-agent/tools.json +68 -0
- package/templates/checkout-agent/agents/checkout-agent/tsconfig.json +10 -0
- package/templates/checkout-agent/package.json +38 -0
- package/templates/checkout-agent/packages/agent-core/README.md +34 -0
- package/templates/checkout-agent/packages/agent-core/package.json +24 -0
- package/templates/checkout-agent/packages/agent-core/src/agent.ts +163 -0
- package/templates/checkout-agent/packages/agent-core/src/api/charge-rail.ts +195 -0
- package/templates/checkout-agent/packages/agent-core/src/api/client.ts +76 -0
- package/templates/checkout-agent/packages/agent-core/src/api/mandate-status.ts +74 -0
- package/templates/checkout-agent/packages/agent-core/src/api/rail.ts +153 -0
- package/templates/checkout-agent/packages/agent-core/src/api/sandbox-payer.ts +43 -0
- package/templates/checkout-agent/packages/agent-core/src/approval.ts +135 -0
- package/templates/checkout-agent/packages/agent-core/src/bundle.ts +191 -0
- package/templates/checkout-agent/packages/agent-core/src/channels.ts +186 -0
- package/templates/checkout-agent/packages/agent-core/src/check.ts +276 -0
- package/templates/checkout-agent/packages/agent-core/src/clock.ts +42 -0
- package/templates/checkout-agent/packages/agent-core/src/engine.ts +1074 -0
- package/templates/checkout-agent/packages/agent-core/src/escalate.ts +85 -0
- package/templates/checkout-agent/packages/agent-core/src/events.ts +40 -0
- package/templates/checkout-agent/packages/agent-core/src/guardrails.ts +45 -0
- package/templates/checkout-agent/packages/agent-core/src/hash.ts +118 -0
- package/templates/checkout-agent/packages/agent-core/src/ids.ts +10 -0
- package/templates/checkout-agent/packages/agent-core/src/index.ts +34 -0
- package/templates/checkout-agent/packages/agent-core/src/mandate.ts +100 -0
- package/templates/checkout-agent/packages/agent-core/src/manifest.ts +76 -0
- package/templates/checkout-agent/packages/agent-core/src/providers/anthropic.ts +76 -0
- package/templates/checkout-agent/packages/agent-core/src/providers/replay.ts +63 -0
- package/templates/checkout-agent/packages/agent-core/src/providers/types.ts +39 -0
- package/templates/checkout-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/checkout-agent/packages/agent-core/src/rail.ts +186 -0
- package/templates/checkout-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/checkout-agent/packages/agent-core/src/receipt-verification.ts +577 -0
- package/templates/checkout-agent/packages/agent-core/src/revocation.ts +30 -0
- package/templates/checkout-agent/packages/agent-core/src/secrets.ts +30 -0
- package/templates/checkout-agent/packages/agent-core/src/state/store.ts +307 -0
- package/templates/checkout-agent/packages/agent-core/src/state-machine.ts +157 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/charge-rail.ts +195 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/mandate-status.ts +58 -0
- package/templates/checkout-agent/packages/agent-core/src/stubs/rail.ts +209 -0
- package/templates/checkout-agent/packages/agent-core/src/tools.ts +66 -0
- package/templates/checkout-agent/packages/agent-core/src/types.ts +187 -0
- package/templates/checkout-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/checkout-agent/packages/agent-core/test/approval.test.ts +117 -0
- package/templates/checkout-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/checkout-agent/packages/agent-core/test/bundle.test.ts +85 -0
- package/templates/checkout-agent/packages/agent-core/test/clock.test.ts +25 -0
- package/templates/checkout-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/checkout-agent/packages/agent-core/test/engine.test.ts +596 -0
- package/templates/checkout-agent/packages/agent-core/test/escalate.test.ts +42 -0
- package/templates/checkout-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt-keys.json +18 -0
- package/templates/checkout-agent/packages/agent-core/test/fixtures/staging-receipt.json +24 -0
- package/templates/checkout-agent/packages/agent-core/test/helpers.ts +142 -0
- package/templates/checkout-agent/packages/agent-core/test/mandate-status.test.ts +229 -0
- package/templates/checkout-agent/packages/agent-core/test/manifest.test.ts +45 -0
- package/templates/checkout-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/checkout-agent/packages/agent-core/test/receipt-verification.test.ts +316 -0
- package/templates/checkout-agent/packages/agent-core/test/receivables.test.ts +416 -0
- package/templates/checkout-agent/packages/agent-core/test/secrets.test.ts +28 -0
- package/templates/checkout-agent/packages/agent-core/test/state-machine.test.ts +67 -0
- package/templates/checkout-agent/packages/agent-core/test/transitions.type-test.ts +59 -0
- package/templates/checkout-agent/packages/agent-core/tsconfig.json +10 -0
- package/templates/checkout-agent/packages/agent-runtime/README.md +80 -0
- package/templates/checkout-agent/packages/agent-runtime/bin.mjs +32 -0
- package/templates/checkout-agent/packages/agent-runtime/package.json +23 -0
- package/templates/checkout-agent/packages/agent-runtime/src/adversarial.ts +199 -0
- package/templates/checkout-agent/packages/agent-runtime/src/agent.ts +68 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/contact.ts +13 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/index.ts +72 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/rules.ts +120 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/types.ts +173 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +459 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +216 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/evidence.ts +109 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +350 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +180 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/present.ts +47 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +120 -0
- package/templates/checkout-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +127 -0
- package/templates/checkout-agent/packages/agent-runtime/src/cli.ts +98 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/check.ts +15 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/decide.ts +63 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/eval.ts +58 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/inspect.ts +100 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +367 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/poll.ts +145 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/reconcile.ts +42 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/rerun.ts +86 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/resume.ts +49 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +134 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/start.ts +248 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/verify.ts +330 -0
- package/templates/checkout-agent/packages/agent-runtime/src/commands/webhook.ts +40 -0
- package/templates/checkout-agent/packages/agent-runtime/src/default-kit.ts +83 -0
- package/templates/checkout-agent/packages/agent-runtime/src/index.ts +13 -0
- package/templates/checkout-agent/packages/agent-runtime/src/inspect.ts +801 -0
- package/templates/checkout-agent/packages/agent-runtime/src/kit.ts +229 -0
- package/templates/checkout-agent/packages/agent-runtime/src/poll.ts +105 -0
- package/templates/checkout-agent/packages/agent-runtime/src/scenarios.ts +274 -0
- package/templates/checkout-agent/packages/agent-runtime/src/setup.ts +239 -0
- package/templates/checkout-agent/packages/agent-runtime/src/terminal.ts +263 -0
- package/templates/checkout-agent/packages/agent-runtime/src/webhook.ts +131 -0
- package/templates/checkout-agent/packages/agent-runtime/test/inspect.test.ts +336 -0
- package/templates/checkout-agent/packages/agent-runtime/test/verify-cli.test.ts +329 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +277 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-evidence.test.ts +94 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-rules.test.ts +174 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-session.test.ts +167 -0
- package/templates/checkout-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/checkout-agent/packages/agent-runtime/tsconfig.json +9 -0
- package/templates/checkout-agent/scripts/whatsapp-emulator.mjs +84 -0
- package/templates/checkout-agent/tsconfig.base.json +16 -0
- package/templates/checkout-agent/vitest.config.ts +12 -0
- package/templates/collections-agent/README.md +6 -1
- package/templates/collections-agent/agents/collections-agent/README.md +12 -8
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/README.md +8 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/acordo-1042-retomada.json +10 -0
- package/templates/collections-agent/agents/collections-agent/channels/whatsapp/templates.json +27 -1
- package/templates/collections-agent/agents/collections-agent/src/kit.ts +2 -0
- package/templates/collections-agent/agents/collections-agent/src/modules/bolepix-receivables.ts +12 -2
- package/templates/collections-agent/agents/collections-agent/test/ambiguous-charge.test.ts +55 -0
- package/templates/collections-agent/agents/collections-agent/test/fixtures/tap-emitir-nova-1042.transcript.jsonl +2 -0
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-poll.test.ts +12 -6
- package/templates/collections-agent/agents/collections-agent/test/whatsapp-status-replies.test.ts +148 -0
- package/templates/collections-agent/agents/collections-agent/test/whatsapp.test.ts +5 -0
- package/templates/collections-agent/package.json +1 -0
- package/templates/collections-agent/packages/agent-core/README.md +14 -1
- package/templates/collections-agent/packages/agent-core/package.json +1 -1
- package/templates/collections-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/collections-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/collections-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/collections-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/collections-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/collections-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/collections-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/collections-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/collections-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/collections-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/collections-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/collections-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/collections-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/collections-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/collections-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/collections-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/collections-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/collections-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/collections-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/collections-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/collections-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/collections-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/collections-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/collections-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/collections-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/collections-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/collections-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/collections-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/collections-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/collections-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/collections-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/collections-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/collections-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/collections-agent/packages/agent-runtime/README.md +12 -0
- package/templates/collections-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/collections-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/collections-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/collections-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/collections-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/collections-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/collections-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/collections-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/collections-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/collections-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/collections-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/collections-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/collections-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/collections-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/collections-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/collections-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/collections-agent/scripts/whatsapp-emulator.mjs +84 -0
- package/templates/hello-agent/README.md +1 -1
- package/templates/hello-agent/packages/agent-core/README.md +14 -1
- package/templates/hello-agent/packages/agent-core/package.json +1 -1
- package/templates/hello-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/hello-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/hello-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/hello-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/hello-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/hello-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/hello-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/hello-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/hello-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/hello-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/hello-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/hello-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/hello-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/hello-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/hello-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/hello-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/hello-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/hello-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/hello-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/hello-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/hello-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/hello-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/hello-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/hello-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/hello-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/hello-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/hello-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/hello-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/hello-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/hello-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/hello-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/hello-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/hello-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/hello-agent/packages/agent-runtime/README.md +12 -0
- package/templates/hello-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/hello-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/hello-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/hello-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/hello-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/hello-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/hello-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/hello-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/hello-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/hello-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/hello-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/hello-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/hello-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/hello-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/hello-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/hello-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/kits.lock.json +29 -6
- package/templates/supplier-payments-agent/README.md +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/AGENTS.md +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/CLAUDE.md +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/README.md +11 -9
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/kit.ts +1 -1
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/src/modules/batch-payout.ts +67 -20
- package/templates/supplier-payments-agent/agents/supplier-payments-agent/test/batch.test.ts +504 -5
- package/templates/supplier-payments-agent/packages/agent-core/README.md +14 -1
- package/templates/supplier-payments-agent/packages/agent-core/package.json +1 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/agent.ts +39 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/api/charge-rail.ts +54 -34
- package/templates/supplier-payments-agent/packages/agent-core/src/api/client.ts +28 -5
- package/templates/supplier-payments-agent/packages/agent-core/src/api/mandate-status.ts +28 -12
- package/templates/supplier-payments-agent/packages/agent-core/src/api/rail.ts +62 -26
- package/templates/supplier-payments-agent/packages/agent-core/src/api/sandbox-payer.ts +3 -18
- package/templates/supplier-payments-agent/packages/agent-core/src/approval.ts +14 -2
- package/templates/supplier-payments-agent/packages/agent-core/src/bundle.ts +35 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/channels.ts +49 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/check.ts +25 -2
- package/templates/supplier-payments-agent/packages/agent-core/src/engine.ts +235 -46
- package/templates/supplier-payments-agent/packages/agent-core/src/hash.ts +57 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/index.ts +2 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/quote.ts +50 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/rail.ts +89 -4
- package/templates/supplier-payments-agent/packages/agent-core/src/receipt-chain.ts +270 -0
- package/templates/supplier-payments-agent/packages/agent-core/src/receipt-verification.ts +194 -18
- package/templates/supplier-payments-agent/packages/agent-core/src/revocation.ts +1 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/state-machine.ts +10 -1
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/mandate-status.ts +4 -3
- package/templates/supplier-payments-agent/packages/agent-core/src/stubs/rail.ts +100 -32
- package/templates/supplier-payments-agent/packages/agent-core/src/tools.ts +17 -6
- package/templates/supplier-payments-agent/packages/agent-core/src/types.ts +54 -1
- package/templates/supplier-payments-agent/packages/agent-core/test/approval-seal.test.ts +233 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/attempt-replay.test.ts +265 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/composition.test.ts +204 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/fixtures/production-receipt-keys.json +1 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/helpers.ts +4 -1
- package/templates/supplier-payments-agent/packages/agent-core/test/mandate-status.test.ts +85 -9
- package/templates/supplier-payments-agent/packages/agent-core/test/quote.test.ts +198 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receipt-chain.test.ts +220 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receipt-read-verification.test.ts +197 -0
- package/templates/supplier-payments-agent/packages/agent-core/test/receivables.test.ts +103 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/README.md +12 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/adversarial.ts +32 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/types.ts +53 -4
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/cloud-api.ts +123 -22
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/emulator.ts +30 -20
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/index.ts +146 -21
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/open.ts +40 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/run.ts +8 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/channels/whatsapp/session.ts +16 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll-whatsapp.ts +70 -20
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/poll.ts +2 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/resume.ts +8 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start-whatsapp.ts +3 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/start.ts +6 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/verify.ts +239 -38
- package/templates/supplier-payments-agent/packages/agent-runtime/src/commands/webhook.ts +3 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/index.ts +1 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/src/inspect.ts +5 -1
- package/templates/supplier-payments-agent/packages/agent-runtime/src/kit.ts +25 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/scenarios.ts +8 -2
- package/templates/supplier-payments-agent/packages/agent-runtime/src/setup.ts +13 -3
- package/templates/supplier-payments-agent/packages/agent-runtime/src/terminal.ts +127 -18
- package/templates/supplier-payments-agent/packages/agent-runtime/test/verify-cli.test.ts +211 -9
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-cloud-api.test.ts +45 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-session.test.ts +32 -0
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-status-replies.test.ts +182 -0
- package/templates/bills-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
- package/templates/collections-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
- package/templates/hello-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
- package/templates/supplier-payments-agent/packages/agent-runtime/test/whatsapp-emulator.integration.test.ts +0 -204
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opening the WhatsApp channel: wiring, and only wiring.
|
|
3
|
+
*
|
|
4
|
+
* It lives apart from the command that starts a conversation because a second
|
|
5
|
+
* command now JOINS one — `codespar-agent poll --channel whatsapp`, which
|
|
6
|
+
* comes back to a conversation the run that opened it has long since ended.
|
|
7
|
+
* Two commands wiring the same channel by hand is how a rule ends up enforced
|
|
8
|
+
* on one path and not the other, and the whole claim of `channels/` is that
|
|
9
|
+
* the rules are above the backend. So both go through here.
|
|
10
|
+
*
|
|
11
|
+
* The decisions are all elsewhere: the rules in `channels/rules.ts`, the
|
|
12
|
+
* session window in `session.ts`, the gates in `terminal.ts`.
|
|
13
|
+
*/
|
|
14
|
+
import type { ConversationScript, ProofBundle } from "@codespar/agent-core";
|
|
15
|
+
import type { Agent } from "../../agent.js";
|
|
16
|
+
import type { Setup } from "../../setup.js";
|
|
17
|
+
import { knownSubjects, loadTemplates } from "../index.js";
|
|
18
|
+
import type { ChannelBackend, ChannelLogLine, Conversation } from "../types.js";
|
|
19
|
+
import { WhatsAppChannel } from "./index.js";
|
|
20
|
+
import { WhatsAppCloudApi, loadCloudApiConfig, type CloudApiConfig } from "./cloud-api.js";
|
|
21
|
+
import { EmulatorDriver, EMULATOR_DEFAULTS, EMULATOR_ENV, WhatsAppEmulator } from "./emulator.js";
|
|
22
|
+
import type { SessionState } from "./session.js";
|
|
23
|
+
|
|
24
|
+
export type WhatsAppBackendName = "simulator" | "cloud-api";
|
|
25
|
+
|
|
26
|
+
export interface OpenWhatsAppOptions {
|
|
27
|
+
agent: Agent;
|
|
28
|
+
setup: Setup;
|
|
29
|
+
conversation: Conversation;
|
|
30
|
+
backend: WhatsAppBackendName;
|
|
31
|
+
now: () => Date;
|
|
32
|
+
say: (line: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Where the conversation is recorded. Normally the run's own bundle; a poll
|
|
35
|
+
* hands the bundle of the run it is continuing, because a conversation is
|
|
36
|
+
* one record and the message that closes it belongs with the ones that
|
|
37
|
+
* opened it.
|
|
38
|
+
*/
|
|
39
|
+
bundle: ProofBundle;
|
|
40
|
+
/** The person's turns, when a script drives them. Absent and with no `ask`, nobody writes. */
|
|
41
|
+
script?: ConversationScript | undefined;
|
|
42
|
+
/** Interactive only: reads the person's next line. */
|
|
43
|
+
ask?: ((question: string) => Promise<string>) | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Pins the emulator's CONVERSATION clock. A run that starts a conversation
|
|
46
|
+
* pins it; a run that joins one must not, because rewinding the provider's
|
|
47
|
+
* clock would reopen a window that really is shut. The agent's own notion of
|
|
48
|
+
* now is `now` above and is a different thing (#16).
|
|
49
|
+
*/
|
|
50
|
+
pinAt?: Date | undefined;
|
|
51
|
+
/** Where the session window stood when an earlier run left this conversation. */
|
|
52
|
+
session?: SessionState | undefined;
|
|
53
|
+
/** Draws the conversation on the operator's console. */
|
|
54
|
+
render?: ((line: string) => void) | undefined;
|
|
55
|
+
/** What earlier runs wrote for this conversation: a status about one of their messages is tied back to it. */
|
|
56
|
+
priorLines?: ReadonlyArray<ChannelLogLine> | undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* How long a command that has just told an outcome waits for status webhooks
|
|
61
|
+
* before it reports the outcome as told. A provider reports a failed delivery
|
|
62
|
+
* AFTER the send answered, and a process that has exited hears nothing; this
|
|
63
|
+
* bounds the wait. `WHATSAPP_STATUS_GRACE_MS` overrides it (0 turns it off).
|
|
64
|
+
*/
|
|
65
|
+
export function statusGraceMs(env: NodeJS.ProcessEnv = process.env): number {
|
|
66
|
+
const raw = Number(env["WHATSAPP_STATUS_GRACE_MS"]);
|
|
67
|
+
return Number.isFinite(raw) && raw >= 0 && env["WHATSAPP_STATUS_GRACE_MS"]?.trim() ? raw : 1000;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The cursor that says an outcome's message came back `failed`, so a later poll reports it as not told rather than as told. */
|
|
71
|
+
export function undeliveredCursor(executionId: string, state: string): string {
|
|
72
|
+
return `undelivered:${executionId}:${state}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A `failed` status is an event in the proof bundle, always. When the message
|
|
77
|
+
* told an outcome it is `message.debtor.failed`, and the cursor above is taken:
|
|
78
|
+
* the cursor that says the person was told (`markTold`) stays held — a failed
|
|
79
|
+
* delivery is not retried by itself, because the usual cause (not a WhatsApp
|
|
80
|
+
* number, blocked) fails again — but nothing reports that outcome as told.
|
|
81
|
+
*/
|
|
82
|
+
export function recordDeliveryFailure(s: Setup, line: ChannelLogLine): void {
|
|
83
|
+
const payload = { message_id: line.message_id, errors: line.errors ?? [], channel: "whatsapp" };
|
|
84
|
+
if (!line.about) {
|
|
85
|
+
s.engine.note("message.failed", null, payload);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
s.store.setCursor(undeliveredCursor(line.about.execution_id, line.about.state), JSON.stringify(payload));
|
|
89
|
+
s.engine.note("message.debtor.failed", line.about.execution_id, { ...payload, state: line.about.state });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface OpenedWhatsApp {
|
|
93
|
+
channel: WhatsAppChannel;
|
|
94
|
+
/** The `_sim/*` side, when the backend is the emulator. Absent against Meta. */
|
|
95
|
+
driver?: EmulatorDriver;
|
|
96
|
+
/** How the emulator keys this conversation, for the priced timeline. */
|
|
97
|
+
sessionKey?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Builds the channel, or explains what is missing. Nothing is opened here:
|
|
102
|
+
* the caller opens it, because an emulator that is not running has to be
|
|
103
|
+
* reported differently depending on how far the command already got.
|
|
104
|
+
*/
|
|
105
|
+
export function buildWhatsApp(options: OpenWhatsAppOptions): OpenedWhatsApp | { refusal: string[] } {
|
|
106
|
+
const { agent, setup: s, say } = options;
|
|
107
|
+
let backend: ChannelBackend;
|
|
108
|
+
let driver: EmulatorDriver | undefined;
|
|
109
|
+
let sessionKey: string | undefined;
|
|
110
|
+
|
|
111
|
+
if (options.backend === "cloud-api") {
|
|
112
|
+
const { config, missing } = loadCloudApiConfig(process.env);
|
|
113
|
+
if (!config) {
|
|
114
|
+
return {
|
|
115
|
+
refusal: [
|
|
116
|
+
`the cloud-api backend needs credentials this repo ships none of: ${missing.join(", ")}.`,
|
|
117
|
+
`They live in the agent's .env (commented out in .env.example) and belong to a Meta Business account. The emulator needs none: drop --backend cloud-api.`,
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
say("[whatsapp] cloud-api backend: this repo has never run one against Meta. The shapes are written from the published documentation; the first live run is yours.");
|
|
122
|
+
backend = new WhatsAppCloudApi({ config, conversation: { contact: options.conversation.contact }, say });
|
|
123
|
+
} else {
|
|
124
|
+
// The SAME backend, pointed somewhere else. What the emulator adds is the
|
|
125
|
+
// `_sim/*` side: making the person write, and moving the conversation clock.
|
|
126
|
+
const env = process.env;
|
|
127
|
+
const url = env[EMULATOR_ENV.url]?.trim() || EMULATOR_DEFAULTS.url;
|
|
128
|
+
const config: CloudApiConfig = {
|
|
129
|
+
baseUrl: url,
|
|
130
|
+
phoneNumberId: env[EMULATOR_ENV.phoneNumberId]?.trim() || EMULATOR_DEFAULTS.phoneNumberId,
|
|
131
|
+
// Not credentials: the emulator has no auth, and these are the values
|
|
132
|
+
// `npm run whatsapp:emulator` starts it with.
|
|
133
|
+
accessToken: "emulator",
|
|
134
|
+
verifyToken: "emulator",
|
|
135
|
+
appSecret: env[EMULATOR_ENV.appSecret]?.trim() || EMULATOR_DEFAULTS.appSecret,
|
|
136
|
+
apiVersion: EMULATOR_DEFAULTS.apiVersion,
|
|
137
|
+
webhookPort: Number(env[EMULATOR_ENV.webhookPort]?.trim() || EMULATOR_DEFAULTS.webhookPort),
|
|
138
|
+
};
|
|
139
|
+
driver = new EmulatorDriver(url);
|
|
140
|
+
sessionKey = `${config.phoneNumberId}:${options.conversation.contact}`;
|
|
141
|
+
backend = new WhatsAppEmulator({
|
|
142
|
+
config,
|
|
143
|
+
conversation: { contact: options.conversation.contact },
|
|
144
|
+
driver,
|
|
145
|
+
say,
|
|
146
|
+
render: options.render ?? say,
|
|
147
|
+
...(options.script ? { script: options.script } : {}),
|
|
148
|
+
...(options.ask ? { ask: options.ask } : {}),
|
|
149
|
+
...(options.pinAt ? { pinAt: options.pinAt } : {}),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// The hours are the agent's, from `guardrails.envelope.collection_hours`. An agent that declares none gets no hours rule, which is correct: not every conversation is a collection.
|
|
154
|
+
const window = typeof s.guardrails.envelope?.["collection_hours"] === "string" ? (s.guardrails.envelope["collection_hours"] as string) : undefined;
|
|
155
|
+
|
|
156
|
+
const channel = new WhatsAppChannel({
|
|
157
|
+
backend,
|
|
158
|
+
conversation: options.conversation,
|
|
159
|
+
now: options.now,
|
|
160
|
+
...(window ? { hours: { window, timezone: s.guardrails.timezone } } : {}),
|
|
161
|
+
knownSubjects: knownSubjects(agent),
|
|
162
|
+
templates: loadTemplates(agent),
|
|
163
|
+
bundle: options.bundle,
|
|
164
|
+
say,
|
|
165
|
+
render: options.render ?? say,
|
|
166
|
+
...(options.session ? { session: options.session } : {}),
|
|
167
|
+
...(options.priorLines ? { priorLines: options.priorLines } : {}),
|
|
168
|
+
onDeliveryFailed: (line) => recordDeliveryFailure(s, line),
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return { channel, ...(driver ? { driver } : {}), ...(sessionKey ? { sessionKey } : {}) };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The priced timeline the emulator computed: read for the console, never asserted on. */
|
|
175
|
+
export async function simulatedCost(driver: EmulatorDriver | undefined, sessionKey: string | undefined): Promise<{ total: number; currency: string } | undefined> {
|
|
176
|
+
if (!driver || !sessionKey) return undefined;
|
|
177
|
+
const state = (await driver.state(sessionKey).catch(() => undefined)) as { priced?: { total?: number; currency?: string } } | undefined;
|
|
178
|
+
if (typeof state?.priced?.total !== "number") return undefined;
|
|
179
|
+
return { total: state.priced.total, currency: state.priced.currency ?? "BRL" };
|
|
180
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a payable receivable is put into a conversation.
|
|
3
|
+
*
|
|
4
|
+
* It is here, and not inside the run loop, because two commands now present
|
|
5
|
+
* one: the run that issues the charge, and the poll that comes back to it
|
|
6
|
+
* later. A payer told how to pay by a poll must be told the same way as a
|
|
7
|
+
* payer told by a run, and the only way to guarantee that is for both to
|
|
8
|
+
* build the same messages from the same place.
|
|
9
|
+
*
|
|
10
|
+
* The shape is the point. The QR goes as an IMAGE and the copy-and-paste as
|
|
11
|
+
* its OWN message underneath, because a code inside a picture cannot be
|
|
12
|
+
* copied and a code inside a paragraph cannot be tapped.
|
|
13
|
+
*/
|
|
14
|
+
import type { ChargeInstrument, Execution } from "@codespar/agent-core";
|
|
15
|
+
import type { OutboundBody } from "../types.js";
|
|
16
|
+
|
|
17
|
+
export function instrumentBodies(execution: Execution, instalment: number, chargeId: string, instrument: ChargeInstrument, currency: string): OutboundBody[] {
|
|
18
|
+
const money = new Intl.NumberFormat("pt-BR", { style: "currency", currency });
|
|
19
|
+
const item = execution.items[instalment - 1];
|
|
20
|
+
const count = execution.items.length;
|
|
21
|
+
const bodies: OutboundBody[] = [
|
|
22
|
+
{
|
|
23
|
+
kind: "text",
|
|
24
|
+
text: [
|
|
25
|
+
count > 1 ? `Parcela ${instalment}/${count}: ` : "",
|
|
26
|
+
item ? money.format(item.amount / 100) : "",
|
|
27
|
+
instrument.due_date ? `, vence ${formatDate(instrument.due_date)}` : "",
|
|
28
|
+
` — cobranca ${chargeId}`,
|
|
29
|
+
].join(""),
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
if (instrument.pix_copy_paste) {
|
|
33
|
+
bodies.push({ kind: "media", media: "qr", data: instrument.pix_copy_paste });
|
|
34
|
+
bodies.push({ kind: "instrument", instrument: "pix_copy_paste", value: instrument.pix_copy_paste });
|
|
35
|
+
}
|
|
36
|
+
if (instrument.boleto_bank_line) {
|
|
37
|
+
bodies.push({ kind: "text", text: "Ou pelo boleto, linha digitavel:" });
|
|
38
|
+
bodies.push({ kind: "instrument", instrument: "boleto_bank_line", value: instrument.boleto_bank_line });
|
|
39
|
+
}
|
|
40
|
+
return bodies;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** `2026-09-30` -> `30/09/2026`. */
|
|
44
|
+
export function formatDate(iso: string): string {
|
|
45
|
+
const [y, m, d] = iso.split("-");
|
|
46
|
+
return `${d}/${m}/${y}`;
|
|
47
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The conversation loop: the WhatsApp equivalent of `interactive()`, and the
|
|
3
|
+
* same gates in the same order. The person writes, the model answers, the
|
|
4
|
+
* core decides, and what the core produced goes back into the conversation.
|
|
5
|
+
*
|
|
6
|
+
* The one difference from the terminal, and it is the whole reason the
|
|
7
|
+
* channel exists as its own thing: on a terminal the operator and the
|
|
8
|
+
* counterparty are the same pair of eyes, and here they are not. The
|
|
9
|
+
* approval question and the console go to stderr; only `tell` reaches the
|
|
10
|
+
* person. Nothing checks that — it is the wiring: `ask` and `say` are
|
|
11
|
+
* functions this module never hands to the channel.
|
|
12
|
+
*
|
|
13
|
+
* The payable artifact is sent the way a person can actually use it: the QR
|
|
14
|
+
* as an image, and the copy-and-paste as its OWN message underneath, because
|
|
15
|
+
* a code inside a picture cannot be copied and a code inside a paragraph
|
|
16
|
+
* cannot be tapped.
|
|
17
|
+
*/
|
|
18
|
+
import type { ChargeInstrument, Execution } from "@codespar/agent-core";
|
|
19
|
+
import { handleExecution, type TerminalOptions } from "../../terminal.js";
|
|
20
|
+
import type { Setup } from "../../setup.js";
|
|
21
|
+
import type { OutboundBody } from "../types.js";
|
|
22
|
+
import { instrumentBodies } from "./present.js";
|
|
23
|
+
import { statusGraceMs } from "./open.js";
|
|
24
|
+
import type { WhatsAppChannel } from "./index.js";
|
|
25
|
+
|
|
26
|
+
export interface ConverseOptions {
|
|
27
|
+
setup: Setup;
|
|
28
|
+
channel: WhatsAppChannel;
|
|
29
|
+
approver: { id: string; channel: string };
|
|
30
|
+
/** The operator's decision when nobody is at a keyboard. The gate runs `mandate`, where there is none to take. */
|
|
31
|
+
decision?: "approve" | "deny" | "none";
|
|
32
|
+
waitSeconds?: number | undefined;
|
|
33
|
+
simulatePayer?: boolean | undefined;
|
|
34
|
+
/** The operator's console. Never the conversation. */
|
|
35
|
+
say: (line: string) => void;
|
|
36
|
+
/** The operator's keyboard, when there is one. Never the conversation. */
|
|
37
|
+
ask?: ((question: string) => Promise<string>) | undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ConverseResult {
|
|
41
|
+
turns: number;
|
|
42
|
+
replies: string[];
|
|
43
|
+
toolCalls: Array<{ name: string; refused: boolean }>;
|
|
44
|
+
executions: Execution[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Keeps outbound messages in the order the run produced them, across await points. */
|
|
48
|
+
class Outbox {
|
|
49
|
+
private chain: Promise<void> = Promise.resolve();
|
|
50
|
+
|
|
51
|
+
constructor(private readonly channel: WhatsAppChannel) {}
|
|
52
|
+
|
|
53
|
+
push(body: OutboundBody): void {
|
|
54
|
+
this.chain = this.chain.then(async () => {
|
|
55
|
+
await this.channel.send(body);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
drain(): Promise<void> {
|
|
60
|
+
return this.chain;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function converse(options: ConverseOptions): Promise<ConverseResult> {
|
|
65
|
+
const { setup: s, channel, approver, say } = options;
|
|
66
|
+
const outbox = new Outbox(channel);
|
|
67
|
+
let toldOutcome = false;
|
|
68
|
+
const replies: string[] = [];
|
|
69
|
+
const toolCalls: Array<{ name: string; refused: boolean }> = [];
|
|
70
|
+
let turns = 0;
|
|
71
|
+
|
|
72
|
+
const presentInstrument = (execution: Execution, instalment: number, chargeId: string, instrument: ChargeInstrument): Promise<void> => {
|
|
73
|
+
for (const body of instrumentBodies(execution, instalment, chargeId, instrument, s.mandate.currency)) outbox.push(body);
|
|
74
|
+
// Awaited by the poll: the person has the code in hand before the next look, not after the cycle closed.
|
|
75
|
+
return outbox.drain();
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const terminalOptions: TerminalOptions = {
|
|
79
|
+
setup: s,
|
|
80
|
+
approver,
|
|
81
|
+
say,
|
|
82
|
+
tell: (line: string, about?: Execution) => {
|
|
83
|
+
if (about) toldOutcome = true;
|
|
84
|
+
outbox.push({ kind: "text", text: line, ...(about ? { about: { execution_id: about.id, state: about.state } } : {}) });
|
|
85
|
+
},
|
|
86
|
+
presentInstrument,
|
|
87
|
+
...(options.decision ? { decision: options.decision } : {}),
|
|
88
|
+
...(options.ask ? { ask: options.ask } : {}),
|
|
89
|
+
...(options.waitSeconds !== undefined ? { waitSeconds: options.waitSeconds } : {}),
|
|
90
|
+
...(options.simulatePayer !== undefined ? { simulatePayer: options.simulatePayer } : {}),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// The channel is opened by the caller, which is where an emulator that is not
|
|
94
|
+
// running has to be reported: by here the run has already started.
|
|
95
|
+
const runtime = s.makeRuntime();
|
|
96
|
+
const loop = s.makeLoop(runtime, (execution) => handleExecution(execution, terminalOptions));
|
|
97
|
+
|
|
98
|
+
say(`${s.manifest.manifest.name} ${s.manifest.manifest.version} — canal: whatsapp (${channel.backend}) — approval: ${s.mode} — trilho: ${s.railKind} — ${s.kit.labels.mandateWord} ${s.mandate.id}`);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
for (;;) {
|
|
102
|
+
const message = await channel.next();
|
|
103
|
+
if (!message) break;
|
|
104
|
+
turns += 1;
|
|
105
|
+
const result = await loop.turn(message.text);
|
|
106
|
+
toolCalls.push(...result.tool_calls);
|
|
107
|
+
// Whatever the execution put in the conversation goes first: the QR before the sentence that explains it.
|
|
108
|
+
await outbox.drain();
|
|
109
|
+
if (result.reply.trim()) await channel.say(result.reply);
|
|
110
|
+
replies.push(result.reply);
|
|
111
|
+
}
|
|
112
|
+
await outbox.drain();
|
|
113
|
+
// An outcome told in this run may still come back failed; give the provider its bounded moment before the receiver closes.
|
|
114
|
+
if (toldOutcome) await channel.settle(statusGraceMs());
|
|
115
|
+
} finally {
|
|
116
|
+
await channel.close();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { turns, replies, toolCalls, executions: s.engine.list().filter((e) => e.run_id === s.runId) };
|
|
120
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WhatsApp's own rule, which is not ours and which the simulator obeys
|
|
3
|
+
* anyway: outside the 24 hours that follow the person's last message, a
|
|
4
|
+
* business may only send a TEMPLATE that Meta approved in advance. Inside
|
|
5
|
+
* the window it may write freely.
|
|
6
|
+
*
|
|
7
|
+
* The simulator enforces it for the reason a simulator exists: something that
|
|
8
|
+
* only fails against the real provider is something you discover in
|
|
9
|
+
* production. A collections agent hits this constantly — the debtor answers
|
|
10
|
+
* on Tuesday, the charge is paid on Friday, and "recebemos, acordo quitado"
|
|
11
|
+
* falls outside the window.
|
|
12
|
+
*
|
|
13
|
+
* Template APPROVAL is a STUB and cannot be anything else from here: a
|
|
14
|
+
* template is registered in a Meta Business account, reviewed by Meta, and
|
|
15
|
+
* given a status we have no way to read without that account. What this holds
|
|
16
|
+
* is the LOCAL registry — the templates the agent declares it uses, from
|
|
17
|
+
* `channels/whatsapp/templates.json` — so sending one that was never declared
|
|
18
|
+
* is refused here instead of 400-ing at Meta. Whether Meta approved it is the
|
|
19
|
+
* developer's to check, and the README says so.
|
|
20
|
+
*/
|
|
21
|
+
import { templateArity, type WhatsAppTemplate } from "@codespar/agent-core";
|
|
22
|
+
|
|
23
|
+
export const SESSION_WINDOW_SECONDS = 24 * 60 * 60;
|
|
24
|
+
|
|
25
|
+
export interface SessionState {
|
|
26
|
+
/** Unix seconds of the last message the PERSON sent, on the provider's clock. */
|
|
27
|
+
lastInboundAt?: number | undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Why a template send would be refused. `undefined` means it may go. */
|
|
31
|
+
export type TemplateRefusal = { rule: string; detail: string } | undefined;
|
|
32
|
+
|
|
33
|
+
export class SessionWindow {
|
|
34
|
+
private readonly templates: Map<string, WhatsAppTemplate>;
|
|
35
|
+
private lastInboundAt: number | undefined;
|
|
36
|
+
/** The provider said the window is shut (131047) after ours read it open. Holds until the person writes again. */
|
|
37
|
+
private shutByProvider = false;
|
|
38
|
+
|
|
39
|
+
constructor(templates: readonly WhatsAppTemplate[], initial?: SessionState) {
|
|
40
|
+
this.templates = new Map(templates.map((t) => [t.name, t]));
|
|
41
|
+
this.lastInboundAt = initial?.lastInboundAt;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
observeInbound(timestamp: number): void {
|
|
45
|
+
if (this.lastInboundAt === undefined || timestamp > this.lastInboundAt) {
|
|
46
|
+
this.lastInboundAt = timestamp;
|
|
47
|
+
this.shutByProvider = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The provider refused a free-form message because its window is shut. Its
|
|
53
|
+
* clock is the one the 24 hours are counted on, so from here the window
|
|
54
|
+
* reads shut whatever ours says, and the next message is a template.
|
|
55
|
+
*/
|
|
56
|
+
observeProviderShut(): void {
|
|
57
|
+
this.shutByProvider = true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Seconds left before free-form messages stop being allowed; 0 when the window is shut or was never opened. */
|
|
61
|
+
remainingSeconds(now: Date): number {
|
|
62
|
+
if (this.lastInboundAt === undefined || this.shutByProvider) return 0;
|
|
63
|
+
return Math.max(0, this.lastInboundAt + SESSION_WINDOW_SECONDS - Math.floor(now.getTime() / 1000));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
open(now: Date): boolean {
|
|
67
|
+
return this.remainingSeconds(now) > 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The declaration, for a caller that has to build a send: the language is the registry's, never the caller's guess. */
|
|
71
|
+
declared(name: string): WhatsAppTemplate | undefined {
|
|
72
|
+
return this.templates.get(name);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* What the Cloud API would refuse about this template send, decided against
|
|
77
|
+
* the declaration. Three things are visible to a registry: a name nobody
|
|
78
|
+
* declared, a language the template was not registered in, and a variable
|
|
79
|
+
* count the body has no placeholders for. Meta answers each with a 4xx and
|
|
80
|
+
* delivers nothing, so each is refused here instead.
|
|
81
|
+
*/
|
|
82
|
+
refuse(body: { template: string; language: string; variables: readonly string[] }): TemplateRefusal {
|
|
83
|
+
const declared = this.templates.get(body.template);
|
|
84
|
+
if (!declared) {
|
|
85
|
+
return {
|
|
86
|
+
rule: "template_unknown",
|
|
87
|
+
detail: `the agent declares no template named ${body.template}; Meta only delivers templates it approved, and this one was never registered here`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (declared.language !== body.language) {
|
|
91
|
+
return {
|
|
92
|
+
rule: "template_language_unknown",
|
|
93
|
+
detail: `${body.template} is declared in ${declared.language}, not ${body.language}; a template approved in one language does not exist in another`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const expected = templateArity(declared.body);
|
|
97
|
+
if (body.variables.length !== expected) {
|
|
98
|
+
return { rule: "template_variables_mismatch", detail: `${body.template} takes ${expected} variable(s) and this send carries ${body.variables.length}` };
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Where a conversation's window stood, read back from a bundle's
|
|
106
|
+
* `channel.jsonl`. This is what lets a later process — a poll, days after the
|
|
107
|
+
* run that opened the conversation — know whether it may still write freely,
|
|
108
|
+
* instead of assuming the worst and always sending a template.
|
|
109
|
+
*
|
|
110
|
+
* It reads the PROVIDER's timestamp and never the line's `at`, which is the
|
|
111
|
+
* run's own clock: the 24 hours are counted by WhatsApp on WhatsApp's clock,
|
|
112
|
+
* and a record written by a machine whose clock drifted is not evidence about
|
|
113
|
+
* theirs. A log carrying no provider timestamp on any inbound line yields no
|
|
114
|
+
* state, and the window then reads as SHUT — the safe direction, because a
|
|
115
|
+
* template is deliverable in both halves of the rule and a free-form message
|
|
116
|
+
* is deliverable in one.
|
|
117
|
+
*/
|
|
118
|
+
export function sessionStateFromChannelLog(lines: ReadonlyArray<Record<string, unknown>>): SessionState {
|
|
119
|
+
let lastInboundAt: number | undefined;
|
|
120
|
+
for (const line of lines) {
|
|
121
|
+
if (line["direction"] !== "in") continue;
|
|
122
|
+
const at = line["provider_timestamp"];
|
|
123
|
+
if (typeof at !== "number" || !Number.isFinite(at)) continue;
|
|
124
|
+
if (lastInboundAt === undefined || at > lastInboundAt) lastInboundAt = at;
|
|
125
|
+
}
|
|
126
|
+
return { lastInboundAt };
|
|
127
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `codespar-agent <command> [args]`. The agent is the working directory; the
|
|
3
|
+
* command is this runner's, the same for every agent. What differs between
|
|
4
|
+
* agents is behind `kit`.
|
|
5
|
+
*/
|
|
6
|
+
import { stderr } from "node:process";
|
|
7
|
+
import { loadAgent, type Agent } from "./agent.js";
|
|
8
|
+
import { readDotEnv } from "./setup.js";
|
|
9
|
+
import { check } from "./commands/check.js";
|
|
10
|
+
import { decide } from "./commands/decide.js";
|
|
11
|
+
import { runEval } from "./commands/eval.js";
|
|
12
|
+
import { inspect } from "./commands/inspect.js";
|
|
13
|
+
import { poll } from "./commands/poll.js";
|
|
14
|
+
import { reconcile } from "./commands/reconcile.js";
|
|
15
|
+
import { rerun } from "./commands/rerun.js";
|
|
16
|
+
import { resume } from "./commands/resume.js";
|
|
17
|
+
import { start } from "./commands/start.js";
|
|
18
|
+
import { verify } from "./commands/verify.js";
|
|
19
|
+
import { webhook } from "./commands/webhook.js";
|
|
20
|
+
|
|
21
|
+
const COMMANDS = ["start", "consent", "approve", "deny", "resume", "rerun", "reconcile", "inspect", "poll", "webhook", "check", "eval", "verify"] as const;
|
|
22
|
+
|
|
23
|
+
/** Commands that are not about an agent. `verify` reads a receipt file and a
|
|
24
|
+
* public key set, which is all a party outside CodeSpar has: requiring an
|
|
25
|
+
* `agent.yaml` above the working directory would mean the verifier needed the
|
|
26
|
+
* agent that produced the receipt, and the whole point is that it does not. */
|
|
27
|
+
const AGENTLESS: readonly string[] = ["verify"];
|
|
28
|
+
|
|
29
|
+
export async function main(argv: string[] = process.argv.slice(2)): Promise<number> {
|
|
30
|
+
const rest = [...argv];
|
|
31
|
+
let dir: string | undefined;
|
|
32
|
+
const at = rest.indexOf("--agent");
|
|
33
|
+
if (at >= 0) {
|
|
34
|
+
dir = rest[at + 1];
|
|
35
|
+
rest.splice(at, dir === undefined ? 1 : 2);
|
|
36
|
+
}
|
|
37
|
+
const first = rest[0];
|
|
38
|
+
const command = first !== undefined && !first.startsWith("-") ? rest.shift()! : "start";
|
|
39
|
+
if (!(COMMANDS as readonly string[]).includes(command)) {
|
|
40
|
+
stderr.write(`unknown command ${command}; one of: ${COMMANDS.join(", ")}\n`);
|
|
41
|
+
return 2;
|
|
42
|
+
}
|
|
43
|
+
if (AGENTLESS.includes(command)) return verify(rest);
|
|
44
|
+
const agent = await loadAgent(dir);
|
|
45
|
+
readDotEnv(agent.dir);
|
|
46
|
+
return run(agent, command, rest);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function run(agent: Agent, command: string, argv: string[]): Promise<number> {
|
|
50
|
+
const awaitsPayer = agent.settlement === "await-payer";
|
|
51
|
+
switch (command) {
|
|
52
|
+
case "start":
|
|
53
|
+
return start(agent, argv);
|
|
54
|
+
case "consent": {
|
|
55
|
+
if (!agent.kit.consent) {
|
|
56
|
+
stderr.write(`${agent.slug}: this agent has no consent step (its policy is its own file, not a signed mandate)\n`);
|
|
57
|
+
return 2;
|
|
58
|
+
}
|
|
59
|
+
return agent.kit.consent({ agentDir: agent.dir, argv, say: (l) => void stderr.write(l + "\n") });
|
|
60
|
+
}
|
|
61
|
+
case "approve":
|
|
62
|
+
return decide(agent, "approve", argv);
|
|
63
|
+
case "deny":
|
|
64
|
+
return decide(agent, "deny", argv);
|
|
65
|
+
case "resume":
|
|
66
|
+
return resume(agent, argv);
|
|
67
|
+
case "rerun":
|
|
68
|
+
return rerun(agent, argv);
|
|
69
|
+
case "reconcile":
|
|
70
|
+
return reconcile(agent, argv);
|
|
71
|
+
case "inspect":
|
|
72
|
+
return inspect(agent, argv);
|
|
73
|
+
case "check":
|
|
74
|
+
return check(agent, argv);
|
|
75
|
+
case "eval":
|
|
76
|
+
return runEval(agent, argv);
|
|
77
|
+
case "poll":
|
|
78
|
+
case "webhook": {
|
|
79
|
+
if (!awaitsPayer) {
|
|
80
|
+
stderr.write(`${agent.slug}: ${command} is for an agent that waits for a payer; this one settles on the rail's answer\n`);
|
|
81
|
+
return 2;
|
|
82
|
+
}
|
|
83
|
+
return command === "poll" ? poll(agent, argv) : webhook(agent, argv);
|
|
84
|
+
}
|
|
85
|
+
default:
|
|
86
|
+
stderr.write(`unknown command ${command}\n`);
|
|
87
|
+
return 2;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (process.argv[1] && import.meta.url === new URL(`file://${process.argv[1]}`).href) {
|
|
92
|
+
main()
|
|
93
|
+
.then((code) => process.exit(code))
|
|
94
|
+
.catch((err) => {
|
|
95
|
+
stderr.write(`${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `codespar-agent check [--json]`: the manifest-coherence gate of section 4.3.
|
|
3
|
+
*/
|
|
4
|
+
import { stderr, stdout } from "node:process";
|
|
5
|
+
import { checkAgent } from "@codespar/agent-core";
|
|
6
|
+
import type { Agent } from "../agent.js";
|
|
7
|
+
|
|
8
|
+
export function check(agent: Agent, argv: string[]): number {
|
|
9
|
+
const json = argv.includes("--json");
|
|
10
|
+
const report = checkAgent(agent.dir);
|
|
11
|
+
if (json) stdout.write(JSON.stringify(report) + "\n");
|
|
12
|
+
for (const f of report.findings) stderr.write(`${f.level === "error" ? "ERROR" : "warn "} ${f.code}: ${f.message}\n`);
|
|
13
|
+
stderr.write(report.ok ? `check ok: ${report.agent} agrees with its agent.yaml\n` : `check FAILED for ${report.agent ?? "agent"}\n`);
|
|
14
|
+
return report.ok ? 0 : 1;
|
|
15
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `codespar-agent approve|deny <execution-id> [--user <id>] [--json]`:
|
|
3
|
+
* decides an execution left in `awaiting_approval` (a `--input` run without
|
|
4
|
+
* `--approve`, a restart). Produces the section 4.2 artifact and runs the
|
|
5
|
+
* execution through the same last gate `start` uses.
|
|
6
|
+
*/
|
|
7
|
+
import { relative } from "node:path";
|
|
8
|
+
import { stderr, stdout } from "node:process";
|
|
9
|
+
import type { Agent } from "../agent.js";
|
|
10
|
+
import { setup } from "../setup.js";
|
|
11
|
+
import { handleExecution } from "../terminal.js";
|
|
12
|
+
|
|
13
|
+
export async function decide(agent: Agent, decision: "approve" | "deny", argv: string[]): Promise<number> {
|
|
14
|
+
const awaitsPayer = agent.settlement === "await-payer";
|
|
15
|
+
const json = argv.includes("--json");
|
|
16
|
+
const user = argv.includes("--user") ? argv[argv.indexOf("--user") + 1] : undefined;
|
|
17
|
+
const wait = awaitsPayer && argv.includes("--wait") ? Number(argv[argv.indexOf("--wait") + 1]) : undefined;
|
|
18
|
+
const simulatePayer = awaitsPayer && argv.includes("--simulate-payer");
|
|
19
|
+
const executionId = argv.find((a, i) => !a.startsWith("--") && argv[i - 1] !== "--user" && (!awaitsPayer || argv[i - 1] !== "--wait"));
|
|
20
|
+
const say = (l: string) => stderr.write(l + "\n");
|
|
21
|
+
if (!executionId) {
|
|
22
|
+
say(`usage: npm run ${decision} <execution-id> [--user <id>]${awaitsPayer ? " [--wait <seconds>] [--simulate-payer]" : ""} [--json]`);
|
|
23
|
+
return 2;
|
|
24
|
+
}
|
|
25
|
+
const s = setup(agent, { say, runId: `run_${decision}_${Date.now().toString(36)}` });
|
|
26
|
+
try {
|
|
27
|
+
const execution = s.engine.get(executionId);
|
|
28
|
+
if (!execution) {
|
|
29
|
+
say(`unknown execution ${executionId}`);
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
if (execution.state !== "awaiting_approval") {
|
|
33
|
+
say(`${executionId} is ${execution.state}, not awaiting_approval; nothing to decide`);
|
|
34
|
+
return 1;
|
|
35
|
+
}
|
|
36
|
+
const final = await handleExecution(execution, {
|
|
37
|
+
setup: s,
|
|
38
|
+
approver: { id: user ?? s.kit.labels.defaultUser, channel: "terminal" },
|
|
39
|
+
decision,
|
|
40
|
+
say,
|
|
41
|
+
...(json ? { tell: say } : {}),
|
|
42
|
+
waitSeconds: wait,
|
|
43
|
+
simulatePayer,
|
|
44
|
+
});
|
|
45
|
+
if (json) {
|
|
46
|
+
stdout.write(
|
|
47
|
+
JSON.stringify({
|
|
48
|
+
execution_id: final.id,
|
|
49
|
+
state: final.state,
|
|
50
|
+
reason: final.reason ?? null,
|
|
51
|
+
approval_id: final.approval_id ?? null,
|
|
52
|
+
...(awaitsPayer ? { charge_ids: final.outcomes.map((o) => o.transaction_id ?? null) } : {}),
|
|
53
|
+
receipt_ids: final.outcomes.filter((o) => o.receipt_id).map((o) => o.receipt_id),
|
|
54
|
+
bundle_dir: relative(process.cwd(), s.bundle.dir),
|
|
55
|
+
}) + "\n",
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (awaitsPayer) return final.state === "executing" && final.reason !== "awaiting_settlement" ? 3 : 0;
|
|
59
|
+
return final.state === "executing" ? 3 : 0;
|
|
60
|
+
} finally {
|
|
61
|
+
s.close();
|
|
62
|
+
}
|
|
63
|
+
}
|