evOo-local/packages/adapters/src/executor-tools.ts
2026-10-03 14:25:28 +00:00

129 lines
4.6 KiB
TypeScript

import type { ConnectorTool } from "@rakazo/adapter-kit";
import { agentConnectionTools, builtinAgentTools } from "./builtin-tools.js";
import { selectCloudAgentTools } from "./cloud-agent-tools-select.js";
import { COACH_TOOL_NAMES } from "./coach-tools.js";
import { selectMemoryTools } from "./memory-tools.js";
import { filterImageReturningComputerTools } from "./model-vision.js";
import {
filterBuiltinToolsForRun,
filterBuiltinToolsForThread,
withScheduleTargetBot,
} from "./schedule-tools.js";
/** Browser tools that require a graphical computer (Chrome). */
export const PAGE_BROWSER_TOOL_NAMES = new Set([
"browser_navigate",
"browser_snapshot",
"browser_act",
]);
/** Builtin tools only the user's main bot may use; every other bot's runs never see them. */
export const MAIN_ONLY_BUILTIN_TOOLS: ReadonlySet<string> = new Set<string>([
"delegate_task",
"notify_user",
"update_user_profile",
...COACH_TOOL_NAMES,
]);
/** Tools that write what the user's bots know about the user, so only the user may drive them. */
const USER_MODEL_TOOLS: ReadonlySet<string> = new Set<string>([
"update_user_profile",
"notify_user",
...COACH_TOOL_NAMES,
]);
const BUILTIN_TOOL_NAMES = [...builtinAgentTools, ...agentConnectionTools].map((tool) => tool.name);
/** The bot fields that decide which builtin tools its runs may use. */
export interface BuiltinToolPolicy {
kind: string;
restrictBuiltinTools: boolean;
allowedBuiltinTools: readonly string[];
}
/**
* Builtin tool names a bot's allowlist can choose from, in catalog order. Agent connection tools
* only ever reach chat-linked bots, so only those list them.
*/
export function builtinToolNamesForBot(bot: { kind: string; chatLinked: boolean }): string[] {
return [...builtinAgentTools, ...(bot.chatLinked ? agentConnectionTools : [])]
.map((tool) => tool.name)
.filter((name) => bot.kind === "main" || !MAIN_ONLY_BUILTIN_TOOLS.has(name));
}
/** Names that are not builtin tools; an allowlist must not carry typos or connector tools. */
export function unknownBuiltinToolNames(names: readonly string[]): string[] {
return names.filter((name) => !BUILTIN_TOOL_NAMES.includes(name));
}
/**
* Least privilege per bot: main-only tools stay with the main bot, a restricted bot keeps only
* its allowlist (empty means none), and the main bot may schedule for another bot.
*/
export function filterBuiltinToolsForBot(
tools: ConnectorTool[],
bot: BuiltinToolPolicy,
): ConnectorTool[] {
const main = bot.kind === "main";
const allowed = new Set(bot.allowedBuiltinTools);
return tools
.filter(
(tool) =>
(main || !MAIN_ONLY_BUILTIN_TOOLS.has(tool.name)) &&
(!bot.restrictBuiltinTools || allowed.has(tool.name)),
)
.map((tool) => (main && tool.name === "schedule_create" ? withScheduleTargetBot(tool) : tool));
}
export function filterPageBrowserTools<T extends { name: string }>(
tools: T[],
pageBrowserAllowed: boolean,
): T[] {
if (pageBrowserAllowed) return tools;
return tools.filter((tool) => !PAGE_BROWSER_TOOL_NAMES.has(tool.name));
}
export function selectBuiltinToolsForRun(options: {
graphicalToolsAllowed: boolean;
/** Page browser tools need a graphical computer (Chrome), not model vision. */
pageBrowserAllowed?: boolean;
groupId: string | null;
trigger: string;
semanticMemoryEnabled: boolean;
cloudAgentEnabled?: boolean;
messagingChannelRun: boolean;
/** Cross-owner agent connections only exist for chat-linked bots. */
agentConnectionsEnabled?: boolean;
/** Answering another owner's agent: what the run hears is not the user, so no profile writes. */
foreignAgentRun?: boolean;
bot: BuiltinToolPolicy;
}) {
const selected = selectCloudAgentTools(
selectMemoryTools(
filterBuiltinToolsForRun(
filterBuiltinToolsForThread(
filterPageBrowserTools(
filterImageReturningComputerTools(builtinAgentTools, options.graphicalToolsAllowed),
options.pageBrowserAllowed ?? options.graphicalToolsAllowed,
),
options.groupId,
),
options.trigger,
),
options.semanticMemoryEnabled,
),
Boolean(options.cloudAgentEnabled),
).filter(
(tool) =>
!(options.foreignAgentRun && USER_MODEL_TOOLS.has(tool.name)) &&
(!options.messagingChannelRun ||
(!["remember", "save_memory", "recall_memory", "forget_memory"].includes(tool.name) &&
!USER_MODEL_TOOLS.has(tool.name) &&
!tool.name.startsWith("scratchpad_"))),
);
return filterBuiltinToolsForBot(
options.agentConnectionsEnabled ? [...selected, ...agentConnectionTools] : selected,
options.bot,
);
}