
























@@ -90,6 +90,23 @@ function userMessage(text: string, timestamp: number): AgentMessage {
9090} as AgentMessage;
9191}
929293+function toolResultMessage(payload: unknown, timestamp: number): AgentMessage {
94+return {
95+role: "toolResult",
96+toolCallId: `call-${timestamp}`,
97+toolName: "bulk_context_probe",
98+content: [
99+{
100+type: "toolResult",
101+toolUseId: `call-${timestamp}`,
102+output: payload,
103+},
104+],
105+isError: false,
106+ timestamp,
107+} as unknown as AgentMessage;
108+}
109+93110function threadStartResult(threadId = "thread-1") {
94111return {
95112thread: {
@@ -991,6 +1008,61 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => {
9911008expect(savedBinding?.contextEngine?.projection?.epoch).toBe("epoch-after");
9921009});
99310101011+it("compacts over-budget rendered context-engine prompts before Codex turn/start", async () => {
1012+const sessionFile = path.join(tempDir, "session.jsonl");
1013+const workspaceDir = path.join(tempDir, "workspace");
1014+SessionManager.open(sessionFile).appendMessage(
1015+assistantMessage("pre-compaction context", Date.now()) as never,
1016+);
1017+const hugePayload = {
1018+rows: Array.from({ length: 10 }, (_, index) => ({
1019+id: index,
1020+body: "0123456789abcdef".repeat(4000),
1021+})),
1022+};
1023+const compact = vi.fn<ContextEngine["compact"]>(async () => ({
1024+ok: true,
1025+compacted: true,
1026+result: { summary: "summary", firstKeptEntryId: "entry-1", tokensBefore: 100_000 },
1027+}));
1028+const assemble = vi
1029+.fn<ContextEngine["assemble"]>()
1030+.mockResolvedValueOnce({
1031+messages: Array.from({ length: 8 }, (_, index) =>
1032+toolResultMessage(hugePayload, index + 1),
1033+),
1034+estimatedTokens: 100_000,
1035+contextProjection: { mode: "thread_bootstrap", epoch: "epoch-before" },
1036+})
1037+.mockResolvedValueOnce({
1038+messages: [assistantMessage("successor compacted context", 2) as never],
1039+estimatedTokens: 100,
1040+contextProjection: { mode: "thread_bootstrap", epoch: "epoch-after" },
1041+});
1042+const contextEngine = createContextEngine({ assemble, compact });
1043+const harness = createStartedThreadHarness();
1044+const params = createParams(sessionFile, workspaceDir);
1045+params.contextEngine = contextEngine;
1046+params.contextTokenBudget = 16_000;
1047+1048+const run = runCodexAppServerAttempt(params);
1049+await harness.waitForMethod("turn/start");
1050+1051+expect(compact).toHaveBeenCalledTimes(1);
1052+expect(assemble).toHaveBeenCalledTimes(2);
1053+expect(harness.requests.map((request) => request.method)).toEqual([
1054+"thread/start",
1055+"turn/start",
1056+]);
1057+const inputText = getRequestInputText(harness);
1058+expect(inputText).toContain("successor compacted context");
1059+expect(inputText).not.toContain("0123456789abcdef");
1060+1061+await harness.completeTurn();
1062+const result = await run;
1063+expect(result.assistantTexts).toContain("final answer");
1064+});
1065+9941066it("bounds a hung owning context-engine compaction during Codex overflow recovery", async () => {
9951067const sessionFile = path.join(tempDir, "session.jsonl");
9961068const workspaceDir = path.join(tempDir, "workspace");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。