test: trim models and cron partial mocks

This commit is contained in:
Peter Steinberger
2026-04-03 18:32:00 +01:00
parent 1c16c6a94a
commit fb9be1fcb6
10 changed files with 208 additions and 366 deletions

View File

@@ -4,7 +4,7 @@ let modelsListCommand: typeof import("./models/list.list-command.js").modelsList
let loadModelRegistry: typeof import("./models/list.registry.js").loadModelRegistry;
let toModelRow: typeof import("./models/list.registry.js").toModelRow;
const loadConfig = vi.fn();
const getRuntimeConfig = vi.fn();
const readConfigFileSnapshotForWrite = vi.fn().mockResolvedValue({
snapshot: { valid: false, resolved: {} },
writeOptions: {},
@@ -14,16 +14,9 @@ const ensureOpenClawModelsJson = vi.fn().mockResolvedValue(undefined);
const resolveOpenClawAgentDir = vi.fn().mockReturnValue("/tmp/openclaw-agent");
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
const listProfilesForProvider = vi.fn().mockReturnValue([]);
const resolveAuthProfileDisplayLabel = vi.fn(({ profileId }: { profileId: string }) => profileId);
const resolveAuthStorePathForDisplay = vi
.fn()
.mockReturnValue("/tmp/openclaw-agent/auth-profiles.json");
const resolveProfileUnusableUntilForDisplay = vi.fn().mockReturnValue(null);
const resolveEnvApiKey = vi.fn().mockReturnValue(undefined);
const resolveAwsSdkEnvVarName = vi.fn().mockReturnValue(undefined);
const hasUsableCustomProviderApiKey = vi.fn().mockReturnValue(false);
const resolveUsableCustomProviderApiKey = vi.fn().mockReturnValue(null);
const getCustomProviderApiKey = vi.fn().mockReturnValue(undefined);
const modelRegistryState = {
models: [] as Array<Record<string, unknown>>,
available: [] as Array<Record<string, unknown>>,
@@ -32,60 +25,18 @@ const modelRegistryState = {
};
let previousExitCode: typeof process.exitCode;
vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../config/config.js")>();
return {
...actual,
CONFIG_PATH: "/tmp/openclaw.json",
STATE_DIR: "/tmp/openclaw-state",
loadConfig,
readConfigFileSnapshotForWrite,
setRuntimeConfigSnapshot,
};
});
vi.mock("./models/load-config.runtime.js", () => ({
getRuntimeConfig,
readSourceConfigSnapshotForWrite: readConfigFileSnapshotForWrite,
setRuntimeConfigSnapshot,
getModelsCommandSecretTargetIds: vi.fn().mockReturnValue([]),
resolveCommandSecretRefsViaGateway: vi.fn(async ({ config }: { config: unknown }) => ({
resolvedConfig: config,
diagnostics: [],
})),
}));
vi.mock("../agents/models-config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../agents/models-config.js")>();
return {
...actual,
ensureOpenClawModelsJson,
};
});
vi.mock("../agents/agent-paths.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../agents/agent-paths.js")>();
return {
...actual,
resolveOpenClawAgentDir,
};
});
vi.mock("../agents/auth-profiles.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../agents/auth-profiles.js")>();
return {
...actual,
ensureAuthProfileStore,
listProfilesForProvider,
resolveAuthProfileDisplayLabel,
resolveAuthStorePathForDisplay,
resolveProfileUnusableUntilForDisplay,
};
});
vi.mock("../agents/model-auth.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../agents/model-auth.js")>();
return {
...actual,
resolveEnvApiKey,
resolveAwsSdkEnvVarName,
hasUsableCustomProviderApiKey,
resolveUsableCustomProviderApiKey,
getCustomProviderApiKey,
};
});
vi.mock("../agents/pi-model-discovery.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../agents/pi-model-discovery.js")>();
vi.mock("./models/list.runtime.js", () => {
class MockModelRegistry {
find(provider: string, id: string) {
return (
@@ -110,16 +61,16 @@ vi.mock("../agents/pi-model-discovery.js", async (importOriginal) => {
}
return {
...actual,
ensureAuthProfileStore,
ensureOpenClawModelsJson,
resolveOpenClawAgentDir,
listProfilesForProvider,
resolveEnvApiKey,
resolveAwsSdkEnvVarName,
hasUsableCustomProviderApiKey,
loadModelCatalog: vi.fn(async () => []),
discoverAuthStorage: () => ({}) as unknown,
discoverModels: () => new MockModelRegistry() as unknown,
};
});
vi.mock("../agents/pi-embedded-runner/model.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../agents/pi-embedded-runner/model.js")>();
return {
...actual,
resolveModelWithRegistry: ({
provider,
modelId,
@@ -158,6 +109,8 @@ beforeEach(() => {
process.exitCode = undefined;
modelRegistryState.getAllError = undefined;
modelRegistryState.getAvailableError = undefined;
getRuntimeConfig.mockReset();
getRuntimeConfig.mockReturnValue({});
listProfilesForProvider.mockReturnValue([]);
ensureOpenClawModelsJson.mockClear();
readConfigFileSnapshotForWrite.mockClear();
@@ -225,13 +178,13 @@ describe("models list/status", () => {
};
function setDefaultModel(model: string) {
loadConfig.mockReturnValue({
getRuntimeConfig.mockReturnValue({
agents: { defaults: { model } },
});
}
function configureModelAsConfigured(model: string) {
loadConfig.mockReturnValue({
getRuntimeConfig.mockReturnValue({
agents: {
defaults: {
model,
@@ -310,7 +263,7 @@ describe("models list/status", () => {
});
it("models list plain outputs canonical zai key", async () => {
loadConfig.mockReturnValue({
getRuntimeConfig.mockReturnValue({
agents: { defaults: { model: "z.ai/glm-4.7" } },
});
const runtime = makeRuntime();
@@ -324,7 +277,7 @@ describe("models list/status", () => {
});
it("models list plain keeps canonical OpenRouter native ids", async () => {
loadConfig.mockReturnValue({
getRuntimeConfig.mockReturnValue({
agents: { defaults: { model: "openrouter/hunter-alpha" } },
});
const runtime = makeRuntime();
@@ -441,7 +394,7 @@ describe("models list/status", () => {
expect(Array.from(loaded.availableKeys ?? [])).toEqual(["openai-codex/gpt-5.3-codex-spark"]);
});
it("modelsListCommand persists using the write snapshot config when provided", async () => {
it("modelsListCommand persists using the source snapshot config when provided", async () => {
modelRegistryState.models = [OPENAI_MODEL];
modelRegistryState.available = [OPENAI_MODEL];
const sourceConfig = {
@@ -451,7 +404,7 @@ describe("models list/status", () => {
models: { providers: { openai: { apiKey: "sk-resolved-runtime-value" } } }, // pragma: allowlist secret
};
readConfigFileSnapshotForWrite.mockResolvedValue({
snapshot: { valid: true, resolved: resolvedConfig, source: sourceConfig },
snapshot: { valid: true, resolved: resolvedConfig, sourceConfig },
writeOptions: {},
});
setDefaultModel("openai/gpt-4.1-mini");
@@ -460,7 +413,7 @@ describe("models list/status", () => {
await modelsListCommand({ all: true, json: true }, runtime);
expect(ensureOpenClawModelsJson).toHaveBeenCalled();
expect(ensureOpenClawModelsJson.mock.calls[0]?.[0]).toEqual(resolvedConfig);
expect(ensureOpenClawModelsJson.mock.calls[0]?.[0]).toEqual(sourceConfig);
});
it("toModelRow does not crash without cfg/authStore when availability is undefined", async () => {

View File

@@ -42,8 +42,8 @@ const mocks = vi.hoisted(() => {
return {
sourceConfig,
resolvedConfig,
loadConfig: vi.fn(),
loadModelsConfigWithSource: vi.fn(),
ensureOpenClawModelsJson: vi.fn(),
ensureAuthProfileStore: vi.fn(),
loadModelRegistry: vi.fn(),
loadModelCatalog: vi.fn(),
@@ -55,15 +55,12 @@ const mocks = vi.hoisted(() => {
});
function resetMocks() {
mocks.loadConfig.mockReturnValue({
agents: { defaults: { model: { primary: "openai-codex/gpt-5.4" } } },
models: { providers: {} },
});
mocks.loadModelsConfigWithSource.mockResolvedValue({
sourceConfig: mocks.sourceConfig,
resolvedConfig: mocks.resolvedConfig,
diagnostics: [],
});
mocks.ensureOpenClawModelsJson.mockResolvedValue({ wrote: false });
mocks.ensureAuthProfileStore.mockReturnValue({ version: 1, profiles: {}, order: {} });
mocks.loadModelRegistry.mockResolvedValue({
models: [],
@@ -114,46 +111,9 @@ async function runAllOpenAiCodexCommand() {
}
let modelsListCommand: typeof import("./list.list-command.js").modelsListCommand;
let listRegistryModule: typeof import("./list.registry.js");
function installModelsListCommandForwardCompatMocks() {
vi.doMock("../../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../config/config.js")>();
return {
...actual,
loadConfig: mocks.loadConfig,
getRuntimeConfigSnapshot: vi.fn().mockReturnValue(null),
getRuntimeConfigSourceSnapshot: vi.fn().mockReturnValue(null),
};
});
vi.doMock("../../agents/auth-profiles.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/auth-profiles.js")>();
return {
...actual,
ensureAuthProfileStore: mocks.ensureAuthProfileStore,
listProfilesForProvider: mocks.listProfilesForProvider,
};
});
vi.doMock("../../agents/auth-profiles.runtime.js", () => ({
ensureAuthProfileStore: mocks.ensureAuthProfileStore,
}));
vi.doMock("../../agents/models-config.js", () => ({
ensureOpenClawModelsJson: vi.fn(async () => ({ wrote: false })),
}));
vi.doMock("../../agents/model-catalog.js", () => ({
loadModelCatalog: mocks.loadModelCatalog,
}));
vi.doMock("./list.registry.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./list.registry.js")>();
return {
...actual,
loadModelRegistry: mocks.loadModelRegistry,
};
});
vi.doMock("./load-config.js", () => ({
loadModelsConfigWithSource: mocks.loadModelsConfigWithSource,
}));
@@ -166,18 +126,22 @@ function installModelsListCommandForwardCompatMocks() {
printModelTable: mocks.printModelTable,
}));
vi.doMock("../../agents/pi-embedded-runner/model.js", async (importOriginal) => {
const actual =
await importOriginal<typeof import("../../agents/pi-embedded-runner/model.js")>();
return {
...actual,
resolveModelWithRegistry: mocks.resolveModelWithRegistry,
};
});
vi.doMock("./list.runtime.js", () => ({
ensureOpenClawModelsJson: mocks.ensureOpenClawModelsJson,
ensureAuthProfileStore: mocks.ensureAuthProfileStore,
listProfilesForProvider: mocks.listProfilesForProvider,
loadModelCatalog: mocks.loadModelCatalog,
resolveModelWithRegistry: mocks.resolveModelWithRegistry,
resolveEnvApiKey: vi.fn().mockReturnValue(undefined),
resolveAwsSdkEnvVarName: vi.fn().mockReturnValue(undefined),
hasUsableCustomProviderApiKey: vi.fn().mockReturnValue(false),
}));
}
beforeAll(async () => {
installModelsListCommandForwardCompatMocks();
listRegistryModule = await import("./list.registry.js");
vi.spyOn(listRegistryModule, "loadModelRegistry").mockImplementation(mocks.loadModelRegistry);
({ modelsListCommand } = await import("./list.list-command.js"));
});

View File

@@ -25,8 +25,7 @@ export async function modelsListCommand(
runtime: RuntimeEnv,
) {
ensureFlagCompatibility(opts);
const { ensureAuthProfileStore } = await import("../../agents/auth-profiles.runtime.js");
const { ensureOpenClawModelsJson } = await import("../../agents/models-config.js");
const { ensureAuthProfileStore, ensureOpenClawModelsJson } = await import("./list.runtime.js");
const { sourceConfig, resolvedConfig: cfg } = await loadModelsConfigWithSource({
commandName: "models list",
runtime,

View File

@@ -1,21 +1,22 @@
import type { Api, Model } from "@mariozechner/pi-ai";
import type { ModelRegistry } from "@mariozechner/pi-coding-agent";
import { resolveOpenClawAgentDir } from "../../agents/agent-paths.js";
import type { AuthProfileStore } from "../../agents/auth-profiles.js";
import { listProfilesForProvider } from "../../agents/auth-profiles.js";
import {
hasUsableCustomProviderApiKey,
resolveAwsSdkEnvVarName,
resolveEnvApiKey,
} from "../../agents/model-auth.js";
import { shouldSuppressBuiltInModel } from "../../agents/model-suppression.js";
import { discoverAuthStorage, discoverModels } from "../../agents/pi-model-discovery.js";
import type { OpenClawConfig } from "../../config/config.js";
import {
formatErrorWithStack,
MODEL_AVAILABILITY_UNAVAILABLE_CODE,
shouldFallbackToAuthHeuristics,
} from "./list.errors.js";
import {
discoverAuthStorage,
discoverModels,
hasUsableCustomProviderApiKey,
listProfilesForProvider,
resolveAwsSdkEnvVarName,
resolveEnvApiKey,
resolveOpenClawAgentDir,
} from "./list.runtime.js";
import type { ModelRow } from "./list.types.js";
import { isLocalBaseUrl, modelKey } from "./shared.js";

View File

@@ -1,12 +1,11 @@
import type { Api, Model } from "@mariozechner/pi-ai";
import type { ModelRegistry } from "@mariozechner/pi-coding-agent";
import type { AuthProfileStore } from "../../agents/auth-profiles.js";
import { loadModelCatalog } from "../../agents/model-catalog.js";
import { shouldSuppressBuiltInModel } from "../../agents/model-suppression.js";
import { resolveModelWithRegistry } from "../../agents/pi-embedded-runner/model.js";
import { normalizeProviderId } from "../../agents/provider-id.js";
import type { OpenClawConfig } from "../../config/config.js";
import { loadModelRegistry, toModelRow } from "./list.registry.js";
import { loadModelCatalog, resolveModelWithRegistry } from "./list.runtime.js";
import type { ConfiguredEntry, ModelRow } from "./list.types.js";
import { isLocalBaseUrl, modelKey } from "./shared.js";

View File

@@ -0,0 +1,12 @@
export { ensureAuthProfileStore } from "../../agents/auth-profiles.runtime.js";
export { ensureOpenClawModelsJson } from "../../agents/models-config.js";
export { resolveOpenClawAgentDir } from "../../agents/agent-paths.js";
export { listProfilesForProvider } from "../../agents/auth-profiles.js";
export {
hasUsableCustomProviderApiKey,
resolveAwsSdkEnvVarName,
resolveEnvApiKey,
} from "../../agents/model-auth.js";
export { loadModelCatalog } from "../../agents/model-catalog.js";
export { resolveModelWithRegistry } from "../../agents/pi-embedded-runner/model.js";
export { discoverAuthStorage, discoverModels } from "../../agents/pi-model-discovery.js";

View File

@@ -0,0 +1,8 @@
export { resolveCommandSecretRefsViaGateway } from "../../cli/command-secret-gateway.js";
export { getModelsCommandSecretTargetIds } from "../../cli/command-secret-targets.js";
export {
getRuntimeConfig,
readSourceConfigSnapshotForWrite,
setRuntimeConfigSnapshot,
type OpenClawConfig,
} from "../../config/config.js";

View File

@@ -1,12 +1,12 @@
import { resolveCommandSecretRefsViaGateway } from "../../cli/command-secret-gateway.js";
import { getModelsCommandSecretTargetIds } from "../../cli/command-secret-targets.js";
import type { RuntimeEnv } from "../../runtime.js";
import {
getRuntimeConfig,
readSourceConfigSnapshotForWrite,
setRuntimeConfigSnapshot,
type OpenClawConfig,
} from "../../config/config.js";
import type { RuntimeEnv } from "../../runtime.js";
getModelsCommandSecretTargetIds,
resolveCommandSecretRefsViaGateway,
} from "./load-config.runtime.js";
export type LoadedModelsConfig = {
sourceConfig: OpenClawConfig;

View File

@@ -1,4 +1,5 @@
import { vi, type Mock } from "vitest";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch-error.js";
type CronSessionEntry = {
sessionId: string;
@@ -60,101 +61,79 @@ export const resolveCronPayloadOutcomeMock = createMock();
export const resolveCronDeliveryPlanMock = createMock();
export const resolveDeliveryTargetMock = createMock();
export const resolveSessionAuthProfileOverrideMock = createMock();
const resolveBootstrapWarningSignaturesSeenMock = createMock();
const resolveCronStyleNowMock = createMock();
const resolveFastModeStateMock = createMock();
const resolveNestedAgentLaneMock = createMock();
const resolveAgentTimeoutMsMock = createMock();
const deriveSessionTotalTokensMock = createMock();
const hasNonzeroUsageMock = createMock();
const ensureAgentWorkspaceMock = createMock();
const normalizeThinkLevelMock = createMock();
const normalizeVerboseLevelMock = createMock();
const supportsXHighThinkingMock = createMock();
const resolveSessionTranscriptPathMock = createMock();
const setSessionRuntimeModelMock = createMock();
const registerAgentRunContextMock = createMock();
const buildSafeExternalPromptMock = createMock();
const detectSuspiciousPatternsMock = createMock();
const mapHookExternalContentSourceMock = createMock();
const isExternalHookSessionMock = createMock();
const resolveHookExternalContentSourceMock = createMock();
const getSkillsSnapshotVersionMock = createMock();
const loadModelCatalogMock = createMock();
const getRemoteSkillEligibilityMock = createMock();
vi.mock("../../agents/agent-scope.js", () => ({
vi.mock("./run.runtime.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", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/model-selection.js")>();
return {
...actual,
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")>();
});
vi.mock("../../agents/pi-embedded.js", () => ({
resolveBootstrapWarningSignaturesSeen: resolveBootstrapWarningSignaturesSeenMock,
lookupContextTokens: lookupContextTokensMock,
resolveCronStyleNow: resolveCronStyleNowMock,
DEFAULT_CONTEXT_TOKENS: 128000,
DEFAULT_MODEL: "gpt-4",
DEFAULT_PROVIDER: "openai",
resolveFastModeState: resolveFastModeStateMock,
resolveNestedAgentLane: resolveNestedAgentLaneMock,
LiveSessionModelSwitchError,
loadModelCatalog: loadModelCatalogMock,
runWithModelFallback: runWithModelFallbackMock,
getModelRefStatus: getModelRefStatusMock,
isCliProvider: isCliProviderMock,
normalizeModelSelection: normalizeModelSelectionForTest,
resolveAllowedModelRef: resolveAllowedModelRefMock,
resolveConfiguredModelRef: resolveConfiguredModelRefMock,
resolveHooksGmailModel: resolveHooksGmailModelMock,
resolveThinkingDefault: resolveThinkingDefaultMock,
runEmbeddedPiAgent: runEmbeddedPiAgentMock,
}));
vi.mock("../../agents/context.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/context.js")>();
return {
...actual,
lookupContextTokens: lookupContextTokensMock,
};
});
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-read.js", () => ({
buildWorkspaceSkillSnapshot: buildWorkspaceSkillSnapshotMock,
getSkillsSnapshotVersion: getSkillsSnapshotVersionMock,
countActiveDescendantRuns: countActiveDescendantRunsMock,
listDescendantRunsForRequester: listDescendantRunsForRequesterMock,
resolveAgentTimeoutMs: resolveAgentTimeoutMsMock,
deriveSessionTotalTokens: deriveSessionTotalTokensMock,
hasNonzeroUsage: hasNonzeroUsageMock,
DEFAULT_IDENTITY_FILENAME: "IDENTITY.md",
ensureAgentWorkspace: ensureAgentWorkspaceMock,
normalizeThinkLevel: normalizeThinkLevelMock,
normalizeVerboseLevel: normalizeVerboseLevelMock,
supportsXHighThinking: supportsXHighThinkingMock,
resolveSessionTranscriptPath: resolveSessionTranscriptPathMock,
setSessionRuntimeModel: setSessionRuntimeModelMock,
registerAgentRunContext: registerAgentRunContextMock,
logWarn: (...args: unknown[]) => logWarnMock(...args),
normalizeAgentId: vi.fn((id: string) => id),
buildSafeExternalPrompt: buildSafeExternalPromptMock,
detectSuspiciousPatterns: detectSuspiciousPatternsMock,
mapHookExternalContentSource: mapHookExternalContentSourceMock,
isExternalHookSession: isExternalHookSessionMock,
resolveHookExternalContentSource: resolveHookExternalContentSourceMock,
getRemoteSkillEligibility: getRemoteSkillEligibilityMock,
}));
vi.mock("../../agents/cli-runner.runtime.js", () => ({
@@ -163,96 +142,10 @@ vi.mock("../../agents/cli-runner.runtime.js", () => ({
setCliSessionId: vi.fn(),
}));
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-plan.js", () => ({
resolveCronDeliveryPlan: resolveCronDeliveryPlanMock,
}));
@@ -275,16 +168,6 @@ 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",
@@ -349,6 +232,33 @@ export function resetRunCronIsolatedAgentTurnHarness(): void {
resolveThinkingDefaultMock.mockReturnValue("off");
getModelRefStatusMock.mockReturnValue({ allowed: false });
isCliProviderMock.mockReturnValue(false);
resolveBootstrapWarningSignaturesSeenMock.mockReturnValue(new Set());
resolveCronStyleNowMock.mockReturnValue({
formattedTime: "2026-02-10 12:00",
timeLine: "Current time: 2026-02-10 12:00 UTC",
});
resolveFastModeStateMock.mockReturnValue({ enabled: false });
resolveNestedAgentLaneMock.mockReturnValue(undefined);
resolveAgentTimeoutMsMock.mockReturnValue(60_000);
deriveSessionTotalTokensMock.mockReturnValue(30);
hasNonzeroUsageMock.mockReturnValue(true);
ensureAgentWorkspaceMock.mockResolvedValue({ dir: "/tmp/workspace" });
normalizeThinkLevelMock.mockImplementation((value: unknown) => value);
normalizeVerboseLevelMock.mockImplementation((value: unknown) => value ?? "off");
supportsXHighThinkingMock.mockReturnValue(false);
resolveSessionTranscriptPathMock.mockReturnValue("/tmp/transcript.jsonl");
setSessionRuntimeModelMock.mockReturnValue(undefined);
registerAgentRunContextMock.mockReturnValue(undefined);
buildSafeExternalPromptMock.mockImplementation(
({ message }: { message?: string }) => message ?? "",
);
detectSuspiciousPatternsMock.mockReturnValue([]);
mapHookExternalContentSourceMock.mockReturnValue("unknown");
isExternalHookSessionMock.mockReturnValue(false);
resolveHookExternalContentSourceMock.mockReturnValue(undefined);
getSkillsSnapshotVersionMock.mockReturnValue(42);
loadModelCatalogMock.mockResolvedValue({ models: [] });
getRemoteSkillEligibilityMock.mockResolvedValue({ remoteSkillsEnabled: false });
runWithModelFallbackMock.mockReset();
runWithModelFallbackMock.mockResolvedValue(makeDefaultModelFallbackResult());

View File

@@ -1,48 +1,5 @@
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 { lookupContextTokens } from "../../agents/context.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-read.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 { 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 { resolveCronDeliveryPlan } from "../delivery-plan.js";
import type { CronJob, CronRunOutcome, CronRunTelemetry } from "../types.js";
import {
@@ -58,6 +15,45 @@ 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,
hasNonzeroUsage,
isCliProvider,
isExternalHookSession,
listDescendantRunsForRequester,
loadModelCatalog,
logWarn,
lookupContextTokens,
mapHookExternalContentSource,
normalizeAgentId,
normalizeThinkLevel,
normalizeVerboseLevel,
registerAgentRunContext,
resolveAgentConfig,
resolveAgentDir,
resolveAgentModelFallbacksOverride,
resolveAgentTimeoutMs,
resolveAgentWorkspaceDir,
resolveBootstrapWarningSignaturesSeen,
resolveCronStyleNow,
resolveDefaultAgentId,
resolveFastModeState,
resolveHookExternalContentSource,
resolveNestedAgentLane,
resolveSessionAuthProfileOverride,
resolveSessionTranscriptPath,
resolveThinkingDefault,
runEmbeddedPiAgent,
runWithModelFallback,
setSessionRuntimeModel,
supportsXHighThinking,
} from "./run.runtime.js";
import { resolveCronAgentSessionKey } from "./session-key.js";
import { resolveCronSession } from "./session.js";
import { resolveCronSkillsSnapshot } from "./skills-snapshot.js";