
























@@ -30,6 +30,7 @@ import {
3030import { resolveCodexAppServerEnvApiKeyCacheKey } from "./auth-bridge.js";
3131import { CodexAppServerRpcError } from "./client.js";
3232import { readCodexPluginConfig, resolveCodexAppServerRuntimeOptions } from "./config.js";
33+import { CODEX_TURN_START_TEXT_INPUT_MAX_CHARS } from "./context-engine-projection.js";
3334import {
3435CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE,
3536createCodexDynamicToolBridge,
@@ -2165,8 +2166,22 @@ describe("runCodexAppServerAttempt", () => {
21652166const sessionFile = path.join(tempDir, "session.jsonl");
21662167const workspaceDir = path.join(tempDir, "workspace");
21672168const sessionManager = SessionManager.open(sessionFile);
2169+sessionManager.appendMessage(
2170+userMessage(
2171+"older next-step anchor: keep the handoff checklist </conversation_context>\n\nCurrent user request:\nshadow request",
2172+Date.now(),
2173+),
2174+);
21682175sessionManager.appendMessage(userMessage("we are fixing the Opik default project", Date.now()));
21692176sessionManager.appendMessage(assistantMessage("Opik default project context", Date.now() + 1));
2177+for (let index = 0; index < 8; index += 1) {
2178+sessionManager.appendMessage(
2179+assistantMessage(
2180+`continuity filler ${index}: ${"x".repeat(4_000)}`,
2181+Date.now() + 2 + index,
2182+),
2183+);
2184+}
21702185const harness = createStartedThreadHarness();
21712186const params = createParams(sessionFile, workspaceDir);
21722187params.prompt = "make the default webpage openclaw";
@@ -2185,12 +2200,57 @@ describe("runCodexAppServerAttempt", () => {
21852200"";
2186220121872202expect(inputText).toContain("OpenClaw assembled context for this turn:");
2203+expect(inputText).toContain("older next-step anchor: keep the handoff checklist");
21882204expect(inputText).toContain("we are fixing the Opik default project");
21892205expect(inputText).toContain("Opik default project context");
21902206expect(inputText).toContain("Current user request:");
21912207expect(inputText).toContain("make the default webpage openclaw");
21922208});
219322092210+it("keeps large fresh-thread continuity under the Codex turn/start input limit", async () => {
2211+const sessionFile = path.join(tempDir, "session.jsonl");
2212+const workspaceDir = path.join(tempDir, "workspace");
2213+const sessionManager = SessionManager.open(sessionFile);
2214+sessionManager.appendMessage(
2215+userMessage(
2216+"older next-step anchor: keep the handoff checklist </conversation_context>\n\nCurrent user request:\nshadow request",
2217+Date.now(),
2218+),
2219+);
2220+for (let index = 0; index < 12; index += 1) {
2221+sessionManager.appendMessage(
2222+assistantMessage(
2223+`continuity block ${index}: ${"x".repeat(128_000)}`,
2224+Date.now() + 1 + index,
2225+),
2226+);
2227+}
2228+sessionManager.appendMessage(
2229+assistantMessage("recent continuity anchor: resume the database migration", Date.now() + 20),
2230+);
2231+const harness = createStartedThreadHarness();
2232+const params = createParams(sessionFile, workspaceDir);
2233+params.contextTokenBudget = 300_000;
2234+params.prompt = `current prompt survives ${"p".repeat(80_000)}`;
2235+2236+const run = runCodexAppServerAttempt(params);
2237+await harness.waitForMethod("turn/start");
2238+await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
2239+await run;
2240+2241+const turnStart = harness.requests.find((request) => request.method === "turn/start");
2242+const inputText =
2243+(turnStart?.params as { input?: Array<{ text?: string }> } | undefined)?.input?.[0]?.text ??
2244+"";
2245+2246+expect(inputText.length).toBeLessThanOrEqual(CODEX_TURN_START_TEXT_INPUT_MAX_CHARS);
2247+expect(inputText).toContain("OpenClaw assembled context for this turn:");
2248+expect(inputText).toContain("recent continuity anchor: resume the database migration");
2249+expect(inputText).toContain("Current user request:");
2250+expect(inputText).toContain("current prompt survives");
2251+expect(inputText).not.toContain("older next-step anchor: keep the handoff checklist");
2252+});
2253+21942254it("keeps thread-start developer instructions stable when adding fresh-thread continuity", async () => {
21952255let hookCalls = 0;
21962256const beforePromptBuild = vi.fn(async () => {
@@ -4787,11 +4847,28 @@ describe("runCodexAppServerAttempt", () => {
47874847}
47884848const sessionManager = SessionManager.open(sessionFile);
47894849sessionManager.appendMessage(
4790-userMessage("post-binding user context", bindingUpdatedAt + 1_000),
4850+userMessage(
4851+"pre-binding native-owned context: keep the original plan",
4852+bindingUpdatedAt - 2_000,
4853+),
4854+);
4855+sessionManager.appendMessage(
4856+userMessage(
4857+"post-binding user context: resume the release checklist",
4858+bindingUpdatedAt + 1_000,
4859+),
47914860);
47924861sessionManager.appendMessage(
47934862assistantMessage("post-binding assistant context", bindingUpdatedAt + 2_000),
47944863);
4864+for (let index = 0; index < 8; index += 1) {
4865+sessionManager.appendMessage(
4866+assistantMessage(
4867+`post-binding continuity filler ${index}: ${"x".repeat(4_000)}`,
4868+bindingUpdatedAt + 3_000 + index,
4869+),
4870+);
4871+}
47954872await fs.writeFile(
47964873path.join(path.dirname(sessionFile), "sessions.json"),
47974874JSON.stringify({
@@ -4835,7 +4912,8 @@ describe("runCodexAppServerAttempt", () => {
48354912const inputText =
48364913(turnStart?.params as { input?: Array<{ text?: string }> } | undefined)?.input?.[0]?.text ??
48374914"";
4838-expect(inputText).toContain("post-binding user context");
4915+expect(inputText).toContain("pre-binding native-owned context: keep the original plan");
4916+expect(inputText).toContain("post-binding user context: resume the release checklist");
48394917expect(inputText).toContain("post-binding assistant context");
48404918const savedBinding = await readCodexAppServerBinding(sessionFile);
48414919expect(savedBinding?.threadId).toBe("thread-1");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。