Skip to content

feat(desktop): NIP-AM agent-usage backend — P2 emission/transport/archive + P4a aggregation/D6 - #4000

Open
wpfleger96 wants to merge 1 commit into
mainfrom
duncan/agent-usage-backend
Open

feat(desktop): NIP-AM agent-usage backend — P2 emission/transport/archive + P4a aggregation/D6#4000
wpfleger96 wants to merge 1 commit into
mainfrom
duncan/agent-usage-backend

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jul 31, 2026

Copy link
Copy Markdown
Member

What

Implements Phases 2 and 4a of the Usage v2 plan (plan events d0268cd0/0e95b035), extending the archive backend to emit, transport, archive, and aggregate both cache categories and billing identity fail-closed.

P2 — emission, transport, archive

Tri-state accumulators (Unseen/Exact/Unknown) for cache-read and cache-write in buzz-agent turn and session state. Absent field = Unknown (never zero) through the full pipeline. Kills the unwrap_or(0) pattern.

Conditional wire emission for accumulatedCachedInputTokens and new accumulatedCacheWriteTokens: fields are omitted when the cumulative is Unseen or Unknown. ACP _goose/unstable/session/update contract documented next to the payload with tests for all absence/zero variants.

PricingIdentity stamping (publisher-side):

  • pricing_authority(): canonical parsed-URL endpoint comparison against the official allowlist — HTTPS only, exact allowlisted host (lookalike-safe), default port, required API base path, rejects userinfo/query/fragment/path-prefix lookalikes. Wire token is the NIP-AM bare-host registry identifier (api.anthropic.com, api.openai.com, openrouter.ai).
  • Model: the actually-requested request_model after mesh/auto resolution (not effective_model_str).
  • Turn discipline: identity retained only while ALL usage in the current turn carries one identical proven identity; any mismatch, unproven-usage-bearing response, or unpaired cumulative snapshot poisons to absent; a later matching notification does not heal a mixed turn.

M3 migration: adds turn_cache_write_tokens, cumulative_cache_write_tokens, pricing_authority, pricing_model, pricing_cache_class to agent_metric_index. Additive, idempotent, guarded by marker. Fresh-DB schema includes all M3 columns.

P4a — aggregation layer

Extended S-1 ladder to cache-read and cache-write via the same ladder_token path as the existing token fields.

freshInputTokens derivation: checked arithmetic, fail-closed — absent cache fields produce Unknown (not zero), overflow and cacheRead+cacheWrite > input both produce incomplete: true. Aggregated as a UsageField.

D6 comparator: sort_value() = provider total when known, else input+output when both known, else None (unknown-last). Replaces the prior total-only comparator for both agent-level and model-level sort. Ships a pinned test vector that the TS render layer (P5) must match.

Test coverage

  • buzz-agent: 394 tests (pricing_authority, turn discipline, cache tri-state, wire emission)
  • buzz-acp: 9 tests (identity threading, cache fields)
  • Desktop (Rust): 2256 tests — includes 14 new P4a pinned tests (agent_usage_p4a_tests.rs): cache ladder, fresh-input fail-closed cases (missing slice, overflow, subset > input), D6 comparator vector

Related PRs

@wpfleger96
wpfleger96 requested a review from a team as a code owner July 31, 2026 20:05
@wpfleger96
wpfleger96 force-pushed the duncan/agent-usage-backend branch from 1613f34 to 12f1f29 Compare July 31, 2026 20:53
@wpfleger96
wpfleger96 force-pushed the duncan/agent-usage-backend branch 2 times, most recently from 635b030 to 39e1a91 Compare August 3, 2026 18:56
@wpfleger96
wpfleger96 force-pushed the duncan/agent-usage-backend branch from 39e1a91 to 66a09a8 Compare August 4, 2026 01:03
@wpfleger96 wpfleger96 changed the title feat(desktop): NIP-AM agent-usage archive backend + cache-read schema feat(desktop): NIP-AM agent-usage backend — P2 emission/transport/archive + P4a aggregation/D6 Aug 4, 2026
…) + P4a (aggregation/D6)

P2 — cache-write + pricing-identity emission, transport, and archive:

- buzz-agent: tri-state accumulators (Unseen/Exact/Unknown) for cache-read
  and cache-write. Kills unwrap_or(0) pattern from lib.rs; absent field is
  Unknown, not zero, through the full pipeline.
- buzz-agent/wire: conditional wire emission for accumulatedCachedInputTokens
  and new accumulatedCacheWriteTokens; skip when cumulative is Unseen or
  Unknown. ACP contract documented next to the payload.
- buzz-agent/config: pricing_authority() — canonical URL → bare-host registry
  token (api.anthropic.com, api.openai.com, openrouter.ai). HTTPS only, exact
  host, default port, required path where applicable, rejects lookalikes,
  userinfo, query, fragment.
- buzz-agent/agent: PricingIdentity turn discipline — identity retained only
  while all usage in the current turn carries one identical proven identity;
  mismatch, unproven-usage-bearing response, or unpaired cumulative poisons to
  absent; a later matching notification does not heal a mixed turn.
- buzz-acp/usage: pricing_identity field on TurnUsage and UsageUpdatePayload.
- buzz-core/agent_turn_metric: PricingIdentity and cache-write fields.
- M3 migration (desktop/src-tauri): adds turn_cache_write_tokens,
  cumulative_cache_write_tokens, pricing_authority, pricing_model,
  pricing_cache_class to agent_metric_index. Additive, idempotent,
  guarded by marker. Fresh-DB schema includes all M3 columns.

P4a — aggregation layer (agent_usage.rs):

- Extended S-1 ladder to cache-read and cache-write via the same
  ladder_token path as input/output/total.
- derive_fresh_input: checked arithmetic, fail-closed — absent cache
  fields produce Unknown (not zero), overflow and subset > input both
  produce incomplete. Aggregated as a UsageField.
- D6 comparator: sort_value() = provider total when known, else
  input+output when both known, else None (unknown-last). Replaces the
  prior total-only comparator for both agent-level and model-level sort.
- Tests split into agent_usage_tests.rs (existing) and
  agent_usage_p4a_tests.rs (14 new pinned tests: cache ladder, fresh-input
  fail-closed cases, D6 comparator vector) to stay under the 1000-line
  ratchet.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/agent-usage-backend branch from 66a09a8 to a67062a Compare August 4, 2026 01:26
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