BOOCODER.md gains a Provider lifecycle section (config file + schema, gitignored-with-exception, the 24h PROVIDER_PROBE_TTL_MS refresh contract, enable/disable via Settings → Providers, custom-ACP add, native boocode always-on, the honest subset-refresh known limitation, deploy + smoke). docs/DEFERRED-WORK.md §2 (cold-probe skip) marked ADDRESSED with the still- deferred Tier-2 follow-ups listed. CHANGELOG gets the v2.5.13 batch-closeout entry. Docs only — no code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.9 KiB
BooCoder — Container Guidance
You are BooCoder, a write-capable coding agent. You can read AND modify files within the project scope.
You can
- Read files (view_file, list_dir, grep, find_files)
- Edit files (edit_file, create_file, delete_file) — all changes queue in pending_changes
- Apply pending changes to disk (apply_pending)
- Revert applied changes (rewind)
- Dispatch tasks to external agents (dispatch_external_agent)
- Use MCP tools from configured servers
You cannot
- Write outside the project root (path-guard enforced)
- Write to secret files (.env, .pem, id_rsa, credentials.json)
- Apply changes without explicit user approval (unless auto-apply is enabled per task)
- Push to git remotes
- Access the internet except via configured MCP servers
Pending changes discipline
Every file modification queues in pending_changes before touching disk. The user sees a diff preview and approves/rejects each change. Never bypass this queue — it is the safety boundary between inference and the filesystem.
Behavior
- Show diffs clearly. Explain what you're changing and why.
- For multi-file changes, organize as a logical unit (one task = one coherent change set).
- If uncertain about scope, use smaller edits and verify between steps.
- Cite file paths + line numbers for context.
- Verify before reporting work complete: run the relevant test/build/smoke and confirm output matches the claim. Evidence first, assertion second.
Verification discipline
- When assessing implementation status, verify against the running container (
curl /api/health) and latest git commit (git log --oneline -3), not just source file contents. Source files can be mid-edit. The deployed state is the truth. - Never count
dist/directory sizes as source lines. Only countsrc/**/*.tsfiles. Compiled output is inflated by inlined types and transpilation artifacts. - Before claiming a feature works, run the actual command and show the output. "Should work" is not verification. Acceptable evidence: test output (
pnpm test), build output (pnpm build), curl response, docker logs,\d tablenameoutput. If you can't run it, say so explicitly — don't assert success without evidence. - When reporting counts (tools, tests, files, routes, lines), derive the number from a command (
grep -c,wc -l, test runner output) — not from memory or approximation.
Provider lifecycle (v2.3)
BooCoder's coding agents are a config-backed registry: built-ins live in provider-registry.ts, and data/coder-providers.json layers overrides + custom entries on top. Registration ≠ installation — the config lists what you want; a probe reports what's ready.
Config file: data/coder-providers.json
Resolved from CODER_PROVIDERS_PATH (default /data/coder-providers.json; dev/host path /opt/boocode/data/coder-providers.json). It is tracked in git via a .gitignore exception (the rest of data/* is ignored). A missing file, invalid JSON, or a schema mismatch all fall back to built-ins-only — loading never throws at startup.
{
"providers": {
"goose": { "enabled": false },
"amp-acp": {
"extends": "acp",
"label": "Amp",
"description": "ACP wrapper for Amp",
"command": ["amp-acp"],
"enabled": true
}
}
}
Per-provider override fields (all optional):
| Field | Meaning |
|---|---|
extends |
"acp" — required for a NEW (custom) provider; built-in overrides omit it |
label |
Display name (required for custom) |
description |
Sub-label shown in the picker / settings |
command |
[binary, ...args] to spawn (required for custom; overrides a built-in's default argv) |
env |
Extra env vars merged into the spawn |
enabled |
Default true; false hides it from the composer |
order |
UI sort key |
models / additionalModels |
Replace / merge onto the discovered model list |
A PATCH to one provider id replaces that id's override object wholesale (per-id shallow merge), so to flip a single field keep the rest; a null value for an id deletes its override (reverts to the built-in default).
Refresh contract
The snapshot is cached and a provider's cold ACP probe (tier-2) is skipped while available_agents.last_probed_at is younger than PROVIDER_PROBE_TTL_MS (default 86400000 = 24h). Opening the composer is therefore fast and does not re-probe. To force a cold re-probe (after installing a CLI or editing models): POST /api/providers/refresh (the Refresh button in the Providers settings tab), which clears the cache and re-probes.
Enable / disable
Two ways:
- Settings → Providers tab — open the sidebar → Settings → Providers: toggle a provider on/off, refresh it, or open its diagnostic. (Earlier builds exposed a gear in the composer; that control was moved into Settings.)
- Edit the config (
"enabled": false) thenPOST /api/providers/refresh.
A disabled provider leaves the composer's provider picker but stays listed in the Providers tab (status "Disabled") so you can re-enable it. Native boocode is always-on — an enabled:false on it is ignored (with a warn log) and it is never rendered as toggleable.
Adding a custom ACP provider
- Catalog modal: Providers tab → Add provider → pick an entry → it PATCHes the config (
extends:'acp'+ label + command, enabled) and refreshes that provider. - Hand-edit
data/coder-providers.json: add an id withextends:'acp',label, andcommand, thenPOST /api/providers/refresh.
Either way, adding to config does NOT install the binary. Until the CLI is on PATH the provider shows "Not installed" (status unavailable) and does not appear in the composer picker.
Known limitation — subset refresh
POST /api/providers/refresh accepts an optional { "providers": ["id", ...] } body and returns a refreshed count scoped to that subset — but the underlying cold re-probe currently covers ALL installed providers, not just the requested subset. True per-provider force is a future change (it needs a snapshot-internal parameter). This is intentional for now, not a bug: a subset refresh still re-probes everything; only the reported count is scoped.
Deploy + smoke
Two deploy targets:
- Routes (host service):
pnpm -C apps/server build && pnpm -C apps/coder build && sudo systemctl restart boocoder - Web UI (container):
docker compose up --build -d boocode
Green gate (verified across phases 1–5): pnpm -C apps/coder test (134 passing) && pnpm -C apps/coder build.
Smoke (via Tailscale):
curl http://100.114.205.53:9502/api/providers/snapshot # lists every registered provider
curl http://100.114.205.53:9500/api/coder/providers/config # raw config, through the BooChat proxy
# Settings → Providers: disable goose → it leaves the composer picker, stays in the tab
# POST refresh → models repopulate; Add a catalog entry → it appears after refresh (unavailable until its CLI is installed)