Skip to content

Commit 021a701

Browse files
authored
fix(canvas): deduplicate forwarded thread prompts
Generated-By: PostHog Code Task-Id: 87a79e23-20c4-440b-818b-28154924ffbc
1 parent 83fc1f9 commit 021a701

3 files changed

Lines changed: 57 additions & 1 deletion

File tree

packages/core/src/canvas/threadTimeline.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,45 @@ describe("normalizeAgentPromptText", () => {
3838
});
3939

4040
describe("buildThreadTimeline", () => {
41+
it("omits the session echo of a forwarded thread message", () => {
42+
const timeline = buildThreadTimeline({
43+
prompts: [
44+
{
45+
id: "prompt",
46+
text: "[Thread comment from Peter Kirkham] @agent which model are you?",
47+
timestamp: 200,
48+
},
49+
],
50+
humanMessages: [
51+
{
52+
id: "human",
53+
content: "@agent which model are you?",
54+
createdAt: "1970-01-01T00:00:00.100Z",
55+
forwardedToAgent: true,
56+
},
57+
],
58+
agentMessages: [],
59+
});
60+
61+
expect(timeline.map((row) => row.kind)).toEqual(["human"]);
62+
});
63+
64+
it("keeps a thread-comment prompt without a matching forwarded message", () => {
65+
const timeline = buildThreadTimeline({
66+
prompts: [
67+
{
68+
id: "prompt",
69+
text: "[Thread comment from Peter Kirkham] @agent which model are you?",
70+
timestamp: 200,
71+
},
72+
],
73+
humanMessages: [],
74+
agentMessages: [],
75+
});
76+
77+
expect(timeline.map((row) => row.kind)).toEqual(["prompt"]);
78+
});
79+
4180
it("interleaves prompts, human replies, and agent turns chronologically", () => {
4281
const timeline = buildThreadTimeline({
4382
prompts: [{ id: "prompt", text: "Start", timestamp: 100 }],

packages/core/src/canvas/threadTimeline.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface ThreadHumanMessage<T = unknown> {
88
id: string;
99
content: string;
1010
createdAt: string;
11+
forwardedToAgent?: boolean;
1112
value?: T;
1213
}
1314

@@ -36,8 +37,19 @@ export function buildThreadTimeline<T>({
3637
agentMessages: ThreadAgentMessage[];
3738
humanMessages: ThreadHumanMessage<T>[];
3839
}): ThreadTimelineRow<T>[] {
40+
const forwardedHumanContent = new Set(
41+
humanMessages
42+
.filter((message) => message.forwardedToAgent)
43+
.map((message) => normalizeAgentPromptText(message.content)),
44+
);
45+
const visiblePrompts = prompts.filter(
46+
(message) =>
47+
!isThreadCommentPrompt(message.text) ||
48+
!forwardedHumanContent.has(normalizeAgentPromptText(message.text)),
49+
);
50+
3951
return [
40-
...prompts.map(
52+
...visiblePrompts.map(
4153
(message): ThreadTimelineRow<T> => ({
4254
kind: "prompt",
4355
timestamp: validTimestamp(message.timestamp),
@@ -85,6 +97,10 @@ export function normalizeAgentPromptText(content: string): string {
8597
.trim();
8698
}
8799

100+
function isThreadCommentPrompt(content: string): boolean {
101+
return THREAD_COMMENT_ATTRIBUTION_PATTERN.test(content.trim());
102+
}
103+
88104
export function deriveThreadAgentStatus({
89105
hasActivity = false,
90106
hasError = false,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ function ThreadConversation({
415415
id: message.id,
416416
content: message.content,
417417
createdAt: message.created_at,
418+
forwardedToAgent: !!message.forwarded_to_agent_at,
418419
value: message,
419420
})),
420421
}),

0 commit comments

Comments
 (0)