Files
oh-my-openagent/src/cli/run/event-state.ts
CloudWaddie 159ade05cc fix(run): add event watchdog and secondary timeout for hasReceivedMeaningfulWork
Implements fixes from issue #1880 and #1934 to prevent exit code 130 timeout in CI environments:

- Add lastEventTimestamp to EventState for tracking when events were last received
- Add event watchdog: if no events for 30s, verify session status via direct API call
- Add secondary timeout: after 60s without meaningful work events, check for active children/todos and assume work is in progress

This prevents the poll loop from waiting for full 600s timeout when:
1. Event stream drops silently (common in CI with network instability)
2. Main session delegates to children without producing meaningful work on main session
2026-02-24 21:32:07 +09:00

80 lines
3.0 KiB
TypeScript

export interface EventState {
mainSessionIdle: boolean
mainSessionError: boolean
lastError: string | null
lastOutput: string
lastPartText: string
currentTool: string | null
/** Set to true when the main session has produced meaningful work (text, tool call, or tool result) */
hasReceivedMeaningfulWork: boolean
/** Timestamp of the last received event (for watchdog detection) */
lastEventTimestamp: number
/** Count of assistant messages for the main session */
messageCount: number
/** Current agent name from the latest assistant message */
currentAgent: string | null
/** Current model ID from the latest assistant message */
currentModel: string | null
/** Current model variant from the latest assistant message */
currentVariant: string | null
/** Current message role (user/assistant) — used to filter user messages from display */
currentMessageRole: string | null
/** Agent profile colors keyed by display name */
agentColorsByName: Record<string, string>
/** Part type registry keyed by partID (text, reasoning, tool, ...) */
partTypesById: Record<string, string>
/** Whether a THINK block is currently open in output */
inThinkBlock: boolean
/** Tracks streamed reasoning text to avoid duplicates */
lastReasoningText: string
/** Whether compact thinking line already printed for current reasoning block */
hasPrintedThinkingLine: boolean
/** Last rendered thinking line width (for in-place padding updates) */
lastThinkingLineWidth: number
/** Message role lookup by message ID to filter user parts */
messageRoleById: Record<string, string>
/** Last rendered thinking summary (to avoid duplicate re-render) */
lastThinkingSummary: string
/** Whether text stream is currently at line start (for padding) */
textAtLineStart: boolean
/** Whether reasoning stream is currently at line start (for padding) */
thinkingAtLineStart: boolean
/** Current assistant message ID — prevents counter resets on repeated message.updated for same message */
currentMessageId: string | null
/** Assistant message start timestamp by message ID */
messageStartedAtById: Record<string, number>
/** Prevent duplicate completion metadata lines per message */
completionMetaPrintedByMessageId: Record<string, boolean>
}
export function createEventState(): EventState {
return {
mainSessionIdle: false,
mainSessionError: false,
lastError: null,
lastOutput: "",
lastPartText: "",
currentTool: null,
hasReceivedMeaningfulWork: false,
lastEventTimestamp: Date.now(),
messageCount: 0,
currentAgent: null,
currentModel: null,
currentVariant: null,
currentMessageRole: null,
agentColorsByName: {},
partTypesById: {},
inThinkBlock: false,
lastReasoningText: "",
hasPrintedThinkingLine: false,
lastThinkingLineWidth: 0,
messageRoleById: {},
lastThinkingSummary: "",
textAtLineStart: true,
thinkingAtLineStart: false,
currentMessageId: null,
messageStartedAtById: {},
completionMetaPrintedByMessageId: {},
}
}