perf(cron): isolate runtime-heavy seams

This commit is contained in:
Vincent Koc
2026-04-13 16:38:09 +01:00
parent df27091f5f
commit 35176f3cb7
6 changed files with 50 additions and 19 deletions

View File

@@ -9,7 +9,7 @@ import {
resolveAllowedModelRef,
resolveConfiguredModelRef,
resolveHooksGmailModel,
} from "./run.runtime.js";
} from "./run-model-selection.runtime.js";
type CronSessionModelOverrides = {
modelOverride?: string;

View File

@@ -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";

View File

@@ -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<CronJob["payload"], { kind: "agentTurn" }> | null;
type CronPromptRunResult = Awaited<ReturnType<typeof runCliAgent>>;
type CronSubagentRegistryRuntime = typeof import("./run-subagent-registry.runtime.js");
let cronSubagentRegistryRuntimePromise: Promise<CronSubagentRegistryRuntime> | 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.",

View File

@@ -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";

View File

@@ -0,0 +1,4 @@
export {
countActiveDescendantRuns,
listDescendantRunsForRequester,
} from "../../agents/subagent-registry.js";

View File

@@ -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(),
}));