Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions messages/agent.activate.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,11 @@ Version number of the agent to activate; if not specified, the command provides
# error.missingRequiredFlags

Missing required flags: %s.

# error.agentNotFound

Agent '%s' not found in the org. Check that the API name is correct and that the agent exists.

# error.activationFailed

Failed to activate agent: %s
8 changes: 8 additions & 0 deletions messages/agent.deactivate.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ API name of the agent to deactivate; if not specified, the command provides a li
# error.missingRequiredFlags

Missing required flags: %s.

# error.agentNotFound

Agent '%s' not found in the org. Check that the API name is correct and that the agent exists.

# error.deactivationFailed

Failed to deactivate agent: %s
24 changes: 18 additions & 6 deletions messages/agent.preview.end.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ End an existing programmatic agent preview session and get trace location.

# description

You must have previously started a programmatic agent preview session with the "agent preview start" command to then use this command to end it. This command also displays the local directory where the session trace files are stored.
You must have previously started a programmatic agent preview session with the "agent preview start" command to then use this command to end it. This command also displays the local directory where the session trace files are stored.

The original "agent preview start" command outputs a session ID which you then use with the --session-id flag of this command to end the session. You don't have to specify the --session-id flag if an agent has only one active preview session. You must also use either the --authoring-bundle or --api-name flag to specify the API name of the authoring bundle or the published agent, respecitvely. 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.
The original "agent preview start" command outputs a session ID which you then use with the --session-id flag of this command to end the session. You don't have to specify the --session-id flag if an agent has only one active preview session. You must also use either the --authoring-bundle or --api-name flag to specify the API name of the authoring bundle or the published agent, respecitvely. 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.

# flags.session-id.summary

Session ID outputted by "agent preview start". Not required when the agent has exactly one active session. Run "agent preview sessions" to see the list of all sessions.

# flags.api-name.summary

API name of the activated published agent you want to preview.
API name of the activated published agent you want to preview.

# flags.authoring-bundle.summary

Expand All @@ -28,6 +28,18 @@ No agent preview session found. Run "sf agent preview start" to start a new agen

Multiple preview sessions found for this agent. Use the --session-id flag to identify a specific session. Sessions: %s

# error.agentNotFound

Agent '%s' not found. Check that the API name is correct and that the agent exists in your org or project.

# error.sessionInvalid

Preview session '%s' is invalid or has expired.

# error.endFailed

Failed to end preview session: %s

# output.tracesPath

Session traces: %s
Expand All @@ -36,12 +48,12 @@ Session traces: %s

- End a preview session of a published agent by specifying its session ID and API name ; use the default org:

<%= config.bin %> <%= command.id %> --session-id <SESSION_ID> --api-name My_Published_Agent
<%= config.bin %> <%= command.id %> --session-id <SESSION_ID> --api-name My_Published_Agent

- Similar to previous example, but don't specify a session ID; you get an error if the published agent has more than one active session. Use the org with alias "my-dev-org":

<%= config.bin %> <%= command.id %> --api-name My_Published_Agent --target-org my-dev-org
<%= config.bin %> <%= command.id %> --api-name My_Published_Agent --target-org my-dev-org

- End a preview session of an agent using its authoring bundle API name; you get an error if the agent has more than one active session.

<%= config.bin %> <%= command.id %> --authoring-bundle My_Local_Agent
<%= config.bin %> <%= command.id %> --authoring-bundle My_Local_Agent
20 changes: 16 additions & 4 deletions messages/agent.preview.send.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Send a message to an existing agent preview session.

You must have previously started a programmatic agent preview session with the "agent preview start" command to then use this command to send the agent a message (utterance). This command then displays the agent's response.

The original "agent preview start" command outputs a session ID which you then use with the --session-id flag of this command to send a message. You don't have to specify the --session-id flag if an agent has only one active preview session. You must also use either the --authoring-bundle or --api-name flag to specify the API name of the authoring bundle or the published agent, respecitvely. 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.
The original "agent preview start" command outputs a session ID which you then use with the --session-id flag of this command to send a message. You don't have to specify the --session-id flag if an agent has only one active preview session. You must also use either the --authoring-bundle or --api-name flag to specify the API name of the authoring bundle or the published agent, respecitvely. 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.

# flags.session-id.summary

Expand All @@ -32,16 +32,28 @@ No agent preview session found. Run "sf agent preview start" to start a new agen

Multiple preview sessions found for this agent. Use the --session-id flag to identify a specific session. Sessions: %s

