@totalreclaw/totalreclaw 3.3.8-rc.1 → 3.3.9-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,71 @@ All notable changes to `@totalreclaw/totalreclaw` (the OpenClaw plugin) are docu
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [3.3.9-rc.2] — 2026-05-02
8
+
9
+ Patch release fixing the two OpenClaw 2026.5.x compatibility blockers discovered during 3.3.9-rc.1 auto-QA ([umbrella #224](https://github.com/p-diogo/totalreclaw-internal/issues/224)).
10
+
11
+ ### Fixed
12
+
13
+ - **Finding #1 — memory slot not auto-assigned on install ([#225](https://github.com/p-diogo/totalreclaw-internal/issues/225)):** OpenClaw 2026.5.x introduced memory-slot exclusivity — a `kind: memory` plugin must explicitly claim `plugins.slots.memory` in `openclaw.json` or it is silently disabled ("memory slot set to memory-core"). Previously required manual config edit with no user-visible error. The plugin's `register()` now calls `patchOpenClawConfig()` at startup, which idempotently writes `plugins.slots.memory = "totalreclaw"` if absent. If the file was patched, a warn is emitted asking for a gateway restart.
14
+
15
+ - **Finding #2 — conversation hooks blocked by missing `allowConversationAccess` ([#226](https://github.com/p-diogo/totalreclaw-internal/issues/226)):** OpenClaw 2026.5.x requires `plugins.entries.totalreclaw.hooks.allowConversationAccess = true` for non-bundled plugins to receive `agent_end` and `before_agent_start` hooks. Without it, auto-extraction and recall injection are silently disabled — the hooks simply never fire. The same `patchOpenClawConfig()` call now idempotently writes this key alongside the slot claim.
16
+
17
+ ### Implementation notes
18
+
19
+ - `patchOpenClawConfig()` added to `fs-helpers.ts` — synchronous, best-effort, scanner-safe (pure `node:fs` / `node:path`, no network markers).
20
+ - Called early in `register()` (after install-staging cleanup, before LLM init). Idempotent — if both keys are already correct the file is not touched.
21
+ - Results: `'patched'` (file mutated — warn + restart instruction), `'unchanged'` (no-op), `'skipped'` (no `openclaw.json` found — non-OpenClaw host), `'error'` (parse/write failure — warn with manual instructions).
22
+ - **Restart required when patched:** OpenClaw reads `openclaw.json` at startup. When this runs on the FIRST boot after install, it patches the file and emits the restart warning. After the user restarts, the second boot picks up the keys and all hooks fire normally.
23
+
24
+ ## [3.3.9-rc.1] — 2026-05-02
25
+
26
+ Pedro's QA on 3.3.8-rc.1 (Telegram → canonical OpenClaw 2026.5.2) revealed five issues that all stem from the same architectural problem — TotalReclaw's tools registered via `api.registerTool()` are blocked by OpenClaw 2026.5.2's tool-policy strip race (issue #223, filed upstream). Each release we ship adds a fix for one gate, hits another. 3.3.9-rc.1 pivots to hybrid-primary: the `tr` CLI is now the PRIMARY path. Native tools are kept for back-compat only.
27
+
28
+ ### Five issues from Pedro's 3.3.8-rc.1 QA — root causes and fixes:
29
+
30
+ 1. **Agent hallucinated "without an account, memories are local only"** — FALSE. TR is relay-based, no local storage. Fix: SKILL.md now has a "CRITICAL: Relay-based architecture" section at the top with an explicit forbidden-vocabulary denylist (`local`, `local-only`, `stored locally`, `on disk`, `without an account`, `local memory`, `local storage`). Both `skill/plugin/SKILL.md` and `skill/SKILL.md` updated.
31
+
32
+ 2. **Agent did not autonomously emit account-setup URL+PIN after install** — asked user instead. Fix: SKILL.md now makes step 4 (running `tr pair --json`) UNCONDITIONAL — no "Want me to set up an account?" gate. The URL+PIN is the consent moment.
33
+
34
+ 3. **Pair tool reply did NOT include the URL** ("open in your browser, enter PIN" with no link). Fix: `tr pair --json` output documented in SKILL.md with explicit JSON shape `{"url":"...","pin":"...","expires_at":"..."}`. Agent is instructed to parse and emit URL verbatim.
35
+
36
+ 4. **`totalreclaw_pair` not bound after install, `/totalreclaw-restart` did not restore binding** — root cause is issue #223 (tool-policy strip race). Fix: SKILL.md drops the "wait for tool-bind / /totalreclaw-restart / /new" cycle from the user-facing path. `tr status --json` replaces the tool-binding check. Agent no longer depends on `totalreclaw_*` binding.
37
+
38
+ 5. **Pair-page POST `/respond` returned 502 then 400** — separate relay bug, out of scope for this RC.
39
+
40
+ ### Changed — SKILL.md (both `skill/plugin/SKILL.md` and `skill/SKILL.md`):
41
+
42
+ - Added "CRITICAL: Relay-based architecture" section at top with positive assertion and forbidden-vocabulary denylist.
43
+ - Rewritten setup flow: Step 1 install → Step 2 verify CLI (`tr status --json`) → Step 3 credentials check → Step 4 emit URL+PIN via `tr pair --json` (unconditional) → Step 5 confirm.
44
+ - Dropped the entire "wait for tool-bind, restart with /totalreclaw-restart, fall back to /new" cycle.
45
+ - Added "tr CLI reference" section documenting `--json` flag shapes for all commands.
46
+ - Autonomous account setup: removed consent gate, agent runs `tr pair --json` immediately.
47
+ - User-visible line set updated: line 2 now confirms "hybrid mode"; line 3 emitted immediately from `tr pair --json` output.
48
+
49
+ ### Changed — `tr-cli.ts` → CLI JSON-first output:
50
+
51
+ - `tr status --json` → `{"version":"3.3.9-rc.1","onboarded":bool,"next_step":"pair|none","tool_count":N,"hybrid_mode":bool}`
52
+ - `tr pair --json` → `{"url":"...","pin":"...","expires_at":"..."}` (delegated to pair-cli-relay.ts, already JSON-capable)
53
+ - `tr remember --json '<fact>'` → `{"ok":true,"id":"...","claim_count":0}`
54
+ - `tr recall --json '<query>' --limit 5` → `{"results":[{"text":"...","score":0.8}]}`
55
+ - Plain text mode kept for direct user CLI use; `--json` flag required for agent shell calls.
56
+ - Updated plugin version reference to `3.3.9-rc.1` in pair delegation.
57
+
58
+ ### New tests:
59
+
60
+ - `skill-md-hybrid-primary.test.ts` — asserts SKILL.md contains hybrid-primary instructions, forbidden-vocabulary denylist, relay-based architecture assertion, and autonomous pair call.
61
+ - `tr-cli-json-output.test.ts` — asserts each `tr <cmd> --json` code path returns parseable JSON with expected keys (via static source analysis).
62
+
63
+ ### Not changed:
64
+
65
+ - Hooks (`before_agent_start`, `agent_end`, `message_received`, `before_reset`) — not affected by issue #223, unchanged.
66
+ - HTTP routes (4 QR-pairing routes) — unchanged.
67
+ - `/totalreclaw-restart` slash command — kept for environments where users want a manual reload.
68
+ - `api.registerTool()` calls — kept for back-compat (older OpenClaw versions where tools work).
69
+ - Relay code — 502/400 pair page bugs are out of scope.
70
+ - Hermes Python code — separate runtime.
71
+
7
72
  ## [3.3.7-rc.3] — 2026-05-04
8
73
 
9
74
  Root cause of tool-binding gap on OpenClaw 2026.5.2: the plugin manifest (`openclaw.plugin.json`) was missing the `contracts.tools` declaration that OpenClaw 2026.5.2 now requires before any non-bundled plugin may register agent tools. Without it, every `api.registerTool()` call is silently rejected — `openclaw plugins doctor` shows 17× "plugin must declare contracts.tools before registering agent tools". The gateway loads the plugin module (ESM import succeeds, `plugins.allow` check passes) but no tools bind to any session, so `totalreclaw_pair` / `totalreclaw_remember` / etc. are invisible to the agent and auto-QA step 1 fails.
package/SKILL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: totalreclaw
3
- description: "Install + set up TotalReclaw encrypted memory for OpenClaw, then use totalreclaw_remember / totalreclaw_recall. Trigger on 'install TotalReclaw', 'set up TotalReclaw', 'restore my recovery phrase', or any remember / recall request."
4
- version: 3.3.8-rc.1
3
+ description: "Install + set up TotalReclaw encrypted memory for OpenClaw, then use tr CLI for remember / recall. Trigger on 'install TotalReclaw', 'set up TotalReclaw', 'restore my recovery phrase', or any remember / recall request."
4
+ version: 3.3.9-rc.2
5
5
  author: TotalReclaw Team
6
6
  license: MIT
7
7
  homepage: https://totalreclaw.xyz
@@ -26,9 +26,33 @@ metadata:
26
26
 
27
27
  > The canonical install flow lives at <https://github.com/p-diogo/totalreclaw/blob/main/docs/guides/openclaw-setup.md>. If a user pastes that URL in chat, fetch it and follow its agent-instructions section. This SKILL.md mirrors the same steps so you can act even if the URL is unreachable.
28
28
 
29
+ ## CRITICAL: Relay-based architecture (read before anything else)
30
+
31
+ **TotalReclaw is RELAY-BASED. All memories require a paired account on the staging or production relay (depending on RC vs stable). There is NO local-only mode. NEVER claim memories are stored locally without an account.**
32
+
33
+ Forbidden vocabulary — these claims are FALSE and MUST NEVER appear in your responses:
34
+ - "local"
35
+ - "local-only"
36
+ - "stored locally"
37
+ - "on disk"
38
+ - "without an account"
39
+ - "local memory"
40
+ - "local storage"
41
+ - any phrasing suggesting memories can be stored without pairing a relay account
42
+
43
+ If you find yourself about to say any of these, STOP. The correct statement is: "TotalReclaw requires a paired relay account to store memories — run `tr pair` to set one up."
44
+
29
45
  ## Top-level rule (read this before anything else)
30
46
 
31
- **Emit ONLY the user-visible lines listed below. Do not narrate tool calls. Do not describe what you're doing or about to do. Errors are the only allowed deviation, and only as a single tight line.** The expected user-facing transcript for a healthy install is **5–6 short lines TOTAL** (lines 1, 2, 5, 6 always; lines 3 and 4 only if the corresponding condition fires). Anything more is a SKILL violation. Telegram and similar transports edit the bot's messages live as your tool calls progress — every interim "Let me check…" / "Plugin loaded. Let me verify…" line shows up to the user as awkward edit churn, even if you "delete" it. The fix is to never emit it in the first place.
47
+ **Emit ONLY the user-visible lines listed below. Do not narrate tool calls. Do not describe what you're doing or about to do. Errors are the only allowed deviation, and only as a single tight line.** The expected user-facing transcript for a healthy install is **4–5 short lines TOTAL**. Anything more is a SKILL violation. Telegram and similar transports edit the bot's messages live as your tool calls progress — every interim "Let me check…" / "Plugin loaded. Let me verify…" line shows up to the user as awkward edit churn, even if you "delete" it. The fix is to never emit it in the first place.
48
+
49
+ ## Hybrid-primary architecture (3.3.9-rc.1)
50
+
51
+ OpenClaw 2026.5.2 has a tool-policy-pipeline bug (issue #223) that strips non-bundled plugin tools before they reach the agent toolset. **3.3.9-rc.1 makes the `tr` CLI the PRIMARY path for all memory operations.** Native tools (`totalreclaw_*`) are kept for back-compat with older OpenClaw versions where they work, but the agent MUST NOT depend on them. Use `tr` from your shell tool for everything.
52
+
53
+ **The `tr` binary is installed at:** `~/.openclaw/extensions/totalreclaw/dist/tr-cli.js`
54
+ **Always invoke via:** `node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js <cmd> [--json]`
55
+ **Never rely on `totalreclaw_*` tool binding** — it may not be available due to issue #223.
32
56
 
33
57
  ## User-visible line set (THIS IS ALL YOU SAY)
34
58
 
@@ -36,70 +60,70 @@ Speak only these lines, in order. Substitute the bracketed placeholders with the
36
60
 
37
61
  1. (start of install)
38
62
  > Installing TotalReclaw…
39
- 2. (after install + auto-reload + `totalreclaw_pair` is bound)
40
- > ✓ Installed (totalreclaw &lt;version&gt;)
41
- 3. (optional only if auto-reload didn't fire and you need to issue `/totalreclaw-restart`)
42
- > Restarting OpenClaw to load the plugin…
43
- 4. (optional — only if you successfully disabled the built-in memory tool to prevent dual-write)
44
- > Disabled built-in memory tool.
45
- 5. (account-setup URL + PIN, immediately after `totalreclaw_pair` returns)
63
+ 2. (after install + CLI verify `tr status` works)
64
+ > ✓ Installed (totalreclaw &lt;version&gt;, hybrid mode)
65
+ 3. (account-setup URL + PIN, IMMEDIATELY after `tr pair --json` returns no consent gate)
46
66
  > Open &lt;url&gt; in your browser. Enter PIN &lt;pin&gt;. Generate or paste a 12-word recovery phrase. Reply `done` once it's sealed.
47
- 6. (done + free-tier confirmation)
67
+ 4. (done + free-tier confirmation)
48
68
  > ✓ You're set up on the TotalReclaw free tier — 500 memories/month, unlimited reads. https://totalreclaw.xyz/pricing. Ask me to remember anything.
49
69
 
50
70
  If existing credentials are detected (step "Check for existing credentials" below), emit ONLY this line and stop:
51
71
  > ✓ TotalReclaw is already set up.
52
72
 
53
- ## Setup flow
54
-
55
- If `totalreclaw_*` tools are already bound to your toolset, SKIP install and jump to step 4. Otherwise run all steps in order.
73
+ ## Setup flow (hybrid-primary)
56
74
 
57
75
  1. **Install plugin first, skill second (silent — emit user-visible line 1 once at the start, then no prose until line 2).** Run via your shell tool, in this exact order:
58
76
  ```
59
77
  openclaw plugins install @totalreclaw/totalreclaw
60
78
  openclaw skills install totalreclaw
61
79
  ```
62
- For RC: replace the first command with `openclaw plugins install @totalreclaw/totalreclaw@rc`. **Plugin BEFORE skill is deliberate** (3.3.4-rc.1 — issue from Pedro's QA on rc.3): the skill install triggers a config-driven SIGUSR1 gateway reload that can race a concurrent plugin install. Plugin-first lets the plugin land cleanly, then the skill's reload picks it up on the next gateway loop. If you accidentally do skill-first and the plugin install dies mid-flight, just retry `openclaw plugins install @totalreclaw/totalreclaw` once — it's idempotent.
80
+ For RC: replace the first command with `openclaw plugins install @totalreclaw/totalreclaw@rc`. **Plugin BEFORE skill is deliberate** (3.3.4-rc.1 — issue from Pedro's QA on rc.3): the skill install triggers a config-driven SIGUSR1 gateway reload that can race a concurrent plugin install.
63
81
 
64
82
  If either shell call fails with `command not found` / ENOENT / "not authorized" (managed-service or shell-restricted environment), tell the user verbatim *"Your environment doesn't expose the OpenClaw CLI to me. Please install `totalreclaw` from your service's plugins / skills UI, then reply `done`."* Wait for `done` and skip to step 3.
65
83
 
66
- 2. **Wait for auto-reloadthen issue `/totalreclaw-restart` autonomously if needed (silent — emit user-visible line 2 on success, line 3 only if you had to restart).** OpenClaw's default config (`gateway.reload.mode = "hybrid"`) auto-restarts the gateway via a file-watcher within 1-3 seconds. Wait ~5s, then run `openclaw plugins list | grep totalreclaw` and confirm `totalreclaw_pair` is in your toolset. When bound, emit user-visible line 2 and proceed.
67
-
68
- **If `totalreclaw_pair` is NOT visible after 10s — restart imperative (read carefully):**
69
-
70
- > Your IMMEDIATE next message must be the literal slash command `/totalreclaw-restart` nothing else. Do not narrate. Do not ask permission. Do not describe what `/totalreclaw-restart` does. Do not propose alternatives. Just send `/totalreclaw-restart`. You may emit user-visible line 3 (`Restarting OpenClaw to load the plugin…`) ONCE in the same turn, then issue `/totalreclaw-restart`. Nothing else.
84
+ 2. **Verify CLI works — emit user-visible line 2 when successful.** Run:
85
+ ```
86
+ node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js status --json
87
+ ```
88
+ Parse the JSON. When you get `{"version": "...", "hybrid_mode": true, ...}`, emit user-visible line 2 with the version substituted. **Do NOT wait for tool binding. Do NOT issue `/totalreclaw-restart`. Do NOT check whether `totalreclaw_pair` is in your toolset.** The CLI works independently of tool binding. If `tr status` exits non-zero or the file doesn't exist, wait 5s and retry once — it may still be installing.
71
89
 
72
- `/totalreclaw-restart` is in-process it drains active runs cleanly; your shell is NOT shelling out to `docker restart` or `openclaw gateway restart` (those would suicide the host). After `/totalreclaw-restart`, wait ~3s and re-run `openclaw plugins list`. (3.3.7-rc.2 renamed this from `/restart` because OpenClaw reserves `/restart` for its built-in the plugin-side fallback can only attach to a unique name. The 5-tier auth resolver is unchanged; only the command name moved.)
90
+ 3. **Check for existing credentials (silent unless found).** Run `test -s ~/.totalreclaw/credentials.json`. If it exists and is non-empty, emit *"✓ TotalReclaw is already set up."* and stop.
73
91
 
74
- **If `/totalreclaw-restart` returns "You are not authorized to use this command":** surface this verbatim and stop. Do NOT loop `/totalreclaw-restart` calls. Do NOT propose alternatives.
92
+ 4. **Set up the user's TotalReclaw account IMMEDIATELY no consent gate.** As soon as `tr status --json` returns and credentials are not present, run:
93
+ ```
94
+ node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js pair --json
95
+ ```
96
+ This is UNCONDITIONAL. Do NOT ask "Would you like to set up an account?" or "Want me to pair now?". The user installed TotalReclaw — setup is the expected next step. The URL + PIN itself is the consent moment (the user can simply not open the page to opt out). Parse the JSON response `{"url": "...", "pin": "...", "expires_at": "..."}` and emit user-visible line 3. If `tr pair --json` is not available (old install), fall back to `totalreclaw_pair` tool or `openclaw totalreclaw pair generate --url-pin-only`.
75
97
 
76
- > Your Telegram channel needs `allowFrom` configured. Run on your host: `jq '.channels.<channel>.allowFrom = ["<your-id>"]' ~/.openclaw/openclaw.json | sponge ~/.openclaw/openclaw.json && docker restart <your-container-name>`. Substitute `<channel>` (e.g. `telegram`, `slack`), `<your-id>` (Telegram: `@username` or numeric `chat_id`; Slack: workspace member ID), and `<your-container-name>` (`docker ps` shows it). For non-Docker installs substitute `openclaw gateway restart` for the `docker restart` portion. On a managed service, ask the service's support to enable slash commands. Reply `done` after.
98
+ 5. **Verify and confirm (silent until line 4).** After the user says `done`, re-run `test -s ~/.totalreclaw/credentials.json`. If missing, the PIN expired run `tr pair --json` again and resend line 3 once. If present, emit user-visible line 4 and stop.
77
99
 
78
- Then wait for `done`. Do NOT retry the install. Do NOT keep restarting. Do NOT issue `/new` it wipes the chat context, the agent forgets it was mid-install, and the user's next message is treated as a fresh install request which retries from scratch and re-trips the scanner block (Pedro QA, 3.3.5-rc.1).
100
+ ## `tr` CLI reference (hybrid-primary commands)
79
101
 
80
- **Forbidden vocabulary** (these are anti-patterns they show up to the user even if your transport edits them later, and they violate the silence rule):
81
- - "Let me check that…"
82
- - "Plugin loaded. Let me verify…"
83
- - "I'll now…"
84
- - "Quick fix…"
85
- - "However, I don't see…"
86
- - "I need to use…"
87
- - "Should I /totalreclaw-restart" / "Do you have a public URL" / "Want me to restart" / "Let me check if the tool is bound" / "I need permission to restart"
88
- - "Now installing…" / "Got it" / "In parallel…" / "Step 1a/1b/1c"
102
+ Always use `--json` flag when running from shell for machine-parseable output. Plain text mode is for direct user invocation only.
89
103
 
90
- Substitute all of these with silence — the next user-visible line in the set, or nothing.
104
+ ```bash
105
+ # Status check (parse JSON to confirm install + onboarding state)
106
+ node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js status --json
107
+ # Returns: {"version":"3.3.9-rc.1","onboarded":false,"next_step":"pair","tool_count":17,"hybrid_mode":true}
91
108
 
92
- 3. **Verify `totalreclaw_pair` is bound.** If still missing after `/totalreclaw-restart` (or after the user fixed allowFrom and said `done`), tell them the restart didn't take effect and ask them to retry. Do not loop the install.
109
+ # Pair / account setup
110
+ node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js pair --json
111
+ # Returns: {"url":"https://...","pin":"123456","expires_at":"2026-05-05T..."}
93
112
 
94
- 4. **Check for existing credentials (silent unless found).** Run `test -s ~/.totalreclaw/credentials.json`. If it exists and is non-empty, emit *"✓ TotalReclaw is already set up."* and stop.
113
+ # Remember a fact
114
+ node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js remember --json "I work at Graph Foundation"
115
+ # Returns: {"ok":true,"id":"...","claim_count":N}
95
116
 
96
- 5. **Set up the user's TotalReclaw account (emit user-visible line 5 once, after `totalreclaw_pair` returns).** Call `totalreclaw_pair` silently (the account-setup tool — name kept for backward compatibility). It returns `{url, pin, qr_ascii, qr_png_b64, qr_unicode, expires_at_ms}` (see "Rendering the QR" below). Then emit user-visible line 5 with `<url>` and `<pin>` substituted. Do not pre-narrate ("I'll walk you through…", "Setting up your account now…") — line 5 itself is the only thing the user needs to see. The URL + PIN itself is the real consent moment: surfacing them verbatim is what gives the user the chance to opt out by simply not opening the page.
117
+ # Recall memories
118
+ node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js recall --json "where does the user work" --limit 5
119
+ # Returns: {"results":[{"text":"...","score":0.8},...]}
120
+ ```
97
121
 
98
- 6. **Verify and confirm (silent until line 6).** After the user says `done`, re-run `test -s ~/.totalreclaw/credentials.json`. If missing, the PIN expired call `totalreclaw_pair` again silently and resend line 5 once. If present, emit user-visible line 6 and stop.
122
+ **Note on tool names**: `totalreclaw_remember`, `totalreclaw_recall`, etc. may or may not appear in your toolset depending on OpenClaw version. Under hybrid-primary, you use the `tr` CLI instead. If native tools ARE available (older OpenClaw or future OC fix), they work too but always prefer `tr` for reliability.
99
123
 
100
124
  ## Rendering the QR on your transport (rc.5+)
101
125
 
102
- When you receive the `totalreclaw_pair` payload, render the QR based on your chat transport:
126
+ When you receive the `totalreclaw_pair` payload (from tool OR `tr pair --json`), render the QR based on your chat transport:
103
127
 
104
128
  - **Transport supports image attachments** (Telegram, Slack, web chat, any transport with file/image upload): decode `qr_png_b64` from base64 to bytes, save to a tempfile with a `.png` suffix, and attach the tempfile as an image. Also show the URL + PIN as text so users who prefer clicking can still do so.
105
129
  - **Terminal-only transport** (OpenClaw native CLI chat, bare SSH shell): emit `qr_unicode` inline in your reply. It renders as block characters in most terminal fonts; the user scans from their phone.
@@ -109,44 +133,52 @@ The QR encodes ONLY the account-setup URL. The 6-digit PIN is a separate out-of-
109
133
 
110
134
  ## Phrase safety (HARD — never break)
111
135
 
112
- NEVER echo / generate / ask the user to paste a recovery phrase in chat. NEVER invoke `openclaw totalreclaw onboard` (bare interactive form), `totalreclaw setup`, or any phrase-touching CLI via your shell tool (stdout enters LLM context). The ONLY agent-facilitated account-setup paths are (in order of preference): (1) the `totalreclaw_pair` account-setup tool; (2) if the tool is missing from your tool list a known gap for some container-based agents, issue #87 — shell out to EITHER `openclaw totalreclaw pair generate --url-pin-only` (single-line JSON `{v,url,pin,expires_at_ms}`) OR `openclaw totalreclaw onboard --pair-only` (single-line JSON `{v,pair_url,pin,expires_at_ms}`, issue #95 — same account-setup flow, `pair_url` key naming). Both have zero phrase material on stdout (account-setup crypto is x25519-only, not BIP-39). If the user pastes a phrase anyway: tell them it is compromised, call `totalreclaw_pair` with `mode=generate` for a fresh wallet.
136
+ NEVER echo / generate / ask the user to paste a recovery phrase in chat. NEVER invoke `openclaw totalreclaw onboard` (bare interactive form), `totalreclaw setup`, or any phrase-touching CLI via your shell tool (stdout enters LLM context). The ONLY agent-facilitated account-setup paths are (in order of preference): (1) `tr pair --json`; (2) the `totalreclaw_pair` account-setup tool; (3) if both are missing — shell out to EITHER `openclaw totalreclaw pair generate --url-pin-only` (single-line JSON `{v,url,pin,expires_at_ms}`) OR `openclaw totalreclaw onboard --pair-only` (single-line JSON `{v,pair_url,pin,expires_at_ms}`). Both have zero phrase material on stdout (account-setup crypto is x25519-only, not BIP-39). If the user pastes a phrase anyway: tell them it is compromised, re-run `tr pair --json` for a fresh wallet.
113
137
 
114
138
  ## Usage (post-setup)
115
139
 
116
- - Stable user facts / preferences / identity / "remember X" → `totalreclaw_remember` (don't ask permission).
117
- - First-person factual query ("do I / what's my / where do I …") → `totalreclaw_recall` FIRST, then answer from returned facts. If 0 results, say so.
118
- - "forget / pin / unpin / retype / move to <scope>" X → `totalreclaw_recall` first for `factId`, then `totalreclaw_forget` / `_pin` / `_unpin` / `_retype` / `_set_scope`.
119
- - "status" → `totalreclaw_status`. "upgrade" → `totalreclaw_upgrade` (paste Stripe URL verbatim). Post-upgrade testnet → mainnet: `totalreclaw_migrate` with `dry_run` first.
120
- - "import / export / consolidate" `totalreclaw_import_from` (`dry_run=true` first; `_import_batch` for >50 chunks) · `totalreclaw_export` · `totalreclaw_consolidate` (`dry_run` first).
140
+ - Stable user facts / preferences / identity / "remember X":
141
+ ```
142
+ node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js remember --json "<fact>"
143
+ ```
144
+ - First-person factual query ("do I / what's my / where do I…"):
145
+ ```
146
+ node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js recall --json "<query>" --limit 5
147
+ ```
148
+ Recall FIRST, then answer from returned facts. If 0 results, say so.
149
+ - For forget / pin / unpin / retype / set_scope — use native tools if available, or ask user to run `openclaw totalreclaw` subcommand.
150
+ - "status" → `tr status --json` (parse version + onboarded + next_step).
151
+ - "upgrade" → `totalreclaw_upgrade` native tool (paste Stripe URL verbatim). Post-upgrade testnet → mainnet: `totalreclaw_migrate` with `dry_run` first.
152
+ - "import / export / consolidate" → `totalreclaw_import_from` · `totalreclaw_export` · `totalreclaw_consolidate` native tools.
153
+
154
+ **Forbidden vocabulary** (these are anti-patterns that violate the silence rule):
155
+ - "Let me check that…"
156
+ - "Plugin loaded. Let me verify…"
157
+ - "I'll now…"
158
+ - "Quick fix…"
159
+ - "However, I don't see…"
160
+ - "I need to use…"
161
+ - "Should I /totalreclaw-restart" / "Do you have a public URL" / "Want me to restart" / "Let me check if the tool is bound" / "I need permission to restart"
162
+ - "Now installing…" / "Got it" / "In parallel…" / "Step 1a/1b/1c"
163
+ - "local" / "local-only" / "stored locally" / "on disk" / "without an account" / "local memory" / "local storage"
121
164
 
122
- ## OpenClaw 2026.5.2 hybrid mode (issue #223 workaround — plugin 3.3.8-rc.1+)
123
-
124
- OpenClaw 2026.5.2 has a tool-policy-pipeline bug (issue #223) that strips non-bundled plugin tools before they reach the agent toolset. Plugin 3.3.8-rc.1 ships a hybrid workaround:
125
-
126
- - **Hooks still fire**: `before_agent_start`, `agent_end`, `message_received`, `before_reset` use a separate code path not affected by issue #223. Auto-extraction, digest injection, preloading, and billing checks all work normally.
127
- - **Tools replaced by `tr` CLI**: instead of `totalreclaw_remember`, `totalreclaw_recall`, etc., use the `tr` binary installed with the plugin. The agent runs it via shell tool.
128
-
129
- **Agent usage under hybrid mode** (check `tr status` output for `hybrid-mode: yes`):
130
-
131
- ```
132
- # remember a fact
133
- node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js remember "I work at Graph Foundation"
134
- # → outputs {"ok":true,"id":"...","text":"..."}
135
-
136
- # recall facts
137
- node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js recall "where does the user work"
138
- # → outputs {"ok":true,"count":1,"query":"...","memories":[...]}
165
+ ## Diagnostics
139
166
 
140
- # status check
141
- node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js status
142
- ```
167
+ - `tr` CLI not found → plugin not installed or install still in progress. Wait 5s, retry. If still missing, retry `openclaw plugins install @totalreclaw/totalreclaw@rc` once.
168
+ - `totalreclaw_*` tools not visible → EXPECTED under OpenClaw 2026.5.2 (issue #223). Use `tr` CLI instead. Do not restart, do not issue `/totalreclaw-restart`, do not issue `/new`.
169
+ - If `/totalreclaw-restart` is needed for another reason, issue the literal slash command `/totalreclaw-restart` — nothing else. Do not narrate.
170
+ - User says done but `credentials.json` missing → PIN expired or entered wrong phrase; re-run `tr pair --json` and resend line 3 once.
171
+ - `onboarding required` → credentials missing; redo from the account-setup step.
172
+ - `quota exceeded` → `tr status --json` or `totalreclaw_status`, then offer `totalreclaw_upgrade`.
173
+ - `No LLM available for auto-extraction` at startup → provider key unreachable; check `~/.openclaw/agents/<agent>/agent/auth-profiles.json` or plugin config `extraction.llm`.
143
174
 
144
- Note: the `tr` bin name conflicts with the system `tr` utility; always invoke via full path `node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js <cmd>` or the gateway host's equivalent path.
175
+ ## Required post-install config (OpenClaw 2026.5.2+)
145
176
 
146
- **Required post-install config** (add to `~/.openclaw/openclaw.json`):
177
+ Add to `~/.openclaw/openclaw.json` after installing:
147
178
  ```json
148
179
  {
149
180
  "plugins": {
181
+ "allow": ["totalreclaw"],
150
182
  "entries": {
151
183
  "totalreclaw": {
152
184
  "hooks": { "allowConversationAccess": true }
@@ -156,16 +188,9 @@ Note: the `tr` bin name conflicts with the system `tr` utility; always invoke vi
156
188
  }
157
189
  ```
158
190
 
159
- This unlocks typed hooks (`agent_end`, etc.) for non-bundled plugins. Without it, auto-extraction still works (extraction runs in `message_received`) but the `agent_end` hook that writes the digest is silently blocked.
160
-
161
- ## Diagnostics
162
-
163
- - `totalreclaw_*` tools not visible → plugin not installed or auto-reload didn't fire. Verify via `openclaw plugins list | grep totalreclaw` (or, on a managed service that doesn't expose the CLI, ask the user to confirm via their service's plugins UI). If `totalreclaw` is listed but tools are still absent, the gateway probably hasn't completed its auto-restart yet (give it 5-10s) OR `gateway.reload.mode = off` is set — instruct manual restart as fallback: `openclaw gateway restart` (native), `docker restart <your-container-name>` (Docker self-host), or the managed service's restart control. If the plugin is listed and the gateway has been restarted but tools are still missing (container-agent gap, issue #87 / OC 2026.5.2 issue #223), switch to hybrid mode: use `node ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js <cmd>` for remember/recall/pair/status. If your shell doesn't reach the gateway host (managed service), tell the user to install/enable the plugin via their service's plugins UI and restart the gateway via the service's restart control. Do NOT suggest the bare `openclaw totalreclaw onboard` (leaks phrase on stdout — deprecated in rc.18, removed next RC).
164
- - User says done but `credentials.json` missing → PIN expired or entered wrong phrase; call `totalreclaw_pair` again.
165
- - `onboarding required` → credentials missing; redo from the account-setup step.
166
- - `quota exceeded` → `totalreclaw_status`, then offer `totalreclaw_upgrade`.
167
- - `No LLM available for auto-extraction` at startup → provider key unreachable; check `~/.openclaw/agents/<agent>/agent/auth-profiles.json` or plugin config `extraction.llm`.
191
+ `plugins.allow` suppresses the "plugins.allow is empty" warning. `hooks.allowConversationAccess` unlocks typed hooks (`agent_end`, etc.) for auto-extraction.
168
192
 
169
193
  ## Tool surface
170
194
 
171
- `totalreclaw_pair` (ONLY account-setup path) · `_remember` · `_recall` · `_forget` · `_pin` · `_unpin` · `_retype` · `_set_scope` · `_export` · `_status` · `_upgrade` · `_migrate` · `_import_from` · `_import_batch` · `_consolidate` · `_onboarding_start` (pointer to local-terminal wizard, for users explicitly rejecting the browser flow) · `_report_qa_bug` (RC only).
195
+ Hybrid-primary: `tr remember` · `tr recall` · `tr pair` · `tr status` (primary path for all agent ops)
196
+ Native fallback (when available): `totalreclaw_pair` · `_remember` · `_recall` · `_forget` · `_pin` · `_unpin` · `_retype` · `_set_scope` · `_export` · `_status` · `_upgrade` · `_migrate` · `_import_from` · `_import_batch` · `_consolidate` · `_onboarding_start` · `_report_qa_bug` (RC only)
@@ -929,3 +929,85 @@ export function resolveOnboardingState(credentialsPath, statePath) {
929
929
  writeOnboardingState(statePath, next);
930
930
  return next;
931
931
  }
932
+ /**
933
+ * Auto-patch `~/.openclaw/openclaw.json` with the two entries required by
934
+ * OpenClaw 2026.5.x for `kind: memory` plugins (issues #225 + #226):
935
+ *
936
+ * 1. `plugins.slots.memory = "totalreclaw"`
937
+ * Claim the memory slot so the plugin loads instead of deferring to
938
+ * the built-in `memory-core` tenant.
939
+ *
940
+ * 2. `plugins.entries.totalreclaw.hooks.allowConversationAccess = true`
941
+ * Grant the plugin access to `agent_end` and `before_agent_start`
942
+ * hooks. Without this flag OpenClaw 2026.5.x silently blocks both
943
+ * hooks for non-bundled plugins, disabling auto-extraction and
944
+ * recall injection.
945
+ *
946
+ * Design constraints
947
+ * ------------------
948
+ * - SYNCHRONOUS — called during register() which must be sync.
949
+ * - IDEMPOTENT — reads existing values before deciding to write; no-ops
950
+ * when both keys are already correct.
951
+ * - BEST-EFFORT — all errors are swallowed; the plugin keeps loading even
952
+ * if the patch fails. The caller logs an actionable warning.
953
+ * - SCANNER-SAFE — pure `node:fs` / `node:path`; no outbound markers.
954
+ *
955
+ * Restart semantics
956
+ * -----------------
957
+ * OpenClaw reads `openclaw.json` at gateway startup, not dynamically.
958
+ * When `patchOpenClawConfig` writes new keys during the CURRENT gateway
959
+ * boot, the keys take effect ONLY after the gateway is restarted. The
960
+ * plugin must tell the user via `api.logger.warn` so they know to run
961
+ * `/totalreclaw-restart` or restart the gateway manually.
962
+ *
963
+ * @param configPath Absolute path to `openclaw.json`.
964
+ * Defaults to `<home>/.openclaw/openclaw.json`.
965
+ */
966
+ export function patchOpenClawConfig(configPath) {
967
+ const home = process.env.HOME ?? '/home/node';
968
+ const target = configPath ?? path.join(home, '.openclaw', 'openclaw.json');
969
+ // `'skipped'` when the config file is absent — this host may not be
970
+ // running OpenClaw, or may use a non-standard config location.
971
+ if (!fs.existsSync(target))
972
+ return 'skipped';
973
+ try {
974
+ const raw = fs.readFileSync(target, 'utf-8');
975
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
976
+ const cfg = JSON.parse(raw);
977
+ // Ensure the `plugins` key exists.
978
+ if (typeof cfg.plugins !== 'object' || cfg.plugins === null) {
979
+ cfg.plugins = {};
980
+ }
981
+ let mutated = false;
982
+ // --- Fix #1: plugins.slots.memory = "totalreclaw" ---
983
+ if (typeof cfg.plugins.slots !== 'object' || cfg.plugins.slots === null) {
984
+ cfg.plugins.slots = {};
985
+ }
986
+ if (cfg.plugins.slots.memory !== 'totalreclaw') {
987
+ cfg.plugins.slots.memory = 'totalreclaw';
988
+ mutated = true;
989
+ }
990
+ // --- Fix #2: plugins.entries.totalreclaw.hooks.allowConversationAccess = true ---
991
+ if (typeof cfg.plugins.entries !== 'object' || cfg.plugins.entries === null) {
992
+ cfg.plugins.entries = {};
993
+ }
994
+ if (typeof cfg.plugins.entries.totalreclaw !== 'object' || cfg.plugins.entries.totalreclaw === null) {
995
+ cfg.plugins.entries.totalreclaw = {};
996
+ }
997
+ if (typeof cfg.plugins.entries.totalreclaw.hooks !== 'object' || cfg.plugins.entries.totalreclaw.hooks === null) {
998
+ cfg.plugins.entries.totalreclaw.hooks = {};
999
+ }
1000
+ if (cfg.plugins.entries.totalreclaw.hooks.allowConversationAccess !== true) {
1001
+ cfg.plugins.entries.totalreclaw.hooks.allowConversationAccess = true;
1002
+ mutated = true;
1003
+ }
1004
+ if (!mutated)
1005
+ return 'unchanged';
1006
+ // Write back with 2-space indent to match OpenClaw's own write style.
1007
+ fs.writeFileSync(target, JSON.stringify(cfg, null, 2) + '\n');
1008
+ return 'patched';
1009
+ }
1010
+ catch {
1011
+ return 'error';
1012
+ }
1013
+ }
package/dist/index.js CHANGED
@@ -66,7 +66,7 @@ import { PluginHotCache } from './hot-cache-wrapper.js';
66
66
  import { CONFIG, setRecoveryPhraseOverride } from './config.js';
67
67
  import { buildRelayHeaders } from './relay-headers.js';
68
68
  import { readBillingCache, writeBillingCache, BILLING_CACHE_PATH, } from './billing-cache.js';
69
- import { ensureMemoryHeaderFile, loadCredentialsJson, writeCredentialsJson, deleteCredentialsFile, isRunningInDocker, deleteFileIfExists, resolveOnboardingState, writeOnboardingState, readPluginVersion, cleanupInstallStagingDirs, clearPartialInstallMarker, writePluginManifest, writePluginError, readPluginLoadedManifest, } from './fs-helpers.js';
69
+ import { ensureMemoryHeaderFile, loadCredentialsJson, writeCredentialsJson, deleteCredentialsFile, isRunningInDocker, deleteFileIfExists, resolveOnboardingState, writeOnboardingState, readPluginVersion, cleanupInstallStagingDirs, clearPartialInstallMarker, patchOpenClawConfig, writePluginManifest, writePluginError, readPluginLoadedManifest, } from './fs-helpers.js';
70
70
  import { isRcBuild } from './qa-bug-report.js';
71
71
  import { decideToolGate, isGatedToolName } from './tool-gating.js';
72
72
  import { resolveRestartAuth, rejectMessageFor, } from './restart-auth.js';
@@ -2531,6 +2531,45 @@ const plugin = {
2531
2531
  catch {
2532
2532
  // Best-effort. Helper logs internally and never throws.
2533
2533
  }
2534
+ // 3.3.9-rc.2 (issues #225 + #226): auto-patch openclaw.json for
2535
+ // OpenClaw 2026.5.x. Two required config keys were not auto-applied
2536
+ // by `openclaw plugins install` in 2026.5.x:
2537
+ //
2538
+ // 1. plugins.slots.memory = "totalreclaw"
2539
+ // OpenClaw 2026.5.x introduced memory-slot exclusivity — a
2540
+ // memory-kind plugin MUST explicitly claim the slot or it is
2541
+ // silently disabled (no error shown; `openclaw plugins inspect`
2542
+ // shows "memory slot set to memory-core"). #225.
2543
+ //
2544
+ // 2. plugins.entries.totalreclaw.hooks.allowConversationAccess = true
2545
+ // Non-bundled plugins in 2026.5.x require this flag to receive
2546
+ // agent_end and before_agent_start hooks. Without it, auto-
2547
+ // extraction and recall injection are silently disabled. #226.
2548
+ //
2549
+ // The patch is idempotent — if both keys are already correct the
2550
+ // file is not touched. When the file IS mutated a restart is
2551
+ // required for the new keys to take effect (OpenClaw reads
2552
+ // openclaw.json at startup, not dynamically). We emit a warn so
2553
+ // the user and ops scripts know to trigger a restart.
2554
+ try {
2555
+ const patchResult = patchOpenClawConfig();
2556
+ if (patchResult === 'patched') {
2557
+ api.logger.warn('TotalReclaw: updated openclaw.json with required 2026.5.x keys ' +
2558
+ '(plugins.slots.memory + hooks.allowConversationAccess). ' +
2559
+ 'Gateway restart required for the changes to take effect. ' +
2560
+ 'Run `/totalreclaw-restart` or restart the gateway manually.');
2561
+ }
2562
+ else if (patchResult === 'error') {
2563
+ api.logger.warn('TotalReclaw: failed to auto-patch openclaw.json for OpenClaw 2026.5.x ' +
2564
+ 'compatibility. If memory hooks are silently disabled, add these keys ' +
2565
+ 'manually: plugins.slots.memory="totalreclaw" and ' +
2566
+ 'plugins.entries.totalreclaw.hooks.allowConversationAccess=true.');
2567
+ }
2568
+ // 'unchanged' and 'skipped' are silent — no log needed.
2569
+ }
2570
+ catch {
2571
+ // Best-effort — never let config-patch failure block plugin load.
2572
+ }
2534
2573
  }
2535
2574
  catch {
2536
2575
  rcMode = false;