Skip to content

WIP: public npx @bitrouter/agent — phases 0–4 of the self-serve plan - #7

Open
SPIKESPIGEL404 wants to merge 1 commit into
mainfrom
claude/bitrouter-agent-production-5ffe96
Open

WIP: public npx @bitrouter/agent — phases 0–4 of the self-serve plan#7
SPIKESPIGEL404 wants to merge 1 commit into
mainfrom
claude/bitrouter-agent-production-5ffe96

Conversation

@SPIKESPIGEL404

Copy link
Copy Markdown
Contributor

WIP / draft. The gate is green and every acceptance criterion in the plan
has pasted proof, but this has not run on a real GitHub runner yet and the
nightly jobs have only been exercised locally. See Not done at the bottom.

Implements docs/plans/npx-self-serve.md. The goal: a stranger runs npx @bitrouter/agent in their repo, having installed BitRouter themselves, and gets an audit that is true, advice matching the release they actually have, and nothing of theirs modified.

What changed, by phase

Phase 0 + CI — the package did not run. Two differing bin keys meant npm could not determine an executable; the bitrouter key also shadowed the real router inside every npx run. Now one bin, an exact pi pin with a shipped npm-shrinkwrap.json, a working exports map, and the ModelRuntime migration with an in-memory credential store — ModelRuntime.create() writes ~/.pi/agent/auth.json without one. The repo had no .github/ at all; it now has a gate matrix plus a pack-smoke job that installs the tarball from a fresh resolution with no lockfile, which is the only thing that catches dependency drift.

Phase 1 — the wizard authors no routing config. bitrouter init already writes a strict superset of any manifest we could produce, and under the v2 lock format a wizard-authored lock would assert compiler-owned evidence (owner, eligible_episodes) with zero episodes — fabricated provenance inside a system whose premise is auditable evidence. So buildYaml, the BARE_PROVIDER table, both emit tools, both yaml writers and the .proposed. redirect are deleted rather than fixed. Zero-write becomes true by construction. --target cloud is gone; the endpoint resolves BITROUTER_BASE_URLbitrouter status → loopback, with a one-token preflight because /v1/models is a listing, not a routability guarantee. yaml/validate.ts became a diagnostic over the user's config, running config validate and policy check as a conjunction — a bad lock makes the first report only parsing policy lock <path> while the second gives line and column.

Phase 2 — no unearned percentages. blendedReductionPct was an unweighted mean, over distinct model-id strings found by a regex, of routineShare × (1 − altRate/curRate): a model id appearing only in a __tests__ file became a full-weight row while gpt-4o-mini, the actual routine workhorse, was dropped from the denominator for being unpriced. routineShare also multiplied real metered dollars on the observed path and was extrapolated to a monthly $ headline, so both paths lose it in the same change — deleting only the cold headline would have left DEFAULT_ROUTINE_SHARE = 0.3 as the sole multiplier on the one number that prints in $. Replaced with a spend surface and honest unpriced rendering.

Phase 3 — automation. Nightly pi-bump, registry-refresh and bitrouter-compat. sync-registry.ts retargeted from a sibling checkout no npx user has to the published dist/registry URLs. The trap: the built providers.json includes BitRouter Cloud and eleven gateways, and unfiltered the cheapest-blended rule sends 22 of 57 model ids to bitrouter — quoting Cloud's price as a self-hoster's price. Filtered on kind and status, with a gate that enforces it.

Phase 4 — smithers leaves. Moved to packages/smithers. @smithers-orchestrator/agents and @ai-sdk/openai removed outright, not relocated to peers: 350 → 177 packages in a consumer install, and find node_modules -name binding.gyp is empty.

Review fixes (a fable-5 pass over the whole thing)

Three of these made the product wrong in a user's hands:

  1. ci.yml still asserted the deleted ./smithers export resolves — the first post-merge run would have failed on its own pack-smoke job. Now asserts the inverse, plus no @smithers-orchestrator/@ai-sdk and no binding.gyp in a consumer tree.
  2. The command block recommended a tier on a provider the user's config never declares. bitrouter policy init accepts it, config validate reports valid, policy check binds the preset — and the tier never routes, because nothing backs it. Silent, and invisible to exactly the two commands the audit tells you to re-run. The audit now renders the providers: stanza and says so. (Phase 3's snapshot refresh introduced this by excluding gateways, which moved the economy pick off openrouter; Phase 1's proof predated it.)
  3. OPENAI_BASE_URL=http://localhost:4356 404s — only /v1/* is served. Both reports emit the suffix now, and BITROUTER_BASE_URL is normalized.

