Commit Graph
774 Commits
Author SHA1 Message Date
can1357 259078d9df merge PR #4606: fix(pi-shell): pin spawn observer identity against Windows pid reuse 2026-07-08 15:19:41 +02:00
can1357 b086c5d669 chore: bump version to 16.3.11 2026-07-06 17:48:50 +02:00
can1357 48c53f8c56 chore: since perl crate decided to yank valid dists breaking my ci, im yanking it in return
who even uses perl?
2026-07-06 17:48:28 +02:00
roboomp 1aa31c062d fix(native): bounded inherited ignore files
Stop pi-walker from applying .gitignore/.ignore files from unrelated ancestors above an explicit non-repository search root. Preserve repo-root ignore inheritance when scanning a subdirectory inside a repository.

Fixes #4706
2026-07-06 14:07:41 +00:00
can1357 d8e568fad1 chore: bump version to 16.3.10 2026-07-06 08:13:30 +02:00
can1357 674cf01762 chore: bump version to 16.3.9 2026-07-06 04:19:29 +02:00
can1357 d806ac5e4c chore: bump version to 16.3.8 2026-07-05 19:43:28 +02:00
can1357 0ca345185c chore: bump version to 16.3.7 2026-07-05 16:58:53 +02:00
can1357 cae677702a style: formatted pi-uu-grep allow attribute with rustfmt 2026-07-05 12:46:06 +02:00
roboomp 04258b98d7 fix(pi-shell): bounded SpawnRegistry sweeps by watermark not raw length
Addresses the third review on #4606: `record` guarded pruning with
`spawned.len() >= PRUNE_THRESHOLD`. Once the recorded vec stabilized
above the threshold with entries the sweep could not remove — a run
whose live children exceed the threshold, e.g. `for i in {1..1000}; do
sleep 60 & done` — every subsequent `record` re-entered `prune_exited`
while holding the registry lock. Each sweep is O(N) (a status probe per
entry, plus a Toolhelp descendant walk on Windows for exited roots), so
the per-spawn cost climbed to O(n²) even though the doc-comment
promised amortized O(1).

The registry now tracks a `next_sweep_at` watermark alongside the
recorded vec (both under one mutex — the two fields are always
mutated together). A sweep fires only when `spawned.len()` crosses
that watermark; every sweep rescheds the next fire `PRUNE_THRESHOLD`
further records away from the current post-sweep size. Sweep frequency
is now capped at one per `PRUNE_THRESHOLD` records regardless of live
set size, restoring true amortized O(1) per spawn.

`build_targets` resets the watermark after its own sweep so the
record-time schedule stays consistent with the post-cancel live set.

Added `spawn_registry_watermark_bounds_sweep_frequency`: fills the vec
past threshold with permanently-live entries, records another 20, and
asserts the vec grew by exactly 20 (no sweep modified it) and the
watermark did not advance. Existing tests updated for the collapsed
`state` mutex.

Fixes #4605
2026-07-05 10:27:39 +00:00
roboomp 1877c7ed27 fix(pi-shell): kept Windows spawn entries until descendants exit
Addresses the second review on #4606: `prune_exited` retained an entry
while its process was running OR its process group was alive. On Windows
`process_group_alive` is always `false` (no process groups), so the
predicate collapsed to "root running" — when a brush-spawned root exited
after starting a child that stayed alive (a `pwsh`/shell command that
launches a helper and exits, an MCP stdio wrapper handing off to a
long-running server), pruning immediately dropped the pinned handle.

Dropping that handle closes the last thing keeping the root pid slot
reserved. Windows can then recycle the pid onto an unrelated process,
reintroducing the exact race #4605 closes. It also strands the leftover
child: the next cancellation wave has no root to walk descendants from,
so `signal_tree` never reaches it.

The retain predicate is now:

- root process still running → keep (all platforms);
- Windows-only: root exited but the pinned handle still probes at least
  one live descendant via Toolhelp → keep, because the handle is what
  guarantees the walk targets the original subtree (recycled pids would
  not be reachable while the handle holds the slot);
- pgid still alive → keep (Unix only; Windows falls through).

Unix stays unchanged because a child reparented onto init keeps its
pgid, so `process_group_alive` already catches "root gone, descendants
alive" without a per-entry tree walk.

Fixes #4605
2026-07-05 10:21:26 +00:00
roboomp 79e083124c fix(pi-shell): pruned exited entries from SpawnRegistry
Addresses the review on #4606: pinning a stable `Process` per spawn is
correct against pid reuse, but a long-running shell command that spawns
many short-lived external processes (a bash loop invoking a binary per
iteration) would otherwise retain one owned OS handle per historical
spawn — a pidfd on Linux, a process `HANDLE` on Windows — until the run
ends. Under enough iterations that hits the per-process FD/handle limit
and starts breaking `Process::from_pid` (or any other file operation) for
the rest of the run.

`SpawnRegistry` now sweeps entries whose pinned process and process group
are both gone. The sweep runs opportunistically inside `record` once the
recorded vec crosses a small threshold (`PRUNE_THRESHOLD = 64`) and
unconditionally at the top of `build_targets`, so the retained handle
count is bounded by the current live tree rather than the historical
spawn count. Amortized cost per spawn stays O(1); a sweep is O(N) probes
of `Process::status` (non-blocking pidfd `poll` on Linux, `WaitForSingle
Object(_, 0)` on Windows), running at most once per `PRUNE_THRESHOLD`
records.

The previous identity-pinning regression test was rewritten to defend
the actual invariant — the pinned handle carries into `build_targets`
while the child is alive, and the entry is dropped (never re-opened by
pid) once the child exits. A new regression asserts pruning keeps
retained entries under `PRUNE_THRESHOLD` after 2×threshold spawns of
short-lived children.

Fixes #4605
2026-07-05 10:14:07 +00:00
roboomp ce76309065 fix(pi-shell): pinned spawn observer identity against Windows pid reuse
`SpawnRegistry` previously stored only the raw pid reported by brush's
`SpawnObserver` hook and deferred `Process::from_pid` to `build_targets`
at cancellation time. Between the moment a bash-spawned child exited and
the moment cancellation fired, Windows could recycle that freed pid onto
an unrelated process — typically another `pwsh.exe` or `powershell.exe`
in a different Cursor terminal tab, since PowerShell is the parent shell.
`Process::from_pid` at kill time then opened the impostor, and
`signal_tree` walked the current Toolhelp snapshot for `ppid == root`
matches and `TerminateProcess`'d whatever subtree happened to live under
that recycled pid. That is the reporter's Variant A symptom: OMP crashing
kills unrelated PowerShell sessions.

The `SpawnObserver` impl now pins a stable `Process` handle *at spawn
time*, before any pid recycling window can open:

- Windows: an open process handle keeps the pid slot reserved for the
  handle's lifetime (Raymond Chen's documented invariant), so the pid
  cannot be reassigned while the registry holds a reference.
- Linux: the pidfd carries identity independent of the numeric pid.
- macOS: the recorded `(pid, start_tvsec, start_tvusec)` triple detects
  impersonation on every subsequent access.

`TerminationTargets::add_process` accepts a pre-pinned handle and skips
the `Process::from_pid` re-open entirely, and `build_targets` no longer
consults the raw pid at all — an entry the observer failed to pin (the
child exited before we could open a handle) becomes a no-op instead of
racing pid reuse.

Fixes #4605
2026-07-05 10:06:00 +00:00
roboomp f1e6a119e8 fix(metadata): corrected collab package contracts
Updated collab-web package metadata to the current published version and aligned swarm-extension with the matching pi-coding-agent major.

Documented the pi-uu-grep clap-only fields with dead_code allow reasons so the lint exceptions remain intentional.

Fixes #4549
2026-07-04 19:02:39 +00:00
can1357 98f6e9f004 chore: bump version to 16.3.6 2026-07-04 14:00:53 +02:00
can1357 959408f775 chore: reformat 2026-07-04 13:59:31 +02:00
can1357 81097c4ffa test(tui): consolidated streaming scrollback logic and testing infrastructure
- Exposed `Markdown.getLastRenderSettledRows` to track streaming progress.
- Simplified scrollback architecture by migrating logic to a unified `SeamLineList` boundary.
- Removed legacy safe-end methods and redundant `findCommittedPrefixResync` test suites.
- Fixed live tool and evaluation preview duplication issues during re-layouts.
2026-07-04 11:25:17 +02:00
can1357 cb30c981a0 feat(walker): implemented parallel filesystem traversal with streaming grep
- Introduced parallel streaming grep with windowed result processing to enhance search performance and memory management.
- Implemented stateful parallel file walking with buffer pooling and directory entry record caching to minimize memory allocations.
- Optimized ignore state derivation by using directory entry names instead of stat probes.
- Added comprehensive unit and performance tests covering parallel traversal correctness, early termination, and streaming behavior.
2026-07-04 11:22:04 +02:00
can1357 b258c790a5 style(pi-natives): fixed manual_assert lint in drop bomb test 2026-07-04 09:06:25 +02:00
can1357 db09905c25 refactor(pi-natives): reduced allocations in pattern deduplication
- normalize_pattern_list now skips all allocation for duplicate patterns
  and reuses the input String when trimming is a no-op.
2026-07-04 09:06:25 +02:00
can1357 1d7e20115b refactor(pi-iso): extracted shared command_failed error formatter
- Collapsed seven duplicated stderr-trim + exit-format sites across diff.rs, rcopy.rs, and overlayfs.rs into one pub(crate) helper.
- Exit code stays caller-formatted so signal-death renders unchanged.
- Also covers the fuse_mount site PR #4376 left with an eager to_string.
2026-07-04 09:06:25 +02:00
can1357 a3becdb7b6 Merge PR #4376: refactor(crates): clean up first-party Rust crates 2026-07-04 08:59:59 +02:00
can1357 fef9b62334 chore: bump version to 16.3.5 2026-07-04 05:19:15 +02:00
can1357 d0c1890a6c chore: bump version to 16.3.4 2026-07-03 06:37:14 +02:00
metaphorics 977e43f7f5 style(rust): format cleanup changes
Formats Rust files touched by the cleanup commits after cherry-picking them onto current upstream/main.

Op: compress
2026-07-03 09:31:52 +09:00
metaphorics cb80aaa6fb refactor(pi-walker): behavior-preserving cleanup\n\nOp: compress 2026-07-03 09:31:43 +09:00
metaphorics 17d9ab3b24 refactor(pi_uu_grep): behavior-preserving cleanup\n\nOp: compress 2026-07-03 09:31:43 +09:00
metaphorics 6680312a25 refactor(pi-shell): behavior-preserving cleanup\n\nOp: compress 2026-07-03 09:31:43 +09:00
metaphorics c458b60c8b refactor(pi-natives): behavior-preserving cleanup
Op: compress
2026-07-03 09:31:43 +09:00
metaphorics c79064ba62 refactor(pi-iso): behavior-preserving cleanup\n\nOp: compress 2026-07-03 09:31:43 +09:00
metaphorics 9e8f115d4f refactor(pi-ast): behavior-preserving cleanup\n\nOp: compress 2026-07-03 09:31:43 +09:00
can1357 afc79e4cda chore: bump version to 16.3.3 2026-07-03 00:57:22 +02:00
can1357 016a096611 Merge remote-tracking branch 'origin/farm/43148a2d/fix-git-apply-stderr-deadlock' 2026-07-02 23:43:11 +02:00
can1357 9e0b6cddf2 chore: bump version to 16.3.2 2026-07-02 17:50:38 +02:00
roboomp 2335c717a6 fix(pi-iso): drained git apply stderr during patch writes
Spawned a stderr reader before writing git apply stdin so large diagnostics cannot fill the pipe and deadlock dirty-state seeding.

Added a regression test with a fake git process that fills stderr before consuming stdin.

Fixes #4231
2026-07-02 08:34:23 +00:00
can1357 0ea6ea630b chore: bump version to 16.3.1 2026-07-02 09:00:04 +02:00
can1357 36cfb54e92 chore: bump version to 16.3.0 2026-07-02 04:12:21 +02:00
can1357 ec69edad5d refactor(natives): enabled disk logging for background task panics
- Downgraded `blocking_task_panic_scope` panics from silent to logged recoverable.
- Persists panic reports of caught worker task panics to the disk crash log while keeping stderr silent.
- Consolidated panic payload message extraction by reusing `crash_handler::panic_payload` in the task runner.
- Promoted the `SilenceHook` test helper to the shared testing module for use across multiple test suites.
2026-07-02 02:40:07 +02:00
can1357 ba1bd690e0 fix(natives): survived panic payloads whose destructors panic
- Extracted the panic message before disposing the payload; disposal now runs under its own catch_unwind with mem::forget fallback so a Drop-panicking panic_any payload can no longer unwind across the napi extern C boundary and abort the host.
2026-07-02 00:32:48 +02:00
can1357 3c22d25200 Merge PR #4110: fix(natives): catch task::blocking panics before FFI to reject JS Promise (@roboomp) 2026-07-01 21:53:15 +02:00
can1357 826517dc69 Merge PR #3969: fix: preserve bash cwd without hidden pwd probe (@jeffscottward) 2026-07-01 21:50:54 +02:00
can1357 e4fff0b061 fix(natives): probe reserved rayon capacity 2026-07-01 21:42:25 +02:00
can1357 d1015a6b87 Merge PR #3774: fix(natives): cap rayon pool on Windows (@roboomp) 2026-07-01 21:42:25 +02:00
can1357 5356713eae chore: bump version to 16.2.13 2026-07-01 20:03:42 +02:00
roboomp 3c698d136e test(natives): serialized panic-hook mutations behind a shared mutex
Introduced crate::testing::lock_panic_hook, a process-global Mutex that
every hook-mutating test now holds across its entire take -> set -> run
-> restore window. Without it, two parallel tests in the default cargo
test harness could interleave their take_hook / set_hook calls and pin
the noop hook as the process-global default, silently muting crash
diagnostics for every later test in the crate.

SilenceHook in task tests and blocking_task_panic_scope_restores_after_unwind
in crash_handler tests both acquire the lock on entry; SilenceHook keeps
the guard as a field so the hook restore in Drop happens under the same
lock. Mutex poisoning is unwrapped to inner so a single failing test
does not cascade.

Fixes #4071
2026-07-01 07:17:02 +00:00
roboomp 5662cc73df fix(natives): suppressed crash reports for caught task panics
Blocking task panics are now wrapped in a thread-local crash-handler scope before catch_unwind executes the worker closure. The panic hook recognizes that scope and treats the panic as silently recoverable, so it does not write a native crash report or chain to the default hook before the panic is converted into a GenericFailure Promise rejection.

The existing uutils recoverable path keeps its log-only behavior. Added crash-handler tests for the silent disposition and unwind-safe scope teardown.

Fixes #4071
2026-07-01 07:12:45 +00:00
roboomp bbd4c66768 fix(natives): catch task::blocking panics before FFI to reject Promise
napi-rs 3.9.4 registers async-work `execute` at src/async_work.rs:109 as
a plain `unsafe extern "C" fn` — not `extern "C-unwind"`. Any panic
inside a `Blocking::compute` closure unwound past that boundary and
force-aborted the host process under Rust's stabilized C-unwind rules
(RFC 2945, stable since 1.81), losing the JS Promise and session state.

Wrap the user closure in `std::panic::catch_unwind` inside
`Blocking::compute` and map the panic payload to
`Error::new(Status::GenericFailure, ...)` so it flows through napi-rs's
existing rejection path (`inner_task.reject` in `complete_impl`) and
surfaces as a rejected JS Promise instead of a host abort. Every
`task::blocking` caller (grep, ast, glob, listWorkspace, html-to-markdown,
snapcompact, fuzzy find, clipboard image read) inherits the guard.

Correct the root Cargo.toml `panic = "unwind"` comment — napi-rs's
per-call `catch_unwind` only covers the tokio-future path, not the
`Task`/`AsyncTask` async-work path we use here.

Regression test in `crates/pi-natives/src/task.rs` synchronously invokes
`Blocking::compute` with panicking closures (str literal, formatted,
`panic_any`, plus Ok/Err/double-invoke controls) and asserts the returned
`napi::Error` carries the tag and payload — proving the invariant: a
panicking closure MUST NOT unwind past this method.

Fixes #4071
2026-07-01 07:03:23 +00:00
Jeff Scott Ward 3da25618ea fix: preserve bash cwd without pwd probe 2026-06-30 23:51:28 -04:00
can1357 fccc0ed3ce chore: bump version to 16.2.12 2026-07-01 05:29:19 +02:00
can1357 db8c79cc93 style: apply formatter and fix devin test enum
biome + cargo fmt over merge-sweep eval-fix commits; correct StopReason.END_TURN (nonexistent) to StopReason.FUNCTION_CALL in devin streaming test
2026-07-01 04:45:28 +02:00