The stats layer reads usage/duration only from run.end, but neither
framework populated them, so tokens/cost/avg-duration were always 0.
- hermes: accumulate token usage across each run's api-result calls in
session state and attach the summed usage plus a computed duration_ms
(from a stored runStartedAt) onto run.end. metric.snapshot emission is
unchanged, so there is no double counting.
- claude-code: store runStartedAt and use it as a duration_ms fallback at
all run.end sites. Usage is unavailable from CC hook inputs.
Live verification: a real hermes run now reports duration_ms and
total_tokens on run.end; dashboard tokens_today/avg_duration_ms, both
previously 0, now populate. cost_today stays 0 (no provider emits cost
through the hooks).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
handleNotification("Done") was incorrectly emitting session.end and
calling clearState at the end of each Claude turn. Since "Done" means
a turn finished (not the session), clearing state caused subsequent
tool calls to find no runId, storing spans without run_id and making
them invisible in run-level queries.
- handleNotification: remove session.end emission and clearState call;
only emit run.end for the completed turn
- handleSessionEnd: load state file to get runId (in-memory activeRuns
is always empty in a subprocess)
- handlePromptSubmit: load state file to get runId for ending previous
run before starting a new one
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>