···115115 ];
116116117117 settings.model = {
118118- # The cluster serves Qwen3.6-27B-FP8 (also aliased as "default" in
119119- # /v1/models — both resolve to the same weights); pin the explicit id so
120120- # logs and 400s name the actual model. Verified against the live endpoint
121121- # that it emits native tool_calls, which Hermes Agent requires.
122122- default = "Qwen3.6-27B-FP8";
123123- # hermes's built-in "nous" provider is OAuth-only and hardcodes the dead
124124- # host inference.nousresearch.com (NXDOMAIN) — true on both 0.17.0 and
125125- # main, so we drive the cluster's OpenAI-compatible endpoint instead.
118118+ # Claude Opus 5.5 on lass's Claude subscription: the anthropic provider
119119+ # takes the `claude setup-token` OAuth token (sk-ant-oat…) from
120120+ # ANTHROPIC_TOKEN (hermes-anthropic generator below) and sends it as a
121121+ # Bearer token. Anthropic bills third-party apps on subscription tokens
122122+ # as extra usage; hermes renames some tools on the OAuth wire to avoid
123123+ # that classification (agent/anthropic_adapter.py), which goes against
124124+ # Anthropic's usage policies and risks the account. Chosen knowingly.
126125 #
127127- # It MUST be a *named* custom provider ("custom:<name>" + a
128128- # custom_providers entry), not bare "custom". Bare custom resolves its
129129- # credential from a host-gated candidate list
130130- # (hermes_cli/runtime_provider.py:1244) that only forwards
131131- # OPENAI_API_KEY when base_url's host is openai.com — a deliberate
132132- # anti-credential-leak measure (upstream #28660 / GHSA-76xc-57q6-vm5m).
133133- # For any other host it silently substitutes the literal placeholder
134134- # "no-key-required" and the endpoint 401s. A named entry instead reads
135135- # the key from its declared key_env (runtime_provider.py:692), which is
136136- # not host-gated. Named entries also ignore config.yaml's model.api_key,
137137- # so a stray `hermes model` run can no longer shadow this with a stale
138138- # key (that is exactly how the Nous→cluster switch broke).
139139- provider = "custom:llama";
140140- base_url = "https://inference.p0.contact/v1";
141141- # The custom provider can't auto-detect limits, so unset it leaves
142142- # context uncompressed and defaults max_tokens to 65536 — once history
143143- # outgrows the window, input + max_tokens exceeds it and the endpoint
144144- # 400s ("check the model name and other parameters"). vLLM reports
145145- # max_model_len 262144 for this model: pin that as the window so hermes
146146- # compresses in time, and keep a sane output cap (covers thinking +
147147- # reply) so input + max_tokens always stays under it.
148148- context_length = 262144;
149149- max_tokens = 16384;
126126+ # Context (1M) and output (128k) limits come from hermes's own model
127127+ # tables, so nothing is pinned here.
128128+ provider = "anthropic";
129129+ default = "claude-opus-5-5";
150130 };
151131152152- # The endpoint hermes actually authenticates against. key_env names the
153153- # env var carrying the token; it comes from the hermes-env generator below
154154- # via environmentFiles, so the secret never enters the nix store.
155155- # api_mode is pinned because auto-detection only runs as a fallback.
132132+ # The shared llama cluster stays configured as a named provider, so
133133+ # `/model Qwen3.6-27B-FP8 --provider custom:llama` switches back. Its key
134134+ # comes from hermes-env (key_env), never the nix store. It MUST be a
135135+ # *named* custom provider ("custom:<name>" + this entry), not bare
136136+ # "custom": bare custom resolves its credential from a host-gated list
137137+ # (hermes_cli/runtime_provider.py:1244) that only forwards OPENAI_API_KEY
138138+ # to openai.com and otherwise sends "no-key-required" (upstream #28660 /
139139+ # GHSA-76xc-57q6-vm5m). api_mode is pinned because auto-detection only
140140+ # runs as a fallback.
156141 settings.custom_providers = [
157142 {
158143 name = "llama";
···559544 share = true;
560545 };
561546547547+ # Claude subscription OAuth token for the anthropic provider: run
548548+ # `claude setup-token` (long-lived, sk-ant-oat…) and paste it at the
549549+ # prompt. persist=true so it is asked once.
550550+ clan.core.vars.generators.hermes-anthropic.prompts.oauth-token = {
551551+ description = "Claude subscription OAuth token from `claude setup-token` (sk-ant-oat…)";
552552+ type = "hidden";
553553+ persist = true;
554554+ };
555555+562556 # Assemble the .env Hermes reads (mirrors the opencrow-env pattern).
563557 clan.core.vars.generators.hermes-env = {
564558 dependencies = [
···566560 "hermes-llama"
567561 "hermes-api"
568562 "radicale-hermes"
563563+ "hermes-anthropic"
569564 ];
570565 files."hermes.env" = { };
571566 runtimeInputs = [ pkgs.coreutils ];
···575570 LLAMA_API_TOKEN=$(cat "$in"/hermes-llama/llama-api-token)
576571 API_SERVER_KEY=$(cat "$in"/hermes-api/api_key)
577572 CALDAV_PASSWORD=$(cat "$in"/radicale-hermes/password)
573573+ ANTHROPIC_TOKEN=$(cat "$in"/hermes-anthropic/oauth-token)
578574 EOF
579575 '';
580576 };