- model-resolver: removed three resolveAgentModelPatterns cases that pinned exact
priority.json model names (gpt-5.4 / gemini-3 designer list); priority.json now
leads slow with gpt-5.5 and includes gemini-3.5-flash, so the hardcoded lists
were stale. The remaining cases still cover cross-role alias inheritance and
configured-override precedence.
- settings-selector: removed the condition-hidden group-title assertion that
depended on the pre-autolearn memory-tab group layout.
Hardcoding device index `:0` grabbed whatever avfoundation enumerated first
(often a camera or the wrong input), so recording could capture silence or the
wrong source. Both the single-shot and streaming ffmpeg recorder paths now
request the system default input device.
Treated SearXNG HTTP 200 responses with no usable sources and upstream engine failures as transient provider errors. Added a generic renderable-content guard so provider fallback continues instead of returning an invisible success.\n\nFixes #2571
- Tracked snapshot-safe boundaries to retain commit-stable streamed rows in scrollback.
- Computed durableBoundary from commit and snapshot ends to prevent row drop regressions.
- Updated audit-row handling so drifting durable rows were excluded from resync checks.
- Added regression tests for commit-stable and commit-unstable relayout streaming cases.
- Remove the stale pre-refinement LaTeX entry left behind when the refined
#2550 changelog line was applied over the original.
- Add [Unreleased] entries for the two merged farm fixes that shipped without
one: mnemopi legacy-bank recall isolation (#2412) and slash-command alias
dedup in the prompt-template autocomplete picker.
- agent-loop: raise repetition-detection floor to 180 chars and clear thinking
replay anchors when collapsing a detected loop.
- providers/google: ignore empty text parts, retain terminal thoughtSignatures,
and stop function-call signatures clobbering the prior block.
- autolearn: capture goal-mode at the turn boundary; harden managed-skill writes
against hard-links/symlinks (O_NOFOLLOW + nlink); refuse minting managed skills
whose name an authored skill already claims.
- eager tasks: thread agentKind through the session so a custom top-level agentId
still gets always-mode delegation; split Eager Tasks prompt into hard vs soft.
- title-generator: race the online title model against a local tiny-model fallback.
- eager-todo: keep the soft reminder aligned with the todo init schema.
- mcp/stdio: keep close() detaching the read loop instead of awaiting it.
- stream loop: fix collapsing and tool-call thought-signature handling.
- Added unified `omp setup speech` flow with JSON/check modes and model picker.
- Added local STT pipeline with sherpa workers, recorder/download flow, and streaming inference.
- Added local TTS pipeline with `omp say`, backend selection, and streaming vocalization.
- Replaced legacy speech settings with unified `speech`/`speechgen` configuration keys.
Redirected legacy pi-ai utils/oauth subpaths through the compatibility loader so background workers load the relocated OAuth registry modules.\n\nFixes #2566
The workflow-wide concurrency group was `${{ github.workflow }}-${{ github.ref }}`
with `cancel-in-progress: true`, so the release-script's atomic
`refs/heads/main + v* tag` push shared the `CI-refs/heads/main` group with every
later main push. The newer run cancelled the older release run before
`release_binary` / `release_github` / `release_npm` could execute, and no
future run carried the tag at HEAD, so the tag stayed published-as-a-ref but
unreleased on GitHub and npm (v15.12.6 in the wild).
Release runs are now routed to a per-sha group with `cancel-in-progress: false`
when either:
* the push subject starts with `chore: bump version to ` (the release-script
commit convention from scripts/release.ts), or
* `github.ref` is a `v*` tag (workflow_dispatch recovery from a tag ref).
Other events keep the cheap branch-wide cancel-in-progress for PR/main churn.
release.ts's retry hint now uses the same release commit subject so manual
retries also land in the per-sha group.
Added scripts/ci-concurrency.test.ts: a regression test with a minimal GHA
expression evaluator that asserts the resolved group / cancel-in-progress for
auto-release pushes, retry pushes, tag-ref dispatches, plain main pushes, PRs,
distinct release shas, and a benign `revert: chore: bump version to ...`
follow-up.
Fixes#2564
Removed schema-incompatible task metadata instructions from the eager todo prelude so GPT-5.5 can satisfy the forced initial todo call.
Added regression coverage that keeps the eager init prompt aligned with the todo init schema.
Fixes#2561
mergeDiscoveredModel re-applied baseUrl, headers, and compat from the
provider override when an existing bundled record matched, but dropped
`transport` because the raw /v1/models payload never carries one. With
`transport: pi-native` set for an auth-gateway-routed openrouter
provider in models.yml, the next /model switch after the background
catalog refresh picked the now-transport-less entry and routed through
the default openai-completions client to ${baseUrl}/chat/completions —
a path the auth-gateway never serves, so the gateway returned
"404 No route: POST /chat/completions".
Propagate transport with the same priority as baseUrl: provider
override wins, otherwise preserve the existing (boot-time override-
applied) value, otherwise the discovered value. Cover the regression
both at the merge unit level (xiaomi-tp-discovery-merge) and at the
ModelRegistry refresh level with a mock /models fetch.
Fixes#2555
Unwrap bracketed [path#TAG] headers at the top of WriteTool.execute() so internal-URL detection, plan-mode guard, plan path resolution, and ACP bridge routing all see the same filesystem target. Without this, ['/data/workspaces/can1357__oh-my-pi__2472/.omp-session/2026-06-13T20-19-47-341Z_019ec2a3-fc0d-7000-b1e6-25831d3c3ec5/local/scratch.md' slipped past isInternalUrlPath() and was bridged to the editor instead of staying on disk as a session-local artifact.\n\nFixes #2472
- Derived the short Windows '/data/workspaces/can1357__oh-my-pi__2551/.omp-session/2026-06-14T07-09-37-753Z_019ec4f6-ee59-7000-8226-e1b7ed0680e9/local' root from the stable session id instead of the artifact path, so SessionManager.moveTo() keeps reading the pre-move local files.
- Locked the move stability with a dedicated regression test alongside the long-path coverage.
Refs #2551
Derived HTML default text from the resolved export card/page surface, falling back to the derived user-message export base when themes omit export overrides.
Covered light-status custom themes whose dark userMessageBg drives derived export backgrounds.
Fixes#2516
- Kept queued resolve handlers pending when a downgraded forced tool choice completes without invoking the requested tool.
- Covered the skipped-tool and invoked-tool queue paths in tool-choice queue tests.
Fixes#2546
Reviewer flagged a race left open by the streaming guard added in #2455:
getUserInput() arms onInputCallback and schedules an 800 ms goal
continuation timer; when /goal set takes the streaming branch (or any
extension/hook starts a turn inside that window), the timer fired
unchecked. The downstream onInputCallback resolved the main waiter with
a goal-continuation submission, submitInteractiveInput called
session.promptCustomMessage without a streamingBehavior, and the same
AgentBusyError the PR set out to fix resurfaced.
Make the continuation timer streaming-aware: at fire time, bail out
when session.isStreaming || isCompacting || hasPostPromptWork is true.
Reuses the auto-submit busy check loop mode already relies on (renamed
#isLoopAutoSubmitBlocked -> #isAutoSubmitBlocked since both flows have
the same notion of 'agent is busy, do not submit'). The next agent_end
in #handleGoalSessionEvent reschedules normally.
Fixes#2454
- Added cycle and depth guards for nested task progress rendering so async fan-out snapshots cannot recurse until the TUI crashes.
- Shortened long Windows '/data/workspaces/can1357__oh-my-pi__2551/.omp-session/2026-06-14T07-09-37-753Z_019ec4f6-ee59-7000-8226-e1b7ed0680e9/local' roots into temp-backed session roots before plan/handoff writes hit MAX_PATH.
Fixes#2551
Kept /plan <prompt> from paused plan mode on the prompted entry path while retaining the no-arg third-toggle exit.
Added regression coverage for paused plan mode resuming and submitting the prompt.
Fixes#2510
Without an after-separator state, the new unknown-flag guard rejected
flag-shaped prompts (`omp -p -- --explain-this`): the loop dropped the
`--` token and then re-validated `--explain-this`, recorded it in
`unrecognizedFlags`, and exited 2.
parseArgs now flips a `sawSeparator` latch on `--` and short-circuits
the remaining tokens straight into `messages` — no built-in dispatch,
no extension match, no `@file` expansion. Two regression tests cover
the flag-shaped and `@`-prefixed cases.
Refs #2459
Bun's fetch enforces a hard ~300s pre-response timeout that the caller's AbortSignal cannot lengthen. Every streaming provider's first-event/idle/SDK watchdog was silently capped by it, so cold large-context streams (multi-hundred-K prompts against slow-prefill backends) died at exactly 300s with TimeoutError.
- Added FetchWithRetryOptions.timeout (forwarded to fetch) so fetchWithRetry callers can pass Bun's timeout: false / numeric override directly.
- Passed timeout: false in openai-http, openai-codex-responses, amazon-bedrock, google-gemini-cli, ollama where each provider already constructs the fetch init.
- Added timeout to AnthropicFetchOptions and threaded timeout: false through buildAnthropicClientOptions's fetchOptions; anthropic-client already spreads fetchOptions into every fetch call.
- Allowed compat.streamIdleTimeoutMs: 0 in models.yml so the documented per-model disable knob matches the env-var escape hatch.
Verified with an out-of-tree smoke that stalls a local server 305s before responding: pre-fix the streaming provider died at ~300003ms with the Bun TimeoutError; post-fix the request completes (SUCCESS elapsed=305006ms). The smoke is discarded per directive.
Fixes#2422
The previous `anyBuiltInSkillSourceEnabled` mixed the new
`enableAgentsUser`/`enableAgentsProject` defaults with the named
third-party toggles, so a user who disabled Codex/Claude/Pi to silence
third-party CLI skill sources but kept the default `.agent[s]/skills`
toggles on still saw unknown providers (`opencode`, `github`,
`claude-plugins`, `gemini`) load via the fallback branch. The
`agents` provider already has its own explicit branch in
`isSourceEnabled`, so the fall-through gate now only inspects the named
third-party toggles. Adds a regression test that creates a fake
`~/.config/opencode/skills/leaked-opencode` and verifies it stays
filtered out when the third-party toggles are off and agents toggles
default to on.
Addresses PR #2405 review.