mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-26 16:06:16 +00:00
refactor: trim cron session startup imports
This commit is contained in:
22
src/agents/live-model-switch-error.ts
Normal file
22
src/agents/live-model-switch-error.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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, {
|
||||||
|
|||||||
Reference in New Issue
Block a user