Ask the session what is running instead of counting it
A session whose main agent had stopped while a background subagent worked showed as waiting. The count was kept by hand -- +1 on PreToolUse matched to ^(Agent|Task)$, -1 on SubagentStop -- and the two halves do not see the same thing. A launch reaches the hook only at the top level: a subagent spawning its own subagents does it through events carrying agent_id, which are dropped. SubagentStop arrives for every subagent at every depth. Each nested one subtracted from a batch it had never joined. Measured on the live session that showed it: one background agent, then fourteen nested stops, the first of which took the count to zero and turned the chip white with the batch still running. Replaying those recorded events through the old hook reproduces it exactly, and through the new one holds busy throughout, rising to two while two background agents ran. The events carry the answer themselves. Stop and SubagentStop -- the two that can end a turn, and the only two where it matters -- come with background_tasks: every running task with its type and status. The count is now read from there and nothing accumulates, so it cannot drift, and a subagent that dies without sending SubagentStop no longer leaks a count that pins the chip at busy. Events without the field leave the stored value alone, which is what keeps an idle_prompt nudge from freeing a working session. A background shell is deliberately not counted. A dev server left running says nothing about whether the session needs you, and treating it as work would hold the chip at "working" for as long as it lives. PreToolUse is no longer registered: counting was the only thing it was for. It stays listed as a legacy event so that both install and uninstall sweep it out of settings.json rather than leaving it there to spawn the hook on every agent launch for nothing.
This commit is contained in:
+11
-9
@@ -23,17 +23,11 @@ HOOK = os.path.join(os.path.dirname(os.path.abspath(__file__)), "claude-status-h
|
||||
# before it writes -- observed with `claude -p`, which left a session stuck at
|
||||
# "busy" forever. They fire at most once per turn, so ~20 ms is free; the
|
||||
# per-tool-call events stay async so they never sit in the agent's loop.
|
||||
# (async, matcher). PreToolUse is matched to the agent-spawning tools alone:
|
||||
# unmatched it would fire on every tool call in every session, and the only
|
||||
# thing it is here for is to count subagents as they start. The pattern is
|
||||
# anchored because it is a regex -- a bare "Task" also matches TaskCreate,
|
||||
# TaskUpdate and friends, which are not subagents. The hook re-checks the name
|
||||
# anyway, in case a future matcher works differently.
|
||||
# (async, matcher).
|
||||
EVENTS = {
|
||||
"SessionStart": (True, ""),
|
||||
"UserPromptSubmit": (True, ""),
|
||||
"Notification": (True, ""),
|
||||
"PreToolUse": (True, "^(Agent|Task)$"),
|
||||
"PostToolUse": (True, ""),
|
||||
"PreCompact": (True, ""),
|
||||
"SubagentStop": (True, ""),
|
||||
@@ -41,6 +35,14 @@ EVENTS = {
|
||||
"SessionEnd": (False, ""),
|
||||
}
|
||||
|
||||
# Registered once, no longer. PreToolUse existed to count subagents as they
|
||||
# started; the count now comes from the snapshot the events carry themselves
|
||||
# (see running_agents in the hook). Listed rather than forgotten because both
|
||||
# install and uninstall sweep these out: an entry left behind would go on
|
||||
# spawning the hook on every agent launch for nothing, and an uninstall that
|
||||
# leaves our registrations in settings.json is not an uninstall.
|
||||
LEGACY_EVENTS = ("PreToolUse",)
|
||||
|
||||
|
||||
def entry(spec):
|
||||
async_, matcher = spec
|
||||
@@ -90,9 +92,9 @@ def main():
|
||||
shutil.copymode(path, path + ".bak")
|
||||
|
||||
hooks = settings.setdefault("hooks", {})
|
||||
for event in EVENTS:
|
||||
for event in list(EVENTS) + list(LEGACY_EVENTS):
|
||||
groups = [g for g in hooks.get(event, []) if not is_ours(g)]
|
||||
if not uninstall:
|
||||
if not uninstall and event in EVENTS:
|
||||
groups.append(entry(EVENTS[event]))
|
||||
if groups:
|
||||
hooks[event] = groups
|
||||
|
||||
Reference in New Issue
Block a user