From a6be5f7f628114b180b8ccf3bb3893745829a4d7 Mon Sep 17 00:00:00 2001 From: moranzhi Date: Mon, 14 Sep 2026 01:14:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=85=E5=9C=A8=E6=9C=89=E5=BF=AB=E7=85=A7?= =?UTF-8?q?=E6=88=96=E6=A3=80=E6=9F=A5=E7=82=B9=E6=97=B6=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E9=87=8D=E5=87=BA=E4=B8=80=E7=89=88=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 避免无上下文可复用时误点重跑。 Co-authored-by: Cursor --- src/server/message-branch.ts | 17 ++++++++++ tests/message-branch.test.ts | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/server/message-branch.ts b/src/server/message-branch.ts index 61fd70a..837e38f 100644 --- a/src/server/message-branch.ts +++ b/src/server/message-branch.ts @@ -215,6 +215,23 @@ export function isRefreshableMessage(msg: BranchableMessage): boolean { return kind === "worker_questions" || kind === "worker_output"; } +/** 与 refreshMessage 同一套门槛:类型对,且有 worker 快照或该条检查点。 */ +export function canAttemptRefresh( + msg: BranchableMessage, + opts: { + lastWorkerId?: string; + hasLastWorkerSnapshot: boolean; + hasMessageCheckpoint: boolean; + }, +): boolean { + if (!isRefreshableMessage(msg)) return false; + const workerId = + (typeof msg.actor === "string" && msg.actor.trim()) || + opts.lastWorkerId?.trim(); + if (!workerId) return false; + return opts.hasMessageCheckpoint || opts.hasLastWorkerSnapshot; +} + export function findPrecedingUserIndex(messages: BranchableMessage[], fromIndex: number): number { for (let i = fromIndex - 1; i >= 0; i--) { if (messages[i].role === "user") return i; diff --git a/tests/message-branch.test.ts b/tests/message-branch.test.ts index 600d3cd..7bafc29 100644 --- a/tests/message-branch.test.ts +++ b/tests/message-branch.test.ts @@ -5,6 +5,8 @@ import { ensureBranchForEdit, recordPreMessageCheckpoint, switchBranchVariant, + canAttemptRefresh, + isRefreshableMessage, } from "../src/server/message-branch.js"; import { createSession } from "../src/runtime/phase-machine.js"; @@ -62,3 +64,61 @@ describe("message-branch", () => { expect(branch.variants[0].messages[0].text).toBe("首句"); }); }); + +describe("canAttemptRefresh", () => { + const output = { + id: "w1", + role: "system" as const, + text: "场面", + createdAt: "1", + kind: "worker_output", + actor: "world-simulator", + }; + + it("rejects user bubbles", () => { + expect( + isRefreshableMessage({ + id: "u1", + role: "user", + text: "行动", + createdAt: "1", + kind: "user_input", + }), + ).toBe(false); + expect( + canAttemptRefresh( + { + id: "u1", + role: "user", + text: "行动", + createdAt: "1", + kind: "user_input", + }, + { + lastWorkerId: "world-simulator", + hasLastWorkerSnapshot: true, + hasMessageCheckpoint: true, + }, + ), + ).toBe(false); + }); + + it("allows worker output when a snapshot exists", () => { + expect( + canAttemptRefresh(output, { + lastWorkerId: "world-simulator", + hasLastWorkerSnapshot: true, + hasMessageCheckpoint: false, + }), + ).toBe(true); + }); + + it("rejects worker output with no snapshot and no checkpoint", () => { + expect( + canAttemptRefresh(output, { + hasLastWorkerSnapshot: false, + hasMessageCheckpoint: false, + }), + ).toBe(false); + }); +});