You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On create, the storage coordinator allocates the identity itself: connectionId = randomUUID() and slug = deriveConnectionSlug(providerType, existingSlugs) (packages/storage/src/runtime-policy/coordinator.ts), so a second OpenAI account becomes openai-2, openai-3, …
The TUI /setup wizard exposes both targets (one row per existing connection plus an "add account" row per provider), but has no step to choose a slug or display name.
Desktop already offers custom slug/name, but only by bypassing managed onboarding: the renderer derives a slug with existingSlugs, lets the user edit it, validates format/uniqueness in provider-add-submission.ts, then drives connection.catalog.create + credential.vault.set + model fetch as separate Host operations (with a compensating delete on failure).
Result: a TUI user with two OpenAI accounts gets openai and openai-2 and cannot tell them apart in /model, which displays name · slug. Naming is a Desktop-only privilege today, and Desktop pays for it with a multi-step, non-transactional flow.
Proposal
Let callers optionally request a slug and display name on the managed onboarding create target, keeping the Host as the allocating authority:
Protocol (packages/runtime-host/src/protocol/connection-effects.ts): extend the create target with optional slug / name fields. The decoder is currently a closed shape (['kind', 'providerType']), so this needs a compatibility decision (epoch bump vs. capability negotiation) — older Hosts must not silently ignore a requested name.
Storage (coordinator.ts create branch): if a slug is requested, validate it with the existing slug codec and check uniqueness against the catalog the basis already reads; on collision, reject with a typed failure (mirroring Desktop's duplicate form error) instead of silently deriving a different slug. No requested slug → keep today's deriveConnectionSlug behavior.
TUI (OnboardingWizard): add an optional name/slug step for create targets, prefilled with the Host-style derived suggestion (openai-2); copy goes through the locale catalogs (en / zh-CN).
Design the fields as auth-kind-agnostic so a future OAuth onboarding path gets naming for free — OAuth multi-account arguably needs display names even more (two GitHub Copilot accounts are otherwise indistinguishable).
Non-goals
Renaming existing connections. slug is immutable post-create by design (catalog update pins slug/connectionId/providerType); editing name for existing connections could be a follow-up via connection.catalog.update.
OAuth login in the TUI itself — that is a separate feature on top of the existing oauth.login.start/query/cancel Host operations and does not conflict with this change; the only shared files are the provider-list projection / picker items.
Migrating Desktop's add-provider form onto managed onboarding (would give it single-transaction semantics); nice follow-up, not required here.
Acceptance
From TUI /setup, a user can create a second OpenAI connection named e.g. openai-work / "Work OpenAI"; it appears with that name · slug in /setup and /model.
Requesting a slug that already exists produces a typed, localized error in the wizard; nothing is silently renamed.
Omitting the slug reproduces today's derived-slug behavior exactly.
Protocol change is covered by contract tests, including the old-Host/new-surface compatibility story.
Background
Multi-account model-provider onboarding has landed:
{ kind: 'existing', connectionId }or{ kind: 'create', providerType }.create, the storage coordinator allocates the identity itself:connectionId = randomUUID()andslug = deriveConnectionSlug(providerType, existingSlugs)(packages/storage/src/runtime-policy/coordinator.ts), so a second OpenAI account becomesopenai-2,openai-3, …/setupwizard exposes both targets (one row per existing connection plus an "add account" row per provider), but has no step to choose a slug or display name.Desktop already offers custom slug/name, but only by bypassing managed onboarding: the renderer derives a slug with
existingSlugs, lets the user edit it, validates format/uniqueness inprovider-add-submission.ts, then drivesconnection.catalog.create+credential.vault.set+ model fetch as separate Host operations (with a compensating delete on failure).Result: a TUI user with two OpenAI accounts gets
openaiandopenai-2and cannot tell them apart in/model, which displaysname · slug. Naming is a Desktop-only privilege today, and Desktop pays for it with a multi-step, non-transactional flow.Proposal
Let callers optionally request a slug and display name on the managed onboarding
createtarget, keeping the Host as the allocating authority:packages/runtime-host/src/protocol/connection-effects.ts): extend the create target with optionalslug/namefields. The decoder is currently a closed shape (['kind', 'providerType']), so this needs a compatibility decision (epoch bump vs. capability negotiation) — older Hosts must not silently ignore a requested name.coordinator.tscreate branch): if a slug is requested, validate it with the existing slug codec and check uniqueness against the catalog the basis already reads; on collision, reject with a typed failure (mirroring Desktop'sduplicateform error) instead of silently deriving a different slug. No requested slug → keep today'sderiveConnectionSlugbehavior.OnboardingWizard): add an optional name/slug step for create targets, prefilled with the Host-style derived suggestion (openai-2); copy goes through the locale catalogs (en / zh-CN).Design the fields as auth-kind-agnostic so a future OAuth onboarding path gets naming for free — OAuth multi-account arguably needs display names even more (two GitHub Copilot accounts are otherwise indistinguishable).
Non-goals
slugis immutable post-create by design (catalog update pinsslug/connectionId/providerType); editingnamefor existing connections could be a follow-up viaconnection.catalog.update.oauth.login.start/query/cancelHost operations and does not conflict with this change; the only shared files are the provider-list projection / picker items.Acceptance
/setup, a user can create a second OpenAI connection named e.g.openai-work/ "Work OpenAI"; it appears with thatname · slugin/setupand/model.