mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 00:03:20 +00:00
* Show worktree-attributed child agents inline - Keep orchestration worker rows visible when hook status arrives before the renderer has a tab for the pane. - Show child agent lineage expanded by default with clearer parent/child styling. - Tighten worktree list indentation so grouped and nested rows align correctly. * Sync agent lineage from runtime graph - Return main-owned orchestration metadata during window graph sync so live, retained, and title-derived agent rows stay grouped under the right parent. - Match lineage by terminal handle when pane keys are unavailable after teardown, and ignore stale completed dispatches. - Adjust sidebar indentation and compact agent row chrome/readability. * Preserve agent lineage across status replay and worktree cleanup - Enrich agent status snapshots with runtime terminal and orchestration metadata - Share lineage tree building between dashboard and sidebar, including coordinator fallback - Avoid applying stale runtime dispatch metadata to current hook statuses - Use worktree attribution for PR refresh and live status removal before tabs mount
119 lines
4.2 KiB
TypeScript
119 lines
4.2 KiB
TypeScript
import { BrowserWindow, ipcMain } from 'electron'
|
|
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
|
|
import type {
|
|
RuntimeBrowserDriverState,
|
|
RuntimeStatus,
|
|
RuntimeSyncWindowGraphResult,
|
|
RuntimeSyncWindowGraph,
|
|
RuntimeTerminalDriverState
|
|
} from '../../shared/runtime-types'
|
|
import type { RuntimeRpcResponse } from '../../shared/runtime-rpc-envelope'
|
|
import { RpcDispatcher } from '../runtime/rpc/dispatcher'
|
|
|
|
export function registerRuntimeHandlers(runtime: OrcaRuntimeService): void {
|
|
ipcMain.removeHandler('runtime:syncWindowGraph')
|
|
ipcMain.removeHandler('runtime:getStatus')
|
|
ipcMain.removeHandler('runtime:call')
|
|
|
|
ipcMain.handle(
|
|
'runtime:syncWindowGraph',
|
|
(event, graph: RuntimeSyncWindowGraph): RuntimeSyncWindowGraphResult => {
|
|
const window = BrowserWindow.fromWebContents(event.sender)
|
|
if (!window) {
|
|
throw new Error('Runtime graph sync must originate from a BrowserWindow')
|
|
}
|
|
return runtime.syncWindowGraph(window.id, graph)
|
|
}
|
|
)
|
|
|
|
ipcMain.handle('runtime:getStatus', (): RuntimeStatus => {
|
|
return runtime.getStatus()
|
|
})
|
|
|
|
ipcMain.handle(
|
|
'runtime:call',
|
|
async (
|
|
_event,
|
|
args: { method: string; params?: unknown }
|
|
): Promise<RuntimeRpcResponse<unknown>> => {
|
|
return (await new RpcDispatcher({ runtime }).dispatch({
|
|
id: 'desktop-ipc',
|
|
authToken: 'desktop-ipc',
|
|
method: args.method,
|
|
params: args.params
|
|
})) as RuntimeRpcResponse<unknown>
|
|
}
|
|
)
|
|
|
|
ipcMain.removeHandler('runtime:getTerminalFitOverrides')
|
|
ipcMain.handle(
|
|
'runtime:getTerminalFitOverrides',
|
|
(): { ptyId: string; mode: 'mobile-fit'; cols: number; rows: number }[] => {
|
|
const overrides = runtime.getAllTerminalFitOverrides()
|
|
return Array.from(overrides.entries()).map(([ptyId, override]) => ({
|
|
ptyId,
|
|
...override
|
|
}))
|
|
}
|
|
)
|
|
|
|
ipcMain.removeHandler('runtime:getTerminalDrivers')
|
|
ipcMain.handle(
|
|
'runtime:getTerminalDrivers',
|
|
(): { ptyId: string; driver: RuntimeTerminalDriverState }[] => {
|
|
const drivers = runtime.getAllTerminalDrivers()
|
|
return Array.from(drivers.entries()).map(([ptyId, driver]) => ({ ptyId, driver }))
|
|
}
|
|
)
|
|
|
|
ipcMain.removeHandler('runtime:getBrowserDrivers')
|
|
ipcMain.handle(
|
|
'runtime:getBrowserDrivers',
|
|
(): { browserPageId: string; driver: RuntimeBrowserDriverState }[] => {
|
|
const drivers = runtime.getAllBrowserDrivers()
|
|
return Array.from(drivers.entries()).map(([browserPageId, driver]) => ({
|
|
browserPageId,
|
|
driver
|
|
}))
|
|
}
|
|
)
|
|
|
|
// Why: the desktop "Restore" button sets the display mode to 'desktop' and
|
|
// applies it, which restores the PTY to its original dimensions and emits
|
|
// a 'resized' event to any active mobile subscriber. This uses the same
|
|
// code path as the mobile toggle button (terminal.setDisplayMode RPC).
|
|
ipcMain.removeHandler('runtime:restoreTerminalFit')
|
|
ipcMain.handle('runtime:restoreTerminalFit', async (_event, args: { ptyId: string }) => {
|
|
// Why: this IPC powers the desktop "Take back" button. Beyond restoring
|
|
// PTY dims (the original semantic), it now also reclaims the input
|
|
// floor for the desktop via the driver state machine. The lock banner
|
|
// unmounts and desktop input/resize are unblocked until the next
|
|
// mobile interaction takes the floor again. See
|
|
// docs/mobile-presence-lock.md.
|
|
//
|
|
// Why async: reclaimTerminalForDesktop awaits applyMobileDisplayMode's
|
|
// PTY-resize chain. Returning the unresolved Promise to ipcMain made
|
|
// Electron try to structured-clone a Promise — "An object could not
|
|
// be cloned" error — and the renderer's restoreTerminalFit() rejected
|
|
// with no useful info.
|
|
try {
|
|
const reclaimed = await runtime.reclaimTerminalForDesktop(args.ptyId)
|
|
return { restored: reclaimed }
|
|
} catch {
|
|
return { restored: false }
|
|
}
|
|
})
|
|
|
|
ipcMain.removeHandler('runtime:reclaimBrowserForDesktop')
|
|
ipcMain.handle(
|
|
'runtime:reclaimBrowserForDesktop',
|
|
(_event, args: { browserPageId: string }): { reclaimed: boolean } => {
|
|
try {
|
|
return { reclaimed: runtime.reclaimBrowserForDesktop(args.browserPageId) }
|
|
} catch {
|
|
return { reclaimed: false }
|
|
}
|
|
}
|
|
)
|
|
}
|