merge main

This commit is contained in:
DennisYu07 2026-03-19 17:12:19 +08:00
commit 6f914e4f4e
228 changed files with 28059 additions and 2618 deletions

View file

@ -21,6 +21,8 @@ import type { ContentGeneratorConfigSources } from '../core/contentGenerator.js'
import type { MCPOAuthConfig } from '../mcp/oauth-provider.js';
import type { ShellExecutionConfig } from '../services/shellExecutionService.js';
import type { AnyToolInvocation } from '../tools/tools.js';
import type { ArenaManager } from '../agents/arena/ArenaManager.js';
import { ArenaAgentClient } from '../agents/arena/ArenaAgentClient.js';
// Core
import { BaseLlmClient } from '../core/baseLlmClient.js';
@ -290,6 +292,26 @@ export interface SandboxConfig {
image: string;
}
/**
* Settings shared across multi-agent collaboration features
* (Arena, Team, Swarm).
*/
export interface AgentsCollabSettings {
/** Display mode for multi-agent sessions ('in-process' | 'tmux' | 'iterm2') */
displayMode?: string;
/** Arena-specific settings */
arena?: {
/** Custom base directory for Arena worktrees (default: ~/.qwen/arena) */
worktreeBaseDir?: string;
/** Preserve worktrees and state files after session ends */
preserveArtifacts?: boolean;
/** Maximum rounds (turns) per agent. No limit if unset. */
maxRoundsPerAgent?: number;
/** Total timeout in seconds for the Arena session. No limit if unset. */
timeoutSeconds?: number;
};
}
export interface ConfigParameters {
sessionId?: string;
sessionData?: ResumedSessionData;
@ -387,6 +409,8 @@ export interface ConfigParameters {
channel?: string;
/** Model providers configuration grouped by authType */
modelProvidersConfig?: ModelProvidersConfig;
/** Multi-agent collaboration settings (Arena, Team, Swarm) */
agents?: AgentsCollabSettings;
/** Enable hook system for lifecycle events */
enableHooks?: boolean;
/** Hooks configuration from settings */
@ -522,6 +546,12 @@ export class Config {
private readonly shouldUseNodePtyShell: boolean;
private readonly skipNextSpeakerCheck: boolean;
private shellExecutionConfig: ShellExecutionConfig;
private arenaManager: ArenaManager | null = null;
private arenaManagerChangeCallback:
| ((manager: ArenaManager | null) => void)
| null = null;
private readonly arenaAgentClient: ArenaAgentClient | null;
private readonly agentsSettings: AgentsCollabSettings;
private readonly skipLoopDetection: boolean;
private readonly skipStartupContext: boolean;
private readonly warnings: string[];
@ -657,6 +687,8 @@ export class Config {
this.inputFormat = params.inputFormat ?? InputFormat.TEXT;
this.fileExclusions = new FileExclusions(this);
this.eventEmitter = params.eventEmitter;
this.arenaAgentClient = ArenaAgentClient.create();
this.agentsSettings = params.agents ?? {};
if (params.contextFileName) {
setGeminiMdFilename(params.contextFileName);
}
@ -1273,6 +1305,8 @@ export class Config {
if (this.toolRegistry) {
await this.toolRegistry.stop();
}
await this.cleanupArenaRuntime();
} catch (error) {
// Log but don't throw - cleanup should be best-effort
this.debugLogger.error('Error during Config shutdown:', error);
@ -1425,6 +1459,50 @@ export class Config {
this.geminiMdFileCount = count;
}
getArenaManager(): ArenaManager | null {
return this.arenaManager;
}
setArenaManager(manager: ArenaManager | null): void {
this.arenaManager = manager;
this.arenaManagerChangeCallback?.(manager);
}
/**
* Register a callback invoked whenever the arena manager changes.
* Pass `null` to unsubscribe. Only one subscriber is supported.
*/
onArenaManagerChange(
cb: ((manager: ArenaManager | null) => void) | null,
): void {
this.arenaManagerChangeCallback = cb;
}
getArenaAgentClient(): ArenaAgentClient | null {
return this.arenaAgentClient;
}
getAgentsSettings(): AgentsCollabSettings {
return this.agentsSettings;
}
/**
* Clean up Arena runtime. When `force` is true (e.g., /arena select --discard),
* always removes worktrees regardless of preserveArtifacts.
*/
async cleanupArenaRuntime(force?: boolean): Promise<void> {
const manager = this.arenaManager;
if (!manager) {
return;
}
if (!force && this.agentsSettings.arena?.preserveArtifacts) {
await manager.cleanupRuntime();
} else {
await manager.cleanup();
}
this.setArenaManager(null);
}
getApprovalMode(): ApprovalMode {
return this.approvalMode;
}
@ -1898,6 +1976,7 @@ export class Config {
async createToolRegistry(
sendSdkMcpMessage?: SendSdkMcpMessage,
options?: { skipDiscovery?: boolean },
): Promise<ToolRegistry> {
const registry = new ToolRegistry(
this,
@ -1987,7 +2066,9 @@ export class Config {
registerCoreTool(LspTool, this);
}
await registry.discoverAllTools();
if (!options?.skipDiscovery) {
await registry.discoverAllTools();
}
this.debugLogger.debug(
`ToolRegistry created: ${JSON.stringify(registry.getAllToolNames())} (${registry.getAllToolNames().length} tools)`,
);