UNPKG

@salesforce/agents

Version:

Client side APIs for working with Salesforce agents

367 lines 17 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. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.decodeResponse = exports.Agent = exports.AgentCreateLifecycleStages = void 0; const node_util_1 = require("node:util"); const path = __importStar(require("node:path")); const promises_1 = require("node:fs/promises"); const core_1 = require("@salesforce/core"); const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve"); const kit_1 = require("@salesforce/kit"); const types_1 = require("./types"); const maybe_mock_1 = require("./maybe-mock"); const utils_1 = require("./utils"); const scriptAgent_1 = require("./agents/scriptAgent"); const productionAgent_1 = require("./agents/productionAgent"); const connectionManager_1 = require("./connectionManager"); ; const messages = new core_1.Messages('@salesforce/agents', 'agents', new Map([["invalidAgentSpecConfig", "Missing one or more of the required agent spec arguments: type, role, companyName, companyDescription"], ["missingAgentName", "The \"agentName\" configuration property is required when saving an agent."], ["agentRetrievalError", "Unable to retrieve newly created Agent metadata. Due to: %s"], ["agentRetrievalErrorActions", "Retrieve the agent metadata using the \"project retrieve start\" command."], ["missingAgentNameOrId", "The \"nameOrId\" agent option is required when creating an Agent instance."], ["agentIsDeleted", "The %s agent has been deleted and its activation state can't be changed."], ["agentActivationError", "Changing the agent's activation status was unsuccessful due to %s."], ["versionNotFound", "Version %s not found for this agent."], ["noActiveVersion", "No active version found for agent %s."], ["noVersionsFound", "No versions found for agent %s."]])); let logger; const getLogger = () => { if (!logger) { logger = core_1.Logger.childFromRoot('Agent'); } return logger; }; /** * Events emitted during Agent.create() for consumers to listen to and keep track of progress * * @type {{Creating: string, Previewing: string, Retrieving: string}} */ exports.AgentCreateLifecycleStages = { Creating: 'creatingAgent', Previewing: 'previewingAgent', Retrieving: 'retrievingAgent', }; /** * A client side representation of an agent. Also provides utilities * such as creating agents, listing agents, and creating agent specs. * * **Examples** * * Create a new instance and get the ID (uses the `Bot` ID): * * `const id = await Agent.init({connection, project, apiNameOrId: 'myBot' }).getId();` * * Create a new instance of an agent script based agent * * const agent = await Agent.init({connection, project, aabName: 'myBot' }); * * Start a preview session * * const agent = await Agent.init({connection, project, aabName: 'myBot' }); * await agent.preview.start(); * await agent.preview.send('hi there'); * * Create a new agent in the org: * * `const myAgent = await Agent.create(connection, project, options);` * * List all agents in the local project: * * `const agentList = await Agent.list(project);` */ class Agent { // Implementation static async init(options) { // Pre-warm the per-Connection ConnectionManager cache so the JWT bootstrap and // standard-connection setup happen here instead of on the first SFAP/SOQL call. // Subsequent managerFor(options.connection) lookups inside the agent are cache hits. await (0, connectionManager_1.managerFor)(options.connection); // Type guard: check if it's ScriptAgentOptions by looking for 'aabName' if ('aabName' in options) { return new scriptAgent_1.ScriptAgent(options); } else { const agent = new productionAgent_1.ProductionAgent(options); await agent.getBotMetadata(); return agent; } } /** * List all agents in the current project. * * @param project a `SfProject` for a local DX project. */ static async list(project) { const projectDirs = project.getPackageDirectories(); const bots = []; const collectBots = async (botPath) => { try { const dirStat = await (0, promises_1.stat)(botPath); if (!dirStat.isDirectory()) { return; } bots.push(...(await (0, promises_1.readdir)(botPath))); } catch (_err) { // eslint-disable-next-line no-unused-vars } }; for (const pkgDir of projectDirs) { // eslint-disable-next-line no-await-in-loop await collectBots(path.join(pkgDir.fullPath, 'bots')); // eslint-disable-next-line no-await-in-loop await collectBots(path.join(pkgDir.fullPath, 'main', 'default', 'bots')); } return bots; } /** * Lists all agents in the org. * * @param connection a `Connection` to an org. * @returns the list of agents */ static async listRemote(connection) { const botDefinitionFields = 'Id, IsDeleted, DeveloperName, MasterLabel, CreatedDate, CreatedById, LastModifiedDate, LastModifiedById, SystemModstamp, BotUserId, Description, Type, AgentType, AgentTemplate'; const botVersionFields = 'Id, Status, IsDeleted, BotDefinitionId, DeveloperName, CreatedDate, CreatedById, LastModifiedDate, LastModifiedById, SystemModstamp, VersionNumber, CopilotPrimaryLanguage, ToneType, CopilotSecondaryLanguages'; const agentsQuery = await connection.query(`SELECT ${botDefinitionFields}, (SELECT ${botVersionFields} FROM BotVersions ORDER BY VersionNumber) FROM BotDefinition`); return agentsQuery.records; } /** * Lists all agents available for preview, combining agents from the org and local script files. * * @param connection a `Connection` to an org. * @param project a `SfProject` for a local DX project. * @returns the list of previewable agents with their source (org or script) */ static async listPreviewable(connection, project) { const results = new Array(); (await connection.query("SELECT Id, DeveloperName, MasterLabel FROM BotDefinition WHERE Id IN (SELECT BotDefinitionId FROM BotVersion WHERE Status = 'active')")).records.map((agent) => results.push({ name: agent.MasterLabel, source: types_1.AgentSource.PUBLISHED, id: agent.Id, developerName: agent.DeveloperName, label: agent.MasterLabel, })); // Get local script agents const projectDirs = project.getPackageDirectories(); const localAgentPaths = new Set(); for (const pkgDir of projectDirs) { const agentFiles = (0, utils_1.findLocalAgents)(pkgDir.fullPath); for (const agentFile of agentFiles) { // Extract the directory path (parent of .agent file) const agentDir = path.dirname(agentFile); const agentName = path.basename(agentDir); const normalizedPath = path.resolve(agentDir); // Avoid duplicates if (!localAgentPaths.has(normalizedPath)) { localAgentPaths.add(normalizedPath); const previewableAgent = { name: agentName, source: types_1.AgentSource.SCRIPT, aabName: agentName, }; results.push(previewableAgent); } } } return results; } /** * Creates an agent from a configuration, optionally saving the agent in an org. * * @param connection a `Connection` to an org. * @param project a `SfProject` for a local DX project. * @param config a configuration for creating or previewing an agent. * @returns the agent definition */ static async create(connection, project, config) { const url = '/connect/ai-assist/create-agent'; const connectionManager = await (0, connectionManager_1.managerFor)(connection); const maybeMock = new maybe_mock_1.MaybeMock(connectionManager.getJwtConnection()); // When previewing agent creation just return the response. if (!config.saveAgent) { getLogger().debug(`Previewing agent creation using config: ${(0, node_util_1.inspect)(config)} in project: ${project.getPath()}`); await core_1.Lifecycle.getInstance().emit(exports.AgentCreateLifecycleStages.Previewing, {}); const response = await maybeMock.request('POST', url, config); return (0, exports.decodeResponse)(response); } if (!config.agentSettings?.agentName) { throw messages.createError('missingAgentName'); } getLogger().debug(`Creating agent using config: ${(0, node_util_1.inspect)(config)} in project: ${project.getPath()}`); await core_1.Lifecycle.getInstance().emit(exports.AgentCreateLifecycleStages.Creating, {}); if (!config.agentSettings.agentApiName) { config.agentSettings.agentApiName = (0, core_1.generateApiName)(config.agentSettings?.agentName); } const response = await maybeMock.request('POST', url, config); // When saving agent creation we need to retrieve the created metadata. if (response.isSuccess) { await core_1.Lifecycle.getInstance().emit(exports.AgentCreateLifecycleStages.Retrieving, {}); const defaultPackagePath = project.getDefaultPackage().path ?? 'force-app'; const standardConnection = connectionManager.getStandardConnection(); try { const cs = await source_deploy_retrieve_1.ComponentSetBuilder.build({ metadata: { metadataEntries: [`Agent:${config.agentSettings.agentApiName}`], directoryPaths: [defaultPackagePath], }, org: { username: standardConnection.getUsername(), exclude: [], }, }); const retrieve = await cs.retrieve({ usernameOrConnection: standardConnection, merge: true, format: 'source', output: path.resolve(project.getPath(), defaultPackagePath), }); const retrieveResult = await retrieve.pollStatus({ frequency: kit_1.Duration.milliseconds(200), timeout: kit_1.Duration.minutes(5), }); if (!retrieveResult.response.success) { const errMessages = retrieveResult.response.messages?.toString() ?? 'unknown'; const error = messages.createError('agentRetrievalError', [errMessages]); error.actions = [messages.getMessage('agentRetrievalErrorActions')]; throw error; } } catch (err) { const error = core_1.SfError.wrap(err); if (error.name === 'AgentRetrievalError') { throw error; } throw core_1.SfError.create({ name: 'AgentRetrievalError', message: messages.getMessage('agentRetrievalError', [error.message]), cause: error, actions: [messages.getMessage('agentRetrievalErrorActions')], }); } } return (0, exports.decodeResponse)(response); } /** * Create an agent spec from provided data. * * @param connection a `Connection` to an org. * @param config The configuration used to generate an agent spec. * @returns the agent job spec */ static async createSpec(connection, config) { const connectionManager = await (0, connectionManager_1.managerFor)(connection); const maybeMock = new maybe_mock_1.MaybeMock(connectionManager.getJwtConnection()); verifyAgentSpecConfig(config); const url = '/connect/ai-assist/draft-agent-topics'; const body = { agentType: config.agentType, generationInfo: { defaultInfo: { role: config.role, companyName: config.companyName, companyDescription: config.companyDescription, }, }, generationSettings: { maxNumOfTopics: config.maxNumOfTopics ?? 10, }, }; if (config.companyWebsite) { body.generationInfo.defaultInfo.companyWebsite = config.companyWebsite; } if (config.promptTemplateName) { body.generationInfo.customizedInfo = { promptTemplateName: config.promptTemplateName }; if (config.groundingContext) { body.generationInfo.customizedInfo.groundingContext = config.groundingContext; } } const response = await maybeMock.request('POST', url, body); const htmlDecodedResponse = (0, exports.decodeResponse)(response); if (htmlDecodedResponse.isSuccess) { return { ...config, topics: htmlDecodedResponse.topicDrafts }; } else { throw core_1.SfError.create({ name: 'AgentJobSpecCreateError', message: htmlDecodedResponse.errorMessage ?? 'unknown', data: htmlDecodedResponse, }); } } } exports.Agent = Agent; // private function used by Agent.createSpec() const verifyAgentSpecConfig = (config) => { const { agentType, role, companyName, companyDescription } = config; if (!agentType || !role || !companyName || !companyDescription) { throw messages.createError('invalidAgentSpecConfig'); } }; // Decodes all HTML entities in ai-assist API responses. // Recursively decodes HTML entities in all string values (not keys) throughout the object structure. const decodeResponse = (response) => { if (response === null || response === undefined) { return response; } // Handle arrays if (Array.isArray(response)) { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return response.map((item) => (0, exports.decodeResponse)(item)); } // Handle primitive values (strings, numbers, booleans, etc.) if (typeof response !== 'object') { if (typeof response === 'string') { return (0, utils_1.decodeHtmlEntities)(response); } return response; } // Handle objects - only decode values, preserve keys const decoded = {}; for (const [key, value] of Object.entries(response)) { // Recursively decode the value, preserving the key as-is decoded[key] = (0, exports.decodeResponse)(value); } return decoded; }; exports.decodeResponse = decodeResponse; //# sourceMappingURL=agent.js.map