* feat(system-agent): hatch into the agent after a fresh setup applies
The hatch is a ceremony: a fresh-install setup apply now announces the hatch
and hands straight off (open-tui in the terminal, open-agent for gateway
clients); the web custodian page lands in agent chat with the birth-sequence
opener prefilled as a draft so one Send wakes the seeded BOOTSTRAP. Non-setup
persistent operations keep their in-place replies.
* fix(system-agent): hatch only on clean post-write verification
* fix(ui): scope hatch draft to setup handoffs
* fix(onboarding): seed hatch handoffs across clients
* fix(i18n): refresh native hatch inventory
* fix(onboarding): seed remote gateway hatch handoff
* fix(macos): localize hatch draft
* fix(onboarding): gate hatch on pending bootstrap
* fix(i18n): sync hatch source to ios catalog
* fix(onboarding): gate hatch on canonical pending state
* refactor(ui): restructure Settings IA around user-facing pages
Settings sidebar shrinks to settings-only destinations grouped by attention:
General/Appearance/Notifications up top, Connections, Agents & Tools,
Privacy & Security, System. Sessions, Worktrees, Activity, and Memory
Import leave the takeover: Sessions/Activity return to the workspace
sidebar, Worktrees becomes a Sessions hub tab, Memory Import is reached
from the Agents memory panel. Model Setup becomes a Models subpage.
New config pages: Notifications (web push), Privacy & Security (curated
policy rows + security/approvals schema sections), Advanced (uncurated
schema sections + raw editor, replacing the General quick/advanced
toggle). General slims to model defaults, language, and gateway host;
identity moves to Profile, lobster appearance rows to Appearance.
* feat(agents): widen system-agent config writes to operator parity
Every config surface the Control UI can edit is now agent-writable behind
the exact-operation human approval gate, except a small documented
denylist ($include, auth, env, models, secrets). tools.* and
plugins.entries.* writes are allowed; plugin_uninstall works for plugins
that provably do not back the active default inference route (fail closed
otherwise); set_default_model takes an optional agentId and live-tests a
per-agent model with the same rigor as the default route. Per-agent
routing fields on non-default agents are direct-writable; the default
agent's route keeps the verified set_default_model/onboard paths. A new
config-write-parity contract test forces explicit classification of every
top-level config key.
* feat(ui): sync operator display prefs through config ui.prefs
Theme, theme mode, text scale, locale, and chat display prefs get a
canonical server-side home in config ui.prefs. The Control UI mirrors
them into localStorage for instant boot, writes local changes through to
the gateway (best-effort; viewer-scope or offline clients stay
device-local), and applies server-side deltas on connect and on every
config snapshot reload — so an agent changing the theme through the
approval gate takes effect in the UI, and prefs follow the operator
across devices.
* chore: prune quick.ts from max-lines baseline after slim-down
* style: oxfmt changed files
* chore: regenerate plugin-sdk api baseline for ui.prefs config surface
* fix: dedupe runPluginUninstall dep type and narrow locale extraction
* docs: update Control UI settings structure and ui.prefs sync
* fix: address autoreview findings on parity guards and prefs sync
- plugins.entries.* config writes get the same active-route ownership
check as plugin_uninstall (fail closed when unprovable)
- system-agent prompt now describes the guarded write policy instead of
the old blanket refusals
- per-agent set_default_model logs the agent-scoped label
- legacy /config?section=... deep links redirect to the Advanced page
- prefs sync: pushes coalesce and drain serially (no CAS races dropping
updates), replaced config hashes mark pre-patch snapshots as stale, and
the last-seen server value persists per gateway scope so reloads cannot
revert unpushable viewer-scope edits
- lint: theme membership checks use Sets
* fix: harden uninstall route guard and scope prefs queue per gateway
- plugin uninstall ownership now covers execution owners (runtime policy,
embedded harness override) in addition to the provider, and re-verifies
inside the commit boundary so a concurrent route switch cannot slip a
destructive uninstall past the pre-approval check
- prefs push queue binds to one gateway client; switching gateways drops
the old queue (device-local fallback) instead of cross-writing prefs
* style: top-level type import in config-page test
* fix: close review races in config-write guards and prefs reconciliation
- config_set/config_set_ref re-verify the inference-route guard inside
the commit boundary so conditional verdicts (per-agent routing, plugin
entries) cannot be flipped by a concurrent edit after approval
- server prefs apply per changed field, so a server change to one pref
no longer reverts unpushable local edits of the others
- replaced-hash staleness retires once post-patch state is observed, so
a genuine restore of the pre-patch config becomes authoritative again
- moved-section deep links (communications notifications, automation
approvals) redirect to their new pages
* style: satisfy no-useless-assignment and loop-condition lints in prefs sync
* fix(ci): resolve unhandled profile-test rejections and knip unused exports
The profile page's new identity section reads context.config; the test
context mock now provides it, eliminating the unhandled rejections that
failed checks-ui and the compact-large shard. Exports flagged by the
dependency gate (extract/patch helpers, section-key and prop types) go
module-private; extraction coverage moves behind applyServerUiPrefs.
* refactor(agents): move config-write policy to its own module
The static path classifier and documented denylist move to
config-write-policy.ts, shared by the execution guard and the parity
contract test — no test-only exports left for the dependency gate to
flag. Blocked-root refusals now cite the denylist's documented
escalation for that root.
* feat(gateway): typed structured questions on openclaw.chat
The chat result gains an additive optional question field (2-4 options, one
recommended max, per-option reply text). Producers: the two onboarding welcome
variants (apply-setup yes/ask, ready next-step) and hosted wizard select/confirm
steps, which mirror the awaited step so card clients render real wizard choices.
Prose replies always stand alone for text-only clients (macOS app, TUI). The
custodian page consumes the typed field and drops the PR1 string-marker parser
(never shipped in a release); cards send the option reply while the transcript
shows the label.
* chore: keep OnboardingWelcome type module-local
* chore(protocol): regenerate Swift bindings for the chat question field
* feat(onboarding): persist app recommendations once
* feat(onboarding): add self-naming birth sequence
* chore(wizard): hoist bootstrap-defer imports to the top of the module
* docs(bootstrap): persist agreed identity into IDENTITY.md and SOUL.md as well
* docs: regenerate docs map after onboarding plan merge
* fix(wizard): reuse a pending stored offer instead of rescanning apps
* fix(onboard): deduplicate bootstrap recommendations
* feat(onboarding): recommend plugins and skills from installed apps
Scan installed macOS apps during classic onboarding (TCC-free), gather
candidates from official catalogs + ClawHub search, let the configured
model pick genuine matches, and offer an opt-in multiselect install step.
Adds a device.apps node-host command (default-off sharing, Android-parity
envelope) so remote gateways can request a paired Mac's inventory, and a
wizard.appRecommendations kill switch. Custom setup-inference completions
no longer inherit the 32-token verification-probe output cap.
* feat(onboarding): recommend apps in guided flow
* fix(onboarding): harden app recommendations against ClawHub self-promotion
Third-party ClawHub skills are never pre-selected regardless of model tier
(publisher-controlled listing text reaches the matcher prompt and could
promote itself); their labels now say they install third-party code.
Installed-app scans follow symlinked .app bundles. Matcher output stays
bounded by the resolved model's own maxTokens budget (documented invariant).
* fix(onboarding): key official catalog candidates by resolved plugin id
Real catalog entries are package manifests without a top-level id; keying the
candidate map and channel/provider classification by entry.id collapsed the
whole official catalog into one undefined-keyed entry, so no official plugin
or channel was ever recommended. Regression test runs against the bundled
catalogs.
* fix(onboarding): satisfy lint, types, deadcode, and migration gates
Split the guided-onboarding test into a self-contained custodian suite to stay
under max-lines. Narrow app-recommendation exports (drop dead node-payload
normalizer, unexport internal types/helpers, route candidate tests through the
public API), replace map-spread with a helper, unexport device.apps result
types, add installedAppsSharing to node-host migration expectations, cast the
wizard multiselect mock, and regenerate the docs map.
* test(onboarding): register new live test in the shard classifier
* feat(onboard): guided onboarding becomes the custodian flow
Question zero asks for discovery consent up front (full access recommended,
guarded asks once or configures manually; persisted as wizard.accessMode and
honored on reruns). Discovery quietly collects auto-ladder failures into one
summary with details behind 'See other options', quips about detected coding
agents, and announces the working route as a default with an easy undo.
After inference and the memory offer (skipped when discovery was declined),
fresh installs auto-apply the standard setup plan and hatch the agent
directly in the terminal; configured installs (pre-existing model or gateway
config) stay verification-only and never reinstall or restart the Gateway
service. Setup-apply failures fall back to the conversational chat.
Remote-gateway onboarding keeps its legacy chat handoff explicitly.
listManualSetupInferenceOptions lists manual providers without scanning.
* docs(onboarding): add custodian redesign implementation plan (living doc)
* docs: regenerate docs map for onboarding-redesign page
Final review findings:
- Wrap the operator-approval copy/drop/rename in a single immediate
transaction so a crash mid-migration cannot strand or lose rows.
- The inference fallback marked a provider attempted before probing, so a
stale requester credential blocked another agent's valid same-provider
route. Dedup by credential owner and only retire a whole provider on
provider-wide failures (auth/billing may differ per credential owner).
Refs #107237
New openclaw delegation tool relays to openclaw.chat in-process; persistent
writes surface through the existing durable operator-approval registry as a
third system-agent kind (no parallel store), armed only by a human operator
in the Control UI — a delegated agent can never self-approve. Setup wizards
(channel/model/open-setup/open-tui) are refused in delegated mode so a
machine agent cannot complete setup or persist credentials unattended.
Vendor-neutral inference fallback ladder (requester route first, then other
authed providers by provider id). update.run removed from the gateway tool.
Caveman system-prompt fragment steers config/channels/plugins/agents/updates
to the openclaw tool. Doctor-owned state migration widens the approval-kind
constraint; runtime stays canonical-only.
Refs #107237
The Crestodian->OpenClaw reconciliation restored a pre-#102197 `if (opts.approved)`
wrapper around the execute-time plugin-install trust check, so an unapproved
plugin-install op produced a formatted plan for an arbitrary npm/url/file
source instead of rejecting it. Validate unconditionally, matching #102197,
and restore its dropped regression test. Also fix a stale
crestodian-ring-zero-setup id in the CI compat list and a doubled
openclaw-openclaw-planner tmpdir prefix from the mechanical rename.
Rebase onto main pulled in the ClawHub plugin-install trust check (#102197);
ported it into the renamed system-agent operations/tool/tests, refreshed
native i18n, deadcode, and max-lines baselines for the renamed paths.
Refs #107237
Review findings on the rename: macOS resume store now migrates activation
leases persisted under the retired crestodian UserDefaults key so an app
upgrade cannot orphan a live activation; open-tui results carry
returnToShell so bare /openclaw re-enters the OpenClaw shell instead of
exiting the CLI.
Refs #107237
Identifiers ending in TOKEN false-positive secret scanners on every diff
touching this file; ARG_WORD also describes the grammar role better.
Refs #107237
User-facing name is now OpenClaw (the system speaks); internal code name is
system-agent. Gateway methods crestodian.* -> openclaw.chat/openclaw.setup.*,
agent tool -> openclaw, reserved agent ids openclaw + retired crestodian.
openclaw setup routes: onboarding flags -> onboard, -m/--yes -> system agent,
bare configured interactive -> OpenClaw chat, unconfigured -> onboarding.
Hidden crestodian CLI and /crestodian TUI aliases kept; docs moved to
docs/cli/openclaw.md with redirect stub. macOS/Android strings in lockstep.
Refs #107237