From 04692871cf9bb4fe5d70540735d28f603e25ec85 Mon Sep 17 00:00:00 2001 From: atlas Date: Thu, 2 Jul 2026 21:37:20 +0200 Subject: [PATCH] feat(#2149): set base claude-code env in managed settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `baseClaudeEnv` (always-on) and extend `otelSettingsEnv` with the env vars from #2149: Always-on (every agent): - CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 - CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1 - CLAUDE_CODE_RESUME_INTERRUPTED_TURN=1 (recovers MCP-flap mid-turn) - CLAUDE_CODE_SIMPLE_SYSTEM_PROMPT=1 - CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1 - CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX=hive+ - DISABLE_AUTOUPDATER=1 / DISABLE_UPDATES=1 (nix owns packages) - DISABLE_INSTALL_GITHUB_APP_COMMAND=1 - DO_NOT_TRACK=1 - ENABLE_CLAUDEAI_MCP_SERVERS=0 (hive supplies its own) - FORCE_AUTOUPDATE_PLUGINS=1 OTEL-gated (when hyperhive.otel.enable): - CLAUDE_CODE_ENABLE_FEEDBACK_SURVEY_FOR_OTEL=1 - CLAUDE_CODE_OTEL_DIAG_STDERR=1 - OTEL_METRICS_INCLUDE_VERSION=1 Because baseClaudeEnv includes per-agent values (CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX = "hive+${userName}"), jq is now always run at build time — the OTEL-off branch that returned the static asset verbatim is removed. --- nix/templates/harness-base.nix | 63 ++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/nix/templates/harness-base.nix b/nix/templates/harness-base.nix index e7b0fdb5..81e1f5c5 100644 --- a/nix/templates/harness-base.nix +++ b/nix/templates/harness-base.nix @@ -37,8 +37,38 @@ let # (claude merges the `env` from the user settings on top of these # managed ones), so the token is read from disk at start and never # touches the store. + # Base claude-code environment applied to every agent regardless of OTEL. + # Shipped via the managed settings `env` block so claude and `hivectl + # choom` both inherit them without a launch wrapper. + baseClaudeEnv = { + # Suppress analytics, survey pings, and other non-essential outbound + # traffic — agents are headless and don't need any of that. + CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1"; + DO_NOT_TRACK = "1"; + # Disable claude's self-update machinery; package management is nix's job. + DISABLE_AUTOUPDATER = "1"; + DISABLE_UPDATES = "1"; + # Keep plugin updates synchronized on install; prefer HTTPS for fetches. + CLAUDE_CODE_PLUGIN_PREFER_HTTPS = "1"; + CLAUDE_CODE_SYNC_PLUGIN_INSTALL = "1"; + FORCE_AUTOUPDATE_PLUGINS = "1"; + # Suppress the "install GitHub app" prompt — not applicable in-hive. + DISABLE_INSTALL_GITHUB_APP_COMMAND = "1"; + # Disable Anthropic's hosted claude.ai MCP servers; the hive supplies its own. + ENABLE_CLAUDEAI_MCP_SERVERS = "0"; + # Resume an interrupted turn on reconnect (recovers from transient MCP flaps). + CLAUDE_CODE_RESUME_INTERRUPTED_TURN = "1"; + # Use the simpler system prompt variant suited to headless operation. + CLAUDE_CODE_SIMPLE_SYSTEM_PROMPT = "1"; + # Tag remote-control sessions with the hive+agent name for identification. + CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX = "hive+${userName}"; + }; otelSettingsEnv = { CLAUDE_CODE_ENABLE_TELEMETRY = "1"; + # Attach feedback-survey data to the OTEL pipeline. + CLAUDE_CODE_ENABLE_FEEDBACK_SURVEY_FOR_OTEL = "1"; + # Emit OTEL SDK diagnostics to stderr for easier log capture. + CLAUDE_CODE_OTEL_DIAG_STDERR = "1"; OTEL_METRICS_EXPORTER = "otlp"; OTEL_LOGS_EXPORTER = "otlp"; OTEL_TRACES_EXPORTER = "otlp"; @@ -51,6 +81,8 @@ let OTEL_RESOURCE_ATTRIBUTES = "service.name=hyperhive-agent,agent=${userName},hive=${hiveDisplayName},swarm=${swarmDisplayName}" + lib.optionalString (otelCfg.extraResourceAttributes != "") ",${otelCfg.extraResourceAttributes}"; + # Include the Claude Code version label in emitted metrics. + OTEL_METRICS_INCLUDE_VERSION = "1"; } // lib.optionalAttrs (otelCfg.metricIntervalMs != null) { OTEL_METRIC_EXPORT_INTERVAL = toString otelCfg.metricIntervalMs; @@ -1176,25 +1208,26 @@ in # deliberately NOT shipped here: effort is controlled live via the # `--effort` CLI flag (HIVE_DEFAULT_EFFORT / the per-agent UI slider), # which managed scope would otherwise override and lock. - # Base hive-enforced settings, plus — when OTEL is enabled — an `env` - # block so Claude Code exports telemetry natively from the settings - # file it reads in every context (harness turn-loop AND `hivectl - # choom`), with no launch wrapper. With OTEL off it's the shared - # static asset verbatim; with OTEL on it's a per-agent merge (the - # resource attributes carry the agent name) done at BUILD time via - # `jq` — not eval-time `readFile`, which would be import-from- - # derivation. + # Hive-enforced settings merged with a per-agent `env` block at BUILD + # time via `jq` (not eval-time `readFile`, which would be import-from- + # derivation). The `env` block is always present: `baseClaudeEnv` sets + # behaviour flags and the remote-control session prefix for every agent; + # `otelSettingsEnv` is merged on top when OTEL is enabled. claude-code + # auto-discovers this file in every context (harness turn-loop AND + # `hivectl choom`) so no launch wrapper is needed. environment.etc."claude-code/managed-settings.json".source = let baseSettings = "${pkgs.hyperhive-assets}/share/hyperhive/prompts/claude-settings.json"; + # Merge base env (always) with OTEL env (when enabled). jq is always + # run — `baseClaudeEnv` contains per-agent values (e.g. + # CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX) that can't live in the + # static store asset. + allEnv = baseClaudeEnv // lib.optionalAttrs otelCfg.enable otelSettingsEnv; in - if !otelCfg.enable then - baseSettings - else - pkgs.runCommand "managed-settings.json" { nativeBuildInputs = [ pkgs.jq ]; } '' - jq --argjson env ${lib.escapeShellArg (builtins.toJSON otelSettingsEnv)} \ - '. + { env: $env }' ${baseSettings} > "$out" - ''; + pkgs.runCommand "managed-settings.json" { nativeBuildInputs = [ pkgs.jq ]; } '' + jq --argjson env ${lib.escapeShellArg (builtins.toJSON allEnv)} \ + '. + { env: $env }' ${baseSettings} > "$out" + ''; # Inject the OTEL auth header (a secret) into the agent's *user* # claude settings at runtime, keeping it out of the world-readable