refactor: rebuild agent system prompt

This commit is contained in:
Peter Steinberger
2026-01-08 02:20:18 +01:00
parent e9346e6cf0
commit cad853b547
7 changed files with 184 additions and 131 deletions

View File

@@ -1,6 +1,7 @@
import type { ThinkLevel } from "../auto-reply/thinking.js";
import type { EmbeddedContextFile } from "./pi-embedded-helpers.js";
export function buildAgentSystemPromptAppend(params: {
export function buildAgentSystemPrompt(params: {
workspaceDir: string;
defaultThinkLevel?: ThinkLevel;
extraSystemPrompt?: string;
@@ -10,6 +11,7 @@ export function buildAgentSystemPromptAppend(params: {
modelAliasLines?: string[];
userTimezone?: string;
userTime?: string;
contextFiles?: EmbeddedContextFile[];
heartbeatPrompt?: string;
runtimeInfo?: {
host?: string;
@@ -37,15 +39,16 @@ export function buildAgentSystemPromptAppend(params: {
bash: "Run shell commands",
process: "Manage background bash sessions",
whatsapp_login: "Generate and wait for WhatsApp QR login",
browser: "Control the dedicated clawd browser",
browser: "Control web browser",
canvas: "Present/eval/snapshot the Canvas",
nodes: "List/describe/notify/camera/screen on paired nodes",
cron: "Manage cron jobs and wake events",
gateway:
"Restart, apply config, or run updates on the running Gateway process",
sessions_list: "List sessions with filters and last messages",
sessions_history: "Fetch message history for a session",
sessions_send: "Send a message into another session",
"Restart, apply config, or run updates on the running ClaudeBot process",
sessions_list: "List other sessions (incl. sub-agents) with filters/last",
sessions_history: "Fetch history for another session/sub-agent",
sessions_send: "Send a message to another session/sub-agent",
sessions_spawn: "Spawn a sub-agent session",
image: "Analyze an image with the configured image model",
discord: "Send Discord reactions/messages and manage threads",
slack: "Send Slack messages and manage channels",
@@ -95,11 +98,6 @@ export function buildAgentSystemPromptAppend(params: {
}
const hasGateway = availableTools.has("gateway");
const thinkHint =
params.defaultThinkLevel && params.defaultThinkLevel !== "off"
? `Default thinking level: ${params.defaultThinkLevel}.`
: "Default thinking level: off.";
const extraSystemPrompt = params.extraSystemPrompt?.trim();
const ownerNumbers = (params.ownerNumbers ?? [])
.map((value) => value.trim())
@@ -127,19 +125,9 @@ export function buildAgentSystemPromptAppend(params: {
? `Heartbeat prompt: ${heartbeatPrompt}`
: "Heartbeat prompt: (configured)";
const runtimeInfo = params.runtimeInfo;
const runtimeLines: string[] = [];
if (runtimeInfo?.host) runtimeLines.push(`Host: ${runtimeInfo.host}`);
if (runtimeInfo?.os) {
const archSuffix = runtimeInfo.arch ? ` (${runtimeInfo.arch})` : "";
runtimeLines.push(`OS: ${runtimeInfo.os}${archSuffix}`);
} else if (runtimeInfo?.arch) {
runtimeLines.push(`Arch: ${runtimeInfo.arch}`);
}
if (runtimeInfo?.node) runtimeLines.push(`Node: ${runtimeInfo.node}`);
if (runtimeInfo?.model) runtimeLines.push(`Model: ${runtimeInfo.model}`);
const lines = [
"You are Clawd, a personal assistant running inside Clawdbot.",
"You are a personal assistant running inside ClaudeBot.",
"",
"## Tooling",
"Tool availability (filtered by policy):",
@@ -162,13 +150,17 @@ export function buildAgentSystemPromptAppend(params: {
"- sessions_send: send to another session",
].join("\n"),
"TOOLS.md does not control tool availability; it is user guidance for how to use external tools.",
"If a task is more complex or takes longer, spawn a sub-agent. It will do the work for you and ping you when it's done. You can always check up on it.",
"",
hasGateway ? "## Gateway Self-Update" : "",
"## Skills",
`Skills provide task-specific instructions. Use \`read\` to load from ${params.workspaceDir}/skills/<name>/SKILL.md when needed.`,
"",
hasGateway ? "## ClaudeBot Self-Update" : "",
hasGateway
? [
"Use the gateway tool to update or reconfigure this instance when asked.",
"Use the ClaudeBot self-update tool to update or reconfigure this instance when asked.",
"Actions: config.get, config.schema, config.apply (validate + write full config, then restart), update.run (update deps or git, then restart).",
"After restart, Clawdbot pings the last active session automatically.",
"After restart, ClaudeBot pings the last active session automatically.",
].join("\n")
: "",
hasGateway ? "" : "",
@@ -217,15 +209,11 @@ export function buildAgentSystemPromptAppend(params: {
ownerLine ?? "",
ownerLine ? "" : "",
"## Workspace Files (injected)",
"These user-editable files are loaded by Clawdbot and included below in Project Context.",
"These user-editable files are loaded by ClaudeBot and included below in Project Context.",
"",
"## Messaging Safety",
"Never send streaming/partial replies to external messaging surfaces; only final replies should be delivered there.",
"Clawdbot handles message transport automatically; respond normally and your reply will be delivered to the current chat.",
"",
userTimezone || userTime ? "## Time" : "",
userTimezone ? `User timezone: ${userTimezone}` : "",
userTime ? `Current user time: ${userTime}` : "",
userTimezone || userTime
? `Time: assume UTC unless stated. User TZ=${userTimezone ?? "unknown"}. Current user time (converted)=${userTime ?? "unknown"}.`
: "",
userTimezone || userTime ? "" : "",
"## Reply Tags",
"To request a native reply/quote on supported surfaces, include one tag in your reply:",
@@ -242,17 +230,41 @@ export function buildAgentSystemPromptAppend(params: {
lines.push("## Reasoning Format", reasoningHint, "");
}
const contextFiles = params.contextFiles ?? [];
if (contextFiles.length > 0) {
lines.push(
"# Project Context",
"",
"The following project context files have been loaded:",
"",
);
for (const file of contextFiles) {
lines.push(`## ${file.path}`, "", file.content, "");
}
}
lines.push(
"## Heartbeats",
heartbeatPromptLine,
"If you receive a heartbeat poll (a user message matching the heartbeat prompt above), and there is nothing that needs attention, reply exactly:",
"HEARTBEAT_OK",
'Clawdbot treats a leading/trailing "HEARTBEAT_OK" as a heartbeat ack (and may discard it).',
'ClaudeBot treats a leading/trailing "HEARTBEAT_OK" as a heartbeat ack (and may discard it).',
'If something needs attention, do NOT include "HEARTBEAT_OK"; reply with the alert text instead.',
"",
"## Runtime",
...runtimeLines,
thinkHint,
`Runtime: ${[
runtimeInfo?.host ? `host=${runtimeInfo.host}` : "",
runtimeInfo?.os
? `os=${runtimeInfo.os}${runtimeInfo?.arch ? ` (${runtimeInfo.arch})` : ""}`
: runtimeInfo?.arch
? `arch=${runtimeInfo.arch}`
: "",
runtimeInfo?.node ? `node=${runtimeInfo.node}` : "",
runtimeInfo?.model ? `model=${runtimeInfo.model}` : "",
`thinking=${params.defaultThinkLevel ?? "off"}`,
]
.filter(Boolean)
.join(" | ")}`,
);
return lines.filter(Boolean).join("\n");