@@ -18,7 +18,7 @@ import { resolve, join } from 'node:path';
1818import * as path from 'node:path' ;
1919import { globSync } from 'glob' ;
2020import { SfCommand , Flags } from '@salesforce/sf-plugins-core' ;
21- import { AuthInfo , Connection , Messages , SfError , SfProject } from '@salesforce/core' ;
21+ import { AuthInfo , Connection , Lifecycle , Messages , SfError } from '@salesforce/core' ;
2222import React from 'react' ;
2323import { render } from 'ink' ;
2424import { env } from '@salesforce/kit' ;
@@ -46,18 +46,18 @@ type Choice<Value> = {
4646} ;
4747
4848enum AgentSource {
49- ORG = 'org ' ,
50- LOCAL = 'local ' ,
49+ PUBLISHED = 'published ' ,
50+ SCRIPT = 'script ' ,
5151}
5252
53- type LocalAgent = { DeveloperName : string ; source : AgentSource . LOCAL ; path : string } ;
54- type OrgAgent = {
53+ type ScriptAgent = { DeveloperName : string ; source : AgentSource . SCRIPT ; path : string } ;
54+ type PublishedAgent = {
5555 Id : string ;
5656 DeveloperName : string ;
57- source : AgentSource . ORG ;
57+ source : AgentSource . PUBLISHED ;
5858} ;
5959
60- type AgentValue = LocalAgent | OrgAgent ;
60+ type AgentValue = ScriptAgent | PublishedAgent ;
6161
6262// https://developer.salesforce.com/docs/einstein/genai/guide/agent-api-get-started.html#prerequisites
6363export const UNSUPPORTED_AGENTS = [ 'Copilot_for_Salesforce' ] ;
@@ -93,60 +93,68 @@ export default class AgentPreview extends SfCommand<AgentPreviewResult> {
9393 summary : messages . getMessage ( 'flags.apex-debug.summary' ) ,
9494 char : 'x' ,
9595 } ) ,
96- 'mock -actions' : Flags . boolean ( {
97- summary : messages . getMessage ( 'flags.mock -actions.summary' ) ,
98- dependsOn : [ 'authoring-bundle' ] ,
96+ 'use-live -actions' : Flags . boolean ( {
97+ summary : messages . getMessage ( 'flags.use-live -actions.summary' ) ,
98+ default : false ,
9999 } ) ,
100100 } ;
101101
102102 public async run ( ) : Promise < AgentPreviewResult > {
103+ // STAGES OF PREVIEW
104+ // get user's agent selection either from flags, or interaction
105+ // if .agent selected, use the AgentSimulate class to preview
106+ // if published agent, use AgentPreview for preview
107+ // based on agent, differing auth mechanisms required
103108 const { flags } = await this . parse ( AgentPreview ) ;
104109
105- const { 'api-name' : apiNameFlag } = flags ;
110+ const { 'api-name' : apiNameFlag , 'use-live-actions' : useLiveActions } = flags ;
106111 const conn = flags [ 'target-org' ] . getConnection ( flags [ 'api-version' ] ) ;
107- const agentsQuery = await conn . query < AgentData > (
108- 'SELECT Id, DeveloperName, (SELECT Status FROM BotVersions) FROM BotDefinition WHERE IsDeleted = false'
109- ) ;
110-
111- if ( agentsQuery . totalSize === 0 ) throw new SfError ( 'No Agents found in the org' ) ;
112112
113- const agentsInOrg = agentsQuery . records ;
113+ const agentsInOrg = (
114+ await conn . query < AgentData > (
115+ 'SELECT Id, DeveloperName, (SELECT Status FROM BotVersions) FROM BotDefinition WHERE IsDeleted = false'
116+ )
117+ ) . records ;
114118
115119 let selectedAgent : AgentValue ;
116120
117121 if ( flags [ 'authoring-bundle' ] ) {
122+ // user specified --authoring-bundle, we'll find the script and use it
118123 const bundlePath = findAuthoringBundle ( this . project ! . getPath ( ) , flags [ 'authoring-bundle' ] ) ;
119124 if ( ! bundlePath ) {
120125 throw new SfError ( `Could not find authoring bundle for ${ flags [ 'authoring-bundle' ] } ` ) ;
121126 }
122127 selectedAgent = {
123128 DeveloperName : flags [ 'authoring-bundle' ] ,
124- source : AgentSource . LOCAL ,
129+ source : AgentSource . SCRIPT ,
125130 path : join ( bundlePath , `${ flags [ 'authoring-bundle' ] } .agent` ) ,
126131 } ;
127132 } else if ( apiNameFlag ) {
133+ // user specified --api-name, it should be in the list of agents from the org
128134 const agent = agentsInOrg . find ( ( a ) => a . DeveloperName === apiNameFlag ) ;
129135 if ( ! agent ) throw new Error ( `No valid Agents were found with the Api Name ${ apiNameFlag } .` ) ;
130136 validateAgent ( agent ) ;
131137 selectedAgent = {
132138 Id : agent . Id ,
133139 DeveloperName : agent . DeveloperName ,
134- source : AgentSource . ORG ,
140+ source : AgentSource . PUBLISHED ,
135141 } ;
136142 if ( ! selectedAgent ) throw new Error ( `No valid Agents were found with the Api Name ${ apiNameFlag } .` ) ;
137143 } else {
138144 selectedAgent = await select ( {
139145 message : 'Select an agent' ,
140- choices : getAgentChoices ( agentsInOrg , this . project ! ) ,
146+ choices : this . getAgentChoices ( agentsInOrg ) ,
141147 } ) ;
142148 }
149+
150+ // we have the selected agent, create the appropriate connection
143151 const authInfo = await AuthInfo . create ( {
144152 username : flags [ 'target-org' ] . getUsername ( ) ,
145153 } ) ;
146154 // Get client app - check flag first, then auth file, then env var
147155 let clientApp = flags [ 'client-app' ] ;
148156
149- if ( ! clientApp && selectedAgent ?. source === AgentSource . ORG ) {
157+ if ( ! clientApp && selectedAgent ?. source === AgentSource . PUBLISHED ) {
150158 const clientApps = getClientAppsFromAuth ( authInfo ) ;
151159
152160 if ( clientApps . length === 1 ) {
@@ -157,13 +165,18 @@ export default class AgentPreview extends SfCommand<AgentPreviewResult> {
157165 choices : clientApps . map ( ( app ) => ( { value : app , name : app } ) ) ,
158166 } ) ;
159167 } else {
160- // at this point we should throw an error
161168 throw new SfError ( 'No client app found.' ) ;
162169 }
163170 }
164171
172+ if ( useLiveActions && selectedAgent . source === AgentSource . PUBLISHED ) {
173+ void Lifecycle . getInstance ( ) . emitWarning (
174+ 'Published agents will always use real actions in your org, specifying --use-live-actions and selecting a published agent has no effect'
175+ ) ;
176+ }
177+
165178 const jwtConn =
166- selectedAgent ?. source === AgentSource . ORG
179+ selectedAgent ?. source === AgentSource . PUBLISHED
167180 ? await Connection . create ( {
168181 authInfo,
169182 clientApp,
@@ -173,9 +186,9 @@ export default class AgentPreview extends SfCommand<AgentPreviewResult> {
173186 const outputDir = await resolveOutputDir ( flags [ 'output-dir' ] , flags [ 'apex-debug' ] ) ;
174187 // Both classes share the same interface for the methods we need
175188 const agentPreview =
176- selectedAgent . source === AgentSource . ORG
189+ selectedAgent . source === AgentSource . PUBLISHED
177190 ? new Preview ( jwtConn , selectedAgent . Id )
178- : new AgentSimulate ( jwtConn , selectedAgent . path , flags [ 'mock-actions' ] ?? false ) ;
191+ : new AgentSimulate ( jwtConn , selectedAgent . path , useLiveActions ) ;
179192
180193 agentPreview . toggleApexDebugMode ( flags [ 'apex-debug' ] ) ;
181194
@@ -185,11 +198,48 @@ export default class AgentPreview extends SfCommand<AgentPreviewResult> {
185198 agent : agentPreview ,
186199 name : selectedAgent . DeveloperName ,
187200 outputDir,
201+ isLocalAgent : selectedAgent . source === AgentSource . SCRIPT ,
188202 } ) ,
189203 { exitOnCtrlC : false }
190204 ) ;
191205 await instance . waitUntilExit ( ) ;
192206 }
207+
208+ private getAgentChoices ( agents : AgentData [ ] ) : Array < Choice < AgentValue > > {
209+ const choices : Array < Choice < AgentValue > > = [ ] ;
210+
211+ // Add org agents
212+ for ( const agent of agents ) {
213+ if ( agentIsInactive ( agent ) || agentIsUnsupported ( agent . DeveloperName ) ) {
214+ continue ;
215+ }
216+
217+ choices . push ( {
218+ name : `${ agent . DeveloperName } (Published)` ,
219+ value : {
220+ Id : agent . Id ,
221+ DeveloperName : agent . DeveloperName ,
222+ source : AgentSource . PUBLISHED ,
223+ } ,
224+ } ) ;
225+ }
226+
227+ // Add local agents from .agent files
228+ const localAgentPaths = globSync ( '**/*.agent' , { cwd : this . project ! . getPath ( ) } ) ;
229+ for ( const agentPath of localAgentPaths ) {
230+ const agentName = path . basename ( agentPath , '.agent' ) ;
231+ choices . push ( {
232+ name : `${ agentName } (Agent Script)` ,
233+ value : {
234+ DeveloperName : agentName ,
235+ source : AgentSource . SCRIPT ,
236+ path : path . join ( this . project ! . getPath ( ) , agentPath ) ,
237+ } ,
238+ } ) ;
239+ }
240+
241+ return choices ;
242+ }
193243}
194244
195245export const agentIsUnsupported = ( devName : string ) : boolean => UNSUPPORTED_AGENTS . includes ( devName ) ;
@@ -213,42 +263,6 @@ export const validateAgent = (agent: AgentData): boolean => {
213263 return true ;
214264} ;
215265
216- export const getAgentChoices = ( agents : AgentData [ ] , project : SfProject ) : Array < Choice < AgentValue > > => {
217- const choices : Array < Choice < AgentValue > > = [ ] ;
218-
219- // Add org agents
220- for ( const agent of agents ) {
221- if ( agentIsInactive ( agent ) || agentIsUnsupported ( agent . DeveloperName ) ) {
222- continue ;
223- }
224-
225- choices . push ( {
226- name : `${ agent . DeveloperName } (org)` ,
227- value : {
228- Id : agent . Id ,
229- DeveloperName : agent . DeveloperName ,
230- source : AgentSource . ORG ,
231- } ,
232- } ) ;
233- }
234-
235- // Add local agents from .agent files
236- const localAgentPaths = globSync ( '**/*.agent' , { cwd : project . getPath ( ) } ) ;
237- for ( const agentPath of localAgentPaths ) {
238- const agentName = path . basename ( agentPath , '.agent' ) ;
239- choices . push ( {
240- name : `${ agentName } (local)` ,
241- value : {
242- DeveloperName : agentName ,
243- source : AgentSource . LOCAL ,
244- path : path . join ( project . getPath ( ) , agentPath ) ,
245- } ,
246- } ) ;
247- }
248-
249- return choices ;
250- } ;
251-
252266export const getClientAppsFromAuth = ( authInfo : AuthInfo ) : string [ ] =>
253267 Object . keys ( authInfo . getFields ( ) . clientApps ?? { } ) ;
254268
0 commit comments