From 38f91f875b9d964cf3674017cfe600423ac825d8 Mon Sep 17 00:00:00 2001 From: Nathan Hangen Date: Wed, 28 Jan 2026 19:33:13 -0500 Subject: [PATCH] refactor(windows): re-apply guidance with minimal formatting changes Addresses feedback about formatting noise by reverting system-prompt.ts and re-applying logic minimally. PR template removed (moved to separate PR). --- ...ded-helpers.isfailovererrormessage.test.ts | 2 + src/agents/pi-embedded-helpers/errors.ts | 6 + src/agents/system-prompt.ts | 200 +++++++++--------- 3 files changed, 113 insertions(+), 95 deletions(-) diff --git a/src/agents/pi-embedded-helpers.isfailovererrormessage.test.ts b/src/agents/pi-embedded-helpers.isfailovererrormessage.test.ts index 2afb8557b..13e9c97bc 100644 --- a/src/agents/pi-embedded-helpers.isfailovererrormessage.test.ts +++ b/src/agents/pi-embedded-helpers.isfailovererrormessage.test.ts @@ -14,6 +14,8 @@ describe("isFailoverErrorMessage", () => { const samples = [ "invalid api key", "429 rate limit exceeded", + "No available auth profile for openai-codex (all in cooldown or unavailable).", + "Provider openai-codex is in cooldown (all profiles unavailable)", "Your credit balance is too low", "request timed out", "invalid request format", diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index 849c4293e..6fabb775d 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -362,6 +362,10 @@ const ERROR_PATTERNS = { "quota exceeded", "resource_exhausted", "usage limit", + // Auth-profile selection can fail due to provider-wide cooldown after repeated rate limits. + // Treat as rate_limit so model fallback can engage even before a request is sent. + /all in cooldown/i, + /in cooldown \(all profiles unavailable\)/i, ], overloaded: [/overloaded_error|"type"\s*:\s*"overloaded_error"/i, "overloaded"], timeout: ["timeout", "timed out", "deadline exceeded", "context deadline exceeded"], @@ -388,6 +392,8 @@ const ERROR_PATTERNS = { /\b403\b/, "no credentials found", "no api key found", + // Agent can fail before request if no usable auth profile exists. + /no available auth profile/i, ], format: [ "string should match pattern", diff --git a/src/agents/system-prompt.ts b/src/agents/system-prompt.ts index ed97fd539..a3c973b43 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 ? "" : "", "", @@ -399,47 +399,57 @@ export function buildAgentSystemPrompt(params: { "Treat this directory as the single global workspace for file operations unless explicitly instructed otherwise.", ...workspaceNotes, "", + ...(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`.", + "", + ] + : []), ...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), @@ -472,22 +482,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) {