* feat(ui): add a Memory settings page with Dreaming as a tab
Memory config was scattered across five surfaces: the memory.* schema section
lived on AI & Agents with 43 of 51 keys behind the Advanced tier, the memory
slot owner was only visible on Plugins, dreaming's knobs were JSON-only, its
status UI sat under Agents, and Memory Import was a separate route.
/settings/memory now owns that surface, following the MCP page shape (curated
rows above an embedded schema editor):
- Overview: the exclusive memory slot rendered as a segmented control over
installed memory-kind plugins, memory.backend promoted out of Advanced with
the qmd sub-config revealed only when qmd is selected, additive add-on rows,
and a Memory Import link.
- Search: the memory.search surface via the embedded editor.
- Dreaming: the global frequency/model/timezone/storage/phase knobs, which
previously required hand-editing openclaw.json, plus an agent picker feeding
the existing dream scene/diary/advanced panel for the agent-scoped reads.
Engine selection calls plugins.setEnabled so the gateway's exclusive slot
policy stays the single owner instead of being duplicated in the UI.
* fix(ui): redirect stale ai-agents memory deep links to the memory page
* fix(ui): report memory runtime defaults on the Memory page
The Dreaming tab rendered its own defaults instead of the ones
resolveMemoryDreamingConfig applies, so a config carrying only
dreaming.enabled showed all three phases off while they were running, and
an unset storage mode read as inline instead of separate. Toggle specs now
carry the runtime fallback and the storage default is stated once, both
pointing at src/memory-host-sdk/dreaming.ts.
Three more surfaces asserted things the runtime does not do:
- plugins.slots.memory "none" is the explicit-off sentinel, not an engine
id, so the segmented control selected nothing. The slot now resolves to a
closed auto/off/pinned selection with its own hint.
- memory.backend is resolved by the memory runtime the slot owner
registers, which only memory-core ships, so the row is hidden for any
other engine instead of saving a value nothing reads.
- The Dreaming tab wrote config.dreaming for whichever plugin owns the
slot even when that plugin's schema cannot hold it. It now reuses the
enablement flow's schema check (resolveDreamingConfigPathSupport, shared
with updateDreamingEnabled) and renders an unsupported state instead.
Also key the plugin-catalog sync on the connected phase: the connecting ->
connected transition keeps the same client object, so a page mounted
during the handshake never loaded the catalog and never showed the engine
picker.
The tab keeps the autosave status line and restart banner the embedded
editor renders on the other tabs; these knobs autosave, but nothing
reported it. The pure view moved to memory-dreaming.ts with the element in
memory-dreaming-page.ts, matching memory.ts/memory-page.ts.
* fix(ui): resolve the memory slot through the canonical policy
The Memory page re-derived plugins.slots.memory instead of using the rule the
runtime applies, which broke both directions of the engine control:
- An unset slot was reported as "the first enabled memory-kind plugin in the
catalog". The runtime resolves it to the slot's default owner
(DEFAULT_SLOT_BY_KEY.memory), so the page could show one engine as active
while another was loaded, reveal or hide the backend row for the wrong
plugin, and target the wrong plugin when switching memory off.
- Off called plugins.setEnabled(false), which writes enablement only. The slot
stayed pinned, so the choice did not survive a refresh and re-enabling that
plugin from the Plugins page silently switched memory back on.
resolveSlotSelection now lives next to defaultSlotIdForKey in
src/plugins/slots.ts and owns the rule once; config normalization consumes it
and the page imports it instead of restating it. Off writes the explicit "none"
sentinel through the config form, so it round-trips; picking an engine still
goes through plugins.setEnabled, which is where the exclusive slot policy
lives. The dreaming controller's own copy of the rule is gone too.
Four smaller fixes on the same surface:
- A failed engine change is reported next to the control instead of being
swallowed, so the selector no longer just snaps back.
- Dreaming's numeric inputs carry the memory-core manifest's integer/min/max
bounds and refuse out-of-range edits at the field, rather than patching a
value autosave then fails to write.
- Settings search destinations carry the Memory tab that renders the matched
child, so a memory.search hit no longer lands on Overview, whose narrowed
editor omits it.
- The Dreaming tab caches only a definitive schema-capability answer. An
offline or failed lookup now reports "unknown" and is retried on reconnect
instead of permanently suppressing the recheck.
* fix(ui): model unknown memory state instead of collapsing it
The Memory page reported unknowns as decided values. An empty catalog meant
loading, disconnected, or a failed plugins.list, yet add-on rows rendered
"Disabled"; catalog completions were keyed on client identity, which survives a
phase flip, so a stale load could repopulate a disconnected page or overwrite a
newer read; and `?tab=` was adopted once per distinct value, so a repeat
navigation to a tab the user had left was ignored.
Replace the ad-hoc nullable fields with closed shapes. MemoryCatalog is a
loading/unavailable/ready union, so absence of an entry only decides anything
inside `ready`, and MemoryAddonRow carries a four-state enablement the view
renders without ever inventing an "off". CatalogConnection is one object per
(client, connected) transition and doubles as the request generation an
in-flight load carries, so obsolete completions are dropped by identity. The tab
is no longer page state at all: the URL owns it, tab clicks navigate, and every
arrival is honored.
Settings search now resolves the engine/backend through the same
resolveMemoryBackend the page uses and matches only the `memory.*` children the
page can surface, so a `memory.qmd` hit under the built-in backend no longer
routes to an Overview whose editor omits it.
* fix(ui): surface a disabled memory owner and anchor curated backend search
The slot and plugin enablement are independent config surfaces, so
`plugins.slots.memory` can name a plugin the catalog reports as disabled.
The engine control showed that plugin as selected, and because re-picking an
already-selected radio fires no change event, there was no way back on. Add an
explicit enable row for that state and let the same-id write through when the
owner is not running; picking Off stays a no-op.
`memory.backend` is curated out of the schema editor, so the generic
`#config-section-memory` anchor scrolled past it. Fold the memory tab and hash
choice into one `memoryDestination` owner that routes a curated-only match to
the new anchor above the editor.
* fix(ui): scope the dreaming capability probe to its connection
The probe was deduplicated by plugin id alone, which cannot tell a current
answer from a stale one. A disconnect and reconnect on the same slot owner left
the token armed, so the reconnect read as "already in flight" and swallowed the
retry that an `unknown` answer requires — leaving an unsupported engine's knobs
editable until some unrelated config notification arrived. An A -> B -> A switch
had the mirror problem: the old A response was accepted for the new A probe.
Make the in-flight probe an object whose identity is the generation, drop it
whenever the owner or the connection changes, and accept only the completion
that still owns the slot. Same shape as the catalog guard on the Memory page.
* fix(ui): satisfy the lint and dead-export gates on the memory page
Exhaustive switches need a terminal `default:` to satisfy
typescript/consistent-return, matching the existing view-status.ts shape.
Seven symbols were exported with no production consumer outside their own
module, which the hard-zero Knip production scan rejects. Tests alone do not
make internals contracts, so drop the exports and reach the behavior through
each module's public surface instead: the view props type comes from
`Parameters<typeof renderMemory>`, the tab panel is found by its ARIA role, and
the dreaming number/storage helpers are proven through `renderDreamingSettings`.
Folding those helper unit tests into the render path also corrected one of them:
a `type="number"` input coerces unparseable text to empty, so the "reject
garbage" case was unreachable through the real control. Replaced with the
inclusive-bound and clear-the-field cases, which are reachable.
* refactor(ui): keep the memory schema facts out of the startup bundle
Settings pages are already lazy — the config route is `import("./config-page.ts")`
— but settings search runs from app-host at startup, and it needed the same
answers about which `memory.*` children are reachable and where a match lives.
Importing those from the view module dragged lit, hub-tabs, and settings-ui into
the startup chunk with it, blowing the Control UI startup budget.
Move the rendering-free facts (slot/backend resolution, tab and curated key
lists, schema narrowing, the anchor id) into memory-schema.ts, which imports
only record-coerce and the shared slot policy. The view keeps the templates and
now consumes the same module, so there is still one owner per fact.
* chore(ui): record the memory settings surface in the startup budget baseline
Routing settings search through memory-schema.ts instead of the view module
recovered 10,872 B of the startup chunk (334,992 -> 324,120 B), which is back
under the 324,608 B ceiling. The remaining 2,795 B over the old baseline is the
honest cost of the new surface: its i18n strings, plus the slot/backend facts
the startup search index has to read.
Measured by hosted CI (run 30189972795); this worktree cannot build locally
because pnpm wants to purge a node_modules shared with other running agents.
🦞 OpenClaw — Personal AI Assistant
OpenClaw is a personal AI assistant that learns and grows with you, running on your own devices — developed in the open by the OpenClaw Foundation, a non-profit. It answers you on the channels you already use, can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant.
If you want a personal, single-user assistant that feels local, fast, and always-on, this is it.
Supported channels: WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, SMS (Twilio), IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, ClickClack, Raft, Reef, QQ, and the built-in WebChat.
Website · Docs · Getting Started · Onboarding · Updating · Showcase · FAQ · Vision · DeepWiki · Docker · Nix · Third-party notices · Discord
Sponsors
|
|
|
|
|
|
|
Install
Runtime: Node 24.15+ (recommended), Node 22.22.3+, or Node 25.9+.
# macOS / Linux
curl -fsSL https://openclaw.ai/install.sh | bash
# Windows (PowerShell)
iwr -useb https://openclaw.ai/install.ps1 | iex
Or install via a package manager (npm, pnpm, or bun all work):
npm install -g openclaw@latest
Then run onboarding:
openclaw onboard --install-daemon
OpenClaw Onboard guides you step by step through setting up the gateway, workspace, channels, and skills on macOS, Linux, and Windows, and installs the Gateway daemon (launchd/systemd user service/Scheduled Task) so it stays running. Windows desktop users can also start with the native Windows Hub companion app for setup, tray status, chat, node mode, and local MCP mode.
Full beginner guide (auth, pairing, channels): Getting started.
Quick start (TL;DR)
After onboarding, the Gateway runs as a daemon:
openclaw gateway status # expect: running on port 18789
openclaw dashboard # open the Control UI
Send a test message or talk to the assistant:
# Send a message
openclaw message send --target +1234567890 --message "Hello from OpenClaw"
# Talk to the assistant (optionally deliver the reply to any connected channel)
openclaw agent --message "Ship checklist" --thinking high
Foreground/debug mode:
openclaw gateway stop
openclaw gateway --port 18789 --verbose
Upgrading? Run openclaw update — see the Updating guide — then openclaw doctor.
Models
- Bring the provider you already use: Anthropic, OpenAI, Google (Gemini), xAI (Grok), OpenRouter, GitHub Copilot, MiniMax, and any OpenAI- or Anthropic-compatible endpoint. Details: Model providers.
- Sign in with a subscription (OAuth) instead of an API key: Anthropic (Claude Pro/Max), OpenAI (ChatGPT/Codex), and GitHub Copilot.
- Model note: prefer a current flagship model from the provider you trust and already use. See Onboarding.
- Models config + CLI: Models. Auth profile rotation + fallbacks: Model failover.
Security defaults (DM access)
OpenClaw connects to real messaging surfaces. Treat inbound DMs as untrusted input.
Full security guide: Security. Before remote exposure, use the Gateway exposure runbook.
Default behavior on DM-capable channels (Telegram/WhatsApp/Signal/iMessage/Microsoft Teams/Discord/Google Chat/Slack/…):
- DM pairing (
dmPolicy: "pairing", e.g.channels.discord.dmPolicy): unknown senders receive a short pairing code and the bot does not process their message. - Approve with:
openclaw pairing approve <channel> <code>(then the sender is added to a local allowlist store). - Public inbound DMs require an explicit opt-in: set
dmPolicy: "open"and include"*"in the channel allowlist (allowFrom, e.g.channels.discord.allowFrom).
Run openclaw doctor to surface risky/misconfigured DM policies.
Sandboxing (groups + multi-user surfaces)
- Default: tools run on the host for the
mainsession, so the agent has full access when it is just you. - Group/channel safety: set
agents.defaults.sandbox.mode: "non-main"to run non-mainsessions inside sandboxes. Docker is the default sandbox backend; SSH and OpenShell backends are also available. - Typical sandbox default: allow
bash,process,read,write,edit, and session tools; denybrowser,canvas,nodes,cron,gateway, and channel actions. - Before exposing anything remotely, read Security, the Gateway exposure runbook, Sandboxing, and Configuration.
Highlights
- Local-first Gateway — single control plane for sessions, channels, tools, and events.
- Multi-channel inbox — 25+ channels through bundled plugins (see the list above), plus macOS, iOS, and Android nodes.
- Multi-agent routing — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
- Voice Wake + Talk Mode — wake words on macOS/iOS and continuous voice on Android (ElevenLabs + system TTS fallback).
- Live Canvas — agent-driven visual workspace with A2UI.
- First-class tools — browser, canvas, nodes, cron, sessions, and Discord/Slack actions.
- Companion apps — Windows Hub, macOS menu bar app, and iOS/Android nodes.
- Onboarding + skills — onboarding-driven setup with bundled/managed/workspace skills.
Operator quick refs
- Chat commands:
/status,/new,/reset,/compact,/think <level>,/verbose on|off|full,/trace on|off|raw,/usage off|tokens|full|cost,/restart,/activation mention|always - Session tools:
sessions_list,sessions_history,sessions_send - Skills registry: ClawHub
- Architecture overview: Architecture
Docs by goal
- New here: Getting started, Onboarding, Updating
- Channel setup: Channels index, WhatsApp, Telegram, Discord, Slack
- Apps + nodes: Windows Hub, macOS, iOS, Android, Nodes
- Config + security: Configuration, Security, Exposure runbook, Sandboxing
- Remote + web: Gateway, Remote access, Tailscale, Web surfaces
- Tools + automation: Tools, Skills, Cron jobs, Webhooks, Gmail Pub/Sub
- Internals: Architecture, Agent, Session model, Gateway protocol
- Troubleshooting: Channel troubleshooting, Logging, Docs home
Apps (optional)
The Gateway alone delivers a great experience. All apps are optional and add extra features.
If you plan to build/run companion apps, follow the platform runbooks below.
macOS (OpenClaw.app) (optional)
- Menu bar control for the Gateway and health.
- Voice Wake + push-to-talk overlay.
- WebChat + debug tools.
- Remote gateway control over SSH.
Note: signed builds required for macOS permissions to stick across rebuilds (see macOS Permissions).
iOS node (optional)
- Pairs as a node over the Gateway WebSocket (device pairing).
- Voice trigger forwarding + Canvas surface.
- Controlled via
openclaw nodes ….
Runbook: iOS connect.
Android node (optional)
- Pairs as a WS node via device pairing (
openclaw devices ...). - Exposes Connect/Chat/Voice tabs plus Canvas, Camera, Screen capture, and Android device command families.
- Runbook: Android connect.
From source (development)
Use pnpm for source checkouts. The repository is a pnpm workspace, and bundled
plugins load from extensions/* during development so their package-local
dependencies and your edits are used directly. Plain npm install at the repo
root is not a supported source setup.
For the dev loop:
git clone https://github.com/openclaw/openclaw.git
cd openclaw
pnpm install
# First run only (or after resetting local OpenClaw config/workspace)
pnpm openclaw setup
# Optional: prebuild Control UI before first startup
pnpm ui:build
# Dev loop (auto-reload on source/config changes)
pnpm gateway:watch
If you need a built dist/ from the checkout (for Node, packaging, or release validation), run:
pnpm build
pnpm ui:build
pnpm openclaw setup writes the local config/workspace needed for pnpm gateway:watch. It is safe to re-run, but you normally only need it on first setup or after resetting local state. pnpm gateway:watch hands the configured Gateway port from the installed service to a durable tmux pane; run pnpm openclaw gateway start when you want the installed service back. It does not rebuild dist/control-ui, so rerun pnpm ui:build after ui/ changes or use pnpm ui:dev when iterating on the Control UI. If you want this checkout to run onboarding directly, use pnpm openclaw onboard --install-daemon.
Note: pnpm openclaw ... runs TypeScript directly (via tsx). pnpm build produces dist/ for running via Node / the packaged openclaw binary, while pnpm gateway:watch rebuilds the runtime on demand during the dev loop.
Release channels
- stable: tagged releases (
vYYYY.M.PATCH—PATCHis a sequential release number, not the calendar day), npm dist-taglatest. - extended-stable: the trailing supported month's maintenance releases, npm dist-tag
extended-stable. - beta: prerelease tags (
vYYYY.M.PATCH-beta.N), npm dist-tagbeta(macOS app may be missing). - dev: moving head of
main, npm dist-tagdev(when published).
Switch channels (git + npm): openclaw update --channel stable|extended-stable|beta|dev.
Details: Release channels.
Agent workspace + skills
- Workspace root:
~/.openclaw/workspace(configurable viaagents.defaults.workspace). - Injected prompt files:
AGENTS.md,SOUL.md,TOOLS.md. - Skills:
~/.openclaw/workspace/skills/<skill>/SKILL.md.
Configuration
Minimal ~/.openclaw/openclaw.json (model + defaults):
{
agents: {
defaults: {
model: "<provider>/<model-id>",
},
},
}
Full configuration reference (all keys + examples).
Star History
Molty
OpenClaw was built for Molty, a space lobster AI assistant, by Peter Steinberger and the community. 🦞
Community
See CONTRIBUTING.md for guidelines, maintainers, and how to submit PRs. Use the issue chooser for bugs, docs bugs, and feature requests; ask setup/support questions in Discord; and report vulnerabilities through SECURITY.md. Most new features fit best as plugins built on the plugin SDK and shared via ClawHub, keeping core lean. PRs should link the relevant issue when possible and follow the PR template with problem, impact, and evidence. AI/vibe-coded PRs welcome! 🤖
Special thanks to Mario Zechner for his support and for pi. Special thanks to Adam Doppelt for the lobster.bot domain.
Thanks to all clawtributors:
