mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 00:03:08 +00:00
Extracts the self-contained "hidden-view parking" slice from PR #7214 (orca-performance) as a standalone fix for the Windows renderer OOM/hang under multi-agent load (#7175 / STA-476): long-hidden terminal panes unmount after a 30s hysteresis while a pane-less byte watcher keeps bell/title/agent-completion side effects alive; reveal rides the existing tab-move reattach path. Byte mode only: no main-process side-effect emitter ships here, so constants.ts pins terminalMainSideEffectAuthority / terminalHiddenDeliveryGate / terminalModelQueryAuthority to false (the single intentional divergence from #7214, reconciled by taking #7214's values when its emitter lands). The parking kill switch terminalHiddenViewParking also ships default-OFF (opt-in) until the memory win is confirmed in a soak. Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com> Co-authored-by: Orca <help@stably.ai>
195 lines
5.4 KiB
TypeScript
195 lines
5.4 KiB
TypeScript
import {
|
|
AGY_AGENT_NAME_RE,
|
|
BRAILLE_SPINNER_RE,
|
|
CLAUDE_IDLE,
|
|
CURSOR_NATIVE_TITLE_LOWER,
|
|
DROID_AGENT_NAME_RE,
|
|
GEMINI_IDLE,
|
|
GEMINI_PERMISSION,
|
|
GEMINI_SILENT_WORKING,
|
|
GEMINI_WORKING,
|
|
HERMES_AGENT_NAME_RE,
|
|
STRONG_IDLE_KEYWORDS_RE,
|
|
STRONG_WORKING_KEYWORDS_RE,
|
|
STRONG_WORKING_KEYWORDS_RE_GLOBAL,
|
|
containsAgentName,
|
|
containsAny,
|
|
containsBrailleSpinner,
|
|
containsLegacyAgentName,
|
|
isClaudeManagementTitle,
|
|
isGeminiTerminalTitle,
|
|
isPiAgentTitle,
|
|
isPiTerminalTitle
|
|
} from './agent-title-core'
|
|
import type { AgentStatus } from './agent-title-core'
|
|
import { getPiCompatibleSyntheticAgentStatus } from './pi-compatible-synthetic-title'
|
|
|
|
/**
|
|
* Strip working-status indicators so stale exit titles stop reporting working.
|
|
*/
|
|
export function clearWorkingIndicators(title: string): string {
|
|
let cleaned = title
|
|
|
|
cleaned = cleaned.replace(GEMINI_WORKING, '')
|
|
cleaned = cleaned.replace(GEMINI_SILENT_WORKING, '')
|
|
cleaned = cleaned.replace(BRAILLE_SPINNER_RE, '')
|
|
if (cleaned.startsWith('. ')) {
|
|
cleaned = cleaned.slice(2)
|
|
}
|
|
if (containsAgentName(cleaned)) {
|
|
cleaned = cleaned.replace(STRONG_WORKING_KEYWORDS_RE_GLOBAL, '')
|
|
}
|
|
|
|
cleaned = cleaned.replace(/\s{2,}/g, ' ').trim()
|
|
return cleaned || title
|
|
}
|
|
|
|
/**
|
|
* Tracks agent status transitions from terminal title changes.
|
|
*/
|
|
export function createAgentStatusTracker(
|
|
onBecameIdle: (title: string) => void,
|
|
onBecameWorking?: () => void,
|
|
onAgentExited?: () => void,
|
|
initialTitle?: string
|
|
): {
|
|
handleTitle: (title: string) => void
|
|
seedTitle: (title: string) => void
|
|
reset: () => void
|
|
} {
|
|
// Why: trackers restored mid-session need a last-known status without firing
|
|
// callbacks, or a hidden working agent can miss its later idle transition.
|
|
let lastStatus: AgentStatus | null =
|
|
initialTitle !== undefined ? detectAgentStatusFromTitle(initialTitle) : null
|
|
|
|
return {
|
|
handleTitle(title: string): void {
|
|
const newStatus = detectAgentStatusFromTitle(title)
|
|
if (lastStatus === 'working' && newStatus !== null && newStatus !== 'working') {
|
|
onBecameIdle(title)
|
|
}
|
|
if (lastStatus !== 'working' && newStatus === 'working') {
|
|
onBecameWorking?.()
|
|
}
|
|
// Why: reverting to a plain shell prompt after idle/permission means the
|
|
// agent exited; while working it can just be a transient internal title.
|
|
if (lastStatus !== null && lastStatus !== 'working' && newStatus === null) {
|
|
lastStatus = null
|
|
onAgentExited?.()
|
|
}
|
|
if (newStatus !== null) {
|
|
lastStatus = newStatus
|
|
}
|
|
},
|
|
seedTitle(title: string): void {
|
|
lastStatus = detectAgentStatusFromTitle(title)
|
|
},
|
|
reset(): void {
|
|
lastStatus = null
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Normalize high-churn agent titles into stable display labels before storage.
|
|
*/
|
|
export function normalizeTerminalTitle(title: string): string {
|
|
if (!title) {
|
|
return title
|
|
}
|
|
|
|
if (isGeminiTerminalTitle(title)) {
|
|
const status = detectAgentStatusFromTitle(title)
|
|
if (status === 'permission') {
|
|
return `${GEMINI_PERMISSION} Gemini CLI`
|
|
}
|
|
if (status === 'working') {
|
|
return `${GEMINI_WORKING} Gemini CLI`
|
|
}
|
|
if (status === 'idle') {
|
|
return `${GEMINI_IDLE} Gemini CLI`
|
|
}
|
|
}
|
|
|
|
// Why: Pi animates every 80ms; collapse frames while preserving status.
|
|
if (isPiAgentTitle(title)) {
|
|
const status = detectAgentStatusFromTitle(title)
|
|
if (status === 'working') {
|
|
return '\u280b Pi'
|
|
}
|
|
if (status === 'idle') {
|
|
return 'Pi'
|
|
}
|
|
}
|
|
|
|
return title
|
|
}
|
|
|
|
export function detectAgentStatusFromTitle(title: string): AgentStatus | null {
|
|
if (!title || isClaudeManagementTitle(title)) {
|
|
return null
|
|
}
|
|
if (title.trim().toLowerCase() === CURSOR_NATIVE_TITLE_LOWER) {
|
|
return null
|
|
}
|
|
|
|
if (title.includes(GEMINI_PERMISSION)) {
|
|
return 'permission'
|
|
}
|
|
if (title.includes(GEMINI_WORKING) || title.includes(GEMINI_SILENT_WORKING)) {
|
|
return 'working'
|
|
}
|
|
if (title.includes(GEMINI_IDLE)) {
|
|
return 'idle'
|
|
}
|
|
|
|
// Why: resolve synthetic Pi/OMP permission/idle labels before the broader
|
|
// Pi and braille-spinner checks below.
|
|
const piCompatibleSyntheticAgentStatus = getPiCompatibleSyntheticAgentStatus(title)
|
|
if (piCompatibleSyntheticAgentStatus) {
|
|
return piCompatibleSyntheticAgentStatus
|
|
}
|
|
|
|
if (title.startsWith(`${CLAUDE_IDLE} `) || title === CLAUDE_IDLE) {
|
|
return 'idle'
|
|
}
|
|
if (isPiTerminalTitle(title)) {
|
|
return 'idle'
|
|
}
|
|
if (containsBrailleSpinner(title)) {
|
|
return 'working'
|
|
}
|
|
|
|
const hasDroidAgentName = DROID_AGENT_NAME_RE.test(title)
|
|
const hasHermesAgentName = HERMES_AGENT_NAME_RE.test(title)
|
|
const hasAgyAgentName = AGY_AGENT_NAME_RE.test(title)
|
|
const hasLegacyAgentName = containsLegacyAgentName(title)
|
|
if (!hasLegacyAgentName && !hasDroidAgentName && !hasHermesAgentName && !hasAgyAgentName) {
|
|
return null
|
|
}
|
|
if (containsAny(title, ['action required', 'permission', 'waiting'])) {
|
|
return 'permission'
|
|
}
|
|
// Why: boundary-aware regexes avoid cwd/path and substring false positives.
|
|
if (STRONG_IDLE_KEYWORDS_RE.test(title)) {
|
|
return 'idle'
|
|
}
|
|
if (STRONG_WORKING_KEYWORDS_RE.test(title)) {
|
|
return 'working'
|
|
}
|
|
if (title.startsWith('. ')) {
|
|
return 'working'
|
|
}
|
|
if (title.startsWith('* ')) {
|
|
return 'idle'
|
|
}
|
|
|
|
// Why: Droid hook events are authoritative; native name-only titles should
|
|
// not turn a still-sleeping execute tool into completion.
|
|
if (hasDroidAgentName && !hasLegacyAgentName) {
|
|
return null
|
|
}
|
|
|
|
return 'idle'
|
|
}
|