mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 16:03:45 +00:00
Preserve OpenCode session across command completion, control SessionStart emission (#14866)
* Preserve OpenCode session across command completion - Add session start events and launch token tracking to establish session boundaries - Defer retiring launch authority until OpenCode process actually exits, not just when a command finishes - Fence previous tokens after restarts to prevent status updates from stale sessions - Maps SessionStart as a session boundary for proper turn/state management * Emit SessionStart only from OpenCode, not mimo-code Restrict SessionStart lifecycle events to OpenCode exclusively. Mimo-code no longer emits SessionStart, as it should rely on OpenCode for session boundary signals. This prevents duplicate lifecycle events that could interfere with pane authority tracking and session state management. Also tighten foreground process result validation to reject stale results after title observation changes, fixing a race where a delayed foreground read from a previous cycle would incorrectly retire authority.
This commit is contained in:
@@ -13092,6 +13092,110 @@ describe('OrcaRuntimeService', () => {
|
||||
).toBeUndefined()
|
||||
})
|
||||
|
||||
it('keeps OpenCode launch authority while command-finished leaves it in foreground', async () => {
|
||||
const spawn = vi.fn().mockResolvedValue({ id: 'pty-opencode', incarnationId: 'process-1' })
|
||||
const retireAuthority = vi.fn()
|
||||
const getForegroundProcess = vi.fn(async () => 'opencode')
|
||||
const runtime = new OrcaRuntimeService(store, undefined, {
|
||||
attestAgentHookCompatibilityAuthority: (candidate) => ({
|
||||
paneKey: candidate.paneKey,
|
||||
source: 'current_hook'
|
||||
}),
|
||||
retireAgentHookCompatibilityAuthority: retireAuthority
|
||||
})
|
||||
runtime.setPtyController({
|
||||
spawn,
|
||||
write: () => true,
|
||||
kill: () => true,
|
||||
getForegroundProcess
|
||||
})
|
||||
runtime.setNotifier({
|
||||
worktreesChanged: vi.fn(),
|
||||
reposChanged: vi.fn(),
|
||||
activateWorktree: vi.fn(),
|
||||
createTerminal: vi.fn(),
|
||||
revealTerminalSession: vi.fn().mockResolvedValue({ tabId: 'tab-opencode' }),
|
||||
splitTerminal: vi.fn(),
|
||||
renameTerminal: vi.fn(),
|
||||
focusTerminal: vi.fn(),
|
||||
closeTerminal: vi.fn(),
|
||||
sleepWorktree: vi.fn(),
|
||||
terminalFitOverrideChanged: vi.fn(),
|
||||
terminalDriverChanged: vi.fn()
|
||||
})
|
||||
runtime.attachWindow(1)
|
||||
runtime.syncWindowGraph(1, { tabs: [], leaves: [] })
|
||||
|
||||
const terminal = await runtime.createTerminal(`path:${TEST_WORKTREE_PATH}`, {
|
||||
command: 'opencode',
|
||||
launchConfig: { agentCommand: 'opencode', agentArgs: '', agentEnv: {} },
|
||||
launchAgent: 'opencode'
|
||||
})
|
||||
const spawnEnv =
|
||||
(spawn.mock.calls[0]?.[0] as { env?: Record<string, string> } | undefined)?.env ?? {}
|
||||
const evidence = {
|
||||
terminalHandle: terminal.handle,
|
||||
paneKey: spawnEnv.ORCA_PANE_KEY,
|
||||
launchToken: spawnEnv.ORCA_AGENT_LAUNCH_TOKEN
|
||||
}
|
||||
|
||||
runtime.onPtyData('pty-opencode', '\x1b]133;D;0\x07', 100)
|
||||
await vi.waitFor(() => expect(getForegroundProcess).toHaveBeenCalled())
|
||||
|
||||
expect(retireAuthority).not.toHaveBeenCalled()
|
||||
expect(runtime.verifyOrchestrationCompatibilityCaller(evidence)).not.toBeNull()
|
||||
})
|
||||
|
||||
it('ignores a stale OpenCode foreground result after a newer title observation', async () => {
|
||||
let resolveForegroundProcess: ((process: string | null) => void) | undefined
|
||||
const foregroundProcess = new Promise<string | null>((resolve) => {
|
||||
resolveForegroundProcess = resolve
|
||||
})
|
||||
const spawn = vi.fn().mockResolvedValue({ id: 'pty-opencode-race', incarnationId: 'process-1' })
|
||||
const retireAuthority = vi.fn()
|
||||
const getForegroundProcess = vi.fn(() => foregroundProcess)
|
||||
const runtime = new OrcaRuntimeService(store, undefined, {
|
||||
retireAgentHookCompatibilityAuthority: retireAuthority
|
||||
})
|
||||
runtime.setPtyController({
|
||||
spawn,
|
||||
write: () => true,
|
||||
kill: () => true,
|
||||
getForegroundProcess
|
||||
})
|
||||
runtime.setNotifier({
|
||||
worktreesChanged: vi.fn(),
|
||||
reposChanged: vi.fn(),
|
||||
activateWorktree: vi.fn(),
|
||||
createTerminal: vi.fn(),
|
||||
revealTerminalSession: vi.fn().mockResolvedValue({ tabId: 'tab-opencode-race' }),
|
||||
splitTerminal: vi.fn(),
|
||||
renameTerminal: vi.fn(),
|
||||
focusTerminal: vi.fn(),
|
||||
closeTerminal: vi.fn(),
|
||||
sleepWorktree: vi.fn(),
|
||||
terminalFitOverrideChanged: vi.fn(),
|
||||
terminalDriverChanged: vi.fn()
|
||||
})
|
||||
runtime.attachWindow(1)
|
||||
runtime.syncWindowGraph(1, { tabs: [], leaves: [] })
|
||||
|
||||
await runtime.createTerminal(`path:${TEST_WORKTREE_PATH}`, {
|
||||
command: 'opencode',
|
||||
launchConfig: { agentCommand: 'opencode', agentArgs: '', agentEnv: {} },
|
||||
launchAgent: 'opencode'
|
||||
})
|
||||
|
||||
runtime.onPtyData('pty-opencode-race', '\x1b]133;D;0\x07', 100)
|
||||
await vi.waitFor(() => expect(getForegroundProcess).toHaveBeenCalled())
|
||||
runtime.onPtyData('pty-opencode-race', '\x1b]0;OpenCode working\x07', 101)
|
||||
resolveForegroundProcess?.(null)
|
||||
await foregroundProcess
|
||||
await Promise.resolve()
|
||||
|
||||
expect(retireAuthority).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('retires only receipted restored PTY authority on command completion and exit', () => {
|
||||
const retireAuthority = vi.fn()
|
||||
const runtime = new OrcaRuntimeService(store, undefined, {
|
||||
|
||||
Reference in New Issue
Block a user