Commit Graph

739 Commits

Author SHA1 Message Date
claude-code-best
9d845d77b9 feat: 重构 WebSearch/WebFetch,新增 Tavily 适配器及 /web-tools 面板
- WebSearch: 默认 Tavily,适配器优先级 WEB_SEARCH_ADAPTER > settings.webSearchAdapter > tavily
- WebFetch: 支持 Tavily /extract 返回 Markdown,移除 domain blacklist 远程检查
- 新增 /web-tools 命令面板(Search/Fetch 双 Tab + 二级配置菜单)
- 新增 settings 字段: webSearchAdapter, webFetchAdapter, tavilyEndpointUrl, braveApiKey, exaApiKey, exaEndpointUrl, webFetchHttpTimeoutMs
- 适配器联动: Tavily/Exa 从 settings 读取 endpoint 和 API key

Co-Authored-By: deepseek-v4-pro <deepseek-ai@claude-code-best.win>
2026-06-15 16:51:37 +08:00
claude-code-best
2714bbf812 docs: update contributors 2026-06-15 00:28:17 +00:00
claude-code-best
21e42e24b1 chore: 2.7.0 v2.7.0 2026-06-14 18:14:42 +08:00
claude-code-best
58ee6419b1 feat: dynamic-workflow 来了 (#1271)
* feat(workflow): add workflow engine, /workflows panel, /ultracode skill

将 feat/sdk-backend 分支中 workflow 相关的 20 个 commit 压缩为单 commit:

- 工作流引擎核心:phase / agent / parallel / pipeline 编排原语(packages/workflow-engine/)
- /workflows 面板:三区焦点布局(顶部 run tabs + 左侧 phase 侧栏 + 右侧 agent 列表)
- /ultracode skill:多 agent workflow 编排入口
- 进度存储 / journal / notification 系统
- WorkflowService 生命周期管理 + SentryErrorBoundary
- 脚本沙箱:禁用 dynamic import()、JSON args 防御性归一化
- journal 与 named-workflow 路径统一在 projectRoot
- 错误处理:parallel/pipeline hooks 错误日志、failure routing、semaphore abort
- workflow 工具升级为 core 工具 + PascalCase 命名

Co-Authored-By: glm-5.1 <zai-org@claude-code-best.win>

* feat(workflow): 复刻 ultracode 手册并修复 worktree/inline/opt-in 三处缺口

围绕 ultracode skill 审查 agent 系统一致性后:
- ultracode.ts: 用系统提示版完整 Workflow 编排手册替换中文精简版
- HIGH#1 isolation:'worktree': claudeCodeBackend.run() 用 createAgentWorktree +
  runWithCwdOverride 包裹 runAgent + finally 清理实现真正的 cwd 隔离;slug 用
  sha256(runId:agentId) 派生以匹配 cleanupStaleAgentWorktrees 清理正则
  (修 runId 为 w+base36 非 UUID 导致的泄漏盲区);worktree.ts 注释同步修正
- HIGH#2 inline 持久化: 新增 persistInlineScript,WorkflowTool + service 两条
  inline 路径对称持久化到 .claude/workflow-runs/<runId>/script.js,返回可复用
  scriptPath(闭环 inline→编辑→scriptPath 重提迭代循环)
- HIGH#3 opt-in 分工: ultracode/WorkflowTool/effort 注明 session reminder 由
  harness 注入,repo 内无 ultracode 信号,保持 feature('WORKFLOW_SCRIPTS') +
  isEnabled 两层 gate,不自造注入
- 测试: 新增 persistInline.test.ts;扩展 claudeCodeBackend(isolation 4 用例)/
  WorkflowTool(inline)/service(scriptPath)/ultracode(harness)

含配套 workflow engine/panel 完善与 run-state-persistence design doc。

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(workflow): run 终态落盘 state.json 支持跨重启恢复

终态 RunProgress(含 returnValue/error)此前只在内存 ProgressStore,进程
重启即丢失。本次让其落盘到 .claude/workflow-runs/<runId>/state.json,使
(a) 重启后可按 runId 取 return、(b) /workflows 面板跨重启展示历史 run。
跨进程 resume 明确不在范围。

- persistence.ts: getRunsDir/writeRunState/readRunState/listPersistedRuns
  + attachRunStatePersistence;原子覆盖写(tmp+rename),读容错(缺文件/
  损坏/schemaVersion 不符 → null),写 best-effort(IO 失败只 log warn)
- progress/store.ts: 加 hydrate(run) 直接注入磁盘 run(已存在 runId 跳过,
  内存优先)
- service.ts: getWorkflowService() 接线 attachRunStatePersistence(bus,
  store) 订阅 run_done(completed/failed/killed 三态共用,shutdown-kill
  也走同路径,无需额外钩子);WorkflowService 加 getRunAsync(id) 内存
  miss→读盘 fallback(不注入内存)+ loadPersistedRuns() 扫盘 hydrate
  (persistedLoaded flag 守护幂等)
- panel/WorkflowsPanel.tsx: mount 时调一次 loadPersistedRuns(重 mount
  不重复)
- ports.ts: runsDir 改用 getRunsDir() 消除拼接重复
- 测试: persistence.test.ts(11)/runStatePersistence.test.ts(5)/
  progressStore(2)/service(5)/WorkflowsPanel(1) 共 24 个新测试;
  precheck 5629 pass / 0 fail

设计偏离: 计划原写 monkey-patch getRunsDir 指向 tmpdir,Bun ESM namespace
不可变不可行;改用可选 runsDirProvider 参数(默认 getRunsDir)DI 注入,
加到 attachRunStatePersistence 与 makeService(cwdOverride 之后第 4 参),
与现有 cwdOverride 模式一致。makeService 的 cwdOverride 保持不变,不破坏
inline 持久化特性。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(workflow): 默认并发降为 3 并支持 per-run maxConcurrency 注入

- DEFAULT_MAX_CONCURRENCY=3 替代旧的 min(16, cores-2);MAX_CONCURRENCY_CAP=16 保留为用户输入的绝对上限
- 新增 clampMaxConcurrency() 处理 undefined/<1/>CAP 边界
- WorkflowInput schema 新增 maxConcurrency: number.int().min(1).max(16).optional()
- 引擎层 context/runWorkflow 全链路透传:semaphore 容量来自 per-run 入参
- WorkflowTool prompt 增加指引:fan-out 场景先用 AskUserQuestion 与用户确认并发再启动
- 同步 ultracode skill + audit workflow spec 的并发文字(删 cpu-cores 公式)
- 同步 docs/features/workflow-scripts.md 旧公式

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(workflow): 面板 UI 字符串英文化

WorkflowsPanel 中 4 处面向用户的中文(onDone 错误消息、键位提示行)
改为英文;其他面板组件(AgentList/TabsBar)原本已是英文。代码注释
保留中文,与 workflow 模块惯例一致。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(workflow): 中断系统(x 杀单 agent / K 杀整个 workflow,Dialog 二次确认)

- claudeCodeBackend 桥接 ctx.signal → runAgent.override.abortController(修 'x' 无效根因:abort 到不了内部 fetch)
- AbortError 识别为 throw WorkflowAbortedError(不再吞成 dead,workflow 能感知被 kill)
- ports.taskRegistrar 加 registerAgentAbort/unregisterAgentAbort/killAgent;service.killAgent(runId, agentId) 精确中断
- 面板键位:'x' 杀当前 agent(agents 列聚焦时) / 'K' 杀整个 workflow;Dialog 二次确认 + confirm 模式吞导航键防误触
- 新增测试 8 项(backend signal bridge / hooks inject / ports killAgent / service killAgent)

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* docs(workflow): ultracode skill 加 model tier 选择指引(haiku/sonnet/opus/best 场景匹配)

补足 agent() 已有 model 参数缺的判断依据:列出 4 个 tier 的成本/延迟量级和典型场景,
明确"无法 articulate 为什么换 tier 就 omit"的 rule of thumb。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(workflow): maxConcurrency≠3 必须先 AskUserQuestion(默认 3 推荐值)

把 fan-out 时才问改成任何 maxConcurrency≠3 都必须问。
唯一例外:用户在当前会话已明确说过并发数("use 6" / "maxConcurrency 9")。
prompt (WorkflowTool.ts) + skill (ultracode.ts) + audit spec 三处同步。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(workflow): agent 失败自动重试一次(dead 或非 abort throw)

- hooks.agent 包装 invokeBackend:第一次 dead 或非 abort throw → 重试一次
- WorkflowAbortedError(kill)不重试——是用户意图
- registry.resolve 配置错(AdapterNotFoundError 等)在 try 外直接上抛,不走重试——
  配置问题重试无意义且掩盖 bug
- 重试仍失败:dead 保持 dead;throw 降级 dead(不击穿 workflow,
  与 parallel/pipeline null-on-error 契约一致)
- budget 不重复扣:dead 不 addOutputTokens,重试 ok 才扣一次
- 新增 7 项 hooks 层重试测试 + 1 项 service 层降级测试

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(workflow): 面板 label 截断保留 #数字 后缀(同 dim 多 finding 可区分)

audit workflow 用 verify:\${dim}#\${findingIdx} 命名 verify agent。
旧逻辑 slice(0, 18) 从右切把 #idx 全吃了——同 dimension 多 finding
肉眼无法区分。新逻辑:含 #数字 后缀时保留后缀,前缀截断 + … 省略号。

例:verify:correctness#0 → verify:correctn…#0
   verify:architecture#15 → verify:archite…#15

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(workflow): kill 整个 workflow 后立即回主 chat

run_done→store→notifications.ts 的通知路径已有,但 confirmYes 后面板继续
挂着挡住主 chat,用户看不到"已停止"反馈。kill 后调 onDone() 立即退出面板,
让主 chat 的 `Workflow "<name>" was stopped` 通知直接可见。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(workflow): agent dead 带 reason/detail + prompt 加压 StructuredOutput

12 agent audit workflow 8 个 dead,journal 只记 {kind:"dead"} 无信息,
事后无法区分 "agent 没产 StructuredOutput" vs "runAgent 抛错"。
证据指向主因:sonnet 长 tool chain 后忘记调 StructuredOutput,
extractStructuredOutput 返回 null 即降级 dead。

- types.ts: AgentRunResult.dead 加可选 reason/detail 字段
  (no-structured-output / runagent-threw / worktree-failed / unknown)
  兼容旧 journal(均 optional)。
- claudeCodeBackend.ts: 三处 dead 填 reason + detail;
  no-structured-output 把 finalized 文本前 200 字符做 detail,
  让日志/面板能立刻看到 agent 最后说了什么。
- claudeCodeBackend.ts: schema 模式 prompt 首尾各放一次
  StructuredOutput 强制要求,针对 sonnet 长 tool chain 后忘记收尾。
- hooks.ts: retry 日志带 reason;retry 仍 throw 时降级 dead 也填
  reason=runagent-threw + detail。
- types.test.ts: 加 reason JSON 往返 + 旧 journal 兼容测试。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(workflow): schema 模式弃用 StructuredOutput 工具契约,改鲁棒 JSON 文本解析

上一轮 70a2f76 把"agent 长 tool chain 后忘调 StructuredOutput"当作死因,
加 prompt 头尾双强制。但实测跑 5 个 review agent 4 个 dead,detail 全是
"StructuredOutput tool is not available as a deferred tool"——根因是
该工具从未注入 workflow sub-agent 的工具集(assembleToolPool 默认池不含,
只有 stop_hook 路径 execAgentHook.ts 显式 createStructuredOutputTool())。
prompt 反复要求调一个不可达的工具,agent 困扰、长篇辩解、最终没产 JSON。

- claudeCodeBackend.ts:
  - extractStructuredOutput 重写:括号栈扫描替代 indexOf/lastIndexOf,
    处理嵌套对象、字符串内的括号、转义符;新增 fenced code block
    优先路径(```json / ```),多 JSON 块取第一个 parse 成功的;
    只返回 plain object(拒 array/number/string/null)。不做语法修复
    (尾逗号/单引号/注释)——避免在字符串内误改(如 "http://" 被 // 注释正则吃)。
  - schema 模式 prompt 简化:删首尾双 STRUCTURED OUTPUT 强制(600+ token),
    改成指示 agent 在最后文本块 emit raw JSON;明确告知"StructuredOutput
    is not available in this environment",消除调用幻觉。
- hooks.ts: detail.slice 用 typeof === 'string' 守卫;catch 块用
  e instanceof Error ? e.message : String(e)(旧 journal / 第三方 adapter
  可能写非 string detail,直接 .slice 会抛 TypeError 击穿日志)。
- claudeCodeBackend.test.ts: +9 测试覆盖 fenced / 嵌套 / 字符串内括号 /
  转义引号 / 多块取首 / 类型守卫 / 损坏 JSON。

precheck: 5663 pass / 0 fail。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* docs(effort): 新增 /effort 交互面板设计 spec

设计要点:
- /effort 无参 → 横向 slider 面板(low/medium/high/xhigh/max/ultracode)
- ←/→ 移动光标,Enter 确认,Esc 取消
- ultracode 仅视觉占位,确认后提示走 /ultracode <context>
- env override 时双标记 + 顶部警告
- 模型不支持时面板禁用
- 两阶段交付:先基础面板 commit,再做 ultracode 波纹动画

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* docs(effort): 新增 EffortPanel 基础面板实施计划(第一阶段)

按 TDD 分 6 个 task:纯函数状态 → keybinding 注册 → 组件 → 命令挂载 → 分支测试 → precheck。
波纹动画在第二阶段单独 commit。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* docs(effort): plan 补 q/ctrl+c 取消绑定,对齐 spec §5 状态机

verifier 抓到的 gap:spec §5 写明 Esc / Ctrl+C / q 都是取消事件,
但 plan Task 2.3 只绑了 escape。补上 q 和 ctrl+c → effortPanel:cancel。
同时把 Step 2.2 直接写成 6 个 action 版本(home/end),删除迂回表达。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* docs(effort): plan 修订执行前 review 发现的 5 处 gap

- Task 3.3 EffortPanel.tsx 草稿:Faster/Smarter padEnd 语法错乱重写;
  useKeybindings import 路径从 @anthropic/ink 修正为 ../../keybindings/useKeybinding.js;
  移除冗余 renderSeparatorLine;保留 renderPaddedLine
- Task 5.2 computeConfirmOutcome 改为注入 ApplyFn 模式:
  避免 effortPanelState → effort.tsx → EffortPanel 循环依赖;
  测试可注入 mockApply,无需 mock settings
- Step 5.3 测试代码对齐注入版签名

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(effort): 新增 EffortPanel 纯函数状态模块(PanelPosition + 移动/初始光标)

仅含纯函数与类型,无 React/Ink 依赖,便于单测。
- PANEL_POSITIONS:low → medium → high → xhigh → max → ultracode
- moveLeft/moveRight:边界钳制(low 不再左移、ultracode 不再右移)
- getInitialCursor:env override > displayed level

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(keybindings): 注册 EffortPanel context 与 6 个 action

绑定 ←/→/h/l/home/end/enter/escape/q/ctrl+c 到 effortPanel:* action。
与 ModelPicker context 范式一致,避免左右键被全局 keybinding 拦截。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(effort): 实现 EffortPanel 组件主体(渲染 + 键盘交互 + 确认/取消分支)

- 横向 slider 布局:Faster ↔ Smarter 两极,6 档刻度
- useKeybindings 注册 EffortPanel context(←/→/h/l/home/end/enter/escape/q/ctrl+c)
- Enter 在 5 档之一 → 调 executeEffort 写 settings + AppState
- Enter 在 ultracode → 输出引导文案,不写状态
- Esc/q → "Effort unchanged."
- env override 时顶部黄色警告
- computeConfirmOutcome 注入 ApplyFn,便于测试(Task 5 补测试)

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(effort): /effort 无参时挂载 EffortPanel 交互面板

- 无参 → <EffortPanelWrapper> 透传 AppState.effortValue
- current/status → 仍显示文本(不变)
- 有参 → 直跳 executeEffort(不变)
- help/-h/--help → 不变

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* test(effort): 补 computeConfirmOutcome 分支测试(注入 mockApply)

- ultracode → kind=ultracode-hint,不调 applyFn
- low → kind=apply,message/effortUpdate 来自 applyFn
- applyFn 返回无 effortUpdate 时 outcome.effortUpdate 为 undefined
- CANCEL_MESSAGE / ULTRACODE_HINT 常量

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(effort): 测试里 cursor cast 为 EffortValue,避免 PanelPosition 含 ultracode 触发 TS 错误

computeConfirmOutcome 的 ApplyFn 契约要求 EffortValue,但测试 mockApply 接收 PanelPosition。
实际运行时 computeConfirmOutcome 在 ultracode 档位走 hint 分支不会调 applyFn,
cast 安全。precheck 全量通过:5688 tests / 0 fail。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(effort): 面板对齐与配色修复

- 对齐:用 Box width={SEGMENT} + justifyContent="center" 让 ▲ 与档位名严格居中对齐,
  替代之前 string padEnd(11) 与 SEGMENT=12 不一致导致的 1 列偏移
- 配色:所有面板文字改用 theme.claude(Claude Orange rgb(215,119,87)),
  替代终端默认紫;分隔线/副标签/底栏用 theme.subtle;env 警告用 theme.warning
- 光标档位的档位名也加粗,强化视觉焦点

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(effort): 面板文字改紫色,ULTRACODE_HINT 英文化

- 颜色:theme.claude(橙)→ theme.purple_FOR_SUBAGENTS_ONLY(Purple 600, rgb(147,51,234)),
  覆盖标题、Faster/Smarter、▲、档位名
- ULTRACODE_HINT:中文 → 英文
  "ultracode is not an effort level. Use /ultracode <context> to start a multi-agent workflow."

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(effort): 统一用色版——选中 suggestion(蓝),未选中 subtle(灰)

弃用 purple_FOR_SUBAGENTS_ONLY(subagent 专用)。改与项目其他面板一致:
- 选中档位 + ▲:color="suggestion"(Medium blue rgb(87,105,247))+ bold
- 未选中档位 + 空 ▲ 占位:color="subtle"(Light gray rgb(175,175,175))
- 标题 / Faster / Smarter:color="suggestion"
- 分隔线 / 副标签 / 底栏:color="subtle"

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(workflow): 终态前补发 phase_done,面板自动退出 running→terminal 转换

runWorkflow:脚本结束时 hook.phase 不会触发最后一个 phase 的 phase_done,
UI 左栏会永远显示 running。三路径(completed/killed/failed)统一在 run_done
之前补发 emitTerminalPhaseDone。

WorkflowsPanel:抽 isRunTerminatedTransition 纯函数判定 running → terminal,
面板 useEffect 检测到转换后自动退出聚焦。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(effort): 波纹动画纯函数 pickChar/computeRippleLine/mergeLayers + 18 测试

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(effort): useRippleFrame hook 包装 useAnimationFrame,按需订阅时钟

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(effort): EffortPanel 集成波纹背景——cursor 停在 ultracode 时切换波纹模式

仅在 cursor === 'ultracode' 时启用 useRippleFrame,渲染 5 行波纹背景
+ overlay 文字(Faster/Smarter、分隔线、▲、档位名、副标签)。
其余档位保持原 PlainContent 渲染路径不动。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* refactor(effort): 波纹动画从字符密度改为颜色渐变

按原版风格把波纹背景从 INTENSITY_CHARS 密度字符('·∙░▒▓')改为
suggestion 系颜色渐变(transparent → 暗深紫蓝 → suggestion → 高光):

rippleAnimation.ts:
- 删除 pickChar / INTENSITY_CHARS / WAVE_PEAK_CHARS / mergeLayers
- 新增 intensityToColor(intensity) → 'transparent' | '#xxxxxx'
- 新增 computeRippleCells 返回 Cell[](每位置 char+color)
- 新增 applyOverlaysToCells(cells, overlays) 替代 mergeLayers
- 新增 cellsToSegments(cells) 合并相邻同色段(减少 Text 节点)

EffortPanel.tsx:
- RippleContent 用 cells→segments→tokens 渲染
- 空格段用 BaseText backgroundColor 染色块(纯色块视觉)
- 文字段用 Text color 染色(亮色突出)
- tokens 按空格/文字二次拆分,避免混合段渲染歧义

测试: 29 个 rippleAnimation 测试覆盖 intensityToColor 边界、
computeRippleCells 长度/震源/衰减、applyOverlaysToCells 覆盖/截断/
防御式拷贝、cellsToSegments 合并逻辑。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(effort): 波纹参数调优——铺满左侧 + 速度调慢 + 全面板有底色

用户反馈三个问题:
1. "低峰部分没有颜色变化" → intensity ≤ 0.1 返回 transparent 导致波谷
   位置看不见。改为永不返回 transparent,最低档 #0a0d1a 作为面板
   底色(暗紫黑海洋),波峰在底色上流动。
2. "波浪速度太快" → time 系数 0.012 → 0.004(约 1/3 速)。波峰移动
   速度从 34 cell/s 降到 11 cell/s,每帧颜色变化从 45% 降到 36%。
3. "波浪只到中间部分,没覆盖左侧" → falloff 覆盖半径 40 → 90。
   震源 x=65,左侧 dist=65 < 90,波纹可达最左端(约 30-50% 覆盖)。

色阶调整:
- 删除 transparent 档,新增 #0a0d1a 作最暗档(底色)
- 最高档从 #8aa0ff(高光)改为 #5769F7(suggestion),避免与
  文字 overlay 同色互相吞噬
- 7 档颜色:#0a0d1a → #15182b → #1f2543 → #2a3360 →
  #3a4582 → #4a5bb0 → #5769F7

测试:删除 transparent 期望,改为期望具体颜色(#0a0d1a 等)。
新增"覆盖半径扩大"测试验证 dist=65 仍有非最暗颜色。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(effort): 波纹 v3 — 去黑边 + 删中心高频涟漪 + y 轴覆盖快捷键行

用户反馈三个问题:
1. "黑色边感觉不太对" — 最暗档 #0a0d1a (rgb 10,13,26) 太接近纯黑,
   远端波谷看起来像硬黑边。改为 #1a1f3a (rgb 26,31,58),紫蓝感
   更强而非纯黑。
2. "中心的快速波纹有点奇怪" — 删除震源附近 dist<6 的高频涟漪叠加
   (time*0.02,5 倍主波纹频率)。原本想让震源附近"水波感"更强,
   实际效果像"快速闪烁"反而突兀。主波纹已经足够,无需叠加。
3. "y 方向覆盖快捷键" — RippleContent 新增 y=2 行渲染快捷键 overlay
   ("←/→ adjust · Enter confirm · Esc cancel")。PlainContent 路径
   保持原 Box marginTop=1 + Text 渲染。

色阶调整(紫蓝感更强):
- #1a1f3a (原 #0a0d1a) — 最暗档
- #1f2543 / #252c55 / #2e3870 / #3a4582 / #4a5bb0 / #5769F7
  (中间档略调亮度,保持平滑过渡)

测试:震源点测试更新为"time=0 时波谷最暗,time 推进后扫过波峰变亮",
反映删除高频涟漪后的纯主波纹行为。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* chore(workflow): 工作流相关代码中文文案全部英文化

源码(src/workflow/ + packages/workflow-engine/src/)的中文注释、
用户可见错误消息、字符串字面量;测试文件的标题与注释;同步 6 条
硬编码断言到英文化后的错误消息。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(effort): 波纹 v4 — 平滑波 + 全色环旋转 + 淡入淡出 + 宽度自适应

- 波函数改 (sin+1)/2:消除 max(0,sin) 平直暗带(约 6 行宽)
- 主色相连续旋转(0.03°/ms,12s/圈全色环):蓝→紫→品红→红→橙→黄→绿→青
- 文字 overlay 同步色相旋转(rotateHue 应用到 Faster/▲/档位名/分隔线/副标签)
- 淡入淡出动画:fadeColor/fadeCells + fade 状态机 ~300ms 进出过渡
- 副标签固定 ultracode 段下方,不跟随光标移动
- 顶部/底部各加一行纯波纹行,视觉一致
- 宽度自适应终端列数:窄则 72,宽则铺满(computeSegment/computeRippleSourceX)
- 快捷键改 plain Text,不参与波纹背景渲染
- 新增 18 测试(fadeColor/fadeCells/rotateHue/getHueShiftAtTime)

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* refactor: remove CYBER_RISK_MITIGATION_REMINDER from FileReadTool

Co-Authored-By: deepseek-v4-pro <deepseek-ai@claude-code-best.win>

* fix: prevent ReDoS in extractMeta regex by anchoring to splice boundary

Co-Authored-By: deepseek-v4-pro <deepseek-ai@claude-code-best.win>

* chore: 更新脚本

---------

Co-authored-by: glm-5.1 <zai-org@claude-code-best.win>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: deepseek-v4-pro <deepseek-ai@claude-code-best.win>
2026-06-14 18:13:49 +08:00
moy16
3e3e1de81b feat: /goal命令能力支持,参考codex实现 (#1261)
* feat: /goal命令能力支持,参考codex实现

* fix: 修复promp和提示词不一致的问题

* fix: 修复 goal 功能多项 AI 审查问题

- prompt 中 update 行为描述与运行时不一致(no-op → error)
- src/commands/goal/ 使用相对路径导入,改为 src/* 别名
- /goal 命令标记 bridgeSafe 但含交互式对话框,改为 false
- useGoalContinuation 中 origin 使用 as unknown as string 强转,改为直接传字符串
- ResumeConversation 路径缺少 goal hydration,补齐恢复逻辑
- onCancel 在非查询状态下误暂停 goal,加 queryGuard 守卫
- resumeGoal 允许从终态恢复,收紧为仅允许 paused 状态
- buildGoalContextBlock 生成畸形 XML 属性,改为合法 budget 属性

* fix: 修复剩余AI审查的问题

* fix: 防止goal状态丢失

* fix: 修复Biome规范错误问题

* fix: 修复部分情况下goal无法启动的问题

* fix: 增加断网后状态默认设置为PAUSE机制、完成暂停-恢复状态切换,且正常进行前端渲染。设置达到max turn后处理逻辑。

* fix: 修复终端异常断开情况,resume续跑;修复用户消息排队信息被goal输出信息覆盖的问题。

* fix: apply biome formatting to pass CI lint check

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: skip slash command echo in setUserInputOnProcessing to prevent UI flash

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: moyu <moyu@kingsoft.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 10:44:10 +08:00
YYMa
5bfe6fa590 feat: add China LLM providers guided login flow (#1254)
* docs: update contributors

* docs: update contributors

* feat: add China LLM providers guided login flow

Add a guided login experience for 4 domestic (China) LLM providers
in the /login command: DeepSeek, Zhipu GLM, Tongyi Qianwen, and
MiMo Xiaomi. Each provider includes model presets with pricing,
context windows, and optional Coding Plan integration.

- New file: src/utils/chinaLlmProviders.ts — provider preset configs
- Modified: src/components/ConsoleOAuthFlow.tsx — 4-step guided flow
  (select provider → select mode → select model → enter API key)

All providers are OpenAI-compatible; credentials saved as
OPENAI_BASE_URL + OPENAI_API_KEY under modelType: 'openai'.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat: add custom model input with suggestions and model listing links

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-13 22:25:18 +08:00
claude-code-best
91cffe16e2 chore2.6.13 v2.6.13 2026-06-12 17:02:15 +08:00
claude-code-best
c4dd45f8df fix: 防止 <available-deferred-tools> 在每轮 API 调用中重复注入
使用模块级 Set 缓存已注入的 deferred tool 列表,diff 后仅在有
新增工具时重新注入。根因:注入消息追加到 queryModel 的局部变量
messagesForAPI,不写入消息历史,所以每次调用都是首次。
2026-06-12 17:01:01 +08:00
claude-code-best
b5beafb9bf chore: 2.6.12 v2.6.12 2026-06-11 18:04:55 +08:00
claude-code-best
e897385a7e Feature/docker/run (#1268)
* feat: 删除垃圾更改

* fix: 消除生产代码中的 as any 类型不安全模式

- API 兼容层(openai/grok/gemini): 利用 BetaRawMessageStreamEvent 的
  discriminated union 在 switch/case 中直接属性访问,消除 ~29 个 as any
- ConsoleOAuthFlow: 用 as unknown as Parameters<typeof> 替代 as any
- performanceShim: 用 Record<string, unknown> 和显式类型断言替代 as any
- companionReact/auth: 直接访问已有类型属性消除 as any
- sliceAnsi/textHighlighting: 用 as Char 替代 as any(Token 联合类型收窄)
- ccrClient: 利用 RequestResult 类型收窄直接访问 retryAfterMs
- outputsScanner: 用 TurnStartTime.turnStartTime 属性访问替代双重断言
- plans: 用显式数组类型替代 as any[]
- FeedbackSurvey: 用 in 操作符和 Parameters<typeof> 替代 as any
- messageQueueManager: 用 Record<string, unknown> 替代 as any
- mcp.ts: 用 in 操作符类型守卫替代 as any

precheck 通过: typecheck 零错误 + 5420 测试全部通过 + lint 通过

* fix: 将 pipeIpc 添加到 AppState 类型声明,消除 4 个 as any

- AppStateStore: 添加 pipeIpc?: PipeIpcState 可选字段
- PromptInputFooter: 直接访问 s.pipeIpc
- useBackgroundTaskNavigation: 直接访问 s.pipeIpc
- usePipeRouter: 直接访问 store.getState().pipeIpc
- REPL.tsx: 移除 getPipeIpc(s as any) 中的 as any

precheck 通过

* fix: 消除 UltraplanChoiceDialog 中的 wheelDown/wheelUp as any

Ink Key 类型已包含 wheelDown/wheelUp 属性,直接访问即可。

* fix: 消除 sideQuestion.ts 中的 2 个 as any

- toolUse.name: 使用 as unknown as { name: string } 双重断言
- apiErr.error: 使用 as Parameters<typeof formatAPIError>[0] 类型参数

* fix: 为 auto dream 添加 maxTurns: 20 限制,防止单次执行消耗过多 token

* fix: 补充 SAFE_ENV_VARS 中缺失的 OpenAI/Gemini/Grok provider 环境变量

项目级 settings.local.json 的 env 字段在 trust dialog 之前只有
SAFE_ENV_VARS 白名单中的变量会被应用到 process.env。
OPENAI_API_KEY、OPENAI_BASE_URL 等关键变量不在白名单中,
导致容器中通过 settings.local.json 配置 OpenAI 协议时认证失败。

* fix: 修复 goalState.js 模块不存在的类型错误

* fix: 增强 providers 测试的环境变量隔离,防止 mock 污染

* fix: 内联 providers 测试逻辑,彻底隔离 mock 污染

测试不再 import providers.ts(其默认参数触发 getInitialSettings 全链),
改为内联纯函数逻辑,从根源消除 CI 上其他测试 mock.module 污染。

* fix: 添加 goalState 模块存根,修复 CI 构建打包解析失败

CI 中的 autonomy-lifecycle-user-flow 集成测试会执行 build.ts 打包 CLI。
此前 PromptInputFooterLeftSide.tsx 中 require('../../services/goal/goalState.js')
的路径在源码中不存在,打包器报 Could not resolve,导致 (unnamed) 测试失败。

新增 src/services/goal/goalState.ts 存根模块(getGoal 返回 null,组件不渲染),
让打包器在构建期可以解析该 require 路径。同时把 PromptInputFooterLeftSide.tsx
里两处 as unknown as 内联类型签名换成 as typeof import(...),让类型直接来自
存根模块,避免类型定义重复。
2026-06-11 17:59:08 +08:00
James F
83e891d7b2 feat: support markdown agent format (.md with YAML frontmatter) in mode loader (#1267)
Extends the mode loader to accept .md files alongside .yaml/.yml in
~/.claude/modes/. Markdown files use YAML frontmatter for metadata
and the body as systemPrompt — the same format supported by
OpenCode, Claude Code agents, and Cursor rules.

.md data is normalized to the same shape as .yaml data, reusing
the existing CCBMode mapping with zero code duplication.

- Add kebabCase() helper for slug derivation from name
- Add parseMarkdownFrontmatter() helper (uses existing yaml package)
- .md: body → system_prompt, auto-slug if missing, icon default 🤖
- Add optional model field to CCBMode for cross-tool alignment
- Existing .yaml/.yml path: unchanged
2026-06-10 19:49:11 +08:00
James F
bee711f431 refactor(acp): make bridge SDK message handling type-safe (#1265)
* refactor(acp): make bridge SDK message handling type-safe

- Add BridgeSDKMessage type alias to eliminate 14 type errors from void-leaked IteratorResult
- Replace 18 scattered as-casts with a single uniform as BridgeSDKMessage
- Add 68 lines of unit tests covering bridge message handling
- Fixes docstring coverage to pass CI threshold

* fix(acp): restore IteratorResult return type to nextSdkMessageOrAbort

The simplified SDKMessage | undefined return type collapsed two distinct
states: generator truly done vs generator yielding undefined. This broke
forwardSessionUpdates which needs to distinguish the two — when the
generator yields null/undefined it should continue (calling next() again),
not break out of the loop.

Restored the original IteratorResult<SDKMessage, void> return type so
done and yielded-null are distinct again.
2026-06-09 21:49:05 +08:00
Slayer
4d930eb4eb docs: 添加 JSONL transcript 会话机制文档 (#1262) 2026-06-09 11:50:59 +08:00
Slayer
2567e77d37 sub agents docs (#1266)
* docs: 添加 JSONL transcript 会话机制文档

* docs: 重构多 Agent 编排机制文档
2026-06-09 11:50:46 +08:00
claude-code-best
fac16dab0a docs: update contributors 2026-06-08 00:26:45 +00:00
张三
e77bfa662e Update multi-turn.mdx (#1257)
文档中对于多种交互模式以及会话处理未明确区分。参考源码src\screens\REPL.tsx
2026-06-07 20:51:10 +08:00
James F
1faedff25d fix: eliminate 8 as any in MCP handlers, structured output, and stream events+Claude Soul Document 蒸馏 (#1258)
* fix: eliminate 8 as any in MCP handlers, structured output, and stream events

- Group A: Add : () => AnyObjectSchema type annotations to MCP notification
  schema constants (useIdeSelection, useIdeLogging, usePrompts, channelNotification)
- Group B: Add isStructuredOutputAttachmentMessage type guard for structured
  output attachment payloads (execAgentHook)
- Group C: Add isMessageDeltaStreamEvent type guard for message_delta
  stream event usage extraction (forkedAgent)

These as any casts also exist in the upstream CCB source — this fix provides
real type safety without changing any runtime behavior.

* feat: wire mode persona injection — Claude Soul Document distilled into system prompt

- prompts.ts: add getModePersonaSection() → injects current mode's
  systemPrompt as 'mode_persona' dynamic section (first in order,
  before operational instructions). Previously modes had systemPrompt
  fields but they were never sent to the model.
- modes/personas/claude.ts: 3KB distilled Claude persona from
  Anthropic's leaked Claude 4.5 Opus Soul Document (70KB → operational
  extract): core traits, 7 honesty principles, helpfulness/caution
  balance, collaboration stance, identity stability.
- With custom mode YAML (~/.claude/modes/claude.yaml), 7 modes total
  including the new Claude persona — fully operational at /mode claude.

Co-Authored-By: James Feng <47167674+GhostDragon124@users.noreply.github.com>

* fix: import path convention + reword persona source comment

- prompts.ts: use 'src/modes/store.js' alias instead of relative '../modes/store.js'
  to match the file's existing import convention
- claude.ts: reword JSDoc to say 'based on publicly available reference document'
  instead of 'leaked', addressing CodeRabbit review concern

* docs: add usage note to CLAUDE_PERSONA explaining it's a reference template for YAML config

CodeRabbit noted that CLAUDE_PERSONA has no direct imports. This is
intentional — it's a reference template for users defining custom modes
via ~/.claude/modes/claude.yaml, not a programmatically imported constant.
2026-06-07 20:30:03 +08:00
James F
be0c65678d Fix/coderabbit nits (#1259)
* fix: eliminate 8 as any in MCP handlers, structured output, and stream events

- Group A: Add : () => AnyObjectSchema type annotations to MCP notification
  schema constants (useIdeSelection, useIdeLogging, usePrompts, channelNotification)
- Group B: Add isStructuredOutputAttachmentMessage type guard for structured
  output attachment payloads (execAgentHook)
- Group C: Add isMessageDeltaStreamEvent type guard for message_delta
  stream event usage extraction (forkedAgent)

These as any casts also exist in the upstream CCB source — this fix provides
real type safety without changing any runtime behavior.

* feat: wire mode persona injection — Claude Soul Document distilled into system prompt

- prompts.ts: add getModePersonaSection() → injects current mode's
  systemPrompt as 'mode_persona' dynamic section (first in order,
  before operational instructions). Previously modes had systemPrompt
  fields but they were never sent to the model.
- modes/personas/claude.ts: 3KB distilled Claude persona from
  Anthropic's leaked Claude 4.5 Opus Soul Document (70KB → operational
  extract): core traits, 7 honesty principles, helpfulness/caution
  balance, collaboration stance, identity stability.
- With custom mode YAML (~/.claude/modes/claude.yaml), 7 modes total
  including the new Claude persona — fully operational at /mode claude.

Co-Authored-By: James Feng <47167674+GhostDragon124@users.noreply.github.com>

* fix: import path convention + reword persona source comment

- prompts.ts: use 'src/modes/store.js' alias instead of relative '../modes/store.js'
  to match the file's existing import convention
- claude.ts: reword JSDoc to say 'based on publicly available reference document'
  instead of 'leaked', addressing CodeRabbit review concern
2026-06-07 20:06:16 +08:00
claude-code-best
a972ed795c feat: 添加 cacheWarningEnabled 配置项,支持在 /config 面板关闭缓存率警告 2026-06-06 10:15:24 +08:00
YYMa
9947ae75da feat: add mode system with 6 AI personality presets (#1255)
* docs: update contributors

* docs: update contributors

* feat: add mode system with 6 AI personality presets

Add a /mode command that lets users switch between 6 interaction
modes, each with distinct system prompts, UI themes, permission
defaults, and response verbosity:

- Default () — balanced, everyday development
- Gentle (🌸) — patient explanations for learning
- Dr. Sharp (🔍) — strict 3-phase code review workflow
- Workhorse (🐴) — auto-execute, minimal confirmations
- Token Saver (💰) — minimal replies to save tokens
- Super AI (🧠) — deep analysis, proactive suggestions

Custom modes can be defined via YAML files in ~/.claude/modes/.

New files:
- src/modes/types.ts — CCBMode interface
- src/modes/defaults.ts — 6 built-in mode presets
- src/modes/store.ts — mode state management with useSyncExternalStore
- src/commands/mode/index.ts — command registration
- src/commands/mode/mode.tsx — mode picker UI

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 21:01:02 +08:00
claude-code-best
6b205f5798 chore: 2.6.11 v2.6.11 2026-06-05 10:50:07 +08:00
claude-code-best
7e3d825f0e fix: ACP prompt 未切换全局 sessionId 导致 transcript 写入错误会话文件
prompt() 在调用 submitMessage 前没有 switchSession,recordTranscript
依赖全局 getSessionId() 确定写入路径,多会话场景下新会话内容会覆盖旧会话。
2026-06-05 10:49:37 +08:00
claude-code-best
a077ec8d85 fix: ACP 模式下文本重复显示 — 流式事件与助手消息双重推送
stream_event 和 assistant 消息对同一文本内容各发一次 agent_message_chunk,
导致 ACP 客户端显示两遍。添加 streamingActive 标志,在收到 stream_event 后
过滤掉 assistant 消息中已被流式路径处理的 text/thinking 块。
2026-06-05 10:37:59 +08:00
claude-code-best
55a932df68 chore: 2.6.10 v2.6.10 2026-06-05 00:02:54 +08:00
claude-code-best
230eb489b5 fix: ACP 模式加载 agent 定义并透传 subagent 层级信息
- agent.ts: session 创建时调用 getAgentDefinitionsWithOverrides 加载内置
  subagent(Explore/Plan/General-Purpose 等),注入 appState 和 engineConfig
- bridge.ts: assistantMessageToAcpNotifications 调用时补上 parentToolUseId,
  使 subagent 内部工具调用的 _meta 中携带父级标记
2026-06-05 00:02:21 +08:00
claude-code-best
de477aecf6 chore: 2.6.9 v2.6.9 2026-06-04 21:58:33 +08:00
claude-code-best
01f26cf42b fix: ACP loadSession 历史记录恢复失败 — 用 resolveSessionFilePath 替代 getProjectDir 定位 session 文件
- params.cwd 可能与 session 文件实际存储的项目目录不一致(子目录、
  hash 算法差异等),导致 getProjectDir 推算出的路径找不到文件
- 改用 resolveSessionFilePath(sessionId, cwd) 按 sessionId 跨项目
  搜索,先精确匹配再 fallback 全项目扫描
- 切换回已缓存的 session 时也回放历史消息给客户端
- createSession 内部 switchSession 保留 sessionProjectDir 不被覆盖为 null
2026-06-04 21:57:46 +08:00
claude-code-best
d8892f19d5 chore: 2.6.8 v2.6.8 2026-06-04 15:49:09 +08:00
claude-code-best
b62b384e36 fix: normalizeMessagesForAPI 不再跨 tool_result 边界合并同 ID assistant 消息 (CC-1215)
ACP 模式下 extended thinking + tool_use 同一 turn 时,StreamingToolExecutor
在两个同 message.id 的 AssistantMessage 之间插入 tool_result,导致向后遍历
合并跨越边界,产生重复 tool_use ID → 孤立 tool_result → 连续 user 消息 → 400。

修改向后遍历停止条件:遇到非 assistant 消息(含 tool_result)即停止,不再跳过。
v2.6.7
2026-06-04 15:41:41 +08:00
claude-code-best
d7001b870f fix: add markResourceTiming polyfill to performance shim for Node.js v22 undici compatibility
Node.js v22 undici internal calls performance.markResourceTiming() after
every fetch. The performance shim was missing this method, causing
TypeError crashes in ACP mode when running with Node.js.
2026-06-04 14:30:34 +08:00
claude-code-best
18437c20d2 fix: prevent crash when DiscoverSkills receives undefined query via ExecuteExtraTool
searchSkills() called .trim() on query without null-guard. When
DiscoverSkills is invoked through ExecuteExtraTool with missing
description, query is undefined, causing 'Cannot read properties of
undefined (reading trim)'.

Fixed with optional chaining: !query.trim() → !query?.trim()

Co-Authored-By: deepseek-v4-pro <deepseek-ai@claude-code-best.win>
2026-06-03 21:38:23 +08:00
James F
02298cb199 security: close telemetry leak in preconnectAnthropicApi startup path (#1253)
🔒 Security Discovery: Un-gated outbound connection bypasses privacy controls

Summary
-------
preconnectAnthropicApi() unconditionally sends a TCP+TLS handshake to
api.anthropic.com on every ccb startup — even when the user has explicitly
disabled all non-essential traffic via CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
or DISABLE_TELEMETRY=1.

This is the LAST un-gated outbound connection in the entire startup path.
Every other telemetry sink (Sentry, Langfuse, OpenTelemetry, GrowthBook,
1P Event Logger, Datadog, BigQuery, etc.) already respects the
privacyLevel module's isEssentialTrafficOnly() gate. This one did not.

Impact
------
While the preconnect is a HEAD request with no payload, the connection
itself leaks the client's IP address and session timing to Anthropic's
infrastructure. For privacy-conscious users and enterprise deployments
that have disabled telemetry, this constitutes an unexpected data leak.

Fix
---
Add isEssentialTrafficOnly() check at the function entry, consistent
with every other privacy-gated code path in the codebase. The
privacyLevel module is already imported by init.ts and 12+ other
modules — no new dependencies.

Verification
------------
Reproduced and verified via strace on Linux (aarch64):

  # Before fix
  $ strace -f -e connect ccb -p <<< 'hello'
  connect(16, sin_addr=inet_addr("160.79.104.10"), sin_port=htons(443)) = 0
  # ↑ connector to api.anthropic.com despite CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1

  # After fix
  $ strace -f -e connect ccb -p <<< 'hello'
  # ↑ zero remote TCP connections — all traffic to localhost only

Changes: 1 file, +5 lines (import + gate)
2026-06-02 09:30:13 +08:00
claude-code-best
b2b1981da3 docs: update contributors 2026-06-01 00:23:43 +00:00
claude-code-best
33c52578a6 docs: 修改 README 2026-05-31 22:11:29 +08:00
claude-code-best
e33b17bde7 feat: sideQuery 支持第三方 provider 路由 (OpenAI/Grok/Gemini)
- 新增 getProviderPrimaryModel() 从环境变量解析 provider 主模型
- getDefaultOpus/Sonnet/HaikuModel 在第三方 provider 下回退到用户配置的主模型
- sideQuery 根据 provider 类型分发到对应的 API 适配器
- 新增 sideQueryViaOpenAICompatible (OpenAI + Grok) 和 sideQueryViaGemini 适配函数
- 避免 sideQuery 后台任务在配置第三方端点时仍请求 Anthropic API
2026-05-31 14:08:30 +08:00
claude-code-best
797424115d chore: 2.6.6 v2.6.6 2026-05-29 17:52:25 +08:00
claude-code-best
efc218d8a9 fix: searchSkills 使用缓存 IDF 前校验 index 引用一致性,修复测试间歇性失败 2026-05-28 22:24:29 +08:00
claude-code-best
a91653a0dd fix: 删除 edit tool 中的旧逻辑处理, 现在已经不需要这些处理了, 大模型够屌 (#1251)
* refactor: remove tab/quote normalization from FileEditTool

* fix: resolve pre-existing typecheck errors (zod v4 compat + RCS web exclude)
2026-05-28 21:52:31 +08:00
claude-code-best
c982104476 docs: update contributors 2026-05-25 00:22:36 +00:00
claude-code-best
6dd378bf15 fix: 退出启动对话框时终端残留一行内容
gracefulShutdownSync 启动异步 shutdown 后同步返回,React 立即
重新渲染组件,与 cleanupTerminalModes() 中的 Ink unmount 产生
竞态条件,导致退出后终端残留对话框内容。

修复方案:引入 pendingExitCode state,退出路径先清空画面
(渲染 null),在 useEffect 中延迟到下一个 tick 再调用
gracefulShutdownSync,确保 Ink 在终端清理前已完成空帧刷新。

影响三个启动对话框:TrustDialog、BypassPermissionsModeDialog、
DevChannelsDialog。

Co-Authored-By: glm-5.1 <zai-org@claude-code-best.win>
2026-05-22 22:25:51 +08:00
claude-code-best
ed61932748 fix: subtract cached_tokens from input_tokens in OpenAI stream adapter
OpenAI's prompt_tokens includes cached tokens, but Anthropic's
input_tokens semantic excludes them. The adapter was mapping
prompt_tokens → input_tokens verbatim, causing downstream code
(cache hit rate, cost, autocompact) to double-count.

Real-world impact: DeepSeek returns prompt_tokens=34097 with
cached_tokens=34048, displayed as 50% hit rate instead of 99.86%.

Co-Authored-By: glm-5.1 <zai-org@claude-code-best.win>
2026-05-22 21:58:33 +08:00
claude-code-best
b1c4f40f90 fix: ACP 模式下 extended thinking + tool_use 触发连续 user 消息导致 400 (CC-1215) 2026-05-22 21:58:33 +08:00
Dosion
f91060836f fix(swarm): WindowsTerminalBackend pidFile health check + 5-state lifecycle (#1237)
* fix(swarm): WindowsTerminalBackend pidFile health check + 5-state lifecycle

修 wt.exe split-pane fire-and-forget 导致 teammate 假死、TeamDelete 卡死、
kill-while-spawn race 等多个问题。

- 加 waitForPidFile() 在 wt.exe 返回后等 powershell.exe 真启动写 pidFile
  默认 8s timeout,env CLAUDE_WT_PANE_TIMEOUT_MS 覆盖,超时 throw 含完整诊断
- 加 5 态生命周期 (registered/spawning/ready/killing/dead),sendCommandToPane
  inner Promise 包装 spawnPromise,ready 态重 spawn 直接 throw
- killPane TOCTOU 修正:await spawnPromise 后重读 status;优先用缓存 pane.pid
  避免读盘,Stop-Process 失败也清缓存 + 标 dead 防 PID 复用误杀
- pid 解析严格化:/^\d+$/ + Number.isFinite + >0;移除 dead try/catch
- 构造函数 options 对象注入 pidFileDir(兼容原位置参数)
- 清启动前陈旧 pidFile,killPane fallback 3×500ms retry 兜底

* test(swarm): 12 tests covering WindowsTerminalBackend lifecycle, race, pid validation

为 WindowsTerminalBackend 加 12 个测试覆盖 v2 全部新行为,含 5 个 v1 兼容 + 7 个
v2 新场景。配套构造函数 options 对象,测试用 pidFileDir: tempDir 隔离防泄漏到
真实 OS tmpdir。

新场景覆盖:
- unlinks stale pidFile so a stale pid is not adopted
- rejects re-spawn on a ready pane
- throws on unknown paneId in sendCommandToPane
- rejects corrupted pidFile content ("123abc") and times out
- killPane awaits in-flight spawn before killing (kill-while-spawn race)
- Stop-Process failure clears cached pid and marks pane dead
- killPane uses cached pid and returns false when pane is unknown

createBackend helper 改用 options 对象 + simulatePidWrite 模拟 powershell 写
pidFile,pidFileDir 注入 tempDir,env CLAUDE_WT_PANE_TIMEOUT_MS beforeEach 设置
afterEach 清理。

---------

Co-authored-by: unraid <local@unraid.local>
2026-05-22 21:06:47 +08:00
Dosion
9d17597e58 feat(autofix-pr): 完整完成回流机制 (latent bug fix + completionChecker + 内容回流) (#1240)
* fix(autofix-pr): 修复 taskId 不一致导致 monitor lock dangling

问题:createAutofixTeammate 生成 teammate UUID 作为 monitor lock 的 key,
但 registerRemoteAgentTask 内部生成的 framework taskId 是另一个 UUID。
CCR session 自然完成时框架调 clearActiveMonitor(frameworkTaskId)
guard 失败,lock 永不释放,导致后续 /autofix-pr 报 "already monitoring"。

修复(Phase 1 of remote-agent completion loop):
- monitorState 新增 updateActiveMonitor(partial) 原子更新
- callAutofixPr 在 register 后 swap lock 的 taskId 到 framework 分配的 id
- RemoteAgentTask 引入 registerCompletionHook 注册式 API(参考已有的
  registerCompletionChecker 模式),在 5 个完成路径调 runCompletionHook
- autofix-pr 命令模块自己注册 cleanup hook,避免 framework 反向依赖
  command 模块

测试:
- monitorState 新增 4 个测试(updateActiveMonitor 行为 + bug 复现/修复)
- launchAutofixPr 新增 3 个端到端回归测试(taskId swap + hook 触发 +
  subsequent launch 不报 already monitoring)

完整分析与 Phase 2/3 改造方案见
docs/features/remote-agent-completion-analysis.md。

* feat(autofix-pr): 注册 completionChecker 用 gh CLI 探测 PR 完成

Phase 2 of remote-agent completion loop。Phase 1 修了 monitor lock
dangling,但完成信号仍然只能等 CCR session 自然 archive(timing 不可
预测,且不知道 PR 究竟有没有被修好)。Phase 2 加上主动完成探测。

实现:
- 新增 prOutcomeCheck.ts(纯决策矩阵):summariseAutofixOutcome 给定
  PR 快照 + 基线 SHA 返回 completed/summary。8 个决策分支单元测试。
- 新增 prFetch.ts(spawn 层):runGhPrView 调 gh CLI,fetchPrHeadSha
  在 launch 时捕获基线 SHA,checkPrAutofixOutcome 组合两者。
- AutofixPrRemoteTaskMetadata 加 initialHeadSha?: string 字段,survive
  --resume。
- launchAutofixPr.ts 模块顶部 registerCompletionChecker('autofix-pr',
  ...),5s throttle 防 gh CLI 调用爆。callAutofixPr 启动时调
  fetchPrHeadSha 传入 metadata。

决策矩阵:
  MERGED                  → done(merged)
  CLOSED 未 merge          → done(closed without fix)
  OPEN 无 baseline        → 继续轮询
  OPEN head 未变           → 继续轮询(agent 还没 push)
  OPEN head 变 + CI pending → 继续轮询
  OPEN head 变 + CI failure → done(surface red,user 决定 retry)
  OPEN head 变 + CI success → done(clean fix)

设计:
- gh CLI 而非 Octokit:复用用户已有 auth,不引入 token 管理
- 决策与 spawn 分文件:prOutcomeCheck 纯函数易测,prFetch 单独 mock
  避免 Bun mock.module 进程级污染(已在 launchAutofixPr.test 注释说明)
- 5s throttle:framework 每 1s 轮询,gh CLI subprocess 太重不能跟上
- 失败兜底:fetchPrHeadSha/checkPrAutofixOutcome 失败均不抛,returns
  null/false,framework 继续走原路径

测试:
- prOutcomeCheck 9 个单测覆盖决策矩阵
- launchAutofixPr 5 个新测试:checker 注册 / fetchPrHeadSha 调用 /
  initialHeadSha 传 metadata / SHA 失败仍能 launch / SHA null 处理

完整方案见 docs/features/remote-agent-completion-analysis.md。

* feat(autofix-pr): 内容回流让本地模型读到 PR 修复结果

Phase 3 of remote-agent completion loop。Phase 2 注册了 completionChecker
让框架能在 PR 合并/关闭/有 push+CI 绿时主动完成 task,但 task-notification
仍然只携带 generic 文本(""${owner}/${repo}#42 merged"")。Phase 3 让本地
模型读到远端 agent 自己产出的结构化结果(commits 列表、files 列表、CI
状态、人类可读 summary)。

实现:
- 新增 extractAutofixResultFromLog (src/commands/autofix-pr/
  extractAutofixResult.ts):从 SDKMessage[] 中扫 <autofix-result> tag,
  优先 hook stdout 后 fallback assistant text,latest-wins。10 个单测。
- RemoteAgentTask 新增 registerContentExtractor 注册式 API + 私有
  enqueueRichRemoteNotification(参考 enqueueRemoteReviewNotification),
  在 3 个 generic 完成路径(archived / completionChecker / result-driven)
  先尝试 tryExtractRichContent,有内容用 rich 变体,没有走 generic。
  isRemoteReview 路径不变(它走自己的 enqueueRemoteReviewNotification)。
- launchAutofixPr.ts 模块顶部 registerContentExtractor('autofix-pr',
  extractAutofixResultFromLog)。initialMessage 加 <autofix-result> 输出
  指令(pr-number / commits-pushed / files-changed / ci-status / summary)。

设计:
- 注册式 API(同 Phase 1 hook + Phase 2 checker):framework 不反向依赖
  命令模块,所有 PR-specific 逻辑在 autofix-pr/
- latest-wins:agent 重试时只取最新 tag,旧 tag 不会污染
- truncated tag → null:开 tag 无对应闭 tag 视为不完整,走 generic
  fallback
- 跨 message 不拼接:开 tag 和闭 tag 在不同 message 视为不完整(避免
  误拼字符串)
- 字符串 content 不解析:assistant.message.content 为 string(非 block
  array)的少见路径直接 skip,不 crash

测试:
- extractAutofixResultFromLog 10 个单测(空 log / 无 tag / hook stdout /
  assistant text / hook_response subtype / 多 tag latest-wins / 截断 /
  hook 后于 assistant 的优先级 / 跨 message 不拼接 / 字符串 content
  graceful)
- launchAutofixPr 3 个新测试(extractor 注册 / initialMessage 含 tag
  schema / extractor 真实行为)

完整方案见 docs/features/remote-agent-completion-analysis.md 第 5.3 节。

* fix(autofix-pr): extractBetween 支持 latest tag 截断时回溯到更早完整对

如果远端 agent 重试时写了完整 <autofix-result> 后又开了一个被截断的
第二个 tag, 旧实现只看 lastIndexOf(open) 然后找不到 close 就返回 null,
导致前面那个完整结果被丢弃。改为从尾向首遍历所有 open tag, 返回第一个
能配对的 open/close 对。

附带:
- docs/features/remote-agent-completion-analysis.md: 9 处裸 fenced block
  补 language tag (text/http), 修复 markdownlint MD040 警告
- 同文件: 两处"三选项" → "三个选项" 符合中文量词习惯

* test(autofix-pr): 补齐 completionChecker / 边界 CI 检查覆盖率

针对 codecov patch coverage gap, 补足三块此前未走到的代码路径:

prOutcomeCheck.ts (原 96.92%, 2 lines missing):
- statusCheckRollup === undefined 路径 (与空数组分支不同, GitHub 在无
  checks 配置的 PR 上直接省略字段)
- COMPLETED 状态但 conclusion 为 null/空 的 in-flight 检查归为 pending

launchAutofixPr.ts (原 58.33%, 15 lines missing):
- registerCompletionChecker arrow body: metadata 缺失早返回 / 节流窗口内
  返回 null / completed=false 返回 null / completed=true 返回 summary /
  initialHeadSha 透传到 checkPrAutofixOutcome
- registerCompletionHook 的 if(meta) 短路两侧: 有 metadata 时清空节流条目,
  无 metadata 时仍释放 active monitor lock

所有新测试沿用现有 mock.module 与 registerXxxMock.mock.calls 拉取注册
回调的模式, 无新增依赖。prOutcomeCheck 11/11 本地通过。

* style: biome check --fix 整形 launchAutofixPr.test 新增段

---------

Co-authored-by: unraid <local@unraid.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-22 21:06:26 +08:00
claude-code-best
f2b751f659 chore: 2.6.5 v2.6.5 2026-05-22 21:05:06 +08:00
claude-code-best
d4a601475f fix: 修复 BriefTool 循环依赖导致 isBriefEnabled 未定义
将模块顶层 require() 改为懒加载函数 getBriefToolModule(),
延迟到实际调用时才加载模块,避免循环依赖时模块尚未完成初始化。
2026-05-22 21:04:17 +08:00
claude-code-best
897c186f28 docs: effort 级别描述去掉模型名限制 2026-05-22 20:11:12 +08:00
claude-code-best
03598d3f84 refactor: 移除 resolveAppliedEffort 中的 max/xhigh 降级分支 2026-05-22 20:09:53 +08:00
claude-code-best
7b52054ff5 feat: 解除 max/xhigh effort 级别的模型白名单限制 2026-05-22 20:09:10 +08:00
claude-code-best
66c892521b chore: 2.6.0 v2.6.0 2026-05-21 16:38:25 +08:00