Files
orca/src/shared/agent-title-decoration.ts
Brennan Benson 137e724119 fix(agent-title): treat Claude Code quarter-circle spinners as working (#13889) (#13925)
* fix(agent-title): treat Claude Code quarter-circle spinners as working

Claude Code 2.1.228 swapped its busy OSC title spinner from braille
(U+2800-U+28FF) to quarter circles (U+25D0/U+25D1). Orca recognized a busy
Claude title only by braille codepoints, so the new frames matched nothing.

The summary-bearing busy frame ("<glyph> Say hi in one word") carries no
"claude" name token, so it resolved to no-status. The tracker's "idle or
permission followed by no-status means the agent exited" rule then fired
mid-turn, confirmPtyAgentExit confirmed it, and the chat surface routed
exitChat -- kicking the tab to the terminal view on every message.

Widen the accepted glyph set via a shared containsAgentSpinnerGlyph helper.
Agent-specific braille frame shapes (Grok, Pi, synthetic Cursor) stay pinned
to their own glyph set.

Fixes #13889

* fix(agent-title): satisfy static analysis and trim scope
2026-08-11 21:13:27 -07:00

20 lines
1.1 KiB
TypeScript

// Leading status decorations that coding agents prepend to their OSC title —
// Claude's '✳', Gemini's glyphs (✦ ⏲ ◇ ✋), braille and quarter-circle spinners,
// and Claude's '. '/'* ' working/idle prefixes. Once the tab bar shows the
// provider icon, this leading glyph reads as a redundant second icon, so strip
// it from the displayed title. Scoped to titles we already know belong to an agent.
const LEADING_AGENT_TITLE_DECORATION_RE =
// eslint-disable-next-line no-control-regex -- intentional unicode status-glyph ranges
/^(?:[✳✦⏲◇✋⠀-⣿◐-◓]+|[.*]\s)\s*/
export function stripLeadingAgentTitleDecorationOrEmpty(title: string): string {
return title.replace(LEADING_AGENT_TITLE_DECORATION_RE, '').trimStart()
}
export function stripLeadingAgentTitleDecoration(title: string): string {
const stripped = stripLeadingAgentTitleDecorationOrEmpty(title)
// Why: never return empty — a title that is *only* a status glyph should keep
// its original text rather than collapse to a blank tab label.
return stripped.length > 0 ? stripped : title
}