Skip to content

feat: 终端诊断 Phase 2 + 供应商与模型管理 — PTY 代理架构升级与自动诊断闭环#545

Merged
pionxe merged 27 commits into1024XEngineer:mainfrom
pionxe:main
May 4, 2026
Merged

feat: 终端诊断 Phase 2 + 供应商与模型管理 — PTY 代理架构升级与自动诊断闭环#545
pionxe merged 27 commits into1024XEngineer:mainfrom
pionxe:main

Conversation

@pionxe
Copy link
Copy Markdown
Collaborator

@pionxe pionxe commented May 2, 2026

概述

本 PR 基于 #512#525,继续推进终端诊断链路,并把 Provider / Model 选择能力补齐到可直接落盘、可切换、可回退的状态管理闭环。

相对 origin/main...HEAD,当前实际变更为 44 files / +7480 / -1010。变更不只包含“终端诊断 Phase 2 + Provider / Model 管理”,还额外带入了:

  • provider rm 删除能力
  • CLI 侧统一的 selection service resolver / 缓存层
  • provider / model 选择路径上的并发锁与 snapshot fallback 语义调整
  • diag auto statusdiag diagnose、socket 自动发现等诊断命令面扩展
  • PTY 代理的超时、banner、gateway RPC 连接复用等运行时行为升级

Close #525


一、这个 PR 解决了什么问题

1.1 Phase 1 终端诊断仍然不够“可持续使用”

Phase 1 只能手动触发诊断,存在几个明显问题:

  • shell 代理异常退出时,宿主终端状态可能残留,影响后续命令输入
  • Ctrl+C / SIGTSTP / SIGCONT / SIGWINCH 等信号在 PTY 转发链路上不完整,容易出现中断吞噬、分页器排版异常或僵尸子进程
  • 代理不知道“什么时候用户真正开始执行命令、什么时候命令结束”,因此 Auto 模式无法稳定落地
  • 原始报错日志未经清洗直接喂给诊断工具,噪声大、凭证泄漏风险高、长输出也不利于模型分析
  • 旧版简单文本 IPC 协议扩展性弱,无法承载后续多种诊断指令和状态查询

1.2 Provider / Model 选择还停留在“配置存在”,缺少完整运维闭环

在本 PR 之前,Provider / Model 相关能力还不完整:

  • 缺少从 CLI 直接新增 / 删除自定义 provider 的入口
  • 缺少当前 provider 下模型列表查看与显式切换命令
  • provider 切换和 model 切换分离,容易出现两次写配置之间被并发插入、导致状态漂移
  • 自定义 provider 远程模型发现失败时,缺少快照兜底,容易让切换流程直接失败
  • 删除 provider 后,当前选择、用户环境变量和本地状态的一致性修复不够完整

1.3 需要把“诊断工具”从 demo 变成真实闭环

手动 / 自动诊断的最终目标,不是打印占位文案,而是把终端错误日志真正接入 NeoCode 的工具体系,通过 gateway -> system tool -> sub-agent 完成根因分析,并在失败时具备可预期的降级路径。


二、核心设计思路

2.1 终端诊断仍然沿主链路收敛,不在 TUI / runtime 侧散落逻辑

本 PR 延续仓库既有边界:

用户输入(TUI/CLI) -> Gateway -> Runtime / Tool Manager -> diagnose tool -> 结果回传

其中:

  • PTY 代理负责采集 shell 行为、维护交互体验、透传信号、触发诊断请求
  • internal/tools/diagnose 负责真正的诊断执行与降级策略
  • CLI 只负责命令入口与状态切换,不直接承载分析逻辑

这样做的原因是把“终端交互细节”和“模型可调用能力”继续隔离,避免把诊断逻辑塞回 runtime 或 UI。

2.2 用 Shell Integration + OSC 133 建立“可观测的命令生命周期”

Auto 诊断能否可靠,关键不在关键词匹配,而在于能否知道:

  • Prompt 何时 ready
  • 命令何时开始执行
  • 命令何时结束以及退出码是多少

因此本 PR 引入 shell integration 注入脚本,并统一发射 / 解析 OSC 133;A/C/D 事件,把命令生命周期结构化成 prompt_ready / command_start / command_done。这样 Auto 模式不再依赖脆弱的文本猜测,而是建立在 shell 事件流上。

2.3 用“探针 + 动态降级 / 升级”兼容真实终端环境

不同 shell、tmux、SSH、终端模拟器对 OSC 的实现并不稳定,所以这里没有把 Auto 模式当成硬依赖,而是采用:

  • 启动后 1.5s 主动探针
  • 未收到 prompt_ready 时自动降级为 Manual
  • 后续一旦捕获到完整事件序列,再动态升级回 Auto

目标是优先保证 shell 可用性,而不是为了 Auto 模式牺牲兼容性。

