Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,14 @@
private-extension = {
url = "path:./extensions/empty";
};

learnharness = {
url = "github:GDR/learnharness";
inputs.nixpkgs.follows = "nixpkgs";
};
};

outputs = inputs@{ self, nixpkgs, charon-key, ... }:
outputs = inputs@{ self, nixpkgs, charon-key, learnharness, ... }:
let
lib = nixpkgs.lib.extend (lib: _:
let hm = inputs.home-manager.lib.hm; in {
Expand Down Expand Up @@ -84,7 +89,7 @@
let
pkgs = import nixpkgs { inherit system; config.allowUnfree = true; nvidia.acceptLicense = true; };
in
import ./packages.nix { inherit pkgs lib system charon-key; }
import ./packages.nix { inherit pkgs lib system charon-key learnharness; }
);

devShells = forAllSystems (system:
Expand Down
11 changes: 10 additions & 1 deletion hosts/machines/mac-brightstar/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,16 @@ in
home.utils.obsidian.enable = true;
# host-specific
home.ai-tools.enable = true;
home.ai-tools.learnharness.enable = true;
home.ai-tools.learnharness = {
enable = true;
autoUpdate = false;
obsidianFolders = [ "/Users/dgarifullin/obsidian-vault" ];
models = {
strong = { provider = "antigravity"; id = "gemini-4-argon"; thinkingLevel = "high"; };
cheap = { provider = "antigravity"; id = "gemini-3.8-flash"; thinkingLevel = "high"; };
vision = { provider = "antigravity"; id = "gemini-4-argon"; thinkingLevel = "medium"; };
};
};
home.editors.antigravity = { enable = true; } // userDefaults.antigravity;
home.utils.raycast.enable = false; # disabled: nixpkgs download URL broken
};
Expand Down
1 change: 1 addition & 0 deletions modules/_core/user.nix
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ in
useGlobalPkgs = true;
# Back up existing files instead of failing
backupFileExtension = "backup";
sharedModules = optional (inputs ? learnharness && inputs.learnharness ? homeManagerModules.default) inputs.learnharness.homeManagerModules.default;

users = mapAttrs
(userName: userCfg:
Expand Down
94 changes: 91 additions & 3 deletions modules/home/ai-tools/learnharness.nix
Original file line number Diff line number Diff line change
@@ -1,13 +1,101 @@
# LearnHarness - An agent harness for learning
{ lib, ... }:
{ lib, pkgs ? null, ... }:

let
modelSlotOption = description:
lib.mkOption {
type = lib.types.nullOr (lib.types.submodule {
options = {
provider = lib.mkOption {
type = lib.types.str;
description = "Provider ID (e.g. 'antigravity', 'anthropic', 'openai', 'openrouter').";
};
id = lib.mkOption {
type = lib.types.str;
description = "Model ID within the provider.";
};
thinkingLevel = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [ "off" "minimal" "low" "medium" "high" "xhigh" "max" ]);
default = null;
description = "Optional thinking budget level.";
};
};
});
default = null;
inherit description;
};
in
lib.my.mkFeature {
platforms = [
"linux"
"darwin"
];
description = "LearnHarness - An agent harness for learning";
homeModule = { pkgs, ... }: {
home.packages = [ pkgs.learnharness ];

extraOptions = {
package = lib.mkOption {
type = lib.types.nullOr lib.types.package;
default = if pkgs != null && pkgs ? learnharness then pkgs.learnharness else null;
description = "LearnHarness package to install.";
};

autoUpdate = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether LearnHarness should check for automatic updates.";
};

obsidianFolders = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "~/obsidian-vault" ];
description = "Obsidian vault/project folder paths where agent Markdown write/edit tools are allowed to operate.";
};

models = {
strong = modelSlotOption "Strong model slot (used by prober and teacher agents).";
cheap = modelSlotOption "Cheap/fast model slot (used by researcher, demoBuilder, extractor, auditor).";
vision = modelSlotOption "Vision model slot (used by visualMaker and pdfReader).";
};

tools = lib.mkOption {
type = lib.types.attrsOf lib.types.bool;
default = { };
description = "Per-tool enable/disable overrides (absent = enabled).";
};

searchEngine = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [ "brave" "exa" "perplexity" "serper" "tavily" ]);
default = null;
description = "Preferred web search engine when multiple search API keys are configured.";
};

graphTools = lib.mkOption {
type = lib.types.nullOr lib.types.bool;
default = null;
description = "Whether prober knowledge-graph inspection tools are enabled.";
};

extraSettings = lib.mkOption {
type = lib.types.attrsOf lib.types.anything;
default = { };
description = "Additional raw settings deep-merged into settings.json.";
};
};

homeModule = { pkgs, featureCfg, ... }: {
programs.learnharness = {
enable = true;
package = if (featureCfg.package or null) != null then featureCfg.package else pkgs.learnharness;
inherit (featureCfg)
autoUpdate
obsidianFolders
models
tools
searchEngine
graphTools
extraSettings
;
};
};
}
1 change: 1 addition & 0 deletions overlays/additions.nix
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ final: _prev:
(import ../pkgs { inherit system; pkgs = final; }) // {
charon-key = inputs.charon-key.packages.${system}.default;
clavis = if inputs ? clavis && inputs.clavis ? packages.${system} then inputs.clavis.packages.${system}.default else null;
learnharness = inputs.learnharness.packages.${system}.default;

# Antigravity 2.x packages from the upstream flake
google-antigravity = inputs.antigravity-nix.packages.${system}.google-antigravity;
Expand Down
3 changes: 2 additions & 1 deletion packages.nix
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{ pkgs, lib, system, charon-key }:
{ pkgs, lib, system, charon-key, learnharness }:
let
customPkgs = import ./pkgs { inherit pkgs lib system; };
in
customPkgs // {
charon-key = charon-key.packages.${system}.default;
learnharness = learnharness.packages.${system}.default;
}
1 change: 0 additions & 1 deletion pkgs/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ let

common = {
apple-emoji-ttf = pkgs.callPackage ./apple-emoji-ttf { };
learnharness = pkgs.callPackage ./learnharness { };
# Custom pre-commit to avoid Swift dependency on Darwin
pre-commit = pkgs.callPackage ./pre-commit { };
};
Expand Down
Loading
Loading