Skip to content

feat(adopt): tmux adopt 模式下 bridge 初始化修复 + 文档评论重复回复修复#293

Open
wln-na wants to merge 2 commits into
deepcoldy:masterfrom
wln-na:feat/local-wip
Open

feat(adopt): tmux adopt 模式下 bridge 初始化修复 + 文档评论重复回复修复#293
wln-na wants to merge 2 commits into
deepcoldy:masterfrom
wln-na:feat/local-wip

Conversation

@wln-na

@wln-na wln-na commented Jun 24, 2026

Copy link
Copy Markdown

修复内容

1. tmux adopt bridge sessionId 回退失败(核心修复)

tmux adopt 发现 pane 时,readClaudeSessionMeta(panePid) 可能拿到的是 wrapper 进程(如 ttadk)的 PID 而非真正 Claude 进程的 PID,导致 ~/.claude/sessions/<pid>.json 找不到 → sessionId = undefinedbridgeJsonlPath = undefined → bridge 不初始化 → CLI 回复无法回传飞书。

修复:将 herdr 专属的 cwd fallback(findUniqueClaudeSessionByCwd)扩展到所有 claude-code adopt,不限 adopted.source === 'herdr'

2. adopt session 重启后 PID 校验误杀

重启后 adopt session 的 PID 不匹配旧进程 → 被误判为 'missing' 并关闭。

修复:adopt session 跳过 PID 匹配校验,保留 session 活跃状态。

3. adopt session re-fork 丢失 bridge 语义

adopt session 的 worker 崩溃后 re-fork 走了普通 forkWorker 路径,丢失 adopt 配置。

修复:re-fork 路径改用 forkAdoptWorker,保留 bridge 初始化。

4. persistent-backend 探测误杀 adopt session

adopt session 的 tmux pane 不遵循 bmx-* 命名,被探测逻辑误判为孤立并杀掉。

修复:adopt session 跳过 bmx-* 探测。

5. 文档评论重复回复

callTenant 误加 responseType: 'text' 导致 SDK 返回原始字符串而非对象 → 自触发过滤失效 → bot 重复回复。

修复:移除 responseType: 'text',恢复 SDK 正常返回对象。

附带改进

  • Worker adopt 分支 sendText + sendSpecialKeys('Enter') 路径加 [adopt] 前缀诊断日志
  • backend is null 兜底日志
  • 文档评论 API 超时保护(15s AbortController)
  • deliverFinalOutput 的 doc-comment marker 检查,避免 botmux send 已发评论后重复发送

@wln-na wln-na requested a review from deepcoldy as a code owner June 24, 2026 07:16
@deepcoldy

Copy link
Copy Markdown
Owner

整体方向赞同 👍 core 的 adopt bridge 三修(PID 校验放宽 / re-fork 改走 forkAdoptWorker / persistent-backend 跳过 adopt)和文档评论去重思路都对,本地 pnpm build 通过、合并当前 master 无冲突。下面是建议处理的点(按优先级):

🔴 阻塞:单测红 + 推翻了一条写在测试里的设计决定

pnpm test 有 1 个失败用例:test/herdr-discovery.test.ts:111findUniqueClaudeSessionByCwd > returns undefined when multiple files match the same cwd)。

本 PR 把 findUniqueClaudeSessionByCwd 从「同 cwd 多个会话 → 返回 undefined(歧义不猜)」改成「按 updatedAt 取最近的」,而该用例的注释正好明确否决了这个做法:

Resuming "the most recent one" sounds tempting but is the wrong call: if two adopt targets exist, the user must pick. So the helper bails.

⚠️ 注意 .github/workflows/ci.yml 只跑 pnpm build 不跑 pnpm test,所以这个红用例 CI 不会拦——但不应带红合并。

风险:该函数有两个调用点——worker-pool.ts(adopt 兜底)和 session-discovery.ts:582(herdr 发现的“当前会话”提示)。在「同一 cwd 跑了多个 claude 会话」且 pid 没命中(没走到新增的 3c real-pid 解析)时,“取最近”可能绑到错误的 transcript,把另一个会话的回复转发到当前话题。

建议二选一

  1. 回退这处,保持「歧义返回 undefined」。多实例场景的 sessionId 兜底其实主要靠本 PR 新增的 3c(COMM_ARGV_LAUNCHERS → findLaunchedCliPid 解析 wrapper 下真实 claude pid),这步已能覆盖大部分;或
  2. 确实要「取最近」,则更新该用例 + 注释说明新理由,并考虑只让 adopt 兜底这一个调用点选最近、发现提示保持严格。

无论哪种,suite 得绿。

🟡 文档评论现在会在话题里多发一张卡 —— 建议做成 bot 级配置

deliverFinalOutput 现在对 doc-comment 轮去掉了早 return,改成「文档评论 + 飞书话题卡」双通道,每条评论回复都会在话题里多发一张互动卡。这是有意的(注释写了“双通道”),但它改变了所有 doc 订阅 bot 的现状(以前只回到评论里)。文档评论一活跃,话题里就一条评论对一张卡,偏吵。

建议把它做成 bot 级开关(dashboard + bots.json 两套配置面),并默认关(保持现状只回评论),需要话题镜像的人再开。可直接参照 #287silentTurnReactions——同款“卡片行为开关”,接 card-prefs-store / dashboard-ipc-server 的 PUT 白名单 / bot-payload / bot-defaults 即可。

🟡 ttadk 网关 env 路由:① 没写进描述 ② 只 gate 了 ttadk,aiden/cjadk 可能漏

这块(worker.ts 把网关 ANTHROPIC_*childEnv 挪到 injectEnv + 用 unsetEnvKeys 剥 model 覆盖)本身是对的修复——tmux/zellij 的 pane 只继承 tmux server 启动时的全局 env、不继承 spawn 时传的 env,所以 daemon 继承来的 ambient ANTHROPIC_* 进不去 pane,claude 退回 api.anthropic.com 绕过网关。

两个问题:

  1. 这是一块与 adopt / doc-comment 无关的独立修复,但 PR 描述完全没提,建议补进描述(或单独拆 PR),并补 tmux-backend-env.test.ts 用例。
  2. 修复只 gate 了 isTtadkWrapper。这个坑机制上不是 ttadk 独有,是 tmux/zellij 通病。aiden x claude / cjadk claude 同样底层是 claude-code——如果它们也依赖机器 ambient 的 ANTHROPIC_* 跑在 tmux 上,会有一样的隐患却没被修。请确认:是有意只修 ttadk(aiden/cjadk 自己内部取 token、不依赖 ambient env),还是漏了?更稳的做法是凭证统一走 bot 的 env 配置(本来就经 injectEnv 正确注入到 pane,所有 wrapper 都没问题),或把修复按“需要网关 env 的 wrapper”泛化,而非写死 ttadk。

⚪ Minor

  • 整个 PR 没有新增/更新测试(13 文件 +423/-83)。尤其去重状态机(delivered / cardDelivered + marker)、validateTmuxAdoptTarget 放宽、ttadk env 路由值得补。
  • buildDocCommentPrompt 写死中文,英文 locale 的 bot 也会拿到中文提示词(原走 tr(..., loc))。
  • event-dispatcher 的 30s 超时 setTimeoutclearTimeout,快路径下空转 30s 拿着闭包。
  • worker.tsdocCommentTurnIds Set 只增不删(轻微无界增长)。
  • callTenantsignal as any:确认 lark SDK 是否真把 signal 透给底层 HTTP,否则 tenant 路径的 15s 超时不生效(外层 30s race 仍兜底)。

做得好的几处:3c real-pid 解析是 sessionId 兜底的对症核心修复;doc-comment 去重在「二次 final_output(不同 lastUuid)」和「botmux send 已发」两种场景都正确;自触发过滤三重保险够稳;stripMarkdown 无 ReDoS。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants