





















@@ -1,8 +1,7 @@
11import fs from "node:fs";
22import path from "node:path";
33import type { AgentMessage } from "@mariozechner/pi-agent-core";
4-import type { SessionEntry, SessionHeader } from "@mariozechner/pi-coding-agent";
5-import { SessionManager } from "@mariozechner/pi-coding-agent";
4+import type { FileEntry, SessionEntry, SessionHeader } from "@mariozechner/pi-coding-agent";
65import { sanitizeDiagnosticPayload } from "../agents/payload-redaction.js";
76import { resolveStateDir } from "../config/paths.js";
87import {
@@ -57,6 +56,90 @@ const MAX_TRAJECTORY_RUNTIME_EVENTS = 200_000;
5756const MAX_TRAJECTORY_TOTAL_EVENTS = 250_000;
5857const MAX_TRAJECTORY_SESSION_FILE_BYTES = 50 * 1024 * 1024;
595859+function parseSessionEntries(content: string): FileEntry[] {
60+return content
61+.split(/\r?\n/u)
62+.map((line) => line.trim())
63+.filter(Boolean)
64+.flatMap((line) => {
65+try {
66+return [JSON.parse(line) as FileEntry];
67+} catch {
68+return [];
69+}
70+});
71+}
72+73+function migrateLegacySessionEntries(entries: FileEntry[]): void {
74+const header = entries.find((entry): entry is SessionHeader => entry.type === "session");
75+const version = header?.version ?? 1;
76+if (version < 2) {
77+let previousId: string | null = null;
78+let index = 0;
79+for (const entry of entries) {
80+if (entry.type === "session") {
81+entry.version = 2;
82+continue;
83+}
84+const mutable = entry as unknown as Record<string, unknown>;
85+if (typeof mutable.id !== "string") {
86+mutable.id = `legacy-${index++}`;
87+}
88+mutable.parentId = previousId;
89+const entryId = mutable.id;
90+previousId = typeof entryId === "string" ? entryId : null;
91+if (entry.type === "compaction" && typeof mutable.firstKeptEntryIndex === "number") {
92+const target = entries[mutable.firstKeptEntryIndex];
93+if (target && target.type !== "session") {
94+mutable.firstKeptEntryId = (target as unknown as Record<string, unknown>).id;
95+}
96+delete mutable.firstKeptEntryIndex;
97+}
98+}
99+}
100+if (version < 3) {
101+for (const entry of entries) {
102+if (entry.type === "session") {
103+entry.version = 3;
104+continue;
105+}
106+if (entry.type === "message") {
107+const message = (entry as { message?: { role?: string } }).message;
108+if (message?.role === "hookMessage") {
109+message.role = "custom";
110+}
111+}
112+}
113+}
114+}
115+116+function readSessionBranch(filePath: string): {
117+header: SessionHeader | null;
118+leafId: string | null;
119+branchEntries: SessionEntry[];
120+} {
121+const fileEntries = parseSessionEntries(fs.readFileSync(filePath, "utf8"));
122+migrateLegacySessionEntries(fileEntries);
123+const header =
124+fileEntries.find((entry): entry is SessionHeader => entry.type === "session") ?? null;
125+const entries = fileEntries.filter(
126+(entry): entry is SessionEntry =>
127+entry.type !== "session" &&
128+typeof (entry as { id?: unknown }).id === "string" &&
129+(typeof (entry as { timestamp?: unknown }).timestamp === "string" ||
130+typeof (entry as { timestamp?: unknown }).timestamp === "number"),
131+);
132+const byId = new Map(entries.map((entry) => [entry.id, entry]));
133+const leafId = entries.at(-1)?.id ?? null;
134+const branchEntries: SessionEntry[] = [];
135+let current = leafId ? byId.get(leafId) : undefined;
136+while (current) {
137+branchEntries.unshift(current);
138+current = current.parentId ? byId.get(current.parentId) : undefined;
139+}
140+return { header, leafId, branchEntries };
141+}
142+60143function parseJsonlFile<T>(
61144filePath: string,
62145params: {
@@ -748,10 +831,7 @@ export function exportTrajectoryBundle(params: BuildTrajectoryBundleParams): {
748831`Trajectory session file is too large to export (${sessionStat.size} bytes; limit ${MAX_TRAJECTORY_SESSION_FILE_BYTES})`,
749832);
750833}
751-const sessionManager = SessionManager.open(params.sessionFile);
752-const header = sessionManager.getHeader();
753-const leafId = sessionManager.getLeafId();
754-const branchEntries = sessionManager.getBranch(leafId ?? undefined);
834+const { header, leafId, branchEntries } = readSessionBranch(params.sessionFile);
755835const runtimeFile = resolveTrajectoryRuntimeFile({
756836runtimeFile: params.runtimeFile,
757837sessionFile: params.sessionFile,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。