apache / apache/maka

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

Open
#3,852 2 comments 0 reactions 1 assignee Claimed by @me2seeks View on GitHub
enhancement
Dominant language
TypeScript
Stars
5.4k
Forks
502
Avg merge
1d 2h
Merged PRs (30d)
715

Description

## 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

- **Durable identity foundation — merged:** #3864
- **Legacy/deleted Session recovery UX — merged:** #4102
- **API-key multi-account onboarding and CLI/TUI — merged:** #3882
- **OAuth entity binding — merged:** #3924, closing #3888
- **Desktop OAuth multi-account surface — merged:** #4314 (closes #4312)
- **Desktop standard API-key onboarding convergence — in review:** #4377; advanced/custom configuration remains a follow-up workstream
- **ScheduledTask identity — tracked separately:** #3927

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 #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`

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.