@keyou007/agent-bot 0.1.24-alpha.7 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +9 -5
  3. package/README.zh.md +9 -5
  4. package/dist/codex/CodexProcessManager.js +3 -1
  5. package/dist/codex/CodexProcessManager.js.map +1 -1
  6. package/dist/codex/CodexRuntime.js +165 -23
  7. package/dist/codex/CodexRuntime.js.map +1 -1
  8. package/dist/codex/CodexVersion.js +2 -2
  9. package/dist/codex/CodexVersion.js.map +1 -1
  10. package/dist/codex/ProviderSwitch.js +2 -2
  11. package/dist/codex/ProviderSwitch.js.map +1 -1
  12. package/dist/feishu/CardRenderer.js +10 -6
  13. package/dist/feishu/CardRenderer.js.map +1 -1
  14. package/dist/feishu/FeishuConnector.js +7 -3
  15. package/dist/feishu/FeishuConnector.js.map +1 -1
  16. package/dist/local-files/TurnPreviewPage.js +26 -10
  17. package/dist/local-files/TurnPreviewPage.js.map +1 -1
  18. package/dist/presentation/visibleTurnActivities.js +13 -0
  19. package/dist/presentation/visibleTurnActivities.js.map +1 -0
  20. package/dist/proxy/ProxySessionController.js +84 -17
  21. package/dist/proxy/ProxySessionController.js.map +1 -1
  22. package/dist/runtime/responseErrors.js +60 -0
  23. package/dist/runtime/responseErrors.js.map +1 -0
  24. package/docs/technical-reference.md +11 -1
  25. package/docs/technical-reference.zh.md +11 -1
  26. package/npm-shrinkwrap.json +2 -2
  27. package/package.json +1 -1
  28. package/skills/agent-bot/SKILL.md +5 -3
  29. package/src/codex/CodexProcessManager.ts +3 -1
  30. package/src/codex/CodexRuntime.ts +158 -22
  31. package/src/codex/CodexVersion.ts +2 -2
  32. package/src/codex/ProviderSwitch.ts +2 -2
  33. package/src/feishu/CardRenderer.ts +10 -5
  34. package/src/feishu/FeishuConnector.ts +7 -3
  35. package/src/local-files/TurnPreviewPage.ts +25 -10
  36. package/src/presentation/visibleTurnActivities.ts +12 -0
  37. package/src/proxy/ProxySessionController.ts +90 -20
  38. package/src/runtime/responseErrors.ts +62 -0
  39. package/src/runtime/types.ts +7 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,34 @@ The project follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.24] - 2026-10-24
10
+
11
+ ### 中文
12
+
13
+ - 修复 Turn Preview 只有思考标题、没有正文时仍显示空折叠箭头的问题;后续收到正文才启用展开,保留实时局部更新及飞书思考卡片行为。
14
+ - 修复飞书 SDK 展平事件 ID 后,新建目录按钮因固定名称被误去重而失效的问题;创建成功后原文件浏览卡片进入新目录,不改变任务工作目录。
15
+ - 允许无轮次、分支历史或 Goal 的空会话切换 Provider:验证目标设置后更换底层线程,保留本地任务和群绑定,失败保留原会话。
16
+ - Codex 遇到特定的 Responses 续接状态失效时,在原任务中自动尝试一次空输入续接,保留历史和已完成的工具结果,不重发 Prompt/图片;尊重取消、Reset、设置变化和任务接管。
17
+ - 修复思考卡片及 Preview 重复显示同一运行警告与最终错误,实时更新移除重复项;提取结构化错误的可读信息并保留原始诊断记录。
18
+
19
+ ### English
20
+
21
+ - Render title-only reasoning in Turn Preview without an empty disclosure or detail request; enable expansion when a body arrives, preserving live incremental updates and Feishu thinking cards.
22
+ - Fix folder creation being silently deduplicated by the button's fixed name after the Feishu SDK flattens event IDs; open the created directory in the same browser card without changing the task's working directory.
23
+ - Allow verified empty tasks without Turns, inherited history, or a Goal to switch Providers through a new remote thread, preserving local task/chat bindings and retaining the original on failure.
24
+ - Recover a specific Codex Responses continuation-state rejection once with empty input on the same task, preserving history and completed tool outputs without replaying the Prompt/images; honor cancellation, Reset, settings changes, and newer work.
25
+ - Deduplicate matching runtime warnings and final errors in thinking cards and Preview, including live updates; display readable structured error details while retaining original diagnostic records.
26
+
27
+ ## [0.1.24-alpha.8] - 2026-10-19
28
+
29
+ ### 中文
30
+
31
+ - 简化 Codex Provider 切换:要求 Codex >= 0.162.1,启动时仅为其进程设置零线程卸载延迟。切换等待目标线程实际卸载后恢复同一任务,保留完整历史及失败回退;不再重启共享 App Server,也不要求其他任务空闲。
32
+
33
+ ### English
34
+
35
+ - Simplify Codex Provider switching with Codex >= 0.162.1 and a process-local zero thread-unload delay. Wait for the selected thread to unload before resuming the same task, preserving complete history and verified rollback without restarting the shared App Server or waiting for sibling tasks.
36
+
9
37
  ## [0.1.24-alpha.7] - 2026-10-14
10
38
 
11
39
  ### 中文
package/README.md CHANGED
@@ -27,7 +27,7 @@ Agent Bot runs on your computer and connects a Feishu bot to your local coding a
27
27
 
28
28
  - Node.js 22 or later
29
29
  - At least one supported App Server Agent: Codex or TraeX
30
- - Codex 0.153.4 or later when using Codex (TraeX and ACP Agents have separate versions)
30
+ - Codex 0.162.1 or later when using Codex (TraeX and ACP Agents have separate versions)
31
31
  - A completed local login for the Agent you plan to use
32
32
 
33
33
  Check the installed Agents and their login status:
@@ -292,6 +292,8 @@ Only groups explicitly enabled with `/mute` or `/mute on` supplement ordinary @-
292
292
 
293
293
  `/new` and `/newgroup` use the conversation's default Agent unless `--agent <name>` selects another configured Agent for this task only; the source conversation's default is unchanged. For example, `/new Fix tests --agent codex` or `/newgroup Review --agent traex --dir ~/dev/project`. They inherit the current project; execution settings are inherited only when the selected Agent matches the current task's Agent, otherwise Provider, model, reasoning, and permission mode use the target Agent's saved defaults. Use `--dir` to choose another directory or `--nodir` to start without a project directory; `--nodir` requires the selected Agent to be App Server, and `~` represents your home directory.
294
294
 
295
+ `/dir` opens the file browser. `NewFolder` creates a child directory and opens it in the same card at page one, without changing the task's working directory. `Back` cancels creation and restores the previous directory and page.
296
+
295
297
  `/file` accepts relative paths, absolute paths, and paths beginning with `~`; relative paths resolve from the current task directory.
296
298
 
297
299
  `/fork` and `/forkgroup` branch from completed work without interrupting a running turn. `/sessions` manages tasks across projects and Providers in pages of up to 10 tasks; switching Provider does not hide a task from the list or search. Use each project menu for `New` and `NewGroup`. Each task’s `SwitchGroup` action creates a new group bound to that same task, preserving its history, title, and execution settings without creating or forking an Agent task. The original conversation stays unchanged. An Agent Bot Turn already in progress keeps replying to its original conversation; new messages from the new group queue until it finishes. Tasks running in an external client cannot be switched until they finish. Expanding a task directly shows the first 50 characters of its latest user Prompt, its update time, and task-specific actions. Running tasks also show a direct `Stop` button, including the current task and tasks running in other conversations or external clients. It sends the normal Agent interrupt without switching tasks or killing processes. The list refresh preserves search and pagination and keeps Stop available until the Agent reports the task idle; Archive is available only when idle. Use `Turns` to view that task’s history without switching tasks, including tasks not yet bound to Agent Bot; non-current tasks are read-only until you switch to them. `/turns` restores conversation context without reverting local files.
