import { CACHE_NS_WRITES, CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_NODE_ERROR, CONFIG_KEY_PREVIOUS_STATE, CONFIG_KEY_READ, CONFIG_KEY_RESUME_MAP, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_SEND, CONFIG_KEY_TASK_ID, ERROR, ERROR_SOURCE_NODE, INTERRUPT, NO_WRITES, PREVIOUS, PULL, PUSH, RESERVED, RESUME, RETURN, Send, TAG_HIDDEN, TASKS, _isSend, _isSendInterface } from "../constants.js";
import { EmptyChannelError, InvalidUpdateError, NodeError } from "../errors.js";
import { createCheckpoint, emptyChannels, getOnlyChannels, isDeltaChannel } from "../channels/base.js";
import { XXH3 } from "../hash.js";
import { readChannel, readChannels } from "./io.js";
import { isCall } from "./types.js";
import { getNullChannelVersion } from "./utils/index.js";
import { getRunnableForFunc } from "./call.js";
import { copyCheckpoint, maxChannelVersion, uuid5 } from "@langchain/langgraph-checkpoint";
import { mergeConfigs, patchConfig } from "@langchain/core/runnables";
//#region src/pregel/algo.ts
const increment = (current) => {
	return current !== void 0 ? current + 1 : 1;
};
function triggersNextStep(updatedChannels, triggerToNodes) {
	if (triggerToNodes == null) return false;
	for (const chan of updatedChannels) if (triggerToNodes[chan]) return true;
	return false;
}
function maxChannelMapVersion(channelVersions) {
	let maxVersion;
	for (const chan in channelVersions) {
		if (!Object.prototype.hasOwnProperty.call(channelVersions, chan)) continue;
		if (maxVersion == null) maxVersion = channelVersions[chan];
		else maxVersion = maxChannelVersion(maxVersion, channelVersions[chan]);
	}
	return maxVersion;
}
function shouldInterrupt(checkpoint, interruptNodes, tasks) {
	const nullVersion = getNullChannelVersion(checkpoint.channel_versions);
	const seen = checkpoint.versions_seen["__interrupt__"] ?? {};
	let anyChannelUpdated = false;
	if ((checkpoint.channel_versions["__start__"] ?? nullVersion) > (seen["__start__"] ?? nullVersion)) anyChannelUpdated = true;
	else for (const chan in checkpoint.channel_versions) {
		if (!Object.prototype.hasOwnProperty.call(checkpoint.channel_versions, chan)) continue;
		if (checkpoint.channel_versions[chan] > (seen[chan] ?? nullVersion)) {
			anyChannelUpdated = true;
			break;
		}
	}
	const anyTriggeredNodeInInterruptNodes = tasks.some((task) => interruptNodes === "*" ? !task.config?.tags?.includes(TAG_HIDDEN) : interruptNodes.includes(task.name));
	return anyChannelUpdated && anyTriggeredNodeInInterruptNodes;
}
function _localRead(checkpoint, channels, task, select, fresh = false) {
	let updated = /* @__PURE__ */ new Set();
	if (!Array.isArray(select)) {
		for (const [c] of task.writes) if (c === select) {
			updated = new Set([c]);
			break;
		}
		updated = updated || /* @__PURE__ */ new Set();
	} else updated = new Set(select.filter((c) => task.writes.some(([key, _]) => key === c)));
	let values;
	if (fresh && updated.size > 0) {
		const localChannels = Object.fromEntries(Object.entries(channels).filter(([k, _]) => updated.has(k)));
		const channelsToSnapshot = /* @__PURE__ */ new Set();
		for (const k in localChannels) {
			if (!Object.prototype.hasOwnProperty.call(localChannels, k)) continue;
			const ch = localChannels[k];
			if (isDeltaChannel(ch) && ch.isAvailable()) channelsToSnapshot.add(k);
		}
		const newCheckpoint = createCheckpoint(checkpoint, localChannels, -1, { channelsToSnapshot });
		const newChannels = emptyChannels(localChannels, newCheckpoint);
		_applyWrites(copyCheckpoint(newCheckpoint), newChannels, [task], void 0, void 0);
		values = readChannels({
			...channels,
			...newChannels
		}, select);
	} else values = readChannels(channels, select);
	return values;
}
function _localWrite(commit, processes, writes) {
	for (const [chan, value] of writes) if (["__pregel_push", "__pregel_tasks"].includes(chan) && value != null) {
		if (!_isSend(value)) throw new InvalidUpdateError(`Invalid packet type, expected SendProtocol, got ${JSON.stringify(value)}`);
		if (!(value.node in processes)) throw new InvalidUpdateError(`Invalid node name "${value.node}" in Send packet`);
	}
	commit(writes);
}
const IGNORE = new Set([
	NO_WRITES,
	PUSH,
	RESUME,
	INTERRUPT,
	RETURN,
	ERROR,
	ERROR_SOURCE_NODE
]);
const RESERVED_SET = new Set(RESERVED);
function _applyWrites(checkpoint, channels, tasks, getNextVersion, triggerToNodes) {
	const pathCache = /* @__PURE__ */ new Map();
	for (const task of tasks) pathCache.set(task, task.path?.slice(0, 3) || []);
	tasks.sort((a, b) => {
		const aPath = pathCache.get(a);
		const bPath = pathCache.get(b);
		for (let i = 0; i < Math.min(aPath.length, bPath.length); i += 1) {
			if (aPath[i] < bPath[i]) return -1;
			if (aPath[i] > bPath[i]) return 1;
		}
		return aPath.length - bPath.length;
	});
	const onlyChannels = getOnlyChannels(channels);
	let bumpStep = false;
	const channelsToConsume = /* @__PURE__ */ new Set();
	for (const task of tasks) {
		if (task.triggers.length > 0) bumpStep = true;
		checkpoint.versions_seen[task.name] ??= {};
		for (const chan of task.triggers) {
			if (chan in checkpoint.channel_versions) checkpoint.versions_seen[task.name][chan] = checkpoint.channel_versions[chan];
			if (!RESERVED_SET.has(chan)) channelsToConsume.add(chan);
		}
	}
	let maxVersion = maxChannelMapVersion(checkpoint.channel_versions);
	let usedNewVersion = false;
	for (const chan of channelsToConsume) if (chan in onlyChannels && onlyChannels[chan].consume()) {
		if (getNextVersion !== void 0) {
			checkpoint.channel_versions[chan] = getNextVersion(maxVersion);
			usedNewVersion = true;
		}
	}
	const pendingWritesByChannel = {};
	const pendingWriteTaskIdsByChannel = {};
	for (const task of tasks) {
		const taskId = task.id ?? "";
		for (const [chan, val] of task.writes) if (IGNORE.has(chan)) {} else if (chan in onlyChannels) {
			pendingWritesByChannel[chan] ??= [];
			pendingWritesByChannel[chan].push(val);
			pendingWriteTaskIdsByChannel[chan] ??= [];
			pendingWriteTaskIdsByChannel[chan].push(taskId);
		}
	}
	for (const [chan, vals] of Object.entries(pendingWritesByChannel)) {
		if (vals.length < 2) continue;
		if (onlyChannels[chan]?.lc_graph_name !== "DeltaChannel") continue;
		const taskIds = pendingWriteTaskIdsByChannel[chan];
		const paired = vals.map((val, i) => ({
			val,
			taskId: taskIds[i]
		}));
		paired.sort((a, b) => a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0);
		pendingWritesByChannel[chan] = paired.map((p) => p.val);
	}
	if (maxVersion != null && getNextVersion != null) maxVersion = usedNewVersion ? getNextVersion(maxVersion) : maxVersion;
	const updatedChannels = /* @__PURE__ */ new Set();
	for (const [chan, vals] of Object.entries(pendingWritesByChannel)) if (chan in onlyChannels) {
		const channel = onlyChannels[chan];
		let updated;
		try {
			updated = channel.update(vals);
		} catch (e) {
			if (e.name === InvalidUpdateError.unminifiable_name) {
				const wrappedError = new InvalidUpdateError(`Invalid update for channel "${chan}" with values ${JSON.stringify(vals)}: ${e.message}`);
				wrappedError.lc_error_code = e.lc_error_code;
				throw wrappedError;
			} else throw e;
		}
		if (updated && getNextVersion !== void 0) {
			checkpoint.channel_versions[chan] = getNextVersion(maxVersion);
			if (channel.isAvailable()) updatedChannels.add(chan);
		}
	}
	if (bumpStep) for (const chan in onlyChannels) {
		if (!Object.prototype.hasOwnProperty.call(onlyChannels, chan)) continue;
		const channel = onlyChannels[chan];
		if (channel.isAvailable() && !updatedChannels.has(chan)) {
			if (channel.update([]) && getNextVersion !== void 0) {
				checkpoint.channel_versions[chan] = getNextVersion(maxVersion);
				if (channel.isAvailable()) updatedChannels.add(chan);
			}
		}
	}
	if (bumpStep && !triggersNextStep(updatedChannels, triggerToNodes)) for (const chan in onlyChannels) {
		if (!Object.prototype.hasOwnProperty.call(onlyChannels, chan)) continue;
		const channel = onlyChannels[chan];
		if (channel.finish() && getNextVersion !== void 0) {
			checkpoint.channel_versions[chan] = getNextVersion(maxVersion);
			if (channel.isAvailable()) updatedChannels.add(chan);
		}
	}
	return updatedChannels;
}
function* candidateNodes(checkpoint, processes, extra) {
	if (extra.updatedChannels != null && extra.triggerToNodes != null) {
		const triggeredNodes = /* @__PURE__ */ new Set();
		for (const channel of extra.updatedChannels) {
			const nodeIds = extra.triggerToNodes[channel];
			for (const id of nodeIds ?? []) triggeredNodes.add(id);
		}
		yield* [...triggeredNodes].sort();
		return;
	}
	if ((() => {
		for (const chan in checkpoint.channel_versions) if (checkpoint.channel_versions[chan] !== null) return false;
		return true;
	})()) return;
	for (const name in processes) {
		if (!Object.prototype.hasOwnProperty.call(processes, name)) continue;
		yield name;
	}
}
/**
* Build an index over pendingWrites for O(1) lookups.
*
* @internal Exported for benchmarks and regression tests only.
*/
function _indexPendingWrites(pendingWrites) {
	let nullResume;
	const resumeByTaskId = /* @__PURE__ */ new Map();
	const successfulWriteTaskIds = /* @__PURE__ */ new Set();
	if (pendingWrites) for (const [tid, chan, val] of pendingWrites) {
		if (tid === "00000000-0000-0000-0000-000000000000" && chan === "__resume__" && nullResume === void 0) nullResume = val;
		if (chan === "__resume__" && tid !== "00000000-0000-0000-0000-000000000000") {
			let arr = resumeByTaskId.get(tid);
			if (!arr) {
				arr = [];
				resumeByTaskId.set(tid, arr);
			}
			arr.push(val);
		}
		if (chan !== "__error__") successfulWriteTaskIds.add(tid);
	}
	return {
		nullResume,
		resumeByTaskId,
		successfulWriteTaskIds
	};
}
/**
* Prepare the set of tasks that will make up the next Pregel step.
* This is the union of all PUSH tasks (Sends) and PULL tasks (nodes triggered
* by edges).
*/
function _prepareNextTasks(checkpoint, pendingWrites, processes, channels, config, forExecution, extra) {
	const tasks = {};
	const indexedExtra = extra.pendingWritesIndex ? extra : {
		...extra,
		pendingWritesIndex: _indexPendingWrites(pendingWrites)
	};
	const tasksChannel = channels[TASKS];
	if (tasksChannel?.isAvailable()) {
		const len = tasksChannel.get().length;
		for (let i = 0; i < len; i += 1) {
			const task = _prepareSingleTask([PUSH, i], checkpoint, pendingWrites, processes, channels, config, forExecution, indexedExtra);
			if (task !== void 0) tasks[task.id] = task;
		}
	}
	for (const name of candidateNodes(checkpoint, processes, indexedExtra)) {
		const task = _prepareSingleTask([PULL, name], checkpoint, pendingWrites, processes, channels, config, forExecution, indexedExtra);
		if (task !== void 0) tasks[task.id] = task;
	}
	return tasks;
}
/**
* Prepares a single task for the next Pregel step, given a task path, which
* uniquely identifies a PUSH or PULL task within the graph.
*/
function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, channels, config, forExecution, extra) {
	const { step, checkpointer, manager } = extra;
	const configurable = config.configurable ?? {};
	const parentNamespace = configurable.checkpoint_ns ?? "";
	if (taskPath[0] === "__pregel_push" && isCall(taskPath[taskPath.length - 1])) {
		const call = taskPath[taskPath.length - 1];
		const proc = getRunnableForFunc(call.name, call.func);
		const triggers = [PUSH];
		const checkpointNamespace = parentNamespace === "" ? call.name : `${parentNamespace}|${call.name}`;
		const id = uuid5(JSON.stringify([
			checkpointNamespace,
			step.toString(),
			call.name,
			PUSH,
			taskPath[1],
			taskPath[2]
		]), checkpoint.id);
		const taskCheckpointNamespace = `${checkpointNamespace}:${id}`;
		const outputTaskPath = [...taskPath.slice(0, 3), true];
		const metadata = {
			langgraph_step: step,
			langgraph_node: call.name,
			langgraph_triggers: triggers,
			langgraph_path: outputTaskPath,
			langgraph_checkpoint_ns: taskCheckpointNamespace,
			checkpoint_ns: taskCheckpointNamespace
		};
		if (forExecution) {
			const writes = [];
			const executionInfo = {
				checkpointId: checkpoint.id,
				checkpointNs: taskCheckpointNamespace,
				taskId: id,
				threadId: configurable.thread_id,
				runId: config.runId != null ? String(config.runId) : void 0,
				nodeAttempt: 1
			};
			return {
				name: call.name,
				input: call.input,
				proc,
				writes,
				config: {
					...patchConfig(mergeConfigs(config, {
						metadata,
						store: extra.store ?? config.store
					}), {
						runName: call.name,
						callbacks: manager?.getChild(`graph:step:${step}`),
						configurable: {
							[CONFIG_KEY_TASK_ID]: id,
							[CONFIG_KEY_SEND]: (writes_) => _localWrite((items) => writes.push(...items), processes, writes_),
							[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(checkpoint, channels, {
								name: call.name,
								writes,
								triggers,
								path: outputTaskPath
							}, select_, fresh_),
							[CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable["__pregel_checkpointer"],
							[CONFIG_KEY_CHECKPOINT_MAP]: {
								...configurable[CONFIG_KEY_CHECKPOINT_MAP],
								[parentNamespace]: checkpoint.id
							},
							[CONFIG_KEY_SCRATCHPAD]: _scratchpad({
								pendingWrites: pendingWrites ?? [],
								taskId: id,
								currentTaskInput: call.input,
								resumeMap: config.configurable?.[CONFIG_KEY_RESUME_MAP],
								namespaceHash: XXH3(taskCheckpointNamespace),
								pendingWritesIndex: extra.pendingWritesIndex
							}),
							[CONFIG_KEY_PREVIOUS_STATE]: checkpoint.channel_values[PREVIOUS],
							checkpoint_id: void 0,
							checkpoint_ns: taskCheckpointNamespace
						}
					}),
					executionInfo
				},
				triggers,
				retry_policy: call.retry,
				cache_key: call.cache ? {
					key: XXH3((call.cache.keyFunc ?? JSON.stringify)([call.input])),
					ns: [CACHE_NS_WRITES, call.name ?? "__dynamic__"],
					ttl: call.cache.ttl
				} : void 0,
				id,
				path: outputTaskPath,
				writers: [],
				timeout: call.timeout
			};
		} else return {
			id,
			name: call.name,
			interrupts: [],
			path: outputTaskPath
		};
	} else if (taskPath[0] === "__pregel_push") {
		const index = typeof taskPath[1] === "number" ? taskPath[1] : parseInt(taskPath[1], 10);
		if (!channels["__pregel_tasks"]?.isAvailable()) return;
		const sends = channels[TASKS].get();
		if (index < 0 || index >= sends.length) return;
		const packet = _isSendInterface(sends[index]) && !_isSend(sends[index]) ? new Send(sends[index].node, sends[index].args, sends[index].timeout !== void 0 ? { timeout: sends[index].timeout } : void 0) : sends[index];
		if (!_isSendInterface(packet)) {
			console.warn(`Ignoring invalid packet ${JSON.stringify(packet)} in pending sends.`);
			return;
		}
		if (!(packet.node in processes)) {
			console.warn(`Ignoring unknown node name ${packet.node} in pending sends.`);
			return;
		}
		const triggers = [PUSH];
		const checkpointNamespace = parentNamespace === "" ? packet.node : `${parentNamespace}|${packet.node}`;
		const taskId = uuid5(JSON.stringify([
			checkpointNamespace,
			step.toString(),
			packet.node,
			PUSH,
			index.toString()
		]), checkpoint.id);
		const taskCheckpointNamespace = `${checkpointNamespace}:${taskId}`;
		let metadata = {
			langgraph_step: step,
			langgraph_node: packet.node,
			langgraph_triggers: triggers,
			langgraph_path: taskPath.slice(0, 3),
			langgraph_checkpoint_ns: taskCheckpointNamespace,
			checkpoint_ns: taskCheckpointNamespace
		};
		if (forExecution) {
			const proc = processes[packet.node];
			const node = proc.getNode();
			if (node !== void 0) {
				if (proc.metadata !== void 0) metadata = {
					...metadata,
					...proc.metadata
				};
				const writes = [];
				const executionInfo = {
					checkpointId: checkpoint.id,
					checkpointNs: taskCheckpointNamespace,
					taskId,
					threadId: configurable.thread_id,
					runId: config.runId != null ? String(config.runId) : void 0,
					nodeAttempt: 1
				};
				return {
					name: packet.node,
					input: packet.args,
					proc: node,
					subgraphs: proc.subgraphs,
					writes,
					config: {
						...patchConfig(mergeConfigs(config, {
							metadata,
							tags: proc.tags,
							store: extra.store ?? config.store
						}), {
							runName: packet.node,
							callbacks: manager?.getChild(`graph:step:${step}`),
							configurable: {
								[CONFIG_KEY_TASK_ID]: taskId,
								[CONFIG_KEY_SEND]: (writes_) => _localWrite((items) => writes.push(...items), processes, writes_),
								[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(checkpoint, channels, {
									name: packet.node,
									writes,
									triggers,
									path: taskPath
								}, select_, fresh_),
								[CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable["__pregel_checkpointer"],
								[CONFIG_KEY_CHECKPOINT_MAP]: {
									...configurable[CONFIG_KEY_CHECKPOINT_MAP],
									[parentNamespace]: checkpoint.id
								},
								[CONFIG_KEY_SCRATCHPAD]: _scratchpad({
									pendingWrites: pendingWrites ?? [],
									taskId,
									currentTaskInput: packet.args,
									resumeMap: config.configurable?.[CONFIG_KEY_RESUME_MAP],
									namespaceHash: XXH3(taskCheckpointNamespace),
									pendingWritesIndex: extra.pendingWritesIndex
								}),
								[CONFIG_KEY_PREVIOUS_STATE]: checkpoint.channel_values[PREVIOUS],
								checkpoint_id: void 0,
								checkpoint_ns: taskCheckpointNamespace
							}
						}),
						executionInfo
					},
					triggers,
					retry_policy: proc.retryPolicy,
					cache_key: proc.cachePolicy ? {
						key: XXH3((proc.cachePolicy.keyFunc ?? JSON.stringify)([packet.args])),
						ns: [
							CACHE_NS_WRITES,
							proc.name ?? "__dynamic__",
							packet.node
						],
						ttl: proc.cachePolicy.ttl
					} : void 0,
					id: taskId,
					path: taskPath,
					writers: proc.getWriters(),
					timeout: packet.timeout ?? proc.timeout
				};
			}
		} else return {
			id: taskId,
			name: packet.node,
			interrupts: [],
			path: taskPath
		};
	} else if (taskPath[0] === "__pregel_pull") {
		const name = taskPath[1].toString();
		const proc = processes[name];
		if (proc === void 0) return;
		if (pendingWrites?.length) {
			const checkpointNamespace = parentNamespace === "" ? name : `${parentNamespace}|${name}`;
			const taskId = uuid5(JSON.stringify([
				checkpointNamespace,
				step.toString(),
				name,
				PULL,
				name
			]), checkpoint.id);
			if (extra.pendingWritesIndex ? extra.pendingWritesIndex.successfulWriteTaskIds.has(taskId) : pendingWrites.some((w) => w[0] === taskId && w[1] !== "__error__")) return;
		}
		const nullVersion = getNullChannelVersion(checkpoint.channel_versions);
		if (nullVersion === void 0) return;
		const seen = checkpoint.versions_seen[name] ?? {};
		const trigger = proc.triggers.find((chan) => {
			if (!channels[chan].isAvailable()) return false;
			return (checkpoint.channel_versions[chan] ?? nullVersion) > (seen[chan] ?? nullVersion);
		});
		if (trigger !== void 0) {
			const val = _procInput(proc, channels, forExecution);
			if (val === void 0) return;
			const checkpointNamespace = parentNamespace === "" ? name : `${parentNamespace}|${name}`;
			const taskId = uuid5(JSON.stringify([
				checkpointNamespace,
				step.toString(),
				name,
				PULL,
				[trigger]
			]), checkpoint.id);
			const taskCheckpointNamespace = `${checkpointNamespace}:${taskId}`;
			let metadata = {
				langgraph_step: step,
				langgraph_node: name,
				langgraph_triggers: [trigger],
				langgraph_path: taskPath,
				langgraph_checkpoint_ns: taskCheckpointNamespace,
				checkpoint_ns: taskCheckpointNamespace
			};
			if (forExecution) {
				const node = proc.getNode();
				if (node !== void 0) {
					if (proc.metadata !== void 0) metadata = {
						...metadata,
						...proc.metadata
					};
					const writes = [];
					const executionInfo = {
						checkpointId: checkpoint.id,
						checkpointNs: taskCheckpointNamespace,
						taskId,
						threadId: configurable.thread_id,
						runId: config.runId != null ? String(config.runId) : void 0,
						nodeAttempt: 1
					};
					return {
						name,
						input: val,
						proc: node,
						subgraphs: proc.subgraphs,
						writes,
						config: {
							...patchConfig(mergeConfigs(config, {
								metadata,
								tags: proc.tags,
								store: extra.store ?? config.store
							}), {
								runName: name,
								callbacks: manager?.getChild(`graph:step:${step}`),
								configurable: {
									[CONFIG_KEY_TASK_ID]: taskId,
									[CONFIG_KEY_SEND]: (writes_) => _localWrite((items) => {
										writes.push(...items);
									}, processes, writes_),
									[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(checkpoint, channels, {
										name,
										writes,
										triggers: [trigger],
										path: taskPath
									}, select_, fresh_),
									[CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable["__pregel_checkpointer"],
									[CONFIG_KEY_CHECKPOINT_MAP]: {
										...configurable[CONFIG_KEY_CHECKPOINT_MAP],
										[parentNamespace]: checkpoint.id
									},
									[CONFIG_KEY_SCRATCHPAD]: _scratchpad({
										pendingWrites: pendingWrites ?? [],
										taskId,
										currentTaskInput: val,
										resumeMap: config.configurable?.[CONFIG_KEY_RESUME_MAP],
										namespaceHash: XXH3(taskCheckpointNamespace),
										pendingWritesIndex: extra.pendingWritesIndex
									}),
									[CONFIG_KEY_PREVIOUS_STATE]: checkpoint.channel_values[PREVIOUS],
									checkpoint_id: void 0,
									checkpoint_ns: taskCheckpointNamespace
								}
							}),
							executionInfo
						},
						triggers: [trigger],
						retry_policy: proc.retryPolicy,
						cache_key: proc.cachePolicy ? {
							key: XXH3((proc.cachePolicy.keyFunc ?? JSON.stringify)([val])),
							ns: [
								CACHE_NS_WRITES,
								proc.name ?? "__dynamic__",
								name
							],
							ttl: proc.cachePolicy.ttl
						} : void 0,
						id: taskId,
						path: taskPath,
						writers: proc.getWriters(),
						timeout: proc.timeout
					};
				}
			} else return {
				id: taskId,
				name,
				interrupts: [],
				path: taskPath
			};
		}
	}
}
/**
* Prepare an immediate node-level error handler task for a failed task.
*
* The handler runs only after the failed node's retry policy is exhausted (the
* runner schedules it once a non-bubble-up error settles). It is prepared like
* a PUSH task targeting the auto-generated handler node, receives the failed
* node's input, and is injected with a {@link NodeError} under
* {@link CONFIG_KEY_NODE_ERROR} so the handler can inspect the failure
* provenance (and route via `Command({ goto })`).
*
* @internal
*/
function _prepareNodeErrorHandlerTask(failedTask, handlerNodeName, error, checkpoint, pendingWrites, processes, channels, config, extra) {
	const { step, checkpointer, manager } = extra;
	const proc = processes[handlerNodeName];
	if (proc === void 0) return;
	const node = proc.getNode();
	if (node === void 0) return;
	const configurable = config.configurable ?? {};
	const parentNamespace = configurable.checkpoint_ns ?? "";
	const triggers = [PUSH];
	const checkpointNamespace = parentNamespace === "" ? handlerNodeName : `${parentNamespace}|${handlerNodeName}`;
	const taskId = uuid5(JSON.stringify([
		checkpointNamespace,
		step.toString(),
		handlerNodeName,
		PUSH,
		"node_error_handler",
		failedTask.id
	]), checkpoint.id);
	const taskCheckpointNamespace = `${checkpointNamespace}:${taskId}`;
	const taskPath = [
		PUSH,
		String(failedTask.name),
		handlerNodeName,
		false
	];
	let metadata = {
		langgraph_step: step,
		langgraph_node: handlerNodeName,
		langgraph_triggers: triggers,
		langgraph_path: taskPath,
		langgraph_checkpoint_ns: taskCheckpointNamespace,
		checkpoint_ns: taskCheckpointNamespace
	};
	if (proc.metadata !== void 0) metadata = {
		...metadata,
		...proc.metadata
	};
	const writes = [];
	const executionInfo = {
		checkpointId: checkpoint.id,
		checkpointNs: taskCheckpointNamespace,
		taskId,
		threadId: configurable.thread_id,
		runId: config.runId != null ? String(config.runId) : void 0,
		nodeAttempt: 1
	};
	return {
		name: handlerNodeName,
		input: failedTask.input,
		proc: node,
		subgraphs: proc.subgraphs,
		writes,
		config: {
			...patchConfig(mergeConfigs(config, {
				metadata,
				tags: proc.tags,
				store: extra.store ?? config.store
			}), {
				runName: handlerNodeName,
				callbacks: manager?.getChild(`graph:step:${step}`),
				configurable: {
					[CONFIG_KEY_TASK_ID]: taskId,
					[CONFIG_KEY_SEND]: (writes_) => _localWrite((items) => writes.push(...items), processes, writes_),
					[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(checkpoint, channels, {
						name: handlerNodeName,
						writes,
						triggers,
						path: taskPath
					}, select_, fresh_),
					[CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable["__pregel_checkpointer"],
					[CONFIG_KEY_CHECKPOINT_MAP]: {
						...configurable[CONFIG_KEY_CHECKPOINT_MAP],
						[parentNamespace]: checkpoint.id
					},
					[CONFIG_KEY_SCRATCHPAD]: _scratchpad({
						pendingWrites: pendingWrites ?? [],
						taskId,
						currentTaskInput: failedTask.input,
						resumeMap: config.configurable?.[CONFIG_KEY_RESUME_MAP],
						namespaceHash: XXH3(taskCheckpointNamespace)
					}),
					[CONFIG_KEY_PREVIOUS_STATE]: checkpoint.channel_values[PREVIOUS],
					[CONFIG_KEY_NODE_ERROR]: new NodeError(String(failedTask.name), error),
					checkpoint_id: void 0,
					checkpoint_ns: taskCheckpointNamespace
				}
			}),
			executionInfo
		},
		triggers,
		retry_policy: proc.retryPolicy,
		cache_key: void 0,
		id: taskId,
		path: taskPath,
		writers: proc.getWriters()
	};
}
/**
*  Function injected under CONFIG_KEY_READ in task config, to read current state.
*  Used by conditional edges to read a copy of the state with reflecting the writes
*  from that node only.
*
* @internal
*/
function _procInput(proc, channels, forExecution) {
	let val;
	if (typeof proc.channels === "object" && !Array.isArray(proc.channels)) {
		val = {};
		for (const [k, chan] of Object.entries(proc.channels)) if (proc.triggers.includes(chan)) try {
			val[k] = readChannel(channels, chan, false);
		} catch (e) {
			if (e.name === EmptyChannelError.unminifiable_name) return;
			else throw e;
		}
		else if (chan in channels) try {
			val[k] = readChannel(channels, chan, false);
		} catch (e) {
			if (e.name === EmptyChannelError.unminifiable_name) continue;
			else throw e;
		}
	} else if (Array.isArray(proc.channels)) {
		let successfulRead = false;
		for (const chan of proc.channels) try {
			val = readChannel(channels, chan, false);
			successfulRead = true;
			break;
		} catch (e) {
			if (e.name === EmptyChannelError.unminifiable_name) continue;
			else throw e;
		}
		if (!successfulRead) return;
	} else throw new Error(`Invalid channels type, expected list or dict, got ${proc.channels}`);
	if (forExecution && proc.mapper !== void 0) val = proc.mapper(val);
	return val;
}
/**
* Remove any values belonging to UntrackedValue channels from a Send packet
* before checkpointing.
*
* Send is often called with state to be passed to the destination node,
* which may contain UntrackedValues at the top level.
*
* @internal
*/
function sanitizeUntrackedValuesInSend(packet, channels) {
	if (typeof packet.args !== "object" || packet.args === null) return packet;
	const sanitizedArg = {};
	for (const [key, value] of Object.entries(packet.args)) {
		const channel = channels[key];
		if (!channel || channel.lc_graph_name !== "UntrackedValue") sanitizedArg[key] = value;
	}
	return new Send(packet.node, sanitizedArg);
}
function _scratchpad({ pendingWrites, taskId, currentTaskInput, resumeMap, namespaceHash, pendingWritesIndex }) {
	const nullResume = pendingWritesIndex ? pendingWritesIndex.nullResume : pendingWrites.find(([writeTaskId, chan]) => writeTaskId === "00000000-0000-0000-0000-000000000000" && chan === "__resume__")?.[2];
	const scratchpad = {
		callCounter: 0,
		interruptCounter: -1,
		resume: (() => {
			const result = pendingWritesIndex ? (pendingWritesIndex.resumeByTaskId.get(taskId) ?? []).flat() : pendingWrites.filter(([writeTaskId, chan]) => writeTaskId === taskId && chan === "__resume__").flatMap(([_writeTaskId, _chan, resume]) => resume);
			if (resumeMap != null && namespaceHash in resumeMap) {
				const mappedResume = resumeMap[namespaceHash];
				result.push(mappedResume);
			}
			return result;
		})(),
		nullResume,
		subgraphCounter: 0,
		currentTaskInput,
		consumeNullResume: () => {
			if (scratchpad.nullResume) {
				delete scratchpad.nullResume;
				pendingWrites.splice(pendingWrites.findIndex(([writeTaskId, chan]) => writeTaskId === "00000000-0000-0000-0000-000000000000" && chan === "__resume__"), 1);
				return nullResume;
			}
		}
	};
	return scratchpad;
}
//#endregion
export { _applyWrites, _localRead, _prepareNextTasks, _prepareNodeErrorHandlerTask, _prepareSingleTask, increment, sanitizeUntrackedValuesInSend, shouldInterrupt };

//# sourceMappingURL=algo.js.map