test: isolate media factory planning imports

This commit is contained in:
Peter Steinberger
2026-05-06 01:54:51 +01:00
parent fcf0561da0
commit 384432fd22
3 changed files with 314 additions and 262 deletions

View File

@@ -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(),
}),

View File

@@ -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>
): 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<PluginMetadataSnapshot, "index" | "plugins">;
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,
})),
};
}

View File

@@ -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>): 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<PluginMetadataSnapshot, "index" | "plugins">;
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;