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]>
173 lines
6.0 KiB
TypeScript
173 lines
6.0 KiB
TypeScript
import type { BUN_SQLITE_COBBLE_TASKS } from "@/db/types/db";
|
|
import pushBotNotice from "@/src/bots-queue/push-bot-notice";
|
|
import AppError from "@/src/utils/app-error";
|
|
import handOverPlace from "@/src/utils/hand-over-place";
|
|
import grabUserConversation from "../bot-sessions/grab-user-conversation";
|
|
import ensureBotSession from "../bot-sessions/ensure-bot-session";
|
|
import enqueueBotJob from "../bots-queue/enqueue-bot-job";
|
|
import grabOpenBotTask from "../bots-queue/grab-open-bot-task";
|
|
import grabBotById from "../bots/grab-bot-by-id";
|
|
import insertBotMessage from "../bots/insert-bot-message";
|
|
import grabBotComputers from "../computers/grab-bot-computers";
|
|
import ensureBotInWorkspace from "../workspaces/ensure-bot-in-workspace";
|
|
import addTaskNote from "./add-task-note";
|
|
import grabTaskRow, { isOpenTask } from "./grab-task-scope";
|
|
import moveTask from "./move-task";
|
|
import updateTask from "./update-task";
|
|
|
|
type Params = {
|
|
/**
|
|
* The user the work runs for. The caller checked they (or the bot's
|
|
* run) reach the task.
|
|
*/
|
|
user_id: number | string;
|
|
task_id: number | string;
|
|
/**
|
|
* The bot taking it over.
|
|
*/
|
|
to_bot_id: number | string;
|
|
/**
|
|
* Where the work runs, for a task on a bot's own list: left out, the
|
|
* new bot's own chat. A task on a workspace's list always runs there.
|
|
*/
|
|
workspace_id?: number | string | null;
|
|
/**
|
|
* More for the brief; the task's own brief comes first.
|
|
*/
|
|
instructions?: string;
|
|
/**
|
|
* Who hands it over, shown in the new bot's conversation and the
|
|
* task's log.
|
|
*/
|
|
handed_by: string;
|
|
/**
|
|
* The bot handing it over and the session it did from: the result is
|
|
* reported back there (`relayJobToRequester`). None when a user does.
|
|
*/
|
|
requested_by_bot_id?: number | string | null;
|
|
requested_by_session_id?: number | string | null;
|
|
};
|
|
|
|
export type HandedOverTask = {
|
|
task: BUN_SQLITE_COBBLE_TASKS;
|
|
job_id: number | string;
|
|
bot_name: string;
|
|
workspace_id: number | null;
|
|
/**
|
|
* Says the bot was added to the workspace first, or "".
|
|
*/
|
|
joined: string;
|
|
};
|
|
|
|
/**
|
|
* Hands an open task to another bot and starts the work: a task on a
|
|
* workspace's list stays there (the work runs in that workspace), one on
|
|
* a bot's own list moves to the new bot's. The task goes to doing, its log
|
|
* says who handed it over, and a job is queued in the new bot's
|
|
* conversation with the task's brief and `params.tracker_task_id`, so the
|
|
* bot moves this same task along and the job's end is noted on it.
|
|
*/
|
|
export default async function handOverTask({
|
|
user_id,
|
|
task_id,
|
|
to_bot_id,
|
|
workspace_id,
|
|
instructions,
|
|
handed_by,
|
|
requested_by_bot_id,
|
|
requested_by_session_id,
|
|
}: Params): Promise<HandedOverTask> {
|
|
const task = await grabTaskRow({ task_id });
|
|
|
|
if (!isOpenTask({ status: task.status })) {
|
|
throw new AppError(`Task #${task.id} is ${task.status}. Only an open task can be handed over.`);
|
|
}
|
|
|
|
if (requested_by_bot_id && Number(to_bot_id) == Number(requested_by_bot_id)) {
|
|
throw new AppError(`Hand the task to another bot, not yourself.`);
|
|
}
|
|
|
|
const run_workspace_id = handOverPlace({
|
|
workspace_id: task.workspace_id,
|
|
asked_workspace_id: workspace_id,
|
|
label: `Task #${task.id}`,
|
|
});
|
|
|
|
const bot = await grabBotById({ bot_id: to_bot_id });
|
|
|
|
if (!bot?.id) {
|
|
throw new AppError(`Bot not found.`);
|
|
}
|
|
|
|
const joined = run_workspace_id ? await ensureBotInWorkspace({ user_id, bot, workspace_id: run_workspace_id }) : "";
|
|
|
|
/**
|
|
* The user must reach the bot there: its own chat, or the workspace.
|
|
*/
|
|
const conversation = await grabUserConversation({ user_id, bot_id: bot.id, workspace_id: run_workspace_id });
|
|
const session = await ensureBotSession({ user_id, bot_id: bot.id, workspace_id: run_workspace_id });
|
|
const title = (task.title || "Task").slice(0, 80);
|
|
const open = await grabOpenBotTask({ bot_id: bot.id, session_id: Number(session.id), title });
|
|
|
|
if (open) {
|
|
throw new AppError(
|
|
`${bot.name} already has "${title}" ${open.status == "pending" ? "queued" : "under way"} (job #${open.id}).`,
|
|
);
|
|
}
|
|
|
|
const moved = task.workspace_id ? task : await moveTask({ task_id: task.id!, bot_id: bot.id });
|
|
const computers = await grabBotComputers({ bot_id: bot.id, workspace_id: run_workspace_id });
|
|
|
|
const brief = [
|
|
task.description?.trim(),
|
|
instructions?.trim(),
|
|
`This is task #${task.id} on ${task.workspace_id ? "this workspace's" : "your"} list, handed over by ${handed_by}. Move it along with update_task as you work, file what it produces on it, and mark it done once you've verified the work.`,
|
|
]
|
|
.filter(Boolean)
|
|
.join("\n\n");
|
|
|
|
const job_id = await enqueueBotJob({
|
|
user_id,
|
|
bot_id: bot.id,
|
|
session_id: Number(session.id),
|
|
title,
|
|
instructions: brief,
|
|
params: { tracker_task_id: Number(task.id) },
|
|
computer_id: computers[0]?.id || undefined,
|
|
requested_by_bot_id: requested_by_bot_id || undefined,
|
|
requested_by_session_id: requested_by_session_id || undefined,
|
|
});
|
|
|
|
await updateTask({
|
|
user_id,
|
|
task_id: task.id!,
|
|
status: "doing",
|
|
updated_by_bot_id: requested_by_bot_id || null,
|
|
});
|
|
|
|
await addTaskNote({
|
|
task_id: task.id!,
|
|
text: `${handed_by} handed this over to ${bot.name}.`,
|
|
author: requested_by_bot_id ? "bot" : "user",
|
|
bot_id: requested_by_bot_id || null,
|
|
});
|
|
|
|
await pushBotNotice({
|
|
bot_id: bot.id,
|
|
message: await insertBotMessage({
|
|
bot_id: bot.id,
|
|
session_id: Number(session.id),
|
|
workspace_id: run_workspace_id,
|
|
text: `${handed_by} handed me task #${task.id}: **${title}**`,
|
|
}),
|
|
});
|
|
|
|
return {
|
|
task: { ...moved, status: "doing" },
|
|
job_id,
|
|
bot_name: conversation.bot.name || bot.name || "The bot",
|
|
workspace_id: run_workspace_id,
|
|
joined,
|
|
};
|
|
}
|