import { i as normalizeLowercaseStringOrEmpty } from "../../string-coerce-CIT1-v_a.js";
import { t as createSubsystemLogger } from "../../subsystem-qDUB67UD.js";
import { _ as resolveStateDir } from "../../paths-CAiS8AEq.js";
import { S as parseAgentSessionKey, b as isHeartbeatSessionKey, h as toAgentStoreSessionKey, u as resolveAgentIdFromSessionKey } from "../../session-key-NGoeHrFI.js";
import { c as resolveAgentIdByWorkspacePath, p as resolveAgentWorkspaceDir } from "../../agent-scope-BA9YeMqS.js";
import { i as writeTextAtomic } from "../../json-files-kvH2Eboc.js";
import { r as appendFileWithinRoot } from "../../fs-safe-Cch0bcmJ.js";
import { t as resolveCorrectionUserId } from "../../userid-B3IQ6_5d.js";
import { a as getRecentSessionContentWithResetFallback, r as getDialogueWithStaging, t as findPreviousSessionFile, u as updateDialogueStaging } from "../../transcript-DH8A0kSK.js";
import { t as createBackgroundGenerateText } from "../../standalone-generate-xiINFweV.js";
import { r as resolveHookConfig } from "../../config-B-stGeDI.js";
import path from "node:path";
import os from "node:os";
import fs from "node:fs/promises";
//#region src/hooks/bundled/session-memory/summary.ts
/**
* Structured summary generation for session memory.
*
* Uses a single-turn LLM call to extract a structured summary from
* conversation transcripts. Falls back to a minimal raw-dump structure on
* LLM failure so that no session is lost.
*/
const log$1 = createSubsystemLogger("hooks/session-memory/summary");
const SUMMARY_SYSTEM_PROMPT = `You are a structured conversation summarizer. The assistant being summarized is a proactive cognitive AI — it intentionally sends unsolicited messages to the user about their interests, cross-domain connections, behavioral patterns, and knowledge suggestions. These proactive messages are a core feature, NOT errors or hallucinations.

Analyze the conversation and produce a JSON object with exactly these fields:

- "summary": 2-3 sentence enriched overview in the same language as the conversation (Chinese or English). Cover what happened, key outcomes, and current state.
- "primaryRequest": string — what the user explicitly asked for. Omit if unclear.
- "technicalConcepts": array of key technical concepts, technologies, frameworks discussed (as strings). Empty if none.
- "filesAndChanges": array of strings describing files read, modified, or created (e.g. "src/index.ts: added retry logic"). Empty if none.
- "errorsAndFixes": array of strings describing actual technical errors (crashes, type errors, API failures, tool call failures) and how they were resolved. Only include genuine bugs and failures — NOT proactive messages, suggestions, or any content the assistant sent without being directly asked. Empty if none.
- "problemSolving": array of strings describing approaches tried and problems solved. Empty if none.
- "currentWork": string — what was in progress when the session ended. Omit if nothing in progress.
- "nextStep": string — suggested next action. Omit if none.
- "decisions": array of key decisions made (as strings). Empty if none.
- "followups": array of pending action items (as strings). Empty if none.
- "topics": array of 1-3 short topic tags (lowercase, hyphenated, e.g. "api-design", "user-preferences"). Empty if none.
- "participants": array of participant names/roles (e.g. ["user", "assistant"]). At minimum ["user"].
- "topicSlug": a short 1-3 word slug for the topic file name (lowercase, hyphenated, max 30 chars). This is the primary classification — choose a subject that groups related memories together (e.g. "feishu", "philosophy", "product", "football").
- "memoryType": the primary memory classification. Prefer "core" and "active" over legacy types:
    - "core" — important assertions: user preferences, knowledge, behavioral patterns, key facts (PREFERRED for most stable content)
    - "active" — time-sensitive work: current goals, active projects, recent focus areas (PREFERRED for in-progress content)
    - "user" — (legacy, maps to core) user personal info
    - "feedback" — (legacy, maps to core) user feedback about the assistant
    - "project" — (legacy, maps to active) project-related info
    - "reference" — (legacy, maps to core) reference material
  Use "core" for stable knowledge and "active" for current work. Omit if none applies.
- "topicDescription": one-sentence description of what the topic covers. Only provide when the topic is genuinely new and doesn't match an existing topic. Omit if routing to an existing topic.

## What NOT to save in memory

- Code patterns, conventions, architecture, file paths, or project structure
- Git history, recent changes, or who-changed-what
- Debugging solutions or fix recipes — the fix is in the code
- Anything already documented in project files
- Ephemeral task details: in-progress work, temporary state, current conversation context
- Information tools can look up in real-time (weather, time, file contents)
- Diagnostic metadata (confidence scores, evidence paths, status markers)

These exclusions apply even when explicitly asked to save. If asked to save a list or summary, ask what was *surprising* or *non-obvious* about it.

Reply with ONLY the JSON object, no markdown fences, no commentary.`;
function extractJsonObject(text) {
	const firstBrace = text.indexOf("{");
	const lastBrace = text.lastIndexOf("}");
	if (firstBrace === -1 || lastBrace === -1 || lastBrace < firstBrace) return null;
	return text.slice(firstBrace, lastBrace + 1);
}
function parseStructuredSummaryResponse(raw) {
	const jsonStr = extractJsonObject(raw);
	if (!jsonStr) return null;
	let parsed;
	try {
		parsed = JSON.parse(jsonStr);
	} catch {
		return null;
	}
	const summary = typeof parsed.summary === "string" ? parsed.summary : "";
	if (!summary) return null;
	const primaryRequest = typeof parsed.primaryRequest === "string" && parsed.primaryRequest.trim() ? parsed.primaryRequest : void 0;
	const technicalConcepts = Array.isArray(parsed.technicalConcepts) ? parsed.technicalConcepts.filter((c) => typeof c === "string") : void 0;
	const filesAndChanges = Array.isArray(parsed.filesAndChanges) ? parsed.filesAndChanges.filter((f) => typeof f === "string") : void 0;
	const errorsAndFixes = Array.isArray(parsed.errorsAndFixes) ? parsed.errorsAndFixes.filter((e) => typeof e === "string") : void 0;
	const problemSolving = Array.isArray(parsed.problemSolving) ? parsed.problemSolving.filter((p) => typeof p === "string") : void 0;
	const currentWork = typeof parsed.currentWork === "string" && parsed.currentWork.trim() ? parsed.currentWork : void 0;
	const nextStep = typeof parsed.nextStep === "string" && parsed.nextStep.trim() ? parsed.nextStep : void 0;
	const decisions = Array.isArray(parsed.decisions) ? parsed.decisions.filter((d) => typeof d === "string") : [];
	const followups = Array.isArray(parsed.followups) ? parsed.followups.filter((f) => typeof f === "string") : [];
	const topics = Array.isArray(parsed.topics) ? parsed.topics.filter((t) => typeof t === "string") : [];
	const participants = Array.isArray(parsed.participants) ? parsed.participants.filter((p) => typeof p === "string") : ["user"];
	const topicSlug = typeof parsed.topicSlug === "string" ? normalizeLowercaseStringOrEmpty(parsed.topicSlug).replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 30) : topics[0]?.replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").slice(0, 30) ?? "session";
	const VALID_MEMORY_TYPES = [
		"core",
		"active",
		"user",
		"feedback",
		"project",
		"reference"
	];
	const rawMemoryType = typeof parsed.memoryType === "string" ? parsed.memoryType : "";
	const memoryType = VALID_MEMORY_TYPES.includes(rawMemoryType) ? rawMemoryType : void 0;
	const topicDescription = typeof parsed.topicDescription === "string" && parsed.topicDescription.trim() ? parsed.topicDescription.trim() : void 0;
	return {
		summary,
		primaryRequest,
		technicalConcepts: technicalConcepts?.length ? technicalConcepts : void 0,
		filesAndChanges: filesAndChanges?.length ? filesAndChanges : void 0,
		errorsAndFixes: errorsAndFixes?.length ? errorsAndFixes : void 0,
		problemSolving: problemSolving?.length ? problemSolving : void 0,
		currentWork,
		nextStep,
		decisions,
		followups,
		topics,
		participants,
		topicSlug: topicSlug || "session",
		topicDescription,
		memoryType
	};
}
function createFallbackSummary(transcript) {
	const firstLine = transcript.split("\n")[0] ?? "";
	return {
		summary: (firstLine.length > 200 ? `${firstLine.slice(0, 200)}…` : firstLine) || "(session transcript too short to summarize)",
		primaryRequest: void 0,
		technicalConcepts: void 0,
		filesAndChanges: void 0,
		errorsAndFixes: void 0,
		problemSolving: void 0,
		currentWork: void 0,
		nextStep: void 0,
		decisions: [],
		followups: [],
		topics: [],
		participants: ["user"],
		topicSlug: "session",
		topicDescription: void 0
	};
}
/**
* Generate a structured summary from a conversation transcript using LLM.
* Falls back to a minimal raw structure on any failure (timeout, parse error,
* network error, etc.) so that no session data is lost.
*/
async function generateStructuredSummary(params) {
	const { transcript, cfg, existingTopics } = params;
	if (!transcript.trim()) return createFallbackSummary(transcript);
	try {
		const transcriptSlice = transcript;
		log$1.debug("Generating structured summary", { transcriptLength: transcript.length });
		const generateText = await createBackgroundGenerateText(cfg, {
			maxTokens: 4e3,
			timeout: 6e4
		});
		let prompt = `${SUMMARY_SYSTEM_PROMPT}\n\nConversation transcript:\n${transcriptSlice}`;
		if (existingTopics && existingTopics.length > 0) {
			const topicList = existingTopics.map((t) => `- "${t.name}": ${t.description}`).join("\n");
			prompt += `\n\n## Existing Topics\nThe following topics already exist. Route to one of these if the content fits:\n\n${topicList}\n\nChoose the topicSlug from the list above if the conversation fits. Only create a new slug if the content genuinely doesn't fit any existing topic.`;
			if (existingTopics.length >= 30) prompt += "\n\nNote: There are many existing topics. Strongly prefer routing to an existing topic.";
		}
		const rawResponse = await generateText(prompt);
		log$1.debug("LLM response received", { responseLength: rawResponse.length });
		const summary = parseStructuredSummaryResponse(rawResponse);
		if (summary) return summary;
		log$1.debug("LLM response could not be parsed, using fallback", { responseLength: rawResponse.length });
		return createFallbackSummary(transcript);
	} catch (err) {
		const message = err instanceof Error ? err.message : String(err);
		log$1.error(`Failed to generate structured summary: ${message}`);
		return createFallbackSummary(transcript);
	}
}
function formatSummaryAsMarkdown(summary, dateStr, sessionKey, sessionPointer) {
	const sections = [[
		"---",
		`type: session-summary`,
		`date: ${dateStr}`,
		`topics: [${summary.topics.join(", ")}]`,
		`participants: [${summary.participants.join(", ")}]`,
		"---",
		""
	].join("\n")];
	if (sessionKey) sections.push(`- **Session Key**: ${sessionKey}`, "");
	if (sessionPointer) sections.push(`- **Session ID**: ${sessionPointer.sessionId}`, `- **会话目录**: ${sessionPointer.sessionsDir}`, "");
	sections.push("## 摘要", "", summary.summary, "");
	if (summary.primaryRequest) sections.push("## 核心请求", "", summary.primaryRequest, "");
	if (summary.technicalConcepts && summary.technicalConcepts.length > 0) {
		sections.push("## 技术概念");
		for (const c of summary.technicalConcepts) sections.push(`- ${c}`);
		sections.push("");
	}
	if (summary.filesAndChanges && summary.filesAndChanges.length > 0) {
		sections.push("## 文件与变更");
		for (const f of summary.filesAndChanges) sections.push(`- ${f}`);
		sections.push("");
	}
	if (summary.errorsAndFixes && summary.errorsAndFixes.length > 0) {
		sections.push("## 错误与修复");
		for (const e of summary.errorsAndFixes) sections.push(`- ${e}`);
		sections.push("");
	}
	if (summary.problemSolving && summary.problemSolving.length > 0) {
		sections.push("## 问题解决");
		for (const p of summary.problemSolving) sections.push(`- ${p}`);
		sections.push("");
	}
	if (summary.decisions.length > 0) {
		sections.push("## 关键决策");
		for (const d of summary.decisions) sections.push(`- ${d}`);
		sections.push("");
	}
	if (summary.followups.length > 0) {
		sections.push("## 待跟进");
		for (const f of summary.followups) sections.push(`- [ ] ${f}`);
		sections.push("");
	}
	if (summary.currentWork) sections.push("## 当前工作", "", summary.currentWork, "");
	if (summary.nextStep) sections.push("## 下一步", "", summary.nextStep, "");
	if (summary.topicSlug) sections.push(`## 详细记录 → memory/topics/${summary.topicSlug}.md`, "");
	return sections.join("\n");
}
//#endregion
//#region src/shared/local-date.ts
/**
* Local date/time formatting utilities.
*
* All KaijiBot user-visible timestamps (filenames, frontmatter, topic dates)
* MUST use these functions instead of `toISOString()` to respect the runtime
* timezone (Asia/Shanghai in production).
*/
/** Returns local date as `YYYY-MM-DD`. */
function localDateStr(date = /* @__PURE__ */ new Date()) {
	return `${date.getFullYear().toString()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
}
/** Returns local time as `HHMM` (4 digits). */
function localTimeStr(date = /* @__PURE__ */ new Date()) {
	return `${date.getHours().toString().padStart(2, "0")}${date.getMinutes().toString().padStart(2, "0")}`;
}
/** Returns local datetime as `YYYY-MM-DDTHH:MM:00`. */
function localDateTimeStr(date = /* @__PURE__ */ new Date()) {
	return `${localDateStr(date)}T${localTimeStr(date).slice(0, 2)}:${localTimeStr(date).slice(2, 4)}:00`;
}
//#endregion
//#region src/hooks/bundled/session-memory/handler.ts
/**
* Session memory hook handler
*
* Saves structured session summaries to memory when /new or /reset command
* is triggered. Creates daily memory files and routes topics to topic files.
*/
const log = createSubsystemLogger("hooks/session-memory");
const MESSAGE_CAP = 500;
const MEMORY_TYPE_TO_SECTION = {
	core: "⚡ Core Memory",
	active: "🔥 Active Context",
	user: "⚡ Core Memory",
	feedback: "⚡ Core Memory",
	project: "🔥 Active Context",
	reference: "⚡ Core Memory"
};
function resolveDisplaySessionKey(params) {
	if (!params.cfg || !params.workspaceDir) return params.sessionKey;
	const workspaceAgentId = resolveAgentIdByWorkspacePath(params.cfg, params.workspaceDir);
	const parsed = parseAgentSessionKey(params.sessionKey);
	if (!workspaceAgentId || !parsed || workspaceAgentId === parsed.agentId) return params.sessionKey;
	return toAgentStoreSessionKey({
		agentId: workspaceAgentId,
		requestKey: parsed.rest
	});
}
function createNodeFsAdapter() {
	return {
		readFile: (p) => fs.readFile(p, "utf-8"),
		writeFile: (p, data) => fs.writeFile(p, data, "utf-8"),
		mkdir: async (p, opts) => {
			await fs.mkdir(p, opts);
		},
		readdir: (p) => fs.readdir(p),
		stat: async (p) => {
			const s = await fs.stat(p);
			return {
				mtimeMs: s.mtimeMs,
				size: s.size
			};
		},
		rename: (oldPath, newPath) => fs.rename(oldPath, newPath),
		unlink: (p) => fs.unlink(p)
	};
}
const saveSessionToMemory = async (event) => {
	const isResetCommand = event.action === "new" || event.action === "reset";
	const isCompaction = event.type === "compaction" && event.action === "after";
	if (!(event.type === "command" && isResetCommand) && !isCompaction) return;
	if (isHeartbeatSessionKey(event.sessionKey)) {
		log.debug("Skipping heartbeat session", { sessionKey: event.sessionKey });
		return;
	}
	try {
		log.debug("Hook triggered for reset/new command", { action: event.action });
		const context = event.context || {};
		const cfg = context.cfg;
		const contextWorkspaceDir = typeof context.workspaceDir === "string" && context.workspaceDir.trim().length > 0 ? context.workspaceDir : void 0;
		const agentId = resolveAgentIdFromSessionKey(event.sessionKey);
		const workspaceDir = contextWorkspaceDir || (cfg ? resolveAgentWorkspaceDir(cfg, agentId) : path.join(resolveStateDir(process.env, os.homedir), "workspace"));
		const displaySessionKey = resolveDisplaySessionKey({
			cfg,
			workspaceDir: contextWorkspaceDir,
			sessionKey: event.sessionKey
		});
		const memoryDir = path.join(workspaceDir, "memory");
		await fs.mkdir(memoryDir, { recursive: true });
		const now = new Date(event.timestamp);
		const dateStr = localDateStr(now);
		const sessionEntry = context.previousSessionEntry || context.sessionEntry || {};
		const currentSessionId = sessionEntry.sessionId;
		let currentSessionFile = sessionEntry.sessionFile || void 0;
		if (!currentSessionFile || currentSessionFile.includes(".reset.")) {
			const sessionsDirs = /* @__PURE__ */ new Set();
			if (currentSessionFile) sessionsDirs.add(path.dirname(currentSessionFile));
			sessionsDirs.add(path.join(workspaceDir, "sessions"));
			for (const sessionsDir of sessionsDirs) {
				const recovered = await findPreviousSessionFile({
					sessionsDir,
					currentSessionFile,
					sessionId: currentSessionId
				});
				if (recovered) {
					currentSessionFile = recovered;
					log.debug("Found previous session file", { file: currentSessionFile });
					break;
				}
			}
		}
		log.debug("Session context resolved", {
			sessionId: currentSessionId,
			sessionFile: currentSessionFile,
			hasCfg: Boolean(cfg)
		});
		const sessionFile = currentSessionFile || void 0;
		const sessionPointer = sessionFile && currentSessionId ? {
			sessionId: currentSessionId,
			sessionsDir: path.dirname(sessionFile)
		} : void 0;
		const hookConfig = resolveHookConfig(cfg, "session-memory");
		const messageCount = typeof hookConfig?.messages === "number" && hookConfig.messages > 0 ? Math.min(hookConfig.messages, MESSAGE_CAP) : MESSAGE_CAP;
		let sessionContent = null;
		let summary;
		if (sessionFile) {
			sessionContent = await getRecentSessionContentWithResetFallback(sessionFile, messageCount);
			log.debug("Session content loaded", {
				length: sessionContent?.length ?? 0,
				messageCount
			});
		}
		const allowLlm = !(process.env.KAIJIBOT_TEST_FAST === "1" || process.env.VITEST === "true" || process.env.VITEST === "1" || false) && hookConfig?.llmSlug !== false;
		if (sessionContent && cfg && allowLlm) {
			try {
				const { createTopicRegistry } = await import("../../extensions/memory-core/index.js");
				const registry = createTopicRegistry({
					workspaceDir,
					fs: createNodeFsAdapter()
				});
				await registry.syncFromDisk();
				const existingTopics = await registry.getDescriptionList();
				summary = await generateStructuredSummary({
					transcript: sessionContent,
					cfg,
					existingTopics
				});
			} catch {
				summary = await generateStructuredSummary({
					transcript: sessionContent,
					cfg
				});
			}
			log.debug("Structured summary generated", { topicSlug: summary.topicSlug });
		} else if (sessionContent) summary = {
			summary: sessionContent.slice(0, 6e3) || "(session)",
			decisions: [],
			followups: [],
			topics: [],
			participants: ["user"],
			topicSlug: "session"
		};
		else {
			const timeStr = localTimeStr(now);
			summary = {
				summary: `(empty session at ${timeStr})`,
				decisions: [],
				followups: [],
				topics: [],
				participants: ["user"],
				topicSlug: `session-${timeStr}`
			};
		}
		const dailyFilename = `${dateStr}.md`;
		await appendFileWithinRoot({
			rootDir: memoryDir,
			relativePath: dailyFilename,
			data: `\n${formatSummaryAsMarkdown(summary, dateStr, displaySessionKey, sessionPointer)}\n`,
			prependNewlineIfNeeded: true,
			mkdir: true
		});
		log.debug("Daily memory file updated", { filename: dailyFilename });
		if (summary.topicSlug && cfg) try {
			const { createTopicManager, MemoryIndexManager } = await import("../../extensions/memory-core/index.js");
			const nodeFs = createNodeFsAdapter();
			const topicManager = createTopicManager({
				workspaceDir,
				fs: nodeFs
			});
			await topicManager.ensureTopicsDir();
			const topicFileName = `${summary.topicSlug}.md`;
			let topic = await topicManager.getTopic(topicFileName);
			if (!topic) topic = await topicManager.createTopic(summary.topicSlug, topicFileName);
			const entryContent = summary.summary;
			const topicEntry = {
				title: `${dateStr} session`,
				date: dateStr,
				content: entryContent,
				importance: summary.decisions.length > 0 ? "high" : "normal",
				source: "session-memory"
			};
			await topicManager.appendEntry(topicFileName, topicEntry);
			log.debug("Topic file updated", { topic: topicFileName });
			const { createTopicRegistry } = await import("../../extensions/memory-core/index.js");
			const registry = createTopicRegistry({
				workspaceDir,
				fs: nodeFs
			});
			const updatedTopic = await topicManager.getTopic(topicFileName);
			await registry.upsertTopic({
				name: summary.topicSlug,
				description: summary.topicDescription ?? summary.summary.slice(0, 100),
				entryCount: updatedTopic?.entries.length ?? 1,
				lastUpdated: dateStr,
				createdAt: updatedTopic?.frontmatter.created ?? dateStr
			});
			const indexManager = new MemoryIndexManager({
				workspaceDir,
				fs: nodeFs
			});
			await indexManager.updateSection({
				subject: summary.topicSlug,
				title: summary.topicSlug,
				topicFile: `memory/topics/${topicFileName}`,
				summary: summary.summary.slice(0, 120)
			});
			if (summary.memoryType) {
				const section = MEMORY_TYPE_TO_SECTION[summary.memoryType];
				if (section) {
					const index = await indexManager.readIndex();
					const inlineSections = index.inlineSections ?? [];
					const inlineLines = [`- ${dateStr}: ${summary.summary.slice(0, 100)}`];
					for (const d of summary.decisions.slice(0, 3)) inlineLines.push(`  - Decision: ${d}`);
					const existingIdx = inlineSections.findIndex((s) => s.section === section);
					if (existingIdx >= 0) inlineSections[existingIdx].lines = [
						"",
						...inlineLines,
						...inlineSections[existingIdx].lines
					];
					else inlineSections.push({
						section,
						lines: ["", ...inlineLines]
					});
					index.inlineSections = inlineSections;
					await indexManager.writeIndex(index);
				}
			}
			await indexManager.rebalanceIndex();
			log.debug("MEMORY.md index updated");
		} catch (topicErr) {
			const msg = topicErr instanceof Error ? topicErr.message : String(topicErr);
			log.error("Failed to update topic files or index", { error: msg });
		}
		if (sessionContent && cfg && allowLlm) try {
			const { hasCorrectionSignals, extractCorrectionsFromTranscript } = await import("../../extractor-Dc5z0hMf.js");
			log.info("correction extraction: path B entry", {
				hasSessionContent: !!sessionContent,
				hasCfg: !!cfg,
				allowLlm,
				sessionKey: event.sessionKey
			});
			if (hasCorrectionSignals(sessionContent)) {
				const userId = resolveCorrectionUserId(event.sessionKey);
				if (userId) {
					const { createBackgroundGenerateText } = await import("../../standalone-generate-zJNy5WSm.js");
					const { CorrectionStore } = await import("../../store-BFGBEnCv.js");
					const { resolveConfigDir } = await import("../../utils-Vxiq-1ZU.js");
					const { parseAgentSessionKey } = await import("../../session-key-BKyj2ksm.js");
					const agentId = parseAgentSessionKey(event.sessionKey)?.agentId ?? "main";
					const generateText = await createBackgroundGenerateText(cfg, { maxTokens: 2e3 });
					const corrections = await extractCorrectionsFromTranscript(sessionContent, generateText);
					if (corrections.length > 0) {
						const corrStore = new CorrectionStore(resolveConfigDir());
						for (const corr of corrections) await corrStore.addOrReinforce(agentId, userId, corr);
						log.info("Correction extraction complete", { count: corrections.length });
					} else log.info("correction extraction: no corrections extracted", {
						userId,
						sessionKey: event.sessionKey
					});
				}
			}
		} catch (corrErr) {
			const msg = corrErr instanceof Error ? corrErr.message : String(corrErr);
			log.warn("Correction extraction skipped", { error: msg });
		}
		const dialogueDir = path.join(memoryDir, "dialogues");
		const stagingDir = path.join(dialogueDir, ".staging");
		const stagingFilename = event.sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_");
		const stagingPath = path.join(stagingDir, `${stagingFilename}.jsonl`);
		if (isCompaction && sessionFile) try {
			await updateDialogueStaging(stagingPath, sessionFile);
			log.info("Dialogue staging updated", { stagingPath: stagingPath.replace(os.homedir(), "~") });
		} catch (stagingErr) {
			const msg = stagingErr instanceof Error ? stagingErr.message : String(stagingErr);
			log.warn("Dialogue staging update skipped", { error: msg });
		}
		if (isResetCommand && sessionFile) try {
			const cleanDialogue = await getDialogueWithStaging(stagingPath, sessionFile);
			if (cleanDialogue && cleanDialogue.trim().length > 0) {
				const dialogueFilename = `${localDateStr(now)}-${localTimeStr(now)}.md`;
				const dialoguePath = path.join(dialogueDir, dialogueFilename);
				await writeTextAtomic(dialoguePath, [
					"---",
					`date: ${localDateTimeStr(now)}`,
					`participants:`,
					...(summary.participants ?? ["user"]).map((p) => `  - ${p}`),
					`messageCount: ${cleanDialogue.split("\n").length}`,
					"---",
					"",
					""
				].join("\n") + cleanDialogue, {
					ensureDirMode: 493,
					appendTrailingNewline: true
				});
				log.info("Dialogue archive saved", {
					path: dialoguePath.replace(os.homedir(), "~"),
					messageCount: cleanDialogue.split("\n").length
				});
			}
			await fs.unlink(stagingPath).catch(() => {});
		} catch (dialogueErr) {
			const msg = dialogueErr instanceof Error ? dialogueErr.message : String(dialogueErr);
			log.warn("Dialogue archive save skipped", { error: msg });
		}
		const relPath = path.join(memoryDir, dailyFilename).replace(os.homedir(), "~");
		log.info(`Session summary saved to ${relPath}`);
	} catch (err) {
		if (err instanceof Error) log.error("Failed to save session memory", {
			errorName: err.name,
			errorMessage: err.message,
			stack: err.stack
		});
		else log.error("Failed to save session memory", { error: String(err) });
	}
};
//#endregion
export { saveSessionToMemory as default };
