Skip to content

openclaw: patch plugins.allow at install time + drop blocking auto-recall#124

Merged
kaghni merged 5 commits into
mainfrom
fix/openclaw-plugins-allow-121
May 18, 2026
Merged

openclaw: patch plugins.allow at install time + drop blocking auto-recall#124
kaghni merged 5 commits into
mainfrom
fix/openclaw-plugins-allow-121

Conversation

@kaghni
Copy link
Copy Markdown
Collaborator

@kaghni kaghni commented May 12, 2026

Fixes #121.

Why

Two related bugs surfaced in a user report (the original symptom: "openclaw plugin installed but auto-capture silently does nothing"):

  1. plugins.allow gating — the installer copies files into ~/.openclaw/extensions/hivemind/ but never patches ~/.openclaw/openclaw.json. If the gateway's plugins.allow is an explicit array missing "hivemind", the plugin never registers, agent_end never fires, and /hivemind_setup is unreachable from inside the agent (chicken-and-egg: the slash command needs the plugin to load first).
  2. Blocking auto-recallbefore_agent_start ran a Deeplake sessions-table search on every user turn. Sessions queries currently take 11.7s / 10.8s / 11.2s (three sequential probes) against a 10s SDK timeout, so every turn paid the full timeout before the agent could read the message. Other agents (claude-code/codex/cursor/hermes/pi) don't do this — they intercept the agent's Grep tool and let the agent decide when to search.

What changed

