Repository navigation
Name the session's cause on each model request in external mode - #516
Merged
pufit merged 6 commits intoOct 17, 2026
Merged
Conversation
This was referenced Oct 17, 2026
alex-clickhouse
added this pull request to stack #521
October 17, 2026 07:20
In external mode, a proxy in front of the model API can attribute model usage to the person who caused it, if each request names that person. The cause of all agent work in a session is the session's cause: the person who started it, the system actor for a session that Nerve started for itself, or none when it is unknown. The CLI process of a session now gets ANTHROPIC_CUSTOM_HEADERS with one "X-Nerve-Inference-Principal: <actor ID>" line for the session's cause. Other inherited header lines stay; an inherited principal line is replaced. A NULL cause and local mode send no header. The CLI reads the variable from its environment, which is fixed when the process starts. The session's cause never changes, so the process keeps its header for its whole life: a turn of another person in the session names the session's cause too, and a client rebuilt after a model change or a crash names the same principal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nerve also calls the model API directly: session titles, prompt rewrite, source condensing, memory, setup checks and the model catalogue. In external mode these requests now carry X-Nerve-Inference-Principal with the system actor ID, so a proxy in front of the model API attributes them to the agent's own principal instead of to no principal. The client factories in NerveConfig add the header as a default header. They read the system actor from the database that the server opened (nerve.db.global_system_actor_id), so a re-keyed system actor gives its new ID and a process that opened no database sends no header. memU's Bedrock adapter gets the same header. memU's OpenAI-compatible clients are built inside memU from a configuration that takes no headers, so their requests stay without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operators of a proxy in front of the model API need to know what the header says, when Nerve sends it, which requests have none, and that Nerve does not prove the claim. The external mode section of docs/config.md describes it: the model requests of a session name the session's cause for the life of the session, and Nerve's own model calls name the system actor. docs/accounts.md links to it and says that the shared-session approximation of the session cause applies to model usage too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both the Claude CLI and the Anthropic Python SDK add the lines of ANTHROPIC_CUSTOM_HEADERS from the environment to every request. In local mode Nerve left that value alone, so a principal line in Nerve's own environment reached the model API, although local mode sends no principal. In external mode, Nerve's own clients sent such a line beside the system actor's header when its name had other letter case, which a proxy reads as a repeated header. The CLI environment now drops every principal line (any case) in both modes and keeps the other custom headers. Nerve's own clients set anthropic.omit for each inherited principal name, and in external mode add the system actor's header once. Tests check the environment that the SDK gives the CLI process and the headers that a direct request carries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`nerve doctor` sends a real model request to check the API, but it opens no server database, so the request named no principal in external mode and its cost was stored without one. The Anthropic client factories take an optional principal_id; None keeps the system actor of the server's database. The doctor reads the stored system actor read-only from nerve.db (new read_system_actor_id) when NERVE_AUTH_MODE in its shell is external, and passes it. In local mode, with an invalid mode or without a database, the probe names none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nerve's own model clients took the system actor only from the database that the server opens. A CLI command such as `nerve sync` or `nerve migrate-openclaw` opens its own connection instead, so in external mode its source condensing and memory requests named no principal. When the server's database is not open in the process, the client factories now read the stored system actor read-only from nerve.db (the lookup that the doctor probe uses). Tests send the condensing request of `nerve sync` through its own connection to a local stand-in of the Messages API, and pin that an inherited line already naming the principal gives no override. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alex-clickhouse
force-pushed
the
alex/attr-02-inference-principal
branch
from
October 17, 2026 11:42
1aebcdf to
2029c13
Compare
alex-clickhouse
marked this pull request as ready for review
October 17, 2026 12:06
pufit
approved these changes
Oct 17, 2026
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.
What
In external mode, every model request that Nerve sends names the principal that caused the work, in one
X-Nerve-Inference-Principalheader. A proxy in front of the model API can use the header to attribute usage to a person. Nerve does not prove the claim; the proxy checks it.How
ANTHROPIC_CUSTOM_HEADERSin its environment.nerve doctormodel check. They go through the client factories inNerveConfig. A CLI command such asnerve syncornerve doctorreads the stored system actor read-only fromnerve.db.nerve/inference_principal.py. Docs are indocs/config.mdanddocs/accounts.md.Why
Inference is the main cost of a hosted agent. With the session's cause on each request, usage can be attributed to the person who started the work, and autonomous work to the agent's own principal.
Approximation
In a session that several people use, all model usage is attributed to the person who started the session. The agent's other work in the session follows the same rule.
Not covered
memU's OpenAI-compatible calls and Codex sessions.
Testing
tests/test_inference_principal.py:nerve doctorandnerve syncpaths are checked against a local stub.Stack
main)Independent: #514 (system actor takes the seeded system principal ID).
🤖 Generated with Claude Code