From 6f2e80418207becb88b5143e2a93d39c24a1dbfa Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 5 Apr 2026 03:17:10 +0900 Subject: [PATCH] fix(agents): prefer background completion wake over polling (#60877) * fix(agents): prefer completion wake over polling * fix(changelog): note completion wake guidance * fix(agents): qualify quiet exec completion wake * fix(agents): qualify disabled exec completion wake * fix(agents): split process polling from control actions --- CHANGELOG.md | 1 + src/agents/bash-tools.exec.ts | 1 + src/agents/bash-tools.process.ts | 1 + src/agents/bash-tools.test.ts | 12 ++++++++++++ src/agents/system-prompt.test.ts | 6 ++++++ src/agents/system-prompt.ts | 7 +++++-- 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a081b857abb..aa6a9961832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,7 @@ Docs: https://docs.openclaw.ai - Gateway/auth: serialize async shared-secret auth attempts per client so concurrent Tailscale-capable failures cannot overrun the intended auth rate-limit budget. Thanks @Telecaster2147. - Doctor/config: compare normalized `talk` configs by deep structural equality instead of key-order-sensitive serialization so `openclaw doctor --fix` stops repeatedly reporting/applying no-op `talk.provider/providers` normalization. (#59911) Thanks @ejames-dev. - Gateway/device auth: reuse cached device-token scopes only for cached-token reconnects, while keeping explicit `deviceToken` scope requests and empty-cache fallbacks intact so reconnects preserve `operator.read` without breaking explicit auth flows. (#46032) Thanks @caicongyang. +- Agents/scheduling: steer background-now work toward automatic completion wake and treat `process` polling as on-demand inspection or intervention instead of default completion handling. (#60877) Thanks @vincentkoc. - Google Gemini CLI auth: improve OAuth credential discovery across Windows nvm and Homebrew libexec installs, and align Code Assist metadata so Gemini login stops failing on packaged CLI layouts. (#40729) Thanks @hughcube. - Mattermost/config schema: accept `groups.*.requireMention` again so existing Mattermost configs no longer fail strict validation after upgrade. (#58271) Thanks @MoerAI. - Agents/failover: scope Anthropic `An unknown error occurred` failover matching by provider so generic internal unknown-error text no longer triggers retryable timeout fallback. (#59325) Thanks @aaron-he-zhu. diff --git a/src/agents/bash-tools.exec.ts b/src/agents/bash-tools.exec.ts index 9ab3cf07bae..1a186fc7d6f 100644 --- a/src/agents/bash-tools.exec.ts +++ b/src/agents/bash-tools.exec.ts @@ -1119,6 +1119,7 @@ export function describeExecTool(params?: { agentId?: string; hasCronTool?: bool const base = [ "Execute shell commands with background continuation for work that starts now.", "Use yieldMs/background to continue later via process tool.", + "For long-running work started now, rely on automatic completion wake when it is enabled and the command emits output or fails; otherwise use process to confirm completion. Use process whenever you need logs, status, input, or intervention.", params?.hasCronTool ? "Do not use exec sleep or delay loops for reminders or deferred follow-ups; use cron instead." : undefined, diff --git a/src/agents/bash-tools.process.ts b/src/agents/bash-tools.process.ts index 0945d6f7c8d..19f5f3fca58 100644 --- a/src/agents/bash-tools.process.ts +++ b/src/agents/bash-tools.process.ts @@ -120,6 +120,7 @@ function resetPollRetrySuggestion(sessionId: string): void { export function describeProcessTool(params?: { hasCronTool?: boolean }): string { return [ "Manage running exec sessions for commands already started: list, poll, log, write, send-keys, submit, paste, kill.", + "Use poll/log when you need status, logs, quiet-success confirmation, or completion confirmation when automatic completion wake is unavailable. Use write/send-keys/submit/paste/kill for input or intervention.", params?.hasCronTool ? "Do not use process polling to emulate timers or reminders; use cron for scheduled follow-ups." : undefined, diff --git a/src/agents/bash-tools.test.ts b/src/agents/bash-tools.test.ts index 93f03a6975d..5828b2f48ad 100644 --- a/src/agents/bash-tools.test.ts +++ b/src/agents/bash-tools.test.ts @@ -415,6 +415,15 @@ describe("tool descriptions", () => { const execWithCron = createTestExecTool({ hasCronTool: true }); const processWithCron = createProcessTool({ hasCronTool: true }); + expect(execWithCron.description).toContain( + "rely on automatic completion wake when it is enabled and the command emits output or fails; otherwise use process to confirm completion. Use process whenever you need logs, status, input, or intervention.", + ); + expect(processWithCron.description).toContain( + "completion confirmation when automatic completion wake is unavailable.", + ); + expect(processWithCron.description).toContain( + "Use write/send-keys/submit/paste/kill for input or intervention.", + ); expect(execWithCron.description).toContain( "Do not use exec sleep or delay loops for reminders or deferred follow-ups; use cron instead.", ); @@ -423,6 +432,9 @@ describe("tool descriptions", () => { ); expect(execTool.description).not.toContain("use cron instead"); expect(processTool.description).not.toContain("scheduled follow-ups"); + expect(execTool.description).toContain("otherwise use process to confirm completion"); + expect(processTool.description).toContain("completion confirmation when automatic completion wake is unavailable"); + expect(processTool.description).toContain("Use write/send-keys/submit/paste/kill for input or intervention."); }); }); diff --git a/src/agents/system-prompt.test.ts b/src/agents/system-prompt.test.ts index 15a138e3d6a..8425a5d06ea 100644 --- a/src/agents/system-prompt.test.ts +++ b/src/agents/system-prompt.test.ts @@ -124,6 +124,9 @@ describe("buildAgentSystemPrompt", () => { expect(prompt).toContain( "Use exec/process only for commands that start now and continue running in the background.", ); + expect(prompt).toContain( + "For long-running work that starts now, start it once and rely on automatic completion wake when it is enabled and the command emits output or fails; otherwise use process to confirm completion, and use it for logs, status, input, or intervention.", + ); expect(prompt).toContain( "Do not emulate scheduling with sleep loops, timeout loops, or repeated polling.", ); @@ -295,6 +298,9 @@ describe("buildAgentSystemPrompt", () => { expect(prompt).toContain( "Use exec/process only for commands that start now and continue running in the background.", ); + expect(prompt).toContain( + "For long-running work that starts now, start it once and rely on automatic completion wake when it is enabled and the command emits output or fails; otherwise use process to confirm completion, and use it for logs, status, input, or intervention.", + ); expect(prompt).toContain("Completion is push-based: it will auto-announce when done."); expect(prompt).toContain("Do not poll `subagents list` / `sessions_list` in a loop"); expect(prompt).toContain( diff --git a/src/agents/system-prompt.ts b/src/agents/system-prompt.ts index 6f0f69c2f6c..50cb2af3ba9 100644 --- a/src/agents/system-prompt.ts +++ b/src/agents/system-prompt.ts @@ -244,8 +244,9 @@ export function buildAgentSystemPrompt(params: { const sandboxedRuntime = params.sandboxInfo?.enabled === true; const acpSpawnRuntimeEnabled = acpEnabled && !sandboxedRuntime; const execToolSummary = - "Run shell commands (pty available for TTY-required CLIs; use for work that starts now, not delayed follow-ups)"; - const processToolSummary = "Manage background exec sessions for commands already started"; + "Run shell commands (pty available for TTY-required CLIs; use for work that starts now, not delayed follow-ups; background completion may wake automatically when enabled)"; + const processToolSummary = + "Manage background exec sessions for commands already started (poll/log for inspection, debugging, input, intervention, or completion confirmation when auto-wake is unavailable)"; const cronToolSummary = "Manage cron jobs and wake events (use for reminders, delayed follow-ups, and recurring tasks; for requests like 'check back in 10 minutes' or 'remind me later', use cron instead of exec sleep, yieldMs delays, or process polling; 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)"; const coreToolSummaries: Record = { @@ -477,10 +478,12 @@ export function buildAgentSystemPrompt(params: { ? [ `For follow-up at a future time (for example "check back in 10 minutes", reminders, run-later work, or recurring tasks), use cron instead of ${execToolName} sleep, yieldMs delays, or ${processToolName} polling.`, `Use ${execToolName}/${processToolName} only for commands that start now and continue running in the background.`, + `For long-running work that starts now, start it once and rely on automatic completion wake when it is enabled and the command emits output or fails; otherwise use ${processToolName} to confirm completion, and use it for logs, status, input, or intervention.`, "Do not emulate scheduling with sleep loops, timeout loops, or repeated polling.", ] : [ `For long waits, avoid rapid poll loops: use ${execToolName} with enough yieldMs or ${processToolName}(action=poll, timeout=).`, + `For long-running work that starts now, start it once and rely on automatic completion wake when it is enabled and the command emits output or fails; otherwise use ${processToolName} to confirm completion, and use it for logs, status, input, or intervention.`, ]), "If a task is more complex or takes longer, spawn a sub-agent. Completion is push-based: it will auto-announce when done.", ...(acpHarnessSpawnAllowed