diff --git a/README.md b/README.md index 8c29c23..795635b 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Using these rules, everyone can build their own copy of Bantam from scratch in l ### Agentic loop (`AL(cfg, messages)`) function 1. Call OpenAI-compatible Completions API (`POST {endpoint}/chat/completions`) forwarding relevant parameters from `cfg` (`model`, `temperature`, `stream`, `reasoning_effort`, etc., excluding internal agent configs like `endpoint`, `api_key`, `timeout`, `shell_timeout`, `max_al_iterations`, `color`, `context_window`) and optional `api_key` bearer header. - - Set custom request headers. If the endpoint host is `opencode.ai` (or a subdomain), send the OpenCode Zen header set: `User-Agent: opencode/1.18.31 ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.14`, `x-opencode-client: cli`, `x-opencode-project: `, `x-opencode-session: `, and a fresh `x-opencode-request: msg_<26 base32 chars>` per call. For every other endpoint, keep the legacy `User-Agent: opencode/1.18.31` plus `X-Session-Id` and `x-session-affinity`. The `Authorization: Bearer ` header is added whenever `api_key` is set and not `-`. + - Set custom request headers. If the endpoint host is `opencode.ai` (or a subdomain), send the OpenCode Zen header set: `User-Agent: opencode/1.18.31 ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.14`, `x-opencode-client: cli`, `x-opencode-project: global`, `x-opencode-session: `, and a fresh `x-opencode-request: msg_<26 base32 chars>` per call. For every other endpoint, keep the legacy `User-Agent: opencode/1.18.31` plus `X-Session-Id` and `x-session-affinity`. The `Authorization: Bearer ` header is added whenever `api_key` is set and not `-`. - Retry network/HTTP errors with Fibonacci backoff delays (`1s, 1s, 2s, 3s, 5s, 8s, 13s, 21s, 34s`). - If `stream=true`, parse SSE stream (`data: {...}`) for real-time reasoning and text output, bracketing reasoning with `--- reasoning start ---` / `--- reasoning end ---` markers. 2. Append the assistant's response message object to `messages`. If non-streaming and response has reasoning tokens (`reasoning_content` or `reasoning`), output them wrapped in `--- reasoning start ---` / `--- reasoning end ---` markers. diff --git a/main.go b/main.go index b53cd4d..a3530a3 100644 --- a/main.go +++ b/main.go @@ -90,6 +90,7 @@ var defCfg = Cfg{"https://api.kilo.ai/api/openrouter", "openrouter/free", "-", 0 const opencodeAgentVersion = "opencode/1.18.31" const opencodeProviderUA = "ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.14" const opencodeIDAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" +const opencodeProjectID = "global" func atoiD(s string, d int) int { if v, e := strconv.Atoi(strings.TrimSpace(s)); e == nil { @@ -1721,7 +1722,7 @@ func applyLLMHeaders(req *http.Request, cfg *Cfg, msgID string) { if isOpencodeEndpoint(cfg.Endpoint) { req.Header.Set("User-Agent", opencodeAgentVersion+" "+opencodeProviderUA) req.Header.Set("x-opencode-client", "cli") - req.Header.Set("x-opencode-project", projectID()) + req.Header.Set("x-opencode-project", opencodeProjectID) req.Header.Set("x-opencode-session", sid) req.Header.Set("x-opencode-request", msgID) } else { diff --git a/main_test.go b/main_test.go index 91cc7d6..2f4c59b 100644 --- a/main_test.go +++ b/main_test.go @@ -1254,8 +1254,8 @@ func TestApplyLLMHeadersOpencode(t *testing.T) { if got := req.Header.Get("x-opencode-client"); got != "cli" { t.Errorf("x-opencode-client = %q, want cli", got) } - if got := req.Header.Get("x-opencode-project"); got != projectID() { - t.Errorf("x-opencode-project = %q, want %q", got, projectID()) + if got := req.Header.Get("x-opencode-project"); got != "global" { + t.Errorf("x-opencode-project = %q, want global", got) } if got := req.Header.Get("x-opencode-session"); got != opencodeSessionID() { t.Errorf("x-opencode-session = %q, want %q", got, opencodeSessionID()) @@ -1271,6 +1271,26 @@ func TestApplyLLMHeadersOpencode(t *testing.T) { } } +func TestApplyLLMHeadersRequestID(t *testing.T) { + cfg := defCfg + cfg.Endpoint = "https://opencode.ai/zen/v1" + cfg.APIKey = "-" + req, _ := http.NewRequest("POST", "https://opencode.ai/zen/v1/chat/completions", nil) + applyLLMHeaders(req, &cfg, ocRequestID()) + got := req.Header.Get("x-opencode-request") + if !strings.HasPrefix(got, "msg_") { + t.Errorf("x-opencode-request = %q, want msg_ prefix", got) + } + if len(got) != 30 { + t.Errorf("x-opencode-request length = %d, want 30: %q", len(got), got) + } + for _, c := range got[4:] { + if !strings.ContainsRune(opencodeIDAlphabet, c) { + t.Errorf("invalid character %c in x-opencode-request %q", c, got) + } + } +} + func TestApplyLLMHeadersOpencodeAuth(t *testing.T) { cfg := defCfg cfg.Endpoint = "https://opencode.ai/zen/v1"