import { MultiCursorBuffer } from "../multi-cursor-buffer.js";
import { ToolCallAssembler, shouldIgnoreScopedTaskToolEvent, toClientAssembledToolCall } from "./tools.js";
import { StreamingMessageAssembler } from "../messages.js";
import { MediaAssembler } from "../media.js";
import { SubgraphDiscoveryHandle } from "./subgraphs.js";
//#region src/client/stream/handles/subagents.ts
/**
* Discovered subagent within a streaming session. Mirrors the
* in-process `SubagentRunStream` from DeepAgent.
*
* Each subagent is discovered when a `tool-started` event with
* `tool_name === "task"` is observed. The `taskInput` and `output`
* promises resolve from the task tool's lifecycle events.
*
* Use lazy getters (`sub.messages`, `sub.toolCalls`, etc.) for
* namespace-scoped projections.
*/
var SubagentHandle = class {
	name;
	callId;
	taskInput;
	output;
	namespace;
	#session;
	#messagesIterable;
	#toolCallsIterable;
	#subgraphsIterable;
	#mediaDispatcherStarted = false;
	#audioBuffer;
	#imagesBuffer;
	#videoBuffer;
	#filesBuffer;
	constructor(name, callId, namespace, taskInput, output, session) {
		this.name = name;
		this.callId = callId;
		this.namespace = namespace;
		this.taskInput = taskInput;
		this.output = output;
		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 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 toolsEvent = event;
			if (shouldIgnoreScopedTaskToolEvent(this.namespace, toolsEvent)) return;
			const tc = assembler.consume(toolsEvent);
			if (tc) buffer.push(toClientAssembledToolCall(tc));
		}, () => 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;
	}
	#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();
		});
	}
	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"],
				namespaces: [this.namespace]
			}), this.#session, this.namespace);
			for await (const sub of discovery) buffer.push(sub);
			buffer.close();
		})();
		return buffer;
	}
	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 SubagentHandle} instances as task
* tool calls are discovered from the `tools` channel.
*
* Mirrors the in-process `createSubagentTransformer` from DeepAgent:
* watches for `tool_name === "task"` with `tool-started`, extracts
* `subagent_type` and `description` from the input, and resolves
* `output` on `tool-finished`.
*/
var SubagentDiscoveryHandle = class {
	#source;
	#session;
	#queue = [];
	#waiters = [];
	#pending = /* @__PURE__ */ new Map();
	#sourcePump;
	#closed = false;
	constructor(source, session) {
		this.#source = source;
		this.#session = session;
	}
	#processEvent(event) {
		if (event.method !== "tools") return void 0;
		const tools = event;
		const data = tools.params.data;
		const toolCallId = data.tool_call_id;
		if (data.tool_name === "task" && data.event === "tool-started") {
			const rawInput = data.input;
			const input = typeof rawInput === "string" ? JSON.parse(rawInput) : rawInput ?? {};
			const name = input.subagent_type ?? "unknown";
			const description = input.description ?? "";
			let resolveTaskInput;
			let resolveOutput;
			let rejectOutput;
			const taskInput = new Promise((r) => {
				resolveTaskInput = r;
			});
			const output = new Promise((res, rej) => {
				resolveOutput = res;
				rejectOutput = rej;
			});
			resolveTaskInput(description);
			this.#pending.set(toolCallId, {
				resolveOutput,
				rejectOutput
			});
			return new SubagentHandle(name, toolCallId, [...tools.params.namespace], taskInput, output, this.#session);
		}
		if (toolCallId) {
			const pending = this.#pending.get(toolCallId);
			if (pending) {
				if (data.event === "tool-finished") {
					pending.resolveOutput(data.output);
					this.#pending.delete(toolCallId);
				} else if (data.event === "tool-error") {
					const message = data.message ?? "unknown error";
					pending.rejectOutput(new Error(message));
					this.#pending.delete(toolCallId);
				}
			}
		}
	}
	#start() {
		if (this.#sourcePump) return;
		this.#sourcePump = (async () => {
			for await (const event of this.#source) {
				const handle = this.#processEvent(event);
				if (!handle) continue;
				const waiter = this.#waiters.shift();
				if (waiter) waiter({
					done: false,
					value: handle
				});
				else this.#queue.push(handle);
			}
			this.#closed = true;
			for (const pending of this.#pending.values()) pending.resolveOutput(void 0);
			this.#pending.clear();
			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 { SubagentDiscoveryHandle, SubagentHandle };

//# sourceMappingURL=subagents.js.map