Fix task chat always showing "no agent is working" in ephemeral mode

isActiveAgentSession required a persistent assignedAgentId/checkedOutBy,
but the scheduler only sets those when ephemeralAgentsEnabled === false
(scheduler.ts:1857). The default is ephemeralAgentsEnabled: true
(settings-schema.ts:351), so an actively-executing in-progress task
never has either field and always read as idle, showing the
"No agent is working on this task right now" hint while an agent was
running. sessionLive only covers live CLI sessions, not ephemeral runs.

Treat assignment as sufficient-but-not-necessary: a non-blocked,
non-"queued" in-progress task is a live agent session on its own.
"queued" (the waiting marker) stays assignment-gated and in-review is
unchanged, so existing FN-6314 idle/active behavior is preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-06-20 19:48:09 -07:00
parent 2d327604ff
commit a2342cadae
3 changed files with 52 additions and 1 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Fix the task detail chat always showing "No agent is working on this task" for in-progress tasks. The active-session check required a persistent `assignedAgentId`/`checkedOutBy`, but in the default ephemeral-agents mode the scheduler never sets those fields, so an actively-executing task always read as idle. An assignment is now sufficient-but-not-necessary: a non-blocked, non-`queued` in-progress task counts as a live agent session on its own (`queued` stays assignment-gated, in-review is unchanged).

View File

@@ -172,8 +172,20 @@ function isActiveAgentSession(task: Task | TaskDetail, opts: { sessionLive?: boo
const statusAllowsReviewSteering = !task.status || REVIEW_STEERABLE_STATUSES.has(task.status); const statusAllowsReviewSteering = !task.status || REVIEW_STEERABLE_STATUSES.has(task.status);
const columnAllowsSteering = (task.column === "in-progress" && statusAllowsProgressSteering) const columnAllowsSteering = (task.column === "in-progress" && statusAllowsProgressSteering)
|| (task.column === "in-review" && statusAllowsReviewSteering); || (task.column === "in-review" && statusAllowsReviewSteering);
// 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";
return columnAllowsSteering return columnAllowsSteering
&& hasAssignedAgent; && (hasAssignedAgent || inProgressExecuting);
} }
function isToolLikeEntry(entry: AgentLogEntry): boolean { function isToolLikeEntry(entry: AgentLogEntry): boolean {

View File

@@ -1802,6 +1802,40 @@ describe("TaskChatTab", () => {
expectComposerSendableAfterDraft(); expectComposerSendableAfterDraft();
}); });
it.each([undefined, "planning", "merging", "merging-fix"])(
"treats an actively-executing in-progress task as an active session even without an assignment (ephemeral mode): %s status",
(status) => {
// In the default ephemeral-agents mode the scheduler never writes
// assignedAgentId/checkedOutBy, so a running in-progress task has no
// assignment field yet IS being worked. It must NOT show the idle
// "no agent is working" hint.
render(
<TaskChatTab
task={makeTask({ column: "in-progress", status, assignedAgentId: undefined, checkedOutBy: undefined })}
active
addToast={vi.fn()}
sessionLive={false}
/>,
);
expectActiveSessionCopy();
expect(screen.getByLabelText("Message active agent session")).not.toBeDisabled();
},
);
it("keeps a queued (waiting) unassigned in-progress task idle in ephemeral mode", () => {
render(
<TaskChatTab
task={makeTask({ column: "in-progress", status: "queued", assignedAgentId: undefined, checkedOutBy: undefined })}
active
addToast={vi.fn()}
sessionLive={false}
/>,
);
expectIdleSessionHint();
});
it.each(["busy", "ready", "starting", "waitingOnInput"] as const)("treats %s CLI sessions as live", (agentState) => { it.each(["busy", "ready", "starting", "waitingOnInput"] as const)("treats %s CLI sessions as live", (agentState) => {
expect(isCliSessionLive(makeCliSession(agentState))).toBe(true); expect(isCliSessionLive(makeCliSession(agentState))).toBe(true);
}); });