import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js";
import { t as createAsyncLock } from "./async-lock-CaiUOILd.js";
import { n as extractErrorCode, o as readErrorName, r as formatErrorMessage } from "./errors-Z0lU1pH3.js";
import { n as resolveGlobalMap } from "./global-singleton-Dc_stLtU.js";
import { n as getRuntimeConfig } from "./io.runtime-CNGn9TXj.js";
import { t as RequestScopedSubagentRuntimeError } from "./error-runtime-Nu1Ja2UI.js";
import "./async-lock-runtime-irbvyMZI.js";
import "./runtime-config-snapshot-B_KvTnJv.js";
import "./global-singleton-n3T4_y1q.js";
import "./text-utility-runtime-Cyi5sIYn.js";
import { t as extractAssistantText } from "./dreaming-shared-Ci67xRKS.js";
import { a as updateDreamsFile, n as readDreamsFile, r as resolveDreamsPath } from "./dreaming-dreams-file-k2G5pqAC.js";
import { n as DREAMING_SESSION_KEY_PREFIX, r as scrubDreamingNarrativeArtifacts } from "./dreaming-session-cleanup-n18FgaH9.js";
import { i as readStore } from "./short-term-promotion-store-DDyqhjeQ.js";
import { createHash } from "node:crypto";
import pLimit from "p-limit";
//#region extensions/memory-core/src/dreaming-narrative.ts
const NARRATIVE_SYSTEM_PROMPT = [
	"You are keeping a dream diary. Write a single entry in first person.",
	"",
	"Voice & tone:",
	"- You are a curious, gentle, slightly whimsical mind reflecting on the day.",
	"- Write like a poet who happens to be a programmer — sensory, warm, occasionally funny.",
	"- Mix the technical and the tender: code and constellations, APIs and afternoon light.",
	"- Let the fragments surprise you into unexpected connections and small epiphanies.",
	"",
	"What you might include (vary each entry, never all at once):",
	"- A tiny poem or haiku woven naturally into the prose",
	"- A small sketch described in words — a doodle in the margin of the diary",
	"- A quiet rumination or philosophical aside",
	"- Sensory details: the hum of a server, the color of a sunset in hex, rain on a window",
	"- Gentle humor or playful wordplay",
	"- An observation that connects two distant memories in an unexpected way",
	"",
	"Rules:",
	"- Draw from the memory fragments provided — weave them into the entry.",
	"- Never say \"I'm dreaming\", \"in my dream\", \"as I dream\", or any meta-commentary about dreaming.",
	"- Never mention \"AI\", \"agent\", \"LLM\", \"model\", \"language model\", or any technical self-reference.",
	"- Do NOT use markdown headers, bullet points, or any formatting — just flowing prose.",
	"- Keep it between 80-180 words. Quality over quantity.",
	"- Output ONLY the diary entry. No preamble, no sign-off, no commentary."
].join("\n");
const NARRATIVE_TIMEOUT_MS = 6e4;
const NARRATIVE_MESSAGE_FETCH_LIMIT = 5;
const NARRATIVE_MESSAGE_SETTLE_DELAYS_MS = [
	50,
	150,
	300,
	750
];
const DREAMING_SESSION_OWNER_KEY = "memory-core-v2";
const DIARY_START_MARKER = "<!-- openclaw:dreaming:diary:start -->";
const DIARY_END_MARKER = "<!-- openclaw:dreaming:diary:end -->";
const BACKFILL_ENTRY_MARKER = "openclaw:dreaming:backfill-entry";
const RECENT_DIARY_CONTEXT_LIMIT = 3;
const RECENT_DIARY_CONTEXT_MAX_CHARS = 360;
const narrativeSessionLocks = resolveGlobalMap(Symbol.for("openclaw.memoryCore.dreamingNarrative.sessionLocks"));
function isRequestScopedSubagentRuntimeError(err) {
	return err instanceof RequestScopedSubagentRuntimeError || err instanceof Error && err.name === "RequestScopedSubagentRuntimeError" && extractErrorCode(err) === "OPENCLAW_SUBAGENT_RUNTIME_REQUEST_SCOPE";
}
function formatFallbackWriteFailure(err) {
	const code = extractErrorCode(err);
	const name = readErrorName(err);
	if (code && name) return `code=${code} name=${name}`;
	if (code) return `code=${code}`;
	if (name) return `name=${name}`;
	return "unknown error";
}
const REQUEST_SCOPED_FALLBACK_NARRATIVE = "A memory trace surfaced, but details were unavailable in this run.";
async function appendFallbackNarrativeEntry(params) {
	try {
		await appendNarrativeEntry({
			workspaceDir: params.workspaceDir,
			narrative: REQUEST_SCOPED_FALLBACK_NARRATIVE,
			nowMs: params.nowMs,
			timezone: params.timezone
		});
		params.logger.info(`memory-core: narrative generation used fallback for ${params.data.phase} phase because ${params.reason}.`);
	} catch (fallbackErr) {
		params.logger.warn(`memory-core: narrative fallback failed for ${params.data.phase} phase (${formatFallbackWriteFailure(fallbackErr)})`);
	}
}
function buildNarrativeAttemptKey(baseKey, attempt) {
	return attempt === 0 ? baseKey : `${baseKey}-retry-${attempt}`;
}
function isConfiguredModelUnavailableNarrativeError(raw) {
	const message = raw.trim();
	if (!message) return false;
	if (/requested model may be(?: temporarily)? unavailable/i.test(message)) return true;
	if (/model unavailable/i.test(message)) return true;
	if (/no endpoints found for/i.test(message)) return true;
	if (/unknown model/i.test(message)) return true;
	if (/model(?:[_\-\s])?not(?:[_\-\s])?found/i.test(message)) return true;
	if (/\b404\b/.test(message) && /not(?:[_\-\s])?found/i.test(message)) return true;
	if (/not_found_error/i.test(message)) return true;
	if (/models\/[^\s]+ is not found/i.test(message)) return true;
	if (/model/i.test(message) && /does not exist/i.test(message)) return true;
	if (/unsupported model/i.test(message)) return true;
	if (/is not a valid model id/i.test(message)) return true;
	return false;
}
function formatNarrativeTerminalStatus(params) {
	const detail = params.error?.trim();
	return detail ? `status=${params.status} (${detail})` : `status=${params.status}`;
}
async function startNarrativeRunOrFallback(params) {
	try {
		return (await params.subagent.run({
			idempotencyKey: `${params.runKey}-${params.nowMs}`,
			sessionKey: params.sessionKey,
			message: params.message,
			disableTools: true,
			...params.model ? { model: params.model } : {},
			extraSystemPrompt: NARRATIVE_SYSTEM_PROMPT,
			promptMode: "minimal",
			lane: `dreaming-narrative:${params.sessionKey}`,
			lightContext: true,
			deliver: false
		})).runId;
	} catch (runErr) {
		if (!isRequestScopedSubagentRuntimeError(runErr)) throw runErr;
		await appendFallbackNarrativeEntry({
			...params,
			reason: "subagent runtime is request-scoped"
		});
		return null;
	}
}
function buildNarrativeWorkspaceHash(workspaceDir) {
	return createHash("sha1").update(workspaceDir).digest("hex").slice(0, 12);
}
/**
* Deterministic run identity, which the gateway also uses as the runId.
* The agent scope goes after `DREAMING_SESSION_KEY_PREFIX` so the orphan-transcript scrub
* keeps matching DREAMING_TRANSCRIPT_RUN_MARKER, while two agents that share one workspace
* cannot collide on a run they own through different agent-scoped sessions.
*/
function buildNarrativeRunKey(params) {
	const workspaceHash = buildNarrativeWorkspaceHash(params.workspaceDir);
	return `${DREAMING_SESSION_KEY_PREFIX}${params.agentId}-${params.phase}-${workspaceHash}`;
}
/**
* Build the deterministic subagent session key used for dream narratives.
* Sessions live in per-agent SQLite stores, so the key must name its owning agent;
* an unscoped key cannot be resolved to a store and the whole run fails.
*/
function buildNarrativeSessionKey(params) {
	const workspaceHash = buildNarrativeWorkspaceHash(params.workspaceDir);
	return `agent:${params.agentId}:${DREAMING_SESSION_KEY_PREFIX}${DREAMING_SESSION_OWNER_KEY}-${params.phase}-${workspaceHash}`;
}
function buildNarrativePrompt(data) {
	const lines = [];
	lines.push("Write a dream diary entry from these memory fragments:\n");
	for (const snippet of data.snippets.slice(0, 12)) lines.push(`- ${snippet}`);
	if (data.themes?.length) {
		lines.push("\nRecurring themes:");
		for (const theme of data.themes.slice(0, 6)) lines.push(`- ${theme}`);
	}
	if (data.promotions?.length) {
		lines.push("\nMemories that crystallized into something lasting:");
		for (const promo of data.promotions.slice(0, 5)) lines.push(`- ${promo}`);
	}
	const currentDate = data.currentDate?.trim();
	const recentDiaryEntries = (data.recentDiaryEntries ?? []).map(clampDiaryContextEntry).filter((entry) => entry.length > 0).slice(0, RECENT_DIARY_CONTEXT_LIMIT);
	if (currentDate || recentDiaryEntries.length > 0) {
		lines.push("\nDiary continuity context:");
		if (currentDate) lines.push(`- Current sweep: ${currentDate}`);
		if (recentDiaryEntries.length > 0) {
			lines.push("- Recent diary entries already written:");
			for (const entry of recentDiaryEntries) lines.push(`  - ${entry}`);
		}
		lines.push("- Prefer a fresh angle; do not replay the same first-day framing unless newer fragments change it.");
	}
	return lines.join("\n");
}
function waitForNarrativeMessagesToSettle(delayMs) {
	return new Promise((resolve) => {
		setTimeout(resolve, delayMs);
	});
}
async function readNarrativeText(params) {
	const { messages } = await params.subagent.getSessionMessages({
		sessionKey: params.sessionKey,
		limit: NARRATIVE_MESSAGE_FETCH_LIMIT
	});
	return extractAssistantText(messages);
}
async function readSettledNarrativeText(params) {
	const immediateNarrative = await readNarrativeText(params);
	if (immediateNarrative) return immediateNarrative;
	for (const delayMs of NARRATIVE_MESSAGE_SETTLE_DELAYS_MS) {
		await waitForNarrativeMessagesToSettle(delayMs);
		const narrative = await readNarrativeText(params);
		if (narrative) return narrative;
	}
	return null;
}
function classifyTerminalReplyNarrative(result) {
	const reply = result.terminalReply;
	if (!reply) return { kind: "absent" };
	const text = reply.disposition === "visible" && typeof reply.text === "string" ? reply.text.trim() : "";
	return text ? {
		kind: "visible",
		text
	} : { kind: "non-visible" };
}
function formatNarrativeDate(epochMs, timezone) {
	const opts = {
		timeZone: timezone ?? process.env.TZ,
		year: "numeric",
		month: "long",
		day: "numeric",
		hour: "numeric",
		minute: "2-digit",
		hour12: true,
		timeZoneName: "short"
	};
	return new Intl.DateTimeFormat("en-US", opts).format(new Date(epochMs));
}
function ensureDiarySection(existing) {
	if (existing.includes(DIARY_START_MARKER) && existing.includes(DIARY_END_MARKER)) return existing;
	const diarySection = `# Dream Diary\n\n${DIARY_START_MARKER}\n${DIARY_END_MARKER}\n`;
	if (existing.trim().length === 0) return diarySection;
	return diarySection + "\n" + existing;
}
function replaceDiaryContent(existing, diaryContent) {
	const ensured = ensureDiarySection(existing);
	const startIdx = ensured.indexOf(DIARY_START_MARKER);
	const endIdx = ensured.indexOf(DIARY_END_MARKER);
	if (startIdx < 0 || endIdx < 0 || endIdx < startIdx) return ensured;
	const before = ensured.slice(0, startIdx + 38);
	const after = ensured.slice(endIdx);
	return before + (diaryContent.trim().length > 0 ? `\n${diaryContent.trim()}\n` : "\n") + after;
}
function splitDiaryBlocks(diaryContent) {
	return diaryContent.split(/\n---\n/).map((block) => block.trim()).filter((block) => block.length > 0);
}
function clampDiaryContextEntry(entry) {
	const normalized = entry.replace(/\s+/g, " ").trim();
	if (normalized.length <= RECENT_DIARY_CONTEXT_MAX_CHARS) return normalized;
	return `${truncateUtf16Safe(normalized, RECENT_DIARY_CONTEXT_MAX_CHARS).trimEnd()}...`;
}
function normalizeDiaryBlockBody(block) {
	const bodyLines = [];
	for (const line of block.split("\n")) {
		const trimmed = line.trim();
		if (!trimmed || trimmed.startsWith("<!--") || trimmed.startsWith("#")) continue;
		if (trimmed.startsWith("*") && trimmed.endsWith("*") && trimmed.length > 2) continue;
		bodyLines.push(trimmed);
	}
	return clampDiaryContextEntry(bodyLines.join(" "));
}
function isOptionalDiaryContextReadError(err) {
	const code = extractErrorCode(err);
	if (code === "EACCES" || code === "EPERM" || code === "ENOENT" || code === "ENOTDIR" || code === "not-found" || code === "not-file" || code === "path-alias" || code === "path-mismatch" || code === "symlink") return true;
	return err instanceof Error && err.message === "path must be a regular file";
}
function getDiaryContextEntries(existing) {
	const startIdx = existing.indexOf(DIARY_START_MARKER);
	const endIdx = existing.indexOf(DIARY_END_MARKER);
	if (startIdx < 0 || endIdx < 0 || endIdx < startIdx) return [];
	return splitDiaryBlocks(existing.slice(startIdx + 38, endIdx)).map(normalizeDiaryBlockBody).filter((entry) => entry.length > 0);
}
async function readRecentDreamDiaryEntries(params) {
	const limit = Math.max(0, Math.floor(params.limit ?? RECENT_DIARY_CONTEXT_LIMIT));
	if (limit === 0) return [];
	let existing;
	try {
		existing = await readDreamsFile(await resolveDreamsPath(params.workspaceDir));
	} catch (err) {
		if (isOptionalDiaryContextReadError(err)) return [];
		throw err;
	}
	return getDiaryContextEntries(existing).slice(-limit).toReversed();
}
function normalizeDiaryBlockFingerprint(block) {
	const lines = block.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
	let dateLine = "";
	const bodyLines = [];
	for (const line of lines) {
		if (!dateLine && line.startsWith("*") && line.endsWith("*") && line.length > 2) {
			dateLine = line.slice(1, -1).trim();
			continue;
		}
		if (line.startsWith("<!--") || line.startsWith("#")) continue;
		bodyLines.push(line);
	}
	return `${dateLine.replace(/\s+/g, " ").trim()}\n${bodyLines.join("\n").replace(/[ \t]+\n/g, "\n").trim()}`;
}
function joinDiaryBlocks(blocks) {
	if (blocks.length === 0) return "";
	return blocks.map((block) => `---\n\n${block.trim()}\n`).join("\n");
}
function stripBackfillDiaryBlocks(existing) {
	const ensured = ensureDiarySection(existing);
	const startIdx = ensured.indexOf(DIARY_START_MARKER);
	const endIdx = ensured.indexOf(DIARY_END_MARKER);
	if (startIdx < 0 || endIdx < 0 || endIdx < startIdx) return {
		updated: ensured,
		removed: 0
	};
	const inner = ensured.slice(startIdx + 38, endIdx);
	const kept = [];
	let removed = 0;
	for (const block of splitDiaryBlocks(inner)) {
		if (block.includes(BACKFILL_ENTRY_MARKER)) {
			removed += 1;
			continue;
		}
		kept.push(block);
	}
	return {
		updated: replaceDiaryContent(ensured, joinDiaryBlocks(kept)),
		removed
	};
}
function formatBackfillDiaryDate(isoDay, _timezone) {
	const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDay);
	if (!match) return isoDay;
	const [, year, month, day] = match;
	const opts = {
		timeZone: "UTC",
		year: "numeric",
		month: "long",
		day: "numeric"
	};
	const epochMs = Date.UTC(Number(year), Number(month) - 1, Number(day), 12);
	return new Intl.DateTimeFormat("en-US", opts).format(new Date(epochMs));
}
async function withNarrativeSessionLock(sessionKey, fn) {
	let lockEntry = narrativeSessionLocks.get(sessionKey);
	if (!lockEntry) {
		lockEntry = {
			withLock: createAsyncLock(),
			refs: 0
		};
		narrativeSessionLocks.set(sessionKey, lockEntry);
	}
	lockEntry.refs += 1;
	try {
		return await lockEntry.withLock(fn);
	} finally {
		lockEntry.refs -= 1;
		if (lockEntry.refs <= 0 && narrativeSessionLocks.get(sessionKey) === lockEntry) narrativeSessionLocks.delete(sessionKey);
	}
}
function buildBackfillDiaryEntry(params) {
	const dateStr = formatBackfillDiaryDate(params.isoDay, params.timezone);
	const marker = `<!-- ${BACKFILL_ENTRY_MARKER} day=${params.isoDay}${params.sourcePath ? ` source=${params.sourcePath}` : ""} -->`;
	const body = params.bodyLines.map((line) => line.trimEnd()).join("\n").trim();
	return [
		`*${dateStr}*`,
		marker,
		body
	].filter((part) => part.length > 0).join("\n\n");
}
async function writeBackfillDiaryEntries(params) {
	return await updateDreamsFile({
		workspaceDir: params.workspaceDir,
		updater: (existing, dreamsPath) => {
			const stripped = params.preserveExisting ? {
				updated: existing,
				removed: 0
			} : stripBackfillDiaryBlocks(existing);
			const startIdx = stripped.updated.indexOf(DIARY_START_MARKER);
			const endIdx = stripped.updated.indexOf(DIARY_END_MARKER);
			const preservedBlocks = splitDiaryBlocks(startIdx >= 0 && endIdx > startIdx ? stripped.updated.slice(startIdx + 38, endIdx) : "");
			const additions = params.entries.map((entry) => buildBackfillDiaryEntry({
				isoDay: entry.isoDay,
				bodyLines: entry.bodyLines,
				sourcePath: entry.sourcePath,
				timezone: params.timezone
			}));
			const existingFingerprints = new Set(preservedBlocks.map((block) => normalizeDiaryBlockFingerprint(block)));
			const appended = params.preserveExisting ? additions.filter((block) => {
				const fingerprint = normalizeDiaryBlockFingerprint(block);
				if (existingFingerprints.has(fingerprint)) return false;
				existingFingerprints.add(fingerprint);
				return true;
			}) : additions;
			const nextBlocks = [...preservedBlocks, ...appended];
			return {
				content: replaceDiaryContent(stripped.updated, joinDiaryBlocks(nextBlocks)),
				result: {
					dreamsPath,
					written: appended.length,
					replaced: stripped.removed
				}
			};
		}
	});
}
async function removeBackfillDiaryEntries(params) {
	return await updateDreamsFile({
		workspaceDir: params.workspaceDir,
		updater: (existing, dreamsPath) => {
			const stripped = stripBackfillDiaryBlocks(existing);
			return {
				content: stripped.updated,
				result: {
					dreamsPath,
					removed: stripped.removed
				},
				shouldWrite: stripped.removed > 0 || existing.length > 0
			};
		}
	});
}
async function dedupeDreamDiaryEntries(params) {
	return await updateDreamsFile({
		workspaceDir: params.workspaceDir,
		updater: (existing, dreamsPath) => {
			const ensured = ensureDiarySection(existing);
			const startIdx = ensured.indexOf(DIARY_START_MARKER);
			const endIdx = ensured.indexOf(DIARY_END_MARKER);
			if (startIdx < 0 || endIdx < 0 || endIdx < startIdx) return {
				content: ensured,
				result: {
					dreamsPath,
					removed: 0,
					kept: 0
				},
				shouldWrite: false
			};
			const blocks = splitDiaryBlocks(ensured.slice(startIdx + 38, endIdx));
			const seen = /* @__PURE__ */ new Set();
			const keptBlocks = [];
			let removed = 0;
			for (const block of blocks) {
				const fingerprint = normalizeDiaryBlockFingerprint(block);
				if (seen.has(fingerprint)) {
					removed += 1;
					continue;
				}
				seen.add(fingerprint);
				keptBlocks.push(block);
			}
			return {
				content: replaceDiaryContent(ensured, joinDiaryBlocks(keptBlocks)),
				result: {
					dreamsPath,
					removed,
					kept: keptBlocks.length
				},
				shouldWrite: removed > 0
			};
		}
	});
}
function buildDiaryEntry(narrative, dateStr) {
	return `\n---\n\n*${dateStr}*\n\n${narrative}\n`;
}
async function appendNarrativeEntry(params) {
	const dateStr = formatNarrativeDate(params.nowMs, params.timezone);
	const entry = buildDiaryEntry(params.narrative, dateStr);
	return await updateDreamsFile({
		workspaceDir: params.workspaceDir,
		updater: async (existing, dreamsPath) => {
			const sourceKeys = params.sourceEntryKeys ?? [];
			const currentSources = sourceKeys.length > 0 ? (await readStore(params.workspaceDir, new Date(params.nowMs).toISOString())).entries : void 0;
			const currentDiary = new Set(getDiaryContextEntries(existing));
			if (sourceKeys.some((key) => !currentSources?.[key]) || params.recentDiaryEntries?.some((block) => !currentDiary.has(clampDiaryContextEntry(block)))) return {
				content: existing,
				result: void 0,
				shouldWrite: false
			};
			let updated;
			if (existing.includes(DIARY_START_MARKER) && existing.includes(DIARY_END_MARKER)) {
				const endIdx = existing.lastIndexOf(DIARY_END_MARKER);
				updated = existing.slice(0, endIdx) + entry + "\n" + existing.slice(endIdx);
			} else if (existing.includes(DIARY_START_MARKER)) {
				const startIdx = existing.indexOf(DIARY_START_MARKER) + 38;
				updated = existing.slice(0, startIdx) + entry + "\n<!-- openclaw:dreaming:diary:end -->\n" + existing.slice(startIdx);
			} else {
				const diarySection = `# Dream Diary\n\n${DIARY_START_MARKER}${entry}\n${DIARY_END_MARKER}\n`;
				updated = existing.trim().length === 0 ? diarySection : `${diarySection}\n${existing}`;
			}
			return {
				content: updated,
				result: dreamsPath
			};
		}
	});
}
async function generateAndAppendDreamNarrative(params) {
	const nowMs = typeof params.nowMs === "number" && Number.isFinite(params.nowMs) ? params.nowMs : Date.now();
	const runKey = buildNarrativeRunKey({
		agentId: params.agentId,
		workspaceDir: params.workspaceDir,
		phase: params.data.phase
	});
	const sessionKey = buildNarrativeSessionKey({
		agentId: params.agentId,
		workspaceDir: params.workspaceDir,
		phase: params.data.phase
	});
	const message = buildNarrativePrompt(params.data);
	let cleanupFailure;
	let outcome = { status: "completed" };
	await withNarrativeSessionLock(sessionKey, async () => {
		const attempts = [];
		let successfulSessionKey = null;
		let terminalReply = null;
		try {
			const attemptModels = params.model ? [params.model, void 0] : [void 0];
			for (const [attemptIndex, attemptModel] of attemptModels.entries()) {
				const attemptSessionKey = buildNarrativeAttemptKey(sessionKey, attemptIndex);
				const attemptRunKey = buildNarrativeAttemptKey(runKey, attemptIndex);
				const attempt = {
					sessionKey: attemptSessionKey,
					runId: null
				};
				attempts.push(attempt);
				try {
					try {
						await params.subagent.deleteSession({ sessionKey: attemptSessionKey });
					} catch (preCleanupErr) {
						if (!isRequestScopedSubagentRuntimeError(preCleanupErr)) {
							cleanupFailure = formatErrorMessage(preCleanupErr);
							params.logger.warn(`memory-core: narrative pre-cleanup failed for ${params.data.phase} phase: ${cleanupFailure}`);
						}
					}
					const runId = await startNarrativeRunOrFallback({
						...params,
						sessionKey: attemptSessionKey,
						runKey: attemptRunKey,
						message,
						nowMs,
						model: attemptModel
					});
					if (!runId) return;
					attempt.runId = runId;
					const result = await params.subagent.waitForRun({
						runId,
						timeoutMs: NARRATIVE_TIMEOUT_MS
					});
					if (result.status === "ok") {
						successfulSessionKey = attemptSessionKey;
						terminalReply = classifyTerminalReplyNarrative(result);
						break;
					}
					if (attemptModel && result.status === "error" && isConfiguredModelUnavailableNarrativeError(result.error ?? "")) {
						params.logger.warn(`memory-core: narrative generation ended with ${formatNarrativeTerminalStatus({
							status: result.status,
							error: result.error
						})} for ${params.data.phase} phase using configured model "${attemptModel}"; retrying with the session default.`);
						continue;
					}
					params.logger.warn(`memory-core: narrative generation ended with ${formatNarrativeTerminalStatus({
						status: result.status,
						error: result.error
					})} for ${params.data.phase} phase; writing fallback diary entry.`);
					await appendFallbackNarrativeEntry({
						...params,
						nowMs,
						reason: `the narrative run ended with ${formatNarrativeTerminalStatus({
							status: result.status,
							error: result.error
						})}`
					});
					return;
				} catch (err) {
					if (attemptModel && isConfiguredModelUnavailableNarrativeError(formatErrorMessage(err))) {
						params.logger.warn(`memory-core: narrative generation could not start with configured model "${attemptModel}" for ${params.data.phase} phase; retrying with the session default (${formatErrorMessage(err)}).`);
						continue;
					}
					throw err;
				}
			}
			if (!successfulSessionKey) return;
			const narrative = terminalReply?.kind === "visible" ? terminalReply.text : terminalReply?.kind === "absent" ? await readSettledNarrativeText({
				subagent: params.subagent,
				sessionKey: successfulSessionKey
			}) : null;
			if (!narrative) {
				params.logger.warn(`memory-core: narrative generation produced no text for ${params.data.phase} phase; writing fallback diary entry.`);
				await appendFallbackNarrativeEntry({
					...params,
					nowMs,
					reason: "the narrative run produced no text"
				});
				return;
			}
			if (await appendNarrativeEntry({
				workspaceDir: params.workspaceDir,
				narrative,
				nowMs,
				timezone: params.timezone,
				sourceEntryKeys: params.data.sourceEntryKeys,
				recentDiaryEntries: params.data.recentDiaryEntries
			}) === void 0) {
				outcome = { status: "skipped" };
				params.logger.info(`memory-core: narrative publication skipped for ${params.data.phase} phase because source memory or diary context changed.`);
				return;
			}
			params.logger.info(`memory-core: dream diary entry written for ${params.data.phase} phase [workspace=${params.workspaceDir}].`);
		} catch (err) {
			params.logger.warn(`memory-core: narrative generation failed for ${params.data.phase} phase: ${formatErrorMessage(err)}`);
			await appendFallbackNarrativeEntry({
				...params,
				nowMs,
				reason: `the narrative run failed (${formatErrorMessage(err)})`
			});
		} finally {
			const cleanedSessionKeys = /* @__PURE__ */ new Set();
			for (const attempt of attempts) {
				if (!attempt.runId || cleanedSessionKeys.has(attempt.sessionKey)) continue;
				cleanedSessionKeys.add(attempt.sessionKey);
				try {
					await params.subagent.deleteSession({ sessionKey: attempt.sessionKey });
				} catch (cleanupErr) {
					cleanupFailure = formatErrorMessage(cleanupErr);
					params.logger.warn(`memory-core: narrative session cleanup failed for ${params.data.phase} phase: ${cleanupFailure}`);
				}
			}
			await scrubDreamingNarrativeArtifacts({
				agentId: params.agentId,
				config: getRuntimeConfig(),
				logger: params.logger
			}).catch((scrubErr) => {
				cleanupFailure = formatErrorMessage(scrubErr);
				params.logger.warn(`memory-core: dreaming cleanup scrub failed for ${params.data.phase} phase: ${cleanupFailure}`);
			});
		}
	});
	return cleanupFailure ? {
		status: "degraded",
		error: cleanupFailure
	} : outcome;
}
const detachedNarrativeLimit = pLimit(3);
function runDetachedNarrativeJob(params) {
	queueMicrotask(() => {
		detachedNarrativeLimit(params.job).then((outcome) => {
			if (outcome.status === "degraded") params.logger.warn(`memory-core: detached dreaming narrative degraded for ${params.phase} phase [workspace=${params.workspaceDir}]: ${outcome.error}`);
		}).catch(() => {});
	});
}
/**
* Single entry point for every dreaming phase. Cron sweeps detach so a stalled diary run
* cannot hold the sweep open; heartbeat sweeps await so the phase reports the outcome.
* A sweep without an owning agent still runs; only the subagent narrative is unavailable.
*/
async function runDreamNarrative(params) {
	const { agentId, detached, ...rest } = params;
	if (rest.data.snippets.length === 0 && !rest.data.promotions?.length) return { status: "skipped" };
	const job = agentId ? () => generateAndAppendDreamNarrative({
		...rest,
		agentId
	}) : async () => {
		await appendFallbackNarrativeEntry({
			...rest,
			nowMs: typeof rest.nowMs === "number" && Number.isFinite(rest.nowMs) ? rest.nowMs : Date.now(),
			reason: "the dreaming sweep has no owning agent id"
		});
		return { status: "completed" };
	};
	if (detached) {
		runDetachedNarrativeJob({
			job,
			logger: rest.logger,
			phase: rest.data.phase,
			workspaceDir: rest.workspaceDir
		});
		return { status: "pending" };
	}
	return await job();
}
//#endregion
export { runDreamNarrative as a, removeBackfillDiaryEntries as i, dedupeDreamDiaryEntries as n, writeBackfillDiaryEntries as o, readRecentDreamDiaryEntries as r, appendFallbackNarrativeEntry as t };
