refactor: rebuild agent system prompt
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
- CLI: show colored table output for `clawdbot cron list` (JSON behind `--json`).
|
||||
- CLI: add cron `create`/`remove`/`delete` aliases for job management.
|
||||
- Agent: avoid duplicating context/skills when SDK rebuilds the system prompt. (#418)
|
||||
- Agent: replace SDK base system prompt with ClaudeBot prompt, add skills guidance, and document the layout.
|
||||
- Signal: reconnect SSE monitor with abortable backoff; log stream errors. Thanks @nexty5870 for PR #430.
|
||||
- Gateway: pass resolved provider as messageProvider for agent runs so provider-specific tools are available. Thanks @imfing for PR #389.
|
||||
- Doctor: add state integrity checks + repair prompts for missing sessions/state dirs, transcript mismatches, and permission issues; document full doctor flow and workspace backup tips.
|
||||
|
||||
64
docs/concepts/system-prompt.md
Normal file
64
docs/concepts/system-prompt.md
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
summary: "What the ClaudeBot system prompt contains and how it is assembled"
|
||||
read_when:
|
||||
- Editing system prompt text, tools list, or time/heartbeat sections
|
||||
- Changing workspace bootstrap or skills injection behavior
|
||||
---
|
||||
# System Prompt
|
||||
|
||||
ClaudeBot builds a custom system prompt for every agent run. The prompt is **Clawdbot-owned** and does not use the p-coding-agent default prompt.
|
||||
|
||||
The prompt is assembled in `src/agents/system-prompt.ts` and injected by `src/agents/pi-embedded-runner.ts`.
|
||||
|
||||
## Structure
|
||||
|
||||
The prompt is intentionally compact and uses fixed sections:
|
||||
|
||||
- **Tooling**: current tool list + short descriptions.
|
||||
- **Skills**: tells the model how to load skill instructions on demand.
|
||||
- **ClaudeBot Self-Update**: how to run `config.apply` and `update.run`.
|
||||
- **Workspace**: working directory (`agent.workspace`).
|
||||
- **Workspace Files (injected)**: indicates bootstrap files are included below.
|
||||
- **Time**: UTC default + the user’s local time (already converted).
|
||||
- **Reply Tags**: optional reply tag syntax for supported providers.
|
||||
- **Heartbeats**: heartbeat prompt and ack behavior.
|
||||
- **Runtime**: host, OS, node, model, thinking level (one line).
|
||||
|
||||
## Workspace bootstrap injection
|
||||
|
||||
Bootstrap files are trimmed and appended under **Project Context** so the model sees identity and profile context without needing explicit reads:
|
||||
|
||||
- `AGENTS.md`
|
||||
- `SOUL.md`
|
||||
- `TOOLS.md`
|
||||
- `IDENTITY.md`
|
||||
- `USER.md`
|
||||
- `HEARTBEAT.md`
|
||||
- `BOOTSTRAP.md` (only on brand-new workspaces)
|
||||
|
||||
Large files are truncated with a marker. Missing files inject a short missing-file marker.
|
||||
|
||||
## Time handling
|
||||
|
||||
The Time line is compact and explicit:
|
||||
|
||||
- Assume timestamps are **UTC** unless stated.
|
||||
- The listed **user time** is already converted to `agent.userTimezone` (if set).
|
||||
|
||||
Use `agent.userTimezone` in `~/.clawdbot/clawdbot.json` to change the user time zone.
|
||||
|
||||
## Skills
|
||||
|
||||
Skills are **not** auto-injected. Instead, the prompt instructs the model to use `read` to load skill instructions on demand:
|
||||
|
||||
```
|
||||
<workspace>/skills/<name>/SKILL.md
|
||||
```
|
||||
|
||||
This keeps the base prompt small while still enabling targeted skill usage.
|
||||
|
||||
## Code references
|
||||
|
||||
- Prompt text: `src/agents/system-prompt.ts`
|
||||
- Prompt assembly + injection: `src/agents/pi-embedded-runner.ts`
|
||||
- Bootstrap trimming: `src/agents/pi-embedded-helpers.ts`
|
||||
@@ -543,6 +543,7 @@
|
||||
"concepts/architecture",
|
||||
"concepts/agent",
|
||||
"concepts/agent-loop",
|
||||
"concepts/system-prompt",
|
||||
"concepts/agent-workspace",
|
||||
"concepts/multi-agent",
|
||||
"concepts/compaction",
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
|
||||
import {
|
||||
buildSystemPrompt,
|
||||
SessionManager,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import { SessionManager } from "@mariozechner/pi-coding-agent";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
applyGoogleTurnOrderingFix,
|
||||
buildEmbeddedSandboxInfo,
|
||||
createSystemPromptAppender,
|
||||
createSystemPromptOverride,
|
||||
splitSdkTools,
|
||||
} from "./pi-embedded-runner.js";
|
||||
import type { SandboxContext } from "./sandbox.js";
|
||||
@@ -109,27 +106,15 @@ describe("splitSdkTools", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("createSystemPromptAppender", () => {
|
||||
it("appends without duplicating context files", () => {
|
||||
const sentinel = "CONTEXT_SENTINEL_42";
|
||||
const defaultPrompt = buildSystemPrompt({
|
||||
cwd: "/tmp",
|
||||
contextFiles: [{ path: "/tmp/AGENTS.md", content: sentinel }],
|
||||
});
|
||||
const appender = createSystemPromptAppender("APPEND_SECTION");
|
||||
const finalPrompt = appender(defaultPrompt);
|
||||
const occurrences = finalPrompt.split(sentinel).length - 1;
|
||||
const contextHeaders = finalPrompt.split("# Project Context").length - 1;
|
||||
expect(typeof appender).toBe("function");
|
||||
expect(occurrences).toBe(1);
|
||||
expect(contextHeaders).toBe(1);
|
||||
expect(finalPrompt).toContain("APPEND_SECTION");
|
||||
describe("createSystemPromptOverride", () => {
|
||||
it("returns the override prompt regardless of default prompt", () => {
|
||||
const override = createSystemPromptOverride("OVERRIDE");
|
||||
expect(override("DEFAULT")).toBe("OVERRIDE");
|
||||
});
|
||||
|
||||
it("returns the default prompt when append text is empty", () => {
|
||||
const defaultPrompt = buildSystemPrompt({ cwd: "/tmp" });
|
||||
const appender = createSystemPromptAppender(" \n ");
|
||||
expect(appender(defaultPrompt)).toBe(defaultPrompt);
|
||||
it("returns an empty string for blank overrides", () => {
|
||||
const override = createSystemPromptOverride(" \n ");
|
||||
expect(override("DEFAULT")).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
discoverModels,
|
||||
SessionManager,
|
||||
SettingsManager,
|
||||
type Skill,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import { resolveHeartbeatPrompt } from "../auto-reply/heartbeat.js";
|
||||
import type {
|
||||
@@ -54,6 +53,7 @@ import {
|
||||
import { ensureClawdbotModelsJson } from "./models-config.js";
|
||||
import {
|
||||
buildBootstrapContextFiles,
|
||||
type EmbeddedContextFile,
|
||||
ensureSessionHeader,
|
||||
formatAssistantErrorText,
|
||||
isAuthAssistantError,
|
||||
@@ -85,12 +85,10 @@ import { resolveSandboxContext } from "./sandbox.js";
|
||||
import {
|
||||
applySkillEnvOverrides,
|
||||
applySkillEnvOverridesFromSnapshot,
|
||||
buildWorkspaceSkillSnapshot,
|
||||
loadWorkspaceSkillEntries,
|
||||
type SkillEntry,
|
||||
type SkillSnapshot,
|
||||
} from "./skills.js";
|
||||
import { buildAgentSystemPromptAppend } from "./system-prompt.js";
|
||||
import { buildAgentSystemPrompt } from "./system-prompt.js";
|
||||
import { normalizeUsage, type UsageLike } from "./usage.js";
|
||||
import { loadWorkspaceBootstrapFiles } from "./workspace.js";
|
||||
|
||||
@@ -496,7 +494,7 @@ export function buildEmbeddedSandboxInfo(
|
||||
};
|
||||
}
|
||||
|
||||
function buildEmbeddedAppendPrompt(params: {
|
||||
function buildEmbeddedSystemPrompt(params: {
|
||||
workspaceDir: string;
|
||||
defaultThinkLevel?: ThinkLevel;
|
||||
extraSystemPrompt?: string;
|
||||
@@ -515,8 +513,9 @@ function buildEmbeddedAppendPrompt(params: {
|
||||
modelAliasLines: string[];
|
||||
userTimezone: string;
|
||||
userTime?: string;
|
||||
contextFiles?: EmbeddedContextFile[];
|
||||
}): string {
|
||||
return buildAgentSystemPromptAppend({
|
||||
return buildAgentSystemPrompt({
|
||||
workspaceDir: params.workspaceDir,
|
||||
defaultThinkLevel: params.defaultThinkLevel,
|
||||
extraSystemPrompt: params.extraSystemPrompt,
|
||||
@@ -529,17 +528,15 @@ function buildEmbeddedAppendPrompt(params: {
|
||||
modelAliasLines: params.modelAliasLines,
|
||||
userTimezone: params.userTimezone,
|
||||
userTime: params.userTime,
|
||||
contextFiles: params.contextFiles,
|
||||
});
|
||||
}
|
||||
|
||||
export function createSystemPromptAppender(
|
||||
appendPrompt: string,
|
||||
export function createSystemPromptOverride(
|
||||
systemPrompt: string,
|
||||
): (defaultPrompt: string) => string {
|
||||
const trimmed = appendPrompt.trim();
|
||||
if (!trimmed) {
|
||||
return (defaultPrompt) => defaultPrompt;
|
||||
}
|
||||
return (defaultPrompt) => `${defaultPrompt}\n\n${appendPrompt}`;
|
||||
const trimmed = systemPrompt.trim();
|
||||
return () => trimmed;
|
||||
}
|
||||
|
||||
const BUILT_IN_TOOL_NAMES = new Set(["read", "bash", "edit", "write"]);
|
||||
@@ -672,25 +669,6 @@ function resolveModel(
|
||||
return { model, authStorage, modelRegistry };
|
||||
}
|
||||
|
||||
function resolvePromptSkills(
|
||||
snapshot: SkillSnapshot,
|
||||
entries: SkillEntry[],
|
||||
): Skill[] {
|
||||
if (snapshot.resolvedSkills?.length) {
|
||||
return snapshot.resolvedSkills;
|
||||
}
|
||||
|
||||
const snapshotNames = snapshot.skills.map((entry) => entry.name);
|
||||
if (snapshotNames.length === 0) return [];
|
||||
|
||||
const entryByName = new Map(
|
||||
entries.map((entry) => [entry.skill.name, entry.skill]),
|
||||
);
|
||||
return snapshotNames
|
||||
.map((name) => entryByName.get(name))
|
||||
.filter((skill): skill is Skill => Boolean(skill));
|
||||
}
|
||||
|
||||
export async function compactEmbeddedPiSession(params: {
|
||||
sessionId: string;
|
||||
sessionKey?: string;
|
||||
@@ -780,12 +758,6 @@ export async function compactEmbeddedPiSession(params: {
|
||||
const skillEntries = shouldLoadSkillEntries
|
||||
? loadWorkspaceSkillEntries(effectiveWorkspace)
|
||||
: [];
|
||||
const skillsSnapshot =
|
||||
params.skillsSnapshot ??
|
||||
buildWorkspaceSkillSnapshot(effectiveWorkspace, {
|
||||
config: params.config,
|
||||
entries: skillEntries,
|
||||
});
|
||||
restoreSkillEnv = params.skillsSnapshot
|
||||
? applySkillEnvOverridesFromSnapshot({
|
||||
snapshot: params.skillsSnapshot,
|
||||
@@ -799,7 +771,6 @@ export async function compactEmbeddedPiSession(params: {
|
||||
const bootstrapFiles =
|
||||
await loadWorkspaceBootstrapFiles(effectiveWorkspace);
|
||||
const contextFiles = buildBootstrapContextFiles(bootstrapFiles);
|
||||
const promptSkills = resolvePromptSkills(skillsSnapshot, skillEntries);
|
||||
const tools = createClawdbotCodingTools({
|
||||
bash: {
|
||||
...params.config?.agent?.bash,
|
||||
@@ -825,7 +796,7 @@ export async function compactEmbeddedPiSession(params: {
|
||||
params.config?.agent?.userTimezone,
|
||||
);
|
||||
const userTime = formatUserTime(new Date(), userTimezone);
|
||||
const appendPrompt = buildEmbeddedAppendPrompt({
|
||||
const appendPrompt = buildEmbeddedSystemPrompt({
|
||||
workspaceDir: effectiveWorkspace,
|
||||
defaultThinkLevel: params.thinkLevel,
|
||||
extraSystemPrompt: params.extraSystemPrompt,
|
||||
@@ -840,8 +811,9 @@ export async function compactEmbeddedPiSession(params: {
|
||||
modelAliasLines: buildModelAliasLines(params.config),
|
||||
userTimezone,
|
||||
userTime,
|
||||
contextFiles,
|
||||
});
|
||||
const systemPrompt = createSystemPromptAppender(appendPrompt);
|
||||
const systemPrompt = createSystemPromptOverride(appendPrompt);
|
||||
|
||||
// Pre-warm session file to bring it into OS page cache
|
||||
await prewarmSessionFile(params.sessionFile);
|
||||
@@ -878,8 +850,8 @@ export async function compactEmbeddedPiSession(params: {
|
||||
customTools,
|
||||
sessionManager,
|
||||
settingsManager,
|
||||
skills: promptSkills,
|
||||
contextFiles,
|
||||
skills: [],
|
||||
contextFiles: [],
|
||||
additionalExtensionPaths,
|
||||
}));
|
||||
|
||||
@@ -1095,12 +1067,6 @@ export async function runEmbeddedPiAgent(params: {
|
||||
const skillEntries = shouldLoadSkillEntries
|
||||
? loadWorkspaceSkillEntries(effectiveWorkspace)
|
||||
: [];
|
||||
const skillsSnapshot =
|
||||
params.skillsSnapshot ??
|
||||
buildWorkspaceSkillSnapshot(effectiveWorkspace, {
|
||||
config: params.config,
|
||||
entries: skillEntries,
|
||||
});
|
||||
restoreSkillEnv = params.skillsSnapshot
|
||||
? applySkillEnvOverridesFromSnapshot({
|
||||
snapshot: params.skillsSnapshot,
|
||||
@@ -1114,10 +1080,6 @@ export async function runEmbeddedPiAgent(params: {
|
||||
const bootstrapFiles =
|
||||
await loadWorkspaceBootstrapFiles(effectiveWorkspace);
|
||||
const contextFiles = buildBootstrapContextFiles(bootstrapFiles);
|
||||
const promptSkills = resolvePromptSkills(
|
||||
skillsSnapshot,
|
||||
skillEntries,
|
||||
);
|
||||
// Tool schemas must be provider-compatible (OpenAI requires top-level `type: "object"`).
|
||||
// `createClawdbotCodingTools()` normalizes schemas so the session can pass them through unchanged.
|
||||
const tools = createClawdbotCodingTools({
|
||||
@@ -1145,7 +1107,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
params.config?.agent?.userTimezone,
|
||||
);
|
||||
const userTime = formatUserTime(new Date(), userTimezone);
|
||||
const appendPrompt = buildEmbeddedAppendPrompt({
|
||||
const appendPrompt = buildEmbeddedSystemPrompt({
|
||||
workspaceDir: effectiveWorkspace,
|
||||
defaultThinkLevel: thinkLevel,
|
||||
extraSystemPrompt: params.extraSystemPrompt,
|
||||
@@ -1160,8 +1122,9 @@ export async function runEmbeddedPiAgent(params: {
|
||||
modelAliasLines: buildModelAliasLines(params.config),
|
||||
userTimezone,
|
||||
userTime,
|
||||
contextFiles,
|
||||
});
|
||||
const systemPrompt = createSystemPromptAppender(appendPrompt);
|
||||
const systemPrompt = createSystemPromptOverride(appendPrompt);
|
||||
|
||||
// Pre-warm session file to bring it into OS page cache
|
||||
await prewarmSessionFile(params.sessionFile);
|
||||
@@ -1202,8 +1165,8 @@ export async function runEmbeddedPiAgent(params: {
|
||||
customTools,
|
||||
sessionManager,
|
||||
settingsManager,
|
||||
skills: promptSkills,
|
||||
contextFiles,
|
||||
skills: [],
|
||||
contextFiles: [],
|
||||
additionalExtensionPaths,
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildAgentSystemPromptAppend } from "./system-prompt.js";
|
||||
import { buildAgentSystemPrompt } from "./system-prompt.js";
|
||||
|
||||
describe("buildAgentSystemPromptAppend", () => {
|
||||
describe("buildAgentSystemPrompt", () => {
|
||||
it("includes owner numbers when provided", () => {
|
||||
const prompt = buildAgentSystemPromptAppend({
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
ownerNumbers: ["+123", " +456 ", ""],
|
||||
});
|
||||
@@ -15,7 +15,7 @@ describe("buildAgentSystemPromptAppend", () => {
|
||||
});
|
||||
|
||||
it("omits owner section when numbers are missing", () => {
|
||||
const prompt = buildAgentSystemPromptAppend({
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ describe("buildAgentSystemPromptAppend", () => {
|
||||
});
|
||||
|
||||
it("adds reasoning tag hint when enabled", () => {
|
||||
const prompt = buildAgentSystemPromptAppend({
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
reasoningTagHint: true,
|
||||
});
|
||||
@@ -35,7 +35,7 @@ describe("buildAgentSystemPromptAppend", () => {
|
||||
});
|
||||
|
||||
it("lists available tools when provided", () => {
|
||||
const prompt = buildAgentSystemPromptAppend({
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
toolNames: ["bash", "sessions_list", "sessions_history", "sessions_send"],
|
||||
});
|
||||
@@ -47,19 +47,19 @@ describe("buildAgentSystemPromptAppend", () => {
|
||||
});
|
||||
|
||||
it("includes user time when provided", () => {
|
||||
const prompt = buildAgentSystemPromptAppend({
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
userTimezone: "America/Chicago",
|
||||
userTime: "Monday 2026-01-05 15:26",
|
||||
});
|
||||
|
||||
expect(prompt).toContain("## Time");
|
||||
expect(prompt).toContain("User timezone: America/Chicago");
|
||||
expect(prompt).toContain("Current user time: Monday 2026-01-05 15:26");
|
||||
expect(prompt).toContain(
|
||||
"Time: assume UTC unless stated. User TZ=America/Chicago. Current user time (converted)=Monday 2026-01-05 15:26.",
|
||||
);
|
||||
});
|
||||
|
||||
it("includes model alias guidance when aliases are provided", () => {
|
||||
const prompt = buildAgentSystemPromptAppend({
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
modelAliasLines: [
|
||||
"- Opus: anthropic/claude-opus-4-5",
|
||||
@@ -72,14 +72,41 @@ describe("buildAgentSystemPromptAppend", () => {
|
||||
expect(prompt).toContain("- Opus: anthropic/claude-opus-4-5");
|
||||
});
|
||||
|
||||
it("adds gateway self-update guidance when gateway tool is available", () => {
|
||||
const prompt = buildAgentSystemPromptAppend({
|
||||
it("adds ClaudeBot self-update guidance when gateway tool is available", () => {
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
toolNames: ["gateway", "bash"],
|
||||
});
|
||||
|
||||
expect(prompt).toContain("## Gateway Self-Update");
|
||||
expect(prompt).toContain("## ClaudeBot Self-Update");
|
||||
expect(prompt).toContain("config.apply");
|
||||
expect(prompt).toContain("update.run");
|
||||
});
|
||||
|
||||
it("includes skills guidance with workspace path", () => {
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
});
|
||||
|
||||
expect(prompt).toContain("## Skills");
|
||||
expect(prompt).toContain(
|
||||
"Use `read` to load from /tmp/clawd/skills/<name>/SKILL.md",
|
||||
);
|
||||
});
|
||||
|
||||
it("renders project context files when provided", () => {
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
contextFiles: [
|
||||
{ path: "AGENTS.md", content: "Alpha" },
|
||||
{ path: "IDENTITY.md", content: "Bravo" },
|
||||
],
|
||||
});
|
||||
|
||||
expect(prompt).toContain("# Project Context");
|
||||
expect(prompt).toContain("## AGENTS.md");
|
||||
expect(prompt).toContain("Alpha");
|
||||
expect(prompt).toContain("## IDENTITY.md");
|
||||
expect(prompt).toContain("Bravo");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user