feat(arena): add IDLE status for agent follow-up task support

- Introduce AgentStatus.IDLE for agents that finished work but can accept follow-up messages
- Add isSettledStatus() helper to check if agent is settled (IDLE or terminal)
- Update ArenaManager to transition to IDLE after agents finish initial task
- Keep agent tabs visible when session is IDLE so users can continue interacting
- Fix listener cleanup to not detach on IDLE (agents remain alive)
- Update tests to expect 'idle' status after successful completion

This enables the arena collaboration feature where agents can receive
additional tasks after completing their initial work.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
tanzhenxin 2026-03-09 21:33:48 +08:00
parent 4a681f435d
commit eaef9efe90
13 changed files with 125 additions and 51 deletions

View file

@ -18,9 +18,11 @@
import { useEffect, useRef } from 'react';
import {
ArenaEventType,
ArenaSessionStatus,
DISPLAY_MODE,
type ArenaManager,
type ArenaAgentStartEvent,
type ArenaSessionCompleteEvent,
type Config,
type InProcessBackend,
} from '@qwen-code/qwen-code-core';
@ -123,9 +125,9 @@ export function useArenaInProcess(config: Config): void {
tryRegister(MAX_AGENT_RETRIES);
};
// On session end, unregister agents, remove listeners from this
// manager, and resume polling for a genuinely new manager instance.
const onSessionEnd = () => {
// Tear down agent tabs, remove listeners, and resume polling for
// a genuinely new manager instance.
const teardown = () => {
actionsRef.current.unregisterAll();
for (const timeout of retryTimeouts) {
clearTimeout(timeout);
@ -133,8 +135,8 @@ export function useArenaInProcess(config: Config): void {
retryTimeouts.clear();
// Remove listeners eagerly so they don't fire again
emitter.off(ArenaEventType.AGENT_START, onAgentStart);
emitter.off(ArenaEventType.SESSION_COMPLETE, onSessionEnd);
emitter.off(ArenaEventType.SESSION_ERROR, onSessionEnd);
emitter.off(ArenaEventType.SESSION_COMPLETE, onSessionComplete);
emitter.off(ArenaEventType.SESSION_ERROR, teardown);
detachListeners = null;
// Keep attachedManager reference — prevents reattach to this
// same (completed) manager on the next poll tick.
@ -144,14 +146,24 @@ export function useArenaInProcess(config: Config): void {
}
};
// When agents settle to IDLE the session is still alive — keep
// the tab bar so users can continue interacting with agents.
// Only tear down on truly terminal session statuses.
const onSessionComplete = (event: ArenaSessionCompleteEvent) => {
if (event.result.status === ArenaSessionStatus.IDLE) {
return;
}
teardown();
};
emitter.on(ArenaEventType.AGENT_START, onAgentStart);
emitter.on(ArenaEventType.SESSION_COMPLETE, onSessionEnd);
emitter.on(ArenaEventType.SESSION_ERROR, onSessionEnd);
emitter.on(ArenaEventType.SESSION_COMPLETE, onSessionComplete);
emitter.on(ArenaEventType.SESSION_ERROR, teardown);
detachListeners = () => {
emitter.off(ArenaEventType.AGENT_START, onAgentStart);
emitter.off(ArenaEventType.SESSION_COMPLETE, onSessionEnd);
emitter.off(ArenaEventType.SESSION_ERROR, onSessionEnd);
emitter.off(ArenaEventType.SESSION_COMPLETE, onSessionComplete);
emitter.off(ArenaEventType.SESSION_ERROR, teardown);
};
};