<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Agentforce ISV Architecture]]></title><description><![CDATA[Agentforce ISV Architecture]]></description><link>https://chandan19.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 12 Nov 2026 07:28:05 GMT</lastBuildDate><atom:link href="https://chandan19.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Agent Release Contract: Lessons From a V67 Agentforce Packaging POC]]></title><description><![CDATA[We got the agent installed in a clean org. Here’s what that proved—and what it didn’t. This is a follow-up to I Tried to Package an Agentforce Agent—and…]]></description><link>https://chandan19.hashnode.dev/the-agent-release-contract-lessons-from-a-v67-agentforce-packaging-poc</link><guid isPermaLink="true">https://chandan19.hashnode.dev/the-agent-release-contract-lessons-from-a-v67-agentforce-packaging-poc</guid><dc:creator><![CDATA[Chandan S]]></dc:creator><pubDate>Mon, 12 Oct 2026 07:09:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/31f05be8-3c69-4e7a-8f00-6454b4cb8fe6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>We got the agent installed in a clean org. Here’s what that proved—and what it didn’t.</em></p>
<p>This is a follow-up to <a href="https://chandan19.hashnode.dev/agentforce-lifecycle">I Tried to Package an Agentforce Agent—and Discovered It Is Really a Lifecycle Problem</a>.</p>
<p>The question behind our POC was simple: could we package an Agentforce agent and get it working in another org? We did get an agent-related package installed in a clean org, and the agent worked there. The end-to-end conversation scenarios we tested also worked as expected. That was a good result. It also left us with a harder question: what evidence would we need before calling the agent ready to release more broadly?</p>
<p>I’m calling that missing piece an <strong>Agent Release Contract</strong>: a versioned, machine-checkable record of the agent, its dependencies, the subscriber-org setup it needs, and the checks it must pass before release. We have not built that contract yet. The V67 POC is what made the need for one clear. Here, V67 means Salesforce API version 67.0—not our package’s product version.</p>
<h2>What was in the project</h2>
<p>The agent definition, <code>PK4_Employee_Agent.agent</code>, is stored as an <code>AiAuthoringBundle</code>. The project also has the supporting Apex actions, custom metadata, object definitions, permission set, bot metadata, and planner bundle.</p>
<p>Keeping these files in source control gives us a history we can review. But a file appearing in the repository is not, by itself, proof that it made it into the package version installed by a subscriber. That distinction shaped the rest of the POC.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/5c44392d-0447-4450-9189-84703e02e63e.png" alt="VS Code Explorer showing the Agentforce POC project structure, including the authoring bundle, planner bundle, bots folder, and package source" style="display:block;margin:0 auto" />

<p><em>The project tree shows where the source artifacts live. It’s useful context, but it isn’t an install record.</em></p>
<h2>The API versions didn’t tell the whole story</h2>
<p>While reviewing the project, we found three API-version settings:</p>
<pre><code class="language-text">sfdx-project.json                 sourceApiVersion: 67.0
manifest/agentforce-deploy.xml    &lt;version&gt;67.0&lt;/version&gt;
manifest/package.xml              &lt;version&gt;63.0&lt;/version&gt;
</code></pre>
<p>The focused Agentforce manifest lists the authoring bundle, custom metadata type definitions, and selected Apex classes. It doesn’t list the <code>Bot</code>, <code>BotVersion</code>, or planner metadata that are also in the source tree. The general package manifest lists other metadata, including custom metadata records and the runtime permission set.</p>
<p>At first glance, those values can look like competing answers to the same question. They aren’t. The project setting and each manifest apply to their respective source, retrieve, or deploy workflows. Package-version creation uses the configured package directory; the deployment manifest is not an inventory of what the package contains.</p>
<p>The repository also records aliases for package versions <code>2.8.0-1</code>, <code>2.8.0-3</code>, <code>2.8.0-4</code> and 2.9.0-4. Those records show that versions were created, but they don’t show which of those versions was installed in the clean-org test or what metadata it received; that requires the install record and a post-install metadata check. The uninstall test described later used a separate Beta 2GP package version, <code>2.9.0.4</code>. These are different version records, not alternate spellings of the same version.</p>
<p>That became the most useful lesson from the POC: <strong>the project API version, a manifest’s API version, and the package contents are related, but none is a substitute for checking the others.</strong></p>
<h2>What we actually proved</h2>
<p>Here’s how I’d describe the evidence today:</p>
<table>
<thead>
<tr>
<th>Evidence</th>
<th>What it tells us</th>
<th>What it doesn’t tell us yet</th>
</tr>
</thead>
<tbody><tr>
<td>Agent and supporting metadata in source control</td>
<td>The files are available for review</td>
<td>That every required component is in the installed package</td>
</tr>
<tr>
<td>Direct Apex skill-executor tests</td>
<td>The action layer can be tested for expected results</td>
<td>That the full agent conversation routes actions and handles confirmation correctly</td>
</tr>
<tr>
<td>Package install and agent check in one clean org</td>
<td>The tested package installed, and the agent worked in that org</td>
<td>The exact installed version, repeatability in another clean org, or the complete installed metadata set</td>
</tr>
<tr>
<td>Package uninstall attempt</td>
<td>In our Beta 2GP package v2.9.0.4 test, bot removal succeeded but uninstall remained blocked by planner-related metadata</td>
<td>Whether the same behavior occurs across other versions or orgs, and a verified cleanup path</td>
</tr>
<tr>
<td>End-to-end agent conversation tests</td>
<td>The scenarios we tested worked as expected</td>
<td>Broader scenario coverage, ongoing monitoring, and upgrade safety still need validation</td>
</tr>
</tbody></table>
<p>One successful install in a clean org is valuable POC evidence. It shows the package installed and the agent worked in the org we tested; it doesn’t establish repeatability in another clean org or confirm the complete installed metadata set. We should record those separately.</p>
<h2>Checks we want before a wider release</h2>
<p>For the next release run, I’d want to:</p>
<ol>
<li><p>Record the exact package version installed in the clean org, along with the target-org type, install log, and post-install steps. Capture a metadata inventory, then repeat the install in another clean org to establish repeatability.</p>
</li>
<li><p>Check that the authoring bundle, matching <code>Bot</code> and <code>BotVersion</code>, planner bundle, actions, and supporting metadata are all present for that version.</p>
</li>
<li><p>Document the subscriber setup: which user runs the agent, what access it needs, and which settings must be configured in the target org.</p>
</li>
<li><p>Re-run and record end-to-end agent conversation tests against the installed package. The scenarios we tested in this POC worked as expected; for the next release, document the prompts and expected results and add broader coverage. Keep the direct action tests too; they cover a different layer.</p>
</li>
<li><p>Investigate whether the remaining planner-related dependencies can be removed, then repeat the uninstall test in a disposable org and record the metadata state and exact result.</p>
</li>
</ol>
<p>We should also name a previously approved version and rehearse restoring it in a non-production org. That gives us a real rollback target rather than an assumption that we can always move back safely.</p>
<h2>We could not uninstall the Beta 2GP package</h2>
<p>Uninstall is the part we could not complete. In our development/test org, Beta 2GP package <code>pk4_agentforce</code> version <code>2.9.0.4</code> could not be uninstalled.</p>
<p>We removed the Agentforce bot <code>PK4_Employee_Agent_Bot</code>, but a planner definition, <code>PK4_Employee_Agent_v1</code>, remained after its planner bundle was no longer present. Related planner-function and plugin records also appeared to remain linked to the managed agent metadata.</p>
<p>We tried to remove the remaining planner definition through the Tooling API. The org returned: <code>INVALID_CROSS_REFERENCE_KEY: In field: members - no GenAiPlannerBundle named PK4_Employee_Agent_v1 found</code>. Attempts to delete the associated planner-function dependency records returned <code>READONLY</code>. We also could not remove the remaining managed records through Setup, Metadata/Tooling API, or the CLI. The package uninstall therefore remained blocked.</p>
<p>That is the result we observed for this Beta package in our development/test org; it does not establish that every Agentforce or 2GP package has the same limitation. We have not yet verified a cleanup path. Our next step is to investigate the remaining planner-related dependencies and repeat the uninstall test in a disposable org.</p>
<p>Salesforce documents deleting an eligible inactive agent or version in Agentforce Studio separately (<a href="https://help.salesforce.com/s/articleView?id=ai.agent_versions_delete.htm&amp;language=en_US&amp;type=5">documentation</a>). Deleting an agent is not the same as uninstalling the package and its metadata, which is the issue we hit here.</p>
<h2>What the contract could look like</h2>
<p>I don’t want this to become just another checklist in a document. The useful version would be a small file that a release pipeline could check: Is the exact package version recorded? Are its dependencies listed? Are the subscriber setup steps clear? Did the required tests pass?</p>
<p>For example, the first draft might look something like this:</p>
<pre><code class="language-yaml">contractVersion: "0.1" # proposed example; not implemented in this POC
agent: "PK4_Employee_Agent"
apiVersion: "67.0"
packageVersion: "&lt;record the exact version tested&gt;"
dependencies:
  - "authoring bundle"
  - "matching Bot and BotVersion"
  - "planner bundle"
  - "actions and supporting metadata"
subscriberSetup:
  - "agent runtime user"
  - "required object and field access"
checks:
  install: "passed"
  cleanOrgInstall: "passed in one clean org; repeatability not tested"
  conversationTests: "passed for scenarios tested"
  uninstall: "blocked in tested org by remaining planner/plugin metadata"
