ADR 0179: Import model configuration from local agent stores
- Status: Accepted for model import semantics; original Settings placement superseded by ADR 0319 / D645 (amended 2026-10-18: Pi auth.json and cached catalog)
- Date: 2026-09-08
- Deciders: PI-Desktop core
- Related: D007, D342, ADR 0012, ADR 0188,
04-ux/06-settings-ia.md,04-ux/08-component-spec.md§18.5,03-runtime/01-ipc-protocol.md,03-runtime/11-provider-model-system.md
Context
At the time this decision was recorded, Settings → Import scanned Claude Code, Codex, OpenCode, and Pi session stores. The same tools keep provider URLs, model ids, and often API keys in well-known files. Users who switch to PI-Desktop otherwise retype those endpoints on Settings → Models.
D007 forbids auto-import of ~/.pi so PI-Desktop owns ~/.pi-desktop. That must stay true for model configuration: a scan is an explicit action, and nothing is written until Import selected.
Secrets cannot cross the renderer. Session import already keeps filePath in the main-process scan cache; model import must keep keys the same way.
OAuth/subscription grants from those tools (Codex ChatGPT login, Claude subscription, OpenCode type: oauth) are not PI-Desktop vendor-account credentials. Copying refresh tokens would be the wrong security boundary.
Decision
The original placement was a Model configuration card under Settings → Import, with its own Scan / selection / Import selected. ADR 0319 / D645 supersedes that placement and retires the Settings session-import UI. The model scan/import semantics below remain in force; plugin session-import contracts remain unchanged.
Sources (same family as session import):
- Claude Code:
~/.claude/settings.jsonplussettings.local.jsonoverlay (env.ANTHROPIC_*,model) - Codex:
~/.codex/config.toml[model_providers.<id>] - OpenCode:
~/.config/opencode/opencode.jsonprovidermap plus~/.local/share/opencode/auth.jsonAPI keys - Pi:
~/.pi/agent/models.json(fallback~/.pi/models.json). Providers connected through Pi's~/.pi/agent/auth.jsonAPI keys (fallback~/.pi/auth.json) are paired with Pi's cached~/.pi/agent/models-store.json(fallback~/.pi/models-store.json) so an auth-only provider still gets its endpoint, model list, and limits. When both files describe one provider,models.jsonstays authoritative. - CC Switch:
~/.cc-switch/cc-switch.dbproviderstable (legacyconfig.json). Each row'ssettings_configis converted by app type. Empty official seeds and OAuth-only rows are omitted. A live Claude / Codex / OpenCode / Pi file that matches a CC Switch endpoint and credential is not listed twice; a different credential remains visible.
- Claude Code:
IPC (Electron only, no host protocol bump):
pi-desktop/modelConfig/importScanreturns public drafts (source,externalId,name,baseUrl,apiStyle,modelIds,hasSecret).importRunlooks the selection up in the latest scan cache and calls hostproviders.create.Secrets. A stored API key,
env:/env_keyresolution, orAuthorization: Bearerheader is copied into the host secret store. Placeholder values (YOUR_API_KEY,${VAR}) are treated as missing. A piapiKeyor auth header written as a!<shell command>is never executed or stored; that provider is imported without a secret. Piauth.jsonentries of typeoauthare skipped entirely; plaintype: "api_key"entries are copied. An unresolvedenv:/$VARreference inmodels.jsonis filled from the matchingauth.jsonkey before import.Idempotence. A candidate whose normalized base URL, API style, and credential match an existing provider is skipped. Different credentials remain independent provider rows; the credential comparison stays in Electron main and never reaches the renderer. Named presets may set
vendorKey; a custom URL stayscustom. ADR 0188 amends this rule.Default model. If
settings.defaultProviderIdis empty after the first successful create in that run, that provider and its first model become the global default. An existing default is never overwritten.
Consequences
- Switching from another local agent can bring both transcripts and the models those transcripts used, without pasting keys into Settings.
- PI-Desktop still does not silently ingest
~/.pi. - Users with only a ChatGPT/Claude subscription still sign in through Vendor accounts; import cannot impersonate that grant.
Alternatives
- Auto-import on first launch: rejected by D007.
- Merge keys into an existing same-URL provider: would overwrite a working row. Skip is safer; the user can edit the Models page.
- Renderer-side
providers.createwith the key in the IPC payload: the scan cache already exists for sessions; keep secrets off the renderer.
Amendment — 2026-10-18: discover Pi auth-only providers
Settings → Models only scanned models.json, so providers connected through pi /login (for example DeepSeek or a Z.AI coding plan) never appeared in the Pi group. The explicit scan now reads auth.json API keys and pairs them with the catalog Pi already cached in models-store.json. OAuth/subscription grants still stay in Pi, !command values are never executed or stored, and a models.json provider remains authoritative when both files describe it.