mirror of
https://github.com/stablyai/orca.git
synced 2026-10-08 16:02:37 +00:00
* fix(native-chat): let a multi-question ask record list its questions A cancelled or pending structured ask with several questions showed only "Asked: 2 questions", and its questions appeared nowhere in the transcript. The row's subject now carries the question texts instead of a count, and the row unfolds them as a list below its toggle unless the record already lists them with their answers. * fix(native-chat): keep a question list's open state off its first question A pending Codex group is keyed by its first question, which becomes its own row once answered. Opening the list left that row expanded, and folding it could drop the toggle under the pointer. The list and a lone question now remember their open state separately.
83 lines
3.1 KiB
TypeScript
83 lines
3.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
hasNativeChatAskCall,
|
|
nativeChatAskRunSubject,
|
|
nativeChatAskRunBlocks
|
|
} from './native-chat-ask-row'
|
|
import type { NativeChatBlock } from './native-chat-types'
|
|
|
|
function askCall(input: unknown, name = 'AskUserQuestion'): NativeChatBlock {
|
|
return { type: 'tool-call', name, input }
|
|
}
|
|
|
|
describe('native chat ask row', () => {
|
|
it('removes the question result without attaching it to another tool', () => {
|
|
const ask = askCall({ questions: [{ question: 'Proceed?' }] })
|
|
const answer: NativeChatBlock = { type: 'tool-result', output: 'yes' }
|
|
const read: NativeChatBlock = { type: 'tool-call', name: 'Read', input: {} }
|
|
const output: NativeChatBlock = { type: 'tool-result', output: 'file contents' }
|
|
expect(nativeChatAskRunBlocks([ask, read, answer, output])).toEqual({
|
|
asks: [ask],
|
|
unansweredAsks: [],
|
|
work: [read, output]
|
|
})
|
|
})
|
|
|
|
it('keeps an ask open only until its FIFO result arrives', () => {
|
|
const ask = askCall({ questions: [{ question: 'Proceed?' }] })
|
|
expect(nativeChatAskRunBlocks([ask])).toEqual({
|
|
asks: [ask],
|
|
unansweredAsks: [ask],
|
|
work: []
|
|
})
|
|
})
|
|
it('names the one question a prompt asks', () => {
|
|
expect(
|
|
nativeChatAskRunSubject([askCall({ questions: [{ question: 'Which branch?' }] })])
|
|
).toEqual({ kind: 'question', text: 'Which branch?' })
|
|
})
|
|
|
|
it('counts a grouped prompt rather than quoting only its first question', () => {
|
|
expect(
|
|
nativeChatAskRunSubject([
|
|
askCall({ questions: [{ question: 'Which branch?' }, { question: 'Proceed?' }] })
|
|
])
|
|
).toEqual({ kind: 'questions', questions: ['Which branch?', 'Proceed?'] })
|
|
})
|
|
|
|
it('aggregates the per-question calls Codex journals for a single prompt', () => {
|
|
// Codex writes one call per question, so a per-call row would stack two
|
|
// pulsing lines for a prompt the reader was shown once.
|
|
expect(
|
|
nativeChatAskRunSubject([
|
|
askCall({ questions: [{ question: 'Which branch?' }] }, 'request_user_input'),
|
|
askCall({ questions: [{ question: 'Proceed?' }] }, 'request_user_input')
|
|
])
|
|
).toEqual({ kind: 'questions', questions: ['Which branch?', 'Proceed?'] })
|
|
})
|
|
|
|
it('decodes the JSON-string arguments Codex delivers', () => {
|
|
expect(
|
|
nativeChatAskRunSubject([
|
|
askCall(
|
|
JSON.stringify({ questions: [{ question: 'Which branch?' }] }),
|
|
'request_user_input'
|
|
)
|
|
])
|
|
).toEqual({ kind: 'question', text: 'Which branch?' })
|
|
})
|
|
|
|
it('still reports an ask whose payload names no question', () => {
|
|
// Decided by the tool name alone: an unreadable payload must not put the raw
|
|
// call back on screen as the row it was meant to replace.
|
|
const blocks = [askCall({ prompt: 'which?' })]
|
|
|
|
expect(hasNativeChatAskCall(blocks)).toBe(true)
|
|
expect(nativeChatAskRunSubject(blocks)).toBeNull()
|
|
})
|
|
|
|
it('leaves an ordinary tool call alone even when its input carries questions', () => {
|
|
expect(hasNativeChatAskCall([askCall({ questions: [{ question: 'x' }] }, 'Read')])).toBe(false)
|
|
})
|
|
})
|