Skip to content

fix: attribute subagent volume to the model that dispatched it - #42

Merged
AqueGen merged 2 commits into
mainfrom
fix/attribute-agent-to-dispatching-model
Sep 10, 2026
Merged

AqueGen merged 2 commits into
mainfrom
fix/attribute-agent-to-dispatching-model

Conversation

@AqueGen

@AqueGen AqueGen commented Sep 9, 2026

Copy link
Copy Markdown
Owner

What

tokens attributed every subagent to sessionModelOf(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:

  1. The toolUseId from the agent's sidecar (agent-<id>.meta.json, already read for agentType) looked up in the parent transcript: the assistant line carrying that tool_use block is the dispatching message, and its message.model is the model in effect at that instant.
  2. For nested agents (spawnDepth 2, parentAgentId in the sidecar) the same lookup in agent-<parentAgentId>.jsonl beside it.
  3. The parent's assistant model with the greatest timestamp at or before the agent's first-line timestamp. Max timestamp, not last line in file order: a resumed session re-appends history, so file order is not chronological.
  4. sessionModelOf as before.

One readFileSync per parent transcript, memoized by path; the walk reaches a subagents/ 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 --session filter 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 and commands/stats.md wording 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 --session on a main session). Existing fixtures carry no sidecars or timestamps and pass through step 4 unchanged.

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.
@AqueGen
AqueGen merged commit 7d01541 into main Sep 10, 2026
2 checks passed
@AqueGen
AqueGen deleted the fix/attribute-agent-to-dispatching-model branch September 10, 2026 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant