{ config, lib, pkgs, inputs, ... }: let # Edge TTS: Seraphina — friendly, multilingual German female voice (free, no API key) edgeVoice = "de-DE-SeraphinaMultilingualNeural"; agentLock = builtins.fromJSON (builtins.readFile ../../../agent-sources.lock.json); agentSkillSelections = { m3ta-agents.exclude = []; anthropic.exclude = ["pdf" "skill-creator" "xlsx"]; basecamp.exclude = []; kestra.exclude = []; mattpocock.exclude = ["grill-me" "caveman"]; superpowers.exclude = ["brainstorming" "systematic-debugging"]; vercel.exclude = []; }; sourceRoot = source: builtins.fetchGit { inherit (source) url rev; }; selectedSkillNames = sourceName: let source = agentLock.sources.${sourceName}; excluded = agentSkillSelections.${sourceName}.exclude; in lib.subtractLists excluded (builtins.attrNames source.items.skills); copySkill = sourceName: skillName: let source = agentLock.sources.${sourceName}; item = source.items.skills.${skillName}; in '' cp -R ${sourceRoot source}/${source.root}/${item.path} $out/${skillName} ''; copySourceSkills = sourceName: lib.concatMapStringsSep "\n" (copySkill sourceName) (selectedSkillNames sourceName); # Build skills from the agent-lib lockfile instead of the legacy AGENTS flake. hermesSkills = pkgs.runCommand "hermes-agent-lib-skills" {} '' mkdir -p $out ${lib.concatMapStringsSep "\n" copySourceSkills (builtins.attrNames agentSkillSelections)} ''; in { virtualisation.docker.enable = true; systemd.tmpfiles.rules = [ "d /var/lib/hermes/.config 0755 hermes hermes -" "d /var/lib/hermes/.config/tea 0755 hermes hermes -" "L+ /var/lib/hermes/.config/tea/yml - - - - ${pkgs.writeText "tea-yml" '' logins: - name: m3ta url: https://code.m3ta.dev token: ssh_host: code.m3ta.dev user: m3ta-chiron default: true ''}" ]; systemd.services.copy-hermes-skills = { description = "Copy agent skills to hermes home directory"; wantedBy = ["hermes-agent.service"]; before = ["hermes-agent.service"]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; script = '' mkdir -p /var/lib/hermes/.agents cp -rT ${hermesSkills} /var/lib/hermes/.agents/skills chown -R hermes:hermes /var/lib/hermes/.agents ''; }; services.hermes-agent = { enable = true; addToSystemPackages = true; # v0.14 lazy-installs heavy optional backends by default. In the sealed # Nix package, include the backends this host config actively uses so the # gateway, Matrix bridge, memory, web search, TTS, and local STT work # without runtime pip/uv mutation. extraDependencyGroups = [ "matrix" "honcho" "exa" "edge-tts" "voice" ]; extraPackages = with pkgs; [ docker git curl jq tea nix python3Minimal uv zellij ]; # Secrets via agenix environmentFiles = [ config.age.secrets."hermes-env".path config.age.secrets."hermes-cloud-env".path config.age.secrets."hermes-api-server-key".path ]; # Non-secret environment variables # Git identity is set entirely via env vars (GIT_AUTHOR_*, GIT_COMMITTER_*, # GIT_INIT_DEFAULT_BRANCH) — no .gitconfig file needed. Env vars take # precedence over any gitconfig, and the hermes gateway injects them into # all terminal sessions via .env. environment = { GLM_BASE_URL = "https://api.z.ai/api/coding/paas/v4/"; GIT_AUTHOR_NAME = "m3ta-chiron"; GIT_AUTHOR_EMAIL = "m3ta-chiron@agentmail.to"; GIT_COMMITTER_NAME = "m3ta-chiron"; GIT_COMMITTER_EMAIL = "m3ta-chiron@agentmail.to"; GIT_INIT_DEFAULT_BRANCH = "master"; # ── API Server (OpenAI-compatible, for Hermes Desktop App) ───────── # Accessible via Netbird mesh VPN — not exposed to the public internet. # Bind to 0.0.0.0 so the Netbird interface can reach it. API_SERVER_ENABLED = "true"; API_SERVER_HOST = "0.0.0.0"; API_SERVER_PORT = toString (config.m3ta.ports.get "hermes-api"); }; # ── Container mode (podman) ────────────────────────────────────────── container = { enable = false; backend = "podman"; extraVolumes = ["/home/m3tam3re/p:/projects:rw"]; extraOptions = []; }; settings = { # ── Model ────────────────────────────────────────────────────────── model = { default = "gpt-5.5"; provider = "openai-codex"; }; fallback_providers = [ { provider = "zai"; model = "glm-5.1"; } { provider = "minimax"; model = "MiniMax-M2.7"; } ]; credential_pool_strategies = { zai = "fill_first"; }; toolsets = ["all"]; # ── Agent ────────────────────────────────────────────────────────── agent = { max_turns = 90; gateway_timeout = 1800; tool_use_enforcement = "auto"; reasoning_effort = "high"; }; # ── Skills ───────────────────────────────────────────────────────── skills = { external_dirs = [ "/var/lib/hermes/.agents/skills" ]; }; # ── Terminal ─────────────────────────────────────────────────────── terminal = { backend = "local"; modal_mode = "auto"; cwd = "."; timeout = 180; persistent_shell = true; }; # ── Browser ──────────────────────────────────────────────────────── browser = { inactivity_timeout = 120; command_timeout = 30; cloud_provider = "local"; }; # ── Checkpoints v2 ───────────────────────────────────────────────── # v0.13.0: Single-store rewrite with real pruning + disk guardrails. checkpoints = { enabled = true; max_snapshots = 50; }; file_read_max_chars = 100000; compression = { enabled = true; threshold = 0.5; target_ratio = 0.2; protect_last_n = 20; }; # ── Display ──────────────────────────────────────────────────────── display = { compact = false; personality = "kawaii"; resume_display = "full"; busy_input_mode = "interrupt"; inline_diffs = true; skin = "default"; tool_progress = "all"; }; # ── TTS / STT / Voice ────────────────────────────────────────────── tts = { provider = "edge"; edge = { voice = edgeVoice; }; }; stt = { enabled = true; provider = "local"; local = {model = "base";}; }; voice = { record_key = "ctrl+b"; max_recording_seconds = 120; silence_threshold = 200; silence_duration = 3.0; }; # ── Memory ───────────────────────────────────────────────────────── memory = { provider = "honcho"; memory_enabled = true; user_profile_enabled = true; memory_char_limit = 2200; user_char_limit = 1375; }; # ── Delegation / Orchestrator ──────────────────────────────────────── delegation = { max_iterations = 50; orchestrator_enabled = true; max_spawn_depth = 2; }; # ── Kanban (v0.13.0 — Multi-Agent Board) ────────────────────────── # Durable task board with embedded dispatcher in gateway process. # Workers are full OS processes with identity, heartbeat, reclaim, # zombie detection, and hallucination gate. kanban = { dispatch_in_gateway = true; dispatch_interval_seconds = 60; }; # ── Matrix ──────────────────────────────────────────────────────── matrix = { homeserver = "https://matrix.m3ta.dev"; user_id = "@chiron:m3ta.dev"; allowed_users = ["@m3tam3re:m3ta.dev"]; encryption = true; group_sessions_per_user = true; auto_thread = true; dm_mention_threads = true; }; # ── Approvals / Security ─────────────────────────────────────────── approvals = { mode = "manual"; timeout = 60; }; security = { redact_secrets = true; tirith_enabled = true; tirith_fail_open = true; }; # ── Cron / Session ───────────────────────────────────────────────── cron = {wrap_response = true;}; session_reset = { mode = "both"; idle_minutes = 1440; at_hour = 4; }; # ── Web ──────────────────────────────────────────────────────────── web = {backend = "exa";}; }; }; users.users.hermes = { isNormalUser = false; openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICAVF7jGP1S6vc5CxeBFD/UxiImHOgbPlKg8WYyNtOA3" ]; }; }