Skip to content

Commit dcf5108

Browse files
committed
chore(site): remove dependency on sidebar_app_id
1 parent 36119ca commit dcf5108

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy