Files
cobble/src/functions/backend/db/bots/insert-user-message.ts
T
tbenandClaude Opus 5.5 7b762fd384 Let workers ask each other through the coordinator in chatrooms
A worker asks for another bot's help with ask_crew. The coordinator
reads the request, asks the right bot (in a workspace, only its
members), and sends the answer back into the asking bot's
conversation, waking it. Each exchange is kept as a read-only
chatroom: listed in the main rail after the workspaces, or in the
workspace's rail for requests made there, with live status rows for
each bot while it's open and a Stop button.

Room sessions are hidden from every conversation list, other bots'
words reach the model fenced as data, and turns, nesting and open
rooms are capped. Closed rooms are pruned after a retention period
and past a cap per place (Settings > General).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 18:57:34 +01:00

111 lines
3.1 KiB
TypeScript

import type {
BUN_SQLITE_COBBLE_BOT_MESSAGES,
BUN_SQLITE_COBBLE_MEDIA,
BunSQLiteTables,
} from "@/db/types/db";
import type { BotMessageWithAttachments } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import mediaToAttachment from "./media-to-attachment";
type Params = {
bot_id: number;
/**
* The session it goes in, and that session's workspace.
*/
session_id: number;
workspace_id?: number | null;
text: string;
/**
* The message this one answers, quoted above it. Already checked to
* be in the same conversation.
*/
reply_to?: BUN_SQLITE_COBBLE_BOT_MESSAGES | null;
/**
* Files to attach, already checked to be the user's own
* (`grabAttachableMedia`).
*/
media?: BUN_SQLITE_COBBLE_MEDIA[];
/**
* Brings a chatroom's outcome back to the bot that asked: stored on the
* user's side (the bot answers it), shown as from the coordinator.
*/
from_chatroom_id?: number;
};
/**
* Appends a user message (and its files) to a session and bumps its
* activity. The session is named later, from its first exchange with the
* bot (`nameBotSession`). Callers must check the bot and session belong to
* the session user first.
*/
export default async function insertUserMessage({
bot_id,
session_id,
workspace_id,
text,
reply_to,
media = [],
from_chatroom_id,
}: Params): Promise<BotMessageWithAttachments> {
const now = Date.now();
const res = await BunSQLite.insert<
BUN_SQLITE_COBBLE_BOT_MESSAGES,
(typeof BunSQLiteTables)[number]
>({
table: "bot_messages",
data: [
{
bot_id,
session_id,
workspace_id: workspace_id || undefined,
author: "user",
text,
needs_you: 0,
reply_to_message_id: reply_to?.id || undefined,
from_chatroom_id,
},
],
});
const id = res.postInsertReturn?.insertId;
if (!res.success || !id) {
throw new Error(`Failed to save message: ${res.msg}`);
}
for (const file of media) {
await BunSQLite.sql({
sql: `INSERT INTO bot_message_media (created_at, updated_at, message_id, media_id) VALUES (?, ?, ?, ?)`,
values: [now, now, id, file.id!],
});
}
await BunSQLite.sql({
sql: `UPDATE bot_sessions SET last_activity_at = ? WHERE id = ?`,
values: [now, session_id],
});
return {
id,
bot_id,
session_id,
workspace_id: workspace_id || undefined,
author: "user",
text,
needs_you: 0,
reply_to_message_id: reply_to?.id || undefined,
from_chatroom_id,
created_at: now,
updated_at: now,
attachments: media.map((file) => mediaToAttachment({ media: file })),
reply_to: reply_to
? {
id: Number(reply_to.id),
author: reply_to.author == "bot" ? "bot" : "user",
text: reply_to.text || "",
}
: null,
};
}