refactor: trim cron session startup imports

This commit is contained in:
Shakker 2026-04-01 16:20:58 +01:00 committed by Shakker
parent 88b1c00b39
commit fc8ab82aab
11 changed files with 319 additions and 152 deletions

View File

@ -0,0 +1,22 @@
export type LiveSessionModelSelection = {
provider: string;
model: string;
authProfileId?: string;
authProfileIdSource?: "auto" | "user";
};
export class LiveSessionModelSwitchError extends Error {
provider: string;
model: string;
authProfileId?: string;
authProfileIdSource?: "auto" | "user";
constructor(selection: LiveSessionModelSelection) {
super(`Live session model switch requested: ${selection.provider}/${selection.model}`);
this.name = "LiveSessionModelSwitchError";
this.provider = selection.provider;
this.model = selection.model;
this.authProfileId = selection.authProfileId;
this.authProfileIdSource = selection.authProfileIdSource;
}
}

View File

@ -1,4 +1,7 @@
import { loadSessionStore, resolveStorePath, type SessionEntry } from "../config/sessions.js";
import { resolveStorePath } from "../config/sessions/paths.js";
import { loadSessionStore } from "../config/sessions/store.js";
import type { SessionEntry } from "../config/sessions/types.js";
import { LiveSessionModelSwitchError } from "./live-model-switch-error.js";
import { resolveDefaultModelForAgent, resolvePersistedModelRef } from "./model-selection.js";
import {
consumeEmbeddedRunModelSwitch,
@ -6,25 +9,9 @@ import {
type EmbeddedRunModelSwitchRequest,
} from "./pi-embedded-runner/runs.js";
import { abortEmbeddedPiRun } from "./pi-embedded.js";
export { LiveSessionModelSwitchError } from "./live-model-switch-error.js";
export type LiveSessionModelSelection = EmbeddedRunModelSwitchRequest;
export class LiveSessionModelSwitchError extends Error {
provider: string;
model: string;
authProfileId?: string;
authProfileIdSource?: "auto" | "user";
constructor(selection: LiveSessionModelSelection) {
super(`Live session model switch requested: ${selection.provider}/${selection.model}`);
this.name = "LiveSessionModelSwitchError";
this.provider = selection.provider;
this.model = selection.model;
this.authProfileId = selection.authProfileId;
this.authProfileIdSource = selection.authProfileIdSource;
}
}
export function resolveLiveSessionModelSelection(params: {
cfg?: { session?: { store?: string } } | undefined;
sessionKey?: string;

View File

@ -9,7 +9,7 @@ import type { AuthProfileStore } from "./auth-profiles.js";
import { saveAuthProfileStore } from "./auth-profiles.js";
import { AUTH_STORE_VERSION } from "./auth-profiles/constants.js";
import { isAnthropicBillingError } from "./live-auth-keys.js";
import { LiveSessionModelSwitchError } from "./live-model-switch.js";
import { LiveSessionModelSwitchError } from "./live-model-switch-error.js";
import { runWithImageModelFallback, runWithModelFallback } from "./model-fallback.js";
import { makeModelFallbackCfg } from "./test-helpers/model-fallback-config-fixture.js";

View File

@ -26,7 +26,7 @@ import {
shouldPreserveTransientCooldownProbeSlot,
shouldUseTransientCooldownProbeSlot,
} from "./failover-policy.js";
import { LiveSessionModelSwitchError } from "./live-model-switch.js";
import { LiveSessionModelSwitchError } from "./live-model-switch-error.js";
import { logModelFallbackDecision } from "./model-fallback-observation.js";
import type { FallbackAttempt, ModelCandidate } from "./model-fallback.types.js";
import {

View File

@ -25,9 +25,9 @@ import {
FailoverError,
resolveFailoverStatus,
} from "../failover-error.js";
import { LiveSessionModelSwitchError } from "../live-model-switch-error.js";
import {
hasDifferentLiveSessionModelSelection,
LiveSessionModelSwitchError,
consumeLiveSessionModelSwitch,
} from "../live-model-switch.js";
import {

View File

@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch.js";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch-error.js";
import type { TemplateContext } from "../templating.js";
import type { GetReplyOptions } from "../types.js";
import { MAX_LIVE_SWITCH_RETRIES } from "./agent-runner-execution.js";

View File

@ -7,7 +7,7 @@ import {
import { resolveBootstrapWarningSignaturesSeen } from "../../agents/bootstrap-budget.js";
import { runCliAgent } from "../../agents/cli-runner.js";
import { getCliSessionBinding } from "../../agents/cli-session.js";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch.js";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch-error.js";
import { runWithModelFallback, isFallbackSummaryError } from "../../agents/model-fallback.js";
import { isCliProvider } from "../../agents/model-selection.js";
import {

View File

@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch.js";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch-error.js";
import {
clearFastTestEnv,
loadRunCronIsolatedAgentTurn,

View File

@ -8,8 +8,11 @@ import {
buildWorkspaceSkillSnapshotMock,
getCliSessionIdMock,
isCliProviderMock,
lookupCachedContextTokensMock,
loadRunCronIsolatedAgentTurn,
logWarnMock,
makeCronSession,
makeCronSessionEntry,
resolveAgentConfigMock,
resolveAgentSkillsFilterMock,
resolveAllowedModelRefMock,
@ -320,4 +323,36 @@ describe("runCronIsolatedAgentTurn — skill filter", () => {
);
});
});
describe("context token fallback", () => {
it("preserves existing session contextTokens when no cached model window is loaded", async () => {
const session = makeCronSession({
sessionEntry: makeCronSessionEntry({
contextTokens: 222_000,
}),
});
resolveCronSessionMock.mockReturnValue(session);
lookupCachedContextTokensMock.mockReturnValue(undefined);
const result = await runSkillFilterCase();
expect(result.status).toBe("ok");
expect(session.sessionEntry.contextTokens).toBe(222_000);
});
it("prefers cached model contextTokens over the previous session value", async () => {
const session = makeCronSession({
sessionEntry: makeCronSessionEntry({
contextTokens: 222_000,
}),
});
resolveCronSessionMock.mockReturnValue(session);
lookupCachedContextTokensMock.mockReturnValue(512_000);
const result = await runSkillFilterCase();
expect(result.status).toBe("ok");
expect(session.sessionEntry.contextTokens).toBe(512_000);
});
});
});

View File

@ -1,5 +1,4 @@
import { vi, type Mock } from "vitest";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch.js";
type CronSessionEntry = {
sessionId: string;
@ -24,6 +23,18 @@ function createMock(): Mock {
return vi.fn();
}
function normalizeModelSelectionForTest(value: unknown): string | undefined {
if (typeof value === "string") {
const trimmed = value.trim();
return trimmed || undefined;
}
if (!value || typeof value !== "object") {
return undefined;
}
const primary = (value as { primary?: unknown }).primary;
return typeof primary === "string" && primary.trim() ? primary.trim() : undefined;
}
export const buildWorkspaceSkillSnapshotMock = createMock();
export const resolveAgentConfigMock = createMock();
export const resolveAgentModelFallbacksOverrideMock = createMock();
@ -37,6 +48,7 @@ export const resolveThinkingDefaultMock = createMock();
export const runWithModelFallbackMock = createMock();
export const runEmbeddedPiAgentMock = createMock();
export const runCliAgentMock = createMock();
export const lookupCachedContextTokensMock = createMock();
export const getCliSessionIdMock = createMock();
export const updateSessionStoreMock = createMock();
export const resolveCronSessionMock = createMock();
@ -48,39 +60,94 @@ export const resolveCronPayloadOutcomeMock = createMock();
export const resolveCronDeliveryPlanMock = createMock();
export const resolveDeliveryTargetMock = createMock();
export const resolveSessionAuthProfileOverrideMock = createMock();
const resolveAgentDirMock = vi.fn().mockReturnValue("/tmp/agent-dir");
const resolveAgentWorkspaceDirMock = vi.fn().mockReturnValue("/tmp/workspace");
const resolveDefaultAgentIdMock = vi.fn().mockReturnValue("default");
const getSkillsSnapshotVersionMock = vi.fn().mockReturnValue(42);
const ensureAgentWorkspaceMock = vi.fn().mockResolvedValue({ dir: "/tmp/workspace" });
const loadModelCatalogMock = vi.fn().mockResolvedValue({ models: [] });
const normalizeModelSelectionMock = vi.fn((value: unknown) =>
typeof value === "string" ? value.trim() || undefined : undefined,
);
const lookupContextTokensMock = vi.fn().mockReturnValue(128000);
const resolveCronStyleNowMock = vi.fn().mockReturnValue({
formattedTime: "2026-02-10 12:00",
timeLine: "Current time: 2026-02-10 12:00 UTC",
vi.mock("../../agents/agent-scope.js", () => ({
resolveAgentConfig: resolveAgentConfigMock,
resolveAgentDir: vi.fn().mockReturnValue("/tmp/agent-dir"),
resolveAgentModelFallbacksOverride: resolveAgentModelFallbacksOverrideMock,
resolveAgentWorkspaceDir: vi.fn().mockReturnValue("/tmp/workspace"),
resolveDefaultAgentId: vi.fn().mockReturnValue("default"),
resolveAgentSkillsFilter: resolveAgentSkillsFilterMock,
}));
vi.mock("../../agents/skills.js", () => ({
buildWorkspaceSkillSnapshot: buildWorkspaceSkillSnapshotMock,
}));
vi.mock("../../agents/skills/refresh.js", () => ({
getSkillsSnapshotVersion: vi.fn().mockReturnValue(42),
}));
vi.mock("../../agents/workspace.js", () => ({
DEFAULT_IDENTITY_FILENAME: "IDENTITY.md",
ensureAgentWorkspace: vi.fn().mockResolvedValue({ dir: "/tmp/workspace" }),
}));
vi.mock("../../agents/model-catalog.js", () => ({
loadModelCatalog: vi.fn().mockResolvedValue({ models: [] }),
}));
vi.mock("../../agents/model-selection.js", () => ({
getModelRefStatus: getModelRefStatusMock,
isCliProvider: isCliProviderMock,
normalizeModelSelection: normalizeModelSelectionForTest,
resolveAllowedModelRef: resolveAllowedModelRefMock,
resolveConfiguredModelRef: resolveConfiguredModelRefMock,
resolveHooksGmailModel: resolveHooksGmailModelMock,
resolveThinkingDefault: resolveThinkingDefaultMock,
}));
vi.mock("../../agents/model-fallback.js", () => ({
runWithModelFallback: runWithModelFallbackMock,
}));
vi.mock("../../agents/auth-profiles/session-override.js", () => ({
resolveSessionAuthProfileOverride: resolveSessionAuthProfileOverrideMock,
}));
vi.mock("../../agents/live-model-switch-error.js", async (importOriginal) => {
return await importOriginal<typeof import("../../agents/live-model-switch-error.js")>();
});
const resolveAgentTimeoutMsMock = vi.fn().mockReturnValue(60_000);
const deriveSessionTotalTokensMock = vi.fn().mockReturnValue(30);
const hasNonzeroUsageMock = vi.fn().mockReturnValue(false);
const normalizeThinkLevelMock = vi.fn().mockReturnValue(undefined);
const normalizeVerboseLevelMock = vi.fn().mockReturnValue("off");
const supportsXHighThinkingMock = vi.fn().mockReturnValue(false);
const resolveSessionTranscriptPathMock = vi.fn().mockReturnValue("/tmp/transcript.jsonl");
const setSessionRuntimeModelMock = vi.fn();
const registerAgentRunContextMock = vi.fn();
const buildSafeExternalPromptMock = vi.fn().mockReturnValue("safe prompt");
const detectSuspiciousPatternsMock = vi.fn().mockReturnValue([]);
const isExternalHookSessionMock = vi.fn().mockReturnValue(false);
const mapHookExternalContentSourceMock = vi.fn().mockReturnValue("unknown");
const resolveHookExternalContentSourceMock = vi.fn().mockReturnValue(undefined);
const estimateUsageCostMock = vi.fn().mockReturnValue(undefined);
const resolveModelCostConfigMock = vi.fn().mockReturnValue(undefined);
const resolveBootstrapWarningSignaturesSeenMock = vi.fn().mockReturnValue([]);
const resolveFastModeStateMock = vi.fn().mockReturnValue({ enabled: false });
const resolveNestedAgentLaneMock = vi.fn((lane: string | undefined) => lane);
vi.mock("../../agents/pi-embedded.js", () => ({
runEmbeddedPiAgent: runEmbeddedPiAgentMock,
}));
vi.mock("../../agents/context-cache.js", () => ({
lookupCachedContextTokens: lookupCachedContextTokensMock,
}));
vi.mock("../../agents/date-time.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/date-time.js")>();
return {
...actual,
formatUserTime: vi.fn().mockReturnValue("2026-02-10 12:00"),
resolveUserTimeFormat: vi.fn().mockReturnValue("24h"),
resolveUserTimezone: vi.fn().mockReturnValue("UTC"),
};
});
vi.mock("../../agents/timeout.js", () => ({
resolveAgentTimeoutMs: vi.fn().mockReturnValue(60_000),
}));
vi.mock("../../agents/usage.js", () => ({
deriveSessionTotalTokens: vi.fn().mockReturnValue(30),
hasNonzeroUsage: vi.fn().mockReturnValue(false),
}));
vi.mock("../../agents/subagent-announce.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/subagent-announce.js")>();
return {
...actual,
runSubagentAnnounceFlow: vi.fn().mockResolvedValue(true),
};
});
vi.mock("../../agents/subagent-registry.js", () => ({
countActiveDescendantRuns: countActiveDescendantRunsMock,
listDescendantRunsForRequester: listDescendantRunsForRequesterMock,
}));
vi.mock("../../agents/cli-runner.runtime.js", () => ({
runCliAgent: runCliAgentMock,
@ -88,60 +155,96 @@ vi.mock("../../agents/cli-runner.runtime.js", () => ({
setCliSessionId: vi.fn(),
}));
vi.mock("./run.runtime.js", () => ({
DEFAULT_CONTEXT_TOKENS: 128000,
DEFAULT_MODEL: "gpt-4",
DEFAULT_PROVIDER: "openai",
LiveSessionModelSwitchError,
buildSafeExternalPrompt: buildSafeExternalPromptMock,
buildWorkspaceSkillSnapshot: buildWorkspaceSkillSnapshotMock,
countActiveDescendantRuns: countActiveDescendantRunsMock,
deriveSessionTotalTokens: deriveSessionTotalTokensMock,
detectSuspiciousPatterns: detectSuspiciousPatternsMock,
ensureAgentWorkspace: ensureAgentWorkspaceMock,
estimateUsageCost: estimateUsageCostMock,
listDescendantRunsForRequester: listDescendantRunsForRequesterMock,
getModelRefStatus: getModelRefStatusMock,
getRemoteSkillEligibility: vi.fn().mockReturnValue({}),
getSkillsSnapshotVersion: getSkillsSnapshotVersionMock,
hasNonzeroUsage: hasNonzeroUsageMock,
isCliProvider: isCliProviderMock,
isExternalHookSession: isExternalHookSessionMock,
loadModelCatalog: loadModelCatalogMock,
logWarn: (...args: unknown[]) => logWarnMock(...args),
lookupContextTokens: lookupContextTokensMock,
mapHookExternalContentSource: mapHookExternalContentSourceMock,
normalizeAgentId: vi.fn((id: string) => id),
normalizeModelSelection: normalizeModelSelectionMock,
normalizeThinkLevel: normalizeThinkLevelMock,
normalizeVerboseLevel: normalizeVerboseLevelMock,
registerAgentRunContext: registerAgentRunContextMock,
resolveAgentConfig: resolveAgentConfigMock,
resolveAgentDir: resolveAgentDirMock,
resolveAgentModelFallbacksOverride: resolveAgentModelFallbacksOverrideMock,
resolveAgentSkillsFilter: resolveAgentSkillsFilterMock,
resolveAgentTimeoutMs: resolveAgentTimeoutMsMock,
resolveAgentWorkspaceDir: resolveAgentWorkspaceDirMock,
resolveAllowedModelRef: resolveAllowedModelRefMock,
resolveBootstrapWarningSignaturesSeen: resolveBootstrapWarningSignaturesSeenMock,
resolveConfiguredModelRef: resolveConfiguredModelRefMock,
resolveCronStyleNow: resolveCronStyleNowMock,
resolveDefaultAgentId: resolveDefaultAgentIdMock,
resolveFastModeState: resolveFastModeStateMock,
resolveHookExternalContentSource: resolveHookExternalContentSourceMock,
resolveHooksGmailModel: resolveHooksGmailModelMock,
resolveModelCostConfig: resolveModelCostConfigMock,
resolveNestedAgentLane: resolveNestedAgentLaneMock,
resolveSessionAuthProfileOverride: resolveSessionAuthProfileOverrideMock,
resolveSessionTranscriptPath: resolveSessionTranscriptPathMock,
resolveThinkingDefault: resolveThinkingDefaultMock,
runEmbeddedPiAgent: runEmbeddedPiAgentMock,
runWithModelFallback: runWithModelFallbackMock,
setSessionRuntimeModel: setSessionRuntimeModelMock,
supportsXHighThinking: supportsXHighThinkingMock,
vi.mock("../../auto-reply/thinking.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../auto-reply/thinking.js")>();
return {
...actual,
normalizeThinkLevel: vi.fn().mockReturnValue(undefined),
normalizeVerboseLevel: vi.fn().mockReturnValue("off"),
supportsXHighThinking: vi.fn().mockReturnValue(false),
};
});
vi.mock("../../cli/outbound-send-deps.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../cli/outbound-send-deps.js")>();
return {
...actual,
createOutboundSendDeps: vi.fn().mockReturnValue({}),
};
});
vi.mock("../../config/sessions/paths.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../config/sessions/paths.js")>();
return {
...actual,
resolveSessionTranscriptPath: vi.fn().mockReturnValue("/tmp/transcript.jsonl"),
};
});
vi.mock("../../config/sessions/store.runtime.js", () => ({
updateSessionStore: updateSessionStoreMock,
}));
vi.mock("../../config/sessions/types.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../config/sessions/types.js")>();
return {
...actual,
setSessionRuntimeModel: vi.fn(),
};
});
vi.mock("../../routing/session-key.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../routing/session-key.js")>();
return {
...actual,
buildAgentMainSessionKey: vi.fn().mockReturnValue("agent:default:cron:test"),
normalizeAgentId: vi.fn((id: string) => id),
};
});
vi.mock("../../infra/agent-events.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../infra/agent-events.js")>();
return {
...actual,
registerAgentRunContext: vi.fn(),
};
});
vi.mock("../../infra/outbound/deliver.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../infra/outbound/deliver.js")>();
return {
...actual,
deliverOutboundPayloads: vi.fn().mockResolvedValue(undefined),
};
});
vi.mock("../../infra/skills-remote.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../infra/skills-remote.js")>();
return {
...actual,
getRemoteSkillEligibility: vi.fn().mockReturnValue({}),
};
});
vi.mock("../../logger.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../logger.js")>();
return {
...actual,
logWarn: (...args: unknown[]) => logWarnMock(...args),
};
});
vi.mock("../../security/external-content.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../security/external-content.js")>();
return {
...actual,
buildSafeExternalPrompt: vi.fn().mockReturnValue("safe prompt"),
detectSuspiciousPatterns: vi.fn().mockReturnValue([]),
getHookType: vi.fn().mockReturnValue("unknown"),
isExternalHookSession: vi.fn().mockReturnValue(false),
};
});
vi.mock("../delivery.js", () => ({
resolveCronDeliveryPlan: resolveCronDeliveryPlanMock,
}));
@ -164,6 +267,16 @@ vi.mock("./session.js", () => ({
resolveCronSession: resolveCronSessionMock,
}));
vi.mock("../../agents/defaults.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/defaults.js")>();
return {
...actual,
DEFAULT_CONTEXT_TOKENS: 128000,
DEFAULT_MODEL: "gpt-4",
DEFAULT_PROVIDER: "openai",
};
});
export function makeCronSessionEntry(overrides?: Record<string, unknown>): CronSessionEntry {
return {
sessionId: "test-session-id",
@ -235,6 +348,8 @@ export function resetRunCronIsolatedAgentTurnHarness(): void {
runEmbeddedPiAgentMock.mockResolvedValue(makeDefaultEmbeddedResult());
runCliAgentMock.mockReset();
lookupCachedContextTokensMock.mockReset();
lookupCachedContextTokensMock.mockReturnValue(undefined);
getCliSessionIdMock.mockReturnValue(undefined);
updateSessionStoreMock.mockReset();

View File

@ -1,5 +1,50 @@
import {
resolveAgentConfig,
resolveAgentDir,
resolveAgentModelFallbacksOverride,
resolveAgentWorkspaceDir,
resolveDefaultAgentId,
} from "../../agents/agent-scope.js";
import { resolveSessionAuthProfileOverride } from "../../agents/auth-profiles/session-override.js";
import { resolveBootstrapWarningSignaturesSeen } from "../../agents/bootstrap-budget.js";
import { lookupCachedContextTokens } from "../../agents/context-cache.js";
import { resolveCronStyleNow } from "../../agents/current-time.js";
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
import { resolveFastModeState } from "../../agents/fast-mode.js";
import { resolveNestedAgentLane } from "../../agents/lanes.js";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch-error.js";
import { loadModelCatalog } from "../../agents/model-catalog.js";
import { runWithModelFallback } from "../../agents/model-fallback.js";
import { isCliProvider, resolveThinkingDefault } from "../../agents/model-selection.js";
import { runEmbeddedPiAgent } from "../../agents/pi-embedded.js";
import {
countActiveDescendantRuns,
listDescendantRunsForRequester,
} from "../../agents/subagent-registry.js";
import { resolveAgentTimeoutMs } from "../../agents/timeout.js";
import { deriveSessionTotalTokens, hasNonzeroUsage } from "../../agents/usage.js";
import { ensureAgentWorkspace } from "../../agents/workspace.js";
import {
normalizeThinkLevel,
normalizeVerboseLevel,
supportsXHighThinking,
} from "../../auto-reply/thinking.js";
import type { CliDeps } from "../../cli/outbound-send-deps.js";
import type { OpenClawConfig } from "../../config/config.js";
import { resolveSessionTranscriptPath } from "../../config/sessions/paths.js";
import { updateSessionStore } from "../../config/sessions/store.runtime.js";
import { setSessionRuntimeModel } from "../../config/sessions/types.js";
import { registerAgentRunContext } from "../../infra/agent-events.js";
import { logWarn } from "../../logger.js";
import { normalizeAgentId } from "../../routing/session-key.js";
import {
buildSafeExternalPrompt,
detectSuspiciousPatterns,
mapHookExternalContentSource,
isExternalHookSession,
resolveHookExternalContentSource,
} from "../../security/external-content.js";
import { estimateUsageCost, resolveModelCostConfig } from "../../utils/usage-format.js";
import { resolveCronDeliveryPlan } from "../delivery.js";
import type { CronJob, CronRunOutcome, CronRunTelemetry } from "../types.js";
import {
@ -15,48 +60,6 @@ import {
} from "./helpers.js";
import { resolveCronModelSelection } from "./model-selection.js";
import { buildCronAgentDefaultsConfig } from "./run-config.js";
import {
DEFAULT_CONTEXT_TOKENS,
LiveSessionModelSwitchError,
buildSafeExternalPrompt,
countActiveDescendantRuns,
deriveSessionTotalTokens,
detectSuspiciousPatterns,
ensureAgentWorkspace,
estimateUsageCost,
hasNonzeroUsage,
isCliProvider,
isExternalHookSession,
listDescendantRunsForRequester,
loadModelCatalog,
logWarn,
lookupContextTokens,
mapHookExternalContentSource,
normalizeAgentId,
normalizeThinkLevel,
normalizeVerboseLevel,
registerAgentRunContext,
resolveAgentConfig,
resolveAgentDir,
resolveAgentModelFallbacksOverride,
resolveAgentTimeoutMs,
resolveAgentWorkspaceDir,
resolveBootstrapWarningSignaturesSeen,
resolveCronStyleNow,
resolveDefaultAgentId,
resolveFastModeState,
resolveHookExternalContentSource,
resolveModelCostConfig,
resolveNestedAgentLane,
resolveSessionAuthProfileOverride,
resolveSessionTranscriptPath,
resolveThinkingDefault,
runEmbeddedPiAgent,
runWithModelFallback,
setSessionRuntimeModel,
supportsXHighThinking,
updateSessionStore,
} from "./run.runtime.js";
import { resolveCronAgentSessionKey } from "./session-key.js";
import { resolveCronSession } from "./session.js";
import { resolveCronSkillsSnapshot } from "./skills-snapshot.js";
@ -163,6 +166,10 @@ function appendCronDeliveryInstruction(params: {
return `${params.commandBody}\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself.`.trim();
}
function resolvePositiveContextTokens(value: unknown): number | undefined {
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
}
async function loadCliRunnerRuntime() {
return await import("../../agents/cli-runner.runtime.js");
}
@ -702,8 +709,9 @@ export async function runCronIsolatedAgentTurn(params: {
const providerUsed =
finalRunResult.meta?.agentMeta?.provider ?? fallbackProvider ?? liveSelection.provider;
const contextTokens =
agentCfg?.contextTokens ??
lookupContextTokens(modelUsed, { allowAsyncLoad: false }) ??
resolvePositiveContextTokens(agentCfg?.contextTokens) ??
lookupCachedContextTokens(modelUsed) ??
resolvePositiveContextTokens(cronSession.sessionEntry.contextTokens) ??
DEFAULT_CONTEXT_TOKENS;
setSessionRuntimeModel(cronSession.sessionEntry, {