仅在有快照或检查点时允许重出一版。
避免无上下文可复用时误点重跑。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -215,6 +215,23 @@ export function isRefreshableMessage(msg: BranchableMessage): boolean {
|
|||||||
return kind === "worker_questions" || kind === "worker_output";
|
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 {
|
export function findPrecedingUserIndex(messages: BranchableMessage[], fromIndex: number): number {
|
||||||
for (let i = fromIndex - 1; i >= 0; i--) {
|
for (let i = fromIndex - 1; i >= 0; i--) {
|
||||||
if (messages[i].role === "user") return i;
|
if (messages[i].role === "user") return i;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import {
|
|||||||
ensureBranchForEdit,
|
ensureBranchForEdit,
|
||||||
recordPreMessageCheckpoint,
|
recordPreMessageCheckpoint,
|
||||||
switchBranchVariant,
|
switchBranchVariant,
|
||||||
|
canAttemptRefresh,
|
||||||
|
isRefreshableMessage,
|
||||||
} from "../src/server/message-branch.js";
|
} from "../src/server/message-branch.js";
|
||||||
import { createSession } from "../src/runtime/phase-machine.js";
|
import { createSession } from "../src/runtime/phase-machine.js";
|
||||||
|
|
||||||
@@ -62,3 +64,61 @@ describe("message-branch", () => {
|
|||||||
expect(branch.variants[0].messages[0].text).toBe("首句");
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user