feat(buzz-acp): add goose usage adapter for NIP-AM turn metrics#1446
Draft
wpfleger96 wants to merge 3 commits into
Draft
feat(buzz-acp): add goose usage adapter for NIP-AM turn metrics#1446wpfleger96 wants to merge 3 commits into
wpfleger96 wants to merge 3 commits into
Conversation
…e 2 Task B) Advertise `clientCapabilities._meta.goose.customNotifications: true` at initialize so goose emits `_goose/unstable/session/update` notifications carrying session-cumulative token counts at turn completion. Add `GooseUsageTracker` (new `goose_usage.rs`) that: - Deserializes the `_goose/unstable/session/update` wire payload - Stores per-session cumulative state (`sessionId`, `turnSeq`, last snapshot) - Computes per-turn deltas per NIP-AM rules: first-turn no-prior → null + deltaReliable:false; counter decrease → null + false; session restart (new sessionId) → treated as first turn - Exposes a `GooseTurnUsage` record via `take()` for consumption by the TurnCompletionGuard emit hook (sequential next task) Wire both dispatch arms (`read_until_response` and `read_until_response_with_idle_timeout`) to handle the new method, mirroring the existing `session/update` pattern. Non-goose harnesses are unaffected: no capability advertised, no dispatch, no state kept. References #1441 (NIP-AM spec) Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…unreliable gap Two Thufir-flagged IMPORTANT fixes for PR #1446. Turn scoping (setup usage misattributed to zero-update turn): - Add in_flight_session: Option<String> field to GooseUsageTracker. - Add begin_turn(session_id) method: sets in_flight_session and clears pending. Must be called before session/prompt is sent. - record() now only sets pending when in_flight_session matches session_id. It ALWAYS updates the sessions baseline so the next real turn gets a correct delta even from setup notifications. - take() clears in_flight_session after draining pending. - Call goose_usage.begin_turn(session_id) at the top of session_prompt_blocks_with_idle_timeout, before sending the prompt. - Setup notifications that arrive during session/new now correctly update the baseline without polluting the first real turn's pending record. - New tests: setup_notification_before_begin_turn_returns_none (verifies baseline still feeds next delta), record_outside_in_flight_does_not_ clobber_pending. Cost counter decrease -> deltaReliable:false (Fix 2): - When both snapshots have cost and current_cost < prev_cost, the computed delta would be negative — NIP-AM requires delta_reliable: false and all turn fields nulled (same as token-decrease path). - The match arm now returns (None, false) for cost decrease; the outer if/else then overrides delta_reliable=false and nulls turn_input/output. - Cost merely absent on either side stays as-is (null cost, reliable tokens). - turn_seq still increments on cost-decrease turns (Thufir-endorsed). - New tests: cost_decrease_sets_delta_unreliable_and_nulls_all_turn_fields, cost_absent_on_one_side_leaves_tokens_reliable. Existing goose_usage unit tests and acp.rs integration tests updated to call begin_turn() before record(), matching the real call flow. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Pure formatting pass — no logic changes. Fixes just fmt-check failure in CI (Rust Lint job 84654119247). Line-length wrapping in acp.rs and goose_usage.rs (record signature, assert! calls). Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the Goose harness adapter for NIP-AM agent turn metrics (kind 44200). This is Task B of Phase 2; Task A (kind/relay plumbing) is #1445.
When a Goose agent session is active and the client advertises
clientCapabilities._meta.goose.customNotifications: true, Goose emits a_goose/unstable/session/updatenotification at the end of every turn. This PR captures those notifications, computes per-turn deltas, and exposes the result viaAcpClient::take_turn_usage()forTurnCompletionGuard(Phase 3).Changes
crates/buzz-acp/src/goose_usage.rs(new)Wire types:
GooseSessionUpdateNotification— top-level params withsessionIdandupdateGooseSessionUpdateVariant— discriminated union;UsageUpdateorOther(unknown variants ignored)GooseUsageUpdatePayload—accumulatedInputTokens,accumulatedOutputTokens,accumulatedCostTracker state:
GooseUsageTracker— per-session baseline map + turn-scoped in-flight stateSessionState— cumulative snapshots and monotonicturn_seqper sessionGooseTurnUsage— per-turn record returned toTurnCompletionGuardTurn lifecycle:
begin_turn(session_id)— marks the tracker in-flight beforesession/promptis sent; clears leftover pending; ensures setup notifications (fired duringsession/new) update the baseline but do NOT produce a publishable record for the first real turnrecord(session_id, payload)— always advances the cumulative baseline; only setspendingwhenin_flight_sessionmatchestake()— drains pending and clears the in-flight markerDelta computation (NIP-AM compliant):
delta_reliable: false, null turn fields, cumulative populateddelta_reliable: false, null turn fields (no negative deltas)delta_reliable: false, null ALL turn fields (not just cost)session_id): treated as first turncrates/buzz-acp/src/acp.rsclientCapabilities._meta.goose.customNotifications: trueinsession/newinitializehandle_goose_usage_update()— parses_goose/unstable/session/updatenotifications and callsgoose_usage.record()take_turn_usage()— public method forTurnCompletionGuardto drain the per-turn recordsession_prompt_blocks_with_idle_timeoutcallsgoose_usage.begin_turn(session_id)before sending the promptDelta computation edge cases
delta_reliablefalsefalsefalsetruetrueRelated
docs/nips/NIP-AM.md, Thufir pass-2 cleared)duncan/nip-am-kind-relay)