2.4 Provider / Model 选择统一收敛到 SelectionService

Provider、Model、Use 三组命令本质上都在操作同一份“当前选择状态”。本 PR 新增 CLI 侧的 resolver / 缓存层,把三组命令统一接到同一个 SelectionService,避免重复 bootstrap,也让后续命令行为更一致。

同时,SelectProviderSetCurrentModelSelectProviderWithModel 都纳入 provider create lock 保护,并对跨进程 provider create/remove 引入更稳健的锁语义,减少状态写入竞争。


三、终端诊断 Phase 2:PTY 代理与自动诊断闭环

3.1 PTY 代理稳定性升级

为保证代理 shell 可长期使用,本 PR 在 internal/ptyproxy/proxy_unix.go 中补上了多层稳定性防线:

  • 增加 defer term.Restore() 兜底恢复宿主终端状态
  • 转发 SIGHUPSIGINTSIGTSTPSIGCONT
  • 同步 SIGWINCH 窗口尺寸,保证 less / vim 等全屏程序显示正常
  • 启动与退出时输出 banner,明确代理状态

此外,代理在启动阶段会预建并复用 gateway RPC 长连接,避免每次诊断再重复做一次完整连接与鉴权。

3.2 Shell Integration 注入与 OSC 133 事件流

新增:

  • internal/ptyproxy/shell_init.go
  • internal/ptyproxy/shell_events.go

能力包括:

  • neocode shell --init 输出 shell integration 脚本
  • 兼容 bash 与 zsh
  • 在 tmux 环境下自动进行控制序列包裹透传
  • 从 PTY 字节流中剥离并解析 OSC 133 序列
  • 支持 chunked 输入和 tmux 双层包络
  • 对 leftover 缓冲设置上限,避免异常流导致内存持续膨胀

3.3 Auto 模式触发过滤

为了避免“命令非零退出就一律诊断”的误报,本 PR新增过滤引擎:

  • 信号类退出码如 130137 不触发 Auto
  • grepfindtestfalse 等常见命令豁免
  • 输出过短、无错误语义、信息量不足时不触发

这样做的目的是把 Auto 诊断聚焦在“值得分析的失败”上,而不是把正常 shell 行为误判成异常。

3.4 日志清洗、脱敏与 UTF-8 安全截断

新增:

  • internal/ptyproxy/sanitizer.go
  • internal/ptyproxy/truncator.go

诊断 RPC 发起前会统一执行:

  • ANSI / 控制字符降噪
  • \r 覆盖输出折叠
  • 空白归一化
  • 高危凭证打码(如 AKIA、JWT、Bearer Token、API key / password / secret、private key)
  • Head / Tail 截断,并保证 UTF-8 字符边界安全

这样既提高模型可读性,也降低把真实密钥送入模型上下文的风险。

3.5 IPC 协议升级为 JSON-Lines

新增 internal/ptyproxy/ipc_protocol.go,将原先简单的行文本协议升级为 JSON-Lines:

{"cmd":"diagnose","payload":"..."}
{"cmd":"auto","enabled":true}
{"ok":true,"message":"diagnosis completed","auto_enabled":true}

升级原因:

  • 支持 Manual 诊断、Auto on/off、Auto status 等多种动作
  • 让错误响应和状态响应都可结构化表达
  • 为后续 Phase 3 的多轮/多消息交互留下协议扩展空间

3.6 Socket 路径规范化与自动发现

新增 internal/ptyproxy/socket_paths.go,并把 socket 路径统一收敛到 ~/.neocode/run/。同时补充:

  • 启动前先清理残留 socket,降低 EADDRINUSE 风险
  • 兼容探测旧 $TMPDIR 路径并给出 warning
  • diag 命令侧支持 --socket > NEOCODE_DIAG_SOCKET > 最近一次运行目录 socket 的自动发现回退链路

这解决了用户每次都要手动记 socket 路径的问题,也提高了命令行诊断入口的可达性。

3.7 真实 diagnose 工具闭环

internal/tools/diagnose/tool.go 不再是占位实现,而是接到真实 system tool 闭环:

  • 通过 gateway 执行 diagnose 系统工具
  • 使用 SpawnSubAgent 做根因分析
  • 解析 JSON 结构化输出
  • 渲染 ANSI 彩色诊断结果
  • 对 sub-agent 不可用、超时、返回损坏等情况做 graceful fallback

目标不是“始终成功调用 AI”,而是在失败时仍然返回保守建议,避免 panic 或空白结果。


四、Provider / Model 管理闭环

4.1 provider add / provider rm / provider ls

本 PR 实际补齐了完整的 provider 管理命令面,而不只是新增 provider add

  • neocode provider add <name> --driver --url --api-key-env [--discovery-endpoint]
  • neocode provider ls
  • neocode provider rm <name>

