import { AsyncLocalStorage } from "node:async_hooks";

//#region src/infra/koi-events.ts
/**
* Per-async-flow run identity. When a koi turn begins, the runner wraps
* execution with `runIdStorage.run(runId, ...)` so every tool invocation
* inside that turn can recover *its own* runId via `getCurrentRunId()`.
*
* Why this matters: sub-kois spawn concurrently. The old module-level
* `activeRunId` tracking via lifecycle listeners was last-writer-wins — when
* a child's `phase: "start"` fired, the parent's tools silently started
* reading/writing state under the child's sessionKey. That produced:
*  - `checklist.complete_item` returning "No checklist exists" after a
*    successful `update`
*  - Each sub-koi's canvas overwritten with the parent's state
*  - `history`-push on phase transition never firing (prior title gone)
*
* AsyncLocalStorage follows async/await chains correctly, so a parent's
* tool call and a concurrent child's tool call see different stores.
*/
const runIdStorage = new AsyncLocalStorage();
function runWithRunId(runId, fn) {
	return runIdStorage.run(runId, fn);
}
function getCurrentRunId() {
	return runIdStorage.getStore();
}
const seqByRun = /* @__PURE__ */ new Map();
const listeners = /* @__PURE__ */ new Set();
const runContextById = /* @__PURE__ */ new Map();
function registerKoiRunContext(runId, context) {
	if (!runId) return;
	const existing = runContextById.get(runId);
	if (!existing) {
		runContextById.set(runId, { ...context });
		return;
	}
	if (context.sessionKey && existing.sessionKey !== context.sessionKey) existing.sessionKey = context.sessionKey;
	if (context.verboseLevel && existing.verboseLevel !== context.verboseLevel) existing.verboseLevel = context.verboseLevel;
	if (context.isHeartbeat !== void 0 && existing.isHeartbeat !== context.isHeartbeat) existing.isHeartbeat = context.isHeartbeat;
	if (context.speakerKoiId && existing.speakerKoiId !== context.speakerKoiId) existing.speakerKoiId = context.speakerKoiId;
}
function getKoiRunContext(runId) {
	return runContextById.get(runId);
}
function clearKoiRunContext(runId) {
	runContextById.delete(runId);
}
function emitKoiEvent(event) {
	const nextSeq = (seqByRun.get(event.runId) ?? 0) + 1;
	seqByRun.set(event.runId, nextSeq);
	const context = runContextById.get(event.runId);
	const sessionKey = typeof event.sessionKey === "string" && event.sessionKey.trim() ? event.sessionKey : context?.sessionKey;
	const enriched = {
		...event,
		sessionKey,
		seq: nextSeq,
		ts: Date.now()
	};
	for (const listener of listeners) try {
		listener(enriched);
	} catch {}
}
function onKoiEvent(listener) {
	listeners.add(listener);
	return () => listeners.delete(listener);
}

//#endregion
export { onKoiEvent as a, getKoiRunContext as i, emitKoiEvent as n, registerKoiRunContext as o, getCurrentRunId as r, runWithRunId as s, clearKoiRunContext as t };