diff --git a/nix/templates/harness-base.nix b/nix/templates/harness-base.nix index 39ac5684..e7b0fdb5 100644 --- a/nix/templates/harness-base.nix +++ b/nix/templates/harness-base.nix @@ -37,38 +37,8 @@ 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 "-" for identification. - CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX = "${hiveDisplayName}-${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"; @@ -81,8 +51,6 @@ 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; @@ -1208,26 +1176,25 @@ 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. - # 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. + # 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. 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 - pkgs.runCommand "managed-settings.json" { nativeBuildInputs = [ pkgs.jq ]; } '' - jq --argjson env ${lib.escapeShellArg (builtins.toJSON allEnv)} \ - '. + { env: $env }' ${baseSettings} > "$out" - ''; + 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" + ''; # Inject the OTEL auth header (a secret) into the agent's *user* # claude settings at runtime, keeping it out of the world-readable