Skip to content

Releases: dahai80/fusion-executor

v0.2.11 — GUI display_info + move_path GuiAction (Issues #43 #44)

Choose a tag to compare

@dahai80 dahai80 released this 06 Sep 14:01
b78f770

Patch release v0.2.11

Fixes the last 2 open issues (#43, #44). Merged via PR #45 (squash).

#43 — Expose scale_factor / display capability query in GuiResult

  • New GuiAction::DisplayInfo{} enumerates active displays via CGDisplay::active_displays() → JSON [{id, scale, bounds:{x,y,w,h}, primary}] exposed as additive GuiResult.displays: Option<String>.
  • Trusted-independent (no TCC required).
  • Shared compute_scale_factor() now fills scale_factor in inspect_tree + screenshot (was screenshot-only) — removes hardcoded 2.0 reliance.

#44 — Batch mouse-move (waypoint path) GuiAction

  • New GuiAction::MovePath{points, duration_ms} — N-waypoint path in single atomic UDS round-trip (avoids N hover round-trips). Linear interp, 8 frames/segment, <2 points reject.

Safety

  • 0 new unsafe blocks (core-graphics 0.24 safe wrappers within fe-gui crate-level scope). GuiAction 18→20 variants.

Verification

  • 528 Rust + 231 Python (1 skip TCC) tests green.
  • clippy --all-targets -D warnings clean (only upstream block v0.1.6). fmt/ruff clean. maturin builds.

Version

3-point SSOT bump 0.2.10 → 0.2.11.

v0.2.10 — GUI coordinate mapping + batch actions + screenshot masking

Choose a tag to compare

@dahai80 dahai80 released this 04 Sep 16:54
caf0a61

What's new

Three GUI features for fusion-osagent (Issues #38, #39, #40):

#38 — Coordinate-mapping (scale_factor)

GuiResult.scale_factor: f32 bridges logical points (GuiAction x/y + AXPosition) ↔ physical pixels (screenshot_width/height). Retina=2.0, non-Retina=1.0. Caller converts pixel = point * scale_factor.

#39 — Batch actions (gui_action_batch)

Sequential multi-action pipeline collecting per-step GuiResult. Non-transactional (single-step failure does not abort later steps).

results = executor.gui_action_batch([
    {"kind": "focus_app", "bundle_id": "com.apple.Terminal"},
    {"kind": "screenshot"},
])

#40 — Screenshot sensitive-region masking (mask_sensitive)

GuiAction::Screenshot gains mask_sensitive: bool (default false). When true, AX-secure text fields (kAXSecureTextFieldSubrole) are pixel-redacted to black in the PNG (Y-flipped, scale_factor-adjusted). Gated on Accessibility trust.

Safety

0 new unsafe blocks. +7 Rust tests, +9 Python tests.

Verification

526 Rust + 222 Python (7 skip TCC) green; clippy --all-targets -D warnings clean; fmt/ruff clean; maturin builds.

Closes #38, #39, #40.

v0.2.9

Choose a tag to compare

@dahai80 dahai80 released this 04 Sep 15:48
de27e69

Patch Release v0.2.9

Ships post-v0.2.8 work already merged to main but unreleased.

PR #36 — dedicated IPC_RT perf fix (#5 soak deadlock)

Decouples IPC server fan-out from the shared BLOCKING_RT (8-worker clamp). Root cause of soak_stress.py deadlock: 64 connections × ~2 long tasks = 128+ tasks starving 8 shared workers → dispatch never scheduled → response frames never written → client 30s timeout.

Fix: dedicated IPC_RT (multi-thread, clamp(4,16) workers) for serve_blocking + probe_runtime. BLOCKING_RT stays for in-process run() path. 0 new deps, 0 new unsafe.

PR #37 — proptest + long_stability harness (enterprise caveats #4/#6)

  • #4 (no fuzz tooling): cargo-fuzz needs nightly (host stable-only) → proptest on stable — 4 property tests × 256 cases over SecurityGuard::validate() (panic-safety, dangerous-prefix block, ReDoS <1s, compound whitelist invariant).
  • #6 (no long stability test): scripts/long_stability.py — N-min sustained mixed exec+stream traffic vs real serve() subprocess, 8-worker concurrency, RSS sampling (no-leak <200MB, no-hang 60s budget, error-rate <1%).

RSS watchdog test fix

rss_watchdog_kills_memory_bomb / rss_watchdog_streaming_path failing: macOS memory compression compresses the 1GB virtual bomb to ~200MB physical RSS, below the 256MB threshold → watchdog correctly did NOT fire. Watchdog logic correct (physical RSS = true OOM metric). Fix: threshold 256MB → 64MB (3× margin, preserves OOM semantics).

Verify

  • cargo fmt --all -- --check ✓
  • cargo clippy --workspace --all-targets -- -D warnings ✓ (only upstream block v0.1.6 future-incompat)
  • cargo test --workspace — 519 passed
  • maturin develop --release — fusion_executor-0.2.9-cp314 wheel
  • ruff check . && ruff format --check . ✓
  • pytest python/tests — 220 passed, 1 skipped (TCC)

Full Changelog: v0.2.8...v0.2.9

v0.2.8 — Per-command Sandbox Profile (Issue #34)

Choose a tag to compare

@dahai80 dahai80 released this 03 Sep 07:36
55c4f15

Per-command Seatbelt/Sandbox Profile (Issue #34)

Add a per-command configurable SandboxProfile to ExecutionRequest — enables seatbelt/sandbox for the detached executor subprocess (fusion-code G2 sandbox-independence). Default-off / opt-in: None profile preserves the existing fixed profile byte-for-byte.

SandboxProfile fields

  • network (allow/deny, default deny) — allow omits network deny
  • filesystem (allow/deny_write/deny, default deny_write) — FS write deny control
  • excluded_commands ([]) — injected as seatbelt deny process-exec, sanitized
  • fail_if_unavailable (false) — fail-closed when sandbox-exec absent

Python usage

from fusion_executor import FusionSandboxExecutor, SandboxProfile

result = FusionSandboxExecutor().run(
    "echo hi",
    sandbox=SandboxProfile(network="allow", excluded_commands=["rm", "curl"]),
)

Properties

  • 4-layer additive wiring (fe-sandbox → fe-core → fe-ipc → fe-pyo3/Python)
  • 0 new unsafe (reuses fe-sandbox safe wrappers + which PATH probe)
  • Baseline preserved: sandbox=None byte-identical to v0.2.7
  • 510 Rust + 214 Python (6 skip TCC) green, clippy/fmt/ruff clean

Closes #34.

v0.2.7 — Server-Side Deterministic Cancel (Issue #32)

Choose a tag to compare

@dahai80 dahai80 released this 02 Sep 02:49
742879e

v0.2.7 — Server-Side Deterministic Cancel (Issue #32)

Patch release. Adds server-side deterministic cancel of in-flight execute_stream over UDS, plus a version bump.

Added

  • Server-side cancel (executor.cancel) — cancel an in-flight streaming execution with a deterministic process-tree kill, not a cooperative stop request. Resolves the stream by its JSON-RPC request id, fires a oneshot channel that fe-sandbox::run_streaming's biased tokio::select! is waiting on, then kills the whole process group: killpg(-pgid, SIGINT) → 500ms grace → killpg(-pgid, SIGKILL) → ppid-tree descendant walk (RUN-9) for setsid orphans. The terminal Done frame returns exit_code: -1 + cancelled: true.
    • fe-sandbox run_streaming(cfg, cancel_rx) — biased select over exit / cancel / channel-closed.
    • fe-ipc StreamRegistry = Mutex<HashMap<String, oneshot::Sender<()>>> (IPC layer, keeps Executor stateless per M-ARCH-1). Cancel works cross-connection.
    • ExecutionResult.cancelled: bool (#[serde(default, skip_serializing_if="is_false")]), 4-layer wired.
    • Python FusionSandboxExecutor.cancel_stream(stream_id, *, sock_path=None) -> bool.

Changed

  • Version bump 0.2.6 → 0.2.7 (3-point SSOT: Cargo.toml + pyproject.toml + python/fusion_executor/__init__.py fallback).

Tests

500 Rust + 210 Python (1 skip TCC) green. clippy --all-targets -D warnings clean (only upstream block v0.1.6 future-incompat). fmt / ruff clean. maturin builds.

Notes

  • 0 new unsafe — reuses fe-sandbox::kill_process_group_async (nix killpg / kill, all safe).
  • In-process execute_streaming(req, None) path is never cancellable (no serve() running → no registry); cancel is a UDS-only capability.
  • Closes #32 via PR #33.

v0.2.6

Choose a tag to compare

@dahai80 dahai80 released this 30 Aug 07:10
e92d81a

Patch Release v0.2.6

Patch over v0.2.5. Ships fusion-guard Phase 3 executor consumer (Issue #23, PR #30):

  • New crate fe-guard — sync UDS client + local wire-type mirror + rules cache (unsafe_code="deny", 0 new deps).
  • fe-security — guard orchestration + verdict mapping (Block|L4→block, L3→block, Redact→block, Preview→allow, Allow+L1/L2→allow) + degraded fail-closed on daemon down (never fail-open).
  • fe-core — ExecutionResult.guard_action_id + seatbelt gating E7 + TOCTOU recheck H4 (symlink_metadata, safe Rust).
  • fe-pyo3 + Python — guard_sock/guard_tenant plumbing, guard OFF by default (backward-compatible).

R4 resolution: existing static blocklist + whitelist fence = compile-time fail-closed fallback; DANGEROUS_BINS const not reintroduced.

Verification

  • 496 Rust tests + 206 Python tests green (1 TCC skip).
  • clippy/fmt/ruff clean (only upstream block v0.1.6 baseline warning).
  • maturin develop --release builds; __version__ == "0.2.6".

Full Changelog: v0.2.5...v0.2.6

v0.3.0-rc0 — Release Candidate (next minor track)

Choose a tag to compare

@dahai80 dahai80 released this 29 Aug 14:41
a7a01c7

v0.3.0-rc0 — Release Candidate (next minor track)

Pre-release / release candidate. This is NOT the latest stable release. The current stable release remains v0.2.4 (Enterprise-Publishable Upgrade).

This release candidate marks the HEAD of the main branch immediately after the v0.2.4 enterprise hardening shipped. It opens the v0.3.0 stabilization track for the next minor release.

Contents

v0.3.0-rc0 points at the same commit as the shipped stable v0.2.4 (a7a01c7). It captures the enterprise-publishable baseline as the starting point of the v0.3.0 line:

  • 11 Rust crates (fe-core / fe-security / fe-sandbox / fe-rollback / fe-diagnostics / fe-gui / fe-telemetry / fe-ipc / fe-tools / fe-shell / fe-pyo3)
  • Python 3.11+ bindings via PyO3 0.29 + maturin
  • 476 Rust tests (22 suites) + 200 Python tests (1 TCC skip) green
  • clippy --all-targets -D warnings clean (only upstream block v0.1.6 future-incompat baseline)
  • fmt / ruff clean; maturin develop --release builds
  • CI green on main (both Python + Rust jobs)

What landed in the v0.2.4 baseline (carried into this RC)

Security hardening

  • D3-1 inline-interpreter gateway (block python -c / node -e / ruby -e / perl -e by default; opt-in with_allow_inline_interpreter)
  • ARCH-2 resolved-path whitelist + D3-6 fail-closed (/tmp/python3 poisoning rejected; trusted_bin_dirs = /usr/bin / /usr/local/bin / /opt/homebrew/bin / /bin / /sbin + auto VIRTUAL_ENV/bin + current_exe parent + $HOME/.cargo/bin)
  • D3-2/D3-3 seatbelt default-on 4-layer + filesystem subpath match
  • D3-4 per-task RSS watchdog (sysinfo polls child-tree RSS, kill -124, oom_killed=true; Darwin RLIMIT_AS no-op)
  • IMPL-7 prompt-injection sanitize chokepoint (file_path / raw_trace / error_type caps 1KB/4KB/256B)
  • RUN-12 default bundle allowlist (Terminal/TextEdit/Finder) + IMPL-9 screenshot Screen-Recording TCC split from Accessibility

Runtime hardening

  • RUN-4/RUN-10 rlimit NOFILE(1024) / NPROC(512)
  • RUN-9 ppid-tree kill (killpg + sysinfo ppid BFS fallback vs setsid orphans)
  • RUN-6 git tokio timeout 30s + RUN-7 worktree gitdir (rev-parse --git-common-dir)
  • RUN-3 stream_sem=64 separate from exec_sem=16
  • RUN-1 py.detach across remaining block_on sites

Operations

  • D6-01 launchd plist + D6-02 latency p50/p95/p99 + D6-03 snapshot inventory
  • M-OPS structured logging (JSON fmt + daily rolling fe.log tee stderr + reload EnvFilter) + Prometheus metrics (UDS text, no HTTP port) + trace_id cross-layer + SIGHUP hot-reload

Architecture

  • ARCH-1 seatbelt default True 4-layer + ARCH-4 in-process metrics + ARCH-6 feature flags (gui/telemetry/diagnostics optional)
  • IMPL-1 default socket ~/.fusion-executor/fe.sock 4-layer + IMPL-2 deny_unknown_fields Rust serde + IMPL-3 Subscription crash marker (ConnectionError vs silent StopIteration)

Integration

  • examples/08_integrate_fusion_code.py integration skeleton (one-way, no fusion-code import) + ARCH-7 caller-circuit contract doc

Deferred

  • Issue #23 (fusion-guard Phase 3 cross-project integration) — intentionally out of scope; tracking comment posted. Executor security baseline hardened and ready to receive guard SSOT rules when Phase 3 is taken up.

Status of this RC

This is an rc0 cut from the same commit as the shipped stable v0.2.4. There is no new code beyond v0.2.4 yet — it establishes the v0.3.0 stabilization track. Subsequent rc1/rc2 cuts will incorporate new work on this line ahead of the final v0.3.0 stable release.

Stable release: https://github.com/dahai80/fusion-executor/releases/tag/v0.2.4

v0.2.5

Choose a tag to compare

@dahai80 dahai80 released this 29 Aug 23:45
5e7f985

v0.2.5 (patch)

Patch release over v0.2.4.

Changes

  • fe-security test fix (PR #28): allows_python and allows_env_prefix now use python3 instead of bare python. The D3-6 fail-closed PATH guard (ARCH-2 resolved-path whitelist hardening) rejects binaries that cannot be PATH-resolved; bare python is absent on some dev machines (only python3 resolves), so those tests failed locally while CI stayed green (CI venv ships python). No production logic changed — fail-closed behavior is correct by design; the test now matches the established allows_python_c_inline convention.

Version sources synced (3-point SSOT)

  • Cargo.toml workspace.package.version → 0.2.5 (11 crates inherit via version.workspace = true)
  • pyproject.toml project.version → 0.2.5 (maturin wheel version)
  • python/fusion_executor/__init__.py fallback __version__ → 0.2.5
  • Cargo.lock regenerated

Verification

  • 476 Rust + 200 Python tests green (1 TCC skip — GUI needs Accessibility/Screen-Recording permission, CI skips)
  • cargo clippy --workspace --all-targets -- -D warnings clean (only upstream block v0.1.6 future-incompat baseline warning, accepted)
  • cargo fmt --all -- --check clean
  • ruff check . + ruff format --check . clean
  • maturin develop --release builds wheel fusion_executor-0.2.5-cp314-cp314-macosx_11_0_arm64
  • fusion_executor.__version__ == "0.2.5"

Full Changelog: v0.2.4...v0.2.5

v0.2.4 — Enterprise-Publishable Upgrade

Choose a tag to compare

@dahai80 dahai80 released this 29 Aug 14:29
a7a01c7

v0.2.4 — Enterprise-Publishable Upgrade

Product-readiness audit (audit/fusion-executor-audit-result-product-0827.md, baseline v0.2.3) judged fusion-executor ❌ NOT enterprise-publishable (6 real CRITICAL + 33 MAJOR/MINOR). This release closes all of them, upgrading to enterprise-publishable. All work is on main; 476 Rust + 200 Python (1 TCC skip) tests green, clippy/fmt/ruff clean, maturin develop builds.

Scope

Per user directive: fix all 6 real CRITICAL + 33 MAJOR/MINOR + D3-1 inline-interpreter hardening (opt-in gateway). D3-4 heap limit had no pure-code fix → per-task RSS watchdog (sysinfo, existing dep) kills on memory overage as mitigation. Only excluded cross-project Issue #23 (fusion-guard Phase 3).

Highlights

Security hardening

  • D3-1 inline-interpreter gateway — fe-security::validate_argv blocks python -c / node -e / ruby -e / perl -e by default (binary+flag level, payload-agnostic — prevents agent-driven model one-liner bypass); with_allow_inline_interpreter(true) opt-in for trusted callers.
  • ARCH-2 resolved-path whitelist — resolve_binary_path double-check: basename in whitelist AND absolute path starts_with(trusted_bin_dirs) (/usr/bin, /usr/local/bin, /opt/homebrew/bin, /bin, /sbin + auto-registered VIRTUAL_ENV/bin, current_exe parent, $HOME/.cargo/bin). /tmp/python3 poisoning rejected.
  • D3-6 fail-closed — PATH-resolve failure (None) rejects instead of allowing.
  • D3-2 seatbelt default on — shell_start defaults seatbelt=true across 4 layers.
  • D3-3 seatbelt FS subpath match — file-write deny scoped correctly.
  • D3-8 socket permission hardening — fail-loud on permission errors.
  • D3-5/D3-10 secret redaction in command logs.
  • IMPL-7 prompt-injection sanitize — single chokepoint in slice(): sanitize_file_path / sanitize_raw_trace / sanitize_error_type with size caps (1KB/4KB/256B).

Runtime hardening

  • D3-4 per-task RSS watchdog — fe-sandbox sysinfo polls child-process-tree RSS; over-threshold kill (exit -124, oom_killed=true). Darwin RLIMIT_AS is a no-op, so watchdog replaces rlimit.
  • RUN-4/RUN-10 rlimit — seatbelt profile + non-seatbelt setrlimit NOFILE(1024)/NPROC(512).
  • RUN-9 ppid-tree kill — kill_tree killpg + sysinfo ppid-tree traversal fallback (defends against setsid-orphaned grandchildren).
  • RUN-6 git timeout + worktree gitdir — git() wrapped in tokio::time::timeout(30s); resolve_git_dir via rev-parse --git-common-dir (worktree .git is a file, ENOTDIR fix).
  • RUN-3 stream/exec sem split — exec_sem=16 (non-stream execute) + stream_sem=64 (streaming); long streams no longer starve short commands.
  • RUN-1 py.detach — all block_on sites release GIL.
  • RUN-12 default bundle allowlist — GuiConfig::default() non-empty safe set (Terminal/TextEdit/Finder); disable_bundle_allowlist opt-in for unrestricted.
  • IMPL-9 screenshot TCC split — Screen Recording TCC probed separately from Accessibility; early-return before AX gate.

Operations

  • D6-01 launchd plist + pidfile template.
  • D6-02 latency histograms p50/p95/p99.
  • D6-03 snapshot inventory list_snapshots + retention.
  • M-OPS structured logging — JSON fmt layer + daily-rolling fe.log tee stderr, runtime-reloadable EnvFilter.
  • M-OPS Prometheus metrics — executor.metrics_prometheus UDS text export (no HTTP port, M-SEC-01 UDS-only).
  • trace_id cross-layer — ExecutionRequest/Result carry trace_id (auto-gen uuid v4) + tracing::span! context.
  • SIGHUP hot-reload — log level + whitelist extras reload without restart.

Architecture

  • ARCH-1 seatbelt default True 4-layer (Python run/run_streaming/shell_start + Rust serde).
  • ARCH-4 in-process metrics — execute_sync counts into the global recorder.
  • ARCH-6 feature flags — fe-core [features] default=["gui","telemetry","diagnostics"]; headless --no-default-features.
  • IMPL-1 default socket ~/.fusion-executor/fe.sock 4-layer (M-SEC-01 private 0o700 dir).
  • IMPL-2 deny_unknown_fields Rust serde aligned with Python extra=forbid.
  • IMPL-3 Subscription crash marker — unexpected disconnect → ConnectionError not silent StopIteration.

Docs / integration

  • examples/08_integrate_fusion_code.py — one-way integration skeleton (consumes executor API, does not import fusion-code) + max_consecutive_failures circuit-breaker demo.
  • ARCH-7 caller-circuit contract documented (reserved field; caller owns consecutive-failure count).

Stats

  • 476 Rust tests green (22 suites)
  • 200 Python tests green (1 TCC skip — manual GUI permission path)
  • clippy --all-targets -D warnings clean (only upstream block v0.1.6 future-incompat)
  • fmt / ruff clean
  • maturin develop --release builds fusion_executor._native

Audit remediation

This release supersedes the prior v0.2.3 enterprise-hardening release by resolving the product-readiness audit's full CRITICAL + MAJOR/MINOR set. Audit reports (read-only reference):

  • audit/fusion-executor-audit-result-product-0827.md — product-readiness (this release's target)

Deferred: cross-project Issue #23 (fusion-guard Phase 3) — out of scope for this repo.

v0.2.3 — Enterprise Hardening

Choose a tag to compare

@dahai80 dahai80 released this 27 Aug 02:23
deae2c3

v0.2.3 — Enterprise Hardening Release

This release lands the enterprise audit (audit/fusion-executor-audit-result-0826.md) follow-up: 10 CRITICAL + 15 MAJOR + 12 MINOR fixes, plus ops/observability hardening and glob spec alignment. All 12 GitHub issues closed.

Highlights

v0.2.1 — M-ARCH-1 (architecture)

  • ShellRegistry moved out of Executor to the IPC/PyO3 layer (Arc<ShellRegistry>).
  • Executor regains strict per-task statelessness — expressed in the type system (shell_start takes &registry, 3 fns became associated).
  • Fixes P-PYO3-01: a serve-path Executor rebuild no longer drops background sh-N handles; in-process + serve paths share one registry.

v0.2.2 — Observability & operations

  • M-OPS-01 structured logging: JSON fmt layer teeing a daily-rolling fe.log file with stderr; runtime-reloadable EnvFilter (basis for SIGHUP). Log dir resolves FE_LOG_DIR → ~/.fusion-executor/logs/.
  • M-OPS-02 Prometheus metrics: executor.metrics_prometheus UDS arm returns text format. No HTTP port opened (M-SEC-01). Counters/gauges: fe_exec_total/fe_exec_success/fe_exec_blocked/fe_exec_timeout/fe_exec_failed/fe_rollback_total/fe_rollback_failed/fe_shell_active/fe_connections.
  • M-OPS-06 + m-OPS-03 trace_id: ExecutionRequest/ExecutionResult gain trace_id (auto-gen uuid v4); carried in tracing::span! log context across layers.
  • m-OPS-02 SIGHUP hot-reload: kill -HUP <pid> reloads log level (RUST_LOG) + whitelist extras (FUSION_EXECUTOR_EXTRA_WHITELIST) without restart. SecurityGuard.whitelist → ArcSwap<HashSet> (interior mutability, Executor stays stateless); extras rebuild from baseline, never accumulate; dangerous interpreters rejected.

v0.2.3 — #20 glob E1 spec alignment

  • fe-tools glob(): globset::Glob::new → GlobBuilder::new(pattern).literal_separator(true).build().
  • */? no longer cross / (matches fusion-event E1 ecosystem glob spec); ** still crosses directories.
  • Bug found + fixed by 3 acceptance tests written before the fix (Rule 9): src/*.swift no longer wrongly matches src/sub/a.swift.

Verification

cargo fmt --all -- --check                      # clean
cargo clippy --workspace --all-targets -- -D warnings   # 0 errors
cargo test --workspace                          # 359 passed, 0 failed
pytest python/tests                             # 184 passed, 1 skipped (TCC)
ruff check . && ruff format --check .           # clean
maturin develop --release                       # fusion_executor-0.2.3

Upgrade notes

  • Socket default changed to ~/.fusion-executor/fe.sock (private 0o700 dir, M-SEC-01) — set FUSION_EXECUTOR_SOCK to override.
  • SIGHUP is reload-only (never shuts down); SIGINT/SIGTERM still stop the server.
  • No breaking API changes — trace_id, metrics, logging are all additive.

Full changelog: see CLAUDE.md version blocks (v0.2.1 / v0.2.2 / v0.2.3).