@@ -400,17 +402,19 @@ For App Server tasks, permission changes apply both approval and sandbox policie
400
402
 
401
403
  The execution settings card ends with a one-sentence Markdown blockquote for the selected tab's scope and timing. Agent selection changes only the default for new tasks in that conversation, without migrating the current task. Model, reasoning, and permission changes apply from the next turn. Agent and Provider options show their identifiers without duplicate display names, while retaining current/default indicators. `/provider` opens this card even when only one Provider is available, keeping the Model, Thinking, and Permission tabs accessible.
402
404
 
403
- Provider changes requested during an Agent Bot turn are saved as pending without interrupting it. The settings card immediately shows the target Provider and the model that will be used; the Model tab lists that Provider’s models, and later selections replace the pending choice. Thinking and permission changes made while a Provider switch is pending are staged with it. The switch is applied after the current turn ends, before a queued, retried, or new turn starts; idle tasks switch immediately. Pending settings survive restarts and do not overwrite active settings or Agent defaults until applied. If a deferred switch fails, Agent Bot restores the previous Provider and model. After verified rollback, the card returns to the original settings with an error notice, clears the pending choice, and queued/new turns continue using the original settings. Only failed or unverified rollback pauses further turns and keeps queued prompts for manual recovery. For a custom Provider, Agent Bot uses its OpenAI-compatible `/models` endpoint when available and shows only that Provider's models. A switch keeps the previous model when the target supports it, otherwise selects the target's default model or its first returned model. Providers without a model-list endpoint remain usable: Agent Bot keeps the current or configured model as the single fallback candidate and lets Codex apply it. Agent Bot releases its subscription only to the selected idle thread, resumes that same thread with the resolved Provider and model, and verifies the ID and both settings before saving or reporting success. Unsubscribing does not itself prove that App Server unloaded the thread. Settings changes always preserve the remote thread ID and all history, including failed and interrupted Turns. They never start, fork, truncate, or replace a thread. A pristine thread without a saved rollout is refused before detaching rather than silently replaced. If a switch fails, the previous settings are retained and remote recovery is attempted. If recovery also fails, further turns are blocked until a Provider switch succeeds.
405
+ Provider changes requested during an Agent Bot turn are saved as pending without interrupting it. The settings card immediately shows the target Provider and the model that will be used; the Model tab lists that Provider’s models, and later selections replace the pending choice. Thinking and permission changes made while a Provider switch is pending are staged with it. The switch is applied after the current turn ends, before a queued, retried, or new turn starts; idle tasks switch immediately. Pending settings survive restarts and do not overwrite active settings or Agent defaults until applied. If a deferred switch fails, Agent Bot restores the previous Provider and model. After verified rollback, the card returns to the original settings with an error notice, clears the pending choice, and queued/new turns continue using the original settings. Only failed or unverified rollback pauses further turns and keeps queued prompts for manual recovery. For a custom Provider, Agent Bot uses its OpenAI-compatible `/models` endpoint when available and shows only that Provider's models. A switch keeps the previous model when the target supports it, otherwise selects the target's default model or its first returned model. Providers without a model-list endpoint remain usable: Agent Bot keeps the current or configured model as the single fallback candidate and lets Codex apply it. Agent Bot verifies the task ID, Provider, and model before saving or reporting success. Tasks with history always preserve their remote thread ID and all history, including failed and interrupted Turns. Before the first message, a verified empty task without inherited history or a Goal may switch through a new remote thread, keeping the same local task, conversation, directory, and title. The old thread remains attached until the new Provider/model and persistence succeed; failure keeps the original task. No synthetic Prompt or history rewrite is used. If a switch fails, the previous settings are retained and remote recovery is attempted. If recovery also fails, further turns are blocked until a Provider switch succeeds.
404
406
 
405
407
  For TraeX's built-in `trae` Provider, Agent Bot uses the complete native `model/list` catalog, including model names and reasoning options, without requiring a custom Provider `base_url`. Other custom Providers keep their own model discovery and fallback behavior.
406
408
 
407
- If Provider model discovery fails, settings cards retain the fallback model but show the failure and warn that service availability is unconfirmed. A successful settings change verifies configuration, not a live inference request. App Server runtime errors and native retry notices show the message and any `additionalDetails` in the existing progress card and Preview, including upstream error codes and retry delays when supplied. Error text is displayed literally, duplicate details are omitted, and long reasons remain available in Preview and activity history. Missing details are not guessed, and previously saved snapshots are not backfilled. Retrying does not create another card or end the turn; the final turn notification still determines success, failure, or cancellation.
409
+ If Provider model discovery fails, settings cards retain the fallback model but show the failure and warn that service availability is unconfirmed. A successful settings change verifies configuration, not a live inference request. App Server runtime errors and native retry notices show the message and any `additionalDetails` in the existing progress card and Preview, including upstream error codes and retry delays when supplied. Error text is displayed literally, duplicate details are omitted, and long reasons remain available in Preview and activity history. Missing details are not guessed. Native retries do not create another card or end the turn; the final turn notification still determines success, failure, or cancellation. If the final failure repeats the preceding runtime warning, cards and Preview display it only once; the original execution record is retained.
410
+
411
+ For Codex's specific `unsupported_persisted_item_context` continuation failure, Agent Bot attempts one empty-input continuation in the same task, preserving history and completed tool results without resending your Prompt or images. This creates a new Turn and progress card, not a new task or Fork. If recovery fails, the error remains visible and you can send a message to continue. See the [technical reference](docs/technical-reference.md#responses-continuation-recovery) for recovery limits.
408
412
 
409
413
  Some Responses-compatible Providers omit assistant message phases. Their text stays in the progress timeline instead of being concatenated into a premature final answer. On successful completion, only the last unphased message not followed by a tool start becomes the final answer; explicit `commentary` and `final_answer` phases take precedence. Cards and Preview share this classification. Previously saved snapshots are not automatically rewritten.
410
414
 
