feat(arena): Persist arena events to chat history and add progress updates

- Replace SESSION_WARNING with SESSION_UPDATE supporting info/warning types
- Emit setup progress messages from ArenaManager during agent initialization
- Record all arena UI events to session JSONL for chat history replay
- Clean up unused agent event types (stream, tool calls, stats)
- Update arena select/stop dialogs to record their output

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
tanzhenxin 2026-02-18 14:33:37 +08:00
parent 6b55c8161f
commit 193bc438bd
8 changed files with 163 additions and 150 deletions

View file

@ -14,7 +14,7 @@ import {
} from '@qwen-code/qwen-code-core';
import { theme } from '../semantic-colors.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { MessageType } from '../types.js';
import { MessageType, type HistoryItemWithoutId } from '../types.js';
import type { UseHistoryManagerReturn } from '../hooks/useHistoryManager.js';
import { formatDuration } from '../utils/formatters.js';
import { getArenaStatusLabel } from '../utils/displayUtils.js';
@ -36,18 +36,25 @@ export function ArenaSelectDialog({
}: ArenaSelectDialogProps): React.JSX.Element {
const pushMessage = useCallback(
(result: { messageType: 'info' | 'error'; content: string }) => {
addItem(
{
type:
result.messageType === 'info'
? MessageType.INFO
: MessageType.ERROR,
text: result.content,
},
Date.now(),
);
const item: HistoryItemWithoutId = {
type:
result.messageType === 'info' ? MessageType.INFO : MessageType.ERROR,
text: result.content,
};
addItem(item, Date.now());
try {
const chatRecorder = config.getChatRecordingService();
chatRecorder?.recordSlashCommand({
phase: 'result',
rawCommand: '/arena select',
outputHistoryItems: [{ ...item } as Record<string, unknown>],
});
} catch {
// Best-effort recording
}
},
[addItem],
[addItem, config],
);
const onSelect = useCallback(

View file

@ -14,7 +14,7 @@ import {
} from '@qwen-code/qwen-code-core';
import { theme } from '../semantic-colors.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { MessageType } from '../types.js';
import { MessageType, type HistoryItemWithoutId } from '../types.js';
import type { UseHistoryManagerReturn } from '../hooks/useHistoryManager.js';
import { DescriptiveRadioButtonSelect } from './shared/DescriptiveRadioButtonSelect.js';
import type { DescriptiveRadioSelectItem } from './shared/DescriptiveRadioButtonSelect.js';
@ -38,18 +38,25 @@ export function ArenaStopDialog({
const pushMessage = useCallback(
(result: { messageType: 'info' | 'error'; content: string }) => {
addItem(
{
type:
result.messageType === 'info'
? MessageType.INFO
: MessageType.ERROR,
text: result.content,
},
Date.now(),
);
const item: HistoryItemWithoutId = {
type:
result.messageType === 'info' ? MessageType.INFO : MessageType.ERROR,
text: result.content,
};
addItem(item, Date.now());
try {
const chatRecorder = config.getChatRecordingService();
chatRecorder?.recordSlashCommand({
phase: 'result',
rawCommand: '/arena stop',
outputHistoryItems: [{ ...item } as Record<string, unknown>],
});
} catch {
// Best-effort recording
}
},
[addItem],
[addItem, config],
);
const onStop = useCallback(

View file

@ -35,7 +35,7 @@ export const ArenaAgentCard: React.FC<ArenaAgentCardProps> = ({
{/* Line 1: Status icon + text + label + duration */}
<Box>
<Text color={color}>
{icon} {text}: {agent.label} · {duration}
{icon} {agent.label} · {text} · {duration}
</Text>
</Box>