fix: omit reasoning summary for codex spark subagents (#594)

This commit is contained in:
SmallSpider
2026-05-07 16:44:56 +08:00
committed by GitHub
parent b17269d9e9
commit 293f944724
3 changed files with 112 additions and 14 deletions
@@ -214,7 +214,7 @@ describe('AcpSdkBackend', () => {
});
it('emits turn_complete after trailing tool updates from the same turn', async () => {
backendStatics.UPDATE_QUIET_PERIOD_MS = 8;
backendStatics.UPDATE_QUIET_PERIOD_MS = 25;
backendStatics.UPDATE_DRAIN_TIMEOUT_MS = 200;
backendStatics.PRE_PROMPT_UPDATE_QUIET_PERIOD_MS = 1;
backendStatics.PRE_PROMPT_UPDATE_DRAIN_TIMEOUT_MS = 50;
@@ -254,7 +254,7 @@ describe('AcpSdkBackend', () => {
status: 'in_progress'
}
});
}, 3);
}, 1);
setTimeout(() => {
backendInternal.handleSessionUpdate({
@@ -266,7 +266,7 @@ describe('AcpSdkBackend', () => {
rawOutput: { ok: true }
}
});
}, 6);
}, 2);
return { stopReason: 'end_turn' };
},
+88 -4
View File
@@ -1,8 +1,10 @@
import { describe, expect, it } from 'vitest';
import type { EnhancedMode } from '../loop';
import {
buildThreadStartParams,
buildTurnStartParams,
codexCollaborationSpawnAgentInstructions
codexCollaborationSpawnAgentInstructions,
supportsReasoningSummary
} from './appServerConfig';
import { codexSystemPrompt } from './systemPrompt';
@@ -123,18 +125,100 @@ describe('appServerConfig', () => {
expect(params.approvalPolicy).toBe('never');
expect(params.sandboxPolicy).toEqual({ type: 'readOnly' });
expect(params.effort).toBe('high');
expect(params.summary).toBe('detailed');
expect(params.summary).toBeUndefined();
expect(params.collaborationMode).toEqual({
mode: 'default',
settings: {
model: 'o3',
reasoning_effort: 'high',
developer_instructions: withCollaborationInstructions(codexSystemPrompt)
}
});
expect(params.model).toBeUndefined();
});
it('omits reasoning summary for models that do not support it', () => {
const params = buildTurnStartParams({
threadId: 'thread-1',
message: 'hello',
cwd: '/workspace/project',
mode: {
permissionMode: 'default',
model: 'gpt-5.3-codex-spark',
modelReasoningEffort: 'high',
collaborationMode: 'default'
}
});
expect(params.effort).toBe('high');
expect(params.summary).toBeUndefined();
expect(params.collaborationMode).toEqual({
mode: 'default',
settings: {
model: 'gpt-5.3-codex-spark',
developer_instructions: withCollaborationInstructions(codexSystemPrompt)
}
});
});
it('detects namespaced models that do not support reasoning summary', () => {
const params = buildTurnStartParams({
threadId: 'thread-1',
message: 'hello',
cwd: '/workspace/project',
mode: {
permissionMode: 'default',
model: 'codex/gpt-5.3-codex-spark',
modelReasoningEffort: 'high',
collaborationMode: 'default'
}
});
expect(params.effort).toBe('high');
expect(params.summary).toBeUndefined();
});
it('normalizes reasoning summary model support checks', () => {
expect(supportsReasoningSummary(' Codex/GPT-5.3-CODEX-SPARK ')).toBe(false);
expect(supportsReasoningSummary('gpt-5.5')).toBe(true);
expect(supportsReasoningSummary(undefined)).toBe(true);
});
it('omits reasoning summary for non-collaboration turns on unsupported models', () => {
const params = buildTurnStartParams({
threadId: 'thread-1',
message: 'hello',
cwd: '/workspace/project',
mode: {
permissionMode: 'default',
model: 'gpt-5.3-codex-spark',
modelReasoningEffort: 'high'
} as EnhancedMode
});
expect(params.effort).toBe('high');
expect(params.summary).toBeUndefined();
expect(params.model).toBe('gpt-5.3-codex-spark');
expect(params.collaborationMode).toBeUndefined();
});
it('keeps reasoning summary for non-collaboration turns on supported models', () => {
const params = buildTurnStartParams({
threadId: 'thread-1',
message: 'hello',
cwd: '/workspace/project',
mode: {
permissionMode: 'default',
model: 'o3',
modelReasoningEffort: 'high'
} as EnhancedMode
});
expect(params.effort).toBe('high');
expect(params.summary).toBe('detailed');
expect(params.model).toBe('o3');
expect(params.collaborationMode).toBeUndefined();
});
it('puts collaboration mode in turn params with model settings', () => {
const params = buildTurnStartParams({
threadId: 'thread-1',
@@ -152,7 +236,6 @@ describe('appServerConfig', () => {
mode: 'plan',
settings: {
model: 'o3',
reasoning_effort: 'high',
developer_instructions: withCollaborationInstructions(codexSystemPrompt)
}
});
@@ -189,6 +272,7 @@ describe('appServerConfig', () => {
expect(instructions).toContain('If you call spawn_agent with fork_context: true');
expect(instructions).toContain('do not set agent_type, model, or reasoning_effort');
expect(instructions).toContain('omit fork_context or set fork_context: false');
expect(instructions).toContain('Do not rely on parent turn reasoning settings for spawned agents');
});
it('rejects collaboration mode payloads without a resolved model', () => {
+21 -7
View File
@@ -14,9 +14,14 @@ import { resolveCodexPermissionModeConfig } from './permissionModeConfig';
export const codexCollaborationSpawnAgentInstructions = [
'Codex sub-agent spawning rules:',
'- If you call spawn_agent with fork_context: true, do not set agent_type, model, or reasoning_effort; full-history forked agents inherit these values from the parent.',
'- If you need a specific agent_type, model, or reasoning_effort, omit fork_context or set fork_context: false, and include only the necessary context in the message.'
'- If you need a specific agent_type, model, or reasoning_effort, omit fork_context or set fork_context: false, and include only the necessary context in the message.',
'- Do not rely on parent turn reasoning settings for spawned agents; only set reasoning_effort on spawn_agent when the chosen child model supports it.'
].join('\n');
const MODELS_WITHOUT_REASONING_SUMMARY = new Set([
'gpt-5.3-codex-spark'
]);
function resolveApprovalPolicy(mode: EnhancedMode): ApprovalPolicy {
return resolveCodexPermissionModeConfig(mode.permissionMode).approvalPolicy;
}
@@ -42,6 +47,13 @@ function resolveSandboxPolicyOverride(value: CodexCliOverrides['sandbox'] | unde
}
}
export function supportsReasoningSummary(model: string | undefined): boolean {
const normalized = model?.trim().toLowerCase();
if (!normalized) return true;
const modelName = normalized.split('/').pop() ?? normalized;
return !MODELS_WITHOUT_REASONING_SUMMARY.has(modelName);
}
function buildMcpServerConfig(mcpServers: McpServersConfig): Record<string, unknown> {
const config: Record<string, unknown> = {};
@@ -151,13 +163,16 @@ export function buildTurnStartParams(args: {
params.sandboxPolicy = sandboxPolicy;
}
if (args.mode?.modelReasoningEffort) {
params.effort = args.mode.modelReasoningEffort;
params.summary = 'detailed';
}
const collaborationMode = args.mode?.collaborationMode;
const model = args.overrides?.model ?? args.mode?.model;
if (args.mode?.modelReasoningEffort) {
params.effort = args.mode.modelReasoningEffort;
if (!collaborationMode && supportsReasoningSummary(model)) {
params.summary = 'detailed';
}
}
if (collaborationMode) {
if (!model) {
throw new Error(`Collaboration mode '${collaborationMode}' requires a resolved model`);
@@ -167,7 +182,6 @@ export function buildTurnStartParams(args: {
mode: collaborationMode,
settings: {
model,
...(args.mode?.modelReasoningEffort ? { reasoning_effort: args.mode.modelReasoningEffort } : {}),
developer_instructions: appendCollaborationInstructions(developerInstructions)
}
};