import { computed, onScopeDispose, shallowRef } from "vue";

//#region src/protocol/routes.ts
const EVE_ROUTE_PREFIX = "/eve/v1";
const EVE_HEALTH_ROUTE_PATH = `${EVE_ROUTE_PREFIX}/health`;
const EVE_INFO_ROUTE_PATH = `${EVE_ROUTE_PREFIX}/info`;
const EVE_CREATE_SESSION_ROUTE_PATH = `${EVE_ROUTE_PREFIX}/session`;
const EVE_CONTINUE_SESSION_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/session/:sessionId`;
const EVE_MESSAGE_STREAM_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/session/:sessionId/stream`;
const EVE_DEV_DISPATCH_SCHEDULE_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/dev/schedules/:scheduleId`;
const EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH = `${EVE_ROUTE_PREFIX}/dev/runtime-artifacts`;
const EVE_CONNECTION_CALLBACK_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/connections/:name/callback/:token`;
const EVE_CALLBACK_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/callback/:token`;
function createEveMessageStreamRoutePath(sessionId) {
	return `${EVE_ROUTE_PREFIX}/session/${encodeURIComponent(sessionId)}/stream`;
}
function createEveContinueSessionRoutePath(sessionId) {
	return `${EVE_ROUTE_PREFIX}/session/${encodeURIComponent(sessionId)}`;
}

//#endregion
//#region src/shared/guards.ts
function isObject(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value);
}

//#endregion
//#region src/client/client-error.ts
var ClientError = class extends Error {
	status;
	body;
	constructor(status, body) {
		let message = body || `Server returned ${status}.`;
		try {
			const parsed = JSON.parse(body);
			if (isObject(parsed) && typeof parsed.error === "string") message = parsed.error;
		} catch {}
		super(message);
		this.name = "ClientError";
		this.status = status;
		this.body = body;
	}
};

//#endregion
//#region src/protocol/message.ts
const EVE_SESSION_ID_HEADER = "x-eve-session-id";
const textEncoder = new TextEncoder();
function isCurrentTurnBoundaryEvent(event) {
	return event.type === "session.completed" || event.type === "session.failed" || event.type === "session.waiting";
}

//#endregion
//#region src/shared/json.ts
const INVALID_JSON_VALUE_CANDIDATE = Symbol("invalid-json-value-candidate");
const JSON_VALUE_ERROR_MESSAGE = "Expected a JSON-serializable value.";
const JSON_OBJECT_ERROR_MESSAGE = "Expected a JSON-serializable object.";
function parseJsonValue(value) {
	const normalized = normalizeJsonValueCandidate(value);
	if (normalized === INVALID_JSON_VALUE_CANDIDATE) throw new TypeError(JSON_VALUE_ERROR_MESSAGE);
	return normalized;
}
function parseJsonObject(value) {
	const normalized = parseJsonValue(value);
	if (!isJsonObjectValue(normalized)) throw new TypeError(JSON_OBJECT_ERROR_MESSAGE);
	return normalized;
}
function normalizeJsonValueCandidate(value, seen = /* @__PURE__ */ new WeakSet()) {
	if (value === null || typeof value === "boolean" || typeof value === "string") return value;
	if (typeof value === "number") return Number.isFinite(value) ? value : INVALID_JSON_VALUE_CANDIDATE;
	if (Array.isArray(value)) {
		const normalizedItems = [];
		for (const item of value) {
			const normalizedItem = normalizeJsonValueCandidate(item, seen);
			if (normalizedItem === INVALID_JSON_VALUE_CANDIDATE) return INVALID_JSON_VALUE_CANDIDATE;
			normalizedItems.push(normalizedItem);
		}
		return normalizedItems;
	}
	if (typeof value !== "object" || value === void 0) return INVALID_JSON_VALUE_CANDIDATE;
	if (!isPlainObject(value)) return INVALID_JSON_VALUE_CANDIDATE;
	if (seen.has(value)) return INVALID_JSON_VALUE_CANDIDATE;
	seen.add(value);
	const normalized = {};
	for (const [key, entry] of Object.entries(value)) {
		if (entry === void 0) continue;
		const normalizedEntry = normalizeJsonValueCandidate(entry, seen);
		if (normalizedEntry === INVALID_JSON_VALUE_CANDIDATE) return INVALID_JSON_VALUE_CANDIDATE;
		normalized[key] = normalizedEntry;
	}
	seen.delete(value);
	return normalized;
}
function isJsonObjectValue(value) {
	return value !== null && !Array.isArray(value) && typeof value === "object";
}
function isPlainObject(value) {
	const prototype = Object.getPrototypeOf(value);
	return prototype === null || prototype === Object.prototype;
}

//#endregion
//#region src/shared/json-schema.ts
const STANDARD_JSON_SCHEMA_TARGET = "draft-07";
function normalizeJsonSchemaDefinition(value, direction = "input") {
	if (isStandardSchema(value)) return parseJsonObject(value["~standard"].jsonSchema[direction]({ target: STANDARD_JSON_SCHEMA_TARGET }));
	return parseJsonObject(value);
}
function isStandardSchema(value) {
	return value !== null && typeof value === "object" && "~standard" in value;
}

//#endregion
//#region src/client/output-schema.ts
function normalizeOutputSchemaForRequest(schema) {
	return schema === void 0 ? void 0 : normalizeJsonSchemaDefinition(schema, "output");
}
function extractCompletedResult(events) {
	let result;
	for (const event of events) if (isResultCompletedEvent(event)) result = event.data.result;
	return result;
}
function isResultCompletedEvent(event) {
	return event.type === "result.completed";
}

//#endregion
//#region src/client/session-utils.ts
function createInitialSessionState() {
	return { streamIndex: 0 };
}
function advanceSession(input) {
	const boundaryEvent = findBoundaryEvent(input.events);
	const streamIndex = input.session.streamIndex + input.events.length;
	if (boundaryEvent?.type === "session.waiting") return {
		continuationToken: input.continuationToken ?? input.session.continuationToken,
		sessionId: input.sessionId,
		streamIndex
	};
	return createInitialSessionState();
}
function extractCompletedMessage(events) {
	let lastMessage;
	for (const event of events) if (isFinalMessageCompleted(event)) lastMessage = event.data.message ?? void 0;
	return lastMessage;
}
function deriveResultStatus(events) {
	const boundary = findBoundaryEvent(events);
	if (boundary?.type === "session.waiting") return "waiting";
	if (boundary?.type === "session.failed") return "failed";
	return "completed";
}
function extractInputRequests(events) {
	const requests = [];
	for (const event of events) if (event.type === "input.requested") requests.push(...event.data.requests);
	return requests;
}
function findBoundaryEvent(events) {
	for (let i = events.length - 1; i >= 0; i--) {
		const event = events[i];
		if (event !== void 0 && isCurrentTurnBoundaryEvent(event)) return event;
	}
}
function isFinalMessageCompleted(event) {
	return event.type === "message.completed" && event.data.finishReason !== "tool-calls";
}

//#endregion
//#region src/client/message-response.ts
var MessageResponse = class {
	continuationToken;
	sessionId;
	#consumed = false;
	#createStream;
	constructor(input) {
		this.continuationToken = input.continuationToken;
		this.sessionId = input.sessionId;
		this.#createStream = input.createStream;
	}
	async result() {
		const events = [];
		for await (const event of this) events.push(event);
		return {
			data: extractCompletedResult(events),
			events,
			inputRequests: extractInputRequests(events),
			message: extractCompletedMessage(events),
			sessionId: this.sessionId,
			status: deriveResultStatus(events)
		};
	}
	[Symbol.asyncIterator]() {
		if (this.#consumed) throw new Error("MessageResponse has already been consumed.");
		this.#consumed = true;
		return this.#createStream();
	}
};

//#endregion
//#region src/client/ndjson.ts
function isStreamDisconnectError(error) {
	if (error instanceof DOMException) return error.name === "AbortError";
	if (!(error instanceof Error)) return false;
	const errorCode = "code" in error && typeof error.code === "string" ? error.code : void 0;
	return error.name === "AbortError" || error.message === "terminated" || errorCode === "UND_ERR_SOCKET" || /abort|cancel|disconnect|premature close|socket|terminated/i.test(error.message);
}
async function* readNdjsonStream(body) {
	const reader = body.getReader();
	const decoder = new TextDecoder();
	let buffer = "";
	try {
		while (true) {
			const result = await reader.read();
			if (result.done) {
				buffer += decoder.decode();
				break;
			}
			if (result.value) buffer += decoder.decode(result.value, { stream: true });
			let newlineIndex = buffer.indexOf("\n");
			while (newlineIndex !== -1) {
				const line = buffer.slice(0, newlineIndex).trim();
				buffer = buffer.slice(newlineIndex + 1);
				if (line.length > 0) yield JSON.parse(line);
				newlineIndex = buffer.indexOf("\n");
			}
		}
		const trailing = buffer.trim();
		if (trailing.length > 0) yield JSON.parse(trailing);
	} finally {
		reader.releaseLock();
	}
}

//#endregion
//#region src/client/url.ts
function createClientUrl(host, routePath, searchParams) {
	const normalizedRoute = routePath.startsWith("/") ? routePath : `/${routePath}`;
	const search = formatSearch(searchParams);
	if (isAbsoluteUrl(host)) {
		const url = new URL(host);
		url.pathname = `${trimTrailingSlash(url.pathname)}${normalizedRoute}`;
		url.search = search;
		url.hash = "";
		return url.toString();
	}
	return `${trimTrailingSlash(host)}${normalizedRoute}${search}`;
}
function isAbsoluteUrl(value) {
	return /^[a-z][a-z\d+\-.]*:/i.test(value);
}
function trimTrailingSlash(value) {
	if (value === "/") return "";
	return value.endsWith("/") ? value.slice(0, -1) : value;
}
function formatSearch(searchParams) {
	if (!searchParams || Object.keys(searchParams).length === 0) return "";
	return `?${new URLSearchParams(searchParams).toString()}`;
}

//#endregion
//#region src/client/open-stream.ts
const STREAM_OPEN_RETRY_ATTEMPTS = 12;
const STREAM_OPEN_RETRY_DELAY_MS = 250;
const STREAM_OPEN_RETRYABLE_STATUS = new Set([
	404,
	409,
	425,
	500,
	502,
	503,
	504
]);
async function* openStreamIterable(input) {
	let startIndex = input.startIndex;
	let remainingReconnectAttempts = input.maxReconnectAttempts;
	while (true) {
		const body = await openStreamBody({
			...input,
			startIndex
		});
		let disconnected = false;
		try {
			for await (const event of readNdjsonStream(body)) {
				startIndex += 1;
				yield event;
			}
		} catch (error) {
			if (!isStreamDisconnectError(error)) throw error;
			disconnected = true;
		}
		if (!disconnected || remainingReconnectAttempts <= 0) return;
		remainingReconnectAttempts -= 1;
	}
}
async function openStreamBody(input) {
	let lastStatus;
	let lastBody;
	for (let attempt = 0; attempt < STREAM_OPEN_RETRY_ATTEMPTS; attempt += 1) {
		const url = createClientUrl(input.host, createEveMessageStreamRoutePath(input.sessionId), input.startIndex > 0 ? { startIndex: String(input.startIndex) } : void 0);
		const headers = await input.resolveHeaders();
		const response = await fetch(url, {
			headers,
			signal: input.signal ?? null
		});
		if (response.ok) {
			if (!response.body) throw new ClientError(response.status, "Response body is null.");
			return response.body;
		}
		lastStatus = response.status;
		lastBody = await response.text();
		if (!STREAM_OPEN_RETRYABLE_STATUS.has(response.status)) throw new ClientError(response.status, lastBody);
		if (attempt < STREAM_OPEN_RETRY_ATTEMPTS - 1) await sleep$1(STREAM_OPEN_RETRY_DELAY_MS);
	}
	throw new ClientError(lastStatus ?? 0, lastBody ?? "Failed to open message stream.");
}
async function sleep$1(ms) {
	await new Promise((resolve) => setTimeout(resolve, ms));
}

//#endregion
//#region src/client/session.ts
const DELIVER_RETRY_ATTEMPTS = 10;
const DELIVER_RETRY_DELAY_MS = 200;
var ClientSession = class {
	#context;
	#state;
	constructor(context, state) {
		this.#context = context;
		this.#state = state;
	}
	get state() {
		return this.#state;
	}
	async send(input) {
		const payload = normalizeSendTurnInput(input);
		const state = this.#state;
		const { continuationToken, sessionId } = await this.#postTurn(payload, state);
		return new MessageResponse({
			continuationToken,
			createStream: () => this.#createEventStream(sessionId, continuationToken, state, payload),
			sessionId
		});
	}
	stream(options) {
		const sessionId = this.#state.sessionId;
		if (!sessionId) throw new Error("Session has no session ID. Send a message first.");
		return this.#streamAndAdvance(sessionId, options);
	}
	async #postTurn(input, session) {
		const routePath = session.sessionId ? createEveContinueSessionRoutePath(session.sessionId) : EVE_CREATE_SESSION_ROUTE_PATH;
		const url = createClientUrl(this.#context.host, routePath);
		const headers = await this.#context.resolveHeaders(input.headers);
		headers.set("content-type", "application/json");
		const body = createHandleMessageBody({
			input,
			outputSchema: normalizeOutputSchemaForRequest(input.outputSchema),
			session
		});
		if (body === null) throw new Error("Session.send requires a non-empty message, inputResponses, or both.");
		const response = await postTurnWithRetry({
			body: JSON.stringify(body),
			headers,
			mustDeliver: (input.inputResponses?.length ?? 0) > 0,
			signal: input.signal,
			url
		});
		const payload = await response.json();
		const sessionId = (typeof payload.sessionId === "string" ? payload.sessionId : void 0) ?? response.headers.get("x-eve-session-id")?.trim() ?? session.sessionId;
		if (!sessionId) throw new Error("Message route did not return a session id.");
		return {
			continuationToken: typeof payload.continuationToken === "string" ? payload.continuationToken : void 0,
			sessionId
		};
	}
	async *#createEventStream(sessionId, continuationToken, initialState, input) {
		const events = [];
		try {
			let currentStreamIndex = initialState.sessionId === sessionId ? initialState.streamIndex : 0;
			let remainingReconnectAttempts = this.#context.maxReconnectAttempts;
			while (true) {
				const body = await this.#openStreamBody(sessionId, currentStreamIndex, input.signal, input.headers);
				let foundBoundary = false;
				try {
					for await (const event of readNdjsonStream(body)) {
						events.push(event);
						currentStreamIndex += 1;
						yield event;
						if (isCurrentTurnBoundaryEvent(event)) {
							foundBoundary = true;
							break;
						}
					}
				} catch (error) {
					if (!isStreamDisconnectError(error)) throw error;
				}
				if (foundBoundary) break;
				if (remainingReconnectAttempts <= 0) break;
				remainingReconnectAttempts -= 1;
			}
		} finally {
			this.#state = advanceSession({
				continuationToken,
				events,
				sessionId,
				session: initialState
			});
		}
	}
	async #openStreamBody(sessionId, startIndex, signal, headers) {
		return await openStreamBody({
			host: this.#context.host,
			resolveHeaders: () => this.#context.resolveHeaders(headers),
			sessionId,
			signal,
			startIndex
		});
	}
	async *#streamAndAdvance(sessionId, options) {
		const initialState = this.#state;
		const streamIndex = options?.startIndex ?? initialState.streamIndex;
		const events = [];
		try {
			for await (const event of openStreamIterable({
				host: this.#context.host,
				maxReconnectAttempts: this.#context.maxReconnectAttempts,
				resolveHeaders: () => this.#context.resolveHeaders(),
				sessionId,
				signal: options?.signal,
				startIndex: streamIndex
			})) {
				events.push(event);
				yield event;
			}
		} finally {
			this.#state = advanceSession({
				continuationToken: initialState.continuationToken,
				events,
				session: {
					...initialState,
					sessionId,
					streamIndex
				},
				sessionId
			});
		}
	}
};
async function postTurnWithRetry(input) {
	const attempts = input.mustDeliver ? DELIVER_RETRY_ATTEMPTS : 1;
	let lastStatus;
	let lastBody;
	for (let attempt = 0; attempt < attempts; attempt += 1) {
		const response = await fetch(input.url, {
			body: input.body,
			headers: input.headers,
			method: "POST",
			signal: input.signal ?? null
		});
		if (response.ok) return response;
		lastStatus = response.status;
		lastBody = await response.text();
		if (!isRetryableDeliveryFailure(response.status, lastBody)) throw new ClientError(response.status, lastBody);
		if (attempt < attempts - 1) await sleep(DELIVER_RETRY_DELAY_MS);
	}
	throw new ClientError(lastStatus ?? 0, lastBody ?? "Failed to deliver session turn.");
}
function isRetryableDeliveryFailure(status, body) {
	return status === 500 && /target session was not found/i.test(body);
}
async function sleep(ms) {
	await new Promise((resolve) => setTimeout(resolve, ms));
}
function normalizeSendTurnInput(input) {
	return typeof input === "string" ? { message: input } : input;
}
function createHandleMessageBody(input) {
	const body = {};
	if (input.input.message !== void 0) body.message = input.input.message;
	if (input.input.inputResponses !== void 0 && input.input.inputResponses.length > 0) body.inputResponses = input.input.inputResponses;
	if (input.input.clientContext !== void 0) body.clientContext = input.input.clientContext;
	if (input.outputSchema !== void 0) body.outputSchema = input.outputSchema;
	if (input.session.continuationToken !== void 0) body.continuationToken = input.session.continuationToken;
	if (Object.keys(body).length === 0) return null;
	if (input.session.continuationToken === void 0 && body.message === void 0) return null;
	if (input.session.continuationToken !== void 0 && body.message === void 0 && body.inputResponses === void 0) return null;
	return body;
}

//#endregion
//#region src/client/client.ts
var Client = class {
	#auth;
	#headers;
	#host;
	#maxReconnectAttempts;
	constructor(options) {
		this.#host = options.host;
		this.#auth = options.auth;
		this.#headers = options.headers;
		this.#maxReconnectAttempts = options.maxReconnectAttempts ?? 3;
	}
	async health() {
		const url = createClientUrl(this.#host, EVE_HEALTH_ROUTE_PATH);
		const headers = await this.#resolveHeaders();
		const response = await fetch(url, { headers });
		if (!response.ok) {
			const body = await response.text();
			throw new ClientError(response.status, body);
		}
		return await response.json();
	}
	async info() {
		const url = createClientUrl(this.#host, EVE_INFO_ROUTE_PATH);
		const headers = await this.#resolveHeaders();
		const response = await fetch(url, { headers });
		if (!response.ok) {
			const body = await response.text();
			throw new ClientError(response.status, body);
		}
		return await response.json();
	}
	async fetch(path, init = {}) {
		const url = createClientUrl(this.#host, path);
		const headers = await this.#resolveHeaders(headersInitToRecord(init.headers));
		return await fetch(url, {
			...init,
			headers
		});
	}
	session(state) {
		let resolved;
		if (typeof state === "string") resolved = {
			continuationToken: state,
			streamIndex: 0
		};
		else if (state) resolved = state;
		else resolved = createInitialSessionState();
		return new ClientSession({
			host: this.#host,
			maxReconnectAttempts: this.#maxReconnectAttempts,
			resolveHeaders: (perRequest) => this.#resolveHeaders(perRequest)
		}, resolved);
	}
	async #resolveHeaders(perRequest) {
		const headers = new Headers();
		const baseHeaders = await resolveHeadersValue(this.#headers);
		for (const [key, value] of Object.entries(baseHeaders)) headers.set(key, value);
		if (perRequest) for (const [key, value] of Object.entries(perRequest)) headers.set(key, value);
		const authorization = await this.#resolveAuthorizationHeader();
		if (authorization) headers.set("authorization", authorization);
		return headers;
	}
	async #resolveAuthorizationHeader() {
		const auth = this.#auth;
		if (!auth) return void 0;
		if ("bearer" in auth) {
			const token = (await resolveTokenValue(auth.bearer)).trim();
			if (token.length === 0) return void 0;
			return `Bearer ${token}`;
		}
		if ("basic" in auth) {
			const password = await resolveTokenValue(auth.basic.password);
			return `Basic ${encodeBasicCredentials(auth.basic.username, password)}`;
		}
	}
};
async function resolveTokenValue(value) {
	return typeof value === "function" ? value() : value;
}
async function resolveHeadersValue(value) {
	if (value === void 0) return {};
	return typeof value === "function" ? await value() : value;
}
function headersInitToRecord(headers) {
	if (headers === void 0) return {};
	return Object.fromEntries(new Headers(headers).entries());
}
function encodeBasicCredentials(username, password) {
	const bytes = new TextEncoder().encode(`${username}:${password}`);
	const binaryString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
	return btoa(binaryString);
}

//#endregion
//#region src/shared/errors.ts
function toErrorMessage(error) {
	if (error instanceof Error) return error.message;
	if (typeof error === "string") return error;
	if (error === null || error === void 0) return String(error);
	if (isObject(error)) {
		if (typeof error.message === "string" && error.message.length > 0) return error.message;
		return safeJsonStringify(error);
	}
	return String(error);
}
function toError(raw) {
	if (raw instanceof Error) return raw;
	const error = new Error(toErrorMessage(raw));
	if (!isObject(raw)) return error;
	if (typeof raw.name === "string" && raw.name.length > 0) error.name = raw.name;
	if (typeof raw.stack === "string" && raw.stack.length > 0) error.stack = raw.stack;
	if ("cause" in raw && raw.cause !== void 0 && raw.cause !== raw) error.cause = raw.cause;
	return error;
}
function safeJsonStringify(value) {
	try {
		return JSON.stringify(value) ?? String(value);
	} catch {
		return String(value);
	}
}

//#endregion
//#region src/client/eve-agent-store.ts
var EveAgentStore = class {
	#createSession;
	#optimistic;
	#reducer;
	#subscribers = /* @__PURE__ */ new Set();
	#abortController;
	#callbacks = {};
	#data;
	#error;
	#events;
	#operationId = 0;
	#pendingMessageSubmission;
	#projectionEvents;
	#session;
	#snapshot;
	#status = "ready";
	constructor(init) {
		this.#createSession = init.session ? void 0 : () => new Client({
			auth: init.auth,
			headers: init.headers,
			host: init.host ?? "",
			maxReconnectAttempts: init.maxReconnectAttempts
		}).session(init.initialSession);
		this.#events = [...init.initialEvents ?? []];
		this.#projectionEvents = [...this.#events];
		this.#optimistic = init.optimistic ?? true;
		this.#reducer = init.reducer;
		this.#session = init.session ?? this.#createOwnedSession();
		this.#data = this.#reduceProjectionEvents(this.#projectionEvents);
		this.#snapshot = this.#createSnapshot();
	}
	get snapshot() {
		return this.#snapshot;
	}
	setCallbacks(callbacks) {
		this.#callbacks = callbacks;
	}
	subscribe(callback) {
		this.#subscribers.add(callback);
		return () => {
			this.#subscribers.delete(callback);
		};
	}
	async send(input) {
		if (this.#status === "streaming" || this.#status === "submitted") throw new Error("Eve session is already processing a turn.");
		const operationId = this.#startOperation();
		const abortController = new AbortController();
		this.#abortController = abortController;
		this.#error = void 0;
		this.#status = "submitted";
		this.#publish();
		try {
			const preparedInput = await this.#callbacks.prepareSend?.(input) ?? input;
			if (!this.#isCurrentOperation(operationId)) return;
			this.#projectOptimisticMessage(preparedInput);
			this.#projectInputResponses(preparedInput);
			this.#publish();
			const response = await this.#session.send({
				...preparedInput,
				signal: createAbortSignal(preparedInput.signal, abortController.signal)
			});
			let sawEvent = false;
			for await (const event of response) {
				if (!this.#isCurrentOperation(operationId)) return;
				if (!sawEvent) {
					sawEvent = true;
					this.#status = "streaming";
				}
				this.#events = [...this.#events, event];
				this.#applyServerEvent(event);
				this.#callbacks.onEvent?.(event);
				this.#applyTerminalStreamFailure(event);
				this.#publish();
			}
			if (!this.#isCurrentOperation(operationId)) return;
			this.#status = this.#error === void 0 ? "ready" : "error";
		} catch (error) {
			if (!this.#isCurrentOperation(operationId)) return;
			if (isAbortError(error)) {
				this.#status = "ready";
				this.#failPendingMessageSubmission(toError(error));
			} else {
				this.#error = toError(error);
				this.#status = "error";
				this.#failPendingMessageSubmission(this.#error);
				this.#callbacks.onError?.(this.#error);
			}
		} finally {
			if (this.#isCurrentOperation(operationId)) {
				this.#abortController = void 0;
				this.#callbacks.onSessionChange?.(this.#session.state);
				this.#publish();
				this.#callbacks.onFinish?.(this.#snapshot);
			}
		}
	}
	stop() {
		this.#abortController?.abort();
	}
	reset() {
		this.#invalidateOperation();
		this.stop();
		this.#abortController = void 0;
		this.#session = this.#createSession?.() ?? this.#session;
		this.#events = [];
		this.#pendingMessageSubmission = void 0;
		this.#projectionEvents = [];
		this.#data = this.#reducer.initial();
		this.#error = void 0;
		this.#status = "ready";
		this.#callbacks.onSessionChange?.(this.#session.state);
		this.#publish();
	}
	#createOwnedSession() {
		if (!this.#createSession) throw new Error("Cannot create an owned Eve session from an external session.");
		return this.#createSession();
	}
	#startOperation() {
		this.#operationId += 1;
		return this.#operationId;
	}
	#invalidateOperation() {
		this.#operationId += 1;
	}
	#isCurrentOperation(operationId) {
		return this.#operationId === operationId;
	}
	#projectOptimisticMessage(input) {
		if (!this.#optimistic || input.message === void 0) return;
		const id = createSubmissionId();
		const pending = {
			createdAt: Date.now(),
			id,
			message: summarizeUserContent(input.message)
		};
		this.#pendingMessageSubmission = pending;
		this.#appendProjectionEvent({
			data: {
				createdAt: pending.createdAt,
				message: pending.message,
				submissionId: pending.id
			},
			type: "client.message.submitted"
		});
	}
	#projectInputResponses(input) {
		if (input.inputResponses === void 0 || input.inputResponses.length === 0) return;
		this.#appendProjectionEvent({
			data: {
				createdAt: Date.now(),
				responses: input.inputResponses
			},
			type: "client.input.responded"
		});
	}
	#applyServerEvent(event) {
		if (event.type === "message.received" && this.#pendingMessageSubmission !== void 0) {
			const submissionId = this.#pendingMessageSubmission.id;
			this.#pendingMessageSubmission = void 0;
			this.#replaceProjectionEvent((candidate) => candidate.type === "client.message.submitted" && candidate.data.submissionId === submissionId, event);
			return;
		}
		this.#appendProjectionEvent(event);
	}
	#applyTerminalStreamFailure(event) {
		const error = toTerminalStreamFailureError(event);
		if (error === void 0) return;
		this.#status = "error";
		this.#failPendingMessageSubmission(error);
		if (this.#error === void 0) {
			this.#error = error;
			this.#callbacks.onError?.(error);
		}
	}
	#failPendingMessageSubmission(error) {
		const pending = this.#pendingMessageSubmission;
		if (pending === void 0) return;
		this.#pendingMessageSubmission = void 0;
		this.#replaceProjectionEvent((event) => event.type === "client.message.submitted" && event.data.submissionId === pending.id, {
			data: {
				createdAt: pending.createdAt,
				error: { message: error.message },
				message: pending.message,
				submissionId: pending.id
			},
			type: "client.message.failed"
		});
	}
	#appendProjectionEvent(event) {
		this.#projectionEvents = [...this.#projectionEvents, event];
		this.#data = this.#reducer.reduce(this.#data, event);
	}
	#replaceProjectionEvent(predicate, replacement) {
		let replaced = false;
		this.#projectionEvents = this.#projectionEvents.map((event) => {
			if (!replaced && predicate(event)) {
				replaced = true;
				return replacement;
			}
			return event;
		});
		if (!replaced) this.#projectionEvents = [...this.#projectionEvents, replacement];
		this.#data = this.#reduceProjectionEvents(this.#projectionEvents);
	}
	#reduceProjectionEvents(events) {
		let data = this.#reducer.initial();
		for (const event of events) data = this.#reducer.reduce(data, event);
		return data;
	}
	#createSnapshot() {
		return {
			data: this.#data,
			error: this.#error,
			events: this.#events,
			session: this.#session.state,
			status: this.#status
		};
	}
	#publish() {
		this.#snapshot = this.#createSnapshot();
		for (const subscriber of this.#subscribers) subscriber();
	}
};
let submissionSequence = 0;
function createSubmissionId() {
	const randomUUID = globalThis.crypto?.randomUUID;
	if (randomUUID !== void 0) return randomUUID.call(globalThis.crypto);
	submissionSequence += 1;
	return `submission_${submissionSequence.toString()}`;
}
function createAbortSignal(first, second) {
	return first ? AbortSignal.any([first, second]) : second;
}
function summarizeUserContent(message) {
	if (typeof message === "string") return message;
	const parts = [];
	for (const part of message) {
		if (part.type === "text") {
			parts.push(part.text);
			continue;
		}
		if (part.type === "file") parts.push(part.filename ? `[file: ${part.filename}]` : "[file]");
	}
	return parts.join("\n");
}
function isAbortError(error) {
	return error instanceof Error && error.name === "AbortError";
}
function toTerminalStreamFailureError(event) {
	if (event.type !== "session.failed") return;
	const error = new Error(event.data.message);
	error.name = event.data.code;
	return error;
}

//#endregion
//#region src/client/message-reducer.ts
function defaultMessageReducer() {
	return {
		initial() {
			return { messages: [] };
		},
		reduce(data, event) {
			return reduceMessageData(data, event);
		}
	};
}
function reduceMessageData(data, event) {
	switch (event.type) {
		case "client.message.submitted": return upsertMessage(data, {
			id: optimisticUserMessageId(event.data.submissionId),
			metadata: {
				optimistic: true,
				status: "submitted"
			},
			parts: [{
				type: "text",
				text: event.data.message
			}],
			role: "user"
		});
		case "client.message.failed": return upsertMessage(data, {
			id: optimisticUserMessageId(event.data.submissionId),
			metadata: {
				optimistic: true,
				status: "failed"
			},
			parts: [{
				type: "text",
				text: event.data.message
			}],
			role: "user"
		});
		case "client.input.responded": {
			let next = data;
			for (const response of event.data.responses) next = respondToInputRequest(next, response);
			return next;
		}
		case "message.received": return upsertMessage(data, {
			id: `${event.data.turnId}:user`,
			metadata: {
				status: "complete",
				turnId: event.data.turnId
			},
			parts: [{
				type: "text",
				text: event.data.message,
				state: "done"
			}],
			role: "user"
		});
		case "step.started": return updateAssistantMessage(data, event.data.turnId, (message) => ensureStepStartPart(message, event.data.stepIndex));
		case "reasoning.appended": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
			state: "streaming",
			stepIndex: event.data.stepIndex,
			text: event.data.reasoningSoFar,
			type: "reasoning"
		}));
		case "reasoning.completed": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
			state: "done",
			stepIndex: event.data.stepIndex,
			text: event.data.reasoning,
			type: "reasoning"
		}));
		case "actions.requested": {
			let next = data;
			for (const action of event.data.actions) {
				const descriptor = normalizeActionRequest(action);
				next = updateAssistantMessage(next, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
					input: "input" in action ? action.input : void 0,
					state: "input-available",
					stepIndex: event.data.stepIndex,
					toolCallId: action.callId,
					toolMetadata: createToolMetadata(descriptor),
					toolName: descriptor.toolName,
					type: "dynamic-tool"
				}));
			}
			return next;
		}
		case "input.requested": {
			let next = data;
			for (const request of event.data.requests) {
				const descriptor = normalizeActionRequest(request.action);
				next = updateAssistantMessage(next, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
					approval: { id: request.requestId },
					input: request.action.input,
					state: "approval-requested",
					stepIndex: event.data.stepIndex,
					toolCallId: request.action.callId,
					toolMetadata: createToolMetadata(descriptor, { inputRequest: toMessageInputRequest(request) }),
					toolName: descriptor.toolName,
					type: "dynamic-tool"
				}));
			}
			return next;
		}
		case "action.result": {
			const descriptor = normalizeActionResult(event.data.result);
			const existing = findToolPart(data, event.data.result.callId);
			const denied = event.data.error?.code === "TOOL_EXECUTION_DENIED";
			const failed = event.data.status === "failed" && !denied;
			const approvalId = existing?.approval?.id ?? event.data.result.callId;
			const toolMetadata = mergeToolMetadata(existing?.toolMetadata, createToolMetadata(descriptor));
			const resultPartBase = {
				input: existing?.input,
				stepIndex: event.data.stepIndex,
				toolCallId: event.data.result.callId,
				toolMetadata,
				toolName: existing?.toolName ?? descriptor.toolName,
				type: "dynamic-tool"
			};
			let nextPart;
			if (denied) nextPart = {
				...resultPartBase,
				approval: {
					approved: false,
					id: approvalId,
					reason: event.data.error?.message
				},
				state: "output-denied"
			};
			else if (failed) nextPart = {
				...resultPartBase,
				approval: approvedApproval(existing),
				errorText: event.data.error?.message ?? stringifyUnknown(event.data.result.output),
				state: "output-error"
			};
			else nextPart = {
				...resultPartBase,
				approval: approvedApproval(existing),
				output: event.data.result.output,
				state: "output-available"
			};
			if (existing !== void 0) return updateToolPart(data, event.data.result.callId, nextPart);
			return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), nextPart));
		}
		case "message.appended": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
			state: "streaming",
			stepIndex: event.data.stepIndex,
			text: event.data.messageSoFar,
			type: "text"
		}));
		case "message.completed": return updateAssistantMessage(data, event.data.turnId, (message) => {
			if (event.data.message === null) return completeExistingTextPart(message);
			return upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
				state: "done",
				stepIndex: event.data.stepIndex,
				text: event.data.message,
				type: "text"
			});
		});
		case "result.completed": return updateAssistantMetadata(data, event.data.turnId, { result: event.data.result });
		case "turn.completed": return updateAssistantMetadata(data, event.data.turnId, { status: "complete" });
		case "turn.failed":
		case "session.failed": return data;
		default: return data;
	}
}
function respondToInputRequest(data, response) {
	const existing = findToolPartByApprovalId(data, response.requestId);
	if (!existing) return data;
	const approval = { id: response.requestId };
	if (response.text !== void 0) approval.reason = response.text;
	return updateToolPart(data, existing.toolCallId, {
		approval,
		input: existing.input,
		state: "approval-responded",
		stepIndex: existing.stepIndex,
		toolCallId: existing.toolCallId,
		toolMetadata: mergeToolMetadata(existing.toolMetadata, { eve: {
			inputResponse: response,
			kind: existing.toolMetadata?.eve?.kind ?? "unknown",
			name: existing.toolMetadata?.eve?.name ?? existing.toolName
		} }),
		toolName: existing.toolName,
		type: "dynamic-tool"
	});
}
function updateAssistantMessage(data, turnId, update) {
	return upsertMessage(data, update(data.messages.find((message) => message.role === "assistant" && message.metadata?.turnId === turnId) ?? createAssistantMessage(turnId)));
}
function updateAssistantMetadata(data, turnId, metadata) {
	return updateAssistantMessage(data, turnId, (message) => ({
		...message,
		metadata: {
			...message.metadata,
			...metadata
		}
	}));
}
function createAssistantMessage(turnId) {
	return {
		id: `${turnId}:assistant`,
		metadata: {
			status: "streaming",
			turnId
		},
		parts: [],
		role: "assistant"
	};
}
function ensureStepStartPart(message, stepIndex) {
	const stepStartCount = message.parts.filter((part) => part.type === "step-start").length;
	if (stepStartCount > stepIndex) return message;
	const missingCount = stepIndex - stepStartCount + 1;
	return {
		...message,
		parts: [...message.parts, ...Array.from({ length: missingCount }, () => ({ type: "step-start" }))]
	};
}
function upsertPart(message, next) {
	const index = message.parts.findIndex((part) => partKey(part) === partKey(next));
	const parts = index === -1 ? [...message.parts, next] : [
		...message.parts.slice(0, index),
		next,
		...message.parts.slice(index + 1)
	];
	return {
		...message,
		metadata: {
			...message.metadata,
			status: next.type === "text" && next.state === "done" ? "complete" : "streaming"
		},
		parts
	};
}
function completeExistingTextPart(message) {
	const index = findLastIndex(message.parts, (part) => part.type === "text");
	if (index === -1) return message;
	const existing = message.parts[index];
	if (existing?.type !== "text") return message;
	return {
		...message,
		metadata: {
			...message.metadata,
			status: "complete"
		},
		parts: [
			...message.parts.slice(0, index),
			{
				...existing,
				state: "done"
			},
			...message.parts.slice(index + 1)
		]
	};
}
function updateToolPart(data, toolCallId, next) {
	const message = data.messages.find((candidate) => candidate.role === "assistant" && candidate.parts.some((part) => part.type === "dynamic-tool" && part.toolCallId === toolCallId));
	if (!message) return data;
	return upsertMessage(data, upsertPart(message, next));
}
function findToolPart(data, toolCallId) {
	for (const message of data.messages) for (const part of message.parts) if (part.type === "dynamic-tool" && part.toolCallId === toolCallId) return part;
}
function findToolPartByApprovalId(data, approvalId) {
	for (const message of data.messages) for (const part of message.parts) if (part.type === "dynamic-tool" && part.approval?.id === approvalId) return part;
}
function partKey(part) {
	switch (part.type) {
		case "text": return `text:${part.stepIndex ?? 0}`;
		case "reasoning": return `reasoning:${part.stepIndex ?? 0}`;
		case "step-start": return "step-start";
		case "dynamic-tool": return `dynamic-tool:${part.toolCallId}`;
	}
}
function upsertMessage(data, next) {
	const index = data.messages.findIndex((message) => message.id === next.id);
	if (index === -1) return { messages: [...data.messages, next] };
	return { messages: [
		...data.messages.slice(0, index),
		next,
		...data.messages.slice(index + 1)
	] };
}
function toMessageInputRequest(request) {
	return {
		allowFreeform: request.allowFreeform,
		display: request.display,
		options: request.options,
		prompt: request.prompt,
		requestId: request.requestId
	};
}
function createToolMetadata(descriptor, extra) {
	return { eve: {
		inputRequest: extra?.inputRequest,
		kind: descriptor.kind,
		name: descriptor.name
	} };
}
function mergeToolMetadata(current, next) {
	const kind = next.eve?.kind ?? current?.eve?.kind ?? "unknown";
	const name = next.eve?.name ?? current?.eve?.name ?? "unknown";
	return { eve: {
		...current?.eve,
		...next.eve,
		inputRequest: next.eve?.inputRequest ?? current?.eve?.inputRequest,
		inputResponse: next.eve?.inputResponse ?? current?.eve?.inputResponse,
		kind,
		name
	} };
}
function approvedApproval(part) {
	if (!part?.approval?.id) return;
	return {
		approved: true,
		id: part.approval.id,
		isAutomatic: part.approval.isAutomatic,
		reason: part.approval.reason
	};
}
function normalizeActionRequest(action) {
	switch (action.kind) {
		case "load-skill": return {
			kind: "load-skill",
			name: "load_skill",
			toolName: "eve:load-skill"
		};
		case "tool-call": return {
			kind: "tool-call",
			name: action.toolName,
			toolName: action.toolName
		};
		case "subagent-call": return {
			kind: "subagent-call",
			name: action.subagentName,
			toolName: `eve:subagent:${action.subagentName}`
		};
		case "remote-agent-call": return {
			kind: "subagent-call",
			name: action.remoteAgentName,
			toolName: `eve:subagent:${action.remoteAgentName}`
		};
	}
}
function normalizeActionResult(result) {
	switch (result.kind) {
		case "load-skill-result": return {
			kind: "load-skill",
			name: result.name ?? "load_skill",
			toolName: "eve:load-skill"
		};
		case "tool-result": return {
			kind: "tool-call",
			name: result.toolName,
			toolName: result.toolName
		};
		case "subagent-result": return {
			kind: "subagent-call",
			name: result.subagentName,
			toolName: `eve:subagent:${result.subagentName}`
		};
	}
}
function optimisticUserMessageId(submissionId) {
	return `optimistic:${submissionId}:user`;
}
function stringifyUnknown(value) {
	if (typeof value === "string") return value;
	try {
		return JSON.stringify(value);
	} catch {
		return "Action failed.";
	}
}
function findLastIndex(items, predicate) {
	for (let index = items.length - 1; index >= 0; index -= 1) if (predicate(items[index])) return index;
	return -1;
}

//#endregion
//#region src/vue/use-eve-agent.ts
function useEveAgent(options = {}) {
	const reducer = options.reducer ?? defaultMessageReducer();
	const store = new EveAgentStore({
		auth: options.auth,
		headers: options.headers,
		host: options.host,
		initialEvents: options.initialEvents,
		initialSession: options.initialSession,
		maxReconnectAttempts: options.maxReconnectAttempts,
		optimistic: options.optimistic,
		reducer,
		session: options.session
	});
	store.setCallbacks({
		onError: options.onError,
		onEvent: options.onEvent,
		onFinish: options.onFinish,
		onSessionChange: options.onSessionChange,
		prepareSend: options.prepareSend
	});
	const snapshot = shallowRef(store.snapshot);
	if ("window" in globalThis) {
		const unsubscribe = store.subscribe(() => {
			snapshot.value = store.snapshot;
		});
		onScopeDispose(() => {
			unsubscribe();
			store.stop();
		});
	}
	return {
		data: computed(() => snapshot.value.data),
		error: computed(() => snapshot.value.error),
		events: computed(() => snapshot.value.events),
		reset: () => store.reset(),
		send: (input) => store.send(input),
		session: computed(() => snapshot.value.session),
		status: computed(() => snapshot.value.status),
		stop: () => store.stop()
	};
}

//#endregion
export { defaultMessageReducer as n, useEveAgent as t };