From 5a53d83ad56ca41105452cead10b554c330514de Mon Sep 17 00:00:00 2001 From: deviosyan Date: Thu, 28 May 2026 02:17:42 +0800 Subject: [PATCH] fix: sync outdated stub docs + integrate LSP closeAllFiles with compact - Remove redundant fork:true and run_in_background params in /fork command - Integrate LSP closeAllFiles() into postCompactCleanup (memory leak #14) - Update 10 docs files: mark implemented features no longer as stubs Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/features/autofix-pr.md | 2 +- docs/features/bash-classifier.md | 2 +- docs/features/coordinator-mode.md | 4 +-- docs/features/fork-subagent.md | 4 +-- docs/features/mcp-skills.md | 10 +++--- docs/features/web-browser-tool.md | 4 +-- docs/features/workflow-scripts.md | 2 +- docs/memory-leak-audit.md | 33 +++++++------------ .../task/task-004-assistant-session-attach.md | 2 +- src/commands/fork/fork.tsx | 17 +++------- src/services/compact/postCompactCleanup.ts | 16 +++++++++ 11 files changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/features/autofix-pr.md b/docs/features/autofix-pr.md index 2ef33a6d4b..d1ad59494d 100644 --- a/docs/features/autofix-pr.md +++ b/docs/features/autofix-pr.md @@ -1,6 +1,6 @@ # `/autofix-pr` 命令实现规格文档 -> **状态**:规划阶段(2026-04-29),等待评审通过后进入实施。 +> **状态**:已实现(598 行代码 + 5 个测试文件)。 > **Worktree**:`E:\Source_code\Claude-code-bast-autofix-pr`,分支 `feat/autofix-pr`,基于 `origin/main` 4f1649e2。 > **架构**:R(Remote-via-CCR),完整版(含 stop 子命令、单例锁、subscribePR、in-process teammate、skills 探测)。 diff --git a/docs/features/bash-classifier.md b/docs/features/bash-classifier.md index bd49620700..65a50d9e0e 100644 --- a/docs/features/bash-classifier.md +++ b/docs/features/bash-classifier.md @@ -1,7 +1,7 @@ # BASH_CLASSIFIER — Bash 命令分类器 > Feature Flag: `FEATURE_BASH_CLASSIFIER=1` -> 实现状态:bashClassifier.ts 全部 Stub,yoloClassifier.ts 完整实现可参考 +> 实现状态:bashClassifier.ts 为 ANT-ONLY stub(需要 Anthropic 内部 API);开源版使用 yoloClassifier.ts (1496行) 作为完整替代方案 > 引用数:45 ## 一、功能概述 diff --git a/docs/features/coordinator-mode.md b/docs/features/coordinator-mode.md index 322ae488b1..7f1ec9ee8d 100644 --- a/docs/features/coordinator-mode.md +++ b/docs/features/coordinator-mode.md @@ -90,7 +90,7 @@ export function isCoordinatorMode(): boolean { 文件:`src/coordinator/workerAgent.ts` -当前为 stub。Worker 实际使用通用 AgentTool 的 `worker` subagent_type。 +Worker 使用通用 AgentTool 的 `worker` subagent_type。工具过滤通过 `getWorkerTools()` 排除内部编排工具(TeamCreate/TeamDelete/SendMessage/SyntheticOutput)。 ### 3.6 数据流 @@ -147,5 +147,5 @@ CLAUDE_CODE_SIMPLE=1 bun run dev | 文件 | 行数 | 职责 | |------|------|------| | `src/coordinator/coordinatorMode.ts` | 370 | 模式检测 + 系统提示 + 用户上下文 | -| `src/coordinator/workerAgent.ts` | — | Worker agent 定义(stub) | +| `src/coordinator/workerAgent.ts` | 68 | Worker agent 定义(完整) | | `src/constants/tools.ts` | — | `ASYNC_AGENT_ALLOWED_TOOLS` 工具白名单 | diff --git a/docs/features/fork-subagent.md b/docs/features/fork-subagent.md index 5aab27ca54..478c9c5653 100644 --- a/docs/features/fork-subagent.md +++ b/docs/features/fork-subagent.md @@ -31,7 +31,7 @@ Agent({ subagent_type: "general-purpose", prompt: "..." }) ### /fork 命令 -注册了 `/fork` 斜杠命令(当前为 stub)。当 FORK_SUBAGENT 开启时,`/branch` 命令失去 `fork` 别名,避免冲突。 +注册了 `/fork` 斜杠命令。当 FORK_SUBAGENT 开启时,`/branch` 命令失去 `fork` 别名,避免冲突。 ## 三、实现架构 @@ -192,4 +192,4 @@ FEATURE_FORK_SUBAGENT=1 bun run dev | `packages/builtin-tools/src/tools/AgentTool/resumeAgent.ts` | — | Fork agent 恢复 | | `src/constants/xml.ts` | — | XML 标签常量 | | `src/utils/forkedAgent.ts` | — | CacheSafeParams + ContentReplacementState 克隆 | -| `src/commands/fork/index.ts` | — | /fork 命令(stub) | +| `src/commands/fork/index.ts` | — | /fork 命令 | diff --git a/docs/features/mcp-skills.md b/docs/features/mcp-skills.md index 45c76056f4..c9ef3d0923 100644 --- a/docs/features/mcp-skills.md +++ b/docs/features/mcp-skills.md @@ -1,7 +1,7 @@ # MCP_SKILLS — MCP 技能发现 > Feature Flag: `FEATURE_MCP_SKILLS=1` -> 实现状态:功能性实现(config 门控筛选器完整,核心 fetcher 为 stub) +> 实现状态:完整实现(fetcher + 缓存 + 实时刷新 + 安全措施) > 引用数:9 ## 一、功能概述 @@ -86,7 +86,7 @@ const fetchMcpSkillsForClient = feature('MCP_SKILLS') ## 三、关键设计决策 1. **Feature gate 隔离**:`feature('MCP_SKILLS')` 守护条件 `require()` 和所有调用点。关闭时无模块加载、无获取操作 -2. **资源到技能映射**:技能从 MCP 服务器的 `skill://` URI 资源中发现。`fetchMcpSkillsForClient` 负责转换(当前为 stub) +2. **资源到技能映射**:技能从 MCP 服务器的 `skill://` URI 资源中发现。`fetchMcpSkillsForClient` 负责转换(LRU 缓存,20 条目) 3. **循环依赖避免**:`mcpSkillBuilders.ts` 作为依赖图叶节点,避免 `client.ts ↔ mcpSkills.ts ↔ loadSkillsDir.ts` 循环 4. **服务器能力检查**:技能获取还需要 MCP 服务器支持 resources (`!!client.capabilities?.resources`) @@ -105,8 +105,8 @@ FEATURE_MCP_SKILLS=1 bun run dev | 文件 | 状态 | 需要实现 | |------|------|---------| -| `src/skills/mcpSkills.ts` | Stub | `fetchMcpSkillsForClient()` — 从 MCP 资源列表中筛选 `skill://` URI 并转换为 Command 对象 | -| `src/skills/mcpSkillBuilders.ts` | Stub | 技能构建器注册(避免循环依赖) | +| `src/skills/mcpSkills.ts` | 143 行 | `fetchMcpSkillsForClient()` — 从 MCP 资源列表中筛选 `skill://` URI 并转换为 Command 对象 | +| `src/skills/mcpSkillBuilders.ts` | 45 行 | 技能构建器注册(避免循环依赖) | ## 六、文件索引 @@ -115,4 +115,4 @@ FEATURE_MCP_SKILLS=1 bun run dev | `src/commands.ts:547-608` | 技能命令过滤 | | `src/services/mcp/client.ts:117-2358` | 技能获取 + 缓存管理 | | `src/services/mcp/useManageMCPConnections.ts` | 实时刷新 | -| `src/skills/mcpSkills.ts` | 核心转换逻辑(stub) | +| `src/skills/mcpSkills.ts` | 核心转换逻辑(完整实现) | diff --git a/docs/features/web-browser-tool.md b/docs/features/web-browser-tool.md index 9290a685ed..2cbf4b0b96 100644 --- a/docs/features/web-browser-tool.md +++ b/docs/features/web-browser-tool.md @@ -1,7 +1,7 @@ # WEB_BROWSER_TOOL — 浏览器工具 > Feature Flag: `FEATURE_WEB_BROWSER_TOOL=1` -> 实现状态:核心工具已实现,面板为 Stub,布线完整 +> 实现状态:核心工具已实现,面板故意为 null(结果在对话中内联显示) > 引用数:4 ## 一、功能概述 @@ -14,7 +14,7 @@ WEB_BROWSER_TOOL 让模型可以启动浏览器实例、导航网页、与页面 | 模块 | 文件 | 状态 | |------|------|------| -| 浏览器面板 | `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserPanel.ts` | **Stub** — 返回 null | +| 浏览器面板 | `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserPanel.ts` | **Null by design** — 结果内联显示,不需面板 | | 浏览器工具 | `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserTool.ts` | **已实现** | | REPL 集成 | `src/screens/REPL.tsx` | **布线** — 渲染 WebBrowserPanel | | 工具注册 | `src/tools.ts` | **布线** — 动态加载 | diff --git a/docs/features/workflow-scripts.md b/docs/features/workflow-scripts.md index 05a59e6052..b8e0128306 100644 --- a/docs/features/workflow-scripts.md +++ b/docs/features/workflow-scripts.md @@ -1,7 +1,7 @@ # WORKFLOW_SCRIPTS — 工作流自动化 > Feature Flag: `FEATURE_WORKFLOW_SCRIPTS=1` -> 实现状态:全部 Stub(7 个文件),布线完整 +> 实现状态:已实现 — WorkflowTool (432行), LocalWorkflowTask (212行), WorkflowDetailDialog (103行), 含测试 > 引用数:10 ## 一、功能概述 diff --git a/docs/memory-leak-audit.md b/docs/memory-leak-audit.md index 7501e35aec..5ff09f5aa4 100644 --- a/docs/memory-leak-audit.md +++ b/docs/memory-leak-audit.md @@ -581,18 +581,11 @@ if (snipResult !== undefined) { ### 问题详情 -`LSPServerManager` 中的 `openedFiles: Map` 追踪所有通过 `didOpen` 打开的文件。`closeFile()` 方法存在可以发送 `didClose` 通知并清理 Map 条目,但代码注释明确标注: +`LSPServerManager` 中的 `openedFiles: Map` 追踪所有通过 `didOpen` 打开的文件。`closeAllFiles()` 方法已实现,在 compaction 后通过 `postCompactCleanup` 自动调用,释放所有 LSP 服务器端的文件状态。 -``` -NOTE: Currently available but not yet integrated with compact flow. -TODO: Integrate with compact - call closeFile() when compact removes files from context -``` +### 修复方式(已完成) -长时间会话中,每次读取/编辑文件都会通过 `openFile()` 添加条目,但 compaction 不会清理这些条目,导致 Map 无限增长。 - -### 修复方式 - -1. **添加 `closeAllFiles()` 方法**:遍历 `openedFiles` Map,对每个文件发送 `didClose` 通知,然后清空 Map。Best-effort 错误处理。 +1. **`closeAllFiles()` 方法**(LSPServerManager.ts:414):遍历 `openedFiles` Map,对每个文件发送 `didClose` 通知,然后清空 Map。Best-effort 错误处理。 ```typescript async function closeAllFiles(): Promise { @@ -612,18 +605,16 @@ async function closeAllFiles(): Promise { } ``` -2. **集成到 `postCompactCleanup`**:在 compaction 后自动调用 `closeAllFiles()`,释放所有 LSP 服务器端的文件状态。 +2. **集成到 `postCompactCleanup`**(已完成):在 compaction 后自动调用 `closeAllFiles()`,使用 fire-and-forget async 模式(与 sweepFileContentCache 一致)。 ```typescript -// postCompactCleanup.ts -try { - const lspManager = getLspServerManager() - if (lspManager) { - await lspManager.closeAllFiles() - } -} catch { - // LSP module may not be available in all environments -} +// postCompactCleanup.ts — isMainThreadCompact guard +void import('../lsp/manager.js') + .then(m => { + const manager = m.getLspServerManager() + if (manager) return manager.closeAllFiles() + }) + .catch(error => { logError(error) }) ``` --- @@ -632,7 +623,7 @@ try { ``` 确认已实现 (12): #1 图片 #2 /usage #3 进度消息 #4 空闲渲染 #5 虚拟滚动器 #6 管道输出 #10 MCP缓冲区 -已修复 (7): #7 语法加载 #8 NO_FLICKER #9 RC权限 #11 LRU缓存键 #12 snipCompact #17 LSP文件追踪 #18 Permission Polling +已修复 (8): #7 语法加载 #8 NO_FLICKER #9 RC权限 #11 LRU缓存键 #12 snipCompact #14 LSP compact集成 #17 LSP文件追踪 #18 Permission Polling ### 测试覆盖 diff --git a/docs/task/task-004-assistant-session-attach.md b/docs/task/task-004-assistant-session-attach.md index c4f59cca86..cfe5e5087b 100644 --- a/docs/task/task-004-assistant-session-attach.md +++ b/docs/task/task-004-assistant-session-attach.md @@ -3,7 +3,7 @@ > 来源: [stub-recovery-design-1-4.md](../features/stub-recovery-design-1-4.md) 第 4 项 > 优先级: P3 > 工作量: Phase 4A 中等,4A-4D 全做完很大 -> 状态: Phase 4A DONE, 4B-4D TODO +> 状态: Phase 4A-4D 全部实现 (src/assistant/ 350行, src/commands/assistant/ 完整) ## 目标 diff --git a/src/commands/fork/fork.tsx b/src/commands/fork/fork.tsx index 2fa7f7ef30..84c65d4bfb 100644 --- a/src/commands/fork/fork.tsx +++ b/src/commands/fork/fork.tsx @@ -39,23 +39,16 @@ export async function call( } try { - // Reuse AgentTool logic for fork path. - // Omitting subagent_type triggers implicit fork. + // Omitting subagent_type triggers the fork path in AgentTool: + // isForkSubagentEnabled() && !subagent_type → fork routing → inherits + // parent context + system prompt + model + tools. + // run_in_background is omitted: when fork gate is on, all agents are + // forced async via forceAsync flag (the param is removed from schema). const input = { prompt: directive, - fork: true, // 触发 AgentTool 的 fork 路径:继承父会话上下文 + system prompt + 模型 - run_in_background: true, // fork always runs async - // description 只显示在底部 selector / BackgroundTasksDialog,保持简短标签 - // 即可;用户输入的 prompt 会作为第一条用户消息呈现在主视图里,这里不要 - // 重复显示。 description: 'forked from main', }; - // Call AgentTool with proper parameters: - // - input: the agent parameters (no subagent_type => fork path) - // - toolUseContext: the current context (ToolUseContext) - // - canUseTool: permission-check function from context - // - assistantMessage: the last assistant message to fork from AgentTool.call(input, context, context.canUseTool!, lastAssistantMessage).catch(error => { logForDebugging(`Fork subagent async error: ${error}`, { level: 'error' }); }); diff --git a/src/services/compact/postCompactCleanup.ts b/src/services/compact/postCompactCleanup.ts index b527302172..515da5876d 100644 --- a/src/services/compact/postCompactCleanup.ts +++ b/src/services/compact/postCompactCleanup.ts @@ -99,6 +99,22 @@ export function runPostCompactCleanup(querySource?: QuerySource): void { }) } clearSessionMessagesCache() + + // Memory leak #14: Release LSP server file tracking state. + // closeAllFiles() sends didClose for every tracked file and clears the Map. + // Next file access re-opens only what's needed, preventing unbounded growth + // in long sessions. Fire-and-forget: best-effort release, non-blocking. + if (isMainThreadCompact) { + void import('../lsp/manager.js') + .then(m => { + const manager = m.getLspServerManager() + if (manager) return manager.closeAllFiles() + }) + .catch(error => { + logError(error) + }) + } + for (const cb of compactCleanupCallbacks) { try { cb()