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 issue or 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

  • Current phase: move fixed-endpoint, standard API-key creation with no advanced request customization onto the existing Runtime Host connection.onboarding.verify/save transaction.
  • Route by normalized capability before any side effect. Runtime Host failure must never fall back to the legacy create path.
  • Treat the Host-returned connectionId + slug as canonical and preserve the existing global default when another account is added.
  • Keep Cloudflare, custom relay/endpoint, custom name/slug, headers/body overlay, and free-text model creation on an explicit legacy advanced path during this phase.
  • Follow-up phase: extend the Host onboarding protocol and recovery journal before moving those advanced fields; use the then-current next compatibility epoch.
  • 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions