


























@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
22import path from "node:path";
33import { isUsageCountedSessionTranscriptFileName } from "../../config/sessions/artifacts.js";
44import { resolveSessionTranscriptsDirForAgent } from "../../config/sessions/paths.js";
5+import { loadSessionStore } from "../../config/sessions/store-load.js";
56import { redactSensitiveText } from "../../logging/redact.js";
67import { createSubsystemLogger } from "../../logging/subsystem.js";
78import { hashText } from "./internal.js";
@@ -24,6 +25,11 @@ export type SessionFileEntry = {
2425generatedByDreamingNarrative?: boolean;
2526};
262728+export type BuildSessionEntryOptions = {
29+/** Optional preclassification from a caller-managed dreaming transcript lookup. */
30+generatedByDreamingNarrative?: boolean;
31+};
32+2733function isDreamingNarrativeBootstrapRecord(record: unknown): boolean {
2834if (!record || typeof record !== "object" || Array.isArray(record)) {
2935return false;
@@ -78,6 +84,79 @@ function isDreamingNarrativeGeneratedRecord(record: unknown): boolean {
7884return hasDreamingNarrativeRunId(nested.runId) || hasDreamingNarrativeRunId(nested.sessionKey);
7985}
808687+function isDreamingNarrativeSessionStoreKey(sessionKey: string): boolean {
88+const trimmed = sessionKey.trim();
89+if (!trimmed) {
90+return false;
91+}
92+const firstSeparator = trimmed.indexOf(":");
93+if (firstSeparator < 0) {
94+return trimmed.startsWith(DREAMING_NARRATIVE_RUN_PREFIX);
95+}
96+const secondSeparator = trimmed.indexOf(":", firstSeparator + 1);
97+const sessionSegment = secondSeparator < 0 ? trimmed : trimmed.slice(secondSeparator + 1);
98+return sessionSegment.startsWith(DREAMING_NARRATIVE_RUN_PREFIX);
99+}
100+101+function normalizeComparablePath(pathname: string): string {
102+const resolved = path.resolve(pathname);
103+return process.platform === "win32" ? resolved.toLowerCase() : resolved;
104+}
105+106+export function normalizeSessionTranscriptPathForComparison(pathname: string): string {
107+return normalizeComparablePath(pathname);
108+}
109+110+function resolveSessionStoreTranscriptPath(
111+sessionsDir: string,
112+entry: { sessionFile?: unknown; sessionId?: unknown } | undefined,
113+): string | null {
114+if (typeof entry?.sessionFile === "string" && entry.sessionFile.trim().length > 0) {
115+const sessionFile = entry.sessionFile.trim();
116+const resolved = path.isAbsolute(sessionFile)
117+ ? sessionFile
118+ : path.resolve(sessionsDir, sessionFile);
119+return normalizeComparablePath(resolved);
120+}
121+if (typeof entry?.sessionId === "string" && entry.sessionId.trim().length > 0) {
122+return normalizeComparablePath(path.join(sessionsDir, `${entry.sessionId.trim()}.jsonl`));
123+}
124+return null;
125+}
126+127+export function loadDreamingNarrativeTranscriptPathSetForSessionsDir(
128+sessionsDir: string,
129+): ReadonlySet<string> {
130+const storePath = path.join(sessionsDir, "sessions.json");
131+const store = loadSessionStore(storePath);
132+const dreamingTranscriptPaths = new Set<string>();
133+for (const [sessionKey, entry] of Object.entries(store)) {
134+if (!isDreamingNarrativeSessionStoreKey(sessionKey)) {
135+continue;
136+}
137+const transcriptPath = resolveSessionStoreTranscriptPath(sessionsDir, entry);
138+if (transcriptPath) {
139+dreamingTranscriptPaths.add(transcriptPath);
140+}
141+}
142+return dreamingTranscriptPaths;
143+}
144+145+export function loadDreamingNarrativeTranscriptPathSetForAgent(
146+agentId: string,
147+): ReadonlySet<string> {
148+return loadDreamingNarrativeTranscriptPathSetForSessionsDir(
149+resolveSessionTranscriptsDirForAgent(agentId),
150+);
151+}
152+153+function isDreamingNarrativeTranscriptFromSessionStore(absPath: string): boolean {
154+const sessionsDir = path.dirname(absPath);
155+const normalizedAbsPath = normalizeComparablePath(absPath);
156+const dreamingTranscriptPaths = loadDreamingNarrativeTranscriptPathSetForSessionsDir(sessionsDir);
157+return dreamingTranscriptPaths.has(normalizedAbsPath);
158+}
159+81160export async function listSessionFilesForAgent(agentId: string): Promise<string[]> {
82161const dir = resolveSessionTranscriptsDirForAgent(agentId);
83162try {
@@ -153,15 +232,19 @@ function parseSessionTimestampMs(
153232return 0;
154233}
155234156-export async function buildSessionEntry(absPath: string): Promise<SessionFileEntry | null> {
235+export async function buildSessionEntry(
236+absPath: string,
237+opts: BuildSessionEntryOptions = {},
238+): Promise<SessionFileEntry | null> {
157239try {
158240const stat = await fs.stat(absPath);
159241const raw = await fs.readFile(absPath, "utf-8");
160242const lines = raw.split("\n");
161243const collected: string[] = [];
162244const lineMap: number[] = [];
163245const messageTimestampsMs: number[] = [];
164-let generatedByDreamingNarrative = false;
246+let generatedByDreamingNarrative =
247+opts.generatedByDreamingNarrative ?? isDreamingNarrativeTranscriptFromSessionStore(absPath);
165248for (let jsonlIdx = 0; jsonlIdx < lines.length; jsonlIdx++) {
166249const line = lines[jsonlIdx];
167250if (!line.trim()) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。