Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,14 @@ 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 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)],
Expand All @@ -224,6 +227,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.
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/sessions/sessionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +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 &&
session.currentPromptId !== undefined;
const turnStartedAtTs =
this.liveTurnContent.get(taskRunId)?.startedAtTs ??
session.promptStartedAt;
Expand All @@ -1957,7 +1960,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",
Expand Down
Loading