
























@@ -27,6 +27,7 @@ const DREAMING_NARRATIVE_RUN_PREFIX = "dreaming-narrative-";
2727// toxic line. Wrapped continuation lines still map back to the same JSONL line.
2828// This limit applies to content only; the role label adds up to 11 chars.
2929const SESSION_EXPORT_CONTENT_WRAP_CHARS = 800;
30+const SESSION_ENTRY_PARSE_YIELD_LINES = 250;
3031const DIRECT_CRON_PROMPT_RE = /^\[cron:[^\]]+\]\s*/;
31323233export type SessionFileEntry = {
@@ -51,6 +52,8 @@ export type BuildSessionEntryOptions = {
5152generatedByDreamingNarrative?: boolean;
5253/** Optional preclassification from a caller-managed cron transcript lookup. */
5354generatedByCronRun?: boolean;
55+/** Override for tests or specialized callers that need a tighter parse yield cadence. */
56+parseYieldEveryLines?: number;
5457};
55585659export type SessionTranscriptClassification = {
@@ -520,6 +523,25 @@ function parseSessionTimestampMs(
520523return 0;
521524}
522525526+function resolveSessionEntryParseYieldLines(opts: BuildSessionEntryOptions): number {
527+const configured = opts.parseYieldEveryLines;
528+if (typeof configured === "number" && Number.isFinite(configured)) {
529+return Math.max(1, Math.floor(configured));
530+}
531+return SESSION_ENTRY_PARSE_YIELD_LINES;
532+}
533+534+async function yieldSessionEntryParseIfNeeded(
535+lineIndex: number,
536+everyLines: number,
537+): Promise<void> {
538+if (lineIndex > 0 && lineIndex % everyLines === 0) {
539+await new Promise<void>((resolve) => {
540+setImmediate(resolve);
541+});
542+}
543+}
544+523545export async function buildSessionEntry(
524546absPath: string,
525547opts: BuildSessionEntryOptions = {},
@@ -543,10 +565,10 @@ export async function buildSessionEntry(
543565};
544566}
545567const raw = (await readRegularFile({ filePath: absPath })).buffer.toString("utf-8");
546-const lines = raw.split("\n");
547568const collected: string[] = [];
548569const lineMap: number[] = [];
549570const messageTimestampsMs: number[] = [];
571+const parseYieldEveryLines = resolveSessionEntryParseYieldLines(opts);
550572const sessionStoreClassification =
551573opts.generatedByDreamingNarrative === undefined || opts.generatedByCronRun === undefined
552574 ? classifySessionTranscriptFromSessionStore(absPath)
@@ -559,8 +581,12 @@ export async function buildSessionEntry(
559581opts.generatedByCronRun ?? sessionStoreClassification?.generatedByCronRun ?? false;
560582const allowArchiveContentCronClassification =
561583isUsageCountedSessionArchiveTranscriptPath(absPath);
562-for (let jsonlIdx = 0; jsonlIdx < lines.length; jsonlIdx++) {
563-const line = lines[jsonlIdx];
584+for (let jsonlIdx = 0, lineStart = 0; lineStart <= raw.length; jsonlIdx++) {
585+await yieldSessionEntryParseIfNeeded(jsonlIdx, parseYieldEveryLines);
586+const newlineIndex = raw.indexOf("\n", lineStart);
587+const lineEnd = newlineIndex === -1 ? raw.length : newlineIndex;
588+const line = raw.slice(lineStart, lineEnd);
589+lineStart = newlineIndex === -1 ? raw.length + 1 : newlineIndex + 1;
564590if (!line.trim()) {
565591continue;
566592}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。