* fix(mcp): bound doctor server checks
* fix(mcp): defer doctor concurrency helper loading
* fix(mcp): preserve doctor check coverage
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* 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
node identity, promos list, daemon restart, and tasks show emitted their --json result via runtime.log() (console.log), which --json mode redirects to stderr — leaving stdout empty and breaking pipes like `openclaw node identity --json | jq`. Use writeRuntimeJson to write the payload to stdout, matching health/tasks-list/capability. Human output unchanged.
`config file` (print the active config path) called readConfigFileSnapshot(), eagerly creating the state dir + state/openclaw.sqlite — so a --profile typo minted a stray profile tree. Resolve the path via resolveConfigPath() instead; no state initialized.
* feat(onboard): add browser-first guided handoff
* chore: drop release-only CHANGELOG edit from browser-handoff branch
* fix(onboard): make browser handoff reach the gateway on real installs
The presence probe connected as a raw shared-auth client with a possibly
SecretRef-managed token, which the gateway rejects as an unpaired Control UI
client ("device identity required"), so the handoff never fired and always
fell through to the terminal hatch. Read presence as a CLI-mode loopback client
(the trusted path every openclaw command uses) so operator.read is granted, and
share that one auth path between the reachability gate and the wait loop. Also
short-circuit when gateway.controlUi.enabled is false so onboarding does not open
a dead dashboard URL and block for the full timeout.
* fix(onboard): share resolved target between handoff gate and wait
The reachability preflight now resolves the same target (and shared secret) the
wait loop uses, and presents the configured secret to the CLI-mode presence
read. Known remaining limitation: on fresh quickstart gateways whose auth token
is written to config but resolved differently at runtime, the presence read is
rejected with a token mismatch and the handoff falls back to the terminal hatch.
Tracked for follow-up before enabling the browser handoff by default.
* docs(onboarding): mark phase 3 browser handoff as held with diagnosis
* docs(onboarding): phase 3 proven end-to-end; token-mismatch hold was a test artifact
* fix(onboard): satisfy docs-map and test-type gates for the handoff branch
* refactor(onboard): split guided manual stage
* 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
* fix(cli): guard secrets plan JSON.parse against malformed input
readPlanFile reads a user-specified file and parses it with JSON.parse
without a try-catch. A malformed or corrupted plan file produces a raw
SyntaxError that is not user-actionable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: tighten malformed secrets plan proof
Co-authored-by: 丁宇婷0668001435 <ding.yuting@xydigit.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
`config file`, `config patch --dry-run --json`, and `config unset --json` ran
the config path through shortenHomePath, emitting `~/…` or the literal
`$OPENCLAW_HOME/…` token in machine output — breaking `cat $(openclaw config
file)` and corrupting JSON path fields. `agents list --json` already resolves.
Emit the raw resolved snapshot path at these machine-output sites; human table
rendering keeps the friendly abbreviation.
* fix(config): improve validation error messages with line numbers, bracket paths, and received values
- Add JSON5-aware path navigator (issue-location.ts) that resolves line
numbers and received values from raw config text without a full parser
- Add formatConfigIssuePath() for bracket notation (agents.list[3])
- Add appendReceivedValueHint() for safe value display (got: "none")
- Add attachConfigIssueDiagnostics() to enrich issues with location info
- Wire enrichment into runConfigValidate() and loadValidConfig() in config-cli.ts
- Add resolveIssueLocationPrefix() to issue-format.ts for sourceFile:line prefix
- Sensitive paths and secret refs are never leaked in received value hints
- Paths in 'd files gracefully degrade (no line number shown)
Closes#104854
* fix(config): type fixes from tsgo:core validation
- Add sourceFile to ConfigIssueFormatOptions (used in resolveIssueLocationPrefix)
- Fix readKey regex test for undefined raw[c.pos]
* fix(config): satisfy eslint(curly) and formatting for issue-location.ts
- Add braces to all single-line if/while/for statements
- Run oxfmt for consistent formatting
* fix(config): trim issue-location.ts to 500 lines for LOC ratchet
* fix(config): fix formatConfigIssuePath ternary bug from trimming
* fix(config): trim issue-location.ts to 469 lines for LOC ratchet
* fix(config): format issue-location.test.ts
* fix(config): document JSON5 subset and add 20 edge case tests
- Add explicit supported-subset documentation to issue-location.ts header
- Add 20 tests covering hex numbers, leading decimals, Infinity, NaN,
null/bool, trailing commas, deep nesting, unicode escapes, multi-line
strings, unicode keys, escaped quotes, numeric separators, block comments,
mixed quotes, empty objects/arrays, and graceful degradation
* fix(config): address autoreview findings on JSON5 docs and tests
- Remove numeric separator claim (not valid JSON5)
- Remove 'None known' unsupported-syntax claim, document known limitations
- Restructure scalar tests to exercise skipVal (resolve sibling after exotic value)
- Remove numeric separator test
* fix(config): constrain received value diagnostics
* fix(config): preserve JSON5 diagnostic locations
* fix(config): preserve validation path ownership
* fix(config): redact dotted plugin paths
* test(config): avoid diagnostic fixture shadowing
* refactor(config): privatize diagnostic helpers
---------
Co-authored-by: Erick Kinnee <erick@ekinnee.dev>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(channels): restart gateway when channels.start fails for unloaded plugin
After channels login, the CLI calls channels.start to reconcile the running
gateway. If the channel plugin was installed or enabled after the gateway
started, the plugin is not in the runtime registry and channels.start rejects
with 'invalid channels.start channel'. Detect this error and fall back to
gateway.restart.request so the gateway reloads plugins and starts the channel
automatically.
Fixes#90296
* ci: retrigger checks (update proof section format)
Co-authored-by: Cursor <cursoragent@cursor.com>
* ci: retrigger proof check (real behavior evidence)
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(channels): narrow unloaded-plugin restart
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* [AI] fix(daemon): set default --max-old-space-size=8192 in gateway service env
Generate a managed NODE_OPTIONS with --max-old-space-size=8192 for the
gateway systemd service, discarding ambient host NODE_OPTIONS to prevent
--require/--inspect persistence. 8192 MB is chosen as a safe default
that prevents the common OOM crash-loop with the default Node ~4 GB heap
under sustained load.
Related to #96203
* fix(daemon): adapt gateway heap limit to host memory
* fix(daemon): keep gateway heap helpers internal
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(cli): filter Commander value placeholders from Fish completion flags
The Fish completion generator was treating Commander.js value placeholders
(like <path|->, <file>, [optional]) as command-line tokens, causing the
angle-bracket values to be interpreted as shell redirects when the
completion script was sourced.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(cli): use parsed option flags for completions
Co-authored-by: 丁宇婷0668001435 <ding.yuting@xydigit.com>
* fix(cli): support long option aliases in Fish completion
Co-authored-by: 丁宇婷0668001435 <ding.yuting@xydigit.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
isLoopbackProxyHostname used split(".", 1)[0] === "127" to detect
127.0.0.0/8 addresses. While isIP() already rejects leading-zero octets
before this branch is reached, startsWith("127.") is more direct and
clearly expresses the intent of matching the entire 127.0.0.0/8 range.
* fix(kill-tree): verify process group leader before group kill to prevent gateway SIGTERM (#76259)
- Add isProcessGroupLeader() to killProcessTree/signalProcessTree: ps -p <pid> -o pgid= primary check with /proc/<pid>/stat fallback on Linux. Group kill only when the PID is its own process group leader; non-leaders fall back to single-pid kill, preventing accidental gateway SIGTERM when a non-detached child shares the gateway's process group.
- Propagate detached: true to all detached-spawn cleanup callers (exec-termination, agent-bundle LSP, mcp-stdio, bash, supervisor pty, agent-core nodejs) so detached group cleanup survives leader exit.
- Gateway/daemon cleanup paths (schtasks, restart-health) keep the leader-checked default (detached omitted).
Closes#76259
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(process): tighten process-group ownership checks
* refactor(daemon): split restart diagnostics
* refactor(daemon): isolate restart health types
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
333c4f9a61 and 244f4965dc landed six exports with no production
consumers, failing the hard-zero deadcode gate on every PR: a dead
re-export block in restart-handoff (the contract module's real importer
uses it directly), three result/mode types only referenced inside their
own modules, and two supervision symbols whose only consumers were their
tests. The types and helpers stay defined for the in-flight supervisor
work to re-export alongside real consumers; the supervision test now
proves mode resolution through the public isGatewayExternallySupervised
surface.
* fix(cli): add 10s timeout to port-inspection subprocess calls
fuser, netstat, and lsof can hang on a busy or misbehaving system.
Node.js execFileSync already supports a timeout option; use 10s to
match the existing convention in debug-claude-usage.ts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(cli): harden port subprocess deadlines
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* refactor(plugin-sdk): narrow wildcard barrels to explicit used exports
* refactor(tools): delete dead tool-planning module exposed by barrel narrowing
* fix(plugin-sdk): restore deprecation tag on OpenClawSchemaType alias
* test(agents): drop test for deleted runtime proxy module
* refactor(tools): trim descriptor types to cache consumers
* refactor(deadcode): harvest exports orphaned by barrel narrowing
* refactor(deadcode): harvest exports orphaned by barrel narrowing (rest)
* fix(agents): restore sdk imports and test markers via public predicate
* fix(plugin-sdk): named type re-exports in plugin-entry; trim types barrel precisely
* chore(plugin-sdk): account unmasked deprecated provider types in budgets
* fix(plugins): name star-only type rows for dts bundling
* fix(plugins): restore host-hook surface; unexport internal api compositions
* fix(plugins): named type imports for api composition; restore needed source exports
* fix(plugins): knip-visible type imports for registry surfaces
* test: adapt tests to privatized media and command internals
* fix(qa-lab): re-export snapshot conversation type
* style: format sessions sdk imports
* fix(plugins): restore smoke entry export; pin budgets to exact actuals
* fix(plugins): canonical smoke-entry import; drop orphaned root shims
* fix(plugins): allowlist manifest probe, repoint qa web import, drop dead browser barrels
* fix(plugin-sdk): pin codex auth marker and scaffold provider type
* fix(qa-lab): keep web-facing model-selection shim within boundary rules
* fix(plugin-sdk): preserve merged contracts through narrowed barrels
* chore(plugin-sdk): pin post-rebase surface budgets