Chat Completions replay flattened every assistant text block with an empty
separator, so two distinct blocks came back as one word-joined sentence. The
same message shape survives distinctly on the Anthropic, Responses and Mistral
lanes, and the string-content flattener for strict OpenAI-compatible servers
already joins with a newline.
Two blocks arise routinely: streaming opens a new text block after a tool call,
and cross-model replay converts a thinking block into a text block.
* fix(ai): Codex stream shows internal parser text on a malformed frame
The Codex SSE parse boundary rethrew JSON.parse failures with its own
message, so the three consumers that key off the shared malformed-fragment
marker by exact string equality could not recognise it and the internal
parser wording reached the operator verbatim.
Mirror the canonical transport contract instead: a SyntaxError becomes the
shared marker with the original error kept as cause, and anything else is
rethrown untouched. The WebSocket twin is left alone because two open PRs
own that block today.
* fix(ai): preserve consumer SSE errors
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
The provider stream path interpolated the rejected SSE frame and its raw lines
into the thrown error, so text the model had generated reached logs and the
operator's error surface. The transport path already answers this exact condition
with the shared MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE and keeps the
SyntaxError on cause, and the user-facing substitution in
formatRawAssistantErrorForUi matches that constant exactly. Align the provider
path with it so the same failure reads the same way on both paths.
The error-event branch is left as is: its payload is parsed downstream to build a
meaningful operator message, so redacting it would be a regression.
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
* fix(ai): prevent websocket cache clobber on concurrent acquire
The WebSocket session cache acquire path (expired or non-reusable cached
entry) used unconditional delete/set around the connectWebSocket() await.
A concurrent request could install a newer entry during that await, which
the resuming acquire then clobbered, orphaning the socket that carried the
real previous_response_id continuation and corrupting multi-turn Codex
conversations.
Mirror the release path's owner-checked helpers: delete via
deleteOwnedWebSocketSession, and install via a new setOwnedWebSocketSession
that only writes when the cache still matches what this acquire left behind
(the stale entry, or undefined after removing it / on first connect). A
different cached entry means a concurrent request already won the session.
The busy-cached branch is unaffected: it returns a transient socket and
never writes the cache.
* fix(ai): close CAS loser websocket promptly without leaking its socket
* fix(ai): correct verifyClient callback type in race regression test
* fix(ai): remove unused variable and fix lint in race regression test
* test(ai): prove concurrent websocket loser closes
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(ai): tool calls fail when an unsupported schema keyword is nested
The strip walked only properties, items, anyOf, oneOf and allOf, copying every
other value through untouched. A keyword the model rejects therefore survived
inside additionalProperties, prefixItems, patternProperties, contains,
propertyNames, not, if/then/else, dependentSchemas and $defs, and the request
was refused by the provider even though the strip reported success.
Walk the same containers the caller already enumerates in
agent-tools-parameter-schema.ts.
* fix(ai): cover all nested schema containers
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(ai): avoid websocket cache clobber on stale reused release
* fix(ai): prefer-const holdFirstReuse in websocket cache race test
* fix(ai): avoid websocket cache release clobber
* fix(ai): add curly braces for websocket cache delete guard
* fix(ai): owner-check websocket cache deletes without LOC growth
* ci: bump plugin SDK public surface budget pins to match main tip
* fix(ai): satisfy curly lint for websocket cache owner delete
* ci: allowlist readonly state db and format SDK surface pins
* style: oxfmt plugin-sdk surface report
* ci: drop duplicate readonly state db kysely allowlist entry
* test(ai): prove cached websocket lease ownership
Move the contributor stale-release regression into the canonical cached transport suite and prove authenticated real WebSocket close, cancellation, and queued idle expiry cannot evict a replacement lease.
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Honor existing per-turn timeout and retry controls across OpenAI Responses, Azure Responses, and OpenAI-compatible Chat Completions. Add real SDK HTTP regressions for all three transports.\n\nRefs: #114203
Preserve trusted policy-required message delivery and native image/computer tools across Code Mode catalogs and provider transports. Keep preferred Gemini capability metadata during live discovery, escape MCP declaration keywords, and reject suspended-run resumes with missing owner identity. Add before-and-after regressions for each user path. Closes#114753.
* build(deps): remove npm shrinkwrap; mirror pnpm lock into transient package locks
npm 12 removed shrinkwrap (command + tarball/root loading). Delete all 82
committed npm-shrinkwrap.json files and stop publishing lockfiles; keep
pnpm-lock.yaml as the single reviewed dependency boundary. The generator
becomes scripts/generate-npm-package-lock.mjs and feeds plugin bundling via
a transient package-lock.json + npm ci (works on npm 11 and 12). Tarball
validation treats the published 2026.7.2 beta train as a shrinkwrap
transition; self-update npm detection now uses install topology instead of
the shipped shrinkwrap.
* fix(deps): repair lint, deadcode, and test-type lanes for the npm 12 migration
- sort integrity comparisons with an explicit comparator (oxlint)
- keep resolveBunGlobalNodeModules module-local (knip unused-export gate)
- model npm pack --json as npm<=11 array / npm 12 name-keyed object
- default calver destructuring in the tarball test fixture