* fix(auth): propagate credentials to every provider-build surface and pin children to the live provider
Sub-agents, swarm members, ACP sessions, and mid-run escalation switches
could build providers without the credentials the parent was running with.
This consolidates credential resolution so every surface gets the same auth:
- Wrap deps.newProvider once in fillAppDeps to fold the stored API key in
centrally; ACP, exec escalation, and buildProvider no longer patch it
per call site (an apiKeyStored profile previously went out keyless on
those paths and 401'd).
- Export ZERO_PROVIDER at startup (buildProvider) and keep exporting on
every runtime switch (wizard, onboarding, /model, /provider), so spawned
children resolve the parent's live provider instead of whatever another
process last persisted to config.json. Injected via appDeps so tests
never mutate the process environment.
- Share one OAuth login selection end to end: ProviderProfile.
OAuthLoginCandidates (name, then catalog-ID fallback; nothing for a
profile with its own configured credential) + oauth.FirstStored resolve
the login ONCE, and the bound store key is threaded through
providers.Options.OAuthLoginKey so the Codex account-id header always
reads the same login that issued the bearer token. A renamed profile
(name != catalog ID) now finds its login; an explicit API key is never
silently overridden by a same-name or catalog-shared OAuth login.
- Export ProviderProfile.HasConfiguredCredential as the single "profile is
key-authed" predicate (cli/tui/config previously had three copies).
- applyProviderWizard commits live state (provider, profile, name, env
export) only after BOTH the build and the config persist succeed, so a
persist failure no longer leaves the chat running on a provider the
status line and children disagree with.
Covered by red-green tests for each path: stored-key wrap, escalation
switch, catalog-ID fallback (incl. case-variant names), explicit-key
protection, same-login account selection, wizard persist-failure rollback,
onboarding/startup env export.
* test: isolate OAuth/config test storage and fix the flaky MCP response-id test
Review + CI fixes, all test-only:
- factory/cli OAuth tests: pin ZERO_OAUTH_STORAGE=file next to the temp
ZERO_OAUTH_TOKENS_PATH — an inherited "keyring" value sends NewStore to
the OS keychain and ignores the temp path entirely, so the tests could
read (or clobber) the developer's real logins. newOAuthStoreForTest now
owns both pins so a future call site can't forget them.
- TestApplyProviderWizardExportsActiveProviderEnv: point m.userConfigPath
at a temp config and stub newProvider, so the SUCCESS path exercises the
full build+persist+commit sequence in isolation (the default empty path
silently skipped persist; a changed default must never reach the real
user config).
- TestClientRequestWaitsForMatchingResponseID (ubuntu smoke flake): the
responses were pre-loaded into a bytes.Buffer, so the client's reader
goroutine could consume the matching response — and hit EOF — before
request() registered its pending id, failing with "request() error =
EOF" on fast runners. The fake server now sits on pipes and responds
only after reading the outgoing request, matching production ordering;
survives -race -count=50.
Encrypted credential storage
- internal/securefile: AES-256-GCM at-rest crypter (fail-closed, atomic, locked).
- internal/credstore: provider API-key store, keyring-first with an encrypted-file
fallback; plaintext only via an explicit ZERO_CRED_STORAGE=file opt-out.
- Provider API keys no longer live in config.json: captured straight into the store
from the wizard/CLI setup, and any existing inline plaintext key is migrated to the
store on launch (fail-soft; stripped only after the store write succeeds).
- buildProvider and every model/provider switch reload the credential (stored key,
env var, or OAuth bearer), so a stored-key provider always authenticates.
- `zero auth logout <provider>` now also removes the stored API key and its marker.
/model and /provider
- /model lists every authenticated provider (filtered to those with a usable
credential), grouped per provider under section headers.
- Each provider's models are live-discovered (its real served models) rather than a
static catalog; local providers show only the models actually pulled.
- Selecting a model from another provider switches provider and model together.
- /provider offers keep / replace / remove when a provider already has a stored key.
Gated throughout: go build, go vet ./..., go test ./... -race, lint.
* Add provider health diagnostics
* Fix provider health auth review findings
* Fix provider health test handler assertion
* Harden provider health connectivity probes