Repository navigation
feat(native-chat): + menu shows chat, disabled chat and CLI rows - #29074
Conversation
Settings → Chat gets a Providers list. Each agent the host can open as a chat has a switch; off opens that agent in a terminal instead. Only choices the user makes are saved. Unchosen: Claude and Codex on, others on when their CLI is installed on this computer. The renderer's route and the host's launch-mode decision both read one rule, so the tab menu, shortcuts, CLI, orchestration and relayed phone launches agree.
99218ce to
8793c72
Compare
With Chat UI on, each agent whose chat provider is on gets a chat row, disabled with the reason where the workspace's machine can't run that chat (not installed, SSH, WSL, not on this host). Every other installed agent gets a row marked (CLI) that opens its terminal. With Chat UI off, or on an older server that opens no chats, the menu is unchanged.
d1b20b8 to
8b964f9
Compare
A chat row whose CLI is installed on the workspace's machine gets a › beside it. Its side menu offers '<Agent> (CLI)', which opens that agent's terminal for this launch only; nothing is saved and the row itself still opens a chat.
…li-rows' into brennanb2025/new-tab-menu-cli-popover
… agent-session surface
…menu-chat-cli-rows # Conflicts: # src/main/runtime/orca-runtime.ts
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
848cb26a-2272-4e64-b84e-9481baeebd84
⛔ Files ignored due to path filters: 1
src/shared/rpc-contract/rpc-params-catalog.generated.tsis excluded by!**/*.generated.*
📒 Files selected for processing: 55
src/main/preflight/workspace-agent-detection.tssrc/main/runtime/orca-runtime.tssrc/main/runtime/rpc/methods/structured-agent-session-agents.tssrc/main/runtime/rpc/methods/structured-agent-session-schemas.tssrc/main/runtime/rpc/methods/structured-agent-session-subscription-id.tssrc/main/runtime/structured-agent-availability.test.tssrc/main/runtime/structured-agent-availability.tssrc/main/runtime/structured-agent-launch-support.tssrc/renderer/src/components/settings/NativeChatProvidersSetting.tsxsrc/renderer/src/components/tab-bar/QuickLaunchButton.launch-status.test.tsxsrc/renderer/src/components/tab-bar/QuickLaunchButton.test.tssrc/renderer/src/components/tab-bar/QuickLaunchButton.tsxsrc/renderer/src/components/tab-bar/TabBar.context-menu.test.tssrc/renderer/src/components/tab-bar/TabBar.tsxsrc/renderer/src/components/tab-bar/TabBarCreateEntry.tsxsrc/renderer/src/components/tab-bar/TabBarCreateEntryRow.tsxsrc/renderer/src/components/tab-bar/new-tab-host-chat-answers.tssrc/renderer/src/components/tab-bar/tab-agent-launch-options.test.tssrc/renderer/src/components/tab-bar/tab-agent-launch-options.tssrc/renderer/src/components/tab-bar/tab-agent-row-presentation.tssrc/renderer/src/components/tab-bar/tab-bar-surface.tsxsrc/renderer/src/components/tab-bar/tab-bar-windows-shell-launch-render-stubs.tssrc/renderer/src/components/tab-bar/tab-create-entry-active-option.tssrc/renderer/src/components/tab-bar/tab-create-entry-network-selection.tssrc/renderer/src/components/tab-bar/tab-create-entry-props.tssrc/renderer/src/components/tab-bar/use-new-tab-agent-menu-rows.test.tsxsrc/renderer/src/components/tab-bar/use-new-tab-agent-menu-rows.tssrc/renderer/src/components/tab-bar/use-tab-bar-create-menu-controller.tssrc/renderer/src/components/tab-bar/use-tab-bar-runtime-model.tssrc/renderer/src/components/ui/dropdown-menu.tsxsrc/renderer/src/i18n/en-runtime-required.jsonsrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/lib/agent-launch-routing.tssrc/renderer/src/lib/agent-session-launch-plan.tssrc/renderer/src/lib/chat-provider-agents.tssrc/renderer/src/lib/launch-agent-in-new-tab-host-agents.test.tssrc/renderer/src/lib/launch-agent-in-new-tab-host-agents.tssrc/renderer/src/lib/launch-agent-in-new-tab.tssrc/renderer/src/lib/launch-structured-agent-session.test.tssrc/renderer/src/lib/new-tab-agent-menu-rows.test.tssrc/renderer/src/lib/new-tab-agent-menu-rows.tssrc/renderer/src/lib/structured-agent-session-host-admission.tssrc/renderer/src/lib/structured-agent-session-launch-admission.test.tssrc/renderer/src/lib/structured-agent-session-launch-admission.tssrc/renderer/src/runtime/host-structured-agent-availability.test.tssrc/renderer/src/runtime/host-structured-agent-availability.tssrc/renderer/src/runtime/structured-agent-session-client.tssrc/renderer/src/runtime/use-host-structured-agent-availability.tssrc/shared/agent-session-agent-availability.tssrc/shared/protocol-version.tssrc/shared/rpc-contract/structured-agent-session-params.tssrc/shared/structured-agent-session-surface-capabilities.tstests/e2e/cross-version-wire/structured-agent-session-surface-calls.tstests/e2e/cross-version-wire/structured-agent-session-surface-execution.tstests/e2e/cross-version-wire/structured-agent-session-surface-manifest.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.
…own, and learns them while the workspace is shown
The + menu no longer checks chat availability per workspace. Each Orca runtime keeps one agent health snapshot: every agent's location rule on this machine plus a typed observation of its installed release, probed in the server's own home. It is taken on first use and replaced only on a launch setting change, an explicit agent Refresh (after a fresh shell capture), or a restart, and kept without readers. - The version probe reports supported, unsupported or inconclusive; a launch still refuses an inconclusive run, while the snapshot reads it as unknown rather than a refusal. - agentSession.subscribeAvailability takes no params and publishes that snapshot; a replay that closes the transport releases its reader. - The renderer keeps the snapshot per host, runtime and pairing, through lost contact and across workspace switches on the same host. - The menu hook drops the per-open createSupport display reads, their deadline and answer cache; older hosts show client facts and the click's admission, unchanged, states any refusal.
…menu-chat-cli-rows # Conflicts: # src/main/runtime/structured-agent-runtime-metadata.ts # src/renderer/src/components/settings/NativeChatProvidersSetting.tsx
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟡 Minor · Hold agent search until availability settles. · use-tab-bar-create-menu-controller.ts:94
src/renderer/src/components/tab-bar/use-tab-bar-create-menu-controller.ts:94
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winHold agent search until availability settles.
When
agentMenu.settlingis true, typed search can select a provisional CLI row and Enter can launch a terminal. After the host reports availability, the same agent can become a chat row. Do not expose agent options to typed search until the snapshot settles.Suggested fix
- const agentLaunchOptions = buildTabAgentLaunchOptionsFromRows(agentMenu.rows, commandOverrides) + const agentLaunchOptions = agentMenu.settling + ? [] + : buildTabAgentLaunchOptionsFromRows(agentMenu.rows, commandOverrides)
🟡 Minor · Keep the settling agent region to one row minimum. · QuickLaunchButton.tsx:181
src/renderer/src/components/tab-bar/QuickLaunchButton.tsx:181
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winKeep the settling agent region to one row minimum.
ProjectedAgentMenuItemsrenders at least two placeholders. A workspace with one candidate resolves to one agent row after the first availability snapshot, so the open+menu shrinks vertically. Thew-72width constraint prevents horizontal resizing, but it does not prevent this height change.Suggested fix
- {PLACEHOLDER_WIDTHS.slice(0, Math.max(rows.length, 2)).map((width) => ( + {PLACEHOLDER_WIDTHS.slice(0, Math.max(rows.length, 1)).map((width) => (
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
665576d3-ec06-47a1-87d9-4279cc1357e2
⛔ Files ignored due to path filters: 1
src/shared/rpc-contract/rpc-params-catalog.generated.tsis excluded by!**/*.generated.*
📒 Files selected for processing: 32
src/main/agent-cli-version-probe.test.tssrc/main/agent-cli-version-probe.tssrc/main/pi/rpc-launch-resolution.tssrc/main/pi/rpc-runtime-metadata.tssrc/main/pi/rpc-runtime-registration.test.tssrc/main/runtime/orca-runtime.tssrc/main/runtime/rpc/methods/structured-agent-session-agents.tssrc/main/runtime/structured-agent-availability.test.tssrc/main/runtime/structured-agent-availability.tssrc/main/runtime/structured-agent-launch-support-probe-omp.test.tssrc/main/runtime/structured-agent-launch-support-probe.test.tssrc/main/runtime/structured-agent-launch-support.test.tssrc/main/runtime/structured-agent-launch-support.tssrc/main/runtime/structured-agent-runtime-metadata.tssrc/main/runtime/structured-agent-runtime-registration-types.tssrc/main/runtime/structured-agent-shell-environment.tssrc/renderer/src/components/settings/NativeChatProvidersSetting.tsxsrc/renderer/src/components/tab-bar/QuickLaunchButton.launch-status.test.tsxsrc/renderer/src/components/tab-bar/use-new-tab-agent-menu-rows.test.tsxsrc/renderer/src/components/tab-bar/use-new-tab-agent-menu-rows.tssrc/renderer/src/i18n/en-runtime-required.jsonsrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/lib/agent-session-launch-plan.tssrc/renderer/src/lib/structured-agent-session-host-admission.tssrc/renderer/src/runtime/host-structured-agent-availability.test.tssrc/renderer/src/runtime/host-structured-agent-availability.tssrc/renderer/src/runtime/use-host-structured-agent-availability.tssrc/shared/agent-session-agent-availability.tssrc/shared/rpc-contract/structured-agent-session-params.tssrc/shared/structured-agent-session-surface-capabilities.tstests/e2e/cross-version-wire/structured-agent-session-surface-execution.tstests/e2e/cross-version-wire/structured-agent-session-surface-manifest.ts
🚧 Files skipped from review as they are similar to previous changes: 2
- src/renderer/src/i18n/en-runtime-required.json
- src/renderer/src/i18n/locales/en.json
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.
…menu-chat-cli-rows # Conflicts: # src/renderer/src/components/settings/NativeChatProvidersSetting.tsx
Hovering a chat row in the + menu, or pressing ArrowRight on it, now opens the "<Agent> (CLI)" submenu to the right instead of needing a separate chevron button. Clicking the row, or Enter/Space, still starts the chat and closes the menu; hosts pass closeMenu since a submenu trigger never closes the menu on its own.
Clicking a chat row in the + menu starts a chat. Hovering the row (or pressing ArrowRight on it) reveals a terminal button at its right edge that opens the agent in a terminal instead. This replaces the submenu, whose chevron made the row look like it only opened a submenu. The button's click bubbles into the row, so the menu closes as for any item and the hosts no longer need a closeMenu callback.
…s and screen readers A press on + dragged onto a chat row's terminal button and released there now opens the CLI: the row reads whether the pointer or focus is on its button instead of the click target, which a drag-release never sets. Arrow, Home and End keys pressed on the focused button move between rows as they do on the row. The row is announced as "Start a <Agent> chat" instead of including the button's label.
…menu-chat-cli-rows
"Start a OpenCode chat" was ungrammatical for agents whose name starts with a vowel.
…menu-chat-cli-rows
#29074 landed a per-agent "is this version string supported" predicate and a supported/unsupported/inconclusive observation for the + menu's agent health snapshot. Keep one version policy: an agent's launch check now returns the shared minimum-version result (ready, tooOld, unknown, missing), create support runs anything but tooOld or missing, and the health snapshot reads ready as available, tooOld as refused and anything else as unknown.
The old picker change counted managed agents as detected CLIs, which #29074's menu now uses for terminal rows. Everything else it carried has landed on main. The rework follows in the next commits.
227 commits landed on main between 91b696b and 1529b0f, and only en-runtime-required.json conflicted; it is taken from main and regenerated. The six catalogs merged cleanly, so the earlier prune carries over. On the merged tree the detector found three more keys: the Checking chat support placeholder and the Could not build launch command message that stablyai#29074 left behind when it reworked the + menu, and the native-chat cannotResume message whose last reader stablyai#29150 removed. The prune is now 1,509 keys and none left the set. en.json goes from 16,395 entries to 14,886; en-runtime-required.json keeps 578 of them.
ELI5
With Chat UI on, the + menu did not explain whether each agent opens a chat or a terminal. Enabled chats that cannot run in this workspace could disappear, and opening a chat agent’s terminal once required changing a preference.
The menu now groups runnable chats, disabled chats with their reasons, and terminal choices labelled “(CLI)”. Clicking a chat row starts a chat; hovering it shows a terminal button at its right edge that opens that agent in a terminal once instead. Typing in search and sending review notes to a new agent use the same choices. Chat UI off and older paired servers retain their existing plain list.
What Changed
Each runtime owns one shared agent health snapshot. It checks registered agents’ location rules and version support in that machine’s home folder, and publishes complete snapshots only to clients that support the stream. A restart, launch-setting change or explicit agent Refresh refreshes health; every health pass first captures fresh shell facts. Workspaces and menu reopens reuse the retained snapshot, including during contact loss.
The renderer derives one loading or settled menu observation. During initial acquisition, quiet placeholders reserve the agent region and search exposes no provisional agent action. Settled rows and search share the same availability and installation facts. Failed reads retain independently known terminal choices, show truthful disabled reasons and preserve the existing inventory explanation. Generic search, New Terminal and Agent settings remain usable.
The clicked row, or its terminal button, carries its requested surface through the existing launcher. An explicit chat refusal reports the problem once without opening a terminal; ordinary preference-based launches retain their existing routing.
Why
This follows the common pattern of host-owned readiness and derived picker choices while preserving independent client/server updates. Retained host facts avoid repeated per-workspace checks, and a shared menu observation prevents a search result from changing its launch surface as loading finishes.
Intended differences from the common pattern and accepted product costs:
Enabled unavailable agents remain visible with a reason. Settings → Agents Disabled still hides an agent everywhere. No provider default changes.
Visual Proof
Current look, captured on the final code (
e995e707f3e) with Chat UI on: a hidden test copy of Orca with throwaway settings, Mac on an M4Air and Windows native on Windows high spec. The only later commit changes the chat rows' tooltip and screen-reader label to "Start a chat with ". WSL QA is blocked because the test copy cannot isolate the WSL guest's home folder (the WSL row comes from an unchanged client rule). Full review notes are in the status comment.Mac, main
acc235187f62versus this branch:Windows native, main
10505696a2c3versus this branch (Antigravity shows as a disabled chat row with its reason, and as a CLI row):What the row and its button do (Windows native QA of the terminal button, 6/6 at
38733100b31and 6/6 at44e54cbcda0):Terminal button on each chat row, Windows native at
44e54cbcda0(the code in the final head; Claude terminal and keyboard focus from the same design at38733100b31):Real chat replies (Mac, before the terminal button replaced the earlier › control; the chat itself is unchanged): main Claude reply, branch Claude reply.
The first-open loading placeholder (grey bars while agents are still being detected) appears too briefly on a local machine to capture: six tries at
e995e707f3efound the list already complete, and an immediate reopen showed the full list at once. The placeholder code is unchanged since its capture atea92205bc09(rows 32px in both states).Testing
44e54cbcda0(6/6) and38733100b31(6/6); Mac and Windows screenshots of the current menu ate995e707f3e; macOS and Windows menu QA atea92205bc09; WSL blocked (see Visual Proof).Targeted backend and renderer tests passed. Full PR workflow 39205965263 (aggregate PR checks passed 117911432779) succeeded at 07db62a, after the final main merge. CI is the full lint and typecheck gate.
Linked Issue
N/A (maintainer).
AI Disclosure
Review
Fresh full review loop: four rounds at
ea92205bc09, no unresolved proven P0/P1/P2. The later terminal-button change (replacing the earlier › submenu, which users could mistake for the row's only action) had an independent review; its three minor findings (drag-release onto the button, arrow keys from the button, the row's screen-reader name) are fixed in44e54cbcda0. The chat rows' label now reads "Start a chat with " (it read "Start a OpenCode chat"). main was merged at the end.Agent skill upstream boundary
Notes
SSH workspaces retain their terminal path and show unavailable chats with a reason. Native Windows and WSL use their existing execution-host routing. Independent client/server updates and older-server launch paths remain supported. Mobile click admission is unchanged. Author: @BrennanKB5.
Checklist