UNPKG

@copilotkit/runtime

Version:

<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />

392 lines (390 loc) 13.6 kB
require("reflect-metadata"); const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs'); let _copilotkit_shared = require("@copilotkit/shared"); //#region src/v2/runtime/intelligence-platform/client.ts /** * Header name carrying the per-call end-user identity that the CopilotKit * Intelligence `/mcp` endpoint requires. Internal CopilotKit machinery — the * runtime stamps this onto `agent.headers` after `identifyUser` resolves, * and the auto-attach in `configureAgentForRequest` reads it back to gate * MCP-server attachment and to populate the outbound `X-Cpki-User-Id` * header on every MCP request. Not part of the public user API. * * @internal */ const INTELLIGENCE_USER_ID_HEADER = "x-cpki-user-id"; /** * Error thrown when an Intelligence platform HTTP request returns a non-2xx * status. Carries the HTTP {@link status} code so callers can branch on * specific failures (e.g. 404 for "not found", 409 for "conflict") without * parsing the error message string. * * @example * ```ts * try { * await intelligence.getThread({ threadId }); * } catch (error) { * if (error instanceof PlatformRequestError && error.status === 404) { * // thread does not exist yet * } * } * ``` */ var PlatformRequestError = class extends Error { constructor(message, status) { super(message); this.status = status; this.name = "PlatformRequestError"; } }; var CopilotKitIntelligence = class { #apiUrl; #runnerWsUrl; #clientWsUrl; #apiKey; #mcpServerEnabled; #threadCreatedListeners = /* @__PURE__ */ new Set(); #threadUpdatedListeners = /* @__PURE__ */ new Set(); #threadDeletedListeners = /* @__PURE__ */ new Set(); constructor(config) { const intelligenceWsUrl = normalizeIntelligenceWsUrl(config.wsUrl); this.#apiUrl = config.apiUrl.replace(/\/$/, ""); this.#runnerWsUrl = deriveRunnerWsUrl(intelligenceWsUrl); this.#clientWsUrl = deriveClientWsUrl(intelligenceWsUrl); this.#apiKey = config.apiKey; this.#mcpServerEnabled = config.mcpServer ?? false; if (config.onThreadCreated) this.onThreadCreated(config.onThreadCreated); if (config.onThreadUpdated) this.onThreadUpdated(config.onThreadUpdated); if (config.onThreadDeleted) this.onThreadDeleted(config.onThreadDeleted); } /** * Register a listener invoked whenever a thread is created. * * Multiple listeners can be registered. Each call returns an unsubscribe * function that removes the listener when called. * * @param callback - Receives the newly created {@link ThreadSummary}. * @returns A function that removes this listener when called. * * @example * ```ts * const unsubscribe = intelligence.onThreadCreated((thread) => { * console.log("Thread created:", thread.id); * }); * // later… * unsubscribe(); * ``` */ onThreadCreated(callback) { this.#threadCreatedListeners.add(callback); return () => { this.#threadCreatedListeners.delete(callback); }; } /** * Register a listener invoked whenever a thread is updated (including archive). * * Multiple listeners can be registered. Each call returns an unsubscribe * function that removes the listener when called. * * @param callback - Receives the updated {@link ThreadSummary}. * @returns A function that removes this listener when called. */ onThreadUpdated(callback) { this.#threadUpdatedListeners.add(callback); return () => { this.#threadUpdatedListeners.delete(callback); }; } /** * Register a listener invoked whenever a thread is deleted. * * Multiple listeners can be registered. Each call returns an unsubscribe * function that removes the listener when called. * * @param callback - Receives the {@link ThreadDeletedPayload} identifying * the deleted thread. * @returns A function that removes this listener when called. */ onThreadDeleted(callback) { this.#threadDeletedListeners.add(callback); return () => { this.#threadDeletedListeners.delete(callback); }; } ɵgetApiUrl() { return this.#apiUrl; } ɵgetRunnerWsUrl() { return this.#runnerWsUrl; } ɵgetClientWsUrl() { return this.#clientWsUrl; } ɵgetRunnerAuthToken() { return this.#apiKey; } /** @internal Used by the runtime's auto-attach to populate `Authorization`. */ ɵgetApiKey() { return this.#apiKey; } /** @internal Used by the runtime's auto-attach to gate MCP attachment. */ ɵisMcpServerEnabled() { return this.#mcpServerEnabled; } async #request(method, path, body) { const url = `${this.#apiUrl}${path}`; const headers = { Authorization: `Bearer ${this.#apiKey}`, "Content-Type": "application/json" }; const response = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : void 0 }); if (!response.ok) { const text = await response.text().catch(() => ""); _copilotkit_shared.logger.error({ status: response.status, body: text, path }, "Intelligence platform request failed"); throw new PlatformRequestError(`Intelligence platform error ${response.status}: ${text || response.statusText}`, response.status); } const text = await response.text(); if (!text) return; return JSON.parse(text); } #invokeLifecycleCallback(callbackName, payload) { const listeners = callbackName === "onThreadCreated" ? this.#threadCreatedListeners : callbackName === "onThreadUpdated" ? this.#threadUpdatedListeners : this.#threadDeletedListeners; for (const callback of listeners) try { callback(payload); } catch (error) { _copilotkit_shared.logger.error({ err: error, callbackName, payload }, "Intelligence lifecycle callback failed"); } } /** * List all non-archived threads for a given user and agent. * * @param params.userId - User whose threads to list. * @param params.agentId - Agent whose threads to list. * @returns The thread list along with realtime subscription credentials. * @throws {@link PlatformRequestError} on non-2xx responses. */ async listThreads(params) { const query = { userId: params.userId, agentId: params.agentId }; if (params.includeArchived) query.includeArchived = "true"; if (params.limit != null) query.limit = String(params.limit); if (params.cursor) query.cursor = params.cursor; const qs = new URLSearchParams(query).toString(); return this.#request("GET", `/api/threads?${qs}`); } async ɵsubscribeToThreads(params) { return this.#request("POST", "/api/threads/subscribe", { userId: params.userId }); } /** * Update thread metadata (e.g. name). * * Triggers the `onThreadUpdated` lifecycle callback on success. * * @returns The updated thread summary. * @throws {@link PlatformRequestError} on non-2xx responses. */ async updateThread(params) { const response = await this.#request("PATCH", `/api/threads/${encodeURIComponent(params.threadId)}`, { userId: params.userId, agentId: params.agentId, ...params.updates }); this.#invokeLifecycleCallback("onThreadUpdated", response.thread); return response.thread; } /** * Create a new thread on the platform. * * Triggers the `onThreadCreated` lifecycle callback on success. * * @returns The newly created thread summary. * @throws {@link PlatformRequestError} with status 409 if a thread with the * same `threadId` already exists. */ async createThread(params) { const response = await this.#request("POST", `/api/threads`, { threadId: params.threadId, userId: params.userId, agentId: params.agentId, ...params.name !== void 0 ? { name: params.name } : {} }); this.#invokeLifecycleCallback("onThreadCreated", response.thread); return response.thread; } /** * Fetch a single thread by ID. * * @returns The thread summary. * @throws {@link PlatformRequestError} with status 404 if the thread does * not exist. */ async getThread(params) { return (await this.#request("GET", `/api/threads/${encodeURIComponent(params.threadId)}`)).thread; } /** * Get an existing thread or create it if it does not exist. * * Handles the race where a concurrent request creates the thread between * the initial 404 and the subsequent `createThread` call by catching the * 409 Conflict and retrying the get. * * Triggers the `onThreadCreated` lifecycle callback when a new thread is * created. * * @returns An object containing the thread and a `created` flag indicating * whether the thread was newly created (`true`) or already existed (`false`). * @throws {@link PlatformRequestError} on non-2xx responses other than * 404 (get) and 409 (create race). */ async getOrCreateThread(params) { try { return { thread: await this.getThread({ threadId: params.threadId }), created: false }; } catch (error) { if (!(error instanceof PlatformRequestError && error.status === 404)) throw error; } try { return { thread: await this.createThread(params), created: true }; } catch (error) { if (error instanceof PlatformRequestError && error.status === 409) return { thread: await this.getThread({ threadId: params.threadId }), created: false }; throw error; } } /** * Fetch the full message history for a thread. * * @returns All persisted messages in chronological order. * @throws {@link PlatformRequestError} on non-2xx responses. */ async getThreadMessages(params) { return this.#request("GET", `/api/threads/${encodeURIComponent(params.threadId)}/messages`); } /** * Fetch the persisted AG-UI event stream for a thread. * * Backed by the platform's `GET /api/_inspect/threads/:id/events` * introspection endpoint (see Intelligence PR #144). Events are returned * in replay order across every run that targeted the thread. The * `_inspect/` prefix flags this as debug-only — production code paths * must not depend on it. * * @throws {@link PlatformRequestError} on non-2xx responses. */ async getThreadEvents(params) { return this.#request("GET", `/api/_inspect/threads/${encodeURIComponent(params.threadId)}/events`); } /** * Fetch the current agent state for a thread. * * Backed by the platform's `GET /api/_inspect/threads/:id/state` * introspection endpoint (see Intelligence PR #144). The platform folds * RFC 6902 STATE_DELTA events on top of the latest STATE_SNAPSHOT, so * the returned state reflects the thread's current state — not just the * last snapshot. The discriminated response distinguishes "no snapshot * persisted yet" from "snapshot present" so consumers can render the * correct empty state. * * @throws {@link PlatformRequestError} on non-2xx responses. */ async getThreadState(params) { return this.#request("GET", `/api/_inspect/threads/${encodeURIComponent(params.threadId)}/state`); } /** * Mark a thread as archived. * * Archived threads are excluded from {@link listThreads} results. * Triggers the `onThreadUpdated` lifecycle callback on success. * * @throws {@link PlatformRequestError} on non-2xx responses. */ async archiveThread(params) { const response = await this.#request("PATCH", `/api/threads/${encodeURIComponent(params.threadId)}`, { userId: params.userId, agentId: params.agentId, archived: true }); this.#invokeLifecycleCallback("onThreadUpdated", response.thread); } /** * Permanently delete a thread and its message history. * * This is irreversible. Triggers the `onThreadDeleted` lifecycle callback * on success. * * @throws {@link PlatformRequestError} on non-2xx responses. */ async deleteThread(params) { await this.#request("DELETE", `/api/threads/${encodeURIComponent(params.threadId)}`, { reason: `Deleted via CopilotKit runtime (userId=${params.userId}, agentId=${params.agentId})` }); this.#invokeLifecycleCallback("onThreadDeleted", params); } async ɵacquireThreadLock(params) { return this.#request("POST", `/api/threads/${encodeURIComponent(params.threadId)}/lock`, { runId: params.runId, userId: params.userId, agentId: params.agentId, ...params.lockKeyPrefix !== void 0 ? { lockKeyPrefix: params.lockKeyPrefix } : {}, ...params.ttlSeconds !== void 0 ? { ttlSeconds: params.ttlSeconds } : {} }); } async ɵcleanupThreadLock(params) { return this.#request("DELETE", `/api/threads/${encodeURIComponent(params.threadId)}/lock`, { runId: params.runId }); } async ɵrenewThreadLock(params) { return this.#request("PATCH", `/api/threads/${encodeURIComponent(params.threadId)}/lock`, { runId: params.runId, ttlSeconds: params.ttlSeconds, ...params.lockKeyPrefix !== void 0 ? { lockKeyPrefix: params.lockKeyPrefix } : {} }); } async ɵgetActiveJoinCode(params) { const qs = new URLSearchParams({ userId: params.userId }).toString(); return this.#request("GET", `/api/threads/${encodeURIComponent(params.threadId)}/join-code?${qs}`); } async ɵconnectThread(params) { return await this.#request("POST", `/api/threads/${encodeURIComponent(params.threadId)}/connect`, { userId: params.userId, agentId: params.agentId }) ?? null; } }; function normalizeIntelligenceWsUrl(wsUrl) { return wsUrl.replace(/\/$/, ""); } function deriveRunnerWsUrl(wsUrl) { if (wsUrl.endsWith("/runner")) return wsUrl; if (wsUrl.endsWith("/client")) return `${wsUrl.slice(0, -7)}/runner`; return `${wsUrl}/runner`; } function deriveClientWsUrl(wsUrl) { if (wsUrl.endsWith("/client")) return wsUrl; if (wsUrl.endsWith("/runner")) return `${wsUrl.slice(0, -7)}/client`; return `${wsUrl}/client`; } //#endregion exports.CopilotKitIntelligence = CopilotKitIntelligence; exports.INTELLIGENCE_USER_ID_HEADER = INTELLIGENCE_USER_ID_HEADER; exports.PlatformRequestError = PlatformRequestError; //# sourceMappingURL=client.cjs.map