* 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.
* 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.
* feat(tools): per-model code-mode capability flags and auto master-switch tier
* feat(anthropic): flag claude-sonnet-4-6 as code-mode preferred
* revert(anthropic): drop claude-sonnet-4-6 from the code-mode preferred set
* test(anthropic): type the manifest compat field in the catalog contract test
* fix(anthropic): carry catalog compat onto hand-built forward-compat model rows
Request shaping is selected by version predicates in llm-core, so a Claude id
from a generation newer than anything the plugin encodes fell through to pre-4.6
shaping: manual budget_tokens thinking plus caller sampling parameters, both of
which current models reject.
Resolve such an id through the plugin's existing forward-compat path and stamp
params.canonicalModelId, the same seam Bedrock and Mantle use to map a
provider-native id onto a canonical Claude contract. Shared contracts are
untouched, so Claude served through third-party providers keeps its own
shaping.
* fix(models): authenticate Anthropic discovery with OAuth tokens
Live model discovery sent every credential as x-api-key. Claude subscription
credentials are OAuth access tokens, which Anthropic rejects in that header, so
discovery 401d and silently fell back to the shipped catalog.
Select the auth header from the detected credential shape: API keys keep
x-api-key, OAuth tokens use Authorization: Bearer. The two are mutually
exclusive because Anthropic rejects requests carrying both.
* fix(models): keep shipped Anthropic models when discovery succeeds
Live discovery replaces the seed catalog with the /v1/models response. Anthropic
does not publish every model it serves, so a shipped entry with no live row was
dropped from the provider listing once discovery started working.
Re-add manifest models the live response omitted; discovered rows still win on
shared ids.
* feat(plugins): support additional upstream session kinds
* feat(acpx): monitor upstream Pi sessions
* feat(opencode): monitor upstream OpenCode sessions
* fix(opencode): ignore hidden user text activity
* refactor(plugins): share upstream echo filtering
* fix(opencode): preserve marker tuple narrowing
* refactor(opencode): detect upstream turns via event_sequence cursor
Replace timestamp/grace-window completeness heuristics with OpenCode's own
per-session event_sequence.seq cursor, which advances inside the same
transaction as the message/part projections. Detects change instead of
inferring completion, so staged part writes can no longer drop a turn.
Handles cursor regression from OpenCode migrations that clear event_sequence,
suppresses compaction replay and summary re-publish, and restricts human-turn
classification to visible non-synthetic text.
* docs(plugins): record why the adoption coordinator requires complete
* fix(opencode): align cursor proof with strict types
* fix(acpx): keep Pi JSON parser private
* feat(anthropic): gate live model discovery on contract coverage
Live catalog discovery cloned a template for any newly discovered Claude id, so
a future model generation would be selectable while request shaping treated it
as pre-4.6 and 400d. Add an opt-in acceptUnknownModel gate to the shared live
catalog seam and have the Anthropic plugin accept a discovered model only when
Anthropic's advertised capabilities agree with the contracts we would apply.
Manifest-published ids bypass the gate and keep their metadata.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(plugins): document the live-discovery admission hook
Record acceptUnknownModel in the provider-plugin SDK reference: when to use
it, that manifest-published ids bypass it, and the fail-closed guidance.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Main already resolves bare opus to Claude Opus 5 but documents nothing about
the rolling behavior. State that bare family aliases follow the current
generation and that pinning is the opt-out, and add regression coverage
separating bare aliases, pinned aliases, and retired-ref upgrades.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(anthropic): detect Claude CLI routes pinned to non-default models
Route detection probed one hardcoded model id, so moving the packaged CLI
default stopped advertising session creation for configs routing an older
Claude model, and adoption stamped the packaged default onto sessions the
operator never routed. Probe the seeded CLI model ids instead and reuse the
routed model for both paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(anthropic): keep the canonical CLI model id module-local
The route probe replaced its only external consumer, so the export is dead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(models): add Claude Opus 5 support
* test(models): align Opus 5 setup fixtures
* test(models): update ambient Opus 5 detection
* fix(models): reconcile Opus 5 support with main
* feat(anthropic): add Claude Opus 5 model support
Wire claude-opus-5 through the Claude 5 contract seams: adaptive-by-default
thinking with the full low..max effort range, default-sampling and prefill
stripping, streaming refusal contract, model-bound thinking replay, and 1M/128k
catalog metadata across anthropic, claude-cli, Vertex, Bedrock, and Mantle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(anthropic-vertex): normalize explicit Vertex Opus 5 model rows
Extend normalizeAnthropicVertexResolvedModel so user-configured Vertex Opus 5
rows regain reasoning, image input, 1M/128k limits, and the native thinking
map; update the live-model priority expectation and regenerate docs_map.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>