@oh-my-pi/pi-ai
Added
- Added
CheckCredentialsOptions.completionProbe(andcompletionTimeoutMs) soAuthStorage.checkCredentialscan additionally exercise each credential against the provider's chat-completion endpoint after refresh-on-expiry. Result lands onCredentialHealthResult.completion({ok, reason?, modelId?, latencyMs?}) without disturbing the usageokfield. Public types:CompletionProbe,CompletionProbeInput,CompletionProbeCredential,CredentialCompletionResult. The probe is invoked even when noUsageProvideris registered for the row, and is skipped when OAuth refresh fails (the stale bytes would only mask the upstream failure). - Added Wafer Pass and Wafer Serverless providers (
wafer-pass,wafer-serverless). OpenAI-compatible (https://pass.wafer.ai/v1), bearer auth,wfr_…keys./login wafer-passand/login wafer-serverlesspaste-and-validate the key against/v1/models.WAFER_PASS_API_KEYandWAFER_SERVERLESS_API_KEYenvironment variables wired intogetEnvApiKey. Bundled catalog seedswafer-pass/{GLM-5.1, Qwen3.5-397B-A17B}andwafer-serverless/{GLM-5.1, Kimi-K2.6, Qwen3.5-397B-A17B, Qwen3.6-35B-A3B, qwen3.7-max, deepseek-v4-flash, deepseek-v4-pro}; dynamic discovery via/v1/modelsoverlays additional models at runtime. Pass-tier discovery filterswafer.tier === "pass_included". Pass-SKU costs are seeded at0(flat-rate subscription, no per-token charge — matcheskimi-code/firepass/alibaba-coding-plan). Serverless costs are the wafer.ai retail rate, derived from the*_cents_per_millionenvelope viavalue × 125 / 10000(e.g. GLM-5.1120→ $1.50/M, Kimi-K2.688→ $1.10/M). Reasoning entries get a thinking compat picked from thewafer.providerenvelope:zai/moonshotai→ zai-stylethinking: { type },qwen→ top-levelenable_thinking,deepseekand unknown upstreams stay unset sodetectOpenAICompatcan pickreasoning_effortfrom the id pattern at request time.
Changed
- Changed auth-gateway credential resolution to use per-conversation
promptCacheKey/sessionIdwhen callingAuthStorage.getApiKey, so repeated turns can keep the same credential until it becomes unavailable - Changed auth-gateway and pi-native request handling to align
sessionIdwith prompt/context identity before credential lookup - Changed Anthropic prompt preparation to downscale image blocks over 2000px when a request includes 20+ images, reducing oversized payloads automatically
- Changed OpenAI chat request parsing to accept
nameontoolmessages and fall back to the matching assistanttool_callsname, so parsed tool results now carry a proper tool name when the wire omits it - Changed
checkCredentialsto skip runningcompletionProbewhen OAuth refresh fails, so stale bearer tokens are never probed and the refresh failure remains the returnedreason - Changed completion reporting to return
completion: { ok: null, reason: ... }when a credential has no usable bearer bytes instead of attempting the probe - Refactored
AuthStorage.checkCredentialsso OAuth refresh-on-expiry runs up-front and the refreshed credential is shared between the usage probe and the new completion probe; rows without a registeredUsageProviderno longer short-circuit before the completion probe runs.
Fixed
- Fixed DeepSeek DSML tool-call envelope leaks on Ollama Cloud and OpenAI-compatible streams by healing leaked envelopes into structured tool calls without displaying raw DSML markers. (#1462)
- Fixed auth-gateway to classify usage-limit messages such as
usage_limit_reached,resource_exhausted, and Codex-styleTry again in ~X mintext as 429rate_limit_errorresponses - Fixed auth-gateway usage-limit handling to honor parsed retry hints and switch to a sibling credential via
markUsageLimitReachedinstead of invalidating the rate-limited credential - Fixed
streamSimpleto retry on usage-limit errors (including message-only error events) before any content is emitted, soonAuthErrorcan rotate credentials automatically - Fixed auth-gateway error classification to extract embedded status codes and use word-boundary matching, so
GenerateContentRequestand similar messages are no longer misreported as rate-limit errors - Fixed
checkCredentialsto handlecompletionProbeexceptions by recording the failure inCredentialHealthResult.completion.reasonwhile still returning the usage probe result
Fixed
- Fixed Google Vertex's bundled model list to use the authoritative models.dev catalog, including MaaS entries such as
deepseek-ai/deepseek-v3.2-maasand removing retired Gemini 1.5 fallbacks. (#1456)
@oh-my-pi/pi-coding-agent
Breaking Changes
- Changed hashline edit parsing to require wrapped hunk headers such as
@@ A..B @@(including@@ BOF @@and@@ EOF @@), with empty@@ A..B @@blocks deleting the anchored range and legacy inline payload forms treated as malformed
Added
-
Added
vault.enabledsetting (Tools → Obsidian Vault, defaultfalse) gating thevault://internal URL. When disabled,VaultProtocolHandler.resolve/write,resolveVaultUrlToPath, andhasObsidian()all refuse — the latter hides thevault://entry from the system prompt's Handlebars{{#if hasObsidian}}block. Tests can opt in viavi.spyOn(vaultProtocol, "isVaultEnabled").mockReturnValue(true). -
Added support for
vault://URLs in path resolution utilities, including plan mode and internal selector parsing soreadand edit paths can target Obsidian vault files directly -
Added
vault://internal URLs for editable Obsidian vault files, with filesystem-backed read/write/listing and CLI-backed vault index operations. -
Added strict-mode indicators to
omp auth-gateway checkoutput by appending[strict]to strict-mode text headers and adding a top-levelstrictfield in--jsonoutput -
omp auth-gateway check --strictexercises each broker-supplied credential against its provider's chat-completion endpoint (cheapest bundled chat model per provider, with 15s/attempt timeout and up to 4 catalog fall-throughs on "model not found / invalid model" errors). Surfaces failures where the usage endpoint reports 200 but the chat endpoint 401s the same bearer (revoked OAuth scope, mislabeled provider row, …). Output gains a[chat: ok|FAIL|skip]column in text mode and acompletionfield on each credential in--jsonmode; the chat-failed count contributes to the non-zero exit code.
Changed
- Changed hashline apply behavior to preserve duplicated boundary and context lines in replacement and insert payloads instead of auto-absorbing or dropping them
- Updated hashline syntax: replaced
↑/↓payload sigils with^repeat syntax and|literal rows for clearer edit semantics - Changed hashline delete syntax from bare
A:orA-B:to explicitA-B:-inline delete marker - Modified hashline anchor syntax to require explicit range notation
A-B:instead of shorthandA:for single-line operations - Updated hashline description in settings to clarify pure insert context behavior without arrow notation
Removed
- Removed the
edit.hashlineAutoDropPureInsertDuplicatessetting - Removed the
edit.hashlineAutoDropPureInsertDuplicatessetting from configuration and execution paths - Removed the
edit.hashlineAutoDropPureInsertDuplicatessetting - Removed the
edit.hashlineAutoDropPureInsertDuplicatessetting from configuration and execution paths
Fixed
- Fixed agent yielding silently on
response.incomplete(OpenAI Responses / CodexstopReason: "length"). The agent now treats output-side incompletion as a recovery case: drops the truncated/reasoning-only assistant turn, attempts context promotion to a larger model, and falls back to compaction or handoff.AutoCompactionStartEvent.reasonand the custom-toolauto_compaction_start.triggerdiscriminator gain an"incomplete"value. The handoff strategy is honored for"incomplete"(unlike"overflow", where the input is broken and handoff would hit the same wall). - Fixed
evaltool to resize large displayed images and append dimension notes to text output - Fixed
writetool to strip malformed or loose hashline section headers before writing file content - Fixed
evaltool image rendering to resize displayed images before returning them and append image-dimension notes to text output - Fixed
writetool output sanitation to strip malformed or loose hashline section headers before writing file content - Fixed
omp auth-broker servecrashing at startup withlogger.setTransports is not a function— switched the call site toimport { setTransports } from "@oh-my-pi/pi-utils/logger", bypassing theloggernamespace re-export that some Bun versions failed to expose at runtime - Fixed
omp auth-gatewayreturning502 upstream_errorand refusing to rotate credentials when a provider responded with a non-401 usage-limit error (Codexusage_limit_reached, Anthropicusage_limit_reached, Googleresource_exhausted).classifyGatewayErrornow reusespi-ai's centralisUsageLimitErrorheuristic and reports those failures as429 rate_limit_error.streamSimple's pre-emit retry hook fires on usage-limit phrasing in addition to HTTP 401; the gateway's refresh callback branches on the error type and callsAuthStorage.markUsageLimitReached(provider, sessionId, { retryAfterMs })— temporarily blocking just the exhausted credential and surfacing the next sibling — instead ofinvalidateCredentialMatching, which would have suspect/deleted the row. The same branching is wired into the coding-agentstreamFncallback so subscription multi-account rotation works the same on both surfaces. - Fixed
extractRetryHintnot recognising Codex'sTry again in ~N min./… hour/… hoursphrasing, which left the gateway and TUI without a server-suggested retry window when an upstream account hit its usage cap. The sharedtry again inpattern now acceptsmin,minutes,mins,h,hr,hour,hoursunits in addition toms/s/sec, and tolerates a leading~and embedded whitespace. - Fixed the auth-gateway threading
sessionId: undefinedintoAuthStorage.getApiKey, which left#sessionLastCredentialempty and mademarkUsageLimitReacheda no-op for gateway-mediated requests. Both/v1/chat/completions-style endpoints and the/v1/pi/streamfast path now derive a stablesessionIdfrom the client'sprompt_cache_key(or the existing model+system+tools+first-message hash when absent) and reuse the same identity for credential-stickiness and prefix-cache routing. - Fixed
evaltool to resize large displayed images and append dimension notes to text output - Fixed
writetool to strip malformed or loose hashline section headers before writing file content - Fixed
evaltool image rendering to resize displayed images before returning them and append image-dimension notes to text output - Fixed
writetool output sanitation to strip malformed or loose hashline section headers before writing file content - Fixed
omp auth-broker servecrashing at startup withlogger.setTransports is not a function— switched the call site toimport { setTransports } from "@oh-my-pi/pi-utils/logger", bypassing theloggernamespace re-export that some Bun versions failed to expose at runtime - Fixed user shortcut Python execution to namespace session IDs like eval, so both paths share one kernel
Security
- Secured
vault://reads and writes by validating URL paths and blocking traversal, absolute paths, and symlink escapes outside the selected vault root
@oh-my-pi/hashline
Breaking Changes
- Removed the single-number hunk header shorthand. A hunk header now REQUIRES two line numbers (
A Afor a single line,A Bfor a range); a bareArow throwssingle-number hunk header "A" is no longer accepted. The&Abody-row shorthand for&A..Ais unchanged. - Changed hunk header syntax from
A-B:to@@ A..B @@with@@ A @@shorthand for single lines - Changed repeat payload sigil from
^A-Bto&A..Bwith&Ashorthand for single lines - Changed range separator from
-to..in all contexts (anchors and repeats) - Changed empty hunk behavior: concrete ranges now delete (no blank-line insertion); BOF/EOF empty hunks are now no-ops
- Removed
ApplyOptionsparameter fromapplyEdits()and related APIs; auto-absorb behavior is no longer configurable - Removed diagnostic warnings for auto-absorbed duplicates from
ApplyResult; warnings now come only from parser, patcher, or recovery - Removed legacy hashline block syntax
A-B:,A-B:-, and^A-Band replaced edits with@@ A..B @@hunks using+and&body rows - Removed
A:shorthand syntax; use explicitA-A:for single-line anchors - Removed
↑and↓payload sigils; use|TEXTfor literal rows and^A-Bfor repeating original lines - Removed standalone delete rows; use inline
A-B:-syntax instead - Removed
after_anchorcursor kind; all inserts now usebefore_anchorpositioning - Replaced insert-above/insert-below payload sigils with linear body rows:
|TEXTemits literal text and^A-Brepeats original file lines inline. - Replaced standalone delete rows with inline range deletes: use
A-B:-. - Changed empty
A-B:,BOF:, andEOF:blocks to write one blank line instead of being rejected.
Added
- Added compatibility parsing for apply_patch-style and unified-diff row noise by stripping path noise and converting context/delete body rows into hashline-compatible operations with warnings
- Added
A-B:-inline delete syntax for concrete range anchors - Added
^A-Brepeat payload syntax to emit original file lines inline - Added support for empty anchor blocks to write one blank line at the anchor position
Changed
- Changed unified-diff compatibility mode to silently drop
-oldrows and convert context rows to+TEXTliterals with a warning instead of rejecting them - Changed
ABORT_MARKERbehavior to terminate parsing without surfacing a warning - Changed numeric ranges to
A..Bform and accepted@@ A @@as shorthand for@@ A..A @@ - Changed empty hunk behavior so a concrete empty hunk deletes the selected range and
BOF/EOFempty hunks no longer insert a blank line - Changed parse behavior for
*** Abortto stop processing without returning a speculative truncation warning - Changed payload row format from three sigils (
|,↑,↓) to two (|,^) - Changed range anchor syntax to require explicit
A-Bform (no single-line shorthand) - Changed error messages to reference new syntax and remove references to removed sigils
What's Changed
- fix(ai): update google vertex model catalog by @roboomp in #1457
- fix(coding-agent): share Python kernel for
$by @shyndman in #1466 - feat(ai): add Wafer Pass and Wafer Serverless providers by @oldschoola in #1468
- docs: fix stale links and add missing CHANGELOGs by @duiguangbin810 in #1470
- fix(codex): prefer gpt-5.5 for web search by @daandden in #1471
- fix(mcp): bound optional HTTP SSE startup by @roboomp in #1461
- fix: add EventLoopKeepalive to Agent.prompt() for session.prompt() busy-wait by @hezhiyang2000 in #1464
New Contributors
- @duiguangbin810 made their first contribution in #1470
Full Changelog: v15.5.7...v15.5.8