* docs(backup): describe manual restore flow
* docs(backup): make restore copy-back fail closed
* docs(backup): make inspect-and-stage block fail closed
Add set -euo pipefail to the first restore staging snippet so a failed
openclaw backup verify stops before mktemp/tar extraction, matching the
fail-closed copy-back block. Addresses ClawSweeper P1 on docs/cli/backup.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(backup): reconcile restore guidance with exclusions
Punchcard-Session: brisk-harbor-harbor-6w
* docs(backup): centralize archive restore guidance
Punchcard-Session: calm-cedar-workshop-by
---------
Co-authored-by: clawSean <260045960+clawSean@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* refactor(memory-core): split index manager along the inheritance chain
* fix(memory): restore the manager lazy boundary
* refactor(memory-core): delete dead manager plumbing
* chore(lint): ratchet max-lines baseline after memory manager split
* fix(memory): keep the manager out of the runtime facade
* test(memory): cover transitive manager facade imports
* fix(whatsapp): label voice transcripts as untrusted
* fix(whatsapp): preserve audio provenance in group history
Frame deferred voice transcripts while retaining their structured audio facts for later model-visible replay.
Refs #87269
---------
Signed-off-by: zhifu gao <lauragpt@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: zhifu gao <lauragpt@users.noreply.github.com>
* refactor(agents): split CLI runner concepts
* chore(lint): ratchet cli-runner max-lines baseline
* refactor(agents): remove stale runner import
* refactor(agents): preserve cleanup error typing
* refactor(agents): disambiguate CLI backend predicate
* test(agents): align code-mode pending boundary
Fixes red main: both host calls are unsettled when the guest snapshot is first parked; later waits still prove that settled calls are filtered.
* test(agents): tolerate code-mode settlement race
Fixes red main: the fast host call may settle as the guest snapshot is parked, while the deliberately slow call must remain pending.
* feat(ui): commit-scoped session diff viewer with file, sync, and view menus
The Control UI session diff panel becomes a dense Amp-style viewer: per-file
menus (copy path, open file, reveal in file tree, open in editor), a Sync
Locally popover with a copyable git fetch command, view options (collapse all,
wrapping, split/unified layout), and a sticky footer that reports how far the
branch is ahead of its merge base and switches between all changes,
uncommitted work, and individual commits.
sessions.diff gains an additive scope param (all | uncommitted | commit) plus
commits, aheadCount, and mergeBase metadata. Commit-scope diffs read only the
object database, skip untracked collection, and bypass session-start baseline
filtering; unknown commits surface a typed unavailableReason. Offscreen file
bodies use content-visibility so large diffs stay responsive.
* fix(ui): satisfy session diff CI gates
* fix(gateway): fence commit-scoped session diffs to the advertised branch history
ClawSweeper found that commit-scoped sessions.diff accepted any commit resolvable in the checkout. Fence operator.read commit reads to the advertised merge-base..HEAD history and cover sibling-branch and base-history commits.
* fix(plugins): own packed entry lifecycles by package
Persist package ownership per runtime child, route lifecycle actions through one closed resolver, reconcile removed child policy during updates, and retain rollback generations until durable config/index commit.
* fix(plugins): break uninstall policy import cycle
* test(plugins): model package ownership in lifecycle fixtures
* improve(ui): make Control UI feel native on mobile
* fix(ui): keep coarse-pointer input floor text-scale aware
* fix(ui): let self-sized controls opt out of the touch input floor
* fix(ui): fold per-control coarse-pointer font floors into the shared touch floor
* feat(gateway): projects.list groups known checkouts by repo identity
Implements docs/plan/runners.md milestone 4 derived projects read model.
* feat(ui): regroup the Where picker by gateway, devices, and cloud
* feat(ui): placement chip shows where a session runs with reclaim
Implements docs/plan/runners.md milestone 4 placement display and reclaim.
* test(ui): advertise terminal and browser panels in the mock harness
The mocked Control UI never advertised browser.request or terminal.open and
left terminalEnabled false, so the chat header's panel toggles were invisible
in the harness and could not be visually verified.
* fix(ui): give the chat pane header the only panel toggle row
The session workspace rail header rendered Terminal, Browser, Ask OpenClaw and
Changes alongside its own dock/refresh/collapse controls. Terminal and Changes
already lived in the chat pane header, so both rendered twice at once, while
Browser was reachable only from inside a files rail and Ask OpenClaw sat in a
per-session rail despite being a global surface that already owns a sidebar
entry (settings route 'custodian').
The rail header now owns workspace-file actions only. Browser moves up to the
pane header beside Terminal and into the narrow-header overflow menu; the
duplicated Terminal and Changes buttons and the Ask OpenClaw toggle are gone.
Production LOC: +21 -84.
* test(ui): stop the vite stub from shadowing the mock bootstrap config
ui/vite.config.ts registers a placeholder /control-ui-config.json middleware
and config-file plugins load before inline ones, so the mock gateway plugin's
bootstrap body never reached the app and every scenario bootstrap field was
silently dropped. Marking the mock plugin 'pre' lets it answer first.
* refactor(gateway): extract source-agnostic desktop relay core
Split the cloud-worker desktop observer into a reusable core under
src/gateway/desktop/ so upcoming desktop sources (gateway host, nodes)
plug into one relay pipeline:
- attachment.ts: RfbAttachment union (unix socket | loopback TCP)
- session-registry.ts: generic observer lifecycle (8-observer cap,
controller eviction, 60s linger, owner-epoch fencing) with typed
stale-owner/stopped errors
- observe-bridge.ts: single-use observe tokens + WS relay, path moves
to /desktop/observe (clients consume wsPath verbatim)
- rfb-view-only-filter.ts: pure move
desktop-tunnel.ts keeps only worker SSH acquisition and app launch,
plugged into the shared registry; external API unchanged. Behavior
is identical; worker desktop tests pass unmodified apart from import
and path renames. Drops the never-used now() option.
* fix(gateway): fence only superseded desktop sessions
The desktop core extraction replaced the owner fence's "stop strictly
older owners" check with an unconditional session stop. A launcher that
claims an owner epoch first, then reaches its async fencing pass after a
same-epoch observe has already created the session, tore that session
down and failed the observer with "stopped before connecting".
Restore the original invariant in the registry that owns it:
stopSuperseded() retires an entry only when its epoch is strictly lower
than the claimant's, so peers sharing a generation keep the session.
The regression test drives launch-then-observe at one epoch and fails on
the pre-fix code inside fenceReplacedOwners.
* refactor(gateway): drop unused WorkerDesktopTunnels type export
Use successful per-attachment staging results in unsupported-document guidance so sandboxed agents receive only workspace-readable paths. Failed or partial staging keeps the existing fallback.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* feat(webui): auto-request notification permission on first chat send
The Control UI now asks for notification permission automatically on the first chat message you send (once per browser/origin; web push in browsers, native prompt in the macOS app; denied permission is never re-asked automatically — Settings -> Notifications stays the manual path).
* fix(webui): preserve notification prompt gesture boundary
Restrict the one-shot notification prompt to direct non-command composer sends, invoke browser permission synchronously, and keep Settings documented as the management and recovery path.
* fix(webui): exclude deferred notification sends
Require an actively dispatching browser input event before consuming the notification one-shot, so retained catalog handoffs and other deferred sends cannot suppress the first real composer prompt.
* refactor(agents): delete dead model-selection surface, consolidate compaction target assembly
Removes production surface with zero live callers: buildConfiguredAllowlistKeys
(orphaned since fallback allowlist filtering was split out), the
retryTransientProviderRuntimeMiss resolveModelAsync option (its gateway startup
prewarming caller was replaced by prepared runtime snapshots), the ignored
useAsyncModelResolution plumbing (kept only as a deprecated no-op field on the
plugin-SDK-shipped prepareSimpleCompletionModelForAgent), and dead facade
re-exports (inferUniqueProviderFromCatalog, ThinkLevel, ModelRefStatus).
Consolidates resolveEmbeddedCompactionTarget's five hand-built result sites into
one assembleTarget helper owning the auth-profile-drop-on-provider-change rule;
resolution precedence is unchanged and now pinned by new table-driven cases
(unique-provider inference, ambiguous literal, profile-suffix preservation).
Net -93 production LOC, -106 test LOC.
* chore(sdk): regenerate plugin SDK API baseline after facade export removals
* feat(gateway): add /startupz startup probe with auth-gated version detail
Startup/traffic-admission probe that excludes downstream channel health:
200 started once startup work completes and the gateway is not draining,
503 starting/draining otherwise. Version and uptime are only included for
local-direct or authenticated callers, reusing the /readyz detail gate.
* fix(deploy): use /startupz for traffic admission in bundled templates
fly.toml gains its missing HTTP check; render.yaml stops using pure
liveness as admission; k8s pins an immutable image tag, seeds config
only when missing, and adds a startupProbe; stale Fly healthcheck-port
doc corrected (healthcheck follows the active gateway lock port since
bc4221a07e).
* docs(k8s): make persistent-file config ownership explicit with reseed path