mirror of
https://github.com/stablyai/orca.git
synced 2026-10-09 00:02:39 +00:00
* docs(cli): file open/diff/open-changed say they switch the user's view and are for user requests only Refs #9944 * fix(cli): file open/diff/open-changed leave the user's view alone unless --focus `orca file open`, `file diff` and `file open-changed` always switched the desktop to the target worktree, selected the tab and revealed it in the sidebar. An agent skill that opens its answer pulled the user out of whatever they were typing in (#9944), and a phone opening a file moved the desktop too. The commands now add the tab in its worktree without changing anything on screen, including when that worktree is the one being viewed: the new tab is added to the tab bar but the active tab, tab type and focus stay put. In a worktree the user is not viewing, the tab becomes that worktree's selection so it is in front when they go there. `--focus` keeps today's behavior. files.open / files.openDiff take an optional `navigation` target (the existing RUNTIME_NAVIGATION_TARGETS vocabulary); the CLI sends 'all' for --focus, like `worktree create --activate`, and nothing otherwise. The renderer moves the host view only when the target reaches the host; a missing field (phones, older CLIs) leaves it still. Editor opens for a worktree other than the on-screen one no longer write the global activeFileId/activeTabType. Refs #9944 * test(cli): justify the window and runtime stubs in the file-open notification test * fix(cli): keep phone file opens switching the desktop; the CLI asks for 'caller' Phone opens send no `navigation` field, and the phone's diff-review "Open in session" relies on the desktop selecting the diff it opened. A missing field now keeps the original switch exactly; the CLI says what it wants instead: 'caller' (no host move) by default and 'all' for --focus. Older CLIs, which send nothing, keep switching as they always have. Refs #9944 * fix(cli): background file opens select the tab without counting as a visit A CLI open into a worktree the user is not viewing selected the new tab with the same activation a user click uses, which stamps lastFocusedAt and the group's recency list. The worktree jump palette sorts recent tabs by that time, so every agent `orca file open` into another worktree jumped to the top of the user's recent tabs. Editor opens now take a selection mode: 'focus' (default, unchanged), 'background' (select within its worktree without recording focus or recency) and 'none' (add only). createUnifiedTab and activateTab gain recordFocus:false for the background case. Also: tests for reopening an already-open file or diff without --focus, a comment that file opens move only the host window ('all' acts as 'host'), root help lines back under 100 columns, and an accurate remote test title. Refs #9944 * fix(tabs): a background-selected tab still joins its group's tab history recordFocus:false skipped both the focus-time stamp and the group's recentTabIds append while still making the tab the group's active tab. Ctrl+Tab looks the active tab up in that history, so after a background CLI open it did nothing (or went to the wrong tab) once the user switched to that worktree, and hydrate kept the broken history across a restart. Only the focus-time stamp is skipped now; the jump palette's recent rows sort by that alone, so the palette fix stands. Refs #9944 * fix(cli): file open/diff/open-changed --focus help says it brings the user to the file The three commands borrowed the shared --focus line written for terminal create ("Reveal the created terminal session in Orca"). They now use the per-command flag help table; terminal create's line is unchanged. Refs #9944
162 lines
6.2 KiB
TypeScript
162 lines
6.2 KiB
TypeScript
import type { SleepingAgentLaunchConfig } from '../../shared/agent-session-resume'
|
|
import type { TerminalPaneSplitSource } from '../../shared/feature-education-telemetry'
|
|
import type { TerminalRevealIdentity } from '../../shared/terminal-reveal-identity'
|
|
import type { TuiAgent } from '../../shared/tui-agent'
|
|
import type { RuntimeNavigationTarget } from '../../shared/runtime-navigation'
|
|
import type { ClientHostedBrowserRowsEvent } from '../../shared/client-hosted-browser-rows'
|
|
import type {
|
|
WorktreeBaseStatusEvent,
|
|
WorktreeRemoteBranchConflictEvent
|
|
} from '../../shared/worktree/base-ref-drift-types'
|
|
import type { CreateWorktreeResult } from '../../shared/worktree/create-types'
|
|
import type { WorktreeStartupLaunch } from '../../shared/worktree/launch-types'
|
|
import type {
|
|
RuntimeBrowserDriverState,
|
|
RuntimeMarkdownReadTabResult,
|
|
RuntimeMarkdownSaveTabResult,
|
|
RuntimeMobileSessionTabMove,
|
|
RuntimeTerminalDriverState,
|
|
RuntimeTerminalPresentation
|
|
} from '../../shared/runtime-types'
|
|
|
|
type DriverState = RuntimeTerminalDriverState
|
|
|
|
export type RuntimeNotifier = {
|
|
automationsChanged?(payload: {
|
|
selector?: { kind: 'self' } | { kind: 'ssh'; targetId: string } | { kind: 'orphan' }
|
|
reason?: 'definition' | 'run' | 'usage'
|
|
}): void
|
|
worktreesChanged(repoId: string, renamed?: { oldWorktreeId: string; newWorktreeId: string }): void
|
|
worktreeBaseStatus?(event: WorktreeBaseStatusEvent): void
|
|
worktreeRemoteBranchConflict?(event: WorktreeRemoteBranchConflictEvent): void
|
|
reposChanged(): void
|
|
activateWorktree(
|
|
repoId: string,
|
|
worktreeId: string,
|
|
setup?: CreateWorktreeResult['setup'],
|
|
startup?: WorktreeStartupLaunch,
|
|
defaultTabs?: CreateWorktreeResult['defaultTabs']
|
|
): void
|
|
createTerminal(
|
|
worktreeId: string,
|
|
opts: {
|
|
command?: string
|
|
cwd?: string
|
|
env?: Record<string, string>
|
|
title?: string
|
|
presentation?: RuntimeTerminalPresentation
|
|
}
|
|
): void
|
|
revealTerminalSession?(
|
|
worktreeId: string,
|
|
opts: {
|
|
ptyId: string
|
|
title?: string | null
|
|
cwd?: string
|
|
launchConfig?: SleepingAgentLaunchConfig
|
|
launchToken?: string
|
|
launchAgent?: TuiAgent
|
|
viewMode?: 'terminal' | 'chat'
|
|
activate?: boolean
|
|
presentation?: RuntimeTerminalPresentation
|
|
surfaceOwner?: false
|
|
tabId?: string
|
|
leafId?: string
|
|
splitFromLeafId?: string
|
|
splitDirection?: 'horizontal' | 'vertical'
|
|
splitTelemetrySource?: TerminalPaneSplitSource
|
|
focus?: boolean
|
|
expectedProcessIdentity?: {
|
|
terminalHandle: string
|
|
incarnationId: string
|
|
}
|
|
}
|
|
):
|
|
| Promise<{ tabId: string; title?: string | null; identity?: TerminalRevealIdentity }>
|
|
| { tabId: string; title?: string | null; identity?: TerminalRevealIdentity }
|
|
| void
|
|
resolveLegacyWorkerTerminalRecovery?(
|
|
paneKey: string,
|
|
resolution: 'adopted' | 'exited' | 'rolled_back',
|
|
ptyId?: string
|
|
): void
|
|
/** The fence lives in the workspace session, which a live renderer only re-reads at startup. */
|
|
splitTerminal(
|
|
tabId: string,
|
|
paneRuntimeId: number,
|
|
opts: {
|
|
direction: 'horizontal' | 'vertical'
|
|
command?: string
|
|
worktreeId?: string
|
|
sourceLeafId?: string
|
|
telemetrySource?: TerminalPaneSplitSource
|
|
newLeafId?: string
|
|
}
|
|
): void
|
|
renameTerminal(tabId: string, title: string | null): void
|
|
focusTerminal(tabId: string, worktreeId: string, leafId?: string | null): void
|
|
focusEditorTab?(tabId: string, worktreeId: string): void
|
|
closeSessionTab?(tabId: string, worktreeId: string): void | Promise<void>
|
|
moveSessionTab?(worktreeId: string, move: RuntimeMobileSessionTabMove): void
|
|
/**
|
|
* Acts only on the host's own window: 'host'/'all' move it, 'caller'/'clients' open without moving
|
|
* it, absent keeps the original switch. Paired clients are never navigated (intended; 'all' == 'host').
|
|
*/
|
|
openFile?(
|
|
worktreeId: string,
|
|
filePath: string,
|
|
relativePath: string,
|
|
runtimeEnvironmentId?: string | null,
|
|
navigation?: RuntimeNavigationTarget
|
|
): void
|
|
openDiff?(
|
|
worktreeId: string,
|
|
filePath: string,
|
|
relativePath: string,
|
|
staged: boolean,
|
|
runtimeEnvironmentId?: string | null,
|
|
navigation?: RuntimeNavigationTarget
|
|
): void
|
|
readMobileMarkdownTab?(worktreeId: string, tabId: string): Promise<RuntimeMarkdownReadTabResult>
|
|
saveMobileMarkdownTab?(
|
|
worktreeId: string,
|
|
tabId: string,
|
|
baseVersion: string,
|
|
content: string
|
|
): Promise<RuntimeMarkdownSaveTabResult>
|
|
/** Closes the whole tab. */
|
|
closeTerminal(tabId: string): void
|
|
/** Drops one split pane main already closed; never closes its tab. */
|
|
closeTerminalPane?(tabId: string, leafId: string): void
|
|
closeTerminalTab?(
|
|
tabId: string,
|
|
options?: { localPtyTeardownOwnedExternally?: boolean; force?: boolean }
|
|
): Promise<void>
|
|
sleepWorktree(worktreeId: string): void
|
|
// Why: a phone opening a worktree wakes its slept agents by asking the host
|
|
// renderer to run its own navigation-free wake (experimental agent sleep);
|
|
// the runtime has no in-memory sleeping records or wake authority. Optional to
|
|
// match the many renderer-backed notifier methods only the real bridge wires.
|
|
resumeSleepingAgents?(worktreeId: string): void
|
|
terminalFitOverrideChanged(
|
|
ptyId: string,
|
|
mode: 'mobile-fit' | 'remote-desktop-fit' | 'desktop-fit',
|
|
cols: number,
|
|
rows: number
|
|
): void
|
|
// Why: presence-based lock signal — desktop renderer mounts the lock
|
|
// banner when `driver.kind === 'mobile'` and unmounts otherwise. The
|
|
// structured payload (vs a `locked: boolean`) carries the active mobile
|
|
// actor's clientId so the renderer can disambiguate multi-phone scenarios
|
|
// and so a future write coordinator can use the same signal as scheduling
|
|
// input. See docs/mobile-presence-lock.md.
|
|
terminalDriverChanged(ptyId: string, driver: DriverState): void
|
|
nativeChatLaunchDraftResolved?(
|
|
tabId: string,
|
|
resolution: { text: string; createdAt: number }
|
|
): void
|
|
browserDriverChanged?(browserPageId: string, driver: RuntimeBrowserDriverState): void
|
|
browserRemoteViewersChanged?(browserPageId: string, hasRemoteViewers: boolean): void
|
|
clientHostedBrowserRowsChanged?(event: ClientHostedBrowserRowsEvent): void
|
|
}
|