diff --git a/src/agents/pi-embedded-runner.test.ts b/src/agents/pi-embedded-runner.test.ts
index 366b9be93..147c09a7c 100644
--- a/src/agents/pi-embedded-runner.test.ts
+++ b/src/agents/pi-embedded-runner.test.ts
@@ -101,7 +101,7 @@ beforeAll(async () => {
workspaceDir = path.join(tempRoot, "workspace");
await fs.mkdir(agentDir, { recursive: true });
await fs.mkdir(workspaceDir, { recursive: true });
-}, 20_000);
+}, 120_000);
afterAll(async () => {
if (!tempRoot) return;
diff --git a/src/agents/system-prompt.ts b/src/agents/system-prompt.ts
index 8264f934a..633ef869b 100644
--- a/src/agents/system-prompt.ts
+++ b/src/agents/system-prompt.ts
@@ -83,21 +83,21 @@ function buildMessagingSection(params: {
"- Never use exec/curl for provider messaging; Moltbot handles all routing internally.",
params.availableTools.has("message")
? [
- "",
- "### message tool",
- "- Use `message` for proactive sends + channel actions (polls, reactions, etc.).",
- "- For `action=send`, include `to` and `message`.",
- `- If multiple channels are configured, pass \`channel\` (${params.messageChannelOptions}).`,
- `- If you use \`message\` (\`action=send\`) to deliver your user-visible reply, respond with ONLY: ${SILENT_REPLY_TOKEN} (avoid duplicate replies).`,
- params.inlineButtonsEnabled
- ? "- Inline buttons supported. Use `action=send` with `buttons=[[{text,callback_data}]]` (callback_data routes back as a user message)."
- : params.runtimeChannel
- ? `- Inline buttons not enabled for ${params.runtimeChannel}. If you need them, ask to set ${params.runtimeChannel}.capabilities.inlineButtons ("dm"|"group"|"all"|"allowlist").`
- : "",
- ...(params.messageToolHints ?? []),
- ]
- .filter(Boolean)
- .join("\n")
+ "",
+ "### message tool",
+ "- Use `message` for proactive sends + channel actions (polls, reactions, etc.).",
+ "- For `action=send`, include `to` and `message`.",
+ `- If multiple channels are configured, pass \`channel\` (${params.messageChannelOptions}).`,
+ `- If you use \`message\` (\`action=send\`) to deliver your user-visible reply, respond with ONLY: ${SILENT_REPLY_TOKEN} (avoid duplicate replies).`,
+ params.inlineButtonsEnabled
+ ? "- Inline buttons supported. Use `action=send` with `buttons=[[{text,callback_data}]]` (callback_data routes back as a user message)."
+ : params.runtimeChannel
+ ? `- Inline buttons not enabled for ${params.runtimeChannel}. If you need them, ask to set ${params.runtimeChannel}.capabilities.inlineButtons ("dm"|"group"|"all"|"allowlist").`
+ : "",
+ ...(params.messageToolHints ?? []),
+ ]
+ .filter(Boolean)
+ .join("\n")
: "",
"",
];
@@ -282,15 +282,15 @@ export function buildAgentSystemPrompt(params: {
: undefined;
const reasoningHint = params.reasoningTagHint
? [
- "ALL internal reasoning MUST be inside ....",
- "Do not output any analysis outside .",
- "Format every reply as ... then ..., with no other text.",
- "Only the final user-visible reply may appear inside .",
- "Only text inside is shown to the user; everything else is discarded and never seen by the user.",
- "Example:",
- "Short internal reasoning.",
- "Hey there! What would you like to do next?",
- ].join(" ")
+ "ALL internal reasoning MUST be inside ....",
+ "Do not output any analysis outside .",
+ "Format every reply as ... then ..., with no other text.",
+ "Only the final user-visible reply may appear inside .",
+ "Only text inside is shown to the user; everything else is discarded and never seen by the user.",
+ "Example:",
+ "Short internal reasoning.",
+ "Hey there! What would you like to do next?",
+ ].join(" ")
: undefined;
const reasoningLevel = params.reasoningLevel ?? "off";
const userTimezone = params.userTimezone?.trim();
@@ -336,21 +336,21 @@ export function buildAgentSystemPrompt(params: {
toolLines.length > 0
? toolLines.join("\n")
: [
- "Pi lists the standard tools above. This runtime enables:",
- "- grep: search file contents for patterns",
- "- find: find files by glob pattern",
- "- ls: list directory contents",
- "- apply_patch: apply multi-file patches",
- `- ${execToolName}: run shell commands (supports background via yieldMs/background)`,
- `- ${processToolName}: manage background exec sessions`,
- "- browser: control clawd's dedicated browser",
- "- canvas: present/eval/snapshot the Canvas",
- "- nodes: list/describe/notify/camera/screen on paired nodes",
- "- cron: manage cron jobs and wake events (use for reminders; when scheduling a reminder, write the systemEvent text as something that will read like a reminder when it fires, and mention that it is a reminder depending on the time gap between setting and firing; include recent context in reminder text if appropriate)",
- "- sessions_list: list sessions",
- "- sessions_history: fetch session history",
- "- sessions_send: send to another session",
- ].join("\n"),
+ "Pi lists the standard tools above. This runtime enables:",
+ "- grep: search file contents for patterns",
+ "- find: find files by glob pattern",
+ "- ls: list directory contents",
+ "- apply_patch: apply multi-file patches",
+ `- ${execToolName}: run shell commands (supports background via yieldMs/background)`,
+ `- ${processToolName}: manage background exec sessions`,
+ "- browser: control clawd's dedicated browser",
+ "- canvas: present/eval/snapshot the Canvas",
+ "- nodes: list/describe/notify/camera/screen on paired nodes",
+ "- cron: manage cron jobs and wake events (use for reminders; when scheduling a reminder, write the systemEvent text as something that will read like a reminder when it fires, and mention that it is a reminder depending on the time gap between setting and firing; include recent context in reminder text if appropriate)",
+ "- sessions_list: list sessions",
+ "- sessions_history: fetch session history",
+ "- 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.",
"",
@@ -375,11 +375,11 @@ export function buildAgentSystemPrompt(params: {
hasGateway && !isMinimal ? "## Moltbot Self-Update" : "",
hasGateway && !isMinimal
? [
- "Get Updates (self-update) is ONLY allowed when the user explicitly asks for it.",
- "Do not run config.apply or update.run unless the user explicitly requests an update or config change; if it's not explicit, ask first.",
- "Actions: config.get, config.schema, config.apply (validate + write full config, then restart), update.run (update deps or git, then restart).",
- "After restart, Moltbot pings the last active session automatically.",
- ].join("\n")
+ "Get Updates (self-update) is ONLY allowed when the user explicitly asks for it.",
+ "Do not run config.apply or update.run unless the user explicitly requests an update or config change; if it's not explicit, ask first.",
+ "Actions: config.get, config.schema, config.apply (validate + write full config, then restart), update.run (update deps or git, then restart).",
+ "After restart, Moltbot pings the last active session automatically.",
+ ].join("\n")
: "",
hasGateway && !isMinimal ? "" : "",
"",
@@ -396,61 +396,57 @@ export function buildAgentSystemPrompt(params: {
params.modelAliasLines && params.modelAliasLines.length > 0 && !isMinimal ? "" : "",
"## Workspace",
`Your working directory is: ${params.workspaceDir}`,
- "Treat this directory as the single global workspace for file operations unless explicitly instructed otherwise.",
- ...workspaceNotes,
- "",
- ...(runtimeInfo?.os?.toLowerCase().includes("windows")
+ ...(runtimeInfo?.os?.toLowerCase()?.includes("windows")
? [
- "## Windows Shell Guidance",
- "You are running on Windows (PowerShell).",
- "- Use PowerShell syntax (e.g. `$env:VAR` instead of `%VAR%` or `$VAR`).",
- "- Do NOT use Unix commands like `grep`, `sed`, `awk`, `head`, `tail` in exec/shell (PowerShell) commands unless you are sure they are installed. Pi's built-in tools named `grep`, `find`, and `ls` are safe to use.",
- "- Use `findstr` or `Select-String` instead of `grep`.",
- "- Use `Get-ChildItem` (dir/ls) with `-Recurse` instead of `find`.",
- "",
- ]
+ "## Windows Shell Guidance",
+ "You are running on Windows (PowerShell).",
+ "- Use PowerShell syntax (e.g. `$env:VAR` instead of `%VAR%` or `$VAR`).",
+ "- Do NOT use Unix commands like `grep`, `sed`, `awk`, `head`, `tail` in exec/shell (PowerShell) commands unless you are sure they are installed. Pi's built-in tools named `grep`, `find`, and `ls` are safe to use.",
+ "- Use `findstr` or `Select-String` instead of `grep`.",
+ "- Use `Get-ChildItem` (dir/ls) with `-Recurse` instead of `find`.",
+ "",
+ ]
: []),
...docsSection,
params.sandboxInfo?.enabled ? "## Sandbox" : "",
params.sandboxInfo?.enabled
? [
- "You are running in a sandboxed runtime (tools execute in Docker).",
- "Some tools may be unavailable due to sandbox policy.",
- "Sub-agents stay sandboxed (no elevated/host access). Need outside-sandbox read/write? Don't spawn; ask first.",
- params.sandboxInfo.workspaceDir
- ? `Sandbox workspace: ${params.sandboxInfo.workspaceDir}`
+ "You are running in a sandboxed runtime (tools execute in Docker).",
+ "Some tools may be unavailable due to sandbox policy.",
+ "Sub-agents stay sandboxed (no elevated/host access). Need outside-sandbox read/write? Don't spawn; ask first.",
+ params.sandboxInfo.workspaceDir
+ ? `Sandbox workspace: ${params.sandboxInfo.workspaceDir}`
+ : "",
+ params.sandboxInfo.workspaceAccess
+ ? `Agent workspace access: ${params.sandboxInfo.workspaceAccess}${params.sandboxInfo.agentWorkspaceMount
+ ? ` (mounted at ${params.sandboxInfo.agentWorkspaceMount})`
+ : ""
+ }`
+ : "",
+ params.sandboxInfo.browserBridgeUrl ? "Sandbox browser: enabled." : "",
+ params.sandboxInfo.browserNoVncUrl
+ ? `Sandbox browser observer (noVNC): ${params.sandboxInfo.browserNoVncUrl}`
+ : "",
+ params.sandboxInfo.hostBrowserAllowed === true
+ ? "Host browser control: allowed."
+ : params.sandboxInfo.hostBrowserAllowed === false
+ ? "Host browser control: blocked."
: "",
- params.sandboxInfo.workspaceAccess
- ? `Agent workspace access: ${params.sandboxInfo.workspaceAccess}${
- params.sandboxInfo.agentWorkspaceMount
- ? ` (mounted at ${params.sandboxInfo.agentWorkspaceMount})`
- : ""
- }`
- : "",
- params.sandboxInfo.browserBridgeUrl ? "Sandbox browser: enabled." : "",
- params.sandboxInfo.browserNoVncUrl
- ? `Sandbox browser observer (noVNC): ${params.sandboxInfo.browserNoVncUrl}`
- : "",
- params.sandboxInfo.hostBrowserAllowed === true
- ? "Host browser control: allowed."
- : params.sandboxInfo.hostBrowserAllowed === false
- ? "Host browser control: blocked."
- : "",
- params.sandboxInfo.elevated?.allowed
- ? "Elevated exec is available for this session."
- : "",
- params.sandboxInfo.elevated?.allowed
- ? "User can toggle with /elevated on|off|ask|full."
- : "",
- params.sandboxInfo.elevated?.allowed
- ? "You may also send /elevated on|off|ask|full when needed."
- : "",
- params.sandboxInfo.elevated?.allowed
- ? `Current elevated level: ${params.sandboxInfo.elevated.defaultLevel} (ask runs exec on host with approvals; full auto-approves).`
- : "",
- ]
- .filter(Boolean)
- .join("\n")
+ params.sandboxInfo.elevated?.allowed
+ ? "Elevated exec is available for this session."
+ : "",
+ params.sandboxInfo.elevated?.allowed
+ ? "User can toggle with /elevated on|off|ask|full."
+ : "",
+ params.sandboxInfo.elevated?.allowed
+ ? "You may also send /elevated on|off|ask|full when needed."
+ : "",
+ params.sandboxInfo.elevated?.allowed
+ ? `Current elevated level: ${params.sandboxInfo.elevated.defaultLevel} (ask runs exec on host with approvals; full auto-approves).`
+ : "",
+ ]
+ .filter(Boolean)
+ .join("\n")
: "",
params.sandboxInfo?.enabled ? "" : "",
...buildUserIdentitySection(ownerLine, isMinimal),
@@ -483,22 +479,22 @@ export function buildAgentSystemPrompt(params: {
const guidanceText =
level === "minimal"
? [
- `Reactions are enabled for ${channel} in MINIMAL mode.`,
- "React ONLY when truly relevant:",
- "- Acknowledge important user requests or confirmations",
- "- Express genuine sentiment (humor, appreciation) sparingly",
- "- Avoid reacting to routine messages or your own replies",
- "Guideline: at most 1 reaction per 5-10 exchanges.",
- ].join("\n")
+ `Reactions are enabled for ${channel} in MINIMAL mode.`,
+ "React ONLY when truly relevant:",
+ "- Acknowledge important user requests or confirmations",
+ "- Express genuine sentiment (humor, appreciation) sparingly",
+ "- Avoid reacting to routine messages or your own replies",
+ "Guideline: at most 1 reaction per 5-10 exchanges.",
+ ].join("\n")
: [
- `Reactions are enabled for ${channel} in EXTENSIVE mode.`,
- "Feel free to react liberally:",
- "- Acknowledge messages with appropriate emojis",
- "- Express sentiment and personality through reactions",
- "- React to interesting content, humor, or notable events",
- "- Use reactions to confirm understanding or agreement",
- "Guideline: react whenever it feels natural.",
- ].join("\n");
+ `Reactions are enabled for ${channel} in EXTENSIVE mode.`,
+ "Feel free to react liberally:",
+ "- Acknowledge messages with appropriate emojis",
+ "- Express sentiment and personality through reactions",
+ "- React to interesting content, humor, or notable events",
+ "- Use reactions to confirm understanding or agreement",
+ "Guideline: react whenever it feels natural.",
+ ].join("\n");
lines.push("## Reactions", guidanceText, "");
}
if (reasoningHint) {