import { r as __exportAll } from "./chunk-D_gEzPfs.js";
import { r as setRuntimeKoiOverlay } from "./runtime-koi-overlay-DeKEFkkl.js";
import { promises } from "node:fs";
import path from "node:path";
import os from "node:os";

//#region src/gateway/companions.ts
/**
* @module gateway/companions
*
* Fetches the list of "companion" kois from the SkyKoi platform —
* every koi whose customSettings.sharesInstanceWith points at the
* host koi running this gateway. Companions share the host's EC2
* instance + gateway, but each one has its own identity (name +
* Opus-generated systemPrompt) and its own session participantId.
*
* The runtime uses this list to:
*   1. Provision a per-koi workspace dir (with SOUL.md set to that
*      koi's systemPrompt) so cfg.kois.list can route to it.
*   2. Extend cfg.kois.list at runtime with one KoiConfig per
*      companion so the existing per-koi dispatch in chat.send
*      (resolveSessionKoiId) routes to each companion's persona.
*   3. Translate `@[Name](koi:<id>)` in incoming user messages
*      into a per-koi sessionKey so the matched companion answers
*      that turn instead of the host.
*
* The fetch is best-effort and keeps a stale cache on failure.
*/
var companions_exports = /* @__PURE__ */ __exportAll({
	applyCompanions: () => applyCompanions,
	fetchCompanions: () => fetchCompanions,
	findCompanionById: () => findCompanionById,
	findCompanionByName: () => findCompanionByName,
	getCachedCompanions: () => getCachedCompanions
});
let cache = null;
function getCachedCompanions() {
	return cache;
}
/** Find a companion by its koi id (case-insensitive). */
function findCompanionById(id) {
	if (!cache) return null;
	const norm = id.trim().toLowerCase();
	return cache.companions.find((c) => c.id.toLowerCase() === norm) ?? null;
}
/** Find a companion by its display name (case-insensitive, exact match). */
function findCompanionByName(name) {
	if (!cache) return null;
	const norm = name.trim().toLowerCase();
	return cache.companions.find((c) => c.name.toLowerCase() === norm) ?? null;
}
/** GET /api/koi/{hostKoiId}/companions with bearer auth. */
async function fetchCompanions(args) {
	const url = `${args.platformUrl.replace(/\/$/, "")}/api/koi/${encodeURIComponent(args.hostKoiId)}/companions`;
	try {
		const res = await fetch(url, {
			method: "GET",
			headers: { Authorization: `Bearer ${args.gatewayToken}` }
		});
		if (!res.ok) {
			args.log.warn(`companions: GET failed ${res.status} ${res.statusText}`);
			return null;
		}
		const json = await res.json();
		if (!json || typeof json.hostKoiId !== "string" || !Array.isArray(json.companions)) return null;
		cache = {
			hostKoiId: json.hostKoiId,
			hostName: typeof json.hostName === "string" ? json.hostName : "",
			sessionId: typeof json.sessionId === "string" ? json.sessionId : "",
			companions: json.companions.filter((c) => !!c && typeof c.id === "string" && typeof c.name === "string")
		};
		args.log.info(`companions: ${cache.companions.length} loaded for host ${cache.hostKoiId}`);
		return cache;
	} catch (err) {
		args.log.warn(`companions: fetch threw — ${err?.message ?? err}`);
		return cache;
	}
}
/**
* Materialize each companion as a workspace dir + KoiConfig entry
* inside the live cfg. Idempotent: re-running with the same set is
* a no-op. Dirs land under the host's workspace parent so they
* inherit the same disk volume, but each gets its own SOUL.md
* derived from the platform-supplied systemPrompt.
*/
async function applyCompanions(args) {
	if (!cache) return;
	const parent = path.dirname(args.hostWorkspaceDir) || path.join(os.homedir(), "skykoi");
	const list = Array.isArray(args.cfg.kois?.list) ? [...args.cfg.kois.list] : [];
	const existingIds = new Set(list.map((k) => k.id?.toLowerCase()));
	for (const c of cache.companions) {
		if (c.id.toLowerCase() === args.hostKoiId.toLowerCase()) continue;
		const dir = path.join(parent, `koi-${c.id}`);
		try {
			await promises.mkdir(dir, { recursive: true });
			const soulPath = path.join(dir, "SOUL.md");
			const soulBody = c.systemPrompt && c.systemPrompt.trim() || `# ${c.name}\n\nYou are ${c.name}, a koi participant in a multi-koi chat.`;
			await promises.writeFile(soulPath, soulBody + "\n", "utf8");
		} catch (err) {
			args.log.warn(`companions: workspace setup failed for ${c.id}: ${err?.message ?? err}`);
			continue;
		}
		if (existingIds.has(c.id.toLowerCase())) continue;
		list.push({
			id: c.id,
			name: c.name,
			workspace: dir,
			koiDir: dir,
			identity: { name: c.name }
		});
		existingIds.add(c.id.toLowerCase());
	}
	if (!args.cfg.kois) args.cfg.kois = {};
	args.cfg.kois.list = list;
	const overlayEntries = list.filter((entry) => {
		const id = entry?.id?.trim().toLowerCase();
		return id && id !== args.hostKoiId.toLowerCase();
	});
	setRuntimeKoiOverlay(overlayEntries);
	args.log.info(`companions: cfg.kois.list now has ${list.length} entries (incl. host); overlay published with ${overlayEntries.length} companion(s)`);
}

//#endregion
export { findCompanionByName as a, findCompanionById as i, companions_exports as n, getCachedCompanions as o, fetchCompanions as r, applyCompanions as t };