kimi-code/handoff.md
2026-07-01 12:27:58 +08:00

15 KiB
Raw Permalink Blame History

pi-tui 改造交接Phase B 完成 / Phase C 进行中)

接手 agent 请先读本文,再读 plan/ 下的阶段计划phase-b / phase-cresearch.mdoh-my-pi 调研)。本文记录已完成的工作、当前状态、待决事项和下一步。


0. TL;DR当前状态

  • 分支: chore/upgrade-pi-tui-0.78.1(非 main可直接提交。工作树干净plan/research.mdhandoff.md 未跟踪(不要提交这些)。
  • Phase B终端基础设施 全部完成6 task + 整阶段 review + 收尾复审,全部通过)。
  • Phase CApp 层接入):🟡 进行中。Task 1/2/3 已完成 + 通过 reviewTask 5 刚开始即被用户中断未实施Task 6/7 未开始Task 4 经用户确认推迟到 Phase D
  • 测试: pi-tui node --test packages/pi-tui/test/*.test.ts 753/753app pnpm --filter @moonshot-ai/kimi-code test 全绿typecheckpi-tui + kimi-code均通过。
  • 下一步: 继续 Phase C Task 5组件级渲染 partial roots + loader 接入)。

1. 工作流背景

superpowers:subagent-driven-development 技能执行:每个 Task 派 实现者coder→ spec compliance reviewercoder→ code quality reviewercoder,串行;质量审查若不通过则实现者修复后重审。对设计级 scaffold 先派 plan agent 做 deep-dive 逐字提取机制,再交实现者。

关键约束(每个提交都遵守):

  • 语义化提交(feat/fix/refactor/test/docs/perf/chorescope 为 pi-tuikimi-code)。
  • 绝不加 co-author绝不在任何地方出现 "claude" 字样。
  • 只暂存本次改动的文件;plan/research.mdhandoff.md 保持未跟踪。
  • Node-pure禁止 Bun.*、FFI、pi-nativespi-utilsBun.envprocess.env
  • 账本引擎仍在 PI_TUI_ENGINE=ledger 开关后Phase A Task 11 的"切默认"未做),保持现状,不要切默认
  • apps/kimi-code 必须通过 @moonshot-ai/kimi-code-sdk 消费核心能力,直接依赖 @moonshot-ai/agent-core
  • app 侧 TUI 改动遵循 .agents/skills/write-tui/SKILL.md + apps/kimi-code/AGENTS.mdprintableChar() 比可打印键、chalk.hex(colors.<token>) 上色CI guard

验证命令:

# pi-tui
cd packages/pi-tui && node --test test/*.test.ts
pnpm --filter @moonshot-ai/pi-tui typecheck

# appvitest--filter 下路径要包相对)
pnpm --filter @moonshot-ai/kimi-code exec vitest run test/tui/<file>.test.ts
pnpm --filter @moonshot-ai/kimi-code typecheck

参考实现源(只读对照): /Users/moonshot/Desktop/moonshot/oh-my-pi/packages/tui/src/


2. Phase B终端基础设施—— 全部完成

15 个提交BASE 7418b035

Task 提交 内容
1 8ff1ced5 c41edab4 终端能力表 terminal-capabilities.ts + 注入引擎sync/image/ED22 走 caps
2 62b414a2 a077db20 DA1 哨兵探测 FIFO + DECRQMterminal-probe.ts+ 可变 ProcessTerminalCapabilities + engine.refreshSyncFraming()
3 273fd0ad 90a9da7c Headless 模式(#safeWrite 唯一出口、start() 早返回)
4 437492eb fc310cc2 c037f1bc Resize viewport deferalt-screen fast path + 120ms settleledger-safe
5 539d6396 c04635da d7c822f7 输入鲁棒性stdin-buffer partial-hold + paste watchdog + bracketed-paste+ coalesced Escape 修复 + fresh-escape Critical 修复
收尾 8262da57 d83afaf1 7a2a7e55 探测字节防泄漏 + probe→sync 集成测试 + stdin-buffer 定时器 flake 稳定

Phase B 中解决的几个真问题(接手须知):

  • kitty 查询与探测 FIFO 冲突: 我们 terminal.ts 已有 \x1b[>7u\x1b[?u\x1b[c],若再跑探测会让键盘 owner 多发 CSI c 导致 FIFO 失同步——已拆分为 start() 只 push \x1b[>7u、探测的第一个探测 \x1b[?u\x1b[c] 作为唯一 kitty 查询。改探测/键盘逻辑时别再引入重复 CSI c
  • CMUX/TERM 让 mux 检测恒为真: isMultiplexerSession()TMUX/STY/ZELLIJ/CMUX_*/TERM。任何 mux 相关测试必须显式清理这些 envsnapshot/restore否则非 mux 用例变空壳。见 test/resize-defer.test.tsMUX_ENV_KEYS(含 TERM)。
  • WezTerm coalesced Escape 回归: 移植 OMP stdin-buffer.ts 后,\x1b\x1b[27;129:3u 被合并成一个 meta-CSI token我们的 keys.ts 不识别、Escape 失灵。调研发现 OMP 的 keys.ts 委托 Rust nativekeys.rs)且把合并形式解析成 alt+<inner>,照抄既不现实也修不了 Escape。改用方案 (b)-minimalkeys.ts matchesKeyescape 分支加一条窄规则(剥前导 ESC 后递归,仅当内层本身也是 Escape 才认),~12 行,不误伤 \x1b\x1b[Aalt+up
  • fresh-escape Critical bug stdin-buffer 在事件循环卡顿 + 输入洪流时fresh escape 分支会重 hold 而非 flush导致 stale partial 与新按键合并丢键。修复force-flush stale partial + 幂等 #armFlushTimer,带确定性复现测试(回退后 8/8 复现丢键 ["\x1b[<\x1b[A]"])。
  • 探测字节漏进编辑器违反计划明确目标DA1 FIFO 的核心目的就是防这个。已在 terminal negotiation gate 过滤 DECRPM/OSC11parseKeyboardProtocolNegotiationSequence),不让它们到 forwardInputSequence
  • 测试 flake Task 5 两段式 flush timer10ms + setTimeout(0))让旧测试的 wait(15) 余量太紧——提到 wait(50)

