* feat: node-hosted plugins — dynamic tools, MCP servers, and skills
Nodes become declarative plugin hosts:
- node.pluginTools.update: node hosts publish plugin-registered agent tool
descriptors; gateway materializes them as agent tools executing via
node.invoke under the node command allowlist, with tools.effective
invalidation and node online/offline removal.
- Trusted paired-node descriptors: no gateway-side plugin registration
required; gateway.nodes.pluginTools.enabled off-switch (default on);
description/count caps; deterministic node-prefixed collision names.
- Declarative node-hosted MCP: nodeHost.mcp.servers (McpServerConfig shape)
starts MCP clients on the node host, publishes tools as pluginId node-mcp,
executes via built-in mcp.tools.call.v1 with per-layer timeouts, failure
isolation, and orphan-safe shutdown. No re-pairing when servers change.
- Node-hosted skills: node.skills.update publishes ~/.openclaw/skills
content (64 skills/64KB/512KB caps both sides); gateway merges them into
the skills snapshot while connected and exec host=node is available, with
node:// locators, node-prefixed collisions, disabled command dispatch,
and gateway.nodes.skills.enabled + nodeHost.skills.enabled switches.
- Security: node-supplied pluginIds cannot satisfy pluginId-scoped tool
allowlists unless gateway-registered; reserved node-mcp id requires the
core MCP descriptor shape; protocol registry kept out of public
plugin-sdk dts.
- E2E: pond harness proves publication, MCP round-trip, skills locator, and
disconnect/reconnect for all three surfaces.
* style: format node-plugin-tools test
* fix(skills): keep status loader unfiltered when eligibility is passed
skills.status started passing eligibility for the node-skill merge, which
flipped loadWorkspaceSkillEntries into filtered mode and dropped disabled
skills from status reports (QA plugin-lifecycle-hot-reload timeout). Status
now merges node skills explicitly around an unfiltered load. Also: regen
docs_map for new node docs sections; add the intentional node-host MCP
onclose suppression to the lint-suppression allowlist.
* feat(ui): overlay hover meta and even out chat thread rhythm
* fix(ui): keep the overlay footer single-line so it fits the rhythm gap
* fix(ui): wrap the revealed footer in narrow lanes and gate hidden-overlay hit-testing
* test(ui): travel through the group before hovering pointer-gated footers
* fix(ui): keep the streaming footer sender and time on one shrinkable row
Approving a node whose pairing surface advertises browser.proxy only required
operator.write, while invoking browser.proxy already requires operator.admin
(server-methods/nodes.ts). resolveNodePairApprovalScopes bumped the approval
scope to operator.admin only for NODE_SYSTEM_RUN_COMMANDS, so a write-scoped
operator could trust a browser.proxy-capable node that later routes bundled
browser-tool traffic. Add NODE_BROWSER_PROXY_COMMAND to the admin-approval set
so approval scope matches the invoke-time gate.
One RUN_STALE_TAKEOVER_MS constant and one resolveRunStaleThresholdMs in
diagnostic-run-activity.ts replace the hand-paired constants and duplicated
blocked-tool-floor derivations in the reply and embedded run registries.
Thresholds are byte-identical before and after; zero behavior change.
Part of #104219 (seam 4).
* fix (Computer Use) Stabilize Codex Computer Use readiness
Co-authored-by: Ben Badejo <188106718+bdjben@users.noreply.github.com>
* fix (Computer Use) Stabilize Codex Computer Use readiness
* fix (Computer Use) Stabilize Codex Computer Use readiness
* fix (Computer Use) Stabilize Codex Computer Use readiness
* fix(clawsweeper): address review for automerge-openclaw-openclaw-103331 (1)
* fix (Computer Use) Stabilize Codex Computer Use readiness
---------
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: Ben Badejo <188106718+bdjben@users.noreply.github.com>
* fix(cron): pass allowEmptyAssistantReplyAsSilent from cron executor
Intentionally-silent cron jobs (alert watchers that say nothing unless there
is something to report) fail with FailoverError "empty_response" because the
allowEmptyAssistantReplyAsSilent flag is only computed for DM/group chat
contexts — the cron executor never sets it.
Instead of patching the shared CLI runner with a lane substring heuristic,
set allowEmptyAssistantReplyAsSilent: true at the cron executor level for
both the CLI and embedded runner paths. This:
- Owns the silent-empty policy at the correct boundary (the cron executor)
- Covers both runner paths uniformly
- Does not broaden the shared CLI empty-response guard for non-cron callers
Fixes#94224
Co-Authored-By: Claude <noreply@anthropic.com>
* test(cron): add regression test for allowEmptyAssistantReplyAsSilent flag propagation
Verify that the cron executor passes allowEmptyAssistantReplyAsSilent: true
to both runCliAgent and runEmbeddedAgent calls. The embedded test uses
mockTexts: [""] to truly exercise empty output, and the CLI test mocks
runCliAgent with empty payload text — both scenarios require the flag to
avoid empty_response errors on intentionally-silent cron watchers.
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(cron): centralize silent reply policy
Reuse existing executor suites and keep CLI and embedded cron runners aligned.
Co-authored-by: 刘镇业 0668001127 <liu.zhenye@xydigit.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* chore(lint): enforce no-floating-promises repo-wide
* feat(tooling): split DOM globals out of the Node-side typecheck program
tsconfig.core.json now compiles src/ + packages/ with lib ES2023; new
tsconfig.ui.json owns ui/ with DOM. Web-global names used by Node code
alias to canonical undici/stream-web types; highlight.js loads through a
validated narrow API because its d.ts force-includes lib.dom. Boundary,
sparse-guard, changed-lane routing, and profile lanes cover the new ui
graph.
* chore(deps): declare undici-types and teach knip about the highlight.js runtime require
* ci: retrigger dropped pull_request synchronize event
* refactor(types): derive web-global aliases from @types/node fetch globals, drop undici-types dep
* ci: retrigger after dependency guard cleared
* fix(scripts): add ui lane to changed-lanes declaration union
* chore(types): add declaration files for scripts/lib and scripts/e2e modules
* chore(types): add declaration files for top-level script modules (a-m)
* chore(types): add declaration files for top-level script modules (n-z)
* test: use a non-secret-shaped gateway token fixture
* test: type ci workflow guard helpers for the root test lane
* chore(tooling): typecheck root test/** with a dedicated tsgo lane
- test/tsconfig/tsconfig.test.root.json: root-test program (strict unused checks,
fixtures excluded; two Docker E2E clients that import built dist/** stay out,
same rationale as the scripts/e2e exclusion in tsconfig.scripts.json)
- tsgo:test:root wired into tsgo:test, check:test-types, scripts/check.mjs, and
the ci.yml test-types shard, mirroring the tsgo:scripts lane (#104348)
- changed-lane routing: test/**/*.ts (excluding fixtures) and the lane tsconfig
now trigger 'typecheck test root' in check:changed; previously test/ paths ran
lint only, so harness type errors surfaced first in CI (#104287 envDir case)
- burn down all 1071 latent type errors in the program: precise param/local
types across test/scripts, test/vitest, test/e2e, and transitive scripts/e2e
program members; 205 sibling .d.mts declaration files for imported .mjs
modules (committed separately); zero any, zero ts-expect-error
- resolve the pre-existing testing star-export ambiguity in
scripts/e2e/parallels/common.ts with an explicit re-export
Closes#104388
* chore(types): correct declaration fidelity per structured review
- re-derive 51 .d.mts files from implementation data flow instead of
initializers: fix a wrong never return (runTestProjectsDelegation returns
the child), add encoding-sensitive exec/spawn overloads (plain-gh), restore
the full release profile union, make parsed paths string | null, add missing
parseArgs fields via help/non-help unions, add a missing sibling declaration
(budget-number-args), drop 15 unused lint directives
- precise install-record/tuple typing removes the type-aware oxlint
regressions the first declarations caused in scripts/e2e implementations
- route .mts declaration edits under test/ to the testRoot lane and reference
the test-root project from tsconfig.projects.json so tsgo:all covers it
(closes both review findings against the lane wiring)
* chore(scripts): keep telegram runner dist typing structural for the boundary guard
* chore(types): declare runtime pack and gateway readiness exports added on main
* test: pin the importTargetPlan form of the plugin-contract plan import
The guard expectation still referenced the raw await import( form that
7ae5996bb3 (#103975) replaced with the importTargetPlan fallback helper;
the assertion fails on current main.
* fix(onboard): wait for gateway warnings before installing service
* fix(onboard): show gateway warnings after plan errors
* refactor(onboard): drain install warnings from queue
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>