refactor(agents): centralize run wait helpers

This commit is contained in:
Peter Steinberger
2026-04-04 20:22:03 +09:00
parent 42778ccd46
commit 3dda75894b
9 changed files with 290 additions and 224 deletions

View File

@@ -15,9 +15,9 @@ vi.mock("../../agents/subagent-registry-read.js", () => ({
listDescendantRunsForRequester: vi.fn().mockReturnValue([]),
}));
vi.mock("../../agents/tools/agent-step.js", async () => {
const actual = await vi.importActual<typeof import("../../agents/tools/agent-step.js")>(
"../../agents/tools/agent-step.js",
vi.mock("../../agents/run-wait.js", async () => {
const actual = await vi.importActual<typeof import("../../agents/run-wait.js")>(
"../../agents/run-wait.js",
);
return {
...actual,
@@ -30,7 +30,8 @@ vi.mock("../../gateway/call.js", () => ({
}));
const { listDescendantRunsForRequester } = await import("../../agents/subagent-registry-read.js");
const { readLatestAssistantReply } = await import("../../agents/tools/agent-step.js");
const { __testing: runWaitTesting, readLatestAssistantReply } =
await import("../../agents/run-wait.js");
const { callGateway } = await import("../../gateway/call.js");
async function resolveAfterAdvancingTimers<T>(promise: Promise<T>, advanceMs = 100): Promise<T> {
@@ -237,10 +238,14 @@ describe("waitForDescendantSubagentSummary", () => {
vi.mocked(listDescendantRunsForRequester).mockReturnValue([]);
vi.mocked(readLatestAssistantReply).mockResolvedValue(undefined);
vi.mocked(callGateway).mockResolvedValue({ status: "ok" });
runWaitTesting.setDepsForTest({
callGateway: ((opts) => vi.mocked(callGateway)(opts as never)) as typeof callGateway,
});
});
afterEach(() => {
vi.useRealTimers();
runWaitTesting.setDepsForTest();
});
it("returns initialReply immediately when no active descendants and observedActiveDescendants=false", async () => {

View File

@@ -1,8 +1,5 @@
import { readLatestAssistantReply, waitForAgentRunsToDrain } from "../../agents/run-wait.js";
import { listDescendantRunsForRequester } from "../../agents/subagent-registry-read.js";
import {
readLatestAssistantReply,
waitForAgentRunsUntilQuiescent,
} from "../../agents/tools/agent-step.js";
import { SILENT_REPLY_TOKEN } from "../../auto-reply/tokens.js";
import { expectsSubagentFollowup, isLikelyInterimCronMessage } from "./subagent-followup-hints.js";
export { expectsSubagentFollowup, isLikelyInterimCronMessage } from "./subagent-followup-hints.js";
@@ -100,11 +97,11 @@ export async function waitForDescendantSubagentSummary(params: {
return initialReply;
}
// --- Push-based wait for all active descendants ---
// We iterate in case first-level descendants spawn their own subagents while
// we wait, so new active runs can appear between rounds.
await waitForAgentRunsUntilQuiescent({
// Wait until no descendant runs remain active. Descendants can finish and
// spawn more descendants, so the helper refreshes the run set until it drains.
await waitForAgentRunsToDrain({
deadlineAtMs: deadline,
initialPendingRunIds: initialActiveRuns.map((entry) => entry.runId),
getPendingRunIds: () => getActiveRuns().map((entry) => entry.runId),
});