diff --git a/command-snapshot.json b/command-snapshot.json index e27f0217..050e1343 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -141,7 +141,16 @@ "command": "agent:preview:start", "flagAliases": [], "flagChars": ["n", "o"], - "flags": ["api-name", "api-version", "authoring-bundle", "flags-dir", "json", "target-org", "use-live-actions"], + "flags": [ + "api-name", + "api-version", + "authoring-bundle", + "flags-dir", + "json", + "simulate-actions", + "target-org", + "use-live-actions" + ], "plugin": "@salesforce/plugin-agent" }, { diff --git a/messages/agent.preview.start.md b/messages/agent.preview.start.md index c326d99f..e6a04de3 100644 --- a/messages/agent.preview.start.md +++ b/messages/agent.preview.start.md @@ -8,7 +8,12 @@ This command outputs a session ID that you then use with the "agent preview send Identify the agent you want to start previewing with either the --authoring-bundle flag to specify a local authoring bundle's API name or --api-name to specify an activated published agent's API name. To find either API name, navigate to your package directory in your DX project. The API name of an authoring bundle is the same as its directory name under the "aiAuthoringBundles" metadata directory. Similarly, the published agent's API name is the same as its directory name under the "Bots" metadata directory. -When starting a preview session using the authoring bundle, which contains the agent's Agent Script file, the preview uses mocked actions by default. Specify --use-live-actions for live mode, which uses the real Apex classes, flows, etc, in the org for the actions. +When starting a preview session with --authoring-bundle, you must explicitly specify the execution mode using one of these flags: + +- --use-live-actions: Executes real Apex classes, flows, and other actions in the org. This surfaces compile and validation errors during preview. +- --simulate-actions: Uses AI to simulate action execution without calling real implementations. + +Published agents (--api-name) always use live actions. The mode flags are optional and have no effect for published agents. # flags.api-name.summary @@ -20,7 +25,11 @@ API name of the authoring bundle metadata component that contains the agent's Ag # flags.use-live-actions.summary -Use real actions in the org; if not specified, preview uses AI to simulate (mock) actions. +Execute real actions in the org (Apex classes, flows, etc.). Required with --authoring-bundle. + +# flags.simulate-actions.summary + +Use AI to simulate action execution instead of calling real actions. Required with --authoring-bundle. # output.sessionId @@ -40,14 +49,14 @@ Failed to start preview session: %s # examples -- Start a programmatic agent preview session by specifying an authoring bundle; uses mocked actions by default. Use the org with alias "my-dev-org": +- Start a programmatic agent preview session by specifying an authoring bundle; use simulated actions. Use the org with alias "my-dev-org": - <%= config.bin %> <%= command.id %> --authoring-bundle My_Agent_Bundle --target-org my-dev-org + <%= config.bin %> <%= command.id %> --authoring-bundle My_Agent_Bundle --target-org my-dev-org --simulate-actions - Similar to previous example but use live actions and the default org: <%= config.bin %> <%= command.id %> --authoring-bundle My_Agent_Bundle --use-live-actions -- Start a preview session with an activated published agent: +- Start a preview session with an activated published agent (always uses live actions): <%= config.bin %> <%= command.id %> --api-name My_Published_Agent diff --git a/src/commands/agent/preview/start.ts b/src/commands/agent/preview/start.ts index 738f40a7..c360cfe6 100644 --- a/src/commands/agent/preview/start.ts +++ b/src/commands/agent/preview/start.ts @@ -15,7 +15,7 @@ */ import { Flags, SfCommand, toHelpSection } from '@salesforce/sf-plugins-core'; -import { Lifecycle, Messages, SfError, EnvironmentVariable } from '@salesforce/core'; +import { EnvironmentVariable, Lifecycle, Messages, SfError } from '@salesforce/core'; import { Agent, ProductionAgent, ScriptAgent } from '@salesforce/agents'; import { createCache } from '../../../previewSessionStore.js'; import { COMPILATION_API_EXIT_CODES } from '../../../common.js'; @@ -61,14 +61,29 @@ export default class AgentPreviewStart extends SfCommand { const { flags } = await this.parse(AgentPreviewStart); + + // Validate: authoring-bundle requires exactly one mode flag + // (mutual exclusion of mode flags handled by 'exclusive' in flag definitions) + if (flags['authoring-bundle'] && !flags['use-live-actions'] && !flags['simulate-actions']) { + throw new SfError( + 'When using --authoring-bundle, you must specify either --use-live-actions or --simulate-actions.', + 'MissingModeFlag' + ); + } + const conn = flags['target-org'].getConnection(flags['api-version']); const useLiveActions = flags['use-live-actions']; + const simulateActions = flags['simulate-actions']; const agentIdentifier = flags['authoring-bundle'] ?? flags['api-name']!; // Track telemetry for agent initialization @@ -112,13 +127,16 @@ export default class AgentPreviewStart extends SfCommand { }); describe('setMockMode', () => { - it('should call setMockMode with "Mock" when --use-live-actions is not set', async () => { - await AgentPreviewStart.run(['--authoring-bundle', 'MyAgent', '--target-org', 'test@org.com']); + it('should call setMockMode with "Mock" when --simulate-actions is set', async () => { + await AgentPreviewStart.run([ + '--authoring-bundle', + 'MyAgent', + '--simulate-actions', + '--target-org', + 'test@org.com', + ]); expect(setMockModeStub.calledOnce).to.be.true; expect(setMockModeStub.firstCall.args[0]).to.equal('Mock'); @@ -96,5 +102,39 @@ describe('agent preview start', () => { expect(setMockModeStub.calledOnce).to.be.true; expect(setMockModeStub.firstCall.args[0]).to.equal('Live Test'); }); + + it('should throw error when using --authoring-bundle without mode flag', async () => { + try { + await AgentPreviewStart.run(['--authoring-bundle', 'MyAgent', '--target-org', 'test@org.com']); + expect.fail('Should have thrown an error'); + } catch (error: unknown) { + expect((error as Error).message).to.include('must specify either --use-live-actions or --simulate-actions'); + } + }); + + it('should throw error when using both mode flags together', async () => { + try { + await AgentPreviewStart.run([ + '--authoring-bundle', + 'MyAgent', + '--use-live-actions', + '--simulate-actions', + '--target-org', + 'test@org.com', + ]); + expect.fail('Should have thrown an error'); + } catch (error: unknown) { + expect((error as Error).message).to.match(/cannot also be provided when using/i); + } + }); + + it('should throw error when neither --api-name nor --authoring-bundle is provided', async () => { + try { + await AgentPreviewStart.run(['--use-live-actions', '--target-org', 'test@org.com']); + expect.fail('Should have thrown an error'); + } catch (error: unknown) { + expect((error as Error).message).to.match(/exactly one|api-name|authoring-bundle/i); + } + }); }); }); diff --git a/test/nuts/z3.agent.preview.nut.ts b/test/nuts/z3.agent.preview.nut.ts index 6e7ab7e6..d6317977 100644 --- a/test/nuts/z3.agent.preview.nut.ts +++ b/test/nuts/z3.agent.preview.nut.ts @@ -37,12 +37,17 @@ describe('agent preview', function () { it('should fail when authoring bundle does not exist', async () => { const invalidBundle = 'NonExistent_Bundle'; - execCmd(`agent preview --authoring-bundle ${invalidBundle} --target-org ${getUsername()}`, { ensureExitCode: 1 }); + execCmd( + `agent preview start --authoring-bundle ${invalidBundle} --simulate-actions --target-org ${getUsername()}`, + { ensureExitCode: 1 } + ); }); it('should fail when api-name does not exist in org', async () => { const invalidApiName = 'NonExistent_Agent_12345'; - execCmd(`agent preview --api-name ${invalidApiName} --target-org ${getUsername()}`, { ensureExitCode: 1 }); + execCmd(`agent preview start --api-name ${invalidApiName} --target-org ${getUsername()}`, { + ensureExitCode: 1, + }); }); describe('using preview start/send/end commands', () => { @@ -53,7 +58,7 @@ describe('agent preview', function () { const targetOrg = getUsername(); const startResult = execCmd( - `agent preview start --authoring-bundle ${bundleApiName} --target-org ${targetOrg} --json` + `agent preview start --authoring-bundle ${bundleApiName} --simulate-actions --target-org ${targetOrg} --json` ).jsonOutput?.result; expect(startResult?.sessionId).to.be.a('string'); const sessionId = startResult!.sessionId;