plugins.allow patch in the installer

  • src/cli/install-openclaw.ts now calls the same ensureHivemindAllowlisted helper that /hivemind_setup uses
  • Mirrors openclaw's own ensurePluginAllowlisted semantics (ext/openclaw/src/config/plugins-allowlist.ts:7): only patches plugins.allow when it's an explicit non-empty array. Absent / empty → left alone (default-allow stays default-allow; we don't flip the user into restrictive mode and break their other plugins)
  • Same logic for tools.alsoAllow
  • Atomic write (tmp + rename) with timestamped backup
  • Installer prints: which arrays were patched, restart hint, and a "capture starts on the next turn — no backfill" caveat so users aren't confused when older turns don't appear

Blocking auto-recall removed

  • openclaw/src/index.ts:before_agent_start no longer runs searchDeeplakeTables
  • Recall flows exclusively through the agent-callable hivemind_search / hivemind_read / hivemind_index tools (already registered), with the SKILL.md body in the system prompt directing the agent to use them
  • Side-effect win: the recursive <recalled-memories> capture bug (old recall context was getting captured as part of the user's prompt and stored) is gone too
  • The hook still handles the login nudge and post-auth welcome banner — those don't touch Deeplake
  • extractKeywords + RECALL_STOPWORDS removed as dead code

Tests

  • openclaw/tests/setup-command.test.ts+9 cases for plugins.allow patching: idempotency, default-allow semantics, both-arrays patched, detectAllowlistMissing extension
  • openclaw/tests/install-openclaw.test.ts (new, 12 cases) — drives the real installer against tmpdir fixtures: file copy, both patches, idempotency, default-allow preservation, malformed config tolerance, restart hint, no-duplicate
  • openclaw/tests/auto-recall.test.tsrewritten: asserts no Deeplake call on a normal turn, no <recalled-memories> injection, tools still registered, login-nudge path preserved

All 2214 hivemind tests pass.

Real-world E2E on a live openclaw gateway (2026.4.21)

  • Bundle installed → Hivemind plugin registered in gateway log
  • Idempotency: re-running installer logs allowlist already covers hivemind, no extra backup
  • Zero Auto-recall failed log lines after the restart with the new bundle (previous bundle emitted them on every turn that hit Deeplake slowness)
  • hivemind_search / hivemind_read / hivemind_index tools confirmed registered in before_prompt_build

Caveats

  • Capture (write path) still hits the same Deeplake timeout intermittently — Auto-capture failed: Query timeout after 10000ms lines appear in the gateway log. agent_end runs after the agent responds, so this doesn't block the user, but it does drop some turns from memory until Deeplake's sessions-table query latency improves. Separate issue, not addressed here.
  • The exact host state the original reporter hit (where adding "hivemind" to plugins.allow was needed even though tools were allowlisted) can't be reproduced on openclaw 2026.4.21 — that version has an "auto-promote plugins when their tools are in alsoAllow" feature that masks the gating. Older openclaw versions without that feature are the broken-state case the installer's plugins.allow patch is for.

Confidence

Confidence: high, because the unit tests cover every edge case of the new config-patching logic, the local fixture E2E drove the real installer end-to-end against multiple fixture shapes, and the live gateway confirms the bundle loads + the recall hook no longer fires.

Untested: openclaw versions older than 2026.4.21 (no host available; the installer's plugins.allow patch is the right fix for them but unverified end-to-end on those versions).

Test plan

  • CI green on this branch
  • Reviewer reads through openclaw/src/setup-config.ts semantics (esp. isPluginsAllowMissingHivemind only-patch-explicit-arrays rule)
  • Reviewer skims openclaw/tests/install-openclaw.test.ts and confirms test coverage matches the design intent in the description
  • After merge: cut a release and verify users on default-restrictive openclaw configs see the plugin register after a fresh install

Summary by CodeRabbit

  • New Features

    • OpenClaw installation now validates and patches configuration to ensure the hivemind plugin is properly allowlisted, with backup creation before changes.
    • Setup command displays which allowlist fields were updated during configuration patching.
  • Changes

    • Removed automatic memory recall that occurred on every turn; recall is now available only through direct tool invocation.
    • Improved installation and cleanup processes for extension management.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 25c23e9d-d401-4270-913a-3abca4f63908

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements issue #121 fix (OpenClaw installer now patches plugins.allow to register the hivemind plugin) and removes expensive proactive per-turn auto-recall from the plugin hook. Supporting infrastructure refactors fs/path imports to use node: protocol variants throughout the CLI bundle.

Changes

OpenClaw allowlist patching and plugin hook simplification

Layer / File(s) Summary
Allowlist contract and detection helpers
openclaw/src/setup-config.ts
Adds isPluginsAllowMissingHivemind() to detect explicit allowlists missing hivemind, introduces AllowlistDelta type describing which allowlists were patched, updates SetupResult to include delta. Refactors ensureHivemindAllowlisted() to independently detect and patch both plugins.allow and tools.alsoAllow, and expands detectAllowlistMissing() to check both arrays.
OpenClaw installer patching implementation
bundle/cli.js, src/cli/install-openclaw.ts
OpenClaw install flow validates and patches openclaw.json to ensure hivemind appears in explicit-allowlist plugins.allow (if present as array) and tools.alsoAllow. Writes timestamped backup of original config, uses atomic temp-file + rename, and logs patch status. Installer entry point imports and invokes ensureHivemindAllowlisted() after copying files to disk.
Plugin hook and auto-recall removal
openclaw/src/index.ts
before_agent_start hook no longer issues proactive Deeplake queries on every turn. Removed keyword extraction logic and multi-table UNION ALL memory search. Hook now handles only login URL nudging (unauthenticated path) and one-time welcome banner, with explicit logger.error() for exceptions. hivemind_setup command output now builds and displays touched allowlist array showing which fields were patched, plus updated messaging about capture timing and backup path.
Comprehensive installer and setup command tests
openclaw/tests/install-openclaw.test.ts, openclaw/tests/setup-command.test.ts
New test suite mocks homedir() and openclaw.json fixtures, verifying bundle copy, config patching (both allowlists and only when needed), idempotency, timestamped backup creation, and stdout guidance. Extended setup-command tests cover plugins.allow gating: patching explicit arrays missing hivemind, preserving default-allow when absent, handling empty arrays, combined patching, and detectAllowlistMissing() edge cases.
Auto-recall test refactoring
openclaw/tests/auto-recall.test.ts
Auto-recall tests refactored to document and verify post-removal behavior: before_agent_start no longer calls Deeplake or injects <recalled-memories> tags, continues short-circuit on empty/short prompts, registers hivemind_search/read/index tools, and safely handles unauthenticated/missing-credential paths without throwing. Test helper extracted buildMockApi() for clarity.

Node imports and path infrastructure refactoring

Layer / File(s) Summary
Index marker and cursor installation paths
bundle/cli.js
Index marker store updates node:fs/node:path imports and revises workspace/org/table keying helpers. Cursor installer updates imports, path constants, hook command construction, bundle copy checks, and uninstall operations.
Hermes installation and configuration wiring
bundle/cli.js
Hermes installer updates fs/path imports, introduces path constants for home/hivemind/bundle/config directories, refactors config read/write (uses updated existsSync for fallback), install flow validation and bundle copy, and expands uninstall to remove skill dir, hivemind dir, and gateway config with logging.
MCP shared server and Pi agent installation
bundle/cli.js
MCP shared-server installer introduces path constants for ~/.hivemind/mcp layout and wires ensureMcpServerInstalled flow. Pi installer updates imports, declares extension/worker/version-path constants, refactors install/uninstall to remove legacy skill dir, rewrite AGENTS.md hivemind block, copy extension and optional worker bundles, and prune orphaned blocks.
Embeddings support and auth credential/config wiring
bundle/cli.js
Embeddings shared-deps updates linked-state detection, manifest installation, and daemon bundle copy. Embeddings status adds daemon/shared-deps state reporting and optional shared-dir pruning. Auth credential handling rewrites ~/.deeplake/credentials.json path derivation and load/save helpers. Config loader and debug logger update credential file reading and LOG path construction.
Skillify scope-config, manifest, and backup wiring
bundle/cli.js
Skillify top-level imports, scope-config paths and loaders, and pull/skill-writer module imports updated to use node: variants. Manifest loading preserves same validation/fallback. saveManifest refactored to atomic temp-file + rename with permissions. Pull flow now backs up existing SKILL.md to SKILL.md.bak before overwriting.
Skillify destination resolution and root detection
bundle/cli.js
Pull destination and fanOut symlink helpers updated for global vs project root resolution. Unpull root resolution, agent-roots detection, backfill canonical-path check, and orphan detection refactored to use updated join/exists import aliases. Version read and manifest operations use new readFileSync and existsSync imports.
Skillify status display and project promotion
bundle/cli.js
showStatus state directory resolution and tracked-project existence checks refactored. Tracked project state file reads updated to new readFileSync import. setInstall and promoteSkill destination path computation and existence checks refactored. Pull and unpull dest string computation updated to use join/homedir helpers.
Update and version detection helpers
bundle/cli.js
Update and version-check module imports updated to use node: variants. detectInstallKind() package.json reading and .git existence check refactored to use new import aliases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 A plugin once lost in the config so deep,
Now patched with allowlists both promises to keep,
Auto-recall gone—let the tools take the lead,
With node: imports fresh, we're fast-freed indeed! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the two main changes: patching plugins.allow at install time and removing the blocking auto-recall behavior.
Description check ✅ Passed The PR description comprehensively covers the motivation, detailed changes, test coverage, and real-world E2E validation; it aligns well with the template structure and includes all critical context.
Linked Issues check ✅ Passed All primary objectives from issue #121 are implemented: installer now patches plugins.allow and tools.alsoAllow with safe semantics, blocking auto-recall is removed, comprehensive tests added, and real-world E2E validation confirms the fix.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to fixing issue #121: installer patching, auto-recall removal, related test additions, and setup-config extensions. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/openclaw-plugins-allow-121

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

Coverage Report

Scope: files changed in this PR. Enforced threshold: 90% per metric (per file via vitest.config.ts).

Status Category Percentage Covered / Total
🟢 Lines 100.00% (🎯 90%) 34 / 34
🟢 Statements 100.00% (🎯 90%) 39 / 39
🟢 Functions 100.00% (🎯 90%) 2 / 2
🟢 Branches 90.91% (🎯 90%) 20 / 22
File Coverage — 1 file changed
File Stmts Branches Functions Lines
src/cli/install-openclaw.ts 🟢 100.0% 🟢 90.9% 🟢 100.0% 🟢 100.0%

Generated for commit 45e40c7.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openclaw/src/index.ts (1)

1009-1032: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

before_agent_start should no longer be gated by autoRecall (Line 1009).

Now that proactive recall was removed, this gate unintentionally disables the login nudge + post-auth welcome paths for users who already have autoRecall: false.

Suggested fix
-    if (config.autoRecall !== false) {
-      hook("before_agent_start", async (event: { prompt?: string }) => {
+    hook("before_agent_start", async (event: { prompt?: string }) => {
         if (!event.prompt || event.prompt.length < 5) return;
         try {
           const dl = await getApi();
@@
         } catch (err) {
           logger.error(`before_agent_start failed: ${err instanceof Error ? err.message : String(err)}`);
         }
       });
-    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openclaw/src/index.ts` around lines 1009 - 1032, The before_agent_start hook
is incorrectly wrapped by the config.autoRecall check which prevents the login
nudge and post-auth welcome for users with autoRecall: false; remove this gating
so the hook registration always runs (i.e., register the hook unconditionally
instead of inside the if (config.autoRecall !== false) block) while keeping the
existing logic that checks getApi(), authUrl, and justAuthenticated handling
inside the hook callback (referencing the before_agent_start hook, getApi(),
justAuthenticated, loadCredentials(), and authUrl).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bundle/cli.js`:
- Around line 456-458: The current coercion of tools.alsoAllow to [] forces
patching even when the field is absent; change the logic so you preserve
"missing" vs "explicit empty" by not defaulting to [], e.g. keep alsoAllowRaw as
undefined when tools.alsoAllow is not an array and compute
toolsAlsoAllowNeedsPatch only when an actual array was provided (e.g.
toolsAlsoAllowNeedsPatch = Array.isArray(alsoAllowRaw) ?
!isAllowlistCoveringHivemind(alsoAllowRaw) : false); apply the same pattern to
the analogous block around lines 470-474 so patching only occurs for an
explicitly supplied non-empty allowlist.
- Line 3313: The command string built for Hermes is not quoting the executable
path which will break on paths with spaces; update the assignment that creates
command (currently using `node ${join8(BUNDLE_DIR, bundleFile)}`) to wrap the
resolved path in quotes (e.g. `node "..."`) so the `join8(BUNDLE_DIR,
bundleFile)` result is quoted, ensuring paths with spaces are handled correctly;
locate the `command` property assignment in bundle/cli.js and modify it to
include the quotes around the template substitution.
- Around line 516-529: The installer currently ignores result.status === "error"
from ensureHivemindAllowlisted(), so surface failures by handling that branch:
when ensureHivemindAllowlisted() returns result.status === "error", log a clear
error including result.error, result.configPath and result.backupPath (like the
other branches), and ensure the installer returns non-zero/aborts or otherwise
indicates failure (same behavior as on other fatal install errors). Update the
code path around ensureHivemindAllowlisted() to check result.status for "error"
and emit a descriptive processLogger/log error message using those result
fields.
- Around line 453-456: The code dereferences parsed.plugins and parsed.tools
without validating parsed's shape; update the top of the parsing logic to verify
parsed is a non-null object and throw a clear, structured error if not (e.g.,
"Invalid config: expected JSON object"); then guard access to parsed.plugins and
parsed.tools by treating them as objects (e.g., if parsed.plugins is not an
object default to {} before reading plugins.allow) and ensure tools.alsoAllow is
read only after confirming tools is an object (or default to {}), keeping the
existing assignments to pluginsAllowRaw and alsoAllowRaw but using these
validated/defaulted objects.

In `@openclaw/src/index.ts`:
- Line 663: The manual-fix string returned when updating the allowlist (the
returned object that uses result.error and result.configPath) is too
prescriptive; change it to recommend adding "hivemind" to tools' alsoAllow and
only recommend adding it to plugins.allow if an explicit plugin allowlist
already exists (i.e., plugins.allow is present and non-empty). Update the
message built around result.error/result.configPath to conditionally instruct:
if plugins.allow exists suggest adding to both, otherwise suggest adding to
tools.alsoAllow and warn not to create a plugins.allow (to avoid switching to
restrictive explicit-allow mode).

In `@openclaw/tests/auto-recall.test.ts`:
- Line 157: The test calls expect(before({ prompt: "anything that triggered the
path before" })) but uses the wrong matcher; replace .resolves.not.toThrow()
with .resolves.toBeUndefined() so the promise resolution is asserted correctly
(i.e., expect(before(...)).resolves.toBeUndefined()).

---

Outside diff comments:
In `@openclaw/src/index.ts`:
- Around line 1009-1032: The before_agent_start hook is incorrectly wrapped by
the config.autoRecall check which prevents the login nudge and post-auth welcome
for users with autoRecall: false; remove this gating so the hook registration
always runs (i.e., register the hook unconditionally instead of inside the if
(config.autoRecall !== false) block) while keeping the existing logic that
checks getApi(), authUrl, and justAuthenticated handling inside the hook
callback (referencing the before_agent_start hook, getApi(), justAuthenticated,
loadCredentials(), and authUrl).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 609726ec-8d39-46ec-ae3f-586579147896

📥 Commits

Reviewing files that changed from the base of the PR and between 070a5fc and 429b67a.

📒 Files selected for processing (7)
  • bundle/cli.js
  • openclaw/src/index.ts
  • openclaw/src/setup-config.ts
  • openclaw/tests/auto-recall.test.ts
  • openclaw/tests/install-openclaw.test.ts
  • openclaw/tests/setup-command.test.ts
  • src/cli/install-openclaw.ts

Comment thread bundle/cli.js Outdated
Comment thread bundle/cli.js Outdated
Comment thread bundle/cli.js
Comment thread bundle/cli.js Outdated
Comment thread openclaw/src/index.ts Outdated
Comment thread tests/openclaw/auto-recall.test.ts Outdated
@kaghni
Copy link
Copy Markdown
Collaborator Author

kaghni commented May 12, 2026

User-flow E2E: drove the real hivemind claw install CLI against the reporter's exact broken-state config

Reviewer asked for proof that the user-facing flow (not just the unit-test-mocked one) actually patches the allowlists. Here's the transcript.

Fixture: reporter's broken state, on a tmp $HOME. No plugins.entries.hivemind (so openclaw can't auto-promote), neither array contains "hivemind":

{
  "meta": { "lastTouchedVersion": "2026.4.14" },
  "agents": { "defaults": { "model": "anthropic/claude-haiku-4-5-20251001" } },
  "channels": { "telegram": { "enabled": true } },
  "plugins": {
    "allow": ["telegram", "anthropic", "openai", "duckduckgo"],
    "entries": {
      "telegram":  { "enabled": true },
      "anthropic": { "enabled": true }
    }
  },
  "tools": {
    "profile":   "coding",
    "alsoAllow": ["memory_store"]
  }
}

Before:

plugins.allow contains 'hivemind'? False
tools.alsoAllow contains 'hivemind'? False
plugins.entries.hivemind present? False

Run the real CLI users invoke:

$ HOME=/tmp/hivemind-claw-install-flow-Y4fLiK node bundle/cli.js claw install
  OpenClaw       installed -> /tmp/hivemind-claw-install-flow-Y4fLiK/.openclaw/extensions/hivemind
  OpenClaw       patched plugins.allow + tools.alsoAllow in /tmp/hivemind-claw-install-flow-Y4fLiK/.openclaw/openclaw.json
  OpenClaw       backup: /tmp/hivemind-claw-install-flow-Y4fLiK/.openclaw/openclaw.json.bak-hivemind-1778620171180
  OpenClaw       restart the gateway to activate: systemctl --user restart openclaw-gateway.service
  OpenClaw       capture starts on the NEXT turn — earlier turns are NOT backfilled

After:

{
  "meta":     { "lastTouchedVersion": "2026.4.14" },
  "agents":   { "defaults": { "model": "anthropic/claude-haiku-4-5-20251001" } },
  "channels": { "telegram": { "enabled": true } },
  "plugins":  {
    "allow":   ["telegram", "anthropic", "openai", "duckduckgo", "hivemind"],
    "entries": { "telegram": { "enabled": true }, "anthropic": { "enabled": true } }
  },
  "tools":    {
    "profile":   "coding",
    "alsoAllow": ["memory_store", "hivemind"]
  }
}

Assertions:

  • plugins.allow contains "hivemind"
  • tools.alsoAllow contains "hivemind"
  • Unrelated keys preserved (meta, agents.defaults.model, channels.telegram.enabled, plugins.entries.{telegram,anthropic}) ✅
  • Backup file written: openclaw.json.bak-hivemind-1778620171180
  • Plugin files on disk at .openclaw/extensions/hivemind/: .hivemind_version, dist/, openclaw.plugin.json, package.json, skills/

Idempotency — re-running on the same fixture:

$ HOME=/tmp/... node bundle/cli.js claw install
  OpenClaw       installed -> .openclaw/extensions/hivemind
  OpenClaw       allowlist already covers hivemind in .openclaw/openclaw.json

Backup file count after re-run: 1 (unchanged — no spurious second backup). ✅

Before this fix, the same hivemind claw install against the same starting config would have left openclaw.json untouched: files would land on disk under extensions/hivemind/, but the gateway would refuse to load the plugin (plugins.entries.hivemind: plugin disabled (not in allowlist) but config is present per openclaw's own loader warning), so auto-capture would silently no-op and /hivemind_setup would be unreachable from inside the agent. That's exactly the symptom in #121.

@kaghni kaghni force-pushed the fix/openclaw-plugins-allow-121 branch from 429b67a to a1ca705 Compare May 15, 2026 18:44
@kaghni
Copy link
Copy Markdown
Collaborator Author

kaghni commented May 15, 2026

Rebased onto current main (0.7.26)

Force-pushed a1ca705. Conflicts resolved:

  • Test files relocated to tests/openclaw/ (per 21df218 chore(tests): relocate agent-specific tests to tests/<agent>/) — auto-recall.test.ts and install-openclaw.test.ts moved, import paths adjusted to ../../openclaw/src/...
  • bundle/cli.js rebuilt from the merged sources
  • setup-command.test.ts — same path adjustment for detectAllowlistMissing imports

Verified after rebase:

Related but out of scope: #169

While verifying this rebase against the live gateway, I noticed the user's gateway had auto-updated to 0.7.26, replacing my branch's bundle. Investigating further found that ClawHub has already removed the hivemind plugin from its store ("This plugin does not exist or has been removed") because the openclaw bundle fails the static scan. That's tracked at #169 — separate PR, not gated on this one.

kaghni added a commit that referenced this pull request May 18, 2026
CodeRabbit caught 5 actionable issues. All addressed.

1. before_agent_start was wrongly gated by config.autoRecall

The hook handler no longer does any recall — but the registration
block was still wrapped in `if (config.autoRecall !== false)`. For
users with autoRecall: false, that silently disabled the login URL
nudge AND the post-auth welcome banner. Removed the gate: the hook
always registers; the body only does auth-state work which is
unrelated to recall.

2. tools.alsoAllow coercion to [] was patching default-allow setups

The original logic at `Array.isArray(tools.alsoAllow) ? ... : []`
treated an absent alsoAllow as an empty array and then always
patched it to ["hivemind"], flipping the user from default-allow
into restrictive explicit-allowlist mode. Now matches the same
explicit-non-empty-only contract used for plugins.allow: only
patch when the user has opted into an explicit list. Absent /
empty → leave alone.

Tests updated:
 - was: "handles config where alsoAllow is missing entirely"
   expecting ["hivemind"] to be created
 - now: "does NOT create alsoAllow when missing" expecting it to
   stay undefined; plus a new case for empty-array

3. JSON.parse can return null; properties dereferenced unsafely

Added a shape check after JSON.parse in both ensureHivemindAllowlisted
and detectAllowlistMissing. JSON.parse("null") returns null, and
JSON.parse("[...]") returns an array — both would crash on .plugins
or .tools access. Now returns a structured "openclaw config is not a
JSON object" error.

Added a test for the malformed-JSON path so we don't regress.

4. installer was silent on patch-write failure

installOpenclaw() ignored `result.status === "error"`. If the
allowlist patch fails for a non-no-op reason (malformed JSON,
unwritable file), the user wouldn't know why the plugin doesn't
load. Now: keep the silent path for the common "config file not
found" case (gateway never started), but surface any other error
via warn(). CodeRabbit's exact suggestion.

5. Manual-fix error text was too strict

The slash-command failure message said "add hivemind to BOTH
arrays", which could mislead users with default-allow configs
into creating explicit allowlists. Reworded to make conditional
on each array existing as a non-empty list.

6. Vitest matcher pattern in auto-recall.test.ts

`.resolves.not.toThrow()` is invalid when the promise resolves
to `undefined` (not a function). Switched to
`.resolves.toBeUndefined()` which actually asserts the resolved
value and naturally surfaces any rejection.

Test: 2404/2404 passing. Typecheck clean. Bundle regenerated.
@kaghni
Copy link
Copy Markdown
Collaborator Author

kaghni commented May 18, 2026

Thanks @coderabbitai — addressed 5 of the 6 findings on the latest push (7451e75 + bundle regen on 3b2f6cc). One left as intentional.

Fixed:

# Finding Action
1 before_agent_start wrongly gated by config.autoRecall Removed the gate. Hook always registers; body is auth/welcome only. Users with autoRecall: false would otherwise have lost the login URL nudge AND post-auth welcome banner.
2 tools.alsoAllow coercion to [] always patched Changed to explicit-non-empty-only contract (matches plugins.allow). Absent/empty → leave alone. Test updated from "creates [\"hivemind\"]" to "stays undefined". Added a new test for the empty-array case.
3 JSON.parse null-check Added in both ensureHivemindAllowlisted and detectAllowlistMissing. Returns structured "openclaw config is not a JSON object" error. Added a malformed-JSON test.
4 Installer silent on patch-write failure Kept the silent path for the common "config file not found" case (gateway never started), but surface other errors via warn().
5 Manual-fix message said "add to BOTH arrays" unconditionally Reworded to be conditional on each array existing as a non-empty list.
6 .resolves.not.toThrow() invalid for undefined-resolving promise Switched to .resolves.toBeUndefined() in tests/openclaw/auto-recall.test.ts.

Not changed:

  • The Hermes hook command path quoting (bundle/cli.js line ~3?) is unrelated to this PR's scope (plugins.allow + auto-recall). Worth filing as its own issue if it's a real concern — recommend doing so against src/cli/install-hermes.ts rather than fixing in the regenerated bundle file.

2404/2404 tests pass after the changes. Typecheck clean. Bundle regenerated.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

@kaghni — great work getting through all five; the resolutions look well-reasoned:

  • #1 (autoRecall gate): Agreed — removing the gate is the right call. Auth nudge and welcome banner are orthogonal to the recall setting and must always run.
  • #2 (tools.alsoAllow coercion): The explicit-non-empty-only contract now correctly mirrors plugins.allow semantics, and the new empty-array test closes the gap cleanly.
  • #3 (null-check): Structured error on non-object parse result is better than letting it silently fall through.
  • #4 (installer write failure): The split — silent on missing config, warn() on unexpected errors — is a sensible UX distinction.
  • #5 (manual-fix message): Conditional wording is more accurate and avoids confusing users in default-allow configs.
  • #6 (.resolves.toBeUndefined()): Correct fix; .resolves.not.toThrow() on a void-resolving promise was always vacuously passing.

On the Hermes path quoting — your reasoning is sound; it's a pre-existing concern in the install-hermes path, unrelated to this PR's surface. Would you like me to open a GitHub issue against src/cli/install-hermes.ts so it doesn't get lost? I can file it now and link back to this PR.

I'll go ahead and resolve my addressed comments.

(ノ◕ヮ◕)ノ*:・゚✧

✅ Actions performed

Comments resolved. Approval is disabled; enable reviews.request_changes_workflow to allow explicit top-level @coderabbitai resolve or @coderabbitai approve commands.

kaghni added a commit that referenced this pull request May 18, 2026
CodeRabbit caught 5 actionable issues. All addressed.

1. before_agent_start was wrongly gated by config.autoRecall

The hook handler no longer does any recall — but the registration
block was still wrapped in `if (config.autoRecall !== false)`. For
users with autoRecall: false, that silently disabled the login URL
nudge AND the post-auth welcome banner. Removed the gate: the hook
always registers; the body only does auth-state work which is
unrelated to recall.

2. tools.alsoAllow coercion to [] was patching default-allow setups

The original logic at `Array.isArray(tools.alsoAllow) ? ... : []`
treated an absent alsoAllow as an empty array and then always
patched it to ["hivemind"], flipping the user from default-allow
into restrictive explicit-allowlist mode. Now matches the same
explicit-non-empty-only contract used for plugins.allow: only
patch when the user has opted into an explicit list. Absent /
empty → leave alone.

Tests updated:
 - was: "handles config where alsoAllow is missing entirely"
   expecting ["hivemind"] to be created
 - now: "does NOT create alsoAllow when missing" expecting it to
   stay undefined; plus a new case for empty-array

3. JSON.parse can return null; properties dereferenced unsafely

Added a shape check after JSON.parse in both ensureHivemindAllowlisted
and detectAllowlistMissing. JSON.parse("null") returns null, and
JSON.parse("[...]") returns an array — both would crash on .plugins
or .tools access. Now returns a structured "openclaw config is not a
JSON object" error.

Added a test for the malformed-JSON path so we don't regress.

4. installer was silent on patch-write failure

installOpenclaw() ignored `result.status === "error"`. If the
allowlist patch fails for a non-no-op reason (malformed JSON,
unwritable file), the user wouldn't know why the plugin doesn't
load. Now: keep the silent path for the common "config file not
found" case (gateway never started), but surface any other error
via warn(). CodeRabbit's exact suggestion.

5. Manual-fix error text was too strict

The slash-command failure message said "add hivemind to BOTH
arrays", which could mislead users with default-allow configs
into creating explicit allowlists. Reworded to make conditional
on each array existing as a non-empty list.

6. Vitest matcher pattern in auto-recall.test.ts

`.resolves.not.toThrow()` is invalid when the promise resolves
to `undefined` (not a function). Switched to
`.resolves.toBeUndefined()` which actually asserts the resolved
value and naturally surfaces any rejection.

Test: 2404/2404 passing. Typecheck clean. Bundle regenerated.
@kaghni kaghni force-pushed the fix/openclaw-plugins-allow-121 branch from 7451e75 to 7a951f9 Compare May 18, 2026 06:25
Comment thread openclaw/src/index.ts
Comment on lines -1040 to -1057
const grepParams: GrepMatchParams = {
pattern: keywords.join(" "),
ignoreCase: true,
wordMatch: false,
filesOnly: false,
countOnly: false,
lineNumber: false,
invertMatch: false,
fixedString: true,
};
const searchOpts = buildGrepSearchOptions(grepParams, "/");
searchOpts.limit = 10;
const rows = await searchDeeplakeTables(dl, memoryTable, sessionsTable, searchOpts);
if (!rows.length) return;

const recalled = rows
.map(r => {
const body = normalizeContent(r.path, r.content);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this part removed?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@efenocchi yes — the entire before_agent_start recall block is gone. The hook handler now only runs two narrow paths: the login URL nudge (when no creds) and the post-auth welcome banner (once after device-flow). No more extractKeywords, no more searchDeeplakeTables call, no more <recalled-memories> injection.

Recall is now agent-initiated via the registered tools (hivemind_search, hivemind_read, hivemind_index) — matches how CC/Codex/Cursor/Hermes/Pi already work (their Grep PreToolUse intercept is the same pattern: lazy, on agent demand). The full diff for the removal is at openclaw/src/index.ts:991-1031 on the latest commit.

Note: CodeRabbit also caught (and I fixed) that I'd left the entire hook wrapped in if (config.autoRecall !== false) — that would have disabled the auth nudge + welcome banner for users with autoRecall: false. Now unconditional.

kaghni added 5 commits May 18, 2026 18:12
…call

Two related fixes for openclaw users (issue #121):

1. plugins.allow gating: the installer drops files into
   ~/.openclaw/extensions/hivemind/ but never touches the gateway's
   openclaw.json. If plugins.allow is an explicit array missing
   "hivemind", openclaw refuses to load the plugin — auto-capture
   silently does nothing because agent_end never fires, and
   /hivemind_setup is unreachable from inside the agent
   (chicken-and-egg: the slash command needs the plugin to load).

   src/cli/install-openclaw.ts now calls the same
   ensureHivemindAllowlisted helper that /hivemind_setup uses, so the
   installer patches plugins.allow + tools.alsoAllow at install time
   (when each is an explicit array). Mirrors openclaw's own
   ensurePluginAllowlisted semantics: never creates the array if
   absent, never touches it if empty — default-allow stays
   default-allow so we don't flip the user into restrictive mode and
   break other plugins.

   Adds a restart hint + "capture starts on the next turn — no
   backfill" caveat to installer output so users aren't confused
   when older turns don't appear.

2. blocking auto-recall removed: before_agent_start used to run a
   searchDeeplakeTables query across memory + sessions on every user
   turn. With Deeplake's sessions-table latency varying from 200ms
   to 10s+ (measured 11.7s/10.8s/11.2s across three sequential
   probes), every openclaw turn was paying up to 10s of blocking
   recall before the agent could even read the message. Other
   agents (claude-code/codex/cursor/hermes/pi) don't do this — they
   intercept the agent's own Grep tool calls and let the agent
   decide when to search.

   openclaw now matches that pattern: recall flows exclusively
   through the registered hivemind_search/_read/_index tools, with
   the SKILL.md body in the system prompt directing the agent to
   call them. Side effect: the recursive <recalled-memories>
   capture (the old recall context was getting captured as part of
   the user's prompt and stored) is gone too.

   The before_agent_start hook still handles the login nudge and
   the post-auth welcome banner — those don't touch Deeplake.

Tests:
 - openclaw/tests/setup-command.test.ts: 9 new cases covering
   plugins.allow patching, idempotency, default-allow semantics,
   both-arrays patched, detectAllowlistMissing extension
 - openclaw/tests/install-openclaw.test.ts (new): 12 cases driving
   the real installer against tmpdir fixtures — file copy, both
   patches, idempotency, default-allow preservation, malformed
   config tolerance, restart hint, no-duplicate
 - openclaw/tests/auto-recall.test.ts: rewritten — asserts no
   Deeplake call on a normal turn, no <recalled-memories>
   injection, hivemind_search/_read/_index tools still registered,
   login nudge path preserved

Real-world E2E on a live openclaw gateway:
 - Bundle installed → "Hivemind plugin registered" in gateway log
 - Idempotency: re-running installer logs "allowlist already covers
   hivemind", no extra backup
 - Zero "Auto-recall failed" log lines after the restart with the
   new bundle (previous bundle emitted them on every turn that hit
   Deeplake slowness)

Caveat: capture (agent_end → INSERT into sessions) still hits the
same Deeplake-side timeout intermittently. That's a separate
performance issue and runs after the agent responds, so it doesn't
block the user — but it does drop some turns from memory until
Deeplake's sessions queries are faster.

Confidence: high, because the unit tests cover every edge case of
the new config-patching logic, the local fixture E2E drove the real
installer end-to-end against multiple fixture shapes, and the live
gateway confirms the bundle loads + the recall hook no longer fires.

Untested: openclaw versions older than 2026.4.21 (where the
auto-promote-via-tools.alsoAllow heuristic doesn't exist) — the
manual fix from the original reporter implies they would have been
the broken-state case; the installer's plugins.allow patch is the
right fix for them, but we couldn't reproduce that exact host state
on the 2026.4.21 gateway available for testing.

Fixes #121
CI 'Verify bundle/ directories are up to date' check failed on 7e0cccf
because the previous import-path fix in tests/openclaw/setup-command.test.ts
changed module-graph shape enough to renumber esbuild's deduped imports
(readFileSync16 → readFileSync17, existsSync21 → existsSync22, etc.).
The cli.js bundle pulls in transitively-related code so its hashed
identifiers shift.

Source-only commits don't pass the bundle-drift check because the
release pipeline ships pre-built artifacts. Catch and commit them
together going forward (or rebuild from the repo root, not a deeper
worktree, where esbuild's path comments differ too).

No code-behavior changes — just regenerated artifact.
CodeRabbit caught 5 actionable issues. All addressed.

1. before_agent_start was wrongly gated by config.autoRecall

The hook handler no longer does any recall — but the registration
block was still wrapped in `if (config.autoRecall !== false)`. For
users with autoRecall: false, that silently disabled the login URL
nudge AND the post-auth welcome banner. Removed the gate: the hook
always registers; the body only does auth-state work which is
unrelated to recall.

2. tools.alsoAllow coercion to [] was patching default-allow setups

The original logic at `Array.isArray(tools.alsoAllow) ? ... : []`
treated an absent alsoAllow as an empty array and then always
patched it to ["hivemind"], flipping the user from default-allow
into restrictive explicit-allowlist mode. Now matches the same
explicit-non-empty-only contract used for plugins.allow: only
patch when the user has opted into an explicit list. Absent /
empty → leave alone.

Tests updated:
 - was: "handles config where alsoAllow is missing entirely"
   expecting ["hivemind"] to be created
 - now: "does NOT create alsoAllow when missing" expecting it to
   stay undefined; plus a new case for empty-array

3. JSON.parse can return null; properties dereferenced unsafely

Added a shape check after JSON.parse in both ensureHivemindAllowlisted
and detectAllowlistMissing. JSON.parse("null") returns null, and
JSON.parse("[...]") returns an array — both would crash on .plugins
or .tools access. Now returns a structured "openclaw config is not a
JSON object" error.

Added a test for the malformed-JSON path so we don't regress.

4. installer was silent on patch-write failure

installOpenclaw() ignored `result.status === "error"`. If the
allowlist patch fails for a non-no-op reason (malformed JSON,
unwritable file), the user wouldn't know why the plugin doesn't
load. Now: keep the silent path for the common "config file not
found" case (gateway never started), but surface any other error
via warn(). CodeRabbit's exact suggestion.

5. Manual-fix error text was too strict

The slash-command failure message said "add hivemind to BOTH
arrays", which could mislead users with default-allow configs
into creating explicit allowlists. Reworded to make conditional
on each array existing as a non-empty list.

6. Vitest matcher pattern in auto-recall.test.ts

`.resolves.not.toThrow()` is invalid when the promise resolves
to `undefined` (not a function). Switched to
`.resolves.toBeUndefined()` which actually asserts the resolved
value and naturally surfaces any rejection.

Test: 2404/2404 passing. Typecheck clean. Bundle regenerated.
The rebase used `--theirs` to skip the merge of an old-source-vs-new-source
bundle/cli.js conflict (the generated artifact differs because the source
rebased cleanly). `npm run build` regenerates the correct artifact from
the rebased source.
@kaghni kaghni force-pushed the fix/openclaw-plugins-allow-121 branch from 7a951f9 to ad96d8b Compare May 18, 2026 18:15
@kaghni kaghni merged commit 1a43099 into main May 18, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenClaw installer doesn't patch plugins.allow → plugin loads to disk but never registers; auto-capture silently no-ops

2 participants