最后整理:2026-06-13
本文记录 Deepreef 已落地的能力、已验证修复和重要历史实现。
TODO.md 已整理为纯待办入口。此前仍留在 TODO 中的完成任务正文已从 TODO 移除,其实施记录继续由本文保存:
RM-10 至 RM-30:见第 22 至 24 节。QST-10、PERM-10:见第 25 至 26 节。DRF-00 至 DRF-80:见第 27 至 38 节。FG-60-R 与 CTX-70 文档部分:见第 39 节。当前未完成的永久 Worker/Supervisor 双角色主线 DA-00 至 DA-60 只保留在 TODO.md,完成后逐项迁入本文。
状态标记:
| 标记 | 含义 |
|---|---|
当前有效 |
当前目标架构继续保留,后续开发可依赖 |
历史已实现,待删除 |
当前代码仍存在,但架构已决定删除 |
部分完成 |
只记录已落地部分,剩余工作不算完成 |
历史记录 |
用于解释代码演进,不应作为新开发方向 |
核心运行时
ReasonixEngine.submit() -> AsyncGenerator<LoopEvent>。ContextManager、Session JSONL、上下文 trim/compact 和 summarizer;/context 策略持久化。StreamingToolExecutor、shared/exclusive 并发、exactly-once tool result。safe/balanced/yolo)、Question 交互闭环。/thinking off|open|high 的 provider 参数映射。融合主线(DRF-10 → DRF-80)
ModelTarget / 角色化 client resolver(Worker/Supervisor/Oracle 独立端点)。ModelProfile + HarnessProfile(本地小模型保守默认)。ReadTracker(read-before-write)+ EarlyStopDetector(重复/只读循环/patch 螺旋)。BranchBudgetTracker + CheckpointEngine v2(长任务防循环、可恢复)。createBashTool({ dualTrack: true }))。TaskLedger + Verification Gate(改动后必须验证)。EvidenceBundle → SupervisorAdvice → scratch 回注。packages/core/scripts/benchmark-matrix.ts)。TUI(Gemini 风格移植 TUI-GM)
融合主线完成矩阵(2026-06-12)
| 编号 | 任务 | 状态 |
|---|---|---|
| RM-10 | 删除 free-auto 自动路由 |
✅ |
| RM-20 | 删除自动推理强度 / StrategyTier | ✅ |
| RM-30 | 删除 Token 预估专项 | ✅ |
| QST-10 | Question 交互闭环 | ✅ |
| PERM-10 | 权限规则 + 子 Agent 冒泡 | ✅ |
| DRF-00 | 基线与复制台账 | ✅ |
| DRF-10 | ModelTarget + client resolver | ✅ |
| DRF-11 | ModelProfile + HarnessProfile | ✅ |
| DRF-20 | read-before-write + early-stop | ✅ |
| DRF-30 | BranchBudget + Checkpoint v2 | ✅ |
| DRF-31 | 参数 / 文本 tool-call salvage | ✅ |
| DRF-32 | Shell 双轨执行 | ✅ |
| DRF-40 | TaskLedger + Verification Gate | ✅ |
| DRF-50 | SupervisorAdvice + 触发器 | ✅ |
| DRF-51 | Supervisor 池 + 预算 | ✅ |
| DRF-60 | 指导回注闭环 | ✅ |
| DRF-70 | 两阶段工具路由 + free/forced | ✅ |
| DRF-80 | Benchmark 矩阵 + 发布门禁 | ✅ |
| FG-60-R | sessionWriter → /status |
✅ |
| CTX-70 | /context 文档 |
✅ 文档;人工验收待做 |
| OS-12/13-R | macOS/Windows 原生验收 | ⏳ 待人工 |
以下能力曾实现并有历史测试,但不再属于目标架构:
| 能力 | 当前状态 | 删除任务 |
|---|---|---|
free-auto 虚拟 provider、自动候选路由和跨模型 failover |
历史已实现,已删除 | DONE.md 的 RM-10 |
/thinking auto、ModeSelector、ModeStats 和自动 thinking 切换 |
历史已实现,已删除 | DONE.md 的 RM-20 |
| StrategyTier、动态 tier 推荐和对 model/temperature/reasoning 的覆盖 | 历史已实现,已删除 | DONE.md 的 RM-20 |
| TokenizerPool、Worker、精细 Token 预估和 TUI token/s 展示 | 历史已实现,已删除 | DONE.md 的 RM-30 |
删除前可以阅读本文对应历史章节理解接线范围,但禁止基于这些能力继续扩展。
| 项 | 状态 | 说明 |
|---|---|---|
CTX-70 人工验收 |
部分 | README /context 文档已补;长会话 trim/compact 需项目负责人人工验证 |
OS-12/13-R |
待验收 | 需真实 macOS/Windows 终端验证 PTY/ConPTY、中文路径、通知、剪贴板 |
| Supervisor 免费池 smoke | 可选 | DEEPREEF_SUPERVISOR_SMOKE=1;StepFun 候选默认 disabled |
| ECC content-pack CLI 端到端 | 暂缓 | 非融合主线阻塞项 |
| AgentMemory 上游测试增强 | 暂缓 | 全仓 bun test 仍有 memory 包预置失败 |
最后验证:2026-06-15(SFR-00 ~ SFR-90 全部完成)
bun test packages/core packages/tools packages/tui packages/cli packages/security
bun run typecheck # 全仓通过
| 检查项 | 状态 | 说明 |
|---|---|---|
| 融合包测试 | ⚠️ | 1695 pass / 18 skip / 8 fail,共 121 个测试文件;8 个预置失败均为 build/plan 移除与 mouse tracking 历史遗留,与 SFR 任务无关 |
packages/core |
⚠️ | 同上,core 内仅 6 个与 SFR 无关的预置失败 |
| TypeScript | ✅ | bunx tsc --noEmit 全仓通过 |
| 发布门禁 | ✅ | bun run packages/core/scripts/benchmark-matrix.ts 通过 |
全仓 bun test |
⚠️ | 2630 pass / 18 skip / 484 fail / 22 errors;484 个失败集中在 packages/memory/ / packages/agentmemory/,与融合主线和 SFR 任务均无关 |
历史 CI 基线(参考):
26928659701:ubuntu/windows/macos 三平台绿用户任务
→ ReasonixEngine.submit()
→ resolveModelProfile + resolveDefaultHarness
→ TaskLedger(复杂任务)+ VerificationGate
→ runLoop(LoopOptions)
→ ChatClient 流式响应
→ 文本 tool-call salvage(无原生 tool_calls 时)
→ StreamingToolExecutor(ReadTracker / 参数 salvage / 权限)
→ EarlyStop / BranchBudget(治理信号)
→ SupervisorGuidance(失败达阈值时)
→ AsyncGenerator<LoopEvent>
→ packages/tui/src/bridge.tsx → Ink TUI
→ OrchestrationSummary(Workers / Supervisor / Loop 三栏)
→ DeepiMessages(聊天记录)
→ LoadingIndicator(Gemini CLI 风格 spinner)
→ DialogManager(权限/提问优先级弹窗)
| 主题 | 当前实现 |
|---|---|
| 运行时 | Bun |
| API Provider | DeepSeek / Zen / Mimo / Kilo / NVIDIA / OpenAI-compatible;free-auto 已删除 |
| TUI | React 19 + Ink;Gemini 风格主题/动画/DialogManager/OrchestrationSummary/LoadingIndicator(TUI-GM) |
| 融合治理 | ModelTarget、ModelProfile、BranchBudget、Checkpoint、Supervisor 指导闭环 |
| Core 事件 | AsyncGenerator<LoopEvent>,使用 role-based 事件模型 |
| 工具并发 | shared 并行,exclusive 串行 |
| 工具进度 | 已有 tool_start、tool_progress: running/done 粗粒度事件 |
| 会话持久化 | .deepreef/sessions/*.jsonl,best-effort append |
| 上下文 | ImmutablePrefix + AppendOnlyLog + VolatileScratch |
| 权限 | PermissionEngine 的 deny → allow → ask 判定 |
| MainMode | plan(只读)+ build(完整工具集),PlanMode 工具切换 |
| Subagent | 三种内置角色:general-purpose / Explore / Plan,独立 child engine + 工具过滤 + 四级权限 |
StreamingToolExecutor 支持 shared 并行和 exclusive 串行。toolCallIndex 用于关联 tool delta、start、result 和 progress。ToolContext.signal 传递到工具,支持中断。ToolContext.invokeTool() 支持嵌套调用并拒绝递归。settled 集合,成功、失败、拒绝和中断路径统一避免重复追加 tool result。P2 已完成并通过对应 Core 测试:
CoreEngine.enqueueInstruction() 返回 queued / idle / ignored / full。pendingInstructionQueue 上限为 10。TUI 路由已完成接入,P3-R 已收口 bridge 回归测试。
TimelineItem[] + TurnView 状态模型。toolCallIndex + sequence,避免后续批次重复 index 覆盖历史记录。respondPermission(false),再调用 interrupt(),避免权限 Promise 悬空。messageQueue 保留串行提交语义。zh-CN / en,/lang 切换并写入 .deepreef/lang.json。Ctrl+F 消息搜索与屏幕空间高亮。/context 菜单已完成:居中弹窗,支持 strategy 切换、trigger/target 比例调整、当前用量显示和 Run now 立即执行。setErrorObserver() 观察错误。.env*、.git、密钥、证书、npmrc、AWS 凭据等。bash 内部使用当前平台 shell backend,设置非交互环境变量,限制危险命令,并支持进程树终止。Build Agent 当前开放 34 个静态工具:
packages/tools:29 个。5 个。Plan Agent 只开放:
read_file
list_dir
grep
todowrite
MCP 已完成:
McpClient:stdio JSON-RPC。McpHost:多 server 管理、后台加载配置、工具和资源发现。ListMcpResources、ReadMcpResource、ListMcpTools、CallMcpTool。McpAuth:项目级 token 存储,支持 set / list / delete;文件权限 0600,list 仅返回掩码。Skills 已接入:
packages/tools/src/skills/ 当前包含 52 个 SKILL.md。/skill 命令。运行时诊断系统已落地:
核心组件:
RuntimeLogger:默认关闭,异步写入 JSONL,支持事件过滤。RuntimeLogSink:单 sink 设计,timer-based flush(1s),队列超限自动丢弃。parseDebugArgs():支持 --debug/-d/--debug=<pattern>/--debug-file=<path>。createRuntimeLoggerFromEnv():从环境变量创建 logger。registerShutdownFlush():优雅退出时 flush 日志。cleanupOldLogs():后台清理过期日志。checkDeprecatedDebugEnv():弃用 DEEPREEF_DEBUG 提示。已实现事件(全流程覆盖):
| 阶段 | 事件 |
|---|---|
| Core | api.stream.first_event, api.usage, loop.stream.retry, loop.max_turns, reasoning.mode.switch |
| Executor | tool.batch.start/done, tool.execute.denied |
| MCP | mcp.host.start, mcp.server.connect.*, mcp.request.* |
| Tools | tool.result.overflow, tool.result.persisted |
| Process | process.shutdown.start/done |
配置:
DEEPREEF_LOG_LEVEL=debug|info|warn|error|off
DEEPREEF_LOG_FILE=<path>
DEEPREEF_LOG_FILTER=<pattern>
DEEPREEF_LOG_RETENTION_DAYS=7
DEEPREEF_LOG_MAX_TOTAL_MB=100
DEEPREEF_LOG_SYMLINK=1
DEEPREEF_TUI_DEBUG=1
DEEPREEF_TRACE=1
Perfetto 追踪:
.deepreef/traces/trace-<session-id>.json。状态:以下内容用于保留实现历史。
ModeSelector、ModeStats、自动模式与相关 TUI 状态将由RM-20删除;仅保留 Provider thinking 能力映射和用户手动选择off/open/high的能力。
历史上曾实现基于 Provider 能力和规则的自动推理模式切换:
核心组件:
ProviderThinkingCapabilities:Provider 思考能力映射。ModeSelector:纯规则评估器,状态机(idle → pending → active → cooldown)。ModeStats:切换统计和成功率追踪。已实现功能:
| 阶段 | 内容 |
|---|---|
| AS0 | reasoning_content 工具链连续性修复 |
| AS1 | Provider 能力和请求映射 |
| AS2 | 纯规则评估器(120s cooldown) |
| AS3 | Controller 和 loop 集成 |
| AS4 | TUI 状态显示(StatusBar + bridge 事件) |
| AS5 | 手动覆盖 /thinking 命令 |
| AS6 | 统计追踪和成功率计算 |
配置:
模式映射:
off → thinking disabled
low → thinking enabled
medium → thinking enabled
high → thinking + reasoningEffort=high
read_file:路径解析、敏感路径拒绝、二进制检测、大小限制、行范围和截断提示。write_file:敏感路径拒绝、父目录创建和 10 MiB 限制。edit:stale-read 校验、CRLF 保持、hash-anchored 主路径和 fuzzy fallback。hash-edit:随机临时文件、原子 rename、权限位保持和二进制保护。fuzzy-edit:多 pass 匹配;遇到歧义时拒绝猜测。| 编号 | 内容 |
|---|---|
| F3/F5 | StreamingCard 与 token/s 估算 |
| T20 | 多行输入 |
| T21 / T21-R | 斜杠命令自动补全与键盘事件冲突修复 |
| T22 | 跳词和 Ctrl+Backspace |
| T30/T31/T32 | i18n 基础设施、文案替换、/lang |
| T40 | 长会话渲染优化 |
| T41 | 消息搜索 |
| 编号 | 内容 | 验证命令 |
|---|---|---|
| LIFE-01 | CLI 与 Engine 生命周期闭环 | bun test e2e/system/cli-pipe-mode.acceptance.test.ts |
实现边界:
ReasonixEngine.shutdown():幂等;中断活跃 submit;调用 ctx.shutdown() 终止 tokenizer worker;drain session writer;flush runtime logger。ContextManager.shutdown() / TokenizerPool.shutdown():异步 terminate worker,清理 pending tasks。AsyncSessionWriter.drain():best-effort,等待队列排空,不阻塞 shutdown。RuntimeLogSink.flush():显式清除 flushTimer,避免定时器残留。tui.ts:try/finally 管理 engine + MCP host;main() resolve 后 process.exit(0) 兜底。delegateTask():子 Engine 在 finally 中关闭。保留限制:
fetch() keep-alive 连接阻止自然进程退出(连接池不在 _getActiveHandles() 中)。已尝试 keepalive: false、resp.body?.cancel() 均无效。最终在所有资源显式关闭后由 process.exit(0) 兜底,不依赖 Bun 内部连接池超时。/exit 后仍由同一 finally 块关闭 engine,未单独测试 PTY 路径(需原生终端环境)。| 编号 | 内容 |
|---|---|
| L2 | SessionWriter 有界队列 |
| L5 | 编辑链路 CRLF 归一化并恢复原格式 |
| N1 | NotebookEdit 异步原子写 |
| N2 | /skill 使用 @deepreef/tools 跨包导入 |
| N3 | SessionPicker 避免卸载后 setState |
| N4 | 空 tool call id 规范化 |
| N5 | client.ts 类型断言收紧 |
| LOG-READABILITY-01 | 收窄日志敏感字段规则:凭证 token 继续脱敏,token 用量统计保留数值 |
| 编号 | 内容 | 状态 |
|---|---|---|
| P0 | 工具结果、中断、权限和 TUI 队列契约测试 | 已完成 |
| P1 | tool result exactly-once | 已完成 |
| P2 | Core 中途指令队列和 loop 安全点 | 已完成 |
| P3 / P3-R | TUI 注入优先路由和反馈基础接入 | 已完成 |
| P4 | 结果溢出持久化 | 已完成 |
| P5 | Hook 可观测性增强 | 已完成 |
| 编号 | 内容 | 状态 |
|---|---|---|
| LOG0 | 冻结并验收现有骨架 | 已完成 |
| LOG1 | 完善 Core 全链路日志 | 已完成 |
| LOG2 | 迁移 Claude Code 调试体验 | 已完成 |
| LOG3 | 接入 MCP 日志 | 已完成 |
| LOG4 | 定点接入 Tools 日志 | 已完成 |
| LOG5 | TUI 与 Ink 性能采样 | 已完成 |
| LOG6 | Perfetto Trace(可选) | 已完成 |
| LOG7 | 轮转、清理和文档 | 已完成 |
| 编号 | 优先级 | 内容 | 状态 |
|---|---|---|---|
| AUD-01 | P0 | bash 敏感文件扫描绕过 | 已修复 |
| AUD-04 | P1 | thinking mode emergency 生命周期 | 已修复 |
| AUD-06 | P2 | fallback tool-call ID 并发稳健性 | 已修复 |
| AUD-09 | P3 | SSE 首事件 BOM 容错 | 已修复 |
| AUD-10 | P3 | 敏感 key 规则补充 | 已修复 |
状态:StrategyTier 及其自动推荐、模型/temperature/reasoning 覆盖将由
RM-20删除。本节只保留历史实现记录。
| 编号 | 内容 | 状态 |
|---|---|---|
| ST1 | strategy/tiers.ts 四级 tiers 数据模型和测试 |
已完成 |
| 编号 | 内容 | 状态 |
|---|---|---|
| CTX-10 | 策略类型、配置加载和菜单解析 | 已完成 |
| CTX-30 | 摘要区和 summarizer 接口 | 已完成 |
实现边界:
packages/core/src/context/policy.ts:定义 ContextPolicy 类型、DEFAULT_CONTEXT_POLICY、validateContextPolicy() 和 mergeContextPolicy()。packages/core/src/context/policy-store.ts:负责从 .deepreef/context.json 读取和写回策略配置,读失败回退默认值。ReasonixEngine 接入 ContextPolicyStore:启动时异步加载配置,setContextPolicy() 异步保存到文件。setContextPolicy() 改为异步方法,TUI 调用点已适配。packages/core/__tests__/context-policy.test.ts:覆盖策略验证、合并和持久化逻辑(26 个测试)。验收命令:
bun test packages/core/__tests__/context-policy.test.ts
bun run typecheck
bun test
保留限制:
.deepreef/context.json 独立持久化,不混入主配置文件。setContextPolicy() 异步保存,best-effort。| 编号 | 内容 | 状态 |
|---|---|---|
| CTX-30 | 摘要区和 summarizer 接口 | 已完成 |
| CTX-40 | Engine 自动 trim/compact 触发 | 已完成 |
| CTX-50 | 真实 LLM summarizer | 已完成 |
实现边界:
packages/core/src/context/summary.ts:ContextSummary 类类,维护 summary message,支持 replace / clear / read,summary 带 [CONTEXT_SUMMARY] / [/CONTEXT_SUMMARY] 标记。packages/core/src/context/summarizer.ts:ContextSummarizer 接口、FakeSummarizer(测试用)和 MechanicalSummarizer(本地机械摘要)。ContextManager 使用 ContextSummary 替代旧的 summaryMessages 字段,暴露 getSummary()、setSummarizer() 和 runSummarize() 方法。packages/core/__tests__/context-summary.test.ts:覆盖 ContextSummary、isSummaryMessage、FakeSummarizer、MechanicalSummarizer 和 ContextManager 集成(25 个测试)。验收命令:
bun test packages/core/__tests__/context-summary.test.ts
bun run typecheck
bun test
保留限制:
setSummarizer() 可选注入,不注入时 runSummarize() 返回 false。实现边界:
ContextPolicyMode 类型扩展支持 "compact" 模式。ReasonixEngine.submit() 在 budget 超过 triggerRatio 时自动触发 context reduction。compact 模式:调用 ctx.runSummarize() 后执行 trim,成功记录 context.reduction.compact.success,失败记录 context.reduction.compact.fallback 并回退 trim。trim 模式:直接执行 trim,记录 context.reduction.trim。ReasonixEngine.setSummarizer() 暴露给外部注入 summarizer 实现。ReasonixEngine.runContextReduction() 将 "compact" 映射为 "compress" 调用底层 reduceToTarget。packages/core/__tests__/engine-context-policy.test.ts:覆盖策略设置、状态获取、reduction 触发和 compact fallback(12 个测试)。验收命令:
bun test packages/core/__tests__/engine-context-policy.test.ts
bun run typecheck
bun test
保留限制:
实现边界:
LLMSummarizer 类:复用 DeepSeekClient,低温度(0.3),不带 tools。truncateMessages() 按 targetTokens 截断旧消息,保留最近消息。maxTokens 受 targetRatio 约束(50%),最小 256 tokens。LLMSummarizerOptions 配置:client、apiKey、baseUrl、model、temperature、timeoutMs。packages/core/__tests__/context-summarizer.test.ts:覆盖 LLM summarizer 成功、截断、错误、超时、空摘要、abort 和配置(11 个测试)。验收命令:
bun test packages/core/__tests__/context-summarizer.test.ts
bun run typecheck
bun test
保留限制:
实现边界:
TokenizerPool:
getDiagnostics(),暴露 healthy、pendingTasks、fallbackCount、timeoutCount、workerErrorCount、lastFallbackReason。fallback.tokenizer。fallbackEstimate([]) 的问题,改为按每个 task 的原始 messages fallback。SessionLoader:
read(sessionId): Promise<ChatMessage[]> 兼容行为。readDetailed(sessionId),区分 ok/missing/empty/corrupt/unreadable,并返回 skippedLines 和可选 error。AsyncSessionWriter:
getStatus(),返回 queueSize、droppedCount、flushing、lastError、lastFlushAt。StreamingToolExecutor:
parseToolCallArgs() 统一解析/修复工具参数。{};直接生成 error tool result,不进入 permission prompt,不执行工具。edit:
warning: "exact_match_failed_used_fuzzy"。McpHost / CLI:
loadConfig() 返回 { serverCount, connected, failed } summary。getStatus()。验收命令:
bun run typecheck
bun test packages/core/__tests__/tokenizer-pool.test.ts packages/core/__tests__/session.test.ts packages/core/__tests__/streaming-executor.test.ts packages/core/__tests__/engine-tools.test.ts
bun test packages/tools/__tests__/edit.test.ts packages/tools/__tests__/edit-integration.test.ts
bun test packages/mcp/__tests__/mcp-host.test.ts
保留限制:
edit strict mode 尚未引入;当前只显式返回 warning。chmod/unlink 失败的日志收尾仍见 TODO.md 的 FG-60-R。以下修复仍然影响当前行为,但不再保留重复的逐轮流水账:
[DONE] 前 finalize tool calls。-- 防选项注入。.bashrc,并清理 timeout timer。recommendedModel(deepseek-v4-flash)覆盖了用户为 Zen 选择的模型(如 mimo-v2.5-free),但 deepseek-v4-flash 在 Zen API 上不存在,Zen 返回 401 “Missing API key”。修复方式:loop.ts:74 模型覆盖只在 provider === "deepseek" 或未指定时生效,第三方 provider 不受影响。该事故也是 RM-20 删除 tier 自动覆盖的重要依据。DeepiPromptInput 使用 forwardRef 暴露 writeText 方法,供 autocomplete 回写文本。suppressHistory prop 在菜单打开时禁用 ↑↓ 历史导航,避免与 autocomplete 光标移动冲突。App.tsx 将 ref 和 suppressHistory 传递给 DeepiPromptInput,并在 CommandAutocomplete 的 onSelect 中通过 ref 写入文本。/status、/context 已重新加入 parseSlashCommand()、CommandRegistry 和 i18n 文案。CommandAutocomplete 行为恢复为:Enter 直接执行命令进入二级菜单或输出 status;Tab 只补全到输入框;Esc 关闭。DeepiPromptInput 支持外部 history、injectedText 和 suppressSubmit,避免 autocomplete 打开时输入框抢 Enter/Tab。/skill 二级菜单恢复为 52 个 skill 的可选择列表;Space 启用/禁用,Enter 插入 #skill-name 到输入框。/context 二级菜单恢复为真实 policy 菜单;支持 trim/compact、trigger/target 调整、当前用量显示和 Run now。AppendOnlyLog.replaceAll();为 loop/plugin 恢复后遗留类型缺口补齐类型。bun run typecheck 通过;bun test packages/tui 38/38 通过。bun test 按用户要求中断,不作为本轮验收结论。bridge.tsx full fallback 后同步更新 pendingInstructionCount,修复 P3-2 断言失败。P0-6 测试更新为验证 enqueueInstruction() 优先路由而非旧的 messageQueue 路径。loadSession() 新增 isSubmitting 保护,活跃 submit 时抛异常阻止切换。this.sessionId、this.ctx.log.clear()、this.toolExecutor.setSessionId()、this.logger.child({ sessionId })、this.rebindSessionWriter()。StreamingToolExecutor 新增 setSessionId(id) 方法。rebindSessionWriter() 工厂方法提取自构造函数,切换时复用。SessionLoader.validateSessionId(id):拒绝空字符串、路径遍历(../)、控制字符、斜杠反斜杠、.、..、超长 ID。SessionLoader.read() 使用 safePath() 验证,防止外部 ID 越出 session 目录。SessionLoader.list() 修复:messageCount 改为最后快照的消息数量(非快照记录数);排序使用最后记录时间戳(非首条记录)。engine.loadSession() 和 engine.recover() 在入口处验证 session ID。sessionByteUsage 内存 Map 跟踪每个 session 的已用字节数。maybePersistResult() 在写入前检查 used + contentBytes > quota,超额时返回 preview + warning 不写入。cleanupOldFiles():目录文件超过 maxFilesPerSession(默认 200)时删除最旧的文件。engine.ts 接通 ResultPersistenceConfig 到 StreamingToolExecutor 构造函数。resetSessionByteUsage() 和 getSessionByteUsage() 用于测试。ContextManager.buildMessages() 新增 truncateToBudget() 步骤:在 round-based 截断后,再次按 token 预算机械 fallback。estimateTokens()(已 mock 为确定性 fallback)估算 prefix + log + scratch 总量。contextWindow 时,从最旧的 user 轮开始移除,直到预算满足。prefix.messages)和 tool-call/tool-result 原子组(round 边界切割)。edit.ts 在 hash-anchored 替换前执行 firstIdx !== lastIdx 唯一性校验。old_string 时返回 "appears multiple times" 错误,要求提供更多上下文。fuzzy-edit.ts 已在 Pass 1 拒绝歧义(≥2 精确匹配返回 null),无需变动。hashAnchoredReplaceOnce should replace first occurrence 改为 should reject ambiguous old_string with multiple occurrences。countOccurrences() 工具函数。engine.ts 调用 hookManager.setErrorObserver() 注册 logger 回调。phase(before / after / loop_event)和错误详情,不记录敏感参数。repair.ts storm() 安全性storm() 改为使用 matchAll 匹配所有 KV 对,而非 match 只取第一个。partial = false(可证明安全);多 KV 时 partial = true。repairToolArguments() 对 storm 多 KV 结果标记 partial。streaming-executor.ts 检测 repaired.partial 时拒绝执行并报错。engine.ts interrupt() 和 emergency paths 中设置 state.lastSwitchTime。lastSwitchTime 保持 0 导致时间计算错误的问题。interface.ts 新增 ToolProgressUpdate 类型,ToolContext.reportProgress 回调。streaming-executor.ts 维护 progress buffer,在 executeToolCall 末尾 flush。shell-exec.ts 数据处理器调用 reportProgress 报告实时 stdout/stderr。loop.ts 产出 tool_progress 事件,仅转发不持久化。bridge.tsx 显示工具进度中间内容。session.ts 新增 validateSessionId() 和 safePath() 函数。list() 修复消息计数和排序(按 ts 降序)。engine.ts loadSession/recover 增加验证。streaming-executor.ts 新增 setSessionId() 方法。engine.ts switchAgent/setSessionId 触发 rebindSessionWriter + logger.child。isSubmitting guard 防止切换冲突。enqueueInstruction 路径。pendingInstructionCount。DeepiPromptInput forwardRef + suppressHistory 属性。App.tsx + CommandAutocomplete 通过 ref 控制。
ST2到ST4均为历史实现记录,相关运行时能力已废弃,待RM-20删除。
engine.ts 新增 currentTier 字段、resolveTierDecision() / setTier() / getTier()。loop.ts 根据 tier 覆盖 maxChainLength、enableReasoning、model、temperature。submit 时 budget 超标给出警告。interface.ts CoreEngine 新增 getTier? / setTier?。engine.ts submit() 首事件产出 strategy_notify。loop.ts 工具批处理后产出 strategy_estimate_refined。bridge.tsx 消费两个事件(目前空 break)。StatusBar.tsx 可选 tier 属性,App.tsx 从 engine 取值传入。strategy/recommender.ts:recommendTier() 函数,分析 cost/turn/context 模式。loop.ts 在工具批处理后调用 recommendTier,非 stay 时产出 tier_recommendation 事件。interface.ts LoopEventRole 新增 tier_recommendation。bridge.tsx 消费事件(空 break)。rejectAllPending() 辅助函数,消除 pending 遍历 + clear 重复代码。request() 发送前检查 proc、stdin、stdin.writable,不可写时立即 reject。stdin.write() callback 处理写入错误,失败时清除 timer + 立即 reject。disconnect() 即使 !_connected,只要 proc 存在也执行清理。initialize 失败(超时/非法响应)时清理 pending、重置 proc 和 _connected。SessionLoader.list() 读取 promptTokens/completionTokens(新格式),
inputTokens/outputTokens(旧格式)作为 fallback。fs.promises.open + fd.read(0, 8192) 代替 readFile(filePath)
(大文件不再整文件读取)。writer.end() 在未命中路径上改为 await new Promise(writer.end),
确保临时文件完全刷新后才进入 finally 清理。flushSharedBatch() 中每个共享工具收集 progress buffer,工具全部完成后 flush。maxChars * 2 时丢弃早期数据,
最终输出标注 droppped 计数。createProgressThrottle() 对 reportProgress 限频(200ms 时间窗口内去重)。AbortSignal listener 在 close/error/timeout 路径解除注册。reject(不混淆为非零退出码)。prefix 单独超过 window:抛异常 prefix alone exceeds window。scratch 单独超过 window:抛异常 scratch alone exceeds window。truncateToBudget 处理无 user messages 的极端情况(仅 assistant+tool 循环),
避免无限循环。getBudget() 方法返回 { prefixTokens, logTokens, scratchTokens, totalTokens, window }。maybePersistResult 首次 overflow 时扫描 .deepreef/results/<sessionId>/ 初始化用量。sessionInitialized 集合)。cleanupOldFiles 删除文件后同步减去内存计数(subtractByteUsage)。logger.warn 通路。AsyncSessionWriter 构造函数增加 RuntimeLogger,默认 noopRuntimeLogger。init() 成功后 debug log session.writer.ready。enqueue 序列化失败 debug log session.writer.serialize_error。evictIfNeeded queue overflow debug log session.writer.overflow。flushSoon append 失败 debug log session.writer.append_error。tsconfig.json 新增 @deepreef/core 和 @deepreef/tui 的 paths 映射。@deepreef/tools 补齐 exports、types 字段,新增 @deepreef/core 依赖。@deepreef/mcp 补齐 exports 条件导出,新增 @deepreef/core、@deepreef/tools 依赖。@deepreef/cli 新增 @deepreef/tools、@deepreef/mcp、@deepreef/tui 依赖。@deepreef/tui 补齐 exports、types 字段。packages/tools/src/index.ts 新增 safeStringify、hasBinaryEncoding、clearReadTracker 导出。packages/core/src/index.ts 新增 ToolProgressUpdate 类型导出。../../core/src/...、../../tools/src/...、../../mcp/src/... 相对路径 import 全部替换为包名 import(@deepreef/core、@deepreef/tools、@deepreef/mcp、@deepreef/tui)。packages/tools/src/index.ts 新增 createDefaultTools() 工厂函数,返回 29 个内置工具实例。packages/cli/src/tui.ts 改用 createDefaultTools() 循环注册,不再逐个 import + register。createListMcpToolsTool、createCallMcpToolTool 等)。packages/tools/src/grep.ts:spawnSync("rg"/"grep") → spawn 异步,支持 AbortSignal、15s 超时、500KB 输出上限。packages/tools/src/web-browser.ts:spawnSync(node, [runner]) → spawn 异步,支持 AbortSignal、可配置超时、5MB 输出上限。packages/tools/src/cron.ts:spawnSync("crontab") → spawn 异步,getCrontab() 和 setCrontab() 均支持 AbortSignal、5s 超时。packages/core/src/executor-helpers.ts,提取 4 个纯函数:
evaluatePermission():权限决策逻辑(allow/deny/ask)createSettleLedger():工具调用结算账本(settled set + settle closure)createProgressQueue():有界进度缓冲队列(push/flush/length)applyResultPersistence():结果溢出持久化适配器streaming-executor.ts 改为调用上述 helper,内部逻辑不变。packages/core/src/loop-helpers.ts,提取 4 个纯函数:
normalizeToolCallId() + resetToolCallSeq():工具调用 ID 规范化createDuplicateDetector():重复工具调用检测器(3+ 次相同 tool+args 告警)evaluateModeSwitchForTurn():思维模式切换信号构造与评估injectPendingInstruction():待注入指令安全点 helperloop.ts 改为调用上述 helper,主控制流不变。packages/tui/src/commands.ts,提取 slash command 纯逻辑:
parseSlashCommand():解析 /exit、/bye、/help、/model、/sessions、/skill、/agent、/thinking、/langvalidateThinkingMode() + getThinkingModes():思考模式校验toggleAgent():Build / Plan Agent 切换buildHelpText():帮助文本构造formatSkillList():Skill 列表格式化和 malformed JSON fallbackpackages/tui/src/App.tsx 的 handleSubmit() 改用上述 helper;React state、异步 Skill 加载、退出和 bridge submit 行为保持在组件内。packages/tui/__tests__/commands.test.ts,覆盖命令别名、未知输入、thinking 校验、Agent 切换、help 文本、Skill 截断和 malformed fallback。6 pass / 0 fail;typecheck 通过;全量 780 pass / 0 fail,共 55 个测试文件。packages/tools/src/platform/:
capabilities.ts:集中式平台能力模型shell-backend.ts:Bash / pwsh / powershell 探测、缓存、环境变量覆盖和诊断事件process-tree.ts:POSIX process group 与 Windows taskkill.exe /T 回收入口monitor-backend.ts:memory、process、disk 平台采样入口scheduler-backend.ts、notification-backend.ts:平台 backend 选择契约bash 工具保持历史名称,内部改为平台 shell;system prompt 增加 shell backend 信息。glob 使用 relative() + isAbsolute() 做目录边界判断;Browser runner 使用 fileURLToPath()。chmod(0600) 在 Windows 上降级为 best-effort 文件写入。os,process/disk 不再使用同步 shell pipeline。787 pass / 0 fail,共 56 个测试文件。packages/tools/src/worktree.ts 的 runGit() 改用 terminateProcessTree() 终止子进程,设置 detached: platform !== "win32"。packages/mcp/src/client.ts 的 connect() 和 disconnect() 改用 terminateProcessTree() 替代直接 proc.kill("SIGTERM"/"SIGKILL")。packages/tools/src/index.ts 新增 terminateProcessTree 导出供 MCP 包消费。packages/tools/src/platform/scheduler-backend.ts 大幅扩展:新增 listJobs()、createJob()、deleteJob() 统一入口,内部包含 crontab 和 schtasks 两条实现路径:
getCrontab/setCrontab/parseCronJobs/removeCronJob 逻辑。listSchTasksJobs()、createSchTaskJob()、deleteSchTaskJob(),任务名前加 DEEPREEF_TASK_PREFIX。cronToSchTaskSchedule():支持 MINUTE、HOURLY、DAILY、WEEKLY、MONTHLY 子集映射;不支持表达式返回明确错误。packages/tools/src/cron.ts 完全重写为调用 scheduler-backend.ts 统一入口,不再直接操作 crontab。getSchedulerBackend() 和 normalizePlatform() 保持导出。packages/tools/src/push-notification.ts 完全重写:
execFile("notify-send", args),不拼接 shell 字符串。execFile("osascript", ...),参数经过 osAEscape() 转义。spawn("powershell.exe", ...) 通过 WScript.Shell.Popup 弹窗,无需额外依赖。process.stdout.write("\x07"))。{ sent, method, fallbackReason? } 结构化结果。packages/tools/src/lsp-client.ts 的 runLspRequest() 改用 terminateProcessTree() 替代直接 child.kill():
spawn() 增加 detached: platform !== "win32"。withTimeout 回调和 finally 清理路径统一使用 terminateProcessTree(child, true, platform)。packages/tui/src/ModelPicker.tsx 的 tryReadClipboard() 新增 Windows 剪贴板读取:
powershell.exe -NoProfile -NonInteractive -Command Get-Clipboard。darwin → win32 → linux 优先级探测,各平台互斥。'child_process' → 'node:child_process')。clearTerminal.ts、termio/osc.ts、terminal.ts、use-terminal-title.ts、App.tsx 等),Deepreef TUI 无需重复实现。buildSystemPrompt() 新增 options 参数({ osPlatform?, shellBackend? }),允许调用方传入平台信息。packages/cli/src/tui.ts 在设置 system prompt 前调用 normalizePlatform() + resolveShellBackend() 获取平台能力,传给 buildSystemPrompt()。process.platform 和 DEEPREEF_SHELL 环境变量。.github/workflows/ci.yml:GitHub Actions matrix(ubuntu-latest、macos-latest、windows-latest)。fail-fast: false 确保一个平台失败不影响其他平台结果。master push / pull request,也支持手工 workflow_dispatch。fetch(vi.spyOn(globalThis, "fetch"))替代真实 Google 网络调用,返回可控 HTML fixture,消除外部依赖超时。afterEach 增加 Promise.race 3s 超时保护,防止 server.stop() 挂起阻塞后续测试。dual-agent-runtime.test.ts:修复事件类型(text_delta/done 替代 delta/final)da-r7-e2e.test.ts:18 个端到端测试(DA-R0-R7 全覆盖)wf-00-integration-baseline.test.ts:19 个测试(全部通过)runtime.ts 重写,AgentRuntime 包装 ReasonixEngine,注册工具,委托 submit()dual-runtime.ts 重写,移除重复 workflow 状态,接受 workerTools/supervisorToolscoordinator.ts 重写,新增 runWorkflow() async generator,setRuntime(),setQuestionService()submit(text, isQueueResubmit, role?) 按角色路由消息/run <goal> 启动 Workflow,设置 workflowState 并提交给 Supervisor/talk [worker|supervisor] 切换输入目标角色dual-session、workflow-checkpoint、advice-historysubmit(userInput, agentConfig?, role?) 按角色路由到对应 Agent 配置以下任务的测试文件已创建(92 个测试全部通过),生产代码部分完成:
wf-10-role-runtime-convergence.test.ts:24 个测试(已重写匹配新 API)wf-20-coordinator-executor.test.ts:10 个测试wf-30-question-fusion.test.ts:7 个测试wf-40-ask-user-loop.test.ts:9 个测试wf-50-tui-integration.test.ts:10 个测试wf-60-session-recovery.test.ts:11 个测试wf-70-migration-gate.test.ts:10 个测试已完成的生产代码修改:
待完成的生产代码修改:
ADVICE.md 原先用于复核 Code Clean 报告、保存专项设计和安排阶段路线。历史可执行内容已经拆分完成:
| 原路线 | 归档状态 | 对应专项 |
|---|---|---|
| Phase 0:类型检查和回归门禁 | 已建立 | 每个任务执行 bun run typecheck、bun test、git diff --check;三平台 scaffold 见 OS-17 |
| Phase 1:生命周期和数据正确性 | 已完成原规划 | CL-10、CL-11、CL-12 |
| Phase 2:Tool Progress 和 Bash 有界输出 | 已完成 | P5.5、CL-20、CL-21 |
| Phase 3:上下文和持久化边界 | 已完成 | CL-30、CL-31、CL-32 |
| Phase 4:Windows 与 macOS 代码适配 | 代码层已完成 | OS-00、OS-10、OS-11、OS-12、OS-13、OS-14、OS-15、OS-16、OS-17 |
| Phase 5:渐进式边界清理 | 已完成 | CL-40、CL-41、CL-42 |
| Phase 6:受测试保护的提取 | 已完成 | CL-50、CL-51、CL-52 |
TEST-STABILITY-01 和 OS-17-R 已完成并记录在本文。仍未完成的原生平台人工验收见 TODO.md。
自 2026-06-11 起,ADVICE.md 已重构为审核 Agent 面向开发 Agent 的审核意见与下一步动作入口。技术方案、任务、完成事实分别以 Deepreef后续开发计划.md、TODO.md、DONE.md 为准。
| 阶段 | 状态 | 说明 |
|---|---|---|
| LSP-10:配置、语言识别、返回格式 | ✅ 已完成 | config.ts、language.ts、normalize.ts、lsp.ts 升级 |
| LSP-20:协议层和长驻 Client | ✅ 已完成 | vscode-jsonrpc 协议层、LspClient 类、11 个测试 |
| LSP-30:Manager 和文档同步 | ✅ 已完成 | LspManager 类、文档同步、12 个测试 |
| LSP-40:完整动作集 | ✅ 已完成 | 14 个 actions + 5 个别名、28 个测试 |
| LSP-50:真实语言服务器 smoke | ✅ 已完成 | TypeScript/Python/Go/Rust smoke tests、14 个测试 |
| LSP-60:工具链集成和可观测性 | ✅ 已完成 | LspLogger、9 种事件、12 个测试、@deepreef/core 导出 RuntimeLogger |
ImmutablePrefix + AppendOnlyLog + VolatileScratch 三区域上下文布局。ReasonixEngine.submit() 和 runLoop() 的 AsyncGenerator<LoopEvent> 外部语义。tool_call_id 最多写入一个 tool result。bash 作为兼容名称,内部选择当前平台 shell backend。packages/tools/src/platform/,不要散落平台分支。| 阶段 | 状态 | 说明 |
|---|---|---|
| PLG-10:配置与 spec 解析 | ✅ 已完成 | packages/plugin、config.ts、loader.ts、18 个测试 |
| PLG-20:loader 与 v1 server plugin shape | ✅ 已完成 | server() 调用、hooks 验证、21 个测试 |
| PLG-30:tool adapter | ✅ 已完成 | tool-adapter.ts、schema 转换、9 个测试 |
| PLG-40:hook adapter | ✅ 已完成 | hook-adapter.ts、PluginHookRegistry、10 个测试 |
| PLG-50:CLI 集成和生命周期 | ✅ 已完成 | runtime.ts、PluginRuntime、7 个测试 |
| PLG-60:文档和验收 | ✅ 已完成 | README、examples、历史验收记录 |
| 阶段 | 状态 | 说明 |
|---|---|---|
| STAT-10:Core 状态快照 | ✅ 已完成 | EngineStatusSnapshot、getStatusSnapshot()、8 个测试 |
| STAT-20:Slash command 接入 | ✅ 已完成 | /status 命令、format.ts、6 个测试 |
| STAT-30:Codex 风格格式化 | ✅ 已完成 | format.ts 增强、Unicode/ASCII、16 个测试 |
| STAT-40:文档和验收 | ✅ 已完成 | README、历史验收记录、G0-04 |
| 阶段 | 状态 | 说明 |
|---|---|---|
CTX-UI:TUI /context 菜单 |
✅ 已完成 | ContextModal.tsx 居中菜单,支持 strategy、triggerRatio、targetRatio、当前用量和 Run now |
保留限制:
/context 的代码链路已完成;完整长会话人工验收按 TODO.md 的 CTX-70 执行。bun test 未作为最新结论记录,因用户要求中断。| 阶段 | 状态 | 说明 |
|---|---|---|
| AGENT-90 | ✅ 已完成 | Plan/Build 主状态 + 临时 Subagent 第一阶段 |
实现边界:
packages/core/src/main-mode.ts:MainMode 类型("plan" | "build"),MainModeDefinition 接口包含 name / label / systemPrompt / toolNames / permissionProfile。agent.ts 收敛为主状态定义,AGENTS 与 MAIN_MODES 同步,保持 getAgent() / agentConfigFor() 兼容。Plan 的 permissionProfile: "readonly",toolNames 仅包含读取工具;Build 的 permissionProfile: "build",包含完整工具集。switchAgent() 切换,Plan 写/exec 工具在 permission 层 fail-closed。packages/core/src/subagent/,包含 5 个模块:| 模块 | 职责 |
|---|---|
types.ts |
SubagentDefinition、SubagentRun、SubagentRunOptions、SubagentRunResult 等类型 |
definition.ts |
三个内置子代理:general-purpose / Explore / Plan |
registry.ts |
SubagentRegistry:注册、解析、工具过滤 |
permission.ts |
四级权限:readonly / denyExec / acceptEdits / bubble |
run.ts |
SubagentRunner:基于子定义创建 child engine 并执行 |
disallowedTools 包含 AgentTool,防止嵌套调用。description、prompt、subagent_type(Explore / Plan / general-purpose)。task → prompt,agent_type → subagent_type。ctx.spawnSubagent() 新路径,回退 ctx.delegateTask() 旧路径。{ status, id, subagent_type, description, result, files, usage, warnings }。engine.spawnSubagent(options) 创建子 ReasonixEngine,共享 API client。AgentTool),按子代理定义过滤 toolNames / disallowedTools。permissionMode 在 permission 层添加 deny rule(fail-closed)。systemPrompt,使用独立 agentConfig 运行。finally 中始终 shutdown。packages/core/__tests__/subagent-registry.test.ts(9 个测试)packages/core/__tests__/subagent-permission.test.ts(16 个测试)packages/core/__tests__/subagent-run.test.ts(9 个测试)agent.test.ts 增加 getMainMode 测试。workflow-agent-send-lsp.test.ts 增加新参数测试 + spawnSubagent 测试。验收命令:
bun test packages/core/__tests__/agent.test.ts
bun test packages/core/__tests__/subagent-registry.test.ts
bun test packages/core/__tests__/subagent-permission.test.ts
bun test packages/core/__tests__/subagent-run.test.ts
bun test packages/tools/__tests__/workflow-agent-send-lsp.test.ts
bun run typecheck
保留限制:
run_in_background 为第三阶段,未实现。/agent 命令别名 /mode 和 Plan→Build 切换确认尚未更新文案。| 阶段 | 状态 | 说明 |
|---|---|---|
| 用户显式选择免费 Provider | 当前有效 | Kilo、NVIDIA NIM,以及其他用户手动配置的免费 API |
| OpenAI-compatible 本地 Provider | 当前有效 | 用户显式配置 Base URL 和模型 |
free-auto 虚拟 Provider 与自动路由 |
历史已实现,待删除 | 由 RM-10 删除,不迁移到新架构 |
本章混合记录了仍有效的免费 Provider 支持和
free-auto的历史实现。后续开发只能复用显式 Provider 与通用ChatClient抽象,不能继续依赖自动候选选择、sticky、惩罚、cooldown 或跨模型 failover。
packages/core/src/config.ts:新增 4 个 provider:
api.kilo.ai,2 个免费模型(Nemotron-3 Super 120B / Laguna XS 2),keyless: truevirtual: true,baseUrl: "",无实际 API,由内部路由处理keyless: true,自定义 URL 和模型integrate.api.nvidia.com/v1,6 个模型(Nemotron-3 Super 120B / Nano 30B / Nano Omni / Llama 70B / Llama 49B / Ultra 253B),requiresKey: trueProviderInfo 新增 keyless?: boolean 和 virtual?: boolean 字段。loadConfig() 对 keyless provider 跳过 API Key 加载;对 virtual provider 不设 baseUrl。ModelPicker.tsx:新增 kilo、free-auto、openai-compatible、nvidia 到提供商选择列表;keyless provider 跳过 Key 输入步骤。| 文件 | 定位 |
|---|---|
catalog.ts |
候选列表:LLM7 Codestral (priority 1)、LLM7 Qwen3 235B (priority 2)、LLM7 Mistral (priority 3)、Kilo Nemotron (priority 10) |
router.ts |
429 惩罚衰减、cooldown 管理、retryable 错误分类、task 分类策略 |
client.ts |
FreeAutoClient 实现 ChatClient 接口,串行 failover 路由 |
路由逻辑:
status 类型 LoopEvent 向 TUI 上报 route 状态(provider/model/reason/attempt)。packages/core/src/interface.ts:新增 ChatClient 接口,定义 chatCompletionsStream() 方法。DeepSeekClient 和 FreeAutoClient 分别实现该接口。engine.ts 的 client 类型从 DeepSeekClient 改为 ChatClient;resolveClient() 根据 provider 选择实现。loop.ts 的 LoopOptions.client 类型从 DeepSeekClient 改为 ChatClient。loop.ts 新增 status 事件传递。| 改进 | 说明 |
|---|---|
| Keyless 支持 | keyless 标志跳过 Authorization header |
| Per-request timeout | 通过 AbortController + combineAbortSignals 实现 |
| SSE stall 检测 | Promise.race 替代旧 watchdog,timeout 抛出 Error |
| 异常 EOF 检测 | stream 结束无 [DONE] 且无 finish_reason 时 yield error |
max_tokens vs max_completion_tokens |
通过 useMaxCompletionTokens 区分 Kilo/LLM7(用 max_tokens)和 DeepSeek(用 max_completion_tokens) |
bridge.tsx:
routedModel / routedModelDetail 状态,处理 free_auto_route 事件。effectiveThinkingMode 状态,处理 thinking_mode_switch 事件。reasoningActive 状态。StatusBar.tsx:
auto:on / auto:open / auto:high。TONE.warn 高亮。WelcomeScreen.tsx:引入 figlet ASCII 大标题。
routedModel、free_auto_route、Auto thinking 状态及相应展示属于RM-10/RM-20删除范围;普通 Provider/模型展示保留。
WebFetch:
turndown(HTML→Markdown) + htmlparser2(HTML→plain text)。format 参数(markdown / text / html),默认 markdown。User-Agent header。turndown、htmlparser2、@types/turndown。WebSearch:
livecrawl、type。OPENCODE_WEBSEARCH_PROVIDER 环境变量,或自动检测 EXA_API_KEY / PARALLEL_API_KEY。| 文件 | 测试数 | 说明 |
|---|---|---|
free-auto-router.test.ts |
新文件 14 个 | 429 惩罚、cooldown、health tracking、retryable 分类、task 分类 |
config.test.ts |
新增 7 个 | Kilo/Free-Auto/OpenAI-Comaptible/NVIDIA 配置验证 + loadConfig keyless 行为 |
LLM7 provider 已删除: llm7 从 SUPPORTED_PROVIDERS、MODEL_PRICING、ModelPicker 及所有 provider 列表中移除。
NVIDIA NIM 作为替代免费 provider 加入。Free Auto 路由候选列表相应更新,不再含 LLM7 模型。
LLM7 匿名用户有 8000 字符总数限制(所有 messages 的 content 长度之和)。system prompt 中嵌入了 frontend-design 等技能的完整文档(>5000 字符),导致第一句话就超限,所有模型返回 HTTP 400:
{"detail":"Total content length of messages exceeds limit of 8000 characters for anonymous users. Get a free token at https://token.llm7.io to get access to higher limits."}
Kilo 的 nemotron-3-nano-omni-reasoning:free 本身不是有效模型 ID,返回 400 “not a valid model ID”。之前 thinking 参数也被怀疑,但实测 LLM7 所有模型通过 curl 均正常返回 200(streaming、tools、中文、长 max_tokens 均无误)。
| 修复 | 文件 | 说明 |
|---|---|---|
| Kilo 无效模型清理 | config.ts:80-84 |
移除 nemotron-3-nano-omni-reasoning:free。Kilo free tier 确认可用模型:nvidia/nemotron-3-super-120b-a12b:free、poolside/laguna-xs.2:free |
| LLM7 精简系统提示词 | engine.ts:325-338 |
buildActiveSkillsPrompt(brief=true) — LLM7/Free Auto 时只保留技能名称和描述,不嵌入完整 content,节省 >5000 字符 |
| 8000 字符友好错误提示 | loop.ts:286 |
检测响应体中包含 "8000 characters" + "token.llm7" 时,显示 actionable 的提示信息并引导用户申请 token |
| 错误元数据增强 | loop.ts:286、client.ts:172-177 |
streamError.metadata.responseBody 附带原始错误详情;api.request.http_error 日志中记录请求体前 5000 字符和响应体前 2000 字符 |
| Free Auto 跨 submit sticky | free-auto/client.ts + engine.ts |
移除 engine.ts 中每次 submit 调用的 resetSticky(),改为 5 分钟时间过期;成功候选持续复用 |
| 非 DeepSeek 提供商移除 thinking 参数 | loop.ts:122 |
supportsThinking 仅对 deepseek/zen/mimo 为 true |
验收命令:
bun run typecheck
bun test packages/core/__tests__/config.test.ts
用户可以使用 TUI Model Picker、环境变量或 last-config.json 连接任意 OpenAI 兼容的本地推理服务。
支持范围: vLLM、Ollama、llama.cpp、LM Studio、LocalAI、Text Generation Web UI 等。
使用方式:
/model → 选中 OpenAI Compatible (Local) → 输入 Base URL(默认 http://localhost:8000/v1)→ Enter → 输入模型名 → Enter 确认OPENAI_COMPATIBLE_BASE_URL、OPENAI_COMPATIBLE_MODELlast-config.json:baseUrl 和 model 字段自动持久化关键特性:
keyless: true:跳过 API Key 步骤models: []:不限制模型名,用户自由输入loadConfig() 中跳过 normalizeModelForProvider,保留用户输入的原样模型名loop.ts:标记为 isKeyless 和 useMaxTokens,不发送 Authorization 头,使用 max_tokens(非 max_completion_tokens)MODEL_PRICING 无匹配项 → calculateCost() 返回 0)环境变量命名规范: 包含连字符的 provider ID(如 openai-compatible)在 env var 中自动转换为下划线:OPENAI_COMPATIBLE_*。
验收: bun run typecheck 通过;27 个 config 测试全部通过(含 2 个 openai-compatible 专项测试)。
插件作者可以使用 Zod 4 schema 声明工具参数,Deepreef 自动生成 JSON Schema 发给 LLM,并在执行前验证模型返回的参数。
| 文件 | 定位 |
|---|---|
packages/plugin/src/define-tool.ts |
definePluginTool() helper — 接受 { description, inputSchema, execute },返回带 deepreefTool 元数据的函数 |
packages/plugin/src/schema-adapter.ts |
StandardSchemaLike 最小契约类型、convertSchemaToJsonSpec()、validateSchemaArgs() |
packages/plugin/src/schemas.ts |
PluginSpecSchema / PluginConfigSchema(Zod schema 定义) |
packages/mcp/src/schemas.ts |
McpConfigSchema / McpAuthStoreSchema / McpAuthEntrySchema |
packages/core/src/schemas/json.ts |
parseJsonConfig() — 泛型 JSON 文件加载+验证 helper |
packages/core/src/schemas/config.ts |
LastConfigSchema — last-config.json 验证 |
packages/tui/src/settings-schema.ts |
TuiSettingsSchema / LangConfigSchema |
packages/plugin/__tests__/zod-tool.test.ts |
23 个 Zod 集成测试 |
Standard Schema 优先:schema-adapter.ts 使用 Standard Schema V1 形状(~standard.validate)作为运行时契约,而非硬编码 ZodType。Zod 4 原生支持,未来其他库也可通过同一接口接入。
JSON Schema 生成:
schema["~standard"].jsonSchema.input({ target: "draft-07" })z.toJSONSchema(schema, { io: "input", target: "draft-07", unrepresentable: "any" })_def 访问(zodType()、zodEnumValues()、zodShape()、zodInnerType()、convertZodToJsonSchema())执行前验证:executePluginTool → schema-aware tool 的 execute wrapper 调用 validateSchemaArgs(),失败时返回结构化错误(invalid_schema + issues),不调用插件业务函数。
| 配置边界 | Schema | 策略 |
|---|---|---|
plugins.json |
PluginConfigSchema |
定义了 schema,未替换现有解析器(保留原容错行为) |
mcp.json |
McpConfigSchema |
loadConfig 中接入验证,失败时降级使用 raw parsed |
mcp-auth.json |
McpAuthStoreSchema |
readAuthStore 中接入验证 |
last-config.json |
LastConfigSchema |
loadLastConfig 中接入验证 |
ui-settings.json |
TuiSettingsSchema |
loadTuiSettings 中接入验证 |
lang.json |
LangConfigSchema |
loadLang 中接入验证 |
PluginHooks 仍为 Record<string, Function>,loader 无需修改definePluginTool() 返回可调用函数,通过 deepreefTool 属性携带元数据packages/plugin/package.json:zod: "4.4.3"packages/core/package.json:zod: "4.4.3"packages/mcp/package.json:zod: "4.4.3"packages/tui/package.json:zod: "4.4.3"bun run typecheck # 通过
bun test packages/plugin/ # 64 pass, 0 fail
bun test # 1134 pass, 5 pre-existing fail (mode-selector + bridge, 与本次无关)
DONE.md 主体只记录已落地事实;当前有效性必须明确标为“当前有效”“历史已实现,待删除”或“部分完成”。TODO.md;DONE 中只能记录已落地范围、验收事实和保留限制,不能维护第二套待办列表。bun run typecheck 和 bun test。TODO.md 对应章节。依据 docs/ecc-manifest-content-pack-review-fixes.md 审查文档完成全部 P0 和关键 P1 修复。
代码架构层面的修复(类型系统、解析器、安全策略、接线管道)已全部完成并通过单元测试验证,
但缺少最后一步生产接线:PluginRuntime.init() 从 .deepreef/plugins.json 读取配置,
当前未创建该文件,CLI 启动时不会实际加载 ECC 内容包。
Resolver 和管线逻辑已验证可正确运行(185 个测试全部通过,含 ECC smoke 验证), 但缺少”用真实配置启动 CLI → Skill 搜索 → Agent 注册 → Rules 注入 → Hooks 注册”的完整端到端验收。
| 编号 | 优先级 | 内容 | 状态 |
|---|---|---|---|
| P0-1 | P0 | Profile/Module/Component 类型定义和解析 | ✅ 已完成 |
| P0-2 | P0 | 禁用默认目录发现绕过选择性安装 | ✅ 已完成 |
| P0-3 | P0 | 路径边界安全(path-security.ts) |
✅ 已完成 |
| P0-4 | P0 | MCP Manifest 解析与安全选项 | ✅ 已完成 |
| P0-5 | P0 | Hooks 接入与默认安全(ecc-hook-adapter.ts) |
✅ 已完成 |
| P1-6 | P1 | Rules mode(off/system/skill)+ 来源标注 | ✅ 已完成 |
| P1-7 | P1 | Commands 转 Skills 接线 | ✅ 已完成 |
| P1-8 | P1 | Skills 来源/命名空间冲突解决 | ✅ 已完成 |
| P1-9 | P1 | TUI 状态展示(内容包/资产/诊断) | ✅ 已完成 |
修改文件:
| 文件 | 变更 |
|---|---|
packages/plugin/src/content-pack/types.ts |
InstallProfiles 从数组改为对象映射;InstallModule.paths 从 Record 改为 string[];新增 family 字段;ResolvedContentPack 新增 options 字段 |
packages/plugin/src/content-pack/index.ts |
导出更新 |
packages/plugin/src/content-pack/parser.ts |
移除无条件默认目录发现;修复 mcpServers 解析(支持字符串路径、数组、内联对象) |
packages/plugin/src/content-pack/resolver.ts |
全面重写:ECC 模式 vs 标准模式分离;profile 对象映射查找;module kind 分类资产解析;targetMode 严格/兼容/忽略实现;未知 profile/module 诊断 |
packages/plugin/src/content-pack/rules-compiler.ts |
稳定路径排序;来源标注 header |
packages/plugin/src/runtime.ts |
MCP 安全选项全面实施(enabled/allowStdio/allowHttp/allowNpx/allowPlaceholderEnv/servers 白名单);Rules mode 处理;Command skills 接线;ECC hooks 注册 |
packages/plugin/src/index.ts |
导出更新 |
packages/cli/src/tui.ts |
传入 hookManager 到 PluginRuntime;Command skills 加载;状态信息传递 |
packages/tui/src/App.tsx |
AppProps 扩展(contentPackCount/assetCounts/diagnosticCounts) |
packages/tui/src/WelcomeScreen.tsx |
WelcomeScreenProps 扩展;组件状态面板新增内容包、代理、规则、MCP、诊断显示 |
packages/tools/src/skill-loader.ts |
loadSkillsDirs() 支持可选 source 参数 |
packages/tools/src/skills/index.ts |
命名空间冲突解决:外部重名 skill 使用 <pluginId>:<name> |
新增文件:
| 文件 | 定位 |
|---|---|
packages/plugin/src/content-pack/path-security.ts |
validateAssetPath() 统一路径边界安全 |
packages/plugin/src/content-pack/ecc-hook-adapter.ts |
ECC command hooks → HookManager 桥接适配器 |
新增测试文件:
| 文件 | 测试数 |
|---|---|
packages/plugin/__tests__/ecc-content-pack.test.ts |
8 |
packages/plugin/__tests__/content-pack-resolver.test.ts |
8 |
packages/plugin/__tests__/content-pack-path-security.test.ts |
5 |
packages/plugin/__tests__/content-pack-mcp.test.ts |
3 |
packages/plugin/__tests__/content-pack-hooks.test.ts |
3 |
packages/plugin/__tests__/content-pack-rules.test.ts |
3 |
packages/plugin/__tests__/content-pack-commands.test.ts |
3 |
packages/plugin/__tests__/content-pack-discovery.test.ts |
4 |
packages/plugin/__tests__/content-pack-runtime-integration.test.ts |
8 |
minimal: modules=5, agents=64, skills=21, rules=1, commands=0, hooks=0, mcp=0
developer: modules=9, agents=64, skills=78, rules=1, commands=0, hooks=1, mcp=0
full: modules=23, agents=64, skills=196, rules=1, commands=0, hooks=1, mcp=0
验证结果:
minimal < developer < full module 数量递增 ✅bun run typecheck # packages/memory 因缺 Zod 依赖存在预置错误,其余包通过
bun test # 185 pass, 0 fail
覆盖范围:
| 问题 | 修复 |
|---|---|
| ECC Skills 无法使用 | getSkillDirs() 改为返回父目录而非单个 skill 目录 |
| Commands 转 Skills 未接线 | 添加 preloadedSkills 管道,createDefaultTools 传参 |
| Hook allowCommandHooks 未检查 | 增加 allowCommandHooks 显式启用检查 |
| Hook allowlist 比较错误 | 未配置 allowlist 时默认阻止所有 hooks |
| Hook 超时不杀子进程 | child.kill('SIGTERM') + SIGKILL 回退 |
| Lifecycle hooks 重复执行 | 追踪已执行 phase,每 lifecycle 只触发一次 |
| 路径安全测试被 skip | ../ traversal 测试启用;新增 symlink escape 测试 |
| DONE.md typecheck 状态不准确 | 标注 packages/memory 预置错误 |
要将 ECC 真正接入生产管线,还需:
.deepreef/plugins.json,配置 ECC content-pack 条目:
[
{
"spec": "/vol4/Agent/ECC",
"options": {
"type": "content-pack",
"profile": "developer",
"target": "deepreef",
"targetMode": "compatible",
"hooks": { "enabled": false },
"mcp": { "enabled": false }
}
}
]
tdd-workflow 可加载)、profile 外 skill 不可加载minimal / developer / full 三个 profile 全部走通| 问题 | 修复 |
|---|---|
Selective install 被 getSkillDirs() 绕过 |
getSkillDirs() 返回 [];新增 loadSkillDefs() 为每个选中 skill 直接读取 SKILL.md,通过 preloadedSkills 接入 Skill 工具。只加载选中 module 的 skills,不再通过父目录加载全集 |
| 子资产 symlink 逃逸未阻止 | discoverAgentFiles/discoverRuleFiles/discoverCommandFiles/discoverHookFiles/discoverPlatformAssets 中所有 readdirSync 发现的文件均调用 validateAssetPath() |
| Hook allowlist 按 command 不按 ID | BridgedHook 新增 id 字段;parseEccHooks 从 manifest 的 hook.id 生成;allowlist 过滤改为 h.id |
| Lifecycle hooks 全部一起执行 | onLoopEvent 根据 event.type 分发到对应 phase;onStartup/onShutdown/onGenerationComplete 仅在匹配事件时触发且各只执行一次 |
| Hook 超时不终止后代进程 | spawn 使用 detached: true;超时通过 process.kill(-pid) 杀进程组,1000ms 后 SIGKILL 回退 |
| Skill source 未传入生产调用 | ECC skills 通过 loadSkillDefs() 直接构造带 source 的 SkillDef,不经过 loadSkillsDirs |
| 问题 | 修复 |
|---|---|
Hook ID 解析为内层 hook.id 而非外层 entry.id |
parseEccHooks 改为读取 entry.id,生成正确 ID 如 ecc:pre:bash:dispatcher |
复合 matcher(Edit\|Write 等)等值匹配失败 |
新增 matchToolMatcher(),按 \| 拆分 matcher,匹配任一子项即触发 |
| ECC hooks dispose 后不注销 | registerEccHooks() 存储 adapter 引用;dispose() 调用 hookManager.removeHooks() + clearEccHookState() |
| 嵌套 MCP 路径未验证 | discoverPlatformAssets 中嵌套 plugin.json 的 mcpServers 引用增加 validateAssetPath() |
Lifecycle hooks 按 event.role 分发 |
onLoopEvent 改为检测 event.role(Deepreef 实际字段),"done" → onGenerationComplete |
Hook 命令缺少 CLAUDE_PLUGIN_ROOT |
executeHookCommandSafe() 新增 pluginRoot 参数,env 中设置 CLAUDE_PLUGIN_ROOT=cp.rootDir + HOME |
bun run typecheck # 除 packages/memory 预置错误外全部通过
bun test # 185 pass, 0 fail
entry.id 正确解析(ecc:pre:bash:dispatcher 等) ✅Edit|Write 等)按 | 拆分匹配 ✅CLAUDE_PLUGIN_ROOT 已注入 hook 运行环境 ✅SessionStart/SessionEnd 在生产中不会触发,这是 Deepreef 引擎行为限制(loop.ts 只产出 role: "done" 事件,未发出 startup/shutdown LoopEvent),不是适配器代码缺陷。Stop hook(对应 role: "done")可正常执行。.deepreef/plugins.json 和完整 CLI 验收)packages/memory/ 因依赖 zod 模块缺失存在 typecheck 错误(预置问题,非本修复引入)| 子项 | 状态 | 说明 |
|---|---|---|
packages/memory/ 目录创建 |
✅ 已完成 | package.json、tsconfig.json、src/ 骨架 |
| 64 个 function 模块复制 | ✅ 已完成 | 来自 /vol4/Agent/agentmemory/src/functions/ |
| 12 个 state 文件复制 | ✅ 已完成 | 搜索索引、向量索引、schema 等 |
| providers / prompts / eval / health / viewer / mcp 复制 | ✅ 已完成 | 全部源自 agentmemory |
iii-sdk 导入替换 |
✅ 已完成 | 全部替换为 ../runtime/index.js |
MemoryRuntimeSdk 实现 |
✅ 已完成 | 进程内 ISdk,registerFunction() / trigger() / registerTrigger() |
MemoryStore 文件型 KV |
✅ 已完成 | ~/.deepreef/memory/state/<scope>/<key>.json |
MemoryService 完整初始化 |
✅ 已完成 | 57 个 function 注册 + 定时器管道 |
DeepreefMemoryBridge |
✅ 已完成 | Session/tool 生命周期 hooks |
config.ts 路径迁移 |
✅ 已完成 | .agentmemory → .deepreef/memory |
| 独立 typecheck 通过 | ✅ 已完成 | 0 个 TSC 错误 |
| LICENSE / NOTICE 文件 | ✅ 已完成 | LICENSE.agentmemory(Apache-2.0)、NOTICE.md(上游 commit 749c280) |
| 测试文件复制 | ✅ 已完成 | 129 个 test 文件从 agentmemory 复制 |
| 独立测试通过 | ⏳ 部分(728/1186 pass) | 458 fail 依赖 iii-engine 环境,需 Phase B 适配 |
MemoryRuntimeSdk 替代 iii-sdk:trigger() 直接调用本地 Map<string, FunctionHandler>,不依赖外部 iii-engine 二进制MemoryStore 替代 iii-KV:文件型 JSON store,每个 key 独立文件,原子写入,保留 scope/key schemaDeepreefMemoryBridge 替代独立 MCP/REST:通过 Deepreef 的 HookManager 监听 session/tool/loop 事件,不走 localhost HTTPmem::remember、mem::search、mem::context 等 57 个函数保持原有 handler 实现MemoryRuntimeSdk.trigger() 现支持 <A, B> 泛型,与原始 iii-sdk 签名兼容health/monitor.ts 中 sdk.on("connection_state", ...) 已移除(MemoryRuntimeSdk 不支持事件监听)@anthropic-ai/sdk、@anthropic-ai/claude-agent-sdk、@xenova/transformers 为 optional peer deps,缺失时 fallback| 子项 | 状态 | 说明 |
|---|---|---|
| MemoryService 启动/停止 | ✅ 已完成 | tui.ts 中 engine 创建后 start,finally 中 stop |
| HookManager tool 后钩子 | ✅ 已完成 | afterToolCall → bridge.onPostToolUse/onPostToolFailure |
| Loop 事件钩子 | ⚠️ 已修复 | onLoopEvent 已从 assistant_delta 改为用户输入真实入口 |
| mem::context 注入 system prompt | ⚠️ 已修复 | 启动时调用 mem::context,内容追加后重新调用 engine.setSystemPrompt() |
| Session 生命周期 | ✅ 已完成 | onSessionEnd 已接线;onSessionStart 已修复接入;onGenerationComplete 已接入(onLoopEvent 检测 role === "done");onPreToolUse 明确不接入(DONE 已列为限制) |
| 故障隔离 | ✅ 已完成 | 所有 bridge 调用 try/catch,初始化失败不阻断启动 |
| 开关控制 | ✅ 已完成 | DEEPREEF_MEMORY=false 环境变量禁用 |
| hooks/ 死代码清理 | ✅ 已完成 | 独立脚本添加 @ts-nocheck,被 bridge 替代 |
tui.ts (CLI)
├─ new MemoryService({ autoObserve, injectContext })
├─ await memoryService.start()
├─ bridge.onSessionStart(sessionId)
├─ engine.hookManager.addHooks({
│ afterToolCall → bridge.onPostToolUse / onPostToolFailure
│ onLoopEvent → (不再用于 prompt 观察)
│ })
├─ App.onUserInput → bridge.onPromptSubmit (用户输入真实入口)
├─ mem::context → system prompt injection → engine.setSystemPrompt()
└─ finally:
├─ bridge.onSessionEnd
├─ memoryService.stop()
└─ engine.shutdown()
pre_tool_use 钩子明确不接入(bridge.onPreToolUse 已实现但未从 HookManager 调用,DONE 已将其列为限制)| 子项 | 状态 | 说明 |
|---|---|---|
memory_recall 工具 |
✅ 已完成 | 调用 mem::search,返回相关记忆 |
memory_save 工具 |
✅ 已完成 | 调用 mem::remember,持久化内容 |
memory_smart_search 工具 |
✅ 已完成 | 调用 mem::smart-search,混合 BM25+向量 |
memory_forget 工具 |
✅ 已完成 | 调用 mem::evict,按 ID 删除 |
memory_timeline 工具 |
✅ 已完成 | 调用 mem::timeline,时间线分组 |
memory_status 工具 |
⚠️ 已修复 | 调用 ID 从 mem::diagnostics 修正为 mem::diagnose |
| CLI 接线 | ✅ 已完成 | tui.ts 中启用记忆时自动注册 7 个工具(含 memory_migrate) |
| typecheck | ✅ 已完成 | 0 错误 |
| 无回归 | ✅ 已完成 | 基线测试 965 pass / 1 fail(预置 AS2) |
DEEPREEF_MEMORY_ADVANCED=true 环境变量开启deepreef memory * CLI 命令尚未实现| 子项 | 状态 | 说明 |
|---|---|---|
MemoryServiceConfig 高级开关 |
⚠️ 已修复 | 构造函数现在保存并消费完整 config,不再丢弃 |
| 环境变量门控 | ✅ 已完成 | DEEPREEF_MEMORY_ADVANCED/GRAPH/CONSOLIDATE/REFLECT/SLOTS |
~/.agentmemory 迁移 |
✅ 已完成 | migrateFromAgentMemory() 复制 state 目录,跳过已存在的文件 |
memory_migrate 工具 |
⚠️ 已修复 | 已导出并注册到 CLI,已移除无用 store 参数 |
| typecheck | ✅ 已完成 | 0 错误 |
| 子项 | 状态 | 说明 |
|---|---|---|
| 全量测试(核心包) | ⚠️ 部分完成 | 1180 pass / 5 fail(预置 mode-selector + bridge,无新增) |
| typecheck | ✅ 已完成 | 0 错误 |
| 无 iii-engine 依赖 | ✅ 已完成 | 无 iii-sdk、iii-engine 引用 |
| 启动故障隔离 | ✅ 已验证 | Memory init 失败不阻断 CLI 启动 |
| 关闭清理 | ✅ 已完成 | finally 中 memoryService.stop() 清理所有 timer |
记忆开关 DEEPREEF_MEMORY=false |
✅ 已完成 | 禁用后不加载 @deepreef/memory 模块(动态 import)、不初始化 MemoryService、不注册工具、不读写数据 |
packages/memory/
src/
runtime/ MemoryRuntimeSdk (ISdk), MemoryStore (file KV)
functions/ 57 个 mem::* 函数(原样复制自 agentmemory)
state/ 索引、搜索、schema
providers/ LLM provider 适配层
bridge/ DeepreefMemoryBridge(生命周期桥梁)
tools.ts AgentTool 工厂(6 个记忆工具 + memory_migrate)
migrate.ts ~/.agentmemory 迁移
memory-service.ts 统一入口(start/stop/trigger),完整消费 config
hooks/ 死代码(被 bridge 替代,@ts-nocheck 保留)
packages/cli/src/tui.ts — MemoryService init + HookManager 接线 + 工具注册 + 动态 import
packages/tui/src/bridge.tsx — createBridge 新增 onUserInput 回调
依赖关系:@deepreef/memory → @deepreef/core(AgentTool 类型)
@deepreef/cli → @deepreef/memory(创建 + 接线)
| 能力 | agentmemory (iii-engine) | deepreef memory |
|---|---|---|
| 记忆存储 | iii-engine KV | MemoryStore 文件 KV |
| 函数注册 | iii-sdk.registerFunction() |
MemoryRuntimeSdk.registerFunction() |
| 函数触发 | iii-sdk.trigger() |
MemoryRuntimeSdk.trigger() |
| 生命周期 | 独立 MCP/REST 进程 | DeepreefMemoryBridge + HookManager |
| 上下文注入 | 独立 hook 脚本写 stdout | mem::context 注入 system prompt |
| BM25 索引 | iii-engine | IndexPersistence 文件持久化 |
| 向量索引 | iii-engine | VectorIndex 内存 + 文件持久化 |
| 工具暴露 | 53 个 MCP 工具 | 7 个原生 AgentTool(含 memory_migrate,高级工具可配) |
| AgentMemory 数据 | ~/.agentmemory |
~/.deepreef/memory(可迁移) |
依据 docs/agentmemory-native-integration-review-fixes.md 审查文档完成全部 P0 和关键 P1 修复,随后通过二、三轮审查修复剩余问题。
第一轮(698355f)
| 编号 | 优先级 | 内容 | 修改文件 |
|---|---|---|---|
| P0-1 | P0 | memory context 注入后重新调用 engine.setSystemPrompt() |
tui.ts |
| P0-2 | P0 | prompt 观察从 assistant_delta 改为用户输入真实入口 |
tui.ts, bridge.tsx, App.tsx |
| P0-3 | P0 | memory_status 调用 ID 修正为 mem::diagnose |
tools.ts |
| P1-1 | P1 | 接入 onSessionStart(),hook adapter 引用已保存 |
tui.ts |
| P1-2 | P1 | MemoryServiceConfig 完整消费,开关实际控制函数注册和定时器 |
memory-service.ts |
| P1-3 | P1 | 导出并注册 memory_migrate,移除无用 store 参数 |
migrate.ts, index.ts, tui.ts |
| P1-4 | P1 | memory 改为动态 import(),DEEPREEF_MEMORY=false 时不加载模块 |
tui.ts |
| P1-5 | P1 | 日志前缀从 [agentmemory] 改为 [deepreef:memory] |
logger.ts |
第二轮(a4be2b0)
| 编号 | 优先级 | 内容 | 修改文件 |
|---|---|---|---|
| FIX-1 | 高 | CLI memory-integration 测试修正导出名和路径 | memory-integration.test.ts |
| FIX-2 | 高 | P0-2 队列去重:fromQueue 全局标志改为 per-call isQueueResubmit 参数 |
bridge.tsx |
| FIX-3 | 中 | consolidation timer 增加 advancedTools 门控 |
memory-service.ts |
| FIX-4 | 中 | injectContext=false 时跳过 mem::context 调用 |
tui.ts |
| FIX-5 | 低 | DONE.md 移除 onGenerationComplete 未接线 过时描述 |
DONE.md |
第三轮(本轮)
| 编号 | 优先级 | 内容 | 修改文件 |
|---|---|---|---|
| FIX-6 | 中 | ignored 输入不再写入 Memory:onUserInput 移到 enqueueInstruction 状态检查之后 |
bridge.tsx |
| FIX-7 | 中 | CLI 测试移除 tui.ts 导入(触发 main()/process.exit()) |
memory-integration.test.ts |
| FIX-8 | 中 | onGenerationComplete 竞态:HookManager.drain() 等待所有 in-flight hooks |
hooks.ts, tui.ts |
| FIX-9 | 低 | package.json 添加 test:memory-native 脚本 |
package.json |
第四轮(本轮)
| 编号 | 优先级 | 内容 | 修改文件 |
|---|---|---|---|
| FIX-6v2 | 高 | 修复 FIX-6 回归:onUserInput 改为非 ignored 时立即观察,queued/full/idle 均记录 |
bridge.tsx |
| FIX-8v2 | 中 | 用 HookManager.drain() 替代 trackedAdapter,彻底消除竞态 |
hooks.ts, tui.ts |
P0-2:prompt 观察路径
旧路径(错误):
onLoopEvent → assistant_delta → onPromptSubmit()
问题:assistant_delta 是模型输出,不是用户输入;一个回复产生多个 delta
新路径(正确):
pipe mode: 直接调用 bridge.onPromptSubmit() before engine.submit()
TUI mode: App.onUserInput → bridge.onPromptSubmit()
机制:bridge.tsx 的 createBridge 新增 onUserInput 回调参数
App.tsx 新增 onUserInput prop
tui.ts 传递 memoryBridge.onPromptSubmit 作为回调
P0-2 FIX-2:队列去重
旧:fromQueue 是全局 boolean,submit() 执行期间保持 true
→ 用户在此期间的新输入被跳过
新:submit(text, isQueueResubmit = false) per-call 参数
processQueue 调用 submit(next, true),直接调用不传参
→ 每次调用独立判断,新输入不受队列处理影响
P1-4:动态 import
旧:文件顶部静态 import { MemoryService, ... } from "@deepreef/memory"
新:if (enableMemory) { const memory = await import("@deepreef/memory") ... }
效果:DEEPREEF_MEMORY=false 时完全不加载 memory 模块
P1-2:配置优先级
旧:构造函数只读 dataDir,其余 config 字段全部丢弃
新:this.userConfig = userConfig 完整保存
registerAllFunctions() 中 uc.enableGraph ?? isGraphExtractionEnabled()
显式构造参数 > 环境变量 > 默认值
FIX-3:consolidation timer 门控
旧:startTimers() 只检查 enableConsolidation,不检查 advancedTools
→ advancedTools=false 时 timer 启动但 mem::consolidate-pipeline 未注册
新:shouldConsolidate = advancedTools && enableConsolidation
→ advancedTools=false 时不启动 timer
FIX-4:injectContext=false 门控
旧:无论 memoryInjectContext 是否关闭,启动时都调用 mem::context
新:if (memoryInjectContext) { ... mem::context ... }
→ DEEPREEF_MEMORY_INJECT_CONTEXT=false 时不调用,不污染 system prompt
FIX-6v2:ignored 输入不再观察(修正版)
v1(有缺陷):onUserInput() 移到 running 分支之后
→ queued/full/idle 输入也被跳过,运行期间完全不观察
v2(正确):在 running 分支内,ignored 检查之后立即观察
→ ignored 跳过;queued/full/queued-ok 均观察一次
→ isQueueResubmit 标记仍防止队列重提交重复观察
FIX-8v2:onGenerationComplete 竞态(彻底修复)
v1(部分):trackedAdapter 包装 onLoopEvent,存储 lastHookPromise
→ 仅等 CLI 自己的 hook,前序插件 hook 仍可能未完成
v2(彻底):HookManager 新增 drain() 方法
→ 内部跟踪所有 pending promise(Set<Promise<void>>)
→ CLI finally 调用 engine.hookManager.drain() 等待全部 in-flight hooks
→ 移除 trackedAdapter,简化代码
bun run typecheck # 通过
bun run --cwd packages/memory typecheck # 通过
bun run test:memory-native # 32/32 通过
bun run test:memory-native # 独立脚本,可接入 CI
| 测试文件 | 覆盖内容 | 状态 |
|---|---|---|
test/deepreef-memory-service.test.ts |
service start/stop/CRUD/evict | ✅ 5/5 |
test/deepreef-memory-tools.test.ts |
agent tool shape/execute/full flow | ✅ 8/8 |
test/deepreef-memory-bridge.test.ts |
bridge hook lifecycle/autoObserve | ✅ 11/11 |
test/deepreef-memory-migration.test.ts |
migrate tool shape/schema/execute | ✅ 3/3 |
packages/cli/src/__tests__/memory-integration.test.ts |
CLI import/tool registration/service lifecycle | ✅ 5/5 |
onPreToolUse 明确不接入,属于设计限制。test:memory-native 脚本已就绪,但尚未接入 CI pipeline。基于 CodeGraph(@colbymchenry/codegraph)项目的分析与评估,将其作为内置 MCP Server 自动接入 deepreef。
CodeGraph 是一个本地代码智能库(tree-sitter 解析 + SQLite 知识图谱),通过 MCP 协议暴露代码符号关系、调用图和影响半径。与 deepreef 通过 MCP 协议集成,不需要代码合并。
集成方式决策:
| 方案 | 结论 | 原因 |
|---|---|---|
| 代码合并(merge) | ❌ 不适合 | 运行时冲突(Bun vs Node.js)、native addon 依赖(better-sqlite3)、产品边界清晰(独立 npm 包) |
| MCP 协议集成 | ✅ 采用 | deepreef 已有完整 MCP 客户端系统,CodeGraph 自身就是 MCP Server,零代码修改即可使用 |
协同价值:
| 场景 | 没有 CodeGraph | 有 CodeGraph |
|---|---|---|
| “这个函数被谁调用?” | grep → 读多个文件 → 分析调用关系,大量 token | codegraph_callers 一次调用,毫秒级返回 |
| “修改 AuthService 会影响什么?” | Agent 猜测影响范围 | codegraph_impact 返回完整影响半径 |
| Agent 探索性工具调用 | grep + read 循环,每轮消耗 token | 减少约 58% 工具调用 |
| 对 deepreef 省钱目标 | — | CodeGraph 减少工具调用 × deepreef 减少 cache miss = 双重节省 |
修改文件:packages/mcp/src/host.ts、packages/mcp/src/index.ts、packages/mcp/__tests__/mcp-host.test.ts
新增内容:
BUILTIN_MCP_SERVERS 常量:定义内置 MCP Server 列表,当前仅包含 codegraph:
const BUILTIN_MCP_SERVERS: Record<string, McpServerConfig> = {
codegraph: {
command: "codegraph",
args: ["serve", "--mcp"],
},
}
isCommandAvailable() 辅助函数(已导出):异步跨平台检测命令是否在 PATH 上。不使用 shell,直接遍历 PATH 目录并用 fs.access() 检查文件是否存在,完全避免 shell 注入风险。Windows 额外检查 .cmd / .exe / .bat 后缀。
McpHost 构造函数扩展:接受可选 options 参数,支持注入 builtinServers 和 checkCommand,便于测试和未来扩展:
constructor(
logger?: DiagnosticLogger,
options?: {
builtinServers?: Record<string, McpServerConfig>
checkCommand?: (command: string) => Promise<boolean>
},
)
loadConfig() 修改:
.deepreef/mcp.json 配置后,自动合并内置 Servercodegraph 不在 PATH 上,静默跳过failed 数组,不计入 serverCount,不产生警告日志options.loadBuiltins 参数:true 强制加载(无论是否传了 configPath),false 强制跳过,省略则保持原有行为(仅默认路径时加载)connect() 方法扩展:接受可选 { silent?: boolean } 选项。当 silent: true 时,为 McpClient 注入 noopDiagnosticLogger,从根源抑制所有 warn/debug 级别日志(包括 mcp.server.connect.error、mcp.request.timeout、mcp.request.fail 等),而非仅抑制单条日志。行为逻辑:
| 场景 | 结果 |
|---|---|
| 用户安装了 codegraph + 没在 mcp.json 里配过 | ✅ 自动连接,Agent 立刻可用 |
| 用户已经在 mcp.json 里配了 codegraph | ✅ 跳过内置配置,以用户自己的为准 |
| 用户没安装 codegraph | ✅ 静默跳过,不报错、不打日志 |
用户传了自定义 configPath 调用 loadConfig() |
✅ 不加载内置,除非传入 { loadBuiltins: true } |
| 内置服务连接失败 | ✅ 不计入 serverCount / failed,不产生任何警告日志 |
bun run typecheck # 通过
bun test packages/mcp # 34 pass, 0 fail
新增测试覆盖(11 个):
| 测试 | 覆盖场景 |
|---|---|
isCommandAvailable > returns true |
PATH 遍历检测可用命令 |
isCommandAvailable > returns false |
不存在的命令返回 false |
isCommandAvailable > does not execute shell metacharacters |
验证 shell 注入不生效 |
auto-loads a built-in server |
loadBuiltins: true + checkCommand 返回 true 时自动加载 |
skips built-in when user config has the same name |
用户配置同名服务时跳过内置 |
silently skips built-in when command not on PATH |
checkCommand 返回 false 时静默跳过 |
does not load built-ins when loadBuiltins is false |
loadBuiltins: false 时不触发内置加载 |
built-in connection failure excluded from statistics |
内置服务连接失败不计入 serverCount / failed |
mixed user + built-in failures |
混合场景下只暴露用户服务失败 |
connect() silent suppresses logs |
silent: true 时 host + client 均不输出任何警告日志 |
connect() non-silent logs on failure |
默认行为仍输出连接错误日志 |
npm i -g @colbymchenry/codegraph)才能使用codegraph init -i)才能产生有效的知识图谱数据P0:Linux/macOS 上 CodeGraph 永远不会自动加载(阻断性)
原因:isCommandAvailable() 使用 execFileSync("command", ["-v", name]),但 command 是 POSIX shell 内建命令,不是可执行文件,实际运行结果为 ENOENT。
修复:改为 promisify(exec) 异步执行 shell 命令。
P1:新增功能完全没有测试覆盖
修复:新增 10 个测试 + McpHost 构造函数支持注入 builtinServers / checkCommand。
P1:”连接失败静默”与实际行为不符
原因:connect() 的 catch 块仍输出 mcp.server.connect.error 警告。
修复:connect() 新增 { silent?: boolean } 选项。
P2:失败状态统计不准确
原因:失败的内置服务计入 serverCount 但不计入 failed。
修复:新增 builtinFailedCount,serverCount 和 connected 均排除失败的内置服务。
P2:同步命令检测可能阻塞事件循环
修复:随 P0 一并改为异步 execAsync。
P2:DONE 第 21 节重复
修复:删除重复副本。
P0:命令检测存在 shell 注入(阻断性)
原因:第一轮修复后 isCommandAvailable() 使用 execAsync(\command -v ${command}`) 将 command 直接拼接到 shell 命令字符串。传入 node; printf injected-marker 会执行后面的命令,Windows 的 where ${command}` 同样存在风险。
修复:完全移除 shell,改为直接遍历 PATH 目录检查可执行文件:
// 修复后:纯 fs.access 检查,零 shell
export async function isCommandAvailable(command: string): Promise<boolean> {
const dirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean)
for (const dir of dirs) {
try { await access(resolve(dir, command)); return true } catch { /* continue */ }
if (process.platform === "win32") {
for (const ext of WIN_EXTS) {
try { await access(resolve(dir, command + ext)); return true } catch { /* continue */ }
}
}
}
return false
}
新增单元测试 does not execute shell metacharacters 验证 node; touch /tmp/injected-marker 不会产生 marker 文件。
P1:2 个 CL-10 测试回归是本次修改造成的
原因:writeFileSync 模板字符串中的 \\n 被错误写成 \\\\n(多了一层转义),导致写入文件的是字面量 \n(两个字符:反斜杠 + n)而非换行符,MCP JSONL 消息无法解析,测试稳定超时。
修复:恢复正确的 \\n 转义,即模板字符串中写 \\n,文件中产生真正的 \n 换行。
P1:4 个新增测试是假覆盖
原因:这些测试传入了自定义 configPath,而 loadConfig() 的 if (!configPath) 条件禁止自定义路径时加载 built-in。测试虽然通过,但 built-in 分支根本没有执行。
修复:loadConfig() 新增第二个参数 options?: { loadBuiltins?: boolean }:
true:无论是否传了 configPath,都加载内置服务false:不加载内置服务所有假覆盖测试改为传入 { loadBuiltins: true },确保 built-in 分支确实执行。
P1:静默失败仍可能产生警告日志
原因:silent 仅抑制 host.ts 中 mcp.server.connect.error 一条日志。如果 CodeGraph 初始化超时或返回协议错误,client.ts 中的 mcp.request.timeout、mcp.request.fail、mcp.request.error 等警告仍会输出。
修复:当 options.silent === true 时,为 McpClient 注入 noopDiagnosticLogger,从根源抑制所有 warn/debug 级别日志:
const clientLogger: DiagnosticLogger = options?.silent
? noopDiagnosticLogger
: this.logger
const client = new McpClient(name, clientLogger)
测试验证:检查 warnLogs 中不包含 mcp.server.connect.error、mcp.request.timeout、mcp.request.fail 任何一项。
bun run typecheck # 通过
bun test packages/mcp # 34 pass, 0 fail
free-auto 自动免费模型路由| 阶段 | 状态 | 说明 |
|---|---|---|
| RM-10 | ✅ 已完成 | 删除 free-auto 虚拟 provider 和自动候选切换 |
实现边界:
| 文件/目录 | 操作 |
|---|---|
packages/core/src/free-auto/ |
整个目录删除 |
packages/core/src/engine.ts |
删除 FreeAutoClient 导入、freeAutoClient 字段、resolveClient() 中的 free-auto 分支 |
packages/core/src/config.ts |
删除 free-auto provider 条目、virtual 字段、相关 baseUrl 逻辑 |
packages/core/src/index.ts |
删除 Free Auto 相关 exports |
packages/core/src/loop.ts |
删除 isKeyless 和 useMaxTokens 中的 free-auto 条件 |
packages/tui/src/ModelPicker.tsx |
从 PROVIDER_ORDER 中删除 free-auto |
packages/tui/src/bridge.tsx |
删除 routedModel、routedModelDetail 字段和 free_auto_route 事件处理 |
packages/tui/src/App.tsx |
删除 routedModel 和 routedModelDetail 的使用 |
packages/core/__tests__/free-auto-router.test.ts |
整个文件删除 |
packages/core/__tests__/config.test.ts |
删除 free-auto 相关测试用例 |
/model 不再出现 free-auto,但仍显示各免费 provider/model。.deepreef/last-config.json 若保存了 provider: "free-auto",加载时安全回退到默认 provider(zen)。virtual 字段后,loadConfig() 中 baseUrl 逻辑简化为直接赋值。resolveClient() 简化为直接返回 DeepSeekClient。routedModel 和 routedModelDetail 字段后,App.tsx 中 StatusBar 的 model 和 statusMessage 直接使用 activeModel 和 statusMessage。bun run typecheck
bun test packages/core/__tests__/config.test.ts packages/core/__tests__/engine-tools.test.ts
bun test packages/tui/__tests__/commands.test.ts
bun test
git diff --check
rg "free-auto|FreeAuto|free_auto" packages 不再发现任何匹配(旧配置迁移兼容代码除外)。RM-20(自动推理强度调节)的内容。/thinking auto 和自动 thinking 切换| 阶段 | 状态 | 说明 |
|---|---|---|
| RM-20 | ✅ 已完成 | 删除 ModeSelector、ModeStats、StrategyTier、自动推理强度调节 |
实现边界:
| 文件/目录 | 操作 |
|---|---|
packages/core/src/mode-selector.ts |
整个文件删除 |
packages/core/src/mode-stats.ts |
整个文件删除 |
packages/core/src/strategy/tiers.ts |
整个文件删除 |
packages/core/src/strategy/recommender.ts |
整个文件删除 |
packages/core/src/strategy/ |
整个目录删除 |
packages/core/src/engine.ts |
删除 ModeSelectorState/ModeStats/StrategyTier 导入、字段(modeSelectorState, modeStats, currentTier, pendingTierDecision)、方法(setThinkingMode, getThinkingMode, getModeSummary, resolveTierDecision, getTier, setTier)、strategy_notify 事件、loopOpts tier 参数 |
packages/core/src/loop.ts |
删除 imports(ModeSelectorState, StrategyTier, ModeStats, logModeSwitch, recommendTier)、删除 LoopOptions 中 modeSelectorState/modeStats/tier 字段、删除 tier config overrides(maxChainLength, enableReasoning, model, temperature)、删除 auto thinking 分支、删除 currentMode 变量、删除 strategy_estimate_refined/tier_recommendation/thinking_mode_switch 事件 |
packages/core/src/interface.ts |
删除 LoopEventRole 中的 “strategy_notify”、”strategy_estimate_refined”、”tier_recommendation”;删除 CoreEngine 中的 resolveTierDecision、getTier、setTier、getThinkingMode 方法 |
packages/core/src/index.ts |
删除 strategy tier exports |
packages/core/src/loop-helpers.ts |
删除 evaluateModeSwitchForTurn 函数和相关 mode-selector/mode-stats 导入 |
packages/core/src/provider-thinking.ts |
将 ThinkingMode 类型从 "off" | "open" | "high" | "auto" 改为 "off" | "open" | "high";更新 createDeepSeekCapabilities |
packages/tui/src/commands.ts |
从 THINKING_MODES 中删除 “auto” |
packages/tui/src/bridge.tsx |
删除 effectiveThinkingMode 字段、thinking_mode_switch 事件处理、strategy_notify/strategy_estimate_refined/tier_recommendation 事件处理 |
packages/tui/src/App.tsx |
删除 effectiveThinkingMode 使用、engine.getThinkingMode()、engine.setThinkingMode()、engine.getTier() |
packages/tui/src/StatusBar.tsx |
删除 effectiveThinkingMode prop、简化 thinkingLabel 逻辑 |
packages/core/__tests__/provider-thinking.test.ts |
更新测试:删除 “auto” 期望、删除 mapMode(‘auto’) 测试 |
packages/tui/__tests__/commands.test.ts |
更新测试:删除 “auto” 期望、删除 validateThinkingMode(“auto”) 测试 |
/thinking off|open|high 仍然由用户显式选择,不会被运行时自动修改。.deepreef/last-config.json 若保存了 thinkingMode: "auto",加载时安全回退到 "off"。bun run typecheck
bun test packages/core/__tests__/provider-thinking.test.ts
bun test packages/core/__tests__/engine-tools.test.ts
bun test packages/tui/__tests__/commands.test.ts
git diff --check
bun run typecheck 通过。/thinking 命令不再显示 “auto” 选项。RM-30(删除 Token 用量预估专项代码)的内容。docs/auto-reasoning-design.md 保留为历史参考文档。| 阶段 | 状态 | 说明 |
|---|---|---|
| RM-30 | ✅ 已完成 | 删除 TokenizerPool、Worker、精细预估和 TUI token/s 展示 |
实现边界:
| 文件/目录 | 操作 |
|---|---|
packages/core/src/context/tokenizer-pool.ts |
整个文件删除 |
packages/core/src/context/tokenizer-worker.js |
整个文件删除 |
packages/core/__tests__/tokenizer-pool.test.ts |
整个文件删除 |
packages/core/src/context/token-estimator.ts |
简化:删除 refinedEstimate()、CJK/标点精细化 |
packages/core/src/context/manager.ts |
删除 TokenizerPool 导入和使用;estimateTokens()、getBudget()、getFoldDecision()、shutdown() 改为同步 |
packages/core/src/engine.ts |
删除 await ctx.getBudget() 中的 await |
packages/core/src/loop.ts |
简化 fold check:删除 Promise.race,直接调用同步 ctx.getFoldDecision() |
packages/tui/src/reasonix/StreamingCard.tsx |
删除 token/s 估算逻辑、CHARS_PER_TOKEN、estimateTokens()、formatRate();改为显示经过秒数 |
packages/core/__tests__/token-estimator.test.ts |
删除 refinedEstimate 测试 |
packages/core/__tests__/benchmark.test.ts |
删除 refinedEstimate 导入和测试 |
promptTokens/completionTokens/cacheHitTokens/cacheMissTokens 保留。SessionStats 中真实 prompt/completion/cache hit/cache miss 保留。StatusBar 和 /status 中基于真实 Provider 数据计算的 cache 命中率保留。pricing.ts 基于真实 usage 的成本计算保留。contextWindow 配置保留。rg "TokenizerPool|tokenizer-worker|refinedEstimate|CHARS_PER_TOKEN" packages/core packages/tui packages/cli
bun test packages/core/__tests__/token-estimator.test.ts
bun test packages/core/__tests__/context.test.ts packages/core/__tests__/context-summary.test.ts packages/core/__tests__/engine-context-policy.test.ts
bun test packages/core/__tests__/benchmark.test.ts
bun run typecheck
bun test
git diff --check
TokenizerPool、Worker、精细 Token 预估和 TUI token/s 猜测已删除。DONE.md 记录删除事实与保留边界,不再把 Token 用量预估列为当前能力。| 阶段 | 状态 | 说明 |
|---|---|---|
| QST-10 | ✅ 已完成 | Agent 可暂停、询问用户并在回答后继续;Subagent 问题冒泡到主 TUI |
实现边界:
| 文件 | 职责 |
|---|---|
id.ts |
createQuestionId() 生成 que 前缀唯一 ID |
types.ts |
QuestionInfo、QuestionRequest、QuestionAnswer、QuestionReply、QuestionReject 类型定义 |
service.ts |
QuestionService 类:ask/reply/reject/list/interrupt/shutdown |
index.ts |
导出所有类型和 QuestionService |
CoreEngine 新增 respondQuestion(requestId, answers)、rejectQuestion(requestId)、listPendingQuestions(sessionId?) 方法LoopEventRole 新增 question_ask、question_replied、question_rejected 事件ToolContext 新增 askUser?(questions: QuestionInfo[]): Promise<QuestionAnswer[]> 可选方法StreamingToolExecutor 将 askUser 传递到工具上下文ctx.askUser() 暂停执行等待用户回答| 文件 | 变更 |
|---|---|
question-state.ts |
纯状态机:tab/select/edit/submit/reject,支持 single 和 multi-question 模式 |
QuestionPrompt.tsx |
问题面板组件:选项列表、自定义输入、确认摘要、键盘导航 |
bridge.tsx |
处理 question_ask/question_replied/question_rejected 事件;新增 respondQuestion/rejectQuestion 方法 |
App.tsx |
渲染 QuestionPrompt;问题挂起时禁用输入框;cancel 时自动 reject |
packages/core/__tests__/question-service.test.ts:10 个测试,覆盖 ask/reply/reject/interrupt/shutdown/listbun run typecheck
bun test packages/core/__tests__/question-service.test.ts
git diff --check
QuestionService 管理 pending 问题,ask 返回 Promise,reply/reject 解析 PromiseaskUser 调用冒泡到主 TUI 的 QuestionPromptDONE.md 记录 QST-10 完成事实| 阶段 | 状态 | 说明 |
|---|---|---|
| PERM-10 | ✅ 已完成 | Pattern-based 权限规则、once/always/reject 生命周期、safe/balanced/yolo 模式、子 Agent bubble |
实现边界:
| 文件 | 职责 |
|---|---|
types.ts |
PermissionAction、PermissionMode、PermissionRule、PermissionRequest、PermissionReply 类型定义 |
rules.ts |
evaluateRules() 通配符匹配、mergeRulesets()、fromConfig()、createSessionRule() |
service.ts |
PermissionService 类:ask/reply/list/interrupt/shutdown,session-approved rules |
patterns/shell.ts |
extractShellPatterns() 从 shell 命令提取文件路径和命令模式 |
index.ts |
导出所有类型和函数 |
* 匹配任意字符,? 匹配单个字符"ask"fromConfig() 将配置规则转换为 PermissionRule[]createSessionRule() 创建会话级临时规则ask() 返回 Promise,阻塞调用方直到用户回复reply("once") 仅批准当前实例reply("always") 添加 session-approved rules 并自动批准其他匹配的 pending 请求reply("reject") 拒绝当前请求并级联拒绝同 session 所有 pending 请求matchesSessionRules() 检查请求是否匹配 session 规则interrupt(sessionId?) 拒绝指定 session 的所有 pending 请求shutdown() 清理所有 pending 和 session 规则dirsexecutor-helpers.ts 的 evaluatePermission() 扩展支持 PermissionServiceextractResourcePatterns() 从工具参数提取资源模式| 文件 | 变更 |
|---|---|
PermissionPrompt.tsx |
三阶段 UI:permission → always 确认 → reject 反馈;显示 permission type、resource patterns、tool name |
bridge.tsx |
处理 permission_ask 事件,解析 PermissionRequest;新增 respondPermission(reply, message?) |
App.tsx |
渲染新 PermissionPrompt,传递 PermissionRequest |
deriveSubagentPermissions() 从父级规则继承 deny rules 和 external_directory 限制checkSubagentPermission() 支持 readonly/denyExec/acceptEdits/bubble 四种模式bubble 模式返回 { allowed: false, bubble: true },由父级处理acceptEdits 模式仍需 exec 工具的父级批准packages/core/__tests__/permission-service.test.ts:18 个测试
bun run typecheck
bun test packages/core/__tests__/permission-service.test.ts
git diff --check
once/always/reject 生命周期正确工作,session rules 自动批准匹配请求yolo 模式(预留)仅自动批准 ask,不覆盖任何 denyDONE.md 记录 PERM-10 完成事实| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-00 | ✅ 已完成 | 固化 RM-10/20/30/QST-10/PERM-10 完成后的基线,建立复制台账 |
| 验证项 | 结果 | 说明 |
|---|---|---|
bun run typecheck |
通过 | tui-opentui 预置错误(非本次变更),本次变更文件无新增错误 |
bun test |
1954 pass, 503 fail | 失败项为 memory 相关预置问题,与本次变更无关 |
git diff --check |
通过 | packages/core/tui/security 无 whitespace 错误 |
所有 QST-10 和 PERM-10 来源文件均已验证存在:
opencode/packages/opencode/src/question/index.ts ✓opencode/packages/opencode/src/question/schema.ts ✓opencode/packages/opencode/src/tool/question.ts ✓opencode/packages/opencode/src/tool/question.txt ✓opencode/packages/opencode/src/cli/cmd/run/question.shared.ts ✓opencode/packages/opencode/src/permission/index.ts ✓opencode/packages/opencode/src/core/src/v1/config/permission.ts ✓opencode/packages/opencode/src/tool/shell.ts ✓opencode/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx ✓创建 docs/fusion-copy-ledger.md,包含:
bun run typecheck
bun test
git diff --check
DONE.md 记录 DRF-00 完成事实
| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-10 | ✅ 已完成 | Worker/Supervisor 可按 target 使用独立 client/provider/baseUrl |
| 来源 | 目标 | 类型 |
|---|---|---|
| 新写(架构前置,无直接源文件) | packages/core/src/model-target.ts |
新写 |
ModelTarget 接口:id/role/provider/model/baseUrl/apiKeyPolicyworker.local、supervisor.zen-free、oracle.optionalresolveModelTarget()、createClientForTarget()、targetToConfig()SubagentRunOptions.target / SubagentDefinition.target 支持SubagentRunner 按 target 创建独立 child client,不再共享父级 clientDeepreefConfig.modelTargets 支持项目级覆盖(.deepreef/model-targets.json)bun test packages/core/__tests__/model-target.test.ts
bun run typecheck
| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-11 | ✅ 已完成 | 本地小模型启动时可加载优化配置 |
| 来源 | 目标 | 类型 |
|---|---|---|
smallcode/src/model/profiles.js |
packages/core/src/model-profile/profiles.ts |
adapt |
smallcode/profiles/qwen3-8b.toml |
内置 qwen3-8b profile |
adapt |
smallcode/profiles/qwen2.5-coder-14b.toml |
内置 qwen2.5-coder-14b profile |
adapt |
smallcode/profiles/devstral-small.toml |
内置 devstral-small profile |
adapt |
local-small-strict、local-medium-forced、remote-adaptivesupervisor-advice-only、free-chatbun test packages/core/__tests__/model-profile.test.ts
bun run typecheck
| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-20 | ✅ 已完成 | read-before-write 守卫 + early-stop 检测 |
| 来源 | 目标 | 类型 |
|---|---|---|
smallcode/src/tools/read_tracker.js |
packages/core/src/read-before-write.ts |
adapt |
smallcode/src/governor/early_stop.js |
packages/core/src/early-stop.ts |
adapt |
StreamingToolExecutor:ReadTracker 写入前守卫 + 读/写跟踪runLoop:EarlyStopDetector 重复输出、只读循环、问候回归检测bun test packages/core/__tests__/read-before-write.test.ts packages/core/__tests__/early-stop.test.ts
bun test packages/core packages/tui packages/cli packages/security packages/tools
bun run typecheck
| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-30 | ✅ 已完成 | 长任务防循环、可恢复 checkpoint |
| 来源 | 目标 | 类型 |
|---|---|---|
iceCoder/src/harness/branch-budget.ts |
packages/core/src/governance/branch-budget.ts |
adapt |
iceCoder/src/harness/branch-budget-path.ts |
packages/core/src/governance/branch-budget-path.ts |
adapt |
iceCoder/src/harness/checkpoint-engine.ts |
packages/core/src/checkpoint/checkpoint-engine.ts |
adapt |
iceCoder/src/types/runtime-checkpoint.ts |
packages/core/src/checkpoint/runtime-checkpoint.ts |
adapt |
bun test packages/core/__tests__/branch-budget*.test.ts packages/core/__tests__/checkpoint-engine.test.ts
bun run typecheck
| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-31 | ✅ 已完成 | 小模型畸形工具调用可恢复;截断写入拒绝落盘 |
| 来源 | 目标 | 类型 |
|---|---|---|
iceCoder/src/tools/tool-arguments-normalizer.ts |
packages/core/src/tool-arguments/ |
adapt |
iceCoder/src/tools/tool-arguments-salvage.ts |
同上 | adapt |
iceCoder/src/harness/text-format-tool-call-parsers.ts |
packages/core/src/tool-calls/ |
adapt |
iceCoder/src/harness/text-tool-call-salvage.ts |
同上 | adapt |
parseToolCallArgs → normalizeToolArgumentsstreaming-executor 拒绝 _salvageTruncated 写入类工具loop.ts 文本 tool-call 抢救 + TextToolCallStreamFilter 流式净化| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-32 | ✅ 已完成 | short 前台 / long 后台 / auto 软超时升级 |
packages/tools/src/shell-dual-track/(classifier、background-task-manager、bash-dual-track)createBashTool({ dualTrack: true }) 或 createDualTrackBashTool()| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-40 | ✅ 已完成 | 有状态执行 + 改动后验证门禁 |
| 来源 | 目标 | 类型 |
|---|---|---|
smallcode/src/session/plan_tracker.js |
packages/core/src/task-ledger.ts |
adapt |
iceCoder/.../verification-gate.ts 等 |
packages/core/src/governance/ |
adapt |
engine.submit() 按启发式创建 ledger,注入 ctx.scratchloop.ts 在 done 前拦截未验证完成| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-50 | ✅ 已完成 | 结构化指导协议 + EvidenceBundle + 触发判定 |
packages/core/src/supervisor/types.ts、evidence.ts、triggers.ts、advice-schema.ts| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-51 | ✅ 已完成 | 用户显式配置候选池;session 8 次 / signature 2 次预算 |
packages/core/src/supervisor/pool.ts、router.ts、budget.ts、smoke.ts.deepreef/supervisor-pool.json| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-60 | ✅ 已完成 | Worker 失败 → Advice → scratch 回注 → 继续执行 |
packages/core/src/supervisor/guided-loop.tsloop.ts 工具批次后安全点请求指导engine.ts 按 HarnessProfile.supervisorPolicy 接线 supervisorGuidance| 阶段 | 状态 | 说明 |
|---|---|---|
| DRF-70 | ✅ 已完成 | 两阶段工具路由 + free/forced 模式决策 |
| DRF-80 | ✅ 已完成 | benchmark 矩阵 + 发布门禁 + overnight 检测 |
packages/core/src/tool-routing/two-stage-router.tspackages/core/src/governance/mode-decision.tspackages/core/src/benchmark/ + packages/core/scripts/benchmark-matrix.tsbun test packages/core/__tests__/two-stage-router.test.ts packages/core/__tests__/mode-decision.test.ts
bun test packages/core/__tests__/fusion-benchmark.test.ts
bun test packages/core/__tests__/supervisor*.test.ts
bun run packages/core/scripts/benchmark-matrix.ts
| 阶段 | 状态 | 说明 |
|---|---|---|
| FG-60-R | ✅ 已完成 | EngineStatusSnapshot.sessionWriter;/status 展示 queue/dropped/flushing |
| CTX-70 | 部分完成 | README 已补充 /context 说明;人工验收待项目负责人 |
| OS-12/13-R | 待验收 | 需真实 macOS/Windows 终端人工验证,见 TODO.md §8 |
packages/core/src/status.ts — 增加 sessionWriter 字段packages/core/src/engine.ts — getStatusSnapshot() 接入 sessionWriter.getStatus()packages/core/src/session.ts — cleanup unlink 失败低噪音 debugpackages/tui/src/status/format.ts — /status 展示 SESSION WRITER 区块⚠️ 2026-06-12 评估:本阶段组件已完成文件移植但未接通真实数据链路,以下标记”部分完成”。 🔄 2026-06-12 更新:TUI-FIX 任务已接入数据链路和主布局集成,以下为最新状态。
| 阶段 | 状态 | 说明 |
|---|---|---|
| TUI-GM-00 | ✅ 已完成 | 删除 OpenTUI 失败原型,清理 CLI 切换逻辑和依赖 |
| TUI-GM-10 | ⚠️ 部分完成 | 主题系统(23 文件)、语义色、ThemeManager 已移植;/theme 命令已添加但缺少独立选择菜单 UI |
| TUI-GM-20 | ⚠️ 部分完成 | 动画组件文件已移植;缺少终端失焦、低动画、测试模式和 NO_COLOR 降频机制 |
| TUI-GM-30 | ⚠️ 部分完成 | DialogManager 文件已移植;已集成到 App.tsx 主布局(Permission/Question),BridgeScrollAlerts 仍保留为备用路径 |
| TUI-GM-40 | ⚠️ 部分完成 | 多 Agent 展示组件文件已移植;OrchestrationSummary 已接入真实 Store 数据(不再使用固定空数组) |
| TUI-GM-50 | ⚠️ 部分完成 | WorkerActivityPanel 文件已移植;已导入 App.tsx 但详情视图和暂停/恢复/取消回调尚未接线 |
| TUI-GM-60 | ⚠️ 部分完成 | VirtualizedTranscript 文件已移植;按条目数量而非渲染高度计算窗口,未接入现有 ScrollBox |
| TUI-GM-70 | ⚠️ 部分完成 | 新增 17 个 OrchestrationStore 测试(86 pass / 0 fail);缺少组件渲染测试和集成测试 |
| TUI-GM-80 | ⚠️ 部分完成 | OrchestrationSummary、AgentGroupDisplay、DialogManager 已集成到 App.tsx 主布局;WorkerActivityPanel 已导入但未激活 |
删除内容:
packages/tui-opentui/ 整个目录(30 个文件)packages/cli/src/tui-wrapper.ts(OpenTUI session 隔离)packages/cli/src/tui.ts 中 OpenTUI 分支和 TUI_MODE 常量packages/cli/package.json 中 @deepreef/tui-opentui、@opentui/core、@opentui/react 依赖验收:
typecheck 通过(tui-opentui 预置错误消除)rg "tui-opentui|@opentui" 仅剩文档历史记录新增 packages/tui/src/theme/ 目录(23 个文件):
| 文件 | 说明 | 来源 |
|---|---|---|
theme.ts |
Theme 类、ColorsTheme、颜色解析、插值 | Gemini themes/theme.ts |
semantic-tokens.ts |
SemanticColors 接口(含 running/idle) | Gemini themes/semantic-tokens.ts |
semantic-colors.ts |
getter facade 委托 ThemeManager | Gemini themes/semantic-colors.ts |
color-utils.ts |
isValidColor、shouldSwitchTheme、parseColor | Gemini themes/color-utils.ts |
constants.ts |
DEFAULT_*_OPACITY 常量 | Gemini constants.ts |
theme-manager.ts |
ThemeManager 单例 | Gemini themes/theme-manager.ts |
index.ts |
模块导出 | 新建 |
builtin/dark/default-dark.ts |
Default Dark 主题 | Gemini 同名文件 |
builtin/dark/tokyonight-dark.ts |
Tokyo Night 主题 | Gemini 同名文件 |
builtin/dark/dracula-dark.ts |
Dracula 主题 | Gemini 同名文件 |
builtin/dark/github-dark.ts |
GitHub Dark 主题 | Gemini 同名文件 |
builtin/dark/solarized-dark.ts |
Solarized Dark 主题 | Gemini 同名文件 |
builtin/dark/ansi-dark.ts |
ANSI Dark 主题 | Gemini 同名文件 |
builtin/dark/ayu-dark.ts |
Ayu Dark 主题 | Gemini 同名文件 |
builtin/dark/atom-one-dark.ts |
Atom One Dark 主题 | Gemini 同名文件 |
builtin/dark/github-dark-colorblind.ts |
GitHub Dark Colorblind 主题 | Gemini 同名文件 |
builtin/light/default-light.ts |
Default Light 主题 | Gemini 同名文件 |
builtin/light/github-light.ts |
GitHub Light 主题 | Gemini 同名文件 |
builtin/light/solarized-light.ts |
Solarized Light 主题 | Gemini 同名文件 |
builtin/light/ansi-light.ts |
ANSI Light 主题 | Gemini 同名文件 |
builtin/light/ayu-light.ts |
Ayu Light 主题 | Gemini 同名文件 |
builtin/light/github-light-colorblind.ts |
GitHub Light Colorblind 主题 | Gemini 同名文件 |
builtin/no-color.ts |
No Color 降级主题 | Gemini 同名文件 |
新增依赖: tinycolor2、tinygradient、@types/tinycolor2
适配点:
@google/gemini-cli-core 依赖interpolateColor 统一从 theme.ts 导出running 和 idle 状态色新增 packages/tui/src/components/shared/ 目录(4 个文件):
| 文件 | 说明 | 来源 |
|---|---|---|
GradientSpinner.tsx |
渐变 braille spinner (~33fps) | Gemini GeminiSpinner.tsx |
RespondingSpinner.tsx |
状态感知 spinner | Gemini GeminiRespondingSpinner.tsx |
LoadingIndicator.tsx |
加载状态、耗时、取消提示 | Gemini LoadingIndicator.tsx |
ThemedGradient.tsx |
渐变标题文本 | Gemini ThemedGradient.tsx |
新增 packages/tui/src/components/dialogs/ 和 packages/tui/src/store/:
| 文件 | 说明 |
|---|---|
dialogs/DialogManager.tsx |
优先级弹窗管理器(Permission > Question > 其他) |
store/dialog-store.ts |
Dialog 状态管理 |
新增 packages/tui/src/components/agents/ 和 packages/tui/src/components/orchestration/:
| 文件 | 说明 | 来源 |
|---|---|---|
agents/AgentGroupDisplay.tsx |
Worker 组折叠/展开显示 | Gemini SubagentGroupDisplay.tsx |
agents/AgentProgressDisplay.tsx |
单个 Worker 活动详情 | Gemini SubagentProgressDisplay.tsx |
orchestration/OrchestrationSummary.tsx |
三栏总览(Workers/Supervisor/Loop) | 新建 |
新增 packages/tui/src/components/workers/:
| 文件 | 说明 | 来源 |
|---|---|---|
workers/WorkerActivityPanel.tsx |
后台 Worker 活动面板 | Gemini BackgroundTaskDisplay.tsx |
新增:
| 文件 | 说明 |
|---|---|
components/shared/VirtualizedTranscript.tsx |
虚拟化聊天记录(anchor、可见项渲染、自动滚动) |
修改 packages/tui/src/App.tsx:
OrchestrationSummary, LoadingIndicator<OrchestrationSummary>(三栏编排概览:Workers/Supervisor/Loop)DeepiMessages 与 WelcomeWhenEmpty 之间插入 <LoadingIndicator>(loading 时显示 spinner + 时间)验收:
typecheck 通过(0 错误)bun test 2325 pass,474 fail(memory 预置问题)git diff --stat 仅 packages/tui/src/App.tsx 变更(+12 行)bun run typecheck 通过(0 错误)bun test 2325 pass,474 fail(memory 预置问题)getSemanticColors() / themeManager.getColors()@google/gemini-cli-core 依赖⚠️ 2026-06-12 验收发现:以下状态已于 2026-06-12 下调至实际完成水平。
| 任务 | 状态 | 说明 |
|---|---|---|
| TUI-FIX-10 | ✅ 已完成 | Core 在 submit/loop/subagent/supervisor 生命周期节点产出 orchestration 事件;Worker 生命周期完整(starting→running→终态),elapsedMs 真实计算,session 切换清除 worker |
| TUI-FIX-20 | ✅ 已完成 | 新增 OrchestrationStore(SubscribeStore 模式),Bridge 消费 orchestration 事件;终态 Worker 上限 50 自动清理 |
| TUI-FIX-30 | ✅ 已完成 | OrchestrationSummary 读取真实 Store 数据,删除 App.tsx 固定空数组 |
| TUI-FIX-40 | ⚠️ 部分完成 | AgentGroupDisplay 已接入 App.tsx 主布局;WorkerActivityPanel 已导入但详情视图和暂停/恢复/取消回调未接线 |
| TUI-FIX-50 | ✅ 已完成 | DialogManager 已集成到 App.tsx 主布局;BridgeScrollAlerts 不再渲染 Permission/Question,由 DialogManager 独占处理 |
| TUI-FIX-60 | ✅ 已完成 | /theme 命令已添加(列表/切换主题),已持久化到 TuiSettings,启动时自动恢复;已移除 auto 推理档位 |
| TUI-FIX-70 | ❌ 未开始 | VirtualizedTranscript 需基于 ScrollBox 和真实渲染高度重写 |
| TUI-FIX-80 | ⚠️ 部分完成 | 新增 17 个 OrchestrationStore 测试(86 pass / 0 fail);缺少组件渲染测试和集成测试 |
修改文件:
packages/core/src/engine.ts — setOnOrchestrationEvent 回调;submit() 开始/结束时发射 loop_transition;spawnSubagent() 发射 worker_upsert/worker_removepackages/core/src/loop.ts — loop 入口发射 loop_transition;早停信号处发射 runtime_signal;Supervisor 指导点发射 supervisor_upsert/supervisor_advicepackages/core/src/supervisor/guided-loop.ts — 扩展返回类型包含 result/trigger 字段新增文件:
packages/tui/src/store/orchestration-store.ts — OrchestrationStore 类(基于 SubscribeStore)修改文件:
packages/tui/src/store/index.ts — 导出 OrchestrationStorepackages/tui/src/bridge.tsx — 接受可选 orchestrationStore 参数;case 'orchestration' 转发到 Storepackages/tui/src/App.tsx — 创建 OrchestrationStore 实例;连接引擎回调;Session 切换时重置支持事件类型:
worker_upsert / worker_remove — Worker 创建/移除(幂等更新)supervisor_upsert / supervisor_advice — Supervisor 状态/建议loop_transition / runtime_signal — Loop 阶段/信号checkpoint / agent_tree_upsert — 检查点/Agent 树约束:
新增文件:
packages/tui/src/components/orchestration/OrchestrationContext.tsx — React context + focused subscription hooks修改文件:
packages/tui/src/App.tsx — OrchestrationSummaryFromStore 组件;OrchestrationStoreProvider 包裹主布局修改文件:
packages/tui/src/App.tsx — 导入 DialogManager,在 scrollableContent 中渲染;Permission/Question 通过 bridgeState 传递修改文件:
packages/tui/src/commands.ts — 添加 /theme 命令类型、解析和帮助文本packages/tui/src/App.tsx — /theme 命令处理器(无参数时列表,带参数时切换)bun run typecheck — 通过(0 错误)bun test packages/tui/__tests__/ — 86 pass / 0 fail(69 原测试 + 17 新增 OrchestrationStore 测试)git diff --check — 通过(0 空白符问题)| 优先级 | 问题 | 状态 |
|---|---|---|
| P0 | ADV-HAR-07: toolRouting 仅传入 LoopOptions,runLoop 未读取/执行 | 已修复 — loop.ts 解构 toolRouting,在每轮 chatCompletionsStream 前调用 resolveToolRouting 应用工具路由决策 |
| P0 | ADV-HAR-08: verificationPolicy 的 require-or-waive 与 block 行为相同;loose 模式 Verification Gate 在入口处被 !requireVerificationBeforeFinal 跳过 |
已修复 — loop.ts 增加 require-or-waive 分支(首次豁免+重复退化硬阻断);warn 模式下即使 requireVerificationBeforeFinal=false 也进入 tryVerificationGate 产生警告 |
| P1 | Worker 生命周期事件不完整 + 终态 Worker 无限累积 | 已修复 — elapsedMs 跟踪 workerStartedAt;工具错误仅重复/严重时标记失败;OrchestrationStore 终态 Worker 上限 50(超出时删除最旧);loadSession 时发射 worker_remove:”*” |
基于 ADVICE.md ADV-HAR-01~08 任务,实现三档 Harness 严格度体系并在 Engine 中集中接线。
| 任务 | 状态 | 说明 |
|---|---|---|
| ADV-HAR-01 | ✅ 已完成 | HarnessStrictness 类型 + EffectiveHarnessPolicy + 优先级解析器 + /harness TUI 菜单 |
| ADV-HAR-02 | ✅ 已完成 | Engine.submit() 入口固化策略,传递 effectivePolicy 到 LoopOptions |
| ADV-HAR-03 | ✅ 已完成 | 根据 shellPolicy 自动启用 dual-track bash 工具 |
| ADV-HAR-04 | ✅ 已完成 | Supervisor 池默认空,用户必须显式配置 .deepreef/supervisor-pool.json |
| ADV-HAR-05 | ✅ 已完成 | ReadTracker 按 readBeforeWrite 策略分级(block/warn/off) |
| ADV-HAR-06 | ✅ 已完成 | EarlyStopDetector 按 earlyStop 策略分级(aggressive/standard/critical-only) |
| ADV-HAR-07 | ✅ 已完成 | toolRouting 策略传入 LoopOptions + runLoop 解构并在每轮通过 resolveToolRouting 应用 |
| ADV-HAR-08 | ✅ 已完成 | verificationPolicy 策略传入 LoopOptions + runLoop 实现三态分支(block/require-or-waive/warn) |
新增文件:
packages/core/src/harness/strictness.ts — resolveHarnessStrictness()、readProjectHarnessConfig()、resolveDefaultStrictness()packages/core/src/harness/policy.ts — resolveEffectiveHarnessPolicy()、getBasePolicy()packages/core/src/harness/config.ts — 读写 .deepreef/harness.json修改文件:
packages/core/src/model-profile/types.ts — 新增 HarnessStrictness、EffectiveHarnessPolicy、ProjectHarnessConfig 类型packages/tui/src/App.tsx — /harness 命令(显示当前策略/设置严格度)packages/tui/src/commands.ts — /harness 命令解析packages/tui/src/CommandRegistry.ts — /harness 自动补全优先级链: session > project.modelOverrides[global] > project.global > model-profile.default
修改文件:
packages/core/src/engine.ts — submit() 入口调用 resolveEffectiveHarnessPolicy(),存储为 this.effectivePolicy,传递到 LoopOptionspackages/core/src/loop.ts — LoopOptions 新增 effectivePolicy 字段修改文件:
packages/core/src/engine.ts — 根据 effectivePolicy.shellPolicy 决定是否传递 shellTool 到 createDefaultToolspackages/tools/src/index.ts — createDefaultTools() 接受可选 shellTool 参数修改文件:
packages/core/src/supervisor/pool.ts — loadSupervisorPool() 无配置文件时返回空对象(不加载默认候选)修改文件:
packages/core/src/engine.ts — 根据 effectivePolicy.readBeforeWrite 策略实例化 ReadTracker修改文件:
packages/core/src/engine.ts — 根据 effectivePolicy.earlyStop 配置 EarlyStopDetector.repetitionThreshold(aggressive=2, standard=3, critical-only=5)修改文件:
packages/core/src/loop.ts — LoopOptions 新增 toolRouting 和 verificationPolicy 字段packages/core/src/engine.ts — 传递这两个策略到 loop| 策略 | strict | normal | loose |
|---|---|---|---|
| shellPolicy | off | dual-track | dual-track |
| readBeforeWrite | block | warn | off |
| earlyStop | aggressive | standard | critical-only |
| toolRouting | two-stage | auto | direct |
| verification | block | require-or-waive | warn |
| supervisorPolicy | guided | on | off |
| approval | full-auto | full-auto | ask-before |
bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/harness-strictness.test.ts — 19 pass / 0 failbun test packages/core/__tests__/engine-tools.test.ts — 29 pass / 0 fail(含 ADV-HAR-02 集成测试)bun test packages/core/__tests__/supervisor-pool.test.ts — 13 pass / 0 fail(含 ADV-HAR-04 空池测试)验收发现 5 个问题,全部修复。
| 问题 | 级别 | 说明 | 状态 |
|---|---|---|---|
| ADV-HAR-07/08 未生效 | P0 | toolRouting/verificationPolicy 传入 loop 但从未读取 |
✅ 已修复 |
| 未知本地模型未自动 strict | P0 | inferDefaultStrictness() 收到 null,永远返回 normal |
✅ 已修复 |
| Harness 配置缺 Zod 校验 | P0 | 非法 JSON(如 {"strictness":"invalid"})直接强转,可崩溃 |
✅ 已修复 |
| orchestration 事件破坏测试 | P1 | 新增首个 orchestration 事件改变了事件顺序,2 个测试失败 | ✅ 已修复 |
| Worker 生命周期事件不完整 | P1 | submit() 开头 worker_remove: "*" 立即删除完成状态 |
✅ 已修复 |
修改文件:
packages/core/src/loop.ts — tryVerificationGate() 现在读取 verificationPolicy:
"block": 硬阻断,必须验证"require-or-waive": 要求验证或用户豁免"warn": 仅发出 verification_gate_warning,不阻断修改文件:
packages/core/src/engine.ts — submit() 调用 resolveModelProfile() 获取 modelProfile,传递给 resolveHarnessStrictness()inferDefaultStrictness() 现在能正确识别 unknown-local 模型并返回 "strict"修改文件:
packages/core/src/harness/strictness.ts — 新增 ProjectHarnessConfigSchema(Zod)readProjectHarnessConfig() 使用 safeParse() 校验,非法配置返回 null + console.warnSchema 定义:
const ProjectHarnessConfigSchema = z.object({
strictness: z.enum(["strict", "normal", "loose"]).optional(),
modelOverrides: z.record(z.string(), z.enum(["strict", "normal", "loose"])).optional(),
}).strict()
修改文件:
packages/core/__tests__/engine-tools.test.ts — P2-2、LIFE-01 测试现在跳过 orchestration 和 strategy_notify 事件修改文件:
packages/core/src/engine.ts — 移除 submit() 开头的 worker_remove: "*"starting → running → waiting_permission/question → completed/failed/cancelledworker_remove 仅在 session 切换时调用bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/engine-tools.test.ts — 29 pass / 0 failbun test packages/core/__tests__/harness-strictness.test.ts — 19 pass / 0 fail将 Deepreef TUI 的配色和布局升级为 /vol4/Agent/new_tui 风格。
| 任务 | 状态 | 说明 |
|---|---|---|
| TUI-STYLE-01 | ✅ 已完成 | reasonix/tokens.ts — 全面替换配色为 new_tui 暗色调板 |
| TUI-STYLE-02 | ✅ 已完成 | FullscreenLayout.tsx — 移除硬编码背景色(透明),保留 ─ 分隔线 |
| TUI-STYLE-03 | ✅ 已完成 | StatusBar.tsx — 顶部 ─ 分隔线 + 紧凑信息排列 |
| TUI-STYLE-04 | ✅ 已完成 | DeepiMessages.tsx — 用户前缀 > / 助手前缀 ●(紫) |
| TUI-STYLE-05 | ✅ 已完成 | StreamingCard.tsx — 完成态前缀 ●(紫) |
| TUI-STYLE-06 | ✅ 已完成 | OrchestrationSummary.tsx — 完整重写为 new_tui 卡片风格(左侧 accent 色条 + 大写 Badge 标签) |
| TUI-STYLE-07 | ✅ 已完成 | useMessageScroll.ts — 恢复消息区滚轮/PageUp/PageDown/Ctrl+方向键滚动;用户上滚后锁定视口 |
| TUI-STYLE-08 | ✅ 已完成 | 消息区优先消费滚轮事件,防止滚轮触发输入历史导航 |
修改文件: packages/tui/src/reasonix/tokens.ts
| 令牌 | 原值 | 新值(new_tui) |
|---|---|---|
fg.strong |
#ffffff |
#e0e0e0 |
fg.body |
#E1D3DC |
#85a9ff |
fg.sub |
#8D7B88 |
#9ca3af |
fg.meta |
#8D7B88 |
#6b7280 |
fg.faint |
#5D5159 |
#4b5563 |
tone.brand |
#00FF66 |
#3b82f6 |
tone.accent |
#4A90E2 |
#a855f7 |
tone.ok |
#00FF66 |
#00ff41 |
tone.warn |
#FFBD2E |
#f59e0b |
tone.err |
#FF5F56 |
#ef4444 |
tone.info |
#4A90E2 |
#3b82f6 |
surface.bg |
#000000 |
#050505 |
surface.bgInput |
#653a99be |
#0c0c0c |
surface.bgCode |
#0C0C0C |
#0c0c0c |
surface.bgElev |
#13283F |
#0a0a0a |
修改文件:
packages/tui/src/FullscreenLayout.tsx — 移除所有 backgroundColor 硬编码(#050505/#0a0a0a),布局背景透明继承终端默认背景;保留顶部/底部 ─ 分隔线packages/tui/src/StatusBar.tsx — 添加顶部 ─ 分隔线;信息排列:agent(蓝) · provider/model(灰) · [thinking](紫) | tokens修改文件:
packages/tui/src/DeepiMessages.tsx — 用户消息前缀改为 > (蓝)、助手消息前缀改为 ●(紫)packages/tui/src/reasonix/StreamingCard.tsx — 完成态前缀改为 ●(紫)匹配助手消息修改文件: packages/tui/src/components/orchestration/OrchestrationSummary.tsx
完整重写,核心变化:
getSemanticColors() 切换为 FG/TONE/SURFACE 令牌体系AcctCard),匹配 new_tui 的 border-l 风格RUNNING/DONE/FAILED 等)WORKERS/SUPERVISOR/LOOP)active/total 统计修改文件:
packages/tui/src/useMessageScroll.ts — 消息区处理滚轮、PageUp/PageDown、Ctrl+方向键、Home/End;滚动事件消费后不再进入输入框packages/tui/src/fullscreen.ts / packages/tui/src/App.tsx — Alternate Screen 默认开启 SGR 鼠标跟踪,使内部 ScrollBox 能收到滚轮事件bun run typecheck — 通过(0 错误)bun test packages/tui/__tests__/ — 91 pass / 0 failWelcomeScreen.tsx)未做改动修复 Alternate Screen 中滚轮无法查看历史消息、反而触发上一条命令,以及流式字符输出强制跳回最新消息的问题。
| 任务 | 状态 | 说明 |
|---|---|---|
| TUI-SCROLL-01 | ✅ 已完成 | Alternate Screen 默认启用 SGR 鼠标跟踪,滚轮事件能够进入 Ink |
| TUI-SCROLL-02 | ✅ 已完成 | 消息区优先消费 wheelUp/wheelDown,不再触发输入框历史命令 |
| TUI-SCROLL-03 | ✅ 已完成 | 恢复滚轮、PageUp/PageDown、Ctrl+Up/Down、Home/End 消息滚动 |
| TUI-SCROLL-04 | ✅ 已完成 | 用户向上滚动后解除 sticky,流式输出期间锁定当前视口 |
| TUI-SCROLL-05 | ✅ 已完成 | 滚回底部或按 End 后恢复 sticky 自动跟随 |
| TUI-SCROLL-06 | ✅ 已完成 | 新增消息滚动策略回归测试 |
ScrollBox 滚动App.tsx 硬编码 mouseTracking={false},Ink 无法收到真实 wheelUp/wheelDownuseMessageScroll.ts 删除了滚轮和翻页处理,只保留 Home/Endpackages/tui/src/fullscreen.ts
isMouseTrackingEnabled() 默认返回 trueDEEPCODE_ENABLE_MOUSE=0 显式关闭能力packages/tui/src/App.tsx
<AlternateScreen> 使用 isMouseTrackingEnabled()packages/tui/src/useMessageScroll.ts
applyMessageScrollKey() 统一滚动策略ScrollBox.scrollBy(),自动解除 stickyscrollToBottom() 恢复 stickystopImmediatePropagation() 阻止滚轮继续进入输入框packages/tui/src/DeepiMessages.tsx
packages/tui/__tests__/message-scroll.test.ts
bun test packages/tui/__tests__/ — 91 pass / 0 failbun run typecheck — 通过(0 错误)git diff --check — 通过将现有全局或单会话 Agent 配置升级为两套永久角色配置(Worker/Supervisor),支持独立的模型、Harness、Thinking、工具权限和能力配置。
packages/core/src/agent-profile/types.ts — 新增
AgentRole、HarnessStrictness、ThinkingMode 类型AgentRoleProfile 和 AgentProfilesConfig 接口DEFAULT_AGENT_PROFILES 默认配置packages/core/src/agent-profile/schema.ts — 新增
validateAgentProfiles() 验证函数packages/core/src/agent-profile/store.ts — 新增
loadAgentProfiles() 配置加载saveAgentProfiles() 配置保存getAgentProfile() 和 updateAgentProfile() 查询更新ui-settings.json 旧格式迁移packages/core/src/agent-profile/index.ts — 新增
packages/core/__tests__/agent-profile.test.ts — 新增
.deepreef/agents.json.deepreef/ui-settings.json(自动迁移).deepreef/agents.json 时检测旧格式并自动转换bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/agent-profile.test.ts — 14 pass / 0 failgit diff --check — 通过contextWindow 必须 clamp 到 ModelTarget 声明窗口(后续 DA-10 实现)worker/supervisor;旧名称只读兼容共享加载底层能力,按角色配置过滤暴露工具、Plugin、MCP server 和 Skill。Supervisor 的工具权限由用户配置决定,不硬编码只读。
packages/core/src/capability-catalog/types.ts — 新增
Capability、CapabilitySource、CapabilityCatalogSnapshot 类型RoleCapabilityViewOptions 接口packages/core/src/capability-catalog/catalog.ts — 新增
CapabilityCatalog 类,统一管理所有能力RoleCapabilityView 类,按角色配置过滤工具packages/core/src/capability-catalog/index.ts — 新增
packages/core/__tests__/capability-catalog.test.ts — 新增
packages/core/src/capability-catalog/@deepreef/core.deepreef/agents.json 来调整bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/capability-catalog.test.ts — 19 pass / 0 failgit diff --check — 通过将单一 ReasonixEngine.currentAgent 模式升级为 DualAgentRuntime,Worker 和 Supervisor 分别持有独立的 ChatClient、ContextManager、消息历史和运行状态。
packages/core/src/dual-agent-runtime/types.ts — 新增
AgentRuntimeStatus、AgentRuntimeState、DualAgentRuntimeConfig 类型WorkflowState、WorkflowPhase、SendToOptions、InterruptRoleOptions 类型packages/core/src/dual-agent-runtime/runtime.ts — 新增
AgentRuntime 类,单个角色的运行时packages/core/src/dual-agent-runtime/dual-runtime.ts — 新增
DualAgentRuntime 类,管理 Worker 和 Supervisor 两个运行时sendTo(role, input) 方法,向指定角色发送消息interruptRole(role) 方法,中断指定角色getState(role) 方法,获取指定角色状态transitionWorkflow(to) 方法,管理工作流状态机packages/core/src/dual-agent-runtime/index.ts — 新增
packages/core/__tests__/dual-agent-runtime.test.ts — 新增
packages/core/src/dual-agent-runtime/@deepreef/corebun run typecheck — 通过(0 错误)bun test packages/core/__tests__/dual-agent-runtime.test.ts — 12 pass / 0 failgit diff --check — 通过实现固定工作流状态机,管理 Supervisor analyse → Worker do → Worker report → Supervisor check 流程,支持版本化通信和 Advice 采用/拒绝。
packages/core/src/workflow-coordinator/types.ts — 新增
WorkflowPhase、WorkflowDecision、WorkflowConfig 类型WorkflowLoopState、WorkflowEvidence、WorkflowSupervisorAdvice 类型WorkflowCheckpoint、StartWorkflowOptions、WorkflowEvent 类型packages/core/src/workflow-coordinator/coordinator.ts — 新增
WorkflowCoordinator 类,管理工作流状态机startWorkflow(goal) 方法,启动工作流transition(to) 方法,转换工作流阶段applyAdvice(advice) 方法,采用 Supervisor 建议saveCheckpoint() 和 restoreCheckpoint() 方法,支持检查点保存和恢复packages/core/src/workflow-coordinator/index.ts — 新增
packages/core/__tests__/workflow-coordinator.test.ts — 新增
packages/core/src/workflow-coordinator/@deepreef/corebun run typecheck — 通过(0 错误)bun test packages/core/__tests__/workflow-coordinator.test.ts — 21 pass / 0 failgit diff --check — 通过实现双角色 Session 持久化和恢复,支持 Worker 和 Supervisor 独立消息历史、Workflow checkpoint 和 Advice 采用/拒绝记录。
packages/core/src/dual-session/types.ts — 新增
DualSessionConfig、RoleSessionState、DualSessionSnapshot 类型AdviceHistoryEntry、SessionCheckpoint、DualSessionOptions 类型packages/core/src/dual-session/session.ts — 新增
DualSession 类,管理双角色 Sessionpackages/core/src/dual-session/store.ts — 新增
DualSessionStore 类,Session 持久化存储packages/core/src/dual-session/index.ts — 新增
packages/core/__tests__/dual-session.test.ts — 新增
packages/core/src/dual-session/@deepreef/corebun run typecheck — 通过(0 错误)bun test packages/core/__tests__/dual-session.test.ts — 19 pass / 0 failgit diff --check — 通过实现 TUI 双角色 Tab 系统和 Workflow 状态栏,支持 Worker 和 Supervisor 独立对话、Tab 切换和状态显示。
packages/tui/src/components/workflow/WorkflowStatusBar.tsx — 新增
WorkflowStatusBar 组件,显示 Workflow 状态| 第二行:Supervisor | Worker | goal 三段卡片 |
packages/tui/src/components/workflow/DualTabSystem.tsx — 新增
DualTabSystem 组件,管理双角色 Tab 系统packages/tui/src/components/workflow/index.ts — 新增
packages/tui/src/index.ts — 更新
packages/tui/__tests__/workflow-components.test.ts — 新增
packages/tui/src/components/workflow/@deepreef/tuibun run typecheck — 通过(0 错误)bun test packages/tui/__tests__/workflow-components.test.ts — 5 pass / 0 failgit diff --check — 通过清理旧模式依赖,更新帮助文本和命令说明,确保双角色模式成为主架构。
packages/core/src/engine.ts — 更新
currentAgent、thinkingMode、activeSkills、sessionStrictness 标记为 deprecatedpackages/tui/src/commands.ts — 更新
/agent 命令为 deprecatedpackages/tui/src/CommandRegistry.ts — 更新
/agent 命令描述为 deprecatedpackages/tui/src/ChoiceMenu.tsx — 更新
packages/tui/src/i18n/en.ts — 更新
cmdAgent 描述为 deprecatedpackages/core/src/、packages/tui/src/@deepreef/core、@deepreef/tui/agent 命令标记为 deprecatedbun run typecheck — 通过(0 错误)bun test packages/core/__tests__/engine-status.test.ts packages/core/__tests__/engine-tools.test.ts — 37 pass / 0 failgit diff --check — 通过ReasonixEngine.currentAgent、全局 thinkingMode、全局 activeSkills 和单一 sessionStrictness 的依赖build/plan 仅保留一个版本周期的读取迁移适配器为 Agent Profile 启用 Zod 严格校验,拒绝未知字段,强制角色字段匹配,确保配置安全。
packages/core/src/agent-profile/schema.ts — 更新
z.object() 改为 z.strictObject(),拒绝未知字段refine() 验证,强制 worker.role === "worker" 和 supervisor.role === "supervisor"packages/core/src/agent-profile/@deepreef/corez.strictObject() 拒绝未知字段,防止配置污染refine() 确保角色字段与键名匹配bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/da-r0-baseline.test.ts — 7 pass / 5 fail(Agent Profile 测试全部通过)git diff --check — 通过将 CapabilityCatalog 接入真实启动链路,强制 Supervisor 只读,确保角色安全边界。
packages/core/src/capability-catalog/catalog.ts — 更新
RoleCapabilityView.computeFilteredTools() 中添加 Supervisor 只读强制supervisor 时,只保留 tier === "read" 的工具packages/core/src/capability-catalog/@deepreef/coreCapability.tier 字段进行过滤,而不是工具名称猜测bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/da-r0-baseline.test.ts — 7 pass / 5 fail(Supervisor 测试全部通过)git diff --check — 通过修复 AgentRuntime 和 DualAgentRuntime 的配置参数传递、上下文管理和统计跟踪问题。
runtime.ts)config 参数到 AgentRuntimeOptionsapiKey、baseUrl、model、maxTokens、temperature、providerctx.prefix.build(systemPrompt)reset() 方法创建新的 ContextManager 实例ctx.getMaxRounds() 和 ctx.getContextWindow() 获取参数stats 类型对齐为 SessionStatsusage 事件的嵌套结构dual-runtime.ts)workerConfig 和 supervisorConfig 参数AgentRuntimeDualAgentRuntimeConfig 类型导入| 缺陷 | 修复方式 |
|---|---|
AgentRuntime 使用硬编码空字符串 |
添加 config 参数支持 |
reset() 访问私有属性 |
使用公开的 getter 方法 |
usage 事件结构不匹配 |
正确访问 event.usage.promptTokens |
DualAgentRuntime 配置不完整 |
添加完整的配置参数 |
reset() 通过重建 ContextManager 而非清除现有实例SessionStats 类型确保统计字段一致bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/da-r0-baseline.test.ts — 9 pass / 3 fail
DualAgentRuntime 可正确创建并接受配置AgentRuntime.reset() 正确重置上下文和统计修复 WorkflowCoordinator 的状态转换验证、返回值和轮次限制问题。
workflowId 和 maxRounds 参数maxRounds 可覆盖配置默认值void 改为 { success: boolean; error?: string }isValidTransition() 私有方法idle → supervisor_analyse, blocked, completed, failed
supervisor_analyse → worker_do, blocked, completed, failed
worker_do → worker_report, blocked, completed, failed
worker_report → supervisor_check, blocked, completed, failed
supervisor_check → supervisor_analyse, blocked, completed, failed
blocked → supervisor_analyse, completed, failed
completed → (无)
failed → (无)
iteration < maxRoundscompleted 或 failed| 缺陷 | 修复方式 |
|---|---|
transition 返回 void |
返回 { success, error } |
| 非法转换不被拒绝 | 添加转换验证 |
canContinue 不检查状态 |
添加状态检查 |
startWorkflow 不接受 maxRounds |
扩展参数 |
maxRounds 参数可覆盖配置默认值bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/da-r0-baseline.test.ts — 11 pass / 1 fail
修复 DualSessionStore 的路径穿越漏洞,确保 session ID 安全性。
添加 validateSessionId() 私有方法,验证以下规则:
.. 的 ID/ 或 \ 的 ID/ 开头的 IDX:\ 格式开头的 ID(Windows 路径)\0(null 字节)的 ID%、&、? 的 ID(URL 编码字符)getSessionPath() 方法调用验证save() 方法在验证失败时抛出异常delete() 方法在验证失败时抛出异常| 缺陷 | 修复方式 |
|---|---|
路径穿越 ../../etc/passwd |
拒绝包含 .. 和路径分隔符的 ID |
绝对路径 /etc/passwd |
拒绝以 / 开头的 ID |
Windows 路径 C:\Windows |
拒绝以盘符开头的 ID |
validateSessionId() 方法bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/da-r0-baseline.test.ts — 12 pass / 0 fail ✅
将 DualTabSystem 和 WorkflowStatusBar 真正接入 App.tsx,实现双角色交互和状态显示。
DualTabSystem 和 WorkflowStatusBarAgentRole、WorkflowPhase、WorkflowStateactiveRole 状态(worker/supervisor)workerMessages 和 supervisorMessages 独立消息列表workerDraft 和 supervisorDraft 独立草稿workerScrollPosition 和 supervisorScrollPosition 独立滚动位置workflowState 工作流状态isOverlayActive 检测所有覆盖层状态| 功能 | 实现方式 |
|---|---|
| Tab 切换 | Tab 键切换 Worker/Supervisor |
| 独立消息 | 两角色独立消息列表 |
| 独立草稿 | 两角色独立草稿保存 |
| 独立滚动 | 两角色独立滚动位置 |
| 覆盖层禁用 | 覆盖层激活时禁用 Tab 切换 |
| 状态栏固定 | WorkflowStatusBar 固定在输入框上方 |
bun run typecheck — 通过(0 错误)bun test packages/core/__tests__/da-r0-baseline.test.ts — 12 pass / 0 fail ✅bun test packages/tui/__tests__/workflow-components.test.ts — 22 pass / 0 fail ✅完成双角色运行时的端到端测试,验证所有组件集成正确,并建立发布门禁。
创建 packages/core/__tests__/da-r7-e2e.test.ts,覆盖以下场景:
transition 方法现在为 failed 状态设置 blockedReason| 测试套件 | 结果 |
|---|---|
da-r0-baseline.test.ts |
12 pass / 0 fail ✅ |
da-r7-e2e.test.ts |
18 pass / 0 fail ✅ |
workflow-components.test.ts |
22 pass / 0 fail ✅ |
bun run typecheck |
通过 ✅ |
DA-R 系列任务(DA-R0 到 DA-R7)已完成双角色运行时的修复、集成和验证。
| 任务 | 描述 | 章节 | 状态 |
|---|---|---|---|
| DA-R0 | 基线、失败测试与完成状态纠正 | §53 | ✅ |
| DA-R1 | Agent Profile 严格校验与安全迁移 | §53 | ✅ |
| DA-R2 | CapabilityCatalog 接线与角色安全边界 | §54 | ✅ |
| DA-R3 | 双 Runtime 真实执行能力与主路径接线 | §55 | ✅ |
| DA-R4 | 唯一 WorkflowCoordinator 与治理闭环 | §56 | ✅ |
| DA-R5 | 双角色 Session 安全持久化与恢复 | §57 | ✅ |
| DA-R6 | TUI 双角色交互和状态栏真实接线 | §58 | ✅ |
| DA-R7 | 旧路径迁移、端到端测试与发布门禁 | §59 | ✅ |
| 测试套件 | 测试数 | 通过 | 失败 | 状态 |
|---|---|---|---|---|
da-r0-baseline.test.ts |
12 | 12 | 0 | ✅ |
da-r7-e2e.test.ts |
18 | 18 | 0 | ✅ |
workflow-components.test.ts |
22 | 22 | 0 | ✅ |
| 总计 | 52 | 52 | 0 | ✅ |
所有发布门禁验证通过:
| 修复 | 描述 | 文件 |
|---|---|---|
| Zod 严格校验 | 使用 z.strictObject() 拒绝未知字段 |
agent-profile/schema.ts |
| Supervisor 只读 | RoleCapabilityView 强制 Supervisor 只读 |
capability-catalog/catalog.ts |
| 配置注入 | AgentRuntime 支持配置参数注入 |
dual-agent-runtime/runtime.ts |
| 转换验证 | WorkflowCoordinator 验证合法转换 |
workflow-coordinator/coordinator.ts |
| 路径穿越防护 | DualSessionStore 拒绝恶意路径 |
dual-session/store.ts |
| TUI 集成 | DualTabSystem 和 WorkflowStatusBar 接入 App.tsx |
tui/src/App.tsx |
| 提交 | 描述 |
|---|---|
858da6a |
feat: implement DA-00 to DA-60 dual-role runtime upgrade |
72b0a3d |
feat: complete DA-R1 through DA-R5 fixes |
95f61eb |
feat: complete DA-R6 TUI dual-role integration |
d820218 |
feat: complete DA-R7 end-to-end tests and release gate |
双角色运行时已完整集成并通过所有验证门禁。Worker 和 Supervisor 拥有独立的上下文、配置和能力边界,工作流协调器正确管理状态转换和轮次限制,Session 持久化安全可靠,TUI 正确接线双角色交互。
TODO.md 逐项对照packages/、examples/、types/ 源代码 Bug 审查| 状态 | 数量 | 条目 |
|---|---|---|
| 已完成 | 5 | DA-01(类型部分)、DA-02、DA-03、DA-04、DA-05(组件部分) |
| 部分完成 | 2 | DA-01(示例文件缺失)、DA-05(未与引擎连通) |
| 未完成/未开始 | 7 | DA-01(示例文件)、DA-06、DA-R7~DA-R12 |
核心发现: DA-01~DA-05 的基础数据结构、运行时类、TUI 组件已实现并有单元测试覆盖,但 DA-06(引擎端集成)完全缺失,导致整个双角色架构无法在实际对话流程中激活。
| 属性 | 描述 |
|---|---|
| 严重程度 | 中 |
| 位置 | packages/core/src/dual-agent-runtime/runtime.ts 第 115-134 行 |
| 问题 | submit() 方法中检查 event.type === "text_delta"、"done"、"usage",但测试 mock 中使用的是 { type: "delta" } 和 { type: "final" }。实际运行会静默失败 |
| 建议 | 统一事件类型契约,对齐 engine.ts 中 runLoop 使用的 LoopEvent 类型 |
| 属性 | 描述 |
|---|---|
| 严重程度 | 中 |
| 位置 | TUI: WorkflowStatusBar.tsx;Core: workflow-coordinator/types.ts |
| 问题 | TUI 侧 WorkflowPhase 包含 'continue'、'revise'、'approve'、'ask_user',但 core 侧为不同定义 |
| 建议 | 统一 WorkflowPhase 定义 |
| 属性 | 描述 |
|---|---|
| 严重程度 | 高 |
| 位置 | packages/tui/src/App.tsx 第 359-370 行 |
| 问题 | workerMessages、supervisorMessages、workflowState 均通过 useState 初始化为空,且从未被更新 |
| 建议 | 在 DA-06 集成完成前,通过 feature flag 控制 DualTabSystem 的显示 |
| 属性 | 描述 |
|---|---|
| 严重程度 | 低 |
| 位置 | packages/core/src/dual-agent-runtime/dual-runtime.ts 第 93-94 行 |
| 问题 | getState() 返回的 stats 和 messages 可能反映不同时刻的快照 |
| 建议 | 使用统一的 snapshot() 方法一次性捕获状态快照 |
| 属性 | 描述 |
|---|---|
| 严重程度 | 中 |
| 位置 | packages/core/src/question/service.ts 第 55-63 行 |
| 问题 | ask() 返回一个永不超时的 Promise,可能永久挂起 |
| 建议 | 添加可配置的超时机制(如默认 120 秒) |
| 任务 | 描述 | 优先级 |
|---|---|---|
| DA-06 | 引擎端集成 | P0 |
| DA-R7b | ask_user 多路分发 | P0 |
| DA-R8 | 执行拆分 | P0 |
| DA-R9 | 角色工具权限隔离 | P1 |
| DA-R10 | Supervisor Plan 预览 | P2 |
| DA-R11 | Worker 死循环检测 | P2 |
| DA-R12 | 对话历史线程化 | P3 |
| 属性 | 描述 |
|---|---|
| 修复日期 | 2026-06-13 |
| 修复提交 | 95d3dcf |
| 修复内容 | 测试 mock 事件类型对齐 client.ts 中 DeepSeekStreamEvent 定义 |
| 修改文件 | packages/core/__tests__/dual-agent-runtime.test.ts |
修复详情:
{ type: "delta", content: response } → { type: "text_delta", delta: response }{ type: "final", content: response } → { type: "done", finishReason: null }| Bug | 优先级 | 描述 |
|---|---|---|
| Bug #2 | P1 | WorkflowPhase 类型不一致 |
| Bug #3 | P1 | DualTabSystem 数据源孤立 |
| Bug #4 | P3 | getState 快照一致性不足 |
| Bug #5 | P3 | QuestionService.ask() 无超时机制 |
| 优先级 | 任务 | 说明 |
|---|---|---|
| P0 | DA-06 引擎集成 | 将 DualAgentRuntime 接入 engine.submit() |
| P0 | 修复 Bug #1 | 事件类型不匹配会静默失败 |
| P1 | 修复 Bug #2 | 统一 WorkflowPhase 类型定义 |
| P1 | DA-R9 角色工具隔离 | 按角色分配不同工具集 |
| P1 | DA-R7 ask_user 路由 | 将 question 请求路由到对应 Worker |
| P2 | DA-R10 plan 预览 | 复用 WorkflowCoordinator.supervisorPlan |
| P2 | DA-R11 循环检测 | 添加重复工具调用模式检测 |
| P3 | DA-R12 历史线程化 | 新增 assignHistoryThread |
| P3 | DA-01 示例文件 | 创建 examples/dual-agent-basic.ts |
| P3 | Bug #5 超时 | 为 QuestionService 添加超时机制 |
本节记录围绕”双角色(Worker/Supervisor)可独立配置模型与 Agent 身份”这一主线落地的 TUI 改动与配套修复。所有提交均在本地 windev 分支,未推送远程。
| 提交 | 标题 | 范围 |
|---|---|---|
0089504 |
feat(tui): remove orchestration summary panel, surface loop count in status bar | TUI |
48707af |
feat(tui): per-role model config + remove DualTabSystem visual indicator | core/cli/tui |
f3f8032 |
feat: per-role agent identity binding + remove build/plan | core/tui |
ec19348 |
fix(dual-runtime): requiresApiKey must honor requiresKey:false providers | core |
目标:移除消息区顶部占用纵向空间的 Workers | Supervisor | Loop 三栏卡片(显示 No active workers / No supervisor / OBSERVE),保留 loop 轮次计数。
实现边界:
packages/tui/src/components/orchestration/OrchestrationSummary.tsx(237 行)。OrchestrationContext.tsx 原从此文件导入 SupervisorDisplayData / SummaryLoopPhase 两个类型,改为就地内联定义,保持 useOrchestrationLoop() 等 hook 签名不变。App.tsx 删除 OrchestrationSummaryFromStore 包装组件及其渲染调用;清理不再使用的 useOrchestrationSupervisors 导入。StatusBar.tsx 新增可选 loopAttempt?: number prop,渲染为 Loop #N(brand 蓝加粗)。订阅点放在 BridgeStatusBar 内部(它在 OrchestrationStoreProvider 之内,符合 Context 规则),避免在 App 顶层 hook 跨 Provider 边界。关键 Bug 修复:初版误将 useOrchestrationLoop() 放在 App 组件体(Provider 之外),运行时抛 useOrchestrationStore must be used within OrchestrationStoreProvider。改由 BridgeStatusBar 内部订阅修复。
保留限制:
OrchestrationStore / OrchestrationContext / 各 hook 完全保留,AgentGroupDisplay 等其它组件不受影响。Loop #3(纯数字轮次,不含 phase)。目标:移除消息区顶部两个并排的 Supervisor | Worker 圆角框,但保留 Tab 键切换角色的交互能力。
实现边界:
packages/tui/src/components/workflow/DualTabSystem.tsx。useInput 监听从被删组件挪到 App.tsx,放在 isOverlayActive 定义之后(无覆盖层时响应 Tab),切换逻辑 setActiveRole(prev => prev === 'worker' ? 'supervisor' : 'worker') 不变。AgentRole 类型原由 DualTabSystem 导出,删除后内联进 App.tsx(底部 WorkflowStatusBar 自行内联同名联合类型,不依赖此处)。components/workflow/index.ts 与 tui/src/index.ts 清理对 DualTabSystem / TabHeader / DualTabSystemProps / TabState 的重导出。WorkflowStatusBar 的 Supervisor/Worker 卡片显示。保留限制:
activeRole 状态、bridge.submit 的 role 路由逻辑不动。目标:worker 和 supervisor 各自持有独立的 model/provider 配置,/model 与底部状态栏跟随当前 activeRole,配置持久化到磁盘,向后兼容旧的单模型配置。
决策: Tab 切换既切消息路由,也切显示/配置上下文;持久化新建 .deepreef/role-config.json,不动 last-config.json(作为单模型 fallback)。
实现边界(跨 core / cli / tui 三包):
getModel(): string 与 getProvider(): string 公共 getter(config 原为 private 且无任何访问器)。RoleConfig 类型、RoleConfigSchema、saveRoleConfig(role, cfg)、loadRoleConfig(role)。文件 .deepreef/role-config.json 结构为 { worker: {provider, model, baseUrl}, supervisor: {...} },部分写入(读-改-写,保留另一 role)。apiKey 不持久化。saveRoleConfig / loadRoleConfig / RoleConfig。loadRoleConfig("worker") / ("supervisor"),若存在则覆盖到各自 config 块;worker 引擎热更新,supervisor 引擎按其 role config 独立创建。roleConfig: Record<'worker'|'supervisor', {provider, model}> 状态;activeModel/activeProvider 改为从 roleConfig[activeRole] 派生。handleModelSelect 改为 role-aware:activeRole === 'supervisor' && dualRuntime 时取 dualRuntime.getSupervisor().getEngine(),否则 worker engine;更新 roleConfig + saveRoleConfig + saveLastConfig(后者作全局 fallback)。ModelPicker 组件无需改动 —— 通过 currentProvider/currentModel props 自动接收 per-role 派生值。保留限制:
last-config.json 及其读写逻辑不动(向后兼容 fallback)。bridge.tsx 的 submit 路由逻辑不动(已正确按 role 分发)。role-config.json → loadRoleConfig 返回 null → 两 role 都用全局 config(与改动前行为一致)。目标:每个 role 绑定独立的 Agent 身份(system prompt),Tab 切换时状态栏最左侧显示该 role 绑定的 Agent 名,/agent 针对当前 role 绑定。删除 build/plan 原生身份,原生只留 worker/supervisor。
决策: per-role agent 持久化复用现有 .deepreef/agents.json(agent-profile 系统),给 AgentRoleProfile 加 agent 字段;不塞进 role-config.json(那个只管 model)。
实现边界(core + tui 两包):
build/plan 注册;重写 worker 的 system prompt(更实质的执行型描述,工具集仍取自 MAIN_MODES.build.toolNames);getAgent/agentConfigFor 的 fallback 从 AGENTS.build 改为 AGENTS.worker。MAIN_MODES 保留作为工具清单来源,但不再注册为 agent 身份。AgentRoleProfile 新增 agent?: string 字段;DEFAULT_AGENT_PROFILES 中 worker 默认 agent: "worker",supervisor 默认 agent: "supervisor"。AgentRoleProfileSchema(z.strictObject)同步加 agent: z.string().optional()。loadAgentProfiles / saveAgentProfiles / getAgentProfile / updateAgentProfile 及相关类型。activeAgent state → agentByRole: Record<'worker'|'supervisor', string>,启动从 loadAgentProfiles() 读取各自绑定;activeAgent = agentByRole[activeRole] 派生。handleAgentChoose 改为 role-aware:对当前 role 的 engine(supervisor 走 dualRuntime.getSupervisor().getEngine())调 switchAgent,更新 agentByRole + updateAgentProfile + saveAgentProfiles。switchAgent 到绑定身份;旧 persistedAgent(ui-settings.json)作为 worker 兼容回退。/agent 菜单标题标注当前 role(Agent [supervisor]),fallback 列表从 build/plan 改为 worker/supervisor。关键架构发现: 每个 role 有独立 engine(workerEngine / supervisorEngine),各自持有 currentAgent。switchAgent 设的是各自 engine 的 currentAgent,submit 时 engine 自然用自己的 currentAgent,无需改动 runtime.ts / engine.submit() / bridge.tsx 的调用链。
保留限制:
AgentRegistry)保留,用户仍可通过插件注册自定义 Agent 身份并经 /agent 菜单绑定到任一 role。ui-settings.json 的全局 agent 字段不再迁移(migrateLegacyConfig 未加 agent 迁移),首次升级时两 role 用默认 worker/supervisor —— 可接受的降级。engine.spawnSubagent 用 agentConfigFor("build"))fallback 到 worker,工具集与 build 相同,风险低。症状:per-role 模型配置落地后,supervisor 配置为 zen provider 时,bun run dev 抛 supervisorConfig is required with baseUrl and model,尽管 baseUrl/model 都有值。
根因:DualAgentRuntime 构造时的 requiresApiKey() 只检查 keyless 字段,不认 requiresKey: false。DeepReef 有两种”不需要用户提供 key”的表达:
keyless: true(kilo 等)—— 完全无 key 通道requiresKey: false + defaultKey(zen 等)—— 有兜底 public keyzen.keyless 为 undefined(未定义该字段),!undefined === true → requiresApiKey("zen") 错误返回 true。supervisor 的 apiKey 为空(worker 用 kilo,loadConfig 没填 key)→ 触发 OR 条件抛错。
之前 worker/supervisor 共用同一 provider 时此 bug 潜伏(kilo 恰有 keyless:true);per-role 配置允许 supervisor 用不同 provider 后暴露。
修复:packages/core/src/dual-agent-runtime/dual-runtime.ts 的 requiresApiKey() 现在同时认两种:keyless: true 或 requiresKey: false → 都视为不需要 key。
验收:repro 脚本(zen supervisor)从 ERROR 变为 OK;bun run dev 启动正常;typecheck 通过。
| 检查项 | 状态 | 说明 |
|---|---|---|
bun run typecheck |
✅ | 全项目 tsc 通过,零错误 |
bun run dev 启动 |
✅ | 无 Provider/context/requiresApiKey 报错(仅剩既有的 memory warn,与本系列无关) |
| Provider 报错回归 | ✅ | useOrchestrationStore must be used within Provider 已修复(订阅挪入 BridgeStatusBar) |
| requiresApiKey | ✅ | zen supervisor 构造成功 |
待人工终端验证(非交互环境无法模拟键盘):
/model 在 supervisor role 下改的是 supervisor 模型,切回 worker 不受影响/agent 针对当前 role 绑定,选另一 role 不受影响/agent 菜单| 包 | 文件 | 改动 |
|---|---|---|
| core | src/engine.ts |
加 getModel() / getProvider() getter |
| core | src/config.ts |
加 RoleConfig / saveRoleConfig / loadRoleConfig |
| core | src/schemas/config.ts |
加 RoleConfigEntrySchema / RoleConfigSchema |
| core | src/index.ts |
导出新函数与类型 |
| core | src/agent.ts |
删 build/plan,重写 worker prompt,fallback→worker |
| core | src/agent-profile/types.ts |
AgentRoleProfile 加 agent? 字段 + 默认值 |
| core | src/agent-profile/schema.ts |
zod schema 加 agent 字段 |
| core | src/dual-agent-runtime/dual-runtime.ts |
修复 requiresApiKey 认 requiresKey:false |
| cli | src/tui.ts |
启动分别 seeding worker/supervisor 模型配置 |
| tui | src/App.tsx |
roleConfig + agentByRole 状态、role-aware handleModelSelect/handleAgentChoose、Tab useInput、启动 seeding |
| tui | src/StatusBar.tsx |
加 loopAttempt prop 与 Loop #N 显示 |
| tui | src/BridgeConnected.tsx |
BridgeStatusBar 内部订阅 loop + 透传 |
| tui | src/components/orchestration/OrchestrationContext.tsx |
内联迁移的类型 |
| tui | src/components/orchestration/OrchestrationSummary.tsx |
删除 |
| tui | src/components/workflow/DualTabSystem.tsx |
删除 |
| tui | src/components/workflow/index.ts |
清理 DualTabSystem 重导出 |
| tui | src/index.ts |
清理 DualTabSystem 重导出 |
注:
WorkflowStatusBar.tsx与CommandRegistry.ts为用户手动改动,未包含在本系列提交中。
修复了 Supervisor 工具被全部过滤、角色提示词覆盖基础系统提示、三模式未真实分流等问题。
涉及文件:
| 文件 | 类型 | 变更说明 |
|---|---|---|
packages/core/src/engine.ts |
修改 | submit() 传递 role/mode;baseSystemPrompt 持久化;分层组合系统提示 |
packages/core/src/agent.ts |
修改 | Supervisor toolNames 从 [] 改为 undefined |
packages/core/src/dual-agent-runtime/runtime.ts |
修改 | submit() 传递 mode: "loop" |
packages/core/src/dual-agent-runtime/dual-runtime.ts |
修改 | sendDirect() 传递 mode |
packages/core/src/dual-agent-runtime/types.ts |
修改 | 添加 maxWorkflowRounds |
packages/core/src/resolve-effective-tools.ts |
新增 | 纯函数工具解析 |
packages/core/src/workflow-coordinator/coordinator.ts |
修改 | 中断后区分 “Interrupted by user” / “Max rounds reached” |
packages/core/src/workflow-coordinator/types.ts |
修改 | WorkflowEvent.type 添加 role_output |
packages/cli/src/tui.ts |
修改 | 加载 agentProfiles;setThinkingMode();启动诊断 |
packages/tui/src/App.tsx |
修改 | 三模式路由;workflowRunningRef;.catch().finally();菜单中断提示 |
packages/tui/src/bridge.tsx |
修改 | cancel() 中断 Coordinator;runWorkflow() 处理两种事件类型 + try/catch/finally |
packages/tui/src/workflow-mode-router.ts |
新增 | 纯函数路由 |
packages/tui/src/components/workflow/WorkflowStatusBar.tsx |
修改 | 按 mode+lifecycle 显示真实状态;alone/subagent 无伪造 phase/goal |
packages/core/__tests__/supervisor-request-contract.test.ts |
新增 | 11 条请求契约测试 |
packages/tui/__tests__/workflow-mode-router.test.ts |
新增 | 16 条纯函数路由测试 |
packages/tui/__tests__/workflow-menu-e2e.test.ts |
新增 | 4 条菜单端到端集成测试 |
packages/cli/src/__tests__/supervisor-wiring.test.ts |
新增 | 2 条角色装配独立性测试 |
验证命令:
bun run typecheck # 通过
bun test packages/core/__tests__/supervisor-request-contract.test.ts # 11 pass
bun test packages/core/__tests__/dual-agent-runtime.test.ts # 11 pass
bun test packages/core/__tests__/workflow-coordinator.test.ts # 27 pass
bun test packages/tui/__tests__/workflow-mode-router.test.ts # 16 pass
保留限制:
build/plan agent 移除后的当前代码。packages/memory 已从默认测试套件中排除(接口保留,bun test 只跑融合包)。运行 bun run test:memory 可单独执行 memory 测试,bun run test:all 全量运行。bun test 另有 484 个失败集中在 packages/memory/ / packages/agentmemory/,与本任务无关,未在 §SFR 验收范围内。DEEPREEF_SUPERVISOR_SMOKE=1。全仓基线对齐:
bun test(3132 tests / 276 files)结果:
失败集中在以下模块,均与 SFR-00 ~ SFR-90 任务范围无关:
| 模块 | 失败数 | 失败原因 |
|---|---|---|
packages/memory/ (GraphRetrieval / HybridSearch) |
~80 | Dijkstra 边界 / BM25 fallback,与 SFR 无关 |
packages/agentmemory/ (Hermes / loadEnvFile / Signals / Team / MCP / Auto-Forget / Sketches) |
~400 | 内存/索引/资源模块历史回归 |
packages/core 融合包 |
6 | build/plan agent 移除遗留,见上文 |
packages/tui |
1 | mouse tracking 行为回归 |
packages/cli |
1 | slash command routing 依赖已移除的 build/plan |
SFR 提交附带工作区清理:
SFR commit bd62d56 之前工作区存在 4 个未提交 docs/CodeReviewReport*.md,commit 后状态为 deleted。经用户确认于 2026-06-15 视为放弃,不再恢复。