From fdad8ea3b022db11c74a9aa6350646f92a429cca Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 1 Apr 2026 06:01:19 +0900 Subject: [PATCH] fix(status): show agent-local task counts when session tasks are empty --- src/auto-reply/reply/commands-status.test.ts | 28 ++++++++++++++++++++ src/auto-reply/reply/commands-status.ts | 16 ++++++++++- src/tasks/task-registry.ts | 11 ++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/auto-reply/reply/commands-status.test.ts b/src/auto-reply/reply/commands-status.test.ts index 10e6310bac2..2838bda6509 100644 --- a/src/auto-reply/reply/commands-status.test.ts +++ b/src/auto-reply/reply/commands-status.test.ts @@ -205,4 +205,32 @@ describe("buildStatusReply subagent summary", () => { expect(reply?.text).toContain("馃搶 Tasks: 2 active 路 2 total"); expect(reply?.text).toMatch(/馃搶 Tasks: 2 active 路 2 total 路 (subagent|cron) 路 /); }); + + it("falls back to same-agent task counts without details when the current session has none", async () => { + createRunningTaskRun({ + runtime: "subagent", + requesterSessionKey: "agent:main:other", + childSessionKey: "agent:main:subagent:status-agent-fallback-running", + runId: "run-status-agent-fallback-running", + agentId: "main", + task: "hidden task title", + progressSummary: "hidden progress detail", + }); + createQueuedTaskRun({ + runtime: "cron", + requesterSessionKey: "agent:main:another", + childSessionKey: "agent:main:subagent:status-agent-fallback-queued", + runId: "run-status-agent-fallback-queued", + agentId: "main", + task: "another hidden task title", + }); + + const reply = await buildStatusReplyForTest({ sessionKey: "agent:main:empty-session" }); + + expect(reply?.text).toContain("馃搶 Tasks: 2 active 路 2 total 路 agent-local"); + expect(reply?.text).not.toContain("hidden task title"); + expect(reply?.text).not.toContain("hidden progress detail"); + expect(reply?.text).not.toContain("subagent"); + expect(reply?.text).not.toContain("cron"); + }); }); diff --git a/src/auto-reply/reply/commands-status.ts b/src/auto-reply/reply/commands-status.ts index e32823cfb21..2062b95800a 100644 --- a/src/auto-reply/reply/commands-status.ts +++ b/src/auto-reply/reply/commands-status.ts @@ -22,7 +22,7 @@ import { resolveUsageProviderId, } from "../../infra/provider-usage.js"; import type { MediaUnderstandingDecision } from "../../media-understanding/types.js"; -import { listTasksForSessionKey } from "../../tasks/task-registry.js"; +import { listTasksForAgentId, listTasksForSessionKey } from "../../tasks/task-registry.js"; import { normalizeGroupActivation } from "../group-activation.js"; import { resolveSelectedAndActiveModel } from "../model-runtime.js"; import { buildStatusMessage } from "../status.js"; @@ -74,6 +74,17 @@ function formatSessionTaskLine(sessionKey: string): string | undefined { return parts.length ? `馃搶 Tasks: ${parts.join(" 路 ")}` : undefined; } +function formatAgentTaskCountsLine(agentId: string): string | undefined { + const tasks = listTasksForAgentId(agentId); + if (tasks.length === 0) { + return undefined; + } + const active = tasks.filter( + (task) => task.status === "queued" || task.status === "running", + ).length; + return `馃搶 Tasks: ${active} active 路 ${tasks.length} total 路 agent-local`; +} + export async function buildStatusReply(params: { cfg: OpenClawConfig; command: CommandContext; @@ -209,6 +220,9 @@ export async function buildStatusReply(params: { const { mainKey, alias } = resolveMainSessionAlias(cfg); const requesterKey = resolveInternalSessionKey({ key: sessionKey, alias, mainKey }); taskLine = formatSessionTaskLine(requesterKey); + if (!taskLine) { + taskLine = formatAgentTaskCountsLine(statusAgentId); + } const runs = listControlledSubagentRuns(requesterKey); const verboseEnabled = resolvedVerboseLevel && resolvedVerboseLevel !== "off"; if (runs.length > 0) { diff --git a/src/tasks/task-registry.ts b/src/tasks/task-registry.ts index 3e30c6676e8..c3e82b443cd 100644 --- a/src/tasks/task-registry.ts +++ b/src/tasks/task-registry.ts @@ -1551,6 +1551,17 @@ export function listTasksForSessionKey(sessionKey: string): TaskRecord[] { return listTasksFromIndex(taskIdsByRelatedSessionKey, key); } +export function listTasksForAgentId(agentId: string): TaskRecord[] { + ensureTaskRegistryReady(); + const lookup = agentId.trim(); + if (!lookup) { + return []; + } + return snapshotTaskRecords(tasks) + .filter((task) => task.agentId?.trim() === lookup) + .toSorted(compareTasksNewestFirst); +} + export function findLatestTaskForOwnerKey(ownerKey: string): TaskRecord | undefined { const task = listTasksForOwnerKey(ownerKey)[0]; return task ? cloneTaskRecord(task) : undefined;