

















@@ -9,10 +9,7 @@ import {
99resolveTrajectoryPointerFilePath,
1010resolveTrajectoryPointerOpenFlags,
1111} from "./paths.js";
12-import {
13-createTrajectoryRuntimeRecorder,
14-toTrajectoryToolDefinitions,
15-} from "./runtime.js";
12+import { createTrajectoryRuntimeRecorder, toTrajectoryToolDefinitions } from "./runtime.js";
16131714type TrajectoryRuntimeRecorder = NonNullable<ReturnType<typeof createTrajectoryRuntimeRecorder>>;
1815@@ -57,7 +54,7 @@ describe("trajectory runtime", () => {
5754env: { OPENCLAW_TRAJECTORY_DIR: "/tmp/traces" },
5855sessionId: "../evil/session",
5956}),
60-).toBe("/tmp/traces/___evil_session.jsonl");
57+).toBe(path.join(path.resolve("/tmp/traces"), "___evil_session.jsonl"));
6158});
62596360it("records sanitized runtime events by default", () => {
@@ -136,6 +133,54 @@ describe("trajectory runtime", () => {
136133);
137134});
138135136+it("preserves usage when truncating oversized runtime events", () => {
137+const writes: string[] = [];
138+const usage = {
139+input: 384_954,
140+output: 5_624,
141+cacheRead: 333_824,
142+reasoningTokens: 2_038,
143+total: 724_402,
144+};
145+const promptCache = { readTokens: 333_824, writeTokens: 51_130 };
146+const recorder = createTrajectoryRuntimeRecorder({
147+sessionId: "session-1",
148+sessionFile: "/tmp/session.jsonl",
149+writer: {
150+filePath: "/tmp/session.trajectory.jsonl",
151+write: (line) => {
152+writes.push(line);
153+},
154+flush: async () => undefined,
155+},
156+});
157+158+const runtimeRecorder = expectTrajectoryRuntimeRecorder(recorder);
159+runtimeRecorder.recordEvent("model.completed", {
160+ usage,
161+ promptCache,
162+messagesSnapshot: Array.from({ length: 12 }, (_value, index) => ({
163+role: index % 2 === 0 ? "user" : "assistant",
164+content: `message-${index} ${"x".repeat(32_000)}`,
165+})),
166+});
167+168+expect(writes).toHaveLength(1);
169+const parsed = JSON.parse(writes[0]);
170+expect(parsed.type).toBe("model.completed");
171+expect(parsed.data).toMatchObject({
172+truncated: true,
173+reason: "trajectory-event-size-limit",
174+ usage,
175+ promptCache,
176+});
177+expect(parsed.data.messagesSnapshot).toBeUndefined();
178+expect(parsed.data.droppedFields).toContain("messagesSnapshot");
179+expect(Buffer.byteLength(writes[0], "utf8")).toBeLessThanOrEqual(
180+TRAJECTORY_RUNTIME_EVENT_MAX_BYTES + 1,
181+);
182+});
183+139184it("rotates runtime capture at the file budget and keeps newer events", async () => {
140185const tmpDir = makeTempDir();
141186const sessionFile = path.join(tmpDir, "session.jsonl");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。