diff --git a/internal/harnesses/claude.go b/internal/harnesses/claude.go index 16dc357..933051b 100644 --- a/internal/harnesses/claude.go +++ b/internal/harnesses/claude.go @@ -15,9 +15,10 @@ import ( type claudeSettings struct { Env struct { - AnthropicBaseURL string `json:"ANTHROPIC_BASE_URL"` - AnthropicAuthToken string `json:"ANTHROPIC_AUTH_TOKEN"` - AnthropicModel string `json:"ANTHROPIC_MODEL"` + AnthropicBaseURL string `json:"ANTHROPIC_BASE_URL"` + AnthropicAuthToken string `json:"ANTHROPIC_AUTH_TOKEN"` + AnthropicModel string `json:"ANTHROPIC_MODEL"` + AnthropicCustomHeaders string `json:"ANTHROPIC_CUSTOM_HEADERS,omitempty"` } `json:"env"` } @@ -120,6 +121,13 @@ func (c *ClaudeHarness) configureMerge(opts ConfigureOptions) error { return fmt.Errorf("failed to merge settings file: %w", err) } + env, ok := settings["env"].(map[string]any) + if !ok { + return fmt.Errorf("failed to merge settings file: env is not an object") + } + existingHeaders, _ := env["ANTHROPIC_CUSTOM_HEADERS"].(string) + env["ANTHROPIC_CUSTOM_HEADERS"] = withRequestyClientHeaders(existingHeaders) + if err := backupAndWriteConfigFileAsJSON(settingsPath, &settings); err != nil { return fmt.Errorf("failed to write settings file: %w", err) } @@ -132,6 +140,7 @@ func (c *ClaudeHarness) configureOverwrite(opts ConfigureOptions) error { settings.Env.AnthropicBaseURL = c.config.RouterBaseURL settings.Env.AnthropicAuthToken = c.config.APIKey settings.Env.AnthropicModel = opts.Model + settings.Env.AnthropicCustomHeaders = withRequestyClientHeaders("") if err := backupAndWriteConfigFileAsJSON(c.settingsPath(), &settings); err != nil { return fmt.Errorf("failed to write settings file: %w", err) @@ -189,6 +198,7 @@ func (c *ClaudeHarness) Launch(opts LaunchOptions) error { env["ANTHROPIC_BASE_URL"] = c.config.RouterBaseURL env["ANTHROPIC_API_KEY"] = c.config.APIKey env["REQUESTY_API_KEY"] = c.config.APIKey + env["ANTHROPIC_CUSTOM_HEADERS"] = withRequestyClientHeaders(env["ANTHROPIC_CUSTOM_HEADERS"]) delete(env, "ANTHROPIC_AUTH_TOKEN") // The fast-mode check calls an Anthropic-only org endpoint that a gateway // cannot answer; skipping it avoids a startup warning. @@ -256,3 +266,24 @@ func hasAnyFlag(args []string, flags []string) bool { return false } + +// withRequestyClientHeaders adds the Requesty client headers to headers, a +// newline-separated list of `Name: Value` lines as ANTHROPIC_CUSTOM_HEADERS +// expects, replacing any earlier values and keeping the user's other headers. +func withRequestyClientHeaders(headers string) string { + lines := []string{} + for _, line := range strings.Split(headers, "\n") { + name, _, _ := strings.Cut(line, ":") + name = strings.TrimSpace(name) + if strings.TrimSpace(line) == "" || strings.EqualFold(name, requestyClientHeader) || strings.EqualFold(name, requestyClientVersionHeader) { + continue + } + lines = append(lines, line) + } + lines = append(lines, + requestyClientHeader+": "+requestyClientName, + requestyClientVersionHeader+": "+requestyClientVersion(), + ) + + return strings.Join(lines, "\n") +} diff --git a/internal/harnesses/claude_test.go b/internal/harnesses/claude_test.go index 7d858f7..dc51887 100644 --- a/internal/harnesses/claude_test.go +++ b/internal/harnesses/claude_test.go @@ -59,7 +59,36 @@ func TestClaudeHarnessConfigureCreatesMissingConfig(t *testing.T) { "env": { "ANTHROPIC_BASE_URL": "https://router.requesty.ai", "ANTHROPIC_AUTH_TOKEN": "my-api-key", - "ANTHROPIC_MODEL": "anthropic/claude-fable-5" + "ANTHROPIC_MODEL": "anthropic/claude-fable-5", + "ANTHROPIC_CUSTOM_HEADERS": "X-Requesty-Client: requesty-cli\nX-Requesty-Client-Version: dev" + } + }`, string(settings)) +} + +func TestClaudeHarnessConfigureKeepsCustomHeaders(t *testing.T) { + config := config.Config{ + RouterBaseURL: "https://router.requesty.ai", + APIKey: "my-api-key", + } + configDir := t.TempDir() + settingsPath := filepath.Join(configDir, "settings.json") + require.NoError(t, os.WriteFile(settingsPath, []byte(`{ + "env": {"ANTHROPIC_CUSTOM_HEADERS": "X-Team: platform\nX-Requesty-Client-Version: 0.0.1\nX-Requesty-Client: requesty-cli"} + }`), 0o600)) + harness := newClaudeHarness(config, configDir) + + require.NoError(t, harness.Configure(ConfigureOptions{ + Model: "anthropic/claude-fable-5", + })) + + settings, err := os.ReadFile(settingsPath) + require.NoError(t, err) + assert.JSONEq(t, `{ + "env": { + "ANTHROPIC_BASE_URL": "https://router.requesty.ai", + "ANTHROPIC_AUTH_TOKEN": "my-api-key", + "ANTHROPIC_MODEL": "anthropic/claude-fable-5", + "ANTHROPIC_CUSTOM_HEADERS": "X-Team: platform\nX-Requesty-Client: requesty-cli\nX-Requesty-Client-Version: dev" } }`, string(settings)) } diff --git a/internal/harnesses/codex.go b/internal/harnesses/codex.go index d09e986..fb27506 100644 --- a/internal/harnesses/codex.go +++ b/internal/harnesses/codex.go @@ -145,7 +145,9 @@ func (c *CodexHarness) configureMerge(opts ConfigureOptions) error { "name": "Requesty", "base_url": fmt.Sprintf("%s/v1", c.config.RouterBaseURL), "http_headers": map[string]any{ - "X-Title": "OpenAI Codex", + "X-Title": "OpenAI Codex", + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), }, "auth": map[string]any{ "command": "requesty", @@ -183,7 +185,9 @@ func (c *CodexHarness) configureOverwrite(opts ConfigureOptions) error { Name: "Requesty", BaseURL: fmt.Sprintf("%s/v1", c.config.RouterBaseURL), HTTPHeaders: map[string]string{ - "X-Title": "OpenAI Codex", + "X-Title": "OpenAI Codex", + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), }, Auth: codexProviderAuth{ Command: "requesty", @@ -232,6 +236,8 @@ func (c *CodexHarness) Launch(opts LaunchOptions) error { "-c", provider + ".name=" + tomlString("Requesty"), "-c", provider + ".base_url=" + tomlString(c.config.RouterBaseURL+"/v1"), "-c", provider + ".http_headers.X-Title=" + tomlString("OpenAI Codex"), + "-c", provider + ".http_headers." + requestyClientHeader + "=" + tomlString(requestyClientName), + "-c", provider + ".http_headers." + requestyClientVersionHeader + "=" + tomlString(requestyClientVersion()), "-c", provider + ".auth.command=" + tomlString(requestyExecutable()), "-c", provider + ".auth.args=[" + tomlString("auth") + "," + tomlString("token") + "," + tomlString("--profile") + "," + tomlString(c.config.Name) + "]", "-c", "model_supports_reasoning_summaries=false", diff --git a/internal/harnesses/codex_test.go b/internal/harnesses/codex_test.go index f2c3f61..5643646 100644 --- a/internal/harnesses/codex_test.go +++ b/internal/harnesses/codex_test.go @@ -170,7 +170,9 @@ custom_provider_setting = "keep-me" "base_url": "https://router.requesty.ai/v1", "custom_provider_setting": "keep-me", "http_headers": map[string]any{ - "X-Title": "OpenAI Codex", + "X-Title": "OpenAI Codex", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev", }, "auth": map[string]any{ "command": "requesty", diff --git a/internal/harnesses/deepseek.go b/internal/harnesses/deepseek.go index a5cc771..ebc3baf 100644 --- a/internal/harnesses/deepseek.go +++ b/internal/harnesses/deepseek.go @@ -194,8 +194,10 @@ func (d *DeepSeekHarness) configureMerge(opts ConfigureOptions) error { "defaultContextWindow": deepseekContextWindow, "defaultMaxTokens": deepseekMaxTokens, "headers": map[string]any{ - "HTTP-Referer": "https://requesty.ai", - "X-Title": "DeepSeek Harness", + "HTTP-Referer": "https://requesty.ai", + "X-Title": "DeepSeek Harness", + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), }, "models": models, }, @@ -233,8 +235,10 @@ func (d *DeepSeekHarness) configureOverwrite(opts ConfigureOptions) error { DefaultContextWindow: deepseekContextWindow, DefaultMaxTokens: deepseekMaxTokens, Headers: map[string]string{ - "HTTP-Referer": "https://requesty.ai", - "X-Title": "DeepSeek Harness", + "HTTP-Referer": "https://requesty.ai", + "X-Title": "DeepSeek Harness", + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), }, Models: []deepseekModelConfig{ {ID: opts.Model}, diff --git a/internal/harnesses/deepseek_test.go b/internal/harnesses/deepseek_test.go index c46db82..5fb5697 100644 --- a/internal/harnesses/deepseek_test.go +++ b/internal/harnesses/deepseek_test.go @@ -71,8 +71,10 @@ llm-deepseek: "defaultContextWindow": 200000, "defaultMaxTokens": 8192, "headers": map[string]any{ - "HTTP-Referer": "https://requesty.ai", - "X-Title": "DeepSeek Harness", + "HTTP-Referer": "https://requesty.ai", + "X-Title": "DeepSeek Harness", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev", }, "models": []any{ map[string]any{"id": "deepseek/deepseek-v4-pro-0813"}, @@ -127,8 +129,10 @@ func TestDeepSeekHarnessConfigureCreatesMissingFiles(t *testing.T) { DefaultContextWindow: 200000, DefaultMaxTokens: 8192, Headers: map[string]string{ - "HTTP-Referer": "https://requesty.ai", - "X-Title": "DeepSeek Harness", + "HTTP-Referer": "https://requesty.ai", + "X-Title": "DeepSeek Harness", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev", }, Models: []deepseekModelConfig{ {ID: "anthropic/claude-fable-5"}, diff --git a/internal/harnesses/harnesses.go b/internal/harnesses/harnesses.go index d637b00..87a5269 100644 --- a/internal/harnesses/harnesses.go +++ b/internal/harnesses/harnesses.go @@ -1,9 +1,25 @@ package harnesses import ( + "strings" + "github.com/requestyai/cli/internal/config" + "github.com/requestyai/cli/internal/version" +) + +// requestyClientHeader and requestyClientVersionHeader are sent on every +// request from a harness the CLI set up or launched, so the router can tell +// CLI traffic apart from harnesses configured by hand. +const ( + requestyClientHeader = "X-Requesty-Client" + requestyClientVersionHeader = "X-Requesty-Client-Version" + requestyClientName = "requesty-cli" ) +func requestyClientVersion() string { + return strings.TrimPrefix(version.Version, "v") +} + type Status struct { Files []string Executable bool diff --git a/internal/harnesses/hermes.go b/internal/harnesses/hermes.go index 2fb3219..0c8d7ab 100644 --- a/internal/harnesses/hermes.go +++ b/internal/harnesses/hermes.go @@ -18,6 +18,9 @@ const ( // hermesAPIMode is the native Anthropic Messages format, which lets Requesty // apply automatic prompt caching to Hermes' large system prompt. hermesAPIMode = "anthropic_messages" + + hermesReferer = "https://hermes-agent.nousresearch.com" + hermesTitle = "Hermes Agent" ) type hermesConfig struct { @@ -26,16 +29,16 @@ type hermesConfig struct { } type hermesModel struct { - Default string `yaml:"default"` - Provider string `yaml:"provider"` - DefaultHeaders map[string]string `yaml:"default_headers,omitempty"` + Default string `yaml:"default"` + Provider string `yaml:"provider"` } type hermesCustomProvider struct { - Name string `yaml:"name"` - BaseURL string `yaml:"base_url"` - APIKey string `yaml:"api_key"` - APIMode string `yaml:"api_mode"` + Name string `yaml:"name"` + BaseURL string `yaml:"base_url"` + APIKey string `yaml:"api_key"` + APIMode string `yaml:"api_mode"` + ExtraHeaders map[string]string `yaml:"extra_headers,omitempty"` } type HermesHarness struct { @@ -243,10 +246,6 @@ func (h *HermesHarness) configureMerge(opts ConfigureOptions) error { "model": map[string]any{ "default": opts.Model, "provider": hermesProvider, - "default_headers": map[string]any{ - "HTTP-Referer": "https://hermes-agent.nousresearch.com", - "X-Origin-Title": "Hermes", - }, }, }) if err != nil { @@ -273,17 +272,14 @@ func (h *HermesHarness) configureOverwrite(opts ConfigureOptions) error { Model: hermesModel{ Default: opts.Model, Provider: hermesProvider, - DefaultHeaders: map[string]string{ - "HTTP-Referer": "https://hermes-agent.nousresearch.com", - "X-Origin-Title": "Hermes", - }, }, CustomProviders: []hermesCustomProvider{ { - Name: hermesProvider, - BaseURL: h.config.RouterBaseURL, - APIKey: h.config.APIKey, - APIMode: hermesAPIMode, + Name: hermesProvider, + BaseURL: h.config.RouterBaseURL, + APIKey: h.config.APIKey, + APIMode: hermesAPIMode, + ExtraHeaders: hermesExtraHeaders(), }, }, } @@ -298,11 +294,16 @@ func (h *HermesHarness) configureOverwrite(opts ConfigureOptions) error { // upsertCustomProvider replaces the Requesty entry in providers, appending it // when the user has not configured Requesty yet. func (h *HermesHarness) upsertCustomProvider(providers []any) []any { + extraHeaders := map[string]any{} + for key, value := range hermesExtraHeaders() { + extraHeaders[key] = value + } provider := map[string]any{ - "name": hermesProvider, - "base_url": h.config.RouterBaseURL, - "api_key": h.config.APIKey, - "api_mode": hermesAPIMode, + "name": hermesProvider, + "base_url": h.config.RouterBaseURL, + "api_key": h.config.APIKey, + "api_mode": hermesAPIMode, + "extra_headers": extraHeaders, } for i, existing := range providers { @@ -343,3 +344,14 @@ func hermesCustomProviders(settings map[string]any) ([]any, error) { func (h *HermesHarness) configPath() string { return filepath.Join(h.configDir, "config.yaml") } + +// hermesExtraHeaders go on the provider entry rather than in +// model.default_headers, which Hermes ignores in anthropic_messages mode. +func hermesExtraHeaders() map[string]string { + return map[string]string{ + "HTTP-Referer": hermesReferer, + "X-Title": hermesTitle, + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), + } +} diff --git a/internal/harnesses/hermes_test.go b/internal/harnesses/hermes_test.go index faa08ec..3c18dcb 100644 --- a/internal/harnesses/hermes_test.go +++ b/internal/harnesses/hermes_test.go @@ -56,10 +56,6 @@ custom_providers: "model": map[string]any{ "default": "anthropic/claude-fable-5", "provider": "requesty", - "default_headers": map[string]any{ - "HTTP-Referer": "https://hermes-agent.nousresearch.com", - "X-Origin-Title": "Hermes", - }, }, "custom_providers": []any{ map[string]any{ @@ -72,6 +68,12 @@ custom_providers: "base_url": "https://router.requesty.ai", "api_key": "my-api-key", "api_mode": "anthropic_messages", + "extra_headers": map[string]any{ + "HTTP-Referer": "https://hermes-agent.nousresearch.com", + "X-Title": "Hermes Agent", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev", + }, }, }, }, settings) @@ -107,7 +109,13 @@ func TestHermesHarnessUpdatesExistingProvider(t *testing.T) { "base_url": "https://router.eu.requesty.ai", "api_key": "my-new-api-key", "api_mode": "anthropic_messages", - "timeout": 300, + "extra_headers": map[string]any{ + "HTTP-Referer": "https://hermes-agent.nousresearch.com", + "X-Title": "Hermes Agent", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev", + }, + "timeout": 300, }, }, settings["custom_providers"]) @@ -139,10 +147,6 @@ func TestHermesHarnessConfigureCreatesMissingConfig(t *testing.T) { "model": map[string]any{ "default": "anthropic/claude-fable-5", "provider": "requesty", - "default_headers": map[string]any{ - "HTTP-Referer": "https://hermes-agent.nousresearch.com", - "X-Origin-Title": "Hermes", - }, }, "custom_providers": []any{ map[string]any{ @@ -150,6 +154,12 @@ func TestHermesHarnessConfigureCreatesMissingConfig(t *testing.T) { "base_url": "https://router.requesty.ai", "api_key": "my-api-key", "api_mode": "anthropic_messages", + "extra_headers": map[string]any{ + "HTTP-Referer": "https://hermes-agent.nousresearch.com", + "X-Title": "Hermes Agent", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev", + }, }, }, }, settings) diff --git a/internal/harnesses/merge_test.go b/internal/harnesses/merge_test.go index d9bcc12..116722f 100644 --- a/internal/harnesses/merge_test.go +++ b/internal/harnesses/merge_test.go @@ -79,7 +79,9 @@ custom_project_setting = "keep-me" codexModelProvider: map[string]any{ "base_url": "https://router.requesty.ai/v1", "http_headers": map[string]any{ - "X-Title": "OpenAI Codex", + "X-Title": "OpenAI Codex", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev", }, "auth": map[string]any{ "command": "requesty", @@ -102,8 +104,10 @@ custom_project_setting = "keep-me" "custom_provider_setting": "keep-me", "base_url": "https://router.requesty.ai/v1", "http_headers": map[string]any{ - "Existing": "keep-me", - "X-Title": "OpenAI Codex", + "Existing": "keep-me", + "X-Title": "OpenAI Codex", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev", }, "auth": map[string]any{ "custom_auth_setting": "keep-me", diff --git a/internal/harnesses/opencode.go b/internal/harnesses/opencode.go index c822ae5..c3db33b 100644 --- a/internal/harnesses/opencode.go +++ b/internal/harnesses/opencode.go @@ -169,7 +169,9 @@ func (o *OpenCodeHarness) inlineConfig(existing, model string) (string, error) { "baseURL": o.baseURL(), "apiKey": "{env:REQUESTY_API_KEY}", "headers": map[string]any{ - "X-Title": "OpenCode", + "X-Title": "OpenCode", + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), }, }, } @@ -256,7 +258,9 @@ func (o *OpenCodeHarness) configureMerge(opts ConfigureOptions) error { "baseURL": o.baseURL(), "apiKey": o.config.APIKey, "headers": map[string]any{ - "X-Title": "OpenCode", + "X-Title": "OpenCode", + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), }, }, }, @@ -283,7 +287,9 @@ func (o *OpenCodeHarness) configureOverwrite(opts ConfigureOptions) error { BaseURL: o.baseURL(), APIKey: o.config.APIKey, Headers: map[string]string{ - "X-Title": "OpenCode", + "X-Title": "OpenCode", + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), }, }, }, diff --git a/internal/harnesses/opencode_test.go b/internal/harnesses/opencode_test.go index a05cb43..75a5578 100644 --- a/internal/harnesses/opencode_test.go +++ b/internal/harnesses/opencode_test.go @@ -50,7 +50,7 @@ func TestOpenCodeHarnessRoundTrip(t *testing.T) { "options": { "baseURL": "https://router.requesty.ai/v1", "apiKey": "my-api-key", - "headers": {"X-Title": "OpenCode"} + "headers": {"X-Title": "OpenCode", "X-Requesty-Client": "requesty-cli", "X-Requesty-Client-Version": "dev"} } } } @@ -80,7 +80,7 @@ func TestOpenCodeHarnessConfigureCreatesMissingConfig(t *testing.T) { "options": { "baseURL": "https://router.requesty.ai/v1", "apiKey": "my-api-key", - "headers": {"X-Title": "OpenCode"} + "headers": {"X-Title": "OpenCode", "X-Requesty-Client": "requesty-cli", "X-Requesty-Client-Version": "dev"} } } } diff --git a/internal/harnesses/pi.go b/internal/harnesses/pi.go index f10bfcf..12d256b 100644 --- a/internal/harnesses/pi.go +++ b/internal/harnesses/pi.go @@ -261,6 +261,7 @@ func (p *PiHarness) Launch(opts LaunchOptions) error { env["REQUESTY_API_KEY"] = p.config.APIKey env["REQUESTY_BASE_URL"] = p.config.RouterBaseURL env["REQUESTY_PI_CATALOG"] = catalogPath + env["REQUESTY_CLIENT_VERSION"] = requestyClientVersion() argv := []string{"pi", "--extension", extensionPath} switch { @@ -398,8 +399,10 @@ func (p *PiHarness) configureMerge(opts ConfigureOptions) error { "api": piAPI, "apiKey": p.config.APIKey, "headers": map[string]any{ - "HTTP-Referer": "https://pi.dev", - "X-Title": "Pi", + "HTTP-Referer": "https://pi.dev", + "X-Title": "Pi", + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), }, "models": []any{ map[string]any{"id": opts.Model}, @@ -441,8 +444,10 @@ func (p *PiHarness) configureOverwrite(opts ConfigureOptions) error { API: piAPI, APIKey: p.config.APIKey, Headers: map[string]string{ - "HTTP-Referer": "https://pi.dev", - "X-Title": "Pi", + "HTTP-Referer": "https://pi.dev", + "X-Title": "Pi", + requestyClientHeader: requestyClientName, + requestyClientVersionHeader: requestyClientVersion(), }, Models: []piModelConfig{ {ID: opts.Model}, diff --git a/internal/harnesses/pi/requesty.ts b/internal/harnesses/pi/requesty.ts index 984003a..66c8900 100644 --- a/internal/harnesses/pi/requesty.ts +++ b/internal/harnesses/pi/requesty.ts @@ -5,6 +5,7 @@ // REQUESTY_API_KEY the key for this profile // REQUESTY_BASE_URL the router, without a /v1 suffix // REQUESTY_PI_CATALOG a JSON file listing the models the profile can use +// REQUESTY_CLIENT_VERSION the CLI version, sent as X-Requesty-Client-Version import { readFileSync } from "node:fs"; import type { ExtensionAPI, ProviderModelConfig } from "@earendil-works/pi-coding-agent"; @@ -47,6 +48,8 @@ export default function (pi: ExtensionAPI): void { headers: { "HTTP-Referer": "https://pi.dev", "X-Title": "Pi", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": trimmed(process.env.REQUESTY_CLIENT_VERSION) ?? "unknown", }, models: loadCatalog(trimmed(process.env.REQUESTY_PI_CATALOG)), }); diff --git a/internal/harnesses/pi_test.go b/internal/harnesses/pi_test.go index 7fb400c..c4d5369 100644 --- a/internal/harnesses/pi_test.go +++ b/internal/harnesses/pi_test.go @@ -60,7 +60,9 @@ func TestPiHarnessRoundTrip(t *testing.T) { "apiKey": "my-api-key", "headers": { "HTTP-Referer": "https://pi.dev", - "X-Title": "Pi" + "X-Title": "Pi", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev" }, "models": [ {"id": "anthropic/claude-fable-5"} @@ -104,7 +106,9 @@ func TestPiHarnessConfigureCreatesMissingConfig(t *testing.T) { "apiKey": "my-api-key", "headers": { "HTTP-Referer": "https://pi.dev", - "X-Title": "Pi" + "X-Title": "Pi", + "X-Requesty-Client": "requesty-cli", + "X-Requesty-Client-Version": "dev" }, "models": [ {"id": "anthropic/claude-fable-5"}