renderAgentResult and the live-progress sibling cast
extractedToolData?.yield to Array<{ data }> and called ?.map without
checking the actual runtime shape. Optional chaining only short-circuits
on null/undefined, so any stray non-array value (a single yield object
landing in the slot) made .map undefined and threw
TypeError: completeData?.map is not a function — taking down every
`review` task render.
Both sites now route through a new normalizeYieldData helper (next to
normalizeReportFindings) that returns an array of yield records: it
preserves arrays unchanged, wraps a single object as a 1-element array
so the verdict still renders, and drops primitives. Added a regression
test exercising the result branch, the progress branch, the primitive
fall-through, and the canonical array shape — all of the failing-branch
ones reproduce the crash on the pre-fix renderer.
Fixes#1987
`TtsrManager` previously ignored the `ttsr.enabled: false` toggle:
- `addRule()` still registered rules, so `bucketRules` demoted them
to the TTSR bucket and dropped the rulebook/alwaysApply fallback
- `hasRules()` still returned true, so `AgentSession` entered the
TTSR matching path on every message_update
- `checkDelta` / `checkSnapshot` (via `#matchBuffer`) still matched
patterns and could abort the stream and inject system reminders
Gate all three public-ish entry points on `#settings.enabled` so
disabling TTSR short-circuits the whole path. Condition rules fall
through to the rulebook bucket in `bucketRules` instead of being
silently swallowed.
Closes#1767
The Ctrl+Q follow-up fallback should only be removed when a recognized
keybinding action claims that chord. Unknown or stale config entries are
ignored by the keybinding manager and should not suppress a working
follow-up default.
Addresses code review on #1905.
Returned the current workspace folder list when a server queried workspace/workspaceFolders after initialization so advertising the capability no longer left late requests answered with -32601.
Ensured rust-analyzer textDocument/didOpen ran before workspace readiness polling so the server could discover the file's Cargo workspace, and skipped the readiness wait entirely for standalone .rs files outside any Cargo workspace ancestor.
Continued rust-analyzer workspace readiness polling across transient analyzerStatus request timeouts while preserving early exit for unsupported methods and server failures.
Advertised workspace folder support during LSP initialization and waited for rust-analyzer to finish loading Cargo workspaces before opening project-indexed files.
Fixes#1976
The TTY-OR heuristic still mis-classifies the all-stdio-redirected case:
`omp -p "..." < in.txt > out.txt 2> err.log` from a real Windows Terminal
session has `stdin.isTTY === stdout.isTTY === stderr.isTTY === false`,
but the host process can still own a console that the kernel could
inherit. The TTY signals reflect handle redirection, not console
attachment — `GetConsoleWindow()` is the authoritative Win32 signal.
`spawn-options.ts` now:
- Calls `kernel32!GetConsoleWindow()` via `bun:ffi` on Windows. A non-NULL
HWND means the host has a console regardless of how the standard
streams are wired, so `windowsHide` stays `false` and the kernel
inherits the console — which is what fixes the #1960
numpy/pandas `LoadLibraryExW` hang and lets SIGINT recover via
`GenerateConsoleCtrlEvent`.
- Falls back to the TTY-OR heuristic when the FFI probe is unavailable
or off-Windows. That keeps the predicate working on non-Bun-FFI
runtimes and on POSIX, where `windowsHide` is a no-op anyway.
- Caches the probe result; console attachment is stable for the host's
lifetime in practice, and dlopening kernel32 on every kernel spawn
would be wasteful.
The pure helpers (`shouldHideKernelWindow`, `consoleAttachedViaTTY`)
stay separately exported so they remain unit-testable. The integration
boundary `hostHasInheritableConsole()` is what the kernel spawn site
calls, and its return is asserted to be a concrete boolean (kernel spawn
must commit to a `windowsHide` value).
Stand-alone `process.stdout.isTTY` mis-classifies any partial stdio
redirection — `omp -p "..." > out.txt` reports `stdout.isTTY === false`
even though the parent still owns a console via stdin/stderr. With the
previous check the kernel would have been spawned with `CREATE_NO_WINDOW`
in that scenario and re-hit the #1960 numpy/pandas `LoadLibraryExW` hang
and broken SIGINT.
The host owns a console it can share with the child whenever ANY of
stdin / stdout / stderr is still a TTY; only a fully detached launch
(true service / daemon, or `< in > out 2> err`) has nothing to inherit.
Renamed the predicate parameter to `hostHasInheritableConsole` so the
contract is unambiguous, and added five regression tests covering the
realistic shell redirection combinations.
`PythonKernel.start()` spawned the runner with `windowsHide: true`, which
in Bun maps to the Win32 `CREATE_NO_WINDOW` flag — that detaches the
long-lived child from any inherited console. Two consequences for OMP's
Python eval on Windows:
1. Native extensions that probe the console at init (e.g. NumPy's
`_core/_multiarray_umath.pyd` plus its bundled OpenBLAS/SLEEF
thread-pool init) can deadlock inside `LoadLibraryExW`, so the very
first `import pandas` / `import numpy` after a cold kernel never
returned. The reporter's faulthandler stack pinned the hang to
`_bootstrap_external.create_module` → `numpy/_core/multiarray.py:11`.
2. SIGINT cannot be delivered to a console-less process via
`GenerateConsoleCtrlEvent`, so the host's `proc.kill("SIGINT")`
silently no-ops — matching the reporter's "kernel unresponsive to
interrupt" log line. The 5s escalation then hard-kills the kernel.
Plain `python.exe -u -c "import pandas"` from the same venv inherits the
terminal's console and runs to completion, which is the contract this
change restores. The Python kernel now hides its window only when the
host itself has no console to share (service / piped launch); an
interactive TUI launch lets the kernel inherit the parent's console —
analogous to `python.exe` invoked from `cmd.exe`.
The `shouldHideKernelWindow` predicate lives in its own module so it can
be unit-tested without dragging in the kernel's runtime dependencies.
Short-lived helper subprocesses elsewhere (LSP probes, git, plugin
installs) intentionally keep `windowsHide: true` — they don't load
complex native modules and a brief console flash would be user-visible
noise.
Fixes#1960
- Described "auto" default gating MCP tools past 40-tool threshold.
- Noted late resolution in createAgentSession after registry exists.
- Updated legacy mcp.discoveryMode mapping to MCP-only.
- Made "auto" the default, hiding MCP tools past 40-tool threshold.
- Centralized discovery mode resolution in shared mode helper.
- Activated search tool in createAgentSession once full registry exists.
- Fixed assistant transcript blocks to expose append-only commit-safe boundaries.
- Updated TUI live-region commit and pinned-paint logic to clamp commit-safe ends.
- Fixed long streamed assistant replies to remain in native scrollback on overflow.
- Added a persistent error banner so stream errors survive transcript scroll.
- Cleared the banner when the next turn starts.
- Skipped pinning for aborts and normal stops.
- Removed async image-link materialization between user message_start and addMessageToChat.
- Fixed steering bubbles rendering below the tool output they were sent to steer.
- Materialized clickable image links via synchronous blob-store fallback instead.
- Cleared native scrollback on `omp`/`omp -c` so the resumed transcript no longer stacks on the prior run's welcome screen.
- Tracked assistant block finalization so aborted streaming messages stay repaintable when status rows land beneath them on ED3-risk terminals.
- Added image-reference rendering to make `[Image #N]` placeholders clickable in chat.
- Added MIME-aware image blob materialization with extensioned sidecar paths.
- Added clickable path, line, and URL hyperlinks for read, search, and fetch outputs.
- Hardened OSC8 hyperlink emission with URI validation and control-byte/idempotency checks.
- Replaced bottom-most-block liveness with a finalization check so the live region spans every still-mutating block.
- Kept unfinalized tools repaintable when out-of-band inserts append finalized blocks below them.
- Recomputed blocks as they cross out of the live region to seal their final content.
- Stopped capping the synthesized answer at 12 lines while sources expanded in full.
- Rendered the answer through Markdown so headings, bold, lists, and code display formatted.
- Added regression tests for expanded/collapsed answer rendering.
- Resolved in-flight bridge calls the instant the cell's signal aborts.
- Let the kernel unwind via KeyboardInterrupt instead of being hard-killed.
- Preserved persistent session state across wide subagent fan-out teardown.
The no-selector run_watch guard was using resolveDefaultRepoMemoized via tryResolveCurrentRepo, so a long-lived process could validate against a stale cwd-to-repo cache entry after the checkout or GitHub remote at that path changed. That allowed the guard to trust the current HEAD for an explicit repo based on the old cached repository.
Add a fresh best-effort cwd repo lookup for safety checks and use it before deriving branch/HEAD. Cached lookup remains for search default scoping where stale data only affects a convenience fallback. Added a regression test that populates the cache, changes the mocked repo at the same cwd, and asserts run_watch rejects before issuing API calls.
Refs #1949#1951
resolveGitHubRepo rejected calls that supplied both an explicit repo and a full Actions run URL when the two owner/repo slugs differed only by casing. GitHub repository paths are case-insensitive, so this was the same class of false mismatch as the cwd guard fixed earlier.
Compare repo slugs through a shared ASCII case-insensitive helper and use it for both the run-URL consistency check and the cwd guard. Added a regression test for repo=cagedbird043/cxf with a run URL under CagedBird043/CXF.
Refs #1949#1951
GitHub owner/repo slugs are case-insensitive; `gh repo view` returns
the canonical casing while callers may pass any casing. The new guard
used strict equality, so a caller in the correct repo who typed
`owner/repo` while the canonical form was `Owner/Repo` was forced to
pass a redundant `branch`/`run` selector. Normalize both sides via
toLowerCase() before deciding the cwd is a different repository.
Regression test covers the casing-only match.
Refs #1949#1951
executeRunWatch passed undefined for the explicit `repo` to
resolveGitHubRepo, so a call like
`{op: "run_watch", repo: "owner/cxf", branch: "main"}` from a nested
or umbrella workspace silently fell through to `gh repo view` in cwd
and streamed `watching <sha> on <cwd-repo>` against the wrong
repository.
Route params.repo through resolveGitHubRepo so the explicit owner/repo
wins over both cwd inference and run-URL inference. When no `branch`
or `run` selector is given, refuse to derive the watched commit from
`git HEAD` unless the cwd actually points at the resolved repo —
otherwise raise a ToolError telling the caller to pass `branch` or
`run` instead of silently rebinding to an unrelated commit.
Also deduped resolveSearchRepoScope's best-effort cwd resolution into a
shared tryResolveCurrentRepo helper used by the new guard.
Fixes#1949
`omp plugin install .` (and any cwd-relative, absolute, or tilde-prefixed
spec) failed with `Invalid package name: .` because
`classifyInstallTarget` only emitted `marketplace` / `npm`, so local paths
fell through to `validatePackageName`, which rejects every non-npm name.
The classifier now emits a third `local` arm for `.`, `..`, `./…`,
`..\…`, `~`, `~/…`, `~\…`, `/…`, `C:\…`, `C:/…`, and `\\unc` specs.
`handleInstall` dispatches that arm to `PluginManager.link()` — the same
code path as `omp plugin link <path>` — so the two verbs are
interchangeable for local plugin directories. `--dry-run` short-circuits
before any filesystem work, `--scope`/`--force` surface a warning since
they are no-ops here (link is idempotent, and scope only governs
marketplace installs).
Coverage: thirteen-case classifier matrix in `marketplace/cli.test.ts`
plus a new `plugin-install-local.test.ts` with spy-based routing checks
for `./`, `../`, `/`, `~/`, plus a real-filesystem test that stages a
plugin folder, invokes `runPluginCommand`, and verifies the resulting
symlink + lockfile entry. The new test calls `mock.restore()` in
`afterEach` so `piUtils` / `MarketplaceManager.prototype` spies do not
leak into sibling suites.
Fixes#1945
Hard-killed the tiny-model subprocess after worker-reported execution errors so ONNX native allocations are released before retries.
Added a fake-worker regression for the unknown-failure path and queued local completions.
Fixes#1940
- Parsed zip metadata via central directory and lazy ranged reads.
- Inflated member contents only when a specific entry is read.
- Prevented large or corrupt zips from freezing directory reads.
- Added selectorLineRanges to extract ranges from raw/conflicts selectors.
- Routed internal URLs through URL-aware splitter in content search.
- Treated display-mode selectors as whole-resource searches instead of rejecting.
- Resolved @-mentions to exact existing paths only.
- Relied on the TUI @-selector to insert complete real paths.
- Dropped candidate scanning to avoid dragging in same-named files.
- Made `@`-mention resolution directory-aware so a mention ending in `/` or `\` matches only directory candidates.
- Stopped stripping the trailing separator and fuzzy-matching an arbitrary same-named file (e.g. npm scopes like `@scope/`).
- Left non-slash mentions unchanged.
Add retry.modelFallback so users can keep automatic retry enabled while preventing retry recovery from switching through configured fallback model chains.
The default remains enabled, preserving existing fallback behavior. When disabled, retry still honors retry-after delays and retry limits while staying on the primary model.
Op: correct
Restores: spec:retry can stay enabled without automatic model switching