feat(agent-pane): bridge opt-in native tab-row icon tools

Merged
TE
5d ago
+873-180

When the host offers tabRow.nativeRowTools: true with a positive row height, the chat's quick-action icons and Session Info/Summary toggle are described to the native tab-row host. A shared button list supplies both DOM and native labels, symbols, visibility, toggle state and callbacks. The opt-in remains off by default. This web half lands after the native implementation in #27133 and coordinator clearance.

Leo, 2026-10-29:

"speaking of that top right buttons section area thing, okay, what do you think about moving it into Swift? Like just going more native with it? Because I just feel like on my fing God, I've ran into so many fing different hacks and stuff just to get this nonsense working. ... we keep running into this nonsense, right? ... And then we could just RPC it or whatever the f*** into TypeScript sometimes. But I feel like, okay, more native buttons and stuff, maybe that might be nicer."

The page calls pane.tabRowTools.set through callNative with {tools: [{id, symbol, label, enabled, toggled}]}. tabRowToolInvoked {id, menuItemId?} arrives through cmux.agent.host.events and dispatches the shared callbacks. These buttons have no native menus; unknown ids, disabled tools and menu-item invocations do nothing. Labels are bounded to 64 characters and lists to 16 tools.

Values are deduplicated, so callback identity changes and token renders do not resend tools. Hiding/unmounting the header or disabling the opt-in clears the list and unregisters dispatch. A rejected host update restores the web buttons. No timers, polling, observers, animation loops or per-token RPCs are added.

Scope: quick actions plus Inspector/Summary only. Summary has no badge today; none is invented. Needs You retains its web button and correctly anchored rich popover (Allow/Deny/choices, Go to source, Ask about this); its native migration belongs to a later step. PR chip, worktree pill, presence, status, chat menu and clearance CSS are unchanged. Existing automation can still open Summary while its visible toggle is native.

The change is kept together because extracting the shared action list, its host bridge, and the native/off lifecycle tests establish one dispatch path; splitting the extraction from the consumer would leave the behavior invariant untested.

Risk: normal — opt-in web/native action routing. No default changes. The native renderer and tunable ship in #27133; this PR does not claim native appearance evidence.

Validation:

  • Local Node 24 web lint passes; 19 focused tests pass (228 assertions).
  • List/dispatch tests cover ids, shared SF Symbols, toggled/enabled state, absent Summary badge, all handlers, malformed events, value deduplication, current callbacks, off/hidden/unmount cleanup and failed-host recovery.
  • The real header switches through page-level tabRow events and invokes Terminal/Summary through tabRowToolInvoked; web clicks use the same callbacks. Removing either the flag parser or invocation routing makes this regression fail. Native mode hides migrated DOM buttons, restoring them when disabled.
  • Existing header behavior tests cover Browser/Preview routing, Changes, Files, Split Down, tooltips and customization.
  • Gallery native-row-off/native-row-tools, light and dark: the web icons disappear only with native opt-in; Needs You and the chat menu remain.
  • Combined exact-head lint and tests: run 40794610520, PASS: full web lint and all 19 tests / 228 assertions at 2af5d59e4175c09ce60921135c1edef38259bb00. This update strengthens the host-event regression without product changes.
  • Independent Astra review: SHIP at 2af5d59e4175c09ce60921135c1edef38259bb00, including the page-level host-event regression.

Gallery comparison: native opt-in off/on in the same header fixture. The after image shows the remaining web controls; native icons are rendered by the separate native PR.

ThemeBefore (off)After (native on)
DarkWeb tools off darkWeb tools native dark
LightWeb tools off lightWeb tools native light

Agent session: agent session