mirror of
https://github.com/stablyai/orca.git
synced 2026-10-09 08:02:35 +00:00
* fix(sidebar): show Cursor rows and stop a stray "claude" title hijacking OpenCode Two defects in the same title-resolution path. **#10258** — Cursor's only native OSC title is the literal `cursor agent`, which both title trackers dropped unconditionally. A hookless Cursor pane therefore had neither a status entry nor any title carrying Cursor identity, so the worktree card showed nothing at all. **#8940** — two owner-blind paths let an incidental `claude` token anywhere in an OpenCode session or task title outrank the pane's known owner, so the tab icon and sidebar row flipped to Claude Code. #10258: let the literal through exactly once as identity, so a restored or mobile tab keeps its Cursor row instead of vanishing. #8940: require an *identity frame* — after stripping status decoration the title must PRESENT Claude, not merely mention it — before a Claude title may reclaim a pane from its prior identity, and make the sidebar row builder owner-aware. > These two are in one PR because they share the `ownerAgentType` plumbing through `buildTitleDerivedAgentRow` — split apart, neither half compiles on its own. Fixes #10258 Fixes #8940 Co-authored-by: Orca <help@stably.ai> * test(e2e): add recordable proof for sidebar-agent-row-identity Fails on origin/main, passes on this branch. Test: sidebar keeps a Cursor pane visible and an OpenCode pane out of Claude Code hands Co-authored-by: Orca <help@stably.ai> * fix(terminal): preserve restored Cursor identity * test(terminal): cover restored Cursor redraw suppression * refactor(terminal): tighten Cursor identity handling and Claude frame matching Review follow-ups on the title-resolution path: - pty-transport dropped a native Cursor literal that main emits whenever a non-Cursor title preceded it, re-introducing the #10258 blank row in the renderer path. The pre-filter now projects the predecessor the drain will actually see, and defers to the drain gate while facts are still queued. - applyTrackedPtyTitle threaded the cursor flag through 12 sites, including ptyRecordChanged bookkeeping the sole caller ignores. Force the status null once, and the activity-gated effects fall out unchanged. - isClaudeIdentityFrameTitle missed a multiplexer-wrapped Claude title ("zsh | Claude Code"), costing a genuine Claude pane its identity. Reuse the ' | ' segment split that agent-title-owner already had inline. - Keep title normalization on launchAgent: it only rewrites within an identity group (OMP wraps Pi), so a split does not make it wrong, and the hook-row path normalizes the same way. - Drop the tab.ptyId tracker fallback, which read a pty that the pane identity check had just rejected. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
21 lines
847 B
TypeScript
21 lines
847 B
TypeScript
const WRAPPER_SEPARATOR = ' | '
|
|
|
|
/**
|
|
* A wrapped terminal title split into the texts an identity check should consider, whole
|
|
* title first and innermost pane title last. Multiplexers and session wrappers prefix the
|
|
* pane's own dynamic title (`zsh | ⠋ Claude Code`), so a check anchored to the start of the
|
|
* string would miss the agent that actually owns the pane.
|
|
*/
|
|
export function getWrapperTitleSegments(title: string): string[] {
|
|
const segments = [title]
|
|
let separatorIndex = title.indexOf(WRAPPER_SEPARATOR)
|
|
while (separatorIndex >= 0) {
|
|
const wrapped = title.slice(separatorIndex + WRAPPER_SEPARATOR.length).trim()
|
|
if (wrapped && !segments.includes(wrapped)) {
|
|
segments.push(wrapped)
|
|
}
|
|
separatorIndex = title.indexOf(WRAPPER_SEPARATOR, separatorIndex + WRAPPER_SEPARATOR.length)
|
|
}
|
|
return segments
|
|
}
|