import { DEFAULT_MESSAGES_KEY } from "../constants.js";
import { normalizeAIMessageToolCalls } from "../message-coercion.js";
import { namespaceKey } from "../namespace.js";
//#region src/stream/discovery/namespace-from-history.ts
function getTasks(checkpoint) {
	const tasks = checkpoint.tasks;
	return Array.isArray(tasks) ? tasks : [];
}
/**
* Phase 1 (preferred): map a `task` tool-call id directly to its
* execution namespace via the task's result `ToolMessage`. Unambiguous
* — works even when a step mixes subagent and non-subagent tool calls.
*
* The subgraph checkpoint_ns is `task.name + ":" + task.id` (mirrors
* pregel's `taskCheckpointNamespace`), so we derive it from name+id
* rather than the always-null completed-task checkpoint.
*/
function collectDirectTaskMappings(tasks, targets, out) {
	for (const task of tasks) {
		if (!Array.isArray(task.path) || task.path[0] !== "__pregel_push" || typeof task.id !== "string" || typeof task.name !== "string") continue;
		const resultMessages = task.result?.messages;
		if (!Array.isArray(resultMessages)) continue;
		for (const msg of resultMessages) {
			const m = msg;
			const id = m.tool_call_id;
			if (m.type === "tool" && typeof id === "string" && targets.has(id) && !out.has(id)) out.set(id, `${task.name}:${task.id}`);
		}
	}
}
/**
* Phase 2 (fallback): align still-pending push tasks to the triggering
* AI message's tool calls by Send index (`path[1]`), where `path[1]`
* indexes into the *full* `tool_calls` array. Only push tasks whose
* targeted call is a subagent `task` are mapped, so a sibling
* non-subagent tool call in the same message can't capture a subagent's
* namespace. Applied only to ids Phase 1 could not resolve so a correct
* direct mapping is never overwritten by a positional guess.
*/
function collectPositionalTaskMappings(checkpoint, targets, out, messagesKey) {
	const pushTasks = getTasks(checkpoint).filter((t) => Array.isArray(t.path) && t.path[0] === "__pregel_push" && typeof t.path[1] === "number" && typeof t.id === "string" && typeof t.name === "string");
	if (pushTasks.length === 0) return;
	const msgs = checkpoint.values?.[messagesKey];
	if (!Array.isArray(msgs)) return;
	let toolCalls;
	for (let i = msgs.length - 1; i >= 0; i -= 1) {
		const m = msgs[i];
		if (m.type !== "ai") continue;
		const normalizedToolCalls = normalizeAIMessageToolCalls(m).tool_calls;
		if (Array.isArray(normalizedToolCalls) && normalizedToolCalls.some((tc) => tc.name === "task")) {
			toolCalls = normalizedToolCalls;
			break;
		}
	}
	if (toolCalls == null) return;
	for (const task of pushTasks) {
		const index = task.path[1];
		const tc = toolCalls[index];
		if (tc?.name === "task" && tc.id != null && typeof task.id === "string" && typeof task.name === "string" && targets.has(tc.id) && !out.has(tc.id)) out.set(tc.id, `${task.name}:${task.id}`);
	}
}
/** Build a `getHistory` `before` cursor from a history entry. */
function beforeCursor(entry) {
	const checkpointId = entry.checkpoint?.checkpoint_id;
	if (typeof checkpointId !== "string") return void 0;
	return { configurable: { checkpoint_id: checkpointId } };
}
function applyCollectors(history, targets, out, messagesKey) {
	for (const checkpoint of history) collectDirectTaskMappings(getTasks(checkpoint), targets, out);
	const stillUnmapped = () => [...targets].some((id) => !out.has(id));
	if (stillUnmapped()) for (const checkpoint of history) {
		if (!stillUnmapped()) break;
		collectPositionalTaskMappings(checkpoint, targets, out, messagesKey);
	}
}
/**
* Synchronous subagent namespace mapping over an already-fetched
* history page. Used by {@link resolveSubagentNamespaces} and by the
* controller's hydrate-time bulk seed (which shares a single
* `getHistory` page with subgraph host detection).
*/
function mapSubagentNamespaces(history, toolCallIds, messagesKey = DEFAULT_MESSAGES_KEY) {
	const out = /* @__PURE__ */ new Map();
	const targets = new Set(toolCallIds);
	if (targets.size === 0) return out;
	applyCollectors(history, targets, out, messagesKey);
	return out;
}
/**
* Resolve execution namespaces for the given subagent `task` tool-call
* ids from checkpoint history.
*
* Bounded and O(1) in calls: one `getHistory` page, plus at most one
* `before`-cursor fallback page when the first leaves ids unresolved.
* Never fans out per id.
*
* @returns Map of `toolCallId` → single execution namespace segment
*   (e.g. `tools:<uuid>`). Unresolved ids are omitted.
*/
async function resolveSubagentNamespaces(client, threadId, toolCallIds, opts) {
	const out = /* @__PURE__ */ new Map();
	const targets = new Set(toolCallIds);
	if (targets.size === 0) return out;
	const limit = opts?.limit ?? 20;
	const messagesKey = opts?.messagesKey ?? "messages";
	const signal = opts?.signal;
	const page1 = await getHistoryPage(client, threadId, {
		limit,
		signal
	});
	applyCollectors(page1, targets, out, messagesKey);
	if ([...targets].filter((id) => !out.has(id)).length > 0 && page1.length > 0) {
		const before = beforeCursor(page1[page1.length - 1]);
		if (before != null) applyCollectors(await getHistoryPage(client, threadId, {
			limit,
			before,
			signal
		}), targets, out, messagesKey);
	}
	return out;
}
/**
* Fetch one bounded history page typed as plain records, sidestepping
* the client's `TStateType` generic so the discovery collectors (which
* read raw `values`/`tasks`) get a stable {@link AnyCheckpoint} shape.
*/
function getHistoryPage(client, threadId, options) {
	return client.threads.getHistory(threadId, options);
}
function checkpointNsToSegments(checkpointNs) {
	if (typeof checkpointNs !== "string" || checkpointNs.length === 0) return [];
	return checkpointNs.split("|").filter((segment) => segment.length > 0);
}
function isInternalSegment(segment) {
	return segment.startsWith("tools:") || segment.startsWith("task:");
}
/**
* Identify subgraph host namespaces from checkpoint history, mirroring the
* promotion rule of the live {@link ./subgraphs} `SubgraphDiscovery`: a
* namespace is a host iff it is a *strict ancestor* of a deeper namespace,
* and inner function-node leaves are never promoted on their own.
*
* Two host signals are derived from the checkpoint namespaces observed in
* history (`state` + `task` `checkpoint_ns`):
*
*  - **Interior hosts** — every strict ancestor of an observed namespace.
*    Each segment of a nested `checkpoint_ns` wraps a deeper one, so the
*    parent is always a subgraph (this also recovers a host whose own
*    checkpoint fell outside the fetched page).
*  - **Top-level hosts** — every depth-1 observed namespace. A top-level
*    subgraph is always a host, including the values-only shape supported
*    by `SubgraphDiscovery.#onValuesEvent`, where the host (e.g.
*    `research:<uuid>`) appears with no deeper `research:<uuid>|...` key.
*    The old strict-prefix rule dropped those, so reconnecting waited for
*    SSE replay instead of hydrating the card immediately.
*
* A *deeper* observed namespace's own deepest segment is NOT promoted
* unless it is itself an ancestor of something deeper — otherwise a plain
* inner node (e.g. `worker:<uuid>|inner:<uuid>`) would surface as a
* spurious subgraph card that was never present during live streaming.
*
* Tool/subagent namespaces (`tools:` / `task:`) are excluded — those are
* owned by {@link ./subagents}. A namespace nested under a subgraph (e.g.
* `research:<uuid>|tools:<uuid>`) still promotes its non-internal
* `research:<uuid>` ancestor.
*/
function collectSubgraphHostNamespaces(history) {
	const observed = /* @__PURE__ */ new Map();
	const record = (segments) => {
		if (segments.length === 0) return;
		observed.set(namespaceKey(segments), segments);
	};
	for (const state of history) {
		record(checkpointNsToSegments(state.checkpoint?.checkpoint_ns));
		for (const task of getTasks(state)) record(checkpointNsToSegments(task.checkpoint?.checkpoint_ns));
	}
	const pending = /* @__PURE__ */ new Set();
	if (history.length > 0) for (const task of getTasks(history[0])) {
		const segments = checkpointNsToSegments(task.checkpoint?.checkpoint_ns);
		if (segments.length > 0) pending.add(namespaceKey(segments));
	}
	const candidates = /* @__PURE__ */ new Map();
	for (const segments of observed.values()) {
		for (let depth = 1; depth < segments.length; depth += 1) {
			const slice = segments.slice(0, depth);
			candidates.set(namespaceKey(slice), slice);
		}
		if (segments.length === 1) candidates.set(namespaceKey(segments), segments);
	}
	const hosts = [];
	for (const [key, segments] of candidates) {
		if (segments.some(isInternalSegment)) continue;
		const prefix = key + "\0";
		const running = pending.has(key) || [...pending].some((p) => p.startsWith(prefix));
		hosts.push({
			namespace: segments,
			status: running ? "running" : "complete"
		});
	}
	return hosts;
}
//#endregion
export { collectSubgraphHostNamespaces, getHistoryPage, mapSubagentNamespaces, resolveSubagentNamespaces };

//# sourceMappingURL=namespace-from-history.js.map