Tell every bot its own id, in every run

The bot's id never reached its prompt: `composeBotPrompt` only took its
instructions, the appendix, its memory and its workspace, and
`grabBotPrompt` never passed `bot.id` or `bot.name` on. A bot could only
learn its id by accident, from a tool description that named it, so
"which bot am I" was unanswerable in most contexts.

- Open the prompt with who the bot is (`composeIdentity`): its name,
  whether it's the coordinator or a worker, its role, and its id, said
  to be how tools and refs point back at it. `grabBotPrompt` now passes
  the bot, which covers chat replies, tasks, routines and delegated
  tasks, since both runners build their prompt there.
- Turn the notes' example refs into functions bound to the running bot:
  `chatReplyNote`, `botFilesNote` and `coordinatorReplyNote` take its id,
  so the folder-ref example no longer points at a hardcoded `owner_id: 5`
  (another bot's folder) nor `[bot:3]`. ComputerFilesNote keeps the
  computer it's given.
- Stop `create_task` from hiding the worker's own id in its own chat.

Tests: four new `composeBotPrompt` cases pin the identity block (worker,
coordinator, unnamed, no bot at all), and a new `harness-prompts.test.ts`
checks each note's own-folder and self refs point at exactly the running
bot, for three ids.
This commit is contained in:
2026-10-23 17:40:28 +01:00
parent 0166f31cfc
commit 4261535741
8 changed files with 187 additions and 23 deletions
+50 -2
View File
@@ -9,7 +9,27 @@ type WorkspaceBotPrompt = {
memory?: string | null;
};
type BotPrompt = {
/**
* The bot's id: how it names itself, and how the user, the crew tools
* and every ref in a message point at it.
*/
id?: number | string | null;
name?: string | null;
kind?: string | null;
/**
* The one-line role it does that job as, when it has one.
*/
role?: string | null;
};
type Params = {
/**
* The bot itself. Its id is always in the prompt: it is how the bot
* names itself and how everything else (crew tools, refs, tasks,
* files) points back at it, in chat replies, tasks and routines alike.
*/
bot?: BotPrompt | null;
system_prompt?: string | null;
/**
* The install-wide text every bot gets, right after its own core
@@ -32,14 +52,16 @@ type Params = {
};
/**
* The full prompt a bot runs with (markdown): its own core instructions,
* then the install-wide appendix, then, in a workspace, the workspace's
* The full prompt a bot runs with (markdown): who it is (its name and id,
* so it always knows which bot it is), its own core instructions, then the
* install-wide appendix, then, in a workspace, the workspace's
* context and the bot's instructions there, then its core memory, its
* memory about the user and its memory in the workspace (said to be the
* bot's own notes, not the workspace's context). Empty parts are left out;
* the workspace heading always shows, so the bot knows where it is.
*/
export default function composeBotPrompt({
bot,
system_prompt,
appendix,
memory,
@@ -49,6 +71,11 @@ export default function composeBotPrompt({
}: Params) {
const parts: string[] = [];
const name = workspace?.name?.trim() || "Untitled";
const identity = composeIdentity({ bot });
if (identity) {
parts.push(identity);
}
if (system_prompt?.trim()) {
parts.push(system_prompt.trim());
@@ -95,3 +122,24 @@ export default function composeBotPrompt({
return parts.join("\n\n");
}
/**
* Who the bot is, said once at the top of its prompt: its name, kind and
* id, and that the id is how it is named. Empty when the caller didn't say
* which bot it is.
*/
function composeIdentity({ bot }: { bot?: BotPrompt | null }) {
const id = bot?.id === null || bot?.id === undefined ? "" : String(bot.id).trim();
if (!id) return "";
const name = bot?.name?.trim() || "A bot";
const kind = bot?.kind == "coordinator" ? "the coordinator" : "a worker bot";
const role = bot?.role?.trim();
return [
`# You are ${name}`,
`You are ${kind} on this crew.${role ? ` Your role: ${role}.` : ""}`,
`Your bot id is ${id}. That id is you: when a tool takes a bot id for the bot it is about (your own tasks, your own picture), this is it, and when you name yourself in a message write the ref \`[bot:${id}]\`, never another bot's.`,
].join("\n\n");
}