diff --git a/src/acp/translator.session-rate-limit.test.ts b/src/acp/translator.session-rate-limit.test.ts index 162afe6160c..55446550f9f 100644 --- a/src/acp/translator.session-rate-limit.test.ts +++ b/src/acp/translator.session-rate-limit.test.ts @@ -308,7 +308,6 @@ describe("acp session UX bridge behavior", () => { "low", "medium", "high", - "xhigh", "adaptive", ]); expect(result.configOptions).toEqual( diff --git a/src/config/schema.labels.ts b/src/config/schema.labels.ts index 62302e976af..ae1c8d2829d 100644 --- a/src/config/schema.labels.ts +++ b/src/config/schema.labels.ts @@ -863,6 +863,9 @@ export const FIELD_LABELS: Record = { "plugins.entries.*.enabled": "Plugin Enabled", "plugins.entries.*.hooks": "Plugin Hook Policy", "plugins.entries.*.hooks.allowPromptInjection": "Allow Prompt Injection Hooks", + "plugins.entries.*.subagent": "Plugin Subagent Policy", + "plugins.entries.*.subagent.allowModelOverride": "Allow Plugin Subagent Model Override", + "plugins.entries.*.subagent.allowedModels": "Plugin Subagent Allowed Models", "plugins.entries.*.apiKey": "Plugin API Key", // pragma: allowlist secret "plugins.entries.*.env": "Plugin Environment Variables", "plugins.entries.*.config": "Plugin Config", diff --git a/src/media-understanding/apply.echo-transcript.test.ts b/src/media-understanding/apply.echo-transcript.test.ts index ae62d294989..6411ab0f48d 100644 --- a/src/media-understanding/apply.echo-transcript.test.ts +++ b/src/media-understanding/apply.echo-transcript.test.ts @@ -10,26 +10,28 @@ import { createSafeAudioFixtureBuffer } from "./runner.test-utils.js"; // Module mocks // --------------------------------------------------------------------------- -vi.mock("../agents/model-auth.js", () => ({ - resolveApiKeyForProvider: vi.fn(async () => ({ +type ResolveApiKeyForProvider = typeof import("../agents/model-auth.js").resolveApiKeyForProvider; + +const resolveApiKeyForProviderMock = vi.hoisted(() => + vi.fn(async () => ({ apiKey: "test-key", // pragma: allowlist secret source: "test", mode: "api-key", })), - requireApiKey: (auth: { apiKey?: string; mode?: string }, provider: string) => { - if (auth?.apiKey) { - return auth.apiKey; - } - throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth?.mode}).`); - }, - resolveAwsSdkEnvVarName: vi.fn(() => undefined), - resolveEnvApiKey: vi.fn(() => null), - resolveModelAuthMode: vi.fn(() => "api-key"), - getApiKeyForModel: vi.fn(async () => ({ apiKey: "test-key", source: "test", mode: "api-key" })), - getCustomProviderApiKey: vi.fn(() => undefined), - ensureAuthProfileStore: vi.fn(async () => ({})), - resolveAuthProfileOrder: vi.fn(() => []), -})); +); +const hasAvailableAuthForProviderMock = vi.hoisted(() => + vi.fn(async (...args: Parameters) => { + const resolved = await resolveApiKeyForProviderMock(...args); + return Boolean(resolved?.apiKey); + }), +); +const getApiKeyForModelMock = vi.hoisted(() => + vi.fn(async () => ({ apiKey: "test-key", source: "test", mode: "api-key" })), +); +const fetchRemoteMediaMock = vi.hoisted(() => vi.fn()); +const runExecMock = vi.hoisted(() => vi.fn()); +const runCommandWithTimeoutMock = vi.hoisted(() => vi.fn()); +const mockDeliverOutboundPayloads = vi.hoisted(() => vi.fn()); const { MediaFetchErrorMock } = vi.hoisted(() => { class MediaFetchErrorMock extends Error { @@ -43,22 +45,6 @@ const { MediaFetchErrorMock } = vi.hoisted(() => { return { MediaFetchErrorMock }; }); -vi.mock("../media/fetch.js", () => ({ - fetchRemoteMedia: vi.fn(), - MediaFetchError: MediaFetchErrorMock, -})); - -vi.mock("../process/exec.js", () => ({ - runExec: vi.fn(), - runCommandWithTimeout: vi.fn(), -})); - -const mockDeliverOutboundPayloads = vi.fn(); - -vi.mock("../infra/outbound/deliver.js", () => ({ - deliverOutboundPayloads: (...args: unknown[]) => mockDeliverOutboundPayloads(...args), -})); - // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- @@ -145,6 +131,38 @@ function createAudioConfigWithoutEchoFlag() { describe("applyMediaUnderstanding – echo transcript", () => { beforeAll(async () => { + vi.resetModules(); + vi.doMock("../agents/model-auth.js", () => ({ + resolveApiKeyForProvider: resolveApiKeyForProviderMock, + hasAvailableAuthForProvider: hasAvailableAuthForProviderMock, + requireApiKey: (auth: { apiKey?: string; mode?: string }, provider: string) => { + if (auth?.apiKey) { + return auth.apiKey; + } + throw new Error( + `No API key resolved for provider "${provider}" (auth mode: ${auth?.mode}).`, + ); + }, + resolveAwsSdkEnvVarName: vi.fn(() => undefined), + resolveEnvApiKey: vi.fn(() => null), + resolveModelAuthMode: vi.fn(() => "api-key"), + getApiKeyForModel: getApiKeyForModelMock, + getCustomProviderApiKey: vi.fn(() => undefined), + ensureAuthProfileStore: vi.fn(async () => ({})), + resolveAuthProfileOrder: vi.fn(() => []), + })); + vi.doMock("../media/fetch.js", () => ({ + fetchRemoteMedia: fetchRemoteMediaMock, + MediaFetchError: MediaFetchErrorMock, + })); + vi.doMock("../process/exec.js", () => ({ + runExec: runExecMock, + runCommandWithTimeout: runCommandWithTimeoutMock, + })); + vi.doMock("../infra/outbound/deliver-runtime.js", () => ({ + deliverOutboundPayloads: (...args: unknown[]) => mockDeliverOutboundPayloads(...args), + })); + const baseDir = resolvePreferredOpenClawTmpDir(); await fs.mkdir(baseDir, { recursive: true }); suiteTempMediaRootDir = await fs.mkdtemp(path.join(baseDir, TEMP_MEDIA_PREFIX)); @@ -155,6 +173,12 @@ describe("applyMediaUnderstanding – echo transcript", () => { }); beforeEach(() => { + resolveApiKeyForProviderMock.mockClear(); + hasAvailableAuthForProviderMock.mockClear(); + getApiKeyForModelMock.mockClear(); + fetchRemoteMediaMock.mockClear(); + runExecMock.mockReset(); + runCommandWithTimeoutMock.mockReset(); mockDeliverOutboundPayloads.mockClear(); mockDeliverOutboundPayloads.mockResolvedValue([{ channel: "whatsapp", messageId: "echo-1" }]); clearMediaUnderstandingBinaryCacheForTests?.();