Ship Paseo-equivalent provider snapshot, AgentComposerBar, ACP dispatch rewrite with streaming/persist, permission prompts, and agent commands. Follow-up: pane-scoped chat resolution, CoderMessageList tool timeline, WS user-delta replace, and inference orphan tool_call stripping. Archive openspec v2-2; update CHANGELOG and CURRENT. Co-authored-by: Cursor <cursoragent@cursor.com>
28 KiB
v2.3 Provider lifecycle — design
Detailed implementation plan for Paseo-style provider registration, readiness probing, and enable/disable toggles in BooCoder.
Audience: Sam + future agents implementing the batch.
Paseo reference: /opt/forks/paseo/packages/server/src/server/agent/ (registry, snapshot manager, generic ACP), /opt/forks/paseo/packages/app/src/screens/settings/providers-section.tsx (UI behavior).
1. Current state vs target
1.1 BooCode today (v2.2)
┌─────────────────┐ startup ┌──────────────────┐
│ provider- │ ───────────────► │ available_agents │ (which, version, models JSONB)
│ registry.ts │ agent-probe │ (Postgres) │
│ (7 hardcoded) │ └────────┬─────────┘
└────────┬────────┘ │
│ │
▼ ▼
┌─────────────────┐ cache miss ┌──────────────────┐
│ getProvider │ ──────────────► │ probeAcpProvider │ (full ACP session, 30s)
│ Snapshot() │ per agent │ per installed │
└────────┬────────┘ └──────────────────┘
│
▼
Omit uninstalled ──► AgentComposerBar never sees them
No enabled flag
status: ready | error only
Key files:
| File | Role |
|---|---|
apps/coder/src/services/provider-registry.ts |
Static PROVIDERS[] |
apps/coder/src/services/agent-probe.ts |
Boot which + DB upsert |
apps/coder/src/services/provider-snapshot.ts |
Cache + cold probe + merge |
apps/coder/src/services/acp-spawn.ts |
Per-agent argv switch |
apps/coder/src/routes/providers.ts |
snapshot + refresh |
apps/web/src/components/AgentComposerBar.tsx |
Picker UI |
1.2 Target (Paseo-aligned, BooCode-native)
┌──────────────────┐
│ Built-in registry│──┐
│ (provider- │ │
│ registry.ts) │ │ merge at boot + on config reload
└──────────────────┘ │
▼
┌──────────────────┐ ┌──────────────────┐
│ /data/coder- │─►│ ResolvedProvider │
│ providers.json │ │ Registry (in-mem) │
└──────────────────┘ └────────┬───────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
agent-probe (fast) getProviderSnapshot dispatch
which + version tier-1: isAvailable generic ACP for
→ available_agents tier-2: cold ACP config entries
enabled filter
│
▼
Always emit entry per registered provider
loading → ready | unavailable | error
Principles copied from Paseo (docs/providers.md in fork):
- Registration ≠ installation — config lists what you want; probe tells you what’s ready.
- Warm until refresh — no TTL re-probe on picker open; explicit
POST /api/providers/refreshonly. - Disabled skips probe —
enabled: false→unavailablewithout spawning. - Config reload replaces registry — no redeploy to add an ACP wrapper.
2. Config file: /data/coder-providers.json
2.1 Location and loading
| Env var | Default | Notes |
|---|---|---|
CODER_PROVIDERS_PATH |
/data/coder-providers.json |
Same bind-mount pattern as SKILLS_ROOT, MCP_CONFIG_PATH |
- BooCoder runs on host systemd — path resolves to
/opt/boocode/data/coder-providers.jsonin dev (add to repo asdata/coder-providers.json+.env.host). - Missing file →
{}(built-ins only, all enabled). - Invalid JSON → log error, fall back to
{}(do not crash boot). - Reload: on
POST /api/providers/configsuccess, orSIGHUPoptional later; v1: restartboocoder.serviceafter manual edit is acceptable for solo use.
2.2 Schema (Zod)
New file: apps/coder/src/services/provider-config.ts
const ProviderOverrideSchema = z.object({
extends: z.enum(['acp']).optional(), // v2.3: only 'acp' for custom; built-ins omit extends
label: z.string().min(1).optional(),
description: z.string().optional(),
command: z.array(z.string().min(1)).min(1).optional(), // [binary, ...args]
env: z.record(z.string()).optional(),
enabled: z.boolean().optional(), // default true
order: z.number().int().optional(), // UI sort key
models: z.array(z.object({ id: z.string(), label: z.string() })).optional(),
additionalModels: z.array(z.object({ id: z.string(), label: z.string() })).optional(),
});
const CoderProvidersFileSchema = z.object({
providers: z.record(ProviderOverrideSchema).default({}),
});
Rules:
| Case | Behavior |
|---|---|
Built-in id (e.g. goose) |
Override merges: enabled, label, command (replace spawn), env |
New id + extends: "acp" |
New registry entry; requires label + command |
New id without extends |
Reject at load with log (v2.3) |
enabled: false on built-in |
Stays in registry; snapshot enabled: false, status unavailable |
| Custom id collision with built-in | Config wins for overrides only; cannot redefine boocode transport |
2.3 Example file (ship in data/coder-providers.json)
{
"providers": {
"goose": { "enabled": true },
"copilot": { "enabled": false },
"amp-acp": {
"extends": "acp",
"label": "Amp",
"description": "ACP wrapper for Amp",
"command": ["amp-acp"],
"enabled": true
}
}
}
2.4 Paseo parity notes
Paseo uses ~/.paseo/config.json under agents.providers with the same fields (extends, command, enabled, models, …). We intentionally use a repo-adjacent data file instead of dotfile — matches AGENTS.md / skills layout and survives container/host split (coder reads host path).
3. Resolved provider registry
3.1 New module: provider-config-registry.ts
Responsibility: Single in-memory source of truth after merge.
export interface ResolvedProviderDef extends ProviderDef {
id: string;
enabled: boolean;
isBuiltin: boolean;
isCustomAcp: boolean;
/** Full argv for spawn: [binary, ...args] */
launchCommand: [string, ...string[]] | null;
env: Record<string, string> | undefined;
configLabel?: string;
configDescription?: string;
}
export function buildResolvedRegistry(
builtins: ProviderDef[],
config: CoderProvidersFile,
): Map<string, ResolvedProviderDef>;
export function loadProviderConfig(path: string): CoderProvidersFile;
export function reloadProviderConfig(): void; // called after PATCH
Merge algorithm (mirror Paseo buildProviderRegistry / addDerivedProviders):
- For each built-in in
PROVIDERS:- Apply config override if present
enabled = override.enabled !== falselaunchCommand= override.command ?? default fromacp-spawn+install_pathat dispatch time
- For each config key not in built-ins:
- Require
extends: "acp",label,command - Insert as
isCustomAcp: true,transport: 'acp',modelSource: 'probe'
- Require
boocodealways enabled; ignoreenabled: falsewith warn log
Consumers: agent-probe, provider-snapshot, dispatcher, acp-dispatch, routes.
3.2 agent-probe changes
File: apps/coder/src/services/agent-probe.ts
- Iterate
getResolvedProviderIds()instead ofPROBED_AGENT_NAMESonly. - For custom ACP: probe
command[0]viawhich(not agent name). - Upsert
available_agentsfor custom ids (new rows). - Store
label,transport: 'acp'from resolved def. - Skip probe entirely when
enabled: false(optional: delete row or keep stale — keep row, setinstall_path nullon disable refresh).
3.3 Schema migration (optional column)
File: apps/coder/src/schema.sql
ALTER TABLE available_agents ADD COLUMN IF NOT EXISTS enabled BOOLEAN NOT NULL DEFAULT true;
ALTER TABLE available_agents ADD COLUMN IF NOT EXISTS source TEXT DEFAULT 'builtin';
-- source: 'builtin' | 'config'
Mirror enabled from config on each probe pass. Custom providers get source = 'config'.
Alternative (simpler v2.3.0): don’t add DB column; read enabled only from in-memory registry at snapshot time. DB holds install facts only. Prefer this for phase 1; add column if settings page needs to show state after coder restart without re-reading JSON.
4. Snapshot lifecycle
4.1 Type changes
Files: apps/coder/src/services/provider-types.ts, apps/web/src/api/types.ts
export type ProviderSnapshotStatus = 'loading' | 'ready' | 'unavailable' | 'error';
export interface ProviderSnapshotEntry {
name: string;
label: string;
description?: string;
transport: string;
status: ProviderSnapshotStatus;
enabled: boolean;
installed: boolean; // binary found on last fast probe
models: ProviderModel[];
modes: ProviderMode[];
defaultModeId: string | null;
commands: AgentCommand[];
error?: string;
fetchedAt?: string; // ISO — when tier-2 probe completed
}
Restore unavailable (removed in stale cleanup — intentional regression for this batch).
4.2 buildProviderEntry rewrite
File: apps/coder/src/services/provider-snapshot.ts
Stop returning null for uninstalled. Always return an entry for every resolved registry id.
for each resolvedProvider:
if !enabled:
return { status: 'unavailable', enabled: false, installed: false, models: [], ... }
if native boocode:
return { status: 'ready', enabled: true, installed: true, models: llamaSwap, ... }
fast = agentRow?.install_path != null // or isCommandAvailable(launchCommand[0])
if !fast:
return { status: 'unavailable', enabled: true, installed: false, models: [], modes: manifest, commands: manifest }
if tier2_skipped: // see §4.3
return { status: 'ready', enabled: true, installed: true, models: from DB, modes: manifest or DB, ... }
cold ACP probe:
ok → ready + models/modes/commands merge
fail → error + error message
4.3 Two-tier probe (implements deferred work §2)
Tier 1 — fast (always on cold read if enabled + installed):
async function isProviderAvailable(resolved: ResolvedProviderDef, agentRow: AgentRow): Promise<boolean> {
if (resolved.isNative) return true;
if (agentRow?.install_path) return true;
if (resolved.launchCommand) return isCommandAvailable(resolved.launchCommand[0]);
return false;
}
New util: apps/coder/src/services/command-availability.ts — which-style check (lift idea from Paseo utils/executable.ts, ~20 lines, no full port).
Tier 2 — slow (ACP session):
Run only when:
| Condition | Action |
|---|---|
force === true (POST /refresh) |
Always cold probe installed enabled providers |
last_probed_at older than PROVIDER_PROBE_TTL_MS (default 24h, env override) |
Cold probe |
| DB models empty AND installed | Cold probe |
| Otherwise | Use available_agents.models + manifest modes/commands |
Env: PROVIDER_PROBE_TTL_MS default 86400000 (24h). Paseo uses warm-forever until refresh; 24h is a homelab compromise so stale model lists self-heal.
Paseo contract (adopt explicitly):
- Opening
AgentComposerBardoes not call refresh or force probe. POST /api/providers/refreshclears cache + forces tier-2 for home cwd.- Document in
BOOCODER.md.
4.4 Loading state
On cache miss, before async probe completes:
- Return entries with
status: 'loading'immediately (sync). - Singleflight inflight map (already exists) — on completion, flip to terminal status + emit…
Tier 2 optional: WS frame provider_snapshot_updated — defer to follow-up; v2.3 can rely on client polling 2s while any entry loading (CoderPane already polls when WS disconnected; extend: poll while snapshot has loading).
4.5 Cache keys
Keep cwd-keyed cache (resolvedCwd = cwd ?? homedir()). Settings UI uses snapshot with no cwd or explicit cwd=~ — same as Paseo home-directory snapshot for provider management.
5. Generic ACP dispatch
5.1 Problem
acp-spawn.ts switch grows with every agent. Custom config entries cannot dispatch today.
5.2 Solution
File: apps/coder/src/services/acp-spawn.ts
export function resolveLaunchSpec(
resolved: ResolvedProviderDef,
installPath: string | null,
): { binary: string; args: string[]; env?: Record<string, string> } | null {
if (resolved.launchCommand) {
return {
binary: resolved.launchCommand[0],
args: resolved.launchCommand.slice(1),
env: resolved.env,
};
}
// built-in fallback
const args = resolveAcpSpawnArgs(resolved.id);
if (!args || !installPath) return null;
return { binary: installPath, args, env: resolved.env };
}
File: apps/coder/src/services/acp-dispatch.ts
- Replace
resolveAcpSpawnArgs(agent)+spawn(installPath, args)withresolveLaunchSpec(resolved, installPath). - Merge
envinto spawnenv: { ...process.env, ...spec.env }. - Dispatcher loads resolved def by task.agent name.
Do not port Paseo GenericACPAgentClient class — keep procedural dispatch + existing acp-stream.ts.
6. HTTP API
File: apps/coder/src/routes/providers.ts
| Method | Path | Body | Response |
|---|---|---|---|
| GET | /api/providers/snapshot?cwd= |
— | ProviderSnapshotEntry[] (unchanged path) |
| POST | /api/providers/refresh |
{ providers?: string[] } optional |
{ refreshed: number } — if providers set, refresh subset only (Paseo pattern) |
| GET | /api/providers/config |
— | { providers: Record<string, ProviderOverride> } |
| PATCH | /api/providers/config |
partial providers map | merged file written, registry reload, { ok: true } |
| GET | /api/providers/:id/diagnostic |
— | { diagnostic: string } Tier 2 |
PATCH semantics: shallow merge at top level per provider id (same as Paseo patchConfig). Writing enabled: false triggers registry reload + snapshot reconcile (mark unavailable without probe).
Proxy: BooChat server may proxy /api/coder/providers/* — check apps/server/src/index.ts coder proxy prefix; add config routes if missing.
Web client: apps/web/src/api/client.ts
coder: {
snapshot: ...
refreshProviders: (providers?: string[]) => ...
getProviderConfig: () => ...
patchProviderConfig: (patch) => ...
getProviderDiagnostic: (id) => ...
}
7. Web UI
7.1 Settings: Provider management drawer
New: apps/web/src/components/coder/ProviderSettingsDrawer.tsx (or section under existing settings)
Behavior lifted from Paseo providers-section.tsx:
| UI element | Action |
|---|---|
| Row per registered provider | Label, status dot, model count |
| Switch | PATCH config { [id]: { enabled } } |
| Refresh icon | POST /api/providers/refresh |
| Add provider | Opens catalog modal |
| Row click | Diagnostic sheet (optional phase 2) |
Status labels: Disabled · Loading · Available · Not installed · Error
Entry point: link from AgentComposerBar (gear icon) or CoderPane header.
7.2 AgentComposerBar filter
File: apps/web/src/components/AgentComposerBar.tsx
const selectable = entries.filter(
(e) => e.enabled && e.status === 'ready' && e.models.length > 0
);
// boocode: allow ready with empty models if llama-swap down? keep current fallback
Show subtitle when current provider becomes unavailable (toast + reset to boocode).
7.3 Add provider modal
New: apps/web/src/data/acp-provider-catalog.ts
Curated entries (start with 5–10 you might install):
| id | command | installLink |
|---|---|---|
| amp-acp | ["amp-acp"] |
github amp-acp |
| cline | ["npx","-y","cline@…","--acp"] |
cline.bot |
| pi-acp | from fork | … |
Copy structure from Paseo acp-provider-catalog.ts + buildAcpProviderConfigPatch — trim versions aggressively; pin only when you’ve verified on homelab.
Modal: search, Install → patchProviderConfig(buildPatch(entry)) → refreshProviders([entry.id]).
Do not port: React Native components, remote SVG icon pipeline — use lucide fallback icon.
7.4 Loading UX
While any entry status === 'loading', show spinner in composer provider dropdown; optional 2s poll until terminal state (reuse CoderPane poll pattern).
8. Diagnostics (Tier 2 in batch — lightweight)
Paseo getDiagnostic() runs version probe + short ACP initialize. For solo debugging:
File: apps/coder/src/services/provider-diagnostic.ts
export async function getProviderDiagnostic(
resolved: ResolvedProviderDef,
agentRow: AgentRow | undefined,
cwd: string,
): Promise<string> {
// Plaintext report:
// - enabled, installed, binary path
// - last_probed_at, model count from DB
// - optional: 8s ACP initialize probe (reuse acp-probe with shorter timeout)
}
No need for Paseo diagnostic-utils.ts formatting library — a template string is fine.
9. Testing strategy
| Test | File |
|---|---|
| Config load + merge | provider-config-registry.test.ts |
| Snapshot: disabled → unavailable, no probe mock call | extend provider-snapshot.test.ts |
| Snapshot: uninstalled → unavailable, installed true/false | same |
| Tier-2 skip when fresh DB models | same |
| force refresh calls probe | same |
| PATCH config writes file | routes/providers.test.ts (optional integration) |
| resolveLaunchSpec custom command | acp-spawn.test.ts |
Run: pnpm -C apps/coder test, npx tsc -p apps/web/tsconfig.app.json --noEmit.
Smoke:
curl http://100.114.205.53:9502/api/providers/snapshot
curl -X PATCH http://100.114.205.53:9502/api/providers/config -d '{"providers":{"goose":{"enabled":false}}}'
curl -X POST http://100.114.205.53:9502/api/providers/refresh
10. Implementation phases
Phase 1 — Config + registry (backend only)
provider-config.ts,provider-config-registry.tsdata/coder-providers.json+CODER_PROVIDERS_PATH- Wire
agent-probeto resolved ids - Unit tests
Exit: custom entry in JSON → row in available_agents after restart.
Phase 2 — Snapshot lifecycle
- Types:
loading,unavailable,enabled - Rewrite
buildProviderEntry(never omit) - Tier-1 fast availability
- Tier-2 skip when DB fresh
- Restore warm-cache + force refresh semantics
Exit: disabled goose visible in API as unavailable; picker filters it out.
Phase 3 — Generic dispatch
resolveLaunchSpec- Dispatcher passes resolved def
- Smoke: dispatch task for config-only provider (amp-acp if installed)
Phase 4 — HTTP config API
- GET/PATCH config
- Reload registry on PATCH
- Subset refresh
Phase 5 — Web UI
- Provider settings drawer + toggle
- AgentComposerBar filter
- Catalog modal (minimal list)
Phase 6 — Docs + deploy
BOOCODER.mdsection: Provider configCHANGELOG.mdentrydocs/DEFERRED-WORK.md— mark cold-probe item resolvedpnpm -C apps/coder build && sudo systemctl restart boocoder
11. Tier 2 follow-ups (document, don’t build in v2.3)
| Item | Paseo source | When |
|---|---|---|
WS provider_snapshot_updated |
ProviderSnapshotManager EventEmitter |
When loading poll feels hacky |
MCP list_providers / inspect_provider |
mcp-server.ts |
When BooCoder MCP orchestration matures |
Profile overrides (extends: "claude") |
provider-registry.ts derived providers |
When you run Z.AI / multi-endpoint |
order field UI sort |
config schema | When catalog >10 entries |
| Per-workspace snapshot in picker | cwd param | Already partial — verify project path passed from CoderPane |
12. Tier 3 reference — what Paseo has and why we don’t port it
This section is reference only. These are large subsystems in /opt/forks/paseo that solve problems BooCode doesn’t have at solo scale, or that BooCode already solved differently.
12.1 ACPAgentClient base class (~2,800 lines)
Path: packages/server/src/server/agent/providers/acp-agent.ts
What it does: Full ACP lifecycle — spawn, initialize, session/new, streaming, permissions, tool calls, MCP injection, revert, persisted agent import, probe sessions.
Why Paseo needs it: Paseo is the primary runtime for dozens of providers; one abstraction reduces duplication across copilot, cursor, generic ACP, etc.
Why BooCode skips it: acp-dispatch.ts + acp-stream.ts + acp-probe.ts already cover dispatch and probe as scripts (~400 lines total). Replacing with the class hierarchy is a multi-week rewrite with high regression risk on v2.2 dispatch that works on homelab.
What we take instead: Patterns only — isAvailable() = resolve binary; permission waiter (already shipped); derive models/modes (already shipped).
12.2 Per-provider client classes (claude, codex, opencode, pi, copilot, cursor…)
Paths: packages/server/src/server/agent/providers/*/agent.ts, codex-app-server-agent.ts (5,000+ lines)
What they do: Native SDK/RPC integration — not just CLI spawn. Codex uses app-server RPC; Claude uses Claude Agent SDK; OpenCode manages a sidecar server.
Why Paseo needs it: Deep integration — voice, revert, persisted sessions, feature toggles, OAuth diagnostics.
Why BooCode skips it: BooCode delegates to existing CLIs in worktrees. No embedded SDKs. PTY path for claude/qwen is stdin pipe; ACP path uses @agentclientprotocol/sdk at dispatch boundary only.
Lift risk: Importing codex-app-server-agent would drag thousands of lines + unknown deps.
12.3 ProviderSnapshotManager class (full port)
Path: packages/server/src/server/agent/provider-snapshot-manager.ts
What it does: Per-cwd Maps, loading states, singleflight, event emitter, reconcile on registry replace, settings vs workspace refresh split.
Why not full port: BooCode’s provider-snapshot.ts is ~250 lines and already has cache + inflight. Selective lift: loading status, reconcile on config reload, subset refresh — not a class-for-class rewrite.
12.4 React Native settings app (packages/app)
Paths: providers-section.tsx, add-provider-modal.tsx, use-providers-snapshot.ts
What it does: Mobile/desktop cross-platform provider UI with Unistyles, native Switch, adaptive sheets.
Why BooCode skips it: BooChat is React web + Tailwind. Port interaction design (toggle, status dots, add flow), not components.
12.5 Daemon config system (patchConfig, migrations, Zod wire messages)
Path: packages/server/src/shared/messages.ts (4000+ lines), daemon config patch RPC
What it does: Every settings change is a typed WS/HTTP patch to daemon with validation, persistence, broadcast.
Why BooCode simplifies: Single-user — PATCH writes JSON file + reloads in-process Map. No multi-client sync requirement. If BooChat and CLI both edit, last-write-wins on file is acceptable.
12.6 Full ACP catalog (30+ providers, version-pinned npx)
Path: packages/app/src/data/acp-provider-catalog.ts (~400 lines)
Why trim: Maintenance burden — every upstream version bump is a PR in Paseo. Solo homelab: 5–10 entries you actually install, update when you install.
12.7 Voice provider stack
Path: packages/server/src/server/speech/*
Why skip: BooCode has no voice surface; unrelated to coder provider lifecycle.
12.8 Workspace git service inside agents
Path: Codex client integration with WorkspaceGitService
Why skip: BooCode worktrees (worktrees.ts) are explicit per-task; agents run in worktree cwd. Different architecture.
12.9 OpenCode server manager sidecar
Path: packages/server/src/server/agent/providers/opencode/server-manager.ts
What it does: Manages long-lived OpenCode server process.
Why skip: BooCode spawns opencode acp per dispatch — stateless, simpler, good enough for single user.
12.10 Pi RPC agent + session import from JSONL
Paths: packages/server/src/server/agent/providers/pi/agent.ts (1,500+ lines)
Why skip until needed: Only lift if you add pi as a built-in with import/revert requirements. Otherwise generic ACP + extends: "acp" + pi-acp catalog entry suffices.
12.11 Summary table
| Paseo subsystem | Lines (approx) | BooCode v2.3 approach |
|---|---|---|
| ACPAgentClient | 2,800 | Keep acp-dispatch |
| Codex app server agent | 5,500 | Don't import |
| Provider registry merge | 700 | New 200-line module |
| Snapshot manager | 490 | Extend existing snapshot |
| Generic ACP agent | 300 | resolveLaunchSpec only |
| RN providers UI | 400 | Web drawer ~200 lines |
| MCP list_providers | 200 | Defer |
| Config wire protocol | 4,000+ | JSON file PATCH |
Rule of thumb for solo project: Lift data models and lifecycle rules, not class hierarchies.
13. Risk register
| Risk | Mitigation |
|---|---|
| Custom npx provider slow cold start | Show loading; subset refresh; don’t block picker on whole snapshot |
| Config file edit while coder running | PATCH API primary; manual edit requires restart (document) |
enabled: false but task in flight |
Allow running task to finish; block new sends (picker filter) |
| Type drift web/coder | Update both provider-types.ts and api/types.ts; optional zod parity test |
| Security: arbitrary command in config | Single-user trusted path; same trust as AGENTS.md — no app-layer auth |
| Re-enabling cold probe slowness on refresh | Expected; refresh is explicit user action |
14. File map (new + touched)
| Action | Path |
|---|---|
| New | apps/coder/src/services/provider-config.ts |
| New | apps/coder/src/services/provider-config-registry.ts |
| New | apps/coder/src/services/command-availability.ts |
| New | apps/coder/src/services/provider-diagnostic.ts |
| New | apps/coder/src/services/__tests__/provider-config-registry.test.ts |
| New | data/coder-providers.json |
| New | apps/web/src/data/acp-provider-catalog.ts |
| New | apps/web/src/components/coder/ProviderSettingsDrawer.tsx |
| New | apps/web/src/components/coder/AddProviderModal.tsx |
| Edit | apps/coder/src/services/provider-snapshot.ts |
| Edit | apps/coder/src/services/agent-probe.ts |
| Edit | apps/coder/src/services/acp-spawn.ts |
| Edit | apps/coder/src/services/acp-dispatch.ts |
| Edit | apps/coder/src/services/dispatcher.ts |
| Edit | apps/coder/src/routes/providers.ts |
| Edit | apps/coder/src/config.ts — CODER_PROVIDERS_PATH |
| Edit | apps/coder/.env.host |
| Edit | apps/coder/src/services/provider-types.ts |
| Edit | apps/web/src/api/types.ts |
| Edit | apps/web/src/api/client.ts |
| Edit | apps/web/src/components/AgentComposerBar.tsx |
| Edit | BOOCODER.md |
| Edit | docs/DEFERRED-WORK.md |
15. Attribution
Design patterns from Paseo (/opt/forks/paseo), especially:
provider-registry.ts— merge built-ins + config +enabledprovider-snapshot-manager.ts— loading/unavailable/ready lifecycleprovider-launch-config.ts— override schemaproviders-section.tsx— settings UXpublic-docs/custom-providers.md— config file semantics
BooCode implementation remains original code — no copy-paste of Paseo sources required; licensing treated as irrelevant per project owner directive.