- Added a setup-system-deps action with preloaded-runner guards and apt fallbacks.
- Updated CI workflows to download Linux x64 native artifacts and gate on native job success.
- Renamed coding-agent fast mode to singleton in scripts and test partitioning logic.
- Added settings test-state begin/restore helpers with recursive cleanup in affected tests.
- 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.
GitHub Copilot's Anthropic-compatible proxy (api.githubcopilot.com/v1/messages) validates tool definitions with additionalProperties: false and rejected every tool-bearing turn with `400 tools.0.custom.eager_input_streaming: Extra inputs are not permitted`. The catalog's Anthropic compat builder defaulted supportsEagerToolInputStreaming to true for all hosts, so convertTools added the flag to every tool sent to Copilot.
Gate the flag on host in buildAnthropicCompat (false for github-copilot, matching the existing supportsLongCacheRetention: official pattern), and stop pushing the legacy fine-grained-tool-streaming-2025-05-14 beta header on the Copilot transport — the proxy doesn't whitelist Anthropic beta features either, so the fallback path would 400 too.
Fixes#2558
Direct callers of ./providers/{amazon-bedrock,google-gemini-cli,ollama,openai-codex-responses} bypass register-builtins' iterator-level first-event watchdog. With Bun's native ~300s fetch ceiling disabled in #2422, a stalled upstream that accepts the POST and never sends headers would hang forever.
Combine the caller's signal with AbortSignal.timeout(firstEventTimeoutMs) at each fetchWithRetry site so pre-response stalls fail within the configured budget. Bedrock/Gemini use getStreamFirstEventTimeoutMs (Gemini floors at 5min to match its register-builtins limits); Ollama uses getOpenAIStreamFirstEventTimeoutMs floored by getOpenAIStreamIdleTimeoutMs; Codex SSE threads firstEventTimeoutMs through CodexRequestSetup -> openCodexSseTransport -> openCodexSseEventStream.
Verified with an out-of-tree smoke that aborts a stalling Bedrock fetch via streamFirstEventTimeoutMs: 2_000ms — elapsed 2005ms, stopReason error, errorMessage 'Request was aborted'. Smoke discarded per directive.
Refs #2422
Cumulative reasoning_content dedup keyed on currentBlock.thinking failed
once </think> and visible text arrived: currentBlock flipped to text, the
guard missed, and the next chunk's cumulative snapshot was re-emitted as
a fresh thinking block after the answer had started.
Replaced the block-scoped check with lastCumulativeReasoningBySignature,
a closure-scoped Map keyed by signature (the reasoning field name) that
holds the last full snapshot regardless of currentBlock state. Cumulative
chunks now slice against this tracker, so post-answer snapshots resolve
to an empty delta and never reinstate a phantom thinking block.
Added a regression test covering the post-text cumulative case.
Fixes#2433
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
GitLab Duo OAuth callback handling now rejects HTTPS loopback redirect URIs before browser auth because the embedded callback server only serves plaintext HTTP.
Fixes#2424
Added OpenAI-compatible compat metadata for endpoints that allow tools but reject forced tool_choice. OpenCode Go kimi-k2.7-code now downgrades resolve-gate forcing to auto tool selection while preserving thinking-mode request state.\n\nFixes #2546
- Validated queued toolChoice against active tools in agent and coding-agent sessions.
- Rejected queued forced choices with reason "unavailable" when selected tools were inactive.
- Dropped provider toolChoice payloads when requested function tools were not offered.
- Probed Tokio worker-thread support and fell back to current-thread runtime creation.
Seed glm-5.2 and glm-5.2[1m] on the zai (GLM Coding Plan) provider
as selectable catalog entries with 1M context, pin the context at
catalog generation so discovery cannot regress to 200k, and use
glm-5.2 for Z.AI API key validation. Default model stays glm-5.1
(bumping requires maintainer sign-off).
- Extended gateway stream control to pass abort signals and onCancel into encodeStream.
- Added optional cancellation control parameters to provider encodeStream handlers.
- Stopped provider stream loops on cancellation and suppressed SSE completion/error output after abort.
- Added a regression test verifying reader.cancel triggers onCancel and aborts upstream request.
- Added a credential-picker `/logout` flow for selecting one OAuth account.
- Added optional `/logout` provider argument and unknown-provider error handling.
- Changed logout handling to delete only the chosen credential and keep others.
- Added AuthStorage APIs to list and remove credentials by id, with remote deletion hook.
- Added shared Google empty-stream retry constants and reused them in CLI.
- Replaced local content checks with hasMeaningfulGoogleContent for retry gating.
- Refactored streamGoogleGenAI to bound empty STOP retries with state reset and reopen.
- Moved stream start emission before retry loops to avoid duplicate start events.
- Applied a finite 64,000-token fallback for OpenAI completion requests when model maxTokens is unavailable.
- Refactored stream max-token computation to use a shared helper and preserve a finite cap when caller and model limits are null.
- Added regression coverage for null maxTokens flows and bumped the model-cache schema to invalidate entries using retired unknown-limit sentinels.
- Replaced unknown model contextWindow/maxTokens sentinels with nullable values across types and catalog data.
- Mapped request token calculations to treat null maxTokens as unlimited output caps.
- Updated remote compaction and context checks to ignore unknown limits by using Infinity/0 fallbacks.
- Adjusted CLI/model registry flows to skip cap enforcement for null limits and render unknown values as '-'.
MiniMax-M3 OpenAI-compatible streams can carry the same reasoning in visible <think> content and structured reasoning_content. The stream parser now treats the structured reasoning field as authoritative for that chunk and collapses cumulative MiniMax reasoning snapshots to deltas.\n\nFixes #2433
- Augmented streaming SSE observers to infer event names from payload fields when the event field is missing.
- Added `X-Stainless-Timeout` request headers derived from `requestTimeoutMs` for OpenAI and Azure response/completions streaming calls.
- Replaced Azure and OpenAI provider calls with `postOpenAIStream` flow.
- Fixed stream error handling by retaining status, headers, and body on failures.
- Fixed stream parsing by handling raw JSON SSE frames and `[DONE]` events.
- Added `OpenAIHttpError` with parsed messages and timeout-aware retry behavior.
- Added remote-compaction tests for timeout, abort, and 500-fallback behavior.
The bundled GitLab Duo `client_id` had its registered redirect URI list
drift away from `http://localhost:8080/callback`, so every browser
authorize request landed on GitLab's "The redirect URI included is not
valid" error page. The random-port fallback in `callback-server.ts` was
guaranteed to break for the same reason (strict OAuth redirect URI
matching).
- Honour `GITLAB_CLIENT_ID` / `GITLAB_REDIRECT_URI` env vars in both the
authorize URL and the token-exchange / refresh POSTs so users running
with their own GitLab OAuth application can replace the bundled
credentials end-to-end.
- When `GITLAB_REDIRECT_URI` is set, advertise the exact string to
GitLab, bind the local callback server to the URI's loopback host/port
(random port for non-loopback URIs that only complete via paste-code),
and pass `OAuthCallbackFlowOptions.redirectUri` so the silent random
port fallback is disabled — a mismatched URI now surfaces the bind
error rather than producing a token GitLab refuses to redeem.
- Surface the new env vars (and the existing `GITLAB_TOKEN` PAT path) in
the browser instructions text when GitLab rejects the URI.
- Thread `ctrl.fetch` through the login flow to match the xiaomi /
anthropic patterns.
Fixes#2424
- Mapped OpenAI completions streams ending with `finish_reason: "error"` to `stopReason: "error"` with the message `Provider returned error finish_reason`.
- Added regression tests for bare `finish_reason: "error"` streams, including tool-call payloads, to verify the error remains retryable.
Concurrent omp --session restores after an unclean shutdown crashed
in SqliteAuthCredentialStore.#initializeSchema() with
SQLITE_BUSY_RECOVERY because the multi-statement schema run installed
PRAGMA busy_timeout=5000 AFTER PRAGMA journal_mode=WAL, the first
lock-taking statement during WAL recovery. Bun's default busy_timeout
is 0, so the lock conflict surfaces immediately.
- packages/ai/src/auth-storage.ts: hoisted PRAGMA busy_timeout to a
standalone first statement, dropped it from the multi-statement
schema run, wrapped SqliteAuthCredentialStore.open() in a 4-attempt
exponential-backoff retry loop on the SQLITE_BUSY family, and the
exhausted-retry error now includes the DB path. Exported
isSqliteBusyError(err) (matches code prefix 'SQLITE_BUSY').
- packages/coding-agent/src/session/agent-storage.ts: same hoist and
the existing retry loop now uses isSqliteBusyError so
SQLITE_BUSY_RECOVERY / _SNAPSHOT / _TIMEOUT also trigger backoff.
- Hoisted busy_timeout before journal_mode=WAL in every other shared
SQLite open path: history-storage, autoresearch/storage,
memories/storage, github-cache, report-tool-issue (auto-QA),
catalog/model-cache; stats/db.ts now sets busy_timeout at all.
- packages/ai/test/auth-storage-sqlite-busy.test.ts pins the contract:
isSqliteBusyError matches every BUSY extended code (rejects
SQLITE_LOCKED, non-errors, strings); open() leaves the connection in
WAL mode (proves busy_timeout ran before journal_mode); open() retries
through synthetic SQLITE_BUSY_RECOVERY; non-BUSY errors (SQLITE_CORRUPT)
short-circuit; exhausted retries throw an error mentioning the DB path
with exactly 3 sleeps for a 4-attempt budget.
Fixes#2421
- Added optional `useless` flags to tool result types and payload builders.
- Added `pruneUseless` and `dropUeless` options to control uneventful result pruning.
- Changed compaction and shake passes to prune or ignore non-error useless tool results.
- Changed conversation serialization to omit useless toolCall/toolResult pairs from output.
- Added coverage for useless tagging, pruning, and serialization behavior.
- Applied `toWellFormedDeep` to Anthropic stream payloads after optional replacement before request serialization.
- Added a regression test that injects a surrogate-escaping tool-call payload and verifies the serialized request body is sanitized to `\ufffd`.
- Documented the payload-sanitization fix in the `ai` package changelog.
- Sanitized Anthropic tool-use arguments with toWellFormedDeep on every replay path.
- Updated unsigned-thinking replay tests to assert same-API tool arguments are sanitized.
- Documented the lone-surrogate replay fix in the ai package changelog.
- Removed stateful SSE chain controls from Codex provider options.
- Changed SSE transport to send full request bodies without `previous_response_id` deltas.
- Updated stale-chain recovery to treat `unsupported` as stale and recover via websocket.
- Scoped `x-codex-turn-state` to in-turn follow-ups and cleared turn state otherwise.
- Adjusted Anthropic tool-result conversion to keep error results text-only while collecting any image blocks separately.
- Reattached collected images after the tool-result run in the same user message with a separator note for Anthropic compatibility.
- Added tests covering image-only error tool_results, image hoisting order, and successful tool_result image preservation.
- Previously, `anthropic-messages` requests using `resolveWireModelId` would always derive the non-thinking variant for `requestModelId`, even when reasoning was explicitly enabled.
- This change ensures the `reasoning` state is correctly passed to `resolveWireModelId`, allowing the API request to include the appropriate `X-thinking` model variant when thinking is active, standardizing behavior across providers.
- Added `normalizeMandatoryReasoningOptions` to `stream.ts`: models baked with `thinking.requiresEffort` floor omitted or disabled reasoning to `minimumSupportedEffort` instead of sending an explicit disable, fixing "Reasoning is mandatory for this endpoint and cannot be disabled" 400s on OpenRouter Gemini 3.x.
- Skipped the clamp for `suppressWhenOff` models, which handle off provider-side via an explicit wire config.
- Added `requires-effort.test.ts` covering omitted-reasoning clamping, `disableReasoning` suppression, untouched explicit efforts, flag-free pair routing, and flagged collapsed pairs.
- Added the pi-ai changelog entry.
- Added a `requestModelId` option to `AnthropicOptions`, serialized as `requestModelId ?? model.requestModelId ?? model.id` in `buildParams`.
- Replaced `resolveOpenAICompletionsModelId`'s pinned-id short-circuit with `resolveWireModelId`-based resolution ahead of the provider-specific id transforms.
- Passed `requestModelId: resolveWireModelId(model, reasoning)` from every `mapOptionsForApi` case in `stream.ts`, so collapsed `X`/`X-thinking` pairs on aggregators and custom providers switch to the thinking SKU when reasoning is enabled.
- Added the pi-ai changelog entry.
- Mapped `google-gemini-cli` request model ids through `resolveWireModelId` in `stream.ts`: the session effort picks the backing variant id (collapsed `gemini-3.5-flash` at high → `gemini-3.5-flash-low`; claude pairs route off → bare id, efforts → `-thinking`) while `AssistantMessage.model` and usage attribution stay on the logical id; a thinking budget clamped to zero now falls through to the thinking-off path instead of only disabling thinking.
- Added `requestModelId` and `thinking.suppress` request options to the `google-gemini-cli` provider, fixing `Model.requestModelId` being ignored at serialization and sending an explicit `thinkingConfig` (`includeThoughts: false` with `thinkingLevel: "MINIMAL"` or `thinkingBudget: 0`) for thinking-off requests on models with `thinking.suppressWhenOff` — Cloud Code Assist re-applies the baked server default when the config is omitted.
- Added `google-gemini-cli-variant-routing.test.ts` covering collapsed-variant effort routing, pair off-routing, and off-suppression serialization.
- Recorded the ai changelog entries.
- Added `handleUsageResetCommand` support to list and redeem usage reset credits.
- Refactored `/usage` into `show` and `reset` subcommands and removed `/reset-usage`.
- Handled ACP/TUI `/usage` flows so `show` reports usage and `reset` redeems credits.
- Kept selected theme setting values dirty-colored while selected in the UI list.
- Removed the `note` operation from `TodoOp` validation and the `text` field from todo operation payloads.
- Deleted `TodoItem` note storage and stripped note-aware logic from execution, markdown round-trip helpers, summaries, and rendering.
- Updated todo docs and prompt schema text to remove `note` usage and keep the public operation set aligned.