clickclack inbound mention gating: #114388 (1ca60fbc3a) made multi-agent fallback ownership explicit, so the #115484 fixture now declares its clickclack:default service-bot binding.
policy CLI: #114388 (1ca60fbc3a) removed ambient ownership from normalized multi-agent configs, so the #111087 routing-probe fixture uses the sole main agent and still reaches the intended findings exit.
Teams and Zoom node invoke policy: #118451 (7d4066639e) added trusted audio backend, buffer size, and format forwarding; assert those fields alongside the configured commands.
Teams node host: #118451 (7d4066639e) made default audio commands platform-dependent at import time after #111455 added the sox dedupe test; reset modules and mock Darwin before importing defaults.
* fix(channels): validate headless channel setup
* docs(channels): document headless provisioning
* fix(channels): repair setup metadata typing
* chore(channels): regenerate official channel catalog for env metadata
* fix(slack): keep mode-conditional env contract plugin-owned
Static --use-env declaration keeps only the unconditional SLACK_BOT_TOKEN;
socket-vs-HTTP conditional requirements (app token, signing secret) stay in
Slack's own setup validation so HTTP mode no longer demands an irrelevant
SLACK_APP_TOKEN.
* chore(sdk): regenerate api baselines and catalog after rebase
* fix(slack): align manifest env declaration with runtime contract
* chore(sdk): regenerate api baselines after rebase
* chore(sdk): regenerate api baselines after rebase
* chore(sdk): regenerate api baselines after rebase
* 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.
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>
* 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.
Why: managed discussion reconciliation must tolerate current ClickClack response shapes without persisting undefined plugin state or stale display titles.
Co-authored-by: Chisel <chisel@psiclawops.dev>
Separate durable ClickClack room identity from replaceable exact-session attachments. Session archive, reset, deletion, and recreation retain the existing room and history while rotating local routing authority and preserving fail-closed exact-incarnation grants.
Pin ambiguous room recovery to the originating account and credential, and retain prior attachments when persistence fails.
Closes#119300
Co-authored-by: Chisel <chisel@psiclawops.dev>
* fix(clickclack): answer thread replies past the server thread-reply window
Thread replies stopped being answered once a ClickClack thread passed 100
replies, with no reply, no transcript entry, no gateway warning, and no
replay after reconnect.
The gateway resolved a thread.reply_created event by fetching the whole
thread and searching its reply list. The ClickClack thread endpoint caps
that response at 100 earliest replies by default, so reply 101 was absent,
the lookup returned null, and the event was treated as handled while the
websocket cursor advanced past it. A DM thread reply was worse: it hit the
DM branch first, and thread replies never appear in the root DM timeline
that branch pages, so every DM thread reply was dropped.
Resolve the event's own message_id through the authoritative single-message
endpoint the plugin already uses, which collapses the three window
heuristics into one canonical path. Transient failures still propagate so
reconnect replays them; only a 404 resolves to unreadable, and that now
records an operator warning instead of counting as a handled event.
* test(clickclack): harden reply recovery proof
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* feat(plugins): deliver sessions.changed to plugin services
* docs: regenerate docs map
* refactor(clickclack): split reconcile scheduler and history formatting out of the discussion service
* style: format discussion service