Skip to content

config cap for bash wait: true (e.g. bash.wait_max_ms), auto-promoting to background when reached #398

Description

@null-axiom

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 bash with wait: true, timeout: 900000 to run a Playwright suite.
  • The command took 308 s.
  • The next model request had cache_read: 0 and cache_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_ms is only consulted for non-blocking calls, and the only bound on a wait: true call is the agent's own timeout argument.
  • Config keys today (config.d.ts): foreground_wait_window_ms, watch_sync_max_ms, long_running_reminder_*. None of them limits wait: 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_watch it, and each watch is already capped by watch_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 timeout semantics. Today a very large timeout with wait: true means "block up to that long". With the new setting it would still kill at timeout, but stop blocking the turn at wait_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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions