feat(adopt): connect Grok Build, Qwen Code and Hermes Agent - #265
Merged
Merged
Conversation
Takeover (diff preview, backup, restore) and MCP/skills/hooks scanning for three more clients, all marked FieldsOnly. - Grok Build: one [model."thinkwatch/<model>"] table per gateway model in ~/.grok/config.toml, each with its own api_key so Grok never falls back to sending the xAI session token; models.default and features.campaigns. - Qwen Code: a custom "thinkwatch" provider (providerProtocol = openai, /v1) in ~/.qwen/settings.json with the key in settings.env, so requests keep Qwen Code's own User-Agent. - Hermes Agent: model.provider = custom with base_url/api_key/api_mode/ default in ~/.hermes/config.yaml (default profile only); other and active profiles and a CUSTOM_BASE_URL in .env are called out. - Scan: their MCP servers and hooks, skills (Hermes' category layout), commands, agents and instruction files; Cursor's hooks.json, which Grok also runs. - README: the one-step client list and the MCP client count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Lite can now take over three more clients and include them in the MCP list and the security scan. All three are marked FieldsOnly: the field names were checked against each client's source, but none of the clients was installed or run on this machine.
$GROK_HOME/config.toml(~/.grok)$QWEN_HOME/settings.json(~/.qwen)$HERMES_HOME/config.yaml(~/.hermes, default profile)What each client gets:
unverified_format).Config location:
GROK_HOME,QWEN_HOMEandHERMES_HOME, plusHERMES_DATA_DIR_SUFFIX. Hermes on Windows defaults to%LOCALAPPDATA%\hermes.Grok Build
The takeover writes one table per gateway model. With the gateway offering
claude-sonnet-5andgpt-5.5, an empty config becomes this (comment block shortened):Every table carries its own
api_key. Without one, Grok would send the user's xAI session token to the gateway. Two facts in the source make this so:api_key/env_key, then an auth-provider token, then the session token, thenXAI_API_KEY(config.rs).may_receive_sessionreturnstruefor every URL (backend/grok.rs).When the gateway has no key, each table gets
api_key = "no-key"for the same reason.Other Grok details:
api_backend:messagesforclaude*responsesforgpt-*, the o-series,*codex*andgrok*chat_completionsfor everything elsemodels.default:features.campaigns = false: remote campaign patches can rewritemodels.default(26-config-reference.md). This is stated in the cost list.clients::stale_container).adopt.plan.no_models_nothing_writtensays so. The same applies to Qwen and Hermes.requirements.toml: counts as an overriding file only when it sets models (26-config-reference.md).Qwen Code
{ "modelProviders": { "thinkwatch": [ { "id": "claude-sonnet-5", "name": "claude-sonnet-5 (ThinkWatch)", "baseUrl": "http://127.0.0.1:8788/v1", "envKey": "THINKWATCH_QWEN_API_KEY" }, { "id": "gpt-5.5", "name": "gpt-5.5 (ThinkWatch)", "baseUrl": "http://127.0.0.1:8788/v1", "envKey": "THINKWATCH_QWEN_API_KEY" } ] }, "providerProtocol": { "thinkwatch": "openai" }, "env": { "THINKWATCH_QWEN_API_KEY": "<gateway key>" }, "security": { "auth": { "selectedType": "openai" } }, "model": { "name": "claude-sonnet-5", "baseUrl": "http://127.0.0.1:8788/v1" } }Requests always use the OpenAI wire with
/v1, never theanthropictype. Theanthropictype presents itself asclaude-clion any host that is not Anthropic's own (anthropicContentGenerator.ts, L476-L486).This departs from "add them to the
openailist". Instead, the takeover writes a custom provider id,thinkwatch, and maps it to theopenaiprotocol throughproviderProtocol(model-providers.md). There are two reasons:openailist would leak the user's OpenAI key to the gateway.modelProviders(model-providers.md#L23), but it does not reloadsettings.env.THINKWATCH_QWEN_API_KEYexists.OPENAI_API_KEYorsecurity.auth.apiKey(modelConfigResolver.ts), so the user's OpenAI key would go to the gateway.providerProtocolis read only at startup (hot-reload.ts). A custom id therefore stays unusable until the restart that also loadssettings.env./authrewrites theopenailist. A separate id survives/auth, and the user's ownopenaientries stay untouched.providerProtocolfirst shipped in v0.19.3 (0d20772), which is where the version note comes from. The key goes intosettings.env, which has the lowest priority (auth.md).THINKWATCH_QWEN_API_KEYis listed among the environment variables that can override what was written.Hermes Agent
api_mode:anthropic_messagesforclaude*, otherwisechat_completions. Plaincustomignorescodex_responseson hosts that are not OpenAI (runtime_provider.py#L183-L193).provider: custommakes Hermes trustmodel.base_url(runtime_provider.py#L65-L81).CUSTOM_BASE_URLstill wins over it (runtime_provider_backends.py).~/.hermes/.envsetsCUSTOM_BASE_URL, Lite shows that file as overriding the config.active_profilemakes a plainhermesstart in another profile (main.py).hermes profile use default.model: "<string>"form: the takeover is refused as a parse error, and nothing is written.What the gateway answers on Hermes' probe paths (findings only; core is not changed)
For a local endpoint, Hermes runs a series of probes with
Authorization: Bearer <key>and a 2-second timeout (model_metadata.py#L717-L772):GET /api/v1/models: any 200 means LM Studio./api/tags: a 200 withmodelsmeans Ollama./v1/props, then/props: means llama.cpp./version: means vLLM.A negative result is cached for 5 minutes (L74-L80).
This was measured against twcore 0.57.0, using an isolated instance and a fake upstream that returns 404 for everything except
/v1/models:GET /api/v1/models,/api/tags,/v1/props,/props,/versionGET /v1/models,/v1/models/m1passthrough(server.rs)./v1/api/v1/models,/v1/api/tags,/v1/propstwice, and/v1/version.adopt.cost.hermes_agent.probessays so.ModelUnavailable(failure.rs#L71), which does not pause the upstream (health.rs#L173).POST …/api/v1/modelsgets that 200 passed through, and Hermes would conclude the gateway is LM Studio. This was confirmed with the fake upstream.Scanning
config.toml([mcp_servers],[hooks])hooks/*.json,skills/,commands/*.md,agents/*.md,rules/*.md,AGENTS.md.grok/(07-mcp-servers.md, 10-hooks.md)settings.json(mcpServers, includinghttpUrl;hooks)skills/,commands/*.md|*.toml,agents/,rules/,QWEN.md,AGENTS.md.qwen/…andQWEN.mdconfig.yaml(mcp_servers,hooks)SOUL.md.hermes.md,HERMES.mdand.hermes/skillshooks.json(user and project level) is listed under Cursor. Cursor runs it, and Grok also runs it by default (10-hooks.md). When Grok Build is installed, the hooks section of the MCP page adds one sentence saying this.~/.agents/skillsstays under the shared folder from feat(adopt): connect Pi and oh-my-pi; list the shared skills folder #263. Grok and Qwen read it too (storage.ts).Also in this PR
api_keyof other Grok tablesenvandsecurity.auth.apiKeyhermesagent(MIT, the same 1.95.1 package as the other marks).Not verified / caveats
disable_api_key_auth,force_login_team_uuid,GROK_DISABLE_API_KEY_AUTH), Grok treats loopback as an xAI URL and may swap our per-model key for the session token. Not tested.[models] extra_headersandenv_http_headersalso apply to our tables.GROK_CONFIGoverlay andallowed_modelscan override or block our tables.--bareignores settings entirely.NO_PROXY, which the cost list says./etc/hermesfiles override the user config.HOOK.yaml/handler.py) and Python plugins are not scanned.$VARinsideHERMES_HOMEis not expanded by Lite.Checks
cargo fmt --all -- --check: passcargo clippy --all-targets -- -D warnings: passcargo test(whole workspace, with twcore 0.57.0 inresources/): all passUPDATE_MSG_CODES=1/UPDATE_TS=1regeneration: no diffnpx tsc --noEmitandnpx tsc --noEmit -p scripts/shots: passnpx vitest run: 611 passed🤖 Generated with Claude Code