eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
3,428 lines • 118 kB
JavaScript
import { onMount } from "svelte";
import { createSubscriber } from "svelte/reactivity";
import { z } from "#compiled/zod/index.js";
import { asSchema } from "ai";
//#region src/protocol/routes.ts
const EVE_ROUTE_PREFIX = "/eve/v1";
const EVE_PRODUCTION_CRON_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/cron/:token`;
const EVE_HEALTH_ROUTE_PATH = `${EVE_ROUTE_PREFIX}/health`;
const EVE_INFO_ROUTE_PATH = `${EVE_ROUTE_PREFIX}/info`;
const EVE_SESSION_ROUTE_PATH = `${EVE_ROUTE_PREFIX}/session`;
const EVE_SESSION_ROUTE_PATTERN = `${EVE_SESSION_ROUTE_PATH}/:sessionId`;
const EVE_SESSION_CANCEL_ROUTE_PATTERN = `${EVE_SESSION_ROUTE_PATH}/:sessionId/cancel`;
const EVE_SESSION_COMPACT_ROUTE_PATTERN = `${EVE_SESSION_ROUTE_PATH}/:sessionId/compact`;
const EVE_SESSION_CLEAR_ROUTE_PATTERN = `${EVE_SESSION_ROUTE_PATH}/:sessionId/clear`;
const EVE_SESSION_RESET_ROUTE_PATTERN = `${EVE_SESSION_ROUTE_PATH}/:sessionId/reset`;
const EVE_SESSION_STREAM_ROUTE_PATTERN = `${EVE_SESSION_ROUTE_PATH}/:sessionId/stream`;
const EVE_SUBAGENT_STREAM_ROUTE_PATTERN = `${EVE_SESSION_ROUTE_PATH}/:parentSessionId/subagents/:callId/:childSessionId/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_DEV_RUNTIME_ARTIFACTS_REBUILD_ROUTE_PATH = `${EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH}/rebuild`;
const EVE_DEV_RUNTIME_ARTIFACTS_SUSPEND_ROUTE_PATH = `${EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH}/suspend`;
const EVE_DEV_RUNTIME_ARTIFACTS_RESUME_ROUTE_PATH = `${EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH}/resume`;
const EVE_CONNECTION_CALLBACK_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/connections/:name/callback/:attemptId/:token`;
const EVE_CALLBACK_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/callback/:token`;
const EVE_TASK_INPUT_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/task-input/:token`;
const EVE_ACTIVITY_ROUTE_PATTERN = `${EVE_ROUTE_PREFIX}/activity/:token`;
function createEveSessionRoutePath(sessionId) {
return `${EVE_SESSION_ROUTE_PATH}/${encodeURIComponent(sessionId)}`;
}
function createEveSessionCancelRoutePath(sessionId) {
return `${EVE_SESSION_ROUTE_PATH}/${encodeURIComponent(sessionId)}/cancel`;
}
function createEveSessionCompactRoutePath(sessionId) {
return `${EVE_SESSION_ROUTE_PATH}/${encodeURIComponent(sessionId)}/compact`;
}
function createEveSessionClearRoutePath(sessionId) {
return `${EVE_SESSION_ROUTE_PATH}/${encodeURIComponent(sessionId)}/clear`;
}
function createEveSessionResetRoutePath(sessionId) {
return `${EVE_SESSION_ROUTE_PATH}/${encodeURIComponent(sessionId)}/reset`;
}
function createEveSessionStreamRoutePath(sessionId) {
return `${EVE_SESSION_ROUTE_PATH}/${encodeURIComponent(sessionId)}/stream`;
}
//#endregion
//#region src/client/agent-info-error.ts
var AgentInfoResponseError = class extends Error {
issues;
constructor(issues = []) {
const detail = issues.length === 0 ? "" : ` (${issues.join("; ")})`;
super(`The server returned an unrecognized response from the eve agent info route.${detail}`);
this.name = "AgentInfoResponseError";
this.issues = issues;
}
};
//#endregion
//#region src/client/health-response-error.ts
var HealthResponseError = class extends Error {
issues;
constructor(issues = []) {
const detail = issues.length === 0 ? "" : ` (${issues.join("; ")})`;
super(`The server returned an unrecognized eve health response.${detail}`);
this.name = "HealthResponseError";
this.issues = issues;
}
};
//#endregion
//#region src/client/health-schema.ts
const HealthResultSchema = z.object({
ok: z.literal(true),
status: z.literal("ready"),
workflowId: z.string().min(1)
}).strict();
//#endregion
//#region src/internal/http/basic-auth.ts
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/client/agent-info-schema.ts
const owner = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("application") }).strict(),
z.object({
feature: z.string(),
kind: z.literal("framework")
}).strict(),
z.object({
kind: z.literal("extension"),
namespace: z.string(),
packageName: z.string()
}).strict()
]);
const moduleBacking = z.discriminatedUnion("kind", [z.object({
externalDependencies: z.array(z.string()),
extensionScope: z.object({
namespace: z.string(),
sourceRoot: z.string()
}).strict().optional(),
kind: z.literal("filesystem"),
sourcePath: z.string()
}).strict(), z.object({
dependencies: z.record(z.string(), z.string()).optional(),
kind: z.literal("programmatic"),
moduleId: z.string(),
parameters: z.record(z.string(), z.unknown()).optional(),
registryId: z.string(),
revision: z.string(),
semanticRevision: z.string().optional()
}).strict()]);
const binding = z.object({
backing: moduleBacking,
logicalPath: z.string(),
owner
}).strict();
const source = z.object({
binding: binding.optional(),
exportName: z.string().optional(),
logicalPath: z.string(),
owner,
sourceId: z.string(),
sourceKind: z.enum([
"markdown",
"module",
"skill-package"
])
}).strict();
const entry = source.extend({ name: z.string() }).strict();
const dynamicResolver = source.extend({
eventNames: z.array(z.string()),
slug: z.string()
}).strict();
const modelRouting = z.discriminatedUnion("kind", [z.object({
kind: z.literal("gateway"),
target: z.string(),
byok: z.string().optional()
}).strict(), z.object({
kind: z.literal("external"),
provider: z.string()
}).strict()]);
const modelEndpoint = z.union([
z.object({
kind: z.literal("external"),
provider: z.string()
}).strict(),
z.object({
kind: z.literal("chatgpt"),
state: z.enum([
"checking",
"ready",
"signed-out",
"reauth-required",
"unavailable"
]),
accountLabel: z.string().optional()
}).strict(),
z.object({
kind: z.literal("gateway"),
connected: z.literal(true),
credential: z.enum([
"api-key",
"oidc",
"oauth"
]),
team: z.optional(z.string())
}).strict(),
z.object({
kind: z.literal("gateway"),
connected: z.literal(false)
}).strict()
]);
const modelBaseFields = {
contextWindowTokens: z.number().optional(),
providerOptions: z.unknown().optional(),
reasoning: z.enum([
"provider-default",
"none",
"minimal",
"low",
"medium",
"high",
"xhigh"
]).optional().catch(void 0),
source: source.optional()
};
const agentModel = z.union([z.object({
...modelBaseFields,
endpoint: modelEndpoint.optional(),
id: z.string(),
routing: modelRouting
}).strict(), z.object({
...modelBaseFields,
endpoint: z.never().optional(),
id: z.never().optional(),
routing: z.object({
kind: z.literal("dynamic"),
resolver: dynamicResolver
}).strict()
}).strict()]);
const tool = entry.extend({
description: z.string(),
hasAuth: z.boolean(),
hasExecute: z.boolean(),
hasModelOutputProjection: z.boolean(),
hasOutputSchema: z.boolean(),
inputSchema: z.unknown(),
outputSchema: z.unknown().optional(),
requiresApproval: z.boolean()
}).strict();
const skill = entry.extend({
description: z.string(),
license: z.string().optional(),
markdown: z.string(),
metadata: z.record(z.string(), z.string()).optional()
}).strict();
const instructions = entry.extend({
content: z.string(),
role: z.enum(["system", "user"])
}).strict();
const schedule = entry.extend({
cron: z.string(),
hasRun: z.boolean(),
markdown: z.string().optional()
}).strict();
const channelMethod = z.enum([
"GET",
"HEAD",
"POST",
"PUT",
"PATCH",
"DELETE",
"OPTIONS",
"WEBSOCKET"
]);
const channelRoute = entry.extend({
adapterKind: z.string().optional(),
method: channelMethod,
urlPath: z.string()
}).strict();
const sourceDescriptor = z.object({
backing: z.union([moduleBacking, z.object({
kind: z.literal("resource"),
sourcePath: z.string()
}).strict()]),
form: z.enum(["derived", "direct"]),
layer: z.enum([
"framework-default",
"extension-package",
"extension-override",
"application"
]),
logicalPath: z.string(),
owner,
sourceId: z.string()
}).strict();
const shadowedChannelRoute = z.object({
method: channelMethod,
source: sourceDescriptor,
urlPath: z.string(),
winnerSourceId: z.string()
}).strict();
const connection = source.extend({
connectionName: z.string(),
description: z.string(),
hasApproval: z.boolean(),
hasAuthorization: z.boolean(),
hasHeaders: z.boolean(),
protocol: z.string(),
toolFilter: z.unknown().optional(),
url: z.string()
}).strict();
const hook = source.extend({
eventNames: z.array(z.string()),
slug: z.string()
}).strict();
const memory = source.extend({
description: z.string().optional(),
slot: z.string(),
visibility: z.enum(["scope", "session"])
}).strict();
const sandbox = source.extend({
provider: z.string().optional(),
environmentExportName: z.string().optional(),
revisionHash: z.string()
}).strict();
const subagent = entry.extend({
configResolver: dynamicResolver.optional(),
description: z.string().optional(),
entryPath: z.string(),
nodeId: z.string(),
parentNodeId: z.string(),
rootPath: z.string(),
summary: z.object({
channels: z.number(),
connections: z.number(),
hooks: z.number(),
instructions: z.number(),
memories: z.number(),
schedules: z.number(),
skills: z.number(),
tools: z.number()
}).strict()
}).strict();
const remoteAgent = entry.extend({
description: z.string(),
nodeId: z.string(),
parentNodeId: z.string(),
url: z.string().optional()
}).strict();
const kernelEffect = z.object({
action: z.enum([
"subagent-call",
"task-cancel",
"workflow-tool-call"
]).optional(),
audience: z.array(z.enum(["root-session", "delegated-task-child"])),
kind: z.enum(["dispatch", "provider-tool"]),
sourceId: z.string()
}).strict();
const compositionDiagnostic = z.object({
kind: z.enum(["disabled", "shadowed"]),
logicalPath: z.string(),
owner,
sourceId: z.string(),
winnerSourceId: z.string().optional()
}).strict();
const AgentInfoResultSchema = z.object({
agent: z.object({
agentRoot: z.string(),
appRoot: z.string(),
config: source.extend({ binding }).strict(),
description: z.string().optional(),
model: agentModel,
name: z.string(),
nodeId: z.string(),
outputSchema: z.unknown().optional()
}).strict(),
capabilities: z.object({ devRoutes: z.boolean() }).strict(),
channels: z.object({
routes: z.array(channelRoute),
shadowed: z.array(shadowedChannelRoute)
}).strict(),
composition: z.object({
disabled: z.array(compositionDiagnostic),
shadowed: z.array(compositionDiagnostic)
}).strict(),
connections: z.array(connection),
diagnostics: z.object({
discoveryErrors: z.number(),
discoveryWarnings: z.number()
}).strict(),
hooks: z.array(hook),
instructions: z.object({
dynamic: z.array(dynamicResolver),
static: z.array(instructions)
}).strict(),
instrumentation: source.optional(),
kernelEffects: z.array(kernelEffect),
kind: z.literal("eve-agent-info"),
memories: z.array(memory),
mode: z.enum(["development", "production"]),
remoteAgents: z.object({
entries: z.array(remoteAgent),
total: z.number()
}).strict(),
sandbox,
schedules: z.array(schedule),
skills: z.object({
dynamic: z.array(dynamicResolver),
static: z.array(skill)
}).strict(),
subagents: z.object({
local: z.array(subagent),
total: z.number()
}).strict(),
tools: z.object({
dynamic: z.array(dynamicResolver),
static: z.array(tool)
}).strict(),
version: z.literal(5),
workspace: z.object({
resourceRoot: z.unknown(),
rootEntries: z.array(z.string())
}).strict()
}).strict().superRefine((value, context) => {
const assertUnique = (entries, identity, path) => {
const seen = /* @__PURE__ */ new Set();
entries.forEach((entry, index) => {
const key = identity(entry);
if (seen.has(key)) context.addIssue({
code: "custom",
message: `Duplicate public identity "${key}".`,
path: [...path, index]
});
seen.add(key);
});
};
const assertTotal = (total, entries, path) => {
if (!Number.isSafeInteger(total) || total < 0 || total !== entries.length) context.addIssue({
code: "custom",
message: `Expected total ${entries.length}, received ${total}.`,
path: [...path]
});
};
assertUnique(value.tools.static, (entry) => entry.name, ["tools", "static"]);
assertUnique(value.tools.dynamic, (entry) => entry.slug, ["tools", "dynamic"]);
assertUnique(value.skills.static, (entry) => entry.name, ["skills", "static"]);
assertUnique(value.skills.dynamic, (entry) => entry.slug, ["skills", "dynamic"]);
assertUnique(value.instructions.static, (entry) => entry.name, ["instructions", "static"]);
assertUnique(value.instructions.dynamic, (entry) => entry.slug, ["instructions", "dynamic"]);
assertUnique(value.schedules, (entry) => entry.name, ["schedules"]);
assertUnique(value.connections, (entry) => entry.connectionName, ["connections"]);
assertUnique(value.hooks, (entry) => entry.slug, ["hooks"]);
assertUnique(value.memories, (entry) => entry.slot, ["memories"]);
assertUnique(value.channels.routes, (entry) => `${entry.method} ${normalizeRoutePattern(entry.urlPath)}`, ["channels", "routes"]);
assertUnique(value.subagents.local, (entry) => entry.nodeId, ["subagents", "local"]);
assertUnique(value.remoteAgents.entries, (entry) => entry.nodeId, ["remoteAgents", "entries"]);
assertTotal(value.subagents.total, value.subagents.local, ["subagents", "total"]);
assertTotal(value.remoteAgents.total, value.remoteAgents.entries, ["remoteAgents", "total"]);
const boundSources = [
[value.agent.config, ["agent", "config"]],
...value.agent.model.source === void 0 ? [] : [[value.agent.model.source, [
"agent",
"model",
"source"
]]],
...value.agent.model.routing.kind === "dynamic" ? [[value.agent.model.routing.resolver, [
"agent",
"model",
"routing",
"resolver"
]]] : [],
...value.channels.routes.map((entry, index) => [entry, [
"channels",
"routes",
index
]]),
...value.connections.map((entry, index) => [entry, ["connections", index]]),
...value.hooks.map((entry, index) => [entry, ["hooks", index]]),
...value.instructions.dynamic.map((entry, index) => [entry, [
"instructions",
"dynamic",
index
]]),
...value.instructions.static.map((entry, index) => [entry, [
"instructions",
"static",
index
]]),
...value.memories.map((entry, index) => [entry, ["memories", index]]),
...value.instrumentation === void 0 ? [] : [[value.instrumentation, ["instrumentation"]]],
...value.remoteAgents.entries.map((entry, index) => [entry, [
"remoteAgents",
"entries",
index
]]),
...value.schedules.map((entry, index) => [entry, ["schedules", index]]),
...value.skills.dynamic.map((entry, index) => [entry, [
"skills",
"dynamic",
index
]]),
...value.skills.static.map((entry, index) => [entry, [
"skills",
"static",
index
]]),
...value.subagents.local.flatMap((entry, index) => entry.configResolver === void 0 ? [] : [[entry.configResolver, [
"subagents",
"local",
index,
"configResolver"
]]]),
...value.tools.dynamic.map((entry, index) => [entry, [
"tools",
"dynamic",
index
]]),
...value.tools.static.map((entry, index) => [entry, [
"tools",
"static",
index
]]),
[value.sandbox, ["sandbox"]]
];
for (const [entry, path] of boundSources) {
if (entry.sourceKind === "module" && entry.binding === void 0) {
context.addIssue({
code: "custom",
message: "Module source is missing its compiled binding.",
path: [...path, "binding"]
});
continue;
}
if (entry.sourceKind !== "module" && entry.binding !== void 0) {
context.addIssue({
code: "custom",
message: `${entry.sourceKind} source cannot carry a module binding.`,
path: [...path, "binding"]
});
continue;
}
if (entry.binding === void 0) continue;
if (entry.binding.logicalPath !== entry.logicalPath) context.addIssue({
code: "custom",
message: "Source and binding logical paths do not match.",
path: [
...path,
"binding",
"logicalPath"
]
});
if (JSON.stringify(entry.binding.owner) !== JSON.stringify(entry.owner)) context.addIssue({
code: "custom",
message: "Source and binding owners do not match.",
path: [
...path,
"binding",
"owner"
]
});
}
});
function normalizeRoutePattern(path) {
return path.replace(/^\/+|\/+$/g, "").split("/").map((segment) => segment.startsWith(":") || /^\[[^\]]+\]$/.test(segment) ? ":" : segment).join("/");
}
//#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 {
code;
status;
body;
headers;
constructor(status, body, headers) {
let message = body || `Server returned ${status}.`;
let code;
try {
const parsed = JSON.parse(body);
if (isObject(parsed)) {
if (typeof parsed.error === "string") message = parsed.error;
if (typeof parsed.code === "string") code = parsed.code;
}
} catch {}
super(message);
this.name = "ClientError";
this.code = code;
this.status = status;
this.body = body;
this.headers = Object.freeze(Object.fromEntries(new Headers(headers).entries()));
}
};
//#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/shared/ulid.ts
const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
const TIME_CHARS = 10;
const TIME_MAX = 2 ** 48 - 1;
const RANDOM_BYTES = 10;
function createUlidFactory() {
let lastTimeMs = -1;
const lastRandom = new Uint8Array(RANDOM_BYTES);
return function createUlidFromFactory() {
const now = Date.now();
if (!Number.isInteger(now) || now < 0 || now > TIME_MAX) throw new Error(`Cannot mint a ULID: timestamp must be an integer from 0 to ${TIME_MAX}.`);
if (now > lastTimeMs) {
lastTimeMs = now;
randomFill(lastRandom);
} else if (!incrementRandom(lastRandom)) {
if (lastTimeMs === TIME_MAX) throw new Error("Cannot mint a ULID: random component overflowed at the maximum timestamp.");
lastTimeMs += 1;
randomFill(lastRandom);
}
return `${encodeTime(lastTimeMs)}${encodeRandom(lastRandom)}`;
};
}
const createUlid = createUlidFactory();
function randomFill(target) {
const webCrypto = globalThis.crypto;
if (typeof webCrypto?.getRandomValues !== "function") throw new Error("Cannot mint a ULID: globalThis.crypto.getRandomValues is unavailable.");
webCrypto.getRandomValues(target);
}
function encodeTime(timeMs) {
let remaining = timeMs;
let encoded = "";
for (let index = 0; index < TIME_CHARS; index += 1) {
encoded = ENCODING[remaining % 32] + encoded;
remaining = Math.floor(remaining / 32);
}
return encoded;
}
function encodeRandom(bytes) {
let buffer = 0;
let bufferedBits = 0;
let encoded = "";
for (const byte of bytes) {
buffer = buffer << 8 | byte;
bufferedBits += 8;
while (bufferedBits >= 5) {
bufferedBits -= 5;
encoded += ENCODING[buffer >>> bufferedBits & 31];
}
buffer &= (1 << bufferedBits) - 1;
}
return encoded;
}
function incrementRandom(bytes) {
for (let index = bytes.length - 1; index >= 0; index -= 1) {
const byte = bytes[index] ?? 0;
if (byte < 255) {
bytes[index] = byte + 1;
bytes.fill(0, index + 1);
return true;
}
}
return false;
}
//#endregion
//#region src/protocol/message.ts
const EVE_SESSION_ID_HEADER = "x-eve-session-id";
const EVE_STREAM_TAIL_INDEX_HEADER = "x-eve-stream-tail-index";
const EVE_STREAM_VERSION_HEADER = "x-eve-stream-version";
const EVE_STREAM_CONTROL_VERSION_QUERY = "streamControlVersion";
const EVE_STREAM_LEASE_ENDED_CONTROL = {
$eve: "stream.lease-ended",
version: 1
};
const textEncoder = new TextEncoder();
function isCurrentTurnBoundaryEvent(event) {
return event.type === "session.completed" || event.type === "session.failed" || event.type === "session.waiting";
}
function isTurnFailureEvent(event) {
return event.type === "session.failed" || event.type === "step.failed" || event.type === "turn.failed";
}
//#endregion
//#region src/client/session-utils.ts
function summarizeTurnEvents(events) {
let boundary;
let failure;
let message;
const inputRequests = [];
const pendingAuthorizations = /* @__PURE__ */ new Map();
for (const event of events) {
if (isCurrentTurnBoundaryEvent(event)) boundary = event;
if (isTurnFailureEvent(event)) failure = event;
if (isFinalMessageCompleted(event)) message = event.data.message ?? void 0;
if (event.type === "input.requested") inputRequests.push(...event.data.requests);
if (event.type === "authorization.required") pendingAuthorizations.set(event.data.name, event.data);
if (event.type === "authorization.completed") pendingAuthorizations.delete(event.data.name);
}
return {
boundary,
failure,
inputRequests,
message,
pendingAuthorizations: [...pendingAuthorizations.values()],
status: boundary?.type === "session.waiting" ? "waiting" : boundary?.type === "session.failed" ? "failed" : "completed"
};
}
function isFinalMessageCompleted(event) {
return event.type === "message.completed" && event.data.finishReason !== "tool-calls";
}
function updatePendingAuthorizations(pending, event) {
if (event.type === "authorization.required" && event.data.webhookUrl !== void 0) pending.add(event.data.name);
else if (event.type === "authorization.completed") pending.delete(event.data.name);
}
//#endregion
//#region src/client/output-schema.ts
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/message-response.ts
const consumeResponse = Symbol("consumeMessageResponse");
const acceptedDeliveryId = Symbol("acceptedDeliveryId");
var MessageResponse = class {
sessionId;
[acceptedDeliveryId];
#cancelTurn;
#cancellation;
#consumed = false;
#createStream;
#settled = false;
#turnId = Promise.withResolvers();
constructor(input) {
this.#cancelTurn = input.cancelTurn;
this.sessionId = input.sessionId;
this[acceptedDeliveryId] = input.deliveryId;
this.#createStream = input.createStream;
}
cancel() {
if (this.#settled) return Promise.resolve({ status: "no_active_turn" });
if (this.#cancellation !== void 0) return this.#cancellation;
const cancellation = this.#turnId.promise.then((turnId) => turnId === void 0 ? { status: "no_active_turn" } : this.#cancelTurn(turnId));
this.#cancellation = cancellation;
cancellation.catch(() => {
if (!this.#settled && this.#cancellation === cancellation) this.#cancellation = void 0;
});
return cancellation;
}
async result() {
const events = [];
for await (const event of this) events.push(event);
const summary = summarizeTurnEvents(events);
return {
data: extractCompletedResult(events),
events,
inputRequests: summary.inputRequests,
message: summary.message,
sessionId: this.sessionId,
status: summary.status
};
}
[Symbol.asyncIterator]() {
return this[consumeResponse]();
}
[consumeResponse](source) {
if (this.#consumed) throw new Error("MessageResponse has already been consumed.");
this.#consumed = true;
return this.#observeStream(source);
}
async *#observeStream(source) {
try {
for await (const event of this.#createStream(source)) {
if (event.type === "turn.started") this.#turnId.resolve(event.data.turnId);
else if (isCurrentTurnBoundaryEvent(event)) {
this.#settled = true;
this.#turnId.resolve(void 0);
}
yield event;
}
} finally {
this.#turnId.resolve(void 0);
}
}
};
function getMessageResponseDeliveryId(response) {
return response[acceptedDeliveryId];
}
function consumeMessageResponse(response, source) {
return response[consumeResponse](source);
}
//#endregion
//#region src/protocol/message-version.ts
function normalizeMessageStreamEvent(version, event) {
switch (version) {
case "21":
case "22":
case "23":
case "24": return normalizeLegacyMessageStreamEvent(version, event);
case "25": return validateCurrentMessageStreamEvent(event);
default: return assertNever(version);
}
}
function normalizeLegacyMessageStreamEvent(version, event) {
if (event.type === "message.appended") {
assertLegacyAppendSnapshot(event.data.messageSoFar, event.data.messageDelta, "message", version);
return {
data: {
messageDelta: event.data.messageDelta,
sequence: event.data.sequence,
stepIndex: event.data.stepIndex,
turnId: event.data.turnId
},
meta: event.meta,
type: "message.appended"
};
}
if (event.type === "reasoning.appended") {
assertLegacyAppendSnapshot(event.data.reasoningSoFar, event.data.reasoningDelta, "reasoning", version);
return {
data: {
reasoningDelta: event.data.reasoningDelta,
sequence: event.data.sequence,
stepIndex: event.data.stepIndex,
turnId: event.data.turnId
},
meta: event.meta,
type: "reasoning.appended"
};
}
if (event.type === "action.input.appended") {
if (version !== void 0 && version !== "24") throw new TypeError(`Invalid action input append for stream version ${version}.`);
assertLegacyActionInputOffset(event.data.inputTextOffset, version);
return {
data: {
callId: event.data.callId,
inputTextDelta: event.data.inputTextDelta,
sequence: event.data.sequence,
stepIndex: event.data.stepIndex,
toolName: event.data.toolName,
turnId: event.data.turnId
},
meta: event.meta,
type: "action.input.appended"
};
}
return event;
}
function validateCurrentMessageStreamEvent(event) {
if (event.type === "message.appended") {
assertCurrentAppendDelta(event.data.messageDelta, "message");
assertUnsupportedAppendField(event.data, "messageOffset", "message");
assertUnsupportedAppendField(event.data, "messageSoFar", "message");
} else if (event.type === "reasoning.appended") {
assertCurrentAppendDelta(event.data.reasoningDelta, "reasoning");
assertUnsupportedAppendField(event.data, "reasoningOffset", "reasoning");
assertUnsupportedAppendField(event.data, "reasoningSoFar", "reasoning");
} else if (event.type === "action.input.appended") {
assertCurrentAppendDelta(event.data.inputTextDelta, "action input");
assertUnsupportedAppendField(event.data, "inputTextOffset", "action input");
}
return event;
}
function assertCurrentAppendDelta(delta, stream) {
if (typeof delta !== "string") throw new TypeError(`Invalid ${stream} append delta for stream version 25.`);
}
function assertUnsupportedAppendField(data, field, stream) {
if (field in data) throw new TypeError(`Invalid ${stream} append shape for stream version 25.`);
}
function assertNever(value) {
throw new TypeError(`Unsupported message stream version: ${String(value)}.`);
}
function assertLegacyActionInputOffset(offset, version) {
if (!Number.isSafeInteger(offset) || offset < 0) {
const source = version === void 0 ? "persisted stream" : `stream version ${version}`;
throw new TypeError(`Invalid action input append offset for ${source}.`);
}
}
function assertLegacyAppendSnapshot(snapshot, delta, stream, version) {
const offset = snapshot.length - delta.length;
if (offset < 0 || snapshot.slice(offset) !== delta) {
const source = version === void 0 ? "persisted stream" : `stream version ${version}`;
throw new TypeError(`Invalid cumulative ${stream} append for ${source}.`);
}
}
//#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" || error instanceof TypeError && /^(?:failed to fetch|fetch failed)$/i.test(error.message) || /abort|cancel|disconnect|premature close|socket|terminated/i.test(error.message);
}
async function* readNdjsonStream(body, options) {
const reader = body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let reachedEof = false;
const abort = () => {
reader.cancel().catch(() => {});
};
options.signal?.addEventListener("abort", abort, { once: true });
try {
while (true) {
options.signal?.throwIfAborted();
const result = await readWithIdleTimeout(reader, options?.idleTimeoutMs);
options.signal?.throwIfAborted();
if (result.done) {
reachedEof = true;
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) {
const value = JSON.parse(line);
if (options.controlVersion === "1" && isLeaseEndedControl(value)) options.onLeaseEnded?.();
else yield parseMessageStreamEvent(value, options.streamVersion);
}
newlineIndex = buffer.indexOf("\n");
}
}
const trailing = buffer.trim();
if (trailing.length > 0) {
const value = JSON.parse(trailing);
if (options.controlVersion === "1" && isLeaseEndedControl(value)) options.onLeaseEnded?.();
else yield parseMessageStreamEvent(value, options.streamVersion);
}
} finally {
options.signal?.removeEventListener("abort", abort);
if (!reachedEof) reader.cancel().catch(() => {});
reader.releaseLock();
}
}
function isLeaseEndedControl(value) {
if (value === null || typeof value !== "object") return false;
const record = value;
return record.$eve === EVE_STREAM_LEASE_ENDED_CONTROL.$eve && record.version === EVE_STREAM_LEASE_ENDED_CONTROL.version;
}
function parseMessageStreamEvent(value, version) {
return normalizeMessageStreamEvent(version, value);
}
async function readWithIdleTimeout(reader, idleTimeoutMs) {
let timeout;
try {
if (idleTimeoutMs === void 0) return await reader.read();
return await Promise.race([reader.read(), new Promise((_resolve, reject) => {
timeout = setTimeout(() => reject(new DOMException("Session stream was idle.", "AbortError")), idleTimeoutMs);
})]);
} catch (error) {
if (error instanceof TypeError) throw new Error("Session stream disconnected.", { cause: error });
throw error;
} finally {
if (timeout !== void 0) clearTimeout(timeout);
}
}
//#endregion
//#region src/client/stream-version.ts
const supportedMessageStreamVersions = {
"21": true,
"22": true,
"23": true,
"24": true,
"25": true
};
function readMessageStreamVersion(headers) {
const version = headers.get(EVE_STREAM_VERSION_HEADER);
if (version !== null && Object.hasOwn(supportedMessageStreamVersions, version)) return version;
if (version === null) throw new TypeError(`Missing ${EVE_STREAM_VERSION_HEADER} response header.`);
throw new TypeError(`Unsupported message stream version: ${version}.`);
}
//#endregion
//#region src/shared/eve-route-path.ts
const EVE_NAMED_AGENT_MOUNT_PATTERN = /^\/eve\/[a-z0-9][a-z0-9_-]*$/;
const EVE_NAMED_AGENT_PROTOCOL_PATTERN = /^\/eve\/[a-z0-9][a-z0-9_-]*\/v1$/;
function joinEveRoutePath(basePath, routePath) {
const base = trimTrailingSlash$1(basePath);
const route = routePath.startsWith("/") ? routePath : `/${routePath}`;
if (route === "/eve/v1" || route.startsWith(`${"/eve/v1"}/`)) {
if (EVE_NAMED_AGENT_MOUNT_PATTERN.test(base)) return `${base}${route.slice(4)}`;
if (EVE_NAMED_AGENT_PROTOCOL_PATTERN.test(base)) return `${base}${route.slice(EVE_ROUTE_PREFIX.length)}`;
}
return `${base}${route}`;
}
function trimTrailingSlash$1(value) {
if (value === "/") return "";
return value.endsWith("/") ? value.slice(0, -1) : value;
}
//#endregion
//#region src/client/url.ts
function createClientUrl(host, routePath, searchParams) {
const queryIndex = routePath.indexOf("?");
const pathOnly = queryIndex === -1 ? routePath : routePath.slice(0, queryIndex);
const embeddedQuery = queryIndex === -1 ? "" : routePath.slice(queryIndex + 1);
const normalizedRoute = pathOnly.startsWith("/") ? pathOnly : `/${pathOnly}`;
if (isAbsoluteUrl(host)) {
const url = new URL(host);
const basePath = trimTrailingSlash(url.pathname);
url.pathname = joinEveRoutePath(basePath, normalizedRoute);
mergeEmbeddedQuery(url.searchParams, embeddedQuery);
mergeSearchParams(url.searchParams, searchParams);
url.hash = "";
return url.toString();
}
const url = new URL(host, "http://eve.local");
const basePath = trimTrailingSlash(url.pathname);
mergeEmbeddedQuery(url.searchParams, embeddedQuery);
mergeSearchParams(url.searchParams, searchParams);
return `${joinEveRoutePath(basePath, normalizedRoute)}${formatSearch(url.searchParams)}`;
}
function mergeEmbeddedQuery(target, embeddedQuery) {
if (embeddedQuery.length === 0) return;
for (const [name, value] of new URLSearchParams(embeddedQuery)) target.append(name, value);
}
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 mergeSearchParams(target, searchParams) {
if (searchParams === void 0) return;
for (const [name, value] of Object.entries(searchParams)) target.set(name, value);
}
function formatSearch(searchParams) {
const value = searchParams.toString();
return value.length === 0 ? "" : `?${value}`;
}
//#endregion
//#region src/client/open-stream.ts
const DEFAULT_STREAM_READ_IDLE_TIMEOUT_MS = 15e3;
const DEFAULT_STREAM_RECONNECT_POLICY = {
retryableErrorStatuses: /* @__PURE__ */ new Set([
404,
409,
425,
500,
502,
503,
504
]),
streamIdleReconnectPolicy: {
baseDelayMs: 250,
maxAttempts: 5,
maxDelayMs: 4e3
},
streamOpenReconnectPolicy: {
baseDelayMs: 250,
maxAttempts: 12,
maxDelayMs: 5e3
}
};
const NO_STREAM_RECONNECT_POLICY = {
...DEFAULT_STREAM_RECONNECT_POLICY,
streamIdleReconnectPolicy: {
...DEFAULT_STREAM_RECONNECT_POLICY.streamIdleReconnectPolicy,
maxAttempts: 0
},
streamOpenReconnectPolicy: {
...DEFAULT_STREAM_RECONNECT_POLICY.streamOpenReconnectPolicy,
maxAttempts: 1
}
};
function resolveRetryPolicy(policy, defaults) {
return {
...defaults,
...policy
};
}
function resolveStreamReconnectPolicy(policy, keepAlive = false) {
if (policy && "reconnect" in policy && policy.reconnect === false) return NO_STREAM_RECONNECT_POLICY;
const configured = policy;
return {
retryableErrorStatuses: configured?.retryableErrorStatuses ? new Set(configured.retryableErrorStatuses) : DEFAULT_STREAM_RECONNECT_POLICY.retryableErrorStatuses,
streamIdleReconnectPolicy: resolveRetryPolicy(configured?.streamIdleReconnectPolicy, {
...DEFAULT_STREAM_RECONNECT_POLICY.streamIdleReconnectPolicy,
maxAttempts: keepAlive ? Infinity : DEFAULT_STREAM_RECONNECT_POLICY.streamIdleReconnectPolicy.maxAttempts
}),
streamOpenReconnectPolicy: resolveRetryPolicy(configured?.streamOpenReconnectPolicy, DEFAULT_STREAM_RECONNECT_POLICY.streamOpenReconnectPolicy)
};
}
async function* followStreamIterable(input) {
if (input.follow === false && input.startIndex < 0) throw new Error("stream({ follow: false }) requires a nonnegative startIndex; a tail-relative cursor cannot be bounded.");
const resolvePolicy = () => resolveStreamReconnectPolicy(input.resolveReconnectPolicy === void 0 ? input.streamReconnectPolicy : input.resolveReconnectPolicy(), input.keepAlive);
let retryPolicy = resolvePolicy();
let idleRetryPolicy = retryPolicy.streamIdleReconnectPolicy;
let startIndex = input.startIndex;
let reconnectDelayMs = idleRetryPolicy.baseDelayMs;
let idleReconnects = 0;
let initialConnection = true;
let tailIndex;
let caughtUp = false;
while (true) {
retryPolicy = resolvePolicy();
idleRetryPolicy = retryPolicy.streamIdleReconnectPolicy;
let connection;
try {
connection = await openStreamBody({
...input,
retryPolicy,
startIndex,
requestTailIndex: (input.follow === false || input.onCaughtUp !== void 0) && tailIndex === void 0
});
} catch (error) {
if (input.signal?.aborted) return;
throw error;
}
if ((input.follow === false || input.onCaughtUp !== void 0) && tailIndex === void 0) {
tailIndex = connection.tailIndex;
if (tailIndex === void 0) {
connection.close();
throw new Error(`stream({ follow: false }) requires the server to report the ${EVE_STREAM_TAIL_INDEX_HEADER} header. The agent may be running an older eve version.`);
}
}
if (!caughtUp && tailIndex !== void 0 && startIndex > tailIndex) {
caughtUp = true;
input.onCaughtUp?.();
}
if (input.follow === false && tailIndex !== void 0 && startIndex > tailIndex) {
connection.close();
return;
}
let deliveredEvent = false;
let leaseEnded = false;
try {
for await (const event of readNdjsonStream(connection.body, {
signal: input.signal,
controlVersion: connection.controlVersion,
idleTimeoutMs: input.streamReadIdleTimeoutMs ?? DEFAULT_STREAM_READ_IDLE_TIMEOUT_MS,
onLeaseEnded: () => {
leaseEnded = true;
},
streamVersion: connection.streamVersion
})) {
startIndex += 1;
deliveredEvent = true;
reconnectDelayMs = idleRetryPolicy.baseDelayMs;
idleReconnects = 0;
yield event;
if (!caughtUp && tailIndex !== void 0 && startIndex > tailIndex) {
caughtUp = true;
input.onCaughtUp?.();
}
if (input.follow === false && tailIndex !== void 0 && startIndex > tailIndex) return;
}
} catch (error) {
if (!isStreamDisconnectError(error)) throw error;
} finally {
connection.close();
}
idleRetryPolicy = resolvePolicy().streamIdleReconnectPolicy;
if (input.signal?.aborted || input.startIndex < 0 || idleRetryPolicy.maxAttempts === 0) return;
if (leaseEnded) continue;
if (!deliveredEvent && !initialConnection && (idleReconnects += 1) >= idleRetryPolicy.maxAttempts) return;
initialConnection = false;
await sleep(reconnectDelayMs, input.signal);
if (input.signal?.aborted) return;
reconnectDelayMs = Math.min(reconnectDelayMs * 2, idleRetryPolicy.maxDelayMs);
}
}
async function openStreamBody(input) {
const retryPolicy = input.retryPolicy ?? resolveStreamReconnectPolicy(input.streamReconnectPolicy, input.keepAlive);
const openRetryPolicy = retryPolicy.streamOpenReconnectPolicy;
let lastStatus;
let lastBody;
let lastHeaders;
let retryDelayMs = openRetryPolicy.baseDelayMs;
const controlVersion = input.startIndex >= 0 && retryPolicy.streamIdleReconnectPolicy.maxAttempts > 0 ? "1" : void 0;
const searchParams = {};
if (controlVersion !== void 0) searchParams[EVE_STREAM_CONTROL_VERSION_QUERY] = controlVersion;
if (input.startIndex !== 0) searchParams.startIndex = String(input.startIndex);
if (input.requestTailIndex === true) searchParams.includeTailIndex = "1";
for (let attempt = 0; attempt < openRetryPolicy.maxAttempts; attempt += 1) {
input.signal?.throwIfAborted();
const url = createClientUrl(input.host, createEveSessionStreamRoutePath(input.sessionId), Object.keys(searchParams).length > 0 ? searchParams : void 0);
const headers = await input.resolveHeaders();
input.signal?.throwIfAborted();
const connectionController = new AbortController();
const signal = input.signal ? AbortSignal.any([input.signal, connectionController.signal]) : connectionController.signal;
let response;
try {
response = await fetch(url, {
cache: "no-store",
headers,
redirect: input.redirect,
signal
});
} catch (error) {
if (input.signal?.aborted || !isStreamDisconnectError(error) || attempt === openRetryPolicy.maxAttempts - 1) throw error;
await sleep(retryDelayMs, input.signal);
retryDelayMs = Math.min(retryDelayMs * 2, openRetryPolicy.maxDelayMs);
continue;
}
if (response.ok) {
if (!response.body) throw new ClientError(response.status, "Response body is null.", response.headers);
let closed = false;
return {
body: response.body,
close: () => {
if (closed) return;
closed = true;
response.body?.cancel().catch(() => {});
connectionController.abort();
},
controlVersion,
streamVersion: readMessageStreamVersion(response.headers),
tailIndex: parseTailIndexHeader(response.headers)
};
}
lastStatus = response.status;
lastBody = await response.text();
lastHeaders = response.headers;
if (!retryPolicy.retryableErrorStatuses.has(response.status)) throw new ClientError(response.status, lastBody, response.headers);
if (attempt < openRetryPolicy.maxAttempts - 1) {
await sleep(retryDelayMs, input.signal);
retryDelayMs = Math.min(retryDelayMs * 2, openRetryPolicy.maxDelayMs);
}
}
throw new ClientError(lastStatus ?? 0, lastBody ?? "Failed to open message stream.", lastHeaders);
}
function parseTailIndexHeader(headers) {
const raw = headers.get(EVE_STREAM_TAIL_INDEX_HEADER);
if (raw === null || !/^-?\d+$/.test(raw)) return;
const parsed = Number(raw);
return Number.isSafeInteger(parsed) ? parsed : void 0;
}
async function sleep(ms, signal) {
if (signal?.aborted) return;
await new Promise((resolve) => {
const onAbort = () => {
clearTimeout(timer);
resolve();
};
const timer = setTimeout(() => {
signal?.removeEventListener("abort", onAbort);
resolve();
}, ms);
signal?.addEventListener("abort", onAbort, { once: true });
});
}
//#endregion
//#region src/protocol/cancel-turn.ts
const CancelTurnResponseSchema = z.discriminatedUnion("status", [z.strictObject({
ok: z.literal(true),
sessionId: z.string().min(1),
status: z.literal("accepted")
}), z.strictObject({
ok: z.literal(true),
status: z.literal("no_active_turn")
})]);
//#endregion
//#region src/protocol/clear-session.ts
const ClearResponseSchema = z.discriminatedUnion("status", [z.object({
ok: z.literal(true),
sessionId: z.string().min(1),
status: z.literal("accepted")
}), z.object({
ok: z.literal(true),
status: z.literal("no_active_session")
})]);
//#endregion
//#region src/protocol/compact-session.ts
const CompactResponseSchema = z.discriminatedUnion("status", [z.object({
ok: z.literal(true),
sessionId: z.string().min(1),
status: z.literal("accepted")
}), z.object({
ok: z.literal(true),
status: z.literal("no_active_session")
})]);
//#endregion
//#region src/protocol/reset-session.ts
const ResetResponseSchema = z.discriminatedUnion("status", [z.object({
ok: z.literal(true),
previousSessionId: z.string().min(1),
status: z.literal("reset")
}), z.object({
ok: z.literal(true),
status: z.literal("no_active_session")
})]);
//#endregion
//#region src/client/session-controls.ts
async function cancelClientSession(input) {
const { signal, ...body } = input.options ?? {};
const { payload, response } = await postJson({
body,
context: input.context,
operation: "Cancel",
path: createEveSessionCancelRoutePath(input.sessionId),
signal
});
const result = CancelTurnResponseSchema.safeParse(payload);
if (!result.success || result.data.status === "accepted" && result.data.sessionId !== input.sessionId) throw new Error(`Cancel route returned an invalid response (${response.status}).`);
return result.data.status === "accepted" ? {
sessionId: result.data.sessionId,
status: "accepted"
} : { status: "no_active_turn" };
}
async function clearClientSession(input) {
const { payload } = await postJson({
context: input.context,
operation: "Clear",
path: createEveSessionClearRoutePath(input.sessionId)
});
const result = ClearResponseSchema.safeParse(payload);
if (!result.success || result.data.status === "accepted" && result.data.sessionId !== input.sessionId) throw new Error("Clear route returned an invalid response.");
return result.data.status === "accepted" ? {
sessionId: result.data.sessionId,
status: "accepted"
} : { status: "no_active_session" };
}
async function compactClientSession(input) {
const { payload } = await postJson({
context: input.context,
operation: "Compact",
path: createEveSessionCompactRoutePath(input.sessionId)
});
const result = CompactResponseSchema.safeParse(payload);
if (!result.success || result.data.status === "accepted" && result.data.sessionId !== input.sessionId) throw new Error("Compact route returned an invalid response.");
return result.data.status === "accepted" ? {
sessionId: result.data.sessionId,
status: "accepted"
} : { status: "no_active_session" };
}
async function resetClientSession(input) {
const { signal, ...body } = input.options ?? {};
const { payload } = await postJson({
body,
context: input.context,
operation: "Reset",
path: createEveSessionResetRoutePath(input.sessionId),
signal
});
const result = ResetResponseSchema.safeParse(payload);
if (!result.success || result.data.status === "reset" && result.data.previousSessionId !== input.sessionId) throw new Error("Reset route returned an invalid response.");
return result.data.status === "reset" ? {
previousSessionId: result.data.previousSessionId,
status: "reset"
} : { status: "no_active_session" };
}
async function postJson(input) {
const headers = await input.context.resolveHeaders();
headers.set("content-type", "application/json");
const response = await fetch(createClientUrl(input.context.host, input.path), withRedirectPolicy$1({
body: input.body === void 0 ? void 0 : JSON.stringify(input.body),
headers,
method: "POST",
signal: input.signal
}, input.context.redirect));
const text = await response.text();
if (!response.ok) throw new ClientError(response.status, text, response.headers);
try {
return {
payload: JSON.parse(text),
response
};
} catch {
throw new Error(`${input.operation} route returned invalid JSON (${response.status}).`);
}
}
function withRedirectPolicy$1(init, redirect) {
return redirect === void 0 ? init : {
...init,
redirect
};
}
//#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 || Object.getPrototypeOf(prototype) === null;
}
//#endregion
//#region src/tools/schema.ts
const JSON_SCHEMA_TARGET = "draft-07";
function serializeOutputSchema(source) {
return serializeSchema(source, "output");
}
const UNSPECIFIED_INPUT_SCHEMA = z.fromJSONSchema({});
function serializeSchema(source, direction) {
if (source === null || source === void 0) return source;
return toJsonObject(source, direction);
}
function toJsonObject(source, direction) {
const standard = getStandardSchemaProperties(source);
const jsonSchema = standard?.jsonSchema;
const emit = typeof jsonSchema === "object" && jsonSchema !== null ? jsonSchema[direction] : void 0;
const vendor = typeof standard?.vendor === "string" ? standard.vendor : "unknown";
if (standard !== void 0 && typeof emit !== "function" && vendor === "zod") {
if (direction === "input") {
const schema = asSchema(source);
const { $schema: _schemaVersion, ...canonical } = parseJsonObject(schema.jsonSchema);
return canonical;
}
throw new Error("Zod 3 cannot emit an output JSON Schema. Upgrade to Zod 4 or provide a plain JSON Schema object.");
}
if (standard !== void 0 && typeof emit !== "function") throw new Error(`Standard Schema vendor "${vendor}" does not support JSON Schema conversion. Provide a Standard Schema implementation with JSON Schema conversion or a plain JSON Schema object.`);
const { $schema: _schemaVersion, ...canonical } = standard === void 0 ? parseJsonObject(source) : parseJsonObject(emit({ target: JSON_SCHEMA_TARGET }));
return canonical;
}
function getStandardSchemaProperties(value) {
if (typeof value !== "object" || value === null || !("~standard" in value)) return void 0;
const standard = value["~standard"];
return typeof standard === "object" && standard !== null ? standard : void 0;
}
//#endregion
//#region src/client/session.ts
const SESSION_SEND_RETRY_BASE_DELAY_MS = 250;
const SESSION_SEND_RETRY_MAX_DELAY_MS = 2e3;
const SESSION_SEND_READY_TIMEOUT_MS = 2e4;
const followSession = Symbol("followClientSession");
var ClientSession = class ClientSession {
#context;
#state;
constructor(context, state) {
this.#context = context;
this.#state = state;
}
static async create(context, input) {
const response = await postTurn(context, EVE_SESSION_ROUTE_PATH, input, true);
const { sessionId } = await readAcceptedMessage(response);
const session = new ClientSession(context, {
sessionId,
streamIndex: 0
});
return {
response: session.#messageResponse(response, input, 0),
session
};
}
static async prewarm(context, options = {}) {
const { sessionId } = await readAcceptedMessage(await postCreateSession(context, options));
return new ClientSession(context, {
sessionId,
streamIndex: 0
});
}
get state() {
return this.#state;
}
async snapshot(options) {
options?.signal?.throwIfAborted();
const events = [];
for await (const event of this.#readStream({
follow: false,
signal: options?.signal,
startIndex: 0
})) events.push(event);
options?.signal?.throwIfAborted();
return {
events,
session: {
sessionId: this.#state.sessionId,
streamIndex: events.length
}
};
}
async send(message, options = {}) {
return await this.#send({
...options,
message
}, true);
}
async respond(inputResponses, options = {}) {
if (inputResponses.length === 0) throw new Error("ClientSession.respond() requires at least one input response.");
return await this.#send({
...options,
inputResponses
}, false);
}
async #send(input, retrySessionNotReady) {
const initialStreamIndex = this.#state.streamIndex;
const path = createEveSessionRoutePath(this.#state.sessionId);
const response = retrySessionNotReady ? await postSessionSend(this.#context, path, input) : await postTurn(this.#context, path, input, false);
const { sessionId: responseSessionId, deliveryId } = await readAcceptedMessage(response, this.#state.sessionId);
if (responseSessionId !== this.#state.sessionId) throw new Error("Message route returned a different session id.");
if (input.message !== void 0 && deliveryId === void 0) throw new Error("Message route did not return a delivery id. Update the server before sending with this client.");
return this.#messageResponse(response, input, initialStreamIndex, input.message === void 0 ? void 0 : deliveryId);
}
async cancel(options) {
return await cancelClientSession({
context: this.#context,
options,
sessionId: this.#state.sessionId
});
}
async clear() {
return await clearClientSession({
context: this.#context,
sessionId: this.#state.sessionId
});
}
async compact() {
return await compactClientSession({
context: this.#context,
sessionId: this.#state.sessionId
});
}
async reset(options) {
return await resetClientSession({
context: this.#context,
options,
sessionId: this.#state.sessionId
});
}
stream(options) {
if (options?.follow === false && (options.startIndex ?? this.#state.streamIndex) < 0) throw new Error("stream({ follow: false }) requires a nonnegative startIndex; a tail-relative cursor cannot be bounded.");
return this.#streamAndAdvance(options);
}
[followSession](options) {
return this.#streamAndAdvance({
...options,
keepAlive: true
});
}
#messageResponse(response, input, initialStreamIndex, deliveryId) {
response.body?.cancel().catch(() => {});
return new MessageResponse({
cancelTurn: async (turnId) => await this.cancel({ turnId }),
createStream: (source) => this.#createEventStream(initialStreamIndex, input, deliveryId, source),
deliveryId,
sessionId: this.#state.sessionId
});
}
async *#createEventStream(initialStreamIndex, input, deliveryId, source) {
let eventCount = 0;
let started = deliveryId === void 0;
let reachedBoundary = false;
const pendingAuthorizations = /* @__PURE__ */ new Set();
try {
for await (const event of source ?? this.#readStream({
headers: input.headers,
keepAlive: true,
signal: input.signal,
startIndex: initialStreamIndex,
streamReconnectPolicy: input.streamReconnectPolicy
})) {
eventCount += 1;
if (deliveryId !== void 0) {
const matches = event.meta?.deliveryIds?.includes(deliveryId) === true;
const terminal = event.type === "session.failed" || event.type === "session.completed";
if (!matches && terminal && (!started || event.type === "session.completed")) throw new Error("The session ended before the accepted message reached its turn boundary.");
if (!started && !matches) continue;
if (!terminal && event.meta?.deliveryIds !== void 0 && !matches) continue;
started = true;
}
updatePendingAuthorizations(pendingAuthorizations, event);
reachedBoundary = isCurrentTurnBoundaryEvent(event) && (event.type !== "session.waiting" || pendingAuthorizations.size === 0);
yield event;
if (reachedBoundary) break;
}
if (deliveryId !== void 0 && !reachedBoundary && !input.signal?.aborted) throw new Error("The response stream ended before the accepted message reached its turn boundary.");
} finally {
this.#advanceStreamIndex(initialStreamIndex + eventCount);
}
}
async *#streamAndAdvance(options) {
const startIndex = options?.startIndex ?? this.#state.streamIndex;
let eventCount = 0;
for await (const event of this.#readStream({
follow: options?.follow,
headers: options?.headers,
keepAlive: options?.keepAlive,
onCaughtUp: options?.onCaughtUp,
resolveHeaders: options?.resolveHeaders,
resolveReconnectPolicy: options?.resolveReconnectPolicy,
signal: options?.signal,
startIndex,
streamReconnectPolicy: options?.streamReconnectPolicy
})) {
eventCount += 1;
if (startIndex >= 0) this.#advanceStreamIndex(startIndex + eventCount);
yield event;
}
}
#advanceStreamIndex(streamIndex) {
this.#state = {
sessionId: this.#state.sessionId,
streamIndex: Math.max(this.#state.streamIndex, streamIndex)
};
}
#readStream(input) {
return followStreamIterable({
onCaughtUp: input.onCaughtUp,
follow: input.follow,
host: this.#context.host,
keepAlive: input.keepAlive,
resolveHeaders: () => this.#context.resolveHeaders(input.resolveHeaders?.() ?? input.headers),
redirect: this.#context.redirect,
sessionId: this.#state.sessionId,
signal: input.signal,
startIndex: input.startIndex,
streamReconnectPolicy: input.streamReconnectPolicy,
resolveReconnectPolicy: input.resolveReconnectPolicy
});
}
};
function followClientSession(session, options) {
return session[followSession](options);
}
async function postSessionSend(context, path, input) {
const readyDeadline = Date.now() + SESSION_SEND_READY_TIMEOUT_MS;
let retryDelayMs = SESSION_SEND_RETRY_BASE_DELAY_MS;
for (;;) {
try {
return await postTurn(context, path, input, false);
} catch (error) {
if (!isSessionNotReady(error)) throw error;
const remainingMs = readyDeadline - Date.now();
if (remainingMs <= 0) throw error;
await sleep(Math.min(retryDelayMs, remainingMs), input.signal);
}
input.signal?.throwIfAborted();
retryDelayMs = Math.min(retryDelayMs * 2, SESSION_SEND_RETRY_MAX_DELAY_MS);
}
}
async function postCreateSession(context, options) {
const headers = await context.resolveHeaders(options.headers);
const response = await fetch(createClientUrl(context.host, EVE_SESSION_ROUTE_PATH), {
headers,
method: "POST",
redirect: context.redirect,
signal: options.signal ?? null
});
if (!response.ok) {
const responseBody = await response.text();
throw new ClientError(response.status, responseBody, response.headers);
}
return response;
}
function isSessionNotReady(error) {
return error instanceof ClientError && error.status === 409 && error.code === "session_not_ready";
}
async function postTurn(context, path, input, requireMessage) {
const body = createMessageBody(input, requireMessage);
if (body === null) throw new Error(requireMessage ? "Creating a session requires a non-empty message." : "A session turn requires a non-empty message or inputResponses.");
const headers = await context.resolveHeaders(input.headers);
headers.set("content-type", "application/json");
const response = await fetch(createClientUrl(context.host, path), {
body: JSON.stringify(body),
headers,
method: "POST",
redirect: context.redirect,
signal: input.signal ?? null
});
if (!response.ok) {
const responseBody = await response.text();
throw new ClientError(response.status, responseBody, response.headers);
}
return response;
}
async function readAcceptedMessage(response, expected) {
const payload = await response.json();
const sessionId = (typeof payload.sessionId === "string" ? payload.sessionId : void 0) ?? response.headers.get("x-eve-session-id")?.trim() ?? expected;
if (!sessionId) throw new Error("Message route did not return a session id.");
return {
sessionId,
deliveryId: typeof payload.deliveryId === "string" && payload.deliveryId.length > 0 ? payload.deliveryId : void 0
};
}
function createMessageBody(input, requireMessage) {
const body = {};
if (input.message !== void 0) body.message = input.message;
if (input.inputResponses !== void 0 && input.inputResponses.length > 0) body.inputResponses = input.inputResponses;
if (!requireMessage && input.message !== void 0 && input.turnPolicy !== void 0) body.turnPolicy = input.turnPolicy;
if (input.message !== void 0 && input.taskDeliveryPolicy !== void 0) body.taskDeliveryPolicy = input.taskDeliveryPolicy;
if (input.clientContext !== void 0) body.clientContext = input.clientContext;
const outputSchema = serializeOutputSchema(input.outputSchema);
if (outputSchema !== void 0) body.outputSchema = outputSchema;
if (requireMessage && body.message === void 0) return null;
if (body.message === void 0 && body.inputResponses === void 0) return null;
return body;
}
//#endregion
//#region src/client/sessions.ts
var ClientSessions = class {
#context;
constructor(context) {
this.#context = context;
}
async create(input = {}) {
if ("message" in input) return await ClientSession.create(this.#context, input);
return { session: await ClientSession.prewarm(this.#context, input) };
}
attach(sessionId, options) {
if (sessionId.length === 0) throw new Error("sessionId must be a non-empty string.");
return new ClientSession(this.#context, {
sessionId,
streamIndex: options?.streamIndex ?? 0
});
}
};
//#endregion
//#region src/client/types.ts
const VERCEL_TRUSTED_OIDC_IDP_TOKEN_HEADER = "x-vercel-trusted-oidc-idp-token";
//#endregion
//#region src/client/client.ts
var Client = class {
#auth;
#headers;
#host;
#redirect;
sessions;
constructor(options) {
this.#host = options.host;
this.#auth = options.auth;
this.#headers = options.headers;
this.#redirect = options.redirect;
this.sessions = new ClientSessions({
host: this.#host,
redirect: this.#redirect,
resolveHeaders: (perRequest) => this.#resolveHeaders(perRequest)
});
}
async health() {
const url = createClientUrl(this.#host, EVE_HEALTH_ROUTE_PATH);
const headers = await this.#resolveHeaders();
const response = await fetch(url, withRedirectPolicy({ headers }, this.#redirect));
if (!response.ok) {
const body = await response.text();
throw new ClientError(response.status, body, response.headers);
}
let payload;
try {
payload = await response.json();
} catch {
throw new HealthResponseError();
}
const result = HealthResultSchema.safeParse(payload);
if (!result.success) throw new HealthResponseError(result.error.issues.slice(0, 5).map((issue) => {
const path = issue.path.join(".");
return path.length === 0 ? issue.message : `${path}: ${issue.message}`;
}));
return result.data;
}
async info(options = {}) {
const response = await this.fetch(EVE_INFO_ROUTE_PATH, options);
if (!response.ok) {
const body = await response.text();
throw new ClientError(response.status, body, response.headers);
}
let payload;
try {
payload = await response.json();
} catch {
throw new AgentInfoResponseError();
}
const result = AgentInfoResultSchema.safeParse(payload);
if (!result.success) throw new AgentInfoResponseError(result.error.issues.slice(0, 5).map((issue) => {
const path = issue.path.join(".");
return path.length === 0 ? issue.message : `${path}: ${issue.message}`;
}));
return result.data;
}
async fetch(path, init = {}) {
const url = createClientUrl(this.#host, path);
const headers = await this.#resolveHeaders(headersInitToRecord(init.headers));
return await fetch(url, withRedirectPolicy({
...init,
headers
}, this.#redirect));
}
async #resolveHeaders(perRequest) {
const headers = new Headers();
const [baseHeaders, authHeaders] = await Promise.all([resolveHeadersValue(this.#headers), this.#resolveAuthHeaders()]);
for (const [key, value] of Object.entries(baseHeaders)) headers.set(key, value);
for (const [key, value] of Object.entries(authHeaders)) headers.set(key, value);
if (perRequest) for (const [key, value] of Object.entries(perRequest)) headers.set(key, value);
return headers;
}
async #resolveAuthHeaders() {
const auth = this.#auth;
if (!auth) return {};
if ("vercelOidc" in auth) {
const token = (await resolveTokenValue(auth.vercelOidc.token)).trim();
if (token.length === 0) return {};
return {
authorization: `Bearer ${token}`,
[VERCEL_TRUSTED_OIDC_IDP_TOKEN_HEADER]: token
};
}
if ("bearer" in auth) {
const token = (await resolveTokenValue(auth.bearer)).trim();
return token.length === 0 ? {} : { authorization: `Bearer ${token}` };
}
if ("basic" in auth) {
const password = await resolveTokenValue(auth.basic.password);
return { authorization: `Basic ${encodeBasicCredentials(auth.basic.username, password)}` };
}
return {};
}
};
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 withRedirectPolicy(init, redirect) {
return redirect === void 0 ? init : {
...init,
redirect
};
}
//#endregion
//#region src/client/session-event-stream.ts
var SessionEventStream = class {
#controller = new AbortController();
#readers = /* @__PURE__ */ new Set();
#caughtUp = Promise.withResolvers();
caughtUp = this.#caughtUp.promise;
#ended = false;
#error;
#headers;
#reconnectPolicy;
constructor(session, options) {
this.setOptions(options);
this.caughtUp.catch(() => {});
if (!options.catchUp) this.#caughtUp.resolve();
(async () => {
try {
for await (const event of followClientSession(session, {
signal: this.#controller.signal,
resolveHeaders: () => this.#headers,
resolveReconnectPolicy: () => this.#reconnectPolicy,
startIndex: options.startIndex,
onCaughtUp: options.catchUp ? () => this.#caughtUp.resolve() : void 0
})) {
if (this.#controller.signal.aborted) return;
options.onEvent(event);
for (const reader of this.#readers) reader.push(event);
if (event.type === "session.completed" || event.type === "session.failed") break;
}
this.#caughtUp.resolve();
this.#finish();
} catch (error) {
this.#caughtUp.reject(error);
this.#finish(error);
if (!this.#controller.signal.aborted) options.onError(error);
}
})();
}
subscribe(signal) {
const reader = new SessionEventReader(() => this.#readers.delete(reader), signal);
if (this.#ended) reader.end(this.#error);
else this.#readers.add(reader);
return reader;
}
get ended() {
return this.#ended;
}
setOptions(options) {
if ("headers" in options) this.#headers = options.headers;
if ("streamReconnectPolicy" in options) this.#reconnectPolicy = options.streamReconnectPolicy;
}
close() {
const error = new DOMException("Session stream was detached.", "AbortError");
this.#controller.abort(error);
this.#caughtUp.reject(error);
this.#finish(error);
}
#finish(error) {
if (this.#ended) return;
this.#ended = true;
this.#error = error;
for (const reader of this.#readers) reader.end(error);
}
};
var SessionEventReader = class {
#events = [];
#wake = Promise.withResolvers();
#ended = false;
#error;
#dispose;
constructor(unsubscribe, signal) {
const abort = () => this.end(signal?.reason);
signal?.addEventListener("abort", abort, { once: true });
this.#dispose = () => {
signal?.removeEventListener("abort", abort);
unsubscribe();
};
if (signal?.aborted) abort();
}
push(event) {
if (this.#ended) return;
this.#events.push(event);
this.#wake.resolve();
}
end(error) {
if (this.#ended) return;
this.#ended = true;
this.#error = error;
this.#wake.resolve();
}
[Symbol.dispose]() {
this.end();
this.#events = [];
this.#dispose();
}
discard() {
this.#events = [];
}
async *[Symbol.asyncIterator]() {
for (;;) {
while (this.#events.length > 0) yield this.#events.shift();
if (this.#ended) {
if (this.#error !== void 0) throw this.#error;
return;
}
this.#wake = Promise.withResolvers();
await this.#wake.promise;
}
}
};
//#endregion
//#region src/client/eve-agent-projection.ts
var EveAgentProjection = class {
#reducer;
#events;
#data;
constructor(reducer, events) {
this.#reducer = reducer;
this.#events = events;
this.#data = this.#reduce();
}
get data() {
return this.#data;
}
reset() {
this.#events = [];
this.#data = this.#reducer.initial();
}
append(event) {
this.#events = [...this.#events, event];
this.#data = this.#reducer.reduce(this.#data, event);
}
remove(predicate) {
this.#events = this.#events.filter((event) => !predicate(event));
this.#data = this.#reduce();
}
replace(predicate, replacement) {
const index = this.#events.findIndex(predicate);
this.#events = index === -1 ? [...this.#events, replacement] : this.#events.map((event, i) => i === index ? replacement : event);
this.#data = this.#reduce();
}
#reduce() {
let data = this.#reducer.initial();
for (const event of this.#events) data = this.#reducer.reduce(data, event);
return data;
}
};
//#endregion
//#region src/client/eve-agent-store-helpers.ts
function isSettledSessionTail(events) {
const tail = events.at(-1);
return tail !== void 0 && isCurrentTurnBoundaryEvent(tail) && (tail.type !== "session.waiting" || collectPendingAuthorizations(events).size === 0);
}
function collectPendingAuthorizations(events) {
const pending = /* @__PURE__ */ new Set();
for (const event of events) updatePendingAuthorizations(pending, event);
return pending;
}
function assertExclusiveTurnInput(input) {
if (input.message !== void 0 === (input.inputResponses !== void 0)) throw new Error("A turn requires exactly one of message or inputResponses.");
}
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);
else 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 void 0;
const error = new Error(event.data.message);
error.name = event.data.code;
return error;
}
function createActiveTurn(cancel) {
const response = Promise.withResolvers();
const completion = Promise.withResolvers();
const turn = {
abortController: new AbortController(),
acceptedFollowUps: 0,
cancel: () => cancel(turn),
completion: completion.promise,
followUpDispatches: /* @__PURE__ */ new Set(),
receivedFollowUps: 0,
receivedFollowUpEvents: /* @__PURE__ */ new Map(),
followUpSubmissionIds: /* @__PURE__ */ new Set(),
resolveCompletion: completion.resolve,
response: response.promise,
resolveResponse: response.resolve
};
return turn;
}
async function followSteeredTurns(turn, events, isActive) {
while (turn.followUpDispatches.size > 0) await Promise.allSettled(turn.followUpDispatches);
if (turn.receivedFollowUps >= turn.acceptedFollowUps) return;
for await (const event of events) {
if (!isActive()) return;
turn.receivedFollowUps += turn.receivedFollowUpEvents.get(event) ?? 0;
turn.receivedFollowUpEvents.delete(event);
if (isCurrentTurnBoundaryEvent(event)) {
while (turn.followUpDispatches.size > 0) await Promise.allSettled(turn.followUpDispatches);
if (turn.receivedFollowUps >= turn.acceptedFollowUps) return;
}
}
}
async function waitWithSignal(promise, signal) {
if (signal === void 0) return await promise;
const aborted = Promise.withResolvers();
const onAbort = () => aborted.reject(signal.reason);
signal.addEventListener("abort", onAbort, { once: true });
if (signal.aborted) onAbort();
try {
return await Promise.race([aborted.promise, promise]);
} finally {
signal.removeEventListener("abort", onAbort);
}
}
//#endregion
//#region src/client/optimistic-message-submissions.ts
var OptimisticMessageSubmissions = class {
#optimistic;
#projection;
#pending = [];
constructor(projection, optimistic) {
this.#projection = projection;
this.#optimistic = optimistic;
}
reset() {
this.#pending = [];
}
submit(input, eventStartIndex) {
if (input.message === void 0) return void 0;
const pending = {
createdAt: Date.now(),
eventStartIndex,
id: createSubmissionId(),
message: summarizeUserContent(input.message),
requiresDeliveryId: true
};
this.#pending = [...this.#pending, pending];
if (this.#optimistic) this.#projection.append({
data: {
createdAt: pending.createdAt,
message: pending.message,
submissionId: pending.id
},
type: "client.message.submitted"
});
return pending.id;
}
apply(event) {
if (event.type !== "message.received") {
this.#projection.append(event);
return;
}
if (event.data.kind === "execution.background_task") {
this.#projection.append(event);
return;
}
const matching = this.#matching(event);
if (matching.length === 0) {
this.#projection.append(event);
return;
}
return this.#reconcile(matching, event, false);
}
correlate(submissionId, deliveryId, events) {
if (submissionId === void 0) return void 0;
this.#pending = this.#pending.map((pending) => pending.id === submissionId ? {
...pending,
deliveryId,
requiresDeliveryId: deliveryId !== void 0
} : pending);
const pending = this.#pending.find((candidate) => candidate.id === submissionId);
if (pending === void 0) return void 0;
for (const event of events.slice(pending.eventStartIndex)) {
if (event.type !== "message.received" || event.data.kind === "execution.background_task") continue;
const matching = this.#matching(event);
if (matching.some((candidate) => candidate.id === submissionId)) return this.#reconcile(matching, event, true);
}
}
fail(error, submissionId) {
const pending = submissionId === void 0 ? this.#pending[0] : this.#pending.find((candidate) => candidate.id === submissionId);
if (pending === void 0) return;
this.#pending = this.#pending.filter((candidate) => candidate.id !== pending.id);
this.#projection.replace((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"
});
}
failAll(error) {
for (const pending of this.#pending) this.fail(error, pending.id);
}
#matching(event) {
return this.#pending.filter((pending) => pending.deliveryId === void 0 ? !pending.requiresDeliveryId : event.meta.deliveryIds?.includes(pending.deliveryId) === true);
}
#reconcile(submissions, event, alreadyProjected) {
const ids = submissions.map((pending) => pending.id);
const idSet = new Set(ids);
this.#pending = this.#pending.filter((pending) => !idSet.has(pending.id));
if (alreadyProjected) this.#projection.remove((candidate) => candidate.type === "client.message.submitted" && idSet.has(candidate.data.submissionId));
else {
this.#projection.replace((candidate) => candidate.type === "client.message.submitted" && candidate.data.submissionId === ids[0], event);
this.#projection.remove((candidate) => candidate.type === "client.message.submitted" && idSet.has(candidate.data.submissionId));
}
return {
alreadyProjected,
event,
ids
};
}
};
//#endregion
//#region src/protocol/event-dedupe.ts
function createEventDeduper() {
const seen = /* @__PURE__ */ new Set();
return {
admit(event) {
const id = event.meta?.id;
if (id === void 0) return true;
if (seen.has(id)) return false;
seen.add(id);
return true;
},
get size() {
return seen.size;
}
};
}
//#endregion
//#region src/client/eve-agent-store.ts
const detachStore = Symbol("detachEveAgentStore");
const attachStore = Symbol("attachEveAgentStore");
var EveAgentStore = class {
#client;
#autoPrewarm;
#attached = false;
#stream;
#pendingAuthorizations = /* @__PURE__ */ new Set();
#externalSession;
#optimistic;
#projection;
#subscribers = /* @__PURE__ */ new Set();
#seenEvents = createEventDeduper();
#activeTurn;
#callbacks = {};
#error;
#events;
#messageSubmissions;
#prewarmGeneration = 0;
#prewarmPromise;
#prewarmController;
#resumePromise;
#session;
#snapshot;
#status = "ready";
constructor(init) {
this.#autoPrewarm = init.prewarm ?? false;
this.#externalSession = init.session !== void 0;
this.#client = this.#externalSession ? void 0 : new Client({
auth: init.auth,
headers: init.headers,
host: init.host ?? ""
});
const initialEvents = [];
for (const event of init.initialEvents ?? []) {
if (this.#seenEvents.admit(event)) initialEvents.push(event);
updatePendingAuthorizations(this.#pendingAuthorizations, event);
}
this.#events = initialEvents;
this.#projection = new EveAgentProjection(init.reducer, this.#events);
this.#optimistic = init.optimistic ?? true;
this.#messageSubmissions = new OptimisticMessageSubmissions(this.#projection, this.#optimistic);
this.#session = init.session ?? (init.initialSession === void 0 ? void 0 : this.#client?.sessions.attach(init.initialSession.sessionId, { streamIndex: init.initialSession.streamIndex }));
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);
};
}
prewarm() {
if (this.#prewarmPromise !== void 0) return this.#prewarmPromise;
if (this.#session !== void 0 || this.#externalSession) return Promise.resolve();
if (this.#activeTurn !== void 0) return this.#activeTurn.response.then((response) => {
if (response === void 0) throw this.#error ?? new DOMException("Session creation was aborted.", "AbortError");
});
const client = this.#client;
if (client === void 0) return Promise.reject(/* @__PURE__ */ new Error("This eve agent store does not own a session client."));
const generation = this.#prewarmGeneration;
const controller = new AbortController();
this.#prewarmController = controller;
const promise = (async () => {
try {
const created = await client.sessions.create({ signal: controller.signal });
if (generation !== this.#prewarmGeneration) return;
this.#session = created.session;
this.#error = void 0;
if (this.#status === "error") this.#status = "ready";
this.#callbacks.onSessionChange?.(created.session.state);
this.#publish();
this.#ensureStream();
} catch (error) {
if (generation === this.#prewarmGeneration && this.#activeTurn === void 0 && this.#error === void 0) {
this.#error = toError(error);
this.#status = "error";
this.#callbacks.onError?.(this.#error);
this.#publish();
}
throw error;
}
})();
this.#prewarmPromise = promise;
const clear = () => {
if (this.#prewarmPromise === promise) {
this.#prewarmPromise = void 0;
this.#prewarmController = void 0;
}
};
promise.then(clear, clear);
return promise;
}
async send(input) {
return await this.#submit(input, this.#callbacks.prepareSend);
}
async #submit(input, prepareSend) {
if (this.#activeTurn !== void 0) {
if (this.#status === "resuming") throw new Error("eve session is resuming.");
return await this.#sendFollowUp(this.#activeTurn, input, prepareSend);
}
const turn = createActiveTurn((turn) => turn.acceptedFollowUps > 0 && this.#session !== void 0 ? this.#session.cancel() : turn.response.then((response) => response === void 0 ? { status: "no_active_turn" } : response.cancel()));
this.#activeTurn = turn;
this.#error = void 0;
this.#status = "submitted";
this.#publish();
let reader;
try {
const preparedInput = await (prepareSend === void 0 ? input : waitWithSignal(Promise.resolve(prepareSend(input)), createAbortSignal(input.signal, turn.abortController.signal)));
assertExclusiveTurnInput(preparedInput);
if (!this.#isActiveTurn(turn)) return;
const submissionId = this.#messageSubmissions.submit(preparedInput, this.#events.length);
this.#projectInputResponses(preparedInput);
this.#publish();
const turnInput = {
...preparedInput,
signal: createAbortSignal(preparedInput.signal, turn.abortController.signal)
};
const dispatched = await this.#dispatchTurn(turnInput);
const response = dispatched.response;
reader = dispatched.reader;
if (!this.#isActiveTurn(turn)) return;
turn.resolveResponse(response);
if (this.#handleReconciliation(this.#messageSubmissions.correlate(submissionId, getMessageResponseDeliveryId(response), this.#events))) this.#publish();
for await (const event of consumeMessageResponse(response, reader)) {
if (!this.#isActiveTurn(turn)) return;
turn.receivedFollowUps += turn.receivedFollowUpEvents.get(event) ?? 0;
turn.receivedFollowUpEvents.delete(event);
}
if (!this.#isActiveTurn(turn)) return;
await followSteeredTurns(turn, reader, () => this.#isActiveTurn(turn));
if (!this.#isActiveTurn(turn)) return;
this.#status = this.#error === void 0 ? "ready" : "error";
} catch (error) {
if (!this.#isActiveTurn(turn)) return;
if (isAbortError(error)) {
this.#status = "ready";
this.#messageSubmissions.fail(toError(error));
} else {
const reported = this.#error !== void 0;
this.#error ??= toError(error);
this.#status = "error";
this.#messageSubmissions.fail(this.#error);
if (!reported) this.#callbacks.onError?.(this.#error);
}
} finally {
reader?.[Symbol.dispose]();
this.#finishTurn(turn);
}
}
resume() {
if (this.#resumePromise !== void 0) return this.#resumePromise;
const promise = this.#resume();
this.#resumePromise = promise;
const clear = () => {
if (this.#resumePromise === promise) this.#resumePromise = void 0;
};
promise.then(clear, clear);
return promise;
}
async #resume() {
if (this.#status === "resuming" || this.#status === "streaming" || this.#status === "submitted") throw new Error("eve session is already processing a turn.");
if (this.#session === void 0) throw new Error("An eve session is required before resuming.");
const session = this.#session;
const turn = createActiveTurn(() => session.cancel());
this.#activeTurn = turn;
turn.resolveResponse(void 0);
this.#error = void 0;
this.#status = "resuming";
this.#publish();
let reader;
try {
const stream = this.#ensureStream({
catchUp: true,
startIndex: this.#events.length === session.state.streamIndex ? session.state.streamIndex : 0
});
reader = stream.subscribe(turn.abortController.signal);
await stream.caughtUp;
if (!this.#isActiveTurn(turn)) return;
reader.discard();
const tail = this.#events.at(-1);
if (tail !== void 0) this.#applyTerminalStreamFailure(tail);
if (tail !== void 0 && this.#error === void 0 && !isSettledSessionTail(this.#events)) {
this.#status = "streaming";
this.#publish();
for await (const event of reader) {
if (!this.#isActiveTurn(turn)) return;
turn.receivedFollowUps += turn.receivedFollowUpEvents.get(event) ?? 0;
turn.receivedFollowUpEvents.delete(event);
if (isCurrentTurnBoundaryEvent(event) && this.#pendingAuthorizations.size === 0) break;
}
}
await followSteeredTurns(turn, reader, () => this.#isActiveTurn(turn));
if (this.#isActiveTurn(turn)) this.#status = this.#error === void 0 ? "ready" : "error";
} catch (error) {
if (!this.#isActiveTurn(turn)) return;
if (isAbortError(error)) this.#status = "ready";
else {
this.#error = toError(error);
this.#status = "error";
this.#callbacks.onError?.(this.#error);
}
} finally {
reader?.[Symbol.dispose]();
this.#finishTurn(turn);
}
}
cancel() {
const turn = this.#activeTurn;
if (turn === void 0) return this.#status === "streaming" && this.#session !== void 0 ? this.#session.cancel() : Promise.resolve({ status: "no_active_turn" });
return turn.cancel();
}
[attachStore]() {
this.#attached = true;
if (this.#autoPrewarm && this.#session === void 0) this.prewarm().catch(() => {});
}
[detachStore]() {
this.#attached = false;
this.#stream?.close();
this.#stream = void 0;
this.#activeTurn?.abortController.abort();
this.#resetPrewarm();
this.#resumePromise = void 0;
}
reset() {
this.#stream?.close();
this.#stream = void 0;
this.#pendingAuthorizations.clear();
const turn = this.#activeTurn;
this.#activeTurn = void 0;
turn?.resolveResponse(void 0);
turn?.resolveCompletion();
turn?.abortController.abort();
this.#resetPrewarm();
this.#resumePromise = void 0;
if (!this.#externalSession) this.#session = void 0;
this.#events = [];
this.#seenEvents = createEventDeduper();
this.#messageSubmissions.reset();
this.#projection.reset();
this.#error = void 0;
this.#status = "ready";
this.#callbacks.onSessionChange?.(this.#session?.state);
this.#publish();
if (this.#autoPrewarm && this.#attached) this.prewarm().catch(() => {});
}
async #sendFollowUp(turn, input, prepareSend) {
if (input.message === void 0 || input.turnPolicy !== "steer") throw new Error("eve session is already processing a turn. Send a message with turnPolicy: \"steer\" to guide it at the next boundary.");
const generation = this.#prewarmGeneration;
const signal = createAbortSignal(input.signal, turn.abortController.signal);
const preparedInput = await waitWithSignal(Promise.resolve(prepareSend?.(input)), signal) ?? input;
if (generation !== this.#prewarmGeneration) return;
assertExclusiveTurnInput(preparedInput);
if (preparedInput.message === void 0 || preparedInput.turnPolicy !== "steer") throw new Error("An in-flight follow-up requires a message with turnPolicy: \"steer\".");
if (!this.#isActiveTurn(turn)) return await this.#submit(preparedInput);
const submissionId = this.#messageSubmissions.submit(preparedInput, this.#events.length);
if (submissionId !== void 0) turn.followUpSubmissionIds.add(submissionId);
this.#publish();
this.#ensureStream({
headers: preparedInput.headers,
streamReconnectPolicy: preparedInput.streamReconnectPolicy
});
let dispatch;
dispatch = (async () => {
try {
const signal = createAbortSignal(preparedInput.signal, turn.abortController.signal);
await waitWithSignal(turn.response, signal);
if (!this.#isActiveTurn(turn) || this.#session === void 0) throw new Error("The active eve turn ended before the follow-up could be sent.");
const { message, ...options } = preparedInput;
const response = await this.#session.send(message, {
...options,
signal
});
turn.acceptedFollowUps += 1;
if (this.#handleReconciliation(this.#messageSubmissions.correlate(submissionId, getMessageResponseDeliveryId(response), this.#events))) this.#publish();
} catch (error) {
if (this.#isActiveTurn(turn)) {
this.#messageSubmissions.fail(toError(error), submissionId);
this.#publish();
}
throw error;
} finally {
turn.followUpDispatches.delete(dispatch);
}
})();
turn.followUpDispatches.add(dispatch);
await dispatch;
await turn.completion;
}
async #dispatchTurn(input) {
const streamOptions = {
headers: input.headers,
streamReconnectPolicy: input.streamReconnectPolicy
};
if (this.#prewarmPromise !== void 0) {
await waitWithSignal(this.#prewarmPromise.catch((error) => {
if (this.#session !== void 0) throw error;
}), input.signal);
input.signal?.throwIfAborted();
}
if (this.#session === void 0) {
if (this.#client === void 0) throw new Error("An external eve session is required before sending.");
if (input.message === void 0) throw new Error("Cannot answer an input request before the session starts.");
const created = await this.#client.sessions.create({
...input,
message: input.message
});
input.signal?.throwIfAborted();
this.#session = created.session;
this.#callbacks.onSessionChange?.(created.session.state);
this.#publish();
return {
response: created.response,
reader: this.#ensureStream(streamOptions).subscribe(input.signal)
};
}
const reader = this.#ensureStream(streamOptions).subscribe(input.signal);
try {
if (input.inputResponses === void 0) {
const { message, ...options } = input;
return {
response: await this.#session.send(message, options),
reader
};
}
const { inputResponses, ...options } = input;
return {
response: await this.#session.respond(inputResponses, options),
reader
};
} catch (error) {
reader[Symbol.dispose]();
throw error;
}
}
#ensureStream(options = {}) {
if (this.#stream !== void 0 && !this.#stream.ended) {
this.#stream.setOptions(options);
return this.#stream;
}
if (this.#session === void 0) throw new Error("A session is required before opening its stream.");
const session = this.#session;
const generation = this.#prewarmGeneration;
this.#stream = new SessionEventStream(session, {
...options,
onEvent: (event) => {
if (generation === this.#prewarmGeneration) this.#acceptServerEvent(event);
},
onError: (error) => {
if (generation !== this.#prewarmGeneration) return;
this.#stream = void 0;
if (this.#activeTurn !== void 0) return;
this.#error = toError(error);
this.#status = "error";
this.#callbacks.onError?.(this.#error);
this.#publish();
}
});
return this.#stream;
}
#resetPrewarm() {
this.#prewarmGeneration += 1;
this.#prewarmController?.abort();
this.#prewarmController = void 0;
this.#prewarmPromise = void 0;
}
#finishTurn(turn) {
if (!this.#isActiveTurn(turn)) return;
turn.resolveResponse(void 0);
this.#activeTurn = void 0;
try {
this.#callbacks.onSessionChange?.(this.#session?.state);
this.#publish();
this.#callbacks.onFinish?.(this.#snapshot);
} finally {
turn.resolveCompletion();
}
}
#isActiveTurn(turn) {
return this.#activeTurn === turn;
}
#projectInputResponses(input) {
if (input.inputResponses === void 0 || input.inputResponses.length === 0) return;
this.#projection.append({
data: {
createdAt: Date.now(),
responses: input.inputResponses
},
type: "client.input.responded"
});
}
#acceptServerEvent(event) {
if (!this.#seenEvents.admit(event)) return;
const wasStreaming = this.#status === "streaming";
updatePendingAuthorizations(this.#pendingAuthorizations, event);
this.#events = [...this.#events, event];
this.#handleReconciliation(this.#messageSubmissions.apply(event));
this.#callbacks.onEvent?.(event);
this.#applyTerminalStreamFailure(event);
const settled = isCurrentTurnBoundaryEvent(event) && this.#pendingAuthorizations.size === 0;
if (this.#status !== "resuming" && this.#error === void 0) {
if ("data" in event && "turnId" in event.data) this.#status = "streaming";
if (this.#activeTurn === void 0 && settled) this.#status = "ready";
}
this.#callbacks.onSessionChange?.(this.#session?.state);
this.#publish();
if (this.#activeTurn === void 0 && wasStreaming && settled) this.#callbacks.onFinish?.(this.#snapshot);
}
#handleReconciliation(reconciliation) {
if (reconciliation === void 0) return false;
let followed = 0;
for (const id of reconciliation.ids) if (this.#activeTurn?.followUpSubmissionIds.delete(id)) followed += 1;
if (followed > 0 && this.#activeTurn !== void 0) {
if (reconciliation.alreadyProjected) this.#activeTurn.receivedFollowUps += followed;
else {
const previous = this.#activeTurn.receivedFollowUpEvents.get(reconciliation.event) ?? 0;
this.#activeTurn.receivedFollowUpEvents.set(reconciliation.event, previous + followed);
}
}
return true;
}
#applyTerminalStreamFailure(event) {
const error = toTerminalStreamFailureError(event);
if (error === void 0) return;
this.#status = "error";
this.#messageSubmissions.failAll(error);
if (this.#error === void 0) {
this.#error = error;
this.#callbacks.onError?.(error);
}
}
#createSnapshot() {
return {
data: this.#projection.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();
}
};
function detachEveAgentStore(store) {
store[detachStore]();
}
function attachEveAgentStore(store) {
store[attachStore]();
}
//#endregion
//#region src/client/agent-host.ts
const AGENT_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]*$/;
const EVE_NAMED_AGENT_ROUTE_PREFIX = "/eve";
function resolveEveAgentHost(input) {
if (input.agent === void 0) return input.host ?? "";
if (input.host !== void 0) throw new Error("useEveAgent cannot combine agent and host. Use one target option.");
assertValidAgentName(input.agent);
return `${EVE_NAMED_AGENT_ROUTE_PREFIX}/${input.agent}`;
}
function assertValidAgentName(name) {
if (!AGENT_NAME_PATTERN.test(name)) throw new Error(`eve agent name ${JSON.stringify(name)} is invalid. Use lowercase letters, numbers, underscores, or hyphens, starting with a letter or number.`);
}
//#endregion
//#region src/client/authorization-message-parts.ts
function createAuthorizationRequiredPart(event) {
const displayName = event.data.authorization?.displayName ?? formatAuthorizationDisplayName(event.data.name);
return {
authorization: event.data.authorization,
description: normalizeAuthorizationDescription(event.data.description, event.data.name, displayName),
displayName,
name: event.data.name,
state: "required",
stepIndex: event.data.stepIndex,
turnId: event.data.turnId,
type: "authorization"
};
}
function createAuthorizationCompletedPart(event, existing) {
const displayName = event.data.authorization?.displayName ?? existing?.displayName ?? formatAuthorizationDisplayName(event.data.name);
return {
authorization: existing?.authorization || event.data.authorization ? {
...existing?.authorization,
...event.data.authorization
} : void 0,
description: existing?.description ?? buildCompletedAuthorizationDescription(displayName, event.data.outcome, event.data.reason),
displayName,
name: event.data.name,
outcome: event.data.outcome,
reason: event.data.reason,
state: "completed",
stepIndex: existing?.stepIndex ?? event.data.stepIndex,
turnId: existing?.turnId ?? event.data.turnId,
type: "authorization"
};
}
function buildCompletedAuthorizationDescription(displayName, outcome, reason) {
if (outcome === "authorized") return `${displayName} connected.`;
return `${displayName} authorization ${outcome}${reason !== void 0 ? ` (${reason})` : ""}.`;
}
function normalizeAuthorizationDescription(description, name, displayName) {
if (description === `Authorization required for ${name}`) return `Authorization required for ${displayName}`;
return description;
}
function formatAuthorizationDisplayName(name) {
if (name.length === 0) return name;
return `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
}
//#endregion
//#region src/client/message-action-parts.ts
function toMessageInputRequest(request) {
return {
allowFreeform: request.allowFreeform,
display: request.display,
kind: request.kind,
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":
case "workflow-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 stringifyUnknown(value) {
if (typeof value === "string") return value;
try {
return JSON.stringify(value);
} catch {
return "Action failed.";
}
}
//#endregion
//#region src/client/message-reducer-primitives.ts
function projectReceivedParts(parts, message) {
return parts?.map((part) => part.type === "text" ? {
state: "done",
text: part.text,
type: "text"
} : {
filename: part.filename,
mediaType: part.mediaType,
size: part.size,
type: "file",
url: part.url
}) ?? [{
state: "done",
text: message,
type: "text"
}];
}
function partKey(part) {
switch (part.type) {
case "text": return `text:${part.stepIndex ?? 0}`;
case "reasoning": return `reasoning:${part.stepIndex ?? 0}`;
case "file": return `file:${part.stepIndex ?? 0}:${part.filename ?? part.url ?? part.mediaType}`;
case "step-start": return "step-start";
case "authorization": return `authorization:${part.turnId}:${part.stepIndex}:${part.name}`;
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 removeStreamingToolPartsForTurn(data, turnId) {
const index = data.messages.findIndex((message) => message.role === "assistant" && message.metadata?.turnId === turnId);
const message = data.messages[index];
if (message === void 0) return data;
return upsertMessage(data, {
...message,
parts: message.parts.filter((part) => part.type !== "dynamic-tool" || part.state !== "input-streaming")
});
}
function optimisticUserMessageId(submissionId) {
return `optimistic:${submissionId}:user`;
}
//#endregion
//#region src/client/message-run-parts.ts
function append(message, append) {
const current = latestStreamingRun(message, append.type, append.stepIndex);
return upsert(message, {
state: "streaming",
stepIndex: append.stepIndex,
text: (current?.text ?? "") + append.delta,
type: append.type
});
}
function latestStreamingRun(message, type, stepIndex) {
for (let index = message.parts.length - 1; index >= 0; index -= 1) {
const part = message.parts[index];
if (part?.type === type && part.stepIndex === stepIndex) return part.state === "streaming" ? part : void 0;
}
}
function upsert(message, next) {
let lastIndex = -1;
for (let index = message.parts.length - 1; index >= 0; index -= 1) {
const part = message.parts[index];
if (part?.type === next.type && part.stepIndex === next.stepIndex) {
lastIndex = index;
break;
}
}
const parts = lastIndex !== -1 && message.parts[lastIndex].state === "streaming" ? [
...message.parts.slice(0, lastIndex),
next,
...message.parts.slice(lastIndex + 1)
] : [...message.parts, next];
return {
...message,
metadata: {
...message.metadata,
status: next.type === "text" && next.state === "done" ? "complete" : "streaming"
},
parts
};
}
const messageRun = {
append,
upsert
};
//#endregion
//#region src/client/message-reducer.ts
function receivedMessageEventId(event) {
return event.meta.id ?? `${event.data.turnId}:${event.data.sequence}`;
}
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 "input.resolved": {
let next = data;
for (const resolution of event.data.resolutions) next = resolveInputRequest(next, resolution);
return next;
}
case "message.received":
if (event.data.kind === "execution.background_task") return data;
return upsertMessage(data, {
id: `${receivedMessageEventId(event)}:user`,
metadata: {
status: "complete",
turnId: event.data.turnId
},
parts: projectReceivedParts(event.data.parts, event.data.message),
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) => messageRun.append(ensureStepStartPart(message, event.data.stepIndex), {
delta: event.data.reasoningDelta,
stepIndex: event.data.stepIndex,
type: "reasoning"
}));
case "reasoning.completed": return updateAssistantMessage(data, event.data.turnId, (message) => messageRun.upsert(ensureStepStartPart(message, event.data.stepIndex), {
state: "done",
stepIndex: event.data.stepIndex,
text: event.data.reasoning,
type: "reasoning"
}));
case "action.input.appended": {
const existing = findToolPart(data, event.data.callId);
if (existing !== void 0 && existing.state !== "input-streaming") return data;
const nextPart = {
input: void 0,
inputText: (existing?.state === "input-streaming" ? existing.inputText : "") + event.data.inputTextDelta,
state: "input-streaming",
stepIndex: event.data.stepIndex,
toolCallId: event.data.callId,
toolMetadata: existing?.toolMetadata ?? { eve: {
kind: "unknown",
name: event.data.toolName
} },
toolName: event.data.toolName,
type: "dynamic-tool"
};
if (existing !== void 0) return updateToolPart(data, event.data.callId, nextPart);
return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), nextPart));
}
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 "approval.candidate": return data;
case "approval.settled": {
const existing = findToolPartByApprovalId(data, event.data.requestId);
if (existing === void 0) return data;
if (event.data.outcome === "approved") return updateToolPart(data, existing.toolCallId, {
approval: {
approved: true,
id: event.data.requestId,
reason: void 0
},
input: existing.input,
state: "approval-responded",
stepIndex: existing.stepIndex,
toolCallId: existing.toolCallId,
toolMetadata: existing.toolMetadata,
toolName: existing.toolName,
type: "dynamic-tool"
});
return updateToolPart(data, existing.toolCallId, {
approval: {
approved: false,
id: event.data.requestId,
reason: "Tool execution was cancelled."
},
input: existing.input,
state: "output-denied",
stepIndex: existing.stepIndex,
toolCallId: existing.toolCallId,
toolMetadata: existing.toolMetadata,
toolName: existing.toolName,
type: "dynamic-tool"
});
}
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 "action.partial": {
const existing = findToolPart(data, event.data.result.callId);
if (existing !== void 0 && isSettledToolPart(existing)) return data;
const descriptor = normalizeActionResult(event.data.result);
const nextPart = {
approval: approvedApproval(existing),
input: existing?.input,
output: event.data.result.output,
partial: true,
state: "output-available",
stepIndex: event.data.stepIndex,
toolCallId: event.data.result.callId,
toolMetadata: mergeToolMetadata(existing?.toolMetadata, createToolMetadata(descriptor)),
toolName: existing?.toolName ?? descriptor.toolName,
type: "dynamic-tool"
};
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 "authorization.required": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), createAuthorizationRequiredPart(event)));
case "authorization.completed": return completeAuthorization(data, event);
case "message.appended": return updateAssistantMessage(data, event.data.turnId, (message) => messageRun.append(ensureStepStartPart(message, event.data.stepIndex), {
delta: event.data.messageDelta,
stepIndex: event.data.stepIndex,
type: "text"
}));
case "message.completed": return updateAssistantMessage(data, event.data.turnId, (message) => {
if (event.data.message === null) return removeTextPart(message, event.data.stepIndex);
return messageRun.upsert(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 updateAssistantMessage(data, event.data.turnId, (message) => ({
...message,
metadata: {
...message.metadata,
status: "complete"
},
parts: removeStreamingToolParts(message.parts)
}));
case "turn.cancelled": return updateAssistantMessage(data, event.data.turnId, (message) => ({
...message,
metadata: {
...message.metadata,
status: "complete"
},
parts: removeStreamingToolParts(message.parts.map((part) => (part.type === "text" || part.type === "reasoning") && part.state === "streaming" ? {
...part,
state: "done"
} : part))
}));
case "turn.failed": return removeStreamingToolPartsForTurn(data, event.data.turnId);
case "session.failed": return data;
default: return data;
}
}
function removeStreamingToolParts(parts) {
return parts.filter((part) => part.type !== "dynamic-tool" || part.state !== "input-streaming");
}
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 resolveInputRequest(data, resolution) {
if (resolution.response !== void 0) return respondToInputRequest(data, resolution.response);
const existing = findToolPartByApprovalId(data, resolution.requestId);
if (!existing) return data;
return updateToolPart(data, existing.toolCallId, {
input: existing.input,
output: { status: resolution.outcome },
state: "output-available",
stepIndex: existing.stepIndex,
toolCallId: existing.toolCallId,
toolMetadata: existing.toolMetadata,
toolName: existing.toolName,
type: "dynamic-tool"
});
}
function updateAssistantMessage(data, turnId, update) {
const message = data.messages.find((message) => message.role === "assistant" && message.metadata?.turnId === turnId) ?? createAssistantMessage(turnId);
return upsertMessage(data, update(message));
}
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 removeTextPart(message, stepIndex) {
const parts = message.parts.filter((part) => part.type !== "text" || part.stepIndex !== stepIndex);
if (parts.length === message.parts.length) return message;
return {
...message,
metadata: {
...message.metadata,
status: "complete"
},
parts
};
}
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 completeAuthorization(data, event) {
const existing = findLatestPendingAuthorizationPart(data, event.data.name);
const next = createAuthorizationCompletedPart(event, existing);
if (existing !== void 0) return updateAuthorizationPart(data, existing, next);
return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), next));
}
function updateAuthorizationPart(data, existing, next) {
const message = data.messages.find((candidate) => candidate.role === "assistant" && candidate.parts.some((part) => part === existing));
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 isSettledToolPart(part) {
return part.state === "output-denied" || part.state === "output-error" || part.state === "output-available" && part.partial !== true;
}
function findLatestPendingAuthorizationPart(data, name) {
for (let messageIndex = data.messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
const message = data.messages[messageIndex];
if (message?.role !== "assistant") continue;
for (let partIndex = message.parts.length - 1; partIndex >= 0; partIndex -= 1) {
const part = message.parts[partIndex];
if (part?.type === "authorization" && part.state === "required" && part.name === name) 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;
}
//#endregion
//#region src/svelte/use-eve-agent.ts
var SvelteEveAgent = class {
#snapshot;
#store;
#subscribe;
constructor(store) {
this.#store = store;
this.#snapshot = store.snapshot;
this.#subscribe = createSubscriber((update) => {
if (!("window" in globalThis)) return;
const unsubscribe = store.subscribe(() => {
this.#snapshot = store.snapshot;
update();
});
return () => {
unsubscribe();
};
});
}
get data() {
this.#subscribe();
return this.#snapshot.data;
}
get error() {
this.#subscribe();
return this.#snapshot.error;
}
get events() {
this.#subscribe();
return this.#snapshot.events;
}
get session() {
this.#subscribe();
return this.#snapshot.session;
}
get status() {
this.#subscribe();
return this.#snapshot.status;
}
cancel = () => {
return this.#store.cancel();
};
reset = () => {
this.#store.reset();
};
prewarm = () => {
return this.#store.prewarm();
};
resume = () => {
return this.#store.resume();
};
respond = (inputResponses, options) => {
return this.#store.send({
...options,
inputResponses
});
};
send = (message, options) => {
return this.#store.send({
...options,
message
});
};
};
function useEveAgent(options = {}) {
if (options.resume && options.initialSession === void 0 && options.session === void 0) throw new Error("useEveAgent({ resume: true }) requires initialSession or session.");
const reducer = options.reducer ?? defaultMessageReducer();
const store = new EveAgentStore({
auth: options.auth,
headers: options.headers,
host: resolveEveAgentHost({
agent: options.agent,
host: options.host
}),
initialEvents: options.initialEvents,
initialSession: options.initialSession,
optimistic: options.optimistic,
prewarm: options.prewarm,
reducer,
session: options.session
});
store.setCallbacks({
onError: options.onError,
onEvent: options.onEvent,
onFinish: options.onFinish,
onSessionChange: options.onSessionChange,
prepareSend: options.prepareSend
});
if ("window" in globalThis) onMount(() => {
attachEveAgentStore(store);
if (options.resume) store.resume();
return () => detachEveAgentStore(store);
});
return new SvelteEveAgent(store);
}
//#endregion
export { defaultMessageReducer as n, useEveAgent as t };