mirror of
https://github.com/stablyai/orca.git
synced 2026-10-21 08:03:33 +00:00
fix(native-chat): identify option-less models on narrow panes
Live capture at 60 columns: a Haiku session prints a bare `Haiku 4.5` row with its billing wrapped to the next line. No middot, no effort suffix — nothing marks it as the model, so it reported nothing. Claude always closes the frame with the working directory and prints at most the descriptor plus a wrapped billing line above it, so fall back to walking up from there when no row carries descriptor metadata. The height bound is what keeps the walk from climbing into the welcome art.
This commit is contained in:
@@ -73,6 +73,39 @@ const FRESH_SESSION_60_COLS = [
|
||||
'\u001b[2C\u001b[38;2;255;107;128m⏵⏵ bypass permissions on\u001b[38;2;153;153;153m (shift+tab to cycle)\u001b[4A\u001b[45D\u001b[0m\u001b[?2004h\u001b[?1004h\u001b[?1003h'
|
||||
].join('\n')
|
||||
|
||||
const OPTION_LESS_MODEL_60_COLS = [
|
||||
'\u001b[?1049h\u001b[H\r',
|
||||
'\u001b[38;2;215;119;87m╭─ Claude Code ────────────────────────────────────────────╮\r',
|
||||
'│\u001b[58C│\r',
|
||||
'│\u001b[22C\u001b[39;1mWelcome back!\u001b[23C\u001b[38;2;215;119;87;22m│\r',
|
||||
'│\u001b[58C│\r',
|
||||
'│\u001b[25C ▐\u001b[48;2;0;0;0m▛███▜\u001b[49m▌\u001b[25C│\r',
|
||||
'│\u001b[25C▝▜\u001b[48;2;0;0;0m█████\u001b[49m▛▘\u001b[24C│\r',
|
||||
'│\u001b[25C ▘▘ ▝▝ \u001b[24C│\r',
|
||||
'│\u001b[58C│\r',
|
||||
'│\u001b[24C\u001b[38;2;153;153;153mHaiku 4.5\u001b[25C\u001b[38;2;215;119;87m│\r',
|
||||
'│\u001b[20C\u001b[38;2;153;153;153mAPI Usage Billing\u001b[21C\u001b[38;2;215;119;87m│\r',
|
||||
'│\u001b[23C\u001b[38;2;153;153;153m/private/tmp\u001b[23C\u001b[38;2;215;119;87m│\r',
|
||||
'│\u001b[58C│\r',
|
||||
'╰──────────────────────────────────────────────────────────╯\r',
|
||||
'\r',
|
||||
'\r',
|
||||
'\r',
|
||||
'\r',
|
||||
'\r',
|
||||
'\r',
|
||||
'\r',
|
||||
'\r',
|
||||
'\r',
|
||||
'\u001b[2C\u001b[38;2;255;193;7m⚠ claude.ai connectors are disabled because ANTHROPIC_AP…\r',
|
||||
'\u001b[38;2;136;136;136m────────────────────────────────────────────────────────────\r',
|
||||
'\u001b[0m❯ \u001b[2mTry "how does <filepath> work?"\r',
|
||||
'\u001b[38;2;136;136;136;22m────────────────────────────────────────────────────────────\r',
|
||||
'\u001b[2C\u001b[38;2;255;193;7m⚠ Transcript saving is off — inherited CLAUDE_CODE_CHILD_…\r',
|
||||
'\u001b[2C\u001b[96m🤖\u001b[38;2;153;153;153m \u001b[96mHaiku 4.5\u001b[37m | \u001b[93m📁\u001b[38;2;153;153;153m \u001b[92mtmp\u001b[37m | \u001b[2C\u001b[95m⚡️ \u001b[38;2;153;153;153m \u001b[95m11.9% · 23.8k tokens\r',
|
||||
'\u001b[2C\u001b[38;2;255;107;128m⏵⏵ bypass permissions on\u001b[38;2;153;153;153m (shift+tab to cycle)\u001b[4A\u001b[45D\u001b[0m\u001b[?2004h\u001b[?1004h\u001b[?1003h'
|
||||
].join('\n')
|
||||
|
||||
const EFFORT = {
|
||||
id: 'effort',
|
||||
label: 'Effort',
|
||||
@@ -104,6 +137,15 @@ describe('Claude terminal session option detection', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('reads a real narrow frame whose option-less model carries no metadata', () => {
|
||||
// Why: `Haiku 4.5` has no effort control and its billing wrapped to the next
|
||||
// row, so nothing marks the row as a model — only its position above the
|
||||
// working directory does.
|
||||
expect(readClaudeSessionOptionsFromTerminalScreen(OPTION_LESS_MODEL_60_COLS)).toEqual({
|
||||
model: 'haiku'
|
||||
})
|
||||
})
|
||||
|
||||
it('reports nothing rather than release-notes prose when the model row is bare', () => {
|
||||
// Why: a custom model with its billing wrapped away carries no descriptor
|
||||
// metadata, so nothing in the frame identifies it. The panel shares rows
|
||||
|
||||
@@ -85,7 +85,40 @@ function claudeModelDescriptorCell(lines: string[], headerIndex: number): string
|
||||
}
|
||||
// xterm serialization can join the descriptor onto the version row itself.
|
||||
const joined = frameCell((lines[headerIndex] ?? '').replace(/^.*?\bClaude Code\s*v?[\d.]*/i, ''))
|
||||
return isModelDescriptorCell(joined) ? joined : null
|
||||
if (isModelDescriptorCell(joined)) {
|
||||
return joined
|
||||
}
|
||||
return frameBottom > 0 ? modelRowAboveWorkingDirectory(lines, headerIndex, frameBottom) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* A model with no effort control whose billing wrapped away — `Haiku 4.5` on a
|
||||
* narrow pane — leaves a bare name that no metadata identifies. Claude always
|
||||
* closes the frame with the working directory, and prints at most the
|
||||
* descriptor plus a wrapped billing line above it, so walk up from there.
|
||||
*/
|
||||
function modelRowAboveWorkingDirectory(
|
||||
lines: string[],
|
||||
headerIndex: number,
|
||||
frameBottom: number
|
||||
): string | null {
|
||||
let workingDirectory = -1
|
||||
for (let index = frameBottom - 1; index > headerIndex; index -= 1) {
|
||||
const cell = frameCell(lines[index] ?? '')
|
||||
if (cell.startsWith('/') || cell.startsWith('~')) {
|
||||
workingDirectory = index
|
||||
break
|
||||
}
|
||||
}
|
||||
if (workingDirectory < 0) {
|
||||
return null
|
||||
}
|
||||
let top = workingDirectory
|
||||
while (top - 1 > headerIndex && frameCell(lines[top - 1] ?? '')) {
|
||||
top -= 1
|
||||
}
|
||||
// Anything taller than descriptor + billing is the welcome art, not a model.
|
||||
return top < workingDirectory && workingDirectory - top <= 2 ? frameCell(lines[top] ?? '') : null
|
||||
}
|
||||
|
||||
/** The displayed model name, without the effort suffix or billing tail. */
|
||||
|
||||
Reference in New Issue
Block a user