Skip to content

Commit ba4e0cc

Browse files
pdparchitectclaude
andauthored
Let finished orchestration workers detach (#639)
A session that finishes while unfocused is marked done and, with Live Agents on, kept loaded until the user looks at it. Orchestration workers cannot be focused and are not listed as live agents, so a worker that finished was never seen and stayed in memory with its whole transcript after its run ended and its lead was closed. Do not mark sessions the user cannot look at (workers and inbox discussions), and drop them if they were marked. Co-authored-by: Claude Opus 5.5 <[email protected]>
1 parent cfb79a6 commit ba4e0cc

4 files changed

Lines changed: 49 additions & 2 deletions

File tree

‎src/app/App.tsx‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,10 @@ import {
459459
TranscriptPoolOutlet,
460460
} from "../features/sessions/ui/TranscriptPool";
461461
import { syncDockBadge } from "../features/notifications/model/dockBadge";
462-
import { liveAgentsFromSessions } from "../features/sessions/model/liveAgents";
462+
import {
463+
isLiveAgentSession,
464+
liveAgentsFromSessions,
465+
} from "../features/sessions/model/liveAgents";
463466
import { hiddenApprovalNotices } from "../features/notifications/model/approvalToast";
464467
import { useSessionReminders } from "../features/notifications/hooks/useSessionReminders";
465468
import { ReminderNotices } from "../features/sessions/ui/ReminderNotices";
@@ -1715,6 +1718,11 @@ function Workspace({
17151718
busyIds: busySessionIds,
17161719
previousUnseenIds: unseenFinishedRef.current,
17171720
focusedSessionId: activeSessionId,
1721+
untrackedIds: new Set(
1722+
sessions
1723+
.filter((session) => !isLiveAgentSession(session))
1724+
.map((session) => session.id),
1725+
),
17181726
});
17191727
busyForDoneRef.current = busySessionIds;
17201728
focusedForDoneRef.current = activeSessionId;

‎src/features/sessions/model/liveAgents.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,22 @@ export type LiveAgent = {
2020
done: boolean;
2121
};
2222

23+
/** Inbox discussions and orchestration workers have their own panels and
24+
* never appear as live agents. */
25+
export function isLiveAgentSession(
26+
session: Pick<Session, "inboxAsk" | "orchestrationLeadId">,
27+
): boolean {
28+
return !session.inboxAsk && !session.orchestrationLeadId;
29+
}
30+
2331
export function liveAgentsFromSessions(
2432
sessions: Session[],
2533
unseenFinishedIds: ReadonlySet<string> = new Set(),
2634
): LiveAgent[] {
2735
return sessions
2836
.filter(
2937
(session) =>
30-
!session.inboxAsk && !session.orchestrationLeadId && (isInFlightSession(session) || unseenFinishedIds.has(session.id)),
38+
isLiveAgentSession(session) && (isInFlightSession(session) || unseenFinishedIds.has(session.id)),
3139
)
3240
.map((session) =>
3341
toLiveAgent(session, unseenFinishedIds.has(session.id)),

‎src/features/sessions/model/sessionDone.test.ts‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,29 @@ describe("nextUnseenFinishedSessions", () => {
5656
}),
5757
).toEqual(new Set(["b"]));
5858
});
59+
60+
it("never marks sessions the user cannot look at", () => {
61+
// An unseen session stays loaded until focused; a worker never is.
62+
expect(
63+
nextUnseenFinishedSessions({
64+
previousBusyIds: new Set(["lead", "worker"]),
65+
busyIds: new Set(),
66+
previousUnseenIds: new Set(),
67+
focusedSessionId: "other",
68+
untrackedIds: new Set(["worker"]),
69+
}),
70+
).toEqual(new Set(["lead"]));
71+
});
72+
73+
it("drops untracked sessions that were already marked", () => {
74+
expect(
75+
nextUnseenFinishedSessions({
76+
previousBusyIds: new Set(),
77+
busyIds: new Set(),
78+
previousUnseenIds: new Set(["lead", "worker"]),
79+
focusedSessionId: "other",
80+
untrackedIds: new Set(["worker"]),
81+
}),
82+
).toEqual(new Set(["lead"]));
83+
});
5984
});

‎src/features/sessions/model/sessionDone.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@ export function nextUnseenFinishedSessions({
55
busyIds,
66
previousUnseenIds,
77
focusedSessionId,
8+
untrackedIds,
89
}: {
910
previousBusyIds: ReadonlySet<string>;
1011
busyIds: ReadonlySet<string>;
1112
previousUnseenIds: ReadonlySet<string>;
1213
focusedSessionId?: string;
14+
/** Sessions the user cannot focus or see as done, such as orchestration
15+
* workers and inbox discussions. An unseen session stays loaded until it
16+
* is looked at, so marking one of these would keep it in memory for good. */
17+
untrackedIds?: ReadonlySet<string>;
1318
}): Set<string> {
1419
const next = new Set(previousUnseenIds);
1520
for (const id of previousBusyIds) {
1621
if (!busyIds.has(id) && id !== focusedSessionId) next.add(id);
1722
}
1823
for (const id of busyIds) next.delete(id);
1924
if (focusedSessionId) next.delete(focusedSessionId);
25+
if (untrackedIds) for (const id of untrackedIds) next.delete(id);
2026
return next;
2127
}

0 commit comments

Comments
 (0)