
























@@ -5,6 +5,7 @@ import path from "node:path";
55import { afterEach, describe, expect, it } from "vitest";
66import {
77createCodexTrajectoryRecorder,
8+recordCodexTrajectoryCompletion,
89recordCodexTrajectoryContext,
910resolveCodexTrajectoryAppendFlags,
1011resolveCodexTrajectoryPointerFlags,
@@ -80,7 +81,9 @@ describe("Codex trajectory recorder", () => {
8081expect(content).not.toContain("secret");
8182expect(content).not.toContain("sk-test-secret-token");
8283expect(content).not.toContain("sk-other-secret-token");
83-expect(fs.statSync(filePath).mode & 0o777).toBe(0o600);
84+if (process.platform !== "win32") {
85+expect(fs.statSync(filePath).mode & 0o777).toBe(0o600);
86+}
8487expect(fs.existsSync(path.join(tmpDir, "session.trajectory-path.json"))).toBe(true);
8588});
8689@@ -253,4 +256,61 @@ describe("Codex trajectory recorder", () => {
253256expect(parsed.data?.truncated).toBe(true);
254257expect(parsed.data?.reason).toBe("trajectory-event-size-limit");
255258});
259+260+it("preserves usage when truncating oversized model completion events", async () => {
261+const tmpDir = makeTempDir();
262+const sessionFile = path.join(tmpDir, "session.jsonl");
263+const attempt = {
264+ sessionFile,
265+sessionId: "session-1",
266+sessionKey: "agent:main:session-1",
267+runId: "run-1",
268+provider: "codex",
269+modelId: "gpt-5.4",
270+model: { api: "responses" },
271+} as never;
272+const usage = {
273+input: 384_954,
274+output: 5_624,
275+cacheRead: 333_824,
276+reasoningTokens: 2_038,
277+total: 724_402,
278+};
279+const recorder = createCodexTrajectoryRecorder({
280+cwd: tmpDir,
281+ attempt,
282+env: {},
283+});
284+285+const trajectoryRecorder = expectTrajectoryRecorder(recorder);
286+recordCodexTrajectoryCompletion(trajectoryRecorder, {
287+ attempt,
288+threadId: "thread-1",
289+turnId: "turn-1",
290+timedOut: false,
291+result: {
292+aborted: false,
293+attemptUsage: usage,
294+assistantTexts: ["done"],
295+messagesSnapshot: Array.from({ length: 20 }, (_value, index) => ({
296+role: index % 2 === 0 ? "user" : "assistant",
297+content: `message-${index} ${"x".repeat(32_000)}`,
298+})),
299+} as never,
300+});
301+await trajectoryRecorder.flush();
302+303+const parsed = JSON.parse(
304+fs.readFileSync(path.join(tmpDir, "session.trajectory.jsonl"), "utf8"),
305+);
306+expect(parsed.type).toBe("model.completed");
307+expect(parsed.data).toMatchObject({
308+truncated: true,
309+reason: "trajectory-event-size-limit",
310+ usage,
311+});
312+expect(parsed.data.messagesSnapshot).toBeUndefined();
313+expect(parsed.data.droppedFields).toContain("messagesSnapshot");
314+expect(Buffer.byteLength(JSON.stringify(parsed), "utf8")).toBeLessThanOrEqual(256 * 1024);
315+});
256316});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。