# error.agentNotFound

Agent '%s' not found. Check that the API name is correct and that the agent exists in your org or project.

# error.sessionInvalid

Preview session '%s' is invalid or has expired. Start a new session with "sf agent preview start".

# error.sendFailed

Failed to send message to preview session: %s

# examples

- Send a message to an activated published agent using its API name and session ID; use the default org:

<%= config.bin %> <%= command.id %> --utterance "What can you help me with?" --api-name My_Published_Agent --session-id <SESSION_ID>
<%= config.bin %> <%= command.id %> --utterance "What can you help me with?" --api-name My_Published_Agent --session-id <SESSION_ID>

- Similar to previous example, but don't specify a session ID; you get an error if the agent has more than one active session. Use the org with alias "my-dev-org":

<%= config.bin %> <%= command.id %> --utterance "What can you help me with?" --api-name My_Published_Agent --target-org my-dev-org
<%= config.bin %> <%= command.id %> --utterance "What can you help me with?" --api-name My_Published_Agent --target-org my-dev-org

- Send a message to an agent using its authoring bundle API name; you get an error if the agent has more than one active session:

<%= config.bin %> <%= command.id %> --utterance "what can you help me with?" --authoring-bundle My_Local_Agent
<%= config.bin %> <%= command.id %> --utterance "what can you help me with?" --authoring-bundle My_Local_Agent
26 changes: 19 additions & 7 deletions messages/agent.preview.start.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# summary

Start a programmatic agent preview session.
Start a programmatic agent preview session.

# description

This command outputs a session ID that you then use with the "agent preview send" command to send an utterance to the agent. Use the "agent preview sessions" command to list all active sessions and the "agent preview end" command to end a specific session.
This command outputs a session ID that you then use with the "agent preview send" command to send an utterance to the agent. Use the "agent preview sessions" command to list all active sessions and the "agent preview end" command to end a specific session.

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.
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 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.

# flags.api-name.summary

