Commit Graph

697 Commits

Author SHA1 Message Date
ruel225 34a1cfc69c fix(cli-runner): drop stock watchdog defaults that disable resume promotion (#125045) (#125085)
* fix(cli-runner): drop stock watchdog defaults from CLI backend descriptors (#125045)

The claude-cli and gemini-cli backend descriptors spread
CLI_FRESH/RESUME_WATCHDOG_DEFAULTS into config.reliability.watchdog.
Those copies are byte-identical to the fallback pickWatchdogProfile
already uses, so shipping them only makes `configured` always truthy,
which permanently disables the promotion gate (!configured) and pins
resumed cron/explicit-timeout turns to the 180s resume no-output ceiling
instead of the 600s fresh ceiling.

Removing the blocks lets `configured` be undefined, restoring the
promotion gate. No user-config path is affected: reliability.watchdog
is not exposed in the config schema, the descriptor is the sole source,
and pickWatchdogProfile is the only runtime reader. The codex backend
never shipped the block, so this aligns the three backends.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(cli-runner): add descriptor-through-resolver watchdog proof (#125045)

Drive the real shipped Anthropic/Gemini CLI backend descriptor config through
the production resolveCliNoOutputTimeoutMs resolver to prove the resumed-cron
promotion is live post-fix. Pre-fix the descriptor shipped byte-identical
resume defaults, so configured was truthy and the !configured promotion gate
was dead, pinning resumed cron turns to the 180s resume ceiling (180000).
Post-fix the block is gone and the resolver returns the promoted fresh budget
(480000 = 600000 * 0.8).

resolveCliNoOutputTimeoutMs is re-exported through the test-only
openclaw/plugin-sdk/test-fixtures subpath (excluded from published dist), so
extension tests can drive the real descriptor through the real resolver without
crossing the extension/core boundary.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(anthropic): split watchdog promotion proof into its own file

Move the descriptor-shape and descriptor→resolver real-behavior proof
tests out of cli-shared.test.ts into cli-watchdog-promotion.test.ts.
cli-shared.test.ts exceeded the extensions max-lines budget (1000
non-blank/non-comment lines) once the proof tests landed; the focused
file keeps the same test names and coverage while staying under the
budget. No production change.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(cli-runner): restore inherited resume watchdog promotion

Validate bundled plugin descriptors through their public setup entries and keep custom watchdog contracts intact.

Co-authored-by: ruel225 <ruel225@users.noreply.github.com>

* chore(cli-runner): align conflicting provider owner with main

Co-authored-by: ruel225 <ruel225@users.noreply.github.com>

* fix(cli-runner): complete inherited resume watchdog promotion

Co-authored-by: ruel225 <ruel225@users.noreply.github.com>

* test(cli): keep watchdog regressions in their owning plugins

Keep Anthropic and Google descriptor assertions inside their plugin-owned registration suites, preserve generic resumed/custom watchdog coverage in its core owner, and remove the cross-plugin core test rejected by architecture CI.

Co-authored-by: ruel225 <ruel225@users.noreply.github.com>

---------

Co-authored-by: ruel225 <ruel225@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-25 04:14:45 -07:00
Peter Steinberger 9f472253d4 fix(voice): fence realtime bridge lifecycle (#129249) 2026-08-25 03:58:42 -07:00
Peter Steinberger 7d9cb48804 test(google): isolate Vertex streams from ambient ADC (#129121) 2026-08-25 01:16:14 -07:00
Peter Steinberger a556d5379b fix(google): honor supported Gemini embedding dimensions (#129038) 2026-08-24 23:39:35 -07:00
Peter Steinberger e30279f89f fix(google): support stable Gemini Embedding 2 request contracts (#128716)
* fix(memory): support stable Gemini Embedding 2

* fix(memory): validate async Gemini batch dimensions

* docs(memory): explain stable Gemini index rebuild

* docs(memory): cover explicit-dimension rebuilds

* fix(google): honor stable Gemini embedding task contracts

Co-authored-by: Franck MEYER <meyerfranckpro@gmail.com>

---------

Co-authored-by: Codex OpenClaw Migration <noreply@local>
Co-authored-by: Franck MEYER <meyerfranckpro@gmail.com>
2026-08-24 05:38:54 -07:00
Peter Steinberger 234df15a6d chore: refresh dependencies after seven-day cooldown (#128414)
* build(deps): refresh dependencies after cooldown

Apply dependency, toolchain, action, image, and exact tool updates released by the inclusive 2026-08-16 seven-day cutoff. Adapt owner boundaries for the resulting CUA, logging, Teams, Markdown, native, and test-harness contract changes while retaining versions blocked by upstream compatibility constraints.

* fix(ui): align markdown renderer env typing

* fix(deps): align postcss and mistral peer contracts

* fix(deps): repair refreshed dependency contracts

* fix(deps): retain tslog startup budget

* fix(ci): verify Android tools with SHA-256

* fix(ci): fence Android SDK cache version
2026-08-24 03:01:54 -07:00
Peter Steinberger 12d0fd2ef8 refactor(anthropic): replace handwritten Claude sessions with Agent SDK (#128131)
* refactor(anthropic): explore official Claude Agent SDK runtime

* refactor(anthropic): replace handwritten Claude sessions with SDK

* refactor(anthropic): collapse SDK live-session ownership

* refactor(anthropic): simplify SDK ownership and preserve live skills

* fix(anthropic): fence cancelled SDK runs before process startup

* fix(anthropic): harden SDK approvals, lifecycle, and packaging

* refactor(anthropic): own SDK process trees and streamline runtime

* fix(anthropic): repair rebased packaging and legacy test fixtures
2026-08-24 01:59:16 -07:00
Peter Steinberger 8083d4dd3f fix(memory): enforce canonical SecretRef resolution (#127699)
* fix(memory): enforce canonical SecretRef resolution

Make Gateway runtime snapshots the exclusive owner of memory SecretRef materialization. Bind embedding credentials and headers to provider-owned destinations, and fence per-agent stale reuse by the provider destination/auth contract.

Release note: Memory search resolves secret references through configured provider policy and keeps embedding credentials scoped to their intended destination.

* fix(lmstudio): preserve resolved memory headers

memory remote headers are already materialized by the Gateway snapshot and now bypass SecretRef re-resolution; provider-owned headers retain canonical resolution; final loopback request proof covers literal preservation and precedence.

* fix(memory): bind stale credentials to auth owners

Resolve memory adapter credential owners from snapshot manifest metadata, conservatively fail cold when metadata is absent, and prove Gemini/Google destination changes plus zero-egress unresolved refs.

* fix(memory): scope compatible embedding credentials

Apply destination ownership to the core compatible adapter while preserving destination-owned credentials and intentionally unauthenticated endpoints. Distinguish loopback principals, consolidate duplicate security tests, and verify the final credential boundary through a live isolated Gateway request.

* test(memory): align destination auth precedence

* fix(memory): bind credentials to query identity

Include URL query parameters in embedding destination ownership so provider credentials and headers never cross tenant boundaries.

* fix(memory): preserve query-bound embedding destinations
2026-08-23 20:36:55 -07:00
Onur Solmaz 1319e1e606 fix(providers): report request acceptance consistently (#126028)
* fix(providers): restore provider acceptance lifecycle

* fix(providers): cancel streams on acceptance failure

* test(amazon-bedrock): split stream lifecycle coverage

* refactor(providers): keep acceptance lifecycle private

* refactor(providers): make acceptance observation synchronous

* refactor(providers): narrow private observer safely

* fix(google): keep response hook within retry deadline

* test(ui): tolerate subpixel spacing variance

* test(ui): wait for durable draft before reload
2026-08-21 19:32:07 +03:00
Peter Steinberger c0775c7f6d fix(search): preserve provider cache hits across equivalent queries (#126798) 2026-08-20 12:04:19 -07:00
Peter Steinberger 3378e07d50 refactor(plugin-sdk): promote shared runtime primitives (#126193)
* refactor(plugin-sdk): promote shared runtime primitives

* test(codex): keep one attempt tools owner
2026-08-19 01:10:18 -07:00
Peter Steinberger 3eecab1bee refactor(plugins): adopt shared retry runtime (#126065) 2026-08-18 17:46:38 -07:00
Onur Solmaz 5cabd2b72e Revert "fix(providers): report request acceptance consistently (#125807)" (#126021)
This reverts commit f5e9622fc9.
2026-08-19 00:16:09 +03:00
Onur Solmaz f5e9622fc9 fix(providers): report request acceptance consistently (#125807)
* fix(providers): report request acceptance consistently

* fix(providers): retain response hook compatibility

* fix(providers): keep legacy response hook path

* fix(providers): distinguish rejected response attempts

* fix(providers): keep acceptance evidence truthful

* fix(google): preserve provider acceptance errors

* test(google): satisfy acceptance callback lint

* fix(google): exclude acceptance hooks from retry deadline

* fix(openai): report Codex websocket acceptance

* fix(openai): commit websocket state before acceptance hook

* fix(google): abort pending acceptance callbacks

* fix(providers): abort pending acceptance callbacks

* fix(mistral): report observed HTTP response

* fix(mistral): report rejected HTTP responses

* fix(providers): derive acceptance from HTTP status

* fix(providers): preserve acceptance lifecycle cleanup

* fix(anthropic): report observed HTTP response

* fix(anthropic): report rejected HTTP responses

* fix(plugin-sdk): expose provider lifecycle

* fix(providers): stop after lifecycle abort

* fix(mistral): cancel unread acceptance failures
2026-08-18 23:50:57 +03:00
Peter Steinberger 31afd52e99 refactor(realtime): share audio duration calculation (#125612)
Fix xAI barge-in truncation that reported 3760 ms after only 3700 ms of decoded audio.

Share exact audio-duration math across OpenAI, xAI, and Google while keeping provider-specific rounding at each caller.
2026-08-17 22:25:42 -07:00
Peter Steinberger 568b920b21 feat(lint): enforce import ordering and deduplication (#124730)
* refactor(imports): dedupe and hoist imports

* feat(lint): enforce import/no-duplicates and import/first
2026-08-16 11:44:52 -07:00
Peter Steinberger 99bda3d8cd refactor: make transport stream writer types honest (#124587)
* refactor(ai): give transport streams an honest writer type

* test(ai): use canonical transport stream fixtures

* fix(ai): preserve partial-less stream deltas
2026-08-16 07:03:36 -07:00
Peter Steinberger 6aa27d6ecd refactor: retire August compat windows (embedding API, pi aliases, target parser, spawning hook, setup exports, WhatsApp inbound aliases) (#124416)
* refactor(plugin-sdk): retire embedded Pi aliases

* refactor(channels): retire explicit target compatibility

* refactor(plugins): retire subagent spawning hook

* refactor(plugin-sdk): retire shipped channel setup exports

* refactor(whatsapp): retire inbound callback aliases

Proof: focused build and WhatsApp E2E green; broad WhatsApp suite 188/189 files green. extensions/whatsapp/src/monitor-inbox.policy.test.ts flakes only in the parallel batch and passes isolated (10/10).

* refactor(plugin-sdk): retire memory embedding registrar

Migrate every bundled provider and manifest to registerEmbeddingProvider and contracts.embeddingProviders. Preserve memory-specific batching, local-service acquisition, index identity, and auto-selection through the canonical generic registry adapter, then remove the parallel registrar, registry, diagnostics, contracts, tests, and docs.

* chore(plugin-sdk): tighten retired surface budgets

Pin the post-retirement public SDK surface to 144 entrypoints, 4,312 exports, 2,564 callable exports, and 1,133 deprecated exports; agent-harness-runtime now permits exactly nine deprecated exports.
2026-08-15 22:43:47 -07:00
Peter Steinberger 1384e277af refactor(types): enforce chained-assertion guard in AI and UI (#124361)
* refactor(types): drain chained-assertion ledger — ai and ui

* refactor(ui): extract chat state contract

* fix(ai): preserve strict Claude request cleanup

* test(ui): synchronize terminal timeout clock

* fix(ui): validate observer digest before run mutation
2026-08-15 22:25:42 -07:00
wanyongstar 8f59226bc9 fix(xai): report lazy voice bridge connect failure as a terminal error (#123064)
* fix(xai): report lazy voice bridge connect failure as terminal error

When the loaded realtime voice bridge's connect() rejected, the lazy
wrapper marked the generation terminal internally but never called
req.onClose and never closed the loaded bridge, leaking it and leaving
callers without a terminal outcome. Mirror the queued-flush failure
path: emit a terminal "error" and close the loaded bridge before
propagating the rejection.

* fix: harden realtime voice connection lifecycle

---------

Co-authored-by: Altay <altay@hey.com>
2026-08-15 20:56:11 +03:00
Peter Steinberger 8e79622777 fix(web-search): fence cancelled provider cache writes (#123951) 2026-08-14 20:29:23 -07:00
Peter Steinberger c9621adf3e feat(google): add Gemini 3.7 Flash with LOW thinking floor (#123366)
* feat(google): add Gemini 3.7 Flash to the model catalog

* fix(google): floor Gemini 3.7 Flash thinking at LOW — MINIMAL retired upstream

* fix(google): satisfy no-map-spread in catalog row builder
2026-08-13 16:34:13 -07:00
Peter Steinberger 964c8c84c1 refactor: consolidate coercion ownership (#122299)
* refactor: consolidate coercion ownership

Centralize four canonical coercion helpers, migrate exact core and plugin duplicates through narrow Plugin SDK facades, and enforce declaration and plugin-normalization ownership boundaries.

The sweep adds eight focused SDK exports while deleting more production and tooling code than it adds. User-visible behavior is unchanged except for safer equivalent object and UI parsing at existing boundaries.

* fix: guard integer option ownership

Register resolveIntegerOption with the canonical function owner and extend the declaration-guard fixture so future local duplicates fail validation.

* fix: keep integer helpers on numeric facade

Remove the unshipped duplicate string-coerce exports and route every affected plugin consumer through the existing number-runtime contract.

* fix: point numeric coercion to number runtime

Make boundary and declaration diagnostics recommend the canonical numeric facade, with failing-before coverage for both guidance paths.
2026-08-11 17:14:53 -07:00
Peter Steinberger 7eed2c3f21 feat(google): add current-turn native video input (#122074)
* feat(agents): add current-turn Gemini video handoff

* test(google): add live native video regression

* build(ai): emit provider types entrypoint

* fix(google): preserve video shedding on retry
2026-08-11 12:58:32 -07:00
Peter Steinberger cad77fb39c refactor: consolidate remaining coercion helpers (#122020) 2026-08-11 10:22:01 -07:00
Peter Steinberger fa03d9b913 refactor: consolidate coercion helpers (#121366)
* refactor: consolidate coercion helpers

* fix: remove duplicate coercion imports

* fix: preserve serialized coercion guard

* chore: ratchet coercion helper carve-outs

* fix(test): keep gauntlet subprocess startup lean

* fix: preserve imported session timestamp semantics

* fix: preserve catalog timestamp string semantics

* chore: align plugin SDK surface ratchet

* fix: preserve trajectory and SDK string contracts

* fix(test): preserve QA record assertion semantics

* fix: complete standalone record guard rename

* refactor(cron): use canonical string coercion

* fix(acpx): preserve Pi timestamp parsing

* test(channels): adapt custody test harnesses

* test(telegram): classify media harness as test support

* test(acpx): split timestamp contract coverage

* test(channels): support generated custody contracts

* chore: ban the full coercion helper name set

Extends the declaration guard to all eleven consolidated helper names and
renames the cron schedule-identity readNumber wrapper to readScheduleInteger
so the banned generic name cannot regrow.

* fix(scripts): repair release-validation guard drift and lint cause

Restores the renamed isJsonRecord guard in assertTrustedWorkflowHarness after
main added isRecord call sites in parallel, and attaches the caught YAML error
as the thrown error cause (preserve-caught-error was red on main).

* fix: preserve Claude timestamp string semantics

* fix: preserve persisted timestamp string semantics

* fix: preserve date-first timestamp contracts

* fix(openai): harden delegation failure formatting

* chore: close coercion helper guard gaps

* test(openai): model non-error delegation rejection

* chore: refresh plugin SDK API contract

* fix(tasks): use canonical string field reader

* fix(ai): use canonical provider error field coercion

* fix(browser): migrate native bootstrap coercion

* docs(plugin-sdk): clarify text record export compatibility

* fix(gateway): normalize approval execution identity

* test(outbound): isolate message action poll harness
2026-08-11 00:02:18 -07:00
Peter Steinberger 18e5b0e3e3 test(web): trim duplicate provider test surfaces (#121727) 2026-08-10 15:44:54 -07:00
Peter Steinberger fc9905edb2 refactor(providers): consolidate streaming fixtures (#114475) 2026-08-09 07:37:52 -07:00
Peter Steinberger 00194139ba refactor(providers): consolidate contract fixtures (#114424) 2026-08-09 07:07:47 -07:00
Peter Steinberger 8616c0c374 refactor: finish shared test helper migrations (#120996)
* test: finish shared helper migrations

* test: fix helper migration CI

* style: fix test import ordering

* test(acpx): restore deferred void types

* test: fix helper migrations after rebase
2026-08-09 06:00:06 -07:00
Peter Steinberger 75dbe52e3e refactor: one code path behind doctor legacy-state migrations (#120716)
* refactor(doctor): prefer manifest route-state owners

* refactor(doctor): unify config repair declarations

* refactor(doctor): unify legacy state migrations

* fix(doctor): satisfy migration pipeline guards

* fix(plugin-sdk): keep doctor adapter inside boundary
2026-08-08 18:23:15 -07:00
sunlit-deng 89c792bf64 fix(google): reject malformed video operation JSON (#115465)
* fix(google): reject malformed video operation JSON

* fix(google): reuse shared provider JSON reader for video operations

Route successful Google video operation responses through the canonical
readProviderJsonResponse so fatal UTF-8 decoding and malformed-JSON wrapping
stay on the shared provider contract. Keep the lenient non-OK detail path.
2026-08-09 04:15:39 +03:00
Peter Steinberger 8cb53c7b55 perf(doctor): keep bundled doctor contract closures dependency-light (#120698)
* perf(doctor): keep bundled doctor contract closures dependency-light

Doctor contract enumeration cold-loads each plugin's doctor-contract-api
closure via jiti, so a static value import of openclaw/plugin-sdk/runtime-doctor
pulled the state-db/kysely graph (~4.3s per closure) into
listPluginDoctorLegacyConfigRules / listPluginDoctorStateMigrationEntries.

- migrate all light doctor-contract closures (66 files) to the
  dependency-light openclaw/plugin-sdk/runtime-doctor-migrations subpath
- voice-call: load detect/repairOpenClawStateDatabaseSchema* lazily inside
  the migration bodies; keep only a type-only static runtime-doctor import
- matrix: split pure credential record shapes/normalizers into
  credentials-state.ts so the doctor closure no longer imports the sync
  plugin-state store through credentials-read
- guard: doctor-contract-closure-guard.test.ts now forbids static value
  imports of runtime-doctor in closures alongside agent-runtime

* fix(matrix): keep credential revocation record type module-local

Knip production scan flags the export as consumer-less; the type is only
referenced by the exported union and revocation guard signature.
2026-08-08 17:51:31 -07:00
Peter Steinberger bb019e4a53 fix(providers): preserve realtime tool-result delivery ownership (#120776)
Co-authored-by: Wan Yong <wan.yong@xydigit.com>
2026-08-08 17:03:43 -07:00
Peter Steinberger da4a656cdb improve: doctor migration checks no longer load every bundled plugin runtime (#120678)
* perf(plugins): declare doctor contract surfaces

* perf(doctor): slim migration import closures

* perf(plugins): narrow doctor declaration record surface and wire owner-test lane

Registry records carry only the doctorContract declaration instead of the whole
parsed manifest, and check:changed now selects the src/plugins-owned declaration
honesty and closure-guard tests for extension module/manifest changes so
cross-lane drift cannot pass PR classification.

* fix(doctor): keep control-plane dist imports require-safe

Keep doctor and channel control-plane chunks off exec-class dependencies, and enforce native require(esm) loading during postbuild.

* chore(plugin-sdk): regenerate API baseline

* chore(plugin-sdk): sync export ordering

* fix(plugins): satisfy doctor contract CI boundaries

* perf(doctor): make qqbot doctor closure dependency-light

qqbot was the last plugin above 5s in doctor state-migration enumeration
(~8s under tsx/jiti). The cost was not the state-key builder (already a
leaf): its doctor closure value-imported the runtime-doctor SDK barrel,
whose plugin-state-store/state-db re-exports pull kysely (~330 modules),
plus security-runtime for one fileExists (~200 modules), all resolved
per-module by jiti during enumeration.

Split the migration-define helpers and light re-exports into a new
private-local plugin-sdk/runtime-doctor-migrations subpath; runtime-doctor
re-exports it so its public surface is byte-identical (API baseline hash
unchanged). qqbot's doctor-contract and state-migrations now import only
the light subpath, swapping fileExists for the equivalent async
legacyStateFileExists already in the closure.

qqbot enumeration: ~8.0s/531 modules -> ~0.25s/18 modules.

* chore(plugin-sdk): drop private-local subpath from API baseline

runtime-doctor-migrations is private-local-only; the baseline tracks public
modules, and the earlier line was generated before the classification.

* fix(plugins): register runtime-doctor-migrations boundary paths

The private-local subpath list feeds the extension package boundary map;
the shared paths config and xai's derived overrides must carry the same
entry or the boundary contract test fails.
2026-08-08 13:29:18 -07:00
Peter Steinberger 48639663b0 chore(release): prepare 2026.8.1 (#120375) 2026-08-07 18:44:12 -07:00
sunlit-deng 83963e5135 fix(google): accept base64url provider media (#116204)
* fix(google): accept base64url in Live audio

* fix(google): normalize base64url in one pass

* fix(google): normalize base64url at every Google media boundary

* fix(google): normalize image media and avoid double music validation

* test(google): exercise URL-safe base64 fixtures

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-08-06 19:00:25 -07:00
Peter Steinberger b4a26783f7 refactor(test): consolidate duplicated requireRecord and provider HTTP mock helpers (#119982)
* refactor(test): consolidate duplicated test helpers

* test: remove stale record guard import

* fix(test): remove orphaned record guards

* refactor(test): keep record requirement messages exhaustively typed

* fix(test): keep packages/ai record guard package-local
2026-08-06 14:48:01 -07:00
joshavant 43b1e375a9 fix: preserve healthy incompatible auth profiles 2026-08-06 01:48:09 -05:00
joshavant 9702134882 fix: settle Gemini CLI auth preparation failures 2026-08-06 01:48:09 -05:00
Peter Steinberger 8db2cb7a94 refactor(providers): deduplicate bundled model catalogs (#119627) 2026-08-05 07:40:07 -07:00
Vincent Koc e35d22807e perf(xai): lazy-load optional capability runtimes (#119374)
Punchcard-Session: coral-workshop-workshop-3f
2026-08-05 11:26:40 +08:00
Peter Steinberger 990fdcf6fd fix(providers): recognize configured media credentials across provider boundaries (#118761)
* fix(providers): honor configured media credentials across owner boundaries

* test(providers): align configured auth fixtures and derived SDK surfaces

* fix(providers): enforce shared DashScope media credential policy
2026-08-03 13:26:31 -07:00
Peter Steinberger 8eaf917efb fix(google): honor Cloud SDK credential location and Vertex billing project (#118745) 2026-08-03 10:47:13 -07:00
Peter Steinberger 8df0142771 fix(google): retry stalled Gemini response bodies (#118511)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
2026-08-02 23:20:28 -07:00
Vincent Koc 9e81a9b51c fix(google): restore image generation with blank base URL (#116852)
* fix(google): handle empty image generation base URL

* fix(google): preserve attribution for blank base URL

* test(google): cover blank media base URLs

* fix(google): normalize blank Gemini API URLs

---------

Co-authored-by: Ted Li <tl2493@columbia.edu>
2026-08-03 12:18:16 +08:00
Vincent Koc 2844876be3 fix(google): align unsupported CLI auth paths (#118034) 2026-08-03 00:13:19 +08:00
Vincent Koc 5be95ad4ba fix(google): bound native realtime tool ownership 2026-08-02 15:59:47 +08:00
Vincent Koc b620caabcb Merge pull request #117490 from codex/qa402-google-live-session-activation-deadline
fix(google): honor Gemini Live new-session activation deadlines
2026-08-02 15:50:02 +08:00
Peter Steinberger 131b9e3904 refactor(voice): share realtime session lifecycle (#117865)
* refactor(voice): share realtime session lifecycle

* test(voice): normalize abort rejection
2026-08-01 23:35:57 -07:00