mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 16:02:11 +00:00
* feat(native-chat): report Codex background tasks in the chat strip The background-tasks strip works for Claude only; a structured Codex session shows nothing in it. Feed it from the Codex app-server stream. The strip stands for work that OUTLIVED a turn, which is what the monitoring header, Claude's foreground suppression, and the conversation command gate all already assume. Codex has no `is_backgrounded` flag, so that fact is derived from the turn boundary: a `subAgentActivity` child or a primary-thread `commandExecution` becomes visible once the turn it belongs to completes and it is still unsettled. `turn/completed` only reveals a task here, never settles one — measured on `codex app-server` 0.153.4, a spawn_agent child reported `completed` 95.8s after its parent turn ended. Only a child's own activity kind settles it. Codex exposes no honest stop: `turn/interrupt` on a child ends its turn without emitting a terminal activity item and leaves its shell running. So the state carries a new optional `supportsStopAll: false`, the strip hides a control that could not act, and the blocked-command message asks the user to wait rather than to press a button that does not exist. * refactor(codex): move session teardown out of the structured adapter Merging main crossed the 300-line cap on `codex-structured-session-adapter.ts`: the rewind backend (#19235) and this branch's close-time strip clear both landed in it. The four close paths move verbatim into `codex-structured-session-teardown.ts`, where they funnel through one `settled` helper instead of repeating the notification-retry and background-task cleanup at each call site. No ratchet bump. Also normalize a background task's description once at receipt rather than on every projection; the roster is re-projected on each observed frame. * fix(codex): drop the shell row the journal already settles A `commandExecution` still `inProgress` when its turn ends was reported as a `command` task. But `settleCodexJournalTurn` writes exactly those items to the journal as `state: 'failed'` on `turn/completed` and forgets them, so the strip row would have claimed a shell was still running at the same instant Orca recorded that it was not — two surfaces contradicting each other about the same process. A subagent is the opposite case and stays: the roster pointedly does not sweep at a turn boundary, because children measurably outlive it. That leaves the producer making exactly one claim — these spawn_agent children are still live after their turn — which the durable roster row corroborates. * fix(native-chat): track Codex background execution lifetimes * fix(native-chat): keep running tool groups from claiming completion * Fix runtime catalog and capability expectation * fix(codex): keep a child's name on the command row that outlives it A child agent's commands stay hidden behind its agent row while the child works. Once the child's turn settles with a command still running, that command surfaces as its own row labelled from the raw command string, so 'long_probe' became "/bin/zsh -lc 'ping -c 300 127.0.0.1 > /dev/null'" at the moment that row was the only remaining signal for the work. Qualify a child's command row with the child's label. Resolved on read, so a label registered after the command still lands, and bounded by the existing description cap so admission accounting stays valid. Primary- thread commands are left unqualified: they have no child to name. --------- Co-authored-by: Merge Sim <sim@local>
73 lines
2.0 KiB
TypeScript
73 lines
2.0 KiB
TypeScript
import type { NativeChatSubagentState } from '../../shared/native-chat-types'
|
|
import {
|
|
codexSubagentLabel,
|
|
isCodexRootAgentActivity,
|
|
readCodexSubagentActivity
|
|
} from './codex-subagent-activity'
|
|
import { codexChildTurnState } from './codex-subagent-executions'
|
|
import { readRecord } from './codex-item-field-readers'
|
|
import { readCodexThreadItem } from './codex-structured-item-translation'
|
|
import { readCodexTurnId } from './codex-structured-thread-facts'
|
|
|
|
export type CodexBackgroundTaskFrame =
|
|
| {
|
|
kind: 'subagent'
|
|
agentThreadId: string
|
|
label: string | null
|
|
parentTurnId: string | null | undefined
|
|
}
|
|
| {
|
|
kind: 'turn'
|
|
threadId: string
|
|
turnId: string
|
|
state: NativeChatSubagentState
|
|
}
|
|
|
|
export type CodexBackgroundTaskEvent = {
|
|
method: string
|
|
threadId: string
|
|
params: unknown
|
|
}
|
|
|
|
export function readCodexBackgroundTaskFrame(
|
|
event: CodexBackgroundTaskEvent,
|
|
primaryThreadId: string
|
|
): CodexBackgroundTaskFrame | null {
|
|
if (event.method === 'turn/started' || event.method === 'turn/completed') {
|
|
const turnId = readCodexTurnId(event.params)
|
|
if (turnId === null) {
|
|
return null
|
|
}
|
|
return {
|
|
kind: 'turn',
|
|
threadId: event.threadId,
|
|
turnId,
|
|
state:
|
|
event.method === 'turn/started'
|
|
? 'working'
|
|
: codexChildTurnState(readRecord(readRecord(event.params).turn).status)
|
|
}
|
|
}
|
|
if (event.method !== 'item/started' && event.method !== 'item/completed') {
|
|
return null
|
|
}
|
|
const item = readCodexThreadItem(readRecord(event.params).item)
|
|
const activity = item && readCodexSubagentActivity(item)
|
|
if (
|
|
!activity ||
|
|
activity.agentThreadId === primaryThreadId ||
|
|
isCodexRootAgentActivity(activity)
|
|
) {
|
|
return null
|
|
}
|
|
return {
|
|
kind: 'subagent',
|
|
agentThreadId: activity.agentThreadId,
|
|
label: codexSubagentLabel(activity),
|
|
parentTurnId:
|
|
activity.kind === 'started' || activity.kind === 'interacted'
|
|
? readCodexTurnId(event.params)
|
|
: undefined
|
|
}
|
|
}
|