- Replace the compat cast with an annotated CompatOf<Api> local narrowed
via the in operator: assignment up-cast, compiler-checked field type,
no as assertion.
- Cover the 0 sentinel end-to-end through the lazy wrapper with fake
timers (mirrors the direct-Anthropic 0-disable test): advance 400s
past the generic budget, assert no watchdog abort, then cancel
cleanly.
- Add Unreleased changelog entries for pi-ai and pi-catalog with
external attribution for #7892.
Deferred libei and RemoteDesktop setup until the first native input operation. Read-only capability, window, display, and AX calls no longer request keyboard or pointer access.
Used non-persistent portal grants and retained the portal session so backend teardown closes it explicitly.
Fixes#7884
ashpd caches a process-global D-Bus connection whose I/O tasks bind to whichever runtime first creates it. Libei::portal_context() built a short-lived current_thread runtime and dropped it when Libei::new() returned, orphaning that connection; capture() then built its own runtime and reused the dead connection, so PipeWire capture never delivered a frame whenever libei input init ran first.
Route both portal_context() and capture() through a shared long-lived multi-thread runtime held in a LazyLock, keeping the cached connection's I/O alive for the process lifetime.
Fixes#7886
The `cd <path> && ...` extractor matched everything up to the first `&&`
with a greedy regex, so a redirect or extra argument before the `&&` was
swallowed into the structured cwd. `cd /tmp 2>/dev/null && echo ok` became
cwd `/tmp 2>/dev/null`, which failed fs.stat and killed the command before
the shell ran.
Replace the regex with `extractLeadingCdTarget`, a quote/escape-aware
scanner in shell-tokenize.ts that captures exactly one path token and
bails (leaving the command for the shell) when anything else — a redirect,
extra argument, shell expansion, or a non-`&&` separator — precedes the
top-level `&&`.
Fixes#7883
The lazy provider wrapper ignored model.compat.streamIdleTimeoutMs, so
Bedrock reasoning models sat on the generic 300s idle watchdog despite
ConverseStream sending no ping keepalives; long quiet thinking runs died
with "Provider stream stalled while waiting for the next event" during
plan writing and todo execution (issue #4758's Bedrock variant, worst on
Fable 5 where the display default flipped to omitted).
- catalog: BedrockCompat gains streamIdleTimeoutMs; reasoning models get
a 600s floor, adaptive-thinking Claude (Opus 4.7+, Sonnet/Opus 5,
Fable/Mythos 5) 900s to match direct Anthropic's ping-extended
tolerance; explicit compat overrides still win (0 disables).
- ai: forwardStream resolves options -> env -> model.compat -> default,
and lazy terminal errors carry the structural errorId classification
so session auto-retry classifies stalls without text matching.
Added a canonical guide for the Agent Hub roster, transcript, steering, revive, kill, persisted-agent, and advisor workflows.
Linked the guide from task-agent, TUI, keybinding, collaboration, advisor, and website documentation.
Fixes#7880
Address review feedback: the segment previously read the setting from ctx.session.settings, a second source of truth that could disagree with the component's effectiveSettings.sessionAccent and crashed lightweight test fixtures lacking a settings manager. Resolve the value once in #buildSegmentContext from the effective settings and pass it through SegmentContext.sessionAccent so the name segment and the gap-fill divider consume the same value. Add regression assertions for the enabled and disabled branches and a changelog entry.
The session_name segment rendered the session name text with getSessionAccentHex unconditionally, ignoring the statusLine.sessionAccent setting. The adjacent gap-fill divider (component.ts) already gated on sessionAccent !== false, so disabling the setting only removed the accent-colored divider line while the session name itself stayed hash-colored - an inconsistent half-off state.
Read the setting via ctx.session.settings.get (same pattern already used by the goal segment in this file at line 206) and fall back to the theme accent color when disabled, matching the divider behavior.
#purgeSupersededDisabledRows matched disabled rows against active ones by
exact identity-key string equality, but the active-replacement path it
mirrors (matchesReplacementCredential) claims pre-org legacy rows
(`<b>` vs `<b>|org:<o>`). So a later org-scoped login of the same account
never purged the pre-org tombstone, which then rendered forever as a red
row in `omp usage` with no CLI/TUI escape. This is the OAuth half of the
class of bug #2943 fixed for api_key rows in the same function.
Reuse matchesReplacementCredential in the purge so an org-scoped login
claims and hard-deletes its pre-org tombstone, inheriting the one-way
upgrade and shared-workspace guards unchanged.
Fixes#7876
PUT ... @name with no matching capture no longer fails the patch: it
pastes nothing (a span target is still removed, i.e. it degrades to a
cut) and surfaces a warning naming the available registers. Anonymous
empty/ambiguous pastes still error. validateClipboardSequence now only
guards anonymous sequencing; applyEdits threads clipboard warnings into
ApplyResult.warnings.
Shares a saved session by id prefix or .jsonl path without launching the
agent - same encrypted upload, store selection, and share.redactSecrets
handling as the /share slash command.
Moved the obfuscator assembly (secrets.yml + env entries + built-in
credential patterns, placeholder-key minting rules, redaction-only
fallback) from createAgentSessionScoped into the secrets module so other
entrypoints can build the same obfuscator.
- New agent-plugins provider discovers packages with a root plugin.json
targeting the canonical schema (agent-plugins.org) from marketplace
installs, --plugin-dir, and configured extension roots; skills/ and
mcp.json load per spec with closed-schema validation,
${PLUGIN_ROOT}/${PLUGIN_DATA} expansion, reserved subprocess
environment, instance-keyed data dirs, and per-component isolation.
- Package-boundary containment (spec §4.1) is enforced before every
read via the new contained-path helpers, including skill:// resource
access from the read tool and bash; plugin skill files must
realpath-resolve inside the plugin root (skills carry containRoot).
- Legacy claude-plugins/omp-plugins providers yield skills and MCP
surfaces of standard-targeting roots to the new provider and skip
fatally invalid packages.
- Client-generated HTTP/MCP/authorization headers win over configured
headers case-insensitively (Agent Plugins §7.2.1) via the new
header-policy fetch wrapper used by the HTTP and legacy SSE transports.
- headerPolicy: "origin-locked" pins configured headers to the configured
URL's origin: never forwarded across cross-origin redirects, and
method-changing redirects of JSON-RPC POSTs are refused.
- envPolicy: "literal" exempts stdio env values (and origin-locked
headers) from config-value resolution: no ambient env-name lookup, no
__omp_shell("command execution, empty values preserved.")
resolveAnthropicBaseUrl() resolved the chat base URL from github-copilot,
FOUNDRY_BASE_URL, then model.baseUrl -> hardcoded api.anthropic.com, and
never read $env.ANTHROPIC_BASE_URL. The stock anthropic descriptor pins
model.baseUrl to api.anthropic.com, so the env fallback was unreachable:
gateway-scoped keys were sent to api.anthropic.com (401, credential leak)
regardless of ANTHROPIC_BASE_URL, contradicting docs and the web-search
fix in #1693.
- Chat resolver now returns ANTHROPIC_BASE_URL (after Foundry, ahead of the
official default); an explicit non-official model.baseUrl still wins.
- resolveAnthropicCustomHeaders keys off the resolved base URL so
ANTHROPIC_CUSTOM_HEADERS reach env-configured non-official gateways.
- stream.ts leaked-thinking heal exemption mirror updated to the same
effective-endpoint precedence.
Fixes#7874
Configured Quartz event sources to permit local hardware events in both suppression states and replaced Enigo-backed global posting with the configured source.
Added a macOS regression test for the event-source suppression settings.
Fixes#7872
Zhipu Coding Plan returns '429 已达到 5 小时的使用上限。您的限额将在 … 重置。'
(type=1308) when the 5h window is spent. The error classifier only matched
English quota phrasing, so this message classified as UNKNOWN, Flag.UsageLimit
was never set, and multi-key sessions stayed pinned to the exhausted api_key
credential instead of rotating to a sibling key.
Add CN_QUOTA_EXHAUSTED_PATTERN (达到…使用上限, 已达上限, 额度/配额…耗尽/用完,
限额…重置, 余额不足) consulted by parseRateLimitReason before the transient
branches and by matchesUsageLimitText. Treat Simplified Chinese error bodies
as informative in isOpaqueStatusBody so a plain Chinese throttle (已达到速率限制)
does not rotate credentials via the opaque-429 fallback.
The 达到…使用上限 arm requires the 使用 token, so a concurrency or rate cap
phrased as 达到…上限 (without 使用) stays in the upstream-backoff lane instead
of being misclassified as a credential-exhausting quota.
app.tools.expand (Ctrl+O) was wired only through the editor's input path,
so when a tool-approval prompt or other selection dialog took keyboard
focus the key was delivered to that component and never reached the expand
handler — a large truncated edit could not be expanded while the user was
deciding whether to approve or deny it.
Promote the shortcut to a global TUI input listener (matching the existing
debug and branch/copy shortcuts) so it fires regardless of focus. It defers
when the main transcript is not the active surface (a fullscreen/anchored
overlay: agent hub, transcript viewer, log viewer, model picker) or when the
focused component rebinds Ctrl+O for its own use (the tree selector's filter
cycle). The editor-scoped handler is removed as a clean cutover.
Fixes#7837
git.commit() was awaited without a try in both agentic commit routes and the legacy pipeline, so a refusing pre-commit/commit-msg hook escaped as an uncaught GitCommandError. In a bundled build Bun renders that as kilobytes of minified source. The split loop also threw out of its order loop, reporting no progress.
The empty-staged-tree early return fired before args.push was read, so 'omp commit --push' on a clean tree exited 0 without pushing.
Route commit/push failures through a shared execute helper: abortOnGitFailure prints the hook's own message (plus split-plan progress) and throws a CommitAbortedError the command maps to exit 1; pushOrAbort pushes existing commits when the tree is clean and reports refused pushes cleanly.
Fixes#7834
- mnemopi provider parity 'diagnose, validate, graph' does ~6.7s of real
work under bun --parallel=8 on loaded runners; raised its per-test
timeout to 30s (default 5s flaked twice in three CI runs).
- utils LRUCache updateAgeOnGet drove a 30ms TTL with real 20ms sleeps
(10ms margin); now drives performance.now() via a mocked clock, so the
contract is asserted deterministically with no wall-clock wait.
- A blank run at EOF now breaks the list without consuming the blank,
matching real marked: '- item\n\n' lexes as a tight list plus a space
token instead of a loose list whose raw includes the blank.
- Completes the 17.2.10 mid-document fix; same-marker continuation and
indented item content across blanks are unaffected.
- Added list/blank boundary token-shape tests (verified against marked
v15) since the tui incremental tests compare the lexer to itself.
- deepseek-v4-flash bakes the wire-exact [low, high, max] ladder on
every host since 736b496cc6; V4 Pro stays [high, max].
- The stale xhigh alias-filter assertions now expect the flash ladder.
- Reverted the status-line acknowledgment added for deferred panel
commands: showStatus mounts a Spacer+Text into the transcript, and any
mid-turn transcript mount re-renders rows below the growing live block,
duplicating them in native scrollback (issues #4806/#6767).
- The queue still flushes at every settle, terminal or not.
- A blank line before a non-continuing top-level line (including plain
paragraphs) now closes the list without consuming the blank, so it
always lexes as a separate space token like real marked.
- List token shape no longer depends on the follower's block type,
restoring the TUI streaming lexer's freeze invariant (lex(prefix) ++
lex(tail) == lex(full) under append-only growth).
- A list followed by a paragraph is now tight, not loose, per CommonMark.
- Classified model_not_available_for_integrator as a permanent entitlement denial instead of transient fleet skew.
- Preserved the provider response and Available models list while retaining model_not_supported fleet retries.
Fixes#7819