Attribute requests by repository, branch and user - #25
Open
Thibaultjaigu wants to merge 8 commits into
Open
Conversation
Each harness now has a single headers method feeding both the merge and
overwrite paths, spelling the dynamic dimensions the way that harness
resolves them: Codex reads environment variables, OpenCode expands
{env:...} placeholders, and Pi runs the shell command itself. The
DeepSeek Harness can only be given fixed values, so it carries the user
and leaves the repository and branch out rather than freezing them to
wherever the CLI happened to run.
Hermes only applies the default_headers from its config file when it speaks the OpenAI format. The Requesty integration uses the native Anthropic Messages format, so the block was never sent, and the title it named was X-Origin-Title, which is not a header Requesty reads.
The step sits between the model and the configuration mode, and starts on keeping requests unattributed: a repository name, a branch name and a username only leave the machine when the user asks for it. Choosing to attribute installs the shell hook the harnesses that read their environment depend on, and the list reports which startup file it went into, as the exported variables only reach a shell started afterwards.
A merge only ever set values, so headers written by an opted-in run stayed in a harness config after the user opted out again: a privacy feature that could not be turned off without overwriting the file. The merge now understands a removal patch value, and headerPatch marks every attribution header the current set does not carry, so configuring again sets what is asked for and removes the rest.
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.
Generalises the Claude-Code-only
curl -fsSL https://www.requesty.ai/claude/install.sh | bashheader-injection wrapper into native, per-harness request attribution in the CLI, so repo, branch
and user cost attribution works for every integration we support rather than one.
What it does
The wizard gains a step between model and merge/overwrite asking what requests are attributed to.
Opting in sends three headers, which Requesty turns into
extra.Repo,extra.Branchandextra.Userdimensions and strips before calling a provider:X-Requesty-Repooriginremote, without scheme or.gitsuffixX-Requesty-BranchX-Requesty-UserOff unless asked for. Repo and branch names are often private and branch names are unbounded
cardinality, so the step starts on
Keep requests unattributed.The hard part: setup time vs request time
The CLI runs once, from one directory. Harnesses run later, from anywhere. A repo or branch baked
into a config file at setup time is not stale, it is confidently wrong. So each harness resolves
them the only way it can:
gitcommand itself via!cmdenv_http_headersnames variables{env:VAR}placeholdersANTHROPIC_CUSTOM_HEADERSanthropic_messagesmodeOne managed hook covers all three env readers:
~/.requesty/shell/attribution.{sh,fish}plus amarker-delimited
sourceline in.zshrc/.bashrc/config.fish. It refreshes before each prompt,so a
git checkoutis picked up without a new shell. No wrapper functions, no per-harness shims —users keep typing
claudeandcodex.Two things worth a reviewer's attention
and one hook serves every harness, so opting out for one harness must not delete it. Removal is
manual and documented in the README. A
requesty attribution offcommand is the real answer ifwe want one.
extra.Branchis unbounded cardinality. Everyfeature/…branch anyone works on becomes adistinct dimension value. Worth checking with whoever owns the analytics store before this gets
popular.
Also in here
internal/fileioextracted fromharnesses, so the hook writer reuses the same atomic write,one-time
.requesty.bakbackup and0600behaviour.mergePatchonly ever set values, so attribution headers written byan opted-in run survived opting back out — a privacy feature you could not turn off without
overwrite mode. A removal sentinel fixes it:
headerPatchnow sets what is asked for and removesevery attribution header that is not.
default_headersis a documented no-op inanthropic_messagesmode, and the
X-Origin-Titleit set is not a header the router recognises (onlyX-Titleis).Verification
gofmt -l .silent,go build ./...,go vet ./...,go test ./...all pass. New tests coverevery harness through both the merge and overwrite paths, hand-added headers surviving both a write
and a removal, the wizard flow in each direction, and the generated hook sourced under a real
sh.🤖 Generated with Claude Code