A deleted/nonexistent cwd also makes Bun.spawn throw ENOENT; only claim
'git is not installed.' when the working directory actually exists, else
name the missing directory. Addresses the Codex P2 review on #6237.
The read-only git helpers spawned `git` directly and only inspected the
result exit code. When git is absent from PATH, Bun's spawn throws ENOENT
(uv_spawn 'git') at launch, which escaped as an unhandled rejection and
crashed the process (e.g. Windows without git, relying on WSL git).
Translate a missing-binary launch failure into a non-zero GitCommandResult
in the async git() wrapper and into a null degrade in the sync reftable ref
readers, matching the existing non-zero-exit handling. Mutating/checked
commands keep surfacing the clean "git is not installed." error.
Fixes#6169
Bundled the release history as a fallback when package assets cannot be resolved, while preserving external package-directory overrides.
Added regression coverage for the compiled-binary fallback.
Fixes#6172
In /vibe mode the director is often idle while workers stream, so the
status-line tok/s badge showed a stale/zero rate even while parallel
workers were actively generating tokens. The badge now aggregates the
main session's live tok/s with every live vibe worker's tok/s, and
falls back to the main session's own cached rate when no workers are
streaming.
- Extract calculateTokensPerSecond to utils/token-rate.ts (neutral
location) so vibe/runtime.ts can depend on it without the render
layer depending on the heavy vibe/task graph.
- Add aggregateVibeWorkerTokensPerSecond to vibe/runtime.ts: sums
each live worker's rate via the shared calculator, returns null
when no workers are streaming so the main rate shines through.
- StatusLineComponent takes the aggregator via an injected
setVibeWorkerTokenRateProvider callback (wired in interactive-mode)
keeping the render layer off the vibe/task dependency graph.
- #getTokensPerSecond splits into #getMainSessionTokensPerSecond
(preserves the sticky per-assistant-message cache) plus the
worker-aggregation path, so the director-idle case no longer
short-circuits to null.
rev-parse --git-common-dir resolves symlinks while ensureIsolation derives
sourceCommonDir lexically from the session cwd (resolveRepository walks
path.resolve'd components). On any symlinked repo path (macOS /tmp,
symlinked project dirs) the lexical comparison missed, detachGitDir
returned "independent", and the parent-mutation leak silently survived.
Realpath both sides before comparing; regression test drives the gate
through a symlink alias.
- Match the rcopy worktree-add registration via realpath: git canonicalizes
the admin gitdir back-reference (macOS /var -> /private/var), so the
lexical comparison missed it and left a stale registration in the source
repo's worktree list.
- Carry core.fileMode so an explicit filemode=false source does not read as
mode-changed files in the detached isolation.
- Carry core.splitIndex and the sharedindex.* files referenced by a split
source index; restoring the raw index without them broke every git read.
- Carry the source shallow boundary file so history traversal over the
borrowed object DB stops at the boundary instead of failing.
- Regression test covering all three carries.
A fresh `git init` in detachGitDir dropped core.sparseCheckout and the
sparse-checkout patterns, and rebuilding the index via write-tree/
read-tree discarded skip-worktree bits. Files intentionally absent from
a sparse working tree then read as deletions, which delta capture could
apply back to the parent.
detachGitDir now restores the index verbatim (preserving skip-worktree,
assume-unchanged, and exact stage entries) and carries
core.sparseCheckout, core.sparseCheckoutCone, and info/sparse-checkout
into the detached .git before restoring the index. Falls back to
read-tree HEAD only when the source had no index.
Fixes#6003
A linked git worktree with an unborn HEAD (a fresh/orphan branch with no
commits) still shares the parent's common dir, so an isolated task's
first branch and commit would write into the parent repo. The previous
early return on a missing HEAD SHA left that shared metadata intact.
detachGitDir now severs unborn worktrees too: `git init -b <branch>`
preserves the checked-out branch name, ref freezing is gated on a born
HEAD, and the rcopy worktree registration is still removed.
Fixes#6003
Copy isolation backends (reflink/apfs/btrfs/zfs/block-clone/rcopy)
materialise the worktree by duplicating its `.git` verbatim. When the
parent is a linked git worktree its `.git` is a pointer file, so the
isolation shared the parent's HEAD/index/ref namespace: a task's
`git checkout`/`commit` moved the parent's branch, and the rcopy
`git worktree add` path stacked task branches in the shared namespace.
`ensureIsolation` now runs `git.detachGitDir` after `isoStart`, turning
each isolation into a standalone repo with a frozen HEAD/refs/index
snapshot that borrows the source object database via
`objects/info/alternates`. Isolated git ops stay private, every task
branch is parented on the requested base, and patch/branch capture
(`git fetch <merged>`) still resolves objects.
Fixes#6003
- Treated ZIP-based .jar/.war/.ear/.apk as zip archives in archiveFormatFromPath and parseArchivePathCandidates so read/write member access works.
- Shared one archive-extension alternation between format detection and path splitting to stop them drifting.
- Derived the markit convertible-extension set from a single source of truth (utils/markit) matching the registered converters (pdf/docx/pptx/xlsx/epub), dropping legacy .doc/.ppt/.xls/.rtf that had no converter and only produced Unsupported format errors.
- Updated read/write tool prompts to document the zip-family extensions.
Fixes#5808
- Routed automatic first-input and replan title requests through AgentSession lifecycle cancellation.
- Propagated disposal aborts to online provider and local tiny-model title generation.
- Added a regression test proving an in-flight title request settles when disposal begins.
Fixes#5666
- Replaced legacy `pi/` role alias prefix with canonical `@` syntax across model resolution, documentation, and tests.
- Added support for bare `*` default alias and multiple alias prefix detection with custom role resolution in `resolveConfiguredRolePattern()`.
- Enhanced thinking suffix parsing to accept unambiguous abbreviations (minimum 2 characters) for effort and level selectors.
- Extended `resolveCliModel()` and `filterAvailableModelsByEnabledPatterns()` to accept settings parameter for role alias resolution from `--model` flag.
- Centralized message preprocessing for tiny models to handle noise removal, code block stripping, and context formatting.
- Updated title generation logic to support self-closing tags and improved robustness against partial markers.
- Added structured guidance and system prompts for small models to prioritize output consistency.
- Implemented a title-generation benchmark harness and expanded test coverage for message preprocessing.
- Preserved newest-first ordering for startup changelog markdown so collapsed notices report the current release.
- Kept default changelog rendering oldest-first for explicit recent/full views.
- Added regression assertions for both startup and full-history heading order.
- Treated missing or invalid changelog markers as first install and persisted the current version without replaying historical notes.
- Shared bounded changelog rendering between startup and recent changelog views, with a 64 KiB startup cap and full-history hint on truncation.
- Added marker, truncation, recent/full rendering, and PTY startup regression coverage.
Fixes#5135
- Ran commit host completion before commit-agent session disposal so mnemopi/autolearn teardown cannot preempt a valid proposal.
- Converted missing commit-agent host outputs and split-plan gaps into thrown errors so omp commit cannot resolve into exit 0 without creating a commit.
- Preserved caller GPG_TTY state instead of forcing a bogus signing TTY in git and non-interactive subprocess environments.
Fixes#4794
- Applied the known reasoning envelope filter when deciding whether a title marker is visible.
- Added title extraction regressions for reasoning tag and reasoning fence envelopes before the visible title.
Fixes#5122
- Limited markerless fallback cleanup to leading leaked-thinking envelopes so literal reasoning syntax in plain titles survives.
- Added markerless regression coverage for think tags and thinking-fence titles.
Fixes#5122
- Parse only title markers that remain visible after leaked-thinking cleanup so markers inside leaked reasoning are skipped.
- Preserve literal reasoning tag syntax inside the chosen title and cover it with a regression test.
Fixes#5122
- Reused the leaked-thinking healer before parsing title markers so visible reasoning envelopes cannot win extraction.
- Added regression coverage for <thinking> and <think> envelopes that contain internal title tags before the real title.
Fixes#5122
- Renamed the `explore` agent to `scout` throughout prompt templates, agent definitions, and configuration schemas.
- Updated documentation and internal tool references to reflect the new agent identity.
- Removed literal HTML comment sentinels (`<!-- -->`) from thinking block displays.
- Added logic to hide blocks that consist entirely of reasoning noise and updated display validation to omit empty formatted output.
- Refactored the memoization cache to maintain separate slots for prose and raw modes.
- Replaced tool-based `set_title` invocation with XML-style `<title>` marker tags for session title discovery.
- Implemented robust JSON-unwrapping logic to handle and sanitize title generation outputs.
- Updated model registry in catalog with new model support, provider prefixes, and metadata adjustments.
- Synchronized system prompt documentation and test suites to reflect the new marker-based generation flow.
Follow-up to the #4420 opener hardening: absolute-path rundll32 fixes the
stripped-PATH spawn throw, but rundll32 exits 0 unconditionally, so the
delayed-failure telemetry added there can never observe a Windows launch
failure. Replace it with %SystemRoot%-resolved PowerShell Start-Process
via -EncodedCommand:
- failures ShellExecute itself reports (missing target, no handler
executable, access denied) surface as exit code 1 and reach the
existing non-zero-exit logging (verified live on Windows 11: missing
file exits 1; unregistered schemes exit 0 on any opener because the
OS hands them to the app-picker — documented limitation);
- the UTF-16LE/base64 payload keeps OAuth query strings opaque to
cmd/PowerShell metacharacter parsing; embedded single quotes are
doubled into a PS literal;
- %SystemRoot% anchoring with a bare-name PATH fallback preserves the
stripped-PATH resilience from #4420.
Also pins the WSL-mount test's path.resolve against Windows dev hosts
so the mocked linux platform stays deterministic.
Refs #4418
An intermediate commit whose net effect is already on HEAD (redundant
change, or 3-way merged to HEAD by "theirs == ours") stopped the
sequencer with "The previous cherry-pick is now empty" and was
treated as a hard conflict. mergeTaskBranches aborted the whole range,
marked the branch failed, and dropped every remaining non-overlapping
commit.
Add cherryPick.skip and cherryPick.isEmptyError to the git namespace,
then in mergeTaskBranches' catch classify the failure before aborting:
loop --skip while the error stderr matches the "now empty" phrase so
consecutive empties advance the sequencer; fall through to abort/fail
on the first non-empty error (genuine conflict with unmerged files).
Fixes#4438
Two independent defects broke /mcp reauth against S256-only providers on
Windows boxes whose PATH no longer references System32:
1. openPath spawned bare rundll32 and swallowed the
`Executable not found in $PATH` throw with a bare `catch {}`, so the MCP
controller's outer try/catch was dead and the transcript unconditionally
claimed "Opening browser automatically...".
2. TUI#prepareLine silently truncates any composed row wider than the
viewport. MCPAuthorizationLinkPrompt rendered `Copy URL: <full URL>` as a
single ~271-column line whose trailing parameter is
code_challenge_method=S256. On the reporter's 270-col terminal the cut
landed inside that parameter, dropping the method while keeping
code_challenge — which RFC 7636 §4.3 treats as plain PKCE, which Linear
correctly rejects with "The plain PKCE method is not allowed. Use S256
instead."
OAuthCallbackFlow now hosts a `GET /launch` route on the same loopback
callback server it already runs; the route 302-redirects to the pending
authorization URL and is advertised as `OAuthAuthInfo.launchUrl` — a
~30-char copy target no viewport can meaningfully truncate. The MCP OAuth
fallback, /login, setup wizard, auth-broker CLI, and login-dialog all
prefer the launch URL for the visible copy target, keep the full URL in
the OSC 8 hyperlink for click-through, and the MCP flow additionally
stages the copy target on the clipboard via OSC 52 (same pattern the
setup wizard uses).
openPath now resolves rundll32.exe through %SystemRoot%\System32 (with a
C:\Windows fallback when SystemRoot is unset) and logs both synchronous
spawn throws and non-zero exits via the shared logger, so silent
misconfigurations show up in ~/.omp/logs/omp.*.log. The dead try/catch
around openPath in the MCP controller is removed.
Fixes#4418
Sizing `maxTokens` off the static `model.reasoning` catalog flag cannot
distinguish a thinking model catalogued `reasoning: false` (e.g. Qwen3
served locally via llama.cpp, whose bundled jinja chat template defaults
`enable_thinking: true`) from a model that never emits thinking. The
tight non-reasoning budget was consumed by the thinking preamble before
the useful output could be emitted, so every affected call silently
failed with `stopReason: "length"`.
Drop the `model.reasoning` conditional across every affected online call
site and always reserve the reasoning-safe budget. `maxTokens` is a hard
cap, not a target — non-thinking completions still return in the tiny
happy-path budget.
Sites fixed:
- utils/title-generator.ts (30 -> 1024)
- utils/commit-message-generator (60 -> 1024)
- tts/speech-enhancer (512 -> 1536)
- auto-thinking/classifier online path (8 -> 1024); classifyLocal
keeps its separate LOCAL_ANSWER_MAX_TOKENS
- session/unexpected-stop-classifier online path (16 -> 1024);
classifyLocal keeps ANSWER_MAX_TOKENS
Fixes#4355
readTextFromClipboard called execSync for pbpaste, termux-clipboard-get,
wl-paste, and xclip; readMacFileUrlsFromClipboard did the same for
osascript; copyToClipboard for termux-clipboard-set. execSync parks the
event loop until the child exits or the 2000ms timeout fires, so a hung
clipboard daemon froze the TUI render loop for the whole budget on every
paste and copy chord (input-controller handleImagePaste and
handleClipboardTextRawPaste).
A new spawnCapture helper wraps Bun.spawn with the same 2000ms guard,
stdout-to-string decoding, and non-zero-exit/timeout throw semantics the
outer try/catch already assumed. Every synchronous clipboard shell-out
now yields to the event loop while the child runs. Regression test
under readTextFromClipboard runs a slow fake pbpaste and asserts a
concurrent setInterval keeps ticking; the pre-fix code delivered zero
ticks.
Fixes#4235
Added timeout-backed AbortSignals to update, Hindsight, and Smithery fetch calls so stalled endpoints abort instead of hanging indefinitely.
Added regression coverage for the timeout signals on the exposed command/client paths.
Fixes#4229