Merge remote-tracking branch 'origin/master' into feat/web-diff-card
# Conflicts: # packages/client/ui-conversation/README.i18n.yaml # packages/client/ui-conversation/src/client/chat/GenericToolCard.tsx # packages/client/ui-conversation/src/client/chat/ToolRow.tsx # packages/client/ui-conversation/src/client/skeleton/DetailsPanel.tsx # packages/client/ui-primitives/README.i18n.yaml # packages/client/ui-primitives/src/index.ts
This commit is contained in:
@@ -247,7 +247,7 @@ describe('goodbye message and /resume', () => {
|
||||
({ version: 0, id: SessionId(id), createdAt, cwd })
|
||||
const resumeEvents = (
|
||||
title: string,
|
||||
provider = 'deepseek',
|
||||
provider = 'deepseek-official',
|
||||
time = 100,
|
||||
reason: TurnEndReason = { kind: 'completed' },
|
||||
): SessionEvent[] => [
|
||||
@@ -319,8 +319,8 @@ describe('goodbye message and /resume', () => {
|
||||
sessionPersistence: {
|
||||
list: async () => [older, newer, header('foreign-session', 3000, '/elsewhere')],
|
||||
load: async id => id === newer.id
|
||||
? { meta: newer, events: resumeEvents('Newer product work', 'deepseek', 300) }
|
||||
: { meta: older, events: resumeEvents('Older investigation', 'deepseek', 100) },
|
||||
? { meta: newer, events: resumeEvents('Newer product work', 'deepseek-official', 300) }
|
||||
: { meta: older, events: resumeEvents('Older investigation', 'deepseek-official', 100) },
|
||||
},
|
||||
})
|
||||
result.terminal.send('/resume')
|
||||
@@ -419,7 +419,7 @@ describe('goodbye message and /resume', () => {
|
||||
list: async () => targets,
|
||||
load: async id => ({
|
||||
meta: targets.find(target => target.id === id)!,
|
||||
events: resumeEvents(`Paged ${id.slice('paged-'.length)}`, 'deepseek', 1000 - Number(id.slice('paged-'.length)) * 10),
|
||||
events: resumeEvents(`Paged ${id.slice('paged-'.length)}`, 'deepseek-official', 1000 - Number(id.slice('paged-'.length)) * 10),
|
||||
}),
|
||||
},
|
||||
})
|
||||
@@ -475,7 +475,7 @@ describe('goodbye message and /resume', () => {
|
||||
cwd: '/workspace',
|
||||
sessionPersistence: {
|
||||
list: async () => [target],
|
||||
load: async () => ({ meta: target, events: resumeEvents(`Turn ${label}`, 'deepseek', 100, reason) }),
|
||||
load: async () => ({ meta: target, events: resumeEvents(`Turn ${label}`, 'deepseek-official', 100, reason) }),
|
||||
},
|
||||
})
|
||||
result.terminal.send('/resume')
|
||||
@@ -711,7 +711,7 @@ describe('goodbye message and /resume', () => {
|
||||
it('falls back to assistant provenance and header creation time for sparse logs', async () => {
|
||||
const assistantOnly = header('assistant-route', 20, '/workspace')
|
||||
const empty = header('empty-log', 10, '/workspace')
|
||||
const events = resumeEvents('Assistant route', 'deepseek')
|
||||
const events = resumeEvents('Assistant route', 'deepseek-official')
|
||||
.filter(event => event.type !== 'request/header')
|
||||
.map((event, seq) => ({ ...event, seq })) as SessionEvent[]
|
||||
const result = await setup({
|
||||
@@ -726,7 +726,7 @@ describe('goodbye message and /resume', () => {
|
||||
result.terminal.send('/resume')
|
||||
result.terminal.send('\r')
|
||||
await tick(); await tick()
|
||||
expect(result.terminal.output).toContain('deepseek/model-1')
|
||||
expect(result.terminal.output).toContain('deepseek-official/model-1')
|
||||
expect(result.terminal.output).toContain(new Date(empty.createdAt).toISOString())
|
||||
await dispose(result)
|
||||
})
|
||||
@@ -2397,7 +2397,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
contextWindow: 128_000,
|
||||
contextTokens: 42_000,
|
||||
config: { showReasoning: false },
|
||||
agentOptions: { provider: 'deepseek', model: 'deepseek-v4-pro' },
|
||||
agentOptions: { provider: 'deepseek-official', model: 'deepseek-v4-pro' },
|
||||
tools: {
|
||||
read: {
|
||||
name: 'read', description: 'Read a file', parameters: {},
|
||||
@@ -2445,7 +2445,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
expect(result.terminal.output).toContain('main-session')
|
||||
expect(result.terminal.output).toContain('Inspect status \\x1b]2;unsafe\\x07')
|
||||
expect(result.terminal.output).toContain('/workspace/status')
|
||||
expect(result.terminal.output).toContain('deepseek/deepseek-v4-pro (effort default; reasoning blocks')
|
||||
expect(result.terminal.output).toContain('deepseek-official/deepseek-v4-pro (effort default; reasoning blocks')
|
||||
expect(result.terminal.output).toContain('hidden)')
|
||||
// 6 domain events + the /status invocation's own command/run (open turn: joined directly).
|
||||
expect(result.terminal.output).toContain('running · 7 events · 1 turn · 1 step · 2 tool calls')
|
||||
@@ -3601,7 +3601,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
|
||||
const failed = await setup({
|
||||
catalog: {
|
||||
providers: [{ id: 'deepseek', name: 'DeepSeek' }],
|
||||
providers: [{ id: 'deepseek-official', name: 'DeepSeek' }],
|
||||
models: [],
|
||||
listModels: () => Promise.reject(new Error('catalog offline')),
|
||||
resolveModelInfo: () => Promise.reject(new Error('capacity offline')),
|
||||
@@ -3617,8 +3617,8 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
|
||||
const reasoningFailed = await setup({
|
||||
catalog: {
|
||||
providers: [{ id: 'deepseek', name: 'DeepSeek' }],
|
||||
models: [{ provider: 'deepseek', id: 'model-1', name: 'Model One' }],
|
||||
providers: [{ id: 'deepseek-official', name: 'DeepSeek' }],
|
||||
models: [{ provider: 'deepseek-official', id: 'model-1', name: 'Model One' }],
|
||||
resolveModelInfo: () => Promise.reject(new Error('reasoning metadata offline')),
|
||||
},
|
||||
})
|
||||
@@ -3634,7 +3634,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
const deferred = Promise.withResolvers<never[]>()
|
||||
const result = await setup({
|
||||
catalog: {
|
||||
providers: [{ id: 'deepseek', name: 'DeepSeek' }],
|
||||
providers: [{ id: 'deepseek-official', name: 'DeepSeek' }],
|
||||
models: [],
|
||||
listModels: () => deferred.promise,
|
||||
},
|
||||
@@ -3650,7 +3650,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
const rejected = Promise.withResolvers<never[]>()
|
||||
const rejectedResult = await setup({
|
||||
catalog: {
|
||||
providers: [{ id: 'deepseek', name: 'DeepSeek' }],
|
||||
providers: [{ id: 'deepseek-official', name: 'DeepSeek' }],
|
||||
models: [],
|
||||
listModels: () => rejected.promise,
|
||||
},
|
||||
@@ -3667,7 +3667,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
const contextResult = await setup({
|
||||
contextTokens: 99,
|
||||
catalog: {
|
||||
providers: [{ id: 'deepseek', name: 'DeepSeek' }],
|
||||
providers: [{ id: 'deepseek-official', name: 'DeepSeek' }],
|
||||
models: [],
|
||||
resolveModelInfo: () => context.promise.then(value => ({ context: value })),
|
||||
},
|
||||
@@ -4395,6 +4395,14 @@ describe('tool cards and surface replay', () => {
|
||||
name: 'knownXml', description: '', parameters: {}, output: UNUSED_TOOL_OUTPUT, execute: async () => [],
|
||||
presentCall: () => ({ card: 'generic', title: 'Known XML' }),
|
||||
},
|
||||
// A web card carries no `content` copy, so it falls back to the raw result
|
||||
// content, which must still render through the dim Markdown path (bold
|
||||
// markers stripped) rather than as bare text.
|
||||
webCard: {
|
||||
name: 'webCard', description: '', parameters: {}, output: UNUSED_TOOL_OUTPUT, execute: async () => [],
|
||||
presentCall: () => ({ card: 'generic', title: 'Fetch page', kind: 'fetch' }),
|
||||
presentResult: () => ({ card: 'web', kind: 'fetch', title: 'https://a.test', url: 'https://a.test', statusCode: 200, truncated: false }),
|
||||
},
|
||||
}
|
||||
|
||||
it('uses terminal, diff, generic, fallback, and collapsed tool presentations', async () => {
|
||||
@@ -4415,6 +4423,7 @@ describe('tool cards and surface replay', () => {
|
||||
['c11', 'terminalResult', '{}'],
|
||||
['c12', 'symbolic', '{}'],
|
||||
['c13', 'knownXml', '{}'],
|
||||
['c16', 'webCard', '{}'],
|
||||
] as const
|
||||
appendAssistant(result.session, [
|
||||
{ type: 'text', text: 'Calling tools' },
|
||||
@@ -4508,6 +4517,14 @@ describe('tool cards and surface replay', () => {
|
||||
isError: false,
|
||||
}),
|
||||
}, { surfaceOp: 'append' })
|
||||
result.session.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: 'c16' as never,
|
||||
content: [{ type: 'text', text: 'Fetched **body** text' }],
|
||||
isError: false,
|
||||
}),
|
||||
}, { surfaceOp: 'append' })
|
||||
result.session.append('tool/result', {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
@@ -4557,6 +4574,11 @@ describe('tool cards and surface replay', () => {
|
||||
expect(output).toContain('Empty card')
|
||||
expect(output).toContain('converted terminal')
|
||||
expect(output).toContain('<known><value>literal</value></known>')
|
||||
// A web card carries no `content` copy, so it falls back to the raw result
|
||||
// content, which still renders through the dim Markdown path: the bold
|
||||
// markers are stripped rather than shown literally.
|
||||
expect(output).toContain('Fetched body text')
|
||||
expect(output).not.toContain('Fetched **body** text')
|
||||
expect(output).toContain('path: /tmp/a.txt')
|
||||
expect(output).toContain('line (number="1"): hello')
|
||||
expect(output).not.toContain('<result>')
|
||||
|
||||
Reference in New Issue
Block a user