411
415
  `feishu.groupNameFormat` defines separate name templates for new Project and Projectless groups, with variables for the operating system, Agent, project, task name, and date. See the [technical reference](docs/technical-reference.md#configuration-model) for the complete format.
412
416
 
413
- If App Server ignores the requested Provider/model, Agent Bot reports the requested and actual settings and verifies restoration on the same thread. It never forks to bypass an upstream limitation. Codex 0.153.4 can retain the old Provider after a failed model request even after unsubscribe; an explicit safe restart followed by retry can clear this in-memory state without changing history. Agent Bot does not restart a shared App Server as a side effect of switching settings. Settings pages can load saved task metadata without first resuming a broken old Provider; ordinary resume/reconnect verifies thread identity and known Provider before allowing a new Turn. Permanent authentication failures are not automatically retried by Agent Bot.
417
+ If App Server ignores the requested Provider/model, Agent Bot reports the requested and actual settings and verifies restoration on the same thread. It never forks to bypass an upstream limitation. Provider switching releases and resumes only the selected task, preserving existing history while other tasks keep running; only verified empty tasks may receive a new remote thread ID. It does not restart the shared App Server. If the selected task cannot unload, Agent Bot reports the failure and attempts to restore its previous settings. Settings pages can load saved task metadata without first resuming a broken old Provider; ordinary resume/reconnect verifies thread identity and known Provider before allowing a new Turn. Permanent authentication failures are not automatically retried by Agent Bot.
414
418
 
415
419
  Agent processes inherit ordinary parent-process variables and their explicit `agents.<name>.env` settings. Before starting an Agent, Agent Bot removes inherited `FEISHU_*` credentials and internal `AGENT_BOT_*` state, then provides only namespaced, non-secret Profile and Lark identity context. `FEISHU_APP_SECRET` is never forwarded to an Agent process.
416
420
 
@@ -424,7 +428,7 @@ Thinking cards use the grouped layout by default: auxiliary Commentary and user
424
428
 
425
429
  Whitespace-only Commentary does not use a pinned message slot. Adjacent omission markers generated by the card are shown once, without removing literal message text or changing saved history.
426
430
 
427
- Turn Preview shows each saved reasoning item in a collapsed panel. A leading Markdown heading or standalone bold title appears directly in the header; expanding shows the remaining summary and content without extra Summary/Content labels or a repeated title. Items without an explicit title keep the Reasoning fallback and all original text. Collapsed panels load their full Markdown on expansion and retain expansion during live updates. Reasoning content and authoritative completed items are stored separately from card activities; this does not change thinking-card titles, grouping, or final replies. Existing saved summaries remain viewable, but previously unrecorded content is not backfilled from Agent history.
431
+ Turn Preview shows saved reasoning with a body in a collapsed panel. A leading Markdown heading or standalone bold title appears directly in the header; expanding shows the remaining summary and content without extra Summary/Content labels or a repeated title. Title-only items remain visible as non-interactive rows without a disclosure arrow or detail request; live updates add a disclosure if a body arrives later. Items without an explicit title keep the Reasoning fallback and all original text. Collapsed panels load their full Markdown on expansion and retain expansion during live updates. Reasoning content and authoritative completed items are stored separately from card activities; this does not change thinking-card titles, grouping, or final replies. Existing saved summaries remain viewable, but previously unrecorded content is not backfilled from Agent history.
428
432
 
429
433
  Quoted cards and cards inside merged forwards include their available original text and images in the Agent's context.
430
434
 
package/README.zh.md CHANGED
@@ -26,7 +26,7 @@ Agent Bot 运行在你的电脑上,把飞书机器人连接到本机编程 Age
26
26
  ### 使用前提
27
27
 
28
28
  - Node.js 22 或更高版本
29
- - 使用 Codex 时要求版本不低于 `0.153.4`;此版本限制不适用于 TraeX 和 ACP Agent
29
+ - 使用 Codex 时要求版本不低于 `0.162.1`;此版本限制不适用于 TraeX 和 ACP Agent
30
30
  - 至少安装一个支持的 App Server Agent:Codex 或 TraeX
31
31
  - 已完成准备使用的 Agent 的本机登录
32
32
 
@@ -293,6 +293,8 @@ Fork 只记录来源任务和分支轮次,不同步完整历史。普通任务
293
293
 
294
294
  `/new` 和 `/newgroup` 默认使用当前会话设置的默认 Agent,也可用 `--agent <名称>` 为本次任务指定已配置的 Agent,不改变源会话的默认值。例如:`/new 修复测试 --agent codex`、`/newgroup 代码审查 --agent traex --dir ~/dev/project`。项目目录仍继承当前任务;选中的 Agent 与当前任务相同时继承运行设置,不同时使用目标 Agent 已保存的 Provider、模型、思考强度和权限默认值。使用 `--dir` 指定其他目录,或使用 `--nodir` 创建无项目目录的任务;`--nodir` 要求选中的 Agent 为 App Server,`~` 表示用户主目录。
295
295
 
296
+ `/dir` 打开文件浏览卡片。`NewFolder` 创建子目录后,原卡片直接进入新目录的第一页,不改变任务工作目录;`Back` 取消创建并回到原目录和页码。
297
+
296
298
  `/file` 支持相对路径、绝对路径和以 `~` 开头的用户目录路径;相对路径以当前任务目录为基准。
297
299
 
298
300
  `/fork` 和 `/forkgroup` 会从已完成的工作创建分支,不会中断正在执行的轮次。`/sessions` 用于跨项目、跨 Provider 管理任务,每页最多显示 10 个任务;切换 Provider 不会让任务从列表或搜索结果中消失。项目菜单提供 `New` 和 `NewGroup`。每个任务的 `SwitchGroup` 会创建新群并绑定同一个任务,保留历史、标题和运行设置,不创建新 Agent 任务,也不 fork,原会话绑定不变。若任务正在 Agent Bot 中执行,本轮回复仍留在原会话,新群发来的消息先排队;正在外部客户端执行的任务需要等待完成后再切换。展开任务后会直接显示最后一个用户 Prompt 的前 50 个字符、更新时间以及任务级操作。运行中任务还会显示独立的 `Stop` 按钮,包括当前任务、其他会话和外部客户端中的任务;点击发送正常的 Agent 中断信号,不切换任务、不结束进程。列表刷新保留搜索条件和页码,并根据 Agent 的实际状态保留或移除 Stop;空闲后才提供 Archive。点击 `Turns` 可查看对应任务的轮次,包括尚未绑定到 Agent Bot 的任务,不会切换当前任务;非当前任务仅供查看,需要先切换任务才能 Reset。`/turns` 用于恢复对话上下文,不会回退本地文件。
@@ -345,7 +347,7 @@ Agent Bot 将用户相关文件保存在仓库之外:
345
347
 
346
348
  `/status` 状态卡片底部也提供 **Preview**,与 Refresh、Stop/Switch 放在同一行。点击可查看正在执行的轮次,空闲时则查看最近一轮;刷新卡片后会更新目标。对于尚未被 Agent Bot 记录的外部任务,打开 Preview 时会从 Agent 按需读取所选轮次并缓存完整执行详情;读取失败可刷新页面重试。没有轮次、无法读取历史且无本地记录,或文件预览服务不可用时不显示按钮。查看其他任务的状态不会切换或接管任务。
347
349
 
348
- 思考卡片底部操作区提供 `Preview` 按钮,可在浏览器中查看本轮已保存的执行时间线,采用紧凑布局并适配手机,思考块直接以内容标题作为折叠标题,展开后显示内容。工具块只有一级折叠,默认只显示有意义的单行命令摘要、简短耗时(如 `58s` 或 `2:32`)和完成状态。展开后先显示以 `$` 开头的完整命令,随后直接显示结果、图片和修改文件。相同的输出和错误日志只显示一次。执行中的 Turn 会通过局部更新实时显示变化,保留手动展开状态、滚动位置和未变化的图片节点,避免图片反复加载;Token 等元数据更新不会替换时间线。折叠工具块和文件变更汇总首屏只加载摘要,展开时才请求命令、日志、图片和文件列表;再次展开复用已加载内容,运行中只刷新已展开且内容变化的详情,加载失败可点击重试。已结束的 Turn 不再建立 SSE 实时连接。“正在等待 Agent 返回进度”只在尚无可显示进度时出现,已有回答、其他可见进展或 Turn 已结束时不再显示。点击文件变更列表或工具文件列表中的文件,可在新标签页预览其当前内容;已删除或不可用的文件保留为路径文字。Turn Preview 与本地文件链接共用相同的签名、访问地址和网络范围。“最终回答”旁提供下载、复制两个图标,导出原始 Markdown(保留格式、代码块和原始链接),而非渲染后的网页内容。下载为 UTF-8 `.md` 文件;复制支持 HTTP/局域网页面的回退方式,并显示成功或失败提示。已结束页面及实时到达的最终回答均可使用。
350
+ 思考卡片底部操作区提供 `Preview` 按钮,可在浏览器中查看本轮已保存的执行时间线,采用紧凑布局并适配手机,思考块直接以内容标题作为折叠标题,展开后显示内容。只有标题而没有正文时保留普通标题行,不显示折叠箭头、不请求详情;实时更新收到正文后才变为可展开项。工具块只有一级折叠,默认只显示有意义的单行命令摘要、简短耗时(如 `58s` 或 `2:32`)和完成状态。展开后先显示以 `$` 开头的完整命令,随后直接显示结果、图片和修改文件。相同的输出和错误日志只显示一次。执行中的 Turn 会通过局部更新实时显示变化,保留手动展开状态、滚动位置和未变化的图片节点,避免图片反复加载;Token 等元数据更新不会替换时间线。折叠工具块和文件变更汇总首屏只加载摘要,展开时才请求命令、日志、图片和文件列表;再次展开复用已加载内容,运行中只刷新已展开且内容变化的详情,加载失败可点击重试。已结束的 Turn 不再建立 SSE 实时连接。“正在等待 Agent 返回进度”只在尚无可显示进度时出现,已有回答、其他可见进展或 Turn 已结束时不再显示。点击文件变更列表或工具文件列表中的文件,可在新标签页预览其当前内容;已删除或不可用的文件保留为路径文字。Turn Preview 与本地文件链接共用相同的签名、访问地址和网络范围。“最终回答”旁提供下载、复制两个图标,导出原始 Markdown(保留格式、代码块和原始链接),而非渲染后的网页内容。下载为 UTF-8 `.md` 文件;复制支持 HTTP/局域网页面的回退方式,并显示成功或失败提示。已结束页面及实时到达的最终回答均可使用。
349
351
 
350
352
  Turn Preview 完整保留已记录的执行内容,不再反复把整轮内容写入数据库。页面按增量实时更新,折叠工具展开时加载完整详情。备份时请保留整个 Profile 数据目录,以同时保存执行记录和任务信息;已有历史仍可查看。
351
353
 
@@ -391,17 +393,19 @@ App Server 任务切换权限时,下一轮会同时应用审批与沙箱策略
391
393
 
392
394
  运行设置卡片末尾以 Markdown 引用块显示当前选项页的一句重点提示,标明作用范围和生效时机。Agent 只改变本聊天或话题中新任务的默认 Agent,不迁移当前任务;模型、思考强度和权限从下一轮生效。Agent 和 Provider 选项只显示标识,去掉重复的展示名称,保留当前与默认状态标记。只有一个 Provider 时,`/provider` 仍会打开运行设置卡片,可以继续切换到 Model、Thinking 和 Permission 页。
393
395
 
394
- 任务正在 Agent Bot 中执行时,切换 Provider 会保存为待生效设置,不中断本轮。运行设置卡片立即显示目标 Provider 和将使用的模型,Model 页切换为该 Provider 的模型列表,可继续选择,以最后选择为准;此时修改思考强度和权限也一起暂存。本轮结束后、排队消息、重试或新轮次开始前实际应用,空闲任务则立即切换。待生效设置重启后保留,应用前不改动实际运行设置或 Agent 默认值。如果延迟切换失败,自动回退之前的 Provider 和模型;确认回退成功后,卡片恢复显示原设置并提示失败原因,清除待生效选择,排队消息和后续轮次继续使用原设置。只有回退也失败或无法确认时才暂停后续轮次、保留排队消息,等待重新选择恢复。对于自定义 Provider,Agent Bot 会在接口可用时读取其 OpenAI 兼容 `/models` 接口,并只展示该 Provider 返回的模型。切换时优先保留目标 Provider 也支持的原模型;否则选择目标 Provider 的默认模型,没有默认模型时选择返回列表中的第一个模型。未提供模型列表接口的 Provider 仍可使用:Agent Bot 会把当前或已配置模型作为唯一候选交给 Codex 应用。Agent Bot 只取消选中空闲任务的订阅,再使用解析出的 Provider 和模型恢复同一线程,校验 ID 和两项设置后才保存并提示成功;取消订阅本身不代表 App Server 已卸载线程。设置切换始终保留远端线程 ID 和全部历史,包括失败、中断的轮次;绝不新建、Fork、截断或替换线程。尚未保存 rollout 的全新空会话会在取消订阅前明确拒绝切换,不会偷偷替换。切换失败时保留原设置并尝试恢复远端;如果恢复也失败,将阻止后续执行,直到重新切换 Provider 成功。
396
+ 任务正在 Agent Bot 中执行时,切换 Provider 会保存为待生效设置,不中断本轮。运行设置卡片立即显示目标 Provider 和将使用的模型,Model 页切换为该 Provider 的模型列表,可继续选择,以最后选择为准;此时修改思考强度和权限也一起暂存。本轮结束后、排队消息、重试或新轮次开始前实际应用,空闲任务则立即切换。待生效设置重启后保留,应用前不改动实际运行设置或 Agent 默认值。如果延迟切换失败,自动回退之前的 Provider 和模型;确认回退成功后,卡片恢复显示原设置并提示失败原因,清除待生效选择,排队消息和后续轮次继续使用原设置。只有回退也失败或无法确认时才暂停后续轮次、保留排队消息,等待重新选择恢复。对于自定义 Provider,Agent Bot 会在接口可用时读取其 OpenAI 兼容 `/models` 接口,并只展示该 Provider 返回的模型。切换时优先保留目标 Provider 也支持的原模型;否则选择目标 Provider 的默认模型,没有默认模型时选择返回列表中的第一个模型。未提供模型列表接口的 Provider 仍可使用:Agent Bot 会把当前或已配置模型作为唯一候选交给 Codex 应用。Agent Bot 校验任务 ID、Provider 和模型后才保存并提示成功。已有历史的任务切换时保留远端线程 ID 和全部历史,包括失败、中断的轮次。首次发消息前,确认没有历史、分支来源或 Goal 的空会话可通过新建底层线程切换 Provider,本地任务、群聊/话题绑定、目录和标题保持不变;新 Provider/模型及持久化成功前保留原线程,失败仍使用原会话,不发送虚构 Prompt 或改写历史。切换失败时保留原设置并尝试恢复远端;如果恢复也失败,将阻止后续执行,直到重新切换 Provider 成功。
395
397
 
396
398
  `feishu.groupNameFormat` 可分别设置 Project 与 Projectless 新群的名称模板,支持系统、Agent、项目、任务名和日期变量。完整格式说明见[技术参考](docs/technical-reference.zh.md#配置模型)。
397
399
 
398
400
  TraeX 内置的 `trae` Provider 直接使用原生 `model/list` 返回的完整模型列表,保留模型名称和思考强度选项,不要求配置自定义 Provider 的 `base_url`。其他自定义 Provider 的模型发现和降级行为保持不变。
399
401
 
400
- Provider 模型列表读取失败时,设置卡片保留降级候选模型,同时显示失败原因并提示服务可用性未确认。切换成功只代表配置已通过校验,不代表实际模型请求可用。App Server 执行错误和原生重试通知会在同一张思考卡片和 Preview 中显示消息及 `additionalDetails`,包括 Agent 提供的上游错误码和重试等待时间。错误按纯文本展示,重复详情不重复显示,长原因可在 Preview 和思考活动历史中查看。缺少详情时不猜测原因,不自动回填此前保存的快照。重试不会新建卡片或提前结束任务,最终完成通知仍决定成功、失败或取消状态。
402
+ Provider 模型列表读取失败时,设置卡片保留降级候选模型,同时显示失败原因并提示服务可用性未确认。切换成功只代表配置已通过校验,不代表实际模型请求可用。App Server 执行错误和原生重试通知会在同一张思考卡片和 Preview 中显示消息及 `additionalDetails`,包括 Agent 提供的上游错误码和重试等待时间。错误按纯文本展示,重复详情不重复显示,长原因可在 Preview 和思考活动历史中查看。缺少详情时不猜测原因。原生重试不会新建卡片或提前结束任务,最终完成通知仍决定成功、失败或取消状态。最终错误与此前的运行警告相同时,卡片和 Preview 只显示一次,原始执行记录仍然保留。
403
+
404
+ Codex 出现特定的 `unsupported_persisted_item_context` 续接失败时,Agent Bot 会在同一任务中自动尝试一次空输入续接,保留历史和已完成的工具结果,不重发用户的 Prompt 或图片。它会新增一个轮次及思考卡片,不会新建任务或 Fork。恢复失败时仍显示错误,可以发送消息继续。恢复边界见[技术参考](docs/technical-reference.zh.md#responses-续接恢复)。
401
405
 
402
406
  部分 Responses 兼容 Provider 不返回助手消息阶段。这类文本先按顺序显示在进度时间线,不再提前拼接成最终回答;轮次成功结束时,只有最后一条且之后未开始工具调用的未标记消息会成为最终回答。明确的 `commentary` 和 `final_answer` 标记优先。思考卡片和 Preview 使用同一分类结果,不自动改写此前保存的快照。
403
407
 
404
- 如果 App Server 忽略指定的 Provider 或模型,Agent Bot 会报告请求值和实际值,并在同一线程上验证恢复原设置,绝不通过 Fork 绕过上游限制。实测 Codex 0.153.4 在模型请求失败后,取消订阅再恢复仍可能保留旧 Provider;显式安全重启后重试可以清除此内存状态,不需要改变历史。切换设置不会自动重启共享 App Server。设置入口可直接加载保存的任务元数据,无需先用故障的旧 Provider 恢复任务;普通恢复和断线重连会验证线程 ID 与已知 Provider,再允许发起新轮次。明确的认证失败不再触发 Agent Bot 自动重试。
408
+ 如果 App Server 忽略指定的 Provider 或模型,Agent Bot 会报告请求值和实际值,并在同一线程上验证恢复原设置,绝不通过 Fork 绕过上游限制。切换 Provider 只处理当前任务并保留已有历史,其他任务可以继续执行;只有确认无历史的空会话允许更换底层线程 ID;不会因此重启共享 App Server。若目标任务无法卸载,会提示失败并尝试恢复原设置。设置入口可直接加载保存的任务元数据,无需先用故障的旧 Provider 恢复任务;普通恢复和断线重连会验证线程 ID 与已知 Provider,再允许发起新轮次。明确的认证失败不再触发 Agent Bot 自动重试。
405
409
 
406
410
  Agent 进程会继承普通父进程变量及其显式配置的 `agents.<name>.env`。启动 Agent 前,Agent Bot 会移除继承的 `FEISHU_*` 凭据和内部 `AGENT_BOT_*` 状态,再仅提供带命名空间的非敏感 Profile 与 Lark 身份上下文;`FEISHU_APP_SECRET` 永远不会传入 Agent 进程。
407
411
 
@@ -56,7 +56,9 @@ export class CodexProcessManager {
56
56
  const agentEnvironment = { ...this.env };
57
57
  if (this.getAgentFamily() === "codex")
58
58
  agentEnvironment.CODEX_HOME = this.getCodexHome();
59
- const child = spawnStdioCommand(this.command, this.args, agentBotEnvironment({ ...this.codexEnvironment, ...process.env }, agentEnvironment, environmentContext), environmentContext.profilePath ?? os.homedir());
59
+ const args = this.getAgentFamily() === "codex"
60
+ ? [...this.args, "-c", "thread_unload_delay_secs=0"] : this.args;
61
+ const child = spawnStdioCommand(this.command, args, agentBotEnvironment({ ...this.codexEnvironment, ...process.env }, agentEnvironment, environmentContext), environmentContext.profilePath ?? os.homedir());
60
62
  this.child = child;
61
63
  const client = new AppServerConnection(child, this.logger.child({ component: "codex-app-server" }));
62
64
  this.client = client;
@@ -1 +1 @@
1
- {"version":3,"file":"CodexProcessManager.js","sourceRoot":"","sources":["../../src/codex/CodexProcessManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CodexProcessManager.js","sourceRoot":"","sources":["../../src/codex/CodexProcessManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EACL,mBAAmB,GAEpB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,OAAO,mBAAmB;IAWX,OAAO;IACP,IAAI;IACJ,GAAG;IACH,MAAM;IACN,kBAAkB;IAd7B,MAAM,CAAU,kBAAkB,GAAG,KAAK,CAAC;IAC3C,MAAM,CAAuB;IAC7B,KAAK,CAAkC;IACvC,OAAO,CAAU;IACjB,WAAW,CAAqB;IAChC,YAAY,CAA4B;IAC/B,gBAAgB,GAA2B,EAAE,CAAC;IAC9C,mBAAmB,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEzE,YACmB,OAAe,EACf,IAAc,EACd,GAA2B,EAC3B,MAAc,EACd,kBAAkB,GAAkC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;uBAJ9D,OAAO;oBACP,IAAI;mBACJ,GAAG;sBACH,MAAM;kCACN,kBAAkB;QAEnC,IAAI,IAAI,CAAC,cAAc,EAAE,KAAK,OAAO;YAAE,OAAO;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,MAAM,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7F,IAAI,MAAM,CAAC,KAAK,IAAK,MAAM,CAAC,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9E,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAG,MAAM,CAAC,KAA+B,CAAC,IAAI,EAAE,EACrE,mFAAmF,CACpF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC;QAChD,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC;QACpC,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,YAAY,GAAG,cAAc,CAAC;QACnC,IAAI,CAAC;YACH,OAAO,MAAM,cAAc,CAAC;QAC9B,CAAC;gBAAS,CAAC;YACT,IAAI,IAAI,CAAC,YAAY,KAAK,cAAc;gBAAE,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;QAC1E,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,WAAW;QACvB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACrD,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzC,IAAI,IAAI,CAAC,cAAc,EAAE,KAAK,OAAO;YAAE,gBAAgB,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACzF,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,KAAK,OAAO;YAC5C,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,4BAA4B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACnE,MAAM,KAAK,GAAG,iBAAiB,CAC7B,IAAI,CAAC,OAAO,EACZ,IAAI,EACJ,mBAAmB,CAAC,EAAE,GAAG,IAAI,CAAC,gBAAgB,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,kBAAkB,CAAC,EACvG,kBAAkB,CAAC,WAAW,IAAI,EAAE,CAAC,OAAO,EAAE,CAC/C,CAAC;QACF,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,MAAM,MAAM,GAAG,IAAI,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC;QACpG,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACzF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,oBAAoB,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,2BAA2B,CAAC,CAAC,CAAC;QAC1F,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,4BAA4B,CAAC,CAAC;YACjE,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM;gBAAE,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;YACpD,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK;gBAAE,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;YACjD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,2BAA2B,IAAI,IAAI,MAAM,YAAY,MAAM,IAAI,MAAM,IAAI,CAAC,CAAC;YACnG,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,mBAAmB;gBAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE;gBAC1D,UAAU,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE;gBACvE,YAAY,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;aACxC,CAAC,CAAC;YACH,IAAI,CAAC,OAAO,GAAG,uBAAuB,CAAC,gBAAgB,CAAC,CAAC;YACzD,MAAM,SAAS,GAAG,gBAAgB,IAAI,OAAO,gBAAgB,KAAK,QAAQ;gBACxE,CAAC,CAAE,gBAA4C,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;YACxE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAC5D,IAAI,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC;oBAAE,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;qBACnE,IAAI,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC;oBAAE,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;YAC/E,CAAC;YACD,IAAI,IAAI,CAAC,cAAc,EAAE,KAAK,OAAO;gBAAE,2BAA2B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACjF,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;YACjC,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM;gBAAE,IAAI,CAAC,KAAK,EAAE,CAAC;YACzC,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,cAAc;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC;QAC5B,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,GAAG;YACH,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,cAAc;QACZ,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC;QAC9C,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC,SAAS,CAAC,CAAC;QACrF,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC/B,MAAM,UAAU,GAAG,KAAK,EAAE,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACxE,IAAI,sCAAsC,CAAC,IAAI,CAAC,UAAU,CAAC;gBAAE,OAAO,OAAO,CAAC;YAC5E,IAAI,kDAAkD,CAAC,IAAI,CAAC,UAAU,CAAC;gBAAE,OAAO,OAAO,CAAC;QAC1F,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,YAAY;QACV,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;QAChH,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,sBAAsB,CAAC,IAAY;QACjC,IAAI,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,cAAc,EAAE,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC3F,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC5E,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;QACrB,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxD,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;YAAE,OAAO;QAEjE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,UAAsC,CAAC;YAC3C,MAAM,MAAM,GAAG,CAAC,KAAa,EAAQ,EAAE;gBACrC,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,UAAU;oBAAE,YAAY,CAAC,UAAU,CAAC,CAAC;gBACzC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAC1B,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC5B,IAAI,KAAK;oBAAE,MAAM,CAAC,KAAK,CAAC,CAAC;;oBACpB,OAAO,EAAE,CAAC;YACjB,CAAC,CAAC;YACF,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC,MAAM,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,CAAC,KAAY,EAAQ,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,MAAM,CAAC,IAAI,KAAK,CAAC,4CAA4C,KAAK,CAAC,GAAG,IAAI,SAAS,WAAW,CAAC,CAAC,CAAC;YACnG,CAAC,EAAE,mBAAmB,CAAC,kBAAkB,CAAC,CAAC;YAC3C,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7B,IAAI,CAAC;gBACH,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;gBAClB,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;wBAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;4BACnC,MAAM,CAAC,IAAI,KAAK,CAAC,qCAAqC,KAAK,CAAC,GAAG,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;wBACpF,CAAC;oBACH,CAAC,EAAE,KAAK,CAAC,CAAC;oBACV,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;gBACvB,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACpE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,YAAY,CAAC,QAAgC;QAC3C,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACvC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC;CACF;AAED,SAAS,uBAAuB,CAAC,KAAc;IAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC1D,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ;QAC3E,CAAC,CAAC,MAAM,CAAC,UAAqC;QAC9C,CAAC,CAAC,SAAS,CAAC;IACd,KAAK,MAAM,SAAS,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAChF,IAAI,OAAO,SAAS,KAAK,QAAQ;YAAE,SAAS;QAC5C,MAAM,KAAK,GAAG,+DAA+D,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9F,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -2,6 +2,7 @@ import { statSync } from "node:fs";
2
2
  import { setTimeout as delay } from "node:timers/promises";
3
3
  import { appendGeneratedImageMarkdown } from "../utils/generatedImageMarkdown.js";
4
4
  import { normalizeTaskTitle } from "../utils/taskTitle.js";
5
+ import { isResponseContinuationError } from "../runtime/responseErrors.js";
5
6
  import { AppServerRequestError } from "./AppServerConnection.js";
6
7
  import { workspaceSandboxFromConfig, workspaceSandboxFromResponse } from "./AppServerPermissions.js";
7
8
  import { formatCodexError, mapCodexNotification } from "./CodexEventMapper.js";
@@ -170,15 +171,49 @@ export class CodexRuntime {
170
171
  return session;
171
172
  }
172
173
  async startTurn(sessionId, prompt) {
173
- return this.runSessionOperation(sessionId, () => this.startTurnNow(sessionId, prompt));
174
+ return this.runSessionOperation(sessionId, () => {
175
+ this.requireSession(sessionId).responseFailureTurnId = undefined;
176
+ return this.startTurnNow(sessionId, prompt);
177
+ });
178
+ }
179
+ get recoverResponseContinuation() {
180
+ return this.provider.getAgentFamily?.() === "codex"
181
+ ? (sessionId, failedTurnId, signal) => this.recoverResponseContinuationNow(sessionId, failedTurnId, signal)
182
+ : undefined;
183
+ }
184
+ async recoverResponseContinuationNow(sessionId, failedTurnId, signal) {
185
+ return this.runSessionOperation(sessionId, async () => {
186
+ signal?.throwIfAborted();
187
+ const session = this.requireSession(sessionId);
188
+ if (session.responseFailureTurnId !== failedTurnId || session.activeTurnId || session.needsResume) {
189
+ throw new Error("模型续接恢复已失效,请发送消息继续。");
190
+ }
191
+ // Consume before starting: a timeout must not create a second continuation.
192
+ session.responseFailureTurnId = undefined;
193
+ const client = await this.client();
194
+ const metadata = await client.request("thread/read", {
195
+ threadId: session.remoteSessionId, includeTurns: false,
196
+ }, SYNC_REQUEST_TIMEOUT_MS);
197
+ const latest = await this.readLatestThreadTurns(client, session.remoteSessionId, "summary");
198
+ signal?.throwIfAborted();
199
+ if (session.activeTurnId || session.needsResume || metadata.thread.id !== session.remoteSessionId || metadata.thread.status?.type === "active"
200
+ || latest.at(-1)?.id !== failedTurnId || latest.at(-1)?.status !== "failed") {
201
+ throw new Error("任务已发生变化,未自动恢复模型续接。请检查当前任务后继续。");
202
+ }
203
+ const turnId = await this.startTurnNow(sessionId, "", true);
204
+ if (signal?.aborted && session.activeTurnId === turnId)
205
+ await this.cancelTurn(sessionId, turnId);
206
+ signal?.throwIfAborted();
207
+ return turnId;
208
+ });
174
209
  }
175
- async startTurnNow(sessionId, prompt) {
210
+ async startTurnNow(sessionId, prompt, responseContinuation = false) {
176
211
  const session = this.requireSession(sessionId);
177
212
  const client = await this.client();
178
213
  await this.ensureSessionResumed(session, client);
179
214
  const start = async () => client.request("turn/start", {
180
215
  threadId: session.remoteSessionId,
181
- input: codexUserInput(prompt),
216
+ input: responseContinuation ? [] : codexUserInput(prompt),
182
217
  cwd: session.cwd,
183
218
  model: session.model,
184
219
  effort: session.reasoningEffort,
@@ -197,7 +232,7 @@ export class CodexRuntime {
197
232
  response = await start();
198
233
  }
199
234
  catch (error) {
200
- if (!isInvalidWorkingDirectoryError(error))
235
+ if (responseContinuation || !isInvalidWorkingDirectoryError(error))
201
236
  throw error;
202
237
  this.logger.warn({ error, sessionId: session.localSessionId, cwd: session.cwd }, "App Server rejected the task working directory; refreshing the current thread before retrying.");
203
238
  await this.resumeAppServerSession(session, client);
@@ -617,17 +652,30 @@ export class CodexRuntime {
617
652
  return response.cleared;
618
653
  }
619
654
  async setModel(sessionId, model) {
620
- await this.runSessionOperation(sessionId, async () => { this.requireSession(sessionId).model = model; });
655
+ await this.runSessionOperation(sessionId, async () => {
656
+ const session = this.requireSession(sessionId);
657
+ session.model = model;
658
+ session.responseFailureTurnId = undefined;
659
+ });
621
660
  }
622
661
  async setReasoningEffort(sessionId, effort) {
623
- await this.runSessionOperation(sessionId, async () => { this.requireSession(sessionId).reasoningEffort = effort; });
662
+ await this.runSessionOperation(sessionId, async () => {
663
+ const session = this.requireSession(sessionId);
664
+ session.reasoningEffort = effort;
665
+ session.responseFailureTurnId = undefined;
666
+ });
624
667
  }
625
668
  async setPermissionMode(sessionId, mode) {
626
- await this.runSessionOperation(sessionId, async () => { this.requireSession(sessionId).permissionMode = mode; });
669
+ await this.runSessionOperation(sessionId, async () => {
670
+ const session = this.requireSession(sessionId);
671
+ session.permissionMode = mode;
672
+ session.responseFailureTurnId = undefined;
673
+ });
627
674
  }
628
675
  async setExecutionSettings(sessionId, settings, persist) {
629
676
  return this.runSessionOperation(sessionId, async () => {
630
677
  try {
678
+ this.requireSession(sessionId).responseFailureTurnId = undefined;
631
679
  return await this.setExecutionSettingsNow(sessionId, settings, persist);
632
680
  }
633
681
  catch (error) {
@@ -643,37 +691,31 @@ export class CodexRuntime {
643
691
  const session = this.requireSession(sessionId);
644
692
  if (session.activeTurnId)
645
693
  throw new Error(PROVIDER_SWITCH_BUSY);
646
- const client = await this.client();
694
+ let client = await this.client();
647
695
  const metadata = await client.request("thread/read", {
648
696
  threadId: session.remoteSessionId, includeTurns: false,
649
697
  }, SYNC_REQUEST_TIMEOUT_MS);
698
+ if (metadata.thread.id !== session.remoteSessionId)
699
+ throw new Error("App Server 返回了不同的任务 ID,未切换 Provider。");
650
700
  const localActive = await this.localActivityDetector?.activeThreads([session.remoteSessionId]);
651
701
  if (metadata.thread.status?.type === "active" || localActive?.has(session.remoteSessionId) || session.activeTurnId) {
652
702
  throw new Error(PROVIDER_SWITCH_BUSY);
653
703
  }
654
- if (session.turnCount === 0 && !metadata.thread.forkedFromId && !metadata.thread.preview?.trim()) {
655
- try {
656
- const turns = await this.readLatestThreadTurns(client, session.remoteSessionId, "summary");
657
- if (turns.length === 0)
658
- throw new Error(PROVIDER_SWITCH_EMPTY);
659
- }
660
- catch (error) {
661
- if (!(error instanceof AppServerRequestError) || error.method !== "thread/turns/list"
662
- || !/missing source rollout|no rollout found/iu.test(error.serverMessage))
663
- throw error;
664
- throw new Error(PROVIDER_SWITCH_EMPTY, { cause: error });
665
- }
704
+ if (await this.isEmptySettingsSession(client, session, metadata.thread)) {
705
+ return this.replaceEmptySettingsSession(client, session, settings, persist);
666
706
  }
667
707
  const previous = { ...session };
668
708
  const restoreProvider = previous.modelProvider?.trim()
669
709
  || (metadata.thread.id === previous.remoteSessionId ? metadata.thread.modelProvider?.trim() : undefined);
670
710
  let detached = false;
711
+ let resumeRequested = false;
671
712
  try {
672
713
  const params = {
673
714
  cwd: session.cwd, modelProvider: settings.modelProvider, model: settings.model,
674
715
  ...threadLifecycleParams(session.cwd), ...permissionParams(settings.permissionMode),
675
716
  };
676
717
  await this.detachThreadForSettings(client, session, () => { detached = true; });
718
+ resumeRequested = true;
677
719
  const response = await client.request("thread/resume", {
678
720
  ...params, threadId: session.remoteSessionId, excludeTurns: true,
679
721
  }, SESSION_REQUEST_TIMEOUT_MS);
@@ -696,7 +738,9 @@ export class CodexRuntime {
696
738
  try {
697
739
  if (!restoreProvider)
698
740
  throw new Error("无法确认切换前的 Provider,不能验证原设置已恢复。");
699
- await this.detachThreadForSettings(client, previous);
741
+ client = await this.client();
742
+ if (resumeRequested)
743
+ await this.detachThreadForSettings(client, previous);
700
744
  const restored = await client.request("thread/resume", {
701
745
  threadId: previous.remoteSessionId, excludeTurns: true, cwd: previous.cwd,
702
746
  modelProvider: restoreProvider, model: previous.model,
@@ -722,6 +766,78 @@ export class CodexRuntime {
722
766
  }
723
767
  return session;
724
768
  }
769
+ async isEmptySettingsSession(client, session, thread) {
770
+ if (thread.id !== session.remoteSessionId || thread.preview !== "" || thread.forkedFromId
771
+ || (session.turnCount === undefined && thread.forkedFromId !== null)
772
+ || session.activeTurnId || (session.turnCount ?? 0) > 0 || session.knownTurnIds.size > 0
773
+ || session.terminalTurnIds.size > 0 || (thread.turns?.length ?? 0) > 0)
774
+ return false;
775
+ try {
776
+ if ((await this.readLatestThreadTurns(client, session.remoteSessionId, "summary")).length > 0)
777
+ return false;
778
+ }
779
+ catch (error) {
780
+ if (!(error instanceof AppServerRequestError) || error.method !== "thread/turns/list"
781
+ || !/missing source rollout|no rollout found|not materialized yet/iu.test(error.serverMessage))
782
+ throw error;
783
+ }
784
+ if (!["idle", "notLoaded"].includes(thread.status?.type ?? ""))
785
+ throw new Error(PROVIDER_SWITCH_EMPTY);
786
+ const { goal } = await client.request("thread/goal/get", {
787
+ threadId: session.remoteSessionId,
788
+ }, CONTROL_REQUEST_TIMEOUT_MS);
789
+ if (goal !== null)
790
+ throw new Error(PROVIDER_SWITCH_EMPTY);
791
+ return true;
792
+ }
793
+ async replaceEmptySettingsSession(client, session, settings, persist) {
794
+ const oldId = session.remoteSessionId;
795
+ let replacementId;
796
+ try {
797
+ // Keep the original attachment until target verification and persistence both succeed.
798
+ const response = await client.request("thread/start", {
799
+ cwd: session.cwd, modelProvider: settings.modelProvider, model: settings.model, threadSource: "user",
800
+ ...threadLifecycleParams(session.cwd), ...permissionParams(settings.permissionMode),
801
+ }, SESSION_REQUEST_TIMEOUT_MS);
802
+ if (!response.thread.id || response.thread.id === oldId)
803
+ throw new Error("App Server 未创建独立的空任务,未保存设置。");
804
+ replacementId = response.thread.id;
805
+ assertProviderSettingsApplied(response, settings);
806
+ if (session.title)
807
+ await client.request("thread/name/set", { threadId: replacementId, name: session.title }, CONTROL_REQUEST_TIMEOUT_MS);
808
+ const { thread } = await client.request("thread/read", { threadId: oldId, includeTurns: false }, SYNC_REQUEST_TIMEOUT_MS);
809
+ const active = await this.localActivityDetector?.activeThreads([oldId]);
810
+ if (active?.has(oldId) || !await this.isEmptySettingsSession(client, session, thread))
811
+ throw new Error(PROVIDER_SWITCH_BUSY);
812
+ if (this.attachedClient !== client)
813
+ throw new Error("App Server 连接已变化,请重试 Provider 切换。");
814
+ const candidate = this.makeSession({
815
+ localSessionId: session.localSessionId, agentName: session.agentName, cwd: session.cwd, title: session.title, ...settings,
816
+ }, response, settings.reasoningEffort);
817
+ candidate.title = session.title;
818
+ await persist?.(candidate, { emptyRemoteSessionId: oldId });
819
+ Object.assign(session, candidate, {
820
+ needsResume: this.attachedClient !== client, settingsRecoveryError: undefined, remoteSummary: undefined,
821
+ });
822
+ }
823
+ catch (error) {
824
+ if (replacementId)
825
+ await this.releaseUnusedSettingsThread(client, replacementId);
826
+ throw new ProviderSwitchError(`Provider 切换失败:${providerSwitchFailure(error)} 原会话和设置已保留。`, !session.needsResume && !session.settingsRecoveryError, { cause: error });
827
+ }
828
+ if (![...this.sessions.values()].some(other => other.remoteSessionId === oldId)) {
829
+ await this.releaseUnusedSettingsThread(client, oldId);
830
+ }
831
+ return session;
832
+ }
833
+ async releaseUnusedSettingsThread(client, threadId) {
834
+ try {
835
+ await client.request("thread/unsubscribe", { threadId }, CONTROL_REQUEST_TIMEOUT_MS);
836
+ }
837
+ catch (error) {
838
+ this.logger.warn({ error, threadId }, "Could not release an unused empty settings thread.");
839
+ }
840
+ }
725
841
  async detachThreadForSettings(client, session, onDetach) {
726
842
  const current = await client.request("thread/read", {
727
843
  threadId: session.remoteSessionId, includeTurns: false,
@@ -736,6 +852,24 @@ export class CodexRuntime {
736
852
  if (!["unsubscribed", "notLoaded", "notSubscribed"].includes(result.status)) {
737
853
  throw new Error(`App Server 返回了未知的取消订阅状态:${result.status}`);
738
854
  }
855
+ if (this.provider.getAgentFamily?.() === "codex") {
856
+ const deadline = Date.now() + CONTROL_REQUEST_TIMEOUT_MS;
857
+ while (Date.now() < deadline) {
858
+ if (this.attachedClient !== client)
859
+ throw new Error("App Server 连接已变化,请重试 Provider 切换。");
860
+ const { thread } = await client.request("thread/read", {
861
+ threadId: session.remoteSessionId, includeTurns: false,
862
+ }, Math.min(SYNC_REQUEST_TIMEOUT_MS, Math.max(1, deadline - Date.now())));
863
+ if (thread.id !== session.remoteSessionId)
864
+ throw new Error("App Server 返回了不同的任务 ID,未切换 Provider。");
865
+ if (thread.status?.type === "notLoaded")
866
+ return;
867
+ if (thread.status?.type === "active" || session.activeTurnId)
868
+ throw new Error(PROVIDER_SWITCH_BUSY);
869
+ await delay(100);
870
+ }
871
+ throw new Error("等待任务卸载超时;任务可能仍有订阅或正在关闭,请稍后重试 Provider 切换。");
872
+ }
739
873
  }
740
874
  async respondToApproval(sessionId, requestId, decision) {
741
875
  return this.resolveApproval(sessionId, requestId, decision);
@@ -1214,6 +1348,9 @@ export class CodexRuntime {
1214
1348
  session.activeTurnStartedAt = undefined;
1215
1349
  session.terminalTurnIds.add(mapped.turnId);
1216
1350
  session.messagePhases.clear();
1351
+ session.responseFailureTurnId = mapped.status === "failed" && mapped.error
1352
+ && this.provider.getAgentFamily?.() === "codex" && isResponseContinuationError(mapped.error)
1353
+ ? mapped.turnId : undefined;
1217
1354
  if (mapped.status === "cancelled") {
1218
1355
  this.emit({ type: "turn_cancelled", sessionId, turnId: mapped.turnId });
1219
1356
  }
@@ -1384,6 +1521,7 @@ export class CodexRuntime {
1384
1521
  session.turnCount += 1;
1385
1522
  }
1386
1523
  session.activeTurnId = turnId;
1524
+ session.responseFailureTurnId = undefined;
1387
1525
  session.activeTurnStartedAt = startedAt;
1388
1526
  session.planContinuationTurnId = undefined;
1389
1527
  session.modeChangeRequestIds.clear();
@@ -1418,6 +1556,9 @@ export class CodexRuntime {
1418
1556
  session.activeTurnStartedAt = undefined;
1419
1557
  session.terminalTurnIds.add(turn.id);
1420
1558
  session.messagePhases.clear();
1559
+ const failure = formatCodexError(turn.error);
1560
+ session.responseFailureTurnId = turn.status === "failed" && failure
1561
+ && this.provider.getAgentFamily?.() === "codex" && isResponseContinuationError(failure) ? turn.id : undefined;
1421
1562
  if (turn.status === "interrupted") {
1422
1563
  this.emit({ type: "turn_cancelled", sessionId: session.localSessionId, turnId: turn.id });
1423
1564
  return;
@@ -1427,7 +1568,7 @@ export class CodexRuntime {
1427
1568
  type: "turn_failed",
1428
1569
  sessionId: session.localSessionId,
1429
1570
  turnId: turn.id,
1430
- message: formatCodexError(turn.error) ?? "App Server turn failed.",
1571
+ message: failure ?? "App Server turn failed.",
1431
1572
  });
1432
1573
  return;
1433
1574
  }
@@ -1477,7 +1618,7 @@ export class CodexRuntime {
1477
1618
  workspaceSandboxPolicy: workspaceSandboxFromResponse(response.sandbox),
1478
1619
  rolloutPath: stringValue(response.thread.path)?.trim() || undefined,
1479
1620
  turnCount: "remoteSessionId" in input ? undefined : 0,
1480
- knownTurnIds: new Set(),
1621
+ knownTurnIds: new Set("lastTurnId" in input && input.lastTurnId ? [input.lastTurnId] : []),
1481
1622
  };
1482
1623
  }
1483
1624
  requireSession(sessionId) {
@@ -1550,6 +1691,7 @@ export class CodexRuntime {
1550
1691
  for (const session of this.sessions.values()) {
1551
1692
  session.needsResume = true;
1552
1693
  session.planContinuationTurnId = undefined;
1694
+ session.responseFailureTurnId = undefined;
1553
1695
  const turnId = session.activeTurnId;
1554
1696
  if (!turnId)
1555
1697
  continue;