Files
cobble/src/functions/backend/db/bots/grab-bots.ts
T
tbenandClaude Opus 5.5 0ce61cdb49 Add workspaces, conversation sessions, app-managed computers and pictures
- Rail: Add icon with stacked form modals (bot, workspace, computer),
  coordinator, recent bots with "See all bots", Workspaces section, and
  a workspace mode listing only its crew
- Workspaces: page for context, bots (per-workspace instructions and
  memory), computers (default, link, unlink), models and delete;
  conversations scoped per bot per workspace
- Sessions: conversations split into searchable, reopenable sessions
  with carry-over summaries; sessions and new-session header icons
- Computers: created and provisioned over the docker socket, home dir
  kept at DATA_DIR/computers/<id> after delete; super admins only
- Models: workspace settings win over a bot's own, then the defaults
- Pictures for bots and workspaces; bots editable from the header
- Coordinator tools: update_workspace, set_bot_workspace_instructions

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 08:39:33 +01:00

61 lines
2.1 KiB
TypeScript

import type { BotWithLastMessage } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
type Params = {
user_id: number | string;
/**
* A workspace's crew instead: the coordinator and the bots in it, with
* their conversations in that workspace.
*/
workspace_id?: number | string | null;
};
/**
* A user's crew for the rail: the coordinator pinned first, then
* workers by latest activity. Each row carries its conversation's unread
* flag, latest activity and latest message: in its own chat, or in the
* workspace when one is given.
*/
export default async function grabBots({
user_id,
workspace_id,
}: Params): Promise<BotWithLastMessage[]> {
const scope = workspace_id ? Number(workspace_id) : null;
let sql = ``;
sql += `SELECT\n`;
sql += ` b.*,\n`;
sql += ` COALESCE(t.unread, 0) AS unread,\n`;
sql += ` t.last_activity_at AS last_activity_at,\n`;
sql += ` lm.text AS last_message_text,\n`;
sql += ` lm.created_at AS last_message_at\n`;
sql += `FROM bots b\n`;
sql += `LEFT JOIN (\n`;
sql += ` SELECT bot_id, MAX(unread) AS unread, MAX(last_activity_at) AS last_activity_at\n`;
sql += ` FROM bot_sessions WHERE workspace_id IS ? GROUP BY bot_id\n`;
sql += `) t ON t.bot_id = b.id\n`;
sql += `LEFT JOIN bot_messages lm ON lm.id = (\n`;
sql += ` SELECT id FROM bot_messages\n`;
sql += ` WHERE bot_id = b.id AND workspace_id IS ?\n`;
sql += ` ORDER BY created_at DESC, id DESC\n`;
sql += ` LIMIT 1\n`;
sql += `)\n`;
sql += `WHERE b.user_id = ?\n`;
if (scope) {
sql += `AND (b.kind = 'coordinator' OR b.id IN (SELECT bot_id FROM workspace_bots WHERE workspace_id = ?))\n`;
}
sql += `ORDER BY\n`;
sql += ` CASE WHEN b.kind = 'coordinator' THEN 0 ELSE 1 END,\n`;
sql += ` COALESCE(lm.created_at, t.last_activity_at, b.created_at) DESC\n`;
const res = await BunSQLite.sql<BotWithLastMessage>({
sql,
values: [scope, scope, user_id, ...(scope ? [scope] : [])],
});
return res.payload || [];
}