Subagent summary disable + labeling
Claude Code subagents (the Task tool and built-in agents like Explore/Plan/Bash) no longer trigger a session summary on Stop, and every observation row now carries the originating subagent's identity.
Features
- Subagent Stop hooks skip summarization. When a hook fires inside a subagent (identified by
agent_idon stdin), the handler short-circuits before bootstrapping the worker. Only the main assistant owns the session summary. Sessions started with--agent(which setagent_typebut notagent_id) still own their summary. - Observations are labeled by subagent. The
observationstable gains two new nullable columns —agent_typeandagent_id— populated end-to-end from the hook stdin through the pending queue into storage. Main-session rows remainNULL. Labels survive worker restarts via matching columns onpending_messages.
Safety
- Defense-in-depth guard on the worker
/api/sessions/summarizeroute so direct API callers can't bypass the hook-layer short-circuit. pickAgentFieldtype guard at the adapter edge validates the hook input: must be a non-empty string ≤128 characters, otherwise dropped.- Content-hash dedup intentionally excludes
agent_type/agent_idso the same semantic observation from a subagent and its parent merges to a single row.
Schema
- Migration 010 (version 27) adds the two columns to
observationsandpending_messages, plus indexes onobservations.agent_typeandobservations.agent_id. Idempotent, state-aware logging.
Tests
- 17 new unit tests: adapter extraction (length cap boundary, empty-string rejection, type guards), handler short-circuit behavior, DB-level labeling and dedup invariants.
PR: #2073