* feat: add session discussion panel seam
* fix: keep discussion iframe cookie-capable and changelog release-owned
* test: cover cookie-capable discussion iframe sandbox
* fix: stretch discussion panel host so the embed fills the rail
* fix: keep provider failures retryable and probe discussion availability before showing the action
* fix: block same-origin discussion embeds, show action on catalog sessions, close stale panel on reconnect
* fix: scope discussion probes and panel callbacks to the issuing connection
* fix: dedupe in-flight discussion probes per session
* fix: retry superseded discussion probes and key-scope panel results
* fix: regenerate Swift protocol models and extend advertised-method expectations
* style: format chat-pane-header
* fix: regenerate Kotlin protocol models and date discussion methods in the 2026.7 train
* chore: restore release-owned changelog to main state
* chore: keep changelog untouched relative to merge-base
* fix(ai): honor Retry-After when retry-after-ms is unparseable
The ChatGPT Responses retry path returned as soon as `retry-after-ms` was
present, so a malformed or empty value discarded a valid `Retry-After`
header sent alongside it and the client fell back to blind exponential
backoff. The sibling parser in `provider-transport-fetch.ts` treats the
two headers as ordered preferences and falls through on a parse failure;
this aligns the Responses path with that contract.
* test(ai): deduplicate retry header coverage
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat: avatar upload UI, self-service profiles, server-side avatar proxy
* refactor(ui): make resolveAvatar synchronous so avatar updates render immediately
resolveAvatar no longer does async work (gravatar moved server-side), so
the until()-deferred render left the identity chip img stale after
updateSelfUser. Rendering synchronously reuses the img element and
reflects avatarUrl changes on the next render.
* fix(gateway): bound avatar Gravatar lookup to one timeout budget
Linked-email Gravatar lookups ran sequentially, each with its own 5s
timeout, so an upstream outage stalled the held connection for
5s x linked-email-count. Resolve them concurrently and pick the first
hit in email order, keeping primary-email precedence while capping the
request to a single timeout budget.
* fix(gateway): cap per-profile Gravatar fan-out to bound sockets and memory
A profile with many linked emails would fan out one concurrent fetch per
email with no bound, each able to buffer up to MAX_GRAVATAR_BYTES before
the cache budget applies. Cap the lookups to the first 8 primary-ordered
emails so a single avatar request holds a bounded number of sockets and
transient bytes.
* test: reconcile avatar tests with server-side gravatar model after merge
main landed a client-side Gravatar path (browser computes the gravatar.com
URL); this branch resolves avatars through the same-origin gateway route,
which is the only approach that works under the Control UI CSP (img-src
'self'). Update the identity-section, app-sidebar footer chip, and
profile-page e2e expectations to assert the canonical /api/users/<id>/avatar
route (gateway serves the Gravatar fallback behind it) instead of a
CSP-blocked direct gravatar.com image.
* test(gateway): fix fetchImpl mock param type for check-test-types
The Gravatar mock typed its param as (url: string), narrower than the
fetchImpl signature (URL | RequestInfo); function-param contravariance
made it unassignable under tsgo:test. Widen to the fetch input type and
extract the URL via a small helper that avoids no-base-to-string on a
Request.
* fix(gateway): short-circuit Gravatar lookups and version presence avatar URL
Two autoreview findings on the avatar path:
- Privacy: the concurrent Promise.all fan-out queried every linked email's
hash against Gravatar even when the primary already had one, exposing
secondary work/personal addresses. Resolve sequentially with short-circuit
so a later hash is disclosed only after the earlier email is a definite
miss, under one shared request deadline that still bounds total latency.
- Staleness: presence published an unversioned /api/users/<id>/avatar, so a
reconnecting viewer's <img> kept the stale cached image after an upload.
Carry the profile revision as ?v=<updatedAt> so a changed avatar refetches.
* feat(agents): authoritative write diffs end to end
The write tool now reports what actually happened: no-op (changed:false),
created files (created:true with a bounded numbered diff), and overwrites
(created:false with a real old-to-new diff when the old content is
readable text within byte, line, and edit-distance budgets — unknown
removals are never fabricated). The gateway history projection forwards
the changed/created flags, the web and iOS write rows prefer the
authoritative diff and suppress the +N -0 stat when creation is not
proven, and the iOS transcript cache persists one bounded apply_patch
envelope so patch diffs survive cold opens.
* chore(i18n): sync native inventory
Lazy command-group registration must import the real command tree to render complete help. On macOS with NODE_USE_SYSTEM_CA=1, those imports start Node's system CA loader worker; natural shutdown can block in CleanupCachedRootCertificates while joining LoadSystemCACertificates.
Request the existing stream-flushed one-shot exit immediately after successful Commander help parsing, covering both CommanderError and normal-return plugin help without affecting leaf command execution.
- Close+clear the adopted logical voice session when a config/provider change
invalidates a realtime prefetch (a live call is preserved); prevents the
gateway rejecting a stale voiceSessionId resume under a new provider.
- Retry talk.client.close (0/500/2000ms) before forgetting the session, matching
the web client, so a transient close does not orphan it until the 6h sweep.
- Surface exhausted transcript-persist failures through the session delegate so a
dropped affirmation transcript no longer silently blocks spoken confirmation.
- Localize the "Confirmation needed" status through the presentation switches;
add its native locale translations.
Follow-up to #111393: run-tsgo, check-tsgo-core-boundary, profile-tsgo,
and the format-docs oxfmt shim still resolved node_modules/.bin from the
current checkout only, so dependency-less linked worktrees died with
ENOENT before oxlint's boundary prep could run. All four now use
resolveRepoToolBinPath, completing local lint/typecheck tooling for
linked worktrees.
* chore(pr): reuse green hosted CI from patch-identical pre-rebase heads (24h window)
* fix(pr): type-safe fixtures and lint cleanup for hosted-CI reuse tests