Scope workspace tasks to the bot that has them

A workspace's task now carries the bot that has it there (bot_id with
workspace_id), or none while it's shared. A bot reaches its own list and,
in its workspaces, its own tasks plus the shared ones; a workspace admin
every task there. Bots create workspace tasks for themselves by default,
take a shared one by starting it, and hand-overs record the new bot.
Removing or deleting a bot makes its workspace tasks shared again. The
conversation's Tasks modal shows that bot's tasks in the workspace.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-27 19:19:01 +01:00
co-authored by Claude Opus 5.5
parent 184407d76b
commit 864dcce30a
31 changed files with 316 additions and 153 deletions
@@ -0,0 +1,27 @@
import BunSQLite from "@moduletrace/bun-sqlite";
import AppError from "@/src/utils/app-error";
type Params = {
bot_id: number | string;
workspace_id: number | string;
};
/**
* A workspace's task can only be had by a bot working there: a member
* (`workspace_bots`) or the coordinator, which is in every workspace.
*/
export default async function assertBotWorksThere({ bot_id, workspace_id }: Params) {
const res = await BunSQLite.sql<{ id: number }>({
sql: `SELECT b.id FROM bots b
WHERE b.id = ? AND (
b.kind = 'coordinator'
OR EXISTS (SELECT 1 FROM workspace_bots wb WHERE wb.bot_id = b.id AND wb.workspace_id = ?)
)
LIMIT 1`,
values: [Number(bot_id), Number(workspace_id)],
});
if (!res.payload?.[0]?.id) {
throw new AppError(`Bot ${bot_id} doesn't work in workspace ${workspace_id}, so it can't have a task there.`);
}
}