Repository navigation
fix(ai): Ask AI uses only the session when a session bridge is present - #1685
Merged
Merged
Conversation
When a host attaches its session to the server (Claude Code mod over the pull bridge, Pi in-process, OpenCode 2 in-process or pull), the AI runtime now registers the session bridge as the ONLY Ask AI provider, in both the Bun and Pi runtimes. No SDK provider is registered, so capabilities list only the bridge (and it is the default) and /api/ai/session refuses any other provider id, whatever the client saved. Client: a bridge in the list is the selection in every status, ahead of saved picks, without rewriting the provider cookies. The Ask AI bars and Settings > AI show only the bridge label. A gone or blocked session no longer offers "Ask a separate AI instead"; it shows a plain note that the session can't be reached. The busy flow is unchanged. Without a bridge (mod off, OpenCode 1, remote mode, --tailscale) the provider set and selection are unchanged.
With a session bridge, the SDK providers now register in a catalog-only registry instead of not at all. /api/ai/capabilities still lists only the bridge under providers (Ask AI stays bridge-only, /api/ai/session still refuses SDK ids), but ?activate=<id> naming a catalog-only provider runs its discovery and reports it under catalogProviders, which only the agent-job launchers' useModelCatalogs reads. Review Agents, Code Tour and Guided Review get the same discovered Claude/Codex catalogs as before #1685. HANDOFF: the Ask this session exports shipped in ui 0.50.0; record the removals as breaking for 0.50.0 consumers in the next ui release.
At page load the Ask AI selection can hold the saved cookie's provider id for one render before the resolver moves it to the session bridge, and the activation effect then called ?activate=codex-sdk, starting Codex discovery in a bridged session. useAIProviderActivation takes the listed providers and skips any other id; both apps pass their capabilities list.
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Owner's rule: when the modern plugin/mod/extension for Claude Code, Pi or OpenCode is in place, there is no provider, only direct pass-through to the original session.
Before this change, a session bridge was registered last, beside the SDK providers (claude-agent-sdk, codex-sdk, pi-sdk, opencode-sdk). The client preferred an explicit saved pick over the bridge. So a user who had once saved Codex got Codex answers, even with the Claude session attached.
What changed
Server (both runtimes)
createAIRuntime(packages/server/ai-runtime.ts) andcreatePiAIRuntime(apps/pi-extension/server/ai-runtime.ts) resolve the bridge first. With a bridge,session-bridgeis the only Ask AI provider; the SDK providers go into a separate catalog-only registry (newcatalogRegistrydep oncreateAIEndpoints). Without a bridge, the SDK providers register exactly as before./api/ai/capabilitieslists only the bridge underproviders, anddefaultProviderissession-bridge./api/ai/sessionwith any other provider id answers503.?activate=claude-agent-sdk|codex-sdkruns that provider's discovery and reports it under a newcatalogProvidersfield. OnlyuseModelCatalogsreads that field; Ask AI readsprovidersonly.PLANNOTATOR_AI=disabledis gated before runtime creation, so it still disables everything.Client (
@plannotator/ui, editor, review-editor)resolveAIProviderSelectionselects a bridge in the list in every status (ready, busy, blocked, gone), ahead of saved picks. Without a bridge, the order is unchanged.AIProviderBarand review'sAIConfigBarshow only the bridge label, with no provider, model or effort picker. Settings → AI shows the bridge read-only and writes nothing.resolveSessionBridgeFallback,findUsableSessionBridge, the'fallback'action,sessionAskFallbackLabelandfallbackLabel. These shipped in@plannotator/ui@0.50.0, so this is a breaking change for 0.50.0 consumers.packages/ui/HANDOFF.mdrecords the removals and adds a Publishing note so the next ui release mentions them. No package versions are bumped. CLAUDE.md is updated.useAIProviderActivationtakes the listed providers and skips any other id. Both apps pass their capabilities list. At page load the selection can still hold the saved cookie id (e.g.codex-sdk) for one render, and activating it would start Codex discovery in a bridged session.useModelCatalogsalso readscatalogProviders. This is additive: a backend that never sends the field behaves as before./api/agents/*) are untouched.Per-host effect
--tailscale, older hostsTests
packages/server/ai-runtime.sessionBridge.test.ts: 6 pass. The tests run in child processes with fakecodexandclaudeCLIs on PATH (tests/test-fixtures/fake-agent-clis.ts). The fake codex speaks the app-serverinitializeandmodel/listmessages.session-bridge, which is the default, and returns nocatalogProviders. Sessions forcodex-sdkandclaude-agent-sdkanswer 503, and codex is never executed.?activate=codex-sdkreturns the DISCOVERED codex list (modelsSource: "discovered", version 0.999.0) undercatalogProviders, and?activate=claude-agent-sdkreaches the installed claude (its version is read).providersstays bridge-only and sessions are still refused. This test fails on the previous commit.claude-agent-sdkandcodex-sdkare registered as before.apps/pi-extension/server/ai-runtime.test.ts: the same assertions for the Pi runtime, including the launcher catalogs. This test also fails on the previous commit.packages/ui/hooks/useModelCatalogs.test.ts: the launcher loader reads the discovered list fromcatalogProviderswhenprovidersis bridge-only.packages/ui/hooks/useAIProviderActivation.dom.test.tsx: an id that is not listed is never activated, and without a list any id is activated as before. This test fails without the fix.packages/ui/aiProvider.sessionBridge.test.ts: covers the resolver.packages/ui/aiProviderConfigPersistence.test.tsx: throughuseAIProviderConfig, a bridge-only answer selects the bridge and leaves the saved cookie untouched.packages/ui/components/ai/SessionAskNotice.dom.test.tsx: gone and blocked show the note, with no fallback button.packages/ui/components/ai/AIProviderBar.dom.test.tsx: with a bridge, the bar renders the label and no<select>.Suites run:
packages/server: 1046 pass, 0 fail.apps/pi-extension: 356 pass.packages/ai+apps/opencode-plugin+apps/hook/hooks/mod: 441 pass.DOM_TESTS=1 packages/ui packages/editor packages/review-editor: 2987 pass, 4 fail. The 4 failures are inDocBadges.test.tsxand fail on main too.bun run typecheckis clean.apps/reviewandbuild:hookbuild.