mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 10:27:33 +00:00
fix(program-status): announce a finished OpenCode turn reported only over OSC 7501 (#38222)
OpenCode ends every turn with idle and never reports done, so its idle after work became a session boundary and no finished notification fired.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createProgramStatusProjector } from './agent-status-projector'
|
||||
|
||||
const osc = (body: string): string => `\x1b]7501;${body}\x1b\\`
|
||||
|
||||
function lastRootStates(data: string[]) {
|
||||
const projector = createProgramStatusProjector(() => 1000)
|
||||
return data.map((chunk) => {
|
||||
const payloads = projector.process(chunk).payloads
|
||||
const last = payloads.at(-1)?.payload
|
||||
return last && { state: last.state, sessionBoundary: last.sessionBoundary }
|
||||
})
|
||||
}
|
||||
|
||||
describe('createProgramStatusProjector', () => {
|
||||
it('ends an OpenCode turn on idle but not its startup', () => {
|
||||
// Captured from OpenCode 2.2.5, which never reports done.
|
||||
const session = 'ses_e57cc6093ffeU9dbfhOFmwKNgt'
|
||||
expect(
|
||||
lastRootStates([
|
||||
osc('state=idle:app=opencode'),
|
||||
osc('state=working:app=opencode') + osc(`state=working:id=${session}:app=opencode`),
|
||||
osc(`state=clear:id=${session}`) + osc('state=idle:app=opencode')
|
||||
])
|
||||
).toEqual([
|
||||
{ state: 'done', sessionBoundary: true },
|
||||
{ state: 'working', sessionBoundary: undefined },
|
||||
{ state: 'done', sessionBoundary: undefined }
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -154,10 +154,17 @@ export function createProgramStatusProjector(now: () => number = Date.now): Prog
|
||||
case 'error':
|
||||
return { state: 'done', outcome: 'failure' }
|
||||
case 'idle':
|
||||
// Idle may follow startup work (Codex), so it proves rest, not cancellation; interrupt keystrokes own that verdict.
|
||||
if (newInvocation || lastRoot === null || lastRoot.sessionBoundary === true) {
|
||||
return { state: 'done', sessionBoundary: true }
|
||||
}
|
||||
// Keep unseen done results when the same invocation rests.
|
||||
return !newInvocation && lastRoot?.state === 'done' && lastRoot.sessionBoundary !== true
|
||||
? lastRoot
|
||||
if (lastRoot.state === 'done') {
|
||||
return lastRoot
|
||||
}
|
||||
// OpenCode ends every turn with idle, never done, so its idle after work is a turn end with no verdict.
|
||||
// Others' idle may follow startup work (Codex), so it proves rest; interrupt keystrokes own cancellation.
|
||||
return root.app === 'opencode'
|
||||
? { state: 'done' }
|
||||
: { state: 'done', sessionBoundary: true }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user