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 { resolveDefaultModelForAgent, resolvePersistedModelRef } from "./model-selection.js";
import { import {
consumeEmbeddedRunModelSwitch, consumeEmbeddedRunModelSwitch,
@@ -6,25 +9,9 @@ import {
type EmbeddedRunModelSwitchRequest, type EmbeddedRunModelSwitchRequest,
} from "./pi-embedded-runner/runs.js"; } from "./pi-embedded-runner/runs.js";
import { abortEmbeddedPiRun } from "./pi-embedded.js"; import { abortEmbeddedPiRun } from "./pi-embedded.js";
export { LiveSessionModelSwitchError } from "./live-model-switch-error.js";
export type LiveSessionModelSelection = EmbeddedRunModelSwitchRequest; 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: { export function resolveLiveSessionModelSelection(params: {
cfg?: { session?: { store?: string } } | undefined; cfg?: { session?: { store?: string } } | undefined;
sessionKey?: string; sessionKey?: string;

View File

@@ -9,7 +9,7 @@ import type { AuthProfileStore } from "./auth-profiles.js";
import { saveAuthProfileStore } from "./auth-profiles.js"; import { saveAuthProfileStore } from "./auth-profiles.js";
import { AUTH_STORE_VERSION } from "./auth-profiles/constants.js"; import { AUTH_STORE_VERSION } from "./auth-profiles/constants.js";
import { isAnthropicBillingError } from "./live-auth-keys.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 { runWithImageModelFallback, runWithModelFallback } from "./model-fallback.js";
import { makeModelFallbackCfg } from "./test-helpers/model-fallback-config-fixture.js"; import { makeModelFallbackCfg } from "./test-helpers/model-fallback-config-fixture.js";

View File

@@ -26,7 +26,7 @@ import {
shouldPreserveTransientCooldownProbeSlot, shouldPreserveTransientCooldownProbeSlot,
shouldUseTransientCooldownProbeSlot, shouldUseTransientCooldownProbeSlot,
} from "./failover-policy.js"; } 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 { logModelFallbackDecision } from "./model-fallback-observation.js";
import type { FallbackAttempt, ModelCandidate } from "./model-fallback.types.js"; import type { FallbackAttempt, ModelCandidate } from "./model-fallback.types.js";
import { import {

View File

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

View File

@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; 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 { TemplateContext } from "../templating.js";
import type { GetReplyOptions } from "../types.js"; import type { GetReplyOptions } from "../types.js";
import { MAX_LIVE_SWITCH_RETRIES } from "./agent-runner-execution.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 { resolveBootstrapWarningSignaturesSeen } from "../../agents/bootstrap-budget.js";
import { runCliAgent } from "../../agents/cli-runner.js"; import { runCliAgent } from "../../agents/cli-runner.js";
import { getCliSessionBinding } from "../../agents/cli-session.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 { runWithModelFallback, isFallbackSummaryError } from "../../agents/model-fallback.js";
import { isCliProvider } from "../../agents/model-selection.js"; import { isCliProvider } from "../../agents/model-selection.js";
import { import {

View File

@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest"; 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 { import {
clearFastTestEnv, clearFastTestEnv,
loadRunCronIsolatedAgentTurn, loadRunCronIsolatedAgentTurn,

View File

@@ -8,8 +8,11 @@ import {
buildWorkspaceSkillSnapshotMock, buildWorkspaceSkillSnapshotMock,
getCliSessionIdMock, getCliSessionIdMock,
isCliProviderMock, isCliProviderMock,
lookupCachedContextTokensMock,
loadRunCronIsolatedAgentTurn, loadRunCronIsolatedAgentTurn,
logWarnMock, logWarnMock,
makeCronSession,
makeCronSessionEntry,
resolveAgentConfigMock, resolveAgentConfigMock,
resolveAgentSkillsFilterMock, resolveAgentSkillsFilterMock,
resolveAllowedModelRefMock, 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 { vi, type Mock } from "vitest";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch.js";
type CronSessionEntry = { type CronSessionEntry = {
sessionId: string; sessionId: string;
@@ -24,6 +23,18 @@ function createMock(): Mock {
return vi.fn(); 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 buildWorkspaceSkillSnapshotMock = createMock();
export const resolveAgentConfigMock = createMock(); export const resolveAgentConfigMock = createMock();
export const resolveAgentModelFallbacksOverrideMock = createMock(); export const resolveAgentModelFallbacksOverrideMock = createMock();
@@ -37,6 +48,7 @@ export const resolveThinkingDefaultMock = createMock();
export const runWithModelFallbackMock = createMock(); export const runWithModelFallbackMock = createMock();
export const runEmbeddedPiAgentMock = createMock(); export const runEmbeddedPiAgentMock = createMock();
export const runCliAgentMock = createMock(); export const runCliAgentMock = createMock();
export const lookupCachedContextTokensMock = createMock();
export const getCliSessionIdMock = createMock(); export const getCliSessionIdMock = createMock();
export const updateSessionStoreMock = createMock(); export const updateSessionStoreMock = createMock();
export const resolveCronSessionMock = createMock(); export const resolveCronSessionMock = createMock();
@@ -48,39 +60,94 @@ export const resolveCronPayloadOutcomeMock = createMock();
export const resolveCronDeliveryPlanMock = createMock(); export const resolveCronDeliveryPlanMock = createMock();
export const resolveDeliveryTargetMock = createMock(); export const resolveDeliveryTargetMock = createMock();
export const resolveSessionAuthProfileOverrideMock = createMock(); export const resolveSessionAuthProfileOverrideMock = createMock();
const resolveAgentDirMock = vi.fn().mockReturnValue("/tmp/agent-dir");
const resolveAgentWorkspaceDirMock = vi.fn().mockReturnValue("/tmp/workspace"); vi.mock("../../agents/agent-scope.js", () => ({
const resolveDefaultAgentIdMock = vi.fn().mockReturnValue("default"); resolveAgentConfig: resolveAgentConfigMock,
const getSkillsSnapshotVersionMock = vi.fn().mockReturnValue(42); resolveAgentDir: vi.fn().mockReturnValue("/tmp/agent-dir"),
const ensureAgentWorkspaceMock = vi.fn().mockResolvedValue({ dir: "/tmp/workspace" }); resolveAgentModelFallbacksOverride: resolveAgentModelFallbacksOverrideMock,
const loadModelCatalogMock = vi.fn().mockResolvedValue({ models: [] }); resolveAgentWorkspaceDir: vi.fn().mockReturnValue("/tmp/workspace"),
const normalizeModelSelectionMock = vi.fn((value: unknown) => resolveDefaultAgentId: vi.fn().mockReturnValue("default"),
typeof value === "string" ? value.trim() || undefined : undefined, resolveAgentSkillsFilter: resolveAgentSkillsFilterMock,
); }));
const lookupContextTokensMock = vi.fn().mockReturnValue(128000);
const resolveCronStyleNowMock = vi.fn().mockReturnValue({ vi.mock("../../agents/skills.js", () => ({
formattedTime: "2026-02-10 12:00", buildWorkspaceSkillSnapshot: buildWorkspaceSkillSnapshotMock,
timeLine: "Current time: 2026-02-10 12:00 UTC", }));
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); vi.mock("../../agents/pi-embedded.js", () => ({
const hasNonzeroUsageMock = vi.fn().mockReturnValue(false); runEmbeddedPiAgent: runEmbeddedPiAgentMock,
const normalizeThinkLevelMock = vi.fn().mockReturnValue(undefined); }));
const normalizeVerboseLevelMock = vi.fn().mockReturnValue("off");
const supportsXHighThinkingMock = vi.fn().mockReturnValue(false); vi.mock("../../agents/context-cache.js", () => ({
const resolveSessionTranscriptPathMock = vi.fn().mockReturnValue("/tmp/transcript.jsonl"); lookupCachedContextTokens: lookupCachedContextTokensMock,
const setSessionRuntimeModelMock = vi.fn(); }));
const registerAgentRunContextMock = vi.fn();
const buildSafeExternalPromptMock = vi.fn().mockReturnValue("safe prompt"); vi.mock("../../agents/date-time.js", async (importOriginal) => {
const detectSuspiciousPatternsMock = vi.fn().mockReturnValue([]); const actual = await importOriginal<typeof import("../../agents/date-time.js")>();
const isExternalHookSessionMock = vi.fn().mockReturnValue(false); return {
const mapHookExternalContentSourceMock = vi.fn().mockReturnValue("unknown"); ...actual,
const resolveHookExternalContentSourceMock = vi.fn().mockReturnValue(undefined); formatUserTime: vi.fn().mockReturnValue("2026-02-10 12:00"),
const estimateUsageCostMock = vi.fn().mockReturnValue(undefined); resolveUserTimeFormat: vi.fn().mockReturnValue("24h"),
const resolveModelCostConfigMock = vi.fn().mockReturnValue(undefined); resolveUserTimezone: vi.fn().mockReturnValue("UTC"),
const resolveBootstrapWarningSignaturesSeenMock = vi.fn().mockReturnValue([]); };
const resolveFastModeStateMock = vi.fn().mockReturnValue({ enabled: false }); });
const resolveNestedAgentLaneMock = vi.fn((lane: string | undefined) => lane);
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", () => ({ vi.mock("../../agents/cli-runner.runtime.js", () => ({
runCliAgent: runCliAgentMock, runCliAgent: runCliAgentMock,
@@ -88,60 +155,96 @@ vi.mock("../../agents/cli-runner.runtime.js", () => ({
setCliSessionId: vi.fn(), setCliSessionId: vi.fn(),
})); }));
vi.mock("./run.runtime.js", () => ({ vi.mock("../../auto-reply/thinking.js", async (importOriginal) => {
DEFAULT_CONTEXT_TOKENS: 128000, const actual = await importOriginal<typeof import("../../auto-reply/thinking.js")>();
DEFAULT_MODEL: "gpt-4", return {
DEFAULT_PROVIDER: "openai", ...actual,
LiveSessionModelSwitchError, normalizeThinkLevel: vi.fn().mockReturnValue(undefined),
buildSafeExternalPrompt: buildSafeExternalPromptMock, normalizeVerboseLevel: vi.fn().mockReturnValue("off"),
buildWorkspaceSkillSnapshot: buildWorkspaceSkillSnapshotMock, supportsXHighThinking: vi.fn().mockReturnValue(false),
countActiveDescendantRuns: countActiveDescendantRunsMock, };
deriveSessionTotalTokens: deriveSessionTotalTokensMock, });
detectSuspiciousPatterns: detectSuspiciousPatternsMock,
ensureAgentWorkspace: ensureAgentWorkspaceMock, vi.mock("../../cli/outbound-send-deps.js", async (importOriginal) => {
estimateUsageCost: estimateUsageCostMock, const actual = await importOriginal<typeof import("../../cli/outbound-send-deps.js")>();
listDescendantRunsForRequester: listDescendantRunsForRequesterMock, return {
getModelRefStatus: getModelRefStatusMock, ...actual,
getRemoteSkillEligibility: vi.fn().mockReturnValue({}), createOutboundSendDeps: vi.fn().mockReturnValue({}),
getSkillsSnapshotVersion: getSkillsSnapshotVersionMock, };
hasNonzeroUsage: hasNonzeroUsageMock, });
isCliProvider: isCliProviderMock,
isExternalHookSession: isExternalHookSessionMock, vi.mock("../../config/sessions/paths.js", async (importOriginal) => {
loadModelCatalog: loadModelCatalogMock, const actual = await importOriginal<typeof import("../../config/sessions/paths.js")>();
logWarn: (...args: unknown[]) => logWarnMock(...args), return {
lookupContextTokens: lookupContextTokensMock, ...actual,
mapHookExternalContentSource: mapHookExternalContentSourceMock, resolveSessionTranscriptPath: vi.fn().mockReturnValue("/tmp/transcript.jsonl"),
normalizeAgentId: vi.fn((id: string) => id), };
normalizeModelSelection: normalizeModelSelectionMock, });
normalizeThinkLevel: normalizeThinkLevelMock,
normalizeVerboseLevel: normalizeVerboseLevelMock, vi.mock("../../config/sessions/store.runtime.js", () => ({
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,
updateSessionStore: updateSessionStoreMock, 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", () => ({ vi.mock("../delivery.js", () => ({
resolveCronDeliveryPlan: resolveCronDeliveryPlanMock, resolveCronDeliveryPlan: resolveCronDeliveryPlanMock,
})); }));
@@ -164,6 +267,16 @@ vi.mock("./session.js", () => ({
resolveCronSession: resolveCronSessionMock, 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 { export function makeCronSessionEntry(overrides?: Record<string, unknown>): CronSessionEntry {
return { return {
sessionId: "test-session-id", sessionId: "test-session-id",
@@ -235,6 +348,8 @@ export function resetRunCronIsolatedAgentTurnHarness(): void {
runEmbeddedPiAgentMock.mockResolvedValue(makeDefaultEmbeddedResult()); runEmbeddedPiAgentMock.mockResolvedValue(makeDefaultEmbeddedResult());
runCliAgentMock.mockReset(); runCliAgentMock.mockReset();
lookupCachedContextTokensMock.mockReset();
lookupCachedContextTokensMock.mockReturnValue(undefined);
getCliSessionIdMock.mockReturnValue(undefined); getCliSessionIdMock.mockReturnValue(undefined);
updateSessionStoreMock.mockReset(); 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 { CliDeps } from "../../cli/outbound-send-deps.js";
import type { OpenClawConfig } from "../../config/config.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 { resolveCronDeliveryPlan } from "../delivery.js";
import type { CronJob, CronRunOutcome, CronRunTelemetry } from "../types.js"; import type { CronJob, CronRunOutcome, CronRunTelemetry } from "../types.js";
import { import {
@@ -15,48 +60,6 @@ import {
} from "./helpers.js"; } from "./helpers.js";
import { resolveCronModelSelection } from "./model-selection.js"; import { resolveCronModelSelection } from "./model-selection.js";
import { buildCronAgentDefaultsConfig } from "./run-config.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 { resolveCronAgentSessionKey } from "./session-key.js";
import { resolveCronSession } from "./session.js"; import { resolveCronSession } from "./session.js";
import { resolveCronSkillsSnapshot } from "./skills-snapshot.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(); 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() { async function loadCliRunnerRuntime() {
return await import("../../agents/cli-runner.runtime.js"); return await import("../../agents/cli-runner.runtime.js");
} }
@@ -702,8 +709,9 @@ export async function runCronIsolatedAgentTurn(params: {
const providerUsed = const providerUsed =
finalRunResult.meta?.agentMeta?.provider ?? fallbackProvider ?? liveSelection.provider; finalRunResult.meta?.agentMeta?.provider ?? fallbackProvider ?? liveSelection.provider;
const contextTokens = const contextTokens =
agentCfg?.contextTokens ?? resolvePositiveContextTokens(agentCfg?.contextTokens) ??
lookupContextTokens(modelUsed, { allowAsyncLoad: false }) ?? lookupCachedContextTokens(modelUsed) ??
resolvePositiveContextTokens(cronSession.sessionEntry.contextTokens) ??
DEFAULT_CONTEXT_TOKENS; DEFAULT_CONTEXT_TOKENS;
setSessionRuntimeModel(cronSession.sessionEntry, { setSessionRuntimeModel(cronSession.sessionEntry, {