Skip to content

feat(provider): link each model call to its Ace charge - #958

Merged
KB (KB-syntheticsciences) merged 8 commits into
mainfrom
feat/roadmap
Oct 10, 2026
Merged

KB (KB-syntheticsciences) merged 8 commits into
mainfrom
feat/roadmap

Conversation

@KB-syntheticsciences

Copy link
Copy Markdown
Collaborator

What does this PR do, and why?

Links every model call the CLI makes to the Ace (Atlas) charge it caused, so developers can audit "what did this task cost?" per call. Roadmap step 1 (run audit) and #4 (receipts on calls).

  • Every HTTP attempt gets a client-minted call id (the fetch watchdog's timing requestID). Managed (Ace) requests send x-openscience-call, -message, -attempt and -session; non-managed requests send none.
  • The call id and Atlas's x-openscience-hold-id (validated ^(hold|orgh)_…) travel on each step's own response, so a step can never pick up the next step's call. Step-finish parts save usage.call / usage.hold (optional fields; old sessions still parse) and model.response traces carry http_call_id / hold.
  • Also fixes Settings → Usage returning 500 when a saved session or message record has no id (pre-existing; it also made settings-usage tests fail depending on test order).

Companion Atlas PR (link table, hold-id header, reasoning tokens on the ledger, audit by session): https://github.com/synthetic-sciences/atlas/pull/380

Linked issue

No issue; roadmap work (call → charge link, phase 1). Design spec available on request.

How did you verify it?

  • New tests: test/provider/call-link.test.ts (headers only on managed, hold validation, spoofing, retries), test/session/call-link-step.test.ts (real streamText, lagging consumer: each step keeps its own call and hold), usage-logging and call-link-part tests, settings-usage test for records without an id.
  • bun run typecheck green; SDK regenerated (no further diff). Full backend suite: every remaining failure also fails on main or is timing-flaky there.
  • End to end against local Atlas on Postgres with real upstreams (OpenRouter and direct Anthropic): multi-step, subagent, compaction, client killed mid-stream, idempotent retry. Every step's call/hold matched Atlas's link, hold and charge; no leaked holds.

Checklist

  • bun run check is green — format and typecheck green; backend suite failures all pre-existing on main (see above)
  • ./tooling/repo/generate.ts was run and the tooling/sdk output committed
  • CHANGELOG.md has an Unreleased entry (Settings → Usage fix; the call link is developer-facing)
  • No version bumps

In multi-step tool streams the SDK can start the next step's fetch while
finish-step awaits the snapshot, so the step could save the next call and
hold. Capture the call first in both the processor and onStepFinish.
The call identity sat in a mutable slot on the shared request context, so
in a multi-step stream the next step's fetch could overwrite it before the
consumer handled the previous finish-step. The fetch wrapper now stamps the
call id (and, on the managed route, the validated hold) onto the response
it returns; the processor and onStepFinish read the step's own response
headers. Non-managed responses never carry a hold.

The trace key for the HTTP attempt becomes http_call_id so it no longer
collides with the tool-call call_id on tool.* events.
Local usage listed storage under a record's id without checking it, so a
legacy or corrupt session or message file with no id made Settings → Usage
fail with a 500. Such records have nothing to list; skip them.
@vercel

vercel Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
openscience Ready Ready Preview Oct 10, 2026 7:36am UTC

Request Review

@KB-syntheticsciences
KB (KB-syntheticsciences) merged commit 402a47e into main Oct 10, 2026
10 checks passed

This branch was successfully deployed

1 active deployment
Preview — f477720a Deployed Oct 10, 2026 by vercel[bot]
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