Skip to content

Commit 31e48b6

Browse files
committed
chore(site): remove dependency on sidebar_app_id
1 parent ae1c3b5 commit 31e48b6

File tree

3 files changed

+86
-66
lines changed

3 files changed

+86
-66
lines changed

site/src/pages/TaskPage/TaskApps.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ import { TaskAppIFrame } from "./TaskAppIframe";
2121

2222
type TaskAppsProps = {
2323
task: Task;
24+
sidebarApp: WorkspaceApp | null;
2425
};
2526

2627
type AppWithAgent = {
2728
app: WorkspaceApp;
2829
agent: WorkspaceAgent;
2930
};
3031

31-
export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
32+
export const TaskApps: FC<TaskAppsProps> = ({ task, sidebarApp }) => {
3233
const agents = task.workspace.latest_build.resources
3334
.flatMap((r) => r.agents)
3435
.filter((a) => !!a);
@@ -42,10 +43,7 @@ export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
4243
agent,
4344
})),
4445
)
45-
.filter(
46-
({ app }) =>
47-
!!app && app.id !== task.workspace.latest_build.ai_task_sidebar_app_id,
48-
);
46+
.filter(({ app }) => !!app && app.id !== sidebarApp?.id);
4947

5048
const embeddedApps = apps.filter(({ app }) => !app.external);
5149
const externalApps = apps.filter(({ app }) => app.external);

site/src/pages/TaskPage/TaskPage.tsx

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { API } from "api/api";
22
import { getErrorDetail, getErrorMessage } from "api/errors";
33
import { template as templateQueryOptions } from "api/queries/templates";
4-
import type { Workspace, WorkspaceStatus } from "api/typesGenerated";
4+
import type {
5+
Workspace,
6+
WorkspaceApp,
7+
WorkspaceStatus,
8+
} from "api/typesGenerated";
59
import { Button } from "components/Button/Button";
610
import { Loader } from "components/Loader/Loader";
711
import { Margins } from "components/Margins/Margins";
@@ -105,6 +109,8 @@ const TaskPage = () => {
105109
"stopping",
106110
];
107111

112+
const [sidebarApp, sidebarAppStatus] = getSidebarApp(task);
113+
108114
if (waitingStatuses.includes(task.workspace.latest_build.status)) {
109115
// If no template yet, use an indeterminate progress bar.
110116
const transition = (template &&
@@ -171,7 +177,7 @@ const TaskPage = () => {
171177
</Margins>
172178
);
173179
} else {
174-
content = <TaskApps task={task} />;
180+
content = <TaskApps task={task} sidebarApp={sidebarApp} />;
175181
}
176182

177183
return (
@@ -181,7 +187,11 @@ const TaskPage = () => {
181187
</Helmet>
182188
<PanelGroup autoSaveId="task" direction="horizontal">
183189
<Panel defaultSize={25} minSize={20}>
184-
<TaskSidebar task={task} />
190+
<TaskSidebar
191+
task={task}
192+
sidebarApp={sidebarApp}
193+
sidebarAppStatus={sidebarAppStatus}
194+
/>
185195
</Panel>
186196
<PanelResizeHandle>
187197
<div className="w-1 bg-border h-full hover:bg-border-hover transition-all relative" />
@@ -229,3 +239,66 @@ export const data = {
229239
} satisfies Task;
230240
},
231241
};
242+
243+
const getSidebarApp = (
244+
task: Task,
245+
): [WorkspaceApp | null, "error" | "loading" | "healthy"] => {
246+
if (!task.workspace.latest_build.job.completed_at) {
247+
// while the workspace build is running, we don't have a sidebar app yet
248+
return [null, "loading"];
249+
}
250+
251+
// Ensure all the agents are healthy before continuing.
252+
const healthyAgents = task.workspace.latest_build.resources
253+
.flatMap((res) => res.agents)
254+
.filter((agt) => !!agt && agt.health.healthy);
255+
if (!healthyAgents) {
256+
return [null, "loading"];
257+
}
258+
259+
// TODO(Cian): Improve logic for determining sidebar app.
260+
// For now, we take the first workspace_app with at least one app_status.
261+
const sidebarApps = healthyAgents
262+
.flatMap((a) => a?.apps)
263+
.filter((a) => !!a && a.statuses && a.statuses.length > 0);
264+
265+
// At this point the workspace build is complete but no app has reported a status
266+
// indicating that it is ready. Most well-behaved agentic AI applications will
267+
// indicate their readiness status via MCP(coder_report_task).
268+
// It's also possible that the application is just not ready yet.
269+
// We return "loading" instead of "error" to avoid showing an error state if the app
270+
// becomes available shortly after. The tradeoff is that users may see a loading state
271+
// indefinitely if there's a genuine issue, but this is preferable to false error alerts.
272+
if (!sidebarApps) {
273+
return [null, "loading"];
274+
}
275+
276+
const sidebarApp = sidebarApps[0];
277+
if (!sidebarApp) {
278+
return [null, "loading"];
279+
}
280+
281+
// "disabled" means that the health check is disabled, so we assume
282+
// that the app is healthy
283+
if (sidebarApp.health === "disabled") {
284+
return [sidebarApp, "healthy"];
285+
}
286+
if (sidebarApp.health === "healthy") {
287+
return [sidebarApp, "healthy"];
288+
}
289+
if (sidebarApp.health === "initializing") {
290+
return [sidebarApp, "loading"];
291+
}
292+
if (sidebarApp.health === "unhealthy") {
293+
return [sidebarApp, "error"];
294+
}
295+
296+
// exhaustiveness check
297+
const _: never = sidebarApp.health;
298+
// this should never happen
299+
console.error(
300+
"Task workspace has a finished build but the sidebar app is in an unknown health state",
301+
task.workspace,
302+
);
303+
return [null, "error"];
304+
};

site/src/pages/TaskPage/TaskSidebar.tsx

Lines changed: 7 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -22,66 +22,15 @@ import { TaskStatusLink } from "./TaskStatusLink";
2222

2323
type TaskSidebarProps = {
2424
task: Task;
25+
sidebarApp: WorkspaceApp | null;
26+
sidebarAppStatus: "error" | "loading" | "healthy";
2527
};
2628

27-
type SidebarAppStatus = "error" | "loading" | "healthy";
28-
29-
const getSidebarApp = (task: Task): [WorkspaceApp | null, SidebarAppStatus] => {
30-
const sidebarAppId = task.workspace.latest_build.ai_task_sidebar_app_id;
31-
// a task workspace with a finished build must have a sidebar app id
32-
if (!sidebarAppId && task.workspace.latest_build.job.completed_at) {
33-
console.error(
34-
"Task workspace has a finished build but no sidebar app id",
35-
task.workspace,
36-
);
37-
return [null, "error"];
38-
}
39-
40-
const sidebarApp = task.workspace.latest_build.resources
41-
.flatMap((r) => r.agents)
42-
.flatMap((a) => a?.apps)
43-
.find((a) => a?.id === sidebarAppId);
44-
45-
if (!task.workspace.latest_build.job.completed_at) {
46-
// while the workspace build is running, we don't have a sidebar app yet
47-
return [null, "loading"];
48-
}
49-
if (!sidebarApp) {
50-
// The workspace build is complete but the expected sidebar app wasn't found in the resources.
51-
// This could happen due to timing issues or temporary inconsistencies in the data.
52-
// We return "loading" instead of "error" to avoid showing an error state if the app
53-
// becomes available shortly after. The tradeoff is that users may see a loading state
54-
// indefinitely if there's a genuine issue, but this is preferable to false error alerts.
55-
return [null, "loading"];
56-
}
57-
// "disabled" means that the health check is disabled, so we assume
58-
// that the app is healthy
59-
if (sidebarApp.health === "disabled") {
60-
return [sidebarApp, "healthy"];
61-
}
62-
if (sidebarApp.health === "healthy") {
63-
return [sidebarApp, "healthy"];
64-
}
65-
if (sidebarApp.health === "initializing") {
66-
return [sidebarApp, "loading"];
67-
}
68-
if (sidebarApp.health === "unhealthy") {
69-
return [sidebarApp, "error"];
70-
}
71-
72-
// exhaustiveness check
73-
const _: never = sidebarApp.health;
74-
// this should never happen
75-
console.error(
76-
"Task workspace has a finished build but the sidebar app is in an unknown health state",
77-
task.workspace,
78-
);
79-
return [null, "error"];
80-
};
81-
82-
export const TaskSidebar: FC<TaskSidebarProps> = ({ task }) => {
83-
const [sidebarApp, sidebarAppStatus] = getSidebarApp(task);
84-
29+
export const TaskSidebar: FC<TaskSidebarProps> = ({
30+
task,
31+
sidebarApp,
32+
sidebarAppStatus,
33+
}) => {
8534
return (
8635
<aside className="flex flex-col h-full shrink-0 w-full">
8736
<header className="border-0 border-b border-solid border-border p-4 pt-0">

0 commit comments

Comments
 (0)