docs(agent): update models phase 9 plan

This commit is contained in:
Mario Zechner 2026-06-24 14:45:50 +02:00
parent 3e551faf79
commit c29bbc0958

View file

@ -860,20 +860,21 @@ Check items off as they land. Keep this list current; it is the working state fo
coding-agent replaces AuthStorage and ModelRegistry's internals with `FileCredentialStore` + a `MutableModels` collection. AgentSession itself stays (AgentHarness adoption is pi 2.0); only its model/auth substrate swaps. Layering is strictly one-directional:
```txt
FileCredentialStore (auth.json, locked) + --api-key overlay + $ENV/!command resolution
FileCredentialStore (auth.json, locked, $ENV/!command resolution) + explicit --api-key overlay
MutableModels: builtin factories (wrapped per models.json config) + custom providers (models.json extensions)
ModelRegistry: async facade — reads delegate to the collection; registerProvider/login/logout/status for extensions + UI
ModelRegistry: compatibility facade — sync last-known reads delegate to the collection; registerProvider/login/logout/status for extensions + UI
AgentSession / sdk / interactive-mode (await added; stream via models)
AgentSession / sdk / interactive-mode (stream via models; await only auth/refresh paths)
```
Decisions:
- `AuthStorage` is deleted as a type — it would otherwise depend on provider auth while provider auth depends on its store (circular). Its surface splits: `get`/`set`/`remove` -> `CredentialStore`; `getApiKey` -> `Models.getAuth`; `login`/`logout`/`getAuthStatus` -> ModelRegistry facade methods over `provider.auth.oauth` + the store.
- Runtime `--api-key` overrides are a store overlay (an override reads as an ephemeral stored api-key credential, masking stored OAuth — matches today's priority). Every registered provider is guaranteed an `apiKey` auth slot so overrides apply to OAuth-only providers too.
- `ModelRegistry.getAll`/`find`/`getAvailable` become async, delegating to the collection (no materialized snapshot, no sync lies; dynamic providers like llama.cpp work). The extension-facing `modelRegistry` surface changes accordingly (breaking, changelogged); extensions also get the collection itself as the forward API.
- `FileCredentialStore` is self-contained (path, locking, parse/write, chmod, error buffering) and owns `auth.json` semantics, including `$ENV`/`!command` resolution for stored API-key credentials. Persisted values stay raw; resolution returns copies for auth use.
- Runtime `--api-key` overrides are an explicit store overlay (an override reads as an ephemeral stored api-key credential, masking stored OAuth — matches today's priority). Every registered provider is guaranteed an `apiKey` auth slot so overrides apply to OAuth-only providers too.
- `ModelRegistry.getAll`/`find`/`getAvailable` stay sync for SDK and extension compatibility, delegating to the collection's last-known sync model lists and fast configured-looking status checks. Dynamic providers update through explicit async `refresh()`, and request auth remains async through `getApiKeyAndHeaders()`/`Models.getAuth()`. Extensions also get the collection itself as the forward API.
- models.json keeps FULL feature parity, implemented as provider decoration: builtin factories wrapped so `getModels()` applies provider `baseUrl`/`compat` overlays, `modelOverrides`, and custom-model merges (async-safe); provider `apiKey`/`headers`/`authHeader` configs become that provider's `ApiKeyAuth` (config first, factory auth fallback); parse errors keep `getError()` semantics.
- Extension `ProviderConfig` parity: provider-keyed `streamSimple`, old-style `oauth` adapted to `OAuthAuth` (`modifyModels` -> `getModels` wrap + `toAuth`), full model replacement per provider. Legacy `registerApiProvider` writes stay compat-local for consumers that call global `complete()`; they die with compat.
- Copilot: stored-credential baseUrl applied in the wrapped `getModels()` (extension-visible models stay correct) plus per-request `toAuth().baseUrl`.
@ -884,14 +885,15 @@ Ordering for new sessions:
1. [x] pi-ai rework first: `Provider.getModels()` sync + optional `refreshModels()`; `Models.getModels`/`getModel` sync, `Models.refresh(provider?)` async; `createProvider` takes `models` array + optional `refreshModels` fetcher (in-flight dedupe). Reverses Phase 1's async-listing decision — see "Provider model listing" for rationale (sync-or-async unions breed latent sync assumptions; async-only breaks sync consumer surfaces like extension `find`/`getAll`).
2. [x] Cloudflare provider auth in pi-ai factories: Workers AI and AI Gateway validate their required account/gateway env/config and return resolved `baseUrl`, provider-scoped env, and header suppression/override metadata from provider auth.
3. [ ] Add `FileCredentialStore` in coding-agent.
- Implement the pi-ai `CredentialStore` interface over the existing `auth.json` lock backend (`FileAuthStorageBackend` / `InMemoryAuthStorageBackend` can be reused or renamed).
- Implement the pi-ai `CredentialStore` interface as a self-contained `auth.json` store; do not depend on the old `AuthStorageBackend` abstraction, though its lock/retry semantics may be ported.
- Preserve the existing file format. `ApiKeyCredential` uses `{ type: "api_key", key?, env? }`, matching today's `auth.json`; do not translate `env` into metadata or rewrite discriminators.
- Resolve `$ENV`/`!command` in stored API-key `key` and `env` values out of the box using an injected execution/config environment. `$ENV` lookup should come from that environment, and `!command` should run through the shared shell execution path rather than direct `execSync`.
- Persist raw config values; resolved credentials returned for auth use must be copies and must not rewrite `$ENV`/`!command` strings unless a caller explicitly stores new values.
- `read(provider)` returns the current credential snapshot and records parse/storage errors for status UI parity.
- `modify(provider, fn)` must lock, re-read, run `fn`, merge-write the provider entry, chmod `0600`, and return the post-write credential.
- `delete(provider)` must lock and remove only that provider's entry.
- Add file-backed and in-memory tests covering lock/RMW behavior, `api_key` reads, OAuth reads, provider `env` preservation, delete, parse errors, and concurrent refresh-style modifications.
4. [ ] Add store decorators for coding-agent policy.
- `withConfigValues(store, policy)` resolves stored API-key credentials whose `key` or `env` values use `$ENV` or `!command`, using existing `resolve-config-value.ts` semantics. Command execution stays in coding-agent, not pi-ai.
- Add file-backed and in-memory tests covering lock/RMW behavior, `api_key` reads with config-value resolution, OAuth reads, provider `env` preservation, delete, parse errors, and concurrent refresh-style modifications.
4. [ ] Add runtime override overlay for coding-agent policy.
- `withRuntimeOverrides(store, overrides)` implements CLI `--api-key`: read returns an ephemeral `{ type: "api_key", key }` for each overridden provider, masking stored OAuth/API credentials without persisting.
- Runtime overrides must apply even to OAuth-capable providers; every provider registered in coding-agent must retain or gain an `apiKey` auth slot so the overlay is meaningful.
- Tests cover precedence: runtime override > stored credential > models.json config auth > ambient provider env, with stored credential blocking ambient fallback.
@ -916,21 +918,21 @@ Ordering for new sessions:
- Preserve existing extension runtime compatibility through the `/compat` alias until Phase 10.
8. [ ] Rebuild coding-agent `ModelRegistry` over `MutableModels`.
- It owns a `MutableModels` instance built from decorated built-ins + models.json custom providers + extension providers.
- `getAll()`, `find()`, and `getAvailable()` become async and delegate to the collection/status checks. Update extension-facing types and changelog this breaking change.
- `refresh()` rebuilds provider layers and calls `models.refresh()` where needed; no global api-registry reset should be part of the new path except compat-only grace behavior.
- `getAll()`, `find()`, and `getAvailable()` remain sync compatibility methods over last-known model lists and fast configured-looking auth status. Do not break the extension-facing `modelRegistry` surface for these reads.
- `refresh()` is the explicit async freshness boundary: rebuild provider layers and call `models.refresh()` where needed; no global api-registry reset should be part of the new path except compat-only grace behavior.
- `registerProvider()`/`unregisterProvider()` mutate provider layers and rebuild the collection.
- Facade auth ops (`login`, `logout`, provider status, available OAuth providers) drive `provider.auth.{apiKey,oauth}` and the `CredentialStore`; no `AuthStorage` type remains.
- Legacy `registerApiProvider` writes stay only for `/compat` callers and are removed in Phase 10.
9. [ ] Rewire consumers.
- `AgentSession` stream function resolves through `ModelRegistry`/`Models`, not `getApiKeyAndHeaders()` + compat globals.
- SDK options replace `authStorage` with `credentials?: CredentialStore` or an agent-dir-backed default; update `sdk.md` and examples.
- `model-resolver`, `--list-models`, model selector, login/logout/status UI, and provider attribution await async model/auth APIs.
- `model-resolver`, `--list-models`, model selector, login/logout/status UI, and provider attribution use sync last-known model reads and await only explicit refresh/auth operations.
- CLI `--api-key` populates the runtime override decorator instead of mutating `AuthStorage`.
- Keep extension loader root-to-compat alias until Phase 10, but expose the new collection/facade as the forward API.
10. [ ] Test migration and real-provider validation.
- Unit tests for `FileCredentialStore`, config-value decorators, provider decoration, extension OAuth adapter, ModelRegistry async facade, and consumer rewiring.
- Unit tests for `FileCredentialStore`, runtime override overlay, provider decoration, extension OAuth adapter, Models-backed ModelRegistry facade, and consumer rewiring.
- Regression tests for Cloudflare account/gateway env, Copilot OAuth baseUrl wrapping, runtime `--api-key` precedence, `$ENV`/`!command` resolution, and stored credential blocking ambient fallback.
- Update existing tests that assume sync `ModelRegistry.getAll/find/getAvailable`.
- Update existing tests for sync last-known `ModelRegistry.getAll/find/getAvailable` plus explicit async refresh behavior.
- Run targeted non-e2e suites plus tmux validation of login flows against real providers (Anthropic OAuth/API key, OpenAI Codex OAuth, GitHub Copilot OAuth, Cloudflare AI Gateway, Bedrock if credentials are available).
### Phase 10 — compat deletion (pi 2.0 era, separate)