Skip to content

feat(Phase4):提升终端诊断与 IDM 模式可用性#565

Merged
phantom5099 merged 7 commits into1024XEngineer:mainfrom
pionxe:feature/diag-phase4-altscreen-guard
May 6, 2026
Merged

feat(Phase4):提升终端诊断与 IDM 模式可用性#565
phantom5099 merged 7 commits into1024XEngineer:mainfrom
pionxe:feature/diag-phase4-altscreen-guard

Conversation

@pionxe
Copy link
Copy Markdown
Collaborator

@pionxe pionxe commented May 6, 2026

Summary

本 PR 落地 Phase4 终端诊断可用性改进,重点解决三个问题:

  • 自动诊断在全屏终端程序中误触发。
  • IDM 模式下 @ai 失败后可能卡死、等待提示不及时。
  • 手动/自动诊断首响慢、重复诊断消耗高、用户体感较差。

本阶段保持主链路不变:诊断仍通过 gateway.executeSystemTool(diagnose),IDM 仍通过 Gateway 调 Runtime,不新增公共 Gateway RPC schema,也不修改现有诊断超时预算常量。

Closes #532

Key Changes

1. Phase4 回滚开关

新增 Phase4 相关环境变量开关,方便线上快速回退:

  • NEOCODE_DIAG_ALTSCREEN_GUARD_DISABLED=1
  • NEOCODE_IDM_PLAN_MODE_DISABLED=1
  • NEOCODE_DIAG_FAST_RESPONSE_DISABLED=1
  • NEOCODE_DIAG_CACHE_DISABLED=1

2. 全屏保护

新增备用屏幕状态追踪,识别终端进入/退出全屏程序的控制序列:

  • 支持 CSI ? 1049 h/l
  • 支持 CSI ? 47 h/l
  • 支持 CSI ? 1047 h/l
  • 支持分片输入
  • 支持 tmux passthrough 场景

自动诊断触发前会检查当前是否处于全屏状态:

  • 全屏期间不派发 auto 诊断。
  • 刚退出全屏后吞掉一次 pending auto,避免退出瞬间误诊断。
  • 之后恢复正常自动诊断。

3. IDM 防卡死与 plan 模式接入

IDM @ai 请求接入 Runtime plan 模式:

  • 默认在 gateway.run 中传入 Mode: "plan"
  • 可通过 NEOCODE_IDM_PLAN_MODE_DISABLED=1 回退。
  • 保持 plan 模式原有能力,不破坏 todo_write 可见性。

修复 IDM 卡死问题:

  • 校验 bind_stream ACK / ERROR / 非预期帧。
  • 校验 gateway.run ACK / ERROR / 非预期帧。
  • 失败时立即恢复 IDM 状态,不再继续等待不存在的流式事件。
  • agent_done 无 chunk 时,从 done payload 中兜底提取文本展示。
  • permission_requested 继续自动 reject,并保持状态清理。
  • Ctrl+C cancel 行为不回归。

4. IDM 流式显示

此前 IDM 虽然收到 gateway 的 agent_chunk,但会先缓存,直到 agent_done 才统一 Markdown 渲染显示。

本 PR 改为:

  • 收到 agent_chunk 立即输出到终端。
  • 输出时做终端换行规范化,并保持 IDM AI 输出颜色。
  • 如果已经流式输出过,agent_done 不再重复渲染完整答案。
  • 如果没有收到任何 chunk,继续使用完整 Markdown 渲染和 done payload fallback。

这能显著降低 IDM @ai 的等待感。

5. 诊断响应提速

新增 diagnosisCoordinator,用于诊断请求调度与复用:

  • 使用脱敏后的命令、退出码、错误日志生成 fingerprint。
  • 相同 fingerprint 的 in-flight 诊断会合并,不重复发起。
  • 成功诊断结果进入 5 分钟短 TTL 缓存。
  • 缓存最多保留 64 条。
  • 失败或 transport error 不写入缓存。
  • auto 相同 fingerprint 短窗去抖。
  • manual 诊断优先,auto 诊断限制并发,避免阻塞手动诊断。

新增快速首响:

  • manual 诊断立即输出“正在诊断”。
  • auto 诊断仅在命中明确模式时输出低干扰快速预判。
  • 快速预判置信度上限固定为 0.55。
  • 最终模型诊断结果仍然作为权威结果展示。

Test Plan

已通过本地验证:

go test -count=1 ./internal/ptyproxy ./internal/tools ./internal/runtime

覆盖重点:

  • 全屏期间 auto 诊断不触发。
  • 退出全屏后只吞掉一次 pending auto。
  • alt-screen 回滚开关有效。
  • IDM bind_stream / gateway.run 异常帧不会卡死。
  • IDM agent_done payload fallback 可展示。
  • IDM chunk 在 agent_done 前即可流式显示。
  • IDM plan 模式 env 开关有效。
  • plan/read-only 下 todo_write 保持可见。
  • 诊断 fingerprint、in-flight 合并、TTL 缓存、auto 去抖、快速首响行为正确。

Risk & Rollback

风险主要集中在终端输出体验和诊断调度策略:

  • 不同终端/复用器对备用屏幕序列支持可能存在差异。
  • IDM 流式输出当前采用纯文本流式显示,最终不再重复 Markdown 渲染,避免重复输出。
  • 诊断缓存只保存成功结果,避免错误结果污染后续诊断。

可通过以下环境变量逐项回滚:

NEOCODE_DIAG_ALTSCREEN_GUARD_DISABLED=1
NEOCODE_IDM_PLAN_MODE_DISABLED=1
NEOCODE_DIAG_FAST_RESPONSE_DISABLED=1
NEOCODE_DIAG_CACHE_DISABLED=1

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Copy link
Copy Markdown

@fennoai fennoai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found 3 issues worth addressing before merge.

for _, spec := range specs {
gotNames[spec.Name] = true
}
if len(specs) != 2 || !gotNames[ToolNameFilesystemReadFile] || !gotNames[ToolNameTodoWrite] {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium This assertion only checks that todo_write is listed in read-only/plan mode. The execution path still maps todo_write to a write action and DefaultManager.Execute rejects all non-read actions when input.ReadOnly is true, so the model can see the tool but still cannot call it. This PR claims to restore plan-mode todo support, but with the current coverage it only restores visibility, not usability.

s.leftover = nil

for index := 0; index < len(buffer); {
if hasPrefixAt(buffer, index, tmuxDCSOpen) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium Observe only keeps leftovers after it has already matched a full tmuxDCSOpen / ESC[ prefix. If a PTY read ends with a bare ESC or a partial \x1bPtmux;, those bytes are dropped here and the next chunk can no longer be recognized as an alt-screen transition. PTY reads can split anywhere, so chunked tmux/CSI prefixes will still leak false auto-diagnosis triggers despite the new guard.

Comment thread internal/ptyproxy/idm_controller.go Outdated
}

streamCtx, streamCancel := context.WithCancel(context.Background())
streamCtx, streamCancel := context.WithTimeout(context.Background(), diagnoseCallTimeout)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium Changing the stream context from WithCancel to WithTimeout(diagnoseCallTimeout) puts a hard 90s cap on every @ai run. After that deadline, waitRunStream returns and we cancel the run even if the model is still streaming normally. The bind/run RPCs already have their own 90s call timeouts, so this regresses any legitimately slow or long answer into a deterministic timeout rather than just fixing the stuck-ACK case.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

@phantom5099 phantom5099 merged commit 0f3f9eb into 1024XEngineer:main May 6, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 4: 全屏应用(TUI)兼容性与安全

2 participants