mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-19 14:00:51 +00:00
Agents: stabilize overflow runner test harness
This commit is contained in:
406
src/agents/pi-embedded-runner/run.overflow-compaction.harness.ts
Normal file
406
src/agents/pi-embedded-runner/run.overflow-compaction.harness.ts
Normal file
@@ -0,0 +1,406 @@
|
||||
import { vi, type Mock } from "vitest";
|
||||
import type { ThinkLevel } from "../../auto-reply/thinking.js";
|
||||
import type {
|
||||
PluginHookAgentContext,
|
||||
PluginHookBeforeAgentStartResult,
|
||||
PluginHookBeforeModelResolveResult,
|
||||
PluginHookBeforePromptBuildResult,
|
||||
} from "../../plugins/types.js";
|
||||
import type { EmbeddedRunAttemptResult } from "./run/types.js";
|
||||
|
||||
type MockCompactionResult =
|
||||
| {
|
||||
ok: true;
|
||||
compacted: true;
|
||||
result: {
|
||||
summary: string;
|
||||
firstKeptEntryId?: string;
|
||||
tokensBefore?: number;
|
||||
tokensAfter?: number;
|
||||
};
|
||||
reason?: string;
|
||||
}
|
||||
| {
|
||||
ok: false;
|
||||
compacted: false;
|
||||
reason: string;
|
||||
result?: undefined;
|
||||
};
|
||||
|
||||
export const mockedGlobalHookRunner = {
|
||||
hasHooks: vi.fn((_hookName: string) => false),
|
||||
runBeforeAgentStart: vi.fn(
|
||||
async (
|
||||
_event: { prompt: string; messages?: unknown[] },
|
||||
_ctx: PluginHookAgentContext,
|
||||
): Promise<PluginHookBeforeAgentStartResult | undefined> => undefined,
|
||||
),
|
||||
runBeforePromptBuild: vi.fn(
|
||||
async (
|
||||
_event: { prompt: string; messages: unknown[] },
|
||||
_ctx: PluginHookAgentContext,
|
||||
): Promise<PluginHookBeforePromptBuildResult | undefined> => undefined,
|
||||
),
|
||||
runBeforeModelResolve: vi.fn(
|
||||
async (
|
||||
_event: { prompt: string },
|
||||
_ctx: PluginHookAgentContext,
|
||||
): Promise<PluginHookBeforeModelResolveResult | undefined> => undefined,
|
||||
),
|
||||
runBeforeCompaction: vi.fn(async () => undefined),
|
||||
runAfterCompaction: vi.fn(async () => undefined),
|
||||
};
|
||||
|
||||
export const mockedContextEngine = {
|
||||
info: { ownsCompaction: false as boolean },
|
||||
compact: vi.fn<(params: unknown) => Promise<MockCompactionResult>>(async () => ({
|
||||
ok: false as const,
|
||||
compacted: false as const,
|
||||
reason: "nothing to compact",
|
||||
})),
|
||||
};
|
||||
|
||||
export const mockedContextEngineCompact = mockedContextEngine.compact;
|
||||
export const mockedCompactDirect = mockedContextEngine.compact;
|
||||
export const mockedEnsureRuntimePluginsLoaded = vi.fn<(params?: unknown) => void>();
|
||||
export const mockedPrepareProviderRuntimeAuth = vi.fn(async () => undefined);
|
||||
export const mockedRunEmbeddedAttempt =
|
||||
vi.fn<(params: unknown) => Promise<EmbeddedRunAttemptResult>>();
|
||||
export const mockedSessionLikelyHasOversizedToolResults = vi.fn(() => false);
|
||||
export const mockedTruncateOversizedToolResultsInSession = vi.fn<
|
||||
() => Promise<MockTruncateOversizedToolResultsResult>
|
||||
>(async () => ({
|
||||
truncated: false,
|
||||
truncatedCount: 0,
|
||||
reason: "no oversized tool results",
|
||||
}));
|
||||
|
||||
type MockFailoverErrorDescription = {
|
||||
message: string;
|
||||
reason: string | undefined;
|
||||
status: number | undefined;
|
||||
code: string | undefined;
|
||||
};
|
||||
|
||||
type MockCoerceToFailoverError = (
|
||||
err: unknown,
|
||||
params?: { provider?: string; model?: string; profileId?: string },
|
||||
) => unknown;
|
||||
type MockDescribeFailoverError = (err: unknown) => MockFailoverErrorDescription;
|
||||
type MockResolveFailoverStatus = (reason: string) => number | undefined;
|
||||
type MockTruncateOversizedToolResultsResult = {
|
||||
truncated: boolean;
|
||||
truncatedCount: number;
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
export const mockedCoerceToFailoverError = vi.fn<MockCoerceToFailoverError>();
|
||||
export const mockedDescribeFailoverError = vi.fn<MockDescribeFailoverError>(
|
||||
(err: unknown): MockFailoverErrorDescription => ({
|
||||
message: err instanceof Error ? err.message : String(err),
|
||||
reason: undefined,
|
||||
status: undefined,
|
||||
code: undefined,
|
||||
}),
|
||||
);
|
||||
export const mockedResolveFailoverStatus = vi.fn<MockResolveFailoverStatus>();
|
||||
|
||||
export const mockedLog: {
|
||||
debug: Mock<(...args: unknown[]) => void>;
|
||||
info: Mock<(...args: unknown[]) => void>;
|
||||
warn: Mock<(...args: unknown[]) => void>;
|
||||
error: Mock<(...args: unknown[]) => void>;
|
||||
isEnabled: Mock<(level?: string) => boolean>;
|
||||
} = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
isEnabled: vi.fn(() => false),
|
||||
};
|
||||
|
||||
export const mockedClassifyFailoverReason = vi.fn(() => null);
|
||||
export const mockedExtractObservedOverflowTokenCount = vi.fn((msg?: string) => {
|
||||
const match = msg?.match(/prompt is too long:\s*([\d,]+)\s+tokens\s*>\s*[\d,]+\s+maximum/i);
|
||||
return match?.[1] ? Number(match[1].replaceAll(",", "")) : undefined;
|
||||
});
|
||||
export const mockedIsCompactionFailureError = vi.fn(() => false);
|
||||
export const mockedIsLikelyContextOverflowError = vi.fn((msg?: string) => {
|
||||
const lower = (msg ?? "").toLowerCase();
|
||||
return (
|
||||
lower.includes("request_too_large") ||
|
||||
lower.includes("context window exceeded") ||
|
||||
lower.includes("prompt is too long")
|
||||
);
|
||||
});
|
||||
export const mockedPickFallbackThinkingLevel = vi.fn<(params?: unknown) => ThinkLevel | null>(
|
||||
() => null,
|
||||
);
|
||||
|
||||
export const overflowBaseRunParams = {
|
||||
sessionId: "test-session",
|
||||
sessionKey: "test-key",
|
||||
sessionFile: "/tmp/session.json",
|
||||
workspaceDir: "/tmp/workspace",
|
||||
prompt: "hello",
|
||||
timeoutMs: 30000,
|
||||
runId: "run-1",
|
||||
} as const;
|
||||
|
||||
export function resetRunOverflowCompactionHarnessMocks(): void {
|
||||
mockedGlobalHookRunner.hasHooks.mockReset();
|
||||
mockedGlobalHookRunner.hasHooks.mockReturnValue(false);
|
||||
mockedGlobalHookRunner.runBeforeAgentStart.mockReset();
|
||||
mockedGlobalHookRunner.runBeforeAgentStart.mockResolvedValue(undefined);
|
||||
mockedGlobalHookRunner.runBeforePromptBuild.mockReset();
|
||||
mockedGlobalHookRunner.runBeforePromptBuild.mockResolvedValue(undefined);
|
||||
mockedGlobalHookRunner.runBeforeModelResolve.mockReset();
|
||||
mockedGlobalHookRunner.runBeforeModelResolve.mockResolvedValue(undefined);
|
||||
mockedGlobalHookRunner.runBeforeCompaction.mockReset();
|
||||
mockedGlobalHookRunner.runBeforeCompaction.mockResolvedValue(undefined);
|
||||
mockedGlobalHookRunner.runAfterCompaction.mockReset();
|
||||
mockedGlobalHookRunner.runAfterCompaction.mockResolvedValue(undefined);
|
||||
|
||||
mockedContextEngine.info.ownsCompaction = false;
|
||||
mockedContextEngineCompact.mockReset();
|
||||
mockedContextEngineCompact.mockResolvedValue({
|
||||
ok: false,
|
||||
compacted: false,
|
||||
reason: "nothing to compact",
|
||||
});
|
||||
|
||||
mockedEnsureRuntimePluginsLoaded.mockReset();
|
||||
mockedPrepareProviderRuntimeAuth.mockReset();
|
||||
mockedPrepareProviderRuntimeAuth.mockResolvedValue(undefined);
|
||||
mockedRunEmbeddedAttempt.mockReset();
|
||||
mockedSessionLikelyHasOversizedToolResults.mockReset();
|
||||
mockedSessionLikelyHasOversizedToolResults.mockReturnValue(false);
|
||||
mockedTruncateOversizedToolResultsInSession.mockReset();
|
||||
mockedTruncateOversizedToolResultsInSession.mockResolvedValue({
|
||||
truncated: false,
|
||||
truncatedCount: 0,
|
||||
reason: "no oversized tool results",
|
||||
});
|
||||
|
||||
mockedCoerceToFailoverError.mockReset();
|
||||
mockedCoerceToFailoverError.mockReturnValue(null);
|
||||
mockedDescribeFailoverError.mockReset();
|
||||
mockedDescribeFailoverError.mockImplementation(
|
||||
(err: unknown): MockFailoverErrorDescription => ({
|
||||
message: err instanceof Error ? err.message : String(err),
|
||||
reason: undefined,
|
||||
status: undefined,
|
||||
code: undefined,
|
||||
}),
|
||||
);
|
||||
mockedResolveFailoverStatus.mockReset();
|
||||
mockedResolveFailoverStatus.mockReturnValue(undefined);
|
||||
|
||||
mockedLog.debug.mockReset();
|
||||
mockedLog.info.mockReset();
|
||||
mockedLog.warn.mockReset();
|
||||
mockedLog.error.mockReset();
|
||||
mockedLog.isEnabled.mockReset();
|
||||
mockedLog.isEnabled.mockReturnValue(false);
|
||||
|
||||
mockedClassifyFailoverReason.mockReset();
|
||||
mockedClassifyFailoverReason.mockReturnValue(null);
|
||||
mockedExtractObservedOverflowTokenCount.mockReset();
|
||||
mockedExtractObservedOverflowTokenCount.mockImplementation((msg?: string) => {
|
||||
const match = msg?.match(/prompt is too long:\s*([\d,]+)\s+tokens\s*>\s*[\d,]+\s+maximum/i);
|
||||
return match?.[1] ? Number(match[1].replaceAll(",", "")) : undefined;
|
||||
});
|
||||
mockedIsCompactionFailureError.mockReset();
|
||||
mockedIsCompactionFailureError.mockReturnValue(false);
|
||||
mockedIsLikelyContextOverflowError.mockReset();
|
||||
mockedIsLikelyContextOverflowError.mockImplementation((msg?: string) => {
|
||||
const lower = (msg ?? "").toLowerCase();
|
||||
return (
|
||||
lower.includes("request_too_large") ||
|
||||
lower.includes("context window exceeded") ||
|
||||
lower.includes("prompt is too long")
|
||||
);
|
||||
});
|
||||
mockedPickFallbackThinkingLevel.mockReset();
|
||||
mockedPickFallbackThinkingLevel.mockReturnValue(null);
|
||||
}
|
||||
|
||||
export async function loadRunOverflowCompactionHarness(): Promise<{
|
||||
runEmbeddedPiAgent: typeof import("./run.js").runEmbeddedPiAgent;
|
||||
}> {
|
||||
resetRunOverflowCompactionHarnessMocks();
|
||||
vi.resetModules();
|
||||
|
||||
vi.doMock("../../plugins/hook-runner-global.js", () => ({
|
||||
getGlobalHookRunner: vi.fn(() => mockedGlobalHookRunner),
|
||||
}));
|
||||
|
||||
vi.doMock("../../context-engine/index.js", () => ({
|
||||
ensureContextEnginesInitialized: vi.fn(),
|
||||
resolveContextEngine: vi.fn(async () => mockedContextEngine),
|
||||
}));
|
||||
|
||||
vi.doMock("../runtime-plugins.js", () => ({
|
||||
ensureRuntimePluginsLoaded: mockedEnsureRuntimePluginsLoaded,
|
||||
}));
|
||||
|
||||
vi.doMock("../../plugins/provider-runtime.js", () => ({
|
||||
prepareProviderRuntimeAuth: mockedPrepareProviderRuntimeAuth,
|
||||
}));
|
||||
|
||||
vi.doMock("../auth-profiles.js", () => ({
|
||||
isProfileInCooldown: vi.fn(() => false),
|
||||
markAuthProfileFailure: vi.fn(async () => {}),
|
||||
markAuthProfileGood: vi.fn(async () => {}),
|
||||
markAuthProfileUsed: vi.fn(async () => {}),
|
||||
resolveProfilesUnavailableReason: vi.fn(() => undefined),
|
||||
}));
|
||||
|
||||
vi.doMock("../usage.js", () => ({
|
||||
normalizeUsage: vi.fn((usage?: unknown) =>
|
||||
usage && typeof usage === "object" ? usage : undefined,
|
||||
),
|
||||
derivePromptTokens: vi.fn(
|
||||
(usage?: { input?: number; cacheRead?: number; cacheWrite?: number }) =>
|
||||
usage
|
||||
? (() => {
|
||||
const sum = (usage.input ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
|
||||
return sum > 0 ? sum : undefined;
|
||||
})()
|
||||
: undefined,
|
||||
),
|
||||
}));
|
||||
|
||||
vi.doMock("../workspace-run.js", () => ({
|
||||
resolveRunWorkspaceDir: vi.fn((params: { workspaceDir: string }) => ({
|
||||
workspaceDir: params.workspaceDir,
|
||||
usedFallback: false,
|
||||
fallbackReason: undefined,
|
||||
agentId: "main",
|
||||
})),
|
||||
redactRunIdentifier: vi.fn((value?: string) => value ?? ""),
|
||||
}));
|
||||
|
||||
vi.doMock("../pi-embedded-helpers.js", () => ({
|
||||
formatBillingErrorMessage: vi.fn(() => ""),
|
||||
classifyFailoverReason: mockedClassifyFailoverReason,
|
||||
extractObservedOverflowTokenCount: mockedExtractObservedOverflowTokenCount,
|
||||
formatAssistantErrorText: vi.fn(() => ""),
|
||||
isAuthAssistantError: vi.fn(() => false),
|
||||
isBillingAssistantError: vi.fn(() => false),
|
||||
isCompactionFailureError: mockedIsCompactionFailureError,
|
||||
isLikelyContextOverflowError: mockedIsLikelyContextOverflowError,
|
||||
isFailoverAssistantError: vi.fn(() => false),
|
||||
isFailoverErrorMessage: vi.fn(() => false),
|
||||
parseImageSizeError: vi.fn(() => null),
|
||||
parseImageDimensionError: vi.fn(() => null),
|
||||
isRateLimitAssistantError: vi.fn(() => false),
|
||||
isTimeoutErrorMessage: vi.fn(() => false),
|
||||
pickFallbackThinkingLevel: mockedPickFallbackThinkingLevel,
|
||||
}));
|
||||
|
||||
vi.doMock("./run/attempt.js", () => ({
|
||||
runEmbeddedAttempt: mockedRunEmbeddedAttempt,
|
||||
}));
|
||||
|
||||
vi.doMock("./model.js", () => ({
|
||||
resolveModelAsync: vi.fn(async () => ({
|
||||
model: {
|
||||
id: "test-model",
|
||||
provider: "anthropic",
|
||||
contextWindow: 200000,
|
||||
api: "messages",
|
||||
},
|
||||
error: null,
|
||||
authStorage: {
|
||||
setRuntimeApiKey: vi.fn(),
|
||||
},
|
||||
modelRegistry: {},
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.doMock("../model-auth.js", () => ({
|
||||
applyLocalNoAuthHeaderOverride: vi.fn((model: unknown) => model),
|
||||
ensureAuthProfileStore: vi.fn(() => ({})),
|
||||
getApiKeyForModel: vi.fn(async () => ({
|
||||
apiKey: "test-key",
|
||||
profileId: "test-profile",
|
||||
source: "test",
|
||||
})),
|
||||
resolveAuthProfileOrder: vi.fn(() => []),
|
||||
}));
|
||||
|
||||
vi.doMock("../models-config.js", () => ({
|
||||
ensureOpenClawModelsJson: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.doMock("../context-window-guard.js", () => ({
|
||||
CONTEXT_WINDOW_HARD_MIN_TOKENS: 1000,
|
||||
CONTEXT_WINDOW_WARN_BELOW_TOKENS: 5000,
|
||||
evaluateContextWindowGuard: vi.fn(() => ({
|
||||
shouldWarn: false,
|
||||
shouldBlock: false,
|
||||
tokens: 200000,
|
||||
source: "model",
|
||||
})),
|
||||
resolveContextWindowInfo: vi.fn(() => ({
|
||||
tokens: 200000,
|
||||
source: "model",
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.doMock("../../process/command-queue.js", () => ({
|
||||
enqueueCommandInLane: vi.fn((_lane: string, task: () => unknown) => task()),
|
||||
}));
|
||||
|
||||
vi.doMock("../../utils/message-channel.js", () => ({
|
||||
isMarkdownCapableMessageChannel: vi.fn(() => true),
|
||||
}));
|
||||
|
||||
vi.doMock("../agent-paths.js", () => ({
|
||||
resolveOpenClawAgentDir: vi.fn(() => "/tmp/agent-dir"),
|
||||
}));
|
||||
|
||||
vi.doMock("../defaults.js", () => ({
|
||||
DEFAULT_CONTEXT_TOKENS: 200000,
|
||||
DEFAULT_MODEL: "test-model",
|
||||
DEFAULT_PROVIDER: "anthropic",
|
||||
}));
|
||||
|
||||
vi.doMock("../failover-error.js", () => ({
|
||||
FailoverError: class extends Error {},
|
||||
coerceToFailoverError: mockedCoerceToFailoverError,
|
||||
describeFailoverError: mockedDescribeFailoverError,
|
||||
resolveFailoverStatus: mockedResolveFailoverStatus,
|
||||
}));
|
||||
|
||||
vi.doMock("./lanes.js", () => ({
|
||||
resolveSessionLane: vi.fn(() => "session-lane"),
|
||||
resolveGlobalLane: vi.fn(() => "global-lane"),
|
||||
}));
|
||||
|
||||
vi.doMock("./logger.js", () => ({
|
||||
log: mockedLog,
|
||||
}));
|
||||
|
||||
vi.doMock("./run/payloads.js", () => ({
|
||||
buildEmbeddedRunPayloads: vi.fn(() => []),
|
||||
}));
|
||||
|
||||
vi.doMock("./tool-result-truncation.js", () => ({
|
||||
truncateOversizedToolResultsInSession: mockedTruncateOversizedToolResultsInSession,
|
||||
sessionLikelyHasOversizedToolResults: mockedSessionLikelyHasOversizedToolResults,
|
||||
}));
|
||||
|
||||
vi.doMock("./utils.js", () => ({
|
||||
describeUnknownError: vi.fn((err: unknown) => {
|
||||
if (err instanceof Error) {
|
||||
return err.message;
|
||||
}
|
||||
return String(err);
|
||||
}),
|
||||
}));
|
||||
|
||||
const { runEmbeddedPiAgent } = await import("./run.js");
|
||||
return { runEmbeddedPiAgent };
|
||||
}
|
||||
@@ -1,17 +1,4 @@
|
||||
import "./run.overflow-compaction.mocks.shared.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { isCompactionFailureError, isLikelyContextOverflowError } from "../pi-embedded-helpers.js";
|
||||
|
||||
vi.mock(import("../../utils.js"), async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
resolveUserPath: vi.fn((p: string) => p),
|
||||
};
|
||||
});
|
||||
|
||||
import { log } from "./logger.js";
|
||||
import { runEmbeddedPiAgent } from "./run.js";
|
||||
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
makeAttemptResult,
|
||||
makeCompactionSuccess,
|
||||
@@ -20,26 +7,38 @@ import {
|
||||
queueOverflowAttemptWithOversizedToolOutput,
|
||||
} from "./run.overflow-compaction.fixture.js";
|
||||
import {
|
||||
loadRunOverflowCompactionHarness,
|
||||
mockedContextEngine,
|
||||
mockedCompactDirect,
|
||||
mockedIsCompactionFailureError,
|
||||
mockedIsLikelyContextOverflowError,
|
||||
mockedLog,
|
||||
mockedRunEmbeddedAttempt,
|
||||
mockedSessionLikelyHasOversizedToolResults,
|
||||
mockedTruncateOversizedToolResultsInSession,
|
||||
overflowBaseRunParams as baseParams,
|
||||
} from "./run.overflow-compaction.shared-test.js";
|
||||
} from "./run.overflow-compaction.harness.js";
|
||||
import type { EmbeddedRunAttemptResult } from "./run/types.js";
|
||||
|
||||
const mockedIsCompactionFailureError = vi.mocked(isCompactionFailureError);
|
||||
const mockedIsLikelyContextOverflowError = vi.mocked(isLikelyContextOverflowError);
|
||||
let runEmbeddedPiAgent: typeof import("./run.js").runEmbeddedPiAgent;
|
||||
|
||||
describe("overflow compaction in run loop", () => {
|
||||
beforeAll(async () => {
|
||||
({ runEmbeddedPiAgent } = await loadRunOverflowCompactionHarness());
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockedRunEmbeddedAttempt.mockReset();
|
||||
mockedCompactDirect.mockReset();
|
||||
mockedSessionLikelyHasOversizedToolResults.mockReset();
|
||||
mockedTruncateOversizedToolResultsInSession.mockReset();
|
||||
mockedContextEngine.info.ownsCompaction = false;
|
||||
mockedLog.debug.mockReset();
|
||||
mockedLog.info.mockReset();
|
||||
mockedLog.warn.mockReset();
|
||||
mockedLog.error.mockReset();
|
||||
mockedLog.isEnabled.mockReset();
|
||||
mockedLog.isEnabled.mockReturnValue(false);
|
||||
mockedIsCompactionFailureError.mockImplementation((msg?: string) => {
|
||||
if (!msg) {
|
||||
return false;
|
||||
@@ -87,12 +86,14 @@ describe("overflow compaction in run loop", () => {
|
||||
}),
|
||||
);
|
||||
expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(2);
|
||||
expect(log.warn).toHaveBeenCalledWith(
|
||||
expect(mockedLog.warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"context overflow detected (attempt 1/3); attempting auto-compaction",
|
||||
),
|
||||
);
|
||||
expect(log.info).toHaveBeenCalledWith(expect.stringContaining("auto-compaction succeeded"));
|
||||
expect(mockedLog.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining("auto-compaction succeeded"),
|
||||
);
|
||||
// Should not be an error result
|
||||
expect(result.meta.error).toBeUndefined();
|
||||
});
|
||||
@@ -116,7 +117,7 @@ describe("overflow compaction in run loop", () => {
|
||||
|
||||
expect(mockedCompactDirect).toHaveBeenCalledTimes(1);
|
||||
expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(2);
|
||||
expect(log.warn).toHaveBeenCalledWith(expect.stringContaining("source=promptError"));
|
||||
expect(mockedLog.warn).toHaveBeenCalledWith(expect.stringContaining("source=promptError"));
|
||||
expect(result.meta.error).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -137,7 +138,7 @@ describe("overflow compaction in run loop", () => {
|
||||
expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(1);
|
||||
expect(result.meta.error?.kind).toBe("context_overflow");
|
||||
expect(result.payloads?.[0]?.isError).toBe(true);
|
||||
expect(log.warn).toHaveBeenCalledWith(expect.stringContaining("auto-compaction failed"));
|
||||
expect(mockedLog.warn).toHaveBeenCalledWith(expect.stringContaining("auto-compaction failed"));
|
||||
});
|
||||
|
||||
it("falls back to tool-result truncation and retries when oversized results are detected", async () => {
|
||||
@@ -165,7 +166,9 @@ describe("overflow compaction in run loop", () => {
|
||||
expect.objectContaining({ sessionFile: "/tmp/session.json" }),
|
||||
);
|
||||
expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(2);
|
||||
expect(log.info).toHaveBeenCalledWith(expect.stringContaining("Truncated 1 tool result(s)"));
|
||||
expect(mockedLog.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Truncated 1 tool result(s)"),
|
||||
);
|
||||
expect(result.meta.error).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -284,7 +287,7 @@ describe("overflow compaction in run loop", () => {
|
||||
|
||||
expect(mockedCompactDirect).toHaveBeenCalledTimes(1);
|
||||
expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(2);
|
||||
expect(log.warn).toHaveBeenCalledWith(expect.stringContaining("source=assistantError"));
|
||||
expect(mockedLog.warn).toHaveBeenCalledWith(expect.stringContaining("source=assistantError"));
|
||||
expect(result.meta.error).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -302,7 +305,9 @@ describe("overflow compaction in run loop", () => {
|
||||
await expect(runEmbeddedPiAgent(baseParams)).rejects.toThrow("transport disconnected");
|
||||
|
||||
expect(mockedCompactDirect).not.toHaveBeenCalled();
|
||||
expect(log.warn).not.toHaveBeenCalledWith(expect.stringContaining("source=assistantError"));
|
||||
expect(mockedLog.warn).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("source=assistantError"),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns an explicit timeout payload when the run times out before producing any reply", async () => {
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
import { vi } from "vitest";
|
||||
import type {
|
||||
PluginHookAgentContext,
|
||||
PluginHookBeforeAgentStartResult,
|
||||
PluginHookBeforeModelResolveResult,
|
||||
PluginHookBeforePromptBuildResult,
|
||||
} from "../../plugins/types.js";
|
||||
|
||||
type MockCompactionResult =
|
||||
| {
|
||||
ok: true;
|
||||
compacted: true;
|
||||
result: {
|
||||
summary: string;
|
||||
firstKeptEntryId?: string;
|
||||
tokensBefore?: number;
|
||||
tokensAfter?: number;
|
||||
};
|
||||
reason?: string;
|
||||
}
|
||||
| {
|
||||
ok: false;
|
||||
compacted: false;
|
||||
reason: string;
|
||||
result?: undefined;
|
||||
};
|
||||
|
||||
export const mockedGlobalHookRunner = {
|
||||
hasHooks: vi.fn((_hookName: string) => false),
|
||||
runBeforeAgentStart: vi.fn(
|
||||
async (
|
||||
_event: { prompt: string; messages?: unknown[] },
|
||||
_ctx: PluginHookAgentContext,
|
||||
): Promise<PluginHookBeforeAgentStartResult | undefined> => undefined,
|
||||
),
|
||||
runBeforePromptBuild: vi.fn(
|
||||
async (
|
||||
_event: { prompt: string; messages: unknown[] },
|
||||
_ctx: PluginHookAgentContext,
|
||||
): Promise<PluginHookBeforePromptBuildResult | undefined> => undefined,
|
||||
),
|
||||
runBeforeModelResolve: vi.fn(
|
||||
async (
|
||||
_event: { prompt: string },
|
||||
_ctx: PluginHookAgentContext,
|
||||
): Promise<PluginHookBeforeModelResolveResult | undefined> => undefined,
|
||||
),
|
||||
runBeforeCompaction: vi.fn(async () => undefined),
|
||||
runAfterCompaction: vi.fn(async () => undefined),
|
||||
};
|
||||
|
||||
export const mockedContextEngine = {
|
||||
info: { ownsCompaction: false as boolean },
|
||||
compact: vi.fn<(params: unknown) => Promise<MockCompactionResult>>(async () => ({
|
||||
ok: false as const,
|
||||
compacted: false as const,
|
||||
reason: "nothing to compact",
|
||||
})),
|
||||
};
|
||||
|
||||
export const mockedContextEngineCompact = vi.mocked(mockedContextEngine.compact);
|
||||
export const mockedEnsureRuntimePluginsLoaded: (...args: unknown[]) => void = vi.fn();
|
||||
|
||||
vi.mock("../../plugins/hook-runner-global.js", () => ({
|
||||
getGlobalHookRunner: vi.fn(() => mockedGlobalHookRunner),
|
||||
}));
|
||||
|
||||
vi.mock("../../context-engine/index.js", () => ({
|
||||
ensureContextEnginesInitialized: vi.fn(),
|
||||
resolveContextEngine: vi.fn(async () => mockedContextEngine),
|
||||
}));
|
||||
|
||||
vi.mock("../runtime-plugins.js", () => ({
|
||||
ensureRuntimePluginsLoaded: mockedEnsureRuntimePluginsLoaded,
|
||||
}));
|
||||
|
||||
vi.mock("../auth-profiles.js", () => ({
|
||||
isProfileInCooldown: vi.fn(() => false),
|
||||
markAuthProfileFailure: vi.fn(async () => {}),
|
||||
markAuthProfileGood: vi.fn(async () => {}),
|
||||
markAuthProfileUsed: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("../usage.js", () => ({
|
||||
normalizeUsage: vi.fn((usage?: unknown) =>
|
||||
usage && typeof usage === "object" ? usage : undefined,
|
||||
),
|
||||
derivePromptTokens: vi.fn((usage?: { input?: number; cacheRead?: number; cacheWrite?: number }) =>
|
||||
usage
|
||||
? (() => {
|
||||
const sum = (usage.input ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
|
||||
return sum > 0 ? sum : undefined;
|
||||
})()
|
||||
: undefined,
|
||||
),
|
||||
hasNonzeroUsage: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
vi.mock("../workspace-run.js", () => ({
|
||||
resolveRunWorkspaceDir: vi.fn((params: { workspaceDir: string }) => ({
|
||||
workspaceDir: params.workspaceDir,
|
||||
usedFallback: false,
|
||||
fallbackReason: undefined,
|
||||
agentId: "main",
|
||||
})),
|
||||
redactRunIdentifier: vi.fn((value?: string) => value ?? ""),
|
||||
}));
|
||||
|
||||
vi.mock("../pi-embedded-helpers.js", () => ({
|
||||
formatBillingErrorMessage: vi.fn(() => ""),
|
||||
classifyFailoverReason: vi.fn(() => null),
|
||||
extractObservedOverflowTokenCount: vi.fn((msg?: string) => {
|
||||
const match = msg?.match(/prompt is too long:\s*([\d,]+)\s+tokens\s*>\s*[\d,]+\s+maximum/i);
|
||||
return match?.[1] ? Number(match[1].replaceAll(",", "")) : undefined;
|
||||
}),
|
||||
formatAssistantErrorText: vi.fn(() => ""),
|
||||
isAuthAssistantError: vi.fn(() => false),
|
||||
isBillingAssistantError: vi.fn(() => false),
|
||||
isCompactionFailureError: vi.fn(() => false),
|
||||
isLikelyContextOverflowError: vi.fn((msg?: string) => {
|
||||
const lower = (msg ?? "").toLowerCase();
|
||||
return (
|
||||
lower.includes("request_too_large") ||
|
||||
lower.includes("context window exceeded") ||
|
||||
lower.includes("prompt is too long")
|
||||
);
|
||||
}),
|
||||
isFailoverAssistantError: vi.fn(() => false),
|
||||
isFailoverErrorMessage: vi.fn(() => false),
|
||||
parseImageSizeError: vi.fn(() => null),
|
||||
parseImageDimensionError: vi.fn(() => null),
|
||||
isRateLimitAssistantError: vi.fn(() => false),
|
||||
isTimeoutErrorMessage: vi.fn(() => false),
|
||||
pickFallbackThinkingLevel: vi.fn(() => null),
|
||||
}));
|
||||
|
||||
vi.mock("./run/attempt.js", () => ({
|
||||
runEmbeddedAttempt: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./compact.js", () => ({
|
||||
compactEmbeddedPiSessionDirect: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./model.js", () => ({
|
||||
resolveModel: vi.fn(() => ({
|
||||
model: {
|
||||
id: "test-model",
|
||||
provider: "anthropic",
|
||||
contextWindow: 200000,
|
||||
api: "messages",
|
||||
},
|
||||
error: null,
|
||||
authStorage: {
|
||||
setRuntimeApiKey: vi.fn(),
|
||||
},
|
||||
modelRegistry: {},
|
||||
})),
|
||||
resolveModelAsync: vi.fn(async () => ({
|
||||
model: {
|
||||
id: "test-model",
|
||||
provider: "anthropic",
|
||||
contextWindow: 200000,
|
||||
api: "messages",
|
||||
},
|
||||
error: null,
|
||||
authStorage: {
|
||||
setRuntimeApiKey: vi.fn(),
|
||||
},
|
||||
modelRegistry: {},
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../model-auth.js", () => ({
|
||||
ensureAuthProfileStore: vi.fn(() => ({})),
|
||||
getApiKeyForModel: vi.fn(async () => ({
|
||||
apiKey: "test-key",
|
||||
profileId: "test-profile",
|
||||
source: "test",
|
||||
})),
|
||||
resolveAuthProfileOrder: vi.fn(() => []),
|
||||
}));
|
||||
|
||||
vi.mock("../models-config.js", () => ({
|
||||
ensureOpenClawModelsJson: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("../context-window-guard.js", () => ({
|
||||
CONTEXT_WINDOW_HARD_MIN_TOKENS: 1000,
|
||||
CONTEXT_WINDOW_WARN_BELOW_TOKENS: 5000,
|
||||
evaluateContextWindowGuard: vi.fn(() => ({
|
||||
shouldWarn: false,
|
||||
shouldBlock: false,
|
||||
tokens: 200000,
|
||||
source: "model",
|
||||
})),
|
||||
resolveContextWindowInfo: vi.fn(() => ({
|
||||
tokens: 200000,
|
||||
source: "model",
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../../process/command-queue.js", () => ({
|
||||
enqueueCommandInLane: vi.fn((_lane: string, task: () => unknown) => task()),
|
||||
}));
|
||||
|
||||
vi.mock(import("../../utils/message-channel.js"), async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
isMarkdownCapableMessageChannel: vi.fn(() => true),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../agent-paths.js", () => ({
|
||||
resolveOpenClawAgentDir: vi.fn(() => "/tmp/agent-dir"),
|
||||
}));
|
||||
|
||||
vi.mock("../defaults.js", () => ({
|
||||
DEFAULT_CONTEXT_TOKENS: 200000,
|
||||
DEFAULT_MODEL: "test-model",
|
||||
DEFAULT_PROVIDER: "anthropic",
|
||||
}));
|
||||
|
||||
type MockFailoverErrorDescription = {
|
||||
message: string;
|
||||
reason: string | undefined;
|
||||
status: number | undefined;
|
||||
code: string | undefined;
|
||||
};
|
||||
|
||||
type MockCoerceToFailoverError = (
|
||||
err: unknown,
|
||||
params?: { provider?: string; model?: string; profileId?: string },
|
||||
) => unknown;
|
||||
type MockDescribeFailoverError = (err: unknown) => MockFailoverErrorDescription;
|
||||
type MockResolveFailoverStatus = (reason: string) => number | undefined;
|
||||
|
||||
export const mockedCoerceToFailoverError = vi.fn<MockCoerceToFailoverError>();
|
||||
export const mockedDescribeFailoverError = vi.fn<MockDescribeFailoverError>(
|
||||
(err: unknown): MockFailoverErrorDescription => ({
|
||||
message: err instanceof Error ? err.message : String(err),
|
||||
reason: undefined,
|
||||
status: undefined,
|
||||
code: undefined,
|
||||
}),
|
||||
);
|
||||
export const mockedResolveFailoverStatus = vi.fn<MockResolveFailoverStatus>();
|
||||
|
||||
vi.mock("../failover-error.js", () => ({
|
||||
FailoverError: class extends Error {},
|
||||
coerceToFailoverError: mockedCoerceToFailoverError,
|
||||
describeFailoverError: mockedDescribeFailoverError,
|
||||
resolveFailoverStatus: mockedResolveFailoverStatus,
|
||||
}));
|
||||
|
||||
vi.mock("./lanes.js", () => ({
|
||||
resolveSessionLane: vi.fn(() => "session-lane"),
|
||||
resolveGlobalLane: vi.fn(() => "global-lane"),
|
||||
}));
|
||||
|
||||
vi.mock("./logger.js", () => ({
|
||||
log: {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
isEnabled: vi.fn(() => false),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("./run/payloads.js", () => ({
|
||||
buildEmbeddedRunPayloads: vi.fn(() => []),
|
||||
}));
|
||||
|
||||
vi.mock("./tool-result-truncation.js", () => ({
|
||||
truncateOversizedToolResultsInSession: vi.fn(async () => ({
|
||||
truncated: false,
|
||||
truncatedCount: 0,
|
||||
reason: "no oversized tool results",
|
||||
})),
|
||||
sessionLikelyHasOversizedToolResults: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
vi.mock("./utils.js", () => ({
|
||||
describeUnknownError: vi.fn((err: unknown) => {
|
||||
if (err instanceof Error) {
|
||||
return err.message;
|
||||
}
|
||||
return String(err);
|
||||
}),
|
||||
}));
|
||||
@@ -1,30 +0,0 @@
|
||||
import { vi } from "vitest";
|
||||
import {
|
||||
mockedContextEngine,
|
||||
mockedContextEngineCompact,
|
||||
} from "./run.overflow-compaction.mocks.shared.js";
|
||||
import { runEmbeddedAttempt } from "./run/attempt.js";
|
||||
import {
|
||||
sessionLikelyHasOversizedToolResults,
|
||||
truncateOversizedToolResultsInSession,
|
||||
} from "./tool-result-truncation.js";
|
||||
|
||||
export const mockedRunEmbeddedAttempt = vi.mocked(runEmbeddedAttempt);
|
||||
export const mockedCompactDirect = mockedContextEngineCompact;
|
||||
export const mockedSessionLikelyHasOversizedToolResults = vi.mocked(
|
||||
sessionLikelyHasOversizedToolResults,
|
||||
);
|
||||
export const mockedTruncateOversizedToolResultsInSession = vi.mocked(
|
||||
truncateOversizedToolResultsInSession,
|
||||
);
|
||||
export { mockedContextEngine };
|
||||
|
||||
export const overflowBaseRunParams = {
|
||||
sessionId: "test-session",
|
||||
sessionKey: "test-key",
|
||||
sessionFile: "/tmp/session.json",
|
||||
workspaceDir: "/tmp/workspace",
|
||||
prompt: "hello",
|
||||
timeoutMs: 30000,
|
||||
runId: "run-1",
|
||||
} as const;
|
||||
@@ -1,7 +1,4 @@
|
||||
import "./run.overflow-compaction.mocks.shared.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { pickFallbackThinkingLevel } from "../pi-embedded-helpers.js";
|
||||
import { runEmbeddedPiAgent } from "./run.js";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
makeAttemptResult,
|
||||
makeCompactionSuccess,
|
||||
@@ -10,24 +7,30 @@ import {
|
||||
queueOverflowAttemptWithOversizedToolOutput,
|
||||
} from "./run.overflow-compaction.fixture.js";
|
||||
import {
|
||||
loadRunOverflowCompactionHarness,
|
||||
mockedCoerceToFailoverError,
|
||||
mockedDescribeFailoverError,
|
||||
mockedGlobalHookRunner,
|
||||
mockedPickFallbackThinkingLevel,
|
||||
mockedResolveFailoverStatus,
|
||||
} from "./run.overflow-compaction.mocks.shared.js";
|
||||
import {
|
||||
mockedContextEngine,
|
||||
mockedCompactDirect,
|
||||
mockedRunEmbeddedAttempt,
|
||||
mockedSessionLikelyHasOversizedToolResults,
|
||||
mockedTruncateOversizedToolResultsInSession,
|
||||
overflowBaseRunParams,
|
||||
} from "./run.overflow-compaction.shared-test.js";
|
||||
const mockedPickFallbackThinkingLevel = vi.mocked(pickFallbackThinkingLevel);
|
||||
} from "./run.overflow-compaction.harness.js";
|
||||
|
||||
let runEmbeddedPiAgent: typeof import("./run.js").runEmbeddedPiAgent;
|
||||
|
||||
describe("runEmbeddedPiAgent overflow compaction trigger routing", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
return loadRunOverflowCompactionHarness().then((loaded) => {
|
||||
runEmbeddedPiAgent = loaded.runEmbeddedPiAgent;
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockedRunEmbeddedAttempt.mockReset();
|
||||
mockedCompactDirect.mockReset();
|
||||
mockedCoerceToFailoverError.mockReset();
|
||||
@@ -257,7 +260,8 @@ describe("runEmbeddedPiAgent overflow compaction trigger routing", () => {
|
||||
it("returns retry_limit when repeated retries never converge", async () => {
|
||||
mockedRunEmbeddedAttempt.mockClear();
|
||||
mockedCompactDirect.mockClear();
|
||||
mockedPickFallbackThinkingLevel.mockClear();
|
||||
mockedPickFallbackThinkingLevel.mockReset();
|
||||
mockedPickFallbackThinkingLevel.mockReturnValue(null);
|
||||
mockedRunEmbeddedAttempt.mockResolvedValue(
|
||||
makeAttemptResult({ promptError: new Error("unsupported reasoning mode") }),
|
||||
);
|
||||
@@ -288,15 +292,15 @@ describe("runEmbeddedPiAgent overflow compaction trigger routing", () => {
|
||||
status: 429,
|
||||
});
|
||||
|
||||
mockedRunEmbeddedAttempt.mockResolvedValueOnce(makeAttemptResult({ promptError }));
|
||||
mockedCoerceToFailoverError.mockReturnValueOnce(normalized);
|
||||
mockedRunEmbeddedAttempt.mockResolvedValue(makeAttemptResult({ promptError }));
|
||||
mockedCoerceToFailoverError.mockReturnValue(normalized);
|
||||
mockedDescribeFailoverError.mockImplementation((err: unknown) => ({
|
||||
message: err instanceof Error ? err.message : String(err),
|
||||
reason: err === normalized ? "rate_limit" : undefined,
|
||||
status: err === normalized ? 429 : undefined,
|
||||
code: undefined,
|
||||
}));
|
||||
mockedResolveFailoverStatus.mockReturnValueOnce(429);
|
||||
mockedResolveFailoverStatus.mockReturnValue(429);
|
||||
|
||||
await expect(
|
||||
runEmbeddedPiAgent({
|
||||
|
||||
@@ -3,20 +3,25 @@
|
||||
* with no pending tool calls, so the parent session is idle when subagent
|
||||
* results arrive.
|
||||
*/
|
||||
import "./run.overflow-compaction.mocks.shared.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { runEmbeddedPiAgent } from "./run.js";
|
||||
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { makeAttemptResult } from "./run.overflow-compaction.fixture.js";
|
||||
import { mockedGlobalHookRunner } from "./run.overflow-compaction.mocks.shared.js";
|
||||
import {
|
||||
loadRunOverflowCompactionHarness,
|
||||
mockedGlobalHookRunner,
|
||||
mockedRunEmbeddedAttempt,
|
||||
overflowBaseRunParams,
|
||||
} from "./run.overflow-compaction.shared-test.js";
|
||||
} from "./run.overflow-compaction.harness.js";
|
||||
import { isEmbeddedPiRunActive, queueEmbeddedPiMessage } from "./runs.js";
|
||||
|
||||
let runEmbeddedPiAgent: typeof import("./run.js").runEmbeddedPiAgent;
|
||||
|
||||
describe("sessions_yield orchestration", () => {
|
||||
beforeAll(async () => {
|
||||
({ runEmbeddedPiAgent } = await loadRunOverflowCompactionHarness());
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockedRunEmbeddedAttempt.mockReset();
|
||||
mockedGlobalHookRunner.hasHooks.mockImplementation(() => false);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import "./run.overflow-compaction.mocks.shared.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
loadRunOverflowCompactionHarness,
|
||||
mockedEnsureRuntimePluginsLoaded,
|
||||
mockedRunEmbeddedAttempt,
|
||||
} from "./run.overflow-compaction.harness.js";
|
||||
|
||||
const runtimePluginMocks = vi.hoisted(() => ({
|
||||
ensureRuntimePluginsLoaded: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../runtime-plugins.js", () => ({
|
||||
ensureRuntimePluginsLoaded: runtimePluginMocks.ensureRuntimePluginsLoaded,
|
||||
}));
|
||||
|
||||
import { runEmbeddedPiAgent } from "./run.js";
|
||||
import { runEmbeddedAttempt } from "./run/attempt.js";
|
||||
|
||||
const mockedRunEmbeddedAttempt = vi.mocked(runEmbeddedAttempt);
|
||||
let runEmbeddedPiAgent: typeof import("./run.js").runEmbeddedPiAgent;
|
||||
|
||||
describe("runEmbeddedPiAgent usage reporting", () => {
|
||||
beforeAll(async () => {
|
||||
({ runEmbeddedPiAgent } = await loadRunOverflowCompactionHarness());
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockedEnsureRuntimePluginsLoaded.mockReset();
|
||||
mockedRunEmbeddedAttempt.mockReset();
|
||||
});
|
||||
|
||||
it("bootstraps runtime plugins with the resolved workspace before running", async () => {
|
||||
@@ -39,7 +37,7 @@ describe("runEmbeddedPiAgent usage reporting", () => {
|
||||
runId: "run-plugin-bootstrap",
|
||||
});
|
||||
|
||||
expect(runtimePluginMocks.ensureRuntimePluginsLoaded).toHaveBeenCalledWith({
|
||||
expect(mockedEnsureRuntimePluginsLoaded).toHaveBeenCalledWith({
|
||||
config: undefined,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
});
|
||||
@@ -66,7 +64,7 @@ describe("runEmbeddedPiAgent usage reporting", () => {
|
||||
allowGatewaySubagentBinding: true,
|
||||
});
|
||||
|
||||
expect(runtimePluginMocks.ensureRuntimePluginsLoaded).toHaveBeenCalledWith({
|
||||
expect(mockedEnsureRuntimePluginsLoaded).toHaveBeenCalledWith({
|
||||
config: undefined,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
allowGatewaySubagentBinding: true,
|
||||
|
||||
Reference in New Issue
Block a user