diff --git a/src/agents/openclaw-tools.media-factory-plan.test.ts b/src/agents/openclaw-tools.media-factory-plan.test.ts index db3a9c61cf1..1376b7617d1 100644 --- a/src/agents/openclaw-tools.media-factory-plan.test.ts +++ b/src/agents/openclaw-tools.media-factory-plan.test.ts @@ -12,9 +12,18 @@ import type { PluginManifestRecord } from "../plugins/manifest-registry.js"; import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js"; import { clearSecretsRuntimeSnapshot } from "../secrets/runtime.js"; import type { AuthProfileStore } from "./auth-profiles/types.js"; -import { __testing, createOpenClawTools } from "./openclaw-tools.js"; +import { resolveOptionalMediaToolFactoryPlan } from "./openclaw-tools.media-factory-plan.js"; import * as pdfModelConfigModule from "./tools/pdf-tool.model-config.js"; +type CreateOpenClawToolsOptions = Parameters< + typeof import("./openclaw-tools.js").createOpenClawTools +>[0]; + +async function createOpenClawToolsForTest(options?: CreateOpenClawToolsOptions) { + const { createOpenClawTools } = await import("./openclaw-tools.js"); + return createOpenClawTools(options); +} + function createAuthStore(providers: string[] = []): AuthProfileStore { return { version: 1, @@ -180,7 +189,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(["github-copilot"]), }), @@ -206,7 +215,7 @@ describe("optional media tool factory planning", () => { installSnapshot(config, []); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(), }), @@ -234,7 +243,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(["image-owner", "anthropic"]), toolAllowlist: ["image_generate"], @@ -263,7 +272,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(["image-owner", "anthropic"]), toolDenylist: ["image_generate", "pdf"], @@ -287,7 +296,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(["anthropic"]), }).pdf, @@ -320,7 +329,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(["image-owner", "video-owner", "music-owner", "anthropic"]), toolDenylist: ["*_generate", "p*"], @@ -360,7 +369,7 @@ describe("optional media tool factory planning", () => { vi.stubEnv("VIDEO_OWNER_API_KEY", "video-key"); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(["image-owner", "music-owner", "media-owner"]), }), @@ -372,12 +381,12 @@ describe("optional media tool factory planning", () => { }); }); - it("defers PDF model resolution from the tool-prep hot path", () => { + it("defers PDF model resolution from the tool-prep hot path", async () => { const config: OpenClawConfig = {}; installSnapshot(config, []); const resolveSpy = vi.spyOn(pdfModelConfigModule, "resolvePdfModelConfigForTool"); - const tools = createOpenClawTools({ + const tools = await createOpenClawToolsForTest({ config, agentDir: "/tmp/openclaw-agent-main", authProfileStore: createAuthStore(["anthropic"]), @@ -417,7 +426,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore([ "external-image", @@ -434,7 +443,7 @@ describe("optional media tool factory planning", () => { }); }); - it("keeps manifest-declared image provider auth aliases on the factory path", () => { + it("keeps manifest-declared image provider auth aliases on the factory path", async () => { const config: OpenClawConfig = {}; const plugins = [ createPlugin({ @@ -463,7 +472,7 @@ describe("optional media tool factory planning", () => { installSnapshot(config, plugins); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(["openai-codex"]), }), @@ -472,12 +481,14 @@ describe("optional media tool factory planning", () => { }); installSnapshot(config, plugins, undefined, process.cwd()); expect( - createOpenClawTools({ - config, - workspaceDir: process.cwd(), - authProfileStore: createAuthStore(["openai-codex"]), - pluginToolAllowlist: ["image_generate"], - }).map((tool) => tool.name), + ( + await createOpenClawToolsForTest({ + config, + workspaceDir: process.cwd(), + authProfileStore: createAuthStore(["openai-codex"]), + pluginToolAllowlist: ["image_generate"], + }) + ).map((tool) => tool.name), ).toContain("image_generate"); }); @@ -528,7 +539,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(), }), @@ -539,7 +550,7 @@ describe("optional media tool factory planning", () => { }); }); - it("does not expose manifest-backed generation providers when plugins are globally disabled", () => { + it("does not expose manifest-backed generation providers when plugins are globally disabled", async () => { const config: OpenClawConfig = { plugins: { enabled: false, @@ -587,7 +598,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(), }), @@ -598,15 +609,17 @@ describe("optional media tool factory planning", () => { pdf: false, }); expect( - createOpenClawTools({ - config, - authProfileStore: createAuthStore(), - pluginToolAllowlist: ["image_generate", "video_generate", "music_generate"], - }).map((tool) => tool.name), + ( + await createOpenClawToolsForTest({ + config, + authProfileStore: createAuthStore(), + pluginToolAllowlist: ["image_generate", "video_generate", "music_generate"], + }) + ).map((tool) => tool.name), ).not.toEqual(expect.arrayContaining(["image_generate", "video_generate", "music_generate"])); }); - it("does not count unresolved SecretRef config signals as configured", () => { + it("does not count unresolved SecretRef config signals as configured", async () => { vi.stubEnv("COMFY_TEST_API_KEY", ""); const workspaceDir = process.cwd(); const config: OpenClawConfig = { @@ -660,7 +673,7 @@ describe("optional media tool factory planning", () => { ); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, workspaceDir, authStore: createAuthStore(), @@ -672,12 +685,14 @@ describe("optional media tool factory planning", () => { pdf: false, }); expect( - createOpenClawTools({ - config, - workspaceDir, - authProfileStore: createAuthStore(), - pluginToolAllowlist: ["image_generate", "video_generate", "music_generate"], - }).map((tool) => tool.name), + ( + await createOpenClawToolsForTest({ + config, + workspaceDir, + authProfileStore: createAuthStore(), + pluginToolAllowlist: ["image_generate", "video_generate", "music_generate"], + }) + ).map((tool) => tool.name), ).not.toEqual(expect.arrayContaining(["image_generate", "video_generate", "music_generate"])); }); @@ -737,7 +752,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(), }), @@ -748,7 +763,7 @@ describe("optional media tool factory planning", () => { }); }); - it("does not register the image tool without cheap vision availability evidence", () => { + it("does not register the image tool without cheap vision availability evidence", async () => { const config: OpenClawConfig = {}; installSnapshot(config, [ createPlugin({ @@ -759,12 +774,14 @@ describe("optional media tool factory planning", () => { ]); expect( - createOpenClawTools({ - config, - agentDir: "/tmp/openclaw-agent", - authProfileStore: createAuthStore(), - disablePluginTools: true, - }).map((tool) => tool.name), + ( + await createOpenClawToolsForTest({ + config, + agentDir: "/tmp/openclaw-agent", + authProfileStore: createAuthStore(), + disablePluginTools: true, + }) + ).map((tool) => tool.name), ).not.toContain("image"); }); @@ -804,14 +821,16 @@ describe("optional media tool factory planning", () => { }, ])( "registers generation tools from Comfy $name without a current metadata snapshot", - ({ config }) => { + async ({ config }) => { setBundledPluginsDirOverrideForTest(path.join(process.cwd(), "extensions")); - const toolNames = createOpenClawTools({ - config, - authProfileStore: createAuthStore(), - pluginToolAllowlist: ["image_generate", "video_generate", "music_generate"], - }).map((tool) => tool.name); + const toolNames = ( + await createOpenClawToolsForTest({ + config, + authProfileStore: createAuthStore(), + pluginToolAllowlist: ["image_generate", "video_generate", "music_generate"], + }) + ).map((tool) => tool.name); expect(toolNames).toContain("image_generate"); expect(toolNames).toContain("video_generate"); @@ -853,7 +872,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(["openai-codex"]), }), @@ -878,7 +897,7 @@ describe("optional media tool factory planning", () => { ]); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(["external-image"]), }), @@ -895,7 +914,7 @@ describe("optional media tool factory planning", () => { installSnapshot(config, []); expect( - __testing.resolveOptionalMediaToolFactoryPlan({ + resolveOptionalMediaToolFactoryPlan({ config, authStore: createAuthStore(), }), diff --git a/src/agents/openclaw-tools.media-factory-plan.ts b/src/agents/openclaw-tools.media-factory-plan.ts new file mode 100644 index 00000000000..b6171581e9b --- /dev/null +++ b/src/agents/openclaw-tools.media-factory-plan.ts @@ -0,0 +1,237 @@ +import { + resolveAgentModelFallbackValues, + resolveAgentModelPrimaryValue, +} from "../config/model-input.js"; +import type { AgentModelConfig } from "../config/types.agents-shared.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js"; +import { listProfilesForProvider } from "./auth-profiles/profile-list.js"; +import type { AuthProfileStore } from "./auth-profiles/types.js"; +import { isToolAllowedByPolicyName } from "./tool-policy-match.js"; +import { + hasSnapshotCapabilityAvailability, + hasSnapshotProviderEnvAvailability, + loadCapabilityMetadataSnapshot, +} from "./tools/manifest-capability-availability.js"; + +export type OptionalMediaToolFactoryPlan = { + imageGenerate: boolean; + videoGenerate: boolean; + musicGenerate: boolean; + pdf: boolean; +}; + +type ToolModelConfig = { primary?: string; fallbacks?: string[] }; + +function coerceFactoryToolModelConfig(model?: AgentModelConfig): ToolModelConfig { + const primary = resolveAgentModelPrimaryValue(model); + const fallbacks = resolveAgentModelFallbackValues(model); + return { + ...(primary?.trim() ? { primary: primary.trim() } : {}), + ...(fallbacks.length > 0 ? { fallbacks } : {}), + }; +} + +function hasToolModelConfig(model: ToolModelConfig | undefined): boolean { + return Boolean( + model?.primary?.trim() || (model?.fallbacks ?? []).some((entry) => entry.trim().length > 0), + ); +} + +function hasExplicitToolModelConfig(modelConfig: AgentModelConfig | undefined): boolean { + return hasToolModelConfig(coerceFactoryToolModelConfig(modelConfig)); +} + +function hasExplicitImageModelConfig(config: OpenClawConfig | undefined): boolean { + return hasExplicitToolModelConfig(config?.agents?.defaults?.imageModel); +} + +function hasExplicitPdfModelConfig(config: OpenClawConfig | undefined): boolean { + return ( + hasExplicitToolModelConfig(config?.agents?.defaults?.pdfModel) || + hasExplicitImageModelConfig(config) + ); +} + +function isToolAllowedByFactoryPolicy(params: { + toolName: string; + allowlist?: string[]; + denylist?: string[]; +}): boolean { + return isToolAllowedByPolicyName(params.toolName, { + allow: params.allowlist, + deny: params.denylist, + }); +} + +export function isToolExplicitlyAllowedByFactoryPolicy(params: { + toolName: string; + allowlist?: string[]; + denylist?: string[]; +}): boolean { + if (!params.allowlist?.some((entry) => typeof entry === "string" && entry.trim().length > 0)) { + return false; + } + return isToolAllowedByFactoryPolicy(params); +} + +export function mergeFactoryPolicyList( + ...lists: Array +): string[] | undefined { + const merged = lists.flatMap((list) => (Array.isArray(list) ? list : [])); + return merged.length > 0 ? Array.from(new Set(merged)) : undefined; +} + +export function resolveImageToolFactoryAvailable(params: { + config?: OpenClawConfig; + agentDir?: string; + modelHasVision?: boolean; + authStore?: AuthProfileStore; +}): boolean { + if (!params.agentDir?.trim()) { + return false; + } + if (params.modelHasVision || hasExplicitImageModelConfig(params.config)) { + return true; + } + const snapshot = loadCapabilityMetadataSnapshot({ + config: params.config, + }); + return ( + hasSnapshotCapabilityAvailability({ + snapshot, + authStore: params.authStore, + key: "mediaUnderstandingProviders", + config: params.config, + }) || + hasConfiguredVisionModelAuthSignal({ + config: params.config, + snapshot, + authStore: params.authStore, + }) + ); +} + +function hasConfiguredVisionModelAuthSignal(params: { + config?: OpenClawConfig; + snapshot: Pick; + authStore?: AuthProfileStore; +}): boolean { + const providers = params.config?.models?.providers; + if (!providers || typeof providers !== "object") { + return false; + } + for (const [providerId, providerConfig] of Object.entries(providers)) { + if ( + !providerConfig?.models?.some( + (model) => Array.isArray(model?.input) && model.input.includes("image"), + ) + ) { + continue; + } + if (params.authStore && listProfilesForProvider(params.authStore, providerId).length > 0) { + return true; + } + if ( + hasSnapshotProviderEnvAvailability({ + snapshot: params.snapshot, + providerId, + config: params.config, + }) + ) { + return true; + } + } + return false; +} + +export function resolveOptionalMediaToolFactoryPlan(params: { + config?: OpenClawConfig; + workspaceDir?: string; + authStore?: AuthProfileStore; + toolAllowlist?: string[]; + toolDenylist?: string[]; +}): OptionalMediaToolFactoryPlan { + const defaults = params.config?.agents?.defaults; + const toolAllowlist = mergeFactoryPolicyList(params.config?.tools?.allow, params.toolAllowlist); + const toolDenylist = mergeFactoryPolicyList(params.config?.tools?.deny, params.toolDenylist); + const allowImageGenerate = isToolAllowedByFactoryPolicy({ + toolName: "image_generate", + allowlist: toolAllowlist, + denylist: toolDenylist, + }); + const allowVideoGenerate = isToolAllowedByFactoryPolicy({ + toolName: "video_generate", + allowlist: toolAllowlist, + denylist: toolDenylist, + }); + const allowMusicGenerate = isToolAllowedByFactoryPolicy({ + toolName: "music_generate", + allowlist: toolAllowlist, + denylist: toolDenylist, + }); + const allowPdf = isToolAllowedByFactoryPolicy({ + toolName: "pdf", + allowlist: toolAllowlist, + denylist: toolDenylist, + }); + const explicitImageGeneration = hasExplicitToolModelConfig(defaults?.imageGenerationModel); + const explicitVideoGeneration = hasExplicitToolModelConfig(defaults?.videoGenerationModel); + const explicitMusicGeneration = hasExplicitToolModelConfig(defaults?.musicGenerationModel); + const explicitPdf = hasExplicitPdfModelConfig(params.config); + if (params.config?.plugins?.enabled === false) { + return { + imageGenerate: false, + videoGenerate: false, + musicGenerate: false, + pdf: false, + }; + } + const snapshot = loadCapabilityMetadataSnapshot({ + config: params.config, + ...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}), + }); + return { + imageGenerate: + allowImageGenerate && + (explicitImageGeneration || + hasSnapshotCapabilityAvailability({ + snapshot, + authStore: params.authStore, + key: "imageGenerationProviders", + config: params.config, + })), + videoGenerate: + allowVideoGenerate && + (explicitVideoGeneration || + hasSnapshotCapabilityAvailability({ + snapshot, + authStore: params.authStore, + key: "videoGenerationProviders", + config: params.config, + })), + musicGenerate: + allowMusicGenerate && + (explicitMusicGeneration || + hasSnapshotCapabilityAvailability({ + snapshot, + authStore: params.authStore, + key: "musicGenerationProviders", + config: params.config, + })), + pdf: + allowPdf && + (explicitPdf || + hasSnapshotCapabilityAvailability({ + snapshot, + authStore: params.authStore, + key: "mediaUnderstandingProviders", + config: params.config, + }) || + hasConfiguredVisionModelAuthSignal({ + config: params.config, + snapshot, + authStore: params.authStore, + })), + }; +} diff --git a/src/agents/openclaw-tools.ts b/src/agents/openclaw-tools.ts index b799bf3ced4..648e84d2a52 100644 --- a/src/agents/openclaw-tools.ts +++ b/src/agents/openclaw-tools.ts @@ -1,9 +1,7 @@ import { selectApplicableRuntimeConfig } from "../config/config.js"; -import type { AgentModelConfig } from "../config/types.agents-shared.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { callGateway } from "../gateway/call.js"; import { isEmbeddedMode } from "../infra/embedded-mode.js"; -import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js"; import { getActiveRuntimeWebToolsMetadata, getActiveSecretsRuntimeSnapshot, @@ -11,9 +9,14 @@ import { import { normalizeDeliveryContext } from "../utils/delivery-context.js"; import type { GatewayMessageChannel } from "../utils/message-channel.js"; import { resolveAgentWorkspaceDir, resolveSessionAgentIds } from "./agent-scope.js"; -import { listProfilesForProvider } from "./auth-profiles.js"; import type { AuthProfileStore } from "./auth-profiles/types.js"; import { resolveOpenClawPluginToolsForOptions } from "./openclaw-plugin-tools.js"; +import { + isToolExplicitlyAllowedByFactoryPolicy, + mergeFactoryPolicyList, + resolveImageToolFactoryAvailable, + resolveOptionalMediaToolFactoryPlan, +} from "./openclaw-tools.media-factory-plan.js"; import { applyNodesToolWorkspaceGuard } from "./openclaw-tools.nodes-workspace-guard.js"; import { collectPresentOpenClawTools, @@ -22,7 +25,6 @@ import { import type { SandboxFsBridge } from "./sandbox/fs-bridge.js"; import type { SpawnedToolContext } from "./spawned-context.js"; import type { ToolFsPolicy } from "./tool-fs-policy.js"; -import { isToolAllowedByPolicyName } from "./tool-policy-match.js"; import { createAgentsListTool } from "./tools/agents-list-tool.js"; import { createCanvasTool } from "./tools/canvas-tool.js"; import type { AnyAgentTool } from "./tools/common.js"; @@ -31,18 +33,10 @@ import { createEmbeddedCallGateway } from "./tools/embedded-gateway-stub.js"; import { createGatewayTool } from "./tools/gateway-tool.js"; import { createHeartbeatResponseTool } from "./tools/heartbeat-response-tool.js"; import { createImageGenerateTool } from "./tools/image-generate-tool.js"; -import { coerceImageModelConfig } from "./tools/image-tool.helpers.js"; import { createImageTool } from "./tools/image-tool.js"; -import { - hasSnapshotCapabilityAvailability, - hasSnapshotProviderEnvAvailability, - loadCapabilityMetadataSnapshot, -} from "./tools/manifest-capability-availability.js"; import { createMessageTool } from "./tools/message-tool.js"; -import { coerceToolModelConfig, hasToolModelConfig } from "./tools/model-config.helpers.js"; import { createMusicGenerateTool } from "./tools/music-generate-tool.js"; import { createNodesTool } from "./tools/nodes-tool.js"; -import { coercePdfModelConfig } from "./tools/pdf-tool.helpers.js"; import { createPdfTool } from "./tools/pdf-tool.js"; import { createSessionStatusTool } from "./tools/session-status-tool.js"; import { createSessionsHistoryTool } from "./tools/sessions-history-tool.js"; @@ -68,204 +62,6 @@ const defaultOpenClawToolsDeps: OpenClawToolsDeps = { let openClawToolsDeps: OpenClawToolsDeps = defaultOpenClawToolsDeps; -type OptionalMediaToolFactoryPlan = { - imageGenerate: boolean; - videoGenerate: boolean; - musicGenerate: boolean; - pdf: boolean; -}; - -function hasExplicitToolModelConfig(modelConfig: AgentModelConfig | undefined): boolean { - return hasToolModelConfig(coerceToolModelConfig(modelConfig)); -} - -function hasExplicitImageModelConfig(config: OpenClawConfig | undefined): boolean { - return hasToolModelConfig(coerceImageModelConfig(config)); -} - -function isToolAllowedByFactoryPolicy(params: { - toolName: string; - allowlist?: string[]; - denylist?: string[]; -}): boolean { - return isToolAllowedByPolicyName(params.toolName, { - allow: params.allowlist, - deny: params.denylist, - }); -} - -function isToolExplicitlyAllowedByFactoryPolicy(params: { - toolName: string; - allowlist?: string[]; - denylist?: string[]; -}): boolean { - if (!params.allowlist?.some((entry) => typeof entry === "string" && entry.trim().length > 0)) { - return false; - } - return isToolAllowedByFactoryPolicy(params); -} - -function mergeFactoryPolicyList(...lists: Array): string[] | undefined { - const merged = lists.flatMap((list) => (Array.isArray(list) ? list : [])); - return merged.length > 0 ? Array.from(new Set(merged)) : undefined; -} - -function resolveImageToolFactoryAvailable(params: { - config?: OpenClawConfig; - agentDir?: string; - modelHasVision?: boolean; - authStore?: AuthProfileStore; -}): boolean { - if (!params.agentDir?.trim()) { - return false; - } - if (params.modelHasVision || hasExplicitImageModelConfig(params.config)) { - return true; - } - const snapshot = loadCapabilityMetadataSnapshot({ - config: params.config, - }); - return ( - hasSnapshotCapabilityAvailability({ - snapshot, - authStore: params.authStore, - key: "mediaUnderstandingProviders", - config: params.config, - }) || - hasConfiguredVisionModelAuthSignal({ - config: params.config, - snapshot, - authStore: params.authStore, - }) - ); -} - -function hasConfiguredVisionModelAuthSignal(params: { - config?: OpenClawConfig; - snapshot: Pick; - authStore?: AuthProfileStore; -}): boolean { - const providers = params.config?.models?.providers; - if (!providers || typeof providers !== "object") { - return false; - } - for (const [providerId, providerConfig] of Object.entries(providers)) { - if ( - !providerConfig?.models?.some( - (model) => Array.isArray(model?.input) && model.input.includes("image"), - ) - ) { - continue; - } - if (params.authStore && listProfilesForProvider(params.authStore, providerId).length > 0) { - return true; - } - if ( - hasSnapshotProviderEnvAvailability({ - snapshot: params.snapshot, - providerId, - config: params.config, - }) - ) { - return true; - } - } - return false; -} - -function resolveOptionalMediaToolFactoryPlan(params: { - config?: OpenClawConfig; - workspaceDir?: string; - authStore?: AuthProfileStore; - toolAllowlist?: string[]; - toolDenylist?: string[]; -}): OptionalMediaToolFactoryPlan { - const defaults = params.config?.agents?.defaults; - const toolAllowlist = mergeFactoryPolicyList(params.config?.tools?.allow, params.toolAllowlist); - const toolDenylist = mergeFactoryPolicyList(params.config?.tools?.deny, params.toolDenylist); - const allowImageGenerate = isToolAllowedByFactoryPolicy({ - toolName: "image_generate", - allowlist: toolAllowlist, - denylist: toolDenylist, - }); - const allowVideoGenerate = isToolAllowedByFactoryPolicy({ - toolName: "video_generate", - allowlist: toolAllowlist, - denylist: toolDenylist, - }); - const allowMusicGenerate = isToolAllowedByFactoryPolicy({ - toolName: "music_generate", - allowlist: toolAllowlist, - denylist: toolDenylist, - }); - const allowPdf = isToolAllowedByFactoryPolicy({ - toolName: "pdf", - allowlist: toolAllowlist, - denylist: toolDenylist, - }); - const explicitImageGeneration = hasExplicitToolModelConfig(defaults?.imageGenerationModel); - const explicitVideoGeneration = hasExplicitToolModelConfig(defaults?.videoGenerationModel); - const explicitMusicGeneration = hasExplicitToolModelConfig(defaults?.musicGenerationModel); - const explicitPdf = - hasToolModelConfig(coercePdfModelConfig(params.config)) || - hasToolModelConfig(coerceImageModelConfig(params.config)); - if (params.config?.plugins?.enabled === false) { - return { - imageGenerate: false, - videoGenerate: false, - musicGenerate: false, - pdf: false, - }; - } - const snapshot = loadCapabilityMetadataSnapshot({ - config: params.config, - ...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}), - }); - return { - imageGenerate: - allowImageGenerate && - (explicitImageGeneration || - hasSnapshotCapabilityAvailability({ - snapshot, - authStore: params.authStore, - key: "imageGenerationProviders", - config: params.config, - })), - videoGenerate: - allowVideoGenerate && - (explicitVideoGeneration || - hasSnapshotCapabilityAvailability({ - snapshot, - authStore: params.authStore, - key: "videoGenerationProviders", - config: params.config, - })), - musicGenerate: - allowMusicGenerate && - (explicitMusicGeneration || - hasSnapshotCapabilityAvailability({ - snapshot, - authStore: params.authStore, - key: "musicGenerationProviders", - config: params.config, - })), - pdf: - allowPdf && - (explicitPdf || - hasSnapshotCapabilityAvailability({ - snapshot, - authStore: params.authStore, - key: "mediaUnderstandingProviders", - config: params.config, - }) || - hasConfiguredVisionModelAuthSignal({ - config: params.config, - snapshot, - authStore: params.authStore, - })), - }; -} - export function createOpenClawTools( options?: { sandboxBrowserBridgeUrl?: string;