* feat(config): add canonical group-policy scope-tree resolver
* chore(plugin-sdk): refresh API baseline hash for scope-tree exports
* refactor(channels): migrate googlechat, imessage, and whatsapp group policy onto the scope tree
* chore(plugin-sdk): refresh API baseline hash for groups scope-tree builder
* refactor(channels): migrate line, qqbot, and mattermost group policy onto the scope tree
* chore(plugin-sdk): refresh API baseline hash for case-insensitive scope key helper
* chore(plugin-sdk): refresh API baseline hash after rebase onto current main
* chore(plugin-sdk): refresh API baseline hash after rebase
* chore(plugin-sdk): refresh API baseline hash after rebase
* chore(plugin-sdk): refresh API baseline hash after rebase
* refactor(channels): consolidate the flat streaming fallback into the deprecation module
* chore(plugin-sdk): refresh API baseline hash for compat module move
* chore: re-attach CI to the rescoped head
* chore(plugin-sdk): refresh API baseline hash after rebase
* chore(plugin-sdk): refresh API baseline hash after rebase
Adds a non-interactive command surface (openclaw reef register|status|friend code|request|list|remove) so an agent can claim a handle and manage guarded friendships when its owner asks, without the interactive wizard. --json emits machine-readable output for automation. Registration is crash-safe and idempotent: guard defaults are provider-coupled, the full config is validated before the single-use magic-link token is consumed, a duplicate-handle claim recovers by proving key ownership and reconciling the relay's request policy, the exchanged session is cached scoped to relay+email (never printed) so reruns recover, one state dir is pinned to one identity, and friend removal revokes both the pin and the allowlist entry. Manifest gains commandAliases/onCommands so the CLI resolves; docs cover the agent-driven path.
* feat(web): show session branch row for local changed files
The pre-PR branch row above the composer only appeared once the branch
was pushed and ahead of the default branch. Sessions with local work
(committed, uncommitted, or untracked changes) showed nothing.
The gateway now emits the branch payload whenever the working tree has
changed files vs the default-branch merge base; createUrl is withheld
until GitHub's pull/new page has something to compare, and the UI only
renders the Create PR link when it is present.
* refactor(gateway): keep control-ui-session-prs under LOC ratchet
* docs(gateway): note explicit-base diff counts conflict paths
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.
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>
Both settings grant local UI automation, so run PeekabooBridge only while
Allow Computer Control is on. The toggle now reflects the effective state and
is disabled when Computer Control is off; users drive Peekaboo via its own Mac
app in that case. Removes the confusing standalone bridge switch.
* feat(gateway-protocol): add session placement schema
Closed state discriminator for session execution placement (local/requested/provisioning/syncing/starting/active/draining/reconciling/reclaimed/failed), sessions.dispatch params, and worker-admission transcript/live cursor extensions. Swift protocol models mirror the schema.
* feat(state): add worker session placement table
worker_session_placements rows carry placement state, transition generation, worker ownership metadata, ACK cursors, and the turn claim columns used for atomic admission.
* feat(cloud-workers): add durable placement state machine store
SQLite-backed placement store split by concern: state table (placement-state), discriminated record types + shape invariants (placement-record), row codec + CAS transition values (placement-row-codec), atomic turn-claim admission/release/waiters (placement-turn-claims), and lifecycle CAS transitions (placement-store).
* feat(cloud-workers): sync workspaces and attach sessions to worker environments
Environment service session attachment + turn credentials, tunnel workspace commands over a dedicated SSH runner, and git/plain workspace sync into $HOME/.openclaw-worker/workspaces with an immutable manifest. Symlink escapes are rejected locally before transfer (macOS openrsync stat-fails them opaquely) and again by the remote manifest guard.
* feat(worker): run one-shot embedded turns from launch descriptors
Worker runtime executes a single embedded turn from a stdin launch descriptor and reports completed/failed/fenced on stdout for the gateway launcher. Terminal lifecycle live events are deferred past the final transcript flush; transcript projection helpers are shared via transcript-message instead of duplicated in the runtime.
* feat(cloud-workers): dispatch placements and route worker turns
Dispatch service drives local->requested->provisioning->syncing->starting->active with failure teardown (placement-dispatch-failure) and restart/runtime recovery incl. lost-worker reclaim (placement-dispatch-recovery). Worker turn launcher claims the placement turn atomically, builds a windowed launch descriptor (worker-turn-payload), runs the remote one-shot worker, and reconciles the committed transcript; agent runners route turns through the session placement admission provider.
* feat(gateway): expose session placement RPCs and startup reconciliation
sessions.dispatch RPC with lifecycle admission barriers, operator-facing placement projection on session listings, placement-aware session reset guard, and startup/interval reconciliation wiring for worker placements.
* ci: enforce changed-file TypeScript LOC ratchet
* ci: derive release-gate LOC base from PR
* test(ci): exclude repository test helpers from LOC ratchet
* ci: validate LOC ratchet on PR merge tree
* style: format release maintainer skill
* ci: run LOC ratchet for fast-only changes
* fix(ci): harden LOC ratchet comparisons
* fix(ci): cover native TypeScript in LOC ratchet
* fix(ci): compare LOC against tested merge tree
* test(ci): cover LOC manifest routing
Config surface worktrees.cleanup.{maxCount,maxTotalSizeGb} (0/unset disables),
enforced by hourly gc, worktrees.gc RPC, and openclaw worktrees gc: least
recently active session/Workboard worktrees are snapshotted and removed until
count and total size fit. Manual worktrees, live locks, run leases, and active
session owners are never limit-evicted. Settings -> Worktrees gains a Cleanup
section with debounced, quota-aware stepper controls. Closes#106213.
* feat(ui): move Devices page into settings and redesign device inventory
- Devices now lives at /settings/devices (System group); /nodes stays as alias
- per-device form-factor icons (phone/browser/terminal/machine) with status dot
- equal-height header actions; stale cleanup and pairing only
- drop manual Refresh button; page already auto-refreshes via presence events + 30s poll
- dynamic connected/pending summary replaces static card subtitle
* style(ui): oxfmt pass + loading empty state for devices inventory
* chore(ui): sync locale bundles for devices settings copy; docs point at Settings → Devices
* refactor(ui): split device tile + pending rows out of view-inventory; drop unused icons
Keeps the TypeScript LOC ratchet green: view-inventory.ts returns below its
baseline and icons.ts sheds unused folderOpen/micOff/volumeOff glyphs.
* fix(ui): ratchet LOC baseline, drop unused DeviceIconSource export, settle devices settings copy
- baseline: icons.ts 693, view-inventory.ts back under the 500 ceiling
- subtitles.nodes now describes the settings page; nav tests updated
- locale bundles resynced (fallbacks=0)
* refactor(ui): rename token row param for reviewer-tool clarity
* fix(ui): collapse device rows in narrow containers and classify the TUI as a terminal client
Review findings: the nodes-entry grid override beat the shared 560px
list-item collapse, overflowing phone-width rows; openclaw-tui connects
with mode ui so only its client id marks it as a terminal.
* chore(ui): translate pending locale keys after rebase (fallbacks=0)