rollbackTarget: "&lt;previously approved version&gt;"
</code></pre>
<p>This is only a sketch, not a file our package currently enforces or a Salesforce feature. The exact fields and allowed status values would need to be agreed and validated. The point is to make the release assumptions visible—and eventually testable—before the package reaches another org.</p>
<h2>Next: the V68 POC</h2>
<p>Salesforce’s <a href="https://developer.salesforce.com/docs/ai/agentforce/references/agents-metadata-tooling">V68 Agentforce metadata guide</a> describes a newer pattern. We haven’t tried it in this POC. In the next post, we’ll package the same agent with V68 and compare what changed against this V67 baseline.</p>
<h3>References</h3>
<ul>
<li><p>Salesforce, <a href="https://developer.salesforce.com/docs/ai/agentforce/references/agents-metadata-tooling/agents-metadatav67.html">Agentforce Metadata Types in V67 and Earlier</a></p>
</li>
<li><p>Salesforce, <a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-dx-deploy-metadata.html">Move an Agent to a New Org</a></p>
</li>
<li><p>Salesforce, <a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-dx-nga-publish.html">Publish an Authoring Bundle to Your Org</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[One Chat, Multiple Salesforce Agents: Lessons from Google Chat and Microsoft Teams]]></title><description><![CDATA[A practical design walkthrough of agent selection, conversation context, reviewed actions, and verified results.]]></description><link>https://chandan19.hashnode.dev/one-chat-multiple-salesforce-agents-lessons-from-google-chat-and-microsoft-teams</link><guid isPermaLink="true">https://chandan19.hashnode.dev/one-chat-multiple-salesforce-agents-lessons-from-google-chat-and-microsoft-teams</guid><dc:creator><![CDATA[Chandan S]]></dc:creator><pubDate>Fri, 09 Oct 2026 07:34:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/61545102-4f21-4ffd-8667-ee0f4430d34d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Salesforce Agentforce brings project lookup and time entry into Google Chat and Microsoft Teams. The central design question is agent choice:</p>
<p><strong>How can one chat entry point let an employee choose the Salesforce agent they need, complete a task, and return to select another capability?</strong></p>
<p>The answer starts with an explicit agent chooser. The employee selects an available agent, then has a direct conversation through that capability. Later, they can return to the chooser from the same chat tool.</p>
<p>This working POC brings that experience to Google Chat and Microsoft Teams. The recordings show live conversations, a reviewed time-entry request, confirmation, and a business result. The Teams walkthrough also opens Salesforce to verify the saved entry.</p>
<p>This article follows that build through screenshots from the demonstrations, then explains the design decisions another builder can apply. The architecture describes responsibilities; the evidence below shows the working interaction.</p>
<h2>Start with one business task, then add agent choice</h2>
<p>The example is familiar: an employee needs to find assigned work and record three hours against a task.</p>
<p>The conversation has two kinds of request. Asking about projects or tasks retrieves context. Recording time changes business data. Separating those two helps identify where the employee needs a review step.</p>
<p>Then introduce agent choice. A single channel can expose several available capabilities without making the employee learn a new chat interface for each one.</p>
<p>The scope still comes from the selected agent. The chooser answers “Which capability am I using?” The conversation answers “What do I want that capability to do?”</p>
<h2>The working POC: from a chat request to a Salesforce record</h2>
<p>In the Teams demonstration, the employee asks to record three hours against <strong>TT Demo - Configure Time Tracking</strong> for <strong>September 30</strong>, under <strong>TT Demo Project</strong>. The agent presents the proposed fields, asks whether to proceed, and reports the result after the employee replies <strong>“Yes.”</strong> The walkthrough then checks the Salesforce list for the matching entry.</p>
<table>
<thead>
<tr>
<th>Part of the build</th>
<th>Visible evidence</th>
</tr>
</thead>
<tbody><tr>
<td>Business request</td>
<td>The employee describes the task, work date and duration in chat</td>
</tr>
<tr>
<td>Review and confirmation</td>
<td>The proposal is shown before the employee agrees to proceed</td>
</tr>
<tr>
<td>Executed action</td>
<td>The conversation reports that the time was recorded</td>
</tr>
<tr>
<td>Record verification</td>
<td>Salesforce shows the matching task, work date and three-hour entry</td>
</tr>
<tr>
<td>Agent choice</td>
<td>Google Chat displays multiple choices and returns to the chooser later</td>
</tr>
</tbody></table>
<p>This is the evidence that makes the example a working POC. The time-entry path is demonstrated through to a saved record in Teams. The Google Chat footage also demonstrates the selection interface and the return to that interface after the workflow. It does not show a second agent executing a different business action.</p>
<h2>A small architecture with clear responsibilities</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/89d944c1-363a-4957-8d1b-d9b965e99c3f.png" alt="Functional architecture: Google Chat or Teams, PK4 bridge, selected Salesforce agent and business records" style="display:block;margin:0 auto" />

<table>
<thead>
<tr>
<th>Responsibility</th>
<th>What belongs here</th>
</tr>
</thead>
<tbody><tr>
<td>Google Chat or Microsoft Teams</td>
<td>The employee's conversation and visible choices</td>
</tr>
<tr>
<td>PK4 bridge</td>
<td>Connection experience, agent choice, request delivery and returned responses</td>
</tr>
<tr>
<td>Selected Salesforce agent</td>
<td>The relevant conversation and business capabilities</td>
</tr>
<tr>
<td>Salesforce business actions and records</td>
<td>Retrieval and changes to project, task and time-entry information</td>
</tr>
</tbody></table>
<p>This functional boundary is useful even before choosing infrastructure. Define what each part is responsible for, then validate the complete interaction. The diagram does not prescribe an API, hosting provider, or programming language.</p>
<h2>Lesson 1: connecting an account and choosing an agent are different steps</h2>
<p>The Google Chat demonstration shows the employee connecting Salesforce, returning to chat, and choosing an agent. Those steps serve different purposes.</p>
<p>Connecting establishes the Salesforce environment and authorization used by the experience. Choosing identifies the agent the employee wants to talk to within that environment.</p>
<p>For a similar build, make both states visible. A connected account should not leave the employee guessing which agent is active. An agent choice should not imply access that the user has not been granted.</p>
<p>Before testing, identify the target Salesforce environment, the chat account, the intended agent, the business action, and the demo records. Check the required connection setup and permissions for your implementation. A connection-success message is the beginning of the test, rather than proof that every action can execute.</p>
<h2>Lesson 2: make agent switching an explicit interaction</h2>
<p>Both recordings show the agent chooser. Google Chat shows more than one choice and returns to the chooser after the time-entry workflow. Teams shows the PK4 Employee Agent available in its demonstration environment.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/9eac5afb-a88b-49bf-b816-07981c82e754.jpg" alt="Microsoft Teams Choose agent command and Salesforce agent selection card" style="display:block;margin:0 auto" />

<p><em>Build evidence 1 —</em> <a href="https://www.youtube.com/watch?v=enBYJ6rDrhU&amp;t=120s"><em>Teams, 2:00</em></a><em>: the employee sends “Choose agent,” and the selection card displays PK4 Employee Agent. The card explains that Salesforce verifies access. This frame demonstrates the chooser in the Teams environment.</em></p>
<p>The <a href="https://www.youtube.com/watch?v=a4ILDRPjOGk&amp;t=185s">Google Chat recording at 3:05</a> also shows the user returning to the chooser after the time-entry workflow. Neither chooser frame demonstrates a second agent executing a different business action.</p>
<p>The employee-facing sequence is simple:</p>
<ol>
<li><p>Send <strong>“Choose agent.”</strong></p>
</li>
<li><p>Review the available choices.</p>
</li>
<li><p>Select the intended agent.</p>
</li>
<li><p>Ask a question relevant to that capability.</p>
</li>
</ol>
<p>That makes switching discoverable. It also gives the employee a useful boundary: a new selection means a different capability may be handling the next request.</p>
<p>For a build with two working agents, validate the switch itself. Ask the first agent a question within its scope, select the second agent, and ask a question unique to that second capability. Check the active agent identity and returned action evidence. A chooser with two buttons proves that choices are displayed; responses from both capabilities provide stronger switching evidence.</p>
<p>The supplied recordings do not establish how conversation history transfers between agents. Decide and test that behavior explicitly. Document whether a selection starts fresh or carries context, and explain it to the employee. In particular, a pending write proposal from one agent should not silently become authorization for another agent's action.</p>
<h2>Lesson 3: resolve business fields before requesting confirmation</h2>
<p>The Teams demonstration turns a request for three hours of work into a proposed entry with a project, task, work date and duration. The employee sees those fields before agreeing to proceed.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/2b669110-4b5d-4a7c-ac6e-68a81f812f0b.jpg" alt="Teams conversation showing the reviewed entry, employee confirmation and reported result" style="display:block;margin:0 auto" />

<p><em>Build evidence 2 —</em> <a href="https://www.youtube.com/watch?v=enBYJ6rDrhU&amp;t=59s"><em>Teams, approximately 0:59</em></a><em>: the conversation shows the proposed fields, the employee's “Yes,” and the reported recording result. Watch the linked segment to read the fields at full video resolution.</em></p>
<p>That is a reusable interaction design:</p>
<table>
<thead>
<tr>
<th>Step</th>
<th>What to establish</th>
</tr>
</thead>
<tbody><tr>
<td>Resolve</td>
<td>Which project and task does the employee mean?</td>
</tr>
<tr>
<td>Prepare</td>
<td>What exact date, duration and other required fields will be used?</td>
</tr>
<tr>
<td>Review</td>
<td>Can the employee understand and correct the proposal?</td>
</tr>
<tr>
<td>Confirm</td>
<td>Does the employee agree to that exact operation?</td>
</tr>
<tr>
<td>Execute</td>
<td>Does the business action accept and perform the request?</td>
</tr>
<tr>
<td>Verify</td>
<td>Does the saved record match the proposal?</td>
</tr>
</tbody></table>
<p>Use readable names in the proposal. Keep record identifiers available for the action and diagnostics, but avoid making employees interpret IDs to decide whether the entry is correct.</p>
<p>The Google Chat recording illustrates why name consistency matters: its proposal and completion message use different project wording. Check the actual project/task relationship before calling the result correct. This is a concrete validation lesson, not a confirmed diagnosis of the implementation.</p>
<h2>Lesson 4: employee confirmation and manager approval are separate</h2>
<p>The Teams chat asks the employee whether to proceed. The resulting Salesforce list shows a pending approval status.</p>
<p>The employee has confirmed creation of the entry. That does not mean a manager has approved the timesheet.</p>
<p>Describe those stages precisely in the conversation. “Recorded, pending approval” tells the employee more than a broad statement that everything is complete.</p>
<p>For implementation, enforce authorization and business validation in the action layer. The visible confirmation prompt alone cannot prove backend enforcement. Validate the exact proposal again before writing, and define how changed inputs, expired proposals and repeated requests are handled.</p>
<h2>Lesson 5: verify the record, then evaluate the experience</h2>
<p>The Teams walkthrough opens Salesforce and shows a row matching the demonstrated task, September 30 work date and three-hour duration. That connects the chat action to a visible business result.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/2eb16025-ed56-456e-932a-6597b5fd9918.jpg" alt="Salesforce list used to verify the time entry from the Teams conversation" style="display:block;margin:0 auto" />

