diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx index 6b3594d7e0..35f2deba65 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx @@ -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: [ diff --git a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx index ae33dec515..6af4f89689 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx @@ -656,15 +656,21 @@ const WorkspaceApps: FC = ({ 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; }