UNPKG

@salesforce/agents

Version:

Client side APIs for working with Salesforce agents

314 lines 13.4 kB
"use strict"; /* * Copyright 2026, Salesforce, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AgentDataLibrary = void 0; const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const core_1 = require("@salesforce/core"); function baseUrl(connection, libraryId) { const base = `/services/data/v${String(connection.version)}/einstein/data-libraries`; return libraryId ? `${base}/${libraryId}` : base; } class AgentDataLibrary { static async list(connection, options) { try { let url = baseUrl(connection); if (options?.sourceType) { url += `?sourceType=${options.sourceType.toUpperCase()}`; } return await connection.request({ method: 'GET', url, }); } catch (error) { const wrapped = core_1.SfError.wrap(error); await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_list_failed' }); throw wrapped; } } static async create(connection, input) { let result; try { result = await connection.request({ method: 'POST', url: baseUrl(connection), body: JSON.stringify(input), headers: { 'Content-Type': 'application/json' }, }); } catch (error) { const wrapped = core_1.SfError.wrap(error); await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_create_failed' }); throw wrapped; } await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_create_success' }); if (input.groundingSource.sourceType === 'KNOWLEDGE') { // Trigger indexing — abort after 10s to avoid blocking process exit. // Server starts processing on receipt; we don't need the response. const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 10_000); try { await fetch(`${String(connection.instanceUrl)}${baseUrl(connection, result.libraryId)}/indexing`, { method: 'POST', headers: { Authorization: `Bearer ${String(connection.accessToken)}`, 'Content-Type': 'application/json', }, signal: controller.signal, }); } catch { // best-effort — server processes indexing even if aborted/timeout } finally { clearTimeout(timeout); } } return result; } static async get(connection, libraryId) { try { return await connection.request({ method: 'GET', url: baseUrl(connection, libraryId), }); } catch (error) { const wrapped = core_1.SfError.wrap(error); await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_get_failed' }); throw wrapped; } } static async update(connection, libraryId, input) { try { return await connection.request({ method: 'PATCH', url: baseUrl(connection, libraryId), body: JSON.stringify(input), headers: { 'Content-Type': 'application/json' }, }); } catch (error) { const wrapped = core_1.SfError.wrap(error); await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_update_failed' }); throw wrapped; } } static async delete(connection, libraryId) { try { await connection.request({ method: 'DELETE', url: baseUrl(connection, libraryId), }); } catch (error) { const wrapped = core_1.SfError.wrap(error); await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_delete_failed' }); throw wrapped; } } static async status(connection, libraryId, options) { try { let url = `${baseUrl(connection, libraryId)}/status`; if (options?.includeArtifacts) { url += '?includeArtifacts=true'; } return await connection.request({ method: 'GET', url, }); } catch (error) { const wrapped = core_1.SfError.wrap(error); await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_status_failed' }); throw wrapped; } } static async listFiles(connection, libraryId, options) { try { let url = `${baseUrl(connection, libraryId)}/files`; const params = new URLSearchParams(); if (options?.pageSize !== undefined) params.append('pageSize', String(options.pageSize)); if (options?.offset !== undefined) params.append('offset', String(options.offset)); if (options?.sortBy) params.append('sortBy', options.sortBy); if (options?.sortOrder) params.append('sortOrder', options.sortOrder); if (options?.name) params.append('name', options.name); if (options?.status) params.append('status', options.status); const queryString = params.toString(); if (queryString) { url += `?${queryString}`; } return await connection.request({ method: 'GET', url, }); } catch (error) { const wrapped = core_1.SfError.wrap(error); await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_list_files_failed' }); throw wrapped; } } static async deleteFile(connection, libraryId, fileId) { try { await connection.request({ method: 'DELETE', url: `${baseUrl(connection, libraryId)}/files/${fileId}`, }); } catch (error) { const wrapped = core_1.SfError.wrap(error); await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_file_delete_failed' }); throw wrapped; } } static async upload(connection, libraryId, filePaths, options) { const url = baseUrl(connection, libraryId); const paths = Array.isArray(filePaths) ? filePaths : [filePaths]; if (paths.length === 0) { throw new core_1.SfError('At least one file is required.', 'NoFilesProvided'); } await AgentDataLibrary.checkUploadReadiness(connection, url); const fileNames = paths.map((p) => ({ fileName: (0, node_path_1.basename)(p) })); const uploadEntries = await AgentDataLibrary.getUploadUrls(connection, url, fileNames); await Promise.all(paths.map((path, i) => AgentDataLibrary.uploadToS3(uploadEntries[i], path))); const uploadedFiles = uploadEntries.map((entry, i) => ({ filePath: entry.filePath, fileSize: (0, node_fs_1.statSync)(paths[i]).size, })); await AgentDataLibrary.triggerIndexing(connection, url, uploadedFiles); if (options?.waitMinutes) { const detail = await AgentDataLibrary.pollForReadiness(connection, url, libraryId, options.waitMinutes * 60); return { libraryId, retrieverId: detail.retrieverId, ragFeatureConfigId: `ARFPC_${libraryId}`, status: 'READY', }; } return { libraryId, status: 'IN_PROGRESS' }; } static async addFile(connection, libraryId, filePaths) { const url = baseUrl(connection, libraryId); const paths = Array.isArray(filePaths) ? filePaths : [filePaths]; if (paths.length === 0) { throw new core_1.SfError('At least one file is required.', 'NoFilesProvided'); } const fileInfos = paths.map((p) => ({ fileName: (0, node_path_1.basename)(p) })); const uploadEntries = await AgentDataLibrary.getUploadUrls(connection, url, fileInfos); await Promise.all(paths.map((path, i) => AgentDataLibrary.uploadToS3(uploadEntries[i], path))); const uploadedFiles = uploadEntries.map((entry, i) => ({ filePath: entry.filePath, fileSize: (0, node_fs_1.statSync)(paths[i]).size, })); await connection.request({ method: 'POST', url: `${url}/files`, body: JSON.stringify({ uploadedFiles }), headers: { 'Content-Type': 'application/json' }, }); await core_1.Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_adl_file_add_success' }); const fileNames = paths.map((p) => (0, node_path_1.basename)(p)); const fileName = fileNames.join(', '); return { success: true, fileName, fileNames, libraryId }; } static async waitForReady(connection, libraryId, waitSeconds) { return AgentDataLibrary.pollForReadiness(connection, baseUrl(connection, libraryId), libraryId, waitSeconds); } // ── Private helpers ─────────────────────────────────────── static async checkUploadReadiness(connection, url) { const maxAttempts = 3; for (let attempt = 1; attempt <= maxAttempts; attempt++) { let readiness; try { // eslint-disable-next-line no-await-in-loop readiness = await connection.request({ method: 'GET', url: `${url}/upload-readiness?waitMaxTime=120000`, }); } catch (error) { throw core_1.SfError.wrap(error); } if (readiness.ready) return; if (attempt === maxAttempts) { throw new core_1.SfError('Library not ready for upload after waiting.', 'UploadNotReady'); } } } static async getUploadUrls(connection, url, files) { const response = await connection.request({ method: 'POST', url: `${url}/file-upload-urls`, body: JSON.stringify({ files }), headers: { 'Content-Type': 'application/json' }, }); return response.uploadUrls; } static async uploadToS3(uploadEntry, filePath) { const maxFileSize = 100 * 1024 * 1024; // 100 MB — SearchIndex limit for chunking/vectorization const fileSize = (0, node_fs_1.statSync)(filePath).size; if (fileSize > maxFileSize) { throw new core_1.SfError(`File size (${fileSize} bytes) exceeds maximum of ${maxFileSize} bytes.`, 'FileTooLarge'); } const fileBuffer = (0, node_fs_1.readFileSync)(filePath); const response = await fetch(uploadEntry.uploadUrl, { method: 'PUT', headers: uploadEntry.headers, body: fileBuffer, }); if (!response.ok) { const body = await response.text(); throw new core_1.SfError(`S3 upload failed: HTTP ${response.status}: ${body}`, 'S3UploadFailed'); } } static async triggerIndexing(connection, url, uploadedFiles) { return connection.request({ method: 'POST', url: `${url}/indexing`, body: JSON.stringify({ uploadedFiles }), headers: { 'Content-Type': 'application/json' }, }); } static async pollForReadiness(connection, url, libraryId, waitSeconds) { const deadline = Date.now() + waitSeconds * 1000; const pollInterval = 10000; // eslint-disable-next-line no-await-in-loop while (Date.now() < deadline) { // eslint-disable-next-line no-await-in-loop const detail = await connection.request({ method: 'GET', url }); if (detail.retrieverId) return detail; if (detail.status === 'FAILED') { throw new core_1.SfError('Library indexing failed.', 'IndexingFailed'); } // eslint-disable-next-line no-await-in-loop await new Promise((resolve) => setTimeout(resolve, pollInterval)); } throw new core_1.SfError(`Indexing did not complete within ${waitSeconds} seconds.`, 'UploadTimeout'); } } exports.AgentDataLibrary = AgentDataLibrary; //# sourceMappingURL=agentDataLibrary.js.map