feat(coding): complete PI conversation UI

This commit is contained in:
2026-08-24 08:59:16 +08:00
parent 2613d6530b
commit 863f005203
21 changed files with 2040 additions and 137 deletions

View File

@@ -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();
});
});