Commit graph

32 commits

Author SHA1 Message Date
Peter Steinberger
82a4cfbe57
feat(plugin-sdk): awaited session persistence; deprecate sync transcript writes (#163264)
* feat(plugin-sdk): await session persistence and deprecate sync writes

* test(sessions): fix awaited persistence fixture types

* fix(sessions): preserve binding and delivery publication

* test(sessions): isolate compaction retarget authority

Model the retarget as an independent operation so the fixture reaches post-commit publication validation. Keep the committed receipt, accounting, and replacement-transcript assertions intact.

* fix(ci): remove duplicate database-worker test routing

Main already routes attempt-phase-lifecycle.test.ts through the database-worker owner. Remove the duplicate introduced by the SDK branch. Exact-head preflight and the native local manifest both reproduced the failure; the corrected manifest passes with 69 selected Node rows. Unique test coverage and the duplicate guard stay intact. Formatting and P2 review pass.

* fix(sessions): keep maintenance projections with the host owner

Return committed projection-rebuild facts from maintenance workers and schedule them through the existing host owner. Preserve custody, rollback, and synchronous compatibility. Update async fixtures and host-broker test routing, restore the native wrapper import inventory, and route memory visibility declarations through their existing producer.

Focused Linux proof passed342 tests across17 suites; old-code controls fail at pending projections. SDK declarations retain legacy signatures with four additive exports. Types, lint, T1, Madge, focused routing checks, and P2 review pass.

* test(cli): await blocked-run hook entry without polling

Await the existing hook-entry gate instead of racing awaited transcript persistence against vi.waitFor's one-second default. Preserve the early-settlement failure and the assertion that agent_end must finish before the CLI run settles.

The two focused cases pass in 155.98s including preparation; their test bodies take 3.656s and 1.804s. Fresh P2 review is clean.

* test(cli): use the deferred helper default type

* test(gateway): keep worktree fixture on the shared state root

Use the nested fixture only for workspace and device files. Activating its environment switches process state roots underneath the shared Gateway, whose projection retains its startup environment. Preserve the registry witness guard and every cwd, transcript, initial-run, and follow-up assertion.

CI observed AgentDatabaseRegistryChangedError during creation. The exact callback interleaving was not captured, and unmodified main passed the whole file in 172.229s; that is non-reproduction, not inherited-failure qualification. The corrected fixture passes all 10 cases in original order in 164.077s. Semantic lint, formatting, and fresh P2 review pass.

* refactor(sessions): separate hydration types and CLI hook fixtures

Keep transcript hydration results beside their request contracts and retain aggregate exports. Move the CLI hook fixture owner into test support without changing coverage. This removes both line-cap increases after main integration; 112 composition tests and all 52 reliability tests pass.
2026-10-02 07:03:11 -07:00
Peter Steinberger
5c8a714982
refactor(providers): deslop provider adapters (#162488)
* refactor(providers): deslop provider adapters

* refactor(providers): preserve the auth profile SDK declaration

* test(providers): complete the prepared auth bootstrap fixture

* refactor(providers): make detached catalog method contracts explicit
2026-10-01 05:41:53 -07:00
RoboClaw
1caaef4b6f
feat(ui): select Ultrafast for supported accounts (#160352)
* feat(ui): select Ultrafast for supported accounts

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(gateway): preserve Ultrafast compatibility and account authority

Negotiate speed decoding per connection and keep canonical session state intact. Fence personal-account catalog requests at final guarded HTTP dispatch. Refresh the measured UI boot manifest without changing performance limits.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* refactor(openai): keep account catalog outcomes together

Move the existing account-scoped result projection into the already imported catalog helper without changing its behavior. Keep the provider owner below its existing line-count ratchet.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test: refresh Ultrafast tool prompt fixtures

Regenerate the canonical Codex dynamic-tool fixtures for the authorized Ultrafast speed value. Update only that enum value and its derived size/hash metadata; keep snapshot checks enabled.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix: stop account catalog requests after default unlink

Bind automatic account selections to the canonical profile writer's committed link authority and carry the real request scope through final guarded dispatch. Keep explicit retained account selections usable after unlink and evict failed discovery custody. Preserve the existing active Auto Ultrafast opt-in while explicit Fast stays priority.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* refactor(codex): use narrowed Auto activation flag

Keep the reviewed Auto tier predicate while satisfying the typed boolean lint contract.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(ui): share speed applicability for optional Ultrafast

Respect the selected request mapping before offering an optional entitled tier. Preserve all three choices on eligible routes and clearable stored preferences on unsupported routes. Reproduced the contradictory catalog regression and passed 59 unit/Chromium cases; scoped independent review found no actionable P0/P1.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(ci): export manifest in same-revision preflight harness

Restore the trusted file omitted when the inline manifest moved out of ci.yml in 9d75a8fe87. Actual preflight job109720001696 failed before tests with MODULE_NOT_FOUND; real Git push and PR materialization fixtures reproduce the same omission. Keep the canonical index export owner, regenerate its workflow projection, and preserve all source/credential guards. Fifteen materialization variants, five import/size checks, root test types, and focused independent review pass.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(ci): satisfy extracted manifest static contracts

Repair inherited check-lint failures from the manifest extraction without changing CI routing: avoid namespace shadowing, retain error cause and the diagnostic callback string contract, preserve nonmutating shard copies, and apply required branch syntax. All1259 scripts lint clean;45 planner/import/size cases, formatting, UI i18n, styles, ratchet and independent review pass.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(ci): centralize dependency-free workflow flag parsing

Remove the extracted manifest local coercion helper and preserve its exact narrow Boolean grammar under the existing script argument owner. Register the canonical declaration rather than weakening the guard, and carry its runtime through trusted preflight materialization and fixtures.77 argument cases,11 declaration-guard cases,71 scoped integration cases, types, lint, export scans and remaining guard commands pass; independent review has no actionable P0/P1.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(gateway): separate model publication authority from selection scope

Keep the actual request lifetime in selected-account HTTP assertions without treating every anonymous unscoped catalog read as a personal account projection. Restore the established models.list response shape; no assertions weakened.177 model/catalog/session cases and10physical HTTP authority cases pass, together with types, lint, ratchet and independent review.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix: preserve session response argument tuples

Forward the original response tuple while projecting successful legacy payloads, without appending optional undefined arguments.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(codex): preserve existing Ultrafast opt-ins

Keep the v2026.9.7 Fast and active Auto opt-in semantics while adding explicit per-session Ultrafast. Standard still clears the tier. Cover cold and warm native turn requests without requiring a migration.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(ui): distinguish speed labels from model names

Match the complete Effort and Speed section labels rather than the Speed only fixture model. Retain the independent absence assertions for reasoning and speed controls. All four failures reproduced before the repair; the complete 13-case bundled browser file passes afterward.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(openai): intercept the shared transcription socket

Update the two stale socket mock registrations after the upstream transport consolidation. Keep the actual provider/session code, fake peers, assertions, timeouts, and Bun transport guard unchanged. All 86 OpenAI shard files pass: 1263 passed and one existing skip.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(gateway): construct complete session reset callers

Replace partial caller objects cast as never with the existing typed session mutation client fixture. Preserve provenance and required-sandbox assertions and the production client capability contract. Both CI failures reproduce before the repair; all 15 reset-model cases pass afterward.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix: confirm the selected Ultrafast command mode

Share the direct command, directive reply, and system-event confirmation formatter so the saved Ultrafast tier is named accurately. Include the accepted manual value in help and docs without advertising an unverified optional native-menu choice. Preserve boolean Fast, Auto, reset, authorization and persistence behavior. Three regressions fail before repair; 274 focused cases pass afterward.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

---------

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
2026-09-30 07:45:41 +00:00
Peter Steinberger
9c300442b7
refactor(providers): deslop provider plugins fourth pass (#161471)
* refactor(providers): deslop model-provider plugins fourth pass

* chore(providers): prune removed assertion allowances

* refactor(deepinfra): build catalog entries without conditional spreads

* refactor(openai): remove residual runtime type imports
2026-09-29 19:11:06 -07:00
Peter Steinberger
d77a03bbe1
refactor(providers): deslop model-provider plugins third pass (#161162)
* refactor(providers): deslop model-provider plugins third pass

* refactor(providers): preserve typed configuration and catalog receivers
2026-09-29 05:03:39 -07:00
Olli
4ddd0e3397
fix(openrouter): configured models send high reasoning effort when xhigh is selected (#160651)
* fix(openrouter): retain reasoning effort in prepared models

* fix(openrouter): keep catalog efforts in configured thinking selection

Configured OpenRouter rows on the catalog route carried reasoning: true
without effort metadata, so turn thinking selection clamped xhigh to high
before the runtime model was resolved. Pass the selected route to thinking
profile hooks and let OpenRouter fill missing effort capabilities from its
model catalog on the canonical route only.

* fix(openrouter): keep thinking profiles off the capability store

Thinking profiles run on synchronous Gateway session reads. Consume only
capabilities that runtime model resolution already loaded instead of
initializing the SQLite-backed catalog cache or starting a fetch.

* fix(openrouter): read refreshed catalog efforts for thinking profiles

Thinking profiles kept a plugin-local copy of capabilities from the first
runtime resolution, so catalog refreshes never reached session reads. Read
the owner's in-memory catalog cache instead; it still avoids SQLite and
fetches on synchronous paths. Drop the release-owned changelog entry.

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-29 07:08:38 +05:30
Peter Steinberger
61cb9d07a1
refactor(providers): deslop model-provider plugins second pass (#160038)
* refactor(providers): deslop model-provider plugins second pass

* chore(providers): align cleanup guards with shared owners
2026-09-28 14:12:07 -07:00
stevenlee-oai
3a3ce2be22
fix(openai): discover models for the selected SIWC account (#160027)
* fix(openai): discover models for the selected SIWC account

* test(openai): complete SIWC discovery fetch fixtures

* test(models): avoid shadowing catalog snapshot fixture

* fix(openai): avoid shadowing SIWC catalog rows

* fix(openai): keep SIWC catalog denials scoped to discovery
2026-09-28 11:15:09 -07:00
Peter Steinberger
60acc6bd94
fix(models): preserve legacy catalogs after failed refreshes (#158113)
* fix(models): retain discovered models after refresh failures

Record successful legacy catalog results at the producer boundary so unavailable refreshes retain the accepted inventory. Preserve explicit outcomes, advisory SDK fallback behavior, and first-discovery starter policy.

* fix(models): preserve skipped catalog outcome semantics

Mark bundled static, configured, and advisory catalog projections with
explicit empty outcomes so legacy success inference cannot promote them
to observed account inventory. Preserve live outcomes and helper types.

Keep exact auth provenance histories and move existing fixture/policy
code into focused owners where required by the line-cap ratchet.

Validation: 447 producer and sibling cases, 56 shared self-hosted cases,
95 auth/policy cases, causal missing-outcome failures, maintained checks,
and independent review.

* test(plugin-sdk): keep discovery loader types acyclic

Move the shared loader type into a leaf consumed by both discovery
contract helpers. Preserve its public provider-test-contracts export
without a child-to-parent type import cycle.

Validation: maintained Madge check reports zero cycles; core, all core
test graphs, extension test types, lint, formatting and independent
review pass. Runtime behavior and previous catalog proof are unchanged.

* fix(plugin-sdk): mark generated static catalogs explicitly

Keep the generated non-live, non-strict catalog adapter from claiming
successful acquisition for manifest or configured rows. Preserve null,
errors, strict and custom callbacks, static catalogs, and public types.

Validation: three existing controls fail before the correction; all49
owner and sibling cases pass afterward, with types, lint, line caps and
fresh independent review clean.

* test(models): preserve explicit discovery authority in OAuth fixture

Supply an empty completed-provider map when reusing a prior publication. The existing ready/profile origin must authorize retention; the fixture does not claim legacy acquisition. Preserve all OAuth preparation and no-HTTP assertions.

* fix(models): retain only acquired legacy catalog rows

Capture accepted runtime-hook identities before static fallback and config
merging, then carry matching registry route keys through the existing worker
result and provider inventory bindings. Preserve eligible final published
rows across repeated same-auth failures without inventing public discovery
outcomes or retaining configured-only and augmentation-only rows.

Keep provider aliases, case-distinct IDs, empty replacement, profile/source
fences and prior learned metadata over failed same-route fallback seeds.
Real-worker regressions prove both reviewed completion-map gaps and their
repair; 136 relevant cases pass across final and unchanged sibling runs.
Production and owning test compilers, selected structural/lint gates, both
cycle checks and fresh independent review pass. The inherited 721/720
local shard guard is handled by the existing canonical shard split.

* fix(ci): rebalance agent session typecheck roots

Move 17 session test roots from agents-root to the existing agents-sessions
graph. This restores the 720-root boundary without increasing the limit or
changing test coverage, graph count, or compiler settings.

Verified all 12,941 current roots remain uniquely owned, the exact boundary
guard passes, and both affected canonical type graphs pass. Independent
review found no actionable issues.
2026-09-25 11:39:38 -07:00
Peter Steinberger
52fc3daa84
fix(models): preserve scoped provider discovery outcomes (#158139)
* fix(models): preserve scoped provider discovery outcomes

* fix(models): preserve upstream catalog integration

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-09-25 07:30:23 -07:00
Peter Steinberger
16908037a0
refactor(media): deslop media (#156406)
* refactor(media): deslop media

* docs(plugin-sdk): clarify media helper scope
2026-09-23 10:50:52 -07:00
Marvinthebored
b373c9a9bc
fix(talk): preserve Grok transcripts and confirmed voice consults (#155838)
Grok (xAI) Talk saved each spoken sentence as several duplicate or truncated user messages, because every cumulative transcription snapshot was persisted as a final turn. Longer spoken replies were also cancelled when they overflowed the browser's 10 s playback queue. Tool-call consults did not tell the agent which blocked call the user had confirmed.

The xAI provider now previews input snapshots and commits one final per utterance at a real boundary: next speech, response end, session close, or 1.5 s of quiet after a late recognition. It fences output, cancel errors and buffered tool calls from retired responses. The relay forwards snapshot mode and the saved transcript id, so the Control UI hides a live caption once its saved row arrives. Browser playback allows 60 s / 4,096 sources, and the 20 ms relay frame contract is unchanged. Tool-call consults receive the same blocked-call retry context and confirmation-id reply as native delegation. A same-action retry in a new run reuses the pending challenge without extending it. `onTranscript` gains an optional `{ textMode: "snapshot" }` metadata argument.

Proof: live isolated Gateway and Control UI Talk runs on xAI Grok against main.
- Three utterances were saved as 3 turns instead of 22. Long answers played to the barge-in instead of being cut by overflow.
- A spoken "Yes." wrote the confirmed file exactly once; "No." wrote nothing.
- Non-affirmations, expired challenges, superseded or consumed ids, and ids from a closed session were all rejected before the exec ran.

Co-authored-by: Marvinthebored <peter@lindsey.jp>
Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-23 10:44:33 +05:30
Peter Steinberger
f7dae76bee
feat(search): configure providers and verify search in Settings (#154135)
* feat(search): configure providers and verify search in Settings

* refactor(search): keep settings projections lint clean

* fix(search): bind provider tests to applied settings

* fix(search): preserve protocol order and complete CI coverage

* perf(search): defer model URL validation until needed

* fix(search): recheck authority before provider requests

* fix(search): include authority helper in wrapper closure

* test(qa): retain redacted cron run failure diagnostics

Keep the existing timeout and success predicate while preserving the complete redacted cron response in assertion output and retained proof. The original CI timeout remains unproven after bounded replay; this change improves diagnosis without claiming a causal flake repair.

* fix(search): project status from published auth state

Keep Search settings credential availability and native routing on lifecycle-published auth snapshots. Missing publication remains unavailable instead of reopening persisted auth on the Gateway request thread. Regression cases fail on the previous cold-state fallback; 71 focused and sibling cases, typechecks, lint, and independent review pass.
2026-09-21 03:51:42 -07:00
Ayaan Zaidi
bc0b24713d
fix(search): cancel xAI searches waiting on credentials (#154559)
## What Problem This Solves

Fixes: xAI web searches can remain stuck waiting for OAuth credentials after the search deadline expires or the caller cancels them, including while another operation holds the profile lock.

## User Impact

The existing search timeout now covers credential preparation, the request, and authentication recovery. Cancelled searches cannot make a late credential claim or send a late search request. Started lock acquisition retains its cleanup owner, and an already-claimed OAuth refresh still finishes its independent durable settlement.

An unavailable OAuth sign-in may still require `openclaw models auth login --provider xai --method oauth`. This change does not restore old refresh tokens, remove refresh fences, increase timeouts, or change permissions.

## Why This Change Was Made

Search previously started its timeout only after credential lookup, and auth failures were converted into missing-key responses. The auth manager now makes observation of its whole queued lookup cancellable, including lock acquisition, while retaining the task through cleanup and handing claimed refreshes to their existing settlement owner.

The generic queue remains unchanged. Optional cancellation flows through existing credential resolvers; existing callers without a signal retain their behavior. Supported API-key fallback and non-missing authentication diagnostics are preserved.

## Evidence

- Real Gateway `POST /tools/invoke`, normal bundled xAI registration, synthetic credentials and a local HTTP endpoint: three pending-fence searches completed in **1.040 seconds** with a **1-second budget**. A separate search completed in **1.023 seconds while the OAuth profile lock remained held**. After release, credentials were unchanged and no late search request occurred.
- The same proof exercised a cited answer, HTTP 503 rejection, cancellation during an open response body, and successful search after cancellation. Exactly four requests reached the provider fixture; the cancelled body closed and cleanup joined the remaining work. The published repair's SUT hashes match the tested source.
- The real-lock regression fails on the previous head because the caller remains blocked, then passes after the manager repair. The original Gateway probe also fails on baseline `6beea0a852` before any provider request. The credential-error regression detects the original false `missing_xai_api_key` result.
- Final affected auth/queue/observer run: **14 tests passed in 14.49 seconds**; the real-lock case took **485 ms**. The six-case search-auth suite passed in **15.53 seconds** with one worker, including 9.72 seconds of shared worker preparation and 170 ms of test bodies. Broader auth, xAI and shared-search sibling suites passed.
- Runtime builds and a plain-Node consumer of the built package auth subpath passed. The consumer verified pre-aborted credential lookup, caller-reason preservation and timeout cleanup. Scoped core type-aware lint, formatting, and source-size/suppression/assertion ratchets passed.

No live provider credentials or production jobs were used. Extension declaration preparation is locally blocked by a nested-checkout ancestor-dependency guard; no override or dependency reconciliation was used. Applicable hosted PR CI owns the remaining static and matrix checks.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-21 14:40:55 +05:30
RoboClaw
41ee7fb242
fix(voice): keep audio flowing while the Gateway is busy (#154119)
* fix(voice): keep audio flowing while the Gateway is busy

Move Discord voice transport, packet pacing, codecs and capture deadlines into a session-owned worker. Run GPT Live WebRTC and WebSocket media in workers and connect continuous playback with a bounded direct audio port. Preserve authorization, selected-agent work, recording receipts and transcript ownership on main, with generation fencing and ordered teardown.

Validated affected tests, the capture-finalization red/green regression, source and compiled worker lifecycle, root and plugin builds, type/lint/format and line-cap checks.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(voice): keep audio flowing while the Gateway is busy

Worked on by:
- @steipete

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
OpenClaw-Publication: 58c87505-0064-4391-994e-57587279bcca

* fix(voice): use explicit Node message transfer lists

Replace six Node postMessage lint suppressions with explicit empty transfer lists. Preserve the Node messaging contract and the unchanged production suppression allowlist. Reproduced the original CI shard plan before the fix; the same plan, focused worker messaging tests, and type-aware lint pass afterward.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(openai): keep worker runtime out of cold voice catalogs

Load the default media-socket factory only through abort-aware connection admission. Preserve injected factories and WebRTC routing, and centralize unchanged request-ID construction in the wire owner. Keep the cold-catalog contract and line cap intact.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* refactor(openai): keep socket contracts below connection admission

Move unchanged socket interfaces into the existing shared leaf and migrate all private consumers, preserving lazy worker loading without a type import cycle.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(discord): preserve direct speech ownership through playback

Track exact speech with an epoch-scoped shared playback witness. Order completion behind bounded direct-port flush receipts, including no-audio responses, and prevent stale idle or flush events from retiring newer speech. Cover original failures and the pending-flush race with deterministic production-owner regressions.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(discord): preserve audio ownership across worker retirement

Worked on by:
- @steipete

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
OpenClaw-Publication: 6294ffa7-0c2f-456b-9baf-3dcdab60efc8

* refactor(discord): keep the worker status slot private

Worked on by:
- @steipete

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
OpenClaw-Publication: dde5cff1-21c6-4bfa-825e-47c2f1ec2393

---------

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
2026-09-20 21:49:35 -07:00
Peter Steinberger
ddc25180e5
fix(search): keep native search from using unrelated provider credentials (#154084)
* fix(search): keep native search from using unrelated provider credentials

* fix(search): preserve managed search when its native plugin is disabled

* fix(search): reuse scoped plugin metadata during tool inspection

* test(search): align Codex prompt fixtures and isolate filesystem overrides

* fix(search): preserve provider HTTP status in safe error results

* fix(search): retain HTTP diagnostics for remaining search providers
2026-09-20 17:49:10 -07:00
Peter Steinberger
1b3f38e7e4
fix: avoid false Gateway failures during cold starts (#153063)
Avoid false Gateway timeout and unreachable reports during slow cold starts. Doctor, health, and status diagnostics now reuse the existing readiness owner and one monotonic deadline, preserve observed startup progress, and leave a Gateway that is still starting alone. Remove Doctor's duplicate restart polling loop.

Provider usage consumes the remaining allowance, reports Timeout before dispatch when exhausted, and cancels active usage work on expiry. Preserve explicit timeouts, target/auth selection, and real process/version/build failures.

Health and channel-status JSON can return a documented non-failing starting payload; consumers that need a complete snapshot must distinguish it and rerun after startup. No new configuration, flags, dependencies, or stored-data migration.

Validation: 592 cases across 37 files on isolated Linux Testbox; installed-package cold-start/down-Gateway matrix and unmodified 2026.9.5 updater-to-candidate Doctor proof; focused command/transport regressions and changed-file gates. The existing independent review is scoped-clean, and ClawSweeper reports no actionable correctness finding.

Thanks to @Suidge for the report and startup measurements.

Fixes #152970.
2026-09-19 23:53:35 -07:00
Daniel Peng
e0e6ce7bab
fix(openai): restore cache-TTL context pruning (#127992)
* fix(openai): enable cache-TTL context pruning

* fix(openai): gate cache ttl by resolved route

* fix(openai): include OAuth route in cache ttl

* test(agents): align cache ttl coverage with projection state
2026-09-19 22:51:15 -06:00
Ayaan Zaidi
5d1ac26041
fix(models): renew successful empty provider catalogs (#151580)
## What Problem This Solves
An empty, successful strict provider catalog recorded no expiry, so later models could remain absent from ordinary `models.list` reads.

## Why This Change Was Made
Retain validated strict empty responses in the existing cache and separate strict/advisory identities. This state has one writer: the existing response cache. The prepared inventory owner still controls renewal and publication.

## User Impact
With a positive cache lifetime, reads retain current rows while an expired provider refreshes. A later synthetic model appears without changing credentials, configuration or the selected model.

## Evidence
- [Inspected before/after advertisement and ordinary-renewal images](https://github.com/openclaw/openclaw/pull/151580#issuecomment-5726740331). Both show the candidate, not an old-baseline before/after comparison.
- Registered Gateway regression failed before the fix. On the current-main candidate, all five freshness/account-refresh cases passed (67.26s), including empty retention, prompt held-refresh reads, sibling preservation and failure recovery.
- All 27 test/profile registration checks and four-file formatting passed. Earlier 111 source cases plus published-consumer, packed-helper and installed-Gateway checks cover the unchanged cache implementation; those package checks are not a new current-main build or an official release.
- Full static checks run in required hosted CI.

## Compatibility
No public signature, schema, protocol or config change. Advisory fallback/retry, failures, raw-row reprojection, credential separation and `ttlMs: 0` stay unchanged. No migration is needed.

## Consumers
Existing strict and advisory live-catalog callers; no new export.

## Invalidation
Existing expiry capture and inventory renewal remain the only freshness path.

## Tests
`node scripts/run-vitest.mjs run src/gateway/server-methods/models-list.freshness.integration.test.ts`

Original baseline failure used `-t 'empty: true'`. Current-main checks retain the canonical publication waiter and exact row, failure and nonblocking-read assertions.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-18 14:03:07 +05:30
Peter Steinberger
59b8bf7ef6
fix(models): keep slow fleet preparation from failing startup (#151074)
Keep Gateway startup available when a large configured fleet exceeds the model-preparation publication budget. Publish only agents with complete runtime and authentication facts, continue acquisition in the background, and expose pending agents through degraded health/status until preparation completes.

Keep cancellation and cleanup with the existing model-runtime, plugin-lifecycle, and shutdown-budget owners. Bound shutdown, join registered resource disposal, and reject stale publication. Group cheap health/status reads into work slices so diagnostics remain responsive during fleet preparation. Existing installs need no configuration or migration.

Thanks to @609NFT for the 632-agent reports and startup traces. Related: #149538, #148529.

Validated by exact-head CI run 35297774278, scoped-clean committed review, Linux fleet startup/recovery observations, and mutation-tested shutdown/plugin-cleanup boundaries. The matched published-updater Doctor failure also occurs on main and remains tracked in #151295; successful updater-driven activation remains unverified. The earlier catalog-metadata assertion failure remains unclassified, with its optional baseline replay explicitly skipped in the PR body.
2026-09-17 19:43:42 -07:00
Peter Steinberger
90567a8bfd
refactor(models): preserve selected identities and centralize thinking policy (#150509)
* refactor(models): centralize selection and thinking policy

* test(models): reuse command normalization helpers
2026-09-16 20:18:50 -07:00
Peter Steinberger
b3afbe0d4d
refactor(voice): consolidate lifecycle and handoff ownership (#149865)
* refactor(voice): consolidate lifecycle and handoff ownership

Share Google and xAI lazy bridge disposal while retaining their input policies. Route Discord retained speech through its consult/playback owner and require explicit browser transcript ownership.

Keep cancellation and timeout tasks scoped to each Swift voice change, and start a fresh connection after a terminal pending attempt. Preserve voices, captions, accepted work, authority checks, and provider-specific queues.

* fix(voice): fence cancelled handoffs and simplify provider tests
2026-09-16 02:16:29 -07:00
Peter Steinberger
e360372317
fix: avoid repeated catalog refreshes after slow discovery (#146665) 2026-09-12 20:02:37 -07:00
Peter Steinberger
e7b868bbab
feat(voice): share GPT Live across meetings and calls (#146546)
* feat(voice): unify Live sessions across calls and meetings

Resolve provider capabilities and interruption policy through the shared realtime voice owner. Keep meeting input isolated from virtual-microphone output, reuse native delegation for meetings and Voice Call, and preserve explicit Stop across browser and Apple relay clients.\n\nValidated with real Live API and synthetic Chromium/WebRTC proof, focused regressions, changed-file checks, and independent review. Related to #146289.

* test(voice): align capture fixtures and validation gates

Model browser audio capture in the shared meeting RPC fixtures so startup
failure tests reach the provider and verify capture cleanup. Preserve the
same relay startup behavior while simplifying duplicate lifecycle branches.

Rebalance the pre-existing 702-root platform test graph by moving security
tests beside sandbox/tool tests; keep coverage, graph counts, and limits.

* fix(meetings): preserve configured input commands

Keep explicitly configured capture/filter/mixer output as provider input on
local Chrome and paired nodes, preserving the v2026.9.4 contract. Generated
input and output-only overrides continue to use managed browser capture.

Retain Live's isolation guard and explain how to remove an input override
when selecting Live. Prove the actual PCM paths through both meeting engines
and transports, and document the preserved configuration behavior.
2026-09-12 17:06:51 -07:00
Peter Steinberger
a0cd0b8139
fix(discord): support continuous GPT Live conversations (#146289)
* fix(discord): support continuous GPT Live conversations

Reuse the Gateway-owned GPT Live bridge for Discord voice, preserve speaker-bound agent delegation, and let Live own interruption while microphone input remains admitted during playback. Pace input continuously, play short replies, and preserve queued speech pauses. Document model-specific voice routes and unsupported host turn policies.

* fix(discord): preserve live voice admission and defaults

Keep unpinned realtime configurations on their provider default, ignore silent RTP for speaker retention, and retain live-policy freshness through roster enrichment and agent dispatch. Cover policy revocation during the real participant lookup path, fresh and existing model defaults, and idle speaker reclamation.

* test(discord): isolate delegation admission coverage

Keep the unchanged native delegation admission cases in a focused suite so the voice receive tests remain within the repository file-size limit.

* test(voice): prove delegated agent authority and cancellation

* test(discord): await continuous playback completion
2026-09-12 13:17:52 -07:00
Ayaan Zaidi
3d7d21166f
fix(models): discover account models after sign-in (#145190)
Publish static model choices after sign-in, then let the existing provider-scoped catalog owner acquire and publish account models, including late results. Keep ordinary model menus and picker opens passive, retain rows during renewal, and fence results after account changes.

Validated with registered Gateway tests, Telegram and browser flows, previous-release state and SDK contract checks, and successful CI.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-12 12:18:49 +05:30
Peter Steinberger
8b5fa242a2
fix(update): migrate retired Codex routes without losing the subscription billing path (#145305)
Retired `openai-codex/*` routes migrate to `openai/*` without switching an unpinned heartbeat or subagent from the ChatGPT subscription to the metered API key. Explicit provider auth, provider-bound profiles, and authored auth order keep precedence; the deferred model-retirement Doctor pass completes on the candidate's own receipt.

The two optional Plugin SDK fields are additive, with no version bump or new configuration option, stored shape, schema, retention, or recovery change.

Validated by failing-first authentication regressions, published-driver deferred-retirement proof, and green exact-head CI. Live inference, Gateway restart, and the optional empty-account updater variant remain unproven.

Fixes #145154

Thanks to @dbraendle for reporting the billing regression.
2026-09-11 23:37:46 -07:00
Peter Steinberger
f093c0edde
feat(openai): support GPT-Live and default Talk by account (#145382)
* feat(openai): support public GPT-Live-1 voice sessions

Adapt public Live sessions, startup events, delegation, audio, captions, and
Platform authentication through the existing OpenAI voice plugin. Keep the
Codex subscription transport separate.

Persist public WebRTC transcripts through the Gateway and drain provider
finalization before releasing voice session owners across relay, Discord,
Voice Call, and MeetingBot. Preserve synchronous bridge disposal.

Validate targeted protocol and lifecycle regressions, authenticated synthetic
voice and WebRTC flows, and the iOS Simulator app build.

Closes #145071

* fix(voice): preserve cleanup and package boundaries

Keep the OpenAI capability catalog cold, remove the delegation type cycle, and expose portable Google mock declarations. Route failed call startup through the existing binding cleanup owner and stop failed local audio processes while provider finalization drains.

* fix(ui): preserve public Live caption fragments

Carry explicit verbatim semantics through the browser transcript pipeline so split words, repeated fragments, whitespace, and overlapping speakers bypass legacy ASR heuristics. Keep Gateway-only persistence and avoid synthetic final or item events.

* test(openai): expose portable delegation mock types

Use public logger and gateway callback contracts in test helpers so declaration-enabled plugin package compilation does not reference private Vitest types. Verified declaration compilation for all six affected plugins and test callers.

* fix(openai): wait for delayed GPT-Live delegation transcripts

Retain metadata-only public delegation notices while user captions are empty, resume through the existing admission owners when text arrives, and claim notice IDs before callbacks to prevent duplicate work. Bound pending notices and missing-input waits; revoke them during close, cancellation, and transcript drain. Preserve subscription prompt fallback behavior.

Validation: 103 focused tests pass; new regressions failed against the original behavior. Independent P0-P2 autoreview is scoped-clean. Combined type/lint gates are owned by the landing checkout because declaration boundaries reject this worker worktree borrowed compiler install.

* feat(openai): select GPT-Live Talk defaults by account

Resolve unpinned Talk models with the selected agent account and session requirements. Platform credentials select public GPT-Live; ChatGPT-only accounts select the subscription voice model. Preserve explicit model pins, manual responses, video, Azure, and direct tool bridge defaults. Keep catalog discovery aligned with session creation without rewriting saved config.

Validation: 177 focused tests pass; scope and account regressions fail on the prior owners. Authenticated microphone-to-delegation-to-spoken-answer proof passes with 211200 audio bytes and awaited completed shutdown. Core and plugin production/test typechecks pass; independent review through P2 is scoped-clean. Full changed-file guards continue in the landing workflow.

* test(openai): isolate Talk account default coverage

Keep the routing suite below its existing line limit by reusing its fixtures in a focused defaults suite with injected host auth. Use explicit blocks in the live audio fixture. Production behavior is unchanged.

Validation: all 61 routing/default tests, extension test typecheck and typed lint pass; independent P0-P2 review is scoped-clean.

* refactor(openai): split realtime delegation dispatch and tests

Keep direct bridge admission and dispatch in a focused local owner, reuse the existing bridge fixture across a dedicated delayed-delegation suite, and apply the required block style. Preserve lifecycle checks, callback binding, transcript publication and subscription behavior without relaxing file budgets.

Validation: 103 focused tests pass across five files; independent P0-P2 autoreview is scoped-clean. The landing lane owns canonical typed validation of the combined candidate with physical dependencies.

* test(openai): expose portable bridge mock callback types

Use public Mock annotations tied to the realtime callback and logger contracts so exported bridge fixtures emit declarations without Vitest private Procedure types.

Validation: actual OpenAI declaration emission and extension-test typecheck pass after reproducing TS2883 before the fix; 40 helper-consumer tests pass; independent P0-P2 autoreview scoped-clean.

* fix(openai): preserve camera-capable Talk defaults

* fix(talk): align browser capabilities with launch models

Resolve optional provider and model overrides through the existing Talk catalog before browser camera negotiation. Preserve other provider rows and explicit model choices, while unpinned OpenAI Talk follows the requested GPT Live account defaults.

* fix(ui): avoid shadowing Talk provider selections
2026-09-11 20:42:25 -07:00
Vincent Koc
c41730baf0
docs: scope version-locked claims in tools and plugins pages (#143982)
Close the open accuracy findings for docs/tools/ and docs/plugins/ that
were still valid after the recent page splits.

- exec-approvals: date the non-directory-bound generated-entry migration
  to 2026.8.1 (#129636)
- exec: date the sessions.patch execSecurity/execAsk retirement to
  2026.8.1 (#132740)
- sdk-channel-inbound: date the runtime.channel.turn.* alias removal to
  2026.5.27, and state the runPreparedReply compatibility record
- sdk-channel-plugins: scope the retainNativeCatalog deprecation to
  2026.9.2 from its own @deprecated annotation
- sdk-runtime/state-and-system: date the plugin-state lease removal to
  2026.8.1 (#121140)
- sdk-runtime, sdk-runtime/gateway-and-nodes, sdk-runtime/media,
  sdk-provider-plugins/{media-and-search,runtime-hooks}: mark
  caller-owned helpers in samples as placeholders, not SDK exports
- sdk-overview/tools-and-commands: make the agentPromptGuidance sample a
  complete registerCommand call
- sdk-provider-plugins/runtime-hooks: state that
  resolveWebSocketSessionPolicy carries no compatibility-registry record
- architecture-internals/provider-hooks: give augmentModelCatalog its
  2026-10-01 removal gate from src/plugins/compat
- architecture-internals/load-pipeline: complete the activation consumer
  list with the onAgentHarnesses and onConfigPaths consumers
- hooks: replace "before the next major release" with the per-surface
  compatibility-registry contract
- manifest/surfaces: drop the undated "open" proposal status
- beam: drop "Current" from the automatic-mirror sentence
2026-09-10 19:27:32 +08:00
Ayaan Zaidi
41aec3ca92
feat(deepgram): support Flux voice-note transcription (#141836)
Closes #129452.
Replaces #129442 and preserves Safzan Pirani's original Flux contribution.

## Problem and fix

Selecting `flux-general-en` or `flux-general-multi` sends voice-note audio to Deepgram's prerecorded HTTP `/v1/listen` endpoint, which rejects Flux. This change routes those models through the streaming `/v2/listen` protocol. Operators use their existing media model settings; Nova remains the default. Flux requires `ffmpeg`.

The Deepgram plugin owns bounded audio conversion, frame pacing, protocol parsing, and transcript assembly. The shared WebSocket connector applies resolved authentication, private-network policy, proxy routing, and TLS settings. One connection deadline covers DNS preparation, proxy CONNECT, and the opening handshake; Flux keeps its original transcription-attempt deadline after preparation. Cancellation and socket closure release pending connections.

Proxy connections use the existing shared Node agent backed by `@openclaw/proxyline@0.3.12`. [Proxyline #34](https://github.com/openclaw/proxyline/pull/34), now merged, adds prepared proxy DNS/TLS connection options while preserving its existing pending-socket ownership. OpenClaw passes these through `createNodeProxyAgent(...)`; it does not retain a separate proxy-agent implementation or add `https-proxy-agent` as a direct dependency. Proxy TLS and target TLS remain separate.

Configured proxies retain resolved target-address checks before connection. Applicable managed and ambient HTTP(S) proxies retain their existing DNS delegation. `NO_PROXY` bypasses and `ALL_PROXY` alone do not disable address checks. HTTP and WebSocket paths share the managed-proxy predicate.

The query builder combines saved language and explicit query inputs before applying [Deepgram's model contract](https://developers.deepgram.com/reference/speech-to-text/listen-flux). Only `flux-general-multi` receives `language_hint`; explicit query values keep their precedence. The English-only model ignores both language inputs without requiring changes to saved settings.

The documentation follows main's split-page structure: request policy is documented on [Custom providers](https://docs.openclaw.ai/gateway/config-tools/custom-providers), and connection ownership on [Provider voice capabilities](https://docs.openclaw.ai/plugins/sdk-provider-plugins/voice-and-audio). The Deepgram manifest keeps main's categories and the Flux description. No configuration keys or storage changes are added.

## Canonical transport evidence

These results cover the retained compiled candidate with the canonical Proxyline implementation. They are not claims about a newly installed or rebuilt merge head.

- Independent public CLI acceptance saved and read back all four combinations of the two Flux models with either top-level `language: "en"` or `providerOptions.deepgram.language_hint: "en"`. Each ran `openclaw infer audio transcribe --file sample.wav --json` without model or language overrides, returned the expected “Life moves pretty fast” transcript, and exited 0. The original model configuration was restored and verified.
- A saved Nova configuration and a post-fault Flux control returned the expected transcript and exited 0.
- A configured proxy with private-network access explicitly denied produced a visible target-address rejection and exit 1, with no CONNECT admissions, no target connections or bytes, and no remaining proxy connections.
- A stalled CONNECT produced a visible transcription timeout and exit 1 without forced termination. Both admitted connections closed before the CLI exited; zero connections remained. This records two attempts, not a one-second deadline for the whole CLI invocation.
- A successful real-provider transcription through an HTTPS proxy recorded certificate verification enabled, the explicit server name, an authorized client certificate, CONNECT to the intended provider, 570,755 bytes forwarded upstream and 47,863 downstream, and complete peer cleanup. Verification mode was observed from the operator configuration; this public acceptance did not independently inject an invalid server certificate.
- Focused canonical tests passed: 136 WebSocket/HTTP address-policy tests, 3 shared Node-agent tests, and 37 Deepgram tests. Proxyline's 11 connection-control tests passed, including prepared lookup/TLS settings and Node certificate-verification defaults. The retained runtime build, formatting, lint, and documentation checks also passed.
- Proxyline's upstream review and Linux/macOS/Windows, package, and CodeQL checks passed before merge. Its published `0.3.12` package has been inspected: `src` and `dist` are byte-identical to the tested package. Registry metadata identifies release commit `46a8aa2e3c4b8fbed5fc3ccc16a4631cb7ca3d24` and includes package provenance.

## Regression evidence retained

- On baseline `8954f104fb`, the compiled public command failed with HTTP 400 `V2_MODEL_ON_V1_LISTEN_ENDPOINT` and exit 1. The repaired command returned the expected real transcript.
- Five deadline/cancellation cases failed before repair and passed afterward, including socket termination during pending proxy CONNECT. Both English-only language-input cases also failed before their query repair and passed afterward.
- Before the address-policy repair, the forbidden-target fixture received one connection and 1,600 TLS handshake bytes. After repair, it received zero connections and zero bytes. The canonical acceptance above repeats the repaired denial through the public CLI.
- Earlier broad media validation passed 354 tests across 26 files. Earlier SDK surface and import-cycle checks passed. A missing-file public CLI control produced a visible error and exit 1. These are historical coverage, not fresh merge-head results.
- An explicit `undefined` query value exposed by test-type CI was corrected by omitting absent fixture keys. The 11 Flux tests passed afterward; that correction did not change production code or live-proof inputs.

## Review and merge status

The latest ClawSweeper review of `550a7f60d612b1f19efcaec9b94112cf76338931` found no actionable code defect and requested dependency authorization, conflict resolution, and branch readiness. Its suggested direct `https-proxy-agent` addition is superseded by the canonical Proxyline repair above. The existing Proxyline dependency is updated to the published `0.3.12` release; any repository-enforced dependency approval must cover the eventual head.

The maintainer approved an exact-version release-age exception for `@openclaw/proxyline@0.3.12` through **2026-09-15 10:08 UTC**. This exception does not relax the policy for other packages or versions.

The integration preserves main's documentation moves and both manifest fields, and regenerates the config-help digest from the combined inputs. Main leaves the Flux runtime, WebSocket connection owner, and proxy helpers unchanged. Its shared HTTP capture changes require current HTTP integration evidence, including the saved Nova control.

The integrated tree passes a frozen install from the published registry, formatting, targeted lint, generated config and plugin inventory updates, and a fresh compiled CLI build. Fresh tests passed: 138 WebSocket/HTTP address-policy cases, 18 Deepgram cases, and 13 HTTP capture-release/shared-agent cases. A real saved `nova-3` CLI transcription returned the expected sample text with exit 0. The initial direct test configuration excluded the capture-release file; the canonical test router then ran all 13 cases successfully.

Relative to pinned main `412755bd5c`: production TypeScript +559 net, plugin manifest +13, tests/support +714. The growth implements the missing Flux streaming protocol, bounded conversion/transcription, and generic guarded WebSocket transport. Proxyline itself removes 15 net production lines by unifying the proxy dialers. Final exact-head review and CI remain required.

## Separate follow-ups

- The existing HTTP dispatcher maps explicit provider proxy TLS settings to the target TLS hop. The documented settings describe the proxy hop; this WebSocket implementation applies them there. The HTTP mismatch predates this change and needs its own reproduction and repair.
- The CLI's existing `--model` argument does not override an explicit media-model list. Acceptance selects Nova through saved configuration; override semantics remain a separate follow-up.
- Historical [CI run 34193953000](https://github.com/openclaw/openclaw/actions/runs/34193953000) passed test types, browser-extension end-to-end checks, and the other selected children, except [Control UI shard 3](https://github.com/openclaw/openclaw/actions/runs/34193953000/job/101957740234). Its image-handoff failure also reproduced on the exact main parent `64656c24fa` with the same 67 selected files; the standalone case passed. The mocked image scenario does not invoke Deepgram. This is historical evidence of an unrelated failure, not a result or blanket CI exception for the new head. Nine missing-video-encoder errors in that probe were excluded from the recurrence evidence. The earlier [selected-tab browser failure](https://github.com/openclaw/openclaw/actions/runs/34192553828/job/101953552659) remains a separate browser-lifecycle follow-up with its cause unproven.

AI-assisted repair.

Co-authored-by: Safzan Pirani <5602916+safzanpirani@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-10 09:45:11 +05:30
Ayaan Zaidi
0f3db48abb
feat(providers): keep registered setup connection-only (#142202)
Registered provider setup now saves a connection without pinning a copy of the built-in model catalog. Add shared connection-only presets and move the Cloudflare AI Gateway, Featherless, Hugging Face, LongCat, Meta, Mistral, NVIDIA, Synthetic, Together, regular Xiaomi, and Z.AI registrations to them.

Ordinary setup preserves authored rows, defaults, fallbacks, and aliases. Explicit replace mode keeps the existing catalog-merge and required-default rules. Published helper APIs keep their catalog-seeding behavior. Shared descriptors remove duplicate provider decisions, and generated replace rows belong to the resulting config.

The new SDK imports require a paired host/plugin release. The existing version-sync and package builder update the plugin API and peer ranges with that release. This change does not claim compatibility between the new plugin imports and an older released host.

Validation:

- 128 focused SDK and provider checks pass. Four connection-only checks fail when the mode condition is deliberately removed; the correct source is restored.
- Real registered NVIDIA setup saves seven generated rows on the pinned baseline and zero on the candidate. Authored merge/replace settings and the available default remain intact.
- The required CI artifact passes compiled Mistral setup for fresh, authored merge, and authored replace configurations. A real Gateway lists the configured default and returns one synthetic provider reply with matching requested, effective, and response models. Catalog and inference leave saved configuration unchanged.
- Required CI passed on the exact candidate. Runtime proof uses its recorded CI merge build and matching compiled host/plugin, with existing dependencies and isolated synthetic state. No real vendor request or package publication occurred.
- Independent acceptance passed all five clauses for the retained NVIDIA/Mistral observations. It did not replay runtime or claim coverage beyond those representative scenarios.

The earlier fixture failure from comparing resolved configuration with raw saved JSON remains recorded. Its completed setup was preserved and was not repeated. The artifact's unrelated standalone GoogleChat dependency-link failure is also retained; the exercised Mistral dependency path completed successfully.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-08 20:22:25 +05:30
Vincent Koc
91ea838947
docs(plugins): split the provider plugins SDK reference by reader job (#141958)
The single page was 80,247 characters (8,548 words). It is now a 16,505
character index that keeps the minimal walkthrough, plus five children
grouped by reader job:

- sdk-provider-plugins/model-catalogs.md — live model discovery contract,
  catalog helpers, pricing normalization, single-provider entry helper
- sdk-provider-plugins/hook-families.md — shared replay/stream/tool-compat
  family builders and the SDK seams behind them
- sdk-provider-plugins/runtime-hooks.md — per-hook wiring (token exchange,
  custom headers, native transport identity, usage) and the hook order table
- sdk-provider-plugins/voice-and-audio.md — speech, realtime transcription,
  realtime voice, media understanding
- sdk-provider-plugins/media-and-search.md — embeddings, image and video
  generation, web fetch and search

Anchor strategy: all 32 IDs published by the single-page version were
enumerated with parseDocsDocument, not slug approximation. 18 stay published
by the index itself (headings, Step titles and their compatibility aliases,
including the encoded `step-1%3A-package-and-manifest` and cleaned
`step-1-package-and-manifest` pairs). The 14 that moved are kept alive on the
index as authored `<a id="...">` stubs in a "Where each section moved" list
pointing at the child anchor. No stub duplicates an ID the index still
publishes, so there are no authored/canonical collisions. 32/32 pre-split IDs
resolve on the index and 14/14 stub targets resolve on their child.

Losslessness: 26/26 code fences preserved with identical info strings and
token-identical bodies (one fence was rejoined onto a single line by
scripts/format-docs.mts after the dedent, no token change); 79/79 table rows;
0 prose words lost (6,737 before, 7,430 after, +693 of index and child
scaffolding); 11 markdown links before, 41 after, the +30 being the new
navigation. The only removed line is `### Live model discovery`, promoted to
`## Live model discovery` on its child.

Also adds the five children to the plugin docs contract test, which asserted
on the parent's content and would otherwise have lost coverage of the moved
text, and registers them in docs.json navigation and the zh-CN glossary.

Closes audit findings: r3-0538
2026-09-08 15:23:11 +08:00