DeepSeek Harness fork 子会话继承父会话排队消息并重复执行:切点越界、投影漏守卫与跨代累积的修法
DSH 里从会话中途 fork(分叉)出新会话后,子会话会「自带」一条你在里面根本没发过的消息并自动执行它——那是父会话已经提交、但还没跑完的排队 prompt,被 fork 的切点一起复制了过来。 更糟的是第二档:如果你先提交了自己的新消息,实际被发出去的却是那条继承来的旧 prompt,而你真正想发的消息从此永久滞留在队列里、永不派发,再 fork 一次还会把滞留项继续继承下去。这三份报告(#6197、#6262、#6314、#6555)指向同一个缺陷:fork 的切点没有区分「状态」和「待办动作」,把两者一视同仁地塞进了子会话的 seed。 本文按「机制 → 两种症状 → 四种解法」拆开,每条都给复现步骤、逐事件证据与可粘贴的补丁/命令。
根因:fork 的切点把「待办」当成「状态」一起搬走
fork 本应只复制「一个已完成轮次的前缀」,但缺陷版本的切点会继续前移,把 turn/end 与下一个 turn/start 之间的所有事件都扫进 seed——而用户的下一条 prompt 恰好永远落在这个区间里。
机制:从 boundary.seq + 1 一直前移到下一个 turn/start
在 @deepseek-ai/dsh-api-session-controller 的 fork() 里(0.1.5 线 lib/index.js:683-685,社区在 master 的 packages/api/session-controller/src/commands.ts:243-246 逐行核对过),切点是这样推进的(#6262):
const lastSeq = source.events.at(-1)?.seq ?? -1;
const boundary = (atSeq === undefined ? undefined : source.events.find((event) => event.type === "turn/end" && event.seq >= atSeq))
?? (atSeq === undefined || atSeq > lastSeq ? source.events.findLast((event) => event.type === "turn/end") : undefined);
if (boundary === undefined) throw new RemoteError("session/fork-unavailable", /* … */);
let cut = SessionLogOffset(boundary.seq + 1);
while (cut < source.events.length && source.events[cut]?.type !== "turn/start") cut = SessionLogOffset(cut + 1); // ← 缺陷在这
seed = source.events.slice(0, cut);
inheritedEventCount = cut;
meta = { /* … */ parentSession: source.header.id, isSeeded: true };
那个 while 循环的意图本身是合理的:把 turn/end 之后、下一个 turn/start 之前的包级状态事件一并继承,否则子会话会丢掉权限、沙箱、标题等状态。问题是——它把「状态」和「待办动作」一起扫了进来(#6197):
| 区间内的事件 | 语义 | 是否应继承 |
|---|---|---|
permission/preset、sandbox/mode、approval/policy | 状态 | ✅ 应继承 |
session/title | 状态 | ✅ 应继承 |
request/header(模型选择变更) | 状态 | ✅ 应继承(有测试依赖) |
agent/inbox/spliced(target: "next-turn") | 待办动作 | ❌ 不应继承 |
缺陷本质一句话:切点逻辑没有区分事件语义,把「待办动作」也当成「状态」塞进了 seed。
复现步骤(两种入口都会中招)
触发条件很窄但完全确定性:在「下一轮消息已提交、但尚未执行完毕」的时刻分叉(#6197):
- 与 DSH 跑完一个轮次 turn N(
turn/end turn=N)。 - 提交下一条 prompt(turn N+1 已开始或已完成)——它先以
agent/inbox/spliced {target:"next-turn"}入队,随后才产生turn/start turn=N+1。 - 在 turn N 的最后一条回复上点「在新对话中分支」(客户端执行
forkAt(closing.finalNode.seq));侧边栏会话行菜单的「分叉会话」不带atSeq,同样复现。 - 打开子会话——turn N+1 的那条消息被继承,并在子会话中重新执行一遍。
关键点:第 3、4 步之间不需要抢时机。 host 的切点是「锚点 turn 的 turn/end 之后一直推进到下一个 turn/start 之前」,只要锚点 turn 的下一个 turn 已存在于日志中,该 turn 的 prompt 插入事件就必然落进 seed(#6555)。
逐事件对照:三次独立复现,形状完全一致
三份报告各给了真实日志,事件顺序一模一样(#6262、#6555、#6314):
turn/end(N) → agent/inbox/spliced { inserted: [下一条用户 prompt] } → turn/start(N+1)
→ agent/inbox/spliced { removedCount: 1, inserted: [] } → step/start → user/message
- #6262:父会话
session-892cd3e7…(isSeeded:false)在 seq 695turn/end turn=3,697 是一条带「我刚在本地…」的 splice,698turn/start turn=4,702 才是user/message。在 turn 3 尾部(atSeq ≈ 693)分叉 →boundary = 695,cut被推到 698,seed 的最后一条正是 697 那条 splice。子会话session-54d4d9dd…的 inherited 标记在 698,其首个user/message在 705,内容与父会话 turn 4 逐字相同。 - #6555:子会话
session-6054df05…的session/end-seed {inherited:true}在 seq 32(inheritedEventCount=32),seq 35-63 的turn/start turn=2认领了start=0,seq 38 的user/message id=5b6b79f4正是父会话已执行过的 B,且完整跑了一轮真实工具调用(web_search / web_fetch / pwsh);用户新输入的 C 被排到start=1,直到 seq 64-75 的turn=3才执行。 - #6197:同一批数据里三个切点
inheritedEventCount分别为 254 / 262 / 265,各自把父会话的下一条排队消息跑成了自己的turn 5@257/turn 6@265/ 一直 pending。
fork 链上还会叠加:子会话自己 fork 时,它的日志末尾正好是继承来的 turn/end,切点前移又把「继承 splice + 自己回放的那条 user/message」一并带走 ⇒ 一次分叉继承两条排队 prompt(session-1b17eb0b… 即此形状,#6262)。
一行自测:确认你的日志里是不是这个形状
对任意 v3 日志,用锚点 atSeq 复算旧切点,看被扫进 seed 的区间里有没有 agent/inbox/spliced(#6262):
import json
evs = [json.loads(l) for l in open("session.v3.jsonl") if l.strip() and "seq" in l]
at_seq = 693 # 分支按钮传入的 atSeq
boundary = next(e for e in evs if e["type"] == "turn/end" and e["seq"] >= at_seq)
cut = boundary["seq"] + 1
while cut < len(evs) and evs[cut]["type"] != "turn/start":
cut += 1
print("boundary", boundary["seq"], "cut", cut)
for e in evs[boundary["seq"]:cut]:
print(" swept into seed:", e["seq"], e["type"])
# 实测输出:697 agent/inbox/spliced ← 下一条用户 prompt
两种症状:静默重跑,以及「A 被重放、B/C 永久滞留」
继承(机制)与重放(执行)是两件事,「跨代累积」又是第三件。 三者叠起来才解释得清两种表面症状(#6314)。
症状一:同一条消息在父、子两个会话里各执行一次
静默重跑:子会话打开即多出一轮对话,意外消耗 token,历史里多出一条重复 user/message。用户既没发过它,也拦不住它(#6197)。
症状二:旧 prompt A 劫持首轮,新 prompt B/C 永久滞留
当 fork 发生在源会话回复进行中时,症状升级(#6314):
- 在子会话里提交新 prompt B,实际被发送的却是源会话的原始 prompt A;
- 你真正提交的 B 进入队列后永远不被派发,无论对话是否结束;
- 再 fork 一代(会话 2 → 会话 3),A 又被重放一次,且 B 也随 fork 继承并累积。
机制链一共五环,每环都有源文件行号(社区在 c291e7961a(= tag dsh-v0.1.5-rc.2)上逐条核对,#6314):
- 入队是持久事件,不是内存队列。 inbox 是
agent/inbox/spliced上的投影(packages/core/agent-loop/src/inbox.ts:27-65的inboxProjectionDefinition,live 与 reconstruction 用同一份 fold);followup()→send(msg,'next-turn',true)→inbox.splice(...)→session.append('agent/inbox/spliced', ...)(:230-238)。 - 消费是另一次 splice,而且发生在
turn/start之后。turn()先append('turn/start')(agent-loop/src/agent.ts:278),然后才preStep()→inbox.claim()(:244)。⇒ 已进入 turn 的在途输入,此刻在日志里仍然算 pending。 - fork 的切点会跨过那个
turn/end。 即上文那段while,于是 A 的插入事件被完整切进 seed。 - 投影把整段日志(含继承前缀)重放一遍。 投影不按
inheritedEventCount截断:cellFor()惰性建 cell 时buildCell(..., session.snapshotEvents())折整段日志(session-projection/src/index.ts:614-624);drive()的 late build 也折[0, event.seq)(:662-670)。对照之下,同一版本里schedule与subagentCatalog两个投影都写了if (event.seq < state.inheritedEventCount) return state;——只有 inbox 漏了这个边界守卫(#6555)。 - 子 Agent 第一个 turn 就把它 claim 走。
preStep('next-turn')在 seed 里读到 pending 的 A,:375以surfaceOp:'append'落一条真实user/message——这就是「A 被再次发送」;turn/end之后:344if (!this.inbox.hasPending) return false⇒ 因为 B 还排在队列里(hasPending为真),立刻又开一个 turn;而:294if (turnEnds && decision.messages.length === 0) break只在「消息被摘空」时才让它退出。⇒ B 永远等不到属于自己的 turn 边界,于是永远不派发。每轮只取队首 1 条next-turn(inbox.ts:113)也是这个后果的一部分。
为什么队列面板看不见?干预窗口其实存在,只是不可发现
这里最糟的是两条互不相通的数据通路(#6197):
- 通路 A(分叉复制):持久化日志 → seed 里含
agent/inbox/spliced→ 会执行。 - 通路 B(队列面板):运行时 live agent → 独立 control 流 → 客户端
SessionQueueMirror。冷会话没有 live agent → control 流没有 queue 数据 → 客户端queue为空 →dsh-client-ui-conversation/lib/client.js:14096的if (rowCount === 0) return null;整个面板不渲染。
| 会话状态 | 日志里有这条消息吗 | 队列面板看得见吗 | 能编辑/删除吗 |
|---|---|---|---|
| 刚分叉、未激活(冷) | ✅ 有 | ❌ 面板根本不渲染 | — |
| Live 但没提交 prompt(例如切了一次审批策略) | ✅ 有 | ✅ 能看见 | ✅ 能——仍是待处理队首 |
| 已提交 prompt(turn 运行中) | ✅ 有 | ✅ 能看见 | ❌ 已被消费成历史 user/message |
中间那一行就是干预窗口。 它之所以隐蔽,是因为打开它的动作与你想要编辑的消息毫无关系:dsh-user-approval 的 ApprovalService.setPolicy(agent, policy) 接收一个live agent,所以切换审批策略(never ↔ ask)或权限预设会激活会话、拉起 control 流与队列面板,却不提交任何 prompt——继承项因此还没被消费。此时「编辑排队消息」能拿到完整原文,Edit / Remove 都有效(#6197)。
客户端按钮拦不住它:dsh-client-ui-chat/lib/client.js:3667 的 branchUnavailable 只覆盖「这一轮后面还有聊天节点」:
branchUnavailable: data.branchUnavailable || hasLaterChatNode
它不覆盖「队列里还压着一条未执行消息」,所以按钮照常可点,UI 对将要发生的事完全不解释(#6197)。
怎么修:看版本、打补丁、挂守护、先规避
先确定你这条版本线修没修,再决定是升级、打补丁还是装插件;处在 0.1.5 线时,任何时候都在「随时会中招」的状态。
先看版本:0.1.6-alpha.1 起上游已修,但有残留
社区对已发布的 npm 产物逐个读过(不是读源码树),结论很干净(#6314):
| 版本 | lib/index.js 里的切点 | 是否中招 |
|---|---|---|
0.1.5-alpha.1 / 0.1.5-alpha.2 / 0.1.5-rc.2 | 仍带 while (… !== "turn/start") cut++ | ❌ 中招 |
0.1.6-alpha.1 及以后 | 只有 const cut = SessionLogOffset(boundary.seq + 1); | ✅ 已修 |
修复提交是 f74d625e5a(PR #4001,分支 fix/session-fork-boundary),于 2026-09-11 合入 master,并有实现说明 .agents/notes/implemented/bug-fix/2026-09-11-session-controller-fork-turn-cut.md(#6314)。也就是说:0.1.5-rc.1 与 0.1.5-rc.2 的 lib/index.js 字节完全相同(md5 2443ac0c1184472ca09d064a38a8e344),这两条线都带缺陷。
残留要说清:上游决策只修「切点越界」这一形状,不重新定义「在被选中那一轮内部就已 pending 的输入」——决策原文是 "Events already inside the selected prefix retain their ordinary replay semantics; this decision does not redefine pending input inserted before the selected closing event." 所以:
- 在该轮内部就发起的
followup(); - 或轮次结束时仍停在
next-step的steer()/inject();
每一条版本线(含 0.1.6)都仍会把它们复制给子会话。 另有一条跨版本差异关系到自救方式:inbox 在 0.1.2-rc.1 / 0.1.3-alpha.2 上是从 session.ownEvents() 重建的(seeded 子会话在那里 inbox 为空、幻影不会出现),从 0.1.5 起改成折整段会话——所以做「补偿性移除」时必须作用于「子会话 inbox 实际持有的」与「被复制前缀能解释的」两者交集,不能只看前缀折叠结果(#6314)。
方案一(最省事):升级到 0.1.6-alpha.1 及以上
- 确认当前版本:
dsh --version(在 0.1.5 线就命中旧循环)。 - 升级到
0.1.6-alpha.1或更新的发布版。 - 复测:对非末轮的 turn 尾部执行 fork,打开子会话,确认队列为空、首轮由你自己的输入开启。
代价:alpha 线本身有新的变更面;且上面列的「轮内 pending 输入」残留仍在。
方案二(留在 0.1.5 线的最小补丁):把 inbox splice 当切点硬边界
不要直接删掉那个 while。 仓库里已有一个测试依赖它把 turn/end 之后的 request/header(模型选择变更)带进子会话种子,直接删会让测试变红(#6262)。正确做法是继续扫到下一个 turn/start,但一遇到带 inserted 内容的 agent/inbox/spliced 就停(社区在服务器侧独立复现后给出的已验证最小改动,#6314):
let cut = SessionLogOffset(boundary.seq + 1);
- while (cut < source.events.length && source.events[cut]?.type !== "turn/start") cut = SessionLogOffset(cut + 1);
+ // 种子同样必须在 agent/inbox/spliced 之前截断,而不仅仅在下一个 turn/start 之前
+ while (cut < source.events.length && source.events[cut]?.type !== "turn/start"
+ && source.events[cut]?.type !== "agent/inbox/spliced") cut = SessionLogOffset(cut + 1);
理由:种子本应是 completed-turn prefix,boundary 之后的事件不该进入分支;其中只有 agent/inbox/spliced 是活状态(turn 配对 / compaction 括号 / model-selection 等区间事件保留,避免误伤)。无待发 inbox 变更时,新代码与官方行为逐字节一致。 验证:同一条 atSeq,旧 cut=780 得继承队列 [A, B],新 cut=776 得 [];node --check 通过(#6314)。
同一思路的 upstream 分支是 Mide69/deepseek-harness 的 fix/session-fork-seed-boundary,带回归测试(复现 turn/end → agent/inbox/spliced 带排队 prompt → turn/start),并跑过完整 typecheck 与 lint(#6262、#6555)。
方案三(不想改源码):守护插件
社区把修复打包成了可挂载的守护插件(非官方):
dsh-plugin-fork-inbox-guard:npm 与源码见 npmjs.com/package/dsh-plugin-fork-inbox-guard,复现记录在源码仓库的docs/verification.md(#6197)。@argszero/cordis-plugin-fork-inbox-guard:按上文「交集」逻辑实现补偿移除的另一版(#6314)。- umbrella bundle
dsh-community-fixes收录了它,免去「每个修复各装一次、各挂一次」的麻烦:
dsh plugin --profile <你的 profile> add dsh-community-fixes
再把 dsh-community-fixes 加进该 profile 的 dsh.profile.bundles,一条线即可挂载全部已验证修复(每一个仍可在 profile 自己的补丁层里单独 disabled)。单独安装 dsh-plugin-fork-inbox-guard 依然有效。
它怎么修:在 agent/created 时,对 header.isSeeded 的会话,只重放 session.inheritedEventCount 之前的事件(按 inbox splice 规则折叠),得到「分叉那一刻正在排队」的精确 id 集合,再用 agent.inbox.remove(id) 持久地移除它们——写入的是 agent/inbox/spliced,不是只改内存。子会话在切点之后为自己排队的新输入不受影响;非 seeded 会话一律不碰,所以 resume 回来的会话保留自己的队列。仓库里另有 12 个契约测试。
在 master aa8262ec091 上用真实的 sessionController.fork()(不是模拟)端到端验证(#6555):
| 运行 | 子会话 pendingNextTurn | 子会话自有事件 | 父会话队列 |
|---|---|---|---|
| 未挂插件 | 含父会话排队消息 id | [] | 未受影响 |
| 挂上插件 | [] | 一条持久 agent/inbox/spliced { removedCount: 1, outcome: "canceled" } | 未受影响 |
边界说明:这只是把解决方案打包成可挂载守护插件,不是上游补丁——bundle 补丁够不到 commands.ts 与 inbox 投影内部。真正的 upstream 修复仍应落在那处 cut 条件(或给 inboxProjectionDefinition 加 inheritedEventCount 判断)。在官方合入前,插件让现有部署立刻受益。
方案四(当下规避):控制分叉时机 + 用 QueueDock 清场
- 在分叉点之后还没有新提交时立刻分叉:此时父会话最后一条事件就是
turn/end,cut等于日志长度,分叉干净(#6262)。 - 侧边栏「分叉会话」同理:需在无排队、无进行中轮次时使用。
- 已中招的子会话:立即中止,按上文「切换审批策略→队列面板出现→Edit/Remove」把它清掉;注意子会话可能在打开瞬间就已开跑。
- 不要把「分叉」用于「基于历史继续对话」——这正是缺陷最伤的用法;在修复版本或插件到位前,宁可新建会话重述上下文(#6314)。
生态里还有一个期望语义的参考实现:dsh-better-sidebar 的「保存为新会话」会显式判断 threadTrailingPending(最后一条 user/message 未被 turn/end 回答),并不把待处理追问带进新会话(#6262)。
排查注意事项
分叉类问题最容易走偏的地方,是把它当「消息发错」去查客户端或网络——它其实是 host 侧 seed 语义问题。 八条要点:
- 先看是不是 fork 出来的会话:
header.isSeeded === true且parentSession指向父会话。判别只需一步:取那条被重放的user/message事件的seq,与子会话的inheritedEventCount比较——落在之内 = 继承(core 面),落在之外才是客户端面(#6314)。 - 「不需要抢时机」是本缺陷的关键特征:只要锚点 turn 不是最后一个 turn,下一条排队消息必然落在
turn/end与下一个turn/start之间(#6555)。 - 它很普遍,不是偶发:对本机全部 61 个会话日志扫描每个
turn/end锚点,锚点 640 个,旧切点≠新切点的 603 个,累计被错误继承的待发消息 596 条(#6314)。 - 别用
isSeeded做投影跳过:seed 整体不经过session/event发布(core/session/src/index.ts:477-479注释原文 "constructor seeds do not emit")。读投影时若只看isSeeded就跳过前缀,会把真实历史(含子会话自己落下的那条user/message)一起漏掉,比原缺陷更糟(#6314)。 - 「继承」与「重放」是两件事,「跨代累积」是第三件:继承是 seed 机制,重放成真实
user/message在agent.ts:375,跨代累积来自cut的默认前移(子会话 fork 时日志末尾正是继承来的turn/end)(#6314)。 - 这不是设计意图,而是回归:同一版本里
schedule(dsh-schedule/lib/types/projection.js:55)与subagentCatalog(dsh-subagent/lib/types/catalog.js:73)都写了if (event.seq < state.inheritedEventCount) return state;,且session/end-seed文档写明边界之前的事件「本会话一件都不是它产生的」——只有 inbox 投影漏了边界守卫(#6555)。 - 若你选择给投影加守卫,必须同时提升
stateVersion:否则投影缓存session_projcache里的旧行会在冷启动被原样恢复,修复看起来"没生效"(#6555)。 - 上游把它当「语义决策」而非纯补丁:
packages/core/agent-loop/tests/inbox.spec.ts:129-155(projects inherited inbox events in a forked session)把「继承 pending」断言为期望行为——所以这不是没人踩过的地方,改它需要一次明确的语义拍板,而不是顺手打补丁(#6314)。
排查这类「会话行为不对」的问题时,先用 DSH Plugin Hub 的已安装列表和设置页确认你挂的是哪个版本、装了哪些守护插件,比逐个会话翻日志快得多——尤其是 fork 这类问题,往往就是版本线没对齐。

来源:Discussion #6197、Discussion #6262、Discussion #6314、Discussion #6555。
常见问题
因为 fork 的切点(cut)从「锚点那一轮的 turn/end」一直前移到「下一个 turn/start 之前」,把这一区间里等待执行的 agent/inbox/spliced 事件一并复制进了子会话的 seed。收件箱是从这类事件折叠出来的,所以子会话一打开就把它当成自己的待办,在首轮执行掉。同一条消息于是会在父、子两个会话里各执行一次。
这是同一根因的第二档症状。子会话首轮先消费了继承来的旧消息,而消费后队列里还留着继承项,turn/end 之后的判断看到「仍有 pending」就立刻再开一轮,导致你新提交的消息永远等不到属于自己的轮次边界,永久滞留。在 0.1.5 线可用本地最小补丁或守护插件止血,升级到 0.1.6-alpha.1 及以上可从根上消除该形状。
队列面板由独立的 live control 流填充,冷会话没有 live agent 就整块不渲染,所以你刚分叉、还没做任何操作时看不到它。可干预的窗口确实存在,只是不可发现:在子会话里切换一次审批策略(例如 approval policy 的 never ↔ ask)或权限预设,会话会变成 live 但不提交任何 prompt,此时队列面板会显示这条继承消息,「编辑排队消息」可以拿到完整原文,Edit / Remove 都有效。一旦你先发了一条 prompt,这个窗口就永久关闭。
上游在 2026-09-11 已合入修复(PR #4001,提交 f74d625e5a),首个包含它的发行版是 0.1.6-alpha.1;0.1.5-alpha.1 / alpha.2 / rc.1 / rc.2 都仍带旧循环。注意修的是「切点越界」这一形状,若输入是在被选中的那一轮内部就已 pending(在该轮里 followup,或轮次结束时停在 next-step 的 steer/inject),每一条版本线(含 0.1.6)仍会把它复制给子会话。仍在 0.1.5 线的部署可用守护插件或本地补丁止血。
可以,但要在它「打开瞬间就开跑」之前介入:先中止正在进行的轮次,再用切换审批策略的办法让队列面板出现,然后把那条多出来的消息 Edit 成你真正想发的内容或直接 Remove。若它已经把旧消息跑完并写进了历史,历史消息在 DSH 里没有编辑入口,只能放弃这个子会话重新分叉。
相关术语
- fork 切点(cut)
- fork 切点是 fork 生成子会话时,从父会话日志里截取「已完成轮次前缀」作为 seed 的边界下标。理想实现是 boundary.seq + 1;缺陷版本会从此处继续前移,直到下一个 turn/start 之前,从而把 turn/end 与 turn/start 之间的非轮次事件也扫进 seed。— https://github.com/deepseek-ai/deepseek-harness/discussions/6262
- agent/inbox/spliced
- agent/inbox/spliced 是 DSH 里表示「收件箱变更」的持久事件,带 target(next-turn / next-step)、start、removedCount 与 inserted。用户提交消息先以一次 splice 入队,turn 开始后再以另一次 splice(removedCount)消费;收件箱队列本身是这些事件的投影,不是内存状态。— https://github.com/deepseek-ai/deepseek-harness/discussions/6314
- inheritedEventCount
- inheritedEventCount 是 seeded 子会话头部记录「继承了父会话前多少个事件」的字段,等于 fork 切点。schedule 与 subagentCatalog 投影都会用它跳过 seed 区间,而 inbox 投影漏了这个守卫,于是把继承前缀里的排队消息也折叠成了子会话的待办。— https://github.com/deepseek-ai/deepseek-harness/discussions/6555
- session/end-seed(inherited 标记)
- session/end-seed 是 seeded 子会话在切点写入的标记事件,继承来的那份带 inherited: true,文档明确「边界之前的事件本会话一件都不是它产生的」。它本可作为投影的边界依据,但 inbox 投影没有读取它,只把它当普通事件折叠。— https://github.com/deepseek-ai/deepseek-harness/discussions/6262
来源
- #6197 — [Bug] 分叉(fork)会继承父会话"已入队未执行"的消息并在子会话自动重跑,且没有任何干预窗口· deepseek-ai(GitHub Discussions)
- #6262 — [Bug Report] session.fork 的 seed 多带一条 user prompt:turn/end 与下一个 turn/start 之间的事件被扫进子会话· deepseek-ai(GitHub Discussions)
- #6314 — [Bug][0.1.5-rc.x] Fork 出的会话发送新消息时重放源会话旧 prompt(A),新 prompt(B/C)永久滞留队列不执行· deepseek-ai(GitHub Discussions)
- #6555 — 【BUG】fork 出的子会话继承父会话未认领的收件箱消息并重复执行· deepseek-ai(GitHub Discussions)