hellogrok icon

# hellogrok A cross-platform local proxy that makes Grok Build custom model channels work with common API formats, native Web tools, isolated authentication, and automatic configuration recovery. [![Version](https://img.shields.io/badge/version-0.1.41-2f6feb.svg)](./internal/appinfo/appinfo.go) [![Go](https://img.shields.io/badge/Go-1.26.6-00ADD8.svg)](./go.mod) [![CI](https://github.com/hellowind777/hellogrok/actions/workflows/ci.yml/badge.svg)](https://github.com/hellowind777/hellogrok/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) [![Platforms](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)](#platform-support) [![LINUX DO](https://img.shields.io/badge/LINUX_DO-recognized-0A84FF?logo=linux&logoColor=white)](https://linux.do) [English](./README.md) · [简体中文](./README_CN.md) · [Release notes](./RELEASE_NOTES.md) · [Changelog](./CHANGELOG.md) > 🏅 This project is linked & recognized by the [LINUX DO](https://linux.do) community. ## Contents - [Why hellogrok](#why-hellogrok) - [Features](#features) - [Search and configuration](#search-and-configuration) - [DeepSeek](#deepseek) - [Download](#download) - [Quick start](#quick-start) - [Platform support](#platform-support) - [Tray and CLI](#tray-and-cli) - [Autostart](#autostart) - [How it works](#how-it-works) - [Troubleshooting](#troubleshooting) - [Development](#development) - [Limitations](#limitations) - [Contributing](#contributing) - [License](#license) ## Why hellogrok Grok Build can use custom model endpoints, but real-world providers do not all expose the same protocol, response shape, authentication method, tool-call names, or Web-search behavior. A channel that works with `curl` can still fail in a normal Grok Build conversation, lose native Web tools, call `List` instead of `list_dir`, emit an empty `finish_reason`, dump chain-of-thought after the answer, or receive the wrong login credential. Relayed grok-4.5/4.6 endpoints have the same problem when the relay rewrites the API as Chat Completions or Responses. hellogrok provides one local compatibility layer for those custom channels. It prepares the required Grok configuration while running, keeps each channel tied to its own endpoint and credentials, aligns Grok Build's local tools, subagents, client search, and MCP dispatch across Responses, Messages, and Chat Completions, and restores the original configuration when stopped. It is intended for users who maintain multiple third-party model channels — including Kimi, DeepSeek, GLM, and relayed official Grok models — and want to switch between them from Grok Build without manually rewriting URLs or changing tool configuration for every session. ### Non-goals - **Not a team gateway.** hellogrok is a single-user local proxy: no multi-tenancy, no request auditing, no rate governance. Its resilience semantics optimize for "one unattended conversation survives a relay blip", not for organization-wide failure observability. - **Not a strict-SLA client.** The `balanced` tier deliberately delays deterministic errors by up to the absorb window while it polls for relay-side recovery. When you are actively debugging a channel, keep the default `off` tier so rejections surface immediately. - **Not needed for first-party endpoints.** A direct, compliant official API gains nothing from the compatibility layer; connect to it without the proxy. ## Features [Release notes](./RELEASE_NOTES.md) | [Full Changelog](./CHANGELOG.md) ### Channel compatibility - Supports upstream channels that use `responses`, `messages`, or `chat_completions`. - Keeps the provider's configured upstream protocol, URL path, model, credential, reasoning, and tool semantics at the provider boundary. Ordinary Chat and Messages channels stay on Grok Build's first-party mapper and sampler. Responses projection is used only when hosted search requires it (`supports_backend_search = true` or Grok Build's WebSearchClient hop). - Gives every proxied custom channel a unique runtime model identity equal to its model-table ID. The configured `model` value remains the upstream wire model, while Grok Build persists the channel ID and normalized responses report the same ID. `/resume` therefore returns to the selected custom channel even when several channels and an official model all use `grok-4.6`. The original `model` line is restored byte-for-byte when the proxy stops. - When `supports_backend_search = true`, temporarily exposes the channel to Grok Build as Responses, then translates requests, responses, and SSE events to and from the provider's real protocol. Grok Build can therefore consume `web_search_call` across every supported upstream format, plus sources and site counts whenever the upstream returns real result URLs. - When `supports_backend_search = false`, keeps Grok Build on the configured native consumer so its client `web_search` uses `[models].web_search`, `GROK_WEB_SEARCH_MODEL`, or the authenticated official fallback. Omission preserves Grok Build catalog behavior for every provider, DeepSeek included: after DeepSeek retired its hosted web search (the Responses API now ignores `web_search` tools), an omitted field uses client search instead. An explicit false opts out unless that custom route is selected as the default search model, and an explicit true is still honored as declared. - Exposes channel-scoped `/responses`, `/messages`, and `/chat/completions` routes and restores the original `api_backend` byte-for-byte when the proxy stops. - Validates protocol tool history before forwarding: Responses calls require matching `function_call_output` items, Messages `tool_use` blocks require `tool_result` blocks in the immediately following user message, and Chat tool calls require matching tool messages. Deterministic failures return a non-retryable `400` instead of entering Grok Build's retry loop. - Converts provider-private `keepalive`, `keep-alive`, `keep_alive`, `heartbeat`, and `ping` frames into standard SSE comments before they reach Grok Build, without consuming Responses sequence numbers, and closes each upstream stream as soon as its protocol terminal event arrives. Terminal detection is per protocol, not per channel or model: Chat Completions `finish_reason`, Messages `stop_reason`/`message_stop`, and Responses `status` completed/failed/incomplete or collected output items. A relay that then omits `[DONE]`, `message_stop`, or `response.completed` and closes cleanly gets that trailer synthesized instead of a `proxy_stream_error` that would make Grok Build retry a completed turn. - Bounds ordinary upstream response-header waits and gaps between reads from every response body at 601 seconds, one second beyond Grok Build's 600-second shell default. First-party `api.deepseek.com` routes use 660 seconds to cover the provider's documented ten-minute queue. There is no total request deadline, and every upstream byte, including a non-streaming queue newline or normalized heartbeat, renews the idle window. - Temporarily materializes `inference_idle_timeout_secs = 900` for proxied channels that do not configure one (restored byte-for-byte on stop), so relays that emit heartbeats but no content while queued — stalls of 3+ minutes have been observed — no longer hit Grok Build's 600-second default content deadline; explicit per-model or global `[models]` values remain user-owned. Every streaming path also runs a downstream content watchdog that mirrors Grok Build's per-protocol content classification (text, reasoning, tool-call deltas, and terminal signals count; heartbeats, empty deltas, usage-only chunks, and lifecycle bookkeeping events do not) and fires one 30-second margin ahead of the client's own content timer. On fire it closes the upstream and emits a retryable `proxy_stream_error`, so a genuinely stalled stream re-enters Grok Build's native 15-attempt retry budget instead of dying on the client's non-retryable `IdleTimeout`. Explicit timeouts below 60 seconds disable the watchdog to respect fast-fail choices. - Absorbs transient upstream soft failures before Grok Build ever sees them. While the upstream response headers have not been sent, a retryable failure (busy/overloaded `503`, `429`, retryable `5xx`, or a response-header timeout) is retried inside the proxy with exponential backoff (2s to 30s, honoring an upstream `Retry-After` up to 60s) for up to 90 seconds by default. A Responses SSE whose only terminal so far is a retryable `response.failed` (rate limit, concurrency, overload) is withheld the same way — response headers and early frames stay buffered (up to 32 frames) so the retry does not become a second stream — and replayed inside the same window; `absorb_retry_max_secs = 0` streams that failed event instead. An upstream `408` is treated as what it actually is between two servers — an edge/gateway timeout page, semantically a `504` — so it enters the same window and, if the window is exhausted, passes through remapped to a retryable `504` instead of Grok Build's terminal `408` classification. Cloudflare origin-TLS failures (`525`, `526`) are absorbed the same way even though Grok Build classifies them terminal: a relay origin restart or certificate rotation routinely clears inside the window, and a request that failed the edge-origin TLS handshake never reached the origin application, so the replay is side-effect free; when the window is exhausted they pass through with the terminal disposition Grok Build expects. Grok Build's own 15-attempt retry budget stays untouched during the window: it only sees a failure when the window is exhausted, and even then the failure passes through as retryable, so the client keeps its full native budget. Transport errors are not absorbed because Grok Build's first-retry HTTP/1.1 client rebuild handles those better. Streaming failures after headers are sent are unaffected. `absorb_retry_max_secs = 0` disables the window per channel. - Never shortens Grok Build's native retry budget for soft failures. Only deterministic errors whose retries cannot succeed (authentication, permission, billing, quota, invalid request/model) receive `X-Should-Retry: false`, so Grok Build shows the provider explanation instead of hiding it behind repeated retries. The default passthrough disposition mirrors Grok Build's edge-client policy — `429` and every `5xx` except the origin-TLS codes are retryable — so a transient Cloudflare edge page (`520`–`524`, `529`, `530`) keeps the client's native retry instead of being vetoed by a proxy-stamped header. When a retryable busy/overloaded `503` passes through without an upstream `Retry-After`, the proxy adds a 30-second one so Grok Build paces its retries. - Absorbs soft failures for every channel, always: retryable `5xx`, `429`, upstream `408` edge-timeout pages, Cloudflare origin-TLS `525`/`526`, and response-header timeouts are retried inside the window because they clear on their own and Grok Build's retry of them only burns time. The global `[models]` `error_resilience` setting only controls deterministic 4xx failures (authentication, permission, invalid request/model): by default (`off`) they pass through immediately with the provider's explanation and `X-Should-Retry: false`, because retrying them cannot succeed; the opt-in `balanced` value additionally absorbs them inside the window with a fixed 30s-then-60s wait sequence, because relays routinely recover from those on their own — token rotation, permission fixes, provider config reloads, brief deploys — and an unattended turn survives a multi-minute outage instead of dying on the first response. A reasoning-history rejection is never absorbed: it reports a foreign conversation state the user must see, and hiding it would only delay the recovery. - Keeps an opt-in dead-channel breaker per channel (`dead_channel_fail_fast = true`, default off). Only dial-level failures — connection refused, DNS errors, TLS handshakes — count toward it, because those never succeed on retry; busy `503`, `429`, timeouts, and transport resets never do. After 6 consecutive dial-level failures (configurable via `dead_channel_fail_threshold`) the proxy answers a non-retryable `503 proxy_circuit_open` with `X-Should-Retry: false`. One probe request is allowed through after a 5-minute cooldown: any upstream response of any status closes the breaker automatically, a failed probe re-arms it. - Recognizes relays that answer a failure with HTTP 200 plus an error envelope, a common newapi/sub2api defect. A transient envelope (rate limit, overloaded, timeout) enters the absorb window and is retried inside the proxy; a deterministic envelope (authentication, billing, invalid request) passes through with the provider's explanation instead of an opaque envelope-validation rejection. A Responses terminal body that carries its own error member keeps its native failed-response path untouched. - Recovers from upstream vision rejections on text-only channels: when a channel answers `400` with a `vision` error code or a `not multimodal` / `does not support image` message, hellogrok replaces every image content part (`image_url`, `input_image`, `image`, including images inside tool results such as `read_file` of a picture) with a text placeholder and retries the request once, so a text-only upstream no longer ends the turn on `vision_not_supported`. Without retained per-channel state, a later image-bearing request to the same channel pays one rejected attempt again before the strip retry succeeds. - Treats a Cloudflare shield challenge in front of a relay as transient: a `cf-mitigated: challenge` header, or a Cloudflare-served challenge page, is absorbed inside the retry window, and a challenge that outlasts the window passes through as a retryable `503` because a bare `403` would classify terminal in Grok Build and kill the turn on a self-clearing shield. A genuine origin `403` without Cloudflare markers is untouched. - Synthesizes missing Responses envelope bookkeeping (`id`, `object`, `status`) when a relay omits it on an otherwise complete terminal body, instead of rejecting the repairable omission; present-but-wrong values and a missing or malformed `output` remain rejections. A probe that never reports an outcome — the caller disconnected mid-probe, or the probe's response headers timed out — is presumed lost after a 2-minute lease, and a later request may probe again instead of leaving the channel latched open. - Logs the model declared by the raw upstream response before normalization, including terminal-frame precedence, case-insensitive mismatch detection, and conflicting declarations, without changing routing or response data. Benign mismatches log once per channel/protocol/configured/upstream pair; conflicts and invalid declarations always log. When Grok Build sends an official catalog name such as `grok-4.6` on a non-xAI custom channel, the proxy logs a separate warning with body size, tool count, and session presence to aid `/resume` diagnosis. - Rejects protocol-shape mismatches explicitly: an SSE response to a non-streaming request, or a streaming response to Grok Build's fixed non-streaming WebSearchClient request, returns a non-retryable `502` naming the mismatch instead of forwarding an undecodable body. - Logs upstream failures as structured summaries (`type`, `code`, `message`) for HTTP errors and Responses `response.failed`/`error` events, with bearer tokens, key assignments, and `sk-` values redacted and long messages truncated, so diagnostics never persist credentials. A stream that ends because Grok Build disconnected is logged as aborted by the client and does not emit `proxy_stream_error`. - Completes a missing empty `signature` on Messages `thinking` block starts while preserving the provider's later `signature_delta`, so Messages-compatible relays remain consumable by Grok Build's strict native decoder. - Preserves each configured upstream URL path and wire-model identifier at the provider boundary. - Prepares every explicit custom channel before use, avoiding first-request failures after `/model` switching. - Preserves portable conversation history during model hot switching while withholding only encrypted reasoning known to belong to a different channel, protocol, wire model, or upstream endpoint. - Normalizes missing local tool-call IDs before delivery in native Chat and Messages responses. Responses streams retain output-item and function-call identities across events; a provider that reuses one upstream ID across output slots or Chat tool calls receives a fresh unique ID for the colliding slot instead of a dropped stream, while genuine same-slot conflicts are still rejected. The same tolerance covers Chat Completions `tool_call` IDs reused across parallel calls, in streaming and non-streaming responses alike; Chat JSON-to-SSE fallback assigns independent indexes to parallel tool calls. Existing Chat history is repaired only when one missing call ID has one unambiguous matching result; ambiguous histories remain errors. - Reassembles native Chat tool-call SSE before Grok Build's last-write-wins accumulator sees it: empty later `"name"` fields do not erase a known name, and argument fragments are not rewritten until the JSON is complete. The three frame-rewriting stream rectifiers (Chat, Messages, Responses) share a common `streamFrameRectifier` contract so new protocols plug into the same pipeline. Empty or vendor `finish_reason` values are dropped or mapped onto Grok Build's Chat enum (`stop`, `length`, `tool_calls`, `content_filter`, `function_call`). - Repairs tool-call streams whose first delta was lost in transit. When a Chat, Messages, or Responses stream arrives with an empty function name and arguments truncated at the head (the opening `{"` missing), hellogrok restores the prefix whenever the repaired payload parses as one complete JSON object, infers the name from the argument key set against the tools declared on that request (the shared `command`+`description` shape resolves deterministically to `run_terminal_command`), and applies the same repair to non-streaming responses and to persisted history replayed in later requests. Chat tool frames are held until the stream terminal so argument fragments a relay emits after `finish_reason` are merged instead of dropped; Messages `tool_use` blocks and Responses `function_call` items are held between their start and stop frames for the same reason, with the terminal frame's complete item as a second source for name and arguments. Fragments discarded after an early flush are logged as `late-tool-deltas-discarded`. - Lifts Chat dialects Grok Build does not deserialize: array `content`, `thinking`/`reasoning`/`reasoning_details`, Gemini `functionCall`, and ``…`` in the answer text. Think spans are stripped wherever they appear in the stream, not only at its head: thinking models emit several CoT phases per turn and relays sometimes misroute a reasoning tail into `content` with only the closing tag, so second-phase spans, unbalanced `` tails, stray closing tags, and tags split across stream deltas are classified as reasoning or removed instead of leaking into the visible reply. Tags quoted with backticks or double quotes are prose about the tags, not stream delimiters: reasoning that discusses the tags keeps its full span in the reasoning channel instead of terminating early, and quoted tags inside visible text reach the client intact. Reasoning is forwarded only as a prefix sibling; later thought after visible text is dropped so the TUI does not open a second Thought under the reply. Streaming `reasoning_content` deltas are concatenated as-is so leading BPE spaces are not trimmed per frame. - On Chat history, DeepSeek and MiMo keep previous-turn `reasoning_content` (their gateways 400 without it). Other Chat channels keep the model's own CoT only inside the current tool loop (after the latest user message) and strip cross-turn plaintext thought. Encrypted or signed blobs are never removed, and hellogrok never injects `"tool call"` placeholders. - Streaming Chat requests keep Grok Build's `stream_options.include_usage=true` so terminal usage can drive auto-compaction. GLM Chat channels with tools also receive `tool_stream=true` when the field is omitted. - Projects conversation identity from the original request into `x-opencode-session` for official OpenCode Go and Zen routes across all three protocols, including search conversion and retries. Explicit channel headers take precedence. If the client supplies no identity, an isolated operation ID allows forwarding and remains fixed for internal retries; it does not establish conversation affinity across separate requests. This limitation is logged without the identity value. - Presents every upstream request as Grok Build: an incoming `grok-shell` User-Agent and `X-Grok-Client-Identifier` are kept; otherwise they are synthesized from the installed `grok --version` or a version-less `grok-shell` fallback. hellogrok's own product string is never forwarded. OpenCode still prefers an explicitly configured User-Agent. ### Grok Build local tools Grok Build executes files, shell, grep, subagents, client `web_search`, MCP (`search_tool` then `use_tool`), and skills locally. hellogrok forwards those declarations through Responses, Messages, and Chat Completions, including relayed grok-4.5/4.6 channels. Third-party channels never receive xAI-only `x_search`; provider-hosted tools still require real upstream support. - Upstream tool lists keep Grok Build `client_name` values (`list_dir`, `read_file`, `run_terminal_command`, `spawn_subagent`, …). Claude/Codex aliases (`LS`, `Read`, `Bash`, `Task`, `Grep`, `Write`, `ToolSearch`, …) are rewritten on inbound calls rather than cloned into every request's `tools` array. - Inbound calls are rewritten to the declared `client_name` and parameter keys. Top-level Chat `name`/`arguments`, legacy `function_call`, object-valued arguments, empty names recovered from argument shape (including arguments truncated by a lost first stream delta), and common XML/JSON-in-content calls are normalized before Grok Build dispatches. Parameter aliases are normalized per declared schema (`target_path` becomes `target_file`, `target_directory`, or `file_path`), and a streamed call that accumulates no argument fragments at all is discarded before dispatch with an `empty-args-discarded(name=...)` log note when the declared tool requires properties, because Grok Build would otherwise report a guaranteed `missing field` parse failure for the empty arguments object. An unnamed call that shape inference cannot resolve is routed to a self-healing carrier instead of reaching Grok Build with an empty name: parseable arguments go to `run_terminal_command` (`command` carries the raw text) and corrupt arguments go to `read_file` (`target_file` carries the raw text), logged as `unresolved-name-routed(name=...)`, so the failure returns through a `tool_result` and the model regenerates the call; when neither carrier is declared the call is left untouched. - Direct MCP names (`server__tool`, `mcp__server__tool`) become `use_tool` when that meta-tool is declared. Write-style full-file payloads become `write` or `search_replace`. Glob patterns become `glob`, `grep`, or `rg --files`. Missing required `description` and `subagent_type` fields are filled. - Exact Grok Build names from relayed grok-4.5/4.6 pass through unchanged. History tool-result messages receive the matching `name` so thinking models do not reject the next turn. - hellogrok never invents a tool that was not declared on the current request. A model that never emits a tool call still will not run tools. ### Native Web tools - Supports Grok Build's native `web_search` workflow for hosted and client-search modes. - With `supports_backend_search = true`, every supported format uses the current channel's own hosted search: Responses stays Responses, Messages stays Messages with `web_search_20250305`, and Chat uses its configured search dialect or protocol bridge. First-party DeepSeek follows the configured native protocol now that its Responses API is GA; Chat hosted search defaults to a Responses bridge because DeepSeek Chat documents function tools only. Explicit `chat_search_dialect = "messages"` remains available when a Messages bridge is required. Note that DeepSeek has retired its hosted web search — its Responses API now ignores `web_search` tools — so the flag is only useful there for relayed endpoints that still implement a search extension. - For a channel marked `supports_backend_search = true`, the Grok Build side receives canonical Responses search events when the upstream actually executes search, including completed `web_search_call` items and any verified sources, citations, and usage the provider returned. The flag is a routing declaration, not a capability probe. Every Responses search asks for complete `web_search_call.action.sources` metadata while preserving caller entries; Volcengine Ark (`*.volces.com`) treats `include` as a closed enum and rejects unknown values with `InvalidParameter`, so its hosted search omits the hint and source display falls back to its response annotations; declaration fields Ark's closed schema rejects (unknown tool fields such as `filters`, and the `allowed_tools` selector) are stripped, with a pure hosted-search selection collapsed to the equivalent `required`. Any other upstream that proves its schema closed with a 400 (include or tool-declaration rejection) triggers one in-request rewrite-and-replay — strip the include or sanitize the declaration — without retaining per-host state; a rejected request never executed a search, so the replay is side-effect free. Citation entries missing index fields required by Grok Build's parser are filled with 0 on every route. DeepSeek currently accepts but silently ignores that standard `include` hint, so hellogrok consumes any URLs already present in its native search action but cannot recover omitted results. If DeepSeek implements the hint later, the same request begins receiving sources without a hellogrok update. Messages search-result/citation blocks, provider-level citations, and Chat annotations/search-result containers are normalized into the same canonical source fields. - Selecting a proxied custom route through `[models].web_search` or `GROK_WEB_SEARCH_MODEL` projects that route as `supports_backend_search = true` for the entire proxy lifetime, even when its original value is false. Grok Build can therefore discover hosted `web_search` in ordinary sessions and its fixed non-streaming WebSearchClient request; hellogrok translates both to the provider protocol and restores the original setting on stop. - Preserves real search URLs from adapted search results in both `web_search_call.action.sources` and `output_text.annotations`, including valid final-answer links only when the response independently confirms that search ran. This lets Grok Build display its native deduplicated site count. - Accepts an adapted client-search response only when the upstream independently proves that search completed and also returns non-empty answer text. Providers that silently ignore their search extension receive a non-retryable `502`; hellogrok cannot manufacture search capability for such a channel. - Keeps `web_fetch` available as an independent tool when allowed by the active agent configuration. - Applies the same search behavior to supported subagents. - Keeps official Grok models on Grok Build's native search and login path. ### Authentication and configuration safety - Uses channel-owned API keys, environment keys, authentication providers, and headers. - Prevents an official Grok login token from being sent to an unrelated custom channel. - Validates channel-owned header names and values while loading configuration. Request framing, content, and connection headers remain controlled by the proxy. - Checks and temporarily completes required Grok settings when the proxy starts. - Leaves `reasoning_effort`, `reasoning_efforts`, and `supports_reasoning_effort` entirely user- or Grok Build-owned: it never creates, migrates, reorders, or replaces them. Temporary proxy-owned settings such as `supports_backend_search` remain byte-stable across repeated applies, and stop restores only values managed by the active transaction. - Preserves upstream error status and body while classifying retry behavior from structured error codes. Authentication, permission, billing, insufficient balance/quota, invalid request, and invalid model failures are non-retryable; rate limits, timeouts, overload, concurrency-limit rejections (including nested `response.error` envelopes), and temporary service failures remain retryable. An explicit upstream `X-Should-Retry` header always wins. - Accepts `config.toml` as UTF-8 with or without a BOM. Read-only checks never rewrite the file; every successful proxy apply, restore, or rollback write saves it atomically as UTF-8 without BOM. Invalid TOML reports the file path, line, and column instead of an undecorated parser error. Apply and restore re-read the file immediately before the atomic rename: an external edit made during the update aborts the write with a retryable error and is preserved, and the rename's directory entry is flushed so a power loss cannot lose a committed write. - Computes an independent auto-compaction budget for each custom model from its effective context window and maximum output. It temporarily lowers only unsafe thresholds, never raises a lower user value, and restores every managed value when the proxy stops. - Forwards only live-context usage that can fit the known `context_window`. Hosted-search billing totals and other measurements larger than the window become `usage: null` so Grok Build keeps its previous baseline instead of compacting on an impossible count. - Cross-checks every provider usage report against the physical request before it reaches Grok Build's context meter. Per conversation (channel plus the client's session identity), the guard learns a bytes-per-token ratio from the first coherent report — the tokenizable footprint of the request body, encoded size minus embedded base64 data URIs, divided by the reported prompt tokens — and drops any later report whose prompt count exceeds 1.25× what the current body can hold. Such reports double-count the cached prefix or carry session-cumulative totals, and would otherwise move the meter in one step and fire auto-compaction far above the configured threshold. A dropped report leaves Grok Build on its own byte-based estimate until the provider reports coherently again; a body that shrinks by more than a quarter (compaction, rewind, resume) relearns the baseline, and conversations without a stable session identity skip the check. - Rectifies provider usage measurements before they reach Grok Build's token ledger and auto-compaction meter. Stringly-typed counts (`"prompt_tokens": "12345"`), whole floats, and `json.Number` values are normalized to integers; invalid optional decorations (null detail values, non-map detail containers, fractional `cost_in_usd_ticks`) are removed without poisoning the core measurement; unrecoverable core counts (negative, fractional, overflowing, non-numeric) are deleted so the downstream validator drops the measurement instead of trusting corrupt data. Repairs apply across Chat Completions, Responses, and Messages, in streaming and non-streaming responses, and in protocol-translation paths, and are logged as `usage rectified` proxy notes. - On normal stop, tray exit, Ctrl+C, SIGTERM, or failed startup, restores untouched temporary values while preserving concurrent user edits through a field-level three-way merge. If unrelated edits make the full TOML document invalid but it remains valid UTF-8, line-scoped recovery still restores independently valid managed assignments while preserving the malformed user text. - Always honors the tray **Exit** command after attempting cleanup. If safe restoration is impossible because the file cannot be accessed or an unowned local route remains, the recovery transaction stays on disk for the next launch instead of trapping the user in the tray process. - Keeps a diagnostic listener after an ordinary proxy stop so stale sessions receive a structured, non-retryable `proxy_stopped` error that asks the user to reselect a model. Tray **Exit** closes that listener and releases the port even when configuration cleanup must be deferred. - Recovers proxy-managed settings after an unclean exit with `hellogrok restore`. When `config.toml` no longer exists there is nothing to restore, so the obsolete recovery record is dropped instead of failing every later start and restore. Recovery removes a hellogrok-created `subagents.enabled` dotted key only from the file's root table; the user's own `subagents.enabled` inside any other table is preserved. ### Desktop and operations - Provides a native Windows tray application and a console CLI. - Remembers the user's proxy-enabled choice between tray launches. - Defaults to proxy-enabled on first launch so new users see a working proxy immediately. - Includes login autostart controls for Windows, Linux, and macOS. - Provides route inspection, grouped status, live log search, usage-day log retention, and terminal log following. - Builds for Windows, Linux, and macOS on amd64 and arm64. hellogrok is a Grok Build channel proxy. It is not a system proxy, PAC service, VPN, or general HTTPS interceptor. ## Search and configuration ### Search modes Search behavior follows the explicit search-model selection first, then the active backend: | Setting | Search behavior | |---------|-----------------| | `[models].web_search` or `GROK_WEB_SEARCH_MODEL` is set | If the selected model is a proxied custom route, hellogrok temporarily projects it as `supports_backend_search = true` and Responses-facing for both ordinary sessions and Grok Build's WebSearchClient. The route may use `responses`, `messages`, or `chat_completions` upstream. The environment variable takes precedence, selection overrides an original false only while the proxy runs, and no startup request is sent. | | Any channel with `supports_backend_search = true` | Grok Build uses Responses hosted tools while hellogrok calls that channel's own working search API: Responses, Messages `web_search_20250305`, or the selected Chat search dialect/bridge. | | Any channel with `supports_backend_search = false` | Grok Build uses client `web_search`: first `[models].web_search` or `GROK_WEB_SEARCH_MODEL`, otherwise its authenticated official fallback. A selected custom search route is temporarily overridden to true. An omitted field preserves Grok Build catalog behavior for every provider, including the first-party DeepSeek endpoint. | | No usable hosted or client search path | `web_search` is unavailable for that model. | | `web_fetch` | Remains independent of the search-model selection and follows the active tool permissions. | Backend search is not a second Grok Build request. Its declaration is part of the active model request, so hellogrok must select the provider protocol before the model decides whether to search. Client `web_search` is different: after the active model calls the local function, Grok Build's `WebSearchClient` makes a separate non-streaming `/responses` request to the selected search model, which hellogrok then maps to that route's search protocol. hellogrok never creates, selects, or replaces `[models].web_search`, and startup sends no search-capability probes. A Messages channel marked enabled must actually support `web_search_20250305`. Chat defaults to `web_search_options`; official xAI and first-party DeepSeek Chat bridge to Responses. Set `chat_search_dialect` to `web_search_options`, `search_parameters`, `messages`, or `responses` when a route needs an explicit strategy. The temporary flag is a routing declaration, not proof of provider capability. A real upstream HTTP rejection is preserved; if the fixed WebSearchClient request is silently answered without a completed search, hellogrok returns a non-retryable `502` stating that the selected search model did not complete backend `web_search`. For client search, hellogrok clarifies tool descriptions but never infers or forces tool use from prompt text. Mandatory selection comes only from structured `tool_choice`. Internal wire aliases are changed only in protocol-defined tool declarations, choices, and call-name fields; response text, URLs, tool arguments, tool results, and other business JSON remain unchanged. ### Example configuration This example selects a Chat Completions channel that supports `web_search_options` as Grok Build's default search model: ```toml [models] web_search = "search-relay" [model.search-relay] model = "provider-search-model" base_url = "https://api.example.com/v1" env_key = ["SEARCH_RELAY_API_KEY"] api_backend = "chat_completions" chat_search_dialect = "web_search_options" supports_backend_search = false ``` The original false in this example remains user-owned, but selection temporarily overrides it to true while hellogrok runs and restores it on stop. The provider must still support the selected search dialect. For a channel not selected as the default search model, set `supports_backend_search = true` only after confirming its provider API supports hosted search. Messages uses `web_search_20250305`; Chat relays can select an explicit `chat_search_dialect`. ### Supported channel settings | Setting | Required | Default | Purpose | |---------|----------|---------|---------| | `model` | No | Model table ID | Upstream wire-model identifier. While the proxy is active, hellogrok temporarily exposes the model-table ID to Grok Build as the runtime identity and translates requests back to this configured value at the provider boundary. | | `base_url` or `api_base_url` | Yes | None | Custom upstream endpoint. Models without a custom URL are not proxied. | | `api_backend` | No | Model catalog, then `chat_completions` | Native upstream API format: `responses`, `messages`, or `chat_completions`. A capable non-Responses channel is temporarily projected as Responses only to Grok Build; hellogrok converts at the provider boundary while preserving the selected native protocol unless an explicit search bridge applies. | | `chat_search_dialect` | No | Host-based | Hosted-search strategy override: `web_search_options`, `search_parameters`, `messages`, or `responses`. Official DeepSeek and xAI Chat default to `responses`; other Chat routes default to `web_search_options`. Responses and Messages stay native unless this field explicitly requests a bridge. | | `api_key` | One auth method | None | Static channel credential. Prefer `env_key` for shared configurations. | | `env_key` | One auth method | None | Environment variable name or ordered list of names containing the channel credential. | | `auth_provider` | One auth method | None | Grok command-based authentication provider. | | `auth_scheme` | No | `bearer` | Upstream authentication scheme. Set `x_api_key` only for providers that explicitly require `X-Api-Key`. | | `extra_headers` | No | Empty | Additional channel-owned HTTP headers, including provider-specific authentication. Proxy-controlled framing, content, and connection headers are rejected; names are case-insensitive. | | `env_http_headers` | No | Empty | HTTP headers populated from environment variables. Resolved values use the same header rules as `extra_headers`. | | `reasoning_effort` | No | Grok Build selection | Selected or default Grok Build reasoning level. This value is user- or catalog-owned and is never written or reordered by hellogrok; it can be configured without also defining a reasoning menu. | | `reasoning_efforts` | No | Model catalog | Reasoning levels exposed by Grok Build for the model. String arrays and custom object menus remain unchanged; hellogrok translates protocol field shapes when required but does not reinterpret the selected level. | | `supports_reasoning_effort` | No | Model catalog/menu | Explicit reasoning-selector capability. hellogrok reads it only to preserve protocol semantics and never inserts or changes it. | | `supports_backend_search` | No | Catalog/provider default | When true, uses this channel's own hosted search and exposes canonical Responses search events to Grok Build for all three upstream formats. When false, Grok Build uses its configured or authenticated client-search route unless this custom model is selected by `[models].web_search` or `GROK_WEB_SEARCH_MODEL`, which temporarily overrides it to true. Every model, including the first-party DeepSeek endpoint, remains owned by explicit configuration or Grok Build's model catalog. | | `context_window` | No | Provider/model catalog | Total input-plus-output context capacity. An explicit model/provider value wins; otherwise hellogrok can learn a trustworthy upstream value and temporarily project it at model level so Grok Build uses the same auto-compaction denominator. | | `max_completion_tokens` | No | Model metadata | Maximum generated-token allowance. An explicit value wins. Without one, hellogrok observes the actual outgoing request and trusted upstream metadata for budgeting, but a request-derived value is not written back as a model output cap. | | `auto_compact_threshold_percent` | No | Model value, then `[session]`, then `85` | Preferred percentage of `context_window` at which Grok Build compacts. hellogrok keeps it when safe and temporarily lowers it per model when the output reserve and safety margin require an earlier trigger. Valid range: `0` to `100`. | | `inference_idle_timeout_secs` | No | Managed `900`, else Grok Build/provider policy | Maximum idle gap while waiting for upstream headers or body data. Grok Build additionally applies the same value as its per-chunk content-progress deadline: text, reasoning, tool-call deltas, and terminal signals count as progress, while heartbeats and empty deltas do not. A model value overrides the global `[models]` value; when neither exists, hellogrok temporarily materializes `900` for every proxied channel (restored byte-for-byte on stop) and mirrors it into the live route so proxy timers stay consistent. Every streaming path also runs a downstream content watchdog 30 seconds ahead of that deadline — a stalled stream becomes a retryable `proxy_stream_error` instead of the client's non-retryable `IdleTimeout`. Explicit values below 60 seconds disable the watchdog to respect fast-fail choices. | | `absorb_retry_max_secs` | No | `90` | Total wait window for proxy-side retries of transient upstream soft failures (busy/overloaded `503`, `429`, retryable `5xx`, response-header timeouts, upstream `408` edge-timeout pages, and Cloudflare origin-TLS `525`/`526`). Grok Build's retry budget stays untouched while the window lasts, and once the window is exhausted Grok Build's own retry indicator shows the error reason (` | Retrying (N/M)`) while its native budget continues. An explicit `0` disables the absorb layer for the channel. | | `absorb_retry_backoff_cap_secs` | No | `30` | Cap for each absorb-layer backoff step. An upstream `Retry-After` on the error response still paces the waits, capped at 60 seconds. | | `error_resilience` | No | `off` | Global `[models]` setting (not per channel). Controls deterministic 4xx failures only (soft-failure absorption is always on): `off` passes authentication, permission, and invalid request/model rejections straight through with the provider's explanation; `balanced` additionally absorbs them with fixed 30s-then-60s waits so unattended turns survive relay-side token rotation, permission fixes, and brief deploys. | | `dead_channel_fail_fast` | No | `false` | Opt-in fast-fail for a truly unreachable channel: after consecutive dial-level failures (connection refused, DNS, TLS handshake) the proxy answers a non-retryable `503 proxy_circuit_open` instead of burning Grok Build's retry budget. Soft failures never count. | | `dead_channel_fail_threshold` | No | `6` | Consecutive dial-level failures required to open the opt-in dead-channel breaker. | Model settings may be declared directly under `[model.]` or inherited from a referenced `[model_providers.]`. Model-level values take precedence. When `api_backend` is absent from both, hellogrok leaves it absent in Grok Build's active configuration and follows the concrete protocol Grok Build resolves from its current model catalog; a model unknown to that catalog falls back to Grok Build's `chat_completions` default. A hosted-search tool present on that resolved Responses request is treated as the capability signal, so a future catalog entry does not require a model-ID update in hellogrok. Quote the full ID when a channel ID contains a dot, for example `[model."provider.v1-beta"]`. Dashes do not require quoting. A value such as `name = "Provider.v1-beta"` is display-only and may use dots or dashes directly. While active, hellogrok also accepts a legacy unquoted dotted header and restores the original text on stop. Do not manually set a custom channel URL to hellogrok's local address. The application manages temporary local URLs only while the proxy is active. ### Automatic compaction capacity Grok Build calculates its trigger from the full context window: `context_window * auto_compact_threshold_percent / 100`. It does not subtract the completion allowance first. hellogrok therefore calculates a model-specific safe percentage before Grok samples the next turn: ```text margin = max(8192, ceil(context_window / 20)) safe = floor(100 * (context_window - max_completion_tokens - margin) / context_window) effective = min(user_or_default_threshold, safe) ``` The preferred threshold comes from `[model.]`, then `[session]`, then Grok Build's default `85`. The 5% margin, with an 8K minimum, covers token-estimation and compaction-summary overhead. If both capacities are known and the preferred value is already lower, hellogrok leaves it unchanged. If the output reserve plus margin consumes the whole window, no safe positive percentage exists; hellogrok reports a capacity conflict and keeps the existing threshold instead of injecting `0`. Explicit model/provider capacity remains authoritative. Missing values are learned from the actual outgoing `max_output_tokens` or `max_tokens`, valid upstream `X-Grok-Context-Window` and `X-Grok-Max-Completion-Tokens` headers, or one unambiguous structured context limit. A trustworthy learned window is temporarily written as model-level `context_window`, because the threshold is only correct when Grok Build uses the same denominator. Request-derived output limits participate in the calculation but are not written as `max_completion_tokens`. When one or both capacities are still unknown, hellogrok marks the model as learning and does not guess a threshold from its name. The first request may therefore still use Grok Build's catalog or fallback values; the existing structured context-error path can reduce that request's output allowance and retry once. Learned records use hashed route identities, contain no URL, model name, or credential, expire after 30 days, and are stored as `capacity_cache.json` in the private hellogrok data directory. Every threshold and learned-window projection uses the same recovery transaction as the local route. Model-catalog reload and current-model reselection make an idle open session adopt the update. Active sessions are retried after bounded delays until they become idle; stopping the proxy cancels the worker before restoring the original configuration. `GROK_AUTO_COMPACT_THRESHOLD_PERCENT` still has higher priority than TOML inside Grok Build. When hellogrok inherits that environment variable it can warn about an unsafe value, but it cannot replace the environment of an already running Grok process. ## DeepSeek DeepSeek compatibility is enabled by the exact first-party host `api.deepseek.com`, independently of model ID. Rolling aliases and future models therefore inherit authentication, endpoints, protocol-level request normalization, queue keep-alives, usage handling, and SSE framing without a hellogrok release. Native same-protocol requests retain unknown fields; cross-protocol bridges map the fields Grok Build currently sends that the target protocol can express. Reasoning-effort strings remain provider-owned and pass through without a hellogrok model-level mapping table. As announced on 2026-09-10, the current production IDs are `deepseek-flash` (the V4.1 Flash generation with native vision; the retired `deepseek-v4-flash` and `deepseek-v4-flash-vision-exp` names are still accepted and routed to it) and `deepseek-v4-pro`; these names are examples, not a hellogrok allowlist. A relay that merely reuses a DeepSeek model name receives no first-party assumptions. ### Recommended configuration Responses is the closest native match for Grok Build and is the recommended backend: ```toml [model.deepseek-v4-pro] model = "deepseek-v4-pro" base_url = "https://api.deepseek.com" env_key = ["DEEPSEEK_API_KEY"] api_backend = "responses" context_window = 1048576 max_completion_tokens = 384000 inference_idle_timeout_secs = 660 [model.deepseek-flash] model = "deepseek-flash" base_url = "https://api.deepseek.com" env_key = ["DEEPSEEK_API_KEY"] api_backend = "responses" context_window = 1048576 max_completion_tokens = 384000 inference_idle_timeout_secs = 660 ``` The retired `deepseek-v4-flash` name is still accepted by DeepSeek and routed to V4.1 Flash, so an existing configuration keeps working unchanged. DeepSeek has retired its hosted Web Search: the Responses API now ignores `web_search` tools, so an omitted `supports_backend_search` uses Grok Build client search (via `[models].web_search`, `GROK_WEB_SEARCH_MODEL`, or the authenticated official fallback), matching every other provider. An explicit `true` is still honored as declared but the first-party endpoint silently ignores the search extension. Responses and Messages hosted-search requests stay on their configured native APIs. Chat defaults to a Responses bridge because DeepSeek Chat currently documents only function tools; set `chat_search_dialect = "messages"` only when a Messages bridge is intentionally required. Native Responses and Chat authenticate with Bearer. Messages uses the documented `https://api.deepseek.com/anthropic` SDK base URL (the effective endpoint is `/anthropic/v1/messages`) and `X-Api-Key`; the `[1m]` suffix is accepted only as DeepSeek's documented Anthropic integration alias and must not be used with Responses. The two capacity limits above reflect DeepSeek's documented 1M total context and 384K maximum output; `1,048,576` is the service boundary observed in its structured context error, not a model-ID constant in hellogrok. With these values, an 85% preference is temporarily limited to 58% by the automatic budget. The 660-second value is an idle policy chosen to cover DeepSeek's documented ten-minute queue. A model-level value takes precedence over an inherited provider value. With no explicit limit, hellogrok accepts valid upstream `X-Grok-Context-Window` and `X-Grok-Max-Completion-Tokens` metadata, or an unambiguous structured context-limit field from an error, without guessing from model names. Grok Build's own catalog remains the final fallback; an unknown custom model with no `context_window` defaults to 200,000 tokens until hellogrok learns a trustworthy window. Because DeepSeek does not publish the Grok-private response headers, explicitly configure both values for predictable first-turn behavior. Future models and rolling aliases can change these values without a hellogrok release. Grok Build owns the model's reasoning menu and selected/default level. hellogrok never adds, migrates, reorders, or replaces `reasoning_effort`, `reasoning_efforts`, or `supports_reasoning_effort`; user-defined string arrays, object labels, order, and defaults remain byte-for-byte in place. Users may configure only `reasoning_effort`, only a menu, both, or neither. For example, a user who wants only DeepSeek's distinct native choices can configure `reasoning_efforts = ["none", "low", "high", "max"]`; a user who needs only a fixed selection can set `reasoning_effort` alone. Omitting all three fields leaves Grok Build model-catalog and provider defaults intact. hellogrok does not reinterpret provider effort levels. A Responses-to-Messages bridge mirrors Grok Build's native Messages serializer by omitting `none` and `minimal`; every other non-empty value is copied unchanged. On the first-party DeepSeek endpoint, an explicit `none`, including a standalone `reasoning_effort = "none"`, becomes that protocol's native thinking-off switch so the request does not fall through to DeepSeek's enabled-by-default mode. When no reasoning fields are configured, hellogrok adds no thinking switch and preserves that default. DeepSeek's current documentation describes the following server-side behavior: | Requested level | DeepSeek effective level | |-----------------|--------------------------| | `low` | `low` | | `medium` | `high` | | `high` | `high` | | `xhigh` | `high` | | `max` | `max` | This table is DeepSeek service behavior, not a transformation performed by hellogrok. A menu limited to `none`, `low`, `high`, and `max` avoids duplicate choices that resolve to the same effective level. Any other non-empty effort remains unchanged on the provider request, so DeepSeek's current or future validation is authoritative. Third-party relays receive no DeepSeek-specific reasoning adaptation apart from ordinary cross-protocol field conversion. ### Native capability mapping | DeepSeek surface | hellogrok behavior in Grok Build | |------------------|----------------------------------| | Responses | Preserves `instructions`, developer messages, reasoning, native `text.format` JSON Schema, function tools, and native Web Search blocks when the provider actually returns them. It sends the standard source `include` hint without inventing missing results; source display is limited to URLs DeepSeek actually returns. Provider `action.queries` arrays remain intact while each call gains the singular `action.query` field Grok Build displays. It handles `response.completed`, `response.incomplete`, and `response.failed` as terminal events. | | Chat Completions | Preserves tool-call `reasoning_content`, requests the terminal streaming usage block, maps `max_completion_tokens` to `max_tokens` unless the latter is already explicit, converts developer messages to system messages, supplies non-null assistant tool content, and maps an explicit Responses `user` to DeepSeek `user_id`. DeepSeek documents function tools only on this surface, so hosted Web Search requires a Messages or Responses bridge. DeepSeek rejects `tool_choice` while thinking is enabled, so the selector is removed while function declarations remain available for automatic use; an explicit non-thinking request keeps the documented selector forms. Grok Build `json_schema` becomes documented `json_object` mode plus a schema instruction, and Grok Build validates the returned JSON locally. | | Anthropic Messages | Uses `X-Api-Key`, preserves thinking/tool history, effort control, function tools, and native server Web Search blocks when present. When a reasoning selector is explicitly configured, an omitted Grok Build `None` selection becomes `thinking.type=disabled` instead of falling through to DeepSeek's default `high`; only the supported `output_config.effort` is sent, explicit Responses `user` maps to `metadata.user_id`, and the documented `deepseek-v4-pro[1m]` alias remains on the Messages wire. | | Queue and usage | Accepts non-streaming empty-line keep-alives and streaming `: keep-alive` comments. Real terminal usage is preserved so Grok Build can drive context accounting and auto-compaction. Measurements larger than the known `context_window` stay unknown. | Responses is the only DeepSeek surface here with native JSON Schema output. Chat supports only `json_object`, can occasionally return empty content, and relies on the injected schema instruction plus Grok Build's local validator. Chat function `strict: true` is a separate Beta feature: configure `base_url = "https://api.deepseek.com/beta"` when that exact Chat behavior is required. Messages structured output continues through Grok Build's validated `StructuredOutput` function because DeepSeek's Anthropic-compatible `output_config` supports effort only. Explicit caller-supplied user isolation IDs are preserved across protocol bridges, but hellogrok does not invent one or derive one from unrelated identity headers. A Chat `insufficient_system_resource` finish is surfaced as a structured failure, or as retryable HTTP `503` when the native non-streaming protocol can still return an HTTP error. The provider's API surface is broader than the part Grok Build can address. DeepSeek's current Responses compatibility table marks `include`, image/file input, previous responses, server-side conversations, background mode, truncation, and `stream_options` unsupported; native `file_search`, `code_interpreter`, `computer_use`, and `mcp` tool types are ignored. Unsupported fields are currently accepted and silently ignored, so hellogrok retains forward-compatible standard fields such as the Web Search source `include` hint. This does not disable Grok Build's local shell, MCP, task, file, or `apply_patch` tools: current Grok Build source sends them as supported function tools. DeepSeek also documents a custom tool shape named `apply_patch`; hellogrok leaves that shape intact when a caller supplies it, but current Grok Build source does not dispatch the resulting `custom_tool_call` to its local patch executor, so this passthrough is not presented as a local editing path. DeepSeek's Anthropic API does not support image/document input, code execution blocks, or native MCP blocks. Prefix continuation and FIM exist in DeepSeek's API, but hellogrok does not invent Grok Build UI or completion endpoints that the agent does not call. Caching stays provider-native. hellogrok preserves ordered conversation/tool/reasoning history and Grok Build's `x-grok-conv-id` on all three protocols; it keeps Responses cache keys stable, preserves native Messages `cache_control` markers, and adds the same system/current-tip/previous-turn markers when converting Responses to Messages. Historical backend searches use Grok Build's stable text summary instead of synthetic Messages server-tool blocks, while live calls and results remain provider-native. DeepSeek ignores Responses cache-key parameters and manages its prefix cache automatically, so those inert fields are left stable rather than treated as a cache switch. Cache usage fields are preserved or projected into the receiving protocol, but only a real provider response can prove a cache hit. ## Download Download the latest tagged build from [GitHub Releases](https://github.com/hellowind777/hellogrok/releases/latest). Windows releases provide separate tray and console executables; Linux and macOS releases provide the standard foreground CLI. | Platform | Release assets | |----------|----------------| | Windows amd64 / arm64 | `hellogrok-windows-.exe` and `hellogrok-cli-windows-.exe` | | Linux amd64 / arm64 | `hellogrok-linux-` | | macOS Intel / Apple Silicon | `hellogrok-darwin-` | Every binary has a neighboring `.sha256` file. On Windows, verify an amd64 tray build before running it: ```powershell $artifact = ".\hellogrok-windows-amd64.exe" $expected = ((Get-Content -LiteralPath "${artifact}.sha256") -split '\s+')[0] $actual = (Get-FileHash -LiteralPath $artifact -Algorithm SHA256).Hash.ToLowerInvariant() $actual -eq $expected ``` The final command must print `True`. On Linux use `sha256sum -c .sha256`; on macOS use `shasum -a 256 -c .sha256`. Release binaries are currently unsigned, so checksum verification establishes file integrity but not publisher identity. ## Quick start ### Prerequisites - Grok Build with a readable `~/.grok/config.toml` containing at least one custom model URL. - A valid credential source for every custom channel. - Go **1.26.6** when building from source. Compatibility is verified against the latest fetched Grok Build `origin/main`, not against an older release branch. The exact tested revision is reported with the test results because the local Grok Build checkout may update independently. Set `GROK_HOME` to use a Grok configuration directory other than `~/.grok`. ### Windows ```powershell git clone https://github.com/hellowind777/hellogrok.git cd hellogrok .\scripts\build.ps1 .\dist\hellogrok-cli.exe routes .\dist\hellogrok.exe ``` Use the tray menu to select **Start proxy**. New Grok Build processes read the proxy configuration directly; idle custom-model sessions already attached to a shared leader are hot-switched automatically. ### Linux or macOS ```bash git clone https://github.com/hellowind777/hellogrok.git cd hellogrok mkdir -p dist CGO_ENABLED=0 go build -trimpath -o dist/hellogrok ./cmd/hellogrok ./dist/hellogrok routes ./dist/hellogrok start ``` Expected startup output includes a local channel endpoint and a successful configuration rewrite. Keep the process running while using Grok Build. Ctrl+C or SIGTERM stops the proxy and restores the original configuration. ### First-use checklist 1. Run `hellogrok routes` and confirm every intended custom model is listed with the correct backend and an available authentication source. 2. Start hellogrok. If Grok Build is already open, inspect the shared-leader hot-switch result in status or logs. 3. Start with a unique, non-sensitive detail, switch through the intended models with `/model`, and confirm a later model can refer to the visible conversation history. 4. Test `web_search` and `web_fetch` separately according to the selected search mode. 5. Stop hellogrok normally and confirm Grok Build's configuration no longer points to the local proxy. ## Platform support | Platform | Standard interface | Tagged release artifacts | Architectures | |----------|--------------------|--------------------------|---------------| | Windows | Native tray and CLI | GUI and console `.exe` files | amd64, arm64 | | Linux | Foreground CLI or systemd user service | CLI binary | amd64, arm64 | | macOS | Foreground CLI or LaunchAgent | CLI binary | amd64, arm64 | Standard release binaries use `CGO_ENABLED=0`. Tagged releases are configured to include SHA-256 checksum files. Linux and macOS users can build the optional tray interface from source: ```bash CGO_ENABLED=1 go build -trimpath -tags tray -o dist/hellogrok-tray ./cmd/hellogrok ``` Linux tray builds require GTK 3 and AppIndicator development packages. macOS tray builds require Xcode Command Line Tools. The standard Unix CLI does not require these desktop dependencies. Current Windows and macOS artifacts are not code-signed or notarized. ## Tray and CLI ### Tray controls The Windows tray application and optional Unix tray build provide: - **Start proxy** — enabled by default on first launch; later starts and stops remember the selected state. - **Autostart** — enables or disables login startup. - **Status and logs** — opens the current status and live log window. - **Exit** — attempts to restore the configuration and stop the proxy, then always exits the tray process. An unresolved recovery transaction remains available for the next launch. Only one tray instance runs in a login session; launching it again exits immediately instead of creating a second tray. The remembered tray state is independent from the foreground `hellogrok start` command. On Windows, the divider in **Status and logs** contains a retention selector and log search. Retention counts distinct dates on which hellogrok actually wrote logs rather than elapsed calendar days; the default keeps the latest 7 usage days, with `off`, 3, 7, 14, and 30 available. Cleanup runs at the next application start. Repeated **Search** clicks move to the next match and wrap to the beginning. Repeated **Status and logs** clicks while the window is still being created do not queue a second window; a later click can retry if creation failed or timed out. Status text wraps; raw log lines remain unwrapped for reliable scanning. **Stop protection**: The **Start proxy** toggle and foreground signal handler remain fail-closed when another provider manager owns Grok Build or a temporary hellogrok route cannot be restored safely. The tray **Exit** command is different: it always terminates after the cleanup attempt, so an external ownership conflict can never trap the user in the application. Configuration edits made while the proxy is active are merged field by field during shutdown. Values still matching hellogrok's temporary projection are restored to their startup values, while user-edited values and deleted model channels are preserved. When unrelated edits leave TOML temporarily invalid but still valid UTF-8, hellogrok compares each managed assignment independently, restores only values it still owns, preserves the malformed user text, writes the successful result as UTF-8 without BOM, and checks textually for remaining local routes. Invalid UTF-8 is never guessed or rewritten: cleanup leaves both the configuration and recovery state unchanged, while tray exit nevertheless completes. ### Compatibility with CC Switch CC Switch and hellogrok can run at the same time only when CC Switch is not managing Grok Build. CC Switch's Grok Build proxy takeover and provider switch both write `~/.grok/config.toml`; using either operation while hellogrok owns that file creates a configuration-ownership conflict even though the proxies listen on different ports. - hellogrok refuses to start when it detects CC Switch's Grok Build takeover marker (`PROXY_MANAGED` on its `/grokbuild/v1` route). - If CC Switch takeover is enabled after hellogrok starts, a normal proxy stop remains deferred until CC Switch releases Grok Build. Tray **Exit** still closes hellogrok and retains its recovery transaction; releasing CC Switch first remains the cleanest shutdown order. - If a provider manager completely replaces the live Grok config and no hellogrok route remains, hellogrok preserves the external config and relinquishes its obsolete recovery state. - CC Switch may continue managing Claude, Codex, Gemini, and other applications while hellogrok is active. If both Grok proxies were enabled accidentally, disable CC Switch's Grok Build takeover first, then stop hellogrok. Avoid switching the CC Switch Grok Build provider while hellogrok is active. ### CLI reference | Command | Purpose | |---------|---------| | `hellogrok start` | Run the proxy in the foreground. | | `hellogrok version` | Print the installed version. | | `hellogrok routes` | List custom routes without printing credentials. | | `hellogrok restore` | Restore proxy-managed settings after an unclean exit. | | `hellogrok normalize-config` | Validate `config.toml`, then explicitly remove its UTF-8 BOM without transcoding another encoding. | | `hellogrok autostart enable` | Enable login autostart for the current executable. | | `hellogrok autostart disable` | Disable login autostart. | | `hellogrok autostart status` | Show the current autostart state. | | `hellogrok log` | Print and open the log file. | | `hellogrok logview` | Follow the log in the current terminal. | | `hellogrok help` | Show command help. | ### Runtime data | Platform | Location | |----------|----------| | Windows | `%LOCALAPPDATA%\hellogrok` | | Linux and macOS | `~/.hellogrok` | Runtime data contains application preferences, logs, the recovery state used to restore managed configuration, and `reasoning_provenance.json`. Files owned by hellogrok are written as UTF-8 without BOM. Corrupt derived-state files never block startup: an unparseable capacity cache or preferences file is preserved next to the original with a `.bad` suffix while the application continues with an empty cache or default preferences. The provenance index stores only SHA-256 digests of opaque reasoning values and route signature domains; it never stores raw reasoning, channel IDs, model names, upstream URLs, or credentials. Log retention is applied on every platform. The native retention selector and in-window search are currently Windows-only because the standard Linux and macOS builds use terminal log viewing instead of the Win32 status window. ## Autostart ### Windows Enable **Autostart** from the tray or run `hellogrok autostart enable`. Login startup opens the tray and applies the remembered proxy-enabled state. ### Linux The standard CLI registers a systemd user service. Enable it and start it immediately with: ```bash ./dist/hellogrok autostart enable systemctl --user start hellogrok.service systemctl --user status hellogrok.service ``` ### macOS The standard CLI registers a per-user LaunchAgent. Enable it and load it immediately with: ```bash ./dist/hellogrok autostart enable launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.hellogrok.proxy.plist" ``` Autostart records the current executable's absolute path. Disable and re-enable it after moving the binary. Credentials referenced by `env_key`, `env_http_headers`, or `GROK_HOME` must be available in the login-started process environment, not only in the current shell. ## How it works ```text Grok Build | v hellogrok local channel proxy | v Configured custom API channel ``` At startup, hellogrok validates each custom channel and points its URL to a channel-scoped local route. Channels without backend search keep their configured Grok Build consumer and native stream format unless selected as the default search model. Capability-enabled and selected-search channels are temporarily projected as Responses because that is the Grok Build consumer that serializes hosted tools and renders structured search results; hellogrok then translates at the provider boundary and restores the original configuration on stop. Responses providers remain Responses. Messages providers receive Messages requests and return Responses events through a bidirectional converter. Chat providers use `web_search_options` or `search_parameters`, or bridge to Messages/Responses when configured. Grok Build's fixed non-streaming `WebSearchClient` request uses the same provider adapters when a custom channel is selected as the client-search model. Native `web_search`, `web_fetch`, official Grok login behavior, and supported subagent workflows remain controlled through Grok Build rather than being replaced by a separate search service. ## Troubleshooting ### Startup reports a TOML or UTF-8 error The error identifies the `config.toml` path and, when available, the exact line and column. Standard UTF-8 with or without a BOM is accepted. Other encodings are not guessed or silently transcoded; save the file as UTF-8 in an editor, then retry. Read-only startup checks do not rewrite the file. Whenever the proxy commits an apply, restore, or rollback write, it validates the applicable input and saves `config.toml` atomically as UTF-8 without BOM, including when a user adds a BOM while the proxy is active. To normalize immediately while the proxy is stopped, run `hellogrok normalize-config`; it validates the complete TOML document and leaves invalid input unchanged. ### No custom routes are found Confirm that the intended `[model.]` or referenced provider has a valid `base_url` or `api_base_url`. Official models without a custom URL are intentionally excluded. ### `web_search` is unavailable Check the startup log for the channel's Build and upstream protocols, then inspect the first failing search request. A Responses channel marked enabled must implement the Responses hosted tool; Messages must support `web_search_20250305`; Chat must support its selected `chat_search_dialect`. A channel marked false instead needs a valid `[models].web_search` / `GROK_WEB_SEARCH_MODEL` selection or usable official xAI credentials. Selecting that custom route temporarily makes it backend-search enabled, so a provider rejection or the non-retryable “did not complete backend web_search” error means the upstream capability is missing; the proxy will not hide it with another fallback. `web_fetch` is independent but can still be removed by active tool permissions. ### A request returns 401, 403, 429, 502, or 503 Run `hellogrok routes` and inspect **Status and logs**. Confirm the channel URL, backend, credential source, model identifier, and provider availability. An upstream outage, rate limit, unsupported payload, or stripped search tool must be fixed by the provider or relay. hellogrok preserves the upstream status and error body. Structured rate-limit, timeout, overload, and temporary-unavailable errors remain retryable, and the proxy first retries them inside its own absorb window (`UP absorb` log lines) before the client ever sees them; once the window is exhausted they pass through retryable and Grok Build's retry indicator shows the reason (`Service unavailable | Retrying (N/M)`, `Request timed out (504) | Retrying (N/M)`), so the failure stays visible while the native budget continues. Deterministic errors (authentication, permission, billing, insufficient-balance/quota, invalid request/model) receive `X-Should-Retry: false` and surface immediately by default; the opt-in `[models]` `error_resilience = "balanced"` setting also retries them inside the absorb window first with 30s/60s waits, because relays routinely recover on their own (token rotation, permission fixes, config reloads), and the provider explanation still passes through unchanged if the window is exhausted — with one explicit trade-off: an error that outlasted a long window is the last attempt's snapshot, not a live one, so hellogrok stamps `X-Hellogrok-Absorb-Delay` on the response and prefixes structured error messages with a `[hellogrok: upstream stayed failing for ...]` note. The delay you see is real; the error is as fresh as the last retry. If a relay returns only a generic `503`, hellogrok cannot infer that the account is out of balance; the relay must return a billing code or message. A 502 can also mean that an upstream returned a malformed success response. hellogrok validates the minimum Responses, Messages, or Chat Completions envelope before forwarding it; the log identifies the missing or invalid field. ### `Request timed out (408)` from a relay An upstream 408 between two servers is never the RFC "client took too long" case — there is no idle client between hellogrok and the relay. Cloudflare and relay gateways emit 408 for edge-side timeouts while the origin is queued or restarting. hellogrok therefore treats 408 as a 504: it is retried inside the absorb window, and a 408 that outlasts the window passes through as a retryable 504 so Grok Build keeps its native retry budget instead of failing the turn on its terminal 408 classification. A recurring 408 that never clears inside the window means the relay is saturated for minutes at a time; switch channels or ask the relay operator about queue capacity. ### `Server error (525)` from a relay HTTP 525 means the Cloudflare edge in front of the relay could not complete a TLS handshake with the relay's own origin; 526 means the origin certificate is invalid. The request never reached the relay's application. Grok Build classifies both statuses as terminal — a broken origin certificate never clears on its own — and shows its generic server-error copy, so the turn fails on the first occurrence unless the failure clears inside hellogrok's absorb window, which retries 525/526 inside the proxy by default. A single isolated 525 that disappears on resend is the relay's origin restarting or rotating certificates; check the relay's status page. If a channel reports 525 repeatedly, its origin certificate or TLS termination is misconfigured and only the relay operator can fix it: switch channels with `/model`, or raise `absorb_retry_max_secs` when the relay's rotations routinely take longer than the default window. ### Stuck on “retrying” forever Grok Build retries retryable statuses (429, 5xx) up to 15 times, roughly 5.5 minutes per turn. Transient provider overload (a busy `503`) is hidden first by hellogrok's absorb window and then paced by a synthesized `Retry-After`, so a short outage usually never reaches the UI at all. A turn that stalls with heartbeats but no content first waits out the channel's idle deadline — 900 seconds when hellogrok manages it — before the retry phase begins; a stream that never produces content past the watchdog margin is converted to a retryable `proxy_stream_error` (logged as `SSE stalled: no content reached Grok Build for …`) instead of dying on the client's non-retryable `IdleTimeout`. When a relay's origin stays down for longer than the absorb window, the client keeps its full retry budget and the UI stays in the retrying phase — the expected native behavior. For a channel that is truly unreachable (connection refused, DNS, TLS handshake), retries can never succeed. Set `dead_channel_fail_fast = true` on that channel to opt into the dead-channel breaker: after 6 consecutive dial-level failures (configurable via `dead_channel_fail_threshold`) the proxy answers `503 proxy_circuit_open` with `X-Should-Retry: false` so the turn fails in seconds instead of burning the budget. After a 5-minute cooldown one probe request is allowed through; any upstream response closes the breaker automatically, a failed probe re-arms the cooldown. A probe that never reports an outcome is presumed lost after a 2-minute lease, so a disconnected or header-timed-out probe cannot latch the channel open forever. Busy `503`, `429`, timeouts, and transport resets never count toward the breaker. When you see `proxy_circuit_open`: - The channel is unreachable at the network level, not merely busy. Check the relay's own status and your network path; the `UP breaker` lines in the proxy log record the dial-level failures. - If connectivity is back, wait about 5 minutes and send again; the probe closes the breaker automatically without restarting the proxy or reselecting the model. - If the error repeats, switch to another channel with `/model` until the relay is fixed. Prefer `[model."full.ID"]` when a channel ID contains dots. TOML interprets an unquoted `[model.foo.bar]` as nested tables, so Grok Build originally sees only `foo`; hellogrok temporarily normalizes and validates that header while enabled. Dots or dashes in `name` do not participate in authentication. ### Agent tried calling a tool that doesn't exist Grok Build dispatches by exact `client_name` (`list_dir`, not `List`). A third-party or relayed model may emit Claude/Codex names, a top-level Chat `name`, an empty `function.name` on a later SSE chunk, XML in the message body, or a direct MCP name. A relay can also lose a tool call's first streamed delta, leaving an empty name and arguments missing their opening `{"`. Current hellogrok reassembles Chat tool streams, holds Messages `tool_use` blocks and Responses `function_call` items until their stop frames, repairs truncated argument prefixes, recovers empty names from argument shape, rewrites those calls to the tools declared on that request, and repairs history `name` fields on live responses and replayed history alike. A call that still has no name after shape recovery is routed to a self-healing carrier (`run_terminal_command` for parseable arguments, `read_file` for corrupt arguments, logged as `unresolved-name-routed`) so Grok Build parses it and the raw arguments return to the model through a `tool_result` instead of dying as a silent terminal `NotFound`. Restart both hellogrok executables after upgrading, then start a **new** session. An older conversation can already contain an unmapped `List` or empty name; repairing the live response does not rewrite stored history from before the upgrade. If the log shows `tool identity adapted List->list_dir` (or `->use_tool`) and the TUI still reports a missing tool, the model called a name that was not declared — hellogrok will not invent it. ### `tool_use` IDs have no immediately following `tool_result` Missing Chat or Messages response IDs are normalized before Grok Build receives them. Responses stream identities are associated by `output_index`; when a relay reuses one item ID across output slots, the colliding later slot is remapped to a fresh ID that keeps the provider's type prefix (`rs_`, `ws_`, …) and every later `item_id` event for that slot is rewritten consistently — the turn survives instead of failing after repeated 500s. The same tolerance covers Chat Completions `tool_call` IDs reused across parallel calls. Same-slot identity conflicts are still rejected as errors. If an older Chat session already contains missing IDs, only uniquely associated call/result pairs can be repaired. Start a new session when the history is ambiguous. Repair does not reconstruct missing tool names, arguments, or results. This provider error means the Messages conversation history is structurally invalid: every assistant message containing one or more `tool_use` blocks must be followed immediately by one user message whose leading `tool_result` blocks resolve that entire batch. hellogrok validates native history and also groups parallel Responses calls/results into one adjacent Messages assistant/user pair before the provider call. Missing results still return a non-retryable `400`; they are never invented because that would corrupt tool state. ### Messages reports `serialization error: missing field signature` Restart the proxy with the current build. Some Messages-compatible relays omit the required empty `signature` from a streamed `thinking` block start and provide the real opaque value later through `signature_delta`. Grok Build's native Messages decoder rejects that incomplete start before it can consume the delta. hellogrok completes only the missing protocol field and preserves the real delta unchanged for subsequent turns. If the relay never sends a real signature at all, verified hidden reasoning cannot be reconstructed downstream and the provider must fix its Messages response. ### Output arrives all at once For slow responses, distinguish upstream response-header time from stream completion in the proxy log. `UP absorb` records retry waits; reasoning effort, prompt size, tool loops, and provider queueing can also increase latency. A short request is not a reliable benchmark for a long tool-enabled conversation. For a streaming request, hellogrok sends `stream=true` to the selected provider API. Non-capable channels keep native SSE. Capable Messages and Chat channels are translated incrementally into Responses events so Grok Build can consume reasoning, text, function calls, `web_search_call`, sources, and terminal status. If the log reports a buffered fallback, the upstream returned one complete JSON response and true streaming was unavailable for that request. Current Grok Build has two Responses source paths: hosted search reads `web_search_call.action.sources`, while its client `web_search` tool reads URL citations from `output_text.annotations`. For hosted search, non-empty source URLs produce the collapsed `(N sites)` suffix and a numbered expandable body; with no URLs the item has no source count or expandable content. Before forwarding Responses hosted search, hellogrok adds `web_search_call.action.sources` to `include` without replacing caller entries. Closed-enum lookalikes are negotiated instead of assumed: Volcengine Ark (`*.volces.com`) omits the hint ahead of time and receives a sanitized hosted-search declaration, while any other upstream that proves its schema closed with a 400 triggers one in-request rewrite-and-replay without retained per-host state. Citation entries missing the index fields Grok Build's parser requires are filled with `0` on every route. DeepSeek currently accepts but silently ignores this hint, so its native Responses search action is normalized directly and source display is limited to URLs it actually returns. Keeping the hint makes later DeepSeek support automatic. Responses-level `citations`, Messages search-result/citation blocks, and Chat `annotations`, `citations`, `search_results`, or `web_search_results` are normalized into both Grok Build paths. The WebSearchClient adapter uses the same normalization and, only when search execution is independently confirmed, recovers valid HTTP(S) links from the final answer. A normal answer link does not create a search call. If a provider returns no real URL, search activity can still be shown but a trustworthy site count cannot be fabricated. ### `unknown variant keepalive` or an endless `Waiting for response...` Upgrade both hellogrok executables to the same current release or build, then restart the proxy. Some relays inject private `keepalive`, `keep-alive`, `keep_alive`, `heartbeat`, or `ping` events into an SSE stream. Grok Build's strict Responses deserializer rejects such JSON events even while upstream generation continues. hellogrok absorbs these names from the SSE `event:` field, JSON `type` or `event`, raw data payloads, and empty-data heartbeat frames, then emits the standards-compatible `: keepalive` comment. A completed Responses event, Messages `message_stop`, or Chat Completions `[DONE]` also closes the upstream request immediately rather than waiting for the provider socket. A clean close after a protocol stop signal — Chat Completions `finish_reason`, Messages `stop_reason`, or Responses output items / completed `status` — is completed by synthesizing the missing trailer (`[DONE]`, `message_stop`, or `response.completed`); it is not rewritten as `proxy_stream_error`. The completion log includes `heartbeats=`. If the same error remains while that counter stays zero, confirm that Grok Build is routed through the current proxy with `hellogrok routes`; the provider is likely emitting a different private event name that should be diagnosed from a credential-free stream capture rather than added as a model-specific workaround. For ordinary channels, hellogrok waits at most 601 seconds for upstream response headers and at most 601 seconds between reads from any response body, including non-streaming JSON and error bodies. This stays one second beyond Grok Build's 600-second shell default, so Grok Build remains the owner of its native timeout classification. First-party `api.deepseek.com` routes use 660 seconds because DeepSeek can queue a request for up to ten minutes. A non-streaming queue newline or streaming heartbeat counts as data and renews that idle window. A header timeout is a soft failure: the proxy first retries it inside the absorb window, then returns retryable `504` when the window is exhausted. While reading a non-streaming body the timeout returns retryable `504` immediately; after a `200` stream has begun, hellogrok emits the receiving protocol's `proxy_stream_error` and closes the upstream. There is no total request timeout, so an active long-running response is not terminated. Byte activity and content progress are tracked separately: heartbeats renew the upstream byte window above, while Grok Build's content-progress timer only counts real content (text, reasoning, tool-call deltas, terminal signals). Every streaming path therefore also runs a downstream content watchdog, mirrored on the channel's idle deadline minus 30 seconds; when a stream carries bytes but no content past that point, the proxy closes the upstream and emits a retryable `proxy_stream_error` so the turn enters Grok Build's native retry budget. The `response_model` log line shows the upstream-declared and configured models; `mismatch=true` identifies a relay that silently substituted a model, while `conflict=true` means different response frames declared different models. ### Automatic compaction never starts Grok Build does not add each response's `usage.total_tokens` to a running sum. On a terminal Responses event, the current public source first replaces the typed `total_tokens` with `usage.context_details.input_tokens + usage.context_details.output_tokens` when both private fields are valid; without that extension, the provider's `total_tokens` passes through. Chat state then treats the resulting value as the current complete-context measurement, replaces its token baseline with it, adds only locally estimated items produced since that response, and checks the configured auto-compaction threshold before the next model sample. hellogrok applies that contract uniformly instead of by provider name. A complete, valid Responses pair may use `input_tokens` / `output_tokens` or the common `prompt_tokens` / `completion_tokens` aliases; detail containers are normalized the same way. Messages and Chat usage converted to Responses follows the same rule. Only a complete trustworthy pair receives `context_details`; missing, conflicting, negative, fractional, overflowing, placeholder, or live-context counts larger than the known `context_window` remain `usage: null` so Grok Build keeps its previous baseline. DeepSeek's 1M context is a shared input-plus-generated-output budget, and Responses `max_output_tokens` includes both hidden reasoning and visible output. Reaching that per-response cap produces `status = "incomplete"` with reason `max_output_tokens`; Chat reports `finish_reason = "length"`, which hellogrok maps to the same incomplete result. The partial response remains usable, but it may contain little visible text if reasoning consumed the allowance. Continue in another turn, request a smaller reasoning effort, or raise the output allowance only when the remaining total context permits it. Output-cap exhaustion alone is not an auto-compaction signal. A context-length error near `664K / 1.0M` is not a hidden `500K` limit and does not by itself show a broken counter. With a `1,048,576` context window and `max_completion_tokens = 384000`, the provider can reject as soon as the messages exceed `664,576`, because it validates the full requested completion allowance too. For example, `664,712 + 384,000 = 1,048,712`, which exceeds the window by 136 tokens even though message utilization is only about 63%. The automatic budget reserves that output plus a 5% margin and limits an 85% preference to 58%, so subsequent turns compact earlier. If the correct capacity was not known before this request, hellogrok still recognizes the exact structured rejection, changes only the outgoing completion allowance to `maximum context - message tokens`, and retries once without falsifying displayed usage. A display above 100% during an active tool loop has a different cause. Grok Build performs its normal threshold check before sampling, but after a tool result the loop's separate preflight check triggers only when the estimate is already greater than the context window. Large tool output can therefore move the live estimate from below the threshold to well over 100% before that guard runs; compaction then reduces the same active context. This is an estimated live-context value, not accumulated billing usage. Older hellogrok builds filled missing provider usage with zero-valued fields. That made a syntactically valid response report `total_tokens: 0`, so Grok Build repeatedly reset its baseline to zero and never reached the configured threshold. Current builds preserve trustworthy provider totals, derive a total only when complete input and output measurements exist, and emit `usage: null` for missing, partial, negative, fractional, all-zero placeholder, or otherwise untrustworthy accounting. A native Responses measurement with positive complete `context_details` remains usable even if its separate billing counters are zero. For first-party DeepSeek, hellogrok also requests streaming usage and preserves the provider's billing `total_tokens`. Capacity metadata follows one rule across DeepSeek, GPT, Claude, Grok, Gemini, and other channels: an explicit `[model.*]` or inherited `[model_providers.*]` value is authoritative; without one, valid upstream `X-Grok-*` headers are passed through and learned. A context rejection may also disclose the window through `context_window`, `max_context_tokens`, `maximum_context_length`, `maximum_context_tokens`, `model_context_window`, or `max_model_len`; hellogrok accepts only one positive, unambiguous, non-overflowing value. Provider-only configured `max_completion_tokens` and trustworthy learned `context_window` values are temporarily projected onto `[model.*]` where current Grok Build inheritance or response-header downgrade behavior would otherwise use the wrong value. The terminal usage updates the numerator. Grok Build computes the trigger from the full denominator, so hellogrok temporarily lowers each unsafe model threshold with the documented output reserve and margin. Current Grok Build deliberately ignores a context-window response-header downgrade. Forwarding the header alone therefore cannot correct a catalog or fallback denominator, but hellogrok now places a trustworthy learned window into the temporary model configuration and reloads open sessions. This can only happen after the provider exposes the value, so explicit `context_window` and `max_completion_tokens` remain the only predictable first-turn setup. A relay that omits both capacity and usage cannot be made exact without provider metadata, a tokenizer, and hidden prompt overhead. `usage: null` deliberately preserves Grok Build's prior baseline instead of corrupting it. This is why the built-in `grok-4.5` display is normally correct: Grok Build's model catalog supplies its 500,000-token window and model-specific 80% threshold, while the native xAI Responses stream supplies the live-context `context_details` expected by the sampler. An unknown custom model instead falls back to 200,000 tokens unless configured or discovered, and older hellogrok versions could reset its live baseline to zero. There is no generic hidden 500K cutoff. If a completed or restored session is already at its effective threshold, the next user prompt triggers Grok Build's pre-sampling auto-compaction before any provider request. hellogrok's process-level test covers that resumed-session path through Responses, Messages, and Chat Completions. A large tool result during an active turn can still cross the threshold between checks; the next loop guard then compacts that same active context. Complete input/output usage gives an exact baseline for ordinary DeepSeek requests. Server-side Web Search may return cumulative billing totals instead of the final live prompt. When a prompt or output count exceeds the known `context_window`, hellogrok discards that measurement (`usage: null`) rather than asking Grok Build to compact on an impossible live context. A trustworthy in-window pair, including a provider `context_details` extension that itself fits the window, still updates the baseline. The same guard applies to non-DeepSeek channels. A provider that omits usage can only retain the prior baseline and grow approximately. Explicit `context_window` remains required for this check. The mirror failure is a meter that jumps far above the configured threshold in one step. Grok Build writes the reported `total_tokens` straight into its context meter, so a single relay response that double-counts the cached prefix — or reports session-cumulative prompt tokens — can move a session from half full to past the auto-compact threshold although the conversation never grew. hellogrok therefore cross-checks every usage report against the tokenizable request body per conversation and drops reports whose prompt count overshoots the body-derived expectation by more than 25%, leaving Grok Build on its byte-based estimate until the provider reports coherently again. When compaction still fires at a surprising percentage, compare the proxy log's `usage suppressed` line with the request body size: a suppressed report is a provider misreport, not a threshold misconfiguration. ### The Grok Build window stops accepting clicks First distinguish an unresponsive interface from a long active task. If hellogrok and Grok Build logs continue to show requests, heartbeats, tool calls, or phase transitions, the agent is still running or waiting in the provider queue; DeepSeek may legally keep that request queued for ten minutes. The body-idle limits above now prevent a silent upstream from holding the proxy path forever. If the whole window truly stops processing input while logs also stop advancing, the HTTP proxy alone cannot establish a UI-thread deadlock. Record the exact timestamp, then preserve the matching Grok Build unified log, hellogrok log, Windows Application Hang/crash event, and a process dump if available. A `504` or `proxy_stream_error` identifies a bounded upstream wait; a renderer/process hang without either requires Grok Build-side evidence rather than a provider-specific network workaround. ### A Claude Messages channel selects the wrong model or returns 404 Use `api_backend = "messages"` (plural). Grok Build defines only `chat_completions`, `responses`, and `messages`; hellogrok rejects the obsolete singular spelling. `base_url` must be the API root before `/messages`: for an endpoint at `/v1/messages`, configure a URL ending in `/v1`. A capable Messages channel is temporarily shown to Grok Build as Responses but still calls that upstream `/messages` endpoint. Also ensure `model` is the provider's actual upstream model ID rather than the channel ID. ### An open window did not follow the proxy switch Check the **Grok session hot switch** line in **Status and logs**. Automatic switching applies to idle custom-model sessions on a shared leader and supports both current and legacy ACP model-switch method names. On Windows, a live named-pipe leader misreported as stale by Grok Build 1.0.x is accepted only when its leader lock is actively held. A working or input-blocked session is skipped safely; reselect its current model in `/model` after the active operation finishes. A window started with `--no-leader` exposes no external IPC to hellogrok and also requires manual reselection or a new window. ### `/resume` selects official `grok-4.6` instead of the prior custom channel Upgrade to the current release, enable the proxy, and select the intended custom channel once in `/model`. While enabled, hellogrok temporarily uses the channel's model-table ID as Grok Build's runtime identity and sends the original configured `model` only at the upstream boundary. New and subsequently updated session summaries therefore remain unambiguous even when multiple channels share `grok-4.6`. A historical summary that already contains only `grok-4.6` has no channel evidence to recover automatically; it requires that one explicit reselection. ### Thought appears under the finished answer Grok Build closes the current Thought on the first visible reply. Official grok streams reasoning as a prefix sibling; a later `reasoning_content` / `thinking` delta opens a second Thought under the answer. hellogrok drops post-answer thought on Chat, Messages, and Responses and peels protocol self-talk such as `reply only:` / `任务已全部完成` from prefix CoT. Restart the proxy after upgrading and start a new session; stored bubbles are not rewritten. ### Reasoning text appears inside the reply Thinking models wrap CoT in ``…``, and relays sometimes dump reasoning into `content` mid-turn or with only the closing tag. hellogrok strips think spans wherever they appear in the stream: second-phase spans, unbalanced `` tails, and stray or delta-split closing tags are routed to the reasoning channel or removed instead of leaking into the visible reply. Tags quoted with backticks or double quotes are treated as prose: reasoning that mentions the tags does not end its span early, and quoted tags in the reply are preserved. Reasoning that arrives after visible reply text is dropped by design, because Grok Build renders reasoning only as a prefix Thought. Stored bubbles are not rewritten; restart the proxy after upgrading and start a new session. ### A tool call fails with `missing field` argument errors Grok Build validates tool arguments against the declared schema and echoes the original arguments on failure. Third-party models sometimes emit aliased parameter names (`target_path` instead of `target_file`) or, rarely, no argument fragments at all. hellogrok normalizes known aliases per declared schema and discards argument-less calls to tools with required properties (logged as `empty-args-discarded`), because an empty arguments object can never satisfy them. If the error still appears, the echoed arguments show exactly what the model sent; the failed call is fed back to the model, which usually retries with corrected arguments on the next turn. ### Image input is rejected with `vision_not_supported` Text-only upstreams reject requests that carry image content parts, including images a `read_file` of a picture placed into a tool result. hellogrok recognizes the rejection (`400` with a `vision` error code or a `not multimodal` / `does not support image` message), replaces every image part with a text placeholder, and retries the request once, so the turn continues without the visual payload and the placeholder tells the model the image was omitted. Channels are not remembered as text-only, so each image-bearing request pays one rejected attempt before the strip retry succeeds. ### `read_file` reports `exceeds maximum allowed tokens` This is Grok Build's own per-call cap, not a hellogrok error: one `read_file` window is limited to 25,000 estimated tokens (1,000 lines by default), and a dense file over the cap fails wholesale instead of being truncated. Read such files with `offset`/`limit` ranges, or locate the region with `grep` first. The red `Read … failed` marker is informational; the turn continues with a ranged retry. ### The model keeps repeating a wrong fact from its previous thought Plaintext CoT replay can lock a later user turn onto an earlier generation error. On Chat, hellogrok keeps the model's own `reasoning_content` inside the current tool loop and strips it across user turns, except DeepSeek and MiMo, whose gateways require the field. Encrypted or signed reasoning is never stripped. Start a new session after upgrading; old assistant text already in history is unchanged. ### `List .` in the tool header That is Grok Build's TUI: listing the workspace root is shown as `.`, including for official grok. The call still used the real directory (`.` or the absolute cwd). It is not an empty path and is not rewritten by hellogrok. ### A model switch asks to start a new conversation Grok Build replays all historical reasoning items, including provider-encrypted state, after `/model` changes. hellogrok records the emitting signature domain and removes only known foreign encrypted reasoning from the target request; normal messages, tool calls, tool results, search history, and unencrypted reasoning remain unchanged. For an older conversation whose opaque state predates the local provenance index, hellogrok first preserves the request and performs one clean replay only if the upstream returns a structured signature or decryption rejection. A repeated deterministic rejection is marked non-retryable instead of entering Grok Build's generic retry loop. ### The configuration still points to localhost after a forced exit Ensure no hellogrok process is running, then execute `hellogrok restore`. Do not run `restore` against an active proxy. ### Proxy stop is deferred after editing the active configuration hellogrok merges proxy-managed fields individually during shutdown, so changing `supports_backend_search` or leaving an unrelated TOML setting unfinished does not need to be rolled back before exit. A normal proxy stop is deferred only when recovery still finds an unowned `127.0.0.1:18787` route, the configuration file cannot be accessed, or another manager owns it. Restore the model header or replace the temporary URL with the intended upstream URL for a clean stop. Tray **Exit** always terminates, releases the local port, and leaves unresolved recovery state for the next launch. ### Port `18787` is already in use Stop the process that owns `127.0.0.1:18787` before enabling the proxy. hellogrok claims the port before changing Grok configuration and shows a startup error if it is unavailable; it does not silently switch ports because the rewritten channel URLs and recovery state must agree on one address. An open hellogrok tray keeps this port in diagnostic mode after **Stop proxy** so stale sessions receive a clear error. Use tray **Exit** when another application needs the port. ### Autostart works, but a channel has no credentials Move shell-only environment variables into the persistent user or service environment, then restart the login service. The autostart process cannot inherit variables that existed only in an earlier terminal session. ### A provider manager prevents a clean proxy stop Open the provider manager (for example, CC Switch) and disable its Grok Build takeover before stopping hellogrok. This preserves the intended restoration order. If immediate termination is required, tray **Exit** still closes hellogrok and retains the pending recovery transaction. ## Development Optional installed-client and live-provider checks are separate from ordinary tests. The live probe uses the selected configured channel, an isolated file, and read-only tool availability; it may incur provider charges. Replace the example channel with your own model-table ID. These checks do not establish universal provider compatibility. ```powershell $env:HELLOGROK_GROK_E2E = "1" go test ./internal/proxy -run TestGrokBuildProcess -count=1 -timeout 5m $env:HELLOGROK_LIVE_CONFIG = Join-Path $HOME ".grok/config.toml" $env:HELLOGROK_LIVE_CHANNEL = "my-channel" go test ./internal/proxy -run '^TestLiveProviderReadTool$' -count=1 -timeout 4m ``` Run the local quality checks: ```bash go test ./... -count=1 go vet ./... go run golang.org/x/vuln/cmd/govulncheck@v1.1.4 ./... ``` Windows users with configured live channels can run the integration smoke tests: ```powershell .\scripts\run_grok_all_channels_test.ps1 .\scripts\run_grok_all_channels_test.ps1 -RequireWebSearch -MaxTurns 1 -TimeoutSeconds 150 .\scripts\run_grok_all_channels_test.ps1 -RequireSubagentSearch -MaxTurns 4 -TimeoutSeconds 240 .\scripts\run_grok_all_channels_test.ps1 -RequireWebFetch -MaxTurns 2 -TimeoutSeconds 150 ``` CI runs tests and default builds on Windows, Linux, Intel macOS, and Apple Silicon macOS. It also builds the optional tray target natively on Linux and macOS. Tagged releases produce amd64 and arm64 artifacts for all three operating systems; the release workflow fails a tagged build whose tag does not match `Version` in `internal/appinfo/appinfo.go`. ## Limitations - OpenCode requests without a client conversation identity use operation-scoped IDs, not reconstructed parent/subagent identities. Cross-request routing and cache affinity cannot be guaranteed in this case. Explicit static session headers should not be shared across unrelated conversations. - The Windows status/log window title displays the running application version. Restart both the proxy and its window after upgrading; downloading a release does not replace a running process. - hellogrok cannot create provider-side search capability. A hosted-search channel must actually support search and return its results. - Chat history does not replay plaintext CoT across user turns except on DeepSeek and MiMo. Intra-turn tool-loop reasoning is kept. This matches Grok Build's native Chat mapper plus vendor gateway contracts; it is not a second protocol stack. - Responses-to-Messages/Chat conversation conversion is enabled only for capability-enabled channels (explicit `supports_backend_search = true`, a selected default search model, or a hosted-search request resolved from Grok Build's remote model catalog), plus Grok Build's fixed non-streaming WebSearchClient request. Other cross-protocol requests are rejected. - A relay that removes tool declarations, tool calls, citations, or result events cannot be fully repaired downstream. hellogrok maps declared Grok Build tools and well-known aliases; it does not invent undeclared tools or force a model that never emits a tool call. - A provider that ignores `stream=true` cannot be made truly streaming after its complete JSON response has already arrived; hellogrok logs and uses a buffered compatibility fallback. - Provider-encrypted hidden reasoning is scoped to its emitting signature domain. Cross-domain switching preserves visible conversation and tool history but intentionally omits incompatible private reasoning. - Provider-specific API extensions outside the supported Responses, Chat Completions, and Messages formats may require additional adaptation. - Upstream availability, model access, account pools, rate limits, and gateway errors remain the provider's responsibility. - Optional Unix tray behavior depends on the installed desktop environment; the standard Unix CLI is the portable path. - Current release artifacts are unsigned. Build from source when local trust requirements demand it. ## Contributing 1. Create a focused branch for the change. 2. Follow the existing package boundaries and avoid unrelated refactors. 3. Add or update tests for behavior changes. 4. Run the quality checks above. 5. Update both README files when user-facing behavior changes. 6. Open a pull request describing the problem, approach, and verification results. ## License Licensed under the [MIT License](./LICENSE).