feat(coding): complete PI conversation UI
This commit is contained in:
@@ -159,4 +159,105 @@ describe('CodingConversationTimeline', () => {
|
||||
|
||||
expect(commits).toEqual([]);
|
||||
});
|
||||
|
||||
it('renders nested subagents, compacted retry state, inline tool output, and explicit branch wording', async () => {
|
||||
const { codingConversationStore } = await import('@/stores/coding-conversations');
|
||||
const { CodingConversationTimeline } = await import(
|
||||
'@/pages/Chat/CodingConversationTimeline'
|
||||
);
|
||||
const base = createProductSnapshot('conversation-feature-ui', 1);
|
||||
const snapshot = {
|
||||
...base,
|
||||
nodes: [
|
||||
{
|
||||
kind: 'message' as const,
|
||||
id: 'message-feature',
|
||||
sourceEntryId: 'entry-feature',
|
||||
role: 'assistant' as const,
|
||||
status: 'complete' as const,
|
||||
blocks: [{ kind: 'text' as const, id: 'text-feature', text: 'Ready', status: 'complete' as const }],
|
||||
},
|
||||
{
|
||||
kind: 'tool' as const,
|
||||
id: 'tool-feature',
|
||||
toolCallId: 'call-feature',
|
||||
toolName: 'read_file',
|
||||
title: '读取文件',
|
||||
inputText: 'src/app.ts',
|
||||
status: 'complete' as const,
|
||||
output: [{ kind: 'text' as const, id: 'tool-output', text: 'inline result', status: 'complete' as const }],
|
||||
details: { schema: 'changed-file.v1' as const, paths: ['src/app.ts'] },
|
||||
},
|
||||
{
|
||||
kind: 'subagent' as const,
|
||||
id: 'subagent-feature',
|
||||
runId: 'run-feature',
|
||||
details: {
|
||||
schema: 'subagent.v1' as const,
|
||||
dispatchId: 'dispatch-feature',
|
||||
mode: 'parallel' as const,
|
||||
tasks: [
|
||||
{ taskId: 'task-a', agentId: 'reader', toolProfile: 'read-only' as const, status: 'complete' as const, summary: 'Read complete' },
|
||||
{ taskId: 'task-b', agentId: 'builder', toolProfile: 'coding' as const, status: 'error' as const, errorCode: 'BUILD_FAILED' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'subagent' as const,
|
||||
id: 'subagent-single',
|
||||
runId: 'run-feature',
|
||||
details: {
|
||||
schema: 'subagent.v1' as const,
|
||||
dispatchId: 'dispatch-single',
|
||||
mode: 'single' as const,
|
||||
tasks: [{ taskId: 'task-single', agentId: 'reviewer', toolProfile: 'read-only' as const, status: 'aborted' as const }],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'subagent' as const,
|
||||
id: 'subagent-chain',
|
||||
runId: 'run-feature',
|
||||
details: {
|
||||
schema: 'subagent.v1' as const,
|
||||
dispatchId: 'dispatch-chain',
|
||||
mode: 'chain' as const,
|
||||
tasks: [
|
||||
{ taskId: 'task-chain-a', agentId: 'planner', toolProfile: 'read-only' as const, status: 'complete' as const },
|
||||
{ taskId: 'task-chain-b', agentId: 'implementer', toolProfile: 'coding' as const, status: 'skipped' as const },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'compaction' as const,
|
||||
id: 'compact-feature',
|
||||
runId: 'run-feature',
|
||||
source: 'automatic' as const,
|
||||
status: 'error' as const,
|
||||
willRetry: true,
|
||||
summary: '保留了本轮关键上下文。',
|
||||
},
|
||||
],
|
||||
};
|
||||
codingConversationStore.getState().applySnapshotEvent({
|
||||
type: 'snapshot',
|
||||
conversationId: 'conversation-feature-ui',
|
||||
workerGeneration: 1,
|
||||
seq: snapshot.cursor.seq,
|
||||
snapshot,
|
||||
});
|
||||
const onFork = vi.fn();
|
||||
|
||||
render(<CodingConversationTimeline conversationId="conversation-feature-ui" onFork={onFork} />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '从这里创建新对话分支' }));
|
||||
expect(onFork).toHaveBeenCalledWith('entry-feature');
|
||||
expect(screen.getByText('并行子任务')).toBeInTheDocument();
|
||||
expect(screen.getByText('单个子任务')).toBeInTheDocument();
|
||||
expect(screen.getByText('串行子任务')).toBeInTheDocument();
|
||||
expect(screen.getByText('reader')).toBeInTheDocument();
|
||||
expect(screen.getByText('BUILD_FAILED')).toBeInTheDocument();
|
||||
expect(screen.getByText('系统会自动重试。')).toBeInTheDocument();
|
||||
expect(screen.getByText('inline result').closest('[data-node-kind="tool"]')).not.toBeNull();
|
||||
expect(screen.queryByText(/回滚|revert/i)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user