- Added a SessionFocusController to switch transcript and input context between main and subagent sessions.
- Added agent-hub Enter activation and double-left return behavior for focused local agents.
- Added view-session-based event and render logic to avoid stale focus-session state.
- Added status-line focused agent display with ghost icon and focused-mode border dimming.
- Replaced the fixed `CALL_PREVIEW_MAX_LINES` cap in `render-utils.ts` with a dynamic `previewWindowRows()` (terminal rows minus a chrome reserve); `capPreviewLines` now returns lines untouched when expanded and windows them otherwise.
- Applied the cap in every render state for bash and ssh command previews — streaming and final render identically instead of snapping fully open on completion — and included the preview window in bash's render cache key so terminal resizes invalidate it.
- Capped eval cell code previews via the same window; previously cell code was never capped at all, with `ctrl+o` remaining the only way to uncap.
- Added stream-vs-final parity coverage in `bash-sixel-render.test.ts`, `ssh-render.test.ts`, and the new `eval-code-preview.test.ts`, and a changelog entry.
- Added `fanOutFileItems` plumbing through `resolveSearchPathItems`/`resolveExplicitSearchPaths` and `ToolScopeOptions` so plain-file entries can be scanned as explicit targets instead of being folded into an unrequested ancestor walk.
- Search tool now enabled `fanOutFileTargets` and deduplicated overlapping results by tracking a `path\0line` key before merging matches.
- Expanded multi-path tests for shared non-root ancestors, explicit `.git/config` targets, and overlap deduplication behavior.
A subagent's send await:true to Main during a blocking task spawn was a structural deadlock: deliverIrcMessage queues mid-turn messages as step-boundary asides, but Main's next boundary requires the sender's own batch to finish, so the sender always burned the full irc.timeoutMs. Awaited sends now pass expectsReply through IrcBus.send; a mid-turn recipient with async.enabled off generates an ephemeral no-tools reply via runEphemeralTurn, records an irc:autoreply aside in its own history, and delivers it back over the bus with replyTo threading so the sender's waiter resolves.
- Updated TaskTool to skip `session.asyncJobManager` and run `task` spawns inline whenever `async.enabled` is false.
- Set `async.enabled` default to `true` and updated task prompts/settings text to reflect async-versus-sync behavior.
- Adjusted task batching tests to cover both async background execution and synchronous batched execution when async is disabled.
Adds an `expert` server entry (binary `expert`, invoked `expert --stdio`)
to the built-in LSP defaults, auto-detected for Mix projects
(`mix.exs`/`mix.lock`). Placed after `elixirls` so elixir-ls remains the
primary navigation server when both binaries are installed; this keeps the
default getServerForFile() behavior unchanged. Absent-binary setups are
unaffected by loadConfig's PATH/root-marker gating.
Includes a docs row, changelog entry, and a regression test pinning that
`.ex` files match `expert` and that `elixirls` still sorts ahead of it.
- Replaced task-simple-mode with a `task.batch` setting enabled by default.
- Updated task schema to use batch `{agent, context, tasks[]}` payloads.
- Migrated task execution to spawn one async job per task and merge outputs.
- Removed per-call schema passing while preserving legacy flat task calls.
- Removed `resume` from task params and schema, requiring agent and assignment inputs.
- Dropped resume continuation paths in task execution and call rendering, always spawning a new agent.
- Removed the `irc.enabled` setting and computed IRC availability by task-depth rules.
- Updated task follow-up guidance to use IRC messaging/history links instead of `task(resume:)`.
The task tool now takes a single { agent, assignment, description, ... } and always runs the subagent in the background — the batch tasks[] array and shared context parameter are gone. Fan-out is parallel task calls; shared background flows through a '/Users/can/.omp/agent/sessions/-Projects-.tree-pi-commit/2026-06-10T15-36-32-782Z_019eb22d-970e-7000-8964-72c98becf3e8/local' file referenced in each assignment.\n\nIntroduces a persistent subagent lifecycle: finished subagents stay live as idle, the lifecycle manager parks them to disk after task.agentIdleTtlMs (default 7 minutes; 0 keeps them live until exit), and they revive automatically when prompted from the Agent Hub, messaged on IRC, or resumed via task. New task(resume: "<id>") revives an idle or parked subagent and runs a follow-up assignment in its existing session.\n\nAdds soft request budgets (explore/quick_task 40, others 90, configurable via task.softRequestBudget, 0 disables): crossing the budget injects a one-time wrap-up steer into the child; crossing 1.5× aborts the run gracefully. Cancelled/aborted subagent salvage replaces the old (no output) with the child's last activity snippet plus request/token stats; SingleResult tracks a per-child requests counter (assistant message_end events) used to sort agent lists in runtime-ascending order in both the live progress view (finished agents above pending/running) and the finalized result view, so rows no longer reshuffle on finalize. Adds a task gallery fixture variant for the resume path (renderer key separated from fixture key).\n\nAll task tests are reshaped around the single-call contract; tests for the discarded shared-context flow are removed, and new task-guards/task-resume/task-schema tests pin the new contract surface.
Tracks successful file reads per resolved base path (selector stripped) for the session; once a path has been whole-file-read three times, every subsequent read for that path appends a one-line nudge suggesting narrower line-range re-reads or the context echoed in edit results. Non-file sources (URLs, internal resources, directories, archives, SQLite, images) are never counted.
Adds enforceInlineByteCap() in streaming-output and applies it to bash and browser tool results: oversized outputs are elided head/tail with an artifact:// footer pointing at the full capture, closing paths that previously let 100KB+ inline results past the minimizer. Defense at the tool-result boundary (no-op for already-bounded output).
The session-observer overlay is gone. The Agent Hub (ctrl+s, alt+a, or double-tap left arrow on an empty editor) presents one overlay with two views: a live registry table (status, unread irc count, current task, last activity; j/k to navigate, r to revive, x to abort/release) and per-agent chat (transcript + input line) — submitting revives a parked agent and steers it via the normal prompt path. Main is the ambient chat and stays out of the table.\n\nrenderInitialMessages no longer takes a prebuilt context: every redraw now reaches for AgentSession.buildTranscriptSessionContext() (full-history transcript with each compaction emitted inline at the point it fired, snapcompact frames re-attached on rebuild). UiHelpers drops the deferred-compaction render and the IRC autoreply branch that the new mailbox bus deprecated. CompactionSummaryMessage renders as a slim divider (── 📷 compacted · ctrl+o ──), expanding to the summary + snapcompact frame count. session-manager.buildSessionContext gains a { transcript: true } mode; an exported buildSessionContextFromFile() reads a session file without taking the writer lock so the hub chat view can tail any agent (parked or live). Theme picks up icon.camera + tool.irc symbol entries.
Replaces the blocking auto-reply IRC turn with a process-global IrcBus and a four-op tool (send/wait/inbox/list). send is fire-and-forget with per-recipient delivery receipts (injected/woken/revived/failed); replies become real turns by the recipient, observed via wait (or the send await:true sugar). Bounded per-agent mailboxes (cap 100) drop oldest on overflow; AgentSession.deliverIrcMessage folds an in-flight delivery in as a non-interrupting aside at the next step boundary, or starts a real wake turn when idle. AgentRegistry/lifecycle handle the idle→woken / parked→revived transitions, so messaging a non-running peer brings it back. Adds a dedicated TUI renderer (directional headers, delivery-outcome coloring, quoted bodies, per-recipient receipt trees, status-badged peer lists with unread counts). irc.timeoutMs is now the default timeout for wait / send await:true. AgentSession sheds the agentRegistry config field, the dedupeIrcReply → dedupeEphemeralReply rename (now used by /btw and /omfg), and the background-channel exchange queue / forwardIrcRelayToMain plumbing the auto-reply model needed.
Removes isBackgroundJobSupportEnabled and JobTool.createIf; the tool is now registered unconditionally via `new JobTool(s)`. `async.enabled` now gates async bash commands only — the task tool runs asynchronously regardless. Deletes the async/support module and its barrel re-export.
Follow-up to the loop guard + artifact cap that addressed the root cause
of issue #2081's runaway captures. The reporter then noted Ctrl+X/Ctrl+C
remaining unresponsive — confirming the secondary symptom: per-keystroke
TUI repaints walked every visible bash row and re-ran `split` /
`replaceTabs` / `truncateToVisualLines` over the stored output. With a
1,000+ message transcript and a 50KB-tail per row, that string work was
what pinned the main thread, not the loop itself.
The eval renderer already caches its computed lines keyed by `(width,
previewLines)` — see `eval-render.ts:709-752`. Mirrored that pattern in
the bash result renderer with a slightly wider key (`width`,
`previewLines`, `expanded`, `rawOutput`, `isPartial`) so the cache is
busted whenever any input that affects the produced lines actually
changes. `invalidate()` continues to clear `CachedOutputBlock` and now
also clears the lines cache, so callers that already drive invalidation
keep working unchanged.
A render() with cache-equivalent inputs is now an array-reference
return; the `CachedOutputBlock` round trip is skipped entirely. New
test in `test/tools/bash-sixel-render.test.ts` pins the contract:
identical inputs → same array reference; width change → cache miss;
invalidate() → fresh array.
Refs #2081
- Separated non-contiguous diff regions with a single blank gap row, normalized after block-context insertion.
- Rendered gap rows as one dim ellipsis in the TUI and HTML export.
- Applied the same blank-separator dedupe and edge-trimming to hashline's compact diff preview.
- Normalized untrusted `questions` arguments by parsing double-encoded JSON strings and skipping invalid question entries before rendering.
- Added option normalization that dropped malformed option items while preserving valid entries in multi-choice rendering.
- Expanded ask tool renderer tests to verify malformed or unparsable questions no longer crash and now fall back safely.
- Centralized catalog and registry handling on `ModelSpec` and `buildModel`, resolving compatibility at model build time.
- Removed runtime compatibility detectors and switched provider request flows to direct `model.compat` reads.
- Added compat fields (`supportsReasoningParams`, `alwaysSendMaxTokens`, `strictResponsesPairing`, `whenThinking`).
- Persisted explicit compatibility overrides through `compatConfig` in discovery and cache merge paths.
Move bundled models, model cache/manager, thinking metadata, effort helpers,
provider descriptors/discovery, wire constants, and model identity utilities
into the new @oh-my-pi/pi-catalog package.
Update pi-ai to keep provider runtime/auth concerns, move catalog provider
metadata into CATALOG_PROVIDERS, and migrate coding-agent, agent, stats, docs,
and tests to import catalog values from pi-catalog.
Split coding-agent model registry helpers into discovery, roles, and models
config modules while preserving registry orchestration.
BREAKING CHANGE: @oh-my-pi/pi-ai no longer exports catalog subpaths such as
/models, /model-cache, /model-manager, /model-thinking, /effort,
/provider-models*, discovery helpers, and provider wire constants; use the
matching @oh-my-pi/pi-catalog subpaths instead.
Updated the read tool's provider-visible path schema, prompt docs, CLI help, and internal URL docs so URL and internal URI targets are advertised consistently. Added schema coverage for the read path description.\n\nFixes #2215
- Fixed help rendering so `--help` no longer triggers unrelated command loaders.
- Fixed startup span logging to emit markers only with PI_DEBUG_STARTUP set.
- Fixed logger startup trace behavior for `:start`, `:done`, and `:fail` phases.
- Fixed prompt template processing with cached raw-template compilation and safer formatting.
- Optimized symbol and tag parsing in prompt templates via manual parsers.
pr_push invalidates PR+diff rows; current-branch merge/close invalidates without a positional; run_watch polls adaptively, survives rate limits, gives up on zero runs, and evicts completed-run job caches when a rerun is observed; multi-PR checkout uses allSettled; pagination compares raw page length; date qualifiers drop ms precision; leading-dash identifiers cannot become flags; auth key memoized against hosts.yml mtime; diff stored once per row.
clients publish only after initialize; dead readers tear down for respawn instead of permanent 30s timeouts; framing resyncs past junk headers; numeric code-action selectors pick strictly by index; file URIs percent-encode and raw fragment/query chars route to the lax parser; equal-position inserts keep spec order; workspace edits validate before writing; shutdown covers mid-init clients; reload sends notification; writethrough init deadline-bounded with negative caching; DAP pause/breakpoint races fixed, mutations serialized and abort-aware, output buffering O(n) with correct tail retention.
vault writes now rated write-tier and plan-mode enforced; .tar.gz rewrites keep gzip, are atomic, and write through symlinks; CRLF conflict detection works; conflict twins only invalidated when truly stale; ask discloses timeout auto-selection in result and transcript; todo rejects duplicate ids and stops persisting half-applied batches; auto-generated guard validates against mtime+size; ACP writes run post-write bookkeeping; irc errors set isError.
artifact spill now includes the head-retained bytes (full capture was missing first ~20KB); chunk throttle coalesces instead of dropping; cd-prefix extraction defers shell-expanded paths; interceptor rule is quote-aware and catches clobber and variable targets; completed async jobs release their Shell; at job cap commands degrade to foreground; PTY mode drops the non-interactive env and notes silent downgrades; timeout/abort annotations always appended; removed dead idle-timeout-watchdog.
tar/tgz stat-gated at 256MB, zip entries reject oversized declared sizes; raw ?q= sqlite capped at 1000 rows; giant-file reads stop scanning to EOF; multi-range reads slice one pass; malformed URL selectors error instead of dumping; archive-root selectors, member tag immutability, case-insensitive selector tokens, session-pinned artifact lookups, shared+escaped suffix globs; archive dir listings honor offsets; binary files get a NUL-sniff notice.
abort signal + 30s timeout threaded into native grep; per-file cap stops one hot file starving the result set; footer hedges totals when capped; skip-past-end says no-more-results instead of no-matches; oversized-file skips surfaced; virtual-resource context lines deduped; patterns no longer trimmed.
- Tracked cumulative plus/minus line changes to translate new-file context rows to old indices.
- Merged old and translated new block-boundary context rows in one pass to prevent duplicate/out-of-order rows.
- Added Unreleased changelog documentation for the new read-only `todo.view` behavior.
- Documented revised bash tool guidance distinguishing safe computation pipelines from byte-trimming commands.
- Documented the cached-model selector fix for dropping the first Enter during refresh.
- Added `TodoTool` tests for `view` on populated and empty lists without mutating session state.
The inline writethrough budget is 500ms (INLINE_DIAGNOSTICS_WAIT_TIMEOUT_MS);
the test published the deferred diagnostics at 900ms and asserted the inline
call returned in <800ms, leaving only ~300ms of headroom over the budget. CI
jitter pushed elapsed to 844ms (still correct deferral, just slow), failing the
over-tight bound. Publish at 2000ms and assert <1500ms so the deferral margin
is wide while still proving inline does not block on the slow publish.
Completes the injectable-fetch transport wiring (15.10.8) that the feature
left half-done, fixing the deterministic CI test failures:
- compaction.compact() rebuilt summaryOptions field-by-field but dropped
`fetch`, so the injected transport never reached
requestOpenAiRemoteCompaction / generateSummary's remote path. Thread it.
- Read-tool URL pipeline had no fetch seam: renderHtmlToText gained a
fetchOverride param but renderUrl/ToolSession never carried one, so the
jina/parallel reader backends always used global fetch. Add
ToolSession.fetch -> renderUrl -> renderHtmlToText (defaults to global).
- searchWithParallel mirrored extractWithParallel but missed the fetch
option; add it.
- Repair tests whose deleted hookFetch interceptors were never replaced
with a FetchImpl seam (fetch-kagi-toggle, web-search-parallel,
issue-970 discovery).
- Update issue-1746 POSIX case to the #2154 preserved-scrollback contract:
unknown-viewport streaming deferral is now platform-independent.
- Added optional FetchImpl fields to compaction, proxy, AI, coding-agent, and mnemopi options.
- Threaded injected fetch implementations through OAuth, discovery, and search/LLM request flows.
- Removed exported hookFetch utility and its package entrypoint from utils.
- Replaced global-fetch test monkeypatching with per-test FetchImpl mocks across test suites.
- Added a `bash.enabled` boolean setting with a default of `true` in the settings schema.
- Updated tool generation to include the `bash` model tool only when `bash.enabled` is enabled.
- Extended createTools tests to assert `bash` is omitted when disabled and omitted from requested disabled tool lists.