diff --git a/apps/code/src/renderer/features/command/components/CommandMenu.tsx b/apps/code/src/renderer/features/command/components/CommandMenu.tsx
index 006dc619a..daf431fe5 100644
--- a/apps/code/src/renderer/features/command/components/CommandMenu.tsx
+++ b/apps/code/src/renderer/features/command/components/CommandMenu.tsx
@@ -64,10 +64,17 @@ function TaskCommandIcon({ task }: { task: Task }) {
cloudPrUrl: null,
taskRunEnvironment: task.latest_run?.environment,
});
+ const stateSlackThreadUrl = (
+ task.latest_run?.state as { slack_thread_url?: unknown } | undefined
+ )?.slack_thread_url;
+ const slackThreadUrl =
+ typeof stateSlackThreadUrl === "string" ? stateSlackThreadUrl : undefined;
return (
diff --git a/apps/code/src/renderer/features/sidebar/components/TaskListView.tsx b/apps/code/src/renderer/features/sidebar/components/TaskListView.tsx
index b8cec1f3e..e12c42aec 100644
--- a/apps/code/src/renderer/features/sidebar/components/TaskListView.tsx
+++ b/apps/code/src/renderer/features/sidebar/components/TaskListView.tsx
@@ -120,6 +120,8 @@ function TaskRow({
isPinned={task.isPinned}
needsPermission={task.needsPermission}
taskRunStatus={task.taskRunStatus}
+ originProduct={task.originProduct}
+ slackThreadUrl={task.slackThreadUrl}
prState={prState}
hasDiff={hasDiff}
timestamp={timestamp}
diff --git a/apps/code/src/renderer/features/sidebar/components/items/TaskIcon.tsx b/apps/code/src/renderer/features/sidebar/components/items/TaskIcon.tsx
index 866a5ed65..de44afcd4 100644
--- a/apps/code/src/renderer/features/sidebar/components/items/TaskIcon.tsx
+++ b/apps/code/src/renderer/features/sidebar/components/items/TaskIcon.tsx
@@ -12,7 +12,9 @@ import {
HandPalm,
Pause,
PushPin,
+ SlackLogo,
} from "@phosphor-icons/react";
+import { trpcClient } from "@renderer/trpc/client";
import { isTerminalStatus, type TaskRunStatus } from "@shared/types";
export const ICON_SIZE = 12;
@@ -23,47 +25,145 @@ export const ICON_SIZE = 12;
// selected row, which turns a `currentColor` icon black on hover. An explicit
// `fill` is immune, and renders identically in the sidebar.
+// Map origin_product values to the icon + label used to brand the task's
+// status icon. Extend this when a new product (e.g. email, support) needs its
+// own indicator.
+type OriginProductMeta = { Icon: typeof SlackLogo; label: string };
+const ORIGIN_PRODUCT_META: Record = {
+ slack: { Icon: SlackLogo, label: "Slack" },
+};
+
+function getOriginProductMeta(
+ originProduct?: string,
+): OriginProductMeta | undefined {
+ return originProduct ? ORIGIN_PRODUCT_META[originProduct] : undefined;
+}
+
+// Renders the icon inside a span. When `link` is set the span becomes
+// clickable and opens the originating thread externally. SidebarItem renders
+// the row as a `