From 35176f3cb73096f0262166ad99f2b55874e07f46 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 13 Apr 2026 16:38:09 +0100 Subject: [PATCH] perf(cron): isolate runtime-heavy seams --- src/cron/isolated-agent/model-selection.ts | 2 +- .../isolated-agent/run-execution.runtime.ts | 4 --- src/cron/isolated-agent/run-executor.ts | 27 ++++++++++++++----- .../run-model-selection.runtime.ts | 9 +++++++ .../run-subagent-registry.runtime.ts | 4 +++ src/cron/isolated-agent/run.test-harness.ts | 23 +++++++++++----- 6 files changed, 50 insertions(+), 19 deletions(-) create mode 100644 src/cron/isolated-agent/run-model-selection.runtime.ts create mode 100644 src/cron/isolated-agent/run-subagent-registry.runtime.ts diff --git a/src/cron/isolated-agent/model-selection.ts b/src/cron/isolated-agent/model-selection.ts index b2642c2ab1d..73e0a5faef5 100644 --- a/src/cron/isolated-agent/model-selection.ts +++ b/src/cron/isolated-agent/model-selection.ts @@ -9,7 +9,7 @@ import { resolveAllowedModelRef, resolveConfiguredModelRef, resolveHooksGmailModel, -} from "./run.runtime.js"; +} from "./run-model-selection.runtime.js"; type CronSessionModelOverrides = { modelOverride?: string; diff --git a/src/cron/isolated-agent/run-execution.runtime.ts b/src/cron/isolated-agent/run-execution.runtime.ts index 38e6ac90c1e..e8f73a5fc25 100644 --- a/src/cron/isolated-agent/run-execution.runtime.ts +++ b/src/cron/isolated-agent/run-execution.runtime.ts @@ -6,10 +6,6 @@ export { LiveSessionModelSwitchError } from "../../agents/live-model-switch.js"; export { runWithModelFallback } from "../../agents/model-fallback.js"; export { isCliProvider } from "../../agents/model-selection.js"; export { runEmbeddedPiAgent } from "../../agents/pi-embedded.js"; -export { - countActiveDescendantRuns, - listDescendantRunsForRequester, -} from "../../agents/subagent-registry.js"; export { normalizeVerboseLevel } from "../../auto-reply/thinking.js"; export { resolveSessionTranscriptPath } from "../../config/sessions/paths.js"; export { registerAgentRunContext } from "../../infra/agent-events.js"; diff --git a/src/cron/isolated-agent/run-executor.ts b/src/cron/isolated-agent/run-executor.ts index 14ecf3f7eb1..a3e298726d8 100644 --- a/src/cron/isolated-agent/run-executor.ts +++ b/src/cron/isolated-agent/run-executor.ts @@ -5,10 +5,8 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { CronJob } from "../types.js"; import { resolveCronPayloadOutcome } from "./helpers.js"; import { - countActiveDescendantRuns, getCliSessionId, isCliProvider, - listDescendantRunsForRequester, LiveSessionModelSwitchError, logWarn, normalizeVerboseLevel, @@ -32,6 +30,14 @@ import { isLikelyInterimCronMessage } from "./subagent-followup-hints.js"; type AgentTurnPayload = Extract | null; type CronPromptRunResult = Awaited>; +type CronSubagentRegistryRuntime = typeof import("./run-subagent-registry.runtime.js"); + +let cronSubagentRegistryRuntimePromise: Promise | undefined; + +async function loadCronSubagentRegistryRuntime() { + cronSubagentRegistryRuntimePromise ??= import("./run-subagent-registry.runtime.js"); + return await cronSubagentRegistryRuntimePromise; +} export type CronExecutionResult = { runResult: CronPromptRunResult; @@ -321,15 +327,22 @@ export async function executeCronRun(params: { !runResult.didSendViaMessagingTool && !interimPayloadHasStructuredContent && !interimPayloads.some((payload) => payload?.isError === true) && - !listDescendantRunsForRequester(params.agentSessionKey).some((entry) => { + isLikelyInterimCronMessage(interimText); + + let hasFreshDescendants = false; + let hasActiveDescendants = false; + if (shouldRetryInterimAck) { + const { countActiveDescendantRuns, listDescendantRunsForRequester } = + await loadCronSubagentRegistryRuntime(); + hasFreshDescendants = listDescendantRunsForRequester(params.agentSessionKey).some((entry) => { const descendantStartedAt = typeof entry.startedAt === "number" ? entry.startedAt : entry.createdAt; return typeof descendantStartedAt === "number" && descendantStartedAt >= runStartedAt; - }) && - countActiveDescendantRuns(params.agentSessionKey) === 0 && - isLikelyInterimCronMessage(interimText); + }); + hasActiveDescendants = countActiveDescendantRuns(params.agentSessionKey) > 0; + } - if (shouldRetryInterimAck) { + if (shouldRetryInterimAck && !hasFreshDescendants && !hasActiveDescendants) { const continuationPrompt = [ "Your previous response was only an acknowledgement and did not complete this cron task.", "Complete the original task now.", diff --git a/src/cron/isolated-agent/run-model-selection.runtime.ts b/src/cron/isolated-agent/run-model-selection.runtime.ts new file mode 100644 index 00000000000..f982f9abead --- /dev/null +++ b/src/cron/isolated-agent/run-model-selection.runtime.ts @@ -0,0 +1,9 @@ +export { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../../agents/defaults.js"; +export { loadModelCatalog } from "../../agents/model-catalog.js"; +export { + getModelRefStatus, + normalizeModelSelection, + resolveAllowedModelRef, + resolveConfiguredModelRef, + resolveHooksGmailModel, +} from "../../agents/model-selection.js"; diff --git a/src/cron/isolated-agent/run-subagent-registry.runtime.ts b/src/cron/isolated-agent/run-subagent-registry.runtime.ts new file mode 100644 index 00000000000..98f01bb64e9 --- /dev/null +++ b/src/cron/isolated-agent/run-subagent-registry.runtime.ts @@ -0,0 +1,4 @@ +export { + countActiveDescendantRuns, + listDescendantRunsForRequester, +} from "../../agents/subagent-registry.js"; diff --git a/src/cron/isolated-agent/run.test-harness.ts b/src/cron/isolated-agent/run.test-harness.ts index 43baab862f9..61830c09cf5 100644 --- a/src/cron/isolated-agent/run.test-harness.ts +++ b/src/cron/isolated-agent/run.test-harness.ts @@ -101,15 +101,8 @@ vi.mock("./run.runtime.js", () => ({ lookupContextTokens: lookupContextTokensMock, resolveCronStyleNow: resolveCronStyleNowMock, DEFAULT_CONTEXT_TOKENS: 128000, - DEFAULT_MODEL: "gpt-4", - DEFAULT_PROVIDER: "openai", loadModelCatalog: loadModelCatalogMock, - getModelRefStatus: getModelRefStatusMock, isCliProvider: isCliProviderMock, - normalizeModelSelection: normalizeModelSelectionForTest, - resolveAllowedModelRef: resolveAllowedModelRefMock, - resolveConfiguredModelRef: resolveConfiguredModelRefMock, - resolveHooksGmailModel: resolveHooksGmailModelMock, resolveThinkingDefault: resolveThinkingDefaultMock, buildWorkspaceSkillSnapshot: buildWorkspaceSkillSnapshotMock, getSkillsSnapshotVersion: getSkillsSnapshotVersionMock, @@ -133,6 +126,17 @@ vi.mock("./run.runtime.js", () => ({ getRemoteSkillEligibility: getRemoteSkillEligibilityMock, })); +vi.mock("./run-model-selection.runtime.js", () => ({ + DEFAULT_MODEL: "gpt-4", + DEFAULT_PROVIDER: "openai", + loadModelCatalog: loadModelCatalogMock, + getModelRefStatus: getModelRefStatusMock, + normalizeModelSelection: normalizeModelSelectionForTest, + resolveAllowedModelRef: resolveAllowedModelRefMock, + resolveConfiguredModelRef: resolveConfiguredModelRefMock, + resolveHooksGmailModel: resolveHooksGmailModelMock, +})); + vi.mock("./run-execution.runtime.js", () => ({ resolveEffectiveModelFallbacks: resolveEffectiveModelFallbacksMock, resolveBootstrapWarningSignaturesSeen: resolveBootstrapWarningSignaturesSeenMock, @@ -156,6 +160,11 @@ vi.mock("./run-auth-profile.runtime.js", () => ({ resolveSessionAuthProfileOverride: resolveSessionAuthProfileOverrideMock, })); +vi.mock("./run-subagent-registry.runtime.js", () => ({ + countActiveDescendantRuns: countActiveDescendantRunsMock, + listDescendantRunsForRequester: listDescendantRunsForRequesterMock, +})); + vi.mock("../../agents/cli-runner.runtime.js", () => ({ setCliSessionId: vi.fn(), }));