feat(nix): implemented wayland screencast support and restructured package builder
- Added `withWaylandScreencast` package override option to selectively link libpipewire for Wayland screencast support. - Refactored Nix package builder structure to eliminate wrapper scripts and directly install binary outputs. - Updated home-manager module description to clarify declarative settings file behavior and symlink rules.
This commit is contained in:
@@ -614,7 +614,7 @@ bun setup
|
||||
bun dev
|
||||
```
|
||||
|
||||
Build and smoke-test the distributable Nix package with `nix build .#omp`. `nix/bun.nix` is generated only when `bun.lock` changes; releases regenerate it automatically. For dependency changes, run:
|
||||
Build and smoke-test the distributable Nix package with `nix build .#omp`. Wayland screencast support is off by default (linking libpipewire adds ~750 MB of runtime closure); enable it with `omp.override { withWaylandScreencast = true; }`. `nix/bun.nix` is generated only when `bun.lock` changes; releases regenerate it automatically. For dependency changes, run:
|
||||
|
||||
```sh
|
||||
bun run gen:nix
|
||||
|
||||
@@ -297,7 +297,7 @@ Fields:
|
||||
Subagents run unadvised by default; advisors are opted in **per agent** instead of via a blanket toggle:
|
||||
|
||||
- Agent definition frontmatter `advisor`: `true` advises spawned sessions of that agent with the model resolved for the `advisor` role; a string (e.g. `advisor: "deepseek/deepseek-v4-flash"` or `advisor: "@smol:high"`) sets an explicit advisor model pattern with an optional `:level` thinking suffix.
|
||||
- The `task.agentAdvisor` settings record (agent name → `"on"` / `"off"` / model pattern) overrides the frontmatter, and is edited per agent from `/agents` with `A` (an inline editor with model suggestions and a live resolution preview).
|
||||
- The `task.agentAdvisor` settings record (agent name → `"on"` / `"off"` / model pattern) overrides the frontmatter, and is configured per agent from the `/agents` hub: Enter on an agent opens its property strip; the advisor strip offers on/off, a model-browser pick, or a raw pattern.
|
||||
|
||||
The legacy `advisor.subagents: true` setting migrates to `task.agentAdvisor: { task: "on" }` — the bundled generic `task` agent keeps its advisor, other agents start unadvised.
|
||||
|
||||
|
||||
+1
-1
@@ -374,7 +374,7 @@ See [Advisor and WATCHDOG.md](./advisor-watchdog.md) for runtime behavior, `WATC
|
||||
| Key | Type | Default | Notes |
|
||||
| --------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `advisor.enabled` | boolean | `false` | Enable the advisor runtime when `modelRoles.advisor` resolves to an available model. |
|
||||
| `task.agentAdvisor` | record | `{}` | Per-agent subagent advisor: agent name → `"on"` / `"off"` / advisor model pattern. Overrides agent frontmatter `advisor`; edited from `/agents` with `A`. |
|
||||
| `task.agentAdvisor` | record | `{}` | Per-agent subagent advisor: agent name → `"on"` / `"off"` / advisor model pattern. Overrides agent frontmatter `advisor`; configured from the `/agents` hub. |
|
||||
| `advisor.syncBacklog` | enum | `off` | Bounded advisor catch-up delay: `off`, `1`, `3`, or `5`. The primary waits up to 30 seconds only while advisor backlog is at or above the threshold. |
|
||||
| `advisor.immuneTurns` | number | `3` | After a `concern`/`blocker` interrupts, route further concerns/blockers as non-interrupting asides for this many completed primary turns. |
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ Parsing comes from frontmatter via `parseAgentFields()` (`src/discovery/helpers.
|
||||
- `thinking-level` / `thinking` selects the agent's configured effort. When `task.enableEffort` (default `false`) exposes it, a task item's coarse `effort` (`lo`, `med`, `hi`) takes precedence at launch. OMP maps that hint to the selected model's lowest, middle, or highest supported effort, then clamps it to `task.maxEffort` (default `max`). The ceiling is carried across retry-fallback model switches. If the selected model has no supported effort at or below the ceiling, the spawn fails; models without a controllable effort surface instead fall back to their normal selector.
|
||||
- `blocking: true` makes the parent wait for that agent even when async task execution is enabled
|
||||
- `autoloadSkills` names skills from the parent session to inject before the first child prompt; unknown names are ignored
|
||||
- `prewalk: true` starts the subagent on its resolved model and hands off to the default prewalk target (the `smol` role) at its first edit/write, exactly like the session-level `--prewalk`; a string value (e.g. `prewalk: "@smol"` or `prewalk: "openai/gpt-5-mini"`) picks a custom target. The `task.agentPrewalk` settings record (agent name → `"on"` / `"off"` / pattern, edited per agent from `/agents` with `P`) overrides the frontmatter. Resolution happens in `runSubprocess` (`src/task/executor.ts`). An unavailable target is skipped instead of failing the spawn. A resolved target is skipped only when both its model identity and its effective thinking mode/level match the starting selection after model clamping; a same-model effort downgrade is a real hand-off and still arms and switches at the first edit/write.
|
||||
- `advisor: true` pairs spawned sessions of the agent with an advisor running the model resolved for the `advisor` role; a string value (e.g. `advisor: "deepseek/deepseek-v4-flash"` or `advisor: "@smol:high"`) sets an explicit advisor model pattern (optional `:level` suffix), applied as the spawned session's `modelRoles.advisor`. The `task.agentAdvisor` settings record (agent name → `"on"` / `"off"` / pattern, edited per agent from `/agents` with `A`) overrides the frontmatter. Resolution happens in `runSubprocess` (`src/task/executor.ts`); subagents default to no advisor, and the effective opt-in is persisted in `session_init` so cold revival restores it.
|
||||
- `prewalk: true` starts the subagent on its resolved model and hands off to the default prewalk target (the `smol` role) at its first edit/write, exactly like the session-level `--prewalk`; a string value (e.g. `prewalk: "@smol"` or `prewalk: "openai/gpt-5-mini"`) picks a custom target. The `task.agentPrewalk` settings record (agent name → `"on"` / `"off"` / pattern, configured per agent from the `/agents` hub via its prewalk strip) overrides the frontmatter. Resolution happens in `runSubprocess` (`src/task/executor.ts`). An unavailable target is skipped instead of failing the spawn. A resolved target is skipped only when both its model identity and its effective thinking mode/level match the starting selection after model clamping; a same-model effort downgrade is a real hand-off and still arms and switches at the first edit/write.
|
||||
- `advisor: true` pairs spawned sessions of the agent with an advisor running the model resolved for the `advisor` role; a string value (e.g. `advisor: "deepseek/deepseek-v4-flash"` or `advisor: "@smol:high"`) sets an explicit advisor model pattern (optional `:level` suffix), applied as the spawned session's `modelRoles.advisor`. The `task.agentAdvisor` settings record (agent name → `"on"` / `"off"` / pattern, configured per agent from the `/agents` hub via its advisor strip) overrides the frontmatter. Resolution happens in `runSubprocess` (`src/task/executor.ts`); subagents default to no advisor, and the effective opt-in is persisted in `session_init` so cold revival restores it.
|
||||
|
||||
## Role-backed custom agents
|
||||
|
||||
|
||||
@@ -23,7 +23,12 @@ in
|
||||
settings = lib.mkOption {
|
||||
type = lib.types.nullOr yaml.type;
|
||||
default = null;
|
||||
description = "Settings written declaratively to ~/.omp/agent/config.yml.";
|
||||
description = ''
|
||||
Settings written declaratively to {file}`~/.omp/agent/config.yml`.
|
||||
The file is a read-only store symlink: changes made from inside OMP
|
||||
(`/settings`, onboarding) replace it but revert on the next
|
||||
`home-manager switch`.
|
||||
'';
|
||||
example = {
|
||||
theme.dark = "titanium";
|
||||
startup.quiet = true;
|
||||
|
||||
+132
-139
@@ -1,17 +1,13 @@
|
||||
{
|
||||
autoPatchelfHook ? null,
|
||||
autoSignDarwinBinariesHook ? null,
|
||||
autoPatchelfHook,
|
||||
bun,
|
||||
bun2nix,
|
||||
cmake,
|
||||
darwin,
|
||||
lib,
|
||||
libopus,
|
||||
libpulseaudio ? null,
|
||||
makeWrapper,
|
||||
ninja,
|
||||
openssl,
|
||||
pcre2,
|
||||
pipewire ? null,
|
||||
pipewire,
|
||||
pkg-config,
|
||||
rustPlatform,
|
||||
rustToolchain,
|
||||
@@ -19,8 +15,10 @@
|
||||
stdenv,
|
||||
stdenvNoCC,
|
||||
unzip,
|
||||
zig,
|
||||
zlib,
|
||||
# Wayland screencast support links libpipewire, whose runtime closure adds
|
||||
# ~750 MB (gstreamer, ffmpeg, systemd, ...). Official npm/Bazel addons ship
|
||||
# without it, so default to the lean build; opt in via `.override`.
|
||||
withWaylandScreencast ? false,
|
||||
}:
|
||||
let
|
||||
packageJson = lib.importJSON ../packages/coding-agent/package.json;
|
||||
@@ -68,142 +66,137 @@ let
|
||||
'';
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation (
|
||||
{
|
||||
pname = "omp";
|
||||
inherit (packageJson) version;
|
||||
src = source;
|
||||
stdenv.mkDerivation {
|
||||
pname = "omp";
|
||||
inherit (packageJson) version;
|
||||
src = source;
|
||||
|
||||
cargoDeps = rustPlatform.importCargoLock { lockFile = ../Cargo.lock; };
|
||||
bunDeps = bun2nix.fetchBunDeps {
|
||||
bunNix = ./bun.nix;
|
||||
overrides = patchOverrides;
|
||||
};
|
||||
cargoDeps = rustPlatform.importCargoLock { lockFile = ../Cargo.lock; };
|
||||
bunDeps = bun2nix.fetchBunDeps {
|
||||
bunNix = ./bun.nix;
|
||||
overrides = patchOverrides;
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
bun
|
||||
bun2nix.hook
|
||||
cmake
|
||||
makeWrapper
|
||||
ninja
|
||||
pkg-config
|
||||
rustPlatform.bindgenHook
|
||||
rustPlatform.cargoSetupHook
|
||||
rustToolchain
|
||||
zig
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [ autoPatchelfHook ]
|
||||
++ lib.optionals stdenv.hostPlatform.isDarwin [ autoSignDarwinBinariesHook ];
|
||||
nativeBuildInputs = [
|
||||
bun
|
||||
bun2nix.hook
|
||||
cmake
|
||||
ninja
|
||||
pkg-config
|
||||
rustPlatform.bindgenHook
|
||||
rustPlatform.cargoSetupHook
|
||||
rustToolchain
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [ autoPatchelfHook ]
|
||||
++ lib.optionals stdenv.hostPlatform.isDarwin [ darwin.autoSignDarwinBinariesHook ];
|
||||
|
||||
buildInputs = [
|
||||
libopus
|
||||
openssl
|
||||
pcre2
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
libpulseaudio
|
||||
pipewire
|
||||
stdenv.cc.cc.lib
|
||||
zlib
|
||||
];
|
||||
# pcre2 is vendored via PCRE2_SYS_STATIC, but opus must link the nixpkgs
|
||||
# library: audiopus_sys' bundled cmake build installs to lib64 while its
|
||||
# link-search hardcodes lib, so the pkg-config path is the one that works.
|
||||
# libgcc_s is resolved from the compiler's lib output during autoPatchelf.
|
||||
# All dynamic store paths are pinned into the closure via nix-support (see
|
||||
# installPhase).
|
||||
buildInputs = [
|
||||
libopus
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [ stdenv.cc.cc.lib ]
|
||||
++ lib.optionals withWaylandScreencast [ pipewire ];
|
||||
|
||||
strictDeps = true;
|
||||
# Nix builders cannot reliably hardlink cache files into node_modules
|
||||
# (and Darwin's clonefile backend also rejects store permissions).
|
||||
bunInstallFlags = [
|
||||
"--linker=isolated"
|
||||
"--backend=copyfile"
|
||||
];
|
||||
dontConfigure = true;
|
||||
dontRunLifecycleScripts = true;
|
||||
dontUseBunBuild = true;
|
||||
dontUseBunCheck = true;
|
||||
dontUseBunInstall = true;
|
||||
dontStrip = true;
|
||||
strictDeps = true;
|
||||
# Nix builders cannot reliably hardlink cache files into node_modules
|
||||
# (and Darwin's clonefile backend also rejects store permissions).
|
||||
bunInstallFlags = [
|
||||
"--linker=isolated"
|
||||
"--backend=copyfile"
|
||||
];
|
||||
dontConfigure = true;
|
||||
dontRunLifecycleScripts = true;
|
||||
dontUseBunBuild = true;
|
||||
dontUseBunCheck = true;
|
||||
dontUseBunInstall = true;
|
||||
dontStrip = true;
|
||||
|
||||
env = {
|
||||
CMAKE_POLICY_VERSION_MINIMUM = "3.5";
|
||||
PCRE2_SYS_STATIC = "1";
|
||||
SOURCE_DATE_EPOCH = "1";
|
||||
env = {
|
||||
CMAKE_POLICY_VERSION_MINIMUM = "3.5";
|
||||
PCRE2_SYS_STATIC = "1";
|
||||
SOURCE_DATE_EPOCH = "1";
|
||||
}
|
||||
// lib.optionalAttrs (platform ? rustFlags) { RUSTFLAGS = platform.rustFlags; }
|
||||
// lib.optionalAttrs stdenv.hostPlatform.isDarwin { BUN_NO_CODESIGN_MACHO_BINARY = "1"; };
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
echo "Building pi-natives"
|
||||
cargo build --release -p pi-natives ${lib.optionalString withWaylandScreencast "--features wayland-pipewire"}
|
||||
install -Dm755 "target/release/${platform.nativeLibrary}" \
|
||||
"packages/natives/native/${platform.addon}"
|
||||
${lib.optionalString stdenv.hostPlatform.isLinux ''
|
||||
# The loader extracts this archived addon at runtime, so fix its
|
||||
# interpreter-independent Nix RPATH before Bun embeds it.
|
||||
autoPatchelf -- "packages/natives/native/${platform.addon}"
|
||||
''}
|
||||
${lib.optionalString stdenv.hostPlatform.isDarwin ''
|
||||
# arm64 Darwin requires even locally-built Mach-O addons to carry an
|
||||
# ad-hoc signature. Sign before Bun archives the file.
|
||||
signIfRequired "packages/natives/native/${platform.addon}"
|
||||
''}
|
||||
|
||||
echo "Compiling OMP"
|
||||
BUN_COMPILE_EXECUTABLE_PATH="${bunRuntimeTemplate}/libexec/bun" \
|
||||
bun --cwd="$PWD/packages/coding-agent" run build
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
install -Dm755 packages/coding-agent/dist/omp "$out/bin/omp"
|
||||
|
||||
# The addon is gzip-compressed inside the compiled binary, so the store
|
||||
# paths it links against are invisible to the output reference scanner.
|
||||
# Record them in plain text to pin the libraries into the runtime closure.
|
||||
mkdir -p "$out/nix-support"
|
||||
${
|
||||
if stdenv.hostPlatform.isLinux then
|
||||
''
|
||||
patchelf --print-rpath "packages/natives/native/${platform.addon}" \
|
||||
> "$out/nix-support/embedded-addon-runpath"
|
||||
''
|
||||
else
|
||||
''
|
||||
echo "${lib.getLib libopus}/lib" > "$out/nix-support/embedded-addon-runpath"
|
||||
''
|
||||
}
|
||||
// lib.optionalAttrs (platform ? rustFlags) { RUSTFLAGS = platform.rustFlags; }
|
||||
// lib.optionalAttrs stdenv.hostPlatform.isDarwin { BUN_NO_CODESIGN_MACHO_BINARY = "1"; };
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
echo "Building pi-natives"
|
||||
cargo build --release -p pi-natives ${lib.optionalString stdenv.hostPlatform.isLinux "--features wayland-pipewire"}
|
||||
install -Dm755 "target/release/${platform.nativeLibrary}" \
|
||||
"packages/natives/native/${platform.addon}"
|
||||
${lib.optionalString stdenv.hostPlatform.isLinux ''
|
||||
# The loader extracts this archived addon at runtime, so fix its
|
||||
# interpreter-independent Nix RPATH before Bun embeds it.
|
||||
autoPatchelf -- "packages/natives/native/${platform.addon}"
|
||||
''}
|
||||
${lib.optionalString stdenv.hostPlatform.isDarwin ''
|
||||
# arm64 Darwin requires even locally-built Mach-O addons to carry an
|
||||
# ad-hoc signature. Sign before Bun archives the file.
|
||||
signIfRequired "packages/natives/native/${platform.addon}"
|
||||
''}
|
||||
doInstallCheck = true;
|
||||
installCheckPhase = ''
|
||||
runHook preInstallCheck
|
||||
HOME="$TMPDIR" "$out/bin/omp" --smoke-test | grep -q "smoke-test: ok"
|
||||
BUN_BE_BUN=1 "$out/bin/omp" -e \
|
||||
'if (Bun.version !== "${bun.version}" || typeof Bun.Image !== "function") process.exit(1)'
|
||||
runHook postInstallCheck
|
||||
'';
|
||||
|
||||
echo "Compiling OMP"
|
||||
BUN_COMPILE_EXECUTABLE_PATH="${bunRuntimeTemplate}/libexec/bun" \
|
||||
bun --cwd="$PWD/packages/coding-agent" run build
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
install -Dm755 packages/coding-agent/dist/omp "$out/libexec/omp/omp"
|
||||
|
||||
${lib.optionalString stdenv.hostPlatform.isLinux ''
|
||||
# The addon is gzip-compressed inside the compiled binary, so the Nix
|
||||
# store RUNPATH autoPatchelf wrote into it is invisible to the output
|
||||
# reference scanner. Record it in plain text to pin those libraries
|
||||
# (pipewire, libopus, libgcc) into the runtime closure.
|
||||
mkdir -p "$out/nix-support"
|
||||
patchelf --print-rpath "packages/natives/native/${platform.addon}" \
|
||||
> "$out/nix-support/embedded-addon-runpath"
|
||||
''}
|
||||
|
||||
makeWrapper "$out/libexec/omp/omp" "$out/bin/omp" \
|
||||
--set PI_SKIP_VERSION_CHECK 1
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
doInstallCheck = true;
|
||||
installCheckPhase = ''
|
||||
runHook preInstallCheck
|
||||
HOME="$TMPDIR" "$out/bin/omp" --smoke-test | grep -q "smoke-test: ok"
|
||||
BUN_BE_BUN=1 "$out/libexec/omp/omp" -e \
|
||||
'if (Bun.version !== "${bun.version}" || typeof Bun.Image !== "function") process.exit(1)'
|
||||
runHook postInstallCheck
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Terminal-based coding agent with multi-model support";
|
||||
homepage = "https://omp.sh";
|
||||
changelog = "https://github.com/can1357/oh-my-pi/releases/tag/v${packageJson.version}";
|
||||
license = lib.licenses.mit;
|
||||
mainProgram = "omp";
|
||||
platforms = [
|
||||
"aarch64-darwin"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"x86_64-linux"
|
||||
];
|
||||
sourceProvenance = with lib.sourceTypes; [
|
||||
binaryNativeCode
|
||||
fromSource
|
||||
];
|
||||
};
|
||||
}
|
||||
// lib.optionalAttrs stdenv.hostPlatform.isDarwin {
|
||||
# The compile output is intentionally unsigned until all Darwin fixups are complete.
|
||||
darwinDontCodeSign = false;
|
||||
}
|
||||
)
|
||||
meta = {
|
||||
description = "Terminal-based coding agent with multi-model support";
|
||||
homepage = "https://omp.sh";
|
||||
changelog = "https://github.com/can1357/oh-my-pi/releases/tag/v${packageJson.version}";
|
||||
license = lib.licenses.mit;
|
||||
mainProgram = "omp";
|
||||
platforms = [
|
||||
"aarch64-darwin"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"x86_64-linux"
|
||||
];
|
||||
sourceProvenance = with lib.sourceTypes; [
|
||||
binaryNativeCode
|
||||
fromSource
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user