SWI-11363 Agent-native cutover: cut ordering surface, add readiness + errors + AGENTS.md#15
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundles the agent-facing docs: AGENTS.md is the phased Twilio->Bandwidth cutover runbook (band CLI commands, capability boundaries sourced from src/matrix/twilio-voice.json, and the private error codes). README gets a band-pairing pitch and pointer to AGENTS.md. Both README and docs/demo.md drop stale references to the removed number-ordering surface (AvailablePhoneNumbers/IncomingPhoneNumbers routes, scripts/verify-numbers-live.ts, and the deleted number tests), redirecting to the band CLI instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codex review: unauthenticated /readyz?deep=1 triggered a live OAuth exchange and reflected the upstream response body — an info-disclosure + amplification surface. Remove the network probe from the HTTP endpoint entirely: /readyz now reports required-env presence only (no outbound call, no secrets), and the live Bandwidth token probe lives solely in the local `npm run doctor` CLI. Also fix runbook defects: AGENTS.md double-prefixed https in the band app callback URL; document that removed routes return the framework default 404; qualify docs/demo.md SIP peer as legacy-platform-only (default is VCP). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
…cutover # Conflicts: # README.md # src/server/app.ts # src/server/index.ts # test/server-numbers.test.ts
The /bw/* Basic-auth hook checked req.url.startsWith("/bw/"), but the
router percent-decodes the target before matching, so a raw path like
/%62%77/continue routed to /bw/continue while skipping the check. Key the
hook on req.routeOptions.url (the matched route template) so encoded paths
cannot reach a handler unauthenticated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cap Say/Play loop at 1000 (Twilio's max) and enforce a 2 MiB document-wide serialized-output budget, so a crafted TwiML document cannot force unbounded allocation on the event loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stream the customer response with a 256 KiB cap (checked against Content-Length and enforced during read, cancelling the body on overflow) instead of buffering an unbounded body. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reject + percent-encode call/recording ids before upstream URL interpolation (encoding alone does not neutralize dot path segments), and validate ids at the /bw/* webhook ingress boundary. Adds error code 90004. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
WEBHOOK_USER/WEBHOOK_PASSWORD are required to start; add them to REQUIRED_ENV so /readyz and `npm run doctor` report not-ready when they're missing instead of green-lighting a config that crashes on boot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- README: fix band CLI link (/docs/cli -> /tools/cli/); drop stale test counts - AGENTS: add WEBHOOK_USER/WEBHOOK_PASSWORD to the Phase 3 env table + HOST note - demo: webhook Basic auth (-u) + WEBHOOK_* and EGRESS_ALLOW_PRIVATE for the all-localhost demo flow Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
joshraub-bw
approved these changes
Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Makes the adapter something an AI agent can actually drive end-to-end, rather than just a library a developer reads. Two moves: cut the broken number-ordering surface (the
bandCLI owns provisioning now), and add the affordances + docs that let an agent configure, self-check, and cut a customer over to Bandwidth — while being honest about the steps a human still has to own.Changes
src/numbers/*, theAvailablePhoneNumbers/IncomingPhoneNumbersfacade, the live verify script, the number routes/config, and their tests. This code was never trustworthy (it sent JSON to Bandwidth's PascalCase-XML Dashboard API);band number order/search/releaseis the supported path.npm run doctor— a pre-startup config gate: reports (as JSON) which required env vars are set by their real names, and whether the Bandwidth OAuth token exchange works. Exit 0 = ready.GET /readyz— a running-server config/liveness signal. Env-presence only: no outbound call, no secrets — safe to expose unauthenticated.{ code, message, more_info, status }body (private codes 90001/90002/90003). The handler is deliberately neutral: unknown throws → a generic 500, never relabeled "Bandwidth", anderr.messageis never echoed to the client (Bandwidth errors embed upstream response bodies).AGENTS.md— the agent-facing cutover runbook: theband+ adapter paired model, a phased runbook with inlinebandcommands (Universal Platform / VCP path), a greppable🧍 Human requiredmarker on every step an agent can't own (new account, HTTP-Voice feature flag, public host, by-ear verify), capability boundaries pulled faithfully from the matrix, and an errors reference. Plus README pairing pitch anddocs/demo.mdcleanup.Review trail
Built via subagent-driven development (per-task spec+quality review) and independently reviewed by two models. The second (Codex) caught two issues fixed here: an unauthenticated
/readyz?deep=1that triggered a live OAuth exchange and reflected the upstream response body (info-leak + amplification) — resolved by removing the network probe from HTTP entirely and keeping the live token probe local-only indoctor; and a double-https://in a runbookbandcommand.Testing
npm test→ 225/225 green.npm run typecheckclean.Notes for reviewers
adapter/webhook-callback-authPR both editsrc/server/app.ts. Whichever lands second needs a rebase to re-resolve that file — please sequence, don't merge both simultaneously.src/matrix/twilio-voice.jsonlistsRecordasbxml: "transcribe"while the translator/docs correctly drop transcription with a warning — a matrix data nit worth a separate fix.🤖 Generated with Claude Code