mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-09 17:19:02 +00:00
The concurrency cap (QWEN_CODE_MAX_BACKGROUND_AGENTS) only checked backgrounded agents, letting foreground agents like Explorer bypass it entirely. Count all running agents toward the cap and apply the preflight check regardless of background/foreground flavor. Co-authored-by: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
parent
aa4bccfc47
commit
4400fe6a38
3 changed files with 28 additions and 26 deletions
|
|
@ -511,16 +511,24 @@ describe('BackgroundTaskRegistry', () => {
|
|||
expect(registry.get('bg-1')?.prompt).toBe('resumed continuation');
|
||||
});
|
||||
|
||||
it('does not count foreground, paused, or terminal entries toward the cap', () => {
|
||||
it('counts foreground agents toward the cap but not paused or terminal entries', () => {
|
||||
registry = new BackgroundTaskRegistry({
|
||||
maxConcurrentBackgroundAgents: 1,
|
||||
});
|
||||
|
||||
// A foreground agent occupies a slot.
|
||||
registry.register(
|
||||
makeRegistration('fg-1', {
|
||||
isBackgrounded: false,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(() => registry.register(makeRegistration('bg-1'))).toThrow(
|
||||
'maximum concurrent background agents (1) reached',
|
||||
);
|
||||
|
||||
// Paused entries do not occupy a slot.
|
||||
registry.unregisterForeground('fg-1');
|
||||
registry.register(
|
||||
makeRegistration('paused-1', {
|
||||
status: 'paused',
|
||||
|
|
@ -535,7 +543,6 @@ describe('BackgroundTaskRegistry', () => {
|
|||
registry.complete('bg-1', 'done');
|
||||
registry.register(makeRegistration('bg-2'));
|
||||
|
||||
expect(registry.get('fg-1')).toBeDefined();
|
||||
expect(registry.get('paused-1')).toBeDefined();
|
||||
expect(registry.get('bg-2')?.status).toBe('running');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -428,10 +428,9 @@ export class BackgroundTaskRegistry {
|
|||
registration: AgentTaskRegistration,
|
||||
options: BackgroundTaskRegisterOptions = {},
|
||||
): AgentTask {
|
||||
if (registration.isBackgrounded && registration.status === 'running') {
|
||||
if (registration.status === 'running') {
|
||||
const existing = this.agents.get(registration.agentId);
|
||||
const isReplacingRunning =
|
||||
existing?.isBackgrounded === true && existing.status === 'running';
|
||||
const isReplacingRunning = existing?.status === 'running';
|
||||
if (!isReplacingRunning) {
|
||||
this.assertCanStartBackgroundAgent();
|
||||
}
|
||||
|
|
@ -852,7 +851,7 @@ export class BackgroundTaskRegistry {
|
|||
|
||||
private getRunningBackgroundCount(): number {
|
||||
return Array.from(this.agents.values()).filter(
|
||||
(entry) => entry.isBackgrounded && entry.status === 'running',
|
||||
(entry) => entry.status === 'running',
|
||||
).length;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2135,26 +2135,22 @@ class AgentToolInvocation extends BaseToolInvocation<AgentParams, ToolResult> {
|
|||
// This is not redundant with registry.register() below — that call
|
||||
// remains the authoritative race guard, but by then the launch path
|
||||
// has already run hooks and created a child agent.
|
||||
if (shouldRunInBackground) {
|
||||
try {
|
||||
this.config
|
||||
.getBackgroundTaskRegistry()
|
||||
.assertCanStartBackgroundAgent();
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
this.updateDisplay(
|
||||
{
|
||||
status: 'failed',
|
||||
terminateReason: errorMessage,
|
||||
},
|
||||
updateOutput,
|
||||
);
|
||||
return {
|
||||
llmContent: errorMessage,
|
||||
returnDisplay: this.currentDisplay!,
|
||||
};
|
||||
}
|
||||
try {
|
||||
this.config.getBackgroundTaskRegistry().assertCanStartBackgroundAgent();
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
this.updateDisplay(
|
||||
{
|
||||
status: 'failed',
|
||||
terminateReason: errorMessage,
|
||||
},
|
||||
updateOutput,
|
||||
);
|
||||
return {
|
||||
llmContent: errorMessage,
|
||||
returnDisplay: this.currentDisplay!,
|
||||
};
|
||||
}
|
||||
|
||||
// ── Optional worktree isolation (Phase 1: provision) ──────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue