From cee98fe547ba6df6c6dd6740bcbd8f4c7d4bbc8d Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Fri, 10 Jul 2026 21:14:59 +0100 Subject: [PATCH 1/5] Show channel task kickoff as an agent message and slim the task card Channel feed kickoff rows now render as agent messages (robot avatar, "Agent" author) with an inert mention-styled "@Name started a new task" body instead of the raw prompt. The task card drops the requested-by line, task slug, and Cloud/Local runtime label. Generated-By: PostHog Code Task-Id: 0a03c896-d426-4b8c-acf7-4833e47d03f2 --- .../canvas/components/ChannelFeedView.tsx | 104 +++++------------- 1 file changed, 30 insertions(+), 74 deletions(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index f2056a5e6c..489d670ad8 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -3,7 +3,6 @@ import { ChatCircleIcon, GitBranchIcon, RobotIcon, - UserIcon, } from "@phosphor-icons/react"; import { Avatar, @@ -43,10 +42,6 @@ import { TaskTabIcon } from "@posthog/ui/features/browser-tabs/TaskTabIcon"; import { useChannelTaskData } from "@posthog/ui/features/canvas/hooks/useChannelTaskData"; import { useTaskThread } from "@posthog/ui/features/canvas/hooks/useTaskThread"; import { userDisplayName } from "@posthog/ui/features/canvas/utils/userDisplay"; -import { xmlToPlainText } from "@posthog/ui/features/message-editor/content"; -import { CollapsibleMessageContent } from "@posthog/ui/features/sessions/components/session-update/CollapsibleMessageContent"; -import { extractChannelContext } from "@posthog/ui/features/sessions/components/session-update/channelContext"; -import { getOriginProductMeta } from "@posthog/ui/features/sidebar/components/items/TaskIcon"; import { type SidebarPrState, useTaskPrStatus, @@ -242,53 +237,15 @@ function TaskStatusBadge({ display }: { display: TaskStatusDisplay }) { ); } -// The prompt as the user typed it: drop the channel CONTEXT.md block the saga -// prepended and flatten the editor XML back to plain text. -function promptText(task: Task): string { - const raw = - extractChannelContext(task.description)?.stripped ?? task.description; - try { - return xmlToPlainText(raw).trim() || task.title; - } catch { - return raw.trim() || task.title; - } -} - -// The card's context line, mirroring the storybook feed: who/what kicked the -// task off ("Requested by @Ann" for humans, the origin product otherwise). -function TaskCardOrigin({ task }: { task: Task }) { - const isUserCreated = task.origin_product === "user_created"; - const label = isUserCreated - ? `Requested by @${userDisplayName(task.created_by)}` - : (getOriginProductMeta(task.origin_product)?.label ?? task.origin_product); - return ( - - {isUserCreated ? : } - {label} - - ); -} - // The task the message kicked off, as a card everyone in the channel sees: -// origin + status up top, bold title, then run metadata. +// bold title + status up top, then run metadata. function TaskCard({ task, onOpen }: { task: Task; onOpen: () => void }) { const statusDisplay = useTaskStatusDisplay(task); const prUrl = typeof task.latest_run?.output?.pr_url === "string" ? task.latest_run.output.pr_url : undefined; - // The repository renders separately with its icon; `meta` is the plain-text - // remainder of the row. - const environment = task.latest_run?.environment; - const meta = [ - task.slug || null, - task.latest_run?.stage ?? null, - environment === "cloud" - ? "Cloud" - : environment === "local" - ? "Local" - : null, - ].filter(Boolean) as string[]; + const stage = task.latest_run?.stage; return ( void }) { >
- +
+ {/* Same live status icon as the code side nav, so the card and the + nav never disagree (generating spinner, needs-permission, cloud + status colors, PR state). */} + + + {task.title || "Untitled task"} + +
-
- {/* Same live status icon as the code side nav, so the card and the - nav never disagree (generating spinner, needs-permission, cloud - status colors, PR state). */} - - - {task.title || "Untitled task"} - -
- {(meta.length > 0 || task.repository || prUrl) && ( + {(stage || task.repository || prUrl) && (
{task.repository && ( @@ -323,9 +279,9 @@ function TaskCard({ task, onOpen }: { task: Task; onOpen: () => void }) { {task.repository} )} - {meta.length > 0 && ( + {stage && ( - {meta.join(" · ")} + {stage} )} {prUrl && ( @@ -422,29 +378,20 @@ const FeedItem = memo(function FeedItem({ onOpenTask: (task: Task) => void; onOpenThread: (task: Task) => void; }) { - const prompt = useMemo(() => promptText(task), [task]); - const isAgent = !task.created_by || task.origin_product !== "user_created"; - return ( - {isAgent && !task.created_by ? ( - - ) : ( - getUserInitials(task.created_by) - )} + - - {task.created_by ? userDisplayName(task.created_by) : "Agent"} - - {isAgent && Agent} + Agent + Agent @@ -452,10 +399,19 @@ const FeedItem = memo(function FeedItem({ - - - {prompt} - + + {task.created_by ? ( + <> + {/* Mention-styled but rendered inert: the starter shouldn't be + notified about their own task. */} + + @{userDisplayName(task.created_by)} + {" "} + started a new task + + ) : ( + "A new task was started" + )} onOpenTask(task)} /> From 49c34d59e62e27aa5f23cdf3570e040fda02cc05 Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Fri, 10 Jul 2026 21:15:01 +0100 Subject: [PATCH 2/5] Share the mention chip class and drop a stale meta-row comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factors the non-self mention chip styling out of MentionText into an exported mentionChipClass so the feed's inert kickoff mention can't drift from real thread mentions, and updates a useTaskStatusDisplay comment that still referenced the removed "· Local" meta text. Generated-By: PostHog Code Task-Id: 0a03c896-d426-4b8c-acf7-4833e47d03f2 --- .../features/canvas/components/ChannelFeedView.tsx | 8 ++++---- .../src/features/canvas/components/MentionText.tsx | 14 ++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index 489d670ad8..989a9ac199 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -39,6 +39,7 @@ import type { Task, TaskRunStatus } from "@posthog/shared/domain-types"; import { isTerminalStatus } from "@posthog/shared/domain-types"; import { getUserInitials } from "@posthog/ui/features/auth/userInitials"; import { TaskTabIcon } from "@posthog/ui/features/browser-tabs/TaskTabIcon"; +import { mentionChipClass } from "@posthog/ui/features/canvas/components/MentionText"; import { useChannelTaskData } from "@posthog/ui/features/canvas/hooks/useChannelTaskData"; import { useTaskThread } from "@posthog/ui/features/canvas/hooks/useTaskThread"; import { userDisplayName } from "@posthog/ui/features/canvas/utils/userDisplay"; @@ -193,9 +194,8 @@ function useTaskStatusDisplay(task: Task): TaskStatusDisplay { base = statusBadge(status); } else { // Local, non-terminal: the run status is unreliable (the backend row stays - // "queued" while the agent runs on the creator's machine), and the - // environment already shows in the card's meta row ("· Local"), so we - // render no status badge here rather than a redundant "Local" pill. + // "queued" while the agent runs on the creator's machine), so we render no + // status badge rather than a misleading one. base = null; } @@ -404,7 +404,7 @@ const FeedItem = memo(function FeedItem({ <> {/* Mention-styled but rendered inert: the starter shouldn't be notified about their own task. */} - + @{userDisplayName(task.created_by)} {" "} started a new task diff --git a/packages/ui/src/features/canvas/components/MentionText.tsx b/packages/ui/src/features/canvas/components/MentionText.tsx index ffdb7834ec..ce6c4bd26a 100644 --- a/packages/ui/src/features/canvas/components/MentionText.tsx +++ b/packages/ui/src/features/canvas/components/MentionText.tsx @@ -8,6 +8,12 @@ type RenderSegment = | { type: "link"; text: string; href: string } | { type: "mention"; name: string; email: string }; +// The plain (not-the-viewer) mention chip look, also used by surfaces that +// render a mention-styled name without real mention semantics (e.g. the +// channel feed's "started a new task" row). +export const mentionChipClass = + "rounded px-0.5 font-medium text-[var(--accent-11)]"; + /** * Thread message content with inline mention tokens rendered as highlighted * `@Name` chips (a mention of the viewer gets the stronger treatment) and @@ -52,11 +58,11 @@ export function MentionText({ return ( @{segment.name} From 493b7875187b14e154264700c8a00feafb4772dd Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Fri, 10 Jul 2026 21:15:04 +0100 Subject: [PATCH 3/5] Name the channel kickoff author PostHog instead of Agent Generated-By: PostHog Code Task-Id: 0a03c896-d426-4b8c-acf7-4833e47d03f2 --- packages/ui/src/features/canvas/components/ChannelFeedView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index 989a9ac199..fbeda136e0 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -390,7 +390,7 @@ const FeedItem = memo(function FeedItem({ - Agent + PostHog Agent Date: Fri, 10 Jul 2026 21:15:06 +0100 Subject: [PATCH 4/5] Let the channel task card span the full row width Generated-By: PostHog Code Task-Id: 0a03c896-d426-4b8c-acf7-4833e47d03f2 --- packages/ui/src/features/canvas/components/ChannelFeedView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index fbeda136e0..1b833c5080 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -251,7 +251,7 @@ function TaskCard({ task, onOpen }: { task: Task; onOpen: () => void }) { Date: Fri, 10 Jul 2026 21:15:07 +0100 Subject: [PATCH 5/5] Only attribute kickoff rows to users for channel-started tasks Slack/automation-originated tasks can carry a created_by who didn't start the task in the channel; those rows now fall back to the neutral "A new task was started" copy instead of "@Name started a new task". Generated-By: PostHog Code Task-Id: 0a03c896-d426-4b8c-acf7-4833e47d03f2 --- .../ui/src/features/canvas/components/ChannelFeedView.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index 1b833c5080..fd5a38f871 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -400,7 +400,9 @@ const FeedItem = memo(function FeedItem({ - {task.created_by ? ( + {/* Only attribute channel-started tasks: other origins (Slack, + automations) carry a created_by who didn't start it here. */} + {task.origin_product === "user_created" && task.created_by ? ( <> {/* Mention-styled but rendered inert: the starter shouldn't be notified about their own task. */}