mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:10:45 +00:00
fix(agents): skip core tools for plugin-only allowlists
This commit is contained in:
@@ -62,6 +62,7 @@ type AttemptSpawnWorkspaceHoisted = {
|
||||
ensureGlobalUndiciEnvProxyDispatcherMock: UnknownMock;
|
||||
ensureGlobalUndiciStreamTimeoutsMock: UnknownMock;
|
||||
buildEmbeddedMessageActionDiscoveryInputMock: UnknownMock;
|
||||
createOpenClawCodingToolsMock: UnknownMock;
|
||||
subscribeEmbeddedPiSessionMock: Mock<SubscribeEmbeddedPiSessionFn>;
|
||||
acquireSessionWriteLockMock: Mock<AcquireSessionWriteLockFn>;
|
||||
installToolResultContextGuardMock: UnknownMock;
|
||||
@@ -121,6 +122,7 @@ const hoisted = vi.hoisted((): AttemptSpawnWorkspaceHoisted => {
|
||||
const ensureGlobalUndiciEnvProxyDispatcherMock = vi.fn();
|
||||
const ensureGlobalUndiciStreamTimeoutsMock = vi.fn();
|
||||
const buildEmbeddedMessageActionDiscoveryInputMock = vi.fn((params: unknown) => params);
|
||||
const createOpenClawCodingToolsMock = vi.fn(() => []);
|
||||
const installToolResultContextGuardMock = vi.fn(() => () => {});
|
||||
const installContextEngineLoopHookMock = vi.fn(() => () => {});
|
||||
const flushPendingToolResultsAfterIdleMock = vi.fn(async () => {});
|
||||
@@ -170,6 +172,7 @@ const hoisted = vi.hoisted((): AttemptSpawnWorkspaceHoisted => {
|
||||
ensureGlobalUndiciEnvProxyDispatcherMock,
|
||||
ensureGlobalUndiciStreamTimeoutsMock,
|
||||
buildEmbeddedMessageActionDiscoveryInputMock,
|
||||
createOpenClawCodingToolsMock,
|
||||
subscribeEmbeddedPiSessionMock,
|
||||
acquireSessionWriteLockMock,
|
||||
installToolResultContextGuardMock,
|
||||
@@ -427,26 +430,8 @@ vi.mock("../../cache-trace.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../../pi-tools.js", () => ({
|
||||
createOpenClawCodingTools: (options?: { workspaceDir?: string; spawnWorkspaceDir?: string }) => [
|
||||
{
|
||||
name: "sessions_spawn",
|
||||
execute: async (
|
||||
_callId: string,
|
||||
input: { task?: string },
|
||||
_session?: unknown,
|
||||
_abortSignal?: unknown,
|
||||
_ctx?: unknown,
|
||||
) =>
|
||||
await hoisted.spawnSubagentDirectMock(
|
||||
{
|
||||
task: input.task ?? "",
|
||||
},
|
||||
{
|
||||
workspaceDir: options?.spawnWorkspaceDir ?? options?.workspaceDir,
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
createOpenClawCodingTools: (options?: { workspaceDir?: string; spawnWorkspaceDir?: string }) =>
|
||||
hoisted.createOpenClawCodingToolsMock(options),
|
||||
resolveToolLoopDetectionConfig: () => undefined,
|
||||
}));
|
||||
|
||||
@@ -526,6 +511,9 @@ vi.mock("../../tool-call-id.js", async (importOriginal) => {
|
||||
});
|
||||
|
||||
vi.mock("../../tool-fs-policy.js", () => ({
|
||||
createToolFsPolicy: (params: { workspaceOnly?: boolean }) => ({
|
||||
workspaceOnly: params.workspaceOnly === true,
|
||||
}),
|
||||
resolveEffectiveToolFsWorkspaceOnly: () => false,
|
||||
}));
|
||||
|
||||
@@ -767,6 +755,34 @@ export function resetEmbeddedAttemptHarness(
|
||||
hoisted.buildEmbeddedMessageActionDiscoveryInputMock
|
||||
.mockReset()
|
||||
.mockImplementation((params) => params);
|
||||
hoisted.createOpenClawCodingToolsMock.mockReset().mockImplementation((...args: unknown[]) => {
|
||||
const options = args[0] as
|
||||
| {
|
||||
workspaceDir?: string;
|
||||
spawnWorkspaceDir?: string;
|
||||
}
|
||||
| undefined;
|
||||
return [
|
||||
{
|
||||
name: "sessions_spawn",
|
||||
execute: async (
|
||||
_callId: string,
|
||||
input: { task?: string },
|
||||
_session?: unknown,
|
||||
_abortSignal?: unknown,
|
||||
_ctx?: unknown,
|
||||
) =>
|
||||
await hoisted.spawnSubagentDirectMock(
|
||||
{
|
||||
task: input.task ?? "",
|
||||
},
|
||||
{
|
||||
workspaceDir: options?.spawnWorkspaceDir ?? options?.workspaceDir,
|
||||
},
|
||||
),
|
||||
},
|
||||
];
|
||||
});
|
||||
hoisted.subscribeEmbeddedPiSessionMock
|
||||
.mockReset()
|
||||
.mockImplementation(() => createSubscriptionMock());
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
cleanupTempPaths,
|
||||
createContextEngineAttemptRunner,
|
||||
getHoisted,
|
||||
resetEmbeddedAttemptHarness,
|
||||
} from "./attempt.spawn-workspace.test-support.js";
|
||||
|
||||
describe("runEmbeddedAttempt toolsAllow startup cost", () => {
|
||||
const tempPaths: string[] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
resetEmbeddedAttemptHarness();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await cleanupTempPaths(tempPaths);
|
||||
});
|
||||
|
||||
it("keeps plugin-only allowlists on the shared tool policy path", async () => {
|
||||
const hoisted = getHoisted();
|
||||
hoisted.createOpenClawCodingToolsMock.mockReturnValue([
|
||||
{
|
||||
name: "memory_search",
|
||||
description: "search memory",
|
||||
parameters: { type: "object", properties: {} },
|
||||
execute: async () => "ok",
|
||||
},
|
||||
{
|
||||
name: "plugin_extra",
|
||||
description: "extra plugin tool",
|
||||
parameters: { type: "object", properties: {} },
|
||||
execute: async () => "ok",
|
||||
},
|
||||
]);
|
||||
|
||||
await createContextEngineAttemptRunner({
|
||||
contextEngine: {
|
||||
assemble: async ({ messages }) => ({ messages, estimatedTokens: 1 }),
|
||||
},
|
||||
attemptOverrides: {
|
||||
toolsAllow: ["memory_search"],
|
||||
},
|
||||
sessionKey: "agent:main:main",
|
||||
tempPaths,
|
||||
});
|
||||
|
||||
expect(hoisted.createOpenClawCodingToolsMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
includeCoreTools: false,
|
||||
runtimeToolAllowlist: ["memory_search"],
|
||||
}),
|
||||
);
|
||||
const createSessionOptions = hoisted.createAgentSessionMock.mock.calls[0]?.[0] as
|
||||
| { customTools?: { name: string }[] }
|
||||
| undefined;
|
||||
expect(createSessionOptions?.customTools?.map((tool) => tool.name)).toEqual(["memory_search"]);
|
||||
});
|
||||
});
|
||||
@@ -496,6 +496,54 @@ export function applyEmbeddedAttemptToolsAllow<T extends { name: string }>(
|
||||
return tools.filter((tool) => allowSet.has(normalizeToolName(tool.name)));
|
||||
}
|
||||
|
||||
const CORE_CODING_TOOL_ALLOWLIST_NAMES = new Set([
|
||||
"agents_list",
|
||||
"apply_patch",
|
||||
"bash",
|
||||
"canvas",
|
||||
"cron",
|
||||
"edit",
|
||||
"exec",
|
||||
"gateway",
|
||||
"heartbeat_response",
|
||||
"image",
|
||||
"image_generate",
|
||||
"message",
|
||||
"music_generate",
|
||||
"nodes",
|
||||
"pdf",
|
||||
"read",
|
||||
"session_status",
|
||||
"sessions_history",
|
||||
"sessions_list",
|
||||
"sessions_send",
|
||||
"sessions_spawn",
|
||||
"sessions_yield",
|
||||
"subagents",
|
||||
"tts",
|
||||
"update_plan",
|
||||
"video_generate",
|
||||
"web_fetch",
|
||||
"web_search",
|
||||
"write",
|
||||
]);
|
||||
|
||||
function shouldBuildCoreCodingToolsForAllowlist(toolsAllow?: string[]): boolean {
|
||||
if (!toolsAllow || toolsAllow.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return toolsAllow.some((toolName) => {
|
||||
const normalized = normalizeToolName(toolName);
|
||||
return (
|
||||
normalized === "*" ||
|
||||
normalized.startsWith("group:") ||
|
||||
normalized === "bundle-mcp" ||
|
||||
normalized.includes(TOOL_NAME_SEPARATOR) ||
|
||||
CORE_CODING_TOOL_ALLOWLIST_NAMES.has(normalized)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function normalizeMessagesForLlmBoundary(messages: AgentMessage[]): AgentMessage[] {
|
||||
const normalized = stripToolResultDetails(normalizeAssistantReplayContent(messages));
|
||||
return stripRuntimeContextCustomMessages(normalized);
|
||||
@@ -696,6 +744,10 @@ export async function runEmbeddedAttempt(
|
||||
config: params.config,
|
||||
agentId: params.agentId,
|
||||
});
|
||||
const effectiveFsWorkspaceOnly = resolveAttemptFsWorkspaceOnly({
|
||||
config: params.config,
|
||||
sessionAgentId,
|
||||
});
|
||||
prepStages.mark("workspace-sandbox");
|
||||
|
||||
let restoreSkillEnv: (() => void) | undefined;
|
||||
@@ -833,6 +885,7 @@ export async function runEmbeddedAttempt(
|
||||
currentChannelId: params.currentChannelId,
|
||||
currentThreadTs: params.currentThreadTs,
|
||||
currentMessageId: params.currentMessageId,
|
||||
includeCoreTools: shouldBuildCoreCodingToolsForAllowlist(params.toolsAllow),
|
||||
replyToMode: params.replyToMode,
|
||||
hasRepliedRef: params.hasRepliedRef,
|
||||
modelHasVision: params.model.input?.includes("image") ?? false,
|
||||
@@ -942,10 +995,6 @@ export async function runEmbeddedAttempt(
|
||||
config: params.config,
|
||||
agentId: params.agentId,
|
||||
});
|
||||
const effectiveFsWorkspaceOnly = resolveAttemptFsWorkspaceOnly({
|
||||
config: params.config,
|
||||
sessionAgentId,
|
||||
});
|
||||
// Track sessions_yield tool invocation (callback pattern, like clientToolCallDetected)
|
||||
let yieldDetected = false;
|
||||
let yieldMessage: string | null = null;
|
||||
|
||||
@@ -22,6 +22,7 @@ import { listChannelAgentTools } from "./channel-tools.js";
|
||||
import { shouldSuppressManagedWebSearchTool } from "./codex-native-web-search.js";
|
||||
import { resolveImageSanitizationLimits } from "./image-sanitization.js";
|
||||
import type { ModelAuthMode } from "./model-auth.js";
|
||||
import { resolveOpenClawPluginToolsForOptions } from "./openclaw-plugin-tools.js";
|
||||
import { createOpenClawTools } from "./openclaw-tools.js";
|
||||
import { wrapToolWithAbortSignal } from "./pi-tools.abort.js";
|
||||
import { wrapToolWithBeforeToolCallHook } from "./pi-tools.before-tool-call.js";
|
||||
@@ -352,6 +353,8 @@ export function createOpenClawCodingTools(options?: {
|
||||
enableHeartbeatTool?: boolean;
|
||||
/** Keep the heartbeat response tool available even when the selected profile omits it. */
|
||||
forceHeartbeatTool?: boolean;
|
||||
/** If false, build plugin tools only while preserving the shared policy pipeline. */
|
||||
includeCoreTools?: boolean;
|
||||
/** Whether the sender is an owner (required for owner-only tools). */
|
||||
senderIsOwner?: boolean;
|
||||
/**
|
||||
@@ -467,6 +470,7 @@ export function createOpenClawCodingTools(options?: {
|
||||
const sandboxFsBridge = sandbox?.fsBridge;
|
||||
const allowWorkspaceWrites = sandbox?.workspaceAccess !== "ro";
|
||||
const workspaceRoot = resolveWorkspaceRoot(options?.workspaceDir);
|
||||
const includeCoreTools = options?.includeCoreTools !== false;
|
||||
const workspaceOnly = fsPolicy.workspaceOnly;
|
||||
const applyPatchConfig = execConfig.applyPatch;
|
||||
// Secure by default: apply_patch is workspace-contained unless explicitly disabled.
|
||||
@@ -486,93 +490,99 @@ export function createOpenClawCodingTools(options?: {
|
||||
}
|
||||
const imageSanitization = resolveImageSanitizationLimits(options?.config);
|
||||
|
||||
const base = (createCodingTools(workspaceRoot) as unknown as AnyAgentTool[]).flatMap((tool) => {
|
||||
if (tool.name === "read") {
|
||||
if (sandboxRoot) {
|
||||
const sandboxed = createSandboxedReadTool({
|
||||
root: sandboxRoot,
|
||||
bridge: sandboxFsBridge!,
|
||||
modelContextWindowTokens: options?.modelContextWindowTokens,
|
||||
imageSanitization,
|
||||
});
|
||||
return [
|
||||
workspaceOnly
|
||||
? wrapToolWorkspaceRootGuardWithOptions(sandboxed, sandboxRoot, {
|
||||
containerWorkdir: sandbox.containerWorkdir,
|
||||
})
|
||||
: sandboxed,
|
||||
];
|
||||
}
|
||||
const freshReadTool = createReadTool(workspaceRoot);
|
||||
const wrapped = createOpenClawReadTool(freshReadTool, {
|
||||
modelContextWindowTokens: options?.modelContextWindowTokens,
|
||||
imageSanitization,
|
||||
});
|
||||
return [workspaceOnly ? wrapToolWorkspaceRootGuard(wrapped, workspaceRoot) : wrapped];
|
||||
}
|
||||
if (tool.name === "bash" || tool.name === execToolName) {
|
||||
return [];
|
||||
}
|
||||
if (tool.name === "write") {
|
||||
if (sandboxRoot) {
|
||||
return [];
|
||||
}
|
||||
const wrapped = createHostWorkspaceWriteTool(workspaceRoot, { workspaceOnly });
|
||||
return [workspaceOnly ? wrapToolWorkspaceRootGuard(wrapped, workspaceRoot) : wrapped];
|
||||
}
|
||||
if (tool.name === "edit") {
|
||||
if (sandboxRoot) {
|
||||
return [];
|
||||
}
|
||||
const wrapped = createHostWorkspaceEditTool(workspaceRoot, { workspaceOnly });
|
||||
return [workspaceOnly ? wrapToolWorkspaceRootGuard(wrapped, workspaceRoot) : wrapped];
|
||||
}
|
||||
return [tool];
|
||||
});
|
||||
const { cleanupMs: cleanupMsOverride, ...execDefaults } = options?.exec ?? {};
|
||||
const execTool = createLazyExecTool({
|
||||
...execDefaults,
|
||||
host: options?.exec?.host ?? execConfig.host,
|
||||
security: options?.exec?.security ?? execConfig.security,
|
||||
ask: options?.exec?.ask ?? execConfig.ask,
|
||||
trigger: options?.trigger,
|
||||
node: options?.exec?.node ?? execConfig.node,
|
||||
pathPrepend: options?.exec?.pathPrepend ?? execConfig.pathPrepend,
|
||||
safeBins: options?.exec?.safeBins ?? execConfig.safeBins,
|
||||
strictInlineEval: options?.exec?.strictInlineEval ?? execConfig.strictInlineEval,
|
||||
safeBinTrustedDirs: options?.exec?.safeBinTrustedDirs ?? execConfig.safeBinTrustedDirs,
|
||||
safeBinProfiles: options?.exec?.safeBinProfiles ?? execConfig.safeBinProfiles,
|
||||
agentId,
|
||||
cwd: workspaceRoot,
|
||||
allowBackground,
|
||||
scopeKey,
|
||||
sessionKey: options?.sessionKey,
|
||||
messageProvider: options?.messageProvider,
|
||||
currentChannelId: options?.currentChannelId,
|
||||
currentThreadTs: options?.currentThreadTs,
|
||||
accountId: options?.agentAccountId,
|
||||
backgroundMs: options?.exec?.backgroundMs ?? execConfig.backgroundMs,
|
||||
timeoutSec: options?.exec?.timeoutSec ?? execConfig.timeoutSec,
|
||||
approvalRunningNoticeMs:
|
||||
options?.exec?.approvalRunningNoticeMs ?? execConfig.approvalRunningNoticeMs,
|
||||
notifyOnExit: options?.exec?.notifyOnExit ?? execConfig.notifyOnExit,
|
||||
notifyOnExitEmptySuccess:
|
||||
options?.exec?.notifyOnExitEmptySuccess ?? execConfig.notifyOnExitEmptySuccess,
|
||||
sandbox: sandbox
|
||||
? {
|
||||
containerName: sandbox.containerName,
|
||||
workspaceDir: sandbox.workspaceDir,
|
||||
containerWorkdir: sandbox.containerWorkdir,
|
||||
env: sandbox.backend?.env ?? sandbox.docker.env,
|
||||
buildExecSpec: sandbox.backend?.buildExecSpec.bind(sandbox.backend),
|
||||
finalizeExec: sandbox.backend?.finalizeExec?.bind(sandbox.backend),
|
||||
const base = includeCoreTools
|
||||
? (createCodingTools(workspaceRoot) as unknown as AnyAgentTool[]).flatMap((tool) => {
|
||||
if (tool.name === "read") {
|
||||
if (sandboxRoot) {
|
||||
const sandboxed = createSandboxedReadTool({
|
||||
root: sandboxRoot,
|
||||
bridge: sandboxFsBridge!,
|
||||
modelContextWindowTokens: options?.modelContextWindowTokens,
|
||||
imageSanitization,
|
||||
});
|
||||
return [
|
||||
workspaceOnly
|
||||
? wrapToolWorkspaceRootGuardWithOptions(sandboxed, sandboxRoot, {
|
||||
containerWorkdir: sandbox.containerWorkdir,
|
||||
})
|
||||
: sandboxed,
|
||||
];
|
||||
}
|
||||
const freshReadTool = createReadTool(workspaceRoot);
|
||||
const wrapped = createOpenClawReadTool(freshReadTool, {
|
||||
modelContextWindowTokens: options?.modelContextWindowTokens,
|
||||
imageSanitization,
|
||||
});
|
||||
return [workspaceOnly ? wrapToolWorkspaceRootGuard(wrapped, workspaceRoot) : wrapped];
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
const processTool = createLazyProcessTool({
|
||||
cleanupMs: cleanupMsOverride ?? execConfig.cleanupMs,
|
||||
scopeKey,
|
||||
});
|
||||
if (tool.name === "bash" || tool.name === execToolName) {
|
||||
return [];
|
||||
}
|
||||
if (tool.name === "write") {
|
||||
if (sandboxRoot) {
|
||||
return [];
|
||||
}
|
||||
const wrapped = createHostWorkspaceWriteTool(workspaceRoot, { workspaceOnly });
|
||||
return [workspaceOnly ? wrapToolWorkspaceRootGuard(wrapped, workspaceRoot) : wrapped];
|
||||
}
|
||||
if (tool.name === "edit") {
|
||||
if (sandboxRoot) {
|
||||
return [];
|
||||
}
|
||||
const wrapped = createHostWorkspaceEditTool(workspaceRoot, { workspaceOnly });
|
||||
return [workspaceOnly ? wrapToolWorkspaceRootGuard(wrapped, workspaceRoot) : wrapped];
|
||||
}
|
||||
return [tool];
|
||||
})
|
||||
: [];
|
||||
const { cleanupMs: cleanupMsOverride, ...execDefaults } = options?.exec ?? {};
|
||||
const execTool = includeCoreTools
|
||||
? createLazyExecTool({
|
||||
...execDefaults,
|
||||
host: options?.exec?.host ?? execConfig.host,
|
||||
security: options?.exec?.security ?? execConfig.security,
|
||||
ask: options?.exec?.ask ?? execConfig.ask,
|
||||
trigger: options?.trigger,
|
||||
node: options?.exec?.node ?? execConfig.node,
|
||||
pathPrepend: options?.exec?.pathPrepend ?? execConfig.pathPrepend,
|
||||
safeBins: options?.exec?.safeBins ?? execConfig.safeBins,
|
||||
strictInlineEval: options?.exec?.strictInlineEval ?? execConfig.strictInlineEval,
|
||||
safeBinTrustedDirs: options?.exec?.safeBinTrustedDirs ?? execConfig.safeBinTrustedDirs,
|
||||
safeBinProfiles: options?.exec?.safeBinProfiles ?? execConfig.safeBinProfiles,
|
||||
agentId,
|
||||
cwd: workspaceRoot,
|
||||
allowBackground,
|
||||
scopeKey,
|
||||
sessionKey: options?.sessionKey,
|
||||
messageProvider: options?.messageProvider,
|
||||
currentChannelId: options?.currentChannelId,
|
||||
currentThreadTs: options?.currentThreadTs,
|
||||
accountId: options?.agentAccountId,
|
||||
backgroundMs: options?.exec?.backgroundMs ?? execConfig.backgroundMs,
|
||||
timeoutSec: options?.exec?.timeoutSec ?? execConfig.timeoutSec,
|
||||
approvalRunningNoticeMs:
|
||||
options?.exec?.approvalRunningNoticeMs ?? execConfig.approvalRunningNoticeMs,
|
||||
notifyOnExit: options?.exec?.notifyOnExit ?? execConfig.notifyOnExit,
|
||||
notifyOnExitEmptySuccess:
|
||||
options?.exec?.notifyOnExitEmptySuccess ?? execConfig.notifyOnExitEmptySuccess,
|
||||
sandbox: sandbox
|
||||
? {
|
||||
containerName: sandbox.containerName,
|
||||
workspaceDir: sandbox.workspaceDir,
|
||||
containerWorkdir: sandbox.containerWorkdir,
|
||||
env: sandbox.backend?.env ?? sandbox.docker.env,
|
||||
buildExecSpec: sandbox.backend?.buildExecSpec.bind(sandbox.backend),
|
||||
finalizeExec: sandbox.backend?.finalizeExec?.bind(sandbox.backend),
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
: null;
|
||||
const processTool = includeCoreTools
|
||||
? createLazyProcessTool({
|
||||
cleanupMs: cleanupMsOverride ?? execConfig.cleanupMs,
|
||||
scopeKey,
|
||||
})
|
||||
: null;
|
||||
const applyPatchTool =
|
||||
!applyPatchEnabled || (sandboxRoot && !allowWorkspaceWrites)
|
||||
? null
|
||||
@@ -584,9 +594,53 @@ export function createOpenClawCodingTools(options?: {
|
||||
: undefined,
|
||||
workspaceOnly: applyPatchWorkspaceOnly,
|
||||
});
|
||||
const pluginToolAllowlist = collectExplicitAllowlist([
|
||||
profilePolicy,
|
||||
providerProfilePolicy,
|
||||
globalPolicy,
|
||||
globalProviderPolicy,
|
||||
agentPolicy,
|
||||
agentProviderPolicy,
|
||||
groupPolicy,
|
||||
sandboxToolPolicy,
|
||||
subagentPolicy,
|
||||
options?.runtimeToolAllowlist ? { allow: options.runtimeToolAllowlist } : undefined,
|
||||
]);
|
||||
const pluginToolsOnly = includeCoreTools
|
||||
? []
|
||||
: resolveOpenClawPluginToolsForOptions({
|
||||
options: {
|
||||
agentSessionKey: options?.sessionKey,
|
||||
agentChannel: resolveGatewayMessageChannel(options?.messageProvider),
|
||||
agentAccountId: options?.agentAccountId,
|
||||
agentTo: options?.messageTo,
|
||||
agentThreadId: options?.messageThreadId,
|
||||
agentDir: options?.agentDir,
|
||||
workspaceDir: workspaceRoot,
|
||||
config: options?.config,
|
||||
fsPolicy,
|
||||
requesterSenderId: options?.senderId,
|
||||
senderIsOwner: options?.senderIsOwner,
|
||||
sessionId: options?.sessionId,
|
||||
sandboxBrowserBridgeUrl: sandbox?.browser?.bridgeUrl,
|
||||
allowHostBrowserControl: sandbox ? sandbox.browserAllowHostControl : true,
|
||||
sandboxed: !!sandbox,
|
||||
pluginToolAllowlist,
|
||||
currentChannelId: options?.currentChannelId,
|
||||
currentThreadTs: options?.currentThreadTs,
|
||||
currentMessageId: options?.currentMessageId,
|
||||
modelProvider: options?.modelProvider,
|
||||
modelHasVision: options?.modelHasVision,
|
||||
requireExplicitMessageTarget: options?.requireExplicitMessageTarget,
|
||||
disableMessageTool: options?.disableMessageTool,
|
||||
requesterAgentIdOverride: agentId,
|
||||
allowGatewaySubagentBinding: options?.allowGatewaySubagentBinding,
|
||||
},
|
||||
resolvedConfig: options?.config,
|
||||
});
|
||||
const tools: AnyAgentTool[] = [
|
||||
...base,
|
||||
...(sandboxRoot
|
||||
...(includeCoreTools && sandboxRoot
|
||||
? allowWorkspaceWrites
|
||||
? [
|
||||
workspaceOnly
|
||||
@@ -610,65 +664,56 @@ export function createOpenClawCodingTools(options?: {
|
||||
]
|
||||
: []
|
||||
: []),
|
||||
...(applyPatchTool ? [applyPatchTool as unknown as AnyAgentTool] : []),
|
||||
execTool as unknown as AnyAgentTool,
|
||||
processTool as unknown as AnyAgentTool,
|
||||
...(includeCoreTools && applyPatchTool ? [applyPatchTool as unknown as AnyAgentTool] : []),
|
||||
...(execTool ? [execTool as unknown as AnyAgentTool] : []),
|
||||
...(processTool ? [processTool as unknown as AnyAgentTool] : []),
|
||||
// Channel docking: include channel-defined agent tools (login, etc.).
|
||||
...listChannelAgentTools({ cfg: options?.config }),
|
||||
...createOpenClawTools({
|
||||
sandboxBrowserBridgeUrl: sandbox?.browser?.bridgeUrl,
|
||||
allowHostBrowserControl: sandbox ? sandbox.browserAllowHostControl : true,
|
||||
agentSessionKey: options?.sessionKey,
|
||||
agentChannel: resolveGatewayMessageChannel(options?.messageProvider),
|
||||
agentAccountId: options?.agentAccountId,
|
||||
agentTo: options?.messageTo,
|
||||
agentThreadId: options?.messageThreadId,
|
||||
agentGroupId: options?.groupId ?? null,
|
||||
agentGroupChannel: options?.groupChannel ?? null,
|
||||
agentGroupSpace: options?.groupSpace ?? null,
|
||||
agentMemberRoleIds: options?.memberRoleIds,
|
||||
agentDir: options?.agentDir,
|
||||
sandboxRoot,
|
||||
sandboxContainerWorkdir: sandbox?.containerWorkdir,
|
||||
sandboxFsBridge,
|
||||
fsPolicy,
|
||||
workspaceDir: workspaceRoot,
|
||||
spawnWorkspaceDir: options?.spawnWorkspaceDir
|
||||
? resolveWorkspaceRoot(options.spawnWorkspaceDir)
|
||||
: undefined,
|
||||
sandboxed: !!sandbox,
|
||||
config: options?.config,
|
||||
pluginToolAllowlist: collectExplicitAllowlist([
|
||||
profilePolicy,
|
||||
providerProfilePolicy,
|
||||
globalPolicy,
|
||||
globalProviderPolicy,
|
||||
agentPolicy,
|
||||
agentProviderPolicy,
|
||||
groupPolicy,
|
||||
sandboxToolPolicy,
|
||||
subagentPolicy,
|
||||
options?.runtimeToolAllowlist ? { allow: options.runtimeToolAllowlist } : undefined,
|
||||
]),
|
||||
currentChannelId: options?.currentChannelId,
|
||||
currentThreadTs: options?.currentThreadTs,
|
||||
currentMessageId: options?.currentMessageId,
|
||||
modelProvider: options?.modelProvider,
|
||||
modelId: options?.modelId,
|
||||
replyToMode: options?.replyToMode,
|
||||
hasRepliedRef: options?.hasRepliedRef,
|
||||
modelHasVision: options?.modelHasVision,
|
||||
requireExplicitMessageTarget: options?.requireExplicitMessageTarget,
|
||||
disableMessageTool: options?.disableMessageTool,
|
||||
enableHeartbeatTool,
|
||||
...(cronSelfRemoveOnlyJobId ? { cronSelfRemoveOnlyJobId } : {}),
|
||||
requesterAgentIdOverride: agentId,
|
||||
requesterSenderId: options?.senderId,
|
||||
senderIsOwner: options?.senderIsOwner,
|
||||
sessionId: options?.sessionId,
|
||||
onYield: options?.onYield,
|
||||
allowGatewaySubagentBinding: options?.allowGatewaySubagentBinding,
|
||||
}),
|
||||
...(includeCoreTools ? listChannelAgentTools({ cfg: options?.config }) : []),
|
||||
...(includeCoreTools
|
||||
? createOpenClawTools({
|
||||
sandboxBrowserBridgeUrl: sandbox?.browser?.bridgeUrl,
|
||||
allowHostBrowserControl: sandbox ? sandbox.browserAllowHostControl : true,
|
||||
agentSessionKey: options?.sessionKey,
|
||||
agentChannel: resolveGatewayMessageChannel(options?.messageProvider),
|
||||
agentAccountId: options?.agentAccountId,
|
||||
agentTo: options?.messageTo,
|
||||
agentThreadId: options?.messageThreadId,
|
||||
agentGroupId: options?.groupId ?? null,
|
||||
agentGroupChannel: options?.groupChannel ?? null,
|
||||
agentGroupSpace: options?.groupSpace ?? null,
|
||||
agentMemberRoleIds: options?.memberRoleIds,
|
||||
agentDir: options?.agentDir,
|
||||
sandboxRoot,
|
||||
sandboxContainerWorkdir: sandbox?.containerWorkdir,
|
||||
sandboxFsBridge,
|
||||
fsPolicy,
|
||||
workspaceDir: workspaceRoot,
|
||||
spawnWorkspaceDir: options?.spawnWorkspaceDir
|
||||
? resolveWorkspaceRoot(options.spawnWorkspaceDir)
|
||||
: undefined,
|
||||
sandboxed: !!sandbox,
|
||||
config: options?.config,
|
||||
pluginToolAllowlist,
|
||||
currentChannelId: options?.currentChannelId,
|
||||
currentThreadTs: options?.currentThreadTs,
|
||||
currentMessageId: options?.currentMessageId,
|
||||
modelProvider: options?.modelProvider,
|
||||
modelId: options?.modelId,
|
||||
replyToMode: options?.replyToMode,
|
||||
hasRepliedRef: options?.hasRepliedRef,
|
||||
modelHasVision: options?.modelHasVision,
|
||||
requireExplicitMessageTarget: options?.requireExplicitMessageTarget,
|
||||
disableMessageTool: options?.disableMessageTool,
|
||||
enableHeartbeatTool,
|
||||
...(cronSelfRemoveOnlyJobId ? { cronSelfRemoveOnlyJobId } : {}),
|
||||
requesterAgentIdOverride: agentId,
|
||||
requesterSenderId: options?.senderId,
|
||||
senderIsOwner: options?.senderIsOwner,
|
||||
sessionId: options?.sessionId,
|
||||
onYield: options?.onYield,
|
||||
allowGatewaySubagentBinding: options?.allowGatewaySubagentBinding,
|
||||
})
|
||||
: pluginToolsOnly),
|
||||
];
|
||||
const toolsForMemoryFlush =
|
||||
isMemoryFlushRun && memoryFlushWritePath
|
||||
|
||||
Reference in New Issue
Block a user