mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site/src/pages/WorkspacesPage): show only parent agent apps in workspaces table (#26568)
The workspaces table shortcuts row selected `resources[0].agents[0]`, so a sub-agent that ended up first (for example the Claude/Task sub-agent created on a workspace) could replace the parent agent's launcher icons, and which apps showed depended on agent ordering. Select the parent agent (`parent_id === null`) of the first non-hidden resource instead, matching the convention already used on the workspace detail page (`Workspace.tsx`). This keeps the shortcuts row deterministic and excludes sub-agent apps. Refs [DEVEX-459](https://linear.app/codercom/issue/DEVEX-459/aggregate-workspace-table-shortcuts-across-all-agents) <details> <summary>Decision context and scope</summary> Per the discussion on DEVEX-459, this is the agreed short-term fix: > In the short term, we should display only apps from the parent agent and make the behavior deterministic, rather than the current reported behavior of showing apps from the first discovered agent. Out of scope (tracked as a longer-term backlog item on DEVEX-459): - Aggregating app shortcuts across multiple agents. - Changing the 4-slot cap (`WORKSPACE_APPS_SLOTS`). For workspaces with multiple top-level agents, the first parent agent's apps are shown. This is deterministic but not aggregated. A `ParentAgentApps` Storybook story was added (sub-agent listed first) with a `play` function asserting the parent agent's app renders and the sub-agent's app does not. </details> --- This PR was created by Coder Agents on behalf of @uzair-coder07.
This commit is contained in:
@@ -21,6 +21,8 @@ import {
|
||||
MockUserOwner,
|
||||
MockWorkspace,
|
||||
MockWorkspaceAgent,
|
||||
MockWorkspaceApp,
|
||||
MockWorkspaceSubAgent,
|
||||
mockApiError,
|
||||
} from "#/testHelpers/entities";
|
||||
import {
|
||||
@@ -362,6 +364,66 @@ export const MultipleApps: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
// The shortcuts row only renders apps from the parent agent (the agent without
|
||||
// a `parent_id`). Apps from sub-agents, such as those created by devcontainers,
|
||||
// are excluded so the row stays deterministic regardless of agent ordering.
|
||||
export const ParentAgentApps: Story = {
|
||||
args: {
|
||||
workspaces: [
|
||||
{
|
||||
...MockWorkspace,
|
||||
name: "parent-agent-apps",
|
||||
latest_build: {
|
||||
...MockWorkspace.latest_build,
|
||||
resources: [
|
||||
{
|
||||
...MockWorkspace.latest_build.resources[0],
|
||||
agents: [
|
||||
// Sub-agent is listed first to prove ordering does
|
||||
// not determine which apps are shown.
|
||||
{
|
||||
...MockWorkspaceSubAgent,
|
||||
display_apps: [],
|
||||
apps: [
|
||||
{
|
||||
...MockWorkspaceApp,
|
||||
id: "sub-agent-app",
|
||||
slug: "sub-agent-app",
|
||||
display_name: "Sub Agent App",
|
||||
health: "healthy",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
...MockWorkspaceAgent,
|
||||
display_apps: [],
|
||||
apps: [
|
||||
{
|
||||
...MockWorkspaceApp,
|
||||
id: "parent-agent-app",
|
||||
slug: "parent-agent-app",
|
||||
display_name: "Parent Agent App",
|
||||
health: "healthy",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
count: allWorkspaces.length,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await canvas.findByRole("link", { name: /Open Parent Agent App/i });
|
||||
expect(
|
||||
canvas.queryByRole("link", { name: /Open Sub Agent App/i }),
|
||||
).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const ShowOrganizations: Story = {
|
||||
args: {
|
||||
workspaces: [
|
||||
|
||||
@@ -656,15 +656,21 @@ const WorkspaceApps: FC<WorkspaceAppsProps> = ({ workspace }) => {
|
||||
* Coder is pretty flexible and allows an enormous variety of use cases, such
|
||||
* as having multiple resources with many agents, but they are not common. The
|
||||
* most common scenario is to have one single compute resource with one single
|
||||
* agent containing all the apps. Lets test this getting the apps for the
|
||||
* first resource, and first agent - they are sorted to return the compute
|
||||
* resource first - and see what customers and ourselves, using dogfood, think
|
||||
* about that.
|
||||
* agent containing all the apps. We get the apps from the first compute
|
||||
* resource (they are sorted to return the compute resource first).
|
||||
*
|
||||
* For multi-agent workspaces with sub-agents we show the apps from the parent
|
||||
* agent (the one without a `parent_id`). Sub-agents, such as those created by
|
||||
* devcontainers, are skipped so agent ordering does not determine which apps
|
||||
* appear.
|
||||
*
|
||||
* When a workspace has multiple parent-level agents we show the apps from the
|
||||
* first one only; aggregating apps across agents is tracked separately.
|
||||
*/
|
||||
const agent = workspace.latest_build.resources
|
||||
.filter((r) => !r.hide)
|
||||
.at(0)
|
||||
?.agents?.at(0);
|
||||
?.agents?.find((a) => a.parent_id === null);
|
||||
if (!agent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user