mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
* feat(terminal): the window's store shows main's pane start status (layout-owner build) A status-only frame changes one store entry and no pane tree, so the pane's view stays mounted. * refactor(terminal): keep the frame sync within its line budget * feat(terminal): every pane main starts shows 'starting' from before it is written (layout-owner build) * feat(terminal): a pane main is starting waits and attaches; a failed start shows why (layout-owner build) Fixes owner-visible row 15: a failed agent or setup start no longer opens a plain shell. * test(terminal): iterate store listeners directly * fix(terminal): an exit that reports no code keeps its pane, as lost contact (layout-owner build) * test(terminal): a pane main started attaches to main's terminal at its own grid * test(terminal): detach census tests assert main's move and frame with the switch on * fix(terminal): a restored pane paints history only from its terminal's host, never the window's copy (layout-owner build only) * fix(terminal): main releases a painted cold restore only for the pane's current terminal (layout-owner build only) * test(terminal): typed ack args in the painted cold-restore test * test(terminal): exited-pane tests assert main's close with the switch on These 8 have failed switch-on since #31130 moved the exit close to main. * feat(layout): main starts the panes a client's startPane or wake names, through its one restored-pane starter (layout-owner build only) The model's start and stop effects reach the runtime once the command is written, never the reply. startPane starts a pane whose binding has a start status, and a slept pane from its record. A wake keeps a pane's resume record until its start binds; a failed start keeps it and shows why. With the switch on, main owns a running agent's wake even with a window open. * fix(layout): a window's wake of a pane it resumed itself is record-only, so main does not start it again (layout-owner build only) * test(layout): a client's startPane starts the pane before any other trigger * Revert "fix(terminal): a restored pane paints history only from its terminal's host, never the window's copy (layout-owner build only)" This reverts commita646d89b26. * test(terminal): the cold-restore repaint ack names its pane in the layout-owner build * fix(layout): a wake keeps the pane's resume record in the pane-owned form, so a host crash resumes the agent again (layout-owner build only) * test(layout): a failing start in the pane-start runner test throws * feat(terminal): main writes and starts every split of a pane it holds; the window attaches (layout-owner build) The window's split, a startup split with its command, and a CLI split all go through main's terminal.split; main's frame mounts the pane, with or without a window intent. Deletes the window's split wait and withdraw, the split appear intents, and the queued-split skip and cancel. The e2e helper uses the real split. A startup split leaves focus on its source pane. * fix(terminal): the attach-only wait leaves sleeping, resuming and held panes to their restore path * fix(terminal): a split pane attaches to the terminal main bound it to; a taken pane id is refused Also restores the proof that a split closed while it starts gets one answer and leaves no pane or start status behind. * Reapply "fix(terminal): a restored pane paints history only from its terminal's host, never the window's copy (layout-owner build only)" This reverts commit420e605ad0. * fix(layout): a trigger between a restored pane's start and its bind write joins that start, not a second one (layout-owner build only) * fix(terminal): a window split main cannot start still shows its pane, failed, with why * fix(terminal): keep the switch import the D1b-1 merge dropped from the layout restore * feat(layout): main's starter shows a restored pane starting before any await, and a failed start with its reason; startPane starts a pane whose bound terminal ended (layout-owner build only) * feat(terminal): a window's attach-only view of a pane attaches to its terminal or starts nothing, keeping the binding for main's start (layout-owner build only) * feat(terminal): a restored pane with no live terminal asks main to start it and attaches once main binds it; the window's wake, hibernated wake, restart-on-show and gone-terminal fallbacks route through main (layout-owner build only) * fix(terminal): check a taken pane id first, keep the start's own error, clear a gone pane's status Three test stores gain the pane-start field the window's store always has. * test(terminal): the admission stub answers whether its pane is still written; a closed pane's failed start clears * test(terminal): with the switch on, the window's restores, wakes and restart replacements ask main to start the pane and attach to the terminal main binds, starting and resuming nothing themselves * refactor(terminal): the rebound workspaces' bindings show through one call in the bindings module * fix(terminal): a held pane connects as today so main's refusal holds it; main's start follows output; a capacity death after main's resume keeps the resume (layout-owner build only) * fix(layout): main passes its start options only for commands that start panes; a windowed open resumes the agent under main (layout-owner build only) * refactor(terminal): break the pane start module cycle; typed test fixtures * test(terminal): a pane main wrote and the user closed mid-start drops its failed status * fix(terminal): every waiter on main's start runs, a failed or refused start settles and is asked again on the next view, a hidden gone pane waits for its view, a twin main hands back stays on today's path, and lost contact never starts a pane (layout-owner build only) * test(terminal): iterate the store listeners directly * test(terminal): a cold-restore paint applies no window resume with the switch on * fix(terminal): a split main starts is listed to phones and session.tabs without a window's report * test(terminal): name the main pane start test helper as a fixture * fix(terminal): a reconnect shows its pane starting and why it failed; a lost owner race shows no failure; a split no one asked to see keeps the phones' tab * fix(terminal): a kept create another owner won shows no failure; the window attaches to the winner * fix(terminal): a pane main starts at relaunch keeps its full history and starts before the window's graph lists its tab (layout-owner build only) * feat(terminal): main replaces the empty shell a hibernated agent left and resumes the agent when a view asks; a start main could not attach shows main's own terminal_not_started (layout-owner build only) * test(terminal): main seeds its copy of a restored pane even when a window declared one, with the switch on * fix(terminal): a pane main started hidden paints once when its view attaches (switch-on builds only) The view's attach snapshot, taken after main unmarks the hidden gate, already holds the bytes main dropped, so the reveal no longer asks a second restore from main's buffer. * refactor(terminal): main's pane start reads the pane from its own layout, not the client projection (switch-on builds only) * fix(terminal): a pane restart stops its terminal before writing the pane unbound, so a stop that fails keeps the pane (switch-on builds only) * test(terminal): a start main could not bind shows terminal_not_started through the window's wait, from one copy site (switch-on builds only) * fix(terminal): main's pane start resumes an agent recorded under the pane's number, as the window did; stale switch-on tests show main's start and the known banner loss (switch-on builds only) * fix(terminal): review round: a visible view alone retires main's hidden-start drops, by episode; main reads a split pane's numbered record by its number; the window keeps a shell main kept (switch-on builds only) * fix(terminal): main's start of a pane a window view is attaching to leaves it visible, so the view paints once (switch-on builds only) * fix(terminal): a window view that attaches while main starts its pane takes main's start, with no second provider attach (switch-on builds only) * fix(terminal): review round: a view takes main's start only when it carries a payload and still owns its terminal; attaching views are counted, keep main's pre-spawn mark off, and a failed attach hides only what it kept visible (switch-on builds only) * test(terminal): checked test doubles in the view-join and numbered-record tests; reattach options fit the file budget * test(e2e): with the switch on, the mount-authority spec keeps the window's bindings, which only main's frames write Claude-Session: W5b * test(e2e): the mount-authority spec fits its line budget; the layout-owner build check is one helper Claude-Session: W5b * test(layout): an unbound pane holding a resume record starts and keeps the record, since main's start resumes from it Claude-Session: W5b * fix(terminal): a pane main resumed holds a running agent's record, so it reads awake, not sleeping (switch-on builds only) Claude-Session: W5b * fix(terminal): in a split where no pane owns a numbered record, main resumes the tab's one conversation in pane 1 only (switch-on builds only) Claude-Session: W5b * fix(terminal): main marks only the record a resume started from live, never a sleep captured after it (switch-on builds only) Claude-Session: W5b * test(terminal): with the switch on, a windowless resume leaves the pane's record a running agent's Claude-Session: W5b * test(terminal): the resumed-record test double typechecks Claude-Session: W5b * refactor(terminal): with the switch off, main's pane start reads no resume record for the live mark Claude-Session: W5b * refactor(terminal): the merged files fit their line budget: the folder-fallback notice and a hidden pane's wait for its first view are helpers, and the answering-transport lookup moves to its one test caller Claude-Session: W5b * refactor(runtime): the window's layout apply fits the runtime's line budget after the main merge Claude-Session: W5b
362 lines
17 KiB
TypeScript
362 lines
17 KiB
TypeScript
import type { RuntimeLinearCommandSurface } from './runtime-linear-command-surface'
|
|
import { installRuntimeLinearCommandSurface } from './runtime-linear-command-surface'
|
|
import { publishWorkspaceViewChanges } from './runtime-workspace-view-change-events'
|
|
import {
|
|
OrcaRuntimeWithSidebarTemporal,
|
|
sidebarFactsRuntime
|
|
} from './sidebar-inputs/orca-runtime-sidebar-temporal'
|
|
import { OrcaRuntimeWithAgentTransitions } from './orca-runtime-agent-transitions'
|
|
import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../shared/sidebar-inputs-switch'
|
|
import { ORCA_LAYOUT_OWNER_IS_MAIN } from '../../shared/layout-owner-switch'
|
|
import type { RuntimeCommandSurfaceHost } from './orca-runtime-core'
|
|
import type {
|
|
AgentLaunchTabPublished,
|
|
AgentLaunchTabPublishRequest
|
|
} from '../../shared/agent-launch-tab-publication'
|
|
import type {
|
|
AgentLaunchPaneAddress,
|
|
AgentLaunchPaneVerdict
|
|
} from '../../shared/agent-launch-pane-verdict'
|
|
import { registerWorktreeChangeInvalidator } from '../ipc/worktree-change-invalidators'
|
|
import type { AgentSessionRecordStore } from './agent-session-record-store'
|
|
import { peekOpenedAgentSessionRecordStore } from './agent-session-record-store-slot'
|
|
import { createAgentLaunchRecordWarmupGate } from './agent-launch-record-warmup-gate'
|
|
import { WorkspaceLayoutStream, type WorkspaceLayoutReads } from './workspace-layout-stream'
|
|
import type { RemoteLayoutSnapshot } from '../../shared/remote-layout-snapshot'
|
|
import { registerDetectedWorktreeScanInvalidation } from '../ipc/worktrees/listing/register-detected-worktree-scan-invalidation'
|
|
import {
|
|
listReferenceWorkspaces,
|
|
selectReferenceWorkspace,
|
|
listWorkspaceReferences
|
|
} from './runtime-reference-catalog'
|
|
import { findWorkspaceReferences } from './runtime-reference-find'
|
|
import { createReferenceAgentIndex, referenceConnectionHosts } from './runtime-reference-agents'
|
|
import type { RuntimeReferenceFindParams } from '../../shared/runtime-reference-contracts'
|
|
import { getRepoExecutionHostId } from '../../shared/execution-host'
|
|
import type { LayoutApplyReply } from '../../shared/workspace-layout/workspace-layout-command-types'
|
|
import { rekeyMovedPaneOwners } from './moved-pane-owner-rekey'
|
|
import { applyWindowLayoutCommand, type WindowLayoutCommand } from './runtime-layout-apply'
|
|
import type { TerminalCreateOptions } from './runtime-terminal-contracts'
|
|
import {
|
|
installStructuredAgentAvailability,
|
|
type StructuredAgentAvailabilityReader
|
|
} from './structured-agent-availability'
|
|
import { PairedServerRowsProducer } from './sidebar-inputs/paired-server-rows-producer'
|
|
import { RepoIconDataUrls } from './sidebar-inputs/repo-icon-data-urls'
|
|
import { WorkspaceRowsProducer } from './sidebar-inputs/workspace-rows-producer'
|
|
import { getMainHttpClient } from '../network/http-client'
|
|
import { getRuntimeDesktopSurface } from './runtime-desktop-surface'
|
|
import { CollapseWriteOrder } from './workspace-view-collapse-writes'
|
|
import { SidebarInputsFeed } from './sidebar-inputs/sidebar-inputs-feed'
|
|
import { sidebarInputsFeedDomains } from './sidebar-inputs/sidebar-inputs-feed-domains'
|
|
import { requireSwitchedOn } from './sidebar-inputs/require-switched-on'
|
|
import { HEADLESS_RUNTIME_WINDOW_ID } from '../../shared/runtime-session-contracts'
|
|
import { getHostDisplayLabelOverrides } from '../../shared/host-setting-overrides'
|
|
|
|
// Switch point: a window-fed build keeps no sidebar facts layer.
|
|
const OrcaRuntimeSidebarBase: typeof OrcaRuntimeWithAgentTransitions = ORCA_SIDEBAR_INPUTS_IS_MAIN
|
|
? OrcaRuntimeWithSidebarTemporal
|
|
: OrcaRuntimeWithAgentTransitions
|
|
|
|
// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- SAFETY: The existing Linear installer supplies the canonical prototype surface.
|
|
class OrcaRuntimeService extends OrcaRuntimeSidebarBase {
|
|
/** This runtime as its sidebar facts layer; null unless built with ORCA_SIDEBAR_INPUTS=main. */
|
|
get sidebarFacts() {
|
|
return sidebarFactsRuntime(this)
|
|
}
|
|
// Switch point. Idle until relay hosts attach; the fallback refresh runs while a window shows the sidebar or a phone reads the feed.
|
|
readonly pairedServerRows = ORCA_SIDEBAR_INPUTS_IS_MAIN
|
|
? new PairedServerRowsProducer({
|
|
hasDemand: () =>
|
|
this.sidebarInputsFeed?.subscribed === true ||
|
|
(this.authoritativeWindowId !== null &&
|
|
this.authoritativeWindowId !== HEADLESS_RUNTIME_WINDOW_ID &&
|
|
this.store?.getUI?.().sidebarOpen !== false),
|
|
hostLabelOverrides: () => getHostDisplayLabelOverrides(this.getClientSettings())
|
|
})
|
|
: null
|
|
// Switch point: the phone's icon source.
|
|
readonly repoIconDataUrls = ORCA_SIDEBAR_INPUTS_IS_MAIN
|
|
? new RepoIconDataUrls({
|
|
repos: () => this.store?.getRepos() ?? [],
|
|
// Why not the shared desktop client: its net.fetch rejects every manual redirect.
|
|
fetch: (url, init) => {
|
|
const manualRedirectFetch = getRuntimeDesktopSurface().fetchRepoIcon
|
|
return manualRedirectFetch
|
|
? manualRedirectFetch(url, init)
|
|
: getMainHttpClient().fetch(url, init)
|
|
},
|
|
encoder: () => getRuntimeDesktopSurface().encodeRepoIconPng
|
|
})
|
|
: null
|
|
// Switch point: the per-writer ordering behind ui.setCollapsed.
|
|
readonly collapseWriteOrder = ORCA_SIDEBAR_INPUTS_IS_MAIN ? new CollapseWriteOrder() : null
|
|
// Switch point: this host's workspace rows, from the listing worktree.ps reads.
|
|
readonly workspaceRows = ORCA_SIDEBAR_INPUTS_IS_MAIN
|
|
? new WorkspaceRowsProducer({
|
|
store: this.store,
|
|
// Its own scans, not the shared listing: metadata joins at read, so no write waits on git.
|
|
scan: () => this.scanResolvedWorktreeRepos(),
|
|
join: (scans) => this.joinResolvedWorktreeScans(scans),
|
|
visible: (snapshot) => this.visibleResolvedWorktreeSnapshot(snapshot),
|
|
lifecycle: this.workspaceLifecycle,
|
|
onClientEvent: (listener, options) => this.onClientEvent(listener, options)
|
|
})
|
|
: null
|
|
// Switch point: the phone's feed of the producers above; idle until this host's own phone reads.
|
|
readonly sidebarInputsFeed = ORCA_SIDEBAR_INPUTS_IS_MAIN
|
|
? new SidebarInputsFeed(
|
|
() =>
|
|
sidebarInputsFeedDomains({
|
|
facts: requireSwitchedOn(this.sidebarFacts),
|
|
pairedServerRows: requireSwitchedOn(this.pairedServerRows),
|
|
workspaceLifecycle: requireSwitchedOn(this.workspaceLifecycle),
|
|
workspaceRows: requireSwitchedOn(this.workspaceRows),
|
|
repoIcons: requireSwitchedOn(this.repoIconDataUrls),
|
|
store: () => this.store,
|
|
onClientEvent: (listener, options) => this.onClientEvent(listener, options)
|
|
}),
|
|
(event) => this.emitClientEvent(event)
|
|
)
|
|
: null
|
|
|
|
private async selectReferenceCatalogWorkspace(selector: string, cwd?: string) {
|
|
const catalog = listReferenceWorkspaces(
|
|
this.requireStore(),
|
|
this.resolvedWorktrees.peek()?.worktrees
|
|
)
|
|
try {
|
|
return selectReferenceWorkspace(catalog, selector, cwd)
|
|
} catch (error) {
|
|
if (!(error instanceof Error) || error.message !== 'selector_not_found') {
|
|
throw error
|
|
}
|
|
// Why: unsaved worktrees and cold-cache branches only resolve after a worktree scan.
|
|
const warmed = listReferenceWorkspaces(
|
|
this.requireStore(),
|
|
await this.listResolvedWorktrees()
|
|
)
|
|
return selectReferenceWorkspace(warmed, selector, cwd)
|
|
}
|
|
}
|
|
|
|
async listWorkspaceReferences(selector: string, cwd?: string) {
|
|
return listWorkspaceReferences(await this.selectReferenceCatalogWorkspace(selector, cwd))
|
|
}
|
|
|
|
async findWorkspaceReferences(params: RuntimeReferenceFindParams) {
|
|
if (params.worktree && params.repo) {
|
|
throw new Error('--repo and --worktree cannot be combined')
|
|
}
|
|
let workspaces = params.worktree
|
|
? [await this.selectReferenceCatalogWorkspace(params.worktree, params.cwd)]
|
|
: listReferenceWorkspaces(this.requireStore(), this.resolvedWorktrees.peek()?.worktrees)
|
|
if (params.repo) {
|
|
const repo = await this.resolveRepoSelector(params.repo)
|
|
workspaces = workspaces.filter(
|
|
(row) =>
|
|
row.kind === 'worktree' &&
|
|
row.repoId === repo.id &&
|
|
row.hostId === getRepoExecutionHostId(repo)
|
|
)
|
|
}
|
|
return findWorkspaceReferences(params, {
|
|
workspaces,
|
|
agents: async (workspaceKeys) =>
|
|
createReferenceAgentIndex(
|
|
this,
|
|
this.getOrchestrationDbIfAvailable(),
|
|
await this.openAgentSessionRecordStore(),
|
|
workspaceKeys,
|
|
referenceConnectionHosts(this.listRepos())
|
|
)
|
|
})
|
|
}
|
|
|
|
constructor(...args: ConstructorParameters<typeof OrcaRuntimeWithAgentTransitions>) {
|
|
super(...args)
|
|
// Why: the runtime listing re-runs a scan the worktree-change generation overtook and re-lists
|
|
// through this runtime's scan cache, so a worktree change must reach both. The desktop IPC
|
|
// module registers the generation bump at load; a headless host never loads it.
|
|
registerDetectedWorktreeScanInvalidation()
|
|
registerWorktreeChangeInvalidator((repoId) => this.invalidateWorktreeCatalog(repoId))
|
|
publishWorkspaceViewChanges(this.store, (event) => this.emitClientEvent(event))
|
|
}
|
|
|
|
private structuredAgentAvailability?: ReturnType<typeof installStructuredAgentAvailability>
|
|
|
|
/** This host's agent health snapshot and every replacement, from first use; returns the release. */
|
|
subscribeStructuredAgentAvailability(read: StructuredAgentAvailabilityReader): () => void {
|
|
this.structuredAgentAvailability ??= installStructuredAgentAvailability(this.store, this)
|
|
return this.structuredAgentAvailability.subscribe(read)
|
|
}
|
|
|
|
/** Whether a window owns the layout and can show a launch's tab ahead of its process. */
|
|
canPublishAgentLaunchTab(): boolean {
|
|
return Boolean(this.notifier?.publishAgentLaunchTab && this.getAvailableAuthoritativeWindow())
|
|
}
|
|
|
|
/** Shows an agent launch's tab before its process exists, in the window that owns the layout;
|
|
* null when no window does, and the launch's tab then appears when it spawns, as before. */
|
|
publishAgentLaunchTab(
|
|
request: Omit<AgentLaunchTabPublishRequest, 'requestId'>
|
|
): Promise<AgentLaunchTabPublished> | null {
|
|
if (!this.notifier?.publishAgentLaunchTab || !this.getAvailableAuthoritativeWindow()) {
|
|
return null
|
|
}
|
|
return this.notifier.publishAgentLaunchTab(request)
|
|
}
|
|
|
|
/** Tells the window a launch pane's fate: it keeps a final one on the tab, clears a settled one,
|
|
* and takes a withdrawn pane back (the pane alone when the user split the tab). */
|
|
reportAgentLaunchPaneVerdict(
|
|
pane: AgentLaunchPaneAddress,
|
|
verdict: AgentLaunchPaneVerdict
|
|
): void {
|
|
this.notifier?.agentLaunchPaneVerdict?.({ ...pane, verdict })
|
|
}
|
|
|
|
private readonly agentLaunchRecordWarmup = createAgentLaunchRecordWarmupGate({
|
|
isOpen: () => peekOpenedAgentSessionRecordStore() !== null,
|
|
open: () => this.openAgentSessionRecordStore()
|
|
})
|
|
|
|
/** Startup is done; the launch record may open once a client that can launch is here too. */
|
|
noteAgentLaunchStartupSettled(): void {
|
|
this.agentLaunchRecordWarmup.startupSettled()
|
|
}
|
|
|
|
/** A client that can call `agent.launch` connected; its first launch should not open the record. */
|
|
noteAgentLaunchClientReady(): void {
|
|
this.agentLaunchRecordWarmup.launchClientReady()
|
|
}
|
|
|
|
/** Plans an agent launch a window pane's own spawn asks for, with the planner every host launch
|
|
* uses, including its runtime fact. */
|
|
async planAgentPaneLaunch(
|
|
worktreeId: string,
|
|
opts: TerminalCreateOptions
|
|
): Promise<TerminalCreateOptions> {
|
|
const workspace = await this.resolveTerminalWorkspaceLaunchScope(`id:${worktreeId}`)
|
|
return this.resolveAgentTerminalCreateOptions(workspace, opts)
|
|
}
|
|
|
|
/** Whether a running process holds this pane now: such a pane is attached to, never launched into. */
|
|
hasLiveTerminalForPaneKey(paneKey: string): boolean {
|
|
return this.getPtyRecordForPaneKey(paneKey)?.connected === true
|
|
}
|
|
|
|
private readonly workspaceLayoutStream = new WorkspaceLayoutStream({
|
|
store: () => this.store,
|
|
homeHostId: (key) => this.tryGetWorkspaceSessionHostIdForWorktree(key),
|
|
// An empty title is no title.
|
|
liveTitle: (ptyId) => this.getTrackedDisplayTitleForPty(ptyId) || null,
|
|
paneStartFact: (paneKey, binding) => this.paneStartStatuses?.get(paneKey, binding) ?? null
|
|
})
|
|
|
|
protected markPaneLiveTitlesDirty(): void {
|
|
this.workspaceLayoutStream.markLiveTitlesDirty()
|
|
}
|
|
|
|
/** The read-only layout stream (`layout.subscribe`, not advertised yet). */
|
|
subscribeWorkspaceLayouts(
|
|
listener: Parameters<WorkspaceLayoutStream['subscribe']>[0]
|
|
): ReturnType<WorkspaceLayoutStream['subscribe']> {
|
|
return this.workspaceLayoutStream.subscribe(listener)
|
|
}
|
|
|
|
/** Main's one layout projection for readers beside the stream, with or without a window. */
|
|
get workspaceLayoutReads(): WorkspaceLayoutReads {
|
|
return this.workspaceLayoutStream
|
|
}
|
|
|
|
/** A window's layout command (`layout.apply`), applied to the partitions that hold its tab. */
|
|
async applyLayoutCommand(command: WindowLayoutCommand): Promise<LayoutApplyReply> {
|
|
// A workspace's first surface comes after the live terminals main is placing.
|
|
const placing = this.livePlacementSettledFor(command)
|
|
const apply = (): Promise<LayoutApplyReply> =>
|
|
applyWindowLayoutCommand(
|
|
this.requireStore(),
|
|
(worktreeId, tabId) =>
|
|
tabId !== null
|
|
? this.getWorkspaceSessionHostIdsForTab(worktreeId, tabId)
|
|
: [this.tryGetWorkspaceSessionHostIdForWorktree(worktreeId) ?? []].flat(),
|
|
command,
|
|
(move) => rekeyMovedPaneOwners(this, move),
|
|
this.layoutCommandRun(command)
|
|
)
|
|
// A window's sleep or wake lands after the hook statuses main accepted before it, as in the window.
|
|
const reply = await (ORCA_LAYOUT_OWNER_IS_MAIN && ['sleep', 'wake'].includes(command.type)
|
|
? this.inResumeRecordOrder(apply)
|
|
: (placing?.then(apply) ?? apply()))
|
|
// Sent before the reply, so the window can wait for the frame that shows the change.
|
|
return { ...reply, frameSeq: this.workspaceLayoutStream.settledSeq() }
|
|
}
|
|
|
|
/** This desktop's display-only copy of each paired server's layout (`layout.getRemoteSnapshots`). */
|
|
getRemoteLayoutSnapshots(): Record<string, RemoteLayoutSnapshot> {
|
|
return this.store?.getRemoteLayoutSnapshots?.() ?? {}
|
|
}
|
|
|
|
/** The launch record when it is already open, for a reader that must not wait for it. */
|
|
openedAgentSessionRecordStore(): AgentSessionRecordStore | null {
|
|
return peekOpenedAgentSessionRecordStore()
|
|
}
|
|
}
|
|
// Linear installs these methods on the final runtime prototype below.
|
|
// oxlint-disable-next-line typescript/consistent-type-definitions -- SAFETY: This merge models the installed Linear API.
|
|
interface OrcaRuntimeService extends RuntimeLinearCommandSurface {}
|
|
|
|
type OrcaRuntimeServiceExport = RuntimeCommandSurfaceHost<OrcaRuntimeService>
|
|
const OrcaRuntimeServiceExport: {
|
|
new (...args: ConstructorParameters<typeof OrcaRuntimeService>): OrcaRuntimeServiceExport
|
|
readonly prototype: OrcaRuntimeServiceExport
|
|
} = OrcaRuntimeService
|
|
export { OrcaRuntimeServiceExport as OrcaRuntimeService }
|
|
installRuntimeLinearCommandSurface(OrcaRuntimeServiceExport.prototype)
|
|
|
|
export type { LegacyWorkerTerminalRecoveryResult } from './runtime-legacy-worker-terminal-recovery-types'
|
|
export type {
|
|
RuntimeAutomationCreateInput,
|
|
RuntimeAutomationUpdateInput
|
|
} from './runtime-automation-controller'
|
|
export type { SubscriptionRegistration } from './runtime-subscription-registry'
|
|
export type {
|
|
OrchestrationCompatibilityCallerAuthority,
|
|
OrchestrationCompatibilityTerminalAuthority,
|
|
RuntimeTerminalAgentStatusEvent
|
|
} from './runtime-terminal-contracts'
|
|
export type { MessageWaitResult } from './runtime-message-waiters'
|
|
export type { AccountsSnapshot, CodexRateLimitResetRpcResult } from './runtime-account-controller'
|
|
export type {
|
|
MobileNotificationDispatchEvent,
|
|
MobileNotificationDismissEvent,
|
|
MobileNotificationEvent
|
|
} from './runtime-mobile-notification-controller'
|
|
export type { RuntimeTerminalDataMeta } from './runtime-terminal-stream-consumers'
|
|
export type { RemoteFetchResult, RemoteTrackingBase } from './runtime-remote-fetch-controller'
|
|
export {
|
|
computeTerminalTailWaitState,
|
|
tailGainedNewerBlockedReason,
|
|
type TerminalTailWaitState
|
|
} from './terminal-wait-tail-state'
|
|
export { appendNormalizedToTailBuffer } from './terminal-tail-buffer'
|
|
export { appendNormalizedToMultilineTailBufferUnwindowed } from './terminal-tail-redraw-buffer'
|
|
export { buildPreview } from './terminal-tail-state'
|
|
export { buildRestoredTerminalTailSeed } from './terminal-tail-restore-seed'
|
|
export { projectTerminalTailLines } from './orca-runtime-terminal-projection'
|
|
export { resolveWorktreeScanCacheTtlMs } from './runtime-worktree-scan-cache'
|
|
export type {
|
|
RuntimeWorktreeLifecycleEvent,
|
|
DriverState,
|
|
PtyLayoutTarget,
|
|
PtyLayoutState,
|
|
ApplyLayoutResult,
|
|
RuntimeRendererReloadFence
|
|
} from './orca-runtime-core'
|
|
export {
|
|
AUTHORITATIVE_TERMINAL_SNAPSHOT_TIMEOUT_MS,
|
|
WORKTREE_SCAN_ADMIN_RECONCILE_INTERVAL_MS,
|
|
WORKTREE_SCAN_ADMIN_FINGERPRINT_TIMEOUT_MS
|
|
} from './orca-runtime-postlude'
|