Phase B 范围外 / 后续跟进(已刻意不做):

  • kitty 25ms 去重窗口(#pendingKittyPrintableAtMssibling fix。
  • components/input.ts 单行 input 的 paste 解码:目前完全不解 re-encoded control。
  • 引擎 resize/mux 走 #caps 的统一:当前仍读 process.env
  • in-band resize?2048h/ appearance push?2031h)的启用:探测已产出字段但未启用。
  • Phase A Task 11切 ledger 为默认):未做,且本阶段不做。

3. Phase CApp 层接入)—— 🟡 进行中

计划:plan/2026-06-30-pi-tui-app-integration-phase-c.md。改动集中在 apps/kimi-code/src/tui/ + packages/pi-tui/src/ledger/ + packages/pi-tui/src/components/image.ts

3.1 已确认的产品/架构决策(用户拍板)

  1. 决策 2transcript 内存模型):废弃 rendered sliding window。 旧行不销毁、commit 进 native scrollback组件对象 + renderCache 随 turn 增长Task 4 可在 commit 后清 cache 缓解)。匹配 OMP 模型。
  2. Task 1 live 边界:整个当前 turn 起点(用现成 isTurnBoundaryComponentuser-message 组件 = turn 起点。live 覆盖整个当前 turnthinking/工具/assistant零抖动。代价长 turn 的 commit 推迟到 turn 结束。
  3. 决策 3StepSummaryappend-only。 旧 step 留 scrollbacksummary 作为当前 turn 末尾的新行追加summary 不进 transcriptEntries(不进 LLM contextmergeAllTurnSteps 一并改。
  4. Task 4setNativeScrollbackCommittedRows 优化):推迟到 Phase D。 其"跳过重排"收益已被现有 render cache + 引擎 chainStable 覆盖,实际价值只是 commit 后清 cache 的内存优化。

3.2 计划与代码的出入deep-dive 校正,接手须知)

  1. ImageBudget 桩不存在——image.ts 只有 ImageTask 6 是从零新建。
  2. @moonshot-ai/pi-tui 没导出 seam 子路径——计划里的 from "@moonshot-ai/pi-tui/ledger/seam" 会编译失败;已把 seam 类型加到 packages/pi-tui/src/index.ts 导出Task 1 已做)。
  3. TranscriptEntry 没有 transient 字段——transient 只在 AssistantMessageComponent 私有 lastTransient。但 Task 1 用 turn-boundaryisTurnBoundaryComponent),不依赖 transient
  4. StepSummaryComponent 已经是永远折叠单行——Task 3 的"collapsed-by-default"现状已满足,不用新增 UI。
  5. #frameSegments 只追顶层 child——partial roots 粒度是顶层容器,所以 Task 5 对 activity spinner 有收益(跳过 transcript 重渲),对 transcript 内部 spinner 收益小。

3.3 逐 Task 状态

Task 主题 提交 状态
1 TranscriptContainer seamturn 边界)+ seam 导出 9c166d8d 实现 + spec + quality
2 废弃 rendered sliding window db719a88 + 2d1cad56(注释修正) 实现 + spec + quality
3 StepSummary append-only fe16e74e + a5614cd6over-counting 修复) 实现 + spec (发现 over-counting+ 修复
5 组件级渲染partial roots+ loader 刚开始即被用户中断,未实施
6 ImageBudget 实装 未开始
7 真实 app 验证 + 整阶段 review 未开始

3.4 Phase C 中解决的真问题(接手须知)

  • Task 3 over-counting bug重要 mergeCurrentTurnSteps 在每次 appendTranscriptEntry 都跑(kimi-tui.ts:~1743。append-only 后旧 step 仍在树里,每帧被 existingSummary.addCounts(...) 重复计入 summary计数随每次 append 膨胀)。修复:给 StepSummaryComponentsetCounts(thinking, tool)(替换而非累加),mergeCurrentTurnSteps 每帧重算当前 overflow 总数并 set(幂等)。mergeAllTurnStepsreplay 调一次)不受影响,仍用 addCounts。测试已强化(回退 addCounts → 4恢复 setCounts → 2/3可捕获回归
  • Task 2 metadata 保留: 废弃销毁后,旧组件仍在 transcriptContainer.children,其 metadataWeakMapkey=组件)随之保留(getTranscriptComponentEntry 仍能解析,返回 undefined。这是预期的WeakMap 随 children 增长,与组件对象同量级),不是泄漏。已在 trimTranscriptWindow 注释里写清楚(2d1cad56)。
  • seam 类型导出: app 侧 import type { NativeScrollbackLiveRegion } from "@moonshot-ai/pi-tui"(从 . 导出,不是 /ledger/seam)。

4. 当前待办 / 注意事项

立即下一步Phase C Task 5组件级渲染 partial roots + loader 接入)

被用户中断的 implementer prompt 已设计好,要点:

  • 引擎侧(packages/pi-tui/src/ledger/engine.ts
    • subtreeContains(root, target) 模块级 helperduck typing children,避免 engine→tui 循环 import
    • #componentRenderTargets = new Set<Component>()
    • public requestComponentRender(component) 把 target 加进 set。
    • #resolvePartialComposeRoots():把 target 映射到顶层 segment root首帧必须全量返回 null
    • #composeFrame reuse 分支:非 target 且 previous?.component === child && previous.start === offset复用 previous.lines + seam不调 child.render)。首帧全量。chainStable 可保守置 false。
    • doRender() compose 成功后 this.#componentRenderTargets.clear()
  • TUI.requestComponentRenderpackages/pi-tui/src/tui.tsrequestRender 后): ledger 下 engine.requestComponentRender(component),然后 this.requestRender()
  • loaderapps/kimi-code/src/tui/components/chrome/moon-loader.ts:~101 this.ui.requestRender()this.ui.requestComponentRender(this)MoonLoader 持有 this.ui: TUIextends Text(是 Component
  • footer goal badge不切(低频,不在 transcript 子树,全量 requestRender 由 render cache 兜底)。
  • 测试: pi-tui 侧扩展 ledger-engine-golden.test.tsA 重 render、B 不重 renderapp 侧扩展 moon-loader.test.ts(断言调 requestComponentRender(this))。
  • 提交: perf: component-scoped render for loader to avoid full-tree repaints

完整 prompt 在上一轮 assistant 消息里(被中断那条)。可直接复用。

Phase C 剩余

  • Task 6ImageBudget 实装): 从零新建(非替换桩)。要点:在 packages/pi-tui/src/components/image.tsImageBudgetbeginPass/endPass/observe/takePurgeIdscap 默认 8PI_TUI_IMAGE_CAP 覆盖);引擎 compose 前后 beginPass/endPassemit 用 takePurgeIds() 生成 kitty d=I purgedeleteKittyImage 已存在于 terminal-image.ts)。前置子任务: 确认 parseKittyImageHeader/encodeKitty 是否带 i=<id>budget 需从 line 解析 imageId
  • Task 7真实 app 验证 + 整阶段 review 跑 app + pi-tui 全量测试 + typecheck手动验证PI_TUI_ENGINE=ledger 启动 CLI长会话 scrollback、流式 reveal、activity spinner 不重排 transcript、resize、StepSummary、图片 demote

