From 412990a13972b80589ae2fc3b6d4ae47aeb3f3c4 Mon Sep 17 00:00:00 2001 From: Tobias Bischoff <> Date: Wed, 7 Jan 2026 08:14:47 +0100 Subject: [PATCH] Reduce prompt token overhead with leaner context injections --- src/agents/pi-embedded-helpers.ts | 37 ++++++++++++++++++++++----- src/agents/system-prompt.test.ts | 3 +-- src/agents/system-prompt.ts | 4 --- src/auto-reply/reply.triggers.test.ts | 2 +- src/auto-reply/reply/groups.ts | 4 +-- src/infra/provider-summary.ts | 37 ++++++++------------------- 6 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/agents/pi-embedded-helpers.ts b/src/agents/pi-embedded-helpers.ts index ef8049e3a..82c552130 100644 --- a/src/agents/pi-embedded-helpers.ts +++ b/src/agents/pi-embedded-helpers.ts @@ -16,6 +16,25 @@ import type { WorkspaceBootstrapFile } from "./workspace.js"; export type EmbeddedContextFile = { path: string; content: string }; +const MAX_BOOTSTRAP_CHARS = 4000; +const BOOTSTRAP_HEAD_CHARS = 2800; +const BOOTSTRAP_TAIL_CHARS = 800; + +function trimBootstrapContent(content: string, fileName: string): string { + const trimmed = content.trimEnd(); + if (trimmed.length <= MAX_BOOTSTRAP_CHARS) return trimmed; + + const head = trimmed.slice(0, BOOTSTRAP_HEAD_CHARS); + const tail = trimmed.slice(-BOOTSTRAP_TAIL_CHARS); + return [ + head, + "", + `[...truncated, read ${fileName} for full content...]`, + "", + tail, + ].join("\n"); +} + export async function ensureSessionHeader(params: { sessionFile: string; sessionId: string; @@ -88,12 +107,18 @@ export async function sanitizeSessionMessagesImages( export function buildBootstrapContextFiles( files: WorkspaceBootstrapFile[], ): EmbeddedContextFile[] { - return files.map((file) => ({ - path: file.name, - content: file.missing - ? `[MISSING] Expected at: ${file.path}` - : (file.content ?? ""), - })); + const result: EmbeddedContextFile[] = []; + for (const file of files) { + if (file.missing) continue; + const content = file.content ?? ""; + const trimmed = content.trimEnd(); + if (!trimmed) continue; + result.push({ + path: file.name, + content: trimBootstrapContent(trimmed, file.name), + }); + } + return result; } export function formatAssistantErrorText( diff --git a/src/agents/system-prompt.test.ts b/src/agents/system-prompt.test.ts index 942a6f671..65a09e4d5 100644 --- a/src/agents/system-prompt.test.ts +++ b/src/agents/system-prompt.test.ts @@ -34,7 +34,7 @@ describe("buildAgentSystemPromptAppend", () => { expect(prompt).toContain("..."); }); - it("lists available and unavailable tools when provided", () => { + it("lists available tools when provided", () => { const prompt = buildAgentSystemPromptAppend({ workspaceDir: "/tmp/clawd", toolNames: ["bash", "sessions_list", "sessions_history", "sessions_send"], @@ -44,7 +44,6 @@ describe("buildAgentSystemPromptAppend", () => { expect(prompt).toContain("sessions_list"); expect(prompt).toContain("sessions_history"); expect(prompt).toContain("sessions_send"); - expect(prompt).toContain("Unavailable tools (do not call):"); }); it("includes user time when provided", () => { diff --git a/src/agents/system-prompt.ts b/src/agents/system-prompt.ts index 72dfd9868..5dd713657 100644 --- a/src/agents/system-prompt.ts +++ b/src/agents/system-prompt.ts @@ -85,7 +85,6 @@ export function buildAgentSystemPromptAppend(params: { new Set(normalizedTools.filter((tool) => !toolOrder.includes(tool))), ); const enabledTools = toolOrder.filter((tool) => availableTools.has(tool)); - const disabledTools = toolOrder.filter((tool) => !availableTools.has(tool)); const toolLines = enabledTools.map((tool) => { const summary = toolSummaries[tool]; return summary ? `- ${tool}: ${summary}` : `- ${tool}`; @@ -160,9 +159,6 @@ export function buildAgentSystemPromptAppend(params: { "- sessions_history: fetch session history", "- sessions_send: send to another session", ].join("\n"), - disabledTools.length > 0 - ? `Unavailable tools (do not call): ${disabledTools.join(", ")}` - : "", "TOOLS.md does not control tool availability; it is user guidance for how to use external tools.", "", params.modelAliasLines && params.modelAliasLines.length > 0 diff --git a/src/auto-reply/reply.triggers.test.ts b/src/auto-reply/reply.triggers.test.ts index 6c6ee8b26..c1cdc0647 100644 --- a/src/auto-reply/reply.triggers.test.ts +++ b/src/auto-reply/reply.triggers.test.ts @@ -1006,7 +1006,7 @@ describe("trigger handling", () => { describe("group intro prompts", () => { const groupParticipationNote = - "Be a good group participant: lurk and follow the conversation, but only chime in when you have something genuinely helpful or relevant to add. Don't feel obligated to respond to every message — quality over quantity. Even when lurking silently, you can use emoji reactions to acknowledge messages, show support, or react to humor — reactions are always appreciated and don't clutter the chat."; + "In groups, respond only when helpful; reactions are ok when available."; it("labels Discord groups using the surface metadata", async () => { await withTempHome(async (home) => { vi.mocked(runEmbeddedPiAgent).mockResolvedValue({ diff --git a/src/auto-reply/reply/groups.ts b/src/auto-reply/reply/groups.ts index 9569f1f1e..3d5b60ca1 100644 --- a/src/auto-reply/reply/groups.ts +++ b/src/auto-reply/reply/groups.ts @@ -161,10 +161,10 @@ export function buildGroupIntro(params: { : undefined; const cautionLine = activation === "always" - ? "Be extremely selective: reply only when you are directly addressed, asked a question, or can add clear value. Otherwise stay silent." + ? "Be extremely selective: reply only when directly addressed or clearly helpful. Otherwise stay silent." : undefined; const lurkLine = - "Be a good group participant: lurk and follow the conversation, but only chime in when you have something genuinely helpful or relevant to add. Don't feel obligated to respond to every message — quality over quantity. Even when lurking silently, you can use emoji reactions to acknowledge messages, show support, or react to humor — reactions are always appreciated and don't clutter the chat."; + "In groups, respond only when helpful; reactions are ok when available."; return [ subjectLine, membersLine, diff --git a/src/infra/provider-summary.ts b/src/infra/provider-summary.ts index f7aff300e..02ce6bc09 100644 --- a/src/infra/provider-summary.ts +++ b/src/infra/provider-summary.ts @@ -1,7 +1,5 @@ -import chalk from "chalk"; import { type ClawdbotConfig, loadConfig } from "../config/config.js"; import { resolveTelegramToken } from "../telegram/token.js"; -import { normalizeE164 } from "../utils.js"; import { getWebAuthAgeMs, readWebSelfId, @@ -16,37 +14,33 @@ export async function buildProviderSummary( const webEnabled = effective.web?.enabled !== false; if (!webEnabled) { - lines.push(chalk.cyan("WhatsApp: disabled")); + lines.push("WhatsApp: disabled"); } else { const webLinked = await webAuthExists(); const authAgeMs = getWebAuthAgeMs(); - const authAge = authAgeMs === null ? "unknown" : formatAge(authAgeMs); + const authAge = authAgeMs === null ? "" : ` auth ${formatAge(authAgeMs)}`; const { e164 } = readWebSelfId(); lines.push( webLinked - ? chalk.green( - `WhatsApp: linked${e164 ? ` as ${e164}` : ""} (auth ${authAge})`, - ) - : chalk.red("WhatsApp: not linked"), + ? `WhatsApp: linked${e164 ? ` ${e164}` : ""}${authAge}` + : "WhatsApp: not linked", ); } const telegramEnabled = effective.telegram?.enabled !== false; if (!telegramEnabled) { - lines.push(chalk.cyan("Telegram: disabled")); + lines.push("Telegram: disabled"); } else { const { token: telegramToken } = resolveTelegramToken(effective); const telegramConfigured = Boolean(telegramToken?.trim()); lines.push( - telegramConfigured - ? chalk.green("Telegram: configured") - : chalk.cyan("Telegram: not configured"), + telegramConfigured ? "Telegram: configured" : "Telegram: not configured", ); } const signalEnabled = effective.signal?.enabled !== false; if (!signalEnabled) { - lines.push(chalk.cyan("Signal: disabled")); + lines.push("Signal: disabled"); } else { const signalConfigured = Boolean(effective.signal) && @@ -59,31 +53,20 @@ export async function buildProviderSummary( typeof effective.signal?.autoStart === "boolean", ); lines.push( - signalConfigured - ? chalk.green("Signal: configured") - : chalk.cyan("Signal: not configured"), + signalConfigured ? "Signal: configured" : "Signal: not configured", ); } const imessageEnabled = effective.imessage?.enabled !== false; if (!imessageEnabled) { - lines.push(chalk.cyan("iMessage: disabled")); + lines.push("iMessage: disabled"); } else { const imessageConfigured = Boolean(effective.imessage); lines.push( - imessageConfigured - ? chalk.green("iMessage: configured") - : chalk.cyan("iMessage: not configured"), + imessageConfigured ? "iMessage: configured" : "iMessage: not configured", ); } - const allowFrom = effective.whatsapp?.allowFrom?.length - ? effective.whatsapp.allowFrom.map(normalizeE164).filter(Boolean) - : []; - if (allowFrom.length) { - lines.push(chalk.cyan(`AllowFrom: ${allowFrom.join(", ")}`)); - } - return lines; }