summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--flake.lock237
-rw-r--r--flake.nix2
-rw-r--r--machines/renard/default.nix1
-rw-r--r--modules/agent.nix114
-rw-r--r--secrets/renard-agent.agebin0 -> 1337 bytes
-rw-r--r--secrets/secrets.nix4
6 files changed, 349 insertions, 9 deletions
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": {
@@ -156,6 +203,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=",
         "owner": "NixOS",
@@ -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
--- /dev/null
+++ b/secrets/renard-agent.age
Binary files differdiff --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;