Expand All @@ -26,16 +26,28 @@ Use real actions in the org; if not specified, preview uses AI to simulate (mock

Session ID: %s

# error.agentNotFound

Agent '%s' not found. Check that the API name is correct and that the agent exists in your org or project.

# error.compilationFailed

Agent Script compilation failed. See errors above for details.

# error.previewStartFailed

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":

<%= 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

- Similar to previous example but use live actions and the default org:

<%= config.bin %> <%= command.id %> --authoring-bundle My_Agent_Bundle --use-live-actions
<%= config.bin %> <%= command.id %> --authoring-bundle My_Agent_Bundle --use-live-actions

- Start a preview session with an activated published agent:

<%= config.bin %> <%= command.id %> --api-name My_Published_Agent
<%= config.bin %> <%= command.id %> --api-name My_Published_Agent
56 changes: 52 additions & 4 deletions src/commands/agent/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import { SfCommand, Flags, toHelpSection } from '@salesforce/sf-plugins-core';
import { Messages, SfError, Lifecycle, EnvironmentVariable } from '@salesforce/core';
import { getAgentForActivation, getVersionForActivation } from '../../agentActivation.js';

export type AgentActivateResult = { success: boolean; version: number };
Expand All @@ -28,6 +28,17 @@ export default class AgentActivate extends SfCommand<AgentActivateResult> {
public static readonly examples = messages.getMessages('examples');
public static readonly enableJsonFlag = true;

public static readonly envVariablesSection = toHelpSection(
'ENVIRONMENT VARIABLES',
EnvironmentVariable.SF_TARGET_ORG
);

public static readonly errorCodes = toHelpSection('ERROR CODES', {
'Succeeded (0)': 'Agent activated successfully.',
'NotFound (2)': 'Agent not found in the org.',
'ActivationFailed (4)': 'Failed to activate the agent due to API or network errors.',
});

public static readonly flags = {
'target-org': Flags.requiredOrg(),
'api-version': Flags.orgApiVersion(),
Expand All @@ -47,15 +58,52 @@ export default class AgentActivate extends SfCommand<AgentActivateResult> {
if (!apiNameFlag && this.jsonEnabled()) {
throw messages.createError('error.missingRequiredFlags', ['api-name']);
}
const agent = await getAgentForActivation({ targetOrg, status: 'Active', apiNameFlag });

// Get agent with error tracking
let agent;
try {
agent = await getAgentForActivation({ targetOrg, status: 'Active', apiNameFlag });
} catch (error) {
const wrapped = SfError.wrap(error);
if (wrapped.message.toLowerCase().includes('not found') || wrapped.message.toLowerCase().includes('no agent')) {
await Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_activate_agent_not_found' });
throw new SfError(
messages.getMessage('error.agentNotFound', [apiNameFlag ?? 'unknown']),
'AgentNotFound',
[],
2,
wrapped
);
}
await Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_activate_get_agent_failed' });
throw wrapped;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this have errorCode=4?

Copy link
Copy Markdown
Contributor Author

@WillieRuemmele WillieRuemmele Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 => not found error, meaning getAgentForActivation threw an error
4 => API error, agent.activate(version); threw an error


  - Exit 0: Success
  - Exit 1: Compilation/validation/execution errors (business logic failures)
  - Exit 2: Not found (agent, session, file, API 404)
  - Exit 3: Server error (API 500)
  - Exit 4: Operation failed (API/network errors during operations) ← Your question
  - Exit 5: Ambiguous (multiple resources match, need clarification)

they should follow this

}

const { version, warning } = await getVersionForActivation({
agent,
status: 'Active',
versionFlag: flags.version,
jsonEnabled: this.jsonEnabled(),
});
const result = await agent.activate(version);

// Activate with error tracking
let result;
try {
result = await agent.activate(version);
} catch (error) {
const wrapped = SfError.wrap(error);
await Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_activate_failed' });
throw new SfError(
messages.getMessage('error.activationFailed', [wrapped.message]),
'ActivationFailed',
[wrapped.message],
4,
wrapped
);
}

const metadata = await agent.getBotMetadata();
await Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_activate_success' });

this.log(`${metadata.DeveloperName} v${result.VersionNumber} activated.`);
if (warning) {
Expand Down
54 changes: 50 additions & 4 deletions src/commands/agent/deactivate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import { SfCommand, Flags, toHelpSection } from '@salesforce/sf-plugins-core';
import { Messages, SfError, Lifecycle, EnvironmentVariable } from '@salesforce/core';
import { getAgentForActivation } from '../../agentActivation.js';
import { AgentActivateResult } from './activate.js';

Expand All @@ -27,6 +27,17 @@ export default class AgentDeactivate extends SfCommand<AgentActivateResult> {
public static readonly examples = messages.getMessages('examples');
public static enableJsonFlag = true;

public static readonly envVariablesSection = toHelpSection(
'ENVIRONMENT VARIABLES',
EnvironmentVariable.SF_TARGET_ORG
);

public static readonly errorCodes = toHelpSection('ERROR CODES', {
'Succeeded (0)': 'Agent deactivated successfully.',
'NotFound (2)': 'Agent not found in the org.',
'DeactivationFailed (4)': 'Failed to deactivate the agent due to API or network errors.',
});

public static readonly flags = {
'target-org': Flags.requiredOrg(),
'api-version': Flags.orgApiVersion(),
Expand All @@ -46,9 +57,44 @@ export default class AgentDeactivate extends SfCommand<AgentActivateResult> {
throw messages.createError('error.missingRequiredFlags', ['api-name']);
}

const agent = await getAgentForActivation({ targetOrg, status: 'Inactive', apiNameFlag });
const result = await agent.deactivate();
// Get agent with error tracking
let agent;
try {
agent = await getAgentForActivation({ targetOrg, status: 'Inactive', apiNameFlag });
} catch (error) {
const wrapped = SfError.wrap(error);
if (wrapped.message.toLowerCase().includes('not found') || wrapped.message.toLowerCase().includes('no agent')) {
await Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_deactivate_agent_not_found' });
throw new SfError(
messages.getMessage('error.agentNotFound', [apiNameFlag ?? 'unknown']),
'AgentNotFound',
[],
2,
wrapped
);
}
await Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_deactivate_get_agent_failed' });
throw wrapped;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

}

// Deactivate with error tracking
let result;
try {
result = await agent.deactivate();
} catch (error) {
const wrapped = SfError.wrap(error);
await Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_deactivate_failed' });
throw new SfError(
messages.getMessage('error.deactivationFailed', [wrapped.message]),
'DeactivationFailed',
[wrapped.message],
4,
wrapped
);
}

const metadata = await agent.getBotMetadata();
await Lifecycle.getInstance().emitTelemetry({ eventName: 'agent_deactivate_success' });

this.log(`${metadata.DeveloperName} v${result.VersionNumber} deactivated.`);
return { success: true, version: result.VersionNumber };
Expand Down
Loading
Loading