feat: support max reasoning effort and rebuild release
This commit is contained in:
58
.planning/codex-0147-audit/findings.md
Normal file
58
.planning/codex-0147-audit/findings.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# 审计发现
|
||||
|
||||
## 初始关注面
|
||||
|
||||
- app-server 初始化与能力探测
|
||||
- collaborationMode / goals / guided input
|
||||
- thread/start、turn/start 参数形状
|
||||
- MCP server 线程级注入
|
||||
- 流式事件与 item 类型
|
||||
- CLI 版本检测和降级逻辑
|
||||
- mock / regression 覆盖
|
||||
|
||||
## 已确认基线
|
||||
|
||||
- 本机实际运行 `codex-cli 0.147.0`,路径为 `/home/hdzx/.local/lib/node_modules/@openai/codex/bin/codex.js`。
|
||||
- 官方 0.147.0 发布于 2026-08-07;主要新增 Agent Plugins、会话 sections、`--approve-for-me`、MCP 2026-07-28,并包含多项 app-server 协议调整。
|
||||
- 与 cc-web 最相关的上游提交包括:`Box app-server event payloads`、`Update tests for current tool call and app-server event types`、持久化 thread sections、显式 user-input blocking、MCP 非阻塞启动和 paginated discovery。
|
||||
- cc-web 当前通过 `thread/start.config["mcp_servers.*"]` 注入 MCP;通过 `collaborationMode.settings` 传 model/reasoning/developer instructions;存在 collaborationMode 不支持时的降级设计。
|
||||
- codebase-memory 索引 `home-cc-web` 为 ready(5152 nodes / 10588 edges)。
|
||||
|
||||
## 初步风险判断
|
||||
|
||||
- 0.147.0 的新增 CLI/TUI 功能多数不要求 cc-web 立即适配。
|
||||
- 最值得核对的是 app-server event payload boxing 与 tool-call item 类型变化;这可能直接影响事件分发与前端状态投影。
|
||||
- thread sections、plugin summaries、MCP 2026-07-28 属于可选新能力,若旧协议仍兼容则是功能缺口而非启动阻断。
|
||||
|
||||
## 协议核验
|
||||
|
||||
- 0.147.0 `generate-ts --experimental` 仍包含 cc-web 使用的 `thread/start`、`thread/resume`、`turn/start`、`experimentalFeature/enablement/set`、`collaborationMode/list`、`item/tool/requestUserInput` 和 approval server requests。
|
||||
- `CollaborationMode` 仍为 `{ mode, settings }`;cc-web 当前参数形状与生成协议一致。
|
||||
- 0.147.0 新增 thread sections、plugin APIs、thread status active flags、MCP tool read-only hints 等字段/方法;cc-web 暂未消费,但这些是增量能力。
|
||||
- 上游 “Box app-server event payloads” 只改变 Rust 内部 `AppServerEvent` / `InProcessServerEvent` 的内存表示,远程 JSON-RPC 序列化未变。
|
||||
- plaintext collaboration tool messages 新增的是 raw response item 的可选 `encrypted_function_args`,cc-web 不依赖该字段,当前容错投影不会因此失败。
|
||||
|
||||
## 最终差距
|
||||
|
||||
### P1:`request_user_input.isBlocking` 未适配
|
||||
|
||||
- 0.147.0 的 `item/tool/requestUserInput` 新增必填 `isBlocking`;`autoResolutionMs` 已弃用。
|
||||
- Plan 模式发送 `isBlocking: true`,客户端应持续等待明确回答;Default 模式在实验特性开启时发送 `isBlocking: false`,客户端不应无限阻塞。
|
||||
- cc-web 当前无条件创建 10 分钟 timer,并且发给前端的消息不携带 `isBlocking`;因此 Default 模式非阻塞请求会被错误地当作阻塞请求处理。
|
||||
- mock 没有发送 `isBlocking`,回归也只断言 questions,导致现有测试无法发现 0.147.0 这项变化。
|
||||
|
||||
### P2:MCP 2026-07-28 仅需预防性收口
|
||||
|
||||
- 0.147.0 中 `mcp_2026_07_28` 是 under-development 且默认 false,当前不会影响 cc-web。
|
||||
- cc-web MCP server 目前直接回显客户端传来的任意 `protocolVersion`,等于声明支持并未实现的未来协议;启用 2026 协议后存在协商不严谨风险。
|
||||
- 建议显式维护支持版本集合,并对未知版本按 MCP 协商规则选定受支持版本;`tools/list` 的 cursor/nextCursor 可作为启用 2026 协议前的兼容门槛。
|
||||
|
||||
### P3:可选产品能力
|
||||
|
||||
- thread sections、插件目录/安装、thread status active flags、MCP `readOnlyHint` 都是增量能力;不适配不影响现有聊天、协作、审批和 MCP 主链路。
|
||||
|
||||
## 验证结果
|
||||
|
||||
- `npm run regression`:通过。
|
||||
- 0.147.0 experimental TS / JSON Schema:生成成功。
|
||||
- 本机 0.147.0 的 feature 状态:`goals` stable/on,`plugins` stable/on,`mcp_2026_07_28` under-development/off,`default_mode_request_user_input` under-development/off。
|
||||
7
.planning/codex-0147-audit/progress.md
Normal file
7
.planning/codex-0147-audit/progress.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 审计进度
|
||||
|
||||
- 2026-08-10:开始 Codex CLI 0.147.0 适配审计;已读取项目约定、OpenAI 文档技能和文件化规划技能。
|
||||
- 2026-08-10:Codex manual helper 因 developers.openai.com HEAD 403 失败,已记录并切换官方后备来源。
|
||||
- 2026-08-10:已从 OpenAI 官方 GitHub release/compare 获取 0.147.0 变更,并确认本机版本与 cc-web 主要接入函数。
|
||||
- 2026-08-10:已生成 0.147.0 experimental TypeScript/JSON Schema 并逐项核对 cc-web 使用的请求、通知和参数形状。
|
||||
- 2026-08-10:完整回归通过;确认一个 P1 guided-input 语义差距、一个 P2 MCP 协商风险,其余 0.147.0 变化为兼容或可选能力。
|
||||
25
.planning/codex-0147-audit/task_plan.md
Normal file
25
.planning/codex-0147-audit/task_plan.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Codex CLI 0.147.0 适配审计
|
||||
|
||||
## 目标
|
||||
|
||||
判断 cc-web 对 Codex CLI / app-server 0.147.0 是否已跟上,并给出有证据的兼容性结论与改造优先级。
|
||||
|
||||
## 阶段
|
||||
|
||||
- [x] 获取 0.147.0 官方/上游变更事实
|
||||
- [x] 确认 cc-web 当前 Codex 接入架构与版本假设
|
||||
- [x] 逐项核对协议、事件、配置和工具兼容性
|
||||
- [x] 运行现有相关回归测试与最小协议探测
|
||||
- [x] 汇总结论、风险与建议改造清单
|
||||
|
||||
## 约束
|
||||
|
||||
- 本轮只审计,不修改业务代码。
|
||||
- 代码理解优先使用 codebase-memory-mcp,rg 仅作行号和文本校验。
|
||||
- 不使用 graphify。
|
||||
- 不覆盖用户现有改动。
|
||||
|
||||
## 错误记录
|
||||
|
||||
- 官方 Codex manual helper 的 HEAD 请求返回 HTTP 403;不重复同一路径,按 `openai-docs` 规则降级到官方 Docs MCP / 官方 GitHub 发布源。
|
||||
- 首次用 cc-web client 做只读 app-server 探测时进程约 25 秒后无输出退出;不重复该封装路径,改用 schema 生成、现有回归和当前真实会话证据交叉验证。
|
||||
Binary file not shown.
@@ -211,7 +211,7 @@ function createAgentRuntime(deps) {
|
||||
// cc-web UI supports "gpt-5.4(high)" style selection, but Codex CLI expects:
|
||||
// - model: "gpt-5.4"
|
||||
// - reasoning effort: config key `model_reasoning_effort = "high"`
|
||||
const m = raw.match(/^(.*)\((low|medium|high|xhigh|ultra)\)\s*$/i);
|
||||
const m = raw.match(/^(.*)\((low|medium|high|xhigh|max|ultra)\)\s*$/i);
|
||||
if (m) {
|
||||
const base = String(m[1] || '').trim();
|
||||
const lvl = String(m[2] || '').trim().toLowerCase();
|
||||
|
||||
@@ -10796,6 +10796,7 @@
|
||||
{ value: 'medium', label: 'medium', desc: '中等 thinking' },
|
||||
{ value: 'high', label: 'high', desc: '更强 thinking' },
|
||||
{ value: 'xhigh', label: 'xhigh', desc: '高强度 thinking' },
|
||||
{ value: 'max', label: 'max', desc: '最大强度 thinking' },
|
||||
{ value: 'ultra', label: 'ultra', desc: '最高强度 thinking' },
|
||||
];
|
||||
showOptionPicker('选择 Thinking 强度', thinkingOptions, current.level || '', (lvl) => {
|
||||
|
||||
@@ -4621,12 +4621,16 @@ function assertMultiAgentV2CompatibilityContract() {
|
||||
const frontendSource = fs.readFileSync(PUBLIC_APP_PATH, 'utf8');
|
||||
|
||||
assert(
|
||||
/CODEX_REASONING_LEVELS\s*=\s*new Set\(\[[^\]]*'ultra'/.test(serverSource),
|
||||
'Codex config model parsing should accept the ultra reasoning level'
|
||||
/CODEX_REASONING_LEVELS\s*=\s*new Set\(\[[^\]]*'max'[^\]]*'ultra'/.test(serverSource),
|
||||
'Codex config model parsing should accept the max and ultra reasoning levels'
|
||||
);
|
||||
assert(
|
||||
/low\|medium\|high\|xhigh\|ultra/.test(runtimeSource),
|
||||
'Codex CLI model suffix parsing should accept ultra'
|
||||
/low\|medium\|high\|xhigh\|max\|ultra/.test(runtimeSource),
|
||||
'Codex CLI model suffix parsing should accept max and ultra'
|
||||
);
|
||||
assert(
|
||||
frontendSource.includes("{ value: 'max', label: 'max'"),
|
||||
'Codex model picker should expose max reasoning'
|
||||
);
|
||||
assert(
|
||||
frontendSource.includes("{ value: 'ultra', label: 'ultra'"),
|
||||
@@ -5665,7 +5669,7 @@ async function main() {
|
||||
}, null, 2));
|
||||
|
||||
createFakeClaudeHistory(homeDir);
|
||||
createFakeCodexConfig(homeDir, { reasoningEffort: 'ultra' });
|
||||
createFakeCodexConfig(homeDir, { reasoningEffort: 'max' });
|
||||
const codexFixture = createFakeCodexHistory(homeDir);
|
||||
const codexAppImportFixture = createFakeCodexHistory(homeDir, {
|
||||
threadId: 'codexapp-import-thread',
|
||||
@@ -5815,7 +5819,7 @@ async function main() {
|
||||
ws.send(JSON.stringify({ type: 'new_session', agent: 'codex', cwd: codexInitCwd, mode: 'plan' }));
|
||||
const codexSession = await nextMessage(messages, ws, (msg) => msg.type === 'session_info' && msg.agent === 'codex' && msg.cwd === codexInitCwd);
|
||||
assert(codexSession.mode === 'plan', 'Codex new_session should follow requested mode');
|
||||
assert(codexSession.model === 'gpt-5.5(ultra)', 'Codex new_session should preserve ultra from ~/.codex/config.toml');
|
||||
assert(codexSession.model === 'gpt-5.5(max)', 'Codex new_session should preserve max from ~/.codex/config.toml');
|
||||
|
||||
ws.send(JSON.stringify({ type: 'set_session_pinned', sessionId: codexSession.sessionId, pinned: true }));
|
||||
const pinnedAck = await nextMessage(messages, ws, (msg) => msg.type === 'session_pinned' && msg.sessionId === codexSession.sessionId);
|
||||
@@ -6441,7 +6445,7 @@ async function main() {
|
||||
.find((line) => line.includes(`"event":"process_spawn"`) && line.includes(firstMessageSession.sessionId.slice(0, 8)));
|
||||
assert(spawnLine && !spawnLine.includes('--search') && spawnLine.includes('--image'), 'Codex exec should attach images and not append unsupported --search flag');
|
||||
const parsedSpawnLine = JSON.parse(spawnLine);
|
||||
assert(parsedSpawnLine.args.includes('model_reasoning_effort="ultra"'), 'Codex exec should pass the ultra reasoning level through model_reasoning_effort');
|
||||
assert(parsedSpawnLine.args.includes('model_reasoning_effort="max"'), 'Codex exec should pass the max reasoning level through model_reasoning_effort');
|
||||
|
||||
const allSpawnsForSession = processLog
|
||||
.trim()
|
||||
@@ -6506,7 +6510,7 @@ async function main() {
|
||||
].join('\n'));
|
||||
ws.send(JSON.stringify({ type: 'new_session', agent: 'codexapp', cwd: codexAppCwd, mode: 'yolo' }));
|
||||
const codexAppSession = await nextMessage(messages, ws, (msg) => msg.type === 'session_info' && msg.agent === 'codexapp' && msg.cwd === codexAppCwd);
|
||||
assert(codexAppSession.model === 'gpt-5.5(ultra)', 'Codex App new_session should preserve the ultra default Codex model');
|
||||
assert(codexAppSession.model === 'gpt-5.5(max)', 'Codex App new_session should preserve the max default Codex model');
|
||||
|
||||
ws.send(JSON.stringify({ type: 'composer_suggestions', requestId: 'reg-codexapp-skill', trigger: '$', query: 'reg', sessionId: codexAppSession.sessionId, agent: 'codexapp' }));
|
||||
const codexAppSkillComposer = await nextMessage(messages, ws, (msg) => msg.type === 'composer_suggestions' && msg.requestId === 'reg-codexapp-skill');
|
||||
@@ -6522,7 +6526,7 @@ async function main() {
|
||||
assert(/"hasDeveloperInstructions":true/.test(codexAppDefaultCollab.text || ''), 'Codex App collaboration settings should include sub-agent developer instructions');
|
||||
assert(/"hasSchemaDrivenSubagents":true/.test(codexAppDefaultCollab.text || ''), 'Codex App collaboration settings should use runtime-schema-driven sub-agent guidance');
|
||||
assert(/"hasLegacyV1Guidance":false/.test(codexAppDefaultCollab.text || ''), 'Codex App collaboration settings should omit legacy V1 fork/wait guidance');
|
||||
assert(/"reasoningEffort":"ultra"/.test(codexAppDefaultCollab.text || ''), 'Codex App collaboration settings should pass ultra reasoning_effort');
|
||||
assert(/"reasoningEffort":"max"/.test(codexAppDefaultCollab.text || ''), 'Codex App collaboration settings should pass max reasoning_effort');
|
||||
assert(/"hasTopLevelModel":false/.test(codexAppDefaultCollab.text || ''), 'Codex App collaboration turn should not duplicate model at top level');
|
||||
assert(/"hasTopLevelEffort":false/.test(codexAppDefaultCollab.text || ''), 'Codex App collaboration turn should not duplicate effort at top level');
|
||||
await nextMessage(messages, ws, (msg) => msg.type === 'done' && msg.sessionId === codexAppSession.sessionId);
|
||||
|
||||
@@ -797,7 +797,7 @@ const MCP_PROMPT_RESPONSE_MAX_CHARS = 20000;
|
||||
|
||||
// Codex 默认模型优先读取 ~/.codex/config.toml,缺失时再回退到旧默认值。
|
||||
const FALLBACK_CODEX_MODEL = 'gpt-5.4';
|
||||
const CODEX_REASONING_LEVELS = new Set(['low', 'medium', 'high', 'xhigh', 'ultra']);
|
||||
const CODEX_REASONING_LEVELS = new Set(['low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
|
||||
const CCWEB_TITLE_TOOL_INSTRUCTIONS = [
|
||||
'Use the ccweb title tool sparingly. For a new chat, call "mcp__ccweb__ccweb_set_title" or "ccweb_set_title" once after the user\'s initial request is clear, and set a concise task title.',
|
||||
'Do not rename the chat for routine progress, substeps, implementation details, or slightly better wording. Rename only when the user\'s primary objective changes substantially and the existing title would be misleading.',
|
||||
@@ -10347,7 +10347,7 @@ function handleCodexAppServerExit(signature, info = {}) {
|
||||
|
||||
function codexAppModelSettings(session) {
|
||||
const raw = String(session?.model || getDefaultCodexModel() || '').trim();
|
||||
const match = raw.match(/^(.*)\((low|medium|high|xhigh|ultra)\)\s*$/i);
|
||||
const match = raw.match(/^(.*)\((low|medium|high|xhigh|max|ultra)\)\s*$/i);
|
||||
if (!match) return { model: raw || null, effort: null };
|
||||
return {
|
||||
model: String(match[1] || '').trim() || null,
|
||||
|
||||
Reference in New Issue
Block a user