2 Commits

Author SHA1 Message Date
antonov 6edd2e3d52 Fix the session id never reaching the host, and the split lost on restart
Two real defects, both found by testing rather than reading:

1. The client sent sessionId in the POST body while the route read it from
   the query string, so the host always saw an empty id and answered
   total:null -- the pill was correct to stay silent. The client now sends
   the id in both places and the route reads both.
2. The ledger stored only a session total, so a restart kept the sum but
   dropped the per-purpose split and the call count (9.99 total against
   6.24 in parts, seen on the live route). Sessions are now stored as
   objects; a bare number from an older ledger is still accepted.

Also made the client report why it cannot read a total: swallowing the
error made 'nothing spent yet' indistinguishable from 'request failed'.
2026-10-15 15:40:08 +03:00
antonov b223f51540 Implement the session cost plugin
Host half wraps globalThis.fetch to read usage.cost out of provider
responses (tee, never clone) and listens on the llm/stream seam for the
session; the two halves join on the generation id. Client half adds one
pill to the composer dock under its own id, so the harness's native stats
stay where they are.

Verified against the live API: three calls in one session (agent reply,
compaction, session title) attributed with per-purpose totals, an unknown
session answered with null, the ledger written and reloaded across
instances, and the pill rendering only when a cost exists.
2026-10-15 13:33:05 +03:00