Files
cobble/src/functions/backend/db/bot-sessions/grab-conversation-history.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

108 lines
3.8 KiB
TypeScript

import { AppData } from "@/src/data/app-data";
import type { ConversationHistory } from "@/src/types";
import grabActiveBotTask from "../bots/grab-active-bot-task";
import grabBotMessages from "../bots/grab-bot-messages";
import ensureBotSession from "./ensure-bot-session";
import grabCarriedToSessionId from "./grab-carried-to-session-id";
import reopenBotSession from "./reopen-bot-session";
import grabUserBotSession from "./grab-user-bot-session";
import grabUserConversation from "./grab-user-conversation";
import markBotSessionRead from "./mark-bot-session-read";
type Params = {
user_id: number;
bot_id: number | string;
/**
* The workspace the conversation is in; none for the bot's own chat.
*/
workspace_id?: number | string | null;
/**
* A session of the conversation to show instead of the active one
* (the page's `?session_id=`). One that isn't the user's, or is in
* another conversation, is ignored.
*/
session_id?: number | string | null;
};
/**
* A conversation's history, which the conversation pages load on the
* client once they render (`api/admin/bots/conversation-history`): the
* bot's active session there, or the one asked for (marked read; a closed
* one is reopened, unless it was carried over, which only shows, with
* where the conversation went on), its latest
* `AppData.ConversationFirstMessages` messages (the thread loads older
* ones as it scrolls) and the bot's active task in it. Everything else on
* the page comes with it from the server (`grabConversationProps`).
* Null when the user can't chat with the bot there.
*/
export default async function grabConversationHistory({
user_id,
bot_id,
workspace_id,
session_id,
}: Params): Promise<ConversationHistory | null> {
const conversation = await grabUserConversation({ user_id, bot_id, workspace_id }).catch(
() => null,
);
if (!conversation?.bot?.id) return null;
const open_bot_id = conversation.bot.id;
const conversation_workspace_id = conversation.workspace_id || null;
const asked = session_id
? await grabUserBotSession({ session_id, user_id })
: null;
const in_conversation =
asked?.id &&
!asked.chatroom_id &&
Number(asked.bot_id) == Number(open_bot_id) &&
(asked.workspace_id ? Number(asked.workspace_id) : null) ==
(conversation_workspace_id ? Number(conversation_workspace_id) : null);
/**
* A closed session asked for is reopened, as clicking it in a list
* does, unless it was carried over into a newer one: that one only
* shows, with the way to where the conversation went on.
*/
const carried_to =
in_conversation && asked.status != "active"
? await grabCarriedToSessionId({ session_id: asked.id!, user_id })
: null;
const session = !in_conversation
? await ensureBotSession({
user_id,
bot_id: open_bot_id,
workspace_id: conversation.workspace_id,
})
: asked.status == "active" || carried_to
? asked
: await reopenBotSession({ session_id: asked.id!, user_id });
if (session.unread) {
await markBotSessionRead({ session_id: session.id!, user_id });
session.unread = 0;
}
const [messages, task] = await Promise.all([
grabBotMessages({
session_id: session.id!,
user_id,
limit: AppData.ConversationFirstMessages + 1,
}),
grabActiveBotTask({ bot_id: open_bot_id, session_id: session.id }),
]);
const has_more = messages.length > AppData.ConversationFirstMessages;
return {
session,
messages: has_more ? messages.slice(1) : messages,
has_more,
task,
moved_to_session_id: carried_to,
};
}