Skip to content

[Tracking] Explicit multi-account provider connections across Runtime Host surfaces #3852

Description

@me2seeks

Problem

Maka's storage and execution model can already represent multiple Connection entities for one providerType, with credentials isolated by connectionId. Product support is incomplete and inconsistent:

  • Desktop can create suffixed API-key connections such as openai-2, but it orchestrates catalog creation, credential storage, and discovery as separate Runtime Host calls.
  • CLI/TUI onboarding projects one entry per provider and cannot explicitly create or manage another connection for that provider.
  • Desktop OAuth selects the first connection for a provider and presents one account card per provider, although OAuth tickets themselves are already bound to a specific connectionId.
  • Desktop and TUI model pickers both select connectionSlug + model, but do not apply the same duplicate-account disambiguation rules.
  • Session and AgentRun headers persist the connection slug, not the immutable connection ID. If connection A is deleted and connection B later reuses A's slug, an old Session can resolve to B's credentials instead of reporting that its original account was deleted.

This is related to #2677, but intentionally tracks a different product model. Each account remains a separate, user-visible Connection selected explicitly. Weighted routing, automatic balancing, and silent failover are not part of this issue.

Desired outcome

Support multiple API-key and OAuth accounts for the same provider consistently across CLI/TUI and Desktop, while keeping Runtime Host as the authority for onboarding identity, credential binding, Session targeting, and recovery.

Use these identity semantics:

  • providerType identifies the provider implementation, for example openai.
  • connectionId is the immutable identity of one configured account/connection entity.
  • slug is a unique, human-readable locator such as openai or openai-2; it may be reused after deletion and therefore must not be the sole historical identity.
  • Credentials remain isolated by connectionId.
  • Session persists connectionId + connectionSlug + model; AgentRun also records connectionId for execution attribution.
  • Execution resolves the Connection by connectionId and verifies that the stored slug still describes that entity. It must never fall back from a missing ID to a same-named replacement.
  • If the original Connection is deleted, the old Session clearly reports original account deleted. Only an explicit user selection may bind the Session to another Connection and restore execution.

Example:

  1. Session S selects connection A: { connectionId: "A", connectionSlug: "openai-2", model: "gpt-5" }.
  2. A is deleted.
  3. A new connection B is created with slug openai-2.
  4. S remains blocked and reports that its original account was deleted; it does not use B.
  5. The user explicitly selects B, after which S stores B's ID, slug, and model and may run again.

Architecture boundary

Runtime Host should own:

  • distinguishing edit existing(connectionId) from create another provider account;
  • allocating and committing the final slug against the authoritative catalog snapshot;
  • atomic/recoverable catalog, credential, headers, and onboarding updates;
  • returning the final connectionId + slug;
  • OAuth login against an explicitly selected Connection;
  • validating and persisting Session entity bindings;
  • deletion diagnostics and explicit rebinding admission.

Storage remains the canonical persistence layer. Surfaces query choices, submit user intent, and present results; they do not allocate identity from a possibly stale local existingSlugs snapshot or silently choose credentials.

Implementation progress

Checkboxes below remain acceptance criteria for the tracking issue; an open PR is not marked delivered until it merges.

Workstreams

1. Runtime Host onboarding contract

  • Replace nullable connectionId create semantics with an explicit target such as existing(connectionId) / create(slug?, name?).
  • Allocate a default unique slug (provider, provider-2, ...) inside the authoritative Host/storage write path.
  • Bind the onboarding ticket to the reserved create identity and revalidate it at commit.
  • Return the committed connectionId + slug.
  • Update the Runtime Host compatibility epoch when the exact protocol contract changes.

2. Durable Session and AgentRun identity

  • Persist connectionId + connectionSlug + model on Session.
  • Persist connectionId with the existing connection/model information on AgentRun.
  • Resolve execution by ID; treat slug as display/consistency metadata, never as fallback identity.
  • Keep legacy slug-only Sessions readable, but do not infer an immutable entity from the current owner of a reusable slug. Recovery requires an explicit exact account selection through feat(runtime-host): carry exact Connection identity in Session model targets #3926.

