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]>
This commit is contained in:
2026-10-25 11:32:56 +01:00
co-authored by Claude Opus 5.5
parent 93896646c7
commit d7f88d8dd3
22 changed files with 293 additions and 19 deletions
@@ -11,6 +11,7 @@ import jobSessionId from "./job-session-id";
import grabJobUserId from "./grab-job-user-id";
import grabRunningBotJobFace from "./grab-running-bot-job-face";
import withMessageUsage from "../usage/with-message-usage";
import type { HarnessToolUse } from "@/src/harness/types";
type Params = {
job_id: number | string;
@@ -32,6 +33,10 @@ type Params = {
* Files the bot saved and asked to send with it (`media` ids).
*/
attachments?: number[];
/**
* Every tool call the run made, kept on the message.
*/
tool_log?: HarnessToolUse[];
};
/**
@@ -50,6 +55,7 @@ export default async function completeBotJob({
needs_you,
silent,
attachments,
tool_log,
}: Params) {
const job = await grabLockedBotJob({ job_id, worker });
@@ -89,6 +95,7 @@ export default async function completeBotJob({
text: result,
needs_you,
media_ids: attachments,
tool_log,
});
if (job.id && inserted?.id) {
@@ -3,6 +3,7 @@ import type {
BunSQLiteTables,
} from "@/db/types/db";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { HarnessToolUse } from "@/src/harness/types";
type Params = {
bot_id: number;
@@ -22,6 +23,11 @@ type Params = {
* 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[];
};
/**
@@ -36,10 +42,12 @@ export default async function insertBotMessage({
needs_you,
interim,
media_ids = [],
tool_log,
}: 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,
@@ -55,6 +63,7 @@ export default async function insertBotMessage({
text,
needs_you: needs_you_flag,
interim: interim_flag,
tool_log: tool_log_json,
},
],
});
@@ -86,6 +95,7 @@ export default async function insertBotMessage({
text,
needs_you: needs_you_flag,
interim: interim_flag,
tool_log: tool_log_json,
created_at: now,
updated_at: now,
};