* 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.
* refactor(qqbot): remove bundled extension source
Mechanical deletion half of the #107295 squashed rebase; the catalog
repoint and host integration land in the follow-up commit.
Co-authored-by: sliverp <870080352@qq.com>
* refactor(qqbot): install plugin from Tencent package
Squashed rebase of #107295 onto current main. Repoints the official
external channel catalog at @tencent-connect/openclaw-qqbot@2.0.1 and
adapts onboarding, doctor migrations, secrets, build guards, and tests.
Documents the known limitation that the external package does not
support structured SecretRef clientSecret values; operators move those
to QQBOT_CLIENT_SECRET or clientSecretFile before upgrading.
Co-authored-by: sliverp <870080352@qq.com>
* fix(doctor): reuse shared hasOwnKey record helper
The rebased QQBot migration carried its own hasOwnKey export, colliding
with the one main now ships in legacy-config-record-shared.ts.
Co-authored-by: sliverp <870080352@qq.com>
* fix(plugins): carry catalog integrity through the update bridge
The externalized-bundled-plugin bridge dropped the official catalog's
expectedIntegrity pin, so bundled-user updates installed the external
npm package without integrity verification. The bridge now carries the
pin for the catalog's exact npm spec and both bridge install calls pass
it through; update-channel spec overrides intentionally skip the pin
since it only covers the pinned version.
Co-authored-by: sliverp <870080352@qq.com>
* chore(plugin-sdk): refresh per-entrypoint API baselines
The QQBot compat export and bundled-type removal shift 26 entrypoint
closure hashes in the new split baseline layout.
Co-authored-by: sliverp <870080352@qq.com>
* refactor(qqbot): drop helper reintroduced during rebase
Main's coercion consolidation added this file after the deletion
commit's base; its only consumers were the removed qqbot sources.
Co-authored-by: sliverp <870080352@qq.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(ui): hide unusable models from picker
* refactor(ui): remove stale model availability helper
* refactor(ui): simplify catalog state guards
* style: format provider catalog imports
* chore: refresh plugin SDK API baseline
* refactor(core): break provider catalog type cycle
* chore(protocol): refresh models list Swift output
* chore: refresh plugin SDK API baseline after rebase
* fix(gateway): preserve full catalog preload semantics
* fix(ui): keep model status within startup budget
* fix(ui): preserve provider status within startup budget
* fix(models): scope live catalog outcomes
* test(ui): expect agent-scoped model refresh
* test(ui): align model refresh e2e fixtures
Unset heartbeat.target now resolves "owner": elected heartbeat notifications deliver to the operator's DM resolved from commands.ownerAllowFrom or the channel allowFrom (first concrete entry; wildcards and channel-scoped wildcards excluded; configured owners exhausted across channels before any channel-local fallback). Delivery requires the channel's own classifier to positively prove a direct destination — every bundled messaging plugin now ships an inferTargetChatType contract — and unproven or group-shaped destinations fail closed to the visible no-route state. The first implicitly-routed delivery carries a one-line self-explanation naming the target: "none" opt-out. Explicit target "last" remains as the follow-the-conversation opt-in. Refines the unreleased #121892 default before it ships; refs #121880.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
A final reply whose platform send was accepted but whose response was lost
previously ended in silence. Custody that stays unknown after a claimed send
now records durable pendingDeliveryNotice debt; the next same-route turn
delivers one "could not confirm delivery" notice and acknowledges it into the
transcript. Restart recovery completes ambiguous sessions with the same debt
instead of a fire-and-forget notice; the debt survives reset and rollover, and
suppressed notice sends retain it instead of faking delivery. Permanent typed
no-send rejections settle as terminal suppression (no replay, no false
notice); retryable ones restore prepared custody for safe replay. Google Chat
media-only rejections use the typed no-send contract; Telegram native-command
replies join pending-final custody.
Fixes#80362
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Unset heartbeat.target silently collapsed into the explicit "none" opt-out: heartbeats ran every 30m by default, elected notifications were dropped with only an in-memory event, and health read fine. Unset now resolves to "last" (the most recent conversation); explicit target: "none" keeps its internal-only contract. Polls skip pre-model with reason no-route while no route exists yet, and status/doctor surface the waiting-for-route state. Deliberate maintainer-owned default cutover: existing installs without a configured target start receiving heartbeat alerts in their last conversation after upgrade.
Fixes#121880
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(normalization-core): add promise-like guard
* refactor: consolidate promise-like guards
* fix(normalization-core): keep isPromiseLike non-throwing on hostile then getters
ClawSweeper finding on #121861: the diagnostics-path local guard caught throwing
then getters; the canonical guard must classify, never throw.
* test(normalization-core): annotate intentional hostile-thenable fixture
Replace divergent per-skill curation with one daily isolated collection review that can consolidate, rewrite, create, and drop writable skills atomically.
Autonomous auto mode remains automatic. Adds bounded review, transactional recovery, durable cadence, shared locking, and reuse-first /learn behavior.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
approval.ts was pushed unformatted; worker-provider-registry.ts pulled the
full OpenClawConfig graph back into the plugins layer — use the
CloudWorkersConfig leaf instead.
Keep configured engines selected before the current turn is persisted, then durably advance accepted turns across embedded and CLI paths. Preserve runtime plugin ownership and canonical transcript anchors through prepared execution.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Peter <noreply@anthropic.com>
Keep raw commands, paths, and provider errors out of ordinary chat while preserving explicit raw diagnostics and structured admin history.
Default command progress is status-only; `/verbose full` and `commandText: "raw"` retain diagnostic detail.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Completes the hasWildcard -> isPattern rename in oc-path (exports the
canonical predicate, migrates all callers, deletes the deprecated alias)
and removes the retired buildOpenAICodexProvider catalog module with its
api.ts re-export and contract-list entries. The dead-option-flag half of
this branch converged with an equivalent change that landed on main first.