const require_messages = require("./messages.cjs");
const require_tools = require("../utils/tools.cjs");
//#region src/ui/subagents.ts
/**
* Default tool names that indicate subagent invocation.
* Can be customized via SubagentManager options.
*/
const DEFAULT_SUBAGENT_TOOL_NAMES = ["task"];
/**
* Checks if a namespace indicates a subagent/subgraph message.
*
* Subagent namespaces contain a "tools:" segment indicating they
* originate from a tool call that spawned a subgraph.
*
* @param namespace - The namespace array from stream events (or checkpoint_ns string)
* @returns True if this is a subagent namespace
*/
function isSubagentNamespace(namespace) {
	if (!namespace) return false;
	if (typeof namespace === "string") return namespace.includes("tools:");
	return namespace.some((s) => s.startsWith("tools:"));
}
/**
* Extracts the tool call ID from a namespace path.
*
* Namespaces follow the pattern: ["tools:call_abc123", "model_request:xyz", ...]
* This function extracts "call_abc123" from the first "tools:" segment.
*
* @param namespace - The namespace array from stream events
* @returns The tool call ID, or undefined if not found
*/
function extractToolCallIdFromNamespace(namespace) {
	if (!namespace || namespace.length === 0) return void 0;
	for (const segment of namespace) if (segment.startsWith("tools:")) return segment.slice(6);
}
/**
* Calculates the depth of a subagent based on its namespace.
* Counts the number of "tools:" segments in the namespace.
*
* @param namespace - The namespace array
* @returns The depth (0 for main agent, 1+ for subagents)
*/
function calculateDepthFromNamespace(namespace) {
	if (!namespace) return 0;
	return namespace.filter((s) => s.startsWith("tools:")).length;
}
/**
* Extracts the parent tool call ID from a namespace.
*
* For nested subagents, the namespace looks like:
* ["tools:parent_id", "tools:child_id", ...]
*
* @param namespace - The namespace array
* @returns The parent tool call ID, or null if this is a top-level subagent
*/
function extractParentIdFromNamespace(namespace) {
	if (!namespace || namespace.length < 2) return null;
	const toolSegments = namespace.filter((s) => s.startsWith("tools:"));
	if (toolSegments.length < 2) return null;
	return toolSegments[toolSegments.length - 2]?.slice(6) ?? null;
}
/**
* Manages subagent execution state.
*
* Tracks subagents from the moment they are invoked (AI message with tool calls)
* through streaming to completion (tool message result).
*/
var SubagentManager = class {
	subagents = /* @__PURE__ */ new Map();
	/**
	* Maps namespace IDs (pregel task IDs) to tool call IDs.
	* LangGraph subgraphs use internal pregel task IDs in their namespace,
	* which are different from the tool_call_id used to invoke them.
	*/
	namespaceToToolCallId = /* @__PURE__ */ new Map();
	/**
	* Pending namespace matches that couldn't be resolved immediately.
	* These are retried when new tool calls are registered.
	*/
	pendingMatches = /* @__PURE__ */ new Map();
	/**
	* Messages received before we can map a subgraph namespace to the public
	* subagent tool-call ID. Once the mapping is established, these messages are
	* replayed so early tool calls are not lost.
	*/
	pendingMessages = /* @__PURE__ */ new Map();
	/**
	* Message managers for each subagent.
	* Uses the same MessageTupleManager as the main stream for proper
	* message chunk concatenation.
	*/
	messageManagers = /* @__PURE__ */ new Map();
	subagentToolNames;
	onSubagentChange;
	toMessage;
	constructor(options) {
		this.subagentToolNames = new Set(options?.subagentToolNames ?? DEFAULT_SUBAGENT_TOOL_NAMES);
		this.onSubagentChange = options?.onSubagentChange;
		this.toMessage = options?.toMessage ?? require_messages.toMessageDict;
	}
	/**
	* Get or create a MessageTupleManager for a subagent.
	*/
	getMessageManager(toolCallId) {
		let manager = this.messageManagers.get(toolCallId);
		if (!manager) {
			manager = new require_messages.MessageTupleManager();
			this.messageManagers.set(toolCallId, manager);
		}
		return manager;
	}
	/**
	* Get messages for a subagent with proper chunk concatenation.
	* This mirrors how the main stream handles messages.
	*/
	getMessagesForSubagent(toolCallId) {
		const manager = this.messageManagers.get(toolCallId);
		if (!manager) return [];
		const messages = [];
		for (const entry of Object.values(manager.chunks)) if (entry.chunk) messages.push(this.toMessage(entry.chunk));
		return messages;
	}
	/**
	* Buffer a subagent message until we can resolve its namespace to a tool call.
	*/
	queuePendingMessage(namespaceId, serialized, metadata) {
		const pending = this.pendingMessages.get(namespaceId) ?? [];
		pending.push({
			serialized,
			metadata
		});
		this.pendingMessages.set(namespaceId, pending);
	}
	/**
	* Replay any buffered messages once a namespace has been mapped.
	*/
	flushPendingMessages(namespaceId) {
		const pending = this.pendingMessages.get(namespaceId);
		if (!pending?.length) return;
		this.pendingMessages.delete(namespaceId);
		pending.forEach(({ serialized, metadata }) => {
			this.addMessageToSubagent(namespaceId, serialized, metadata);
		});
	}
	/**
	* Create a complete SubagentStream object with all derived properties.
	* This ensures consistency with UseStream interface.
	*/
	createSubagentStream(base) {
		const { messages } = base;
		const allToolCalls = require_tools.getToolCallsWithResults(messages);
		return {
			...base,
			isLoading: base.status === "running",
			toolCalls: allToolCalls,
			getToolCalls: (message) => {
				return allToolCalls.filter((tc) => tc.aiMessage.id === message.id);
			},
			interrupt: void 0,
			interrupts: [],
			switchThread: () => {},
			subagents: /* @__PURE__ */ new Map(),
			activeSubagents: [],
			getSubagent: () => void 0,
			getSubagentsByType: () => [],
			getSubagentsByMessage: () => []
		};
	}
	/**
	* Get the tool call ID for a given namespace ID.
	* Returns the namespace ID itself if no mapping exists.
	*/
	getToolCallIdFromNamespace(namespaceId) {
		return this.namespaceToToolCallId.get(namespaceId) ?? namespaceId;
	}
	/**
	* Try to match a subgraph to a pending subagent by description.
	* Creates a mapping from namespace ID to tool call ID if a match is found.
	*
	* Uses a multi-pass matching strategy:
	* 1. Exact description match
	* 2. Description contains/partial match
	* 3. Any unmapped pending subagent (fallback)
	*
	* @param namespaceId - The namespace ID (pregel task ID) from the subgraph
	* @param description - The description from the subgraph's initial message
	* @returns The matched tool call ID, or undefined if no match
	*/
	matchSubgraphToSubagent(namespaceId, description) {
		if (this.namespaceToToolCallId.has(namespaceId)) return this.namespaceToToolCallId.get(namespaceId);
		const mappedToolCallIds = new Set(this.namespaceToToolCallId.values());
		const establishMapping = (toolCallId) => {
			this.namespaceToToolCallId.set(namespaceId, toolCallId);
			const subagent = this.subagents.get(toolCallId);
			if (subagent && subagent.status === "pending") {
				this.subagents.set(toolCallId, {
					...subagent,
					status: "running",
					namespace: [namespaceId],
					startedAt: /* @__PURE__ */ new Date()
				});
				this.onSubagentChange?.();
			}
			this.flushPendingMessages(namespaceId);
			return toolCallId;
		};
		for (const [toolCallId, subagent] of this.subagents) if ((subagent.status === "pending" || subagent.status === "running") && !mappedToolCallIds.has(toolCallId) && subagent.toolCall.args.description === description) return establishMapping(toolCallId);
		for (const [toolCallId, subagent] of this.subagents) if ((subagent.status === "pending" || subagent.status === "running") && !mappedToolCallIds.has(toolCallId)) {
			const subagentDesc = subagent.toolCall.args.description || "";
			if (subagentDesc && description.includes(subagentDesc) || subagentDesc && subagentDesc.includes(description)) {
				if (description.length > subagentDesc.length) this.subagents.set(toolCallId, {
					...subagent,
					toolCall: {
						...subagent.toolCall,
						args: {
							...subagent.toolCall.args,
							description
						}
					}
				});
				return establishMapping(toolCallId);
			}
		}
		if (description) this.pendingMatches.set(namespaceId, description);
	}
	/**
	* Check if a tool call is a subagent invocation.
	*/
	isSubagentToolCall(toolName) {
		return this.subagentToolNames.has(toolName);
	}
	/**
	* Check if a subagent_type value is valid.
	* Valid types are proper identifiers like "weather-scout", "experience-curator".
	*/
	isValidSubagentType(type) {
		if (!type || typeof type !== "string") return false;
		if (type.length < 3) return false;
		if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(type)) return false;
		if (type.length > 50) return false;
		return true;
	}
	/**
	* Check if a subagent should be exposed through the public API.
	*
	* We expose subagents as soon as their parent AI message emits a complete
	* subagent tool call, which means pending subagents should be visible.
	*
	* This still filters out corrupted or partial streaming artifacts by
	* requiring a valid-looking `subagent_type`.
	*/
	isValidSubagent(subagent) {
		return this.isValidSubagentType(subagent.toolCall.args.subagent_type);
	}
	/**
	* Build a complete SubagentStream from internal state.
	* Adds messages and derived properties.
	*/
	buildExecution(base) {
		const streamingMessages = this.getMessagesForSubagent(base.id);
		const messages = streamingMessages.length > 0 ? streamingMessages : base.messages;
		return this.createSubagentStream({
			...base,
			messages
		});
	}
	/**
	* Get all subagents as a Map.
	* Filters out incomplete/phantom subagents that lack subagent_type.
	*/
	getSubagents() {
		const result = /* @__PURE__ */ new Map();
		for (const [id, subagent] of this.subagents) if (this.isValidSubagent(subagent)) result.set(id, this.buildExecution(subagent));
		return result;
	}
	/**
	* Get all currently running subagents.
	* Filters out incomplete/phantom subagents.
	*/
	getActiveSubagents() {
		return [...this.subagents.values()].filter((s) => s.status === "running" && this.isValidSubagent(s)).map((s) => this.buildExecution(s));
	}
	/**
	* Get a specific subagent by tool call ID.
	*/
	getSubagent(toolCallId) {
		const subagent = this.subagents.get(toolCallId);
		return subagent ? this.buildExecution(subagent) : void 0;
	}
	/**
	* Get all subagents of a specific type.
	*/
	getSubagentsByType(type) {
		return [...this.subagents.values()].filter((s) => s.toolCall.args.subagent_type === type).map((s) => this.buildExecution(s));
	}
	/**
	* Get all subagents triggered by a specific AI message.
	*
	* @param messageId - The ID of the AI message.
	* @returns Array of subagent streams triggered by that message.
	*/
	getSubagentsByMessage(messageId) {
		return [...this.subagents.values()].filter((s) => s.aiMessageId === messageId && this.isValidSubagent(s)).map((s) => this.buildExecution(s));
	}
	/**
	* Parse tool call args, handling both object and string formats.
	* During streaming, args might come as a string that needs parsing.
	*/
	parseArgs(args) {
		if (!args) return {};
		if (typeof args === "string") try {
			return JSON.parse(args);
		} catch {
			return {};
		}
		return args;
	}
	/**
	* Register new subagent(s) from AI message tool calls.
	*
	* Called when an AI message is received with tool calls.
	* Creates pending subagent entries for each subagent tool call.
	*
	* @param toolCalls - The tool calls from an AI message
	* @param aiMessageId - The ID of the AI message that triggered the tool calls
	*/
	registerFromToolCalls(toolCalls, aiMessageId) {
		let hasChanges = false;
		for (const toolCall of toolCalls) {
			if (!toolCall.id) continue;
			if (!this.isSubagentToolCall(toolCall.name)) continue;
			const parsedArgs = this.parseArgs(toolCall.args);
			const hasValidType = this.isValidSubagentType(parsedArgs.subagent_type);
			const existing = this.subagents.get(toolCall.id);
			if (existing) {
				const newType = parsedArgs.subagent_type || "";
				const oldType = existing.toolCall.args.subagent_type || "";
				const newDesc = parsedArgs.description || "";
				const oldDesc = existing.toolCall.args.description || "";
				const shouldUpdateType = this.isValidSubagentType(newType) && newType.length > oldType.length;
				const shouldUpdateDesc = newDesc.length > oldDesc.length;
				const shouldUpdateMessageId = aiMessageId != null && aiMessageId !== existing.aiMessageId;
				if (shouldUpdateType || shouldUpdateDesc || shouldUpdateMessageId) {
					this.subagents.set(toolCall.id, {
						...existing,
						...shouldUpdateMessageId ? { aiMessageId } : {},
						toolCall: {
							...existing.toolCall,
							args: {
								...existing.toolCall.args,
								...parsedArgs,
								description: shouldUpdateDesc ? newDesc : oldDesc,
								subagent_type: shouldUpdateType ? newType : oldType
							}
						}
					});
					hasChanges = true;
				}
				continue;
			}
			if (!hasValidType) continue;
			const subagentToolCall = {
				id: toolCall.id,
				name: toolCall.name,
				args: {
					description: parsedArgs.description,
					subagent_type: parsedArgs.subagent_type,
					...parsedArgs
				}
			};
			const execution = {
				id: toolCall.id,
				toolCall: subagentToolCall,
				status: "pending",
				values: {},
				result: null,
				error: null,
				namespace: [],
				messages: [],
				aiMessageId: aiMessageId ?? null,
				parentId: null,
				depth: 0,
				startedAt: null,
				completedAt: null
			};
			this.subagents.set(toolCall.id, execution);
			this.getMessageManager(toolCall.id);
			this.flushPendingMessages(toolCall.id);
			hasChanges = true;
		}
		if (hasChanges) {
			this.retryPendingMatches();
			this.onSubagentChange?.();
		}
	}
	/**
	* Retry matching pending namespaces to newly registered tool calls.
	*/
	retryPendingMatches() {
		if (this.pendingMatches.size === 0) return;
		for (const [namespaceId, description] of this.pendingMatches) {
			if (this.namespaceToToolCallId.has(namespaceId)) {
				this.pendingMatches.delete(namespaceId);
				continue;
			}
			if (this.matchSubgraphToSubagent(namespaceId, description)) this.pendingMatches.delete(namespaceId);
		}
	}
	/**
	* Mark a subagent as running and update its namespace.
	*
	* Called when update events are received with a namespace indicating
	* which subagent is streaming.
	*
	* @param toolCallId - The tool call ID of the subagent
	* @param options - Additional update options
	*/
	markRunning(toolCallId, options) {
		const existing = this.subagents.get(toolCallId);
		if (!existing) return;
		const namespace = options?.namespace ?? existing.namespace;
		this.subagents.set(toolCallId, {
			...existing,
			status: "running",
			namespace,
			parentId: existing.parentId ?? extractParentIdFromNamespace(namespace) ?? null,
			depth: existing.depth || calculateDepthFromNamespace(namespace),
			startedAt: existing.startedAt ?? /* @__PURE__ */ new Date()
		});
		this.onSubagentChange?.();
	}
	/**
	* Mark a subagent as running using a namespace ID.
	* Resolves the namespace ID to the actual tool call ID via the mapping.
	*
	* @param namespaceId - The namespace ID (pregel task ID) from the subgraph
	* @param namespace - The full namespace array
	*/
	markRunningFromNamespace(namespaceId, namespace) {
		const toolCallId = this.getToolCallIdFromNamespace(namespaceId);
		this.markRunning(toolCallId, { namespace });
	}
	/**
	* Add a serialized message to a subagent from stream events.
	*
	* This method handles the raw serialized message data from SSE events.
	* Uses MessageTupleManager for proper chunk concatenation, matching
	* how the main stream handles messages.
	*
	* @param namespaceId - The namespace ID (pregel task ID) from the stream
	* @param serialized - The serialized message from the stream
	* @param metadata - Optional metadata from the stream event
	*/
	addMessageToSubagent(namespaceId, serialized, metadata) {
		if (serialized.type === "human" && typeof serialized.content === "string") this.matchSubgraphToSubagent(namespaceId, serialized.content);
		const toolCallId = this.getToolCallIdFromNamespace(namespaceId);
		const existing = this.subagents.get(toolCallId);
		if (!existing) {
			this.queuePendingMessage(namespaceId, serialized, metadata);
			return;
		}
		if (this.getMessageManager(toolCallId).add(serialized, metadata)) if (serialized.type === "ai") this.subagents.set(toolCallId, {
			...existing,
			status: "running",
			startedAt: existing.startedAt ?? /* @__PURE__ */ new Date(),
			messages: this.getMessagesForSubagent(toolCallId)
		});
		else this.subagents.set(toolCallId, {
			...existing,
			messages: this.getMessagesForSubagent(toolCallId)
		});
		this.onSubagentChange?.();
	}
	/**
	* Update subagent values from a values stream event.
	*
	* Called when a values event is received from a subagent's namespace.
	* This populates the subagent's state values, making them accessible
	* via the `values` property.
	*
	* @param namespaceId - The namespace ID (pregel task ID) from the stream
	* @param values - The state values from the stream event
	*/
	updateSubagentValues(namespaceId, values) {
		const toolCallId = this.getToolCallIdFromNamespace(namespaceId);
		const existing = this.subagents.get(toolCallId);
		if (!existing) return;
		this.subagents.set(toolCallId, {
			...existing,
			values,
			status: existing.status === "pending" ? "running" : existing.status,
			startedAt: existing.startedAt ?? /* @__PURE__ */ new Date()
		});
		this.onSubagentChange?.();
	}
	/**
	* Complete a subagent with a result.
	*
	* Called when a tool message is received for the subagent.
	*
	* @param toolCallId - The tool call ID of the subagent
	* @param result - The result content
	* @param status - The final status (complete or error)
	*/
	complete(toolCallId, result, status = "complete") {
		const existing = this.subagents.get(toolCallId);
		if (!existing) return;
		this.subagents.set(toolCallId, {
			...existing,
			status,
			result: status === "complete" ? result : null,
			error: status === "error" ? result : null,
			completedAt: /* @__PURE__ */ new Date()
		});
		this.onSubagentChange?.();
	}
	/**
	* Clear all subagent state.
	*/
	clear() {
		this.subagents.clear();
		this.namespaceToToolCallId.clear();
		this.messageManagers.clear();
		this.pendingMatches.clear();
		this.pendingMessages.clear();
		this.onSubagentChange?.();
	}
	/**
	* Process a tool message to complete a subagent.
	*
	* @param toolCallId - The tool call ID from the tool message
	* @param content - The result content
	* @param status - Whether the tool execution was successful
	*/
	processToolMessage(toolCallId, content, status = "success") {
		if (!this.subagents.get(toolCallId)) return;
		this.complete(toolCallId, content, status === "success" ? "complete" : "error");
	}
	/**
	* Reconstruct subagent state from historical messages.
	*
	* This method parses an array of messages (typically from thread history)
	* to identify subagent executions and their results. It's used to restore
	* subagent state after:
	* - Page refresh (when stream has already completed)
	* - Loading thread history
	* - Navigating between threads
	*
	* The reconstruction process:
	* 1. Find AI messages with tool calls matching subagent tool names
	* 2. Find corresponding tool messages with results
	* 3. Create SubagentStream entries with "complete" status
	*
	* Note: Internal subagent messages (their streaming conversation) are not
	* reconstructed since they are not persisted in the main thread state.
	*
	* @param messages - Array of messages from thread history
	* @param options - Optional configuration
	* @param options.skipIfPopulated - If true, skip reconstruction if subagents already exist
	*/
	reconstructFromMessages(messages, options) {
		if (options?.skipIfPopulated && this.subagents.size > 0) return;
		const toolResults = /* @__PURE__ */ new Map();
		for (const message of messages) if (message.type === "tool" && "tool_call_id" in message) {
			const toolCallId = message.tool_call_id;
			const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
			const status = "status" in message && message.status === "error" ? "error" : "success";
			toolResults.set(toolCallId, {
				content,
				status
			});
		}
		let hasChanges = false;
		for (const message of messages) {
			if (message.type !== "ai" || !("tool_calls" in message) || !Array.isArray(message.tool_calls)) continue;
			for (const toolCall of message.tool_calls) {
				if (!toolCall.id) continue;
				if (!this.isSubagentToolCall(toolCall.name)) continue;
				if (this.subagents.has(toolCall.id)) continue;
				const parsedArgs = this.parseArgs(toolCall.args);
				if (!this.isValidSubagentType(parsedArgs.subagent_type)) continue;
				const subagentToolCall = {
					id: toolCall.id,
					name: toolCall.name,
					args: {
						description: parsedArgs.description,
						subagent_type: parsedArgs.subagent_type,
						...parsedArgs
					}
				};
				const toolResult = toolResults.get(toolCall.id);
				const isComplete = !!toolResult;
				const status = isComplete ? toolResult.status === "error" ? "error" : "complete" : "running";
				const execution = {
					id: toolCall.id,
					toolCall: subagentToolCall,
					status,
					values: {},
					result: isComplete && status === "complete" ? toolResult.content : null,
					error: isComplete && status === "error" ? toolResult.content : null,
					namespace: [],
					messages: [],
					aiMessageId: message.id ?? null,
					parentId: null,
					depth: 0,
					startedAt: null,
					completedAt: isComplete ? /* @__PURE__ */ new Date() : null
				};
				this.subagents.set(toolCall.id, execution);
				hasChanges = true;
			}
		}
		if (hasChanges) this.onSubagentChange?.();
	}
	/**
	* Update a reconstructed subagent's messages and values from its subgraph checkpoint state.
	*
	* This is called after fetching the subgraph's history to restore the internal
	* conversation that was lost on page refresh. Only updates if messages are
	* currently empty (does not overwrite live streaming data).
	*
	* @param toolCallId - The tool call ID identifying the subagent
	* @param messages - Messages from the subgraph's latest checkpoint
	* @param values - Full state values from the subgraph's latest checkpoint
	* @returns true if the subagent was updated, false otherwise
	*/
	updateSubagentFromSubgraphState(toolCallId, messages, values) {
		const subagent = this.subagents.get(toolCallId);
		if (!subagent) return false;
		if (subagent.messages.length > 0) return false;
		if (messages.length === 0) return false;
		subagent.messages = messages;
		if (values != null) subagent.values = values;
		this.onSubagentChange?.();
		return true;
	}
	/**
	* Check if any subagents are currently tracked.
	*/
	hasSubagents() {
		return this.subagents.size > 0;
	}
};
//#endregion
exports.SubagentManager = SubagentManager;
exports.calculateDepthFromNamespace = calculateDepthFromNamespace;
exports.extractParentIdFromNamespace = extractParentIdFromNamespace;
exports.extractToolCallIdFromNamespace = extractToolCallIdFromNamespace;
exports.isSubagentNamespace = isSubagentNamespace;

//# sourceMappingURL=subagents.cjs.map