import fs from "node:fs/promises"; import path from "node:path"; import { stripInternalRuntimeContext } from "../../agents/internal-runtime-context.js"; import { isHeartbeatUserMessage } from "../../auto-reply/heartbeat-filter.js"; import { HEARTBEAT_PROMPT } from "../../auto-reply/heartbeat.js"; import { stripInboundMetadata } from "../../auto-reply/reply/strip-inbound-meta.js"; import { HEARTBEAT_TOKEN, isSilentReplyPayloadText } from "../../auto-reply/tokens.js"; import {
isSessionArchiveArtifactName,
isUsageCountedSessionTranscriptFileName,
} from "../../config/sessions/artifacts.js"; import { resolveSessionTranscriptsDirForAgent } from "../../config/sessions/paths.js"; import { loadSessionStore } from "../../config/sessions/store-load.js"; import { isExecCompletionEvent } from "../../infra/heartbeat-events-filter.js"; import { redactSensitiveText } from "../../logging/redact.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; import { isCronRunSessionKey } from "../../sessions/session-key-utils.js"; import { hashText } from "./internal.js";
const log = createSubsystemLogger("memory"); const DREAMING_NARRATIVE_RUN_PREFIX = "dreaming-narrative-"; // Keep the historical one-line-per-message export shape for normal turns, but // wrap pathological long messages so downstream indexers never ingest a single // toxic line. Wrapped continuation lines still map back to the same JSONL line. // This limit applies to content only; the role label adds up to 11 chars. const SESSION_EXPORT_CONTENT_WRAP_CHARS = 800; const DIRECT_CRON_PROMPT_RE = /^\[cron:[^\]]+\]\s*/;
export type SessionFileEntry = {
path: string;
absPath: string;
mtimeMs: number;
size: number;
hash: string;
content: string; /** Maps each content line (0-indexed) to its 1-indexed JSONL source line. */
lineMap: number[]; /** Maps each content line (0-indexed) to epoch ms; 0 means unknown timestamp. */
messageTimestampsMs: number[]; /** True when this transcript belongs to an internal dreaming narrative run. */
generatedByDreamingNarrative?: boolean; /** True when this transcript belongs to an isolated cron run session. */
generatedByCronRun?: boolean;
};
export type BuildSessionEntryOptions = { /** Optional preclassification from a caller-managed dreaming transcript lookup. */
generatedByDreamingNarrative?: boolean; /** Optional preclassification from a caller-managed cron transcript lookup. */
generatedByCronRun?: boolean;
};
export type SessionTranscriptClassification = {
dreamingNarrativeTranscriptPaths: ReadonlySet<string>;
cronRunTranscriptPaths: ReadonlySet<string>;
};
function isCheckpointTranscriptFileName(fileName: string): boolean { return fileName.endsWith(".jsonl") && fileName.includes(".checkpoint.");
}
function isGeneratedSystemWrapperMessage(text: string, role: "user" | "assistant"): boolean { if (role !== "user") { returnfalse;
} return GENERATED_SYSTEM_MESSAGE_RE.test(text);
}
function isGeneratedCronPromptMessage(text: string, role: "user" | "assistant"): boolean { if (role !== "user") { returnfalse;
} return DIRECT_CRON_PROMPT_RE.test(text);
}
function isGeneratedHeartbeatPromptMessage(text: string, role: "user" | "assistant"): boolean { return role === "user" && isHeartbeatUserMessage({ role, content: text }, HEARTBEAT_PROMPT);
}
function sanitizeSessionText(text: string, role: "user" | "assistant"): string | null { const strippedInbound = stripInboundMetadataForUserRole(text, role); const strippedInternal = stripInternalRuntimeContext(strippedInbound); const normalized = normalizeSessionText(strippedInternal); if (!normalized) { returnnull;
} if (isGeneratedSystemWrapperMessage(normalized, role)) { returnnull;
} if (isGeneratedCronPromptMessage(normalized, role)) { returnnull;
} if (isGeneratedHeartbeatPromptMessage(normalized, role)) { returnnull;
} if (isSilentReplyPayloadText(normalized)) { returnnull;
} // Assistant-side machinery acks: HEARTBEAT_OK is the canonical "all clear, // nothing to do" reply to a heartbeat tick. Drop on the assistant side // directly so we do not have to rely on cross-message coupling with the // preceding user message (which a real user could spoof). if (role === "assistant" && normalized === HEARTBEAT_TOKEN) { returnnull;
} const withoutSystemEnvelope = normalized.replace(GENERATED_SYSTEM_MESSAGE_RE, "").trim(); if (isExecCompletionEvent(withoutSystemEnvelope)) { returnnull;
} return normalized;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.