evOo-local/packages/adapters/src/executor-tools.ts

170 lines
6.1 KiB
TypeScript

import type { ConnectorTool, MediaCapabilities } 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 { selectMediaTools } from "./media-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",
]);
/**
* Main-only tools that stay with the user's personal main agent: a project's main bot leads its
* project but does not coach or nudge the user.
*/
export const PERSONAL_AGENT_TOOLS: ReadonlySet<string> = new Set<string>([
"notify_user",
...COACH_TOOL_NAMES,
]);
/** 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",
"update_user_profile",
...PERSONAL_AGENT_TOOLS,
]);
/** Tools over the user's own photo and video library. */
export const MEDIA_LIBRARY_TOOLS: ReadonlySet<string> = new Set(["search_media", "import_media"]);
/** 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[];
/** The main bot of the user's home space; only it gets the personal agent's tools. */
personalAgent?: boolean;
}
/**
* 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 personal = main && bot.personalAgent === true;
const allowed = new Set(bot.allowedBuiltinTools);
return tools
.filter(
(tool) =>
(main || !MAIN_ONLY_BUILTIN_TOOLS.has(tool.name)) &&
(personal || !PERSONAL_AGENT_TOOLS.has(tool.name)) &&
(!bot.restrictBuiltinTools || allowed.has(tool.name)),
)
.map((tool) =>
main && (tool.name === "schedule_create" || tool.name === "suggest")
? 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;
/** Media tools appear only for operations the space can run. */
mediaCapabilities?: MediaCapabilities;
/** The run's user has a media library folder. */
mediaLibraryEnabled?: 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 = selectMediaTools(
selectCloudAgentTools(
selectMemoryTools(
filterBuiltinToolsForRun(
filterBuiltinToolsForThread(
filterPageBrowserTools(
filterImageReturningComputerTools(builtinAgentTools, options.graphicalToolsAllowed),
options.pageBrowserAllowed ?? options.graphicalToolsAllowed,
),
options.groupId,
),
options.trigger,
),
options.semanticMemoryEnabled,
),
Boolean(options.cloudAgentEnabled),
),
options.mediaCapabilities,
).filter(
(tool) =>
!(options.foreignAgentRun && USER_MODEL_TOOLS.has(tool.name)) &&
// The library is the user's private media: not for other owners' agents or shared chats.
(!MEDIA_LIBRARY_TOOLS.has(tool.name) ||
(options.mediaLibraryEnabled === true &&
!options.foreignAgentRun &&
!options.messagingChannelRun)) &&
(!options.messagingChannelRun ||
(![
"remember",
"save_memory",
"recall_memory",
"forget_memory",
"show_card",
"suggest",
].includes(tool.name) &&
!tool.name.startsWith("autopilot_") &&
!tool.name.startsWith("plan_") &&
!USER_MODEL_TOOLS.has(tool.name) &&
!tool.name.startsWith("scratchpad_"))),
);
return filterBuiltinToolsForBot(
options.agentConnectionsEnabled ? [...selected, ...agentConnectionTools] : selected,
options.bot,
);
}