Restored the historical clampThinkingLevel export through the legacy pi-ai shim and exposed ModelRegistry.getApiKeyAndHeaders for extension request authentication.
Added compatibility and auth result regression coverage.
Fixes#6648
Advisor provider-identity refresh was only invoked from resetSessionState(), so transitions that update the primary identity without re-priming the advisor (branch with skipConversationRestore, fork) left the advisor emitting the previous session id/metadata/telemetry. Move the refresh into #syncAgentSessionId() via a new SessionAdvisors.refreshProviderIdentity() so it fires on every provider-session change regardless of conversation restore. Add a fork regression test.
The advisor overflow-compaction one-shot calls compact() directly, bypassing the advisor Agent and its metadata resolver, so it omitted metadata.user_id. Resolve the advisor's session metadata per candidate provider (after credential selection) and pass it into the compact() options alongside sessionId/promptCacheKey. Add a regression test asserting the direct compaction request carries the advisor session id.
Rebind the live advisor Agent's provider session id, prompt cache key, credential resolver, metadata resolver, and telemetry identity whenever AgentSession crosses a conversation boundary. Add a regression test proving /new preserves the advisor Agent while assigning and emitting a distinct new provider session UUID.
The advisor Agent is constructed separately in session-advisors.ts with
its own advisorProviderSessionId, but unlike AgentSession it never had a
metadata resolver installed. Its outbound requests therefore omitted the
metadata.user_id session identity that main and subagent requests carry,
so custom Anthropic-compatible proxies saw advisor traffic with no stable
session id to route or attribute on.
Extract buildSessionMetadata into session/session-metadata.ts and install
it as the advisor agent's metadata resolver, scoped to the advisor's own
provider session id and resolved live so token refreshes surface the
current account_uuid.
Fixes#6625
- Added `listDisabledCredentials` and `refreshSnapshot` methods to credential stores along with API endpoints and wire schemas.
- Added `authorizedAt` timestamps and Anthropic OAuth grant TTL constants to track credential lifecycles.
- Updated the usage CLI to render auto-disabled credential tombstones and grant expiration warnings.
- Added comprehensive unit and broker integration tests covering the new credential management features.
The pi-native-only filter still let any non-pi-native models.yml provider override (baseUrl/apiKey/headers) reach the broker-backed gateway: config keys shadowed broker credentials (bypassing account pooling, refresh, and accounting) and a custom baseUrl redirected the broker bearer to a configured endpoint. Generalized the flag to ignoreLocalModelConfig, which short-circuits loadCustomModels so the gateway serves bundled plus broker-discovered catalog metadata only, applying no local overrides, config API keys, custom models, or custom discovery.
Fixes#6615
Gateway catalog construction now ignores models.yml provider entries whose transport is pi-native. This keeps the registry's bundled, cached, and upstream-discovered models while preventing client-side gateway base URLs and bearer keys from being applied to server-side dispatch.
Added a regression test proving a normal client registry retains pi-native routing while the gateway registry restores the bundled provider route before indexing the model.
Fixes#6615
runServe built /v1/models and the resolveModel behind /v1/chat/completions from getBundledModels only, freezing the gateway catalog on the compiled-in snapshot. Every discovery-only model omp itself reaches (ids released after the build date) was unroutable through the gateway while the same broker credential answered it in the TUI.
Source the catalog from ModelRegistry (bundled + cached + discovered) like every other omp surface, via a shared indexModelsByRequestId helper that preserves the credential scoping and qualified/bare-id registration. Rebuild it every 15m so a long-lived serve tracks newly discovered models without a restart; a failed refresh keeps serving the previous catalog.
Fixes#6615
Disable reset-mode loops when vibe mode prevents the required session transition, so the prompt is not resubmitted into the unchanged session.
Added focused regression coverage for the blocked transition.
Fixes#6607
Blocked interactive session transitions before they reach the AgentSession vibe guard, preserving the active session and rendering the existing exit-vibe warning instead of rejecting the input callback.
Added regression coverage for new, drop, fork, and move transitions.
Fixes#6607
The whole-text-as-path fallback added in the previous commit claimed any
single-line payload starting with an absolute-path anchor, including one
holding several paths. Dragging two files at once emits
`/tmp/a.png /tmp/b shot.png`, which the segment splitter also refuses
because `shot.png` is not explicit, so the fallback fused the pair into
one unresolvable path. `handleImagePathPaste`'s ENOENT branch only
surfaces a status and — unlike its too-large and generic-error branches
— never re-pastes the text, so both paths vanished.
On `main` the bracketed route returned undefined for that payload and
fell through to a text paste, so this was a regression introduced by the
previous commit rather than behavior inherited from the clipboard route.
It is reachable by the same gesture that motivated #6578, with one more
file selected: macOS screenshot names always contain spaces.
`extractWholeTextImagePath` now rejects payloads carrying a second
absolute-path anchor after unescaped whitespace. The anchor alternation
moves into a shared `ABSOLUTE_PATH_PREFIX_SOURCE` so the leading-anchor
and second-anchor tests cannot drift apart across the POSIX, `~/`,
`file://`, UNC and Windows-drive families. Escaped whitespace is exempt,
since the escape is the terminal asserting the space belongs to the path.
Guarding the shared helper rather than the bracketed caller also settles
`extractImagePathFromText`, whose JSDoc already claimed multi-path text
falls through to a text paste while the fallback leaked around it.
Ambiguous input — a directory whose name ends in a space, as in
`/tmp/odd dir /sub/x.png` — is treated as multi-path and pastes as text:
a text paste loses nothing, a bogus attach loses everything.
11 tests added covering each anchor family, tab separation, the
escaped-space exemption and the unchanged splitter-success path.
Legacy pi's @earendil-works/pi-coding-agent re-exported estimateTokens
from its coding-agent package root. In omp it lives in
@oh-my-pi/pi-agent-core/compaction and the coding-agent barrel does not
forward it, so the shim's `export * from "../index"` left it off the
surface. A named import tripped Bun's static export check during plugin
validation (e.g. `omp plugin install pi-blackhole`).
Re-export estimateTokens from the shim and pin it with a regression test.
Fixes#6583
Tracked the effective global, project, overlay, or runtime source of shellPath so resolution errors identify the configuration users must edit.
Covered custom agent directories and higher-precedence settings layers with focused regressions.
Fixes#6579
The bracketed-paste (drag-drop) image route required every whitespace-split
segment to look path-like, so a raw macOS screenshot path (spaces unescaped,
per the attachment convention terminals implement) degraded to literal text.
Extract the keybind route's whole-text-as-path pass into a shared helper and
apply it when the segment splitter fails; route the bracketed extractor
through the stripped-marker one so both share identical detection.
Fixes#6578
The scan-membership test used a raw string includes, so a resumed single-slash local:/ state path failed to match the scanner's local:// entry and wrongly gained precedence over a newer draft. Normalize both sides via normalizeLocalScheme before comparing.
Fixes#6569
handlePlanApproval and the ACP rejection path selected a resolved draft that could differ from PlanModeState.planFilePath but never updated the state, so a refine turn was rebuilt by #buildPlanModeMessage() from the stale path. Both paths now promote the reviewed path into plan-mode state.
Fixes#6569
A state plan the artifact scan can't surface (cwd-relative, or a local file not ending in plan.md) has no mtime to compete on, so it now keeps precedence over scanned drafts; an in-scan state plan still competes on newest-first order.
Fixes#6569
Preferred the newest session-local plan artifact over a stale state path when the submitted title cannot reconstruct the draft filename.
Added regression coverage for completed-plan re-entry.
Fixes#6569
While a provider error is pinned in the banner above the editor the inline transcript block is suppressed, so the prior guard skipped re-rendering on Ctrl+O and the full body stayed unreachable until the next turn.
- Track whether the message carries a truncatable error regardless of pinning, so setExpanded re-renders while pinned.
- Render the inline error block in full when expanded even while pinned; keep it suppressed only while pinned and collapsed.
- Disable the streaming fast path whenever the inline error block is drawn.
Fixes#6555
New live, rebuilt, and transcript-builder assistant components now inherit the active tool-output expansion state before provider errors render.
Added regression coverage for an error arriving after expanded mode was already enabled.
Fixes#6555
Turn-ending provider errors rendered inline through
AssistantMessageComponent#appendErrorBlock, capped at 8 lines with no
setExpanded method, so isExpandable filtered the component out of the
Ctrl+O tool-output expansion and the truncated tail was unreachable in
the live TUI (full text was persisted but not shown).
- Add setExpanded to AssistantMessageComponent; when expanded the error
block renders the full body (tabs replaced, blank lines preserved,
Text word-wraps to width).
- Collapsed view appends a dim "+N more lines (Ctrl+O to expand)" hint so
the truncation and its remedy are discoverable.
- Only re-render on toggle when the last render produced a truncatable
error block, so expansion skips ordinary turns.
Fixes#6555
Passed session-scoped settings through Edit and Write generated-file checks and fell back to schema defaults when no global singleton exists.
Guarded inline image sizing against an uninitialized global settings proxy and added isolated-session regression coverage.
Fixes#6549
- Remove uniform language inference requirement, allowing mixed-language paths to rewrite each file in its own language.
- Update `ast_edit_blocking` in `crates/pi-natives/src/ast.rs` to compile rewrite rules per language and skip unsupported languages gracefully.
- Update `ast-edit.md` prompt documentation to reflect mixed-language path support.
- Add test coverage verifying mixed-language tree rewrites.
- Treated transient non-array retain items as absent during TUI streaming.
- Added regression coverage for malformed partial renderer arguments.
- Documented the fix in the coding-agent changelog.
Fixes#6528