import { m as toWireMessages } from "./messages-C_1AmSpk.js";
//#region src/session/file-map.ts
function toMeta(data) {
	return {
		id: data.id,
		agentId: data.agentId,
		projectRoot: data.projectRoot,
		runs: data.runs,
		status: data.status,
		metadata: data.metadata,
		createdAt: data.createdAt,
		updatedAt: data.updatedAt
	};
}
function toData(meta, turns) {
	return {
		id: meta.id,
		agentId: meta.agentId,
		...typeof meta.projectRoot === "string" ? { projectRoot: meta.projectRoot } : {},
		turns,
		runs: meta.runs,
		status: meta.status,
		metadata: meta.metadata,
		createdAt: meta.createdAt,
		updatedAt: meta.updatedAt
	};
}
function parseTurnsJsonl(jsonl) {
	if (!jsonl) return [];
	const turns = [];
	for (const line of jsonl.split("\n")) {
		const trimmed = line.trim();
		if (!trimmed) continue;
		try {
			turns.push(JSON.parse(trimmed));
		} catch {}
	}
	return turns;
}
function serializeTurnsJsonl(turns) {
	if (turns.length === 0) return "";
	return `${turns.map((t) => JSON.stringify(t)).join("\n")}\n`;
}
/**
* Create a single-session `SessionStore` backed by a file-map adapter.
*
* @example
* ```ts
* const session = await createSession({
*   store: createFileMapStore(hostSessionStore),
* })
* ```
*/
function createFileMapStore(adapter, options = {}) {
	const turnsFile = options.turnsFile ?? "turns.jsonl";
	const metaFile = options.metaFile ?? "meta.json";
	let cached = null;
	let hydrated = false;
	async function hydrate() {
		if (hydrated) return;
		const { files } = await adapter.get();
		const metaRaw = files[metaFile];
		if (metaRaw) {
			let meta = null;
			try {
				meta = JSON.parse(metaRaw);
			} catch {
				meta = null;
			}
			if (meta) cached = toData(meta, parseTurnsJsonl(files[turnsFile] ?? ""));
		}
		hydrated = true;
	}
	async function persist(data) {
		const meta = toMeta(data);
		await adapter.save({
			[metaFile]: JSON.stringify(meta, null, 2),
			[turnsFile]: serializeTurnsJsonl(data.turns)
		});
	}
	async function ensureCachedFor(sessionId) {
		await hydrate();
		if (cached) return cached.id === sessionId;
		const now = Date.now();
		cached = {
			id: sessionId,
			turns: [],
			runs: [],
			status: "idle",
			metadata: {},
			createdAt: now,
			updatedAt: now
		};
		hydrated = true;
		return true;
	}
	return {
		async load(sessionId) {
			await hydrate();
			if (!cached || cached.id !== sessionId) return null;
			return structuredClone(cached);
		},
		async save(data) {
			cached = structuredClone(data);
			hydrated = true;
			await persist(cached);
		},
		async delete(sessionId) {
			await hydrate();
			if (cached && cached.id !== sessionId) return;
			cached = null;
			await adapter.delete();
		},
		async list(filter) {
			await hydrate();
			if (!cached) return [];
			if (filter?.agentId && cached.agentId !== filter.agentId) return [];
			if (filter && "projectRoot" in filter) {
				const v = filter.projectRoot;
				if (v === null && cached.projectRoot != null) return [];
				if (typeof v === "string" && cached.projectRoot !== v) return [];
			}
			return [cached.id];
		},
		async appendTurns(sessionId, turns) {
			if (!await ensureCachedFor(sessionId)) return;
			cached.turns.push(...structuredClone(turns));
			cached.updatedAt = Date.now();
			await persist(cached);
		},
		async getTurns(sessionId, from = 0, limit) {
			await hydrate();
			if (!cached || cached.id !== sessionId) return [];
			const slice = cached.turns.slice(from, limit !== void 0 ? from + limit : void 0);
			return structuredClone(slice);
		},
		async updateRun(sessionId, run) {
			if (!await ensureCachedFor(sessionId)) return;
			const idx = cached.runs.findIndex((r) => r.id === run.id);
			if (idx >= 0) cached.runs[idx] = structuredClone(run);
			else cached.runs.push(structuredClone(run));
			cached.updatedAt = Date.now();
			await persist(cached);
		},
		async updateStatus(sessionId, status) {
			if (!await ensureCachedFor(sessionId)) return;
			cached.status = status;
			cached.updatedAt = Date.now();
			await persist(cached);
		}
	};
}
//#endregion
//#region src/session/memory.ts
function createMemoryStore() {
	const sessions = /* @__PURE__ */ new Map();
	function ensureSession(sessionId) {
		let data = sessions.get(sessionId);
		if (!data) {
			const now = Date.now();
			data = {
				id: sessionId,
				turns: [],
				runs: [],
				status: "idle",
				metadata: {},
				createdAt: now,
				updatedAt: now
			};
			sessions.set(sessionId, data);
		}
		return data;
	}
	return {
		async load(sessionId) {
			const data = sessions.get(sessionId);
			return data ? structuredClone(data) : null;
		},
		async save(session) {
			sessions.set(session.id, structuredClone(session));
		},
		async delete(sessionId) {
			sessions.delete(sessionId);
		},
		async list(filter) {
			let ids = Array.from(sessions.keys());
			if (filter?.agentId) ids = ids.filter((id) => sessions.get(id)?.agentId === filter.agentId);
			if (filter && "projectRoot" in filter) {
				const v = filter.projectRoot;
				if (v === null) ids = ids.filter((id) => sessions.get(id)?.projectRoot == null);
				else if (typeof v === "string") ids = ids.filter((id) => sessions.get(id)?.projectRoot === v);
			}
			if (filter?.limit) ids = ids.slice(0, filter.limit);
			return ids;
		},
		async appendTurns(sessionId, turns) {
			const data = ensureSession(sessionId);
			data.turns.push(...structuredClone(turns));
			data.updatedAt = Date.now();
		},
		async getTurns(sessionId, from = 0, limit) {
			const data = sessions.get(sessionId);
			if (!data) return [];
			const sliced = data.turns.slice(from, limit !== void 0 ? from + limit : void 0);
			return structuredClone(sliced);
		},
		async updateRun(sessionId, run) {
			const data = sessions.get(sessionId);
			if (!data) return;
			const idx = data.runs.findIndex((r) => r.id === run.id);
			if (idx >= 0) data.runs[idx] = structuredClone(run);
			else data.runs.push(structuredClone(run));
			data.updatedAt = Date.now();
		},
		async updateStatus(sessionId, status) {
			const data = sessions.get(sessionId);
			if (!data) return;
			data.status = status;
			data.updatedAt = Date.now();
		}
	};
}
//#endregion
//#region src/session/remote.ts
const TRAILING_SLASH = /\/$/;
function createRemoteStore(options) {
	const baseUrl = options.url.replace(TRAILING_SLASH, "");
	const defaultHeaders = {
		"Content-Type": "application/json",
		...options.headers
	};
	async function request(path, init) {
		const url = `${baseUrl}${path}`;
		return await fetch(url, {
			...init,
			headers: {
				...defaultHeaders,
				...init?.headers
			}
		});
	}
	return {
		async load(sessionId) {
			const res = await request(`/sessions/${encodeURIComponent(sessionId)}`);
			if (!res.ok) {
				if (res.status === 404) return null;
				throw new Error(`Remote session load failed: ${res.status} ${res.statusText}`);
			}
			return await res.json();
		},
		async save(session) {
			const res = await request(`/sessions/${encodeURIComponent(session.id)}`, {
				method: "PUT",
				body: JSON.stringify(session)
			});
			if (!res.ok) throw new Error(`Remote session save failed: ${res.status} ${res.statusText}`);
		},
		async delete(sessionId) {
			const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, { method: "DELETE" });
			if (!res.ok && res.status !== 404) throw new Error(`Remote session delete failed: ${res.status} ${res.statusText}`);
		},
		async list(filter) {
			const params = new URLSearchParams();
			if (filter?.agentId) params.set("agentId", filter.agentId);
			if (filter?.limit) params.set("limit", String(filter.limit));
			if (filter && "projectRoot" in filter) {
				const v = filter.projectRoot;
				if (v === null) params.set("projectRoot", "__null__");
				else if (typeof v === "string") params.set("projectRoot", v);
			}
			const query = params.toString();
			const res = await request(query ? `/sessions?${query}` : "/sessions");
			if (!res.ok) throw new Error(`Remote session list failed: ${res.status} ${res.statusText}`);
			return (await res.json()).ids;
		},
		async appendTurns(sessionId, turns) {
			const res = await request(`/sessions/${encodeURIComponent(sessionId)}/turns`, {
				method: "POST",
				body: JSON.stringify(turns)
			});
			if (!res.ok) throw new Error(`Remote appendTurns failed: ${res.status} ${res.statusText}`);
		},
		async getTurns(sessionId, from = 0, limit) {
			const params = new URLSearchParams();
			if (from) params.set("from", String(from));
			if (limit !== void 0) params.set("limit", String(limit));
			const query = params.toString();
			const res = await request(`/sessions/${encodeURIComponent(sessionId)}/turns${query ? `?${query}` : ""}`);
			if (!res.ok) throw new Error(`Remote getTurns failed: ${res.status} ${res.statusText}`);
			return await res.json();
		},
		async updateRun(sessionId, run) {
			const res = await request(`/sessions/${encodeURIComponent(sessionId)}/runs/${encodeURIComponent(run.id)}`, {
				method: "PUT",
				body: JSON.stringify(run)
			});
			if (!res.ok) throw new Error(`Remote updateRun failed: ${res.status} ${res.statusText}`);
		},
		async updateStatus(sessionId, status) {
			const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {
				method: "PATCH",
				body: JSON.stringify({ status })
			});
			if (!res.ok) throw new Error(`Remote updateStatus failed: ${res.status} ${res.statusText}`);
		}
	};
}
//#endregion
//#region src/session/index.ts
/**
* Create a new session.
* Async so stores that generate IDs server-side (e.g. Supabase) can be supported.
*/
async function createSession(options = {}) {
	const store = options.store;
	const now = Date.now();
	let sessionId = options.id;
	if (!sessionId && store?.generateSessionId) sessionId = await store.generateSessionId();
	if (!sessionId) sessionId = generateId();
	const data = options._data ?? {
		id: sessionId,
		agentId: options.agentId,
		...options.projectRoot ? { projectRoot: options.projectRoot } : {},
		turns: [],
		runs: [],
		status: "idle",
		metadata: options.metadata ?? {},
		createdAt: now,
		updatedAt: now
	};
	function touch() {
		data.updatedAt = Date.now();
	}
	function findRun(runId) {
		return data.runs.find((r) => r.id === runId);
	}
	/**
	* Apply per-run usage stats onto a SessionRun. Shared by `completeRun`,
	* `abortRun`, and `errorRun` so the on-disk shape stays consistent across
	* exit paths — historically only `completeRun` filled these in, and
	* aborted/errored runs rendered `0 in / 0 out` on reload regardless of
	* how much was actually consumed.
	*/
	function applyRunStats(run, stats) {
		run.turns = stats.turns;
		run.tokensIn = stats.tokensIn;
		run.tokensOut = stats.tokensOut;
		if (stats.turnUsage) {
			run.turnUsage = stats.turnUsage;
			const total = stats.turnUsage.reduce((acc, t) => ({
				input: acc.input + t.input,
				output: acc.output + t.output,
				cacheCreation: (acc.cacheCreation ?? 0) + (t.cacheCreation ?? 0),
				cacheRead: (acc.cacheRead ?? 0) + (t.cacheRead ?? 0),
				thinking: (acc.thinking ?? 0) + (t.thinking ?? 0)
			}), {
				input: 0,
				output: 0,
				cacheCreation: 0,
				cacheRead: 0,
				thinking: 0
			});
			run.totalUsage = {
				input: total.input,
				output: total.output,
				...total.cacheCreation ? { cacheCreation: total.cacheCreation } : {},
				...total.cacheRead ? { cacheRead: total.cacheRead } : {},
				...total.thinking ? { thinking: total.thinking } : {}
			};
		}
		if (stats.cost !== void 0) run.cost = stats.cost;
	}
	return {
		get id() {
			return data.id;
		},
		get agentId() {
			return data.agentId;
		},
		get projectRoot() {
			return data.projectRoot;
		},
		get turns() {
			return data.turns;
		},
		get isEmpty() {
			return data.turns.length === 0;
		},
		get status() {
			return data.status;
		},
		get runs() {
			return data.runs;
		},
		get metadata() {
			return data.metadata;
		},
		startRun(runId, prompt, extras) {
			data.runs.push({
				id: runId,
				startedAt: Date.now(),
				prompt: prompt ?? "",
				status: "running",
				...extras?.parentRunId ? { parentRunId: extras.parentRunId } : {},
				...typeof extras?.depth === "number" ? { depth: extras.depth } : {}
			});
			touch();
		},
		completeRun(runId, stats) {
			const run = findRun(runId);
			if (run) {
				run.status = "completed";
				run.endedAt = Date.now();
				applyRunStats(run, stats);
			}
			touch();
		},
		abortRun(runId, stats) {
			const run = findRun(runId);
			if (run) {
				run.status = "aborted";
				run.endedAt = Date.now();
				if (stats) applyRunStats(run, stats);
			}
			touch();
		},
		errorRun(runId, error, stats) {
			const run = findRun(runId);
			if (run) {
				run.status = "error";
				run.endedAt = Date.now();
				run.error = error;
				if (stats) applyRunStats(run, stats);
			}
			touch();
		},
		async appendTurns(turns) {
			data.turns.push(...turns);
			touch();
			if (store) await store.appendTurns(data.id, turns);
		},
		setTurns(turns) {
			data.turns = turns;
			touch();
		},
		setRuns(runs) {
			data.runs = runs;
			touch();
		},
		async updateStatus(status) {
			data.status = status;
			touch();
			if (store) await store.updateStatus(data.id, status);
		},
		async updateRun(run) {
			if (store) await store.updateRun(data.id, run);
		},
		generateTurnId() {
			if (store?.generateTurnId) return store.generateTurnId();
			return crypto.randomUUID();
		},
		setMeta(key, value) {
			data.metadata[key] = value;
			touch();
		},
		async save() {
			if (!store) throw new Error("No SessionStore configured. Pass a store to createSession() to enable persistence.");
			await store.save(data);
		},
		toMessages(options) {
			return toWireMessages(data.turns, options);
		},
		toJSON() {
			return structuredClone(data);
		}
	};
}
/**
* Load an existing session from a store.
*/
async function loadSession(store, sessionId) {
	const loaded = await store.load(sessionId);
	if (!loaded) return null;
	return createSession({
		store,
		_data: loaded
	});
}
function generateId() {
	return `ses_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
}
//#endregion
export { createFileMapStore as a, createMemoryStore as i, loadSession as n, createRemoteStore as r, createSession as t };

//# sourceMappingURL=session-CtAWwwkn.js.map