fabro/lib/components
Bryan Helmkamp 2b6fcdbb80
Bill subagent spend to stages and make live usage loss-proof
Stage billing previously counted only the parent session's own LLM
calls. SubAgentResult carried no usage, so when a stage completed, the
authoritative billing replaced the live event-accumulated total with a
main-session-only number and every child session's spend vanished. On a
real run this collapsed a ~$175 total to ~$10.

Fix, part 1 — child spend reaches the parent's totals:
- SubAgentSupervisor keeps a spend ledger (SessionSpend). The child
  runner records last_input_usage/last_input_cost after every
  generation, including failed turns.
- The parent session drains the ledger into its input totals before
  publishing them, so AgentApiBackend::record_input_usage bills the
  whole session tree. Nested subagents compose: grandchildren drain
  into the child, whose runner records into the parent.

Fix, part 2 — the live number rides the same data:
- Each session emits a monotonic lifetime counter of its own spend
  (session_spend on AssistantMessage, session_billing on agent.message
  events, spec'd in fabro-api.yaml).
- The projection keeps the latest counter per session at the run level
  and attributes only the delta to the stage a message belongs to, so
  dropped or replayed events can no longer lose or double-count spend,
  and sessions reused across stages (shared threads) bill each stage
  for exactly its own turns. Legacy events without the counter fall
  back to per-message summing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 18:27:56 -04:00
..
fabro-acp refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-agent Bill subagent spend to stages and make live usage loss-proof 2026-08-06 18:27:56 -04:00
fabro-automation Move GitHub coordinate validation to shared types 2026-08-03 14:53:46 -04:00
fabro-checkpoint refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-dump Remove the recorded pre-run push outcome, keep the push 2026-08-03 19:27:33 -04:00
fabro-environment refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-github Simplify async pull request creation 2026-08-04 14:51:29 -04:00
fabro-graphviz Merge pull request #686 from fabro-sh/fix/space-separated-node-classes 2026-07-29 22:31:48 -04:00
fabro-hooks fix: address env interpolation review findings 2026-07-28 17:30:50 -04:00
fabro-install refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-interview refactor: remove duplicated review target rendering and validation 2026-07-28 15:14:23 -04:00
fabro-llm feat(models): add Fireworks Kimi K3 Fast 2026-08-04 13:55:48 -04:00
fabro-manifest Remove the recorded pre-run push outcome, keep the push 2026-08-03 19:27:33 -04:00
fabro-mcp refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-mcp-store refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-sandbox fix(daytona): accept newer permission scopes 2026-07-31 12:22:08 -04:00
fabro-slack fix: stop a pipe in a link label from splitting Slack link markup 2026-07-28 15:31:20 -04:00
fabro-store Bill subagent spend to stages and make live usage loss-proof 2026-08-06 18:27:56 -04:00
fabro-tool Move input scalar coercion to shared types 2026-08-03 19:17:03 -04:00
fabro-tracker refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-validate Merge remote-tracking branch 'origin/main' into feat/command-stdin-source 2026-07-29 11:37:27 -04:00
fabro-variable refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-workflow Bill subagent spend to stages and make live usage loss-proof 2026-08-06 18:27:56 -04:00