feat(cursor): spawn 会话补齐无 botmux send 时的 transcript 兜底(对齐 codex) - #932
Open
deepcoldy wants to merge 2 commits into
Open
feat(cursor): spawn 会话补齐无 botmux send 时的 transcript 兜底(对齐 codex)#932deepcoldy wants to merge 2 commits into
deepcoldy wants to merge 2 commits into
Conversation
botmux 自己 spawn 的 cursor 会话此前没有 transcript 兜底:structured bridge 的 allowlist 把 cursor 限制为 adopt-only,模型完成一轮却忘调 `botmux send` 时整轮静默(claude/codex 均会从 transcript 收割最终回答 补发)。cursor 的排水器与共享 CodexBridgeQueue/兜底闸门早已就位,本次 只补 spawn 侧接线: - structured-bridge-clis: cursor 移入 ALWAYS 列表,删除 adopt-only 特例 (isStructuredBridgeFallbackActive 去掉 adoptMode 参数) - worker spawnCli: 增加 cursor 桥 attach 分支——resume 用已知 chatId 直接 解析 JSONL attach(baseline-existing,历史不重放);fresh 先武装 1s poller,等 chatId 观察到位后 late-attach - observeCursorCliSessionId: 观察到 chatId 后同步通知 codexBridgeNotifyCliSessionId(已 attach 时 first-attach-wins 直接返回) - cursorLateAttachMode: 补非 adopt 语义——fresh spawn 从字节 0 摄入 (chatId 级 JSONL 只含本会话),resume 基线在既有尾部之后;adopt 分支 行为不变(birthtime 判定) 兜底语义与 codex 完全一致(同一 emitReadyCodexTurns + send-marker 闸门): 本轮已 send 则压制、prose+哨兵取 prose 补发、纯哨兵按静默处理。已知边界 不变:`--continue` 降级 resume(无 chatId 可观察)与 sandbox(宿主读不到 JSONL)下兜底不激活,与改动前行为相同。 影响面:cursor 非 adopt 会话新增兜底路径;adopt 路径仅注释级变化;其余 structured CLI(codex/traex/coco/hermes/mtr/pi/grok)的 allowlist 判定 结果不变。 测试:pnpm build 通过;vitest structured-bridge-clis(5)/ cursor-transcript(22)/bridge-fallback-gate(66) 全绿;全量 pnpm test 除 skill-doctor-command 2 例环境相关既有失败(干净 master 同样失败)外全绿。 Co-authored-by: Cursor <cursoragent@cursor.com>
飞书实测暴露:spawn 的 cursor 会话第 1 轮兜底正常(attach 全量排水路径),
第 2 轮起模型不调 botmux send 时整轮静默。根因是 cursor-agent
(2026.08.11)的 agent-transcript JSONL 并非严格 append-only——每轮结束在
文件尾追加 {"type":"turn_ended"} 状态 footer,下一轮落盘时把 footer 截掉、
从其原字节位置重写(footer 挪至新文件尾)。排水器按字节 offset 增量读,
上一轮结束后 offset 停在 footer 之后,下一轮 user 行起始于 footer 原位置
(offset 之前),永远读不到 → 指纹匹配不上 → 20s 归因租约过期整轮丢弃
(live 日志:expired 1 structured head(s) without transcript start)。
adopt / resume 场景第 2 轮起同样受影响。
三处修复:
- cursor-transcript drainCursorTranscript:newOffset 永不越过尾部已解析的
role-less status 行(完整行与无换行 tail 两条路径都处理);footer 每 tick
重读约 40 字节、重解析为零事件,不会产生重复
- cursor-transcript 新增 cursorBaselineOffset:resume/重启重挂的 baseline
同样回退到尾部 footer 之前,worker cursorBridgeAttach 接入
- codex-bridge-queue 新增 refreshUnstartedHeadAttributionLease:cursor 的
mirror 可能到首个 assistant step(纯生成回合则整轮)结束才落盘 user 行,
worker tick 在 CLI 忙时(非 adopt)每秒刷新未开始 head 的归因租约,20s
倒计时实际从 CLI 空闲起算;吞 Enter 场景的僵死保护语义不变
影响面:drainCursorTranscript 与 baseline-existing-skip-tail 均为 cursor
专属路径(已核对全部调用方);queue 新方法仅 cursor tick 分支调用,其它
structured CLI 行为不变。
测试:新增按 live 复现逐字节构造的 footer 重写回归用例、baseline 回退用
例、租约刷新/过期语义用例;cursor-transcript(32)/codex-bridge-queue(75)/
structured-bridge-clis(5)/bridge-fallback-gate(66) 全绿;全量 pnpm test 除
skill-doctor 2 例既有环境失败外,group-join-shared-routing 与
doc-comment-daemon-concurrency 在全量并发下偶发超时、单独复跑均绿。
Co-authored-by: Cursor <cursoragent@cursor.com>
Owner
Author
Live 实测发现回归并已修复(d6bb8bf)首次部署后飞书实测:spawn 会话第 1 轮兜底正常(attach 全量排水路径),第 2 轮起失效——worker 日志 根因:cursor-agent(2026.08.11)的 agent-transcript JSONL 并非严格 append-only。每轮结束在文件尾追加 修复(均 cursor 专属路径,已核对全部调用方):
测试:新增按 live 复现逐字节构造的 footer 重写回归用例等 10+ 例;相关 4 个测试文件 178 例全绿;全量除 skill-doctor 2 例既有环境失败外全绿(group-join-shared-routing / doc-comment-daemon-concurrency 为全量并发下偶发超时,单独复跑绿)。已重新部署 live daemon 复测中。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
改了什么 & 为什么
botmux 自己 spawn 的 cursor 会话此前没有 transcript 兜底:structured bridge 的 allowlist 把 cursor 限制为 adopt-only(
420aa12fe引入 cursor 桥时的范围),模型完成一轮却忘调botmux send时整轮静默——而 claude/codex 会从 transcript 收割最终回答补发。cursor 的排水器(cursor-transcript.ts)与共享 CodexBridgeQueue / 兜底闸门(bridge-fallback-gate.ts)早已就位,本次只补 spawn 侧接线:structured-bridge-clis.ts:cursor 移入STRUCTURED_BRIDGE_ALWAYS_CLI_IDS,删除 adopt-only 特例(isStructuredBridgeFallbackActive去掉adoptMode参数,唯一调用方同步更新)worker.tsspawnCli:新增 cursor 桥 attach 分支——resume 用已知 chatId 直接解析 JSONL attach(baseline-existing,历史不重放);fresh spawn 此时还没有 chatId,先武装 1s pollerworker.tsobserveCursorCliSessionId:pid→store.db fd 观察到 chatId 后,除 persist 外同步调codexBridgeNotifyCliSessionId(JSONL 未创建时挂 pending sid 走 late-attach;已 attach 时 first-attach-wins 直接返回)worker.tscursorLateAttachMode:补非 adopt 语义——fresh spawn 从字节 0 摄入(chatId 级 JSONL 只含本会话,即使 attach 前首轮已落盘也不会误吞),resume 基线在既有尾部之后;adopt 分支行为逐字不变(birthtime 判定 + 异常回退 baseline-existing)兜底语义与 codex 完全一致(同一
emitReadyCodexTurns+ send-marker 闸门):本轮已botmux send则压制;prose+BOTMUX_NOTHING_TO_SEND哨兵取 prose 补发(做了活忘发的场景);纯哨兵按真静默处理,不打扰话题。影响面评估
emitTurnTerminal有 per-turn claim 去重,结构化终端与 screen-idle 终端双路发射与 coco/grok 现状同形--continue降级 resume(无 chatId 可观察);sandbox 下宿主读不到~/.cursor/projectsJSONL测试
pnpm build通过pnpm vitest run test/structured-bridge-clis.test.ts test/cursor-transcript.test.ts test/bridge-fallback-gate.test.ts→ 93 passedpnpm test→ 950 files passed,仅test/skill-doctor-command.test.ts2 例失败;已在干净origin/masterworktree 复跑确认为环境相关的既有失败,与本改动无关pnpm switch:here && pnpm daemon:restart后在飞书新开 cursor 话题实测(结果将补充在评论)Made with Cursor