- Add `renderToolExamplesJsdoc` to generate `@example` comment lines for tool inventories.
- Extract `bareStringArg` helper to simplify single-argument checks across renderers.
- Update tool inventory rendering to use the new JSDoc example format.
- Union changelog merges interleaved stale pre-17.2.5 PR-branch entries into
released sections; released bodies are restored byte-for-byte from the
pre-merge main state.
- [Unreleased] now carries exactly the entries for PR #7080 and the nine
merged fixes (#7495, #7460, #7466, #7468, #7473, #7481, #7477, #7368, #7453).
An agent-attributed developer turn (e.g. a plan-approval handoff into a
fresh session) maps to Ollama's `system` role, so a request whose only
non-system message is that turn carried zero `user`-role turns. Ollama
answers such a request with `done_reason: "load"`, generating nothing,
and `mapDoneReason` laundered it into a clean `stopReason: "stop"` with
zero usage — indistinguishable from a legitimate empty completion, so
every recovery layer retried the impossible request until the cap
surfaced a misleading empty-stop error.
- convertMessages now demotes the last non-prefix `system` turn to
`user` when no user turn survives, keeping the static system-prompt
prefix intact for prefix caching.
- mapDoneReason maps `done_reason: "load"` to `error` with an explicit
message so it surfaces immediately instead of being retried.
Fixes#7465
- Added shared Python call and literal serialization utilities with multiline verbatim support.
- Standardized tool inventories to format as an OpenAI-Harmony functions namespace using TypeScript declarations.
- Updated tool normalization and rendering functions to accept options objects and default to Python-syntax examples.
- Refactored Gemini dialect rendering to leverage shared serialization functions directly.
- Normalized result-bearing Codex image items on terminal output events and emitted standard image content.
- Preserved result-bearing image calls during full Responses history replay despite stale provider status.
- Added stream and replay regressions for the Codex path.
Fixes#7445
The OpenAI service tier could only be chosen through the `tier.openai`
setting, or for a resumed session through whatever tier that session
recorded. Wanting flex or priority for a single run meant editing
settings and putting them back afterwards, while `bench` already took a
`--service-tier` flag that the session CLI did not offer.
Add `--service-tier` to the root command. The flag wins over the
configured setting and over a resumed session's recorded tier, leaves
the Anthropic and Google entries untouched, and records the resulting
map so a later resume keeps it. `none` removes the OpenAI entry, which
omits `service_tier` from the request.
Signed-off-by: Christian Stewart <christian@aperture.us>
On Windows process.env/Bun.env lookups are case-insensitive, so the
"env var name, else literal" resolvers turned a literal /login key like
`public` (OpenCode Zen's free key) into the built-in PUBLIC=C:\Users\Public,
sending `Authorization: Bearer C:\Users\Public` and 401ing every request.
Added `$envExact` in pi-utils, which trusts an env lookup only when an
exact-case key is enumerated (the only case-preserving signal on Windows;
the getter and hasOwnProperty/getOwnPropertyDescriptor traps are all
case-insensitive there). Wired it into all three resolvers:
resolve-config-value.ts, model-registry.ts, and auth-storage.ts.
Fixes#7361
- Added `isStreamEnvelopeErrorText` to packages/ai/src/error/flags.ts to recognize stream envelope truncation errors.
- Updated `streamAnthropicOnce` in packages/ai/src/providers/anthropic.ts to throw an envelope error when streams die mid-generation without a terminal frame.
- Updated `recoverTransientErrorToolTurn` in packages/agent/src/agent-loop.ts to recognize Anthropic stream envelope truncation errors for tool call salvage.
Exclude the synthetic trailing Continue. user pad from Anthropic's message cache window and anchor selection on the preceding assistant instead.
This preserves the only remaining message breakpoint when three system breakpoints consume the rest of Anthropic's four-slot budget.
Fixes#7324
Cache up to the last three eligible Anthropic system blocks so the stable-prefix breakpoint remains before the volatile project footer when an active-repo context block follows it.
Prioritize the newest message when the four-breakpoint budget leaves one message slot, and cover the active-repo block order with a regression test.
Fixes#7324
Classify any Anthropic system array beginning with the generated billing header as the Claude Code layout, including the two-block billing-plus-identity case.
Added a request-payload regression test proving both cloak blocks remain uncached while the user message retains its breakpoint.
Fixes#7324
applyPromptCaching placed its only Anthropic system breakpoint on the last block, which is omp's volatile project footer (cwd, date, workspace tree). A new working directory or a midnight rollover therefore re-wrote the entire cached system prefix instead of reusing it.
System caching now also marks the block that ends the stable prefix (the block before the footer) via cacheSystemPrefixBreakpoints, skipping the OAuth cloak blocks (billing header + Claude Code identity). A footer change now only re-writes its own delta.
Does not cover open-weight chat templates that render tool schemas after the system block; that needs relocating the footer out of the system message (maintainer-owned prompt change).
Fixes#7324
Use isOpenCodeHost (provider id + baseUrl markers) instead of the built-in
provider-id check so DeepSeek reasoning models declared under a custom provider
id pointed at the OpenCode gateway URL also downgrade a forced tool_choice to
auto.
Fixes#7315
Limit the DeepSeek forced tool-choice downgrade to the OpenCode Zen and Go
gateways whose default thinking mode rejects named selectors. Preserve forced
tool selection on NVIDIA and other gateways that can disable thinking for the
request.
Add regression coverage for both the affected OpenCode path and an unaffected
NVIDIA DeepSeek route.
Fixes#7315
DeepSeek reasoning models on the OpenCode Zen/Go gateways 400 with
"Thinking mode does not support this tool_choice" when a specific tool is
forced. The compat descriptor already drops reasoning_effort via
disableReasoningOnToolChoice, but that does not turn off the gateway's
default thinking mode, so the forced named selector still trips DeepSeek's
thinking+tool_choice guard.
Mark forced tool_choice unsupported for DeepSeek reasoning models so
buildParams downgrades the selector to "auto" while keeping the tool
advertised, mirroring the Anthropic and direct-DeepSeek paths.
Fixes#7315
- #readPersistedCredentialBlockReconcileAfter no longer rethrows
non-corruption errors; transient failures (e.g. SQLITE_BUSY) log at
debug and fall back to the in-memory probe window, mirroring
#readPersistedCredentialBlock.
- Constructor's best-effort cleanExpiredCredentialBlocks now routes
errors through #handlePersistedBlockStoreError so init-time corruption
latches immediately instead of deferring to the first block read.
- reload() latches and surfaces repair guidance when listAuthCredentials
throws on a corrupt store, then still rethrows: continuing with zero
credentials would silently log the user out of every provider.
- The #7298 test asserted PRAGMA-vs-DDL ordering by spying on
Database.prototype.run SQL strings — implementation plumbing.
- Now a child process creates the db and holds BEGIN EXCLUSIVE for 750ms
(past open()'s ~700ms retry budget, under the 1000ms headless
busy_timeout), signaling readiness via a filesystem sentinel; open()
only survives if the busy handler is installed before the leases DDL.
- Verified the test fails with the ordering fix reverted.
Fail broker-facing block mutations on the first corrupt SQLite write and every later latched call instead of returning a false durability acknowledgement. Internal request-path persistence remains an in-memory no-op, preserving local availability.
Verify RemoteAuthCredentialStore keeps its optimistic rate-limit block and does not refresh an empty snapshot when the broker rejects persistence.
Route credential-block reconcile-after reads through the same corruption latch used by persisted block reads and writes. A latched store now returns an immediate zero probe time without touching SQLite; a first corruption from reconcile-after latches and reports once, while transient errors still propagate.
Add a Codex regression covering a corrupt block write followed by healthy usage reconciliation.
Route the broker-facing list, upsert, and delete methods through the same per-process corruption latch as credential selection. Preserve transient error propagation while returning safe empty/no-op fallbacks for SQLITE_CORRUPT and SQLITE_NOTADB.
Add table-driven coverage proving each public block operation independently reports once and short-circuits every later store call.
AuthStorage caught unrecoverable SQLite errors (SQLITE_CORRUPT family /
SQLITE_NOTADB) from the persisted credential-block read and write paths
at debug level with no latch. A corrupt agent.db therefore re-queried the
broken store on every credential evaluation while persisted rate-limit
blocks silently stopped applying, failing open in the direction that
hammers rate-limited accounts.
Detect the corruption family via isSqliteCorruptionError, report it once
at error level with the store location and repair guidance, and
short-circuit every later persisted-block read/write for the process
lifetime. Availability is preserved through the in-memory backoff map;
only cross-process persistence is lost.
Fixes#7296
SqliteAuthCredentialStore.open() ran #ensureAuthCredentialRefreshLeasesTable
(CREATE TABLE/INDEX for auth_credential_refresh_leases) with Bun's default
busy_timeout=0, before the constructor's #initializeSchema installed the
handler. Under a concurrent write lock (WAL recovery on parallel omp
startups) the lock-taking DDL failed immediately; the error was not
BUSY-classified, so open()'s bounded retry loop was bypassed.
Install the busy handler on the connection right after it opens, before any
lock-taking statement, via a shared #installBusyTimeout helper reused by
#initializeSchema. Honors the issue-#2421 invariant on every entry path.
Fixes#7298