import { $n as CommandLane, Dt as setHeartbeatWakeHandler, Et as requestHeartbeatNow, Mn as resolveUserTimezone, On as resolveEffectiveMessagesConfig, R as resolveHeartbeatDeliveryTarget, Xn as enqueueCommandInLane, Zn as getQueueSize, an as emitHeartbeatEvent, ar as isHeartbeatContentEffectivelyEmpty, cn as resolveIndicatorType, hr as peekSystemEvents, in as resolveHeartbeatVisibility, ir as DEFAULT_HEARTBEAT_EVERY, or as resolveHeartbeatPrompt$1, rr as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, sr as stripHeartbeatToken, t as getReplyFromConfig, u as isEmbeddedPiRunActive, z as resolveHeartbeatSenderContext } from "./reply-CZUaueaY.js";
import { p as info, s as defaultRuntime, t as createSubsystemLogger } from "./subsystem-hATE68va.js";
import { n as isRich, r as theme, t as colorize } from "./theme-C5EwWDWE.js";
import { d as resolveKoiIdFromSessionKey, h as toKoiStoreSessionKey, l as normalizeKoiId } from "./session-key-B2OXLVi_.js";
import { t as runCommandWithTimeout } from "./exec-BBWSxSu-.js";
import { n as resolveSKYKOIPackageRootSync, t as resolveSKYKOIPackageRoot } from "./skykoi-root-Dsv6eLbL.js";
import { i as resolveKoiConfig, l as resolveKoiWorkspaceDir, p as DEFAULT_HEARTBEAT_FILENAME, r as resolveDefaultKoiId } from "./koi-scope-BeICsBhH.js";
import { t as isTruthyEnvValue } from "./env-ChCKTuXK.js";
import { _ as parseDurationMs, o as loadConfig } from "./config-BOTdzMIB.js";
import { f as resolvePreferredAccountId, n as listChannelPlugins, t as getChannelPlugin, u as buildChannelAccountBindings } from "./plugins-dW6cc0qG.js";
import { A as resolveKoiMainSessionKey, O as canonicalizeMainSessionAlias, i as loadSessionStore, l as updateSessionStore, u as updateSessionStoreEntry } from "./sessions-DyqGkYWU.js";
import { T as formatErrorMessage } from "./chrome-CTicuLaA.js";
import { o as resolveStorePath } from "./paths-DSzdyYR5.js";
import { W as HEARTBEAT_TOKEN, t as deliverOutboundPayloads } from "./deliver-BU-ss2iZ.js";
import { n as callGateway, t as buildGatewayConnectionDetails } from "./call-By5olg7I.js";
import { n as withProgress } from "./progress-X-YSIEVf.js";
import { t as resolveChannelDefaultAccountId } from "./helpers-BS0OoBpd.js";
import { fileURLToPath } from "node:url";
import fs from "node:fs";
import path from "node:path";
import fs$1 from "node:fs/promises";

//#region src/infra/heartbeat-auditor.ts
/**
* Heartbeat Productivity Auditor
*
* A watchdog layer that runs independently of the koi. It tracks recent
* heartbeat outcomes and detects when the koi is being unproductive ("lazy").
*
* When laziness is detected, it injects a randomized user-style nudge message
* into the next heartbeat prompt, tricking the koi into thinking the user
* told it to be productive.
*
* This is a safety/redundancy layer on top of:
* - Prompt-level enforcement (v2026.3.57)
* - Logic-level tool-call retry (v2026.3.58)
* - NO_REPLY token stripping (v2026.3.59)
*/
const log$1 = createSubsystemLogger("heartbeat/auditor");
const PRODUCTIVITY_NUDGES = [
	"Go find dead code. Search for unused exports, unreferenced functions, orphaned files. Remove them. Every line of dead code is tech debt. Be OCD about it.",
	"Pick a file you haven't looked at in a while and audit it. Are there redundant imports? Unused variables? Functions that do the same thing? Clean it up.",
	"Run a search for TODO and FIXME comments in the codebase. Pick one and actually fix it. Don't just report it exists -- resolve it.",
	"Look for copy-pasted logic -- functions that do nearly the same thing in different files. Refactor them into shared utilities. DRY the codebase.",
	"Check for inconsistent patterns. Are some files using one approach while others use another for the same thing? Standardize it. Consistency matters.",
	"Write edge case tests. Think about what could break: null inputs, empty strings, huge payloads, concurrent calls, timeout scenarios. Write tests that prove it handles them.",
	"Run the test suite and look at coverage gaps. Find an important function with no tests and write comprehensive tests for it. Aim for edge cases, not just happy paths.",
	"Do end-to-end feature testing. Pick a user-facing feature and trace it through the entire code path. Verify every step works. Find the weak links.",
	"Look for error handling gaps. Find try/catch blocks that swallow errors silently, or async functions without proper error propagation. Fix them.",
	"Test failure modes. What happens when a network call times out? When disk is full? When config is malformed? Find a scenario that isn't handled and handle it.",
	"Review the file structure. Are there files in the wrong directory? Modules that have grown too large and should be split? Organize the repo like you're OCD about it.",
	"Look for circular dependencies or tangled imports. Map out the dependency graph mentally and find modules that are too tightly coupled. Plan or execute a refactor.",
	"Check for magic numbers and hardcoded strings scattered through the code. Extract them into named constants or config values. Make the code self-documenting.",
	"Find functions that are too long (50+ lines). Break them into smaller, well-named functions. Each function should do one thing and do it well.",
	"Look at the public API surface of each module. Are there exports that should be internal? Interfaces that are too broad? Tighten the boundaries.",
	"SSH into a fleet node and check logs for errors or warnings. Don't just check if it's running -- look at what it's ACTUALLY doing. Find anomalies.",
	"Review the heartbeat enforcement chain end-to-end. Trace the code from heartbeat trigger through prompt injection, koi execution, response normalization, and delivery. Find any gaps.",
	"Check for resource leaks. Are there event listeners that never get removed? Timers that never get cleared? File handles that stay open? Find and fix them.",
	"Look at error rates and retry logic. Are retries bounded? Do they have backoff? Could any retry loop run forever? Harden the resilience patterns.",
	"Audit the config validation. What happens with unexpected config values? Missing required fields? Type mismatches? Make the config handling bulletproof.",
	"Review your memory files for accuracy. Are there outdated facts? Wrong version numbers? Stale decisions? Be obsessive about keeping memory correct.",
	"Look at the codebase documentation. Are there complex functions without JSDoc? Modules without a header comment explaining their purpose? Document them.",
	"Check if the README, CHANGELOG, or any docs are out of date with the actual code. Sync them. Documentation drift is a real problem.",
	"Profile the hot paths. Are there O(n^2) loops hiding in the codebase? Unnecessary array copies? Repeated JSON.parse calls? Find and optimize them.",
	"Look for unnecessary async operations. Are there awaits that could be Promise.all'd? Synchronous operations wrapped in unnecessary promises? Optimize the async flow.",
	"Check bundle/package size. Are there heavy dependencies that could be replaced with lighter alternatives? Unused dependencies in package.json? Trim the fat.",
	"Do a security audit. Look for user input that isn't sanitized, secrets that might leak into logs, or permissions that are too broad. Fix what you find.",
	"Check for injection vectors. Are there places where user-provided strings are interpolated into commands, queries, or templates without escaping? Harden them.",
	"Review token and credential handling. Are secrets properly redacted in logs? Are API keys stored securely? Is there any path where credentials could be exposed?"
];
const MAX_HISTORY = 20;
const koiStates = /* @__PURE__ */ new Map();
function getState(koiId) {
	let state = koiStates.get(koiId);
	if (!state) {
		state = {
			history: [],
			consecutiveLazy: 0
		};
		koiStates.set(koiId, state);
	}
	return state;
}
/** Heuristic patterns that indicate a lazy/unproductive heartbeat response */
const LAZY_PATTERNS = [
	/nothing\s+(actionable|to\s+do|needs\s+attention|pending|outstanding|new)/i,
	/nothing\s+(else|more)\s+(to|needs)/i,
	/everything.*(stable|quiet|clean|fine|good|healthy|wrapped|done|complete|checks\s+out|operational)/i,
	/no\s+(outstanding|pending|actionable|new|open|urgent)\s+(issues?|tasks?|work|items?|problems?)/i,
	/all\s+(tasks?|work|items?).*(done|complete|finished|closed|resolved)/i,
	/waiting\s+(for|on)\s+(sub-?koi|fleet|results?|completion)/i,
	/workspace\s+(is\s+)?(clean|tidy)/i,
	/quiet\s+(evening|night|period|hours|time)/i,
	/nothing\s+actionable\s+this\s+heartbeat/i,
	/^(platform|fleet|everything|system)\s+(healthy|up|ok|good|stable|running)/i,
	/already\s+(done|complete|deployed|running|live|updated)/i,
	/^(all\s+good|looks\s+good|no\s+issues|all\s+clear|nothing\s+to\s+report)/i,
	/^(verified|confirmed|checked|spot-?checked).{0,50}(healthy|working|passing|good|fine|stable|clean)/i,
	/^sub-?koi\s+(still\s+)?(running|working)/i,
	/approaching\s+(quiet|evening|end)/i,
	/wrapping\s+up|winding\s+down/i,
	/only\s+(open\s+)?task.*pending/i,
	/context\s+at\s+\d+%/i
];
/**
* Patterns that indicate GENUINE productivity (overrides lazy detection).
* If any of these match, the response is NOT lazy regardless of other patterns.
*/
const PRODUCTIVE_OVERRIDES = [
	/^(fixed|created|wrote|built|published|deployed|updated|refactored|deleted|moved|merged|pushed|added|removed|rewrote|implemented)/i,
	/commit(ted)?\s+[0-9a-f]{7}/i,
	/\d+\/\d+\s+(tests?\s+)?pass/i,
	/published?\s+v\d{4}\.\d+\.\d+/i,
	/npm\s+publish/i,
	/installed.*\d+\s+packages/i
];
/**
* Patterns that indicate the koi is ACTIVELY WORKING on something mid-task.
* When detected, the auditor should NOT nudge — let the koi finish.
* This is distinct from "productive" — the koi hasn't finished yet but IS working.
*/
const ACTIVE_TASK_PATTERNS = [
	/tests?\s+(are\s+)?(still\s+)?(running|in\s+progress|executing)/i,
	/waiting\s+(for|on)\s+(test|build|compile|deploy|install)/i,
	/test\s+run\s+(in\s+progress|started|kicked\s+off)/i,
	/still\s+(working\s+on|fixing|implementing|debugging|investigating|refactoring)/i,
	/in\s+progress|mid-?task|continuing\s+(work|from|with|to)/i,
	/let\s+me\s+(finish|continue|complete|wrap\s+up|check\s+(on|the\s+results?))/i,
	/build(ing)?\s+(in\s+progress|running|started)/i,
	/install(ing|ation)?\s+(in\s+progress|running)/i,
	/validat(e|ing)\s+(the\s+)?(fix|change|update|patch)/i,
	/running.*to\s+(verify|validate|confirm|check)/i,
	/checking\s+(if|whether|that)\s+(it|the|this)/i,
	/picked\s+up\s+(from|where)/i,
	/following\s+through/i,
	/follow-?through/i
];
function isActiveTask(preview) {
	if (!preview) return false;
	const trimmed = preview.trim();
	for (const pattern of ACTIVE_TASK_PATTERNS) if (pattern.test(trimmed)) return true;
	return false;
}
function isLazyResponse(preview) {
	if (!preview || preview.length < 10) return true;
	const trimmed = preview.trim();
	for (const pattern of PRODUCTIVE_OVERRIDES) if (pattern.test(trimmed)) return false;
	if (isActiveTask(trimmed)) return false;
	for (const pattern of LAZY_PATTERNS) if (pattern.test(trimmed)) return true;
	if (trimmed.length < 50) return true;
	return false;
}
/**
* Record a heartbeat outcome for auditing.
*/
function recordHeartbeatOutcome(koiId, outcome) {
	const state = getState(koiId);
	state.history.push(outcome);
	if (state.history.length > MAX_HISTORY) state.history.shift();
	const isKoiActiveTask = outcome.activeTask || isActiveTask(outcome.preview);
	const wasLazy = !isKoiActiveTask && (outcome.skipped || !outcome.delivered || !outcome.usedTools || outcome.toolCallCount < 2 || isLazyResponse(outcome.preview));
	if (isKoiActiveTask) {
		if (state.consecutiveLazy > 0) log$1.info("heartbeat outcome: active-task (reset)", {
			koiId,
			previousConsecutive: state.consecutiveLazy,
			preview: outcome.preview?.slice(0, 100)
		});
		state.consecutiveLazy = 0;
	} else if (wasLazy) {
		state.consecutiveLazy++;
		log$1.info("heartbeat outcome: lazy", {
			koiId,
			consecutive: state.consecutiveLazy,
			delivered: outcome.delivered,
			usedTools: outcome.usedTools,
			toolCallCount: outcome.toolCallCount,
			preview: outcome.preview?.slice(0, 100)
		});
	} else {
		if (state.consecutiveLazy > 0) log$1.info("heartbeat outcome: productive (reset)", {
			koiId,
			previousConsecutive: state.consecutiveLazy
		});
		state.consecutiveLazy = 0;
	}
}
/** How many consecutive lazy heartbeats before injecting a nudge.
* Set to 1 so the koi gets nudged immediately after the first unproductive
* heartbeat — no free passes. */
const LAZY_THRESHOLD = 1;
/** Minimum time between nudges (ms) -- prevent spam */
const MIN_NUDGE_INTERVAL_MS = 180 * 1e3;
/**
* Check if the koi needs a productivity nudge and return one if so.
* Called before each heartbeat run to potentially override/augment the prompt.
*
* Returns null if no nudge needed, or a nudge message string to inject.
*/
function checkProductivityNudge(koiId) {
	const state = getState(koiId);
	if (state.consecutiveLazy < LAZY_THRESHOLD) return null;
	const lastOutcome = state.history.length > 0 ? state.history[state.history.length - 1] : null;
	if (lastOutcome && (lastOutcome.activeTask || isActiveTask(lastOutcome.preview))) return null;
	if (state.consecutiveLazy < LAZY_THRESHOLD) return null;
	const now = Date.now();
	if (state.lastNudgeAt && now - state.lastNudgeAt < MIN_NUDGE_INTERVAL_MS) return null;
	let index;
	do
		index = Math.floor(Math.random() * PRODUCTIVITY_NUDGES.length);
	while (index === state.lastNudgeIndex && PRODUCTIVITY_NUDGES.length > 1);
	state.lastNudgeAt = now;
	state.lastNudgeIndex = index;
	const nudge = PRODUCTIVITY_NUDGES[index];
	log$1.info("injecting productivity nudge", {
		koiId,
		consecutiveLazy: state.consecutiveLazy,
		nudgeIndex: index
	});
	state.consecutiveLazy = 0;
	return nudge;
}

//#endregion
//#region src/infra/heartbeat-runner.ts
/**
* @module heartbeat-runner
*
* Periodic heartbeat system that wakes kois on a configurable interval.
* Each heartbeat triggers an LLM turn with the HEARTBEAT.md prompt, then
* delivers the response (or HEARTBEAT_OK) to the configured channel/target.
*
* Supports per-koi intervals, active-hours windows, duplicate suppression,
* exec-event relay, productivity nudges, and quiet-hours skipping.
*
* Key exports: {@link runHeartbeatOnce}, {@link startHeartbeatRunner},
* {@link resolveHeartbeatSummaryForKoi}
*/
/** 24 hours expressed in minutes (for interval validation). */
const MAX_HEARTBEAT_INTERVAL_MINUTES = 1440;
/** 24 hours expressed in milliseconds (for duplicate detection window). */
const DUPLICATE_DETECTION_WINDOW_MS = 1440 * 60 * 1e3;
/** Max characters for heartbeat preview text in diagnostic events. */
const PREVIEW_MAX_CHARS = 200;
const log = createSubsystemLogger("gateway/heartbeat");
let heartbeatsEnabled = (process.env.SKYKOI_HEARTBEATS ?? "").toLowerCase() === "on";
function setHeartbeatsEnabled(enabled) {
	heartbeatsEnabled = enabled;
}
const DEFAULT_HEARTBEAT_TARGET = "last";
const ACTIVE_HOURS_TIME_PATTERN = /^([01]\d|2[0-3]|24):([0-5]\d)$/;
const EXEC_EVENT_PROMPT = "An async command you ran earlier has completed. The result is shown in the system messages above. Please relay the command output to the user in a helpful way. If the command succeeded, share the relevant output. If it failed, explain what went wrong.";
function resolveActiveHoursTimezone(cfg, raw) {
	const trimmed = raw?.trim();
	if (!trimmed || trimmed === "user") return resolveUserTimezone(cfg.kois?.defaults?.userTimezone);
	if (trimmed === "local") return Intl.DateTimeFormat().resolvedOptions().timeZone?.trim() || "UTC";
	try {
		new Intl.DateTimeFormat("en-US", { timeZone: trimmed }).format(/* @__PURE__ */ new Date());
		return trimmed;
	} catch {
		return resolveUserTimezone(cfg.kois?.defaults?.userTimezone);
	}
}
function parseActiveHoursTime(opts, raw) {
	if (!raw || !ACTIVE_HOURS_TIME_PATTERN.test(raw)) return null;
	const [hourStr, minuteStr] = raw.split(":");
	const hour = Number(hourStr);
	const minute = Number(minuteStr);
	if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
	if (hour === 24) {
		if (!opts.allow24 || minute !== 0) return null;
		return MAX_HEARTBEAT_INTERVAL_MINUTES;
	}
	return hour * 60 + minute;
}
function resolveMinutesInTimeZone(nowMs, timeZone) {
	try {
		const parts = new Intl.DateTimeFormat("en-US", {
			timeZone,
			hour: "2-digit",
			minute: "2-digit",
			hourCycle: "h23"
		}).formatToParts(new Date(nowMs));
		const map = {};
		for (const part of parts) if (part.type !== "literal") map[part.type] = part.value;
		const hour = Number(map.hour);
		const minute = Number(map.minute);
		if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
		return hour * 60 + minute;
	} catch {
		return null;
	}
}
function isWithinActiveHours(cfg, heartbeat, nowMs) {
	const active = heartbeat?.activeHours;
	if (!active) return true;
	const startMin = parseActiveHoursTime({ allow24: false }, active.start);
	const endMin = parseActiveHoursTime({ allow24: true }, active.end);
	if (startMin === null || endMin === null) return true;
	if (startMin === endMin) return true;
	const timeZone = resolveActiveHoursTimezone(cfg, active.timezone);
	const currentMin = resolveMinutesInTimeZone(nowMs ?? Date.now(), timeZone);
	if (currentMin === null) return true;
	if (endMin > startMin) return currentMin >= startMin && currentMin < endMin;
	return currentMin >= startMin || currentMin < endMin;
}
function hasExplicitHeartbeatKois(cfg) {
	return (cfg.kois?.list ?? []).some((entry) => Boolean(entry?.heartbeat));
}
/** Whether heartbeat is enabled for a specific koi (checks explicit config and default fallback). */
function isHeartbeatEnabledForKoi(cfg, koiId) {
	const resolvedKoiId = normalizeKoiId(koiId ?? resolveDefaultKoiId(cfg));
	const list = cfg.kois?.list ?? [];
	if (hasExplicitHeartbeatKois(cfg)) return list.some((entry) => Boolean(entry?.heartbeat) && normalizeKoiId(entry?.id) === resolvedKoiId);
	return resolvedKoiId === resolveDefaultKoiId(cfg);
}
function resolveHeartbeatConfig(cfg, koiId) {
	const defaults = cfg.kois?.defaults?.heartbeat;
	if (!koiId) return defaults;
	const overrides = resolveKoiConfig(cfg, koiId)?.heartbeat;
	if (!defaults && !overrides) return overrides;
	return {
		...defaults,
		...overrides
	};
}
/**
* Builds a complete heartbeat summary for a koi, merging defaults with
* per-koi overrides. Used by status/diagnostic endpoints.
*/
function resolveHeartbeatSummaryForKoi(cfg, koiId) {
	const defaults = cfg.kois?.defaults?.heartbeat;
	const overrides = koiId ? resolveKoiConfig(cfg, koiId)?.heartbeat : void 0;
	if (!isHeartbeatEnabledForKoi(cfg, koiId)) return {
		enabled: false,
		every: "disabled",
		everyMs: null,
		prompt: resolveHeartbeatPrompt$1(defaults?.prompt),
		target: defaults?.target ?? DEFAULT_HEARTBEAT_TARGET,
		model: defaults?.model,
		ackMaxChars: Math.max(0, defaults?.ackMaxChars ?? DEFAULT_HEARTBEAT_ACK_MAX_CHARS)
	};
	const merged = defaults || overrides ? {
		...defaults,
		...overrides
	} : void 0;
	return {
		enabled: true,
		every: merged?.every ?? defaults?.every ?? overrides?.every ?? DEFAULT_HEARTBEAT_EVERY,
		everyMs: resolveHeartbeatIntervalMs(cfg, void 0, merged),
		prompt: resolveHeartbeatPrompt$1(merged?.prompt ?? defaults?.prompt ?? overrides?.prompt),
		target: merged?.target ?? defaults?.target ?? overrides?.target ?? DEFAULT_HEARTBEAT_TARGET,
		model: merged?.model ?? defaults?.model ?? overrides?.model,
		ackMaxChars: Math.max(0, merged?.ackMaxChars ?? defaults?.ackMaxChars ?? overrides?.ackMaxChars ?? DEFAULT_HEARTBEAT_ACK_MAX_CHARS)
	};
}
function resolveHeartbeatKois(cfg) {
	const list = cfg.kois?.list ?? [];
	if (hasExplicitHeartbeatKois(cfg)) return list.filter((entry) => entry?.heartbeat).map((entry) => {
		const id = normalizeKoiId(entry.id);
		return {
			koiId: id,
			heartbeat: resolveHeartbeatConfig(cfg, id)
		};
	}).filter((entry) => entry.koiId);
	const fallbackId = resolveDefaultKoiId(cfg);
	return [{
		koiId: fallbackId,
		heartbeat: resolveHeartbeatConfig(cfg, fallbackId)
	}];
}
/** Parses the heartbeat interval from config, returning milliseconds or null if disabled. */
function resolveHeartbeatIntervalMs(cfg, overrideEvery, heartbeat) {
	const raw = overrideEvery ?? heartbeat?.every ?? cfg.kois?.defaults?.heartbeat?.every ?? DEFAULT_HEARTBEAT_EVERY;
	if (!raw) return null;
	const trimmed = String(raw).trim();
	if (!trimmed) return null;
	let ms;
	try {
		ms = parseDurationMs(trimmed, { defaultUnit: "m" });
	} catch {
		return null;
	}
	if (ms <= 0) return null;
	return ms;
}
function resolveHeartbeatPrompt(cfg, heartbeat) {
	return resolveHeartbeatPrompt$1(heartbeat?.prompt ?? cfg.kois?.defaults?.heartbeat?.prompt);
}
function resolveHeartbeatAckMaxChars(cfg, heartbeat) {
	return Math.max(0, heartbeat?.ackMaxChars ?? cfg.kois?.defaults?.heartbeat?.ackMaxChars ?? DEFAULT_HEARTBEAT_ACK_MAX_CHARS);
}
function resolveHeartbeatSession(cfg, koiId, heartbeat) {
	const sessionCfg = cfg.session;
	const scope = sessionCfg?.scope ?? "per-sender";
	const resolvedKoiId = normalizeKoiId(koiId ?? resolveDefaultKoiId(cfg));
	const mainSessionKey = scope === "global" ? "global" : resolveKoiMainSessionKey({
		cfg,
		koiId: resolvedKoiId
	});
	const storeKoiId = scope === "global" ? resolveDefaultKoiId(cfg) : resolvedKoiId;
	const storePath = resolveStorePath(sessionCfg?.store, { koiId: storeKoiId });
	const store = loadSessionStore(storePath);
	const mainEntry = store[mainSessionKey];
	if (scope === "global") return {
		sessionKey: mainSessionKey,
		storePath,
		store,
		entry: mainEntry
	};
	const trimmed = heartbeat?.session?.trim() ?? "";
	if (!trimmed) return {
		sessionKey: mainSessionKey,
		storePath,
		store,
		entry: mainEntry
	};
	const normalized = trimmed.toLowerCase();
	if (normalized === "main" || normalized === "global") return {
		sessionKey: mainSessionKey,
		storePath,
		store,
		entry: mainEntry
	};
	const canonical = canonicalizeMainSessionAlias({
		cfg,
		koiId: resolvedKoiId,
		sessionKey: toKoiStoreSessionKey({
			koiId: resolvedKoiId,
			requestKey: trimmed,
			mainKey: cfg.session?.mainKey
		})
	});
	if (canonical !== "global") {
		if (resolveKoiIdFromSessionKey(canonical) === normalizeKoiId(resolvedKoiId)) return {
			sessionKey: canonical,
			storePath,
			store,
			entry: store[canonical]
		};
	}
	return {
		sessionKey: mainSessionKey,
		storePath,
		store,
		entry: mainEntry
	};
}
function resolveHeartbeatReplyPayload(replyResult) {
	if (!replyResult) return;
	if (!Array.isArray(replyResult)) return replyResult;
	for (let idx = replyResult.length - 1; idx >= 0; idx -= 1) {
		const payload = replyResult[idx];
		if (!payload) continue;
		if (payload.text || payload.mediaUrl || payload.mediaUrls && payload.mediaUrls.length > 0) return payload;
	}
}
function resolveHeartbeatReasoningPayloads(replyResult) {
	return (Array.isArray(replyResult) ? replyResult : replyResult ? [replyResult] : []).filter((payload) => {
		return (typeof payload.text === "string" ? payload.text : "").trimStart().startsWith("Reasoning:");
	});
}
async function restoreHeartbeatUpdatedAt(params) {
	const { storePath, sessionKey, updatedAt } = params;
	if (typeof updatedAt !== "number") return;
	const entry = loadSessionStore(storePath)[sessionKey];
	if (!entry) return;
	const nextUpdatedAt = Math.max(entry.updatedAt ?? 0, updatedAt);
	if (entry.updatedAt === nextUpdatedAt) return;
	await updateSessionStore(storePath, (nextStore) => {
		const nextEntry = nextStore[sessionKey] ?? entry;
		if (!nextEntry) return;
		const resolvedUpdatedAt = Math.max(nextEntry.updatedAt ?? 0, updatedAt);
		if (nextEntry.updatedAt === resolvedUpdatedAt) return;
		nextStore[sessionKey] = {
			...nextEntry,
			updatedAt: resolvedUpdatedAt
		};
	});
}
function normalizeHeartbeatReply(payload, responsePrefix, ackMaxChars) {
	const stripped = stripHeartbeatToken(payload.text, {
		mode: "heartbeat",
		maxAckChars: ackMaxChars
	});
	const hasMedia = Boolean(payload.mediaUrl || (payload.mediaUrls?.length ?? 0) > 0);
	if (stripped.shouldSkip && !hasMedia) return {
		shouldSkip: true,
		text: "",
		hasMedia
	};
	let finalText = stripped.text;
	if (finalText) if (/^\s*NO_REPLY\s*$/i.test(finalText)) {
		if (!hasMedia) return {
			shouldSkip: true,
			text: "",
			hasMedia
		};
		finalText = "";
	} else finalText = finalText.replace(/\bNO_REPLY\b/g, "").trim();
	if (responsePrefix && finalText && !finalText.startsWith(responsePrefix)) finalText = `${responsePrefix} ${finalText}`;
	return {
		shouldSkip: false,
		text: finalText,
		hasMedia
	};
}
/**
* Runs a single heartbeat cycle for one koi: resolves the session, checks
* active hours / queue depth / HEARTBEAT.md content, calls the LLM, normalizes
* the reply, deduplicates, and delivers to the target channel.
*
* @param opts.cfg - Runtime config (defaults to `loadConfig()`)
* @param opts.koiId - Koi to heartbeat (defaults to the default koi)
* @param opts.heartbeat - Per-koi heartbeat config overrides
* @param opts.reason - Why this heartbeat was triggered (e.g. "interval", "exec-event")
* @returns Status indicating whether the heartbeat ran, was skipped, or failed
*/
async function runHeartbeatOnce(opts) {
	const cfg = opts.cfg ?? loadConfig();
	const koiId = normalizeKoiId(opts.koiId ?? resolveDefaultKoiId(cfg));
	const heartbeat = opts.heartbeat ?? resolveHeartbeatConfig(cfg, koiId);
	if (!heartbeatsEnabled) return {
		status: "skipped",
		reason: "disabled"
	};
	if (!isHeartbeatEnabledForKoi(cfg, koiId)) return {
		status: "skipped",
		reason: "disabled"
	};
	if (!resolveHeartbeatIntervalMs(cfg, void 0, heartbeat)) return {
		status: "skipped",
		reason: "disabled"
	};
	const startedAt = opts.deps?.nowMs?.() ?? Date.now();
	if (!isWithinActiveHours(cfg, heartbeat, startedAt)) return {
		status: "skipped",
		reason: "quiet-hours"
	};
	if ((opts.deps?.getQueueSize ?? getQueueSize)(CommandLane.Main) > 0) return {
		status: "skipped",
		reason: "requests-in-flight"
	};
	const isExecEventReason = opts.reason === "exec-event";
	const workspaceDir = resolveKoiWorkspaceDir(cfg, koiId);
	const heartbeatFilePath = path.join(workspaceDir, DEFAULT_HEARTBEAT_FILENAME);
	try {
		if (isHeartbeatContentEffectivelyEmpty(await fs$1.readFile(heartbeatFilePath, "utf-8")) && !isExecEventReason) {
			emitHeartbeatEvent({
				status: "skipped",
				reason: "empty-heartbeat-file",
				durationMs: Date.now() - startedAt
			});
			return {
				status: "skipped",
				reason: "empty-heartbeat-file"
			};
		}
	} catch {}
	const { entry, sessionKey, storePath } = resolveHeartbeatSession(cfg, koiId, heartbeat);
	if (isEmbeddedPiRunActive(sessionKey)) {
		log.info("heartbeat: skipping — embedded PI run active", { sessionKey });
		return {
			status: "skipped",
			reason: "run-in-flight"
		};
	}
	const previousUpdatedAt = entry?.updatedAt;
	const delivery = resolveHeartbeatDeliveryTarget({
		cfg,
		entry,
		heartbeat
	});
	const heartbeatAccountId = heartbeat?.accountId?.trim();
	if (delivery.reason === "unknown-account") log.warn("heartbeat: unknown accountId", {
		accountId: delivery.accountId ?? heartbeatAccountId ?? null,
		target: heartbeat?.target ?? "last"
	});
	else if (heartbeatAccountId) log.info("heartbeat: using explicit accountId", {
		accountId: delivery.accountId ?? heartbeatAccountId,
		target: heartbeat?.target ?? "last",
		channel: delivery.channel
	});
	const visibility = delivery.channel !== "none" ? resolveHeartbeatVisibility({
		cfg,
		channel: delivery.channel,
		accountId: delivery.accountId
	}) : {
		showOk: false,
		showAlerts: true,
		useIndicator: true
	};
	const { sender } = resolveHeartbeatSenderContext({
		cfg,
		entry,
		delivery
	});
	const responsePrefix = resolveEffectiveMessagesConfig(cfg, koiId, {
		channel: delivery.channel !== "none" ? delivery.channel : void 0,
		accountId: delivery.accountId
	}).responsePrefix;
	const hasExecCompletion = (opts.reason === "exec-event" ? peekSystemEvents(sessionKey) : []).some((evt) => evt.includes("Exec finished"));
	const productivityNudge = hasExecCompletion ? null : checkProductivityNudge(koiId);
	const basePrompt = hasExecCompletion ? EXEC_EVENT_PROMPT : resolveHeartbeatPrompt(cfg, heartbeat);
	const ctx = {
		Body: productivityNudge ? `${basePrompt}\n\n---\nUser message: ${productivityNudge}` : basePrompt,
		From: sender,
		To: sender,
		Provider: hasExecCompletion ? "exec-event" : "heartbeat",
		SessionKey: sessionKey
	};
	if (!visibility.showAlerts && !visibility.showOk && !visibility.useIndicator) {
		emitHeartbeatEvent({
			status: "skipped",
			reason: "alerts-disabled",
			durationMs: Date.now() - startedAt,
			channel: delivery.channel !== "none" ? delivery.channel : void 0,
			accountId: delivery.accountId
		});
		return {
			status: "skipped",
			reason: "alerts-disabled"
		};
	}
	const heartbeatOkText = responsePrefix ? `${responsePrefix} ${HEARTBEAT_TOKEN}` : HEARTBEAT_TOKEN;
	const canAttemptHeartbeatOk = Boolean(visibility.showOk && delivery.channel !== "none" && delivery.to);
	const maybeSendHeartbeatOk = async () => {
		if (!canAttemptHeartbeatOk || delivery.channel === "none" || !delivery.to) return false;
		const heartbeatPlugin = getChannelPlugin(delivery.channel);
		if (heartbeatPlugin?.heartbeat?.checkReady) {
			if (!(await heartbeatPlugin.heartbeat.checkReady({
				cfg,
				accountId: delivery.accountId,
				deps: opts.deps
			})).ok) return false;
		}
		await deliverOutboundPayloads({
			cfg,
			channel: delivery.channel,
			to: delivery.to,
			accountId: delivery.accountId,
			payloads: [{ text: heartbeatOkText }],
			deps: opts.deps
		});
		return true;
	};
	try {
		const replyResult = await enqueueCommandInLane(CommandLane.Main, () => getReplyFromConfig(ctx, { isHeartbeat: true }, cfg));
		const replyPayload = resolveHeartbeatReplyPayload(replyResult);
		const reasoningPayloads = heartbeat?.includeReasoning === true ? resolveHeartbeatReasoningPayloads(replyResult).filter((payload) => payload !== replyPayload) : [];
		if (!replyPayload || !replyPayload.text && !replyPayload.mediaUrl && !replyPayload.mediaUrls?.length) {
			await restoreHeartbeatUpdatedAt({
				storePath,
				sessionKey,
				updatedAt: previousUpdatedAt
			});
			const okSent = await maybeSendHeartbeatOk();
			emitHeartbeatEvent({
				status: "ok-empty",
				reason: opts.reason,
				durationMs: Date.now() - startedAt,
				channel: delivery.channel !== "none" ? delivery.channel : void 0,
				accountId: delivery.accountId,
				silent: !okSent,
				indicatorType: visibility.useIndicator ? resolveIndicatorType("ok-empty") : void 0
			});
			recordHeartbeatOutcome(koiId, {
				timestamp: startedAt,
				delivered: false,
				usedTools: false,
				toolCallCount: 0,
				preview: "",
				skipped: false
			});
			return {
				status: "ran",
				durationMs: Date.now() - startedAt
			};
		}
		const normalized = normalizeHeartbeatReply(replyPayload, responsePrefix, resolveHeartbeatAckMaxChars(cfg, heartbeat));
		const execFallbackText = hasExecCompletion && !normalized.text.trim() && replyPayload.text?.trim() ? replyPayload.text.trim() : null;
		if (execFallbackText) {
			normalized.text = execFallbackText;
			normalized.shouldSkip = false;
		}
		const shouldSkipMain = normalized.shouldSkip && !normalized.hasMedia && !hasExecCompletion;
		if (shouldSkipMain && reasoningPayloads.length === 0) {
			await restoreHeartbeatUpdatedAt({
				storePath,
				sessionKey,
				updatedAt: previousUpdatedAt
			});
			const okSent = await maybeSendHeartbeatOk();
			emitHeartbeatEvent({
				status: "ok-token",
				reason: opts.reason,
				durationMs: Date.now() - startedAt,
				channel: delivery.channel !== "none" ? delivery.channel : void 0,
				accountId: delivery.accountId,
				silent: !okSent,
				indicatorType: visibility.useIndicator ? resolveIndicatorType("ok-token") : void 0
			});
			recordHeartbeatOutcome(koiId, {
				timestamp: startedAt,
				delivered: false,
				usedTools: false,
				toolCallCount: 0,
				preview: "HEARTBEAT_OK",
				skipped: false
			});
			return {
				status: "ran",
				durationMs: Date.now() - startedAt
			};
		}
		const mediaUrls = replyPayload.mediaUrls ?? (replyPayload.mediaUrl ? [replyPayload.mediaUrl] : []);
		const prevHeartbeatText = typeof entry?.lastHeartbeatText === "string" ? entry.lastHeartbeatText : "";
		const prevHeartbeatAt = typeof entry?.lastHeartbeatSentAt === "number" ? entry.lastHeartbeatSentAt : void 0;
		if (!shouldSkipMain && !mediaUrls.length && Boolean(prevHeartbeatText.trim()) && normalized.text.trim() === prevHeartbeatText.trim() && typeof prevHeartbeatAt === "number" && startedAt - prevHeartbeatAt < DUPLICATE_DETECTION_WINDOW_MS) {
			await restoreHeartbeatUpdatedAt({
				storePath,
				sessionKey,
				updatedAt: previousUpdatedAt
			});
			emitHeartbeatEvent({
				status: "skipped",
				reason: "duplicate",
				preview: normalized.text.slice(0, PREVIEW_MAX_CHARS),
				durationMs: Date.now() - startedAt,
				hasMedia: false,
				channel: delivery.channel !== "none" ? delivery.channel : void 0,
				accountId: delivery.accountId
			});
			return {
				status: "ran",
				durationMs: Date.now() - startedAt
			};
		}
		const previewText = shouldSkipMain ? reasoningPayloads.map((payload) => payload.text).filter((text) => Boolean(text?.trim())).join("\n") : normalized.text;
		if (delivery.channel === "none" || !delivery.to) {
			emitHeartbeatEvent({
				status: "skipped",
				reason: delivery.reason ?? "no-target",
				preview: previewText?.slice(0, PREVIEW_MAX_CHARS),
				durationMs: Date.now() - startedAt,
				hasMedia: mediaUrls.length > 0,
				accountId: delivery.accountId
			});
			return {
				status: "ran",
				durationMs: Date.now() - startedAt
			};
		}
		if (!visibility.showAlerts) {
			await restoreHeartbeatUpdatedAt({
				storePath,
				sessionKey,
				updatedAt: previousUpdatedAt
			});
			emitHeartbeatEvent({
				status: "skipped",
				reason: "alerts-disabled",
				preview: previewText?.slice(0, PREVIEW_MAX_CHARS),
				durationMs: Date.now() - startedAt,
				channel: delivery.channel,
				hasMedia: mediaUrls.length > 0,
				accountId: delivery.accountId,
				indicatorType: visibility.useIndicator ? resolveIndicatorType("sent") : void 0
			});
			return {
				status: "ran",
				durationMs: Date.now() - startedAt
			};
		}
		const deliveryAccountId = delivery.accountId;
		const heartbeatPlugin = getChannelPlugin(delivery.channel);
		if (heartbeatPlugin?.heartbeat?.checkReady) {
			const readiness = await heartbeatPlugin.heartbeat.checkReady({
				cfg,
				accountId: deliveryAccountId,
				deps: opts.deps
			});
			if (!readiness.ok) {
				emitHeartbeatEvent({
					status: "skipped",
					reason: readiness.reason,
					preview: previewText?.slice(0, PREVIEW_MAX_CHARS),
					durationMs: Date.now() - startedAt,
					hasMedia: mediaUrls.length > 0,
					channel: delivery.channel,
					accountId: delivery.accountId
				});
				log.info("heartbeat: channel not ready", {
					channel: delivery.channel,
					reason: readiness.reason
				});
				return {
					status: "skipped",
					reason: readiness.reason
				};
			}
		}
		await deliverOutboundPayloads({
			cfg,
			channel: delivery.channel,
			to: delivery.to,
			accountId: deliveryAccountId,
			payloads: [...reasoningPayloads, ...shouldSkipMain ? [] : [{
				text: normalized.text,
				mediaUrls
			}]],
			deps: opts.deps
		});
		if (!shouldSkipMain && normalized.text.trim()) await updateSessionStoreEntry({
			storePath,
			sessionKey,
			update: async () => ({
				lastHeartbeatText: normalized.text,
				lastHeartbeatSentAt: startedAt
			})
		});
		emitHeartbeatEvent({
			status: "sent",
			to: delivery.to,
			preview: previewText?.slice(0, PREVIEW_MAX_CHARS),
			durationMs: Date.now() - startedAt,
			hasMedia: mediaUrls.length > 0,
			channel: delivery.channel,
			accountId: delivery.accountId,
			indicatorType: visibility.useIndicator ? resolveIndicatorType("sent") : void 0
		});
		recordHeartbeatOutcome(koiId, {
			timestamp: startedAt,
			delivered: true,
			usedTools: (replyPayload?.heartbeatToolCallCount ?? 0) > 0,
			toolCallCount: replyPayload?.heartbeatToolCallCount ?? 0,
			preview: previewText?.slice(0, PREVIEW_MAX_CHARS) ?? "",
			skipped: false
		});
		return {
			status: "ran",
			durationMs: Date.now() - startedAt
		};
	} catch (err) {
		const reason = formatErrorMessage(err);
		emitHeartbeatEvent({
			status: "failed",
			reason,
			durationMs: Date.now() - startedAt,
			channel: delivery.channel !== "none" ? delivery.channel : void 0,
			accountId: delivery.accountId,
			indicatorType: visibility.useIndicator ? resolveIndicatorType("failed") : void 0
		});
		log.error(`heartbeat failed: ${reason}`, { error: reason });
		return {
			status: "failed",
			reason
		};
	}
}
/**
* Creates and starts a heartbeat runner that schedules periodic heartbeats
* for all configured kois. Responds to config reloads and manual wake
* requests.
*
* @param opts.cfg - Initial config snapshot
* @param opts.abortSignal - Signal to stop the runner and clean up timers
* @returns Handle with `stop()` and `updateConfig()` methods
*/
function startHeartbeatRunner(opts) {
	const runtime = opts.runtime ?? defaultRuntime;
	const runOnce = opts.runOnce ?? runHeartbeatOnce;
	const state = {
		cfg: opts.cfg ?? loadConfig(),
		runtime,
		kois: /* @__PURE__ */ new Map(),
		timer: null,
		stopped: false
	};
	let initialized = false;
	const resolveNextDue = (now, intervalMs, prevState) => {
		if (typeof prevState?.lastRunMs === "number") return prevState.lastRunMs + intervalMs;
		if (prevState && prevState.intervalMs === intervalMs && prevState.nextDueMs > now) return prevState.nextDueMs;
		return now + intervalMs;
	};
	const scheduleNext = () => {
		if (state.stopped) return;
		if (state.timer) {
			clearTimeout(state.timer);
			state.timer = null;
		}
		if (state.kois.size === 0) return;
		const now = Date.now();
		let nextDue = Number.POSITIVE_INFINITY;
		for (const koi of state.kois.values()) if (koi.nextDueMs < nextDue) nextDue = koi.nextDueMs;
		if (!Number.isFinite(nextDue)) return;
		const delay = Math.max(0, nextDue - now);
		state.timer = setTimeout(() => {
			requestHeartbeatNow({
				reason: "interval",
				coalesceMs: 0
			});
		}, delay);
		state.timer.unref?.();
	};
	const updateConfig = (cfg) => {
		if (state.stopped) return;
		const now = Date.now();
		const prevKois = state.kois;
		const prevEnabled = prevKois.size > 0;
		const nextKois = /* @__PURE__ */ new Map();
		const intervals = [];
		for (const koi of resolveHeartbeatKois(cfg)) {
			const intervalMs = resolveHeartbeatIntervalMs(cfg, void 0, koi.heartbeat);
			if (!intervalMs) continue;
			intervals.push(intervalMs);
			const prevState = prevKois.get(koi.koiId);
			const nextDueMs = resolveNextDue(now, intervalMs, prevState);
			nextKois.set(koi.koiId, {
				koiId: koi.koiId,
				heartbeat: koi.heartbeat,
				intervalMs,
				lastRunMs: prevState?.lastRunMs,
				nextDueMs
			});
		}
		state.cfg = cfg;
		state.kois = nextKois;
		const nextEnabled = nextKois.size > 0;
		if (!initialized) {
			if (!nextEnabled) log.info("heartbeat: disabled", { enabled: false });
			else log.info("heartbeat: started", { intervalMs: Math.min(...intervals) });
			initialized = true;
		} else if (prevEnabled !== nextEnabled) if (!nextEnabled) log.info("heartbeat: disabled", { enabled: false });
		else log.info("heartbeat: started", { intervalMs: Math.min(...intervals) });
		scheduleNext();
	};
	const run = async (params) => {
		if (!heartbeatsEnabled) return {
			status: "skipped",
			reason: "disabled"
		};
		if (state.kois.size === 0) return {
			status: "skipped",
			reason: "disabled"
		};
		const reason = params?.reason;
		const isInterval = reason === "interval";
		const startedAt = Date.now();
		const now = startedAt;
		let ran = false;
		for (const koi of state.kois.values()) {
			if (isInterval && now < koi.nextDueMs) continue;
			const res = await runOnce({
				cfg: state.cfg,
				koiId: koi.koiId,
				heartbeat: koi.heartbeat,
				reason,
				deps: { runtime: state.runtime }
			});
			if (res.status === "skipped" && res.reason === "requests-in-flight") return res;
			if (res.status !== "skipped" || res.reason !== "disabled") {
				koi.lastRunMs = now;
				koi.nextDueMs = now + koi.intervalMs;
			}
			if (res.status === "ran") ran = true;
		}
		scheduleNext();
		if (ran) return {
			status: "ran",
			durationMs: Date.now() - startedAt
		};
		return {
			status: "skipped",
			reason: isInterval ? "not-due" : "disabled"
		};
	};
	setHeartbeatWakeHandler(async (params) => run({ reason: params.reason }));
	updateConfig(state.cfg);
	const cleanup = () => {
		state.stopped = true;
		setHeartbeatWakeHandler(null);
		if (state.timer) clearTimeout(state.timer);
		state.timer = null;
	};
	opts.abortSignal?.addEventListener("abort", cleanup, { once: true });
	return {
		stop: cleanup,
		updateConfig
	};
}

//#endregion
//#region src/commands/health.ts
const DEFAULT_TIMEOUT_MS = 1e4;
const debugHealth = (...args) => {
	if (isTruthyEnvValue(process.env.SKYKOI_DEBUG_HEALTH)) console.warn("[health:debug]", ...args);
};
const formatDurationParts = (ms) => {
	if (!Number.isFinite(ms)) return "unknown";
	if (ms < 1e3) return `${Math.max(0, Math.round(ms))}ms`;
	const units = [
		{
			label: "w",
			size: 10080 * 60 * 1e3
		},
		{
			label: "d",
			size: 1440 * 60 * 1e3
		},
		{
			label: "h",
			size: 3600 * 1e3
		},
		{
			label: "m",
			size: 60 * 1e3
		},
		{
			label: "s",
			size: 1e3
		}
	];
	let remaining = Math.max(0, Math.floor(ms));
	const parts = [];
	for (const unit of units) {
		const value = Math.floor(remaining / unit.size);
		if (value > 0) {
			parts.push(`${value}${unit.label}`);
			remaining -= value * unit.size;
		}
	}
	if (parts.length === 0) return "0s";
	return parts.join(" ");
};
const resolveHeartbeatSummary = (cfg, koiId) => resolveHeartbeatSummaryForKoi(cfg, koiId);
const resolveKoiOrder = (cfg) => {
	const defaultKoiId = resolveDefaultKoiId(cfg);
	const entries = Array.isArray(cfg.kois?.list) ? cfg.kois.list : [];
	const seen = /* @__PURE__ */ new Set();
	const ordered = [];
	for (const entry of entries) {
		if (!entry || typeof entry !== "object") continue;
		if (typeof entry.id !== "string" || !entry.id.trim()) continue;
		const id = normalizeKoiId(entry.id);
		if (!id || seen.has(id)) continue;
		seen.add(id);
		ordered.push({
			id,
			name: typeof entry.name === "string" ? entry.name : void 0
		});
	}
	if (!seen.has(defaultKoiId)) ordered.unshift({ id: defaultKoiId });
	if (ordered.length === 0) ordered.push({ id: defaultKoiId });
	return {
		defaultKoiId,
		ordered
	};
};
const buildSessionSummary = (storePath) => {
	const store = loadSessionStore(storePath);
	const sessions = Object.entries(store).filter(([key]) => key !== "global" && key !== "unknown").map(([key, entry]) => ({
		key,
		updatedAt: entry?.updatedAt ?? 0
	})).toSorted((a, b) => b.updatedAt - a.updatedAt);
	const recent = sessions.slice(0, 5).map((s) => ({
		key: s.key,
		updatedAt: s.updatedAt || null,
		age: s.updatedAt ? Date.now() - s.updatedAt : null
	}));
	return {
		path: storePath,
		count: sessions.length,
		recent
	};
};
const isAccountEnabled = (account) => {
	if (!account || typeof account !== "object") return true;
	return account.enabled !== false;
};
const asRecord = (value) => value && typeof value === "object" ? value : null;
const formatProbeLine = (probe, opts = {}) => {
	const record = asRecord(probe);
	if (!record) return null;
	const ok = typeof record.ok === "boolean" ? record.ok : void 0;
	if (ok === void 0) return null;
	const elapsedMs = typeof record.elapsedMs === "number" ? record.elapsedMs : null;
	const status = typeof record.status === "number" ? record.status : null;
	const error = typeof record.error === "string" ? record.error : null;
	const bot = asRecord(record.bot);
	const botUsername = bot && typeof bot.username === "string" ? bot.username : null;
	const webhook = asRecord(record.webhook);
	const webhookUrl = webhook && typeof webhook.url === "string" ? webhook.url : null;
	const usernames = /* @__PURE__ */ new Set();
	if (botUsername) usernames.add(botUsername);
	for (const extra of opts.botUsernames ?? []) if (extra) usernames.add(extra);
	if (ok) {
		let label = "ok";
		if (usernames.size > 0) label += ` (@${Array.from(usernames).join(", @")})`;
		if (elapsedMs != null) label += ` (${elapsedMs}ms)`;
		if (webhookUrl) label += ` - webhook ${webhookUrl}`;
		return label;
	}
	let label = `failed (${status ?? "unknown"})`;
	if (error) label += ` - ${error}`;
	return label;
};
const formatAccountProbeTiming = (summary) => {
	const probe = asRecord(summary.probe);
	if (!probe) return null;
	const elapsedMs = typeof probe.elapsedMs === "number" ? Math.round(probe.elapsedMs) : null;
	const ok = typeof probe.ok === "boolean" ? probe.ok : null;
	if (elapsedMs == null && ok !== true) return null;
	const accountId = summary.accountId || "default";
	const botRecord = asRecord(probe.bot);
	const botUsername = botRecord && typeof botRecord.username === "string" ? botRecord.username : null;
	return `${botUsername ? `@${botUsername}` : accountId}:${accountId}:${elapsedMs != null ? `${elapsedMs}ms` : "ok"}`;
};
const isProbeFailure = (summary) => {
	const probe = asRecord(summary.probe);
	if (!probe) return false;
	return (typeof probe.ok === "boolean" ? probe.ok : null) === false;
};
function styleHealthChannelLine(line) {
	const colon = line.indexOf(":");
	if (colon === -1) return line;
	const label = line.slice(0, colon + 1);
	const detail = line.slice(colon + 1).trimStart();
	const normalized = detail.toLowerCase();
	const applyPrefix = (prefix, color) => `${label} ${color(detail.slice(0, prefix.length))}${detail.slice(prefix.length)}`;
	if (normalized.startsWith("failed")) return applyPrefix("failed", theme.error);
	if (normalized.startsWith("ok")) return applyPrefix("ok", theme.success);
	if (normalized.startsWith("linked")) return applyPrefix("linked", theme.success);
	if (normalized.startsWith("configured")) return applyPrefix("configured", theme.success);
	if (normalized.startsWith("not linked")) return applyPrefix("not linked", theme.warn);
	if (normalized.startsWith("not configured")) return applyPrefix("not configured", theme.muted);
	if (normalized.startsWith("unknown")) return applyPrefix("unknown", theme.warn);
	return line;
}
const formatHealthChannelLines = (summary, opts = {}) => {
	const channels = summary.channels ?? {};
	const channelOrder = summary.channelOrder?.length > 0 ? summary.channelOrder : Object.keys(channels);
	const accountMode = opts.accountMode ?? "default";
	const lines = [];
	for (const channelId of channelOrder) {
		const channelSummary = channels[channelId];
		if (!channelSummary) continue;
		const plugin = getChannelPlugin(channelId);
		const label = summary.channelLabels?.[channelId] ?? plugin?.meta.label ?? channelId;
		const accountSummaries = channelSummary.accounts ?? {};
		const accountIds = opts.accountIdsByChannel?.[channelId];
		const filteredSummaries = accountIds && accountIds.length > 0 ? accountIds.map((accountId) => accountSummaries[accountId]).filter((entry) => Boolean(entry)) : void 0;
		const listSummaries = accountMode === "all" ? Object.values(accountSummaries) : filteredSummaries ?? (channelSummary.accounts ? Object.values(accountSummaries) : []);
		const baseSummary = filteredSummaries && filteredSummaries.length > 0 ? filteredSummaries[0] : channelSummary;
		const botUsernames = listSummaries ? listSummaries.map((account) => {
			const probeRecord = asRecord(account.probe);
			const bot = probeRecord ? asRecord(probeRecord.bot) : null;
			return bot && typeof bot.username === "string" ? bot.username : null;
		}).filter((value) => Boolean(value)) : [];
		const linked = typeof baseSummary.linked === "boolean" ? baseSummary.linked : null;
		if (linked !== null) {
			if (linked) {
				const authAgeMs = typeof baseSummary.authAgeMs === "number" ? baseSummary.authAgeMs : null;
				const authLabel = authAgeMs != null ? ` (auth age ${Math.round(authAgeMs / 6e4)}m)` : "";
				lines.push(`${label}: linked${authLabel}`);
			} else lines.push(`${label}: not linked`);
			continue;
		}
		const configured = typeof baseSummary.configured === "boolean" ? baseSummary.configured : null;
		if (configured === false) {
			lines.push(`${label}: not configured`);
			continue;
		}
		const accountTimings = accountMode === "all" ? listSummaries.map((account) => formatAccountProbeTiming(account)).filter((value) => Boolean(value)) : [];
		const failedSummary = listSummaries.find((summary) => isProbeFailure(summary));
		if (failedSummary) {
			const failureLine = formatProbeLine(failedSummary.probe, { botUsernames });
			if (failureLine) {
				lines.push(`${label}: ${failureLine}`);
				continue;
			}
		}
		if (accountTimings.length > 0) {
			lines.push(`${label}: ok (${accountTimings.join(", ")})`);
			continue;
		}
		const probeLine = formatProbeLine(baseSummary.probe, { botUsernames });
		if (probeLine) {
			lines.push(`${label}: ${probeLine}`);
			continue;
		}
		if (configured === true) {
			lines.push(`${label}: configured`);
			continue;
		}
		lines.push(`${label}: unknown`);
	}
	return lines;
};
async function getHealthSnapshot(params) {
	const timeoutMs = params?.timeoutMs;
	const cfg = loadConfig();
	const { defaultKoiId, ordered } = resolveKoiOrder(cfg);
	const channelBindings = buildChannelAccountBindings(cfg);
	const sessionCache = /* @__PURE__ */ new Map();
	const kois = ordered.map((entry) => {
		const storePath = resolveStorePath(cfg.session?.store, { koiId: entry.id });
		const sessions = sessionCache.get(storePath) ?? buildSessionSummary(storePath);
		sessionCache.set(storePath, sessions);
		return {
			koiId: entry.id,
			name: entry.name,
			isDefault: entry.id === defaultKoiId,
			heartbeat: resolveHeartbeatSummary(cfg, entry.id),
			sessions
		};
	});
	const defaultKoi = kois.find((koi) => koi.isDefault) ?? kois[0];
	const heartbeatSeconds = defaultKoi?.heartbeat.everyMs ? Math.round(defaultKoi.heartbeat.everyMs / 1e3) : 0;
	const sessions = defaultKoi?.sessions ?? buildSessionSummary(resolveStorePath(cfg.session?.store, { koiId: defaultKoiId }));
	const start = Date.now();
	const cappedTimeout = Math.max(1e3, timeoutMs ?? DEFAULT_TIMEOUT_MS);
	const doProbe = params?.probe !== false;
	const channels = {};
	const channelOrder = listChannelPlugins().map((plugin) => plugin.id);
	const channelLabels = {};
	for (const plugin of listChannelPlugins()) {
		channelLabels[plugin.id] = plugin.meta.label ?? plugin.id;
		const accountIds = plugin.config.listAccountIds(cfg);
		const defaultAccountId = resolveChannelDefaultAccountId({
			plugin,
			cfg,
			accountIds
		});
		const boundAccounts = channelBindings.get(plugin.id)?.get(defaultKoiId) ?? [];
		const preferredAccountId = resolvePreferredAccountId({
			accountIds,
			defaultAccountId,
			boundAccounts
		});
		const boundAccountIdsAll = Array.from(new Set(Array.from(channelBindings.get(plugin.id)?.values() ?? []).flatMap((ids) => ids)));
		const accountIdsToProbe = Array.from(new Set([
			preferredAccountId,
			defaultAccountId,
			...accountIds,
			...boundAccountIdsAll
		].filter((value) => value && value.trim())));
		debugHealth("channel", {
			id: plugin.id,
			accountIds,
			defaultAccountId,
			boundAccounts,
			preferredAccountId,
			accountIdsToProbe
		});
		const accountSummaries = {};
		for (const accountId of accountIdsToProbe) {
			const account = plugin.config.resolveAccount(cfg, accountId);
			const enabled = plugin.config.isEnabled ? plugin.config.isEnabled(account, cfg) : isAccountEnabled(account);
			const configured = plugin.config.isConfigured ? await plugin.config.isConfigured(account, cfg) : true;
			let probe;
			let lastProbeAt = null;
			if (enabled && configured && doProbe && plugin.status?.probeAccount) try {
				probe = await plugin.status.probeAccount({
					account,
					timeoutMs: cappedTimeout,
					cfg
				});
				lastProbeAt = Date.now();
			} catch (err) {
				probe = {
					ok: false,
					error: formatErrorMessage(err)
				};
				lastProbeAt = Date.now();
			}
			const probeRecord = probe && typeof probe === "object" ? probe : null;
			const bot = probeRecord && typeof probeRecord.bot === "object" ? probeRecord.bot : null;
			if (bot?.username) debugHealth("probe.bot", {
				channel: plugin.id,
				accountId,
				username: bot.username
			});
			const snapshot = {
				accountId,
				enabled,
				configured
			};
			if (probe !== void 0) snapshot.probe = probe;
			if (lastProbeAt) snapshot.lastProbeAt = lastProbeAt;
			const summary = plugin.status?.buildChannelSummary ? await plugin.status.buildChannelSummary({
				account,
				cfg,
				defaultAccountId: accountId,
				snapshot
			}) : void 0;
			const record = summary && typeof summary === "object" ? summary : {
				accountId,
				configured,
				probe,
				lastProbeAt
			};
			if (record.configured === void 0) record.configured = configured;
			if (record.lastProbeAt === void 0 && lastProbeAt) record.lastProbeAt = lastProbeAt;
			record.accountId = accountId;
			accountSummaries[accountId] = record;
		}
		const fallbackSummary = accountSummaries[preferredAccountId] ?? accountSummaries[defaultAccountId] ?? accountSummaries[accountIdsToProbe[0] ?? preferredAccountId] ?? accountSummaries[Object.keys(accountSummaries)[0]];
		if (fallbackSummary) channels[plugin.id] = {
			...fallbackSummary,
			accounts: accountSummaries
		};
	}
	return {
		ok: true,
		ts: Date.now(),
		durationMs: Date.now() - start,
		channels,
		channelOrder,
		channelLabels,
		heartbeatSeconds,
		defaultKoiId,
		kois,
		sessions: {
			path: sessions.path,
			count: sessions.count,
			recent: sessions.recent
		}
	};
}
async function healthCommand(opts, runtime) {
	const cfg = opts.config ?? loadConfig();
	const summary = await withProgress({
		label: "Checking gateway healthâ€¦",
		indeterminate: true,
		enabled: opts.json !== true
	}, async () => await callGateway({
		method: "health",
		params: opts.verbose ? { probe: true } : void 0,
		timeoutMs: opts.timeoutMs,
		config: cfg
	}));
	if (opts.json) runtime.log(JSON.stringify(summary, null, 2));
	else {
		const debugEnabled = isTruthyEnvValue(process.env.SKYKOI_DEBUG_HEALTH);
		if (opts.verbose) {
			const details = buildGatewayConnectionDetails({ config: cfg });
			runtime.log(info("Gateway connection:"));
			for (const line of details.message.split("\n")) runtime.log(`  ${line}`);
		}
		const localKois = resolveKoiOrder(cfg);
		const defaultKoiId = summary.defaultKoiId ?? localKois.defaultKoiId;
		const kois = Array.isArray(summary.kois) ? summary.kois : [];
		const fallbackKois = localKois.ordered.map((entry) => {
			const storePath = resolveStorePath(cfg.session?.store, { koiId: entry.id });
			return {
				koiId: entry.id,
				name: entry.name,
				isDefault: entry.id === localKois.defaultKoiId,
				heartbeat: resolveHeartbeatSummary(cfg, entry.id),
				sessions: buildSessionSummary(storePath)
			};
		});
		const resolvedKois = kois.length > 0 ? kois : fallbackKois;
		const displayKois = opts.verbose ? resolvedKois : resolvedKois.filter((koi) => koi.koiId === defaultKoiId);
		const channelBindings = buildChannelAccountBindings(cfg);
		if (debugEnabled) {
			runtime.log(info("[debug] local channel accounts"));
			for (const plugin of listChannelPlugins()) {
				const accountIds = plugin.config.listAccountIds(cfg);
				const defaultAccountId = resolveChannelDefaultAccountId({
					plugin,
					cfg,
					accountIds
				});
				runtime.log(`  ${plugin.id}: accounts=${accountIds.join(", ") || "(none)"} default=${defaultAccountId}`);
				for (const accountId of accountIds) {
					const account = plugin.config.resolveAccount(cfg, accountId);
					const record = asRecord(account);
					const tokenSource = record && typeof record.tokenSource === "string" ? record.tokenSource : void 0;
					const configured = plugin.config.isConfigured ? await plugin.config.isConfigured(account, cfg) : true;
					runtime.log(`    - ${accountId}: configured=${configured}${tokenSource ? ` tokenSource=${tokenSource}` : ""}`);
				}
			}
			runtime.log(info("[debug] bindings map"));
			for (const [channelId, byKoi] of channelBindings.entries()) {
				const entries = Array.from(byKoi.entries()).map(([koiId, ids]) => `${koiId}=[${ids.join(", ")}]`);
				runtime.log(`  ${channelId}: ${entries.join(" ")}`);
			}
			runtime.log(info("[debug] gateway channel probes"));
			for (const [channelId, channelSummary] of Object.entries(summary.channels ?? {})) {
				const accounts = channelSummary.accounts ?? {};
				const probes = Object.entries(accounts).map(([accountId, accountSummary]) => {
					const probe = asRecord(accountSummary.probe);
					const bot = probe ? asRecord(probe.bot) : null;
					return `${accountId}=${(bot && typeof bot.username === "string" ? bot.username : null) ?? "(no bot)"}`;
				});
				runtime.log(`  ${channelId}: ${probes.join(", ") || "(none)"}`);
			}
		}
		const channelAccountFallbacks = Object.fromEntries(listChannelPlugins().map((plugin) => {
			const accountIds = plugin.config.listAccountIds(cfg);
			const preferred = resolvePreferredAccountId({
				accountIds,
				defaultAccountId: resolveChannelDefaultAccountId({
					plugin,
					cfg,
					accountIds
				}),
				boundAccounts: channelBindings.get(plugin.id)?.get(defaultKoiId) ?? []
			});
			return [plugin.id, [preferred]];
		}));
		const accountIdsByChannel = (() => {
			const entries = displayKois.length > 0 ? displayKois : resolvedKois;
			const byChannel = {};
			for (const [channelId, byKoi] of channelBindings.entries()) {
				const accountIds = [];
				for (const koi of entries) {
					const ids = byKoi.get(koi.koiId) ?? [];
					for (const id of ids) if (!accountIds.includes(id)) accountIds.push(id);
				}
				if (accountIds.length > 0) byChannel[channelId] = accountIds;
			}
			for (const [channelId, fallbackIds] of Object.entries(channelAccountFallbacks)) if (!byChannel[channelId] || byChannel[channelId].length === 0) byChannel[channelId] = fallbackIds;
			return byChannel;
		})();
		const channelLines = Object.keys(accountIdsByChannel).length > 0 ? formatHealthChannelLines(summary, {
			accountMode: opts.verbose ? "all" : "default",
			accountIdsByChannel
		}) : formatHealthChannelLines(summary, { accountMode: opts.verbose ? "all" : "default" });
		for (const line of channelLines) runtime.log(styleHealthChannelLine(line));
		for (const plugin of listChannelPlugins()) {
			const channelSummary = summary.channels?.[plugin.id];
			if (!channelSummary || channelSummary.linked !== true) continue;
			if (!plugin.status?.logSelfId) continue;
			const boundAccounts = channelBindings.get(plugin.id)?.get(defaultKoiId) ?? [];
			const accountIds = plugin.config.listAccountIds(cfg);
			const accountId = resolvePreferredAccountId({
				accountIds,
				defaultAccountId: resolveChannelDefaultAccountId({
					plugin,
					cfg,
					accountIds
				}),
				boundAccounts
			});
			const account = plugin.config.resolveAccount(cfg, accountId);
			plugin.status.logSelfId({
				account,
				cfg,
				runtime,
				includeChannelPrefix: true
			});
		}
		if (resolvedKois.length > 0) {
			const koiLabels = resolvedKois.map((koi) => koi.isDefault ? `${koi.koiId} (default)` : koi.koiId);
			runtime.log(info(`Kois: ${koiLabels.join(", ")}`));
		}
		const heartbeatParts = displayKois.map((koi) => {
			const everyMs = koi.heartbeat?.everyMs;
			return `${everyMs ? formatDurationParts(everyMs) : "disabled"} (${koi.koiId})`;
		}).filter(Boolean);
		if (heartbeatParts.length > 0) runtime.log(info(`Heartbeat interval: ${heartbeatParts.join(", ")}`));
		if (displayKois.length === 0) {
			runtime.log(info(`Session store: ${summary.sessions.path} (${summary.sessions.count} entries)`));
			if (summary.sessions.recent.length > 0) for (const r of summary.sessions.recent) runtime.log(`- ${r.key} (${r.updatedAt ? `${Math.round((Date.now() - r.updatedAt) / 6e4)}m ago` : "no activity"})`);
		} else for (const koi of displayKois) {
			runtime.log(info(`Session store (${koi.koiId}): ${koi.sessions.path} (${koi.sessions.count} entries)`));
			if (koi.sessions.recent.length > 0) for (const r of koi.sessions.recent) runtime.log(`- ${r.key} (${r.updatedAt ? `${Math.round((Date.now() - r.updatedAt) / 6e4)}m ago` : "no activity"})`);
		}
	}
}

//#endregion
//#region src/infra/control-ui-assets.ts
const CONTROL_UI_DIST_PATH_SEGMENTS = [
	"dist",
	"control-ui",
	"index.html"
];
function resolveControlUiDistIndexPathForRoot(root) {
	return path.join(root, ...CONTROL_UI_DIST_PATH_SEGMENTS);
}
async function resolveControlUiDistIndexHealth(opts = {}) {
	const indexPath = opts.root ? resolveControlUiDistIndexPathForRoot(opts.root) : await resolveControlUiDistIndexPath(opts.argv1 ?? process.argv[1]);
	return {
		indexPath,
		exists: Boolean(indexPath && fs.existsSync(indexPath))
	};
}
function resolveControlUiRepoRoot(argv1 = process.argv[1]) {
	if (!argv1) return null;
	const normalized = path.resolve(argv1);
	const parts = normalized.split(path.sep);
	const srcIndex = parts.lastIndexOf("src");
	if (srcIndex !== -1) {
		const root = parts.slice(0, srcIndex).join(path.sep);
		if (fs.existsSync(path.join(root, "ui", "vite.config.ts"))) return root;
	}
	let dir = path.dirname(normalized);
	for (let i = 0; i < 8; i++) {
		if (fs.existsSync(path.join(dir, "package.json")) && fs.existsSync(path.join(dir, "ui", "vite.config.ts"))) return dir;
		const parent = path.dirname(dir);
		if (parent === dir) break;
		dir = parent;
	}
	return null;
}
async function resolveControlUiDistIndexPath(argv1 = process.argv[1]) {
	if (!argv1) return null;
	const normalized = path.resolve(argv1);
	const distDir = path.dirname(normalized);
	if (path.basename(distDir) === "dist") return path.join(distDir, "control-ui", "index.html");
	const packageRoot = await resolveSKYKOIPackageRoot({ argv1: normalized });
	if (packageRoot) return path.join(packageRoot, "dist", "control-ui", "index.html");
	let dir = path.dirname(normalized);
	for (let i = 0; i < 8; i++) {
		const pkgJsonPath = path.join(dir, "package.json");
		const indexPath = path.join(dir, "dist", "control-ui", "index.html");
		if (fs.existsSync(pkgJsonPath) && fs.existsSync(indexPath)) try {
			const raw = fs.readFileSync(pkgJsonPath, "utf-8");
			if (JSON.parse(raw).name === "skykoi") return indexPath;
		} catch {}
		const parent = path.dirname(dir);
		if (parent === dir) break;
		dir = parent;
	}
	return null;
}
function addCandidate(candidates, value) {
	if (!value) return;
	candidates.add(path.resolve(value));
}
function resolveControlUiRootOverrideSync(rootOverride) {
	const resolved = path.resolve(rootOverride);
	try {
		const stats = fs.statSync(resolved);
		if (stats.isFile()) return path.basename(resolved) === "index.html" ? path.dirname(resolved) : null;
		if (stats.isDirectory()) {
			const indexPath = path.join(resolved, "index.html");
			return fs.existsSync(indexPath) ? resolved : null;
		}
	} catch {
		return null;
	}
	return null;
}
function resolveControlUiRootSync(opts = {}) {
	const candidates = /* @__PURE__ */ new Set();
	const argv1 = opts.argv1 ?? process.argv[1];
	const cwd = opts.cwd ?? process.cwd();
	const moduleDir = opts.moduleUrl ? path.dirname(fileURLToPath(opts.moduleUrl)) : null;
	const argv1Dir = argv1 ? path.dirname(path.resolve(argv1)) : null;
	const execDir = (() => {
		try {
			const execPath = opts.execPath ?? process.execPath;
			return path.dirname(fs.realpathSync(execPath));
		} catch {
			return null;
		}
	})();
	const packageRoot = resolveSKYKOIPackageRootSync({
		argv1,
		moduleUrl: opts.moduleUrl,
		cwd
	});
	addCandidate(candidates, execDir ? path.join(execDir, "control-ui") : null);
	if (moduleDir) {
		addCandidate(candidates, path.join(moduleDir, "control-ui"));
		addCandidate(candidates, path.join(moduleDir, "../control-ui"));
		addCandidate(candidates, path.join(moduleDir, "../../dist/control-ui"));
	}
	if (argv1Dir) {
		addCandidate(candidates, path.join(argv1Dir, "dist", "control-ui"));
		addCandidate(candidates, path.join(argv1Dir, "control-ui"));
	}
	if (packageRoot) addCandidate(candidates, path.join(packageRoot, "dist", "control-ui"));
	addCandidate(candidates, path.join(cwd, "dist", "control-ui"));
	for (const dir of candidates) {
		const indexPath = path.join(dir, "index.html");
		if (fs.existsSync(indexPath)) return dir;
	}
	return null;
}
function summarizeCommandOutput(text) {
	const lines = text.split(/\r?\n/g).map((l) => l.trim()).filter(Boolean);
	if (!lines.length) return;
	const last = lines.at(-1);
	if (!last) return;
	return last.length > 240 ? `${last.slice(0, 239)}…` : last;
}
async function ensureControlUiAssetsBuilt(runtime = defaultRuntime, opts) {
	const health = await resolveControlUiDistIndexHealth({ argv1: process.argv[1] });
	const indexFromDist = health.indexPath;
	if (health.exists) return {
		ok: true,
		built: false
	};
	const repoRoot = resolveControlUiRepoRoot(process.argv[1]);
	if (!repoRoot) return {
		ok: false,
		built: false,
		message: `${indexFromDist ? `Missing Control UI assets at ${indexFromDist}` : "Missing Control UI assets"}. Build them with \`pnpm ui:build\` (auto-installs UI deps).`
	};
	const indexPath = resolveControlUiDistIndexPathForRoot(repoRoot);
	if (fs.existsSync(indexPath)) return {
		ok: true,
		built: false
	};
	const uiScript = path.join(repoRoot, "scripts", "ui.js");
	if (!fs.existsSync(uiScript)) return {
		ok: false,
		built: false,
		message: `Control UI assets missing but ${uiScript} is unavailable.`
	};
	runtime.log("Control UI assets missing; building (ui:build, auto-installs UI deps)…");
	const build = await runCommandWithTimeout([
		process.execPath,
		uiScript,
		"build"
	], {
		cwd: repoRoot,
		timeoutMs: opts?.timeoutMs ?? 10 * 6e4
	});
	if (build.code !== 0) return {
		ok: false,
		built: false,
		message: `Control UI build failed: ${summarizeCommandOutput(build.stderr) ?? `exit ${build.code}`}`
	};
	if (!fs.existsSync(indexPath)) return {
		ok: false,
		built: true,
		message: `Control UI build completed but ${indexPath} is still missing.`
	};
	return {
		ok: true,
		built: true
	};
}

//#endregion
//#region src/commands/health-format.ts
const formatKv = (line, rich) => {
	const idx = line.indexOf(": ");
	if (idx <= 0) return colorize(rich, theme.muted, line);
	const key = line.slice(0, idx);
	const value = line.slice(idx + 2);
	const valueColor = key === "Gateway target" || key === "Config" ? theme.command : key === "Source" ? theme.muted : theme.info;
	return `${colorize(rich, theme.muted, `${key}:`)} ${colorize(rich, valueColor, value)}`;
};
function formatHealthCheckFailure(err, opts = {}) {
	const rich = opts.rich ?? isRich();
	const raw = String(err);
	const message = err instanceof Error ? err.message : raw;
	if (!rich) return `Health check failed: ${raw}`;
	const lines = message.split("\n").map((l) => l.trimEnd()).filter(Boolean);
	const detailsIdx = lines.findIndex((l) => l.startsWith("Gateway target: "));
	const summaryLines = (detailsIdx >= 0 ? lines.slice(0, detailsIdx) : lines).map((l) => l.trim()).filter(Boolean);
	const detailLines = detailsIdx >= 0 ? lines.slice(detailsIdx) : [];
	const summary = summaryLines.length > 0 ? summaryLines.join(" ") : message;
	const out = [`${colorize(rich, theme.error.bold, "Health check failed")}: ${summary}`];
	for (const line of detailLines) out.push(`  ${formatKv(line, rich)}`);
	return out.join("\n");
}

//#endregion
export { resolveControlUiRootOverrideSync as a, getHealthSnapshot as c, runHeartbeatOnce as d, setHeartbeatsEnabled as f, resolveControlUiDistIndexPathForRoot as i, healthCommand as l, ensureControlUiAssetsBuilt as n, resolveControlUiRootSync as o, startHeartbeatRunner as p, resolveControlUiDistIndexHealth as r, formatHealthChannelLines as s, formatHealthCheckFailure as t, resolveHeartbeatSummaryForKoi as u };