遗留注意事项

  • oxfmt 未在本仓库强制lint-staged 只跑 oxlint,无 format script/CI。各 task 实现者刻意oxfmt(会 reformat 大量无关文件)。如需 repo-wide 格式化,单独立项。
  • pnpm --filter @moonshot-ai/kimi-code test -- <file> 会跑整个套件(不过滤);聚焦单文件用 pnpm --filter @moonshot-ai/kimi-code exec vitest run <path>
  • 全局 PI_TUI_ENGINE=ledger 跑 pi-tui 全量会触发一个 Phase A 遗留失败tui-render.test.ts 的 maxLinesRendered 测试),非 Phase B/C 回归。本阶段验证用默认legacy跑全量ledger 路径由各 ledger 测试自行 withEnv 打开。

5. 关键上下文

计划与调研

  • 计划:plan/2026-06-30-pi-tui-terminal-infra-phase-b.mdPhase Bplan/2026-06-30-pi-tui-app-integration-phase-c.mdPhase C、同目录还有 phase-a/d。
  • 调研:research.mdoh-my-pi vs 我们的差异地图、价值/effort 分级、风险)。
  • oh-my-pi 源(只读对照):/Users/moonshot/Desktop/moonshot/oh-my-pi/packages/tui/src/

仓库约定(摘自根 AGENTS.md + write-tui skill

  • Node >=24.15.0pnpm 10.33.0engine-strict=true)。
  • 可选属性直接传 undefined,不用条件 spread可选属性类型不必再 | undefined
  • 内部单参数方法不要改成 options 对象。
  • 除包的 index.ts 外,其它 index.ts 优先 export * from './module'
  • app TUI 架构:KimiTUI 是 coordinator控制器在 controllers/;组件在 components/{chrome,dialogs,editor,media,messages,panes}/transcript 块在 components/messages/streaming 在 controllers/streaming-ui.ts

已完成的 deep-dive结论在本文

  • Phase B Task 2DA1 FIFO、Task 5stdin-buffer—— 已落地。
  • Phase C 整体 deep-diveagent-30—— 结论见 §3.1/§3.2,逐 task file:line 在当时的 plan-agent 输出里(如需复核可重派 plan agent 读 plan + 代码)。

6. 如何继续

  1. 读本文 + plan/2026-06-30-pi-tui-app-integration-phase-c.md Task 5/6/7 + apps/kimi-code/AGENTS.md
  2. git log --oneline 7418b035..HEAD 确认当前在 a5614cd6(或更新)。
  3. 继续 Phase C Task 5:派一个 coder 实现prompt 见 §4 上方,或上一轮被中断的那条),然后 spec + quality review。
  4. Task 6ImageBudget→ Task 7验证 + 整阶段 review
  5. 整阶段 review 通过后:跑 gen-changesets 技能(默认 minor,不要自行 major)→ PR英文标题 + 填模板,不要暴露 agent 身份或 claude 字样)。

如需用 subagent 继续,沿用 superpowers:subagent-driven-development 技能(实现者 → spec reviewer → quality reviewer