


























@@ -1,6 +1,5 @@
11import fs from "node:fs";
22import { StringDecoder } from "node:string_decoder";
3-import { SessionManager, type SessionEntry } from "@mariozechner/pi-coding-agent";
43import { deriveSessionTotalTokens, hasNonzeroUsage, normalizeUsage } from "../agents/usage.js";
54import { jsonUtf8Bytes } from "../infra/json-utf8-bytes.js";
65import { hasInterSessionUserProvenance } from "../sessions/input-provenance.js";
@@ -151,69 +150,7 @@ export function readSessionMessages(
151150return [];
152151}
153152154-const lines = fs.readFileSync(filePath, "utf-8").split(/\r?\n/);
155-const hasTreeEntries = lines.some(hasSessionTreeEntry);
156-let branchEntries: SessionEntry[] | null = null;
157-if (hasTreeEntries) {
158-try {
159-branchEntries = SessionManager.open(filePath).getBranch();
160-} catch {
161-branchEntries = null;
162-}
163-}
164-165-if (branchEntries) {
166-const messages: unknown[] = [];
167-let messageSeq = 0;
168-for (const entry of branchEntries) {
169-if (entry.type === "message" && entry.message) {
170-messageSeq += 1;
171-messages.push(
172-attachOpenClawTranscriptMeta(entry.message, {
173- ...(typeof entry.id === "string" ? { id: entry.id } : {}),
174-seq: messageSeq,
175-}),
176-);
177-continue;
178-}
179-180-if (entry.type === "compaction") {
181-const ts = typeof entry.timestamp === "string" ? Date.parse(entry.timestamp) : Number.NaN;
182-const timestamp = Number.isFinite(ts) ? ts : Date.now();
183-messageSeq += 1;
184-messages.push({
185-role: "system",
186-content: [{ type: "text", text: "Compaction" }],
187- timestamp,
188-__openclaw: {
189-kind: "compaction",
190-id: typeof entry.id === "string" ? entry.id : undefined,
191-seq: messageSeq,
192-},
193-});
194-}
195-}
196-return messages;
197-}
198-199-const messages: unknown[] = [];
200-let messageSeq = 0;
201-for (const line of lines) {
202-if (!line.trim()) {
203-continue;
204-}
205-try {
206-const parsed = JSON.parse(line);
207-const message = parsedSessionEntryToMessage(parsed, messageSeq + 1);
208-if (message) {
209-messageSeq += 1;
210-messages.push(message);
211-}
212-} catch {
213-// ignore bad lines
214-}
215-}
216-return messages;
153+return transcriptRecordsToMessages(readSelectedTranscriptRecords(filePath));
217154}
218155219156type ReadRecentSessionMessagesOptions = {
@@ -283,25 +220,7 @@ export function readRecentSessionMessages(
283220.filter((line) => line.trim().length > 0)
284221.slice(-maxLines);
285222286-if (lines.some(hasSessionTreeEntry)) {
287-return readSessionMessages(sessionId, storePath, sessionFile).slice(-maxMessages);
288-}
289-290-const messages: unknown[] = [];
291-let messageSeq = 0;
292-for (const line of lines) {
293-try {
294-const parsed = JSON.parse(line);
295-const message = parsedSessionEntryToMessage(parsed, messageSeq + 1);
296-if (message) {
297-messageSeq += 1;
298-messages.push(message);
299-}
300-} catch {
301-// ignore bad tail lines
302-}
303-}
304-return messages.slice(-maxMessages);
223+return parseRecentTranscriptTailMessages(lines, maxMessages);
305224}) ?? []
306225);
307226}
@@ -401,24 +320,51 @@ function selectBoundedActiveTailRecords(entries: TailTranscriptRecord[]): TailTr
401320return selected.toReversed();
402321}
403322404-function parseRecentTranscriptTailMessages(lines: string[], maxMessages: number): unknown[] {
405-const entries = lines.flatMap((line) => {
406-const entry = parseTailTranscriptRecord(line);
407-return entry ? [entry] : [];
323+function readTranscriptRecords(filePath: string): TailTranscriptRecord[] {
324+const records: TailTranscriptRecord[] = [];
325+visitTranscriptLines(filePath, (line) => {
326+if (!line.trim()) {
327+return;
328+}
329+const record = parseTailTranscriptRecord(line);
330+if (record && record.record.type !== "session") {
331+records.push(record);
332+}
408333});
409-const selected = entries.some(tailRecordHasTreeLink)
410- ? selectBoundedActiveTailRecords(entries)
411- : entries;
334+return records;
335+}
336+337+function selectActiveTranscriptRecords(records: TailTranscriptRecord[]): TailTranscriptRecord[] {
338+return records.some(tailRecordHasTreeLink) ? selectBoundedActiveTailRecords(records) : records;
339+}
340+341+function readSelectedTranscriptRecords(filePath: string): TailTranscriptRecord[] {
342+try {
343+return selectActiveTranscriptRecords(readTranscriptRecords(filePath));
344+} catch {
345+return [];
346+}
347+}
348+349+function transcriptRecordsToMessages(records: TailTranscriptRecord[]): unknown[] {
412350const messages: unknown[] = [];
413351let messageSeq = 0;
414-for (const entry of selected) {
352+for (const entry of records) {
415353const message = parsedSessionEntryToMessage(entry.record, messageSeq + 1);
416354if (message) {
417355messageSeq += 1;
418356messages.push(message);
419357}
420358}
421-return messages.slice(-maxMessages);
359+return messages;
360+}
361+362+function parseRecentTranscriptTailMessages(lines: string[], maxMessages: number): unknown[] {
363+const entries = lines.flatMap((line) => {
364+const entry = parseTailTranscriptRecord(line);
365+return entry ? [entry] : [];
366+});
367+return transcriptRecordsToMessages(selectActiveTranscriptRecords(entries)).slice(-maxMessages);
422368}
423369424370function visitTranscriptLines(filePath: string, visit: (line: string) => void): void {
@@ -479,61 +425,6 @@ async function visitTranscriptLinesAsync(
479425}
480426}
481427482-function transcriptHasTreeEntries(filePath: string): boolean {
483-let hasTreeEntries = false;
484-try {
485-visitTranscriptLines(filePath, (line) => {
486-if (!hasTreeEntries && hasSessionTreeEntry(line)) {
487-hasTreeEntries = true;
488-}
489-});
490-} catch {
491-return false;
492-}
493-return hasTreeEntries;
494-}
495-496-function visitSessionManagerBranchMessages(
497-filePath: string,
498-visit: (message: unknown, seq: number) => void,
499-): number {
500-const branchEntries = SessionManager.open(filePath).getBranch();
501-let messageSeq = 0;
502-for (const entry of branchEntries) {
503-if (entry.type === "message" && entry.message) {
504-messageSeq += 1;
505-visit(
506-attachOpenClawTranscriptMeta(entry.message, {
507- ...(typeof entry.id === "string" ? { id: entry.id } : {}),
508-seq: messageSeq,
509-}),
510-messageSeq,
511-);
512-continue;
513-}
514-515-if (entry.type === "compaction") {
516-const ts = typeof entry.timestamp === "string" ? Date.parse(entry.timestamp) : Number.NaN;
517-const timestamp = Number.isFinite(ts) ? ts : Date.now();
518-messageSeq += 1;
519-visit(
520-{
521-role: "system",
522-content: [{ type: "text", text: "Compaction" }],
523- timestamp,
524-__openclaw: {
525-kind: "compaction",
526-id: typeof entry.id === "string" ? entry.id : undefined,
527-seq: messageSeq,
528-},
529-},
530-messageSeq,
531-);
532-}
533-}
534-return messageSeq;
535-}
536-537428export function visitSessionMessages(
538429sessionId: string,
539430storePath: string | undefined,
@@ -545,35 +436,11 @@ export function visitSessionMessages(
545436return 0;
546437}
547438548-if (transcriptHasTreeEntries(filePath)) {
549-try {
550-return visitSessionManagerBranchMessages(filePath, visit);
551-} catch {
552-return 0;
553-}
439+const messages = transcriptRecordsToMessages(readSelectedTranscriptRecords(filePath));
440+for (const [index, message] of messages.entries()) {
441+visit(message, index + 1);
554442}
555-556-let messageSeq = 0;
557-try {
558-visitTranscriptLines(filePath, (line) => {
559-if (!line.trim()) {
560-return;
561-}
562-try {
563-const parsed = JSON.parse(line);
564-const message = parsedSessionEntryToMessage(parsed, messageSeq + 1);
565-if (message) {
566-messageSeq += 1;
567-visit(message, messageSeq);
568-}
569-} catch {
570-// ignore bad lines
571-}
572-});
573-} catch {
574-return 0;
575-}
576-return messageSeq;
443+return messages.length;
577444}
578445579446export function readSessionMessageCount(
@@ -763,18 +630,6 @@ export function readRecentSessionTranscriptLines(params: {
763630return { lines, totalLines };
764631}
765632766-function hasSessionTreeEntry(line: string): boolean {
767-if (!line.trim()) {
768-return false;
769-}
770-try {
771-const parsed = JSON.parse(line) as { type?: unknown; id?: unknown; parentId?: unknown };
772-return parsed.type !== "session" && typeof parsed.id === "string" && "parentId" in parsed;
773-} catch {
774-return false;
775-}
776-}
777-778633function parsedSessionEntryToMessage(parsed: unknown, seq: number): unknown {
779634if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
780635return null;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。