mirror of
https://github.com/stablyai/orca.git
synced 2026-10-28 16:02:55 +00:00
Polish desktop native chat view (#6641)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Orca <help@stably.ai> Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
Orca
gsxdsm
parent
bcbcf4126b
commit
b916248294
@@ -0,0 +1,192 @@
|
||||
import { mkdtemp, rm, writeFile } from 'fs/promises'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { readNativeChatTranscript } from './transcript-reader'
|
||||
|
||||
let tempRoots: string[] = []
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(tempRoots.map((root) => rm(root, { recursive: true, force: true })))
|
||||
tempRoots = []
|
||||
})
|
||||
|
||||
function jsonLines(records: unknown[]): string {
|
||||
return records.map((record) => JSON.stringify(record)).join('\n')
|
||||
}
|
||||
|
||||
async function writeFixture(prefix: string, records: unknown[]): Promise<string> {
|
||||
const root = await mkdtemp(join(tmpdir(), prefix))
|
||||
tempRoots.push(root)
|
||||
const filePath = join(root, 'transcript.jsonl')
|
||||
await writeFile(filePath, jsonLines(records))
|
||||
return filePath
|
||||
}
|
||||
|
||||
describe('readNativeChatTranscript (claude)', () => {
|
||||
it('returns ordered user/assistant/tool messages with no 5-message cap', async () => {
|
||||
const records: unknown[] = []
|
||||
// 4 user/assistant turns = 8 messages, well past the AI-Vault preview cap.
|
||||
for (let turn = 0; turn < 4; turn++) {
|
||||
records.push({
|
||||
type: 'user',
|
||||
uuid: `u-${turn}`,
|
||||
timestamp: `2026-06-01T10:0${turn}:00.000Z`,
|
||||
message: { role: 'user', content: `Prompt **${turn}**` }
|
||||
})
|
||||
records.push({
|
||||
type: 'assistant',
|
||||
uuid: `a-${turn}`,
|
||||
timestamp: `2026-06-01T10:0${turn}:30.000Z`,
|
||||
message: { role: 'assistant', content: [{ type: 'text', text: `Reply _${turn}_` }] }
|
||||
})
|
||||
}
|
||||
// A tool_use then a tool_result (carried on a user record).
|
||||
records.push({
|
||||
type: 'assistant',
|
||||
uuid: 'a-tool',
|
||||
timestamp: '2026-06-01T10:05:00.000Z',
|
||||
message: {
|
||||
role: 'assistant',
|
||||
content: [{ type: 'tool_use', name: 'Bash', input: { command: 'ls' } }]
|
||||
}
|
||||
})
|
||||
records.push({
|
||||
type: 'user',
|
||||
uuid: 'u-toolresult',
|
||||
timestamp: '2026-06-01T10:05:01.000Z',
|
||||
message: {
|
||||
role: 'user',
|
||||
content: [{ type: 'tool_result', content: 'file-a\nfile-b', is_error: false }]
|
||||
}
|
||||
})
|
||||
|
||||
const filePath = await writeFixture('orca-native-chat-claude-', records)
|
||||
const result = await readNativeChatTranscript('claude', 'sess', { filePath })
|
||||
expect('messages' in result).toBe(true)
|
||||
if (!('messages' in result)) {
|
||||
return
|
||||
}
|
||||
|
||||
expect(result.messages.length).toBe(10)
|
||||
expect(result.messages.length).toBeGreaterThan(5)
|
||||
expect(result.messages[0]).toMatchObject({ role: 'user', source: 'transcript' })
|
||||
// Markdown text preserved verbatim.
|
||||
expect(result.messages[0].blocks[0]).toEqual({ type: 'text', text: 'Prompt **0**' })
|
||||
expect(result.messages[1].blocks[0]).toEqual({ type: 'text', text: 'Reply _0_' })
|
||||
|
||||
const toolCall = result.messages.find((m) => m.blocks[0]?.type === 'tool-call')
|
||||
expect(toolCall?.blocks[0]).toEqual({
|
||||
type: 'tool-call',
|
||||
name: 'Bash',
|
||||
input: { command: 'ls' }
|
||||
})
|
||||
|
||||
const toolResult = result.messages.at(-1)
|
||||
expect(toolResult?.role).toBe('tool')
|
||||
expect(toolResult?.blocks[0]).toEqual({ type: 'tool-result', output: 'file-a\nfile-b' })
|
||||
})
|
||||
|
||||
it('marks thinking-only assistant content as a reasoning surface', async () => {
|
||||
const filePath = await writeFixture('orca-native-chat-claude-think-', [
|
||||
{
|
||||
type: 'assistant',
|
||||
uuid: 'a-think',
|
||||
timestamp: '2026-06-01T10:00:00.000Z',
|
||||
message: { role: 'assistant', content: [{ type: 'thinking', thinking: 'pondering' }] }
|
||||
}
|
||||
])
|
||||
const result = await readNativeChatTranscript('claude', 'sess', { filePath })
|
||||
if (!('messages' in result)) {
|
||||
throw new Error('expected messages')
|
||||
}
|
||||
expect(result.messages[0].blocks[0]).toEqual({ type: 'text', text: 'pondering' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('readNativeChatTranscript (codex)', () => {
|
||||
it('maps tool calls and results to tool-call/tool-result blocks', async () => {
|
||||
const filePath = await writeFixture('orca-native-chat-codex-', [
|
||||
{
|
||||
type: 'session_meta',
|
||||
timestamp: '2026-06-01T10:00:00.000Z',
|
||||
payload: { id: 'codex-sess', cwd: '/repo' }
|
||||
},
|
||||
{
|
||||
type: 'response_item',
|
||||
timestamp: '2026-06-01T10:00:01.000Z',
|
||||
payload: {
|
||||
type: 'message',
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: 'Run the build' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'response_item',
|
||||
timestamp: '2026-06-01T10:00:02.000Z',
|
||||
payload: { type: 'reasoning', summary: [{ type: 'summary_text', text: 'I will run it' }] }
|
||||
},
|
||||
{
|
||||
type: 'response_item',
|
||||
timestamp: '2026-06-01T10:00:03.000Z',
|
||||
payload: {
|
||||
type: 'function_call',
|
||||
name: 'shell',
|
||||
arguments: '{"command":["bash","-lc","make"]}'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'response_item',
|
||||
timestamp: '2026-06-01T10:00:04.000Z',
|
||||
payload: {
|
||||
type: 'function_call_output',
|
||||
output: { content: 'build ok', success: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'response_item',
|
||||
timestamp: '2026-06-01T10:00:05.000Z',
|
||||
payload: { type: 'message', role: 'assistant', content: [{ type: 'text', text: 'Done.' }] }
|
||||
}
|
||||
])
|
||||
|
||||
const result = await readNativeChatTranscript('codex', 'codex-sess', { filePath })
|
||||
if (!('messages' in result)) {
|
||||
throw new Error(`expected messages, got error`)
|
||||
}
|
||||
|
||||
const roles = result.messages.map((m) => m.role)
|
||||
expect(roles).toEqual(['user', 'reasoning', 'assistant', 'tool', 'assistant'])
|
||||
|
||||
const call = result.messages.find((m) => m.blocks[0]?.type === 'tool-call')
|
||||
expect(call?.blocks[0]).toEqual({
|
||||
type: 'tool-call',
|
||||
name: 'shell',
|
||||
input: '{"command":["bash","-lc","make"]}'
|
||||
})
|
||||
|
||||
const toolResult = result.messages.find((m) => m.blocks[0]?.type === 'tool-result')
|
||||
expect(toolResult?.blocks[0]).toEqual({ type: 'tool-result', output: 'build ok' })
|
||||
|
||||
const reasoning = result.messages.find((m) => m.role === 'reasoning')
|
||||
expect(reasoning?.blocks[0]).toEqual({ type: 'text', text: 'I will run it' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('readNativeChatTranscript (errors)', () => {
|
||||
it('returns an error for an unreadable/missing file without throwing', async () => {
|
||||
const result = await readNativeChatTranscript('claude', 'sess', {
|
||||
filePath: join(tmpdir(), 'orca-native-chat-does-not-exist.jsonl')
|
||||
})
|
||||
expect('error' in result).toBe(true)
|
||||
})
|
||||
|
||||
it('returns an error when no transcript can be resolved', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'orca-native-chat-noresolve-'))
|
||||
tempRoots.push(root)
|
||||
const result = await readNativeChatTranscript('claude', 'missing', {
|
||||
claudeProjectsDir: join(root, 'empty')
|
||||
})
|
||||
expect('error' in result).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user