Skip to content

Commit c2880a6

Browse files
authored
Move thread auto-posts server-side; keep client rendering support
The canvas-created and turn-complete thread posts now come from the PostHog backend (PostHog/posthog#70371) so they land even when no client is open. This PR keeps only the rendering side: markdown [label](url) links in thread messages, and authorless messages shown as "Agent" with a robot avatar. Generated-By: PostHog Code Task-Id: ea09a661-1f6d-4bd8-a046-24be60909d28
1 parent 0aac8d4 commit c2880a6

7 files changed

Lines changed: 23 additions & 247 deletions

File tree

packages/shared/src/analytics-events.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -873,8 +873,7 @@ export type ChannelActionType =
873873
| "mention_member"
874874
| "view_activity"
875875
| "open_mention"
876-
| "canvas_mode_toggle"
877-
| "thread_auto_post";
876+
| "canvas_mode_toggle";
878877

879878
export interface ChannelActionProperties {
880879
action_type: ChannelActionType;
@@ -893,8 +892,6 @@ export interface ChannelActionProperties {
893892
suggestion_label?: string;
894893
/** For canvas_mode_toggle: whether canvas mode is being armed. */
895894
armed?: boolean;
896-
/** For thread_auto_post: which auto-comment was posted. */
897-
auto_post_kind?: "canvas_created" | "turn_complete";
898895
/** Whether the underlying mutation resolved successfully. */
899896
success?: boolean;
900897
}

packages/ui/src/features/canvas/components/ThreadPanel.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,21 @@ function ThreadMessageRow({
6565
}) {
6666
const forwarded = !!message.forwarded_to_agent_at;
6767
const showMenu = (isTaskAuthor && !forwarded) || isOwnMessage;
68+
// Authorless messages are posted by the backend on the agent's behalf
69+
// (canvas created, turn complete).
70+
const isAgent = !message.author;
6871

6972
return (
7073
<div className="group flex gap-2 rounded-md px-2 py-1.5 hover:bg-fill-secondary">
7174
<Avatar size="xs" className="mt-0.5 shrink-0">
72-
<AvatarFallback>{getUserInitials(message.author)}</AvatarFallback>
75+
<AvatarFallback>
76+
{isAgent ? <RobotIcon size={12} /> : getUserInitials(message.author)}
77+
</AvatarFallback>
7378
</Avatar>
7479
<div className="min-w-0 flex-1">
7580
<div className="flex items-baseline gap-2">
7681
<Text size="1" weight="medium" className="truncate">
77-
{userDisplayName(message.author)}
82+
{isAgent ? "Agent" : userDisplayName(message.author)}
7883
</Text>
7984
<Text size="1" className="shrink-0 text-muted-foreground">
8085
{formatRelativeTimeShort(message.created_at)}

packages/ui/src/features/canvas/freeform/canvasThreadAutoPost.test.ts

Lines changed: 0 additions & 105 deletions
This file was deleted.

packages/ui/src/features/canvas/freeform/canvasThreadAutoPost.ts

Lines changed: 0 additions & 57 deletions
This file was deleted.

packages/ui/src/features/canvas/freeform/useCanvasGenerationToasts.ts

Lines changed: 12 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,15 @@
1-
import type { PostHogAPIClient } from "@posthog/api-client/posthog-client";
21
import { useServiceOptional } from "@posthog/di/react";
3-
import { ANALYTICS_EVENTS } from "@posthog/shared/analytics-events";
4-
import type { UserBasic } from "@posthog/shared/domain-types";
5-
import { useOptionalAuthenticatedClient } from "@posthog/ui/features/auth/authClient";
62
import {
73
type CanvasTerminalStatus,
84
hasCanvasGenerationStarted,
95
isCanvasGenerating,
106
resolveCanvasGenerationStatus,
117
} from "@posthog/ui/features/canvas/freeform/canvasGenerationStatus";
12-
import { buildCanvasGenerationThreadPosts } from "@posthog/ui/features/canvas/freeform/canvasThreadAutoPost";
13-
import { taskThreadQueryKey } from "@posthog/ui/features/canvas/hooks/useTaskThread";
14-
import {
15-
type TrackedCanvasGeneration,
16-
useCanvasGenerationTrackerStore,
17-
} from "@posthog/ui/features/canvas/stores/canvasGenerationTrackerStore";
8+
import { useCanvasGenerationTrackerStore } from "@posthog/ui/features/canvas/stores/canvasGenerationTrackerStore";
189
import { NotificationBus } from "@posthog/ui/features/notifications/notifications";
1910
import { useSessionStore } from "@posthog/ui/features/sessions/sessionStore";
2011
import { taskDetailQuery } from "@posthog/ui/features/tasks/queries";
21-
import { track } from "@posthog/ui/shell/analytics";
22-
import {
23-
type QueryClient,
24-
useQueries,
25-
useQueryClient,
26-
} from "@tanstack/react-query";
12+
import { useQueries } from "@tanstack/react-query";
2713
import { useEffect, useMemo, useRef } from "react";
2814

2915
// Poll cadence for the run status of a tracked generation task. Matches the
@@ -66,38 +52,6 @@ function emitCanvasGenerationNotification(
6652
// "cancelled" is user-initiated — stay silent.
6753
}
6854

69-
// Drop the finished generation's updates into the task's thread: the one-time
70-
// "[name](link) has been created" comment and a turn-complete note tagging the
71-
// task creator. Posted by the client that started the generation (the thread
72-
// API has no agent author), best-effort per message.
73-
async function postThreadUpdates(
74-
client: PostHogAPIClient,
75-
queryClient: QueryClient,
76-
entry: TrackedCanvasGeneration,
77-
status: CanvasTerminalStatus,
78-
creator: UserBasic | null | undefined,
79-
): Promise<void> {
80-
for (const post of buildCanvasGenerationThreadPosts(entry, status, creator)) {
81-
let success = true;
82-
try {
83-
await client.createTaskThreadMessage(entry.taskId, post.content);
84-
} catch {
85-
success = false;
86-
}
87-
track(ANALYTICS_EVENTS.CHANNEL_ACTION, {
88-
action_type: "thread_auto_post",
89-
surface: "canvas",
90-
channel_id: entry.channelId,
91-
task_id: entry.taskId,
92-
auto_post_kind: post.kind,
93-
success,
94-
});
95-
}
96-
void queryClient.invalidateQueries({
97-
queryKey: taskThreadQueryKey(entry.taskId),
98-
});
99-
}
100-
10155
// Watches every canvas generation started in this client (registered in the
10256
// tracker store) and fires a toast — with a link to the canvas — the moment each
10357
// one stops generating. Mounted on the persistent channel layout so it keeps
@@ -116,10 +70,6 @@ export function useCanvasGenerationToasts(): void {
11670
const bus = useServiceOptional<NotificationBus>(NotificationBus);
11771
const busRef = useRef(bus);
11872
busRef.current = bus;
119-
const client = useOptionalAuthenticatedClient();
120-
const clientRef = useRef(client);
121-
clientRef.current = client;
122-
const queryClient = useQueryClient();
12373

12474
const taskIds = useMemo(() => Object.keys(tracked), [tracked]);
12575

@@ -146,7 +96,7 @@ export function useCanvasGenerationToasts(): void {
14696
latestRun,
14797
session,
14898
});
149-
return { id, generating, latestRun, session, task: details[i]?.data };
99+
return { id, generating, latestRun, session };
150100
});
151101

152102
// A stable signature so the transition effect only runs on real changes.
@@ -189,23 +139,15 @@ export function useCanvasGenerationToasts(): void {
189139

190140
toastedRef.current.add(st.id);
191141
const entry = useCanvasGenerationTrackerStore.getState().tracked[st.id];
192-
if (entry) {
193-
const status = resolveCanvasGenerationStatus({
194-
latestRun: st.latestRun,
195-
session: st.session,
196-
});
197-
if (busRef.current) {
198-
emitCanvasGenerationNotification(busRef.current, entry, status);
199-
}
200-
if (clientRef.current) {
201-
void postThreadUpdates(
202-
clientRef.current,
203-
queryClient,
204-
entry,
205-
status,
206-
st.task?.created_by,
207-
);
208-
}
142+
if (entry && busRef.current) {
143+
emitCanvasGenerationNotification(
144+
busRef.current,
145+
entry,
146+
resolveCanvasGenerationStatus({
147+
latestRun: st.latestRun,
148+
session: st.session,
149+
}),
150+
);
209151
}
210152
// Stop tracking (and polling) this task now that it's done.
211153
untrack(st.id);

packages/ui/src/features/canvas/hooks/useGenerateFreeformCanvas.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,9 @@ export function useGenerateFreeformCanvas(args: {
178178
await setGenerationTask(dashboardId, task.id).catch(() => {});
179179
// Track this run so a toast (with a link back here) fires when it
180180
// finishes, even after the user navigates to another canvas.
181-
useCanvasGenerationTrackerStore.getState().track({
182-
taskId: task.id,
183-
dashboardId,
184-
channelId,
185-
name,
186-
createsCanvas: !currentCode?.trim(),
187-
});
181+
useCanvasGenerationTrackerStore
182+
.getState()
183+
.track({ taskId: task.id, dashboardId, channelId, name });
188184
// Refresh the workspace cache so the new cloud workspace row appears and
189185
// the task view resolves the cloud run instead of the repo-picker prompt.
190186
void queryClient.invalidateQueries({

packages/ui/src/features/canvas/stores/canvasGenerationTrackerStore.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ export interface TrackedCanvasGeneration {
1010
dashboardId: string;
1111
channelId: string;
1212
name: string;
13-
/** True when this run builds the canvas for the first time (not an edit). */
14-
createsCanvas: boolean;
1513
}
1614

1715
interface CanvasGenerationTrackerState {

0 commit comments

Comments
 (0)