- Introduced `isProbablyBinary` utility to sniff file headers for NUL bytes or invalid UTF-8 sequences.
- Updated `ReadTool` to use the binary sniffer, preventing mojibake corruption in output when reading non-text files.
- Refined `file-mentions` auto-reads to skip binary files and mark them as `binary` in the message transcript.
- Added comprehensive unit tests for binary detection logic, covering NUL bytes, truncated multibyte characters, and path-based file sniffing.
Rejoined split Windows extension module paths before launch parsing finishes and stripped extended-length Win32 prefixes before Bun import and worker spawn APIs see them.
Fixes#3804
- Introduced comprehensive support for multiple concurrent, independently-configured advisors via `WATCHDOG.yml` files.
- Implemented a full-screen TUI overlay for managing advisor rosters, models, tools, and instructions.
- Added session-wide advisor initialization, telemetry aggregation, and named transcript isolation.
- Enhanced advisor security and observability with secret redaction in tool results and secure XML attribute encoding.
Bun's `Response(stream).bytes()` returns the raw `ArrayBuffer` once the
body arrives in more than one chunk, which happens for subprocess stdout
past ~128 KB. The public contract of `ptree.ChildProcess.bytes()` is
`Promise<Uint8Array>`, and callers — most visibly the `ssh://` read
path's `decodeUtf8Text` — rely on `Uint8Array` methods such as `.indexOf`
and `.subarray`. On larger remote text files this surfaced as:
TypeError: bytes.indexOf is not a function
Normalize the result at the boundary: when `Response.bytes()` hands back
an `ArrayBuffer`, wrap it in a zero-copy `Uint8Array` view before
returning. Adds a regression test that drives a 256 KB stdout payload
through `ptree.spawn(...).bytes()` and asserts the contract.
Fixes#3712
- Renamed the `find` and `search` tools to `glob` and `grep` respectively across the codebase to improve command clarity.
- Implemented full-stack support for the renamed tools, including CLI arguments, system prompts, SDK exports, and tool registration.
- Added automated migration logic in `settings` to transform legacy `find` and `search` configuration keys to their new equivalents.
- Updated the `collab-web` renderer registry to ensure backwards compatibility with legacy tool outputs.
- Added a `worktree.base` setting to allow users to specify a custom directory for agent-managed git worktrees.
- Updated the `worktree` command to ensure settings are initialized before operations to respect the configured base path.
- Enhanced directory resolution logic to support `~` expansion and enforce absolute paths for worktree environments.
- Centralized JSON parsing and stream processing logic by moving utilities from `packages/ai` to the shared `@oh-my-pi/pi-utils` package.
- Standardized import paths for JSON parsing and streaming across the agent, ai, and coding-agent packages.
- Refactored SSE stream handling to use consolidated `parseStreamingJson` logic and introduced robust error recovery for malformed container-shaped tail events.
- Cleaned up legacy bundled registry references and updated related module exports and tests to reflect the new utility structure.
The retry-on-EBUSY logic in removeSyncWithRetries/removeWithRetries
used 40 retries × 25ms = 1 second total. Windows can hold file locks
on SQLite databases for up to ~1.5s after close(), so tests that
already used removeSyncWithRetries (e.g. settings-manager.test.ts,
sdk-credential-disabled-bridge.test.ts) still failed with EBUSY.
Fix: increase the delay to 50ms (40 × 50ms = 2s total window). This
only affects Windows — the retry logic only runs on win32. Linux CI
is unaffected.
(cherry picked from commit ac67e96f1285ce0d80b2022c40c383b7ca262354)
- Set logger to silent mode when no transports are active to avoid "no transports" errors during log emission.
- Added a regression test to verify that disabling all transports suppresses warnings and that log output resumes after re-enabling transports.
- Added a check for empty transport arrays during logger initialization.
- Set the logger to silent mode if no transports exist to prevent unnecessary warning messages on every log emit.
The async removeWithRetries function in packages/utils/src/temp.ts was
used internally by TempDir.remove() but not exported, while the sync
variant (removeSyncWithRetries) was exported. This inconsistency forced
tests with async cleanup hooks (afterEach(async () => ...)) to use
fs.rm directly, which fails with EBUSY on Windows when SQLite database
files are still locked by the process.
Fix: export removeWithRetries so async tests can import it from
@oh-my-pi/pi-utils and use retry-enabled cleanup. Migrated
auth-storage-api-key-login.test.ts as a demonstration — 5 EBUSY
failures now pass consistently on Windows.
Repeated reads of unchanged PDFs, Office documents, and EPUBs re-ran the
full markit conversion every time. Add a transparent, content-addressed
cache for successful conversions keyed by SHA-256(content) + normalized
extension, so repeat reads reuse converted markdown instead of
reconverting.
- packages/utils: XDG-aware getDocumentConversionCacheDir() helper
- coding-agent: markit-cache module (bounded 256 MiB, oldest-first prune,
best-effort writes that never fail conversion) layered over the central
convertFileWithMarkit/convertBufferWithMarkit wrappers
- imageDir conversions stay uncached (cache:"skipped") to preserve PDF
image extraction side effects; failed/empty/aborted conversions are
never cached
- abort-safe: file byte reads run under untilAborted; cache I/O rechecks
the signal
- Included the timeout option in the initialization object when prepareInit is absent.
- Ensured that custom timeout settings are preserved for long-running streams instead of being silently dropped.
Fixes#2422
Export removeSyncWithRetries from @oh-my-pi/pi-utils as a standalone
function, then migrate the highest-impact fs.rmSync call sites:
- test/helpers/temp-home-cleanup.ts: 2 fs.rmSync → removeSyncWithRetries
(affects all tests using the cleanupTempHome helper)
- test/core/apply-patch-regression.test.ts: 16 fs.rmSync → removeSyncWithRetries
(the most fs.rmSync calls of any test file)
removeSyncWithRetries retries on EBUSY/EPERM/ENOTEMPTY (40x25ms on
Windows), matching TempDir.removeSync's retry logic. On Linux/macOS
(CI) the retries are a no-op — fs.rmSync succeeds immediately.
- Introduced `abortableSource` as a lighter, direct-reader async generator.
- Removed the `createAbortableStream` public API to eliminate unnecessary stream wrapper layers.
- Updated internal stream processing to use `abortableSource` for improved memory and performance.
- Export `directoryExists` in `utils` to safely validate working directories before traversal.
- Update `SessionManager` and startup logic to fallback to the launch directory if a session's recorded working directory no longer exists.
- Add regression tests to ensure sessions now correctly adopt the launch directory instead of crashing on missing paths.
- Added `safeSend` helper wrapping `Subprocess.send()` so sync throws and async EPIPE rejections cannot escape.
- Replaced inline try/catch send wrappers in STT, TTS, and tiny-title clients with shared `safeSend`.
- Added `isIpcSendEpipe` predicate and made matching rejections non-fatal in the `unhandledRejection` handler.
- Added contract tests for `safeSend` and `isIpcSendEpipe` covering sync throws, async rejections, and edge cases.
Fix all Windows-specific test failures caused by path handling problems
and EBUSY errors from unclosed SQLite database handles.
Root causes fixed:
1. POSIX path assumptions: replaced hard-coded file:///tmp, /repo, etc.
with pathToFileURL/path.resolve/path.join computed expectations
2. shortenPath() now normalizes backslashes to forward slashes after ~
and respects home directory boundaries
3. HistoryStorage.resetInstance() leaked its Database — added #close()
that finalizes all prepared statements and closes the DB
4. AgentStorage gained the same resetInstance()/#close() pattern
5. SqliteAuthCredentialStore.close() leaked one-off prepared statements
from inline this.#db.prepare() calls — wrapped each in try/finally
6. model-cache.ts used a process-global DB even for custom dbPath —
now opens/closes per-call via withModelCacheDb
7. createAgentSession leaked AuthStorage on construction failure —
added ownsAuthStorage cleanup in catch block
8. MnemopiBackend.removeDbFiles() now truly best-effort (catches errors)
9. TempDir retry window expanded from 4x10ms to 40x25ms
10. TempDir prefix convention: non-@ prefixes created dirs relative to
cwd instead of os.tmpdir() — all test temp dirs now use @ prefix
11. Shell-escaped interpolated paths in bash tool tests
12. git core.autocrlf false in autoresearch test repo init
All 522 previously-failing Windows tests now pass.
- Removed configurable tab width support and the `display.tabWidth` setting across all packages.
- Deleted obsolete utility functions `getIndentation`, `getIndentationNoescape`, and `setDefaultTabWidth`.
- Standardized tab expansion logic to use a fixed `DEFAULT_TAB_WIDTH` globally.
- Cleaned up related configuration schemas, test suites, and internal API signatures to remove path-dependency.
- Prevents absolute `AbortSignal.timeout` from cutting off active stream bodies by introducing a clearable pre-response timer.
- Clears the watchdog timer for Bedrock, Gemini, Ollama, and Codex providers once headers are received.
- Restores regular caller abort signaling capability on the combined fetch stream.
- Adds comprehensive fake-timer tests to cover the timeout arming and clearing lifecycle.
- Added `isTerminalHeadless()` / `setTerminalHeadless()` to `env`, a process-wide switch that suppresses real-terminal side effects (stdout escape/frame writes, stdin raw mode, CSI/OSC probes, SIGWINCH, window-title changes, emergency restore).
- Defaulted the flag to `isBunTestRuntime()` so it engages when `bun test` sets `NODE_ENV=test`, and made `setTerminalHeadless` return the previous value so callers can restore exact prior state.
- Introduced advisory note output as `<advisory>` tags with optional severity and guidance.
- Updated session transcript formatting to `### Session update` and inline watched role labels.
- Added shared `escapeXmlText` utility and escaped XML-sensitive text in advisor outputs.
- Added one-shot success run token metrics and one-shot statistics reporting.
- Added `WorkerInbox` and `installWorkerInbox(port)` to queue worker messages before bind.
- Added `consumeWorkerInbox()` to replay buffered messages and clear one active inbox.
- Added buffered inbox consumption in JS and tab worker transports before direct message handlers.
- Normalized worker selector arguments to the `__omp_worker_*` naming across workers and tests.
- Fixed OAuth credentials to keep unknown fields in schema while preserving existing shape checks.
- Fixed MCP OAuth IDs to be profile-scoped and avoid deleting credentials from non-active profiles.
- Fixed string-flag parsing so PROFILE_BOOTSTRAP_BOUNDARY tokens are not consumed as values.
- Fixed active-profile directory resolution to refresh after env updates so profile .env overrides apply.
Loaded Kokoro's side-installed transformers runtime by absolute path before requiring kokoro-js, avoiding host/workspace onnxruntime libraries in the worker process.
Kept runtime-cache bare module requests inside the registered runtime cache when the parent module is already inside that cache, and covered the resolver boundary with a regression test.
Fixes#2591
The added node:fs/promises import preceded node:fs, which biome's
organizeImports rejects and fails CI lint. Reorder to unblock merge.
Addresses review feedback on #2382.