fix: dedupe stale subagent rows in reply views

This commit is contained in:
Tak Hoffman
2026-03-24 16:01:58 -05:00
parent 3031f061fc
commit 69d6e95c2a
4 changed files with 157 additions and 9 deletions

View File

@@ -0,0 +1,78 @@
import { describe, expect, it, vi } from "vitest";
const { listBySessionMock } = vi.hoisted(() => ({
listBySessionMock: vi.fn(),
}));
vi.mock("../../../infra/outbound/session-binding-service.js", () => ({
getSessionBindingService: () => ({
listBySession: listBySessionMock,
}),
}));
import { handleSubagentsAgentsAction } from "./action-agents.js";
describe("handleSubagentsAgentsAction", () => {
it("dedupes stale bound rows for the same child session", () => {
const childSessionKey = "agent:main:subagent:worker";
listBySessionMock.mockImplementation((sessionKey: string) =>
sessionKey === childSessionKey
? [
{
bindingId: "binding-1",
targetSessionKey: childSessionKey,
targetKind: "subagent",
conversation: {
channel: "discord",
accountId: "default",
conversationId: "thread-1",
},
status: "active",
boundAt: Date.now() - 20_000,
},
]
: [],
);
const result = handleSubagentsAgentsAction({
params: {
ctx: {
Provider: "discord",
Surface: "discord",
},
command: {
channel: "discord",
},
},
requesterKey: "agent:main:main",
runs: [
{
runId: "run-current",
childSessionKey,
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "current worker label",
cleanup: "keep",
createdAt: Date.now() - 10_000,
startedAt: Date.now() - 10_000,
},
{
runId: "run-stale",
childSessionKey,
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "stale worker label",
cleanup: "keep",
createdAt: Date.now() - 20_000,
startedAt: Date.now() - 20_000,
endedAt: Date.now() - 15_000,
outcome: { status: "ok" },
},
],
restTokens: [],
} as never);
expect(result.reply?.text).toContain("current worker label");
expect(result.reply?.text).not.toContain("stale worker label");
});
});

View File

@@ -46,15 +46,22 @@ export function handleSubagentsAgentsAction(ctx: SubagentsCommandContext): Comma
return resolved;
};
const visibleRuns = sortSubagentRuns(runs).filter((entry) => {
if (!entry.endedAt) {
return true;
const visibleRuns: typeof runs = [];
const seenChildSessionKeys = new Set<string>();
for (const entry of sortSubagentRuns(runs)) {
if (seenChildSessionKeys.has(entry.childSessionKey)) {
continue;
}
if (countPendingDescendantRuns(entry.childSessionKey) > 0) {
return true;
const visible =
!entry.endedAt ||
countPendingDescendantRuns(entry.childSessionKey) > 0 ||
resolveSessionBindings(entry.childSessionKey).length > 0;
if (!visible) {
continue;
}
return resolveSessionBindings(entry.childSessionKey).length > 0;
});
seenChildSessionKeys.add(entry.childSessionKey);
visibleRuns.push(entry);
}
const lines = ["agents:", "-----"];
if (visibleRuns.length === 0) {