Extend ephemeral active-session detection to in-review tasks
Mirror the in-progress fix for in-review: a reviewer/merger runs ephemerally with no assignedAgentId/checkedOutBy, so an in-review task in an active review/merge status (reviewing, merging, merging-fix, fixing) now reads as a live session without an assignment. A null-status in-review row is awaiting human review, not actively worked, so it stays assignment-gated and idle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -175,17 +175,20 @@ function isActiveAgentSession(task: Task | TaskDetail, opts: { sessionLive?: boo
|
||||
// In the default ephemeral-agents mode the scheduler never writes
|
||||
// `assignedAgentId`/`checkedOutBy` — those are only set when
|
||||
// `ephemeralAgentsEnabled === false` (scheduler.ts). An actively-executing
|
||||
// in-progress task therefore has no assignment field yet IS being worked, so
|
||||
// requiring `hasAssignedAgent` made the chat always show "no agent is working"
|
||||
// for default-mode tasks. Treat assignment as sufficient-but-not-necessary:
|
||||
// a non-blocked, non-`queued` in-progress task is a live session on its own.
|
||||
// `queued` is the documented waiting/blocked marker (self-healing.ts), so it
|
||||
// stays assignment-gated; in-review keeps requiring an assignment.
|
||||
const inProgressExecuting = task.column === "in-progress"
|
||||
&& statusAllowsProgressSteering
|
||||
&& task.status !== "queued";
|
||||
// task therefore has no assignment field yet IS being worked, so requiring
|
||||
// `hasAssignedAgent` made the chat always show "no agent is working" for
|
||||
// default-mode tasks. Treat assignment as sufficient-but-not-necessary:
|
||||
// - in-progress with a non-blocked, non-`queued` status is an executing run
|
||||
// (`queued` is the documented waiting marker, self-healing.ts — it stays
|
||||
// assignment-gated);
|
||||
// - in-review with an active review/merge status (REVIEW_STEERABLE_STATUSES)
|
||||
// has a reviewer/merger running. A null-status in-review row is awaiting
|
||||
// human review, not actively worked, so it stays assignment-gated and idle.
|
||||
const executionImpliesActiveAgent =
|
||||
(task.column === "in-progress" && statusAllowsProgressSteering && task.status !== "queued")
|
||||
|| (task.column === "in-review" && task.status != null && REVIEW_STEERABLE_STATUSES.has(task.status));
|
||||
return columnAllowsSteering
|
||||
&& (hasAssignedAgent || inProgressExecuting);
|
||||
&& (hasAssignedAgent || executionImpliesActiveAgent);
|
||||
}
|
||||
|
||||
function isToolLikeEntry(entry: AgentLogEntry): boolean {
|
||||
|
||||
@@ -1976,7 +1976,6 @@ describe("TaskChatTab", () => {
|
||||
["in-progress task without an assigned or checked-out agent", makeTask({ column: "in-progress", status: "queued", assignedAgentId: undefined, checkedOutBy: undefined })],
|
||||
["paused in-progress task", makeTask({ column: "in-progress", status: "queued", paused: true })],
|
||||
["user-paused in-progress task", makeTask({ column: "in-progress", status: "queued", userPaused: true })],
|
||||
["in-review task without an assigned or checked-out agent", makeTask({ column: "in-review", status: "reviewing", assignedAgentId: undefined, checkedOutBy: undefined })],
|
||||
["paused in-review task", makeTask({ column: "in-review", status: "reviewing", paused: true })],
|
||||
["user-paused in-review task", makeTask({ column: "in-review", status: "reviewing", userPaused: true })],
|
||||
])("keeps the composer sendable with idle guidance for %s", (_label, task) => {
|
||||
@@ -1986,6 +1985,39 @@ describe("TaskChatTab", () => {
|
||||
expectComposerSendableAfterDraft();
|
||||
});
|
||||
|
||||
it.each(["reviewing", "merging", "merging-fix", "fixing"])(
|
||||
"treats an actively-reviewing in-review task as an active session even without an assignment (ephemeral mode): %s status",
|
||||
(status) => {
|
||||
// A reviewer/merger runs ephemerally with no assignedAgentId/checkedOutBy,
|
||||
// so an in-review task in an active review/merge status must NOT show the
|
||||
// idle "no agent is working" hint.
|
||||
render(
|
||||
<TaskChatTab
|
||||
task={makeTask({ column: "in-review", status, assignedAgentId: undefined, checkedOutBy: undefined })}
|
||||
active
|
||||
addToast={vi.fn()}
|
||||
sessionLive={false}
|
||||
/>,
|
||||
);
|
||||
|
||||
expectActiveSessionCopy();
|
||||
expect(screen.getByLabelText("Message active agent session")).not.toBeDisabled();
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps a null-status in-review task (awaiting human review) idle without an assignment", () => {
|
||||
render(
|
||||
<TaskChatTab
|
||||
task={makeTask({ column: "in-review", status: undefined, assignedAgentId: undefined, checkedOutBy: undefined })}
|
||||
active
|
||||
addToast={vi.fn()}
|
||||
sessionLive={false}
|
||||
/>,
|
||||
);
|
||||
|
||||
expectIdleSessionHint();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["paused in-progress task with a live session", makeTask({ column: "in-progress", status: "queued", paused: true })],
|
||||
["user-paused in-progress task with a live session", makeTask({ column: "in-progress", status: "queued", userPaused: true })],
|
||||
|
||||
Reference in New Issue
Block a user