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:
@@ -71,8 +71,8 @@ export default function delegateTaskTool({
|
||||
throw new Error(`No crew member has id ${args.bot_id}. Crew: ${crew}.`);
|
||||
}
|
||||
|
||||
const computers = await grabBotComputers({ bot_id: worker.id, user_id, workspace_id });
|
||||
const worker_session = await ensureBotSession({ bot_id: worker.id, workspace_id });
|
||||
const computers = await grabBotComputers({ bot_id: worker.id, workspace_id });
|
||||
const worker_session = await ensureBotSession({ user_id, bot_id: worker.id, workspace_id });
|
||||
|
||||
const job_id = await enqueueBotJob({
|
||||
user_id,
|
||||
|
||||
@@ -43,8 +43,8 @@ type Params = {
|
||||
* in a workspace switching its default computer), start_task for longer
|
||||
* work, search_computers to find what's on its computers,
|
||||
* ask_user_to_take_over when only the user can do something on one, and
|
||||
* for the coordinator delegate_task (to the workspace's crew in a
|
||||
* workspace) plus, in its own chat, assign_computer (which computers each
|
||||
* for the coordinator (the super admin's alone) delegate_task to every
|
||||
* worker (the workspace's in a workspace) plus, in its own chat, assign_computer (which computers each
|
||||
* crew member can use) or, in a workspace, set_bot_workspace_instructions.
|
||||
* Anything else on a computer goes through a task, never mid-reply.
|
||||
*/
|
||||
@@ -88,9 +88,9 @@ export default async function grabBotChatTools({
|
||||
await BunSQLite.sql<BUN_SQLITE_COBBLE_BOTS>({
|
||||
sql: workspace_id
|
||||
? `SELECT b.* FROM bots b JOIN workspace_bots wb ON wb.bot_id = b.id AND wb.workspace_id = ?
|
||||
WHERE b.user_id = ? AND b.kind = 'worker' ORDER BY b.id ASC`
|
||||
: `SELECT * FROM bots WHERE user_id = ? AND kind = 'worker' ORDER BY id ASC`,
|
||||
values: workspace_id ? [workspace_id, user_id] : [user_id],
|
||||
WHERE b.kind = 'worker' ORDER BY b.id ASC`
|
||||
: `SELECT * FROM bots WHERE kind = 'worker' ORDER BY id ASC`,
|
||||
values: workspace_id ? [workspace_id] : [],
|
||||
})
|
||||
).payload || [];
|
||||
|
||||
@@ -117,7 +117,7 @@ export default async function grabBotChatTools({
|
||||
await Promise.all(
|
||||
workers.map(async (worker) => [
|
||||
worker.id || 0,
|
||||
await grabBotComputers({ bot_id: worker.id || 0, user_id }),
|
||||
await grabBotComputers({ bot_id: worker.id || 0 }),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -34,8 +34,8 @@ export default function updateInstructionsTool({
|
||||
return {
|
||||
name: "update_instructions",
|
||||
description: workspace_id
|
||||
? `Replace your instructions with this full text (markdown). scope "workspace" (the default) is your instructions for this workspace, under "# Your instructions in ..."; "core" is your core instructions, the start of your prompt in every workspace and your own chat. Change them only to work better for this user, such as a new standing rule they gave you. Keep everything that still applies.`
|
||||
: `Replace your core instructions with this full text (markdown): the start of your prompt, before any workspace or memory. Change them only to work better for this user, such as a new standing rule they gave you. Keep everything that still applies.`,
|
||||
? `Replace your instructions with this full text (markdown). scope "workspace" (the default) is your instructions for this workspace, under "# Your instructions in ..."; "core" is your core instructions, the start of your prompt in every workspace and your own chat. Both are shared: the workspace's with everyone working in it, your core ones with every user you work for. Change them only for how you should work for all of them, such as a standing rule for your job; a preference of this one user goes in your memory of them (update_memory, scope "user"). Keep everything that still applies.`
|
||||
: `Replace your core instructions with this full text (markdown): the start of your prompt, before any workspace or memory, shared with every user you work for. Change them only for how you should work for every user you work for, such as a standing rule for your job; a preference of this one user goes in your memory of them (update_memory, scope "user"). Keep everything that still applies.`,
|
||||
schema: workspace_id
|
||||
? z.object({ instructions, scope: z.enum(["workspace", "core"]).default("workspace") })
|
||||
: z.object({ instructions: instructions.min(1) }),
|
||||
|
||||
@@ -1,27 +1,38 @@
|
||||
import { z } from "zod";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
import updateBotMemory from "@/src/functions/backend/db/bots/update-bot-memory";
|
||||
import updateBotUserMemory from "@/src/functions/backend/db/bots/update-bot-user-memory";
|
||||
import updateWorkspaceBot from "@/src/functions/backend/db/workspaces/update-workspace-bot";
|
||||
import type { HarnessTool } from "../../types";
|
||||
|
||||
type Params = {
|
||||
/**
|
||||
* The user the run is for: `user` memory is theirs alone.
|
||||
*/
|
||||
user_id: number | string;
|
||||
bot_id: number | string;
|
||||
/**
|
||||
* The workspace the run is in: then the bot rewrites its memory for
|
||||
* the workspace unless it picks `core`.
|
||||
* the workspace unless it picks another scope.
|
||||
*/
|
||||
workspace_id?: number | string | null;
|
||||
};
|
||||
|
||||
type Args = {
|
||||
memory: string;
|
||||
scope?: "workspace" | "core";
|
||||
scope?: "user" | "workspace" | "core";
|
||||
};
|
||||
|
||||
const UserScopeNote = `what you remember about this user, under "# Memory about the user": their preferences, facts about them and their work, decisions they made. Only runs for this user see it.`;
|
||||
|
||||
const CoreScopeNote = `scope "core" is your core memory, under "# Memory", shared with every user you work for: only general knowledge about your job that holds for anyone, never anything about a particular user.`;
|
||||
|
||||
const ReplaceNote = `Anything you leave out is forgotten, so include the parts that still matter.`;
|
||||
|
||||
/**
|
||||
* Lets a bot rewrite its own memory: its core memory (under `# Memory`),
|
||||
* or in a workspace (by default) its memory for that workspace.
|
||||
* Lets a bot rewrite one of its memories: about the user it works for
|
||||
* (the default outside a workspace), for the workspace (the default in
|
||||
* one), or its core memory, shared by every user.
|
||||
*/
|
||||
export default function updateMemoryTool({
|
||||
user_id,
|
||||
@@ -33,19 +44,24 @@ export default function updateMemoryTool({
|
||||
return {
|
||||
name: "update_memory",
|
||||
description: workspace_id
|
||||
? `Replace a memory with this full text (markdown). scope "workspace" (the default) is your memory for this workspace, under "# Memory in ..."; "core" is your core memory, under "# Memory", in every workspace and your own chat. Keep what you must always remember: the user's preferences, facts about their work, decisions made. Anything you leave out is forgotten, so include the parts that still matter.`
|
||||
: `Replace your memory with this full text (markdown). It's in your prompt under "# Memory" on every run. Keep what you must always remember: the user's preferences, facts about their work, decisions made. Anything you leave out is forgotten, so include the parts that still matter.`,
|
||||
? `Replace a memory with this full text (markdown). scope "workspace" (the default) is your memory for this workspace, under "# Memory in ...": what holds for the work here. scope "user" is ${UserScopeNote} ${CoreScopeNote} ${ReplaceNote}`
|
||||
: `Replace a memory with this full text (markdown). scope "user" (the default) is ${UserScopeNote} ${CoreScopeNote} ${ReplaceNote}`,
|
||||
schema: workspace_id
|
||||
? z.object({ memory, scope: z.enum(["workspace", "core"]).default("workspace") })
|
||||
: z.object({ memory }),
|
||||
? z.object({ memory, scope: z.enum(["workspace", "user", "core"]).default("workspace") })
|
||||
: z.object({ memory, scope: z.enum(["user", "core"]).default("user") }),
|
||||
run: async ({ args }) => {
|
||||
if (workspace_id && args.scope != "core") {
|
||||
if (args.scope == "core") {
|
||||
await updateBotMemory({ user_id, bot_id, memory: args.memory });
|
||||
return `Core memory saved.`;
|
||||
}
|
||||
|
||||
if (workspace_id && args.scope == "workspace") {
|
||||
await updateWorkspaceBot({ user_id, workspace_id, bot_id, memory: args.memory });
|
||||
return `Memory for this workspace saved.`;
|
||||
}
|
||||
|
||||
await updateBotMemory({ user_id, bot_id, memory: args.memory });
|
||||
return `Core memory saved.`;
|
||||
await updateBotUserMemory({ user_id, bot_id, memory: args.memory });
|
||||
return `Memory about the user saved.`;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user