Also: README lede / CLI help / npm description still advertised the deleted policy generation; diagnoseUserConfig was wired to nothing; the LLM path skipped every host-authored section and still asked the model to use a rubric Phase 2 deleted; bitrouter-smithers had no bin. Plus hardening — 401/403 reported as an auth problem rather than "no routable model", --model preflighted, nightly fetches retried only on exit 2 so a real finding is never retried into silence.

Verification

pnpm lint / typecheck / build exit 0
pnpm test 149 passed
pnpm test:e2e 4 passed
packages/smithers 93 passed, 1 skipped
Consumer install 178 packages, 141 MB, no binding.gyp
git -C ~/Documents/Code/bitrouter status --porcelain empty throughout

Zero-write proven by running the built CLI against a fixture already containing bitrouter.yaml and policy-lock.yaml: both sha256 identical before and after, find -newer showing nothing written outside --out-dir. Every factual claim the audit makes about the router — the lock shape, the tool-safe clamp, the non-idempotence, the Terminal-Bench figures — was checked against real bitrouter 1.0.0-alpha.26 and the checkout.

Not done

  • Nothing has run on a real GitHub runner. Both create-pull-request jobs need Settings → Actions → General → Allow GitHub Actions to create and approve pull requests, or their first run fails.
  • src/pi/** has no real unit coverage, and src/commands/run-command.ts has none since its only test covered the deleted smithers branch.
  • validate.test.ts skips its real-binary cases when no router is installed, so they will be permanently skipped in CI. Wants a step that installs BitRouter.
  • Phase 5 is deliberately not started — it is gated on the v2 work being tagged, not merged.
  • The audit cites guardrail_with_status in policy_table_router.rs by line number against repo main; those numbers will rot.

Implements docs/plans/npx-self-serve.md. A stranger runs `npx @bitrouter/agent`
in their repo and gets an audit that is true, advice matching the BitRouter
release they actually have, and nothing of theirs modified.

Phase 0 + CI — the package now runs. One bin, exact pi pin plus a shipped
npm-shrinkwrap, a working exports map, ModelRuntime migration with an in-memory
credential store (it writes ~/.pi/agent/auth.json otherwise), and a gate with a
pack-smoke job that installs the tarball from a fresh resolution.

Phase 1 — the wizard authors no routing config. Deleted buildYaml, the
BARE_PROVIDER table, the emit_bitrouter_yaml and emit_optimized_yaml tools,
writeYamlArtifact/writeOptimizedYamlArtifact and the `.proposed.` redirect.
`bitrouter init` already writes a superset of any manifest we could produce, and
a wizard-authored v2 lock would assert compiler-owned evidence with zero
episodes. Deliverables are the audit and .bitrouter/analysis.json. `--target
cloud` is gone; the endpoint resolves BITROUTER_BASE_URL -> `bitrouter status`
-> loopback, with a one-token preflight because /v1/models is a listing, not a
routability guarantee. yaml/validate.ts became a diagnostic over the user's own
config, running `config validate` and `policy check` as a conjunction — a bad
lock makes the former report only the filename while the latter gives line and
column.

Phase 2 — no unearned percentages. blendedReductionPct was an unweighted mean,
over model-id strings found by a regex, of routineShare x (1 - altRate/curRate);
routineShare also multiplied REAL METERED DOLLARS on the observed path and was
extrapolated to a monthly $ headline. Both paths lose it in the same change.
Replaced with a spend surface and honest unpriced-model rendering — BitRouter's
registry is curated, not exhaustive, so gpt-4o-mini renders as unpriced-by-us
rather than 0%. Where a number is wanted, cite BitRouter's own audited
Terminal-Bench result.

Phase 3 — nightly pi-bump, registry-refresh and bitrouter-compat jobs. The built
providers.json includes BitRouter Cloud and eleven gateways; unfiltered, the
cheapest-blended rule sends 22 of 57 model ids to `bitrouter`, which would quote
Cloud's price as a self-hoster's price. sync-registry now filters on kind and
status, and a gate enforces it.

Phase 4 — smithers moves to packages/smithers as its own package.
@smithers-orchestrator/agents and @ai-sdk/openai are removed outright: 350 -> 177
packages in a consumer install, and no binding.gyp at all.

Review fixes: the audit now names any tier provider a stock `bitrouter init`
config does not declare — `policy init` accepts an undeclared provider, both
`config validate` and `policy check` pass, and the tier still never routes;
OPENAI_BASE_URL carries the required /v1 suffix; the LLM path gets the same
host-authored sections as the offline one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SPIKESPIGEL404
SPIKESPIGEL404 marked this pull request as ready for review August 19, 2026 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant