import { MultiCursorBuffer } from "../multi-cursor-buffer.js";
import { ToolCallAssembler, toClientAssembledToolCall } from "./tools.js";
import { StreamingMessageAssembler } from "../messages.js";
import { MediaAssembler } from "../media.js";
//#region src/client/stream/handles/subgraphs.ts
/**
* Discovered subgraph within a streaming session.
*
* Mirrors the in-process `SubgraphRunStream` pattern: each subgraph
* has `name`, `index`, `namespace`, and lazy getters for projections
* scoped to this subgraph's namespace.
*
* ```ts
* for await (const sub of session.subgraphs) {
*   for await (const msg of sub.messages) { ... }
*   const state = await sub.output;
* }
* ```
*/
var SubgraphHandle = class {
	name;
	index;
	namespace;
	/**
	* Non-empty when upstream attached a `cause` to this subgraph's
	* `lifecycle.started` event. Population is product-specific and
	* performed by stream transformers on the runtime side (e.g.
	* deepagents' `SubagentTransformer` emits
	* `{ type: "toolCall", tool_call_id }`). Generic clients should
	* treat `cause.type` as an open enum — the protocol allows future
	* variants (`send`, `edge`, ...) to be forwarded verbatim without
	* a SDK bump.
	*/
	cause;
	graphName;
	/**
	* Raw `tool-started` event that triggered this subgraph, when
	* `cause.type === "toolCall"` and the matching event has been
	* observed on the `tools` channel.
	*/
	toolStartedEvent;
	#session;
	#messagesIterable;
	#valuesProjection;
	#toolCallsIterable;
	#subgraphsIterable;
	#subagentsIterable;
	#outputPromise;
	#mediaDispatcherStarted = false;
	#audioBuffer;
	#imagesBuffer;
	#videoBuffer;
	#filesBuffer;
	constructor(name, index, namespace, session, options) {
		this.name = name;
		this.index = index;
		this.namespace = namespace;
		this.cause = options?.cause;
		this.graphName = options?.graphName;
		this.toolStartedEvent = options?.toolStartedEvent;
		this.#session = session;
	}
	get messages() {
		if (this.#messagesIterable) return this.#messagesIterable;
		const buffer = new MultiCursorBuffer();
		this.#messagesIterable = buffer;
		const assembler = new StreamingMessageAssembler();
		this.#startProjection(["messages"], (event) => {
			if (event.method !== "messages") return;
			const msg = assembler.consume(event);
			if (msg) buffer.push(msg);
		}, () => buffer.close());
		return buffer;
	}
	get values() {
		if (this.#valuesProjection) return this.#valuesProjection;
		const buffer = new MultiCursorBuffer();
		let lastValue;
		let resolveOutput;
		const outputPromise = new Promise((resolve) => {
			resolveOutput = resolve;
		});
		this.#outputPromise = outputPromise;
		const projection = Object.assign(buffer, { then: (onfulfilled, onrejected) => outputPromise.then(onfulfilled, onrejected) });
		this.#valuesProjection = projection;
		this.#startProjection(["values"], (event) => {
			if (event.method !== "values") return;
			const data = event.params.data;
			lastValue = data;
			buffer.push(data);
		}, () => {
			resolveOutput(lastValue);
			buffer.close();
		});
		return projection;
	}
	get toolCalls() {
		if (this.#toolCallsIterable) return this.#toolCallsIterable;
		const buffer = new MultiCursorBuffer();
		this.#toolCallsIterable = buffer;
		const assembler = new ToolCallAssembler();
		this.#startProjection(["tools"], (event) => {
			if (event.method !== "tools") return;
			const tc = assembler.consume(event);
			if (tc) buffer.push(toClientAssembledToolCall(tc));
		}, () => buffer.close());
		return buffer;
	}
	get subgraphs() {
		if (this.#subgraphsIterable) return this.#subgraphsIterable;
		const buffer = new MultiCursorBuffer();
		this.#subgraphsIterable = buffer;
		(async () => {
			const discovery = new SubgraphDiscoveryHandle(await this.#session.subscribe({
				channels: ["lifecycle", "tools"],
				namespaces: [this.namespace]
			}), this.#session, this.namespace);
			for await (const sub of discovery) buffer.push(sub);
			buffer.close();
		})();
		return buffer;
	}
	get subagents() {
		if (this.#subagentsIterable) return this.#subagentsIterable;
		const buffer = new MultiCursorBuffer();
		this.#subagentsIterable = buffer;
		(async () => {
			const rawHandle = await this.#session.subscribe({
				channels: ["tools", "lifecycle"],
				namespaces: [this.namespace]
			});
			const { SubagentDiscoveryHandle: Discovery } = await import("./subagents.js");
			const discovery = new Discovery(rawHandle, this.#session);
			for await (const sub of discovery) buffer.push(sub);
			buffer.close();
		})();
		return buffer;
	}
	get audio() {
		this.#ensureMediaDispatcher();
		return this.#audioBuffer;
	}
	get images() {
		this.#ensureMediaDispatcher();
		return this.#imagesBuffer;
	}
	get video() {
		this.#ensureMediaDispatcher();
		return this.#videoBuffer;
	}
	get files() {
		this.#ensureMediaDispatcher();
		return this.#filesBuffer;
	}
	get output() {
		this.values;
		return this.#outputPromise;
	}
	#ensureMediaDispatcher() {
		if (this.#mediaDispatcherStarted) return;
		this.#mediaDispatcherStarted = true;
		const audio = new MultiCursorBuffer();
		const images = new MultiCursorBuffer();
		const video = new MultiCursorBuffer();
		const files = new MultiCursorBuffer();
		this.#audioBuffer = audio;
		this.#imagesBuffer = images;
		this.#videoBuffer = video;
		this.#filesBuffer = files;
		const assembler = new MediaAssembler({
			onAudio: (m) => audio.push(m),
			onImage: (m) => images.push(m),
			onVideo: (m) => video.push(m),
			onFile: (m) => files.push(m)
		});
		this.#startProjection(["messages"], (event) => {
			if (event.method !== "messages") return;
			assembler.consume(event);
		}, () => {
			assembler.close();
			audio.close();
			images.close();
			video.close();
			files.close();
		});
	}
	subscribe(paramsOrChannels, options = {}) {
		if (typeof paramsOrChannels === "object" && !Array.isArray(paramsOrChannels) && "channels" in paramsOrChannels) return this.#session.subscribe({
			...paramsOrChannels,
			namespaces: paramsOrChannels.namespaces ?? [this.namespace]
		});
		return this.#session.subscribe(paramsOrChannels, {
			...options,
			namespaces: options.namespaces ?? [this.namespace]
		});
	}
	async #startProjection(channels, onEvent, onDone) {
		try {
			const rawHandle = await this.#session.subscribe({
				channels,
				namespaces: [this.namespace]
			});
			for await (const event of rawHandle) onEvent(event);
		} finally {
			onDone();
		}
	}
};
/**
* Async iterable that yields {@link SubgraphHandle} instances as new
* subgraph namespaces are discovered from `lifecycle` events.
*
* Mirrors the in-process `run.subgraphs` pattern. A new subgraph is
* discovered when a `lifecycle` event with `event: "started"` is
* received at a namespace depth of exactly `parentDepth + 1`.
*/
var SubgraphDiscoveryHandle = class {
	#source;
	#session;
	#parentNamespace;
	#discovered = /* @__PURE__ */ new Set();
	#pendingToolStarts = /* @__PURE__ */ new Map();
	#pendingToolCallHandles = /* @__PURE__ */ new Map();
	#queue = [];
	#waiters = [];
	#sourcePump;
	#closed = false;
	constructor(source, session, parentNamespace = []) {
		this.#source = source;
		this.#session = session;
		this.#parentNamespace = parentNamespace;
	}
	#emit(handle) {
		const waiter = this.#waiters.shift();
		if (waiter) waiter({
			done: false,
			value: handle
		});
		else this.#queue.push(handle);
	}
	#processToolEvent(event) {
		if (event.method !== "tools") return false;
		const tools = event;
		const data = tools.params.data;
		if (data.event !== "tool-started") return true;
		const toolCallId = data.tool_call_id;
		if (!toolCallId) return true;
		const pendingHandle = this.#pendingToolCallHandles.get(toolCallId);
		if (pendingHandle) {
			pendingHandle.toolStartedEvent = tools;
			this.#pendingToolCallHandles.delete(toolCallId);
			return true;
		}
		this.#pendingToolStarts.set(toolCallId, tools);
		return true;
	}
	#processEvent(event) {
		if (this.#processToolEvent(event)) return void 0;
		if (event.method !== "lifecycle") return void 0;
		const lifecycle = event;
		if (lifecycle.params.data.event !== "started") return void 0;
		const ns = event.params.namespace;
		if (ns.length !== this.#parentNamespace.length + 1) return void 0;
		if (!this.#parentNamespace.every((seg, i) => ns[i] === seg)) return void 0;
		const nsKey = ns.join("/");
		if (this.#discovered.has(nsKey)) return void 0;
		this.#discovered.add(nsKey);
		const lastSegment = ns[ns.length - 1] ?? "";
		const colonIdx = lastSegment.lastIndexOf(":");
		let name;
		let index;
		if (colonIdx >= 0) {
			name = lastSegment.slice(0, colonIdx);
			const suffix = lastSegment.slice(colonIdx + 1);
			index = /^\d+$/.test(suffix) ? Number(suffix) : 0;
		} else {
			name = lastSegment;
			index = 0;
		}
		const data = lifecycle.params.data;
		const cause = data.cause && typeof data.cause === "object" ? data.cause : void 0;
		let toolStartedEvent;
		if (cause?.type === "toolCall") {
			const toolCallId = cause.tool_call_id;
			if (toolCallId) {
				toolStartedEvent = this.#pendingToolStarts.get(toolCallId);
				this.#pendingToolStarts.delete(toolCallId);
			}
		}
		const handle = new SubgraphHandle(name, index, [...ns], this.#session, {
			cause,
			graphName: data.graph_name,
			toolStartedEvent
		});
		if (cause?.type === "toolCall" && toolStartedEvent == null) {
			const toolCallId = cause.tool_call_id;
			if (toolCallId) this.#pendingToolCallHandles.set(toolCallId, handle);
		}
		return handle;
	}
	#start() {
		if (this.#sourcePump) return;
		this.#sourcePump = (async () => {
			for await (const event of this.#source) {
				const handle = this.#processEvent(event);
				if (!handle) continue;
				this.#emit(handle);
			}
			this.#pendingToolStarts.clear();
			this.#pendingToolCallHandles.clear();
			this.#closed = true;
			while (this.#waiters.length > 0) this.#waiters.shift()?.({
				done: true,
				value: void 0
			});
		})();
	}
	async close() {
		this.#closed = true;
		await this.#source.unsubscribe();
	}
	[Symbol.asyncIterator]() {
		this.#start();
		return {
			next: async () => {
				if (this.#queue.length > 0) return {
					done: false,
					value: this.#queue.shift()
				};
				if (this.#closed) return {
					done: true,
					value: void 0
				};
				return await new Promise((resolve) => {
					this.#waiters.push(resolve);
				});
			},
			return: async () => {
				await this.close();
				return {
					done: true,
					value: void 0
				};
			}
		};
	}
};
//#endregion
export { SubgraphDiscoveryHandle, SubgraphHandle };

//# sourceMappingURL=subgraphs.js.map