3. CLI/TUI connection management

  • Enumerate all Connections for a provider.
  • Offer explicit edit-existing and add-another-account actions.
  • Use Runtime Host onboarding rather than calculating existingSlugs in the surface.
  • Show enough account identity in setup and model selection to disambiguate duplicate names.

4. Desktop API-key convergence

  • Move the current multi-call create/credential/discovery orchestration onto the managed Runtime Host onboarding transaction.
  • Preserve existing IDs and slugs, including data such as openai-2; no catalog migration or renaming should be required.

5. Desktop OAuth multi-account support

  • Replace first-connection-by-provider selection with explicit Connection selection and multiple account cards.
  • Create additional OAuth Connections through the same Host-owned identity path.
  • Keep login/status/refresh/logout scoped to the selected connectionId.
  • Preserve the existing Connection-bound InteractiveOAuthLoginTicket checks.
  • Initially, the Host's single-active-interactive-login limitation may remain if it is surfaced as an explicit conflict and never silently supersedes another account's login. Per-Connection concurrent login lanes can be tracked separately if required.

6. Shared selection and deletion UX

  • Apply consistent duplicate-name and OAuth-safe labels across Desktop and TUI model pickers.
  • Keep the existing default Connection when another account is added unless the user explicitly changes the default.
  • On deletion, show the affected Session as blocked with “original account deleted” semantics and provide an explicit account/model picker to recover.
  • Never perform automatic account balancing, fallback, or slug-based rebinding.

Acceptance criteria

  • Two or more API-key Connections for the same provider can be created, edited, selected, and deleted from CLI/TUI and Desktop.
  • Two or more OAuth Connections for openai-codex and xai-oauth can be created and independently logged in, inspected, refreshed, logged out, selected, and deleted.
  • Each Connection has a unique immutable connectionId and catalog-unique slug; credentials are read and written only through that ID.
  • Concurrent create attempts cannot accidentally allocate the same slug or split catalog and credential ownership across surfaces.
  • Model choice remains explicit at Connection + model granularity and is visibly disambiguated on both Desktop and TUI.
  • A Session and every new AgentRun retain the selected Connection ID, slug, and model.
  • Deleting a selected Connection blocks existing Sessions with an “original account deleted” state.
  • Recreating the same slug never rebinds old Sessions.
  • Only an explicit Session configuration update can bind a blocked Session to a replacement Connection.
  • Adding another account does not silently change the default target.
  • Existing multi-Connection Desktop data remains valid without identity replacement or credential migration.
  • Protocol compatibility behavior is covered for the onboarding and Session contract changes.

Non-goals

  • Multiple credential profiles inside one Connection
  • Weighted load balancing or round-robin routing
  • Automatic quota failover
  • Silent selection of another account
  • Using an OAuth email address as a durable identity or mandatory slug

Current code anchors

  • Connection catalog and slug allocation: packages/storage/src/runtime-policy/connection-catalog-document.ts
  • Credential isolation: packages/storage/src/runtime-policy/credential-vault-document.ts
  • Managed onboarding target resolution: packages/storage/src/runtime-policy/coordinator.ts
  • Runtime Host onboarding effects: packages/runtime-host/src/server/connection-effect-coordinator.ts
  • CLI/TUI projection: packages/cli/src/runtime-host-onboarding.ts
  • Desktop API-key add flow: apps/desktop/src/renderer/settings/provider-add-form.tsx
  • Desktop OAuth selection: apps/desktop/src/main/runtime-host-account-connection.ts
  • Session persistence: packages/core/src/session.ts
  • AgentRun persistence: packages/core/src/agent-run.ts
  • Model choice/disambiguation: packages/core/src/chat-model-choice.ts and packages/ui/src/chat-model-helpers.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions