refactor: unify system prompt runtime params

This commit is contained in:
Peter Steinberger
2026-01-19 05:27:38 +00:00
parent 374da34936
commit 55d034358d
7 changed files with 153 additions and 49 deletions

View File

@@ -102,6 +102,7 @@ export async function runCliAgent(params: {
tools: [],
contextFiles,
modelDisplay,
agentId: sessionAgentId,
});
const { sessionId: cliSessionIdToSend, isNew } = resolveSessionIdToSend({

View File

@@ -9,8 +9,8 @@ import type { ThinkLevel } from "../../auto-reply/thinking.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { CliBackendConfig } from "../../config/types.js";
import { runExec } from "../../process/exec.js";
import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js";
import type { EmbeddedContextFile } from "../pi-embedded-helpers.js";
import { buildSystemPromptParams } from "../system-prompt-params.js";
import { buildAgentSystemPrompt } from "../system-prompt.js";
const CLI_RUN_QUEUE = new Map<string, Promise<unknown>>();
@@ -172,10 +172,19 @@ export function buildSystemPrompt(params: {
tools: AgentTool[];
contextFiles?: EmbeddedContextFile[];
modelDisplay: string;
agentId?: string;
}) {
const userTimezone = resolveUserTimezone(params.config?.agents?.defaults?.userTimezone);
const userTimeFormat = resolveUserTimeFormat(params.config?.agents?.defaults?.timeFormat);
const userTime = formatUserTime(new Date(), userTimezone, userTimeFormat);
const { runtimeInfo, userTimezone, userTime, userTimeFormat } = buildSystemPromptParams({
config: params.config,
agentId: params.agentId,
runtime: {
host: "clawdbot",
os: `${os.type()} ${os.release()}`,
arch: os.arch(),
node: process.version,
model: params.modelDisplay,
},
});
return buildAgentSystemPrompt({
workspaceDir: params.workspaceDir,
defaultThinkLevel: params.defaultThinkLevel,
@@ -184,13 +193,7 @@ export function buildSystemPrompt(params: {
reasoningTagHint: false,
heartbeatPrompt: params.heartbeatPrompt,
docsPath: params.docsPath,
runtimeInfo: {
host: "clawdbot",
os: `${os.type()} ${os.release()}`,
arch: os.arch(),
node: process.version,
model: params.modelDisplay,
},
runtimeInfo,
toolNames: params.tools.map((tool) => tool.name),
modelAliasLines: buildModelAliasLines(params.config),
userTimezone,

View File

@@ -64,7 +64,7 @@ import { prewarmSessionFile, trackSessionManagerAccess } from "../session-manage
import { prepareSessionManagerForRun } from "../session-manager-init.js";
import { buildEmbeddedSystemPrompt, createSystemPromptOverride } from "../system-prompt.js";
import { splitSdkTools } from "../tool-split.js";
import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../../date-time.js";
import { buildSystemPromptParams } from "../../system-prompt-params.js";
import { describeUnknownError, mapThinkingLevel } from "../utils.js";
import { resolveSandboxRuntimeStatus } from "../../sandbox/runtime-status.js";
import { isTimeoutError } from "../../failover-error.js";
@@ -196,25 +196,25 @@ export async function runEmbeddedAttempt(
return level ? { level, channel: "Telegram" } : undefined;
})()
: undefined;
const runtimeInfo = {
host: machineName,
os: `${os.type()} ${os.release()}`,
arch: os.arch(),
node: process.version,
model: `${params.provider}/${params.modelId}`,
channel: runtimeChannel,
capabilities: runtimeCapabilities,
};
const sandboxInfo = buildEmbeddedSandboxInfo(sandbox, params.bashElevated);
const reasoningTagHint = isReasoningTagProvider(params.provider);
const userTimezone = resolveUserTimezone(params.config?.agents?.defaults?.userTimezone);
const userTimeFormat = resolveUserTimeFormat(params.config?.agents?.defaults?.timeFormat);
const userTime = formatUserTime(new Date(), userTimezone, userTimeFormat);
const { defaultAgentId, sessionAgentId } = resolveSessionAgentIds({
sessionKey: params.sessionKey,
config: params.config,
});
const sandboxInfo = buildEmbeddedSandboxInfo(sandbox, params.bashElevated);
const reasoningTagHint = isReasoningTagProvider(params.provider);
const { runtimeInfo, userTimezone, userTime, userTimeFormat } = buildSystemPromptParams({
config: params.config,
agentId: sessionAgentId,
runtime: {
host: machineName,
os: `${os.type()} ${os.release()}`,
arch: os.arch(),
node: process.version,
model: `${params.provider}/${params.modelId}`,
channel: runtimeChannel,
capabilities: runtimeCapabilities,
},
});
const isDefaultAgent = sessionAgentId === defaultAgentId;
const promptMode = isSubagentSessionKey(params.sessionKey) ? "minimal" : "full";
const docsPath = await resolveClawdbotDocsPath({

View File

@@ -23,6 +23,7 @@ export function buildEmbeddedSystemPrompt(params: {
/** Controls which hardcoded sections to include. Defaults to "full". */
promptMode?: PromptMode;
runtimeInfo: {
agentId?: string;
host: string;
os: string;
arch: string;

View File

@@ -0,0 +1,44 @@
import type { ClawdbotConfig } from "../config/config.js";
import {
formatUserTime,
resolveUserTimeFormat,
resolveUserTimezone,
type ResolvedTimeFormat,
} from "./date-time.js";
export type RuntimeInfoInput = {
agentId?: string;
host: string;
os: string;
arch: string;
node: string;
model: string;
channel?: string;
capabilities?: string[];
};
export type SystemPromptRuntimeParams = {
runtimeInfo: RuntimeInfoInput;
userTimezone: string;
userTime?: string;
userTimeFormat?: ResolvedTimeFormat;
};
export function buildSystemPromptParams(params: {
config?: ClawdbotConfig;
agentId?: string;
runtime: Omit<RuntimeInfoInput, "agentId">;
}): SystemPromptRuntimeParams {
const userTimezone = resolveUserTimezone(params.config?.agents?.defaults?.userTimezone);
const userTimeFormat = resolveUserTimeFormat(params.config?.agents?.defaults?.timeFormat);
const userTime = formatUserTime(new Date(), userTimezone, userTimeFormat);
return {
runtimeInfo: {
agentId: params.agentId,
...params.runtime,
},
userTimezone,
userTime,
userTimeFormat,
};
}

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { buildAgentSystemPrompt } from "./system-prompt.js";
import { buildAgentSystemPrompt, buildRuntimeLine } from "./system-prompt.js";
describe("buildAgentSystemPrompt", () => {
it("includes owner numbers when provided", () => {
@@ -252,6 +252,22 @@ describe("buildAgentSystemPrompt", () => {
expect(prompt).toContain("capabilities=inlineButtons");
});
it("includes agent id in runtime when provided", () => {
const prompt = buildAgentSystemPrompt({
workspaceDir: "/tmp/clawd",
runtimeInfo: {
agentId: "work",
host: "host",
os: "macOS",
arch: "arm64",
node: "v20",
model: "anthropic/claude",
},
});
expect(prompt).toContain("agent=work");
});
it("includes reasoning visibility hint", () => {
const prompt = buildAgentSystemPrompt({
workspaceDir: "/tmp/clawd",
@@ -263,6 +279,31 @@ describe("buildAgentSystemPrompt", () => {
expect(prompt).toContain("/status shows Reasoning");
});
it("builds runtime line with agent and channel details", () => {
const line = buildRuntimeLine(
{
agentId: "work",
host: "host",
os: "macOS",
arch: "arm64",
node: "v20",
model: "anthropic/claude",
},
"telegram",
["inlineButtons"],
"low",
);
expect(line).toContain("agent=work");
expect(line).toContain("host=host");
expect(line).toContain("os=macOS (arm64)");
expect(line).toContain("node=v20");
expect(line).toContain("model=anthropic/claude");
expect(line).toContain("channel=telegram");
expect(line).toContain("capabilities=inlineButtons");
expect(line).toContain("thinking=low");
});
it("describes sandboxed runtime and elevated when allowed", () => {
const prompt = buildAgentSystemPrompt({
workspaceDir: "/tmp/clawd",

View File

@@ -149,6 +149,7 @@ export function buildAgentSystemPrompt(params: {
/** Controls which hardcoded sections to include. Defaults to "full". */
promptMode?: PromptMode;
runtimeInfo?: {
agentId?: string;
host?: string;
os?: string;
arch?: string;
@@ -546,27 +547,40 @@ export function buildAgentSystemPrompt(params: {
);
}
lines.push(
"## Runtime",
`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}` : "",
runtimeChannel ? `channel=${runtimeChannel}` : "",
runtimeChannel
? `capabilities=${runtimeCapabilities.length > 0 ? runtimeCapabilities.join(",") : "none"}`
: "",
`thinking=${params.defaultThinkLevel ?? "off"}`,
]
.filter(Boolean)
.join(" | ")}`,
`Reasoning: ${reasoningLevel} (hidden unless on/stream). Toggle /reasoning; /status shows Reasoning when enabled.`,
);
lines.push("## Runtime", buildRuntimeLine(runtimeInfo, runtimeChannel, runtimeCapabilities, params.defaultThinkLevel), `Reasoning: ${reasoningLevel} (hidden unless on/stream). Toggle /reasoning; /status shows Reasoning when enabled.`);
return lines.filter(Boolean).join("\n");
}
export function buildRuntimeLine(
runtimeInfo: {
agentId?: string;
host?: string;
os?: string;
arch?: string;
node?: string;
model?: string;
},
runtimeChannel?: string,
runtimeCapabilities: string[] = [],
defaultThinkLevel?: ThinkLevel,
): string {
return `Runtime: ${[
runtimeInfo?.agentId ? `agent=${runtimeInfo.agentId}` : "",
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}` : "",
runtimeChannel ? `channel=${runtimeChannel}` : "",
runtimeChannel
? `capabilities=${runtimeCapabilities.length > 0 ? runtimeCapabilities.join(",") : "none"}`
: "",
`thinking=${defaultThinkLevel ?? "off"}`,
]
.filter(Boolean)
.join(" | ")}`;
}