其中 provider add 会:

  • 校验输入参数
  • 从环境变量读取 API key
  • 原子写入 provider YAML
  • 持久化用户环境变量
  • 触发配置 reload
  • 自动切换到新 provider,并修正当前模型

provider rm 会:

  • 删除自定义 provider 配置
  • 清理对应用户环境变量
  • 如果删除的是当前 provider,则重置当前选择并重新 EnsureSelection

4.2 use 命令与 provider+model 原子切换

新增 neocode use <provider> [--model <model-id>]

如果只切 provider,会自动修正到该 provider 下的可用模型;如果同时指定 --model,则通过 SelectProviderWithModel 在同一临界区内完成切换,避免先切 provider 再切 model 的两次写入间隙被并发插入。

4.3 model ls / model set

新增:

  • neocode model ls
  • neocode model set <model-id>

其中 model ls 优先读取 snapshot,必要时再触发同步发现;model set 会校验模型归属关系,避免把不属于当前 provider 的模型写进配置。

4.4 SelectionService 与状态一致性改造

除了命令本身,本 PR 还调整了 state 层语义:

  • 新增 CLI 侧 selectionServiceResolver,按 workdir 缓存 selection service
  • SelectProvider / SetCurrentModel 纳入 provider create lock
  • 新增 SelectProviderWithModel
  • 自定义 provider 远程模型发现失败时,回退到 snapshot models
  • provider create/remove 跨进程锁的 stale threshold 从 30s 调整到 60s

这些调整的目标是减少并发切换、配置热更新和远程发现失败时的状态漂移问题。


五、CLI 结构调整

诊断与选择能力的命令面现在更清晰:

neocode shell
neocode shell --init bash
neocode shell --init zsh

neocode diag
neocode diag diagnose
neocode diag auto on
neocode diag auto off
neocode diag auto status

neocode provider add ...
neocode provider ls
neocode provider rm ...

neocode model ls
neocode model set ...

neocode use <provider> [--model <id>]

同时,诊断命令继续保持 skip preload / skip update check,保证错误现场下的响应尽可能直接。


六、测试与验证

本 PR 为以下模块补充了大量测试:

  • internal/ptyproxy
  • internal/tools/diagnose
  • internal/cli
  • internal/config/state

覆盖重点包括:

  • OSC 133 解析与 tmux 包络
  • Auto 过滤规则
  • 日志清洗、脱敏、UTF-8 截断
  • socket 路径规范化与自动发现
  • JSON-Lines IPC 读写
  • provider add/remove 回滚与并发锁语义
  • provider / model / use 命令输出与异常分支
  • diagnose sub-agent 成功 / 失败 / fallback 链路

实测方面,PR 也验证了:

  • Gateway IPC 通信
  • shell proxy 启动与降级路径
  • manual / auto 诊断信令
  • gateway.executeSystemTool(diagnose)
  • sub-agent 诊断输出渲染
  • OpenAI / Anthropic 协议兼容路径

七、风险与注意事项

  1. Auto 模式依赖 shell integration 与终端对 OSC 的支持;探针失败时会自动回退到 Manual,不影响基础 shell 使用。
  2. SSH / tmux / 深度定制终端下,事件到达时序可能更不稳定,因此本 PR 采取“可降级优先”的设计。
  3. 当前 shell 代理主实现仍聚焦 Unix-like;Windows ConPTY 不在本 PR 范围内。
  4. 本 PR 不包含 Phase 3 IDM 交互沙盒、Phase 4 TUI 全屏兼容、Phase 5 Windows ConPTY,这些仍属于后续迭代。

关联 Issue / 文档

fennoai[bot]

This comment was marked as outdated.

pionxe added 22 commits May 4, 2026 13:12
- 提取并重构数据过滤 (`filter.go`)、前置脱敏 (`sanitizer.go`) 与日志截断 (`truncator.go`) 逻辑,强化隐私保护并解耦核心链路。
- 规范化进程间通信 (IPC),引入基于 JSON-Lines 的协议抽象 (`ipc_protocol.go`) 及 Unix Socket 路径管理 (`socket_paths.go`)。
- 引入动态 OSC 133 Shell 事件探测与状态管理机制 (`shell_events.go`, `shell_init.go`)。
- 缩减并重构 `proxy_unix.go` 的超大文件实现,大幅提升代码可维护性。
- 补充完整的并发安全与边界条件单元测试。
- 完善 IDM (Intelligent Diagnostic Mechanism) 上下文管理与数据流转链路。
- 修复并加固技能依赖 (skill-dependency) 逻辑,确保并发诊断请求安全处理。
- 新增 `agent_test.go` 以强化并发数据处理与核心工具逻辑的单元测试覆盖率。
- 确立清晰的 "动词-名词 (Verb-Noun)" 命令层级,整合零散的临时诊断指令。
- 规范化网关守护进程 (Gateway/Daemon) 管理及 Provider 配置初始化流程。
- 遵循非侵入式环境初始化原则,提升工具在生产环境中的可维护性与用户体验。
- 更新相关 CLI 命令测试,保障重构后的输入与配置解析符合预期。
- 新增 'neocode provider add' 命令,支持通过命令行直接注册自定义模型供应商。
- 引入自动化 YAML 配置生成机制,简化底层配置细节,保障配置原子性更新。
- 新增 'neocode use' 命令,支持全局快速切换活跃供应商。
- 完善相关命令的单元测试,覆盖 Mock 注入与原子更新链路。
- 新增 'neocode model ls' 命令,支持查看当前供应商的所有可用模型及激活状态。
- 新增 'neocode model set' 命令,允许手动切换并持久化模型配置。
- 扩展 'neocode use' 命令,支持 '--model' 参数实现供应商与模型的一键切换。
- 采用轻量化配置操作机制,避免 CLI 模式下沉重的运行时依赖注入。
fennoai[bot]

This comment was marked as outdated.

@phantom5099
Copy link
Copy Markdown
Collaborator

@xgopilot 分析PR的实际代码中还有没有PR描述以外的修改

@phantom5099

This comment was marked as resolved.

@1024XEngineer 1024XEngineer deleted a comment from fennoai Bot May 4, 2026
@Cai-Tang-www
Copy link
Copy Markdown
Collaborator

P0 / High:Auto off 会被下一次 PromptReady 自动打开

这个已经有 review 指出来了:PromptReady 会无条件把 autoState.Enabled 设回 true,所以用户执行:

neocode diag auto off

之后,只要下一次 prompt ready,Auto 又被打开。review 里也明确说需要把“OSC ready 状态”和“用户开关状态”拆开。

这是 blocker,因为它破坏了用户显式关闭 Auto 的承诺,也违反 #525 里“用户可通过 neocode diag auto off 实时关闭自动触发”的回滚方案

@Cai-Tang-www
Copy link
Copy Markdown
Collaborator

P1:zsh 写了 init 脚本,但 shell proxy 没自动注入

PR 描述和 #525 都说要兼容 bash/zsh,但 buildShellCommand 只对 bash 走 --rcfile 注入。代码里 prepareBashInitRC(shellPath) 只检查 isBashShell,zsh 不走等价 bootstrap。

review 也指出了:neocode shell --shell /bin/zsh 时不会自动注入 zsh integration,OSC 133 永远不会出现,1.5s 后 fallback manual,所以 Auto 实际 bash-only。

这个不是安全问题,但属于“宣传与实际不一致”。你有两个选择:

方案 A:补 zsh 自动注入。
实现 prepareZshInitRC,通过 ZDOTDIR 或临时 .zshrc 方式注入。

方案 B:承认第一版 auto-injection 只支持 bash

@pionxe
Copy link
Copy Markdown
Collaborator Author

pionxe commented May 4, 2026

/review

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.

Found 3 noteworthy issues after code quality, performance, security, and documentation-accuracy passes.

}

if apiKeyEnv != "" {
if envErr := deleteUserEnvVarForCreate(apiKeyEnv); envErr != nil {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If user-env cleanup fails here, the provider YAML has already been deleted but the method returns before manager.Load() / EnsureSelection(). That leaves the current selection pointing at a provider that no longer exists, so later model/use commands can fail on inconsistent state. This path needs rollback or it needs to continue repairing selection even when env cleanup fails.

Comment thread internal/ptyproxy/proxy_unix.go Outdated
return diagIPCResponse{}, err
}

legacyPath, fallbackErr := ResolveLegacyTmpDiagSocketPath()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This fallback is not scoped to the requested shell/session; it just picks the newest legacy socket under $TMPDIR. If the primary ~/.neocode/run/... socket is gone, diag or diag auto off can be delivered to a stale or unrelated shell, and its buffered output may be sent into diagnose. Please make this opt-in or validate that the fallback target matches the intended session before dialing it.

fmt.Fprintf(out, "当前模型: %s\n", displayCurrentModel(currentModel))
fmt.Fprintln(out, "可用模型:")

models, err := svc.ListModelsSnapshot(cmd.Context())
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

model ls is described as 'prefer snapshot, then do sync discovery if needed', but this only falls back when the snapshot list is empty. Any snapshot read error returns immediately even if live discovery would succeed, so a corrupt/missing snapshot becomes a hard failure instead of a recoverable path.

@pionxe pionxe merged commit 9433c0d into 1024XEngineer:main May 4, 2026
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 2: Shell Integration 与 Auto Beta 落地

3 participants