Files
cobble/src/bots-queue/bot-messages-to-harness.test.ts
T
tbenandClaude Opus 5.5 d7f88d8dd3 Keep a record of each run's tool calls so bots can't claim work they never did
The wrap-up turn now gets the run's actual tool calls, each bot message
keeps them as bot_messages.tool_log, and history follows recorded bot
messages with an [App record] line. The coordinator also gets update_bot
in workspaces, so it can fill a bot's core prompt from there.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 11:32:56 +01:00

201 lines
6.9 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import botMessagesToHarness from "./bot-messages-to-harness";
const message = (author: "user" | "bot", text: string) => ({ author, text });
describe("botMessagesToHarness", () => {
test("maps authors to roles", () => {
expect(
botMessagesToHarness({
messages: [message("user", "hi"), message("bot", "hello")],
}),
).toEqual([
{ role: "user", content: "hi" },
{ role: "assistant", content: "hello" },
]);
});
test("joins back-to-back messages from one side", () => {
expect(
botMessagesToHarness({
messages: [message("user", "one"), message("user", "two")],
}),
).toEqual([{ role: "user", content: "one\n\ntwo" }]);
});
test("starts with the user and skips empty messages", () => {
expect(
botMessagesToHarness({
messages: [
message("bot", "welcome"),
message("user", " "),
message("user", "hi"),
],
}),
).toEqual([{ role: "user", content: "hi" }]);
});
});
describe("botMessagesToHarness with replies", () => {
test("quotes what a user message answers", () => {
expect(
botMessagesToHarness({
messages: [
{ author: "user", text: "hi" },
{ author: "bot", text: "The report is ready." },
{
author: "user",
text: "Great, send it over.",
reply_to: { id: 4, author: "bot", text: "The report is ready." },
},
],
}),
).toEqual([
{ role: "user", content: "hi" },
{ role: "assistant", content: "The report is ready." },
{
role: "user",
content: `[Replying to your earlier message: "The report is ready."]\n\nGreat, send it over.`,
},
]);
});
test("says a quoted file-only message was a file", () => {
expect(
botMessagesToHarness({
messages: [
{
author: "user",
text: "What was that?",
reply_to: { id: 5, author: "user", text: "" },
},
],
}),
).toEqual([
{
role: "user",
content: `[Replying to the user's own earlier message: "(a file)"]\n\nWhat was that?`,
},
]);
});
});
describe("botMessagesToHarness with a file-only reply", () => {
test("keeps the quote when the reply has no text", () => {
expect(
botMessagesToHarness({
messages: [
{
id: 7,
author: "user",
text: "",
reply_to: { id: 4, author: "bot", text: "Send me the logo." },
attachments: [{ id: 9, name: "logo.png", media_type: "image", url: "/media/9" }],
},
],
}),
).toEqual([
{
role: "user",
content: `[Replying to your earlier message: "Send me the logo."]\n\n[Attached earlier: "logo.png", media id 9]`,
},
]);
});
});
describe("botMessagesToHarness with files", () => {
const attachments = [{ id: 9, name: "shot.png", media_type: "image" as const, url: "/media/9" }];
test("puts a message's loaded files after its text", () => {
const parts = new Map([[1, [{ type: "image" as const, url: "data:image/jpeg;base64,AA", name: "shot.png" }]]]);
expect(
botMessagesToHarness({
messages: [{ id: 1, author: "user", text: "What's this?", attachments }],
parts,
}),
).toEqual([
{
role: "user",
content: [
{ type: "text", text: "What's this?" },
{ type: "image", url: "data:image/jpeg;base64,AA", name: "shot.png" },
],
},
]);
});
test("names files it didn't load, and keeps a file-only message", () => {
expect(
botMessagesToHarness({
messages: [{ id: 2, author: "user", text: "", attachments }],
}),
).toEqual([
{
role: "user",
/** Its media id rides along, so a picture can be set from it. */
content: `[Attached earlier: "shot.png", media id 9]`,
},
]);
});
});
describe("botMessagesToHarness with mentions", () => {
test("names the bots and workspaces a user message mentions", () => {
expect(
botMessagesToHarness({
messages: [message("user", "ask [bot:3]"), message("bot", "[bot:3] is on it")],
refs: { bots: new Map([[3, { name: "Ada" }]]), workspaces: new Map() },
}),
).toEqual([
{ role: "user", content: "ask [bot:3]\n\n[Mentions: [bot:3] is Ada]" },
{ role: "assistant", content: "[bot:3] is on it" },
]);
});
test("follows a recorded bot message with the app's record of its tools", () => {
expect(
botMessagesToHarness({
messages: [
message("user", "fill in its prompt"),
{
...message("bot", "Done."),
tool_log: JSON.stringify([
{ name: "fetch_url", ok: true },
{ name: "fetch_url", ok: false },
]),
},
message("user", "and the other one?"),
{ ...message("bot", "Saved too."), tool_log: "[]" },
],
}),
).toEqual([
{ role: "user", content: "fill in its prompt" },
{
role: "assistant",
content: "Done.\n\n[App record: this message's run called fetch_url x2 (1 failed).]",
},
{ role: "user", content: "and the other one?" },
{
role: "assistant",
content: "Saved too.\n\n[App record: this message's run called no tools.]",
},
]);
});
test("adds no record for messages whose run wasn't recorded", () => {
expect(
botMessagesToHarness({
messages: [
message("user", "hi"),
{ ...message("bot", "hello"), tool_log: undefined },
{ ...message("bot", "again"), tool_log: "not json" },
],
}),
).toEqual([
{ role: "user", content: "hi" },
{ role: "assistant", content: "hello\n\nagain" },
]);
});
});