From 7412743cd15ec0e98ec4c6e8f6deaec4a3430d81 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Mon, 20 Jul 2026 09:14:22 +0300 Subject: [PATCH 1/3] fix(sessions): dedupe cloud completion notifications Generated-By: PostHog Code Task-Id: 456e7b37-67a4-4163-a9c9-749c5ff36cbe --- .../sessions/cloudTaskUpdateNotifications.test.ts | 14 ++++++++++---- packages/core/src/sessions/sessionService.ts | 3 ++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts b/packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts index 4fd5c1bc70..340a013e13 100644 --- a/packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts +++ b/packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts @@ -206,10 +206,8 @@ describe("cloud task update notifications", () => { }); // Each case applies a sequence of updates to a fresh harness; `expected` is - // the resulting notify count. Snapshots never ring; each live turn_complete - // rings once. Re-delivered stream entries are dropped upstream in - // CloudTaskService by their event id (see cloud-task.test.ts), so a replay - // never reaches this layer. + // the resulting notify count. Snapshots never ring; each armed turn rings at + // most once even if the producer writes duplicate completion entries. it.each([ { label: "a live turn that starts and completes", @@ -224,6 +222,14 @@ describe("cloud task update notifications", () => { ], expected: 2, }, + { + label: "duplicate completion events for one turn", + updates: [ + logsUpdate([sessionPrompt(1), turnComplete()], 2), + logsUpdate([turnComplete()], 3), + ], + expected: 1, + }, { // Opening a task mid-turn: its session/prompt is already in history and // only the turn_complete arrives live. The completion must still ring. diff --git a/packages/core/src/sessions/sessionService.ts b/packages/core/src/sessions/sessionService.ts index 096826e4fb..331d483367 100644 --- a/packages/core/src/sessions/sessionService.ts +++ b/packages/core/src/sessions/sessionService.ts @@ -1947,6 +1947,7 @@ export class SessionService { // above. Cloud sessions never see that response. const session = this.getSessionByRunId(taskRunId); if (session?.isCloud) { + const completedActiveTurn = session.currentPromptId !== null; const turnStartedAtTs = this.liveTurnContent.get(taskRunId)?.startedAtTs ?? session.promptStartedAt; @@ -1957,7 +1958,7 @@ export class SessionService { }); if (isLive) { // Queued messages will start a new turn — suppress the "done" notification in that case. - if (session.messageQueue.length === 0) { + if (completedActiveTurn && session.messageQueue.length === 0) { this.d.notifyPromptComplete( session.taskTitle, "end_turn", From db36542edc325e62e2857f4b9a07c8f0350d19c2 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Mon, 20 Jul 2026 09:23:15 +0300 Subject: [PATCH 2/3] fix(sessions): ignore unarmed cloud completions Generated-By: PostHog Code Task-Id: 456e7b37-67a4-4163-a9c9-749c5ff36cbe --- .../core/src/sessions/cloudTaskUpdateNotifications.test.ts | 5 +++++ packages/core/src/sessions/sessionService.ts | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts b/packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts index 340a013e13..5ad145a0c0 100644 --- a/packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts +++ b/packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts @@ -209,6 +209,11 @@ describe("cloud task update notifications", () => { // the resulting notify count. Snapshots never ring; each armed turn rings at // most once even if the producer writes duplicate completion entries. it.each([ + { + label: "a completion event without an armed turn", + updates: [logsUpdate([turnComplete()], 1)], + expected: 0, + }, { label: "a live turn that starts and completes", updates: [logsUpdate([sessionPrompt(1), turnComplete()], 2)], diff --git a/packages/core/src/sessions/sessionService.ts b/packages/core/src/sessions/sessionService.ts index 331d483367..3846d2164f 100644 --- a/packages/core/src/sessions/sessionService.ts +++ b/packages/core/src/sessions/sessionService.ts @@ -1947,7 +1947,9 @@ export class SessionService { // above. Cloud sessions never see that response. const session = this.getSessionByRunId(taskRunId); if (session?.isCloud) { - const completedActiveTurn = session.currentPromptId !== null; + const completedActiveTurn = + session.currentPromptId !== null && + session.currentPromptId !== undefined; const turnStartedAtTs = this.liveTurnContent.get(taskRunId)?.startedAtTs ?? session.promptStartedAt; From 84fcbab1a701f54d8b27c0fca0ef2e620e48f559 Mon Sep 17 00:00:00 2001 From: "posthog[bot]" <206114724+posthog[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 11:59:10 +0000 Subject: [PATCH 3/3] chore(visual): update storybook baselines 1 updated Run: ad8db4b1-d88d-42ea-a362-ce200a22839c Co-authored-by: richardsolomou <2622273+richardsolomou@users.noreply.github.com> --- apps/code/snapshots.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/code/snapshots.yml b/apps/code/snapshots.yml index cfbdd290b1..26cb8c4587 100644 --- a/apps/code/snapshots.yml +++ b/apps/code/snapshots.yml @@ -27,7 +27,7 @@ snapshots: archive-archivedtasksview--many-tasks--dark: hash: v1.k4693efd2.40d24f07a24c6400c32ae68b39fd908a056beb5dc6df8bcb237ec2ab2b494f4a.l2vjfSQDalCPRtDNV7pbfTKGlMsQoI81jI-UdJfLHWE archive-archivedtasksview--many-tasks--light: - hash: v1.k4693efd2.52ec9963bfe6f248ffcdaa4c26945d2b7e303115825b65d978aa5aefd4af1007.ZLtOfTZUrznhYeB-N2SIHzQWRzF6TDSaipNkfd8uzjM + hash: v1.k4693efd2.74c25b303262f2d4c0f22890d351e76f482827548548f7c023bc309327c927b8.DP9VVrvBz1naIJMwHORkfqm69BlO785ulOt4o6zFs94 archive-archivedtasksview--mixed-modes--dark: hash: v1.k4693efd2.d94039b8cc17a4ad1b720364f41fee58a4843aa9a901443997ba62602f698794.441LWZhT-2WQZJHni4LoOgQsOSr2O103NZOk1pF8ME4 archive-archivedtasksview--mixed-modes--light: