@totalreclaw/totalreclaw 3.3.9-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,23 @@ 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
+
7
24
  ## [3.3.9-rc.1] — 2026-05-02
8
25
 
9
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.
package/SKILL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: totalreclaw
3
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.1
4
+ version: 3.3.9-rc.2
5
5
  author: TotalReclaw Team
6
6
  license: MIT
7
7
  homepage: https://totalreclaw.xyz
@@ -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;
package/fs-helpers.ts CHANGED
@@ -1126,3 +1126,111 @@ export function resolveOnboardingState(
1126
1126
  writeOnboardingState(statePath, next);
1127
1127
  return next;
1128
1128
  }
1129
+
1130
+ // ---------------------------------------------------------------------------
1131
+ // OpenClaw 2026.5.x config auto-patch (3.3.9-rc.2 — issues #225 + #226)
1132
+ // ---------------------------------------------------------------------------
1133
+
1134
+ /**
1135
+ * Outcome of `patchOpenClawConfig`.
1136
+ * - `'patched'` — one or more required keys were missing; file was updated.
1137
+ * Caller must log a message telling the user to restart.
1138
+ * - `'unchanged'` — all required keys already present; no write.
1139
+ * - `'skipped'` — config file not found (not an OpenClaw host, or pre-2026
1140
+ * version that uses a different config path). Caller is safe
1141
+ * to ignore.
1142
+ * - `'error'` — file exists but read/parse/write failed. Caller logs
1143
+ * warn and continues — the plugin still loads, the user
1144
+ * must apply the keys manually.
1145
+ */
1146
+ export type OpenClawConfigPatchResult = 'patched' | 'unchanged' | 'skipped' | 'error';
1147
+
1148
+ /**
1149
+ * Auto-patch `~/.openclaw/openclaw.json` with the two entries required by
1150
+ * OpenClaw 2026.5.x for `kind: memory` plugins (issues #225 + #226):
1151
+ *
1152
+ * 1. `plugins.slots.memory = "totalreclaw"`
1153
+ * Claim the memory slot so the plugin loads instead of deferring to
1154
+ * the built-in `memory-core` tenant.
1155
+ *
1156
+ * 2. `plugins.entries.totalreclaw.hooks.allowConversationAccess = true`
1157
+ * Grant the plugin access to `agent_end` and `before_agent_start`
1158
+ * hooks. Without this flag OpenClaw 2026.5.x silently blocks both
1159
+ * hooks for non-bundled plugins, disabling auto-extraction and
1160
+ * recall injection.
1161
+ *
1162
+ * Design constraints
1163
+ * ------------------
1164
+ * - SYNCHRONOUS — called during register() which must be sync.
1165
+ * - IDEMPOTENT — reads existing values before deciding to write; no-ops
1166
+ * when both keys are already correct.
1167
+ * - BEST-EFFORT — all errors are swallowed; the plugin keeps loading even
1168
+ * if the patch fails. The caller logs an actionable warning.
1169
+ * - SCANNER-SAFE — pure `node:fs` / `node:path`; no outbound markers.
1170
+ *
1171
+ * Restart semantics
1172
+ * -----------------
1173
+ * OpenClaw reads `openclaw.json` at gateway startup, not dynamically.
1174
+ * When `patchOpenClawConfig` writes new keys during the CURRENT gateway
1175
+ * boot, the keys take effect ONLY after the gateway is restarted. The
1176
+ * plugin must tell the user via `api.logger.warn` so they know to run
1177
+ * `/totalreclaw-restart` or restart the gateway manually.
1178
+ *
1179
+ * @param configPath Absolute path to `openclaw.json`.
1180
+ * Defaults to `<home>/.openclaw/openclaw.json`.
1181
+ */
1182
+ export function patchOpenClawConfig(
1183
+ configPath?: string,
1184
+ ): OpenClawConfigPatchResult {
1185
+ const home = process.env.HOME ?? '/home/node';
1186
+ const target = configPath ?? path.join(home, '.openclaw', 'openclaw.json');
1187
+
1188
+ // `'skipped'` when the config file is absent — this host may not be
1189
+ // running OpenClaw, or may use a non-standard config location.
1190
+ if (!fs.existsSync(target)) return 'skipped';
1191
+
1192
+ try {
1193
+ const raw = fs.readFileSync(target, 'utf-8');
1194
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1195
+ const cfg = JSON.parse(raw) as Record<string, any>;
1196
+
1197
+ // Ensure the `plugins` key exists.
1198
+ if (typeof cfg.plugins !== 'object' || cfg.plugins === null) {
1199
+ cfg.plugins = {};
1200
+ }
1201
+
1202
+ let mutated = false;
1203
+
1204
+ // --- Fix #1: plugins.slots.memory = "totalreclaw" ---
1205
+ if (typeof cfg.plugins.slots !== 'object' || cfg.plugins.slots === null) {
1206
+ cfg.plugins.slots = {};
1207
+ }
1208
+ if (cfg.plugins.slots.memory !== 'totalreclaw') {
1209
+ cfg.plugins.slots.memory = 'totalreclaw';
1210
+ mutated = true;
1211
+ }
1212
+
1213
+ // --- Fix #2: plugins.entries.totalreclaw.hooks.allowConversationAccess = true ---
1214
+ if (typeof cfg.plugins.entries !== 'object' || cfg.plugins.entries === null) {
1215
+ cfg.plugins.entries = {};
1216
+ }
1217
+ if (typeof cfg.plugins.entries.totalreclaw !== 'object' || cfg.plugins.entries.totalreclaw === null) {
1218
+ cfg.plugins.entries.totalreclaw = {};
1219
+ }
1220
+ if (typeof cfg.plugins.entries.totalreclaw.hooks !== 'object' || cfg.plugins.entries.totalreclaw.hooks === null) {
1221
+ cfg.plugins.entries.totalreclaw.hooks = {};
1222
+ }
1223
+ if (cfg.plugins.entries.totalreclaw.hooks.allowConversationAccess !== true) {
1224
+ cfg.plugins.entries.totalreclaw.hooks.allowConversationAccess = true;
1225
+ mutated = true;
1226
+ }
1227
+
1228
+ if (!mutated) return 'unchanged';
1229
+
1230
+ // Write back with 2-space indent to match OpenClaw's own write style.
1231
+ fs.writeFileSync(target, JSON.stringify(cfg, null, 2) + '\n');
1232
+ return 'patched';
1233
+ } catch {
1234
+ return 'error';
1235
+ }
1236
+ }
package/index.ts CHANGED
@@ -165,6 +165,7 @@ import {
165
165
  cleanupInstallStagingDirs,
166
166
  detectPartialInstall,
167
167
  clearPartialInstallMarker,
168
+ patchOpenClawConfig,
168
169
  writePluginManifest,
169
170
  writePluginError,
170
171
  readPluginLoadedManifest,
@@ -3108,6 +3109,48 @@ const plugin = {
3108
3109
  } catch {
3109
3110
  // Best-effort. Helper logs internally and never throws.
3110
3111
  }
3112
+
3113
+ // 3.3.9-rc.2 (issues #225 + #226): auto-patch openclaw.json for
3114
+ // OpenClaw 2026.5.x. Two required config keys were not auto-applied
3115
+ // by `openclaw plugins install` in 2026.5.x:
3116
+ //
3117
+ // 1. plugins.slots.memory = "totalreclaw"
3118
+ // OpenClaw 2026.5.x introduced memory-slot exclusivity — a
3119
+ // memory-kind plugin MUST explicitly claim the slot or it is
3120
+ // silently disabled (no error shown; `openclaw plugins inspect`
3121
+ // shows "memory slot set to memory-core"). #225.
3122
+ //
3123
+ // 2. plugins.entries.totalreclaw.hooks.allowConversationAccess = true
3124
+ // Non-bundled plugins in 2026.5.x require this flag to receive
3125
+ // agent_end and before_agent_start hooks. Without it, auto-
3126
+ // extraction and recall injection are silently disabled. #226.
3127
+ //
3128
+ // The patch is idempotent — if both keys are already correct the
3129
+ // file is not touched. When the file IS mutated a restart is
3130
+ // required for the new keys to take effect (OpenClaw reads
3131
+ // openclaw.json at startup, not dynamically). We emit a warn so
3132
+ // the user and ops scripts know to trigger a restart.
3133
+ try {
3134
+ const patchResult = patchOpenClawConfig();
3135
+ if (patchResult === 'patched') {
3136
+ api.logger.warn(
3137
+ 'TotalReclaw: updated openclaw.json with required 2026.5.x keys ' +
3138
+ '(plugins.slots.memory + hooks.allowConversationAccess). ' +
3139
+ 'Gateway restart required for the changes to take effect. ' +
3140
+ 'Run `/totalreclaw-restart` or restart the gateway manually.',
3141
+ );
3142
+ } else if (patchResult === 'error') {
3143
+ api.logger.warn(
3144
+ 'TotalReclaw: failed to auto-patch openclaw.json for OpenClaw 2026.5.x ' +
3145
+ 'compatibility. If memory hooks are silently disabled, add these keys ' +
3146
+ 'manually: plugins.slots.memory="totalreclaw" and ' +
3147
+ 'plugins.entries.totalreclaw.hooks.allowConversationAccess=true.',
3148
+ );
3149
+ }
3150
+ // 'unchanged' and 'skipped' are silent — no log needed.
3151
+ } catch {
3152
+ // Best-effort — never let config-patch failure block plugin load.
3153
+ }
3111
3154
  } catch {
3112
3155
  rcMode = false;
3113
3156
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totalreclaw/totalreclaw",
3
- "version": "3.3.9-rc.1",
3
+ "version": "3.3.9-rc.2",
4
4
  "description": "End-to-end encrypted, agent-portable memory for OpenClaw and any LLM-agent runtime. XChaCha20-Poly1305 with protobuf v4 + on-chain Memory Taxonomy v1 (claim / preference / directive / commitment / episode / summary).",
5
5
  "type": "module",
6
6
  "keywords": [
package/skill.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "totalreclaw",
3
- "version": "3.3.9-rc.1",
3
+ "version": "3.3.9-rc.2",
4
4
  "description": "End-to-end encrypted memory for AI agents — portable, yours forever. XChaCha20-Poly1305 E2EE: server never sees plaintext.",
5
5
  "author": "TotalReclaw Team",
6
6
  "license": "MIT",