From 6e8ba05449304332ad2e94b20c480a95d223ae3b Mon Sep 17 00:00:00 2001 From: Mel Date: Sun, 14 Jun 2026 01:23:23 +0200 Subject: Run Renard agent in Hermes harness Signed-off-by: Mel --- flake.lock | 237 ++++++++++++++++++++++++++++++++++++++++++-- flake.nix | 2 + machines/renard/default.nix | 1 + modules/agent.nix | 114 +++++++++++++++++++++ secrets/renard-agent.age | Bin 0 -> 1337 bytes secrets/secrets.nix | 4 + 6 files changed, 349 insertions(+), 9 deletions(-) create mode 100644 modules/agent.nix create mode 100644 secrets/renard-agent.age diff --git a/flake.lock b/flake.lock index 4736817..26da37a 100644 --- a/flake.lock +++ b/flake.lock @@ -86,6 +86,50 @@ "type": "github" } }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "hermes-agent", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "hermes-agent": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "npm-lockfile-fix": "npm-lockfile-fix", + "pyproject-build-systems": "pyproject-build-systems", + "pyproject-nix": "pyproject-nix_2", + "uv2nix": "uv2nix_2" + }, + "locked": { + "lastModified": 1781361238, + "narHash": "sha256-BcosUFspfB2aCZ6FyeURiwjR28uaPeOv6oaz+aEkBS8=", + "owner": "NousResearch", + "repo": "hermes-agent", + "rev": "cc14b74718aaab8f3dc69c004b4dccf67077a1e1", + "type": "github" + }, + "original": { + "owner": "NousResearch", + "repo": "hermes-agent", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -109,7 +153,7 @@ }, "nixos-hardware": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs_2" }, "locked": { "lastModified": 1781168557, @@ -127,15 +171,18 @@ }, "nixpkgs": { "locked": { - "lastModified": 1767892417, - "narHash": "sha256-8bW3q88CEg2u4hSP66Vf4lpbLonHz7hqDNBMcCY7E9U=", - "rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba", - "type": "tarball", - "url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre924538.3497aa5c9457/nixexprs.tar.xz" + "lastModified": 1775036866, + "narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6201e203d09599479a3b3450ed24fa81537ebc4e", + "type": "github" }, "original": { - "type": "tarball", - "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" } }, "nixpkgs-unstable": { @@ -155,6 +202,19 @@ } }, "nixpkgs_2": { + "locked": { + "lastModified": 1767892417, + "narHash": "sha256-8bW3q88CEg2u4hSP66Vf4lpbLonHz7hqDNBMcCY7E9U=", + "rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba", + "type": "tarball", + "url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre924538.3497aa5c9457/nixexprs.tar.xz" + }, + "original": { + "type": "tarball", + "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" + } + }, + "nixpkgs_3": { "locked": { "lastModified": 1780952837, "narHash": "sha256-Fwd1+spDtQ0hDyBwme6ufG3n4mY0UrjjFdYHv+G/Hds=", @@ -170,6 +230,27 @@ "type": "github" } }, + "npm-lockfile-fix": { + "inputs": { + "nixpkgs": [ + "hermes-agent", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775903712, + "narHash": "sha256-2GV79U6iVH4gKAPWYrxUReB0S41ty/Y3dBLquU8AlaA=", + "owner": "jeslie0", + "repo": "npm-lockfile-fix", + "rev": "c6093acb0c0548e0f9b8b3d82918823721930fe8", + "type": "github" + }, + "original": { + "owner": "jeslie0", + "repo": "npm-lockfile-fix", + "type": "github" + } + }, "oisd": { "flake": false, "locked": { @@ -186,15 +267,104 @@ "type": "github" } }, + "pyproject-build-systems": { + "inputs": { + "nixpkgs": [ + "hermes-agent", + "nixpkgs" + ], + "pyproject-nix": "pyproject-nix", + "uv2nix": "uv2nix" + }, + "locked": { + "lastModified": 1772555609, + "narHash": "sha256-3BA3HnUvJSbHJAlJj6XSy0Jmu7RyP2gyB/0fL7XuEDo=", + "owner": "pyproject-nix", + "repo": "build-system-pkgs", + "rev": "c37f66a953535c394244888598947679af231863", + "type": "github" + }, + "original": { + "owner": "pyproject-nix", + "repo": "build-system-pkgs", + "type": "github" + } + }, + "pyproject-nix": { + "inputs": { + "nixpkgs": [ + "hermes-agent", + "pyproject-build-systems", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1769936401, + "narHash": "sha256-kwCOegKLZJM9v/e/7cqwg1p/YjjTAukKPqmxKnAZRgA=", + "owner": "nix-community", + "repo": "pyproject.nix", + "rev": "b0d513eeeebed6d45b4f2e874f9afba2021f7812", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "pyproject.nix", + "type": "github" + } + }, + "pyproject-nix_2": { + "inputs": { + "nixpkgs": [ + "hermes-agent", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1772865871, + "narHash": "sha256-/ZTSg97aouL0SlPHaokA4r3iuH9QzHVuWPACD2CUCFY=", + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "rev": "e537db02e72d553cea470976b9733581bcf5b3ed", + "type": "github" + }, + "original": { + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "type": "github" + } + }, + "pyproject-nix_3": { + "inputs": { + "nixpkgs": [ + "hermes-agent", + "uv2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1771518446, + "narHash": "sha256-nFJSfD89vWTu92KyuJWDoTQJuoDuddkJV3TlOl1cOic=", + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "rev": "eb204c6b3335698dec6c7fc1da0ebc3c6df05937", + "type": "github" + }, + "original": { + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "type": "github" + } + }, "root": { "inputs": { "agenix": "agenix", "cloudflare-ips-v4": "cloudflare-ips-v4", "cloudflare-ips-v6": "cloudflare-ips-v6", "flake-compat": "flake-compat", + "hermes-agent": "hermes-agent", "home-manager": "home-manager", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs_3", "nixpkgs-unstable": "nixpkgs-unstable", "oisd": "oisd" } @@ -213,6 +383,55 @@ "repo": "default", "type": "github" } + }, + "uv2nix": { + "inputs": { + "nixpkgs": [ + "hermes-agent", + "pyproject-build-systems", + "nixpkgs" + ], + "pyproject-nix": [ + "hermes-agent", + "pyproject-build-systems", + "pyproject-nix" + ] + }, + "locked": { + "lastModified": 1770770348, + "narHash": "sha256-A2GzkmzdYvdgmMEu5yxW+xhossP+txrYb7RuzRaqhlg=", + "owner": "pyproject-nix", + "repo": "uv2nix", + "rev": "5d1b2cb4fe3158043fbafbbe2e46238abbc954b0", + "type": "github" + }, + "original": { + "owner": "pyproject-nix", + "repo": "uv2nix", + "type": "github" + } + }, + "uv2nix_2": { + "inputs": { + "nixpkgs": [ + "hermes-agent", + "nixpkgs" + ], + "pyproject-nix": "pyproject-nix_3" + }, + "locked": { + "lastModified": 1773039484, + "narHash": "sha256-+boo33KYkJDw9KItpeEXXv8+65f7hHv/earxpcyzQ0I=", + "owner": "pyproject-nix", + "repo": "uv2nix", + "rev": "b68be7cfeacbed9a3fa38a2b5adc0cfb81d9bb1f", + "type": "github" + }, + "original": { + "owner": "pyproject-nix", + "repo": "uv2nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index adfd813..6ba03f3 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,8 @@ url = "https://www.cloudflare.com/ips-v6"; flake = false; }; + + hermes-agent.url = "github:NousResearch/hermes-agent"; }; outputs = inputs @ { self, nixpkgs, nixpkgs-unstable, home-manager, agenix, ... }: diff --git a/machines/renard/default.nix b/machines/renard/default.nix index 2be8e72..98d5860 100644 --- a/machines/renard/default.nix +++ b/machines/renard/default.nix @@ -10,6 +10,7 @@ ../../modules/binary-cache.nix ../../modules/git.nix ../../modules/syncthing.nix + ../../modules/agent.nix ../../services/cgit.nix ../../services/minecraft.nix diff --git a/modules/agent.nix b/modules/agent.nix new file mode 100644 index 0000000..9cc0636 --- /dev/null +++ b/modules/agent.nix @@ -0,0 +1,114 @@ +{ config, hermes-agent, ... }: + +{ + imports = [ + hermes-agent.nixosModules.default + ]; + + age.secrets.renard-agent = { + file = ../secrets/renard-agent.age; + owner = "renard"; + group = "renard"; + mode = "0440"; + }; + + services.hermes-agent = { + enable = true; + + # the agent has it's own user on the host under + # which it runs and can change state at. + user = "renard"; + group = "renard"; + stateDir = "/home/renard"; + + extraDependencyGroups = [ "messaging" ]; + + environmentFiles = [ + # required secrets: + # * OPENROUTER_API_KEY + # * DISCORD_BOT_TOKEN + # * DISCORD_ALLOWED_USERS + config.age.secrets.renard-agent.path + ]; + + settings = { + model = { + provider = "openrouter"; + # my current favorite budget model, with recent price + # changes by far the cheapest for the performance, + # with other chinese models also being close. + # only modality is text, requires auxiliary vision model! + # native price: $0.44/m in, $0.87/m out. + default = "deepseek/deepseek-v4-pro"; + }; + + agent = { + reasoning_effort = "high"; # 'high' rated highest in benchmarks, tune to taste! + max_turns = 90; + }; + + auxiliary.vision = { + # auxiliary vision model for image comprehension. + # this qwen model is built for this exact use-case and is extremely + # cheap for the performance (though below gemini), however, + # it does not do any reasoning on the visual input! + # possible upgrade path for reasoning: qwen3-vl-30b-a3b-thinking + # native price: $0.13/m in, $1.56/m out. + provider = "openrouter"; + model = "qwen/qwen3-vl-32b-instruct"; + timeout = 120; + }; + + toolsets = [ "all" ]; + + terminal = { + backend = "local"; + persistent_shell = true; + timeout = 180; + }; + + memory = { + memory_enabled = true; + user_profile_enabled = true; + }; + + # slightly higher limits and targets than default for better + # long session preservation. + compression = { + enabled = true; + threshold = 0.75; + target_ratio = 0.35; + protect_last_n = 40; + }; + + # progressive message editing, a little silly in discord but looks like + # the classic llm chats. + streaming = { + enabled = true; + }; + display = { + streaming = true; # small regression with streaming.enabled, keep both on. + tool_progress = "new"; # only show fresh new tool calls, not every call. + }; + + discord = { + require_mention = false; + thread_require_mention = false; + auto_thread = false; + reactions = true; + history_backfill = true; + allow_mentions = { + everyone = false; + roles = false; + users = true; + replied_user = true; + }; + }; + + unauthorized_dm_behavior = "ignore"; # ignore strangers + + timezone = "Europe/Berlin"; + group_sessions_per_user = false; + }; + }; +} diff --git a/secrets/renard-agent.age b/secrets/renard-agent.age new file mode 100644 index 0000000..0409f71 Binary files /dev/null and b/secrets/renard-agent.age differ diff --git a/secrets/secrets.nix b/secrets/secrets.nix index c56e2bd..9b09574 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -27,6 +27,10 @@ in renard ] ++ allAdmins; + "renard-agent.age".publicKeys = [ + renard + ] ++ allAdmins; + "mullvad-gluetun.age".publicKeys = [ corsac ] ++ allAdmins; -- cgit 1.4.1