Share one crew across the install, keep user memory per user

Bots, workspaces and computers now belong to the install. Users reach
workers only through bot_users (assigned by a super admin or admin) and
workspace_users; the coordinator and main computer are one per install,
and only the super admin sees the coordinator. Conversations are per
user (bot_sessions.user_id), jobs run for the session's user, and live
updates go only to who should see them.

A bot's core memory is now general knowledge shared by every user it
works for; what it learns about a user lives in bot_user_memories.

One-off scripts move existing installs over: move-bot-memory-to-owners
(run first), then share-crew.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-19 11:37:14 +01:00
co-authored by Claude Opus 5.5
parent 4dd34ed251
commit e45d54d30b
133 changed files with 1854 additions and 436 deletions
+17 -6
View File
@@ -210,7 +210,7 @@ async function insertBot({
values: [bot.face, bot.status_line, coordinator_id],
});
await insertMessages({ bot_id: coordinator_id, bot, now, last_ago });
await insertMessages({ user_id, bot_id: coordinator_id, bot, now, last_ago });
console.log(` ${bot.name} (#${coordinator_id}, existing)`);
return;
@@ -237,7 +237,16 @@ async function insertBot({
throw new Error(`Failed to create bot ${bot.name}: ${bot_res.msg}`);
}
const session_id = await insertMessages({ bot_id, bot, now, last_ago });
/**
* Bots belong to the install: the user reaches a sample worker once
* it's assigned to them.
*/
await BunSQLite.sql({
sql: `INSERT OR IGNORE INTO bot_users (created_at, updated_at, bot_id, user_id) VALUES (?, ?, ?, ?)`,
values: [now, now, bot_id, user_id],
});
const session_id = await insertMessages({ user_id, bot_id, bot, now, last_ago });
if (bot.task) {
await insertTask({ bot_id, session_id, task: bot.task });
@@ -247,21 +256,23 @@ async function insertBot({
}
/**
* Adds the bot's sample messages to its own chat's active session and
* returns the session's id.
* Adds the bot's sample messages to the user's own chat with it (its
* active session) and returns the session's id.
*/
async function insertMessages({
user_id,
bot_id,
bot,
now,
last_ago,
}: {
user_id: number;
bot_id: number;
bot: SeedBot;
now: number;
last_ago: number;
}) {
const session = await ensureBotSession({ bot_id });
const session = await ensureBotSession({ user_id, bot_id });
const session_id = Number(session.id);
await BunSQLite.sql({
@@ -363,7 +374,7 @@ async function seedBots() {
if (existing_workers?.length) {
console.log(
`User #${user.id} <${EMAIL}> already has ${existing_workers.length} worker bots. Skipping.`,
`User #${user.id} <${EMAIL}> already created ${existing_workers.length} worker bots. Skipping.`,
);
return;
}