fix: attribute subagent volume to the model that dispatched it - #42
Merged
Merged
Conversation
The tokens report read one model per session transcript (its head, or its tail as a fallback) and pinned every subagent in it to that model. Long sessions switch models with /model, so in a 7d sample 135 of 269 agents were attributed to a model the session was not on when it dispatched them, and the dispatch report - which stamps the model at dispatch time - disagreed with the tokens report about the same window. Each agent now resolves its session model in this order: the assistant message carrying the tool_use id named by the agent sidecar (in the session transcript, or in the parent agent transcript for nested agents), then the model in effect at the agent transcript's first timestamp (max timestamp <= launch, since a resumed transcript is not in chronological file order), and only then the transcript head/tail as before. The --session filter for main sessions is now per line model, since a switched session no longer belongs wholly to one.
…ents, document nested-agent keying
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
tokensattributed every subagent tosessionModelOf(parent): the first model in the parent transcript's head, else the last in its tail. Long-lived sessions switch models with/model, so half of the agents were credited to a model the session was no longer on when it dispatched them, and the tokens report disagreed with the dispatch report (which stamps the model at dispatch time).The session model of an agent is now resolved in this order:
toolUseIdfrom the agent's sidecar (agent-<id>.meta.json, already read foragentType) looked up in the parent transcript: the assistant line carrying thattool_useblock is the dispatching message, and itsmessage.modelis the model in effect at that instant.spawnDepth2,parentAgentIdin the sidecar) the same lookup inagent-<parentAgentId>.jsonlbeside it.sessionModelOfas before.One
readFileSyncper parent transcript, memoized by path; the walk reaches asubagents/dir before the parent file beside it, so the index is built lazily. No hook change and no new dispatch-log field: the transcripts already hold everything needed, and this fixes history too.The
--sessionfilter on main sessions is now per line model instead of per file, so a session that switched mid-way contributes only the volume that ran on the filtered model. Footer andcommands/stats.mdwording updated.Why
Measured on this machine over 7 days: 269 agent transcripts, 269 sidecars, 269 with
toolUseId; 264 resolve at step 1, the 5 remaining are nested agents. Attribution by the dispatching message vs by session head: fable-5-1 137 vs 17, opus-5 109 vs 232, sonnet-5 18 vs 8. The dispatch log over the same window: fable-5-1 138, opus-5 109, sonnet-5 22. So the two reports now describe the same population. Three independent re-measurements (Opus, Fable, Codex gpt-6) agreed; the timestamp rule and the toolUseId join disagreed on 0 of 264 agents, and no agent lacked a preceding parent model line.Verification
node --test hooks/dispatch-counter.test.mjs hooks/update-check.test.mjs: 97 pass, 0 fail (93 baseline + 4 new: dispatch model over session head, nested agent via the parent agent's transcript, timestamp fallback, per-line--sessionon a main session). Existing fixtures carry no sidecars or timestamps and pass through step 4 unchanged.