Files
cobble/src/functions/backend/db/bots/insert-bot-message.ts
T
tbenandClaude Opus 5.5 cc109bc361 Carry conversations into new sessions with a visible summary
- Bots get continue_in_new_session: the bot writes a title and summary,
  the session closes for good (read-only) and the new one opens with the
  summary as a "Carried over from the last session" message that links
  back to the old session.
- Continue in new session (conversation menu, sessions modal) and the
  automatic rollover at the session length do the same; New session now
  starts a fresh, unlinked session.
- Session length is an app setting in Settings > General.
- ?session_id= opens a session; a carried-over one shows a bar linking to
  where the conversation went on instead of the composer.
- The thread can open the previous session above the current one (shown
  only, never in the prompt), and replies get search_past_sessions and
  read_past_session.
- Sessions are named from their opening messages, not their latest, and
  a carried summary is labelled as the earlier session's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 05:35:52 +01:00

111 lines
3.0 KiB
TypeScript

import type {
BUN_SQLITE_COBBLE_BOT_MESSAGES,
BunSQLiteTables,
} from "@/db/types/db";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { HarnessToolUse } from "@/src/harness/types";
type Params = {
bot_id: number;
/**
* The session it goes in, and that session's workspace.
*/
session_id: number;
workspace_id?: number | null;
text: string;
needs_you?: boolean;
/**
* Sent mid-reply (`send_message`): the reply is still being worked on.
*/
interim?: boolean;
/**
* Files the bot saved and sent with it (`media` ids): a PDF it made, a
* generated image, a document it wrote.
*/
media_ids?: number[];
/**
* Every tool call the run behind it made, told to the bot with this
* message in later turns (`botMessagesToHarness`).
*/
tool_log?: HarnessToolUse[];
/**
* Opens a session the conversation moved into
* (`carryOverBotSession`): the session it carries on from.
*/
carried_from_session_id?: number;
};
/**
* Appends a bot message to a session, marks the session unread and bumps
* its activity. Callers must check the bot and session first.
*/
export default async function insertBotMessage({
bot_id,
session_id,
workspace_id,
text,
needs_you,
interim,
media_ids = [],
tool_log,
carried_from_session_id,
}: Params): Promise<BUN_SQLITE_COBBLE_BOT_MESSAGES> {
const now = Date.now();
const needs_you_flag = needs_you ? 1 : 0;
const interim_flag = interim ? 1 : 0;
const tool_log_json = tool_log ? JSON.stringify(tool_log) : undefined;
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: "bot",
text,
needs_you: needs_you_flag,
interim: interim_flag,
tool_log: tool_log_json,
carried_from_session_id,
},
],
});
const id = res.postInsertReturn?.insertId;
if (!res.success || !id) {
throw new Error(`Failed to save message: ${res.msg}`);
}
for (const media_id of media_ids) {
await BunSQLite.sql({
sql: `INSERT INTO bot_message_media (created_at, updated_at, message_id, media_id) VALUES (?, ?, ?, ?)`,
values: [now, now, id, media_id],
}).catch(() => {});
}
await BunSQLite.sql({
sql: `UPDATE bot_sessions SET last_activity_at = ?, unread = 1 WHERE id = ?`,
values: [now, session_id],
});
return {
id,
bot_id,
session_id,
workspace_id: workspace_id || undefined,
author: "bot",
text,
needs_you: needs_you_flag,
interim: interim_flag,
tool_log: tool_log_json,
carried_from_session_id,
created_at: now,
updated_at: now,
};
}