delegate_task takes a task_id to hand over a task already on a list: it moves to the worker (a workspace's task stays on its list), goes to doing, and the job is linked back so its end is noted on the task. hand_over_schedule moves a bot's own routine to another bot, keeping its workspace. Users get Hand over in the Tasks UI and both routine lists. Co-Authored-By: Claude Opus 5.5 <[email protected]>
258 lines
8.9 KiB
TypeScript
258 lines
8.9 KiB
TypeScript
import type { BUN_SQLITE_COBBLE_BOTS, BUN_SQLITE_COBBLE_COMPUTERS } from "@/db/types/db";
|
|
import grabBotComputers from "@/src/functions/backend/db/computers/grab-bot-computers";
|
|
import grabCrewWorkers from "@/src/functions/backend/db/bots/grab-crew-workers";
|
|
import type { StorageScope } from "@/src/utils/storage-access";
|
|
import type { HarnessTool } from "../../types";
|
|
import askToTakeOverTool from "../computer/ask-to-take-over-tool";
|
|
import searchComputersTool from "../computer/search-computers-tool";
|
|
import addBotToWorkspaceTool from "./add-bot-to-workspace-tool";
|
|
import assignComputerTool from "./assign-computer-tool";
|
|
import createBotTool from "./create-bot-tool";
|
|
import createWorkspaceTool from "./create-workspace-tool";
|
|
import delegateTaskTool from "./delegate-task-tool";
|
|
import handOverScheduleTool from "./hand-over-schedule-tool";
|
|
import deleteBotTool from "./delete-bot-tool";
|
|
import deleteWorkspaceTool from "./delete-workspace-tool";
|
|
import findBotTool from "./find-bot-tool";
|
|
import findWorkspaceTool from "./find-workspace-tool";
|
|
import grabCoordinatorExtensionTools from "./extensions/grab-coordinator-extension-tools";
|
|
import grabBotTools from "./grab-bot-tools";
|
|
import removeBotFromWorkspaceTool from "./remove-bot-from-workspace-tool";
|
|
import updateBotTool from "./update-bot-tool";
|
|
import updateWorkspaceTool from "./update-workspace-tool";
|
|
import grabUserWorkspaces from "@/src/functions/backend/db/workspaces/grab-user-workspaces";
|
|
import setWorkspaceInstructionsTool from "./set-workspace-instructions-tool";
|
|
import startTaskTool from "./start-task-tool";
|
|
import computerRunDirs from "@/src/utils/computer-work-dirs";
|
|
import type { BotRunPowers } from "@/src/functions/backend/db/bot-types/grab-bot-run-powers";
|
|
import grabWorkspaceAdminTools, { type WorkspaceAdminTools } from "./grab-workspace-admin-tools";
|
|
import setBotTypeTool from "./set-bot-type-tool";
|
|
|
|
type Params = {
|
|
bot: BUN_SQLITE_COBBLE_BOTS;
|
|
/**
|
|
* What the bot's types let it do on this run (`grabBotRunPowers`).
|
|
*/
|
|
powers: BotRunPowers;
|
|
user_id: number | string;
|
|
/**
|
|
* The bot's computers where it runs, the current one first
|
|
* (`grabBotComputers`).
|
|
*/
|
|
computers: BUN_SQLITE_COBBLE_COMPUTERS[];
|
|
/**
|
|
* The session being answered; tasks report back to it.
|
|
*/
|
|
session_id: number | string;
|
|
/**
|
|
* The session's workspace, if any.
|
|
*/
|
|
workspace_id?: number | string | null;
|
|
/**
|
|
* The user message being answered.
|
|
*/
|
|
message_id?: number | string;
|
|
/**
|
|
* The files the run reaches (`grabRunScopes`), so a picture it sets is
|
|
* copied from one it can already read.
|
|
*/
|
|
scopes?: StorageScope[];
|
|
/**
|
|
* Called when the bot asks the user to take over a computer.
|
|
*/
|
|
onTakeOver?: (params: { ref: string }) => void;
|
|
/**
|
|
* Called when the coordinator proposes an MCP server or tool for the
|
|
* user to confirm.
|
|
*/
|
|
onPropose?: (params: { ref: string }) => void;
|
|
/**
|
|
* Called with what a workspace admin leads, when the run is one, so
|
|
* the reply can say so in its prompt.
|
|
*/
|
|
onWorkspaceAdmin?: (params: WorkspaceAdminTools) => void;
|
|
};
|
|
|
|
/**
|
|
* The tools a bot replies in chat with: its own (memory, instructions, and
|
|
* in a workspace switching its default computer), start_task for longer
|
|
* work, search_computers to find what's on its computers,
|
|
* ask_user_to_take_over when only the user can do something on one, and
|
|
* for the coordinator (the super admin's alone) create_workspace and
|
|
* update_workspace (a workspace and its shared context), create_bot to
|
|
* grow the crew, delegate_task to every worker (the workspace's in a
|
|
* workspace, where the task runs; from its own chat, in the worker's own
|
|
* chat or in a workspace it names) and hand_over_schedule beside it, plus, in its own chat, find_bot and
|
|
* find_workspace,
|
|
* add_bot_to_workspace and remove_bot_from_workspace, assign_computer,
|
|
* update_bot, delete_bot and delete_workspace (which workspace, which
|
|
* computers, and which name, role and core instructions each crew member
|
|
* gets, and what goes) and the install's skills, MCP servers and custom
|
|
* tools (`grabCoordinatorExtensionTools`) or, in a workspace,
|
|
* set_bot_workspace_instructions, plus set_bot_type to make a worker a
|
|
* workspace's admin. A workspace admin, in the workspace it leads, gets
|
|
* `grabWorkspaceAdminTools`. Every gate reads the bot's types (`powers`),
|
|
* never `bots.kind`. Anything else on a computer goes through a task,
|
|
* never mid-reply.
|
|
*/
|
|
export default async function grabBotChatTools({
|
|
bot,
|
|
powers,
|
|
user_id,
|
|
computers,
|
|
session_id,
|
|
workspace_id,
|
|
message_id,
|
|
scopes = [],
|
|
onTakeOver,
|
|
onPropose,
|
|
onWorkspaceAdmin,
|
|
}: Params): Promise<HarnessTool[]> {
|
|
const bot_id = bot.id || 0;
|
|
const tools = [
|
|
...(await grabBotTools({
|
|
user_id,
|
|
bot_id,
|
|
workspace_id,
|
|
computers,
|
|
session_id,
|
|
is_coordinator: powers.is_coordinator,
|
|
admin_workspace_ids: powers.admin_workspace_ids,
|
|
scopes,
|
|
})),
|
|
startTaskTool({
|
|
user_id,
|
|
bot_id,
|
|
session_id,
|
|
computers,
|
|
message_id,
|
|
}),
|
|
...(computers.length
|
|
? [
|
|
searchComputersTool({
|
|
bot_id,
|
|
computers,
|
|
workspace_id,
|
|
dirs: computerRunDirs({ bot_id, bot_kind: bot.kind, user_id, workspace_id }),
|
|
}),
|
|
askToTakeOverTool({ computers, onAsk: (params) => onTakeOver?.(params) }),
|
|
]
|
|
: []),
|
|
];
|
|
|
|
if (!powers.is_coordinator) {
|
|
if (!workspace_id || !powers.is_workspace_admin) return tools;
|
|
|
|
const admin = await grabWorkspaceAdminTools({ bot, user_id, workspace_id, powers, session_id, scopes });
|
|
|
|
onWorkspaceAdmin?.(admin);
|
|
tools.push(...admin.tools);
|
|
|
|
return tools;
|
|
}
|
|
|
|
const workspaces = (await grabUserWorkspaces({ user_id })).filter((w) => !w.archived);
|
|
|
|
tools.push(createWorkspaceTool({ user_id, workspaces }));
|
|
|
|
if (workspaces.length) {
|
|
tools.push(updateWorkspaceTool({ user_id, workspaces, workspace_id, scopes }));
|
|
}
|
|
|
|
tools.push(createBotTool({ user_id, workspaces, workspace_id }));
|
|
|
|
const workers = await grabCrewWorkers({ workspace_id });
|
|
|
|
if (workspace_id) {
|
|
if (!workers.length) return tools;
|
|
|
|
tools.push(delegateTaskTool({
|
|
user_id,
|
|
requester_id: bot_id,
|
|
session_id,
|
|
workspace_id,
|
|
requester_name: bot.name || "The coordinator",
|
|
workers,
|
|
is_coordinator: true,
|
|
}));
|
|
tools.push(handOverScheduleTool({
|
|
user_id,
|
|
bot_id,
|
|
workspace_id,
|
|
requester_name: bot.name || "The coordinator",
|
|
workers,
|
|
}));
|
|
tools.push(setWorkspaceInstructionsTool({ user_id, workspace_id, workers }));
|
|
|
|
if (powers.can("set_bot_types")) {
|
|
tools.push(setBotTypeTool({ user_id, bot_id, workers, workspaces, workspace_id }));
|
|
}
|
|
|
|
return tools;
|
|
}
|
|
|
|
tools.push(
|
|
findBotTool(),
|
|
findWorkspaceTool({ user_id }),
|
|
...(await grabCoordinatorExtensionTools({ user_id, bot_id, session_id, workspace_id, onPropose })),
|
|
);
|
|
|
|
if (workspaces.length) {
|
|
tools.push(deleteWorkspaceTool({ user_id, workspaces }));
|
|
}
|
|
|
|
if (!workers.length) return tools;
|
|
|
|
tools.push(
|
|
delegateTaskTool({
|
|
user_id,
|
|
requester_id: bot_id,
|
|
session_id,
|
|
workspace_id,
|
|
requester_name: bot.name || "The coordinator",
|
|
workers,
|
|
workspaces,
|
|
is_coordinator: true,
|
|
}),
|
|
handOverScheduleTool({
|
|
user_id,
|
|
bot_id,
|
|
workspace_id,
|
|
requester_name: bot.name || "The coordinator",
|
|
workers,
|
|
workspaces,
|
|
}),
|
|
);
|
|
|
|
tools.push(updateBotTool({ user_id, workers }), deleteBotTool({ user_id, workers }));
|
|
|
|
if (computers.length) {
|
|
const access = Object.fromEntries(
|
|
await Promise.all(
|
|
workers.map(async (worker) => [
|
|
worker.id || 0,
|
|
await grabBotComputers({
|
|
bot_id: worker.id || 0,
|
|
include_workspace_computers: false,
|
|
}),
|
|
]),
|
|
),
|
|
);
|
|
|
|
tools.push(assignComputerTool({ user_id, workers, computers, access }));
|
|
}
|
|
|
|
if (workspaces.length) {
|
|
tools.push(
|
|
addBotToWorkspaceTool({ user_id, workers, workspaces }),
|
|
removeBotFromWorkspaceTool({ user_id, workers, workspaces }),
|
|
);
|
|
|
|
if (powers.can("set_bot_types")) {
|
|
tools.push(setBotTypeTool({ user_id, bot_id, workers, workspaces }));
|
|
}
|
|
}
|
|
|
|
return tools;
|
|
}
|