Repository navigation
cli: answer queued agent hooks inside the agent's hook timeout - #14834
Conversation
Regression for a Claude UserPromptSubmit hook that Claude Code killed after its 5 s timeout although the hook passes CMUXTERM_CLI_RESPONSE_TIMEOUT_SEC=0.5. The socket waits are bounded; nothing bounds the whole command. Against a fake app socket this covers an agent that never closes stdin and an app that accepts the enqueue request and never answers. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A Claude UserPromptSubmit hook ran past Claude Code's 5 s hook timeout although
the hook passes CMUXTERM_CLI_RESPONSE_TIMEOUT_SEC=0.5. That setting bounds only
socket waits. Password resolution can reach the keychain (twice: the route
client resolved it again), stdin was read until EOF, and nothing bounded the
command as a whole.
- Arm a wall-clock watchdog for `hooks enqueue`
(AgentHookDeliveryPolicy.admissionWallClockSeconds, 2 s). Past it the
process writes the neutral {} the shell fallback prints and exits 0. The
command's own answer and the watchdog share one lock, so exactly one
response is written.
- The route-resolution client reuses the password the admission client
already resolved instead of running SocketPasswordResolver again.
- Read stdin with a 1 s poll deadline; a writer that keeps stdin open gets
what arrived admitted.
- Never start Sentry for hooks enqueue: its failures are expected and fail
open.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
All contributors have signed the CLA ✍️ ✅ |
|
Warning Review limit reachedNext included review available in 46 seconds. View limit detailsLimit details: You’ve used all 10 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository: manaflow-ai/cmux/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (8)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review follow-up: a writer that keeps stdin open and is cut mid-document by the 1 s read deadline now gets the neutral payload instead of truncated JSON. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Automatic catch-up couldn't merge Label |
…ck-bound # Conflicts: # CLI/CMUXCLI+AgentHookAdmission.swift
|
Dogfood build of cmux DEV pr-14834-53f2e0c2.app The link opens this exact commit in the cmux dev menu bar app. The build starts on each push and the page waits until it is ready; a newer push replaces it. It signs in against production, so Cloud or backend changes still need a tagged build with a development backend. |
…before the watchdog exits Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Main's #14931 made the hooks enqueue fallback drain the Claude spool first. The drain unlinks a record before admitting it, so the 2 s watchdog could exit after the claim and drop the event. A claim now runs inside a critical section: an expiry during it answers and exits when the claim ends. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Merged main (#14931 moved queued admission into The merge exposed one interaction:
|
|
Merge receipt for |
ee20686 fix: keep SSH exit prompt off PTY output drain (manaflow-ai#15337) 96e7a27 reload.sh: expand the empty resolver args safely under bash 3.2 (manaflow-ai#15352) 558d6b9 ci: move owned gui jobs to Blacksmith only when its queue is shorter (manaflow-ai#15336) fff0b82 UI fuzzer: seeded action sequences, oracles, minimized repros and deduplicated issues (manaflow-ai#15297) 94a6387 Add an agent activity mode to workspace auto-reordering (manaflow-ai#15216) e5231be CI: post screenshots and a GIF of each app PR's build in its dogfood comment (manaflow-ai#15280) 16f1270 cli: answer queued agent hooks inside the agent's hook timeout (manaflow-ai#14834) 3fd61eb Sidebar: show the most urgent pane's status when panes share an agent key (manaflow-ai#15260) 0975d0b Release discarded CodeRouter response bodies after retry (manaflow-ai#15253) 42f93d4 Re-verify the session against a body-supplied VM billing team (manaflow-ai#15339) bdb6920 Keep the mail broker from orphaning a reply to an unknown parent (manaflow-ai#15330) # Conflicts: # .github/workflows/ci-guards.yml # .github/workflows/ci-macos.yml # .github/workflows/test-e2e.yml
A Claude
UserPromptSubmithook installed by cmux ran past Claude Code's 5 s hook timeout, so Claude discarded its output and reported the hook as timed out. The hook passesCMUXTERM_CLI_RESPONSE_TIMEOUT_SEC=0.5, but that bounds only socket waits. Several steps incmux hooks enqueuehad no deadline:SecItemCopyMatching, and the PID route-resolution client resolved it a second time.cmux hooks enqueuenow answers inside a fixed wall-clock budget:hooks enqueuearms a 2 s timer (AgentHookDeliveryPolicy.admissionWallClockSeconds) at dispatch. When it expires, the process writes the same neutral{}the shell fallback prints and exits 0. The command's own answer and the watchdog go through one lock (AgentHookEnqueueWallClock.respond), so exactly one{}is written. The 2 s budget leaves the other 3 s of the declared timeout for process launch, which happens before the timer can be armed.CMUX_AGENT_HOOK_ENQUEUE_BUDGET_SECcan lower the budget for tests but never raise it.SocketClient.hasConfiguredAuthentication), instead of runningSocketPasswordResolveragain.polland a 1 s deadline. If the writer keeps stdin open, whatever arrived by then is admitted. The 1 MiB cap is unchanged.hooks enqueue. Its failures fail open by design and are expected while the app is busy or quitting.The event is dropped only if the app hasn't admitted it within 2 s. That's the same outcome as before, when the agent killed the hook and discarded its output, but the agent now gets a well-formed answer instead of a timeout.
Evidence
f2c142e4b2caddstests/test_cli_hook_enqueue_wall_clock.py(macos-cli-no-socketlane). It runs the real CLI against a fake app socket in three cases: a responsive app, stdin left open, and an app that accepts the enqueue request and never answers (budget lowered to 0.3 s). Each case must exit 0 with{}, within 2.5 s where a bound applies.6d8be52b37dis the fix.FileHandle.readuntil stdin closes, and exits 1 with a timeout error when the app stalls. I didn't build the CLI locally: the shared Mac was at load ~94, and the local rule is to leave native builds to CI. This PR's CI is the first native build and the first run of the new test.AgentHookDeliveryPolicyTestsgains a check that the wall-clock budget sits above the admission response timeout and within half the declared hook timeout.python3 scripts/verify-local.py --affected mf/main --swift-changed mf/main: 13/13 static checks passed, including Swift syntax on the changed files and test-registry validation.Not covered: no app-host tests changed. Nothing was dogfooded against a running app.
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Fixes
cmux hooks enqueuerunning past Claude Code's 5 s hook timeout. The command passed 0.5 s as a response timeout that bounded only socket waits; password resolution (including a duplicate keychain lookup), unbounded stdin reads, and Sentry startup had no deadline, so the agent killed the hook and discarded its output. The hook now answers with the neutral{}and exits 0 within the declared timeout, and the fallback that drains the Claude spool cannot drop a record the watchdog would cut mid-claim.Bug Fixes
{}and exits 0, with one shared lock guaranteeing exactly one response.hooks enqueue; its failures are expected and fail open.Written for commit 53f2e0c. Summary will update on new commits.