smokeTestDaemonBroker mkdtemp'd its runtime dir directly under
os.tmpdir(), and the broker's startup sweep pruneDeadDaemonRuntimeDirs
reclaims path.dirname(runtimeDir). On a default session that is /tmp, so
--smoke-test recursively deleted every aged sibling with no live
broker/clients (tmux/ssh sockets, editor state, build trees) while still
exiting 0.
- client.ts: keep the smoke broker's project and runtime dirs under a
single private mkdtemp parent the process owns.
- presence.ts: refuse any prune root that is not the daemons container
and only prune entries named like a 16-hex daemon scope key, so a
relocated runtime dir can never rm -rf unrelated neighbours.
Fixes#8721
~/.omp/run/daemons/<hash> scopes accumulated forever: broker shutdown
removed only the socket and PID lease, and nothing swept dead scopes, so
inert broker state and the persistent shared Chromium profiles grew
without bound (reporter: 1.3GB / 198 dirs in ~4 weeks).
Each broker now sweeps its sibling daemon roots on startup and removes a
scope only when its broker.pid is absent/dead, no live client presence
remains, and it has been untouched past a stale grace. The caller's own
runtime dir and the machine-global daemon container are skipped. The
sweep is detached and non-throwing so it never delays client connects.
Fixes#8674
- Replaced time-based sleeps and polling loops with event-driven promise resolvers and fake timers across agent and tool tests.
- Migrated test suites to share in-memory auth storage and fixtures using lifecycle hooks.
- Updated catalog model definitions, metadata, and configurations.
The coding-agent CI runner collects test files only from packages/coding-agent/test (scripts/ci-test-ts.ts), so the regression at src/launch/broker.test.ts was skipped by bun run test and CI.
Relocated to test/launch/broker-idle-shutdown.test.ts and rewrote it to the in-process broker pattern used by the sibling broker tests: it awaits the broker's own run() promise (resolves on idle shutdown) instead of watching the PID file, so shutdown is an awaited signal rather than a poll.
Fixes#8110
The idle-shutdown timer that fires while a persistent daemon is still
live returns without rearming, and the terminal-settlement path never
scheduled another idle check. Once the final client disconnected and the
last persistent daemon later exited, nothing rearmed idle shutdown, so
the broker process, endpoint, timers, and record maps stayed alive
indefinitely.
#settle now rearms idle shutdown after reaching a terminal state; the
timer re-checks clients, remaining live persistent records, and detached
project presence before shutting down.
Fixes#8110
Preserved zero-width readiness and wait matches across the daemon wire protocol, and isolated malformed completion events from unrelated pending RPCs.
Fixes#7908
- Implemented in-house, zero-dependency utility modules in `pi-utils` covering DOM manipulation, markdown parsing, templating, browser automation helpers, and terminal buffers.
- Migrated packages across the repository to consume the new internal utilities and `omptype` schema validators instead of external dependencies.
- Removed multiple external runtime and development dependencies including Zod, Marked, LRU cache, Turndown, and Puppeteer browser packages.
Publish terminal daemon completions to the session that started the
process so idle agents can resume without polling hub status.
Persist every unacknowledged generation with a stable completion ID and
immutable snapshot. Replay the collection after reconnect or broker
recovery, and clear each event only after the owning client acknowledges
it.
Signed-off-by: Christian Stewart <christian@aperture.us>
- Added an LSP multiplexer server, protocol definitions, and daemon lifecycle management to route traffic across sessions.
- Introduced `lsp.shared` settings configuration and SDK session creation support for shared language servers.
- Migrated shared daemon ensure helpers into a central launch module with updated import references.
- Added comprehensive unit tests and fake LSP server fixtures covering muxing, sharing, caching, and restarts.
- Implement the OMP Browser Relay extension with WebSocket communication and CDP RPC execution.
- Add browser relay server, daemon management, and bridge multiplexing in the coding agent.
- Introduce CLI commands and settings schema options for configuring and installing the relay.
- Add utility functions and test suites supporting environment parsing and relay lifecycle handling.
These four paths bypassed DirResolver's XDG-aware rootSubdir/agentSubdir
hooks, resolving directly against getConfigRootDir()/getAgentDir() and
ignoring XDG state/data layout. Add XDG-aware path helpers in dirs.ts
and route all four through them:
- secret-placeholder.key → $XDG_STATE_HOME/omp/ (state, agent flattened)
- marketplaces.json → $XDG_DATA_HOME/omp/ (data)
- run/daemons/<hash>/ → $XDG_STATE_HOME/omp/run/ (state)
- run/provider-inflight/ → $XDG_STATE_HOME/omp/run/ (state)
omp config init-xdg migrates secret-placeholder.key and marketplaces.json
from their legacy locations; run/ is ephemeral and rebuilds on restart.
- Added `ensureSharedBrowser` and shared browser acquisition to manage project-shared broker-owned Chromium instances.
- Implemented concurrent duplicate daemon start prevention and single-flight `pendingOpens` deduplication.
- Updated browser handle disposal to disconnect from shared daemons rather than closing them.
- Updated browser documentation and launch specifications to support shared and local headless runs.
A recovered detached daemon has no in-memory process handle, so two
concurrent refreshes can both enter settle for the same dead pid. The
initial guard runs before detached output is read; both continuations
could therefore pass it and then double-settle the generation.
Recheck generation and settled states after the awaited output read,
and cover concurrent refreshes against a recovered daemon. Without the
post-read guard the regression test observes restartCount 2 instead of
1.
Fixes#6852
A detached restart:"always" daemon that exits quickly parks in the
`restarting` state with process/pid cleared and a restartTimer armed.
Every subsequent op ran #refreshDetached, which only skips terminal
states, so it fell through to a re-entrant #settle. #settle's guard
only checked generation and terminalState, so re-entry proceeded:
restartCount++ and record.restartTimer was overwritten without clearing
the previously armed timer, orphaning it.
Consequences: stop cleared only the last timer, so an orphaned timer
later fired #launch (resetting stopRequested) and resurrected the
daemon; and restartCount phantom-inflated on every list/logs poll.
Add `restarting` to #settle's entry guard: it is a settled state
(child exited, relaunch timer pending) and no legitimate caller settles
while in it. Closes both the timer leak and the count inflation.
Fixes#6852
Bun's `process.title` setter is a JS-level no-op: it stores the value
internally but never calls `prctl(PR_SET_NAME)`, so `omp` appeared as
`bun` in ps/pgrep/killall/top and `pkill bun` became a footgun killing
every Bun process. Add `setProcessName` in pi-utils that also drives
prctl via bun:ffi on Linux, and use it at CLI startup and in the daemon
broker.
Fixes#6815
Replaced direct assertions on internal ordering and recovery helpers with an isolated broker integration test. The test seeds recovered terminal metadata, starts an active daemon, sends the authenticated list RPC, and asserts active-first ordering, true exit-time recency, the terminal cap, and protocol serialization/parsing.
Made the ordering and recovery helpers internal again because production wiring now supplies their coverage.
Fixes#6517
Recovery unconditionally restamped every non-detached record's exitedAt with the restart timestamp, including already-exited/failed ones, so after an idle-broker restart the list history cap could keep arbitrary directory-order entries and drop the genuinely most-recently-exited process.
Reap only records that were still alive at recovery; terminal records keep their real exitedAt/exitReason. Extract reapRecoveredSnapshot() and cover it with tests.
Fixes#6517
The broker `list`/ps op sorted every daemon record by createdAt ascending and never pruned, so in a long-lived project the active (newest) daemon rendered behind all exited ones and the response grew without bound.
List non-terminal daemons first (oldest to newest) and cap exited/failed history at the 10 most recently exited; truncated records stay addressable by name via describe/logs/restart.
Fixes#6517
A for:"ready" wait woke on any terminal state, but reported timedOut:false even
when readiness was never observed — the only success signal on the wait result —
so callers could chain work against a dead process. Split the wake predicate
from the ready-observed check: the wait still wakes on a terminal exit, but
timedOut now reflects whether readiness was actually observed (readyAt, live
ready, or a running daemon with no ready spec).
Fixes#6303
readyAt/readyMatch belong to the exited generation but were only reset by
#launch, which runs after the restart backoff delay. During the "restarting"
window the sticky-marker predicate from the prior commit therefore reported a
dead service as ready, letting start and for:"ready" waits race it. Clear both
markers when #settle enters "restarting"; #launch re-sets them once the new
child is up. Adds a regression test that observes the backoff window.
Fixes#6303
hub start and for:"ready" waits polled the live daemon state, so a process
that flipped starting→ready→exited within one 50ms poll interval was only
ever observed as "exited" and the wait blocked for the full readiness
timeout — despite #markReady durably recording readyAt. A pre-ready exit
had the same failure since terminal states only woke the wait during broker
shutdown.
Wake both waits on readyAt !== undefined || terminalState(state); readyTimedOut
= !ready then falls out. The start renderer reports "Process exited before
readiness was observed." for a pre-ready exit. Adds two regression tests that
hang to their caps on the old code.
Fixes#6303
Applied the console-aware launch policy in the original OMP process before spawning the broker. The broker now inherits the caller's console state instead of probing a detached console it created itself.
Applied console-aware spawn options to both non-PTY daemon paths. Windows children now stay attached when the host has a console and use CREATE_NO_WINDOW only for headless hosts.
Fixes#6018
- Reported the spawned PTY child PID through the native start callback.
- Replaced broker PID-file polling with the authoritative spawn event.
- Covered finite PTY startup without the legacy handoff in integration tests.
Fixes#5996
fs.realpath throws EISDIR on Windows drive roots (e.g. R:\), but
canonicalProjectDir in launch/presence.ts and launch/client.ts only
recovered ENOENT, aborting startup. Both now fall back to path.resolve()
on EISDIR, matching the existing ENOENT handling.
Fixes#5708
Added a direct-argv PTY entry point and used it for Windows launch sessions so portable-pty no longer re-quotes cmd.exe command text.
Rejected direct .bat and .cmd applications with guidance to use cmd.exe /c.
Fixes#5416
- Changed daemon log reads to return both sanitized display text and a raw `terminalText` slice, and included it on log RPC responses for PTY runs when grep was not used.
- Extended the logs result contract and launch tool rendering to consume `terminalText`, reconstruct terminal output, and display it in framed, preview-capped sections.
- Kept terminal row layout stable by writing space characters for empty cells when reading rows, preserving spacing during output reconstruction.
- Introduce `DAEMON_PTY_COLUMNS` and `DAEMON_PTY_ROWS` to `protocol.ts` for consistent PTY dimension management.
- Implement `renderTerminalOutput` in `launch/terminal-output.ts` to utilize headless xterm for accurate terminal state replay.
- Add `readTerminalRows` and `styleTerminalRow` in `tools/terminal-output.ts` to serialize terminal buffers while preserving safe ANSI styles.
- Update `broker.ts` to use standardized PTY dimensions and ensure terminal output is properly sanitized and well-formed during stream processing.
- Implemented a consolidated TUI renderer for launch tool operations to unify status headers and metadata.
- Added tracking for unfulfilled readiness conditions to distinguish between timeout causes and daemon states.
- Enhanced timeout reporting to explicitly surface specific unmet log or port conditions.
- Included comprehensive unit and regression tests for tool rendering and start-timeout diagnostics.
- Introduced a project-scoped `launch` tool to orchestrate long-running services, debuggers, and watchers with persistent execution capabilities.
- Implemented a robust daemon broker with Unix/Windows IPC transport that manages process lifecycles, readiness monitoring, and automatic log rotation.
- Added detached process support to ensure services persist independently of the main application lifecycle, including recovery and cleanup mechanisms.
- Updated the `bash` interceptor to prioritize the new `launch` tool for background processes and provided comprehensive documentation for lifecycle and signal handling.