Repository navigation
config cap for bash wait: true (e.g. bash.wait_max_ms), auto-promoting to background when reached #398
Description
Summary
bash.watch_sync_max_ms caps how long a single bash_watch call can block, which is useful for keeping a model's prompt cache warm. However, bash({ wait: true }) bypasses every time cap in the config, so a single call blocks for as long as the command runs. Please add a config setting, for example bash.wait_max_ms, that caps wait: true. When the cap is reached, the command should be promoted to a background task with the usual completion notice, the same way a normal foreground call is promoted after foreground_wait_window_ms.
Why
Anthropic's default prompt cache lasts 5 minutes. Claude Code uses it for subagents, and so do OpenCode setups that mirror Claude Code's cache placement. If no model request happens for over 5 minutes, the next request rewrites the whole context into the cache.
AFT's injected guidance tells agents to use bash({ command, wait: true }) for long commands such as test suites and builds. A subagent then blocks inside that one tool call for the full run, and the cache expires mid-call when the run goes past 5 minutes.
I set bash.watch_sync_max_ms: 270000 specifically so that waits end, and the model takes a cheap step, before the 5-minute cache expires. That works for bash_watch, but wait: true skips it.
Observed (AFT 0.58.2, OpenCode 1.18.x, Claude Sonnet 5.5 subagent)
- The worker called
bashwithwait: true, timeout: 900000to run a Playwright suite. - The command took 308 s.
- The next model request had
cache_read: 0andcache_write: 221,505, so the full context was rewritten. Every request before and after it read the cache normally. - Over two days, 5 of 1,855 tool calls by Sonnet subagents ran past about 5 minutes. Each one was a full cache rewrite of a 150–250K context.
Where it happens in the code (dist/index.js, 0.58.2)
requestedWait = !backgroundDisabled && coerceBoolean(args.wait)(≈ line 42077)blockToCompletion = subagentForcedForeground || backgroundDisabled || requestedWait(≈ line 42093)foreground_wait_window_msis only consulted for non-blocking calls, and the only bound on await: truecall is the agent's owntimeoutargument.- Config keys today (
config.d.ts):foreground_wait_window_ms,watch_sync_max_ms,long_running_reminder_*. None of them limitswait: true.
Proposal
"bash": {
// Maximum time a `wait: true` call blocks before it is promoted to a background
// task (completion notice + bash_watch, same as normal auto-promotion).
// Default: unset (current behavior, block to completion).
"wait_max_ms": 270000
}- When the cap is reached, the result reads exactly like today's auto-promotion: a task id and a note that it's still running, plus the existing completion reminder. The agent can then
bash_watchit, and each watch is already capped bywatch_sync_max_ms. - Subagents with
bash.subagent_background: false: background isn't available, so either ignore the cap or document that it doesn't apply. - Optional: also clamp the agent-supplied
timeoutsemantics. Today a very largetimeoutwithwait: truemeans "block up to that long". With the new setting it would still kill attimeout, but stop blocking the turn atwait_max_ms.
Workaround today
Prompt-level only: tell agents to keep each tool call under about 4 minutes and to background long commands. That conflicts with AFT's injected guidance recommending wait: true, so agents follow it inconsistently.
Activity
aft-alfonso commentedon Oct 4, 2026
Thanks for the measurements; the cache rewrite at 308 s is exactly the cost this should avoid.
mainalready has this for subagent sessions, asbash.worker_wait_max_ms. It isn't in a release yet (0.58.2 predates it). It caps how long a delegated session waits on one command:bashwithwait: true, or every foreground call whenbash.subagent_backgroundisfalse;bash_watchwithout a timeout.At the cap the command is not killed. It keeps running in the background, and the subagent gets its task id, how long it has run, and its latest output, so it can
bash_watchit orbash_killit. An explicittimeoutstill kills at that time.The default is 30 minutes and the minimum is 60 s, so your setting would be:
That also covers the
subagent_background: falsecase you raised: there the cap applies to every foreground call. Main (non-delegated) sessions are not covered by it;wait: truethere still blocks to completion. I'll update this issue when the release that carries it is published.