From 293f9447246f912b08e80e97d4a48d9f4e7fc024 Mon Sep 17 00:00:00 2001 From: SmallSpider <568442079@qq.com> Date: Thu, 7 May 2026 16:44:56 +0800 Subject: [PATCH] fix: omit reasoning summary for codex spark subagents (#594) --- .../agent/backends/acp/AcpSdkBackend.test.ts | 6 +- cli/src/codex/utils/appServerConfig.test.ts | 92 ++++++++++++++++++- cli/src/codex/utils/appServerConfig.ts | 28 ++++-- 3 files changed, 112 insertions(+), 14 deletions(-) diff --git a/cli/src/agent/backends/acp/AcpSdkBackend.test.ts b/cli/src/agent/backends/acp/AcpSdkBackend.test.ts index a38ae2c3..d167e2d9 100644 --- a/cli/src/agent/backends/acp/AcpSdkBackend.test.ts +++ b/cli/src/agent/backends/acp/AcpSdkBackend.test.ts @@ -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' }; }, diff --git a/cli/src/codex/utils/appServerConfig.test.ts b/cli/src/codex/utils/appServerConfig.test.ts index fcb48107..6ac817b6 100644 --- a/cli/src/codex/utils/appServerConfig.test.ts +++ b/cli/src/codex/utils/appServerConfig.test.ts @@ -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', () => { diff --git a/cli/src/codex/utils/appServerConfig.ts b/cli/src/codex/utils/appServerConfig.ts index 3bad471e..292d5dab 100644 --- a/cli/src/codex/utils/appServerConfig.ts +++ b/cli/src/codex/utils/appServerConfig.ts @@ -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 { const config: Record = {}; @@ -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) } };