<p><em>Build evidence 3 —</em> <a href="https://www.youtube.com/watch?v=enBYJ6rDrhU&amp;t=70s"><em>Teams, 1:10</em></a><em>: the Salesforce list provides the record check after the chat action. Compare the task, project, September 30 work date and three-hour duration with the proposal; the approval status is still pending. Open the video for a closer view of the row.</em></p>
<p>Use that same evidence pattern when validating another workflow. Compare the saved fields with the reviewed proposal. Check the work date separately from the record's creation date; a retrospective entry can legitimately have different dates.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/14afb9d7-8f2c-42c6-be1d-c1cdd0b30b72.png" alt="Common workflow: ask about work, receive context, describe time, review, confirm and receive the result" style="display:block;margin:0 auto" />

<h2>A validation guide another builder can use</h2>
<p>Start in an appropriate test environment with clearly identified demo data. Prepare the following checks before recording the happy path:</p>
<table>
<thead>
<tr>
<th>Test</th>
<th>Evidence to collect</th>
</tr>
</thead>
<tbody><tr>
<td>Connect and select</td>
<td>Correct environment and intended agent are shown</td>
</tr>
<tr>
<td>Read assigned work</td>
<td>Response matches the accessible demo records</td>
</tr>
<tr>
<td>Clarify a request</td>
<td>Missing or ambiguous details are resolved before a write</td>
</tr>
<tr>
<td>Confirm an entry</td>
<td>Saved fields match the reviewed proposal</td>
</tr>
<tr>
<td>Cancel an entry</td>
<td>No record is created</td>
</tr>
<tr>
<td>Switch agents</td>
<td>The next request reaches the newly selected capability</td>
</tr>
<tr>
<td>Switch with a pending proposal</td>
<td>Earlier consent is not reused for a different action</td>
</tr>
<tr>
<td>Repeat after a timeout</td>
<td>Operation status is reconciled; no duplicate write</td>
</tr>
<tr>
<td>Use a restricted test user</td>
<td>Actions and records respect the intended access model</td>
</tr>
</tbody></table>
<p>These are recommended validation cases, not a claim that the recordings demonstrate every one. Record expected and actual behavior so another builder can understand both the successful path and its boundaries.</p>
<h2>What this pattern solves</h2>
<p>The implementation makes Salesforce agent capabilities reachable through Google Chat and Teams. The employee can retrieve work context, review a time-entry request, and return to the chooser without leaving the chat tool.</p>
<p>The intended benefit is easier access to business workflows and fewer application changes. The demonstrations establish the interaction and, in Teams, a matching record. They do not measure productivity gains.</p>
<p>To evaluate impact, run the same task manually and through chat using equivalent records. Compare correctness, elapsed time and corrections required. That gives the next article a measured outcome rather than an estimated percentage.</p>
<h2>Apply these lessons to the next build</h2>
<p>Make the active capability visible. Treat agent selection as a deliberate conversation boundary. Review business fields before writing. Distinguish creation from downstream approval. Verify the saved record.</p>
<p>Those decisions make a chat integration easier to understand and test. They also give another builder a starting point for a different workflow—across different infrastructure and business objects.</p>
<p>Watch the examples: <a href="https://www.youtube.com/watch?v=a4ILDRPjOGk">Google Chat walkthrough</a> and <a href="https://www.youtube.com/watch?v=enBYJ6rDrhU">Microsoft Teams walkthrough</a>.</p>
<p><strong>When a user switches agents in your implementation, what happens to their conversation context and pending actions?</strong></p>
]]></content:encoded></item><item><title><![CDATA[Your Chat. Your Choice of Agent: Agentforce in Google Chat and Microsoft Teams]]></title><description><![CDATA[Connect Salesforce, switch between available agents, and turn a conversation into a reviewed business action.]]></description><link>https://chandan19.hashnode.dev/your-chat-your-choice-of-agent-agentforce-in-google-chat-and-microsoft-teams</link><guid isPermaLink="true">https://chandan19.hashnode.dev/your-chat-your-choice-of-agent-agentforce-in-google-chat-and-microsoft-teams</guid><dc:creator><![CDATA[Chandan S]]></dc:creator><pubDate>Fri, 09 Oct 2026 07:02:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/a85eebaa-3283-41c4-b95b-161d5f0b53f0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>An employee finishes a piece of work. The conversation about it is already in chat. The project and task are in Salesforce. The time entry still needs to be recorded.</p>
<p>That last step can feel small, but it asks the employee to change applications, find the right record, and reconstruct the context of work they have just completed.</p>
<p>We wanted to make that handoff easier.</p>
<p>With <strong>PK4 Agentforce for Google Chat and Microsoft Teams</strong>, we brought access to Salesforce agents into the tools employees already use for conversations. Our demonstrations follow a practical workflow: ask about assigned work, describe the time spent, review the proposed entry, and confirm it.</p>
<p>The most useful moment is the transition from a conversation to a business action: the employee sees what will be recorded and decides whether to proceed.</p>
<p>There is another important part of the experience: <strong>the employee can switch between available Salesforce agents without leaving the chat tool.</strong> The channel provides a familiar place to work, while the agent chooser lets the employee select the business capability they need.</p>
<h2>What this solves</h2>
<p>The problem starts with a split workflow: employees discuss work in chat, while project information and time entries live in Salesforce. Our implementation brings access to the selected Salesforce agent into the conversation.</p>
<table>
<thead>
<tr>
<th>Employee need</th>
<th>How the experience helps</th>
</tr>
</thead>
<tbody><tr>
<td>Find assigned work</td>
<td>Ask the selected agent about projects and tasks from chat</td>
</tr>
<tr>
<td>Record completed work</td>
<td>Describe the task, date and hours, then review the proposed entry</td>
</tr>
<tr>
<td>Check what will be saved</td>
<td>See the intended fields before confirming the action</td>
</tr>
<tr>
<td>Reach another agent</td>
<td>Use the agent chooser and select another available capability</td>
</tr>
<tr>
<td>Work in the team's preferred chat tool</td>
<td>Access the experience through Google Chat or Microsoft Teams</td>
</tr>
</tbody></table>
<p>The demonstrated result is a reviewed time-entry request, with a matching Salesforce row visible in the Teams walkthrough. Reduced switching between applications is the intended benefit; we have not measured productivity or time savings yet.</p>
<h2>A simple architecture: chat, bridge, selected agent, records</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/89d944c1-363a-4957-8d1b-d9b965e99c3f.png" alt="Basic architecture: chat, PK4 bridge, selected Salesforce Agentforce agent and business records" style="display:block;margin:0 auto" />

<p><em>Functional overview of the demonstrated experience. The diagram shows responsibilities and conversation flow rather than backend deployment details.</em></p>
<p>The employee uses <strong>Google Chat or Microsoft Teams</strong>. The <strong>PK4 bridge</strong> connects that chat experience to Salesforce, offers agent selection, carries requests, and returns responses. The <strong>selected Agentforce agent</strong> supplies the conversation and business actions that work with <strong>Salesforce records</strong>.</p>
<p>This is a direct conversation with the agent the employee selected. When they need a different capability, they return to the chooser and select another available agent.</p>
<table>
<thead>
<tr>
<th>Stack component</th>
<th>Role</th>
</tr>
</thead>
<tbody><tr>
<td>Google Chat</td>
<td>Conversation in a chat thread</td>
</tr>
<tr>
<td>Microsoft Teams</td>
<td>Conversation through the Teams app</td>
</tr>
<tr>
<td>PK4 bridge</td>
<td>Connection, agent choice, request and response delivery</td>
</tr>
<tr>
<td>Salesforce Agentforce</td>
<td>Selected agent's conversation and business capabilities</td>
</tr>
<tr>
<td>Salesforce records</td>
<td>Project, task and time-entry information used in the example</td>
</tr>
</tbody></table>
<p>Agent availability depends on the connected Salesforce environment and user access. The recordings do not establish the bridge's programming language, hosting provider, or exact API implementation, so those details are outside this diagram.</p>
<h2>Start with the employee's task</h2>
<p>For this implementation, the scenario was project work and time tracking. An employee needs answers to familiar questions:</p>
<ul>
<li><p>Which projects am I assigned to?</p>
</li>
<li><p>What tasks should I look at?</p>
</li>
<li><p>How do I record the time I spent on one of them?</p>
</li>
</ul>
<p>The aim was to make those questions actionable within Google Chat or Teams while keeping the Salesforce agent and its business capabilities central to the experience.</p>
<p>A chat channel becomes useful when it carries the workflow far enough to complete an operation. For time tracking, that means finding the relevant work, resolving the intended entry, and giving the employee a chance to review it.</p>
<h2>Google Chat: connect, choose an agent, continue the conversation</h2>
<p>The <a href="https://www.youtube.com/watch?v=a4ILDRPjOGk">Google Chat walkthrough</a> starts with a connection card. The employee chooses the appropriate Salesforce environment, identifies the Google account being linked, and completes Salesforce authorization.</p>
<p>These are separate steps with different purposes. Google sign-in identifies the chat user linking the account. Salesforce authorization connects that user to the Salesforce environment. The walkthrough explains that Salesforce permissions govern access; the video itself does not constitute a full permissions audit.</p>
<p>Once connected, the employee chooses an available Salesforce agent. In this example, that is the PK4 Employee Agent.</p>
<p>The conversation then moves into everyday work. A project question returns a list of assignments in the Google Chat thread. A follow-up asks about today's tasks. The agent reports that no active tasks are due today or tomorrow, and the employee broadens the question to work during the week.</p>
<p>That is a useful part of the demonstration. An empty result leads to another question rather than ending the interaction. The employee can refine the request in the same thread.</p>
<p>The bridge also acknowledges that a request is being processed. That acknowledgement helps the employee understand what is happening while the answer is prepared. The recording explicitly says that waiting periods have been shortened, so its runtime should not be treated as a response-time benchmark.</p>
<h2>The important step: review before recording time</h2>
<p>Later in the Google Chat demo, the employee requests a three-hour time entry for restaurant systems installation work on September 29.</p>
<p>The agent turns that request into a proposed entry and presents the fields the employee needs to check:</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>What the employee reviews</th>
</tr>
</thead>
<tbody><tr>
<td>Project</td>
<td>The intended project association</td>
</tr>
<tr>
<td>Task</td>
<td>The work the hours should be recorded against</td>
</tr>
<tr>
<td>Work date</td>
<td>The date the work happened</td>
</tr>
<tr>
<td>Hours</td>
<td>The requested duration</td>
</tr>
</tbody></table>
<p>Only after the employee confirms does the conversation report that the three hours have been logged.</p>
<p>One detail deserves attention: the Google Chat footage uses different project wording in the proposal and the completion response. The task, date, and hours are visible, but the project association should be checked against the saved record. That is exactly why the review step matters—and why a clean final demonstration should show consistent names throughout.</p>
<p>For this Google Chat recording, the visible evidence establishes the proposal, the employee's confirmation, and the agent's reported completion. A subsequent Salesforce screen appears, but it does not clearly display the matching time-entry record. The Teams recording provides the clearer record verification.</p>
<h2>Microsoft Teams: follow the action into Salesforce</h2>
<p>The <a href="https://www.youtube.com/watch?v=enBYJ6rDrhU">Microsoft Teams walkthrough</a> shows the same kind of employee experience in another channel.</p>
<p>The employee asks about tasks, expands the date range, and requests three hours for configuring time tracking on September 30. The agent presents the project, task, work date, and hours, then asks whether to proceed.</p>
<p>After the employee confirms, the conversation reports that the entry has been recorded.</p>
<p>The presenter then opens Salesforce. The time-entry list visibly contains a matching row for the demonstrated project and task, with a three-hour duration and the September 30 work date. The creation date is later than the work date, illustrating an important distinction: the date an employee records time is not necessarily the date they performed the work.</p>
<p>This is the strongest evidence in the two recordings. It connects the conversation to a visible Salesforce result.</p>
<p>The chat uses the word “approval” when asking the employee to proceed. Here, that step is <strong>employee confirmation before creation</strong>. The Salesforce list still shows a pending approval status, so the demonstration should not be described as a completed manager-approval workflow.</p>
<h2>Two channels, one workflow pattern</h2>
<p>The recordings show a common interaction pattern across Google Chat and Teams:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/14afb9d7-8f2c-42c6-be1d-c1cdd0b30b72.png" alt="Two channels, one workflow: ask about work, receive context, describe time, review, confirm and receive the result" style="display:block;margin:0 auto" />

<p>The Teams video extends that last step with visible Salesforce record verification.</p>
<p>At a functional level, the channel connects the employee's request to a Salesforce agent and returns its response. The selected agent supplies the business experience. That distinction helps keep the article focused: Google Chat and Teams provide access to the conversation; the Salesforce agent provides the project and time-tracking capability.</p>
<p>The videos do not reveal enough backend detail to establish the exact API, hosting architecture, token handling, or whether each action uses Apex or Flow. Those implementation details need source or configuration evidence before they can become a reproducible technical tutorial.</p>
<h2>Switch agents without leaving Google Chat or Teams</h2>
<p><strong>Agent switching is a core feature of this experience.</strong> The employee can return to the agent chooser from the same chat tool instead of opening another application to find the next capability.</p>
<p>The interaction is straightforward:</p>
<ol>
<li><p>Send <strong>“Choose agent.”</strong></p>
</li>
<li><p>Review the available Salesforce agent choices.</p>
</li>
<li><p>Select the agent you want to use.</p>
</li>
<li><p>Continue with questions and actions relevant to that agent.</p>
</li>
</ol>
<p>Both recordings show an agent-selection interface. Google Chat demonstrates returning to the chooser after the time-entry workflow, with more than one choice visible. Teams shows the chooser with the PK4 Employee Agent available in its demonstrated environment. Google Chat also shows the Salesforce disconnect flow.</p>
<p>This matters because the channel experience can provide an entry point to more than one business capability. The demonstration uses the PK4 Employee Agent; the available choices depend on the connected environment and user access.</p>
<p>The Teams narration discusses other possible agents, such as inventory or opportunity management. Those are extension possibilities in this walkthrough, rather than executed scenarios. The demonstrated behavior is selecting an agent and using its capability. Coordinated execution between several specialist agents would require separate evidence.</p>
<p>For builders, the reusable idea is to keep agent choice visible and understandable. Employees need to know which capability they are asking to act on their behalf. <strong>One chat entry point can offer a choice of available agents, with a direct conversation through the selected capability.</strong></p>
<p>The recordings establish the chooser experience; they do not establish whether conversation history transfers between agents. Each selection should make the active agent and its scope clear.</p>
<h2>What we learned from the demonstrations</h2>
<p><strong>Review the business fields, not just the sentence.</strong> A fluent response can still contain a wrong project association. Project, task, work date, and duration need to be understandable before the employee confirms.</p>
<p><strong>Show the record after the action.</strong> A completion message is useful feedback. A matching Salesforce row gives readers much stronger evidence of the result. The Teams walkthrough does this well.</p>
<p><strong>Make empty results useful.</strong> Both conversations encounter a question with no tasks due in the requested period. The next step is to clarify or broaden the request. The experience benefits from that ordinary interaction as much as from a successful write.</p>
<p><strong>Keep the response readable in chat.</strong> Long task lists, raw record identifiers, and repeated summaries make it harder to find the next decision. The recordings suggest a clear refinement: lead with a short business summary and put additional detail behind a follow-up where the channel supports it.</p>
<p><strong>Separate confirmation from authorization.</strong> The videos show the employee agreeing to a proposed entry. Production enforcement also needs to validate access, inputs, and the exact operation on the server. Duplicate protection, expired connections, cancellations, and restricted-user behavior deserve their own tests; they are not established by a successful demo.</p>
<h2>The value—and what we still need to measure</h2>
<p>The demonstrated value is concrete: an employee can request a time entry through a chat conversation, review it, and receive a completion response. In Teams, the footage also shows the corresponding Salesforce record.</p>
<p>That makes reduced application switching and easier time capture credible benefits to investigate. The videos do not measure time saved, adoption, or productivity improvement, so I would not attach a percentage to them.</p>
<p>A useful next measurement is simple: ask employees to complete the same lookup-and-time-entry task manually and through chat. Compare correctness, elapsed time, and the number of corrections required. Then report what was actually observed.</p>
<h2>Bringing the action closer to the work</h2>
<p>This implementation began with a practical question: can an employee use a Salesforce agent from the chat tool they already work in?</p>
<p>The two recordings show that experience through project questions and time entry. The Teams demonstration adds a visible Salesforce result. Together, they offer a useful pattern for other builders: connect the user, expose the appropriate agent, preserve the conversation, present the intended action clearly, and verify its outcome.</p>
<p>For me, that is the lesson worth sharing. A familiar channel can make a business capability easier to reach. Careful review and visible results make that experience easier to trust.</p>
<p><strong>Which Salesforce workflow would your employees benefit from completing directly in Google Chat or Microsoft Teams?</strong></p>
<hr />
<p>Watch the demonstrations: <a href="https://www.youtube.com/watch?v=a4ILDRPjOGk">Agentforce for Google Chat</a> · <a href="https://www.youtube.com/watch?v=enBYJ6rDrhU">PK4 Agentforce for Teams</a>.</p>
<p><em>This article describes the behavior visible in the supplied demonstrations. The video review did not independently execute the applications or audit their backend security controls.</em></p>
]]></content:encoded></item><item><title><![CDATA[Before Agentforce Answers: Find the Right Salesforce Record]]></title><description><![CDATA[A practical SOQL and SOSL design for ambiguous names, access controls, and incomplete results. “Show me Acme’s open opportunities.” It sounds…]]></description><link>https://chandan19.hashnode.dev/before-agentforce-answers-find-the-right-salesforce-record</link><guid isPermaLink="true">https://chandan19.hashnode.dev/before-agentforce-answers-find-the-right-salesforce-record</guid><dc:creator><![CDATA[Chandan S]]></dc:creator><pubDate>Thu, 24 Sep 2026 11:04:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/7262f073-5804-4d55-9ee0-7691860ba26a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>A practical SOQL and SOSL design for ambiguous names, access controls, and incomplete results.</em></p>
<p>“Show me Acme’s open opportunities.”</p>
<p>It sounds straightforward. But imagine your Salesforce org contains <strong>Acme Manufacturing</strong> and <strong>Acme Services</strong>. Both have open opportunities.</p>
<p>An agent can run a valid query, receive real Salesforce records, and still answer about the wrong customer.</p>
<p>This article shows how an agent can identify the correct Account, retrieve its open opportunities, and explain any limits on the results.</p>
<p>We’ll use SOQL and SOSL behind two proposed Agentforce actions. This is an architectural walkthrough with synthetic examples; the snippets and conversation are illustrative, not results from a deployed agent.</p>
<h2>Identify the Account before retrieving opportunities</h2>
<p>“Acme” may match several Account records. The agent needs to identify which Account the user means before retrieving opportunities.</p>
<p>Separate the request into two decisions:</p>
<ol>
<li><p><strong>Resolve identity:</strong> Which account does the user mean?</p>
</li>
<li><p><strong>Retrieve facts:</strong> Which open opportunities belong to that account?</p>
</li>
</ol>
<p>The first step may require search and clarification. The second needs a precise record ID and a defined query.</p>
<p>SOQL retrieves records from specified objects using filters and relationships. SOSL performs text search against Salesforce’s search index and can return matches across multiple objects. Salesforce’s <a href="https://developer.salesforce.com/docs/platform/salesforce-soql-sosl/guide/sforce-api-calls-soql-sosl-intro.html">SOQL and SOSL guide</a> explains when each is appropriate.</p>
<table>
<thead>
<tr>
<th>What the user provides</th>
<th>Retrieval choice</th>
<th>Reason</th>
</tr>
</thead>
<tbody><tr>
<td>A confirmed Account ID</td>
<td>SOQL</td>
<td>Record identity is already known</td>
</tr>
<tr>
<td>An account name with defined matching criteria</td>
<td>SOQL or SOSL</td>
<td>Choose based on the matching behavior required</td>
</tr>
<tr>
<td>Text that could identify an Account, Contact, or Lead</td>
<td>SOSL</td>
<td>Search may need to span multiple objects</td>
</tr>
<tr>
<td>A request for a count or total over matching records</td>
<td>SOQL aggregate</td>
<td>The question requires a calculation</td>
</tr>
</tbody></table>
<p>This example searches only Account records because the next query filters opportunities by Account ID. SOSL can search a single object; it does not have to search Accounts, Contacts, and Leads together. Choose SOQL or SOSL according to the matching behavior your application needs.</p>
<h2>Action 1: search for matching Accounts</h2>
<p>The first proposed custom action, <code>Search Accounts by Name</code>, accepts a name such as “Acme” and returns matching Account records that the running user can access. This is a descriptive name for the custom action in this example, not a standard Salesforce action.</p>
<p>The results contain Account IDs, names, and an accessible city field so the user can distinguish similar Accounts. Each result is a possible match until the intended Account is confirmed. The agent uses the selected Account ID to retrieve opportunities in the second action.</p>
<p>The following illustrative SOSL search returns Account records whose name fields match the search term:</p>
<pre><code class="language-sql">FIND {Acme*} IN NAME FIELDS
RETURNING Account(Id, Name, BillingCity LIMIT 6)
</code></pre>
<p>The literal term makes the syntax readable. An Apex implementation should validate and bind search input rather than concatenate the conversation into executable query text. The standalone expression above also needs an implementation that explicitly enforces the intended access mode.</p>
<p>The search retrieves up to six Account records, while the agent displays at most five. If a sixth record is returned, the agent asks the user to narrow the search. The extra record detects that the display limit was exceeded.</p>
<p>Show the city only if the running user has permission to view it. The city helps distinguish Accounts with similar names. Ask for further clarification if the displayed details still do not identify the intended Account.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/e51c11c8-58c6-402a-8935-8314be2b6685.png" alt="Search finds two Acme accounts, clarification selects Acme Manufacturing, and SOQL retrieves its opportunities." style="display:block;margin:0 auto" />

<p><em>Illustrative design: resolve the account identity before querying its opportunities.</em></p>
<p>An illustrative exchange:</p>
<blockquote>
<p><strong>User:</strong> Show me Acme’s open opportunities.</p>
<p><strong>Agent:</strong> I found Acme Manufacturing in Pune and Acme Services in Bengaluru. Which account do you mean?</p>
<p><strong>User:</strong> Acme Manufacturing in Pune.</p>
</blockquote>
<p>Pass the selected <strong>Account ID</strong> to the second action. Keep this ID throughout the workflow so the opportunity query uses the Account the user selected.</p>
<p>If no candidate is returned, ask for a more specific name or trusted identifier. Say “I couldn’t find a matching account,” rather than claiming the account does not exist.</p>
<h3>A real platform example: Select Record</h3>
<p>Salesforce’s standard <strong>Select Record</strong> action illustrates the same clarification principle: its description asks the agent to prompt the user when several records match. The configuration accepts record IDs and returns associated records.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/71948646-052c-4ca7-b1ad-834a6bf770bc.png" alt="Actual Salesforce Select Record action configuration showing its clarification description and record-ID inputs and record outputs." style="display:block;margin:0 auto" />

<p><em>Actual screenshot from a Developer Edition org. This is a standard Salesforce action, not the custom SOSL action proposed here. It demonstrates configuration, not a successful execution of this article’s demo.</em></p>
<h2>Action 2: retrieve open opportunities for the selected Account</h2>
<p>The second proposed custom action, <code>Get Open Opportunities</code>, accepts the confirmed Account ID and returns that Account’s accessible open Opportunity records.</p>
<p>The two actions retrieve different records: <code>Search Accounts by Name</code> returns Accounts for selection; <code>Get Open Opportunities</code> returns Opportunities linked to the selected Account through <code>Opportunity.AccountId</code>.</p>
<p>Before querying opportunities, validate that the input is an Account ID, check access to the Account, and reject invalid or unauthorized requests.</p>
<p>This Apex fragment assumes those checks have succeeded:</p>
<pre><code class="language-apex">// accountId: validated, authorized Account ID.
// Retrieve one extra row to detect a truncated display.
List&lt;Opportunity&gt; rows = [
    SELECT Id, Name, StageName, CloseDate
    FROM Opportunity
    WHERE AccountId = :accountId
      AND IsClosed = false
    WITH USER_MODE
    ORDER BY CloseDate ASC, Id ASC
    LIMIT 11
];

Boolean hasMore = rows.size() &gt; 10;
if (hasMore) {
    rows.remove(rows.size() - 1);
}
</code></pre>
<p>The returned list contains at most ten rows. <code>hasMore</code> tells the agent whether the query found an additional accessible match at retrieval time. It does <strong>not</strong> reveal the total number of opportunities.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/bdba1df7-09d8-48fe-9f80-f54323a61c5f.png" alt="Fetch eleven records, return ten, and set hasMore to true when the extra record exists." style="display:block;margin:0 auto" />

<p><em>Illustrative example: an extra accessible row signals more matches, not the total count.</em></p>
<p>The secondary <code>Id</code> ordering makes ties in close date deterministic. The row cap bounds the response; it does not, by itself, establish that the query is selective or efficient.</p>
<p>This query returns opportunity names, stages, and close dates. To answer a question about pipeline value, use a separate calculation that defines which records and currencies it includes.</p>
<p>The answer should change with the result:</p>
<table>
<thead>
<tr>
<th>Action result</th>
<th>Appropriate response</th>
</tr>
</thead>
<tbody><tr>
<td>Three accessible matches</td>
<td>“I found three open opportunities available to you for Acme Manufacturing.”</td>
</tr>
<tr>
<td>Eleven rows fetched; ten returned</td>
<td>“Here are the first ten open opportunities available to you, ordered by close date. More matches are available.”</td>
</tr>
<tr>
<td>Successful query with no rows</td>
<td>“No open opportunities were returned for this account within your access.”</td>
</tr>
<tr>
<td>Access check or query failed</td>
<td>“I couldn’t retrieve the opportunities.”</td>
</tr>
</tbody></table>
<p>Treating the last two cases differently prevents a technical failure from becoming a false business conclusion.</p>
<h2>Return records with a clear status and result limit</h2>
<p>Agentforce can expose Apex methods annotated with <code>@InvocableMethod</code> as custom actions. See Salesforce’s <a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-invocablemethod.html">Apex action documentation</a> and <a href="https://developer.salesforce.com/workshops/agentforce-workshop/agents/4-apex-actions">configuration workshop</a>.</p>
<p>For this design, use an explicit output contract:</p>
<table>
<thead>
<tr>
<th>Output</th>
<th>Purpose</th>
</tr>
</thead>
<tbody><tr>
<td><code>status</code></td>
<td>Distinguish success, no matches, invalid input, and controlled failure</td>
</tr>
<tr>
<td><code>records</code></td>
<td>Supply only permitted fields needed for the answer</td>
</tr>
<tr>
<td><code>returnedCount</code></td>
<td>State how many records the action returned</td>
</tr>
<tr>
<td><code>hasMore</code></td>
<td>Indicate that the displayed set is truncated</td>
</tr>
<tr>
<td><code>scopeDescription</code></td>
<td>Explain the filter, ordering, and display cap</td>
</tr>
</tbody></table>
<p>Use <code>hasMore</code> only when retrieval succeeds; failure cannot establish whether further matches exist. These names are proposed application fields, not built-in Agentforce outputs.</p>
<p>A useful description for the opportunity action would be:</p>
<blockquote>
<p>Retrieve up to ten open opportunities for a confirmed, authorized Account ID, ordered by close date. Use only after account identity is resolved. Explain when additional matches exist. Do not describe the returned subset as the account’s total pipeline.</p>
</blockquote>
<p>Implement validation, permissions, and limits in code. Action descriptions help the agent select and explain behavior; they do not replace those controls.</p>
<p><strong>The retrieval flow</strong></p>
<ol>
<li><p><strong>Search:</strong> use <code>Search Accounts by Name</code> to retrieve matching, accessible Account records.</p>
</li>
<li><p><strong>Clarify:</strong> if identity is unresolved, ask the user to choose or narrow the search.</p>
</li>
<li><p><strong>Validate:</strong> check the selected Account ID and access.</p>
</li>
<li><p><strong>Query:</strong> use <code>Get Open Opportunities</code> to retrieve open Opportunity records linked to the selected Account ID.</p>
</li>
<li><p><strong>Explain:</strong> return the records, status, and scope; disclose truncation.</p>
</li>
</ol>
<p><em>Proposed custom-action flow. Validation and retrieval failures should produce controlled responses.</em></p>
<h2>Permissions depend on who executes the action</h2>
<p>The query explicitly uses <code>WITH USER_MODE</code>. Salesforce recommends explicit access modes to make intent clear across API versions. <code>with sharing</code> addresses record sharing; it is not a substitute for object and field permissions. See <a href="https://developer.salesforce.com/docs/platform/lwc/guide/apex-security">Secure Apex Classes</a>.</p>
<p>For this walkthrough, assume an authenticated employee-facing use case and verify the action’s actual running identity in your configuration.</p>
<p>A customer-facing agent may require additional identity and entitlement checks. The running user’s Salesforce access alone does not prove that a conversation participant is entitled to a particular customer’s data.</p>
<p>Test permissions on <strong>both</strong> actions. A secure opportunity query does not repair an account search that already disclosed unauthorized candidate details.</p>
<h2>Test the workflow with synthetic records</h2>
<p>Use synthetic records in a development org. Create two similarly named accounts, one with twelve open opportunities and the other with none. Add a closed opportunity to verify the filter. Use valid stages from your org.</p>
<p>Then test the behavior that matters:</p>
<table>
<thead>
<tr>
<th>Scenario</th>
<th>Evidence to capture</th>
<th>Pass condition</th>
</tr>
</thead>
<tbody><tr>
<td>Ambiguous account name</td>
<td>Search output and next action</td>
<td>Agent clarifies before retrieving opportunities</td>
</tr>
<tr>
<td>Account with twelve open opportunities</td>
<td>Query output and final response</td>
<td>Ten returned; <code>hasMore=true</code>; response explains truncation</td>
</tr>
<tr>
<td>Account with no open opportunities</td>
<td>Action status and response</td>
<td>Successful empty result is described without invented records</td>
</tr>
<tr>
<td>Closed opportunity on the same account</td>
<td>Returned record IDs</td>
<td>Closed record is excluded</td>
</tr>
<tr>
<td>User lacks required field access</td>
<td>Controlled action result</td>
<td>No restricted fields or raw exception details are exposed</td>
</tr>
<tr>
<td>User cannot access a matching account</td>
<td>Candidate list</td>
<td>Inaccessible account details are absent</td>
</tr>
<tr>
<td>Invalid ID or wrong object type</td>
<td>Validation result</td>
<td>Request is rejected before the opportunity query</td>
</tr>
</tbody></table>
<p>These are acceptance criteria, not claimed results. Record the API version, running identity, inputs, outputs, and observed behavior when implementing the example.</p>
<h2>Confirm the Account, then retrieve its opportunities</h2>
<p>Start by identifying the Account the user means. Search for matching Accounts, clarify ambiguous results, and pass the confirmed Account ID to the opportunity query.</p>
<p>SOQL and SOSL help retrieve records. A dependable Agentforce action also preserves the selected identity, enforces access, and tells the agent when the answer is incomplete.</p>
<p>Verify three behaviors before using this workflow: the agent selects the intended Account, retrieves only authorized records, and explains when more results are available.</p>
]]></content:encoded></item><item><title><![CDATA[Agentforce + Slack: How I Resolved “You Don’t Have Permission to Message This App”]]></title><description><![CDATA[Same user. Same permissions. A different configuration sequence.]]></description><link>https://chandan19.hashnode.dev/agentforce-slack-how-i-resolved-you-don-t-have-permission-to-message-this-app</link><guid isPermaLink="true">https://chandan19.hashnode.dev/agentforce-slack-how-i-resolved-you-don-t-have-permission-to-message-this-app</guid><dc:creator><![CDATA[Chandan S]]></dc:creator><pubDate>Wed, 23 Sep 2026 06:49:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/aa40ba67-f607-4e18-9d6d-91186ec2d137.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The Salesforce org was connected to Slack. The user mapping was correct. Agent Access and the required permissions were assigned.</p>
<p>Yet when the user tried to message my Agentforce Employee Agent, Slack displayed:</p>
<blockquote>
<p>“You don’t have permission to message this app.”</p>
</blockquote>
<p>I began investigating licenses and permissions. What eventually resolved the issue was revisiting the sequence used to configure the Slack connection, commit the agent, activate it, and refresh Slack.</p>
<p>This case changed one question in my troubleshooting checklist: <strong>alongside checking who has access, check which configuration reached the active agent.</strong></p>
<h2>The setup and the symptom</h2>
<p>This case involved an <strong>Agentforce Employee Agent</strong>, a Slack connection configured in <strong>Agentforce Builder</strong>, and testing through Slack. The workflow I used included Draft, commit, and activation.</p>
<p>Before changing anything, I reviewed the setup:</p>
<table>
<thead>
<tr>
<th>Check</th>
<th>What I confirmed</th>
</tr>
</thead>
<tbody><tr>
<td>Salesforce–Slack connection</td>
<td>The org was connected to Slack.</td>
</tr>
<tr>
<td>Slack app</td>
<td>The Agentforce app was installed.</td>
</tr>
<tr>
<td>Agent connection</td>
<td>A Slack connection was configured in Agentforce Builder.</td>
</tr>
<tr>
<td>User mapping</td>
<td>The Slack user mapped to the intended Salesforce user.</td>
</tr>
<tr>
<td>Agent Access</td>
<td>Access was enabled.</td>
</tr>
<tr>
<td>Permissions</td>
<td>The required Agentforce permissions were assigned.</td>
</tr>
<tr>
<td>Salesforce profile</td>
<td>The mapped user had System Administrator access.</td>
</tr>
</tbody></table>
<p>Slack also displayed the agent’s permissions as <strong>“Managed in Salesforce.”</strong> Together with the error, that made Salesforce access settings a natural starting point.</p>
<h2>Where my initial investigation led</h2>
<p>I investigated Agentforce Platform User licensing, Unified Employee or Employee Agent entitlement, user permissions, and possible Developer Edition limitations.</p>
<p>Those checks did not lead to the resolution.</p>
<p>The useful shift was to examine <strong>when the Slack connection was configured in relation to commit and activation</strong>. I had checked that the connection existed; I needed to revisit the workflow that put it into use.</p>
<h2>The sequence that worked in my case</h2>
<p>I followed these steps:</p>
<ol>
<li><p><strong>Deactivate the agent if it is already active.</strong> This was my starting point before revisiting the configuration.</p>
</li>
<li><p><strong>Open the agent in Draft.</strong> Make the connection changes in the editable draft.</p>
</li>
<li><p><strong>Configure the Slack connection.</strong> Complete this while the agent is in Draft.</p>
</li>
<li><p><strong>Save the changes.</strong></p>
</li>
<li><p><strong>Commit the agent version</strong> containing the updated configuration.</p>
</li>
<li><p><strong>Activate that version.</strong></p>
</li>
<li><p><strong>Restart the Slack desktop application</strong>, or perform a hard refresh when using Slack in a browser.</p>
</li>
<li><p><strong>Retest with the affected user.</strong> Confirm that the user can message the agent and receive a response.</p>
</li>
</ol>
<p>The sequence was:</p>
<pre><code class="language-text">Draft → Configure Slack → Save → Commit → Activate
                                              ↓
                                  Refresh Slack → Retest
</code></pre>
<p>After completing the sequence, the Employee Agent became available in Slack and the user could interact with it.</p>
<p><strong>No additional license or permission changes were needed to resolve this case.</strong></p>
<h2>How this relates to the official workflow</h2>
<p>Slack’s <a href="https://slack.com/help/articles/36218109305875-Set-up-and-manage-Agentforce-in-Slack">official Agentforce setup guide</a> describes adding the Slack connection in Agentforce Builder, committing the version, and then activating the agent. It provides separate instructions for the legacy builder, so use the workflow that matches your interface.</p>
<p>Salesforce’s <a href="https://help.salesforce.com/s/articleView?id=ai.agent_versions_commit.htm&amp;language=en_US&amp;type=5">Commit an Agent Version documentation</a> explains that committing creates a snapshot and prepares the agent for use outside Builder. Further edits to a committed version require a new draft.</p>
<p>These references support the connection → commit → activate order. <strong>They do not establish that my exact Slack error always has this cause, or that deactivation and a client restart are universally required.</strong> Those steps describe the recovery sequence I used.</p>
<h2>The result—and the limits of the conclusion</h2>
<table>
<thead>
<tr>
<th>Before the recovery sequence</th>
<th>After the recovery sequence</th>
</tr>
</thead>
<tbody><tr>
<td>Slack prevented the user from messaging the agent.</td>
<td>The user could interact with the Employee Agent.</td>
</tr>
<tr>
<td>Licenses and permissions were under investigation.</td>
<td>No additional license or permission changes were needed.</td>
</tr>
</tbody></table>
<p>I changed the configuration sequence <strong>and</strong> refreshed Slack before retesting. I did not isolate each step independently.</p>
<p>My conclusion is therefore specific:</p>
<blockquote>
<p>Reconfiguring the Slack connection in Draft, saving, committing, activating, and refreshing Slack resolved the issue in my environment. I did not isolate which individual step caused the change.</p>
</blockquote>
<p>The result is useful without turning it into a universal diagnosis. Salesforce’s <a href="https://help.salesforce.com/s/articleView?id=ai.agent_deploy_emp_slack.htm&amp;language=en_US&amp;type=5">connection guidance</a> also makes clear that an agent’s Slack interactions respect the user’s Salesforce licenses, permissions, and access controls. Those checks still matter.</p>
<h2>The visual recap</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/1c14a23a-4f47-4696-b982-8d99c80d66e2.jpg" alt="Infographic summarizing the error, access checks, and recovery sequence in this case." style="display:block;margin:0 auto" />

<p><em>An illustrated recap of this case, rather than a screenshot of the tested system. Its licensing and permissions conclusions apply to this incident.</em></p>
<h2>The checklist I would use next time</h2>
<table>
<thead>
<tr>
<th>Area</th>
<th>Questions I would ask</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Identity and access</strong></td>
<td>Is the correct Slack user mapped? Are Agent Access, permissions, and applicable entitlements in place?</td>
</tr>
<tr>
<td><strong>Agent lifecycle</strong></td>
<td>Which draft contains the connection? Was it saved and committed? Is that version active?</td>
</tr>
<tr>
<td><strong>Client and validation</strong></td>
<td>Has Slack been refreshed? Can the affected user send a message and receive a response?</td>
</tr>
</tbody></table>
<p>Next time, I would record the result after each change. That would help distinguish a configuration correction from a client refresh effect.</p>
<h2>The learning I am taking forward</h2>
<p>In my earlier article, <a href="https://chandan19.hashnode.dev/agentforce-lifecycle">I Tried to Package an Agentforce Agent—and Discovered It Is Really a Lifecycle Problem</a>, I explored how delivering an agent involves more than installing its components.</p>
<p>This experience brought that lifecycle lesson into integration troubleshooting.</p>
<p>My checklist now includes one more question: <strong>did the connection configuration reach the version I activated?</strong> Sharing that small detail may help another builder investigate the same confusing error.</p>
<p>That is the #AgentblazerEffect I want to contribute to: practical experiences, documented clearly enough for someone else to learn from.</p>
<p><strong>Have you encountered a similar error? What helped you distinguish an access issue from a configuration or activation issue?</strong></p>
<hr />
<p><em>This firsthand case study describes the workflow and outcome in my environment. It is not a universal fix for Slack permission errors.</em></p>
]]></content:encoded></item><item><title><![CDATA[I Tried to Package an Agentforce Agent—and Discovered It Is Really a Lifecycle Problem]]></title><description><![CDATA[Installing an Apex class from a managed package is straightforward. Installing a complete, customer-ready Agentforce agent is a very different problem…]]></description><link>https://chandan19.hashnode.dev/agentforce-lifecycle</link><guid isPermaLink="true">https://chandan19.hashnode.dev/agentforce-lifecycle</guid><dc:creator><![CDATA[Chandan S]]></dc:creator><pubDate>Wed, 19 Aug 2026 16:12:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/70294430-e178-4ba8-be00-679fc607c0cc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Installing an Apex class from a managed package is straightforward. Installing a complete, customer-ready Agentforce agent is a very different problem.</p>
<p>That difference became clear while I was experimenting with how an ISV could distribute Agentforce capabilities through a second-generation managed package (2GP).</p>
<p>My original expectation was simple:</p>
<pre><code class="language-text">Install managed package
        ↓
Working agent
</code></pre>
<p>What I discovered looked more like this:</p>
<pre><code class="language-text">Install managed package
        ↓
Install stable capabilities
        ↓
Configure subscriber environment
        ↓
Build customer-specific agent
        ↓
Compile and publish
        ↓
Test and activate
        ↓
Working agent
</code></pre>
<p>That small-looking difference changed how I think about Agentforce distribution.</p>
<blockquote>
<p><strong>The package installs the capabilities. A separate provisioning lifecycle assembles those capabilities into a customer-specific agent.</strong></p>
</blockquote>
<p>Once I understood that, the challenge stopped looking like a packaging problem. It became an agent lifecycle architecture problem.</p>
<h2>Why I started exploring this</h2>
<p>Imagine that you are an ISV shipping a Salesforce application. Your package already contains the components an agent needs: Apex actions, Flows, permission sets, configuration and business logic.</p>
<p>The natural next question is: can the package also deliver the agent, so that the customer installs it, clicks one button and starts using it?</p>
<p>That was the experience I wanted:</p>
<pre><code class="language-text">Customer installs package
        ↓
Opens Agent Installer
        ↓
Reviews configuration
        ↓
Clicks Provision Agent
        ↓
Agent is ready
</code></pre>
<p>I built a proof of concept around that idea using a namespaced package called <code>PK4 Agent Installer POC</code>, with the namespace <code>pk4_agentforce</code>.</p>
<p>The stable package layer contained a global invocable Apex action, an Apex test and a permission set. The business capability packaged successfully. But installing those components did not automatically produce a configured, compiled and published customer agent.</p>
<p>That is where the interesting part began.</p>
<h2>Discovery 1: package installation is not agent publication</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/f592d972-9091-4354-b40c-d3d18c40f211.svg" alt="" style="display:block;margin:0 auto" />

<p>The managed package keeps the reusable business logic. The customer org handles the agent-specific setup, instructions, and configuration.</p>
<p>This gives us a clean split: the package keeps the core capability, while each customer can have its own agent instructions, permissions, and configuration.</p>
<h3>What I actually validated</h3>
<p>I kept the business action simple so I could isolate and test the packaging boundary.</p>
<pre><code class="language-apex">global with sharing class PK4ProjectHealthAction {
    global class Request {
        @InvocableVariable(required=true)
        global String projectName;
    }

    global class Result {
        @InvocableVariable global String projectName;
        @InvocableVariable global String healthStatus;
        @InvocableVariable global String summary;
    }

    @InvocableMethod(label='PK4 Check Project Health')
    global static List&lt;Result&gt; checkHealth(List&lt;Request&gt; requests) {
        List&lt;Result&gt; results = new List&lt;Result&gt;();

        for (Request request : requests) {
            Result result = new Result();
            result.projectName = request.projectName;
            result.healthStatus = 'GREEN';
            result.summary = request.projectName
                + ' is healthy. Executed through the packaged action.';
            results.add(result);
        }

        return results;
    }
}
</code></pre>
<p>After installing the package, the subscriber-side Agent Script can reference the packaged capability through its namespace:</p>
<pre><code class="language-yaml">actions:
  get_project_health:
    label: "PK4 Check Project Health"
    description: "Returns project health for a named project."
    target: "apex://pk4_agentforce__PK4ProjectHealthAction"
</code></pre>
<p>The namespace creates a clean ownership boundary. The package owns the business implementation; the subscriber agent simply references it.</p>
<table>
<thead>
<tr>
<th>Experiment</th>
<th>Result</th>
</tr>
</thead>
<tbody><tr>
<td>Create a namespaced 2GP package</td>
<td>Validated</td>
</tr>
<tr>
<td>Package an invocable Apex action</td>
<td>Validated</td>
</tr>
<tr>
<td>Package Apex permission access</td>
<td>Validated</td>
</tr>
<tr>
<td>Reference the packaged action from Agent Script</td>
<td>Validated in the POC</td>
</tr>
<tr>
<td>Start provisioning from an LWC</td>
<td>Validated as the administrator experience</td>
</tr>
<tr>
<td>Clean-org upgrades, rollback and every failure mode</td>
<td>Production validation backlog</td>
</tr>
</tbody></table>
<p>The POC validated the package/action boundary. The upgrade, retry, and rollback model below is the production architecture I derived from that experiment; those scenarios still require full validation.</p>
<h2>Discovery 2: the LWC should be the control plane</h2>
<p>My next thought was: perhaps the Agent Installer LWC can create everything.</p>
<p>That also needed refinement.</p>
<p>The LWC should handle the admin experience: review the setup, choose the user the agent will run as, check prerequisites, start the process, and show progress. The actual deployment should happen in the background.</p>
<p>The better separation is:</p>
<pre><code class="language-text">Administrator
      ↓
Agent Installer LWC          captures intent and shows status
      ↓
Apex orchestrator            owns workflow and subscriber setup
      ↓
Publishing implementation    compiles, publishes, polls and retries
      ↓
Agentforce
</code></pre>
<p>The LWC is the control plane: it starts provisioning and shows progress. Asynchronous orchestration executes the lifecycle.</p>
<blockquote>
<p><strong>Architect's takeaway:</strong> the UI captures administrator intent; orchestration owns execution.</p>
</blockquote>
<h2>Discovery 3: publishing is more than inserting metadata</h2>
<p>Salesforce represents an editable Agent Script blueprint through the <code>AiAuthoringBundle</code> metadata type. The bundle includes the <code>.agent</code> script and its metadata definition.</p>
<p>Publishing that bundle is not equivalent to inserting a record. The documented Agentforce DX workflow validates and compiles Agent Script, creates or updates related agent metadata and deploys the authoring bundle.</p>
<p>The lifecycle is therefore closer to:</p>
<pre><code class="language-text">Agent Script → validate → compile → publish → test → activate
</code></pre>
<p>So the agent setup process has to handle this full publishing lifecycle safely.</p>
<p>Salesforce documents the authoring-bundle model in <a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-dx-metadata.html">Agent Metadata</a> and the publication command in the <a href="https://developer.salesforce.com/docs/platform/salesforce-cli-reference/guide/cli_reference_agent_publish_authoring-bundle.html"><code>sf agent publish authoring-bundle</code> reference</a>.</p>
<h2>Two production directions emerged</h2>
<p>Once I separated packaging from publishing, I saw two possible architectures.</p>
<h3>Architecture A: hybrid provisioning</h3>
<p>In the hybrid approach, Salesforce handles the customer setup experience and the initial checks. A small external service handles the longer-running work such as compiling, publishing, checking status, and retrying failures.</p>
<pre><code class="language-text">Subscriber administrator
          ↓
Agent Installer LWC
          ↓
Managed Apex orchestrator
  • preflight
  • runtime user
  • permissions
  • configuration
  • rendered template
  • provisioning job
          ↓ authenticated request
Restricted publishing service
  • validate tenant and package
  • compile and publish
  • poll and retry
  • return structured result
          ↓
Published subscriber agent
</code></pre>
<p>The external service handles long-running compilation, deployment polling, retries, and centralized monitoring. Because it accesses subscriber orgs, it also becomes part of the security boundary.</p>
<p>Use explicit, revocable authorization. Validate the subscriber and installed package. Accept only approved templates, and never log usernames, passwords, or access tokens.</p>
<p>The ideal request is logical:</p>
<pre><code class="language-text">Provision template ProjectAgent
Version 1.3
For subscriber org X
Using configuration hash Y
</code></pre>
<p>It should never mean: “execute this arbitrary payload.”</p>
<h3>Architecture B: Salesforce-only provisioning</h3>
<p>The alternative keeps the execution inside the subscriber org:</p>
<pre><code class="language-text">Agent Installer LWC
        ↓
Managed Apex orchestrator
        ↓
Queueable Apex
        ↓
Named Credential
        ↓
Supported Salesforce API
        ↓
Poll, publish, assign and activate
</code></pre>
<p>This keeps customer configuration inside Salesforce and removes the need for an ISV-hosted publishing service.</p>
<p>But “Salesforce-only” does not mean “no integration.” Apex cannot execute Salesforce CLI commands. The design still needs a documented, authenticated API that provides the required publication lifecycle. A basic metadata deployment should not be assumed to perform all the same steps as the documented Agentforce publishing flow.</p>
<p>I would not consider the Salesforce-only design proven until it completes the full lifecycle in a clean subscriber org. Salesforce's current <a href="https://developer.salesforce.com/docs/platform/named-credentials/guide/get-started.html">Named Credentials model</a> is the appropriate starting point for authenticated Apex callouts.</p>
<table>
<thead>
<tr>
<th>Capability</th>
<th>Hybrid publisher</th>
<th>Salesforce-only</th>
</tr>
</thead>
<tbody><tr>
<td>One-click administrator experience</td>
<td>Yes</td>
<td>Potentially</td>
</tr>
<tr>
<td>External infrastructure</td>
<td>Required</td>
<td>Not required</td>
</tr>
<tr>
<td>Direct use of documented CLI publication workflow</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Apex limit exposure</td>
<td>Lower</td>
<td>Higher</td>
</tr>
<tr>
<td>Centralized operations and retry</td>
<td>Strong</td>
<td>Distributed across orgs</td>
</tr>
<tr>
<td>Customer configuration remains entirely in-org</td>
<td>Only if the request is minimized</td>
<td>Yes</td>
</tr>
<tr>
<td>Current recommendation</td>
<td>Validate first</td>
<td>Validate API parity first</td>
</tr>
</tbody></table>
<h2>Agent setup is really a step-by-step state machine</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/b5d1a2e3-0bfd-42c5-acb1-01d9e4d10c7c.svg" alt="" style="display:block;margin:0 auto" />

<p>Agent publication should not depend on an open browser request. The system needs to remember which step it is currently on, even if the page is refreshed or something fails.</p>
<p>Each stage should persist its status, a safe diagnostic, whether the failure is retryable, and the required recovery action. The LWC can then refresh without losing the operation, and a second click can resume an existing job instead of creating another agent.</p>
<p>The setup process also needs duplicate protection, often called <strong>idempotency</strong>.</p>
<pre><code class="language-text">idempotency key =
subscriberOrgId + agentApiName + templateVersion + configurationHash
</code></pre>
<p>Before starting a new setup, the system checks that key to see whether the same request is already running or has already finished:Running: resume or return the current job.</p>
<ul>
<li><p>Completed: return the existing result.</p>
</li>
<li><p>Failed: apply the retry policy.</p>
</li>
<li><p>Missing: create a new job.</p>
</li>
</ul>
<p>Without idempotency, a browser refresh, Queueable retry, or lost response can create duplicate agents or versions.</p>
<h2>Then I found the harder problem: upgrades</h2>
<p>Getting version 1 installed is not the real test of an ISV architecture. Upgrading it safely is.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a85cf8800a9107ef5771575/815ab048-641f-4d43-9dfb-48bdf4ff1413.svg" alt="" style="display:block;margin:0 auto" />

<p>Customers may customize the agent after installation. A later ISV upgrade must preserve those changes instead of overwriting them.</p>
<p>A safer model tracks three layers independently:</p>
<pre><code class="language-text">ISV base template
        +
subscriber configuration
        +
protected customer customization
        =
proposed agent version
</code></pre>
<p>The setup and upgrade process should track the template version, package version, customer configuration, agent/version ID, rendered-script hash, publication result, and rollback target.</p>
<p>An upgrade can then:</p>
<ol>
<li><p>Detect the installed package and agent template versions.</p>
</li>
<li><p>Identify subscriber-owned changes.</p>
</li>
<li><p>Produce a new draft rather than overwrite the active version.</p>
</li>
<li><p>Validate namespaces and action signatures.</p>
</li>
<li><p>Test the proposed version.</p>
</li>
<li><p>Require administrator review when changes are material.</p>
</li>
<li><p>Activate only after validation.</p>
</li>
<li><p>Preserve the prior version as a rollback target.</p>
</li>
</ol>
<blockquote>
<p><strong>Architect's takeaway:</strong> package upgrades and agent upgrades are related, but they are not the same lifecycle.</p>
</blockquote>
<h2>What I would validate before calling it production-ready</h2>
<p>Architecture diagrams are helpful. Clean-org failure testing is better.</p>
<p>The production validation suite should cover:</p>
<ul>
<li><p>Clean package installation and Agent Installer initialization.</p>
</li>
<li><p>Agentforce feature, license and permission checks.</p>
</li>
<li><p>Namespaced action resolution.</p>
</li>
<li><p>Agent Script compilation and publication.</p>
</li>
<li><p>Correct runtime-user assignment.</p>
</li>
<li><p>Duplicate clicks and browser refreshes.</p>
</li>
<li><p>Expired authorization and deployment timeouts.</p>
</li>
<li><p>Invalid action references and invalid Agent Script.</p>
</li>
<li><p>Package upgrades and changed action signatures.</p>
</li>
<li><p>Customer-edited agents and protected configuration.</p>
</li>
<li><p>Failed upgrades and rollback.</p>
</li>
<li><p>Package uninstall while provisioned agent metadata remains.</p>
</li>
</ul>
<p>This is where a proof of concept becomes an ISV product.</p>
<h2>The architecture I would validate first</h2>
<p>Based on what I learned, I would validate the hybrid model first. I would keep the publishing implementation behind an abstraction such as:</p>
<pre><code class="language-apex">public interface AgentPublisher {
    PublishResponse publish(AgentProvisioningJob job);
    JobStatus pollStatus(String externalJobId);
}
</code></pre>
<p>With implementations such as:</p>
<pre><code class="language-text">AgentPublisher
  ├── HybridServicePublisher
  └── SalesforceNativePublisher
</code></pre>
<p>The LWC, setup flow, templates, customer configuration, and upgrade logic should not depend on how the agent is published.</p>
<p>If Salesforce later provides a package-safe API that supports the full publishing lifecycle, only the publishing layer needs to change.</p>
<h2>The bigger idea I took away</h2>
<p>I started this experiment by asking:</p>
<blockquote>
<p>How can I package an Agentforce agent?</p>
</blockquote>
<p>I ended it with a better question:</p>
<blockquote>
<p>How should an ISV manage the complete lifecycle of an agent across customer organizations?</p>
</blockquote>
<p>The product is not simply an agent inside a package. The lifecycle itself becomes part of the product:</p>
<pre><code class="language-text">Install capabilities
        ↓
Validate subscriber
        ↓
Provision and publish
        ↓
Test and activate
        ↓
Observe and upgrade
        ↓
Preserve customer intent
        ↓
Roll back safely
</code></pre>
<p>The managed package supplies the reusable building blocks. The setup process turns them into a customer-specific agent. Later upgrades can then add ISV improvements without losing what the customer configured.</p>
<p>That is the architecture I believe can turn Agentforce distribution from a manual implementation exercise into a repeatable ISV product capability.</p>
<h2>If you are an ISV experimenting with this</h2>
<p>You do not need to build the complete provisioning platform on day one. Start by validating the package-to-agent boundary end to end:</p>
<ol>
<li><p><strong>Package one global invocable Apex action.</strong> Keep the business logic simple so you can focus on validating the packaging and Agentforce integration boundary.</p>
</li>
<li><p><strong>Install the package in a clean subscriber org.</strong> Confirm that the packaged action and required permissions are available through the managed namespace.</p>
</li>
<li><p><strong>Create the subscriber-side Agent Script and bind it to the packaged action.</strong> Define a simple capability in Agent Script, then reference the action installed in step 2 through the managed namespace.</p>
<pre><code class="language-yaml">target: "apex://pk4_agentforce__PK4ProjectHealthAction"
</code></pre>
</li>
<li><p><strong>Publish the Agent Script as an authoring bundle.</strong> This creates the subscriber-side agent definition while keeping the business capability in the managed package.</p>
</li>
<li><p><strong>Configure the runtime user and assign the required permissions.</strong> The runtime user must be able to execute the packaged action that the Agent Script references.</p>
</li>
<li><p><strong>Test the complete flow through the agent.</strong> Verify the full chain:</p>
<p><strong>Agent → Agent Script → namespaced packaged action → response</strong></p>
</li>
<li><p><strong>Only after that works, automate the lifecycle.</strong> Once the package-to-agent boundary is proven, add the installer LWC, orchestration, publishing abstraction, idempotency, upgrade handling, and rollback.</p>
</li>
</ol>
<p>The important part is not completing a list of separate steps. It is proving one connected flow:</p>
<p><strong>Package capability → reference it from Agent Script → publish the agent → grant runtime access → invoke the packaged capability successfully</strong></p>
<p>That sequence is what made the architecture click for me.</p>
<p>Sometimes the most useful discovery is not finding the API you expected. It is realizing that you were solving the wrong boundary.</p>
<hr />
<p><em>How are you approaching agent distribution, subscriber-specific configuration, upgrades and rollback for ISV-built Agentforce products?</em></p>
]]></content:encoded></item></channel></rss>