import type { OpenClawConfig } from "../config/config.js"; import { resolvePluginTools } from "../plugins/tools.js"; import { getActiveRuntimeWebToolsMetadata } from "../secrets/runtime.js"; import type { GatewayMessageChannel } from "../utils/message-channel.js"; import { resolveSessionAgentId } from "./agent-scope.js"; import type { SandboxFsBridge } from "./sandbox/fs-bridge.js"; import type { SpawnedToolContext } from "./spawned-context.js"; import type { ToolFsPolicy } from "./tool-fs-policy.js"; import { createAgentsListTool } from "./tools/agents-list-tool.js"; import { createBrowserTool } from "./tools/browser-tool.js"; import { createCanvasTool } from "./tools/canvas-tool.js"; import type { AnyAgentTool } from "./tools/common.js"; import { createCronTool } from "./tools/cron-tool.js"; import { createGatewayTool } from "./tools/gateway-tool.js"; import { createImageTool } from "./tools/image-tool.js"; import { createMessageTool } from "./tools/message-tool.js"; import { createNodesTool } from "./tools/nodes-tool.js"; import { createPdfTool } from "./tools/pdf-tool.js"; import { createSessionStatusTool } from "./tools/session-status-tool.js"; import { createSessionsHistoryTool } from "./tools/sessions-history-tool.js"; import { createSessionsListTool } from "./tools/sessions-list-tool.js"; import { createSessionsSendTool } from "./tools/sessions-send-tool.js"; import { createSessionsSpawnTool } from "./tools/sessions-spawn-tool.js"; import { createSubagentsTool } from "./tools/subagents-tool.js"; import { createTtsTool } from "./tools/tts-tool.js"; import { createWebFetchTool, createWebSearchTool } from "./tools/web-tools.js"; import { resolveWorkspaceRoot } from "./workspace-dir.js"; export function createOpenClawTools( options?: { sandboxBrowserBridgeUrl?: string; allowHostBrowserControl?: boolean; agentSessionKey?: string; agentChannel?: GatewayMessageChannel; agentAccountId?: string; /** Delivery target (e.g. telegram:group:123:topic:456) for topic/thread routing. */ agentTo?: string; /** Thread/topic identifier for routing replies to the originating thread. */ agentThreadId?: string | number; agentDir?: string; sandboxRoot?: string; sandboxFsBridge?: SandboxFsBridge; fsPolicy?: ToolFsPolicy; sandboxed?: boolean; config?: OpenClawConfig; pluginToolAllowlist?: string[]; /** Current channel ID for auto-threading (Slack). */ currentChannelId?: string; /** Current thread timestamp for auto-threading (Slack). */ currentThreadTs?: string; /** Current inbound message id for action fallbacks (e.g. Telegram react). */ currentMessageId?: string | number; /** Reply-to mode for Slack auto-threading. */ replyToMode?: "off" | "first" | "all"; /** Mutable ref to track if a reply was sent (for "first" mode). */ hasRepliedRef?: { value: boolean }; /** If true, the model has native vision capability */ modelHasVision?: boolean; /** If true, nodes action="invoke" can call media-returning commands directly. */ allowMediaInvokeCommands?: boolean; /** Explicit agent ID override for cron/hook sessions. */ requesterAgentIdOverride?: string; /** Require explicit message targets (no implicit last-route sends). */ requireExplicitMessageTarget?: boolean; /** If true, omit the message tool from the tool list. */ disableMessageTool?: boolean; /** Trusted sender id from inbound context (not tool args). */ requesterSenderId?: string | null; /** Whether the requesting sender is an owner. */ senderIsOwner?: boolean; /** Ephemeral session UUID — regenerated on /new and /reset. */ sessionId?: string; /** * Workspace directory to pass to spawned subagents for inheritance. * Defaults to workspaceDir. Use this to pass the actual agent workspace when the * session itself is running in a copied-workspace sandbox (`ro` or `none`) so * subagents inherit the real workspace path instead of the sandbox copy. */ spawnWorkspaceDir?: string; } & SpawnedToolContext, ): AnyAgentTool[] { const workspaceDir = resolveWorkspaceRoot(options?.workspaceDir); const spawnWorkspaceDir = resolveWorkspaceRoot( options?.spawnWorkspaceDir ?? options?.workspaceDir, ); const runtimeWebTools = getActiveRuntimeWebToolsMetadata(); const imageTool = options?.agentDir?.trim() ? createImageTool({ config: options?.config, agentDir: options.agentDir, workspaceDir, sandbox: options?.sandboxRoot && options?.sandboxFsBridge ? { root: options.sandboxRoot, bridge: options.sandboxFsBridge } : undefined, fsPolicy: options?.fsPolicy, modelHasVision: options?.modelHasVision, }) : null; const pdfTool = options?.agentDir?.trim() ? createPdfTool({ config: options?.config, agentDir: options.agentDir, workspaceDir, sandbox: options?.sandboxRoot && options?.sandboxFsBridge ? { root: options.sandboxRoot, bridge: options.sandboxFsBridge } : undefined, fsPolicy: options?.fsPolicy, }) : null; const webSearchTool = createWebSearchTool({ config: options?.config, sandboxed: options?.sandboxed, runtimeWebSearch: runtimeWebTools?.search, }); const webFetchTool = createWebFetchTool({ config: options?.config, sandboxed: options?.sandboxed, runtimeFirecrawl: runtimeWebTools?.fetch.firecrawl, }); const messageTool = options?.disableMessageTool ? null : createMessageTool({ agentAccountId: options?.agentAccountId, agentSessionKey: options?.agentSessionKey, config: options?.config, currentChannelId: options?.currentChannelId, currentChannelProvider: options?.agentChannel, currentThreadTs: options?.currentThreadTs, currentMessageId: options?.currentMessageId, replyToMode: options?.replyToMode, hasRepliedRef: options?.hasRepliedRef, sandboxRoot: options?.sandboxRoot, requireExplicitTarget: options?.requireExplicitMessageTarget, requesterSenderId: options?.requesterSenderId ?? undefined, }); const tools: AnyAgentTool[] = [ createBrowserTool({ sandboxBridgeUrl: options?.sandboxBrowserBridgeUrl, allowHostControl: options?.allowHostBrowserControl, agentSessionKey: options?.agentSessionKey, }), createCanvasTool({ config: options?.config }), createNodesTool({ agentSessionKey: options?.agentSessionKey, agentChannel: options?.agentChannel, agentAccountId: options?.agentAccountId, currentChannelId: options?.currentChannelId, currentThreadTs: options?.currentThreadTs, config: options?.config, modelHasVision: options?.modelHasVision, allowMediaInvokeCommands: options?.allowMediaInvokeCommands, }), createCronTool({ agentSessionKey: options?.agentSessionKey, }), ...(messageTool ? [messageTool] : []), createTtsTool({ agentChannel: options?.agentChannel, config: options?.config, }), createGatewayTool({ agentSessionKey: options?.agentSessionKey, config: options?.config, }), createAgentsListTool({ agentSessionKey: options?.agentSessionKey, requesterAgentIdOverride: options?.requesterAgentIdOverride, }), createSessionsListTool({ agentSessionKey: options?.agentSessionKey, sandboxed: options?.sandboxed, }), createSessionsHistoryTool({ agentSessionKey: options?.agentSessionKey, sandboxed: options?.sandboxed, }), createSessionsSendTool({ agentSessionKey: options?.agentSessionKey, agentChannel: options?.agentChannel, sandboxed: options?.sandboxed, }), createSessionsSpawnTool({ agentSessionKey: options?.agentSessionKey, agentChannel: options?.agentChannel, agentAccountId: options?.agentAccountId, agentTo: options?.agentTo, agentThreadId: options?.agentThreadId, agentGroupId: options?.agentGroupId, agentGroupChannel: options?.agentGroupChannel, agentGroupSpace: options?.agentGroupSpace, sandboxed: options?.sandboxed, requesterAgentIdOverride: options?.requesterAgentIdOverride, workspaceDir: spawnWorkspaceDir, }), createSubagentsTool({ agentSessionKey: options?.agentSessionKey, }), createSessionStatusTool({ agentSessionKey: options?.agentSessionKey, config: options?.config, }), ...(webSearchTool ? [webSearchTool] : []), ...(webFetchTool ? [webFetchTool] : []), ...(imageTool ? [imageTool] : []), ...(pdfTool ? [pdfTool] : []), ]; const pluginTools = resolvePluginTools({ context: { config: options?.config, workspaceDir, agentDir: options?.agentDir, agentId: resolveSessionAgentId({ sessionKey: options?.agentSessionKey, config: options?.config, }), sessionKey: options?.agentSessionKey, sessionId: options?.sessionId, messageChannel: options?.agentChannel, agentAccountId: options?.agentAccountId, requesterSenderId: options?.requesterSenderId ?? undefined, senderIsOwner: options?.senderIsOwner ?? undefined, sandboxed: options?.sandboxed, }, existingToolNames: new Set(tools.map((tool) => tool.name)), toolAllowlist: options?.pluginToolAllowlist, }); return [...tools, ...pluginTools]; }