mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 20:50:34 +00:00
Merge branch 'main' into feat/support-permission
This commit is contained in:
commit
f9d9a985ce
249 changed files with 26635 additions and 2729 deletions
|
|
@ -241,6 +241,30 @@ describe('parseArguments', () => {
|
|||
expect(argv.prompt).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should parse --system-prompt', async () => {
|
||||
process.argv = [
|
||||
'node',
|
||||
'script.js',
|
||||
'--system-prompt',
|
||||
'You are a test system prompt.',
|
||||
];
|
||||
const argv = await parseArguments();
|
||||
expect(argv.systemPrompt).toBe('You are a test system prompt.');
|
||||
expect(argv.appendSystemPrompt).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should parse --append-system-prompt', async () => {
|
||||
process.argv = [
|
||||
'node',
|
||||
'script.js',
|
||||
'--append-system-prompt',
|
||||
'Be extra concise.',
|
||||
];
|
||||
const argv = await parseArguments();
|
||||
expect(argv.appendSystemPrompt).toBe('Be extra concise.');
|
||||
expect(argv.systemPrompt).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should allow -r flag as alias for --resume', async () => {
|
||||
process.argv = [
|
||||
'node',
|
||||
|
|
@ -432,6 +456,21 @@ describe('parseArguments', () => {
|
|||
mockExit.mockRestore();
|
||||
});
|
||||
|
||||
it('should allow --system-prompt and --append-system-prompt together', async () => {
|
||||
process.argv = [
|
||||
'node',
|
||||
'script.js',
|
||||
'--system-prompt',
|
||||
'Override prompt',
|
||||
'--append-system-prompt',
|
||||
'Append prompt',
|
||||
];
|
||||
|
||||
const argv = await parseArguments();
|
||||
expect(argv.systemPrompt).toBe('Override prompt');
|
||||
expect(argv.appendSystemPrompt).toBe('Append prompt');
|
||||
});
|
||||
|
||||
it('should throw an error when include-partial-messages is used without stream-json output', async () => {
|
||||
process.argv = ['node', 'script.js', '--include-partial-messages'];
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {
|
|||
Config,
|
||||
DEFAULT_QWEN_EMBEDDING_MODEL,
|
||||
FileDiscoveryService,
|
||||
FileEncoding,
|
||||
getAllGeminiMdFilenames,
|
||||
loadServerHierarchicalMemory,
|
||||
setGeminiMdFilename as setServerGeminiMdFilename,
|
||||
|
|
@ -53,16 +52,16 @@ import { appEvents } from '../utils/events.js';
|
|||
import { mcpCommand } from '../commands/mcp.js';
|
||||
|
||||
// UUID v4 regex pattern for validation
|
||||
const UUID_REGEX =
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
const SESSION_ID_REGEX =
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}(-agent-[a-zA-Z0-9_.-]+)?$/i;
|
||||
|
||||
/**
|
||||
* Validates if a string is a valid UUID format
|
||||
* @param value - The string to validate
|
||||
* @returns True if the string is a valid UUID, false otherwise
|
||||
* Validates if a string is a valid session ID format.
|
||||
* Accepts a standard UUID, or a UUID followed by `-agent-{suffix}`
|
||||
* (used by Arena to give each agent a deterministic session ID).
|
||||
*/
|
||||
function isValidUUID(value: string): boolean {
|
||||
return UUID_REGEX.test(value);
|
||||
function isValidSessionId(value: string): boolean {
|
||||
return SESSION_ID_REGEX.test(value);
|
||||
}
|
||||
|
||||
import { isWorkspaceTrusted } from './trustedFolders.js';
|
||||
|
|
@ -112,6 +111,8 @@ export interface CliArgs {
|
|||
debug: boolean | undefined;
|
||||
prompt: string | undefined;
|
||||
promptInteractive: string | undefined;
|
||||
systemPrompt: string | undefined;
|
||||
appendSystemPrompt: string | undefined;
|
||||
yolo: boolean | undefined;
|
||||
approvalMode: string | undefined;
|
||||
telemetry: boolean | undefined;
|
||||
|
|
@ -291,6 +292,16 @@ export async function parseArguments(): Promise<CliArgs> {
|
|||
description:
|
||||
'Execute the provided prompt and continue in interactive mode',
|
||||
})
|
||||
.option('system-prompt', {
|
||||
type: 'string',
|
||||
description:
|
||||
'Override the main session system prompt for this run. Can be combined with --append-system-prompt.',
|
||||
})
|
||||
.option('append-system-prompt', {
|
||||
type: 'string',
|
||||
description:
|
||||
'Append instructions to the main session system prompt for this run. Can be combined with --system-prompt.',
|
||||
})
|
||||
.option('sandbox', {
|
||||
alias: 's',
|
||||
type: 'boolean',
|
||||
|
|
@ -559,10 +570,13 @@ export async function parseArguments(): Promise<CliArgs> {
|
|||
if (argv['sessionId'] && (argv['continue'] || argv['resume'])) {
|
||||
return 'Cannot use --session-id with --continue or --resume. Use --session-id to start a new session with a specific ID, or use --continue/--resume to resume an existing session.';
|
||||
}
|
||||
if (argv['sessionId'] && !isValidUUID(argv['sessionId'] as string)) {
|
||||
if (
|
||||
argv['sessionId'] &&
|
||||
!isValidSessionId(argv['sessionId'] as string)
|
||||
) {
|
||||
return `Invalid --session-id: "${argv['sessionId']}". Must be a valid UUID (e.g., "123e4567-e89b-12d3-a456-426614174000").`;
|
||||
}
|
||||
if (argv['resume'] && !isValidUUID(argv['resume'] as string)) {
|
||||
if (argv['resume'] && !isValidSessionId(argv['resume'] as string)) {
|
||||
return `Invalid --resume: "${argv['resume']}". Must be a valid UUID (e.g., "123e4567-e89b-12d3-a456-426614174000").`;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -1007,6 +1021,8 @@ export async function loadCliConfig(
|
|||
importFormat: settings.context?.importFormat || 'tree',
|
||||
debugMode,
|
||||
question,
|
||||
systemPrompt: argv.systemPrompt,
|
||||
appendSystemPrompt: argv.appendSystemPrompt,
|
||||
// Legacy fields – kept for backward compatibility with getCoreTools() etc.
|
||||
coreTools: argv.coreTools || settings.tools?.core || undefined,
|
||||
allowedTools: argv.allowedTools || settings.tools?.allowed || undefined,
|
||||
|
|
@ -1108,11 +1124,22 @@ export async function loadCliConfig(
|
|||
// always be true and the settings file can never disable recording.
|
||||
chatRecording:
|
||||
argv.chatRecording ?? settings.general?.chatRecording ?? true,
|
||||
defaultFileEncoding:
|
||||
settings.general?.defaultFileEncoding ?? FileEncoding.UTF8,
|
||||
defaultFileEncoding: settings.general?.defaultFileEncoding,
|
||||
lsp: {
|
||||
enabled: lspEnabled,
|
||||
},
|
||||
agents: settings.agents
|
||||
? {
|
||||
displayMode: settings.agents.displayMode,
|
||||
arena: settings.agents.arena
|
||||
? {
|
||||
worktreeBaseDir: settings.agents.arena.worktreeBaseDir,
|
||||
preserveArtifacts:
|
||||
settings.agents.arena.preserveArtifacts ?? false,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
|
||||
if (lspEnabled) {
|
||||
|
|
|
|||
|
|
@ -1294,6 +1294,104 @@ const SETTINGS_SCHEMA = {
|
|||
description: 'Configuration for web search providers.',
|
||||
showInDialog: false,
|
||||
},
|
||||
agents: {
|
||||
type: 'object',
|
||||
label: 'Agents',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: {},
|
||||
description:
|
||||
'Settings for multi-agent collaboration features (Arena, Team, Swarm).',
|
||||
showInDialog: false,
|
||||
properties: {
|
||||
displayMode: {
|
||||
type: 'enum',
|
||||
label: 'Display Mode',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: undefined as string | undefined,
|
||||
description:
|
||||
'Display mode for multi-agent sessions. Currently only "in-process" is supported.',
|
||||
showInDialog: false,
|
||||
options: [
|
||||
{ value: 'in-process', label: 'In-process' },
|
||||
// { value: 'tmux', label: 'tmux' },
|
||||
// { value: 'iterm2', label: 'iTerm2' },
|
||||
],
|
||||
},
|
||||
arena: {
|
||||
type: 'object',
|
||||
label: 'Arena',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: {},
|
||||
description: 'Settings for Arena (multi-model competitive execution).',
|
||||
showInDialog: false,
|
||||
properties: {
|
||||
worktreeBaseDir: {
|
||||
type: 'string',
|
||||
label: 'Worktree Base Directory',
|
||||
category: 'Advanced',
|
||||
requiresRestart: true,
|
||||
default: undefined as string | undefined,
|
||||
description:
|
||||
'Custom base directory for Arena worktrees. Defaults to ~/.qwen/arena.',
|
||||
showInDialog: false,
|
||||
},
|
||||
preserveArtifacts: {
|
||||
type: 'boolean',
|
||||
label: 'Preserve Arena Artifacts',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
description:
|
||||
'When enabled, Arena worktrees and session state files are preserved after the session ends or the main agent exits.',
|
||||
showInDialog: true,
|
||||
},
|
||||
maxRoundsPerAgent: {
|
||||
type: 'number',
|
||||
label: 'Max Rounds Per Agent',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: undefined as number | undefined,
|
||||
description:
|
||||
'Maximum number of rounds (turns) each agent can execute. No limit if unset.',
|
||||
showInDialog: false,
|
||||
},
|
||||
timeoutSeconds: {
|
||||
type: 'number',
|
||||
label: 'Timeout (seconds)',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: undefined as number | undefined,
|
||||
description:
|
||||
'Total timeout in seconds for the Arena session. No limit if unset.',
|
||||
showInDialog: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
team: {
|
||||
type: 'object',
|
||||
label: 'Team',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: {},
|
||||
description:
|
||||
'Settings for Agent Team (role-based collaborative execution). Reserved for future use.',
|
||||
showInDialog: false,
|
||||
},
|
||||
swarm: {
|
||||
type: 'object',
|
||||
label: 'Swarm',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: {},
|
||||
description:
|
||||
'Settings for Agent Swarm (parallel sub-agent execution). Reserved for future use.',
|
||||
showInDialog: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
hooksConfig: {
|
||||
type: 'object',
|
||||
|
|
@ -1365,6 +1463,17 @@ const SETTINGS_SCHEMA = {
|
|||
},
|
||||
},
|
||||
},
|
||||
|
||||
experimental: {
|
||||
type: 'object',
|
||||
label: 'Experimental',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: {},
|
||||
description: 'Setting to enable experimental features',
|
||||
showInDialog: false,
|
||||
properties: {},
|
||||
},
|
||||
} as const satisfies SettingsSchema;
|
||||
|
||||
export type SettingsSchemaType = typeof SETTINGS_SCHEMA;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue