























@@ -6003,6 +6003,132 @@ describe("runCodexAppServerAttempt", () => {
60036003expect(resumeRequestParams?.developerInstructions).toContain(CODEX_GPT5_BEHAVIOR_CONTRACT);
60046004});
600560056006+it("starts a fresh Codex thread before resume when the native rollout is over budget", async () => {
6007+const sessionFile = path.join(tempDir, "session.jsonl");
6008+const workspaceDir = path.join(tempDir, "workspace");
6009+const agentDir = path.join(tempDir, "agent");
6010+await writeExistingBinding(sessionFile, workspaceDir, { dynamicToolsFingerprint: "[]" });
6011+await fs.writeFile(
6012+path.join(path.dirname(sessionFile), "sessions.json"),
6013+JSON.stringify({
6014+"agent:main:session-1": {
6015+ sessionFile,
6016+totalTokens: 12_000,
6017+},
6018+}),
6019+);
6020+const rolloutDir = path.join(agentDir, "codex-home", "sessions");
6021+await fs.mkdir(rolloutDir, { recursive: true });
6022+await fs.writeFile(
6023+path.join(rolloutDir, "rollout-thread-existing.jsonl"),
6024+`${JSON.stringify({
6025+ payload: {
6026+ type: "token_count",
6027+ info: {
6028+ total_token_usage: {
6029+ total_tokens: 70_000,
6030+ },
6031+ },
6032+ },
6033+ })}\n`,
6034+);
6035+const { requests, waitForMethod, completeTurn } = createStartedThreadHarness();
6036+const params = createParams(sessionFile, workspaceDir);
6037+params.agentDir = agentDir;
6038+params.config = {
6039+agents: {
6040+defaults: {
6041+compaction: {
6042+truncateAfterCompaction: true,
6043+maxActiveTranscriptBytes: "1mb",
6044+},
6045+},
6046+},
6047+} as never;
6048+6049+const run = runCodexAppServerAttempt(params, {
6050+pluginConfig: { appServer: { mode: "yolo" } },
6051+});
6052+await waitForMethod("turn/start");
6053+await completeTurn({ threadId: "thread-1", turnId: "turn-1" });
6054+await run;
6055+6056+expect(requests.map((entry) => entry.method)).toContain("thread/start");
6057+expect(requests.map((entry) => entry.method)).not.toContain("thread/resume");
6058+const savedBinding = await readCodexAppServerBinding(sessionFile);
6059+expect(savedBinding?.threadId).toBe("thread-1");
6060+});
6061+6062+it("preserves bound auth when rotating an over-budget native rollout", async () => {
6063+const sessionFile = path.join(tempDir, "session.jsonl");
6064+const workspaceDir = path.join(tempDir, "workspace");
6065+const agentDir = path.join(tempDir, "agent");
6066+await writeExistingBinding(sessionFile, workspaceDir, {
6067+authProfileId: "openai-codex:work",
6068+dynamicToolsFingerprint: "[]",
6069+});
6070+await fs.writeFile(
6071+path.join(path.dirname(sessionFile), "sessions.json"),
6072+JSON.stringify({
6073+"agent:main:session-1": {
6074+ sessionFile,
6075+totalTokens: 12_000,
6076+},
6077+}),
6078+);
6079+const rolloutDir = path.join(agentDir, "codex-home", "sessions");
6080+await fs.mkdir(rolloutDir, { recursive: true });
6081+await fs.writeFile(
6082+path.join(rolloutDir, "rollout-thread-existing.jsonl"),
6083+`${JSON.stringify({
6084+ payload: {
6085+ type: "token_count",
6086+ info: {
6087+ total_token_usage: {
6088+ total_tokens: 70_000,
6089+ },
6090+ },
6091+ },
6092+ })}\n`,
6093+);
6094+const seenAuthProfileIds: Array<string | undefined> = [];
6095+const { requests, waitForMethod, completeTurn } = createStartedThreadHarness(undefined, {
6096+onStart: (authProfileId) => {
6097+seenAuthProfileIds.push(authProfileId);
6098+},
6099+});
6100+const params = createParams(sessionFile, workspaceDir);
6101+delete params.authProfileId;
6102+params.agentDir = agentDir;
6103+params.config = {
6104+agents: {
6105+defaults: {
6106+compaction: {
6107+truncateAfterCompaction: true,
6108+maxActiveTranscriptBytes: "1mb",
6109+},
6110+},
6111+},
6112+} as never;
6113+6114+const run = runCodexAppServerAttempt(params, {
6115+pluginConfig: { appServer: { mode: "yolo" } },
6116+});
6117+await vi.waitFor(() => expect(seenAuthProfileIds).toEqual(["openai-codex:work"]), {
6118+interval: 1,
6119+});
6120+await waitForMethod("turn/start");
6121+await completeTurn({ threadId: "thread-1", turnId: "turn-1" });
6122+await run;
6123+6124+expect(requests.map((entry) => entry.method)).toContain("thread/start");
6125+expect(requests.map((entry) => entry.method)).not.toContain("thread/resume");
6126+expect(seenAuthProfileIds).toEqual(["openai-codex:work"]);
6127+const savedBinding = await readCodexAppServerBinding(sessionFile);
6128+expect(savedBinding?.authProfileId).toBe("openai-codex:work");
6129+expect(savedBinding?.threadId).toBe("thread-1");
6130+});
6131+60066132it("resumes a bound Codex thread when only dynamic tool descriptions change", async () => {
60076133const sessionFile = path.join(tempDir, "session.jsonl");
60086134const workspaceDir = path.join(tempDir, "workspace");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。