Files
orca/src/preload/api/ui-command-event-api.ts
T
Brennan Benson 91d6ed0f3c fix(worktrees): a phone or CLI create puts the agent in the first orca.yaml default tab, as the desktop does (#26106)
* fix(worktrees): the host's create puts its agent in the first default tab, as the window does

With orca.yaml default tabs, the window's create starts the agent in the first
default tab: that tab takes the template's title and color, and the template's
command does not run beside the agent. The host's create started the agent in
its own untitled tab and then created every default tab, the first one running
its command too, so the workspace had one tab more than the window's create.
Now the agent's tab takes the first template's place.

* fix(worktrees): title the agent's default tab as a tab, so the phone keeps the agent's status

The host's create titled the agent's tab by renaming the terminal, which
writes the pane's own title stamped to outrank every title the agent sets
later. On a headless host the phone reads status from those titles, so the
agent tab showed the template title forever and lost its working/idle
status. The window's create only sets the tab's custom title.

The host now titles the tab the same way: the window gets the tab's custom
title (not counted as a user rename), and a headless host saves it on the
tab and shows it on the phone until the agent titles itself. The pane's
title is left to the agent.

The host also finds a terminal's tab itself from its handle, so the create
over SSH, which never passed the tab, colors the agent's tab too. Every
default tab is dressed by the same step, so the other tabs' titles are now
kept on a headless host as well, and a failed title no longer skips the
color.

* fix(worktrees): find a default tab through the window's leaf once the window adopts its handle

The host found a provisioned terminal's tab only through the runtime's own
pty record. Once the window's graph sync adopts the handle, that lookup
misses, and the default tab's title and color would be dropped with only a
log line. No create reaches it today (the tab is dressed before the window's
first graph sync), but any wait added before provisioning would. It now falls
back to the window's leaf, as renaming a terminal already does.

Tests: the restart case now saves the title to a real session and rebuilds
the phone's tabs from it; a create through createManagedWorktree with an
agent and default tabs spawns the agent and the second template only and
titles the agent's tab "Dev" without counting it as a user rename.

* test(worktrees): drop type assertions from the default-tab tests

Use a runtime subclass for the protected launch scope and provisioning host,
a typed store and notifier, and typed mocks in place of casts, so the
changed-code quality gate passes.
2026-10-07 10:22:29 -07:00

259 lines
11 KiB
TypeScript

import type { MarkdownDocument } from '../../shared/filesystem-entry-types'
import type { PersistedUIState } from '../../shared/persisted-ui-state-types'
import type { TuiAgent } from '../../shared/tui-agent'
import type {
AgentLaunchTabPublishReply,
AgentLaunchTabPublishRequest
} from '../../shared/agent-launch-tab-publication'
import type { AgentLaunchPaneVerdictEvent } from '../../shared/agent-launch-pane-verdict'
import type { RuntimeNavigationTarget } from '../../shared/runtime-navigation'
import type { TerminalSurfaceCloseTarget } from '../../shared/terminal-surface-close-target'
import type {
WorktreeDefaultTabsLaunch,
WorktreeSetupLaunch,
WorktreeStartupLaunch
} from '../../shared/worktree/launch-types'
import type { FeatureInteractionId } from '../../shared/feature-interactions'
import type { KeybindingActionId } from '../../shared/keybindings'
import type { BrowserFindSource } from '../../shared/browser-find-source'
import type {
BrowserHistoryNavigateCommand,
BrowserPageCommandTarget
} from '../../shared/browser-page-command-target'
import type { BrowserPageZoomCommand } from '../../shared/browser-page-zoom'
import type {
AgentProviderSessionMetadata,
SleepingAgentLaunchConfig
} from '../../shared/agent-session-resume'
import type { TerminalPaneSplitSource } from '../../shared/feature-education-telemetry'
import type {
RuntimeMobileSessionTabMove,
RuntimeTerminalCreateRequestPayload,
RuntimeTerminalPresentation
} from '../../shared/runtime-types'
import type {
RuntimeMobileMarkdownRequest,
RuntimeMobileMarkdownResponse
} from '../../shared/mobile-markdown-document'
import type { TerminalTabCreateReply } from '../../shared/terminal-reveal-identity'
import type {
TerminalTabCloseRequest,
TerminalTabCloseResponse
} from '../../shared/terminal-tab-close'
import type {
SessionTabCloseRequest,
SessionTabCloseResponse
} from '../../shared/session-tab-close'
export type CloseActiveTabPayload = { sourceId: string }
export type UiCommandEventApi = {
get: () => Promise<PersistedUIState>
set: (args: Partial<PersistedUIState>) => Promise<void>
/** Like set, but REJECTS when the update did not reach the host (the web preload's set
* swallows transport failures for offline use). The diff writer needs the distinction:
* folding an unacked patch into its baseline would silently stop retrying it (STA-5781). */
setWithAck?: (args: Partial<PersistedUIState>) => Promise<void>
recordFeatureInteraction: (id: FeatureInteractionId) => Promise<PersistedUIState>
onStateChanged: (callback: (ui: PersistedUIState) => void) => () => void
onOpenSettings: (callback: () => void) => () => void
/** Consumes a one-shot tray/menu-bar "open settings" intent queued before mount. */
consumePendingOpenSettings: () => Promise<boolean>
onOpenSkillShare: (callback: (shareId: string) => void) => () => void
consumePendingSkillShare: () => Promise<string | null>
/** OS "Open With" Markdown/CSV/TSV documents; the local IPC name is retained. */
onOpenMarkdownFiles: (callback: (documents: MarkdownDocument[]) => void) => () => void
/** Drains the "Open With" paths queued before this renderer's listener attached. */
consumePendingMarkdownFileOpens: () => Promise<MarkdownDocument[]>
onOpenSetupGuide: (callback: () => void) => () => void
onOpenFeatureTour: (callback: () => void) => () => void
onOpenCrashReport: (callback: () => void) => () => void
onToggleLeftSidebar: (callback: () => void) => () => void
onToggleRightSidebar: (callback: () => void) => () => void
onToggleWorktreePalette: (callback: () => void) => () => void
onToggleFloatingTerminal: (callback: () => void) => () => void
onTerminalShortcutCaptured: (
callback: (data: { actionId: KeybindingActionId }) => void
) => () => void
onOpenQuickOpen: (callback: () => void) => () => void
onToggleQuickCommandsMenu: (callback: () => void) => () => void
onOpenNewWorkspace: (callback: () => void) => () => void
onDeleteCurrentWorkspace: (callback: () => void) => () => void
onOpenWorkspaceBoard: (callback: () => void) => () => void
onOpenTasks: (callback: () => void) => () => void
onToggleAgentDashboard: (callback: () => void) => () => void
onJumpToWorktreeIndex: (callback: (index: number) => void) => () => void
onJumpToTabIndex: (callback: (index: number) => void) => () => void
onWorktreeHistoryNavigate: (callback: (direction: 'back' | 'forward') => void) => () => void
onNewBrowserTab: (callback: () => void) => () => void
onNewMarkdownTab: (callback: () => void) => () => void
onNewSimulatorTab: (callback: () => void) => () => void
onRequestTabCreate: (
callback: (data: {
requestId: string
url: string
worktreeId?: string
browserPageId?: string
sessionProfileId?: string | null
sessionPartition?: string
activate?: boolean
}) => void
) => () => void
replyTabCreate: (reply: { requestId: string; browserPageId?: string; error?: string }) => void
onRequestTabSetProfile: (
callback: (data: {
requestId: string
browserPageId: string
profileId: string
sessionPartition?: string
}) => void
) => () => void
replyTabSetProfile: (reply: { requestId: string; error?: string }) => void
onRequestTabClose: (
callback: (data: { requestId: string; tabId: string | null; worktreeId?: string }) => void
) => () => void
replyTabClose: (reply: {
requestId: string
error?: string
code?: 'browser_tab_not_found'
}) => void
onNewTerminalTab: (callback: () => void) => () => void
onFocusBrowserAddressBar: (callback: (target: BrowserPageCommandTarget) => void) => () => void
onFindInBrowserPage: (source: BrowserFindSource, callback: () => void) => () => void
onReloadBrowserPage: (callback: (target: BrowserPageCommandTarget) => void) => () => void
onBrowserHistoryNavigate: (
callback: (command: BrowserHistoryNavigateCommand) => void
) => () => void
onZoomBrowserPage: (callback: (command: BrowserPageZoomCommand) => void) => () => void
onScrollBrowserPage?: (
callback: (event: { browserPageId: string; deltaX: number; deltaY: number }) => void
) => () => void
onHardReloadBrowserPage: (callback: (target: BrowserPageCommandTarget) => void) => () => void
onCloseActiveTab: (callback: (payload?: CloseActiveTabPayload) => void) => () => void
onCloseFloatingItem: (callback: (payload: { sourceId: string }) => void) => () => void
onSelectFloatingIndex: (callback: (payload: { index: number }) => void) => () => void
onSwitchTab: (callback: (direction: 1 | -1) => void) => () => void
onSwitchTabAcrossAllTypes: (callback: (direction: 1 | -1) => void) => () => void
onSwitchRecentTab: (callback: () => void) => () => void
onSwitchTerminalTab: (callback: (direction: 1 | -1) => void) => () => void
onCtrlTabKeyDown: (callback: (data: { shiftKey: boolean }) => void) => () => void
onCtrlTabKeyUp: (callback: () => void) => () => void
onToggleStatusBar: (callback: () => void) => () => void
onDictationKeyDown: (callback: () => void) => () => void
onExportPdfRequested: (callback: () => void) => () => void
onAppMenuPaste: (callback: () => void) => () => void
onAppMenuSelectionAction: (callback: (action: 'copy' | 'select-all') => void) => () => void
onEditableContextPaste: (callback: (data: { plainTextOnly: boolean }) => void) => () => void
onActivateWorktree: (
callback: (data: {
repoId: string
worktreeId: string
setup?: WorktreeSetupLaunch
startup?: WorktreeStartupLaunch
defaultTabs?: WorktreeDefaultTabsLaunch
}) => void
) => () => void
onCreateTerminal: (
callback: (data: {
requestId?: string
worktreeId: string
command?: string
cwd?: string
env?: Record<string, string>
launchConfig?: SleepingAgentLaunchConfig
resumeProviderSession?: AgentProviderSessionMetadata
launchToken?: string
launchAgent?: TuiAgent
viewMode?: 'terminal' | 'chat'
title?: string
ptyId?: string
activate?: boolean
focus?: boolean
presentation?: RuntimeTerminalPresentation
surfaceOwner?: false
tabId?: string
leafId?: string
splitFromLeafId?: string
splitDirection?: 'horizontal' | 'vertical'
splitTelemetrySource?: TerminalPaneSplitSource
}) => void
) => () => void
onRequestTerminalCreate: (
callback: (data: RuntimeTerminalCreateRequestPayload) => void
) => () => void
onRequestTerminalTabMount: (
callback: (data: { worktreeId: string; tabId?: string; ptyId?: string }) => void
) => () => void
replyTerminalCreate: (reply: TerminalTabCreateReply) => void
onPublishAgentLaunchTab: (callback: (data: AgentLaunchTabPublishRequest) => void) => () => void
replyAgentLaunchTabPublish: (reply: AgentLaunchTabPublishReply) => void
onAgentLaunchPaneVerdict: (callback: (data: AgentLaunchPaneVerdictEvent) => void) => () => void
onSplitTerminal: (
callback: (data: {
tabId: string
paneRuntimeId: number
direction: 'horizontal' | 'vertical'
command?: string
worktreeId?: string
sourceLeafId?: string
telemetrySource?: TerminalPaneSplitSource
newLeafId?: string
}) => void
) => () => void
onRenameTerminal: (
callback: (data: { tabId: string; title: string | null; recordInteraction?: false }) => void
) => () => void
onFocusTerminal: (
callback: (data: {
tabId: string
worktreeId: string
leafId?: string | null
ackPaneKeyOnSuccess?: string
flashFocusedPane?: boolean
scrollToBottomIfOutputSinceLastView?: boolean
}) => void
) => () => void
onFocusEditorTab: (
callback: (data: {
tabId: string
worktreeId: string
/** The user clicked a notification, so revealing the tab is navigation and not a courtesy. */
userInitiated?: boolean
}) => void
) => () => void
onCloseSessionTab: (callback: (data: { tabId: string; worktreeId: string }) => void) => () => void
onSessionTabCloseRequest: (callback: (request: SessionTabCloseRequest) => void) => () => void
respondSessionTabClose: (response: SessionTabCloseResponse) => void
onMoveSessionTab: (
callback: (data: { worktreeId: string } & RuntimeMobileSessionTabMove) => void
) => () => void
onOpenFileFromMobile: (
callback: (data: {
worktreeId: string
filePath: string
relativePath: string
runtimeEnvironmentId?: string
navigation?: RuntimeNavigationTarget
}) => void
) => () => void
onOpenDiffFromMobile: (
callback: (data: {
worktreeId: string
filePath: string
relativePath: string
staged: boolean
runtimeEnvironmentId?: string
navigation?: RuntimeNavigationTarget
}) => void
) => () => void
onMobileMarkdownRequest: (callback: (request: RuntimeMobileMarkdownRequest) => void) => () => void
respondMobileMarkdownRequest: (response: RuntimeMobileMarkdownResponse) => void
onCloseTerminal: (callback: (target: TerminalSurfaceCloseTarget) => void) => () => void
onTerminalTabCloseRequest: (callback: (request: TerminalTabCloseRequest) => void) => () => void
respondTerminalTabClose: (response: TerminalTabCloseResponse) => void
onSleepWorktree: (callback: (data: { worktreeId: string }) => void) => () => void
onResumeSleepingAgents: (callback: (data: { worktreeId: string }) => void) => () => void
onTerminalZoom: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void
onSystemResumed: (callback: () => void) => () => void
}