@totalreclaw/totalreclaw 3.3.12-rc.4 → 3.3.12-rc.6
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 +26 -0
- package/SKILL.md +156 -133
- package/dist/index.js +45 -5
- package/dist/llm-client.js +69 -1
- package/dist/tr-cli.js +1 -1
- package/import-adapters/types.ts +1 -1
- package/index.ts +46 -5
- package/llm-client.ts +74 -1
- package/package.json +2 -2
- package/skill.json +1 -1
- package/tr-cli.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,32 @@ 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.12-rc.5] — 2026-05-09
|
|
8
|
+
|
|
9
|
+
Final RC for the 3.3.12 stable promote. Behavioral fix: Pedro's Pop-OS Telegram QA (zai/glm-5-turbo) on rc.4 found the agent storing user statements in `MEMORY.md` / `USER.md` via `write` tool calls instead of calling `totalreclaw_remember`. 28 `write` calls observed in one session, 0 TotalReclaw tool calls — facts never reached the chain. Root cause: SKILL.md trigger language was permissive ("call when the user asks") and the agent's default file-write reflex won out. rc.5 makes the memory-storage rule the TOP RULE of SKILL.md with an explicit prohibition on `write`/`edit` against `MEMORY.md`/`USER.md`, an exhaustive trigger-phrase list (preference / identity / decision / commitment / possessive-assertion patterns), and a multi-fact-per-message instruction so blob-style packing does not happen.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **SKILL.md TOP RULE rewrite — aggressive triggers, no MEMORY.md fallback.** Memory storage section moved to the very top of SKILL.md (before architecture, vocabulary, install flow). Adds: (1) absolute prohibition on `write`/`edit` against `MEMORY.md` / `USER.md` / `~/.claude/memory/*.md`; (2) trigger-phrase list covering preferences, identity, tools, decisions, commitments, explicit asks, possessive assertions; (3) multi-fact-per-message rule (split into one `totalreclaw_remember` call per atomic fact); (4) full 17-tool reference table with canonical use cases; (5) restart policy reaffirmed (gateway self-restarts via SIGUSR1; agent NEVER prompts user to manually restart); (6) phrase-safety hard rail (no phrase in chat / no phrase as tool input / browser-side only).
|
|
14
|
+
- **Public quickstart guide audit** (`docs/guides/openclaw-setup-quickstart.md`) — already prose-style + clean from rc.6 revert. No edits required.
|
|
15
|
+
- **Long-form setup guide cleanup** (`docs/guides/openclaw-setup.md`) — F-flip default URL corrected (`api.totalreclaw.xyz` for free tier, staging via env override; previous text said the relay was `api-staging.*`); line 1 wording aligned with SKILL.md (`Setting up TotalReclaw — this takes about a minute…`); legacy `qr_png_b64` / `qr_unicode` references replaced with `qr_ascii` (the only QR field in the current pair payload); manual restart fallback (`openclaw gateway restart` / `docker restart`) deemphasized — the plugin self-restarts via SIGUSR1; RC pin examples bumped from `3.3.11-rc.5` to `3.3.12-rc.5`.
|
|
16
|
+
- **Version sync** — package.json, skill.json, SKILL.md frontmatter, tr-cli.ts PLUGIN_VERSION all aligned to 3.3.12-rc.5 via `sync-version.mjs`. `check-version-drift` green.
|
|
17
|
+
|
|
18
|
+
### Verified
|
|
19
|
+
|
|
20
|
+
- All 38 test suites pass (manifest-shape, config-schema, config, relay-headers, scope-address-visible, llm-profile-reader, llm-client (×2), gateway-url, retype-setscope, tool-gating, onboarding-noninteractive, pair-cli-json, pair-qr, pair-remote-client, qa-bug-report, nonce-serialization, phrase-safety-registry, onnx-download-ux, onboard-pair-only, import-time-smoke, install-staging-cleanup, partial-install-detection, install-reload-idempotency, json-stdout-cleanliness, load-manifest, url-binding, fs-helpers, pair-cli-default-mode, embedding-fallback-tag, staging-banner-gate, restart-auth, inbound-user-tracker, register-command-name, skill-md-hybrid-primary, tr-cli-json-output).
|
|
21
|
+
- `check-scanner` green (0 flags).
|
|
22
|
+
- `check-version-drift` green (3 sites + tr-cli.ts all = 3.3.12-rc.5).
|
|
23
|
+
- E2E on clean OpenClaw 2026.5.7 container — agent given `"Hi, I'm Pedro. I live in Porto. I prefer PostgreSQL over MySQL."` calls `totalreclaw_remember` ≥1 time, with 0 `write`/`edit` calls against `MEMORY.md` / `USER.md`. (See PR description for the captured `toolSummary` JSON.)
|
|
24
|
+
|
|
25
|
+
### Out of scope (unchanged from rc.4)
|
|
26
|
+
|
|
27
|
+
- Plugin install lifecycle on OpenClaw 2026.5.7 (auto-QA PASS — plugin loads, SIGUSR1 self-restart works, in-process restart, managed-service-friendly).
|
|
28
|
+
- `tr` CLI on-chain memory ops (rc.4 fix verified).
|
|
29
|
+
- Pair flow (browser-side phrase, no leakage).
|
|
30
|
+
- Trajectory poller (auto-extraction backup).
|
|
31
|
+
- F flip (RC + stable both default to prod URL; staging via env override).
|
|
32
|
+
|
|
7
33
|
## [3.3.12-rc.2] — 2026-05-08
|
|
8
34
|
|
|
9
35
|
Hot-fix on rc.1's F flip. Pair flow regression: rc.1 set `pairRelayUrl`'s default to `wss://api.totalreclaw.xyz` (production) independently of `serverUrl`. RC users who set `TOTALRECLAW_SERVER_URL=https://api-staging.totalreclaw.xyz` (per the staging-opt-in flow) had pair WS go to **prod**, which pre-dates the pair feature → 404 on WS upgrade → `totalreclaw_pair failed: Unexpected server response: 404`. End-to-end blocker: pair never completed → no credentials → no memories.
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: totalreclaw
|
|
3
|
-
description: "
|
|
4
|
-
version: 3.3.12-rc.
|
|
3
|
+
description: "End-to-end encrypted, decentralized memory for OpenClaw. Set up an account once, then call totalreclaw_remember / totalreclaw_recall (or the tr CLI under hybrid-primary) instead of writing to MEMORY.md / USER.md / local files. Trigger on 'install TotalReclaw', 'set up TotalReclaw', 'restore my recovery phrase', any remember / recall request, AND any user statement that contains a preference / fact / decision / commitment about themselves."
|
|
4
|
+
version: 3.3.12-rc.6
|
|
5
5
|
author: TotalReclaw Team
|
|
6
6
|
license: MIT
|
|
7
7
|
homepage: https://totalreclaw.xyz
|
|
@@ -24,26 +24,61 @@ metadata:
|
|
|
24
24
|
|
|
25
25
|
# TotalReclaw — OpenClaw
|
|
26
26
|
|
|
27
|
-
> **Agent-executable quickstart
|
|
27
|
+
> **Agent-executable quickstart**: <https://github.com/p-diogo/totalreclaw/blob/main/docs/guides/openclaw-setup-quickstart.md>
|
|
28
28
|
>
|
|
29
|
-
> **Human-readable setup guide
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
> **Human-readable setup guide**: <https://github.com/p-diogo/totalreclaw/blob/main/docs/guides/openclaw-setup.md>
|
|
30
|
+
|
|
31
|
+
## TOP RULE — MEMORY STORAGE (read this before anything else)
|
|
32
|
+
|
|
33
|
+
**When the user states ANYTHING about themselves, their preferences, decisions, plans, identity, work, location, or facts they want retained, you MUST call `totalreclaw_remember` (NOT `write`, NOT `edit`, NOT MEMORY.md/USER.md).** Under hybrid-primary mode (OpenClaw 2026.5.x), if `totalreclaw_remember` is not bound as a tool, fall back to the `tr` CLI: `node "$TR_CLI" remember --json "<fact>"`. Either path lands the fact encrypted, on-chain. Both are valid.
|
|
34
|
+
|
|
35
|
+
### ABSOLUTE PROHIBITION
|
|
36
|
+
|
|
37
|
+
**NEVER use the `write` or `edit` tools to store user statements in `MEMORY.md`, `USER.md`, `~/.claude/memory/*.md`, `~/.openclaw/memory/*.md`, or any other file.** Those files are for AGENT PERSONA and CODEBASE INSTRUCTIONS — read-only operational config the agent loads at startup. Storing user data there is a SKILL VIOLATION. User memories live encrypted on-chain via TotalReclaw, not in flat files.
|
|
38
|
+
|
|
39
|
+
- ✅ DO call `totalreclaw_remember` (or `tr remember --json` under hybrid-primary).
|
|
40
|
+
- ❌ DO NOT `Write` to `MEMORY.md`.
|
|
41
|
+
- ❌ DO NOT `Edit` `USER.md` to append a user fact.
|
|
42
|
+
- ❌ DO NOT echo "saving this to MEMORY.md…" — that file is not yours to write to.
|
|
43
|
+
|
|
44
|
+
### Trigger phrases — call `totalreclaw_remember` on detection
|
|
45
|
+
|
|
46
|
+
Do not wait for an explicit "remember this". These are first-class triggers — every one of them MUST result in a `totalreclaw_remember` call (parallel with your normal reply):
|
|
47
|
+
|
|
48
|
+
- **Preferences**: "I prefer X", "I like X", "I love X", "I hate X", "my favorite X is Y", "I'd rather X", "X over Y"
|
|
49
|
+
- **Identity**: "I'm a X", "I am X", "my name is X", "I'm based in X", "I live in X", "I work at X", "I work on X", "I have X"
|
|
50
|
+
- **Tools / setup**: "I use X", "I'm running X", "my X is Y", "I've configured X to Y"
|
|
51
|
+
- **Decisions / plans**: "I decided to X", "I'm going to X", "I'm planning X", "we agreed X", "we settled on X", "let's call this X", "from now on X"
|
|
52
|
+
- **Commitments**: "I'll X by Y", "remind me to X", "I need to X by Y", "don't forget X"
|
|
53
|
+
- **Explicit asks**: "remember that X", "save X", "note that X", "store X", "make a note of X"
|
|
54
|
+
- **Possessive assertions**: "my [anything] is [value]" (preference assertion pattern)
|
|
55
|
+
- **Any 1st-person factual claim worth retaining** about identity, work, location, tools, preferences, ongoing projects, contacts, schedule.
|
|
56
|
+
|
|
57
|
+
If a single user message contains MULTIPLE such facts (e.g. "I'm Pedro. I live in Porto. I prefer PostgreSQL over MySQL."), call `totalreclaw_remember` MULTIPLE TIMES — once per atomic fact. Don't blob multiple unrelated facts into a single memory.
|
|
58
|
+
|
|
59
|
+
### When to call `totalreclaw_recall`
|
|
32
60
|
|
|
33
|
-
|
|
61
|
+
**ALWAYS call `totalreclaw_recall` first when the user asks about themselves, their preferences, or anything from past conversations.** Trigger phrases:
|
|
62
|
+
|
|
63
|
+
- "what's my X?", "do I X?", "where do I X?", "when did I X?", "have I told you X?"
|
|
64
|
+
- "what do you remember about me?", "what do you know about my X?"
|
|
65
|
+
- "remind me about X", "what did we decide about X?"
|
|
66
|
+
- "do you remember when X?"
|
|
67
|
+
|
|
68
|
+
Recall first, then answer from the returned facts. If 0 results come back, say so plainly — do not guess from session history.
|
|
69
|
+
|
|
70
|
+
## CRITICAL: How TotalReclaw actually stores memories
|
|
34
71
|
|
|
35
72
|
**TotalReclaw is end-to-end encrypted memory stored across a DECENTRALIZED NETWORK — not on a single company server.** Memories are encrypted with keys derived from the user's 12-word recovery phrase, submitted as Account Abstraction UserOps, and persisted on a public blockchain (Base / Gnosis depending on tier). The Graph subgraph indexes them. The TotalReclaw relay only forwards encrypted bundles to the bundler and proxies subgraph reads — it never sees plaintext, can't read memories, and could be replaced by any compatible relay without losing data. No single company controls or can read user memories.
|
|
36
73
|
|
|
37
|
-
Forbidden vocabulary — these claims are WRONG
|
|
38
|
-
- "local", "local-only", "stored locally", "on disk", "local memory", "local storage" — wrong direction (implies local-only).
|
|
39
|
-
- "stored on a relay server", "stored on TotalReclaw's server", "single server owned by", "TotalReclaw stores your", "company server" — wrong direction (implies centralized custody).
|
|
74
|
+
Forbidden vocabulary — these claims are WRONG:
|
|
75
|
+
- "local", "local-only", "stored locally", "on disk", "local memory", "local storage" — wrong direction (implies local-only).
|
|
76
|
+
- "stored on a relay server", "stored on TotalReclaw's server", "single server owned by", "TotalReclaw stores your", "company server" — wrong direction (implies centralized custody).
|
|
40
77
|
- "without an account" — wrong (an account is required to derive the encryption key).
|
|
41
78
|
|
|
42
|
-
|
|
79
|
+
Canonical line: **"Memories are encrypted with a key derived from your recovery phrase, submitted on-chain via Account Abstraction, and stored across a decentralized network. The relay only forwards encrypted bundles — it never sees plaintext. Only you can decrypt them, and only with your recovery phrase."**
|
|
43
80
|
|
|
44
|
-
## User-facing vocabulary
|
|
45
|
-
|
|
46
|
-
Internal terms that the user does NOT know — translate when you must speak:
|
|
81
|
+
## User-facing vocabulary
|
|
47
82
|
|
|
48
83
|
| Internal term | Say to the user |
|
|
49
84
|
|---|---|
|
|
@@ -55,33 +90,39 @@ Internal terms that the user does NOT know — translate when you must speak:
|
|
|
55
90
|
| credentials.json / mnemonic file | "your recovery phrase" |
|
|
56
91
|
| `tr pair --json` / `setsid -f` / shell exec | (don't mention; just do the work silently) |
|
|
57
92
|
|
|
58
|
-
The word "pair" is internal jargon — never use it in user-visible prose.
|
|
93
|
+
The word "pair" is internal jargon — never use it in user-visible prose. Use "set up an account", "your account", "your recovery phrase", "your memories".
|
|
59
94
|
|
|
60
|
-
##
|
|
95
|
+
## Install / setup top-level rule
|
|
61
96
|
|
|
62
|
-
**Emit ONLY the user-visible lines listed below. Do not narrate tool calls. Do not describe what you're doing or about to do
|
|
97
|
+
**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**.
|
|
63
98
|
|
|
64
99
|
### ⚡ MANDATORY ACK BEFORE FIRST TOOL CALL
|
|
65
100
|
|
|
66
|
-
**User-visible line 1 MUST be your FIRST output — emitted BEFORE you run any shell tool, plugin install, or `tr` command.** Install + setup takes 1–2 minutes of silent tool-execution; without an immediate ack the user assumes the request didn't register and may resend or abandon.
|
|
101
|
+
**User-visible line 1 MUST be your FIRST output — emitted BEFORE you run any shell tool, plugin install, or `tr` command.** Install + setup takes 1–2 minutes of silent tool-execution; without an immediate ack the user assumes the request didn't register and may resend or abandon.
|
|
67
102
|
|
|
68
|
-
The ack is unconditional.
|
|
103
|
+
The ack is unconditional. Send line 1, THEN start the work.
|
|
69
104
|
|
|
70
105
|
Forbidden order (silent-then-narrate): ❌ run `openclaw plugins install` → wait 60s → emit line 1.
|
|
71
106
|
Required order (ack-then-work): ✅ emit line 1 → run `openclaw plugins install` silently → emit line 2 when `tr status` returns.
|
|
72
107
|
|
|
73
|
-
## ⛔ FORBIDDEN ACTIONS
|
|
108
|
+
## ⛔ FORBIDDEN ACTIONS
|
|
74
109
|
|
|
75
|
-
These specific actions break the pair flow
|
|
110
|
+
These specific actions break the install / pair flow:
|
|
76
111
|
|
|
77
|
-
1. **Do NOT restart the gateway.** No
|
|
112
|
+
1. **Do NOT restart the gateway.** No `kill -USR1`, no `docker restart`, no `openclaw gateway restart`. The plugin self-patches and signals its own restart needs (SIGUSR1 in-process, automatic). You restarting it kills the in-flight `setsid -f` pair subprocess → 502 on browser respond. If for some specific reason a restart IS required, issue the literal slash command `/totalreclaw-restart` — nothing else. **NEVER prompt the user to manually restart anything**; the plugin handles its own restart.
|
|
78
113
|
|
|
79
|
-
2. **Do NOT write to `~/.openclaw/openclaw.json`.** The plugin's `patchOpenClawConfig()` already wrote
|
|
114
|
+
2. **Do NOT write to `~/.openclaw/openclaw.json`.** The plugin's `patchOpenClawConfig()` already wrote the required keys. Any further write triggers a deferred SIGUSR1 reload that kills pair. No `openclaw config set …`, no `jq …>openclaw.json`, no `sed -i openclaw.json`.
|
|
80
115
|
|
|
81
|
-
3. **Do NOT save QR PNGs.** `tr pair --json` returns `qr_ascii` — emit that inline in a triple-backticked code block. No `/tmp/totalreclaw-pair-qr.png` (OpenClaw media-policy blocks it: `LocalMediaAccessError`). No PNG decode.
|
|
116
|
+
3. **Do NOT save QR PNGs.** `tr pair --json` returns `qr_ascii` — emit that inline in a triple-backticked code block. No `/tmp/totalreclaw-pair-qr.png` (OpenClaw media-policy blocks it: `LocalMediaAccessError`). No PNG decode.
|
|
82
117
|
|
|
83
118
|
4. **`tr pair --json` MUST run via `setsid -f`** (exact form below). Foreground invocation is killed when your shell-tool exec returns → 502.
|
|
84
119
|
|
|
120
|
+
5. **Do NOT pass `--allow-dangerous` to any TotalReclaw command.** The plugin is sandboxed by design.
|
|
121
|
+
|
|
122
|
+
6. **Do NOT manually edit `~/.totalreclaw/credentials.json`.** Round-trip via `tr pair --json` (re-generate) instead.
|
|
123
|
+
|
|
124
|
+
7. **Do NOT install the legacy from-source path alongside the published plugin.** The two paths overlap and will double-write facts.
|
|
125
|
+
|
|
85
126
|
## Hybrid-primary architecture (3.3.9-rc.1)
|
|
86
127
|
|
|
87
128
|
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.
|
|
@@ -96,7 +137,7 @@ TR_CLI="$(ls -d ~/.openclaw/npm/node_modules/@totalreclaw/totalreclaw/dist/tr-cl
|
|
|
96
137
|
```
|
|
97
138
|
Then invoke as `node "$TR_CLI" <cmd> [--json]`.
|
|
98
139
|
|
|
99
|
-
**Never rely on `totalreclaw_*` tool binding** — it may not be available due to issue #223.
|
|
140
|
+
**Never rely on `totalreclaw_*` tool binding** — it may not be available due to issue #223. If `totalreclaw_remember` IS bound, use it (preferred). If not, use `tr remember --json` from the shell. EITHER way, the user fact MUST land in TotalReclaw — never in `MEMORY.md`/`USER.md`.
|
|
100
141
|
|
|
101
142
|
## User-visible line set (THIS IS ALL YOU SAY)
|
|
102
143
|
|
|
@@ -111,78 +152,61 @@ Speak only these lines, in order. Substitute the bracketed placeholders with the
|
|
|
111
152
|
4. (done + free-tier confirmation)
|
|
112
153
|
> ✓ You're set up on the TotalReclaw free tier — 500 memories/month, unlimited reads. https://totalreclaw.xyz/pricing. Ask me to remember anything.
|
|
113
154
|
|
|
114
|
-
If existing credentials are detected
|
|
155
|
+
If existing credentials are detected, emit ONLY this line and stop:
|
|
115
156
|
> ✓ TotalReclaw is already set up.
|
|
116
157
|
|
|
117
158
|
### Hard silence rules between numbered lines
|
|
118
159
|
|
|
119
|
-
Do NOT emit transitional / narrative lines between any of the four user-visible lines above. Specifically forbidden
|
|
160
|
+
Do NOT emit transitional / narrative lines between any of the four user-visible lines above. Specifically forbidden:
|
|
120
161
|
|
|
121
|
-
- ❌ "Now let me verify the install:" → silent. Just run `tr status --json`.
|
|
122
|
-
- ❌ "Now let me check if you already have credentials:" → silent.
|
|
123
|
-
- ❌ "Need to pair. Let me kick that off:" → silent.
|
|
124
|
-
- ❌ "Standing by." → silent.
|
|
162
|
+
- ❌ "Now let me verify the install:" → silent. Just run `tr status --json`.
|
|
163
|
+
- ❌ "Now let me check if you already have credentials:" → silent.
|
|
164
|
+
- ❌ "Need to pair. Let me kick that off:" → silent.
|
|
165
|
+
- ❌ "Standing by." → silent.
|
|
125
166
|
- ❌ Any "Let me X" / "Now I'll Y" / "Just give me a second" prelude.
|
|
126
|
-
- ❌ **Skipping line 1 entirely and going straight to shell tool calls.**
|
|
127
|
-
|
|
128
|
-
The transcript Pedro should see is exactly four messages — the four numbered lines, nothing in between. Line 1 fires immediately. Lines 2–4 fire as work completes. No prose between.
|
|
129
|
-
|
|
130
|
-
## How does TotalReclaw work? (canonical answer for user questions)
|
|
131
|
-
|
|
132
|
-
When the user asks "how does this work?" / "where are my memories stored?" / "is this safe?" / similar — answer with this content. Do NOT say "stored on a relay server", "stored on TotalReclaw's server", or any phrasing that implies single-company custody. The actual architecture:
|
|
133
|
-
|
|
134
|
-
> **End-to-end encrypted, decentralized memory.** When you tell me to remember something, I encrypt it locally with a key derived from your 12-word recovery phrase, then submit it on-chain via Account Abstraction (a UserOperation). It lives on a public blockchain (Base Sepolia for free tier, Gnosis mainnet for paid). The Graph subgraph indexes it for fast retrieval. The TotalReclaw relay server only forwards encrypted bundles between your client and the chain — it never sees plaintext, can't read your memories, and could be replaced by any compatible relay without losing data. Your recovery phrase is the only thing that can decrypt the memories. Nobody (not me, not TotalReclaw the company, not the relay operator) can read them without it.
|
|
135
|
-
|
|
136
|
-
If the user asks shorter questions, reply with the relevant slice:
|
|
137
|
-
|
|
138
|
-
- **"Where are memories stored?"** → "Encrypted, on-chain (Base or Gnosis depending on your tier), indexed by The Graph. Not on any single company's server."
|
|
139
|
-
- **"Is the relay reading my data?"** → "No. The relay only forwards encrypted bundles. Plaintext never leaves your device. Only your recovery phrase can decrypt them."
|
|
140
|
-
- **"What if TotalReclaw shuts down?"** → "Your memories survive. They're on a public blockchain. Any compatible relay (or self-hosted relay) can read them with your recovery phrase."
|
|
167
|
+
- ❌ **Skipping line 1 entirely and going straight to shell tool calls.**
|
|
141
168
|
|
|
142
169
|
## Setup flow (hybrid-primary)
|
|
143
170
|
|
|
144
|
-
1. **Install plugin first, skill second
|
|
171
|
+
1. **Install plugin first, skill second.** Run via your shell tool, in this exact order:
|
|
145
172
|
```
|
|
146
173
|
openclaw plugins install @totalreclaw/totalreclaw
|
|
147
174
|
openclaw skills install totalreclaw
|
|
148
175
|
```
|
|
149
|
-
For RC: replace the first command with `openclaw plugins install @totalreclaw/totalreclaw@rc`.
|
|
176
|
+
For RC: replace the first command with `openclaw plugins install @totalreclaw/totalreclaw@rc`. Plugin BEFORE skill is deliberate.
|
|
150
177
|
|
|
151
|
-
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.
|
|
178
|
+
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.
|
|
152
179
|
|
|
153
180
|
2. **Verify CLI works — emit user-visible line 2 when successful.** Resolve `TR_CLI` and run:
|
|
154
181
|
```bash
|
|
155
182
|
TR_CLI="$(ls -d ~/.openclaw/npm/node_modules/@totalreclaw/totalreclaw/dist/tr-cli.js ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js 2>/dev/null | head -1)"
|
|
156
183
|
node "$TR_CLI" status --json
|
|
157
184
|
```
|
|
158
|
-
Parse the JSON. When you get `{"version": "...", "hybrid_mode": true, ...}`, emit user-visible line 2
|
|
185
|
+
Parse the JSON. When you get `{"version": "...", "hybrid_mode": true, ...}`, emit user-visible line 2.
|
|
159
186
|
|
|
160
|
-
3. **Check for existing credentials (silent unless found).** Run `test -s ~/.totalreclaw/credentials.json`. If it exists
|
|
187
|
+
3. **Check for existing credentials (silent unless found).** Run `test -s ~/.totalreclaw/credentials.json`. If it exists, emit *"✓ TotalReclaw is already set up."* and stop.
|
|
161
188
|
|
|
162
|
-
4. **Set up the user's TotalReclaw account IMMEDIATELY — no consent gate.**
|
|
189
|
+
4. **Set up the user's TotalReclaw account IMMEDIATELY — no consent gate.** Use `setsid -f` so the pair process survives gateway SIGUSR1 reloads. The handshake URL+PIN is written to a tmp file the moment the WS opens:
|
|
163
190
|
```bash
|
|
164
191
|
PAIR_OUT="/tmp/tr-pair-out-$(date +%s).json"
|
|
165
192
|
setsid -f node "$TR_CLI" pair --json > "$PAIR_OUT" 2>&1 < /dev/null
|
|
166
193
|
sleep 2
|
|
167
194
|
cat "$PAIR_OUT"
|
|
168
195
|
```
|
|
169
|
-
|
|
196
|
+
Parse the JSON `{"url": "...", "pin": "...", "expires_at_ms": ...}` and emit user-visible line 3. This is UNCONDITIONAL — do NOT ask "Would you like to set up an account?". The URL + PIN itself is the consent moment.
|
|
170
197
|
|
|
171
|
-
5. **Verify and confirm
|
|
198
|
+
5. **Verify and confirm.** After the user says `done`, re-run `test -s ~/.totalreclaw/credentials.json`. If missing, the PIN expired — repeat the pair block and resend line 3 once. If present, emit user-visible line 4 and stop.
|
|
172
199
|
|
|
173
|
-
## `tr` CLI reference
|
|
200
|
+
## `tr` CLI reference
|
|
174
201
|
|
|
175
|
-
Always use `--json` flag when running from shell for machine-parseable output.
|
|
202
|
+
Always use `--json` flag when running from shell for machine-parseable output.
|
|
176
203
|
|
|
177
204
|
```bash
|
|
178
|
-
#
|
|
179
|
-
TR_CLI="$(ls -d ~/.openclaw/npm/node_modules/@totalreclaw/totalreclaw/dist/tr-cli.js ~/.openclaw/extensions/totalreclaw/dist/tr-cli.js 2>/dev/null | head -1)"
|
|
180
|
-
|
|
181
|
-
# Status check (parse JSON to confirm install + onboarding state)
|
|
205
|
+
# Status
|
|
182
206
|
node "$TR_CLI" status --json
|
|
183
|
-
# Returns: {"version":"3.3.
|
|
207
|
+
# Returns: {"version":"3.3.12-rc.5","onboarded":false,"next_step":"pair","tool_count":17,"hybrid_mode":true}
|
|
184
208
|
|
|
185
|
-
# Pair
|
|
209
|
+
# Pair (always via setsid -f)
|
|
186
210
|
PAIR_OUT="/tmp/tr-pair-out-$(date +%s).json"
|
|
187
211
|
setsid -f node "$TR_CLI" pair --json > "$PAIR_OUT" 2>&1 < /dev/null && sleep 2 && cat "$PAIR_OUT"
|
|
188
212
|
# Returns: {"v":1,"sid":"...","url":"https://...","pin":"123456","mode":"generate","expires_at_ms":...,"qr_ascii":"..."}
|
|
@@ -196,102 +220,101 @@ node "$TR_CLI" recall --json "where does the user work" --limit 5
|
|
|
196
220
|
# Returns: {"results":[{"text":"...","score":0.8},...]}
|
|
197
221
|
```
|
|
198
222
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
## Rendering the QR on your transport (3.3.10-rc.4 — corrected)
|
|
202
|
-
|
|
203
|
-
`tr pair --json` returns a `qr_ascii` field — Unicode block-char QR (43 lines × ~43 cols). That is the ONLY QR field in the payload. The legacy `qr_png_b64` and `qr_unicode` field names referenced by older skill docs no longer exist.
|
|
223
|
+
## Tool reference (all 17 plugin tools)
|
|
204
224
|
|
|
205
|
-
The
|
|
225
|
+
The plugin exposes these tools via OpenClaw's plugin runtime. Under hybrid-primary mode, prefer the `tr` CLI for `pair` / `remember` / `recall` / `status`; the rest below are typically only available as native tools.
|
|
206
226
|
|
|
207
|
-
|
|
227
|
+
| Tool | Use case |
|
|
228
|
+
|---|---|
|
|
229
|
+
| `totalreclaw_pair` | Set up the user's account (browser-side phrase generation/import) |
|
|
230
|
+
| `totalreclaw_remember` | **Store a fact / preference / decision (encrypted, on-chain). PRIMARY tool for user statements.** |
|
|
231
|
+
| `totalreclaw_recall` | Semantic search across the user's memories |
|
|
232
|
+
| `totalreclaw_forget` | Delete a memory by id (tombstone on-chain) |
|
|
233
|
+
| `totalreclaw_pin` | Mark a memory as never-supersedable |
|
|
234
|
+
| `totalreclaw_unpin` | Remove pin status |
|
|
235
|
+
| `totalreclaw_retype` | Change a memory's type (claim/preference/directive/etc.) |
|
|
236
|
+
| `totalreclaw_set_scope` | Change a memory's scope (work / personal / health / family / creative / finance / misc) |
|
|
237
|
+
| `totalreclaw_export` | Export all memories (json / markdown) |
|
|
238
|
+
| `totalreclaw_import_from` | Import from another tool (Mem0, MCP-Memory, ChatGPT, Claude, Gemini) |
|
|
239
|
+
| `totalreclaw_import_batch` | Bulk import with chunking + extraction |
|
|
240
|
+
| `totalreclaw_consolidate` | Merge near-duplicates after a fresh import |
|
|
241
|
+
| `totalreclaw_status` | Check onboarding state, version, billing tier, quota |
|
|
242
|
+
| `totalreclaw_upgrade` | Open the Stripe upgrade flow (free → paid tier) |
|
|
243
|
+
| `totalreclaw_migrate` | Move testnet memories to mainnet (Pro tier) |
|
|
244
|
+
| `totalreclaw_onboarding_start` | (Internal — used by setup flow) |
|
|
245
|
+
| `totalreclaw_report_qa_bug` | (RC only) Surface a QA bug into the agent log |
|
|
246
|
+
|
|
247
|
+
All tools accept JSON input and return structured JSON.
|
|
248
|
+
|
|
249
|
+
## Auto-extraction (background, no agent action required)
|
|
250
|
+
|
|
251
|
+
The plugin runs a trajectory poller every 60 seconds that scans `~/.openclaw/agents/<agent>/sessions/*.trajectory.jsonl` for new conversation turns and runs LLM-driven extraction. Extracted facts go through importance filtering and dedup before landing on-chain.
|
|
252
|
+
|
|
253
|
+
**Auto-extraction is a SAFETY NET, not a substitute for explicit `totalreclaw_remember` calls.** Explicit calls are higher-fidelity (the agent decides the importance, type, and scope). Auto-extraction catches things the agent missed.
|
|
208
254
|
|
|
209
|
-
|
|
210
|
-
<qr_ascii contents — 43 lines of ▄▀█▄▀█ block chars>
|
|
211
|
-
```
|
|
255
|
+
## Phrase safety (HARD — never break)
|
|
212
256
|
|
|
213
|
-
|
|
257
|
+
The 12-word recovery phrase is the master key. Hard rules:
|
|
214
258
|
|
|
215
|
-
|
|
259
|
+
- **NEVER echo, generate, log, or ask the user to paste a recovery phrase in chat.** The browser at the pair URL handles phrase generation and import. The phrase NEVER enters the agent's LLM context.
|
|
260
|
+
- **NEVER include a recovery phrase as input to ANY tool call** — not `totalreclaw_pair` (its only inputs are `mode` + optional config), not `totalreclaw_remember`, not `Bash`, not `Write`, not `Edit`. Anything that surfaces the phrase to the agent's context is a security incident.
|
|
261
|
+
- **NEVER invoke any phrase-touching CLI via your shell tool.** `openclaw totalreclaw onboard` (bare interactive form) and `totalreclaw setup` print phrase material to stdout, which enters LLM context. The ONLY agent-facilitated account-setup paths are: (1) `tr pair --json` (preferred); (2) the `totalreclaw_pair` tool; (3) `openclaw totalreclaw pair generate --url-pin-only` or `openclaw totalreclaw onboard --pair-only` (single-line URL+PIN JSON, zero phrase material — both account-setup payloads are x25519-only, not BIP-39).
|
|
262
|
+
- **NEVER display the recovery phrase back to the user in chat** even if the browser leaks it to you somehow.
|
|
263
|
+
- **If the user pastes a phrase anyway**: tell them the phrase is now compromised, re-run `tr pair --json` for a fresh wallet.
|
|
216
264
|
|
|
217
|
-
|
|
265
|
+
## Browser flow context
|
|
218
266
|
|
|
219
|
-
|
|
267
|
+
After you emit user-visible line 3, the user opens the URL on their phone or desktop browser. They walk through a 3-step wizard:
|
|
220
268
|
|
|
221
|
-
|
|
269
|
+
1. **Step 1 — Enter PIN.** 6 digits matching what you sent.
|
|
270
|
+
2. **Step 2 — Recovery phrase.** Two tabs: **Set up** (generates fresh BIP-39 phrase locally) or **Log in** (paste existing phrase to restore). User checks "I've written this down" and clicks Set up.
|
|
271
|
+
3. **Step 3 — You're all set.** Browser already POSTed the encrypted phrase. Plugin writes `~/.totalreclaw/credentials.json`.
|
|
222
272
|
|
|
223
|
-
|
|
224
|
-
2. **Step 2 of 3 — Your recovery phrase.** Two tabs: **Set up** (default — generates a fresh 12-word BIP-39 phrase locally in the browser) and **Log in** (paste an existing phrase to restore on a new device). User must check **"I've written this down and stored it somewhere safe"** then click **Set up TotalReclaw**.
|
|
225
|
-
3. **Step 3 of 3 — You're all set.** Confirmation screen with a "Close this page" link. The browser already POSTed the encrypted phrase by this point — the relay forwards to your gateway, the plugin decrypts, writes `~/.totalreclaw/credentials.json`, and your `awaitPhraseUpload` resolves.
|
|
273
|
+
Common user-side issues during pair:
|
|
226
274
|
|
|
227
|
-
|
|
275
|
+
- **"The page won't load"** → confirm URL host. Default for both stable and RC is `api.totalreclaw.xyz`. Staging via `TOTALRECLAW_SERVER_URL=https://api-staging.totalreclaw.xyz`.
|
|
276
|
+
- **"PIN expired"** → re-run pair block, resend line 3.
|
|
277
|
+
- **"Invalid phrase"** during Log in → user pasted a non-BIP-39-valid phrase. Tell them to re-paste or switch to Set up tab.
|
|
278
|
+
- **502 on Set up** → the gateway WS dropped. Pair subprocess is alive (`setsid -f`). Wait 30s; re-run pair if still 502.
|
|
279
|
+
- **"Where do I write down the phrase?"** → paper, password manager, encrypted note. NOT in this chat.
|
|
228
280
|
|
|
229
|
-
|
|
230
|
-
- **"PIN says expired"** or countdown ran out → re-run the pair block (Step 4 above) and emit line 3 again with the fresh URL+PIN. The previous session is dead.
|
|
231
|
-
- **"It says invalid phrase"** during Log in → the user pasted a phrase that isn't BIP-39 valid (typo or wrong source). Tell them to double-check and re-paste; or switch to **Set up** tab to generate a fresh one (loses existing memories).
|
|
232
|
-
- **"I clicked Set up TotalReclaw but nothing happened"** / **502** → the gateway WS dropped before respond. The pair subprocess is alive (you used `setsid -f`). Wait 30s; if the user still sees the 502, re-run Step 4 (the deferred reload should have completed by then).
|
|
233
|
-
- **"Where do I write down the phrase?"** → tell them: anywhere safe and durable — paper, password manager, encrypted note. NOT in this chat. NOT in a screenshot to cloud-synced photos. The phrase IS the account; losing it means losing all memories.
|
|
281
|
+
## Forbidden vocabulary in agent prose
|
|
234
282
|
|
|
235
|
-
|
|
283
|
+
Unless the user explicitly asks how the install works, these patterns must not appear:
|
|
236
284
|
|
|
237
|
-
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.
|
|
238
|
-
|
|
239
|
-
## Usage (post-setup)
|
|
240
|
-
|
|
241
|
-
- Stable user facts / preferences / identity / "remember X":
|
|
242
|
-
```bash
|
|
243
|
-
node "$TR_CLI" remember --json "<fact>"
|
|
244
|
-
```
|
|
245
|
-
- First-person factual query ("do I / what's my / where do I…"):
|
|
246
|
-
```bash
|
|
247
|
-
node "$TR_CLI" recall --json "<query>" --limit 5
|
|
248
|
-
```
|
|
249
|
-
Recall FIRST, then answer from returned facts. If 0 results, say so.
|
|
250
|
-
- For forget / pin / unpin / retype / set_scope — use native tools if available, or ask user to run `openclaw totalreclaw` subcommand.
|
|
251
|
-
- "status" → `tr status --json` (parse version + onboarded + next_step).
|
|
252
|
-
- "upgrade" → `totalreclaw_upgrade` native tool (paste Stripe URL verbatim). Post-upgrade testnet → mainnet: `totalreclaw_migrate` with `dry_run` first.
|
|
253
|
-
- "import / export / consolidate" → `totalreclaw_import_from` · `totalreclaw_export` · `totalreclaw_consolidate` native tools.
|
|
254
|
-
|
|
255
|
-
**Forbidden vocabulary** (these are anti-patterns that violate the silence rule):
|
|
256
285
|
- "Let me check that…"
|
|
257
286
|
- "Plugin loaded. Let me verify…"
|
|
258
287
|
- "I'll now…"
|
|
259
288
|
- "Quick fix…"
|
|
260
289
|
- "However, I don't see…"
|
|
261
290
|
- "I need to use…"
|
|
262
|
-
- "Should I /totalreclaw-restart" / "
|
|
291
|
+
- "Should I /totalreclaw-restart" / "Want me to restart" / "Let me check if the tool is bound" / "Do you have a public URL"
|
|
263
292
|
- "Now installing…" / "Got it" / "In parallel…" / "Step 1a/1b/1c"
|
|
264
293
|
- "local" / "local-only" / "stored locally" / "on disk" / "without an account" / "local memory" / "local storage"
|
|
265
294
|
|
|
266
295
|
## Diagnostics
|
|
267
296
|
|
|
268
|
-
- `tr` CLI not found → plugin not installed or install still in progress. Wait 5s, retry.
|
|
269
|
-
- `totalreclaw_*` tools not visible → EXPECTED under OpenClaw 2026.5.2 (issue #223). Use `tr` CLI instead.
|
|
270
|
-
-
|
|
271
|
-
- User says done but `credentials.json` missing → PIN expired or entered wrong phrase; re-run `tr pair --json` and resend line 3 once.
|
|
297
|
+
- `tr` CLI not found → plugin not installed or install still in progress. Wait 5s, retry.
|
|
298
|
+
- `totalreclaw_*` tools not visible → EXPECTED under OpenClaw 2026.5.2 (issue #223). Use `tr` CLI instead.
|
|
299
|
+
- User says done but `credentials.json` missing → PIN expired; re-run `tr pair --json` and resend line 3.
|
|
272
300
|
- `onboarding required` → credentials missing; redo from the account-setup step.
|
|
273
|
-
- `quota exceeded` → `tr status --json
|
|
274
|
-
- `No LLM available for auto-extraction`
|
|
275
|
-
|
|
276
|
-
##
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
```
|
|
301
|
+
- `quota exceeded` → `tr status --json`, then offer `totalreclaw_upgrade`.
|
|
302
|
+
- `No LLM available for auto-extraction` → check `~/.openclaw/agents/<agent>/agent/auth-profiles.json`.
|
|
303
|
+
|
|
304
|
+
## How does TotalReclaw work? (canonical answer for user questions)
|
|
305
|
+
|
|
306
|
+
When the user asks "how does this work?" / "where are my memories stored?" / "is this safe?" — answer:
|
|
307
|
+
|
|
308
|
+
> **End-to-end encrypted, decentralized memory.** When you tell me to remember something, I encrypt it locally with a key derived from your 12-word recovery phrase, then submit it on-chain via Account Abstraction (a UserOperation). It lives on a public blockchain (Base Sepolia for free tier, Gnosis mainnet for paid). The Graph subgraph indexes it for fast retrieval. The TotalReclaw relay only forwards encrypted bundles between your client and the chain — it never sees plaintext, can't read your memories, and could be replaced by any compatible relay without losing data. Your recovery phrase is the only thing that can decrypt the memories.
|
|
309
|
+
|
|
310
|
+
Shorter slices:
|
|
311
|
+
- **"Where are memories stored?"** → "Encrypted, on-chain (Base or Gnosis depending on your tier), indexed by The Graph. Not on any single company's server."
|
|
312
|
+
- **"Is the relay reading my data?"** → "No. The relay only forwards encrypted bundles. Plaintext never leaves your device."
|
|
313
|
+
- **"What if TotalReclaw shuts down?"** → "Your memories survive. They're on a public blockchain. Any compatible relay (or self-hosted relay) can read them with your recovery phrase."
|
|
291
314
|
|
|
292
|
-
|
|
315
|
+
## Tool surface summary
|
|
293
316
|
|
|
294
|
-
|
|
317
|
+
Hybrid-primary: `tr remember` · `tr recall` · `tr pair` · `tr status` (primary path under OpenClaw 2026.5.x).
|
|
318
|
+
Native fallback (when bound): `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).
|
|
295
319
|
|
|
296
|
-
|
|
297
|
-
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)
|
|
320
|
+
**Default behavior summary**: the agent's job is to call `totalreclaw_remember` aggressively on user statements that match the trigger-phrase list, and `totalreclaw_recall` when the user asks about themselves. Local files (`MEMORY.md`, `USER.md`, etc.) are NOT for user memory — they are agent-persona configuration only.
|
package/dist/index.js
CHANGED
|
@@ -1811,7 +1811,7 @@ async function handlePluginImportFrom(params, logger) {
|
|
|
1811
1811
|
_importInProgress = true;
|
|
1812
1812
|
const startTime = Date.now();
|
|
1813
1813
|
const source = params.source;
|
|
1814
|
-
const validSources = ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'
|
|
1814
|
+
const validSources = ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'];
|
|
1815
1815
|
if (!source || !validSources.includes(source)) {
|
|
1816
1816
|
return { success: false, error: `Invalid source. Must be one of: ${validSources.join(', ')}` };
|
|
1817
1817
|
}
|
|
@@ -2112,7 +2112,7 @@ async function handleBatchImport(params, logger) {
|
|
|
2112
2112
|
const content = params.content;
|
|
2113
2113
|
const offset = params.offset ?? 0;
|
|
2114
2114
|
const batchSize = params.batch_size ?? 25;
|
|
2115
|
-
const validSources = ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'
|
|
2115
|
+
const validSources = ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'];
|
|
2116
2116
|
if (!source || !validSources.includes(source)) {
|
|
2117
2117
|
return { success: false, error: `Invalid source. Must be one of: ${validSources.join(', ')}` };
|
|
2118
2118
|
}
|
|
@@ -2575,12 +2575,52 @@ const plugin = {
|
|
|
2575
2575
|
// Fix #1 which gates on installs being present).
|
|
2576
2576
|
const patchResult = patchOpenClawConfig(undefined, pluginVersion ?? undefined);
|
|
2577
2577
|
if (patchResult === 'patched') {
|
|
2578
|
+
// 3.3.12-rc.6 (auto-QA finding 2026-05-09): previously we only
|
|
2579
|
+
// warned the user to manually restart. That created a silent
|
|
2580
|
+
// hook-failure on the FIRST gateway boot post-install — the
|
|
2581
|
+
// plugin loads with stale in-memory config, hook handlers
|
|
2582
|
+
// never register, auto-extraction never fires, and only a
|
|
2583
|
+
// second manual restart fixes it. First-time users hit this
|
|
2584
|
+
// every install. Auto-extraction QA reproduced it as 2/5
|
|
2585
|
+
// turns missed (hook silently no-op'd on turns 1-3).
|
|
2586
|
+
//
|
|
2587
|
+
// Fix: when the patch wrote anything, fire SIGUSR1 to our own
|
|
2588
|
+
// PID. The gateway accepts SIGUSR1 iff `commands.restart=true`
|
|
2589
|
+
// (the default); see upstream `setGatewaySigusr1RestartPolicy`.
|
|
2590
|
+
// The signal triggers an in-process restart that re-reads the
|
|
2591
|
+
// freshly-patched openclaw.json and registers hook handlers
|
|
2592
|
+
// with `allowConversationAccess=true` honoured.
|
|
2593
|
+
//
|
|
2594
|
+
// Idempotency: second boot reads the patched config and
|
|
2595
|
+
// returns `'unchanged'` from patchOpenClawConfig, so the
|
|
2596
|
+
// signal fires AT MOST once per config-key-change. No restart
|
|
2597
|
+
// loop possible.
|
|
2598
|
+
//
|
|
2599
|
+
// Defer via setImmediate so register() finishes (logger flush
|
|
2600
|
+
// + plugin load record writeback) before the signal lands.
|
|
2601
|
+
// A 250ms setTimeout adds slack for slow disk on Telegram VPS
|
|
2602
|
+
// (Hetzner small VPS tail-latency observed ~120ms on writes).
|
|
2603
|
+
//
|
|
2604
|
+
// Phrase-safety: process.kill on own PID is local-only; no
|
|
2605
|
+
// outbound markers. Already used by `/totalreclaw-restart`
|
|
2606
|
+
// (registered ~400 lines below) under the same scanner-safe
|
|
2607
|
+
// pattern.
|
|
2578
2608
|
api.logger.warn('TotalReclaw: updated openclaw.json with required 2026.5.x keys ' +
|
|
2579
2609
|
'(plugins.slots.memory + hooks.allowConversationAccess + ' +
|
|
2580
2610
|
'channels.telegram.streaming.mode + plugins.bundledDiscovery + ' +
|
|
2581
2611
|
'plugins.allow + plugins.installs.totalreclaw self-heal). ' +
|
|
2582
|
-
'
|
|
2583
|
-
|
|
2612
|
+
'Auto-restarting gateway via SIGUSR1 to apply.');
|
|
2613
|
+
setTimeout(() => {
|
|
2614
|
+
try {
|
|
2615
|
+
process.kill(process.pid, 'SIGUSR1');
|
|
2616
|
+
}
|
|
2617
|
+
catch (err) {
|
|
2618
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2619
|
+
api.logger.warn(`TotalReclaw: auto-restart SIGUSR1 emit failed (${msg}). ` +
|
|
2620
|
+
'Run `/totalreclaw-restart` or restart the gateway manually ' +
|
|
2621
|
+
'for the patched config to take effect.');
|
|
2622
|
+
}
|
|
2623
|
+
}, 250);
|
|
2584
2624
|
}
|
|
2585
2625
|
else if (patchResult === 'error') {
|
|
2586
2626
|
api.logger.warn('TotalReclaw: failed to auto-patch openclaw.json for OpenClaw 2026.5.x ' +
|
|
@@ -4509,7 +4549,7 @@ const plugin = {
|
|
|
4509
4549
|
properties: {
|
|
4510
4550
|
source: {
|
|
4511
4551
|
type: 'string',
|
|
4512
|
-
enum: ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'
|
|
4552
|
+
enum: ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'],
|
|
4513
4553
|
description: 'The source system to import from (gemini: Google Takeout HTML; chatgpt: conversations.json or memory text; claude: memory text)',
|
|
4514
4554
|
},
|
|
4515
4555
|
api_key: {
|
package/dist/llm-client.js
CHANGED
|
@@ -598,6 +598,47 @@ export function isRetryable(errorMessage) {
|
|
|
598
598
|
// ---------------------------------------------------------------------------
|
|
599
599
|
// OpenAI-compatible chat completion
|
|
600
600
|
// ---------------------------------------------------------------------------
|
|
601
|
+
/**
|
|
602
|
+
* Provider base-URL hints for OpenAI-compatible endpoints that honour the
|
|
603
|
+
* `response_format: {"type": "json_object"}` body field. Sending the field
|
|
604
|
+
* to a provider that supports it makes JSON output deterministic; sending
|
|
605
|
+
* it to a provider that does NOT recognise the field is a 400.
|
|
606
|
+
*
|
|
607
|
+
* Why this exists (3.3.12-rc.6, 2026-05-09):
|
|
608
|
+
* z.ai's GLM family (4.5-flash, 5-turbo, 5.1) silently returns EMPTY
|
|
609
|
+
* `message.content` for the merged-extraction prompt unless this hint
|
|
610
|
+
* is set. No error, no warning — the LLM just emits "" instead of the
|
|
611
|
+
* expected `{"topics": [], "facts": []}` JSON. Plugin's parse step
|
|
612
|
+
* then logs `0 raw facts` from a successful-but-empty branch.
|
|
613
|
+
*
|
|
614
|
+
* This bug was found and fixed on the Python (Hermes) side in
|
|
615
|
+
* 2.3.1-rc.23 (see `python/src/totalreclaw/agent/llm_client.py`
|
|
616
|
+
* `_supports_json_object_response_format`) but the plugin TS port did
|
|
617
|
+
* not carry the fix — observed in plugin 3.3.12-rc.5 auto-QA on
|
|
618
|
+
* 2026-05-09: hook + poller both fired correctly but extraction
|
|
619
|
+
* returned 0 facts on every batch despite trajectories containing
|
|
620
|
+
* explicit "I prefer X" / "I work at Y" statements.
|
|
621
|
+
*
|
|
622
|
+
* Mirror of Python's `_supports_json_object_response_format`. Match by
|
|
623
|
+
* substring on a lowercased baseUrl so cosmetic prefix differences
|
|
624
|
+
* (https://, /v1, etc.) don't matter.
|
|
625
|
+
*/
|
|
626
|
+
const JSON_OBJECT_PROVIDER_HINTS = [
|
|
627
|
+
'z.ai',
|
|
628
|
+
'api.openai.com',
|
|
629
|
+
'groq.com',
|
|
630
|
+
'openrouter.ai',
|
|
631
|
+
'deepseek.com',
|
|
632
|
+
'mistral.ai',
|
|
633
|
+
'x.ai',
|
|
634
|
+
'together.xyz',
|
|
635
|
+
];
|
|
636
|
+
export function supportsJsonObjectResponseFormat(baseUrl) {
|
|
637
|
+
if (!baseUrl)
|
|
638
|
+
return false;
|
|
639
|
+
const lower = baseUrl.toLowerCase();
|
|
640
|
+
return JSON_OBJECT_PROVIDER_HINTS.some((h) => lower.includes(h));
|
|
641
|
+
}
|
|
601
642
|
async function chatCompletionOpenAI(config, messages, maxTokens, temperature, timeoutMs) {
|
|
602
643
|
const url = `${config.baseUrl}/chat/completions`;
|
|
603
644
|
const body = {
|
|
@@ -606,6 +647,12 @@ async function chatCompletionOpenAI(config, messages, maxTokens, temperature, ti
|
|
|
606
647
|
temperature,
|
|
607
648
|
max_completion_tokens: maxTokens,
|
|
608
649
|
};
|
|
650
|
+
// 3.3.12-rc.6: hint the provider to return strict JSON. Critical for
|
|
651
|
+
// z.ai/GLM (silent-empty without it). See JSON_OBJECT_PROVIDER_HINTS
|
|
652
|
+
// doc above.
|
|
653
|
+
if (supportsJsonObjectResponseFormat(config.baseUrl)) {
|
|
654
|
+
body.response_format = { type: 'json_object' };
|
|
655
|
+
}
|
|
609
656
|
try {
|
|
610
657
|
const res = await fetch(url, {
|
|
611
658
|
method: 'POST',
|
|
@@ -621,7 +668,28 @@ async function chatCompletionOpenAI(config, messages, maxTokens, temperature, ti
|
|
|
621
668
|
throw new Error(`LLM API ${res.status}: ${text.slice(0, 200)}`);
|
|
622
669
|
}
|
|
623
670
|
const json = (await res.json());
|
|
624
|
-
|
|
671
|
+
const content = json.choices?.[0]?.message?.content ?? null;
|
|
672
|
+
// 3.3.12-rc.6: loud-on-empty. If the provider returned a 200 with
|
|
673
|
+
// empty content, this almost always means a missing response_format
|
|
674
|
+
// hint or a content-filter. Without this log the silent-empty
|
|
675
|
+
// failure mode (Python rc.23 / plugin rc.5) is invisible to ops.
|
|
676
|
+
if (content === '' || content === null) {
|
|
677
|
+
// Lazy import to avoid circular dep with the registered logger.
|
|
678
|
+
// Fall back to console.warn if logger unavailable.
|
|
679
|
+
const warn = (msg) => {
|
|
680
|
+
try {
|
|
681
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
682
|
+
globalThis?.console?.warn?.(msg);
|
|
683
|
+
}
|
|
684
|
+
catch {
|
|
685
|
+
/* noop */
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
warn(`[totalreclaw][llm-client] provider=${config.baseUrl} model=${config.model} ` +
|
|
689
|
+
`returned empty content (status=200). ` +
|
|
690
|
+
`If using z.ai/GLM/OpenAI-compat, check response_format hint is being sent.`);
|
|
691
|
+
}
|
|
692
|
+
return content;
|
|
625
693
|
}
|
|
626
694
|
catch (err) {
|
|
627
695
|
const msg = err instanceof Error ? err.message : String(err);
|
package/dist/tr-cli.js
CHANGED
|
@@ -51,7 +51,7 @@ const STATE_PATH = CONFIG.onboardingStatePath;
|
|
|
51
51
|
// Auto-synced by skill/scripts/sync-version.mjs from skill/plugin/package.json::version.
|
|
52
52
|
// Do not edit by hand — running tests will catch drift but the publish workflow
|
|
53
53
|
// rewrites this constant at the start of every npm/ClawHub publish.
|
|
54
|
-
const PLUGIN_VERSION = '3.3.12-rc.
|
|
54
|
+
const PLUGIN_VERSION = '3.3.12-rc.6';
|
|
55
55
|
function die(msg, code = 1) {
|
|
56
56
|
process.stderr.write(`tr: ${msg}\n`);
|
|
57
57
|
process.exit(code);
|
package/import-adapters/types.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface NormalizedFact {
|
|
|
19
19
|
tags?: string[];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export type ImportSource = 'mem0' | 'mcp-memory' | 'chatgpt' | 'claude' | 'gemini'
|
|
22
|
+
export type ImportSource = 'mem0' | 'mcp-memory' | 'chatgpt' | 'claude' | 'gemini';
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* What the user passes to the import tool.
|
package/index.ts
CHANGED
|
@@ -2255,7 +2255,7 @@ async function handlePluginImportFrom(
|
|
|
2255
2255
|
const startTime = Date.now();
|
|
2256
2256
|
|
|
2257
2257
|
const source = params.source as string;
|
|
2258
|
-
const validSources = ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'
|
|
2258
|
+
const validSources = ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'];
|
|
2259
2259
|
|
|
2260
2260
|
if (!source || !validSources.includes(source)) {
|
|
2261
2261
|
return { success: false, error: `Invalid source. Must be one of: ${validSources.join(', ')}` };
|
|
@@ -2628,7 +2628,7 @@ async function handleBatchImport(
|
|
|
2628
2628
|
const offset = (params.offset as number) ?? 0;
|
|
2629
2629
|
const batchSize = (params.batch_size as number) ?? 25;
|
|
2630
2630
|
|
|
2631
|
-
const validSources = ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'
|
|
2631
|
+
const validSources = ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'];
|
|
2632
2632
|
if (!source || !validSources.includes(source)) {
|
|
2633
2633
|
return { success: false, error: `Invalid source. Must be one of: ${validSources.join(', ')}` };
|
|
2634
2634
|
}
|
|
@@ -3156,14 +3156,55 @@ const plugin = {
|
|
|
3156
3156
|
// Fix #1 which gates on installs being present).
|
|
3157
3157
|
const patchResult = patchOpenClawConfig(undefined, pluginVersion ?? undefined);
|
|
3158
3158
|
if (patchResult === 'patched') {
|
|
3159
|
+
// 3.3.12-rc.6 (auto-QA finding 2026-05-09): previously we only
|
|
3160
|
+
// warned the user to manually restart. That created a silent
|
|
3161
|
+
// hook-failure on the FIRST gateway boot post-install — the
|
|
3162
|
+
// plugin loads with stale in-memory config, hook handlers
|
|
3163
|
+
// never register, auto-extraction never fires, and only a
|
|
3164
|
+
// second manual restart fixes it. First-time users hit this
|
|
3165
|
+
// every install. Auto-extraction QA reproduced it as 2/5
|
|
3166
|
+
// turns missed (hook silently no-op'd on turns 1-3).
|
|
3167
|
+
//
|
|
3168
|
+
// Fix: when the patch wrote anything, fire SIGUSR1 to our own
|
|
3169
|
+
// PID. The gateway accepts SIGUSR1 iff `commands.restart=true`
|
|
3170
|
+
// (the default); see upstream `setGatewaySigusr1RestartPolicy`.
|
|
3171
|
+
// The signal triggers an in-process restart that re-reads the
|
|
3172
|
+
// freshly-patched openclaw.json and registers hook handlers
|
|
3173
|
+
// with `allowConversationAccess=true` honoured.
|
|
3174
|
+
//
|
|
3175
|
+
// Idempotency: second boot reads the patched config and
|
|
3176
|
+
// returns `'unchanged'` from patchOpenClawConfig, so the
|
|
3177
|
+
// signal fires AT MOST once per config-key-change. No restart
|
|
3178
|
+
// loop possible.
|
|
3179
|
+
//
|
|
3180
|
+
// Defer via setImmediate so register() finishes (logger flush
|
|
3181
|
+
// + plugin load record writeback) before the signal lands.
|
|
3182
|
+
// A 250ms setTimeout adds slack for slow disk on Telegram VPS
|
|
3183
|
+
// (Hetzner small VPS tail-latency observed ~120ms on writes).
|
|
3184
|
+
//
|
|
3185
|
+
// Phrase-safety: process.kill on own PID is local-only; no
|
|
3186
|
+
// outbound markers. Already used by `/totalreclaw-restart`
|
|
3187
|
+
// (registered ~400 lines below) under the same scanner-safe
|
|
3188
|
+
// pattern.
|
|
3159
3189
|
api.logger.warn(
|
|
3160
3190
|
'TotalReclaw: updated openclaw.json with required 2026.5.x keys ' +
|
|
3161
3191
|
'(plugins.slots.memory + hooks.allowConversationAccess + ' +
|
|
3162
3192
|
'channels.telegram.streaming.mode + plugins.bundledDiscovery + ' +
|
|
3163
3193
|
'plugins.allow + plugins.installs.totalreclaw self-heal). ' +
|
|
3164
|
-
'
|
|
3165
|
-
'Run `/totalreclaw-restart` or restart the gateway manually.',
|
|
3194
|
+
'Auto-restarting gateway via SIGUSR1 to apply.',
|
|
3166
3195
|
);
|
|
3196
|
+
setTimeout(() => {
|
|
3197
|
+
try {
|
|
3198
|
+
process.kill(process.pid, 'SIGUSR1');
|
|
3199
|
+
} catch (err) {
|
|
3200
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3201
|
+
api.logger.warn(
|
|
3202
|
+
`TotalReclaw: auto-restart SIGUSR1 emit failed (${msg}). ` +
|
|
3203
|
+
'Run `/totalreclaw-restart` or restart the gateway manually ' +
|
|
3204
|
+
'for the patched config to take effect.',
|
|
3205
|
+
);
|
|
3206
|
+
}
|
|
3207
|
+
}, 250);
|
|
3167
3208
|
} else if (patchResult === 'error') {
|
|
3168
3209
|
api.logger.warn(
|
|
3169
3210
|
'TotalReclaw: failed to auto-patch openclaw.json for OpenClaw 2026.5.x ' +
|
|
@@ -5296,7 +5337,7 @@ const plugin = {
|
|
|
5296
5337
|
properties: {
|
|
5297
5338
|
source: {
|
|
5298
5339
|
type: 'string',
|
|
5299
|
-
enum: ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'
|
|
5340
|
+
enum: ['mem0', 'mcp-memory', 'chatgpt', 'claude', 'gemini'],
|
|
5300
5341
|
description: 'The source system to import from (gemini: Google Takeout HTML; chatgpt: conversations.json or memory text; claude: memory text)',
|
|
5301
5342
|
},
|
|
5302
5343
|
api_key: {
|
package/llm-client.ts
CHANGED
|
@@ -801,6 +801,48 @@ export function isRetryable(errorMessage: string): boolean {
|
|
|
801
801
|
// OpenAI-compatible chat completion
|
|
802
802
|
// ---------------------------------------------------------------------------
|
|
803
803
|
|
|
804
|
+
/**
|
|
805
|
+
* Provider base-URL hints for OpenAI-compatible endpoints that honour the
|
|
806
|
+
* `response_format: {"type": "json_object"}` body field. Sending the field
|
|
807
|
+
* to a provider that supports it makes JSON output deterministic; sending
|
|
808
|
+
* it to a provider that does NOT recognise the field is a 400.
|
|
809
|
+
*
|
|
810
|
+
* Why this exists (3.3.12-rc.6, 2026-05-09):
|
|
811
|
+
* z.ai's GLM family (4.5-flash, 5-turbo, 5.1) silently returns EMPTY
|
|
812
|
+
* `message.content` for the merged-extraction prompt unless this hint
|
|
813
|
+
* is set. No error, no warning — the LLM just emits "" instead of the
|
|
814
|
+
* expected `{"topics": [], "facts": []}` JSON. Plugin's parse step
|
|
815
|
+
* then logs `0 raw facts` from a successful-but-empty branch.
|
|
816
|
+
*
|
|
817
|
+
* This bug was found and fixed on the Python (Hermes) side in
|
|
818
|
+
* 2.3.1-rc.23 (see `python/src/totalreclaw/agent/llm_client.py`
|
|
819
|
+
* `_supports_json_object_response_format`) but the plugin TS port did
|
|
820
|
+
* not carry the fix — observed in plugin 3.3.12-rc.5 auto-QA on
|
|
821
|
+
* 2026-05-09: hook + poller both fired correctly but extraction
|
|
822
|
+
* returned 0 facts on every batch despite trajectories containing
|
|
823
|
+
* explicit "I prefer X" / "I work at Y" statements.
|
|
824
|
+
*
|
|
825
|
+
* Mirror of Python's `_supports_json_object_response_format`. Match by
|
|
826
|
+
* substring on a lowercased baseUrl so cosmetic prefix differences
|
|
827
|
+
* (https://, /v1, etc.) don't matter.
|
|
828
|
+
*/
|
|
829
|
+
const JSON_OBJECT_PROVIDER_HINTS = [
|
|
830
|
+
'z.ai',
|
|
831
|
+
'api.openai.com',
|
|
832
|
+
'groq.com',
|
|
833
|
+
'openrouter.ai',
|
|
834
|
+
'deepseek.com',
|
|
835
|
+
'mistral.ai',
|
|
836
|
+
'x.ai',
|
|
837
|
+
'together.xyz',
|
|
838
|
+
] as const;
|
|
839
|
+
|
|
840
|
+
export function supportsJsonObjectResponseFormat(baseUrl: string | undefined): boolean {
|
|
841
|
+
if (!baseUrl) return false;
|
|
842
|
+
const lower = baseUrl.toLowerCase();
|
|
843
|
+
return JSON_OBJECT_PROVIDER_HINTS.some((h) => lower.includes(h));
|
|
844
|
+
}
|
|
845
|
+
|
|
804
846
|
async function chatCompletionOpenAI(
|
|
805
847
|
config: LLMClientConfig,
|
|
806
848
|
messages: ChatMessage[],
|
|
@@ -817,6 +859,13 @@ async function chatCompletionOpenAI(
|
|
|
817
859
|
max_completion_tokens: maxTokens,
|
|
818
860
|
};
|
|
819
861
|
|
|
862
|
+
// 3.3.12-rc.6: hint the provider to return strict JSON. Critical for
|
|
863
|
+
// z.ai/GLM (silent-empty without it). See JSON_OBJECT_PROVIDER_HINTS
|
|
864
|
+
// doc above.
|
|
865
|
+
if (supportsJsonObjectResponseFormat(config.baseUrl)) {
|
|
866
|
+
body.response_format = { type: 'json_object' };
|
|
867
|
+
}
|
|
868
|
+
|
|
820
869
|
try {
|
|
821
870
|
const res = await fetch(url, {
|
|
822
871
|
method: 'POST',
|
|
@@ -834,7 +883,31 @@ async function chatCompletionOpenAI(
|
|
|
834
883
|
}
|
|
835
884
|
|
|
836
885
|
const json = (await res.json()) as ChatCompletionResponse;
|
|
837
|
-
|
|
886
|
+
const content = json.choices?.[0]?.message?.content ?? null;
|
|
887
|
+
|
|
888
|
+
// 3.3.12-rc.6: loud-on-empty. If the provider returned a 200 with
|
|
889
|
+
// empty content, this almost always means a missing response_format
|
|
890
|
+
// hint or a content-filter. Without this log the silent-empty
|
|
891
|
+
// failure mode (Python rc.23 / plugin rc.5) is invisible to ops.
|
|
892
|
+
if (content === '' || content === null) {
|
|
893
|
+
// Lazy import to avoid circular dep with the registered logger.
|
|
894
|
+
// Fall back to console.warn if logger unavailable.
|
|
895
|
+
const warn = (msg: string) => {
|
|
896
|
+
try {
|
|
897
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
898
|
+
(globalThis as any)?.console?.warn?.(msg);
|
|
899
|
+
} catch {
|
|
900
|
+
/* noop */
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
warn(
|
|
904
|
+
`[totalreclaw][llm-client] provider=${config.baseUrl} model=${config.model} ` +
|
|
905
|
+
`returned empty content (status=200). ` +
|
|
906
|
+
`If using z.ai/GLM/OpenAI-compat, check response_format hint is being sent.`,
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
return content;
|
|
838
911
|
} catch (err) {
|
|
839
912
|
const msg = err instanceof Error ? err.message : String(err);
|
|
840
913
|
throw new Error(`LLM call failed: ${msg}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totalreclaw/totalreclaw",
|
|
3
|
-
"version": "3.3.12-rc.
|
|
3
|
+
"version": "3.3.12-rc.6",
|
|
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": [
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "rm -rf dist && tsc -p tsconfig.json --noCheck",
|
|
69
69
|
"verify-tarball": "node ../scripts/verify-tarball.mjs",
|
|
70
|
-
"test": "npx tsx manifest-shape.test.ts && npx tsx config-schema.test.ts && npx tsx config.test.ts && npx tsx relay-headers.test.ts && npx tsx scope-address-visible.test.ts && npx tsx llm-profile-reader.test.ts && npx tsx llm-client.test.ts && npx tsx llm-client-retry.test.ts && npx tsx gateway-url.test.ts && npx tsx retype-setscope.test.ts && npx tsx tool-gating.test.ts && npx tsx onboarding-noninteractive.test.ts && npx tsx pair-cli-json.test.ts && npx tsx pair-qr.test.ts && npx tsx pair-remote-client.test.ts && npx tsx qa-bug-report.test.ts && npx tsx nonce-serialization.test.ts && npx tsx phrase-safety-registry.test.ts && npx tsx test_issue_92_onnx_download_ux.test.ts && npx tsx onboard-pair-only.test.ts && npx tsx import-time-smoke.test.ts && npx tsx install-staging-cleanup.test.ts && npx tsx partial-install-detection.test.ts && npx tsx install-reload-idempotency.test.ts && npx tsx json-stdout-cleanliness.test.ts && npx tsx load-manifest.test.ts && npx tsx url-binding.test.ts && npx tsx fs-helpers.test.ts && npx tsx pair-cli-default-mode.test.ts && npx tsx embedding-fallback-tag.test.ts && npx tsx staging-banner-gate.test.ts && npx tsx restart-auth.test.ts && npx tsx inbound-user-tracker.test.ts && npx tsx register-command-name.test.ts && npx tsx skill-md-hybrid-primary.test.ts && npx tsx tr-cli-json-output.test.ts",
|
|
70
|
+
"test": "npx tsx manifest-shape.test.ts && npx tsx config-schema.test.ts && npx tsx config.test.ts && npx tsx relay-headers.test.ts && npx tsx scope-address-visible.test.ts && npx tsx llm-profile-reader.test.ts && npx tsx llm-client.test.ts && npx tsx llm-client-retry.test.ts && npx tsx llm-client-json-mode.test.ts && npx tsx gateway-url.test.ts && npx tsx retype-setscope.test.ts && npx tsx tool-gating.test.ts && npx tsx onboarding-noninteractive.test.ts && npx tsx pair-cli-json.test.ts && npx tsx pair-qr.test.ts && npx tsx pair-remote-client.test.ts && npx tsx qa-bug-report.test.ts && npx tsx nonce-serialization.test.ts && npx tsx phrase-safety-registry.test.ts && npx tsx test_issue_92_onnx_download_ux.test.ts && npx tsx onboard-pair-only.test.ts && npx tsx import-time-smoke.test.ts && npx tsx install-staging-cleanup.test.ts && npx tsx partial-install-detection.test.ts && npx tsx install-reload-idempotency.test.ts && npx tsx json-stdout-cleanliness.test.ts && npx tsx load-manifest.test.ts && npx tsx url-binding.test.ts && npx tsx fs-helpers.test.ts && npx tsx pair-cli-default-mode.test.ts && npx tsx embedding-fallback-tag.test.ts && npx tsx staging-banner-gate.test.ts && npx tsx restart-auth.test.ts && npx tsx inbound-user-tracker.test.ts && npx tsx register-command-name.test.ts && npx tsx skill-md-hybrid-primary.test.ts && npx tsx tr-cli-json-output.test.ts",
|
|
71
71
|
"smoke:dist": "npx tsx dist-esm-smoke.test.ts",
|
|
72
72
|
"check-scanner": "node ../scripts/check-scanner.mjs",
|
|
73
73
|
"check-version-drift": "node ../scripts/check-version-drift.mjs",
|
package/skill.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "totalreclaw",
|
|
3
|
-
"version": "3.3.12-rc.
|
|
3
|
+
"version": "3.3.12-rc.6",
|
|
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",
|
package/tr-cli.ts
CHANGED
|
@@ -72,7 +72,7 @@ const STATE_PATH = CONFIG.onboardingStatePath;
|
|
|
72
72
|
// Auto-synced by skill/scripts/sync-version.mjs from skill/plugin/package.json::version.
|
|
73
73
|
// Do not edit by hand — running tests will catch drift but the publish workflow
|
|
74
74
|
// rewrites this constant at the start of every npm/ClawHub publish.
|
|
75
|
-
const PLUGIN_VERSION = '3.3.12-rc.
|
|
75
|
+
const PLUGIN_VERSION = '3.3.12-rc.6';
|
|
76
76
|
|
|
77
77
|
function die(msg: string, code = 1): never {
|
|
78
78
|
process.stderr.write(`tr: ${msg}\n`);
|