

























@@ -6314,6 +6314,104 @@ describe("runCodexAppServerAttempt", () => {
63146314expect(inputText).toContain("make the default webpage openclaw");
63156315});
631663166317+it("projects newer mirrored history when resuming an existing Codex thread binding", async () => {
6318+const sessionFile = path.join(tempDir, "session.jsonl");
6319+const workspaceDir = path.join(tempDir, "workspace");
6320+await writeExistingBinding(sessionFile, workspaceDir, { dynamicToolsFingerprint: "[]" });
6321+const binding = await readCodexAppServerBinding(sessionFile);
6322+const bindingUpdatedAt = Date.parse(binding?.updatedAt ?? "");
6323+if (!Number.isFinite(bindingUpdatedAt)) {
6324+throw new Error("expected valid Codex binding timestamp");
6325+}
6326+const sessionManager = SessionManager.open(sessionFile);
6327+sessionManager.appendMessage(
6328+userMessage("we were discussing the Sonnet leak screenshots", bindingUpdatedAt + 1_000),
6329+);
6330+sessionManager.appendMessage(
6331+assistantMessage("David Ondrej was mentioned in that prior thread", bindingUpdatedAt + 2_000),
6332+);
6333+const harness = createStartedThreadHarness();
6334+const params = createParams(sessionFile, workspaceDir);
6335+params.prompt = "is the previous message trustworthy?";
6336+6337+const run = runCodexAppServerAttempt(params);
6338+await harness.waitForMethod("turn/start");
6339+await new Promise<void>((resolve) => setImmediate(resolve));
6340+await harness.completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
6341+await run;
6342+6343+expect(harness.requests.map((request) => request.method)).toContain("thread/resume");
6344+const turnStart = harness.requests.find((request) => request.method === "turn/start");
6345+const inputText =
6346+(turnStart?.params as { input?: Array<{ text?: string }> } | undefined)?.input?.[0]?.text ??
6347+"";
6348+6349+expect(inputText).toContain("OpenClaw assembled context for this turn:");
6350+expect(inputText).toContain("we were discussing the Sonnet leak screenshots");
6351+expect(inputText).toContain("David Ondrej was mentioned in that prior thread");
6352+expect(inputText).toContain("Current user request:");
6353+expect(inputText).toContain("is the previous message trustworthy?");
6354+});
6355+6356+it("does not reproject Codex-owned mirrored messages on consecutive resumes", async () => {
6357+const sessionFile = path.join(tempDir, "session.jsonl");
6358+const workspaceDir = path.join(tempDir, "workspace");
6359+await writeExistingBinding(sessionFile, workspaceDir, { dynamicToolsFingerprint: "[]" });
6360+const oldBindingUpdatedAt = Date.now() - 60_000;
6361+const bindingPath = `${sessionFile}.codex-app-server.json`;
6362+const bindingPayload = JSON.parse(await fs.readFile(bindingPath, "utf8")) as Record<
6363+string,
6364+unknown
6365+>;
6366+bindingPayload.updatedAt = new Date(oldBindingUpdatedAt).toISOString();
6367+await fs.writeFile(bindingPath, `${JSON.stringify(bindingPayload, null, 2)}\n`);
6368+const sessionManager = SessionManager.open(sessionFile);
6369+sessionManager.appendMessage(
6370+userMessage("we were discussing the Sonnet leak screenshots", oldBindingUpdatedAt + 1_000),
6371+);
6372+sessionManager.appendMessage(
6373+assistantMessage(
6374+"David Ondrej was mentioned in that prior thread",
6375+oldBindingUpdatedAt + 2_000,
6376+),
6377+);
6378+6379+const firstHarness = createResumeHarness();
6380+const firstParams = createParams(sessionFile, workspaceDir);
6381+firstParams.prompt = "is the previous message trustworthy?";
6382+const firstRun = runCodexAppServerAttempt(firstParams);
6383+await firstHarness.waitForMethod("turn/start");
6384+await firstHarness.completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
6385+await firstRun;
6386+6387+const firstTurnStart = firstHarness.requests.find((request) => request.method === "turn/start");
6388+const firstInputText =
6389+(firstTurnStart?.params as { input?: Array<{ text?: string }> } | undefined)?.input?.[0]
6390+?.text ?? "";
6391+expect(firstInputText).toContain("OpenClaw assembled context for this turn:");
6392+expect(firstInputText).toContain("we were discussing the Sonnet leak screenshots");
6393+expect(firstInputText).toContain("is the previous message trustworthy?");
6394+6395+const secondHarness = createResumeHarness();
6396+const secondParams = createParams(sessionFile, workspaceDir);
6397+secondParams.prompt = "continue from there";
6398+const secondRun = runCodexAppServerAttempt(secondParams);
6399+await secondHarness.waitForMethod("turn/start");
6400+await secondHarness.completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
6401+await secondRun;
6402+6403+const secondTurnStart = secondHarness.requests.find(
6404+(request) => request.method === "turn/start",
6405+);
6406+const secondInputText =
6407+(secondTurnStart?.params as { input?: Array<{ text?: string }> } | undefined)?.input?.[0]
6408+?.text ?? "";
6409+expect(secondInputText).not.toContain("OpenClaw assembled context for this turn:");
6410+expect(secondInputText).not.toContain("we were discussing the Sonnet leak screenshots");
6411+expect(secondInputText).not.toContain("is the previous message trustworthy?");
6412+expect(secondInputText).toContain("continue from there");
6413+});
6414+63176415it("passes stable workspace files as Codex developer instructions and keeps MEMORY.md as turn context", async () => {
63186416const sessionFile = path.join(tempDir, "session.jsonl");
63196417const workspaceDir = path.join(tempDir, "workspace");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。