Explains dispatching sessions to ephemeral cloud machines: what runs
where, cloudWorkers profile configuration (crabbox provider, setup
command, bundle vs npm install channels), sessions.dispatch usage, the
security model (closed ingress, minted credentials, host-key pinning,
epoch fencing, CAS transcripts), and troubleshooting from the live AWS
E2E.
* fix(ui): refresh new session after reconnect
* fix(ui): preserve selected agent across reconnect
* fix(ui): harden new session reconnect state
* fix(ui): block unsafe reconnect retries
* test(ui): cover reconnect branch refresh
* fix(ui): block retry after transport loss
* style(ui): preserve new session LOC ratchet
* fix(ui): clear stale branch refs on reconnect
A worker-routed turn stalls with "worker live-event acknowledgement did not
advance" because the gateway rejects the worker's first live event.
The gateway claims a run context for the turn (session identity plus
`isControlUiVisible`) before handing the turn to the remote worker. When the
worker's live events arrive, live-events `claimRun` adopts that pre-existing
context but its fresh-claim gate hardcoded `controlUiVisible = false` and
rejected any existing context whose `isControlUiVisible` differed. A visible
turn registers `isControlUiVisible: true`, so seq=1 was rejected as
`invalid-event` and the stream never advanced.
Visibility is a presentation preference the dispatch owns, not a run-identity
attribute. Inherit the existing context's `isControlUiVisible` when adopting it
and drop it from the identity gate. Session/agent/lifecycle identity must still
match, so foreign runs are still rejected and local turns (no pre-existing turn
context, or a matching one) are unaffected.
* refactor: delete dead infra and config exports
* refactor: preserve live infra and config contracts
* refactor(config): remove obsolete file-store lifecycle APIs
* refactor(infra): finish current-main dead export cleanup
* feat(ui): redesign Channels page with guided channel setup wizard
Adds wizard.start flow=channels gateway RPC (additive protocol change) that
drives the shared channel-setup wizard (openclaw channels add) over the
session step protocol. Control UI Channels page becomes a card hub with
plugin-art covers, guided per-channel setup modal (WhatsApp QR pairing via
web.login.*, BotFather/Slack/Discord helper links), and a detail overlay
hosting the full schema config form.
* test(ui): cover channel wizard controller; refresh channels view test props
* chore(mock): use non-token-shaped wizard placeholder
* test(gateway): scanner-safe auth local in channels wizard test
* fix(ui): cancel gateway wizard session on channels page disconnect
* fix(ui): adopt browse-all channel pick and guard dirty config before setup
* fix(ui): cancel gateway session created by a stale wizard.start
* fix(ui): adopt multiselect channel picks; drop redundant String()
* fix(ui): track all wizard-adopted channels so WhatsApp QR runs regardless of pick order
* feat(gateway): report configured channels on terminal channel-wizard result
Replaces the Control UI's channel-adoption heuristic with the authoritative
outcome: the channels flow reports its actual selection after config commit,
the wizard session surfaces it as an additive channels field on the terminal
wizard.next result, and the UI keys WhatsApp QR linking off that.
* refactor(ui): align channels hub with the unified settings design language
Hub becomes settings-language rows (44px art tiles, status dots, uppercase
section headings, hairline groups) instead of a card gallery; the detail
overlay hosts the migrated per-channel settings sections with Run setup in
the header. Wizard dialog and tile/cover styles move to spacing tokens.
* chore(i18n): translate channels hub/setup strings; refresh raw-copy baseline
* refactor: satisfy LOC ratchet and dead-export gates
Split mock-dev channel/plugin fixtures into their own modules, move wizard
runner types/defaults to server-methods/wizard.ts, extract the channels page
wizard host and nostr profile HTTP ops, and unexport internal-only types
(incl. the unused config-form SECTION_META barrel re-export).
* chore(i18n): retranslate channels strings on rebased locale bundles
* fix(test): drop redundant String() in channels wizard e2e
* fix: address channel-wizard review findings
- lock wizard cancellation before durable installs/config writes
- report configured channel accounts on the terminal wizard result and
start WhatsApp QR pairing for that account (web.login accountId)
- forward request options through the browser gateway client so wizard
RPC timeouts apply
- render skipped setup as a no-change completion
- keep detail/advanced config reachable for unconfigured channels
- surface the dirty-config warning inside the detail overlay
- adopt the picked channel for wizard title/links in browse-all flows
* fix(ui): local wizard RPC timeout; translate no-change completion strings
Stops the legacy run-log dual-write, auto-imports remaining rows and drops
the table at state-DB open (table-presence guard: self-heals downgrade
round-trips), keeps the newest 2000 terminal runs per job in the ledger,
retires cron.runLog config via doctor, and moves live-run cancellation to
src/cron behind the shared task control seam.
Part 2 of 2 for #106041.
* fix(deepgram): reject malformed and non-http(s) realtime base URL overrides
* fix(deepgram): accept direct ws(s):// realtime base URL overrides
Preserve released behavior: v2026.6.11 passes wss:// (and ws://) base URL
overrides straight through to the WebSocket URL builder. The prior validator
rejected every non-http(s) scheme, breaking custom Deepgram realtime proxies
that use a direct wss:// endpoint. Accept ws:/wss: alongside http(s):, keep
rejecting malformed and unrelated schemes, and preserve a direct ws(s):
override's protocol/path/port through toDeepgramRealtimeWsUrl.
* fix(deepgram): keep secure ws:// -> wss:// upgrade for realtime base URL
A prior change preserved direct ws:/wss: overrides unchanged, which also
stopped upgrading a plaintext ws:// override to wss://. That regressed the
released behavior where every accepted non-http: scheme maps to wss:, so an
existing ws:// endpoint would begin sending Deepgram realtime audio and
provider auth over an unencrypted socket. Restore the release mapping
(http: -> ws:, all others -> wss:), which still preserves a direct wss://
override and keeps the ws:// -> wss:// secure upgrade.
* fix(deepgram): preserve custom websocket transport
Co-authored-by: dwc1997 <du.wenchi@xydigit.com>
* test(deepgram): use placeholder credential fixture
* test(deepgram): clarify loopback callbacks
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
CI caught two real PR1 regressions. (1) Wiring the cron-run-logs migration
into state-db open transitively pulled agents/sandbox via the run-log error
codec, breaking ~13 tests that mock config/paths without STATE_DIR: split the
event->entry write codec into task-run-event-codec.ts and dependency-inject
the failover reason resolver so live run-log reads keep recompute while the
ledger read + migration stay dependency-light. (2) cron/service/task-runs.ts
reached into task-registry internals: route through the task-executor facade.
Known bounded limitation documented in the migration: legacy rows lacking a
stored errorReason are imported without a recomputed one (write-time authoring
is canonical going forward).