mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 09:57:48 +00:00
202 lines
6.8 KiB
TypeScript
202 lines
6.8 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||
import { createCommandCodeOutputStatusDetector } from './command-code-output-status'
|
||
|
||
afterEach(() => {
|
||
vi.restoreAllMocks()
|
||
})
|
||
|
||
describe('createCommandCodeOutputStatusDetector', () => {
|
||
it('marks Command Code working with the submitted prompt when the TUI starts thinking', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: 'command-code --trust',
|
||
onWorking
|
||
})
|
||
|
||
expect(detector.observe('❯ Fix the yellow spinner\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe(true)
|
||
|
||
expect(onWorking).toHaveBeenCalledWith('Fix the yellow spinner')
|
||
})
|
||
|
||
it('waits for the Command Code banner before trusting generic status text', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: null,
|
||
onWorking
|
||
})
|
||
|
||
expect(detector.observe('Thinking about unrelated shell output')).toBe(false)
|
||
expect(detector.observe('# Command Code v0.27.2\r\n')).toBe(false)
|
||
expect(detector.observe('⌘ Parsing...')).toBe(true)
|
||
|
||
expect(onWorking).toHaveBeenCalledWith('')
|
||
})
|
||
|
||
it('detects the Command Code banner across PTY chunk boundaries', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: null,
|
||
onWorking
|
||
})
|
||
|
||
expect(detector.observe('# Command')).toBe(false)
|
||
expect(detector.observe(' Code v0.27.2\r\n')).toBe(false)
|
||
expect(detector.observe('⌘ Parsing...')).toBe(true)
|
||
|
||
expect(onWorking).toHaveBeenCalledWith('')
|
||
})
|
||
|
||
it('detects the banner after chunks that carry no banner characters at all', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: null,
|
||
onWorking
|
||
})
|
||
|
||
// Ordinary agent output with no '#': the raw prefilter skips these without building a window.
|
||
expect(detector.observe('Codex is editing the file and rendering a diff\r\n')).toBe(false)
|
||
expect(detector.observe('Codex wrote 12 lines\r\n')).toBe(false)
|
||
expect(detector.observe('# Command Code v0.27.2\r\n')).toBe(false)
|
||
expect(detector.observe('⌘ Parsing...')).toBe(true)
|
||
|
||
expect(onWorking).toHaveBeenCalledWith('')
|
||
})
|
||
|
||
it('detects the Command Code banner when ANSI styling splits the words', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: null,
|
||
onWorking
|
||
})
|
||
|
||
expect(detector.observe('# C\x1b[35mommand Co\x1b[0mde v0.27.2\r\n')).toBe(false)
|
||
expect(detector.observe('⌘ Parsing...')).toBe(true)
|
||
|
||
expect(onWorking).toHaveBeenCalledWith('')
|
||
})
|
||
|
||
it('does not arm when another agent merely discusses Command Code', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: null,
|
||
onWorking
|
||
})
|
||
|
||
expect(detector.observe('// Why: Command Code exposes transcript prompts')).toBe(false)
|
||
expect(detector.observe('\r\n✻ Thinking...')).toBe(false)
|
||
|
||
expect(onWorking).not.toHaveBeenCalled()
|
||
})
|
||
|
||
it('marks Command Code working when active status text is split across PTY chunks', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: 'command-code --trust',
|
||
onWorking
|
||
})
|
||
|
||
expect(detector.observe('❯ Fix the yellow spinner\r\nExpl')).toBe(false)
|
||
expect(detector.observe('oring...')).toBe(true)
|
||
|
||
expect(onWorking).toHaveBeenCalledWith('Fix the yellow spinner')
|
||
})
|
||
|
||
it('does not capture a styled idle composer as the submitted prompt', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: 'command-code --trust',
|
||
onWorking
|
||
})
|
||
|
||
expect(detector.observe('# Command Code v0.27.3\r\n❯ A\x1b')).toBe(false)
|
||
expect(detector.observe('[27msk your question...\r\n✻ Thinking...')).toBe(true)
|
||
|
||
expect(onWorking).toHaveBeenCalledWith('')
|
||
expect(onWorking).not.toHaveBeenCalledWith(expect.stringContaining('[27m'))
|
||
})
|
||
|
||
it('captures a submitted prompt when styling is split across PTY chunks', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: 'command-code --trust',
|
||
onWorking
|
||
})
|
||
|
||
expect(detector.observe('❯ Fix the \x1b')).toBe(false)
|
||
expect(detector.observe('[38;2;99;109;131mstatus row\r\n✻ Thinking...')).toBe(true)
|
||
|
||
expect(onWorking).toHaveBeenCalledWith('Fix the status row')
|
||
})
|
||
|
||
it('marks Command Code done when a no-tool turn returns to the idle prompt', () => {
|
||
const onWorking = vi.fn()
|
||
const onDone = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: 'command-code --trust',
|
||
onWorking,
|
||
onDone
|
||
})
|
||
|
||
expect(detector.observe('❯ say hi\r\n✻ Thinking...')).toBe(true)
|
||
expect(
|
||
detector.observe(
|
||
'\r\n✻ Thought for 1 second\r\n:: Hi! How can I help you today?\r\n❯ Ask your question...'
|
||
)
|
||
).toBe(true)
|
||
|
||
expect(onWorking).toHaveBeenCalledWith('say hi')
|
||
expect(onDone).toHaveBeenCalledWith('say hi')
|
||
})
|
||
|
||
it('does not mark the initial idle composer as done before a submitted prompt', () => {
|
||
const onDone = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: 'command-code --trust',
|
||
onWorking: vi.fn(),
|
||
onDone
|
||
})
|
||
|
||
expect(detector.observe('# Command Code v0.27.3\r\n❯ Ask your question...')).toBe(false)
|
||
|
||
expect(onDone).not.toHaveBeenCalled()
|
||
})
|
||
|
||
it('does not treat completed thought text as a working status', () => {
|
||
const onWorking = vi.fn()
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: 'command-code --trust',
|
||
onWorking
|
||
})
|
||
|
||
expect(detector.observe('❯ Fix the yellow spinner\r\nThought for 1 second')).toBe(false)
|
||
expect(onWorking).not.toHaveBeenCalled()
|
||
})
|
||
})
|
||
|
||
describe('terminal control stripping', () => {
|
||
function promptFrom(raw: string): string | null {
|
||
let captured: string | null = null
|
||
const detector = createCommandCodeOutputStatusDetector({
|
||
startupCommand: 'command-code',
|
||
onWorking: (prompt) => {
|
||
captured = prompt
|
||
}
|
||
})
|
||
detector.observe(raw)
|
||
return captured
|
||
}
|
||
|
||
it('strips control bytes wherever they sit around the prompt text', () => {
|
||
expect(promptFrom('❯ plain prompt\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe('plain prompt')
|
||
expect(promptFrom('\x07❯ leading control\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe(
|
||
'leading control'
|
||
)
|
||
expect(promptFrom('❯ trailing control\x07\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe(
|
||
'trailing control'
|
||
)
|
||
expect(promptFrom('❯ adjacent\x07\x01\x02controls\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe(
|
||
'adjacentcontrols'
|
||
)
|
||
})
|
||
})
|