fabro/lib/foundation
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
..
build-support refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-api Merge remote-tracking branch 'origin/main' into feat/async-pr-create 2026-08-04 15:04:24 -04:00
fabro-auth fix(model): prefer MOONSHOT_API_KEY 2026-07-31 09:14:58 -04:00
fabro-client Merge remote-tracking branch 'origin/main' into feat/async-pr-create 2026-08-04 15:04:24 -04:00
fabro-config Merge pull request #676 from fabro-sh/remove-run-agent-permissions 2026-08-01 10:18:45 -04:00
fabro-core refactor(sandbox): harden activation recovery 2026-07-28 15:38:35 -04:00
fabro-db refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-dev Merge pull request #676 from fabro-sh/remove-run-agent-permissions 2026-08-01 10:18:45 -04:00
fabro-http refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-macros refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-model Bill subagent spend to stages and make live usage loss-proof 2026-08-06 18:27:56 -04:00
fabro-oauth refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-options-metadata refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-proc refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-redact refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-static Merge remote-tracking branch 'origin/main' into fix/modal-provider-catalog 2026-08-01 09:32:19 -04:00
fabro-telemetry refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-template fix(workflow): harden script value interpolation 2026-07-28 17:21:35 -04:00
fabro-test refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-types Bill subagent spend to stages and make live usage loss-proof 2026-08-06 18:27:56 -04:00
fabro-util Neutralize runtime for_each labels before display 2026-07-28 19:52:59 -04:00
fabro-vault refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00