Let the coordinator and workspace admins hand over tasks and routines
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]>
This commit is contained in:
@@ -6,6 +6,7 @@ import setBotFace from "../bots/set-bot-face";
|
||||
import countOpenBotJobs from "./count-open-bot-jobs";
|
||||
import grabLockedBotJob from "./grab-locked-bot-job";
|
||||
import relayJobToRequester from "./relay-job-to-requester";
|
||||
import noteTrackerTask from "./note-tracker-task";
|
||||
import jobSessionId from "./job-session-id";
|
||||
import grabJobUserId from "./grab-job-user-id";
|
||||
import grabRunningBotJobFace from "./grab-running-bot-job-face";
|
||||
@@ -40,7 +41,7 @@ type Params = {
|
||||
* after a task and idle after a reply; with others open, it shows the one
|
||||
* still running (a reply in another session mustn't hide a task's row).
|
||||
* Nobody else's face changes. A handed-over job's result also goes to the bot that handed
|
||||
* it over (`relay`). Throws if this handler no longer holds the job.
|
||||
* it over (`relay`), and a handed-over task left open says so (`noteTrackerTask`). Throws if this handler no longer holds the job.
|
||||
*/
|
||||
export default async function completeBotJob({
|
||||
job_id,
|
||||
@@ -129,6 +130,8 @@ export default async function completeBotJob({
|
||||
face: job.kind == "reply" ? "idle" : "done",
|
||||
});
|
||||
|
||||
await noteTrackerTask({ job });
|
||||
|
||||
const relay = silent ? null : await relayJobToRequester({ job, text: result });
|
||||
|
||||
return { message, bot, relay };
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
|
||||
/**
|
||||
* Whether a job is still queued or working.
|
||||
*/
|
||||
export default async function isBotJobOpen({ job_id }: { job_id: number | string }) {
|
||||
const res = await BunSQLite.sql({
|
||||
sql: `SELECT id FROM bots_queue WHERE id = ? AND status IN ('pending', 'processing') LIMIT 1`,
|
||||
values: [job_id],
|
||||
});
|
||||
|
||||
return Boolean(res.payload?.[0]);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import type { BUN_SQLITE_COBBLE_BOTS_QUEUE } from "@/db/types/db";
|
||||
import parseJobJson from "@/src/bots-queue/parse-job-json";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
import { logError } from "@/src/functions/backend/logger";
|
||||
import grabBotById from "../bots/grab-bot-by-id";
|
||||
import addTaskNote from "../tasks/add-task-note";
|
||||
import grabTaskRow, { isOpenTask } from "../tasks/grab-task-scope";
|
||||
import updateTask from "../tasks/update-task";
|
||||
import grabJobUserId from "./grab-job-user-id";
|
||||
|
||||
type Params = {
|
||||
job: BUN_SQLITE_COBBLE_BOTS_QUEUE;
|
||||
/**
|
||||
* Why it failed; left out when it finished.
|
||||
*/
|
||||
failure?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The end of a job worked on a handed-over task (`params.tracker_task_id`,
|
||||
* set by `handOverTask`), written on that task while it's still open: a
|
||||
* run that finished without closing it says so, a failed one blocks it
|
||||
* with the reason. The bot moves the task along itself as it works; this
|
||||
* only keeps the task honest when it didn't. Best effort: never fails
|
||||
* the job's wrap-up.
|
||||
*/
|
||||
export default async function noteTrackerTask({ job, failure }: Params) {
|
||||
const tracker_task_id = Number(parseJobJson({ text: job.params })?.tracker_task_id || 0);
|
||||
|
||||
if (!tracker_task_id || !job.bot_id) return;
|
||||
|
||||
try {
|
||||
const task = await grabTaskRow({ task_id: tracker_task_id });
|
||||
|
||||
if (!isOpenTask({ status: task.status })) return;
|
||||
|
||||
const name = (await grabBotById({ bot_id: job.bot_id }))?.name || "The bot";
|
||||
|
||||
if (failure === undefined) {
|
||||
await addTaskNote({
|
||||
task_id: tracker_task_id,
|
||||
text: `${name}'s run finished; the task is still ${task.status}.`,
|
||||
bot_id: job.bot_id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const reason = `${name} couldn't finish it. ${failure}`.trim().slice(0, AppData.TaskNoteMaxLength);
|
||||
const user_id = await grabJobUserId({ job });
|
||||
|
||||
if (user_id) {
|
||||
await updateTask({
|
||||
user_id,
|
||||
task_id: tracker_task_id,
|
||||
status: "blocked",
|
||||
blocked_reason: reason,
|
||||
updated_by_bot_id: job.bot_id,
|
||||
});
|
||||
}
|
||||
|
||||
await addTaskNote({ task_id: tracker_task_id, text: reason, bot_id: job.bot_id });
|
||||
} catch (error) {
|
||||
await logError({
|
||||
title: `Couldn't note job #${job.id} on task #${tracker_task_id}`,
|
||||
error,
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import type { BUN_SQLITE_COBBLE_BOTS_QUEUE } from "@/db/types/db";
|
||||
import insertBotMessage from "../bots/insert-bot-message";
|
||||
import setBotFace from "../bots/set-bot-face";
|
||||
import relayJobToRequester from "./relay-job-to-requester";
|
||||
import noteTrackerTask from "./note-tracker-task";
|
||||
import jobSessionId from "./job-session-id";
|
||||
import grabJobUserId from "./grab-job-user-id";
|
||||
import { BotNeedsYouStatusLine } from "@/src/dict/bot-queue-dict";
|
||||
@@ -16,7 +17,8 @@ type Params = {
|
||||
* Wraps up a job that is out of attempts: stamps `finished_at` (so it's
|
||||
* reported once), tells the user in the bot's conversation and sets the
|
||||
* bot to needs-you for that user. Its task stays active, since the work isn't done. A
|
||||
* handed-over job's failure also goes to the bot that handed it over.
|
||||
* handed-over job's failure also goes to the bot that handed it over, and
|
||||
* blocks the handed-over task it worked on (`noteTrackerTask`).
|
||||
*/
|
||||
export default async function notifyBotJobFailed({
|
||||
job,
|
||||
@@ -59,6 +61,8 @@ export default async function notifyBotJobFailed({
|
||||
})
|
||||
: null;
|
||||
|
||||
await noteTrackerTask({ job, failure: failure_message || job.last_error || "" });
|
||||
|
||||
const relay = await relayJobToRequester({
|
||||
job,
|
||||
text: failure_message || job.last_error || "",
|
||||
|
||||
Reference in New Issue
Block a user