概览
“LeanCTX 已经安装,但 Codex 仍然大量使用原生工具”通常不是单一故障,而是安装、MCP、Hooks、模型规则、安全边界和验证方式中的某一层没有闭环。
最有效的排障顺序不是反复重装,而是沿调用链逐层确认:
二进制能否运行
→ Codex 是否注册 MCP
→ 新任务是否暴露 LeanCTX 工具
→ Hooks 是否已信任并启用
→ AGENTS 是否明确优先级
→ Allowlist / 路径隔离是否放行本次操作
→ gain 统计是否产生真实增量
本文记录 Windows 11、Codex Desktop 与 LeanCTX 3.9.19 环境下的完整排查结论,并区分“真正的配置故障”“安全策略正常工作”和“工具输出看起来不同但其实正常”。
如果尚未完成基础安装与注册,请先阅读配套部署篇:Windows 11 上为 Codex 安装与配置 LeanCTX。
一、先采集一份最小诊断快照
在修改任何配置之前运行:
Get-Command lean-ctx -All
lean-ctx --version
lean-ctx status --json
lean-ctx config path
lean-ctx config validate
lean-ctx doctor
lean-ctx doctor integrations --json
lean-ctx allow
lean-ctx gain --deep
保存以下信息即可,不要公开完整配置或环境变量:
- 实际执行的
lean-ctx.exe路径和版本; - LeanCTX 报告的有效配置目录;
- Codex MCP 是否被发现;
- allowlist 模式与新增条目;
- 测试前的 LeanCTX 调用与节省统计。
这些数据能把“我觉得没有生效”变成可重复验证的问题。

二、症状与根因速查
| 症状 | 最可能的原因 | 首先检查 |
|---|---|---|
| PowerShell 可运行 LeanCTX,Codex 没有任何 LeanCTX 工具 | MCP 未注册、客户端未重启、GUI 找不到 exe | config.toml、绝对路径、完整重启 |
只看到 ctx_call | 当前 profile 使用网关工具面 | 通过 ctx_call 调内部 ctx_*,不是故障 |
| Hooks 页面全部显示未信任、开关关闭 | 命令 Hook 尚未审核 | 点击 Trust、打开开关、重启 |
| 修改 hooks.json 后又变成未信任 | Hook 定义哈希发生变化 | 重新审核和信任 |
| SessionEnd 显示 timeout 被限制 | 配置超过 Codex 的 3 秒上限 | 改为 3 |
| 工具存在,但模型仍优先原生调用 | AGENTS 未明确默认路线,或误以为会透明路由 | 加入最小 LeanCTX 路由规则 |
[BLOCKED] | allowlist 或路径边界生效 | 判断命令是否值得最小放行 |
读取 SKILL.md 时出现 path escapes project root | 强制 LeanCTX 路由与 Skills 目录白名单冲突 | 核对根级 allow_paths、配置热加载或重启 MCP |
读取其他根外文件时出现 path escapes project root | 文件不在当前 LeanCTX 工作区或精确白名单中 | 切换正确工作目录或增加精确根目录 |
Daemon not supported on this platform | Windows 平台限制 | 通常不影响 Codex stdio MCP |
Get-Disk/Get-Volume 报模块错误 | Windows Storage 模块问题 | 不要误判为 allowlist |
role:coder tokens 111% WARNING | LeanCTX 角色级会话软预算被超出 | 区分角色预算、SLO 与固定上下文开销 |
| gain 没有变化 | 测试没有经过 LeanCTX,或输出过短/原样透传 | 对比真实工具调用和前后统计 |
三、CLI 正常,但 Codex 没有 LeanCTX 工具
1. 核对 MCP 配置
打开:
%USERPROFILE%\.codex\config.toml
确认存在且没有重复表:
[mcp_servers.lean-ctx]
command = "C:/Users/<YOUR_USER>/bin/lean-ctx.exe"
args = []
startup_timeout_sec = 30
tool_timeout_sec = 120
如果 PowerShell 中 lean-ctx 可用,而 Codex Desktop 中不可用,优先把 command 改为已经核实的绝对路径。图形客户端继承的 PATH 可能与当前终端不同。
也可以重新执行:
lean-ctx wrap codex
lean-ctx doctor
2. 必须完全重启客户端
MCP server 通常在 Codex 启动时加载。以下操作不一定足够:
- 关闭一个任务;
- 清空当前对话;
- 只关闭设置窗口。
应完全退出 Codex Desktop,确认进程结束后重新启动,再创建新任务验证。
四、只暴露 ctx_call:这是网关模式,不是失败
LeanCTX 可以根据工具预算或 profile 暴露不同工具面。当前客户端只看到 ctx_call 时,正确流程是:
Codex
→ ctx_call
→ ctx_read / ctx_search / ctx_shell / 其他 LeanCTX 能力
不要因此重复注册多个 MCP server,也不要把“没有直接显示 84 个工具”理解为能力缺失。网关模式本身还可以减少固定工具 schema 的上下文成本。
真正需要检查的是:
ctx_call能否成功调用内部工具;- SessionStart 是否注入了当前 LeanCTX 指引;
- 真实读取、搜索或 Shell 测试是否进入 LeanCTX;
gain --deep是否出现增量。
五、Hooks 写好了,为什么仍然没有运行
1. hooks.json 不负责保存 enabled
Codex 把 Hook 定义和执行授权分开:
hooks.json:事件、matcher、命令、timeout;config.toml:[features] hooks = true;- Hooks 页面:命令审核、信任和启用状态。
因此,在 JSON 中找不到 enabled 并不是缺少配置。
2. 非托管命令 Hook 必须人工信任
打开 Codex Hooks 页面,逐项检查并启用:
- PreToolUse:
lean-ctx hook codex-pretooluse; - PostToolUse:
lean-ctx hook observe; - SessionStart:
codex-session-start与observe; - SessionEnd:
observe。
本方案共有四类事件、五个命令处理器。信任后还要打开每项右侧开关。
3. 修改定义后需要重新信任
Codex 会记录审核过的 Hook 定义。命令、参数或其他相关定义发生变化后,旧信任不应被视为继续覆盖新命令。因此升级 LeanCTX、修改 exe 路径或调整 Hook 配置后,应再次打开 Hooks 页面核查。
4. SessionEnd 只能配置 3 秒
如果写成:
{
"timeout": 5
}
Codex 会显示类似“clamping SessionEnd hook timeout to 3s”的加载问题。正确值是:
{
"timeout": 3
}
这不是 LeanCTX 超时,而是 Codex 对该生命周期事件的固定上限。
六、Hooks 已生效,模型为什么还会选原生工具
- MCP 提供工具;
- PreToolUse Hook 可以对匹配的原生调用进行重写、拒绝或提示;
- AGENTS 规则决定模型在调用前应该优先选什么。
仅安装 MCP,不代表模型会自然理解 LeanCTX 是默认 token-saving route。仅启用 Hook,也不应假定所有客户端、所有工具名和所有调用都能被透明转发。
推荐使用最小规则:
### LeanCTX Token-Saving Route
- Use LeanCTX for eligible local context work when available. When Hook replacement redirects a native tool, use the specified `ctx_*` route directly. When neither MCP nor Hook routing is available but the CLI is installed, use `lean-ctx -c` for verbose or high-output Shell commands; otherwise use permitted native or specialized tools as needed.
- Prefer one complete read for small files requiring exact structure or formatting. If triage omits required content, do not repeat the same broad read; retrieve only the missing evidence through a narrower read or another permitted source.
- Triage means compressed output, not failure. Treat a permanent denial as terminal only for the denied invocation or route; judge success from blocking status, error text, exit results, and required evidence, then follow any stated alternative or use a materially different permitted path.
- Keep command names aligned with the LeanCTX allowlist. Allowing a command does not allow every invocation form; if inline interpreter code or complex inline Shell logic is blocked, use an approved script or, when writes are authorized, a minimal project-internal temporary script, then remove it after verification if reproducible.
七、Allowlist 拦截:只放行完整命令,不放行解析碎片
1. 当前显式附加清单
截至 2026-08-24,本机 restricted 模式的当前显式附加清单为:
Remove-Item
Copy-Item
New-Item
wsl
Move-Item
Push-Location
Pop-Location
Rename-Item
Expand-Archive
Compress-Archive
Set-Content
Add-Content
Out-File
Tee-Object
Clear-Content
Stop-Process
Invoke-WebRequest
Invoke-RestMethod
Start-Transcript
Stop-Transcript
Set-ItemProperty
Start-Process
Unblock-File
winget
icacls
Set-Acl
takeown
attrib
ssh
scp
sftp
Stop-Computer
Restart-Computer
shutdown
使用下面的命令查看当前清单;不要使用 lean-ctx allow --list,因为当前 CLI 会把参数按 additive 条目处理:
lean-ctx allow
本次直接 cmdlet 入口测试显示,以下命令已由默认规则允许,无需再次显式添加:
Get-Content Get-ChildItem Get-Item
Test-Path Resolve-Path Get-Location
Join-Path Split-Path Select-String
Get-Command Get-Date Get-FileHash
ConvertFrom-Json ConvertTo-Json Select-Object
Sort-Object Measure-Object Group-Object
Format-List Format-Table Out-String
Get-Member Where-Object
2. 即使已放行也要谨慎使用的命令
Tee-Object:当前清单已放行,但-FilePath可以写文件;使用时仍需核对目标。Get-Unique:多数情况可用Sort-Object -Unique。Join-String:多数情况可用 PowerShell-join。
Allowlist 的目标不是让所有命令都能跑,而是让重复、明确、可审计的操作通过;对于文件删除、移动、复制、新建以及 WSL 间接操作,仍必须先核对目标和影响范围。
3. Git 双引号参数的解析异常
在当前客户端与 Hook 组合下,曾观察到下面的原生命令被解析为残缺片段:
git add -n -- "path"
安全层看到的内容可能类似转义不完整的 git add -n -- </code>,从而拒绝执行。这不是缺少某个 Git allowlist 命令,不应把解析碎片加入白名单。
可采用:
git add -n -- 'path'
或者直接通过 LeanCTX ctx_shell 运行并检查精确参数。
4. PowerShell 表达式不要伪装成命令
某些以静态表达式或控制流开头的片段,可能被安全解析器当成命令名。例如直接以范围表达式开头。可以改为:
Write-Output (1..3)
核心原则仍然是:修正命令形态,不要放行数字、反斜杠或其他解析残片。
八、路径隔离:path escapes project root 不是安装失败
LeanCTX 默认限制对当前项目根之外文件的读取。例如,当前任务根在仓库 A,却读取仓库 B 或任意 Documents 子目录时,可能得到:
path escapes project root
对于普通跨项目读取,这通常是安全边界正常工作;但如果被拒绝的是当前规则要求必须读取的 Codex SKILL.md 或 Memory,并且规则同时禁止使用其他读取路线,那么它就是需要修复的配置冲突,而不能长期依赖 Base64、Shell 包装或反复改写命令绕过。
1. 区分 allow_paths 与 extra_roots
LeanCTX 配置通常位于 %USERPROFILE%.config\lean-ctx\config.toml。先以实际输出为准:
lean-ctx config path
lean-ctx config validate
两个路径键都是根级配置,不要写进 [context]:
allow_paths = [
"C:/Users/<YOUR_USER>/.codex/skills",
"C:/Users/<YOUR_USER>/.codex/plugins/cache",
]
extra_roots = [
"C:/Users/<YOUR_USER>/.codex/memories",
]
allow_paths只允许直接访问,不把目录当作项目根扫描,适合 Skills;extra_roots同时允许搜索和索引,适合必须通过ctx_search检索的 Memory;.agents/skills是 Codex 官方用户级 Agent Skills 目录;仅当该目录实际存在并被当前 Codex 使用时才追加到allow_paths。.codex/skills与插件缓存路径应以当前环境实际暴露的技能位置为准。
2. 安全处理顺序
- 确认目标路径确实属于用户本次授权范围;
- 在修改全局配置前建立并校验原文件快照;
- 在正确项目目录中打开任务,或只添加精确
allow_paths/extra_roots; - 保持
path_jail开启; - 不要放行整个用户目录、Documents 或磁盘根。
这类限制正是 LeanCTX 路径安全边界的一部分。为了强制提高 token 节省而关闭路径隔离,会损害部署的稳健性。
3. 配置生效与完整回归
不同版本或运行方式对配置热加载的行为可能不同。若当前 MCP 已阻塞、调用持续挂起或仍保留旧边界,不要继续重试等价调用;完全退出并重新启动 Codex,再创建新任务测试。只关闭当前任务不一定会重建 MCP server。
不要只用 CLI lean-ctx read 证明修复,因为 CLI 与 Codex MCP 的路径执行语义可能不同。完整回归必须在 Codex 中覆盖:
ctx_read成功读取用户级、系统级和插件技能SKILL.md;ctx_search成功检索.codex/memories/MEMORY.md;- 一个未加入白名单的 project root 外测试文件仍被 MCP 拒绝;
lean-ctx doctor继续显示Path jail active。
九、精确输出、压缩输出和原生工具的边界
LeanCTX 默认优化上下文,不保证每次都返回逐字、完整的原始输出。以下情况可以使用原生或专用工具:
- 编辑和写入;
- 交互式或 TTY 进程;
- LeanCTX 没有对应能力;
- 必须获取逐字原始内容;
- 需要一条真正独立的验证路径。
CLI 中需要显式原始输出时可以使用:
lean-ctx raw "command"
需要压缩冗长 Shell 输出时才使用:
lean-ctx -c "command"
不要给每条短命令机械套一层 lean-ctx -c,额外包装本身也有成本。
十、几个容易误判的 Windows 问题
1. Daemon not supported on this platform
Windows 上 daemon 不受支持通常不影响 Codex 通过 stdio MCP 启动 LeanCTX。只要 MCP 工具可用、真实调用成功,就不应把这条提示当作主故障。
2. Get-Disk 或 Get-Volume 报错
这类命令可能因为 Microsoft.Windows.Storage.Core 或本机 PowerShell 模块状态失败。命令已经进入执行阶段时,问题通常不在 LeanCTX allowlist。
先在普通 PowerShell 中独立复现,再检查模块、PowerShell 版本和 Windows 组件,不要继续扩张 allowlist。
3. 不同进程使用了不同配置目录
交互式 PowerShell、Codex Desktop、沙箱进程或其他用户上下文可能解析出不同的 HOME 和配置目录。以:
lean-ctx doctor
lean-ctx allow --list
显示的“有效配置路径”为准。不要只检查一份看起来正确、但实际未被当前进程读取的配置文件。
十一、理解 watch 中的 budget、SLO 与 111% WARNING
运行:
lean-ctx watch
可能看到:
$$ budget role:coder tokens 111% WARNING
!! slo context_budget violated → Warn
这两行是 LeanCTX 自己的预算遥测,不表示 Codex 或 OpenAI 服务发生故障。
1. 每个字段表示什么
| 字段 | 含义 |
|---|---|
$$ budget | LeanCTX 正在检查当前角色的会话预算 |
role:coder | 当前会话使用 coder 角色预算 |
tokens 111% | LeanCTX 统计的会话 token 已达到该角色 token 预算的 111% |
WARNING | 超过软警告阈值,但不等于执行被阻断 |
!! slo | 某个 LeanCTX 服务级目标被触发 |
context_budget violated → Warn | 上下文预算 SLO 被触发,配置动作是发出警告 |
默认 coder 角色使用 200,000 token、100 次 Shell 调用和 5 美元成本预算,并在达到 80% 时开始警告;默认 context_budget SLO 超过 200,000 token 时执行 warn。因此,111% 通常意味着一个持续很久的任务超过了角色软预算,而不是模型上下文窗口已经使用了 111%。
2. 它不代表什么
该百分比不是:
- Codex 模型上下文窗口使用率;
- OpenAI API 配额或账单上限;
AGENTS.md自身占用比例;- LeanCTX 压缩失败率;
- 客户端即将崩溃的倒计时。
截图中同时出现较高 saved tokens 和 compression 时,反而说明流量仍在经过 LeanCTX。
3. 区分角色预算与固定上下文开销
使用:
lean-ctx doctor overhead --json
lean-ctx tools health
doctor overhead 检查 MCP 工具 schema、LeanCTX instructions 和规则文件等每会话固定成本。如果结果中的 over_budget 为 false,那么 tokens 111% 不是“AGENTS 已经装不下”,而是当前长会话累计超过了角色软预算。
tools health 会指出长期未使用、但每个会话都携带 schema 成本的工具。它给出的是优化建议,不应在没有回归验证时直接禁用工具。
4. 是否需要处理
- 只在超长任务中偶尔出现:无需修复,在自然阶段结束后新建 Codex 任务即可。
- 普通短任务也频繁超限:再考虑为长任务建立单独角色,并同步调整角色预算与
context_budgetSLO。 - 不要只提高角色预算而保留更低的 SLO 阈值,否则
!! slo仍会继续出现。 - 不要反复切换角色或重置预算,只为隐藏警告;角色切换会重置预算计数,告警消失不等于根因已经解决。
右侧的 Gain Score 是优化机会评分,不是安装健康度;Cache Hit Rate 0% 也不必单独视为故障。只有在反复读取完全相同内容时仍长期没有缓存命中,才需要继续检查路径规范化、读取模式或是否持续要求 fresh/raw 输出。
十二、如何证明问题已经修复
一次可靠回归至少包含四组证据。
1. 配置证据
config.toml中 MCP 条目有效;[features] hooks = true;hooks.json可解析,SessionEnd为 3 秒;- Hooks 页面五个处理器已信任并启用。
2. 工具证据
- 新任务中出现
ctx_call或ctx_*; ctx_call能调用内部读取、搜索与 Shell 能力;- PreToolUse 对原生读取或 Shell 给出 LeanCTX 路由提示、重写或拒绝。
3. 安全证据
- restricted 模式仍启用;
Group-Object等新增命令通过 additive allowlist 管理;- 写入型命令和解析碎片没有被错误放行;
- 只有精确列出的 Skills/Memory 根获得访问,其他项目根之外的访问仍受到限制。
4. 统计证据
在同一组测试前后比较:
lean-ctx gain --deep
LeanCTX 工具调用和节省统计应继续增长。统计增长证明流量实际经过 LeanCTX;它仍不等于整个模型请求或账单的 token 数。
十三、建议的最终排障流程
1. lean-ctx --version 能否运行?
否 → 修复安装与 PATH
是 ↓
2. doctor 能否发现 Codex MCP?
否 → 修复 config.toml / wrap codex
是 ↓
3. 重启后的新任务是否有 ctx_call 或 ctx_*?
否 → 检查 GUI 进程环境、MCP 启动日志与路径
是 ↓
4. Hooks 是否已信任并启用?
否 → 审核、Trust、打开开关、重启
是 ↓
5. 操作是否被 allowlist 或路径隔离阻止?
是 → 只做最小、完整、可解释的放行
否 ↓
6. AGENTS 是否明确默认路线与例外?
否 → 加入最小三条骨架
是 ↓
7. gain --deep 是否产生增量?
否 → 用真实读取/搜索/Shell 场景重新测试调用链
是 → 部署完成
这套流程避免了两个极端:一是只看“安装成功”就宣布完成,二是为了追求 100% LeanCTX 调用而关闭安全边界。稳定部署的目标是:LeanCTX 在适用操作中优先、可观测、可验证,同时原生工具仍能承担编辑、精确输出、交互进程和独立验证。
路由、永久拒绝与快照故障
LeanCTX 接管原生 Shell 入口,不代表所有命令都会被拒绝。入口路由、命令许可、路径边界和运行环境是四个不同层次,排查时不要把它们混为一谈。
先识别信号类型
| 信号 | 含义 | 正确处理 |
|---|---|---|
[lean-ctx: N lines filtered by triage] | 输出经过压缩;该提示本身不是执行错误 | 检查退出状态和必要证据;仅在关键内容缺失时定向展开 |
[BLOCKED — DO NOT RETRY] 或 allowlist 明确拒绝 | 当前命令路径被永久拒绝 | 停止等价变体重试,改用其他允许工具或路径 |
path escapes project root | 目标超出 LeanCTX 当前项目边界或精确白名单 | 为确有需要的目录添加根级 extra_roots 或 allow_paths,等待热加载或重启 MCP;不要扩大到宽泛父目录 |
action is required、task is required | MCP 调用缺少必要字段 | 修正调用结构,而不是增加命令 allowlist |
Shell 的 command not found 或退出码非零 | 命令、依赖、参数或运行环境问题 | 按普通 Shell 错误排查;它不等于 LeanCTX 安全拒绝 |
单独看到 JSON 中的 "blocked": true 仍不够判断根因;必须读取同一调用附近的明确错误文本。永久拒绝只终止当前路径,不应自动终止整个任务。
使用真正不同的回退路径
遇到永久拒绝后,可以在既有授权范围内自主选择真正不同的方案,例如从压缩读取改为定向读取、从 ctx_shell 改用专用编辑工具,或在 MCP/Hook 不可用而 CLI 已安装时使用:
lean-ctx -c "<verbose-command>"
只有需要未经压缩的精确输出时才使用:
lean-ctx raw "<command>"
不要只改变引号、参数顺序、PowerShell 包装方式或命令别名来重复同一个已拒绝操作。若必要操作仍没有允许的替代路径,或继续执行需要扩大授权范围,再报告具体命令、路径、范围和原因。
快照失败要分别检查命令和路径
New-Item、Copy-Item、Test-Path 与 Get-FileHash 获得命令许可,只解决命令层问题;目标目录位于 project root 外时,仍可能触发路径拒绝。正确的文档修改流程是:
- 确认目标路径在当前授权范围内。
- 使用既有归档约定创建带时间戳的快照目录。
- 复制原文件后用
Test-Path验证快照存在。 - 使用
Get-FileHash比较原件与快照。 - 哈希匹配后再修改原文件;失败时停止,不进行无备份写入。
对于 Git 已完整追踪且工作树干净的文本文件,现有提交历史可以提供精确恢复;未提交、未追踪、二进制、生成但具权威性或位于仓库外的核心文件,仍应在编辑前建立并验证快照。
十二、Codex 会话异常:fc_ ID 被要求使用 ctc
1. 现象
在部分 Codex 会话中,API 返回 HTTP 400,错误类似:
Invalid 'input[216].id': 'fc_09f77ac43cf7db36016a8920e7934487d0a9fd08e9db431d47'. Expected an ID that begins with 'ctc'.
Invalid 'input[9].id': 'fc_0911234de5598a11016a897cb7fba887d1ad603d5c403f8249'. Expected an ID that begins with 'ctc'.
这类错误通常出现在包含 custom_tool_call 的会话工具调用链中。
2. 核心原因
当前排查结论是 Sub2API 的兼容性故障:它在转发 Codex 会话中的工具调用时,保留或生成了 fc_ 前缀的输入项 ID,而接口要求该位置使用 ctc 前缀。相关问题见 GitHub issue #6071 与 #6146。
因此,不要再把模型/思考强度、LeanCTX 压缩、Shell allowlist 或路径隔离列为这类 fc_/ctc 报错的核心根因;它们属于其他独立问题。
3. 临时恢复方案
在报错前一阶段对应的分支中,新开会话执行后续操作,并先压缩文本,再继续任务。不要在原异常会话中反复重试,也不要通过扩大 LeanCTX allowlist、修改 allow_paths 或切换模型来规避。
4. 永久解决方案
等待 Sub2API 修复相关兼容性故障并发布包含修复的新版本;升级到修复版本后,再用最小复现验证错误是否消失。在修复版本发布前,“新会话 + 压缩文本”只能视为临时方案。