UNPKG

@mondaydotcomorg/atp-client

Version:
1,192 lines (1,183 loc) 37.6 kB
'use strict'; var atpProtocol = require('@mondaydotcomorg/atp-protocol'); var atpRuntime = require('@mondaydotcomorg/atp-runtime'); var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); // src/core/session.ts var ClientSession = class { static { __name(this, "ClientSession"); } baseUrl; customHeaders; clientId; clientToken; initPromise; hooks; constructor(baseUrl, headers, hooks) { this.baseUrl = baseUrl; this.customHeaders = headers || {}; this.hooks = hooks; } /** * Initializes the client session with the server. * This MUST be called before any other operations. * The server generates and returns a unique client ID and token. * @param clientInfo - Optional client information * @param tools - Optional client tool definitions to register with the server * @param services - Optional client service capabilities (LLM, approval, embedding) */ async init(clientInfo, tools, services) { if (this.initPromise) { await this.initPromise; return { clientId: this.clientId, token: this.clientToken, expiresAt: 0, tokenRotateAt: 0 }; } this.initPromise = (async () => { const url = `${this.baseUrl}/api/init`; const body = JSON.stringify({ clientInfo, tools: tools || [], services }); const headers = await this.prepareHeaders("POST", url, body); const response = await fetch(url, { method: "POST", headers, body }); if (!response.ok) { throw new Error(`Client initialization failed: ${response.status} ${response.statusText}`); } const data = await response.json(); this.clientId = data.clientId; this.clientToken = data.token; })(); await this.initPromise; return { clientId: this.clientId, token: this.clientToken, expiresAt: 0, tokenRotateAt: 0 }; } /** * Gets the unique client ID. */ getClientId() { if (!this.clientId) { throw new Error("Client not initialized. Call init() first."); } return this.clientId; } /** * Ensures the client is initialized before making requests. */ async ensureInitialized() { if (!this.clientId) { throw new Error("Client not initialized. Call init() first."); } } /** * Creates HTTP headers for requests. */ getHeaders() { const headers = { "Content-Type": "application/json", ...this.customHeaders }; if (this.clientId) { headers["X-Client-ID"] = this.clientId; } if (this.clientToken) { headers["Authorization"] = `Bearer ${this.clientToken}`; } return headers; } getBaseUrl() { return this.baseUrl; } /** * Updates the client token from response headers (token refresh). */ updateToken(response) { const newToken = response.headers.get("X-ATP-Token"); if (newToken) { this.clientToken = newToken; } } /** * Prepares headers for a request, calling preRequest hook if configured */ async prepareHeaders(method, url, body) { let headers = { "Content-Type": "application/json", ...this.customHeaders }; if (this.clientId) { headers["X-Client-ID"] = this.clientId; } if (this.clientToken) { headers["Authorization"] = `Bearer ${this.clientToken}`; } if (this.hooks?.preRequest) { try { const result = await this.hooks.preRequest({ url, method, currentHeaders: headers, body }); if (result.abort) { throw new Error(result.abortReason || "Request aborted by preRequest hook"); } if (result.headers) { headers = result.headers; } } catch (error) { throw error; } } return headers; } }; // src/core/in-process-session.ts var InProcessSession = class { static { __name(this, "InProcessSession"); } server; clientId; clientToken; initialized = false; initPromise; constructor(server) { this.server = server; } async init(clientInfo, tools, services) { if (this.initPromise) { await this.initPromise; return { clientId: this.clientId, token: this.clientToken, expiresAt: 0, tokenRotateAt: 0 }; } this.initPromise = (async () => { await this.server.start(); const ctx = this.createContext({ method: "POST", path: "/api/init", body: { clientInfo, tools: tools || [], services } }); const result = await this.server.handleInit(ctx); this.clientId = result.clientId; this.clientToken = result.token; this.initialized = true; })(); await this.initPromise; return { clientId: this.clientId, token: this.clientToken, expiresAt: 0, tokenRotateAt: 0 }; } getClientId() { if (!this.clientId) { throw new Error("Client not initialized. Call init() first."); } return this.clientId; } async ensureInitialized() { if (!this.initialized) { throw new Error("Client not initialized. Call init() first."); } } getHeaders() { const headers = { "content-type": "application/json" }; if (this.clientId) { headers["x-client-id"] = this.clientId; } if (this.clientToken) { headers["authorization"] = `Bearer ${this.clientToken}`; } return headers; } getBaseUrl() { return ""; } updateToken(_response) { } async prepareHeaders(_method, _url, _body) { return this.getHeaders(); } async getDefinitions(options) { await this.ensureInitialized(); const ctx = this.createContext({ method: "GET", path: "/api/definitions", query: options?.apiGroups ? { apiGroups: options.apiGroups.join(",") } : {} }); return await this.server.getDefinitions(ctx); } async getRuntimeDefinitions(options) { await this.ensureInitialized(); const ctx = this.createContext({ method: "GET", path: "/api/runtime", query: options?.apis?.length ? { apis: options.apis.join(",") } : {} }); return await this.server.getRuntimeDefinitions(ctx); } async getServerInfo() { await this.ensureInitialized(); return this.server.getInfo(); } async search(query, options) { await this.ensureInitialized(); const ctx = this.createContext({ method: "POST", path: "/api/search", body: { query, ...options } }); return await this.server.handleSearch(ctx); } async explore(path) { await this.ensureInitialized(); const ctx = this.createContext({ method: "POST", path: "/api/explore", body: { path } }); return await this.server.handleExplore(ctx); } async execute(code, config) { await this.ensureInitialized(); const ctx = this.createContext({ method: "POST", path: "/api/execute", body: { code, config } }); return await this.server.handleExecute(ctx); } async resume(executionId, callbackResult) { await this.ensureInitialized(); const ctx = this.createContext({ method: "POST", path: `/api/resume/${executionId}`, body: { result: callbackResult } }); return await this.server.handleResume(ctx, executionId); } async resumeWithBatchResults(executionId, batchResults) { await this.ensureInitialized(); const ctx = this.createContext({ method: "POST", path: `/api/resume/${executionId}`, body: { results: batchResults } }); return await this.server.handleResume(ctx, executionId); } createContext(options) { const noopLogger = { debug: /* @__PURE__ */ __name(() => { }, "debug"), info: /* @__PURE__ */ __name(() => { }, "info"), warn: /* @__PURE__ */ __name(() => { }, "warn"), error: /* @__PURE__ */ __name(() => { }, "error") }; return { method: options.method, path: options.path, query: options.query || {}, headers: this.getHeaders(), body: options.body, clientId: this.clientId, clientToken: this.clientToken, logger: noopLogger, status: 200, responseBody: null, throw: /* @__PURE__ */ __name((status, message) => { const error = new Error(message); error.status = status; throw error; }, "throw"), assert: /* @__PURE__ */ __name((condition, message) => { if (!condition) { throw new Error(message); } }, "assert"), set: /* @__PURE__ */ __name(() => { }, "set") }; } }; // src/core/api-operations.ts var APIOperations = class { static { __name(this, "APIOperations"); } session; inProcessSession; apiDefinitions; constructor(session, inProcessSession) { this.session = session; this.inProcessSession = inProcessSession; } /** * Connects to the server and retrieves API definitions. */ async connect(options) { await this.session.ensureInitialized(); if (this.inProcessSession) { const data2 = await this.inProcessSession.getDefinitions(options); this.apiDefinitions = data2.typescript; return { serverVersion: data2.version, capabilities: {}, apiGroups: data2.apiGroups }; } const params = new URLSearchParams(); if (options?.apiGroups) { params.set("apiGroups", options.apiGroups.join(",")); } const url = `${this.session.getBaseUrl()}/api/definitions?${params}`; const headers = await this.session.prepareHeaders("GET", url); const response = await fetch(url, { headers }); if (!response.ok) { throw new Error(`Connection failed: ${response.status} ${response.statusText}`); } const data = await response.json(); this.apiDefinitions = data.typescript; return { serverVersion: data.version, capabilities: {}, apiGroups: data.apiGroups }; } /** * Gets the TypeScript type definitions for available APIs. */ getTypeDefinitions() { if (!this.apiDefinitions) { throw new Error("Not connected. Call connect() first."); } return this.apiDefinitions; } /** * Searches for available API functions. */ async searchAPI(query, options) { await this.session.ensureInitialized(); if (this.inProcessSession) { const data2 = await this.inProcessSession.search(query, options); return data2.results; } const url = `${this.session.getBaseUrl()}/api/search`; const body = JSON.stringify({ query, ...options }); const headers = await this.session.prepareHeaders("POST", url, body); const response = await fetch(url, { method: "POST", headers, body }); if (!response.ok) { throw new Error(`Search failed: ${response.status} ${response.statusText}`); } const data = await response.json(); return data.results; } /** * Explores the API filesystem at the given path. */ async exploreAPI(path) { await this.session.ensureInitialized(); if (this.inProcessSession) { return await this.inProcessSession.explore(path); } const url = `${this.session.getBaseUrl()}/api/explore`; const body = JSON.stringify({ path }); const headers = await this.session.prepareHeaders("POST", url, body); const response = await fetch(url, { method: "POST", headers, body }); if (!response.ok) { throw new Error(`Explore failed: ${response.status} ${response.statusText}`); } return await response.json(); } /** * Gets information about the server. */ async getServerInfo() { await this.session.ensureInitialized(); if (this.inProcessSession) { return await this.inProcessSession.getServerInfo(); } const url = `${this.session.getBaseUrl()}/api/info`; const headers = await this.session.prepareHeaders("GET", url); const response = await fetch(url, { headers }); if (!response.ok) { throw new Error(`Failed to get server info: ${response.status}`); } return await response.json(); } /** * Gets ATP runtime API definitions as TypeScript declarations. * Returns the full TypeScript definitions for atp.llm.*, atp.cache.*, etc. * These are the APIs available during code execution. * * Behavior: * - No options: Returns APIs based on client capabilities (default filtering) * - apis: ['llm', 'cache']: Returns only specified APIs (intersection with client capabilities) * - apis: []: Returns all APIs regardless of client capabilities * * @param options - Optional filtering options * @param options.apis - Specific APIs to include (e.g., ['llm', 'cache', 'approval']) */ async getRuntimeDefinitions(options) { await this.session.ensureInitialized(); if (this.inProcessSession) { return await this.inProcessSession.getRuntimeDefinitions(options?.apis ? { apis: options.apis } : void 0); } const params = new URLSearchParams(); if (options?.apis && options.apis.length > 0) { params.set("apis", options.apis.join(",")); } const url = `${this.session.getBaseUrl()}/api/runtime${params.toString() ? `?${params}` : ""}`; const headers = await this.session.prepareHeaders("GET", url); const response = await fetch(url, { headers }); if (!response.ok) { throw new Error(`Failed to get runtime definitions: ${response.status}`); } return await response.text(); } }; // src/errors.ts var ClientCallbackError = class extends Error { static { __name(this, "ClientCallbackError"); } constructor(message) { super(message); this.name = "ClientCallbackError"; } }; // src/core/provenance-registry.ts var ProvenanceTokenRegistry = class { static { __name(this, "ProvenanceTokenRegistry"); } cache = /* @__PURE__ */ new Map(); maxSize; ttl; sequenceCounter = 0; constructor(maxSize = 1e4, ttlHours = 1) { this.maxSize = maxSize; this.ttl = ttlHours * 3600 * 1e3; } /** * Add a token to the registry */ add(token) { this.evictExpired(); if (this.cache.size >= this.maxSize) { this.evictLRU(); } this.cache.set(token, { token, addedAt: Date.now(), sequence: this.sequenceCounter++ }); } /** * Get recent tokens (non-expired, sorted by age, limited) * Returns tokens in chronological order (oldest first, most recent last) */ getRecentTokens(maxCount = 1e3) { if (maxCount <= 0) { return []; } this.evictExpired(); const now = Date.now(); const expiredTokens = []; const entries = Array.from(this.cache.values()).filter((entry) => { try { const [body] = entry.token.split("."); if (!body) { expiredTokens.push(entry.token); return false; } const payload = JSON.parse(Buffer.from(body, "base64url").toString()); if (!payload.expiresAt || payload.expiresAt <= now) { expiredTokens.push(entry.token); return false; } return true; } catch { expiredTokens.push(entry.token); return false; } }).sort((a, b) => a.sequence - b.sequence).slice(-maxCount); for (const token of expiredTokens) { this.cache.delete(token); } return entries.map((e) => e.token); } /** * Clear all tokens */ clear() { this.cache.clear(); } /** * Get registry size */ size() { return this.cache.size; } /** * Evict expired tokens */ evictExpired() { const now = Date.now(); const toDelete = []; for (const [token, entry] of this.cache.entries()) { if (now - entry.addedAt > this.ttl) { toDelete.push(token); } } for (const token of toDelete) { this.cache.delete(token); } } /** * Evict least recently used (oldest) token */ evictLRU() { let oldestToken = null; let oldestSequence = Infinity; for (const [token, entry] of this.cache.entries()) { if (entry.sequence < oldestSequence) { oldestSequence = entry.sequence; oldestToken = token; } } if (oldestToken) { this.cache.delete(oldestToken); } } }; // src/core/execution-operations.ts var ExecutionOperations = class { static { __name(this, "ExecutionOperations"); } session; inProcessSession; serviceProviders; tokenRegistry; lastExecutionConfig = null; constructor(session, serviceProviders, inProcessSession) { this.session = session; this.inProcessSession = inProcessSession; this.serviceProviders = serviceProviders; this.tokenRegistry = new ProvenanceTokenRegistry(); } /** * Executes code on the server with real-time progress updates via SSE. */ async executeStream(code, config, onProgress) { await this.session.ensureInitialized(); const url = `${this.session.getBaseUrl()}/api/execute/stream`; const body = JSON.stringify({ code, config }); const headers = await this.session.prepareHeaders("POST", url, body); return new Promise((resolve, reject) => { const fetchImpl = typeof fetch !== "undefined" ? fetch : __require("undici").fetch; fetchImpl(url, { method: "POST", headers, body }).then(async (response) => { if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const reader = response.body?.getReader(); if (!reader) { throw new Error("Response body is not readable"); } const decoder = new TextDecoder(); let buffer = ""; let result = null; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() || ""; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line && line.startsWith("event:")) { const event = line.substring(6).trim(); for (let j = i + 1; j < lines.length; j++) { const dataLine = lines[j]; if (dataLine && dataLine.startsWith("data:")) { const dataStr = dataLine.substring(5).trim(); if (dataStr) { try { const data = JSON.parse(dataStr); if (event === "progress" && onProgress) { onProgress(data.message, data.fraction); } else if (event === "result") { result = data; } else if (event === "error") { reject(new Error(data.message)); return; } } catch (e) { atpRuntime.log.error("Failed to parse SSE data", { dataStr, error: e }); } } break; } } } } } if (result) { resolve(result); } else { reject(new Error("No result received from server")); } }).catch(reject); }); } /** * Executes code on the server in a sandboxed environment. */ async execute(code, config) { await this.session.ensureInitialized(); const hints = this.tokenRegistry.getRecentTokens(1e3); const detectedClientServices = { hasLLM: !!this.serviceProviders.getLLM(), hasApproval: !!this.serviceProviders.getApproval(), hasEmbedding: !!this.serviceProviders.getEmbedding(), hasTools: this.serviceProviders.hasTools() }; const executionConfig = { ...config, clientServices: { ...detectedClientServices, ...config?.clientServices || {} }, provenanceHints: hints.length > 0 ? hints : void 0 }; this.lastExecutionConfig = executionConfig; let result; if (this.inProcessSession) { result = await this.inProcessSession.execute(code, executionConfig); } else { const url = `${this.session.getBaseUrl()}/api/execute`; const body = JSON.stringify({ code, config: executionConfig }); const headers = await this.session.prepareHeaders("POST", url, body); const response = await fetch(url, { method: "POST", headers, body }); this.session.updateToken(response); if (!response.ok) { const error = await response.json(); throw new Error(`Execution failed: ${error.error || response.statusText}`); } result = await response.json(); } if (result.provenanceTokens && result.provenanceTokens.length > 0) { for (const { token } of result.provenanceTokens) { this.tokenRegistry.add(token); } } if (result.status === atpProtocol.ExecutionStatus.PAUSED && result.needsCallbacks) { return await this.handleBatchCallbacksAndResume(result); } if (result.status === atpProtocol.ExecutionStatus.PAUSED && result.needsCallback) { return await this.handlePauseAndResume(result); } return result; } /** * Handles batch callbacks by executing them in parallel and resuming. */ async handleBatchCallbacksAndResume(pausedResult) { if (!pausedResult.needsCallbacks || pausedResult.needsCallbacks.length === 0) { throw new Error("No batch callback requests in paused execution"); } const missingServiceIds = new Set(pausedResult.needsCallbacks.filter((cb) => !this.serviceProviders.hasServiceForCallback(cb.type)).map((cb) => cb.id)); if (missingServiceIds.size > 0) { const missingServices = pausedResult.needsCallbacks.filter((cb) => missingServiceIds.has(cb.id)); const explicitlyRequestedMissing = missingServices.filter((cb) => this.wasServiceExplicitlyRequested(cb.type)); const unexpectedMissing = missingServices.filter((cb) => !this.wasServiceExplicitlyRequested(cb.type)); if (explicitlyRequestedMissing.length > 0) { return pausedResult; } const errorMessage = `Missing service providers for callback types: ${unexpectedMissing.map((cb) => cb.type).join(", ")}`; atpRuntime.log.error(`Auto-handling batch paused execution without service providers: ${errorMessage}`, { executionId: pausedResult.executionId, missingServices: unexpectedMissing.map((cb) => ({ type: cb.type, operation: cb.operation, id: cb.id })) }); const existingCallbacks = pausedResult.needsCallbacks.filter((cb) => !missingServiceIds.has(cb.id)); if (existingCallbacks.length > 0) { try { const existingResults = await Promise.all(existingCallbacks.map(async (cb) => { const callbackResult = await this.serviceProviders.handleCallback(cb.type, { ...cb.payload, operation: cb.operation }); return { id: cb.id, result: callbackResult }; })); const allResults = pausedResult.needsCallbacks.map((cb) => { if (missingServiceIds.has(cb.id)) { return { id: cb.id, result: { __error: true, message: `${cb.type} service not provided by client` } }; } return existingResults.find((r) => r.id === cb.id); }); return await this.resumeWithBatchResults(pausedResult.executionId, allResults); } catch (error) { const errorMessage2 = error instanceof Error ? error.message : String(error); atpRuntime.log.error(`Error handling existing services in batch: ${errorMessage2}`, { executionId: pausedResult.executionId }); const allErrorResults = pausedResult.needsCallbacks.map((cb) => ({ id: cb.id, result: { __error: true, message: missingServiceIds.has(cb.id) ? `${cb.type} service not provided by client` : errorMessage2 } })); return await this.resumeWithBatchResults(pausedResult.executionId, allErrorResults); } } else { const allErrorResults = pausedResult.needsCallbacks.map((cb) => ({ id: cb.id, result: { __error: true, message: `${cb.type} service not provided by client` } })); return await this.resumeWithBatchResults(pausedResult.executionId, allErrorResults); } } try { const batchResults = await Promise.all(pausedResult.needsCallbacks.map(async (cb) => { const callbackResult = await this.serviceProviders.handleCallback(cb.type, { ...cb.payload, operation: cb.operation }); return { id: cb.id, result: callbackResult }; })); return await this.resumeWithBatchResults(pausedResult.executionId, batchResults); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); atpRuntime.log.error(`Error handling batch callbacks: ${errorMessage}`, { executionId: pausedResult.executionId, callbackCount: pausedResult.needsCallbacks.length }); const allErrorResults = pausedResult.needsCallbacks.map((cb) => ({ id: cb.id, result: { __error: true, message: errorMessage } })); return await this.resumeWithBatchResults(pausedResult.executionId, allErrorResults); } } /** * Handles a paused execution by processing the callback and resuming. */ async handlePauseAndResume(pausedResult) { if (!pausedResult.needsCallback) { throw new Error("No callback request in paused execution"); } if (!this.serviceProviders.hasServiceForCallback(pausedResult.needsCallback.type)) { const wasExplicitlyRequested = this.wasServiceExplicitlyRequested(pausedResult.needsCallback.type); if (wasExplicitlyRequested) { return pausedResult; } const errorMessage = `${pausedResult.needsCallback.type} service not provided by client`; atpRuntime.log.error(`Auto-handling paused execution without service provider: ${errorMessage}`, { executionId: pausedResult.executionId, callbackType: pausedResult.needsCallback.type, operation: pausedResult.needsCallback.operation }); return await this.resume(pausedResult.executionId, { __error: true, message: errorMessage }); } try { const callbackResult = await this.serviceProviders.handleCallback(pausedResult.needsCallback.type, { ...pausedResult.needsCallback.payload, operation: pausedResult.needsCallback.operation, executionId: pausedResult.executionId }); return await this.resume(pausedResult.executionId, callbackResult); } catch (error) { if (error instanceof ClientCallbackError) { throw error; } const errorMessage = error instanceof Error ? error.message : String(error); atpRuntime.log.error(`Error handling callback: ${errorMessage}`, { executionId: pausedResult.executionId, callbackType: pausedResult.needsCallback.type, operation: pausedResult.needsCallback.operation }); return await this.resume(pausedResult.executionId, { __error: true, message: errorMessage }); } } /** * Check if a service was explicitly requested in clientServices config */ wasServiceExplicitlyRequested(callbackType) { if (!this.lastExecutionConfig?.clientServices) { return false; } switch (callbackType) { case atpProtocol.CallbackType.LLM: return this.lastExecutionConfig.clientServices.hasLLM; case atpProtocol.CallbackType.APPROVAL: return this.lastExecutionConfig.clientServices.hasApproval; case atpProtocol.CallbackType.EMBEDDING: return this.lastExecutionConfig.clientServices.hasEmbedding; case atpProtocol.CallbackType.TOOL: return this.lastExecutionConfig.clientServices.hasTools; default: return false; } } /** * Resumes a paused execution with a callback result. */ async resume(executionId, callbackResult) { await this.session.ensureInitialized(); let result; if (this.inProcessSession) { result = await this.inProcessSession.resume(executionId, callbackResult); } else { const url = `${this.session.getBaseUrl()}/api/resume/${executionId}`; const body = JSON.stringify({ result: callbackResult }); const headers = await this.session.prepareHeaders("POST", url, body); const response = await fetch(url, { method: "POST", headers, body }); this.session.updateToken(response); if (!response.ok) { const error = await response.json(); throw new Error(`Resume failed: ${error.error || response.statusText}`); } result = await response.json(); } if (result.provenanceTokens && result.provenanceTokens.length > 0) { for (const { token } of result.provenanceTokens) { this.tokenRegistry.add(token); } } if (result.status === atpProtocol.ExecutionStatus.PAUSED && result.needsCallbacks) { return await this.handleBatchCallbacksAndResume(result); } if (result.status === atpProtocol.ExecutionStatus.PAUSED && result.needsCallback) { return await this.handlePauseAndResume(result); } return result; } /** * Resumes a paused execution with batch callback results. */ async resumeWithBatchResults(executionId, batchResults) { await this.session.ensureInitialized(); let result; if (this.inProcessSession) { result = await this.inProcessSession.resumeWithBatchResults(executionId, batchResults); } else { const url = `${this.session.getBaseUrl()}/api/resume/${executionId}`; const body = JSON.stringify({ results: batchResults }); const headers = await this.session.prepareHeaders("POST", url, body); const response = await fetch(url, { method: "POST", headers, body }); this.session.updateToken(response); if (!response.ok) { const error = await response.json(); throw new Error(`Batch resume failed: ${error.error || response.statusText}`); } result = await response.json(); } if (result.provenanceTokens && result.provenanceTokens.length > 0) { for (const { token } of result.provenanceTokens) { this.tokenRegistry.add(token); } } if (result.status === atpProtocol.ExecutionStatus.PAUSED && result.needsCallbacks) { return await this.handleBatchCallbacksAndResume(result); } if (result.status === atpProtocol.ExecutionStatus.PAUSED && result.needsCallback) { return await this.handlePauseAndResume(result); } return result; } }; var LLMOperation = { CALL: "call", EXTRACT: "extract", CLASSIFY: "classify" }; var EmbeddingOperation = { EMBED: "embed", SEARCH: "search" }; var ServiceProviders = class { static { __name(this, "ServiceProviders"); } providers = {}; toolHandlers = /* @__PURE__ */ new Map(); constructor(providers) { this.providers = providers || {}; if (providers?.tools) { for (const tool of providers.tools) { this.toolHandlers.set(tool.name, tool.handler); } } } provideLLM(handler) { this.providers.llm = handler; } provideApproval(handler) { this.providers.approval = handler; } provideEmbedding(handler) { this.providers.embedding = handler; } provideTools(tools) { this.providers.tools = tools; for (const tool of tools) { this.toolHandlers.set(tool.name, tool.handler); } } getLLM() { return this.providers.llm; } getApproval() { return this.providers.approval; } getEmbedding() { return this.providers.embedding; } getTools() { return this.providers.tools; } /** * Get tool definitions (without handlers) for sending to server */ getToolDefinitions() { if (!this.providers.tools) { return []; } return this.providers.tools.map((tool) => { const { handler, ...definition } = tool; return definition; }); } /** * Check if client has tools */ hasTools() { return !!(this.providers.tools && this.providers.tools.length > 0); } /** * Check if client has any services or tools */ hasAnyServices() { return !!(this.providers.llm || this.providers.approval || this.providers.embedding || this.hasTools()); } /** * Check if client has a service for a specific callback type */ hasServiceForCallback(callbackType) { switch (callbackType) { case atpProtocol.CallbackType.LLM: return !!this.providers.llm; case atpProtocol.CallbackType.APPROVAL: return !!this.providers.approval; case atpProtocol.CallbackType.EMBEDDING: return !!this.providers.embedding; case atpProtocol.CallbackType.TOOL: return this.hasTools(); default: return false; } } async handleCallback(callbackType, payload) { if (payload.operation === "batch_parallel" && payload.calls) { return await Promise.all(payload.calls.map(async (call) => { return await this.handleCallback(call.type, { ...call.payload, operation: call.operation }); })); } switch (callbackType) { case atpProtocol.CallbackType.LLM: if (!this.providers.llm) { throw new Error("LLM service not provided by client"); } if (payload.operation === LLMOperation.CALL) { return await this.providers.llm.call(payload.prompt, payload.options); } else if (payload.operation === LLMOperation.EXTRACT && this.providers.llm.extract) { return await this.providers.llm.extract(payload.prompt, payload.schema, payload.options); } else if (payload.operation === LLMOperation.CLASSIFY && this.providers.llm.classify) { return await this.providers.llm.classify(payload.text, payload.categories, payload.options); } throw new Error(`Unsupported LLM operation: ${payload.operation}`); case atpProtocol.CallbackType.APPROVAL: if (!this.providers.approval) { throw new Error("Approval service not provided by client"); } const contextWithExecutionId = payload.context ? { ...payload.context, executionId: payload.executionId } : { executionId: payload.executionId }; return await this.providers.approval.request(payload.message, contextWithExecutionId); case atpProtocol.CallbackType.EMBEDDING: if (!this.providers.embedding) { throw new Error("Embedding service not provided by client"); } if (payload.operation === EmbeddingOperation.EMBED) { return await this.providers.embedding.embed(payload.text); } else if (payload.operation === EmbeddingOperation.SEARCH) { const queryEmbedding = await this.providers.embedding.embed(payload.query); return queryEmbedding; } else if (payload.operation === "similarity" && this.providers.embedding.similarity) { return await this.providers.embedding.similarity(payload.text1, payload.text2); } throw new Error(`Unsupported embedding operation: ${payload.operation}`); case atpProtocol.CallbackType.TOOL: if (payload.operation === atpProtocol.ToolOperation.CALL) { const toolName = payload.toolName; const handler = this.toolHandlers.get(toolName); if (!handler) { throw new Error(`Tool '${toolName}' not found in client tools`); } const result = await handler(payload.input); return result; } throw new Error(`Unsupported tool operation: ${payload.operation}`); default: throw new Error(`Unknown callback type: ${callbackType}`); } } }; Object.defineProperty(exports, "CallbackType", { enumerable: true, get: function () { return atpProtocol.CallbackType; } }); exports.APIOperations = APIOperations; exports.ClientSession = ClientSession; exports.ExecutionOperations = ExecutionOperations; exports.InProcessSession = InProcessSession; exports.ServiceProviders = ServiceProviders; //# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map