@salesforce/agents
Version:
Client side APIs for working with Salesforce agents
348 lines (347 loc) • 14.8 kB
TypeScript
import { Connection, SfProject } from '@salesforce/core';
import { type PlannerResponse, PreviewMetadata } from './types';
export declare const metric: readonly ["completeness", "coherence", "conciseness", "output_latency_milliseconds"];
/**
* Sanitize a filename by removing or replacing illegal characters.
* This ensures the filename is valid across different operating systems.
*
* @param filename - The filename to sanitize
* @returns A sanitized filename safe for use across operating systems
*/
export declare const sanitizeFilename: (filename: string) => string;
/**
* Clean a string by replacing HTML entities with their respective characters.
*
* @param str - The string to clean.
* @returns The cleaned string with all HTML entities replaced with their respective characters.
*/
export declare const decodeHtmlEntities: (str?: string) => string;
/**
* Find the authoring bundle directory for a given bot name by recursively searching from a starting directory or directories.
*
* @param dirOrDirs - The directory or array of directories to start searching from
* @param botName - The name of the bot to find the authoring bundle directory for
* @returns The path to the authoring bundle directory if found, undefined otherwise
*/
export declare const findAuthoringBundle: (dirOrDirs: string | string[], botName: string) => string | undefined;
/**
* Find all local agent files matching the pattern aiAuthoringBundles/<name>/<name>.agent
* Only descends into aiAuthoringBundles to check direct children (no full tree walk under it).
*
* @param dir - The directory to start searching from
* @returns Array of paths to agent files
*/
export declare const findLocalAgents: (dir: string) => string[];
/**
* takes a connection and upgrades it to a NamedJWT connection
*
* @param {Connection} connection original Connection
* @returns {Promise<Connection>} upgraded connection
*/
export declare const useNamedUserJwt: (connection: Connection) => Promise<Connection>;
export type TranscriptRole = 'user' | 'agent';
export type TranscriptEntry = {
timestamp: string;
agentId: string;
sessionId: string;
role: TranscriptRole;
text?: string;
reason?: string;
raw?: any;
};
export type TurnIndexEntry = {
turn: number;
timestamp: string;
role: TranscriptRole;
summary: string;
summaryTruncated: boolean;
multiModal: string | null;
traceFile: string | null;
planId: string | null;
reason?: string;
};
export type TurnIndex = {
version: string;
sessionId: string;
agentId: string;
created: string;
turns: TurnIndexEntry[];
};
/**
* returns a path, and ensures it's created, to the agents history directory
*
* Initialize session directory
* Session directory structure:
* .sfdx/agents/<agentId>/sessions/<sessionId>/
* ├── transcript.jsonl # All transcript entries (one per line)
* ├── turn-index.json # Turn-trace correlation index
* ├── traces/ # Individual trace files
* │ ├── <planId1>.json
* │ └── <planId2>.json
* └── metadata.json # Session metadata (start time, end time, planIds, etc.)
*
* @param {string} agentId gotten from Agent.getAgentIdForStorage()
* @param {string} sessionId the preview's start call .SessionId
* @returns {Promise<string>} path to where history/metadata/transcripts are stored inside of local .sfdx
*/
export declare const getHistoryDir: (agentId: string, sessionId: string) => Promise<string>;
/**
* Get the path to the agent index directory. Create the directory if it doesn't exist.
*
* .sfdx/agents/<agentId>/ <-- returns this directory path
* ├── index.md # Session index file
* │── sessions/ # Session directories
* │ ├── <sessionId1>/ # Session 1 directory
* │ └── <sessionId2>/ # Session 2 directory
*
* @param {string} agentId
* @returns {Promise<string>} path to the agent index directory
*/
export declare const getAgentIndexDir: (agentId: string) => Promise<string>;
/**
* Append a transcript entry to the transcript.jsonl transcript file
*
* @param {TranscriptEntry} entry to save
* @param {string} sessionDir the preview's start call .SessionId
* @returns {Promise<void>}
*/
export declare const appendTranscriptToHistory: (entry: TranscriptEntry, sessionDir: string) => Promise<void>;
/**
* writes a trace to <plan-id>.json in history directory
*
* @param {string} planId
* @param {PlannerResponse | undefined} trace
* @param {string} historyDir
* @returns {Promise<void>}
*/
export declare const writeTraceToHistory: (planId: string, trace: PlannerResponse | undefined, historyDir: string) => Promise<void>;
export type TraceFileInfo = {
planId: string;
path: string;
size: number;
mtime: Date;
};
/**
* List trace files for a given agent session.
*
* Returns one entry per .json file in the session's traces/ directory.
* File path is absolute. Returns an empty array if the traces directory does not exist.
*/
export declare const listSessionTraces: (agentId: string, sessionId: string) => Promise<TraceFileInfo[]>;
/**
* Read a single trace file by planId. Returns null if the file does not exist or cannot be parsed.
*/
export declare const readSessionTrace: (agentId: string, sessionId: string, planId: string) => Promise<PlannerResponse | null>;
/**
* Read the turn-index.json for a session. Returns null if not found.
*/
export declare const readTurnIndex: (agentId: string, sessionId: string) => Promise<TurnIndex | null>;
/**
* Write or append a session line to .sfdx/agents/<agentId>/index.md.
* If the file does not exist, creates it with a header and the session line.
* If it exists, appends the new session line.
*/
export declare const logSessionToIndex: (agentDir: string, options: {
sessionId: string;
startTime: string;
simulated: boolean;
agentId: string;
}) => Promise<void>;
/**
* In-memory buffer for session history to minimize file I/O during conversation
*/
export declare class SessionHistoryBuffer {
private readonly sessionDir;
private readonly sessionId;
private readonly agentId;
private readonly created;
private readonly mockMode?;
private turnEntries;
private planIds;
constructor(sessionDir: string, sessionId: string, agentId: string, created: string, mockMode?: "Mock" | "Live Test" | undefined);
/**
* Create a SessionHistoryBuffer from existing session data on disk
* Used when resuming an existing session
*/
static fromDisk(sessionDir: string, sessionId: string, agentId: string): Promise<{
buffer: SessionHistoryBuffer;
turnCount: number;
}>;
/**
* Add a turn to the buffer (no file I/O)
*/
addTurn(entry: TurnIndexEntry): void;
/**
* Add a planId to the buffer (no file I/O)
*/
addPlanId(planId: string): void;
/**
* Update an existing turn with trace info (no file I/O)
*/
updateTurnWithTrace(turnNumber: number, planId: string): void;
/**
* Flush all buffered data to disk
* Called at session start (to create initial files), after each turn (to keep real-time), and at session end (to finalize)
*/
flush(endTime?: string): Promise<void>;
}
/**
* Log a turn to history using buffer (fast, no read-modify-write)
*
* @param {TranscriptEntry} entry the transcript entry to log
* @param {number} turnNumber the turn number (1-based)
* @param {string} sessionDir path to the session directory
* @param {SessionHistoryBuffer} buffer buffer for batched writes
* @returns {Promise<void>}
*/
export declare const logTurnToHistory: (entry: TranscriptEntry, turnNumber: number, sessionDir: string, buffer: SessionHistoryBuffer) => Promise<void>;
/**
* Extract HTTP status code from API errors. Supports:
* - ERROR_HTTP_404 / ERROR_HTTP_500 style (name, errorCode, or data.errorCode)
* - Numeric statusCode on error, cause, or response
*/
export declare function getHttpStatusCode(err: unknown): number | undefined;
export type RequestInfo = {
url: string;
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
headers?: Record<string, string>;
body?: string;
};
/**
* Makes an API request with automatic endpoint fallback.
* Tries api.salesforce.com first, then test.api.salesforce.com on 404, then dev.api.salesforce.com on 404.
*
* @param connection - The Salesforce connection
* @param requestInfo - The request information (url, method, headers, body, etc.)
* @param options - Optional retry/timeout options
* @returns The API response
* @throws SfError if all endpoints return 404, or immediately on non-404 errors
*/
export declare function requestWithEndpointFallback<T>(connection: Connection, requestInfo: RequestInfo, options?: {
retry?: {
maxRetries?: number;
};
}): Promise<T>;
/**
* Record a trace for a turn using buffer (fast, minimal I/O)
*
* @param {string} historyDir path to the session directory
* @param {number} turnNumber the turn number that generated this trace
* @param {string} planId the plan ID for this trace
* @param {PlannerResponse | undefined} trace the trace data to write
* @param {SessionHistoryBuffer} buffer buffer for batched updates
* @returns {Promise<void>}
*/
export declare const recordTraceForTurn: (historyDir: string, turnNumber: number, planId: string, trace: PlannerResponse | undefined, buffer: SessionHistoryBuffer) => Promise<void>;
/**
* Get all history data for a session including metadata, transcript, and traces
*
* @param agentId gotten from Agent.getAgentIdForStorage()
* @param sessionId optional - the preview sessions' ID, gotten originally from /start .SessionId. If not provided, returns the most recent conversation
* @returns Object containing parsed metadata, transcript entries, and traces
*/
export declare const getAllHistory: (agentId: string, sessionId: string | undefined) => Promise<{
metadata: PreviewMetadata | null;
transcript: TranscriptEntry[];
traces: PlannerResponse[];
}>;
/**
* Read and parse the last conversation's transcript entries from JSON.
*
* @param agentId gotten from Agent.getAgentIdForStorage()
* @param sessionId the preview sessions' ID, gotten originally from /start .SessionId
* @returns Array of TranscriptEntry in file order (chronological append order).
*/
export declare const readTranscriptEntries: (agentId: string, sessionId: string) => Promise<TranscriptEntry[]>;
export type TestRunnerType = 'agentforce-studio' | 'testing-center';
/** Detects the test runner from a run ID's Salesforce ID prefix (`3A2` = Agentforce Studio, `4KB` = Testing Center). */
export declare function detectTestRunnerFromId(runId: string): TestRunnerType | undefined;
/**
* Determines which test runner to use based on available metadata types in the org.
*
* This function checks for the presence of:
* - `AiEvaluationDefinition` (Testing Center)
* - `AiTestingDefinition` (Agentforce Studio)
*
* If a test definition with the same name exists in both metadata types, an error is thrown
* to prevent ambiguity.
*
* @param connection - The Salesforce connection
* @param testDefinitionName - Optional test definition name to check for conflicts
* @returns 'agentforce-studio' if only Agentforce Studio metadata exists, 'testing-center' if only Testing Center metadata exists
* @throws {SfError} if both metadata types exist with the same test definition name
* @throws {SfError} if neither metadata type exists
*
* @example
* ```typescript
* const runnerType = await determineTestRunner(connection, 'MyTestSuite');
* if (runnerType === 'agentforce-studio') {
* const tester = new AgentforceStudioTester(connection);
* } else {
* const tester = new AgentTester(connection);
* }
* ```
*/
export declare function determineTestRunner(connection: Connection, testDefinitionName?: string): Promise<TestRunnerType>;
export type SessionType = 'simulated' | 'live' | 'published';
export type PreviewSessionMeta = {
displayName?: string;
timestamp?: string;
sessionType?: SessionType;
};
/**
* Save a marker so send/end can validate that the session was started for this agent.
* Caller must have started the session (agent has sessionId set). Uses agent.getHistoryDir() for the path.
* Pass displayName (authoring bundle name or production agent API name) so "agent preview sessions" can show it.
*/
export declare function createPreviewSessionCache(agent: {
getHistoryDir: () => Promise<string>;
}, options?: {
displayName?: string;
sessionType?: SessionType;
}): Promise<void>;
/**
* Validate that the session was started for this agent (marker file exists in agent's history dir for current sessionId).
* Caller must set sessionId on the agent (agent.setSessionId) before calling.
* Throws SfError if the session marker is not found.
*/
export declare function validatePreviewSession(agent: {
getHistoryDir: () => Promise<string>;
}): Promise<void>;
/**
* Remove the session marker so this session is no longer considered "active" for send/end without --session-id.
* Call after ending the session. Caller must set sessionId on the agent before calling.
*/
export declare function removePreviewSessionCache(agent: {
getHistoryDir: () => Promise<string>;
}): Promise<void>;
/**
* List session IDs that have a cache marker (started via "agent preview start") for this agent.
* Uses project path and agent's storage ID to find .sfdx/agents/<agentId>/sessions/<sessionId>/session-meta.json.
*/
export declare function getCachedPreviewSessionIds(project: SfProject, agent: {
getAgentIdForStorage: () => string | Promise<string>;
}): Promise<string[]>;
/**
* Return the single "current" session ID when safe: exactly one cached session for this agent.
* Returns undefined when there are zero or multiple sessions (caller should require --session-id).
*/
export declare function getCurrentPreviewSessionId(project: SfProject, agent: {
getAgentIdForStorage: () => string | Promise<string>;
}): Promise<string | undefined>;
export type CachedPreviewSessionInfo = {
sessionId: string;
timestamp?: string;
sessionType?: SessionType;
};
export type CachedPreviewSessionEntry = {
agentId: string;
displayName?: string;
sessions: CachedPreviewSessionInfo[];
};
/**
* List all cached preview sessions in the project, grouped by agent ID.
* displayName (when present in session-meta.json) is the authoring bundle name or production agent API name for display.
* Use this to show users which sessions exist so they can end or clean up.
*/
export declare function listCachedPreviewSessions(project: SfProject): Promise<CachedPreviewSessionEntry[]>;