diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f75e21c1c..1721121a6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -904,52 +904,9 @@ jobs: touch "desktop/src-tauri/binaries/buzz-dev-mcp-$TARGET" touch "desktop/src-tauri/binaries/git-credential-nostr-$TARGET" touch "desktop/src-tauri/binaries/buzz-$TARGET" - # Mesh rev is derived from Cargo.lock so a dependency bump needs no - # lockstep edit here; the cache key tracks it automatically. - - name: Resolve mesh-llm rev - id: mesh_rev - run: | - set -euo pipefail - REV=$(grep -oE 'mesh-llm\.git\?rev=[0-9a-f]{40}' Cargo.lock | head -1 | grep -oE '[0-9a-f]{40}') - [[ -n "$REV" ]] || { echo "::error::could not resolve mesh-llm rev from Cargo.lock"; exit 1; } - echo "rev=$REV" >> "$GITHUB_OUTPUT" - echo "short=${REV:0:7}" >> "$GITHUB_OUTPUT" - - name: Restore mesh llama build cache - id: llama_cache - uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5 - with: - path: ${{ github.workspace }}/.cache/mesh-llama - key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }} - - name: Build mesh llama native libraries - if: steps.llama_cache.outputs.cache-hit != 'true' - env: - MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }} - run: | - set -euo pipefail - cargo fetch --manifest-path desktop/src-tauri/Cargo.toml - SHORT="$MESH_REV_SHORT" - MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$SHORT" -type d -name "$SHORT" | head -1) - if [[ -z "$MESH_ROOT" ]]; then - echo "::error::mesh-llm checkout for $SHORT not found after cargo fetch" - exit 1 - fi - export LLAMA_STAGE_BACKEND=metal - export LLAMA_STAGE_BUILD_DIR="$GITHUB_WORKSPACE/.cache/mesh-llama/build-stage-abi-metal" - export CMAKE_OSX_DEPLOYMENT_TARGET=10.15 - "$MESH_ROOT/scripts/prepare-llama.sh" pinned - "$MESH_ROOT/scripts/build-llama.sh" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 - - name: Save mesh llama build cache - if: steps.llama_cache.outputs.cache-hit != 'true' - uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5 - with: - path: ${{ github.workspace }}/.cache/mesh-llama - key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }} - name: Build Tauri app run: cd desktop && pnpm tauri build env: CMAKE_POLICY_VERSION_MINIMUM: "3.5" MACOSX_DEPLOYMENT_TARGET: "10.15" CMAKE_OSX_DEPLOYMENT_TARGET: "10.15" - LLAMA_STAGE_BACKEND: metal - LLAMA_STAGE_BUILD_DIR: ${{ github.workspace }}/.cache/mesh-llama/build-stage-abi-metal - SKIPPY_LLAMA_AUTO_BUILD: "0" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f07fde7544..38f3d0b100 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,45 +133,6 @@ jobs: ./scripts/bundle-sidecars.sh # Mesh rev derived from Cargo.lock (no lockstep edit on dep bump); cache key tracks it. - - name: Resolve mesh-llm rev - id: mesh_rev - run: | - set -euo pipefail - REV=$(grep -oE 'mesh-llm\.git\?rev=[0-9a-f]{40}' Cargo.lock | head -1 | grep -oE '[0-9a-f]{40}') - [[ -n "$REV" ]] || { echo "::error::could not resolve mesh-llm rev from Cargo.lock"; exit 1; } - echo "rev=$REV" >> "$GITHUB_OUTPUT" - echo "short=${REV:0:7}" >> "$GITHUB_OUTPUT" - - name: Restore mesh llama build cache - id: llama_cache - uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5 - with: - path: ${{ github.workspace }}/.cache/mesh-llama - key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }} - - name: Build mesh llama native libraries - if: steps.llama_cache.outputs.cache-hit != 'true' - env: - MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }} - run: | - set -euo pipefail - cargo fetch --manifest-path desktop/src-tauri/Cargo.toml - SHORT="$MESH_REV_SHORT" - MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$SHORT" -type d -name "$SHORT" | head -1) - if [[ -z "$MESH_ROOT" ]]; then - echo "::error::mesh-llm checkout for $SHORT not found after cargo fetch" - exit 1 - fi - export LLAMA_STAGE_BACKEND=metal - export LLAMA_STAGE_BUILD_DIR="$GITHUB_WORKSPACE/.cache/mesh-llama/build-stage-abi-metal" - export CMAKE_OSX_DEPLOYMENT_TARGET=10.15 - "$MESH_ROOT/scripts/prepare-llama.sh" pinned - "$MESH_ROOT/scripts/build-llama.sh" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 - - name: Save mesh llama build cache - if: steps.llama_cache.outputs.cache-hit != 'true' - uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5 - with: - path: ${{ github.workspace }}/.cache/mesh-llama - key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }} - - name: Build unsigned Tauri app run: cd desktop && pnpm tauri build --verbose --no-sign --features mesh-llm --config src-tauri/tauri.release.conf.json env: @@ -182,9 +143,6 @@ jobs: CMAKE_POLICY_VERSION_MINIMUM: "3.5" MACOSX_DEPLOYMENT_TARGET: "10.15" CMAKE_OSX_DEPLOYMENT_TARGET: "10.15" - LLAMA_STAGE_BACKEND: metal - LLAMA_STAGE_BUILD_DIR: ${{ github.workspace }}/.cache/mesh-llama/build-stage-abi-metal - SKIPPY_LLAMA_AUTO_BUILD: "0" TAURI_BUNDLER_DMG_IGNORE_CI: "true" - name: Locate unsigned DMG diff --git a/Justfile b/Justfile index 9f59c2e065..1eb1c19351 100644 --- a/Justfile +++ b/Justfile @@ -6,9 +6,10 @@ desktop_dir := "desktop" desktop_tauri_manifest := "desktop/src-tauri/Cargo.toml" web_dir := "web" -# Opt-in mesh-llm. Off by default so `just dev`/`just staging` skip ~420 extra -# crates + the llama.cpp native runtime build and stay fast to iterate on. -# Turn on to test mesh compute features: `just mesh=1 dev` / `just mesh=1 staging`. +# Opt-in mesh-llm UI + node management (`just mesh=1 dev`). The mesh node is +# no longer compiled in — it is downloaded on first use as the official +# release binary — so this feature only adds a few KB of client code. Kept +# opt-in for now; candidate for default-on. mesh := "" # List all available tasks diff --git a/desktop/src-tauri/src/managed_agents/known_binaries.rs b/desktop/src-tauri/src/managed_agents/known_binaries.rs new file mode 100644 index 0000000000..cf34e7653e --- /dev/null +++ b/desktop/src-tauri/src/managed_agents/known_binaries.rs @@ -0,0 +1,63 @@ +//! Known agent/harness binary names for process ownership checks. +//! +//! Split from `runtime.rs` (file size budget); `#[path]`-included from there +//! so everything stays in the `runtime` module namespace. + +/// Binary name fragments for all known agent/harness processes that Buzz +/// may spawn. Used by `process_belongs_to_us()` and the orphan sweep to +/// identify processes we should clean up. Both hyphenated and underscored +/// variants are listed because macOS `proc_name()` and Linux `/proc/comm` +/// may report either form depending on how the binary was built. +pub(crate) const KNOWN_AGENT_BINARIES: &[&str] = &[ + "buzz-acp", + "buzz_acp", + "buzz-agent", + "buzz_agent", + "claude-agent-acp", + "claude_agent_acp", + "claude-code-acp", + "claude_code_acp", + "codex-acp", + "codex_acp", + "goose", + // buzz-dev-mcp's multicall personalities (rg, tree, buzz, + // git-credential-nostr, git-sign-nostr) are short-lived per-tool-call + // invocations — not listed here. + "buzz-dev-mcp", + "buzz_dev_mcp", + // Downloaded mesh compute node (mesh_llm/node_process.rs). Spawned with + // the BUZZ_MANAGED_AGENT stamp like agents, so the orphan sweep reclaims + // it after a desktop crash. A user-run standalone `mesh-llm serve` has + // no stamp and is never touched. + "mesh-llm", + "mesh_llm", +]; + +/// Script interpreters that may host managed agent wrappers (e.g. npm shims). +/// A process whose name matches here is NOT immediately claimed — it must also +/// carry `BUZZ_MANAGED_AGENT` in its environment (checked by the caller via +/// `process_has_buzz_marker()`). This avoids sweeping unrelated node processes. +pub(crate) const KNOWN_SCRIPT_INTERPRETERS: &[&str] = &["node"]; + +/// Check if a process name matches any of our known agent binaries. +/// Uses exact match or prefix-with-separator to avoid false positives +/// (e.g. `"goose"` must not match `"mongoose"`). +pub(crate) fn name_matches_known_binary(name: &str) -> bool { + KNOWN_AGENT_BINARIES.iter().any(|&binary| { + name == binary || { + name.starts_with(binary) && { + let rest = &name[binary.len()..]; + rest.starts_with('-') || rest.starts_with('_') || rest.starts_with('.') + } + } + }) +} + +/// Check if a process name is a known script interpreter that may be hosting +/// a managed agent wrapper (e.g. `node` running an npm shim for `codex-acp`). +/// Callers must additionally verify `BUZZ_MANAGED_AGENT` ownership. +pub(crate) fn name_matches_interpreter(name: &str) -> bool { + KNOWN_SCRIPT_INTERPRETERS + .iter() + .any(|&interp| name == interp) +} diff --git a/desktop/src-tauri/src/managed_agents/runtime.rs b/desktop/src-tauri/src/managed_agents/runtime.rs index e5624a0516..d32cc9b1a5 100644 --- a/desktop/src-tauri/src/managed_agents/runtime.rs +++ b/desktop/src-tauri/src/managed_agents/runtime.rs @@ -18,64 +18,9 @@ use path::build_augmented_path; type RespondToEnv = (Vec<(&'static str, String)>, Vec<&'static str>); -/// Binary name fragments for all known agent/harness processes that Buzz -/// may spawn. Used by `process_belongs_to_us()` and the orphan sweep to -/// identify processes we should clean up. Both hyphenated and underscored -/// variants are listed because macOS `proc_name()` and Linux `/proc/comm` -/// may report either form depending on how the binary was built. -pub(crate) const KNOWN_AGENT_BINARIES: &[&str] = &[ - "buzz-acp", - "buzz_acp", - "buzz-agent", - "buzz_agent", - "claude-agent-acp", - "claude_agent_acp", - "claude-code-acp", - "claude_code_acp", - "codex-acp", - "codex_acp", - "goose", - // buzz-dev-mcp's multicall personalities (rg, tree, buzz, - // git-credential-nostr, git-sign-nostr) are short-lived per-tool-call - // invocations — not listed here. - "buzz-dev-mcp", - "buzz_dev_mcp", - // Downloaded mesh compute node (mesh_llm/node_process.rs). Spawned with - // the BUZZ_MANAGED_AGENT stamp like agents, so the orphan sweep reclaims - // it after a desktop crash. A user-run standalone `mesh-llm serve` has no - // stamp and is never touched. - "mesh-llm", - "mesh_llm", -]; - -/// Script interpreters that may host managed agent wrappers (e.g. npm shims). -/// A process whose name matches here is NOT immediately claimed — it must also -/// carry `BUZZ_MANAGED_AGENT` in its environment (checked by the caller via -/// `process_has_buzz_marker()`). This avoids sweeping unrelated node processes. -pub(crate) const KNOWN_SCRIPT_INTERPRETERS: &[&str] = &["node"]; - -/// Check if a process name matches any of our known agent binaries. -/// Uses exact match or prefix-with-separator to avoid false positives -/// (e.g. `"goose"` must not match `"mongoose"`). -fn name_matches_known_binary(name: &str) -> bool { - KNOWN_AGENT_BINARIES.iter().any(|&binary| { - name == binary || { - name.starts_with(binary) && { - let rest = &name[binary.len()..]; - rest.starts_with('-') || rest.starts_with('_') || rest.starts_with('.') - } - } - }) -} - -/// Check if a process name is a known script interpreter that may be hosting -/// a managed agent wrapper (e.g. `node` running an npm shim for `codex-acp`). -/// Callers must additionally verify `BUZZ_MANAGED_AGENT` ownership. -fn name_matches_interpreter(name: &str) -> bool { - KNOWN_SCRIPT_INTERPRETERS - .iter() - .any(|&interp| name == interp) -} +#[path = "known_binaries.rs"] +mod known_binaries; +use known_binaries::{name_matches_interpreter, name_matches_known_binary}; #[cfg(unix)] pub(crate) fn process_is_running(pid: u32) -> bool {