@totalreclaw/totalreclaw 3.3.1-rc.3 → 3.3.1-rc.4
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 +18 -0
- package/SKILL.md +19 -7
- package/index.ts +31 -138
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ 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.1-rc.4] — 2026-04-22
|
|
8
|
+
|
|
9
|
+
Phrase-safety hardening: `totalreclaw_onboard` agent tool removed. Paired with Hermes Python `2.3.1rc4` (which ports the QR-pair flow to Python so Hermes users gain a phrase-safe agent setup path too).
|
|
10
|
+
|
|
11
|
+
### Removed (phrase-safety enforcement — BREAKING for agent tool callers)
|
|
12
|
+
|
|
13
|
+
- **`totalreclaw_onboard` agent tool — REMOVED.** rc.3 shipped a `totalreclaw_onboard` tool that generated a fresh BIP-39 mnemonic in-process, wrote it to `credentials.json`, and returned `{scope_address, credentials_path}`. `emitPhrase: false` kept the mnemonic out of the tool's return payload, but NOTHING ARCHITECTURALLY PREVENTED leakage — a future patch could regress the flag, a different code path could echo the mnemonic in a log/error, or the mere existence of the tool signalled to agents that phrase generation inside chat is fine (it isn't). Per `project_phrase_safety_rule.md`: "recovery phrase MUST NEVER cross the LLM context in ANY form." rc.4 removes the registration. The underlying `runNonInteractiveOnboard` code path stays reachable via the CLI `openclaw totalreclaw onboard` — that path runs in the user's own terminal, OUTSIDE any agent shell, so phrase stdout never feeds back into LLM context.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **`SKILL.md` — setup section rewritten.** `totalreclaw_pair` is now the canonical setup surface for all users (local or remote). The CLI wizard (`openclaw totalreclaw onboard`) is explicitly documented as user-terminal-only — agents MUST NOT invoke it via their shell tool. Tool surface table updated: `totalreclaw_onboard` removed, `totalreclaw_pair` promoted to canonical. `totalreclaw_onboarding_start` remains as a pointer-only tool for users who explicitly prefer local-terminal setup.
|
|
18
|
+
- **`index.ts` — `totalreclaw_pair` tool description updated.** Removed backref to `totalreclaw_onboard`; now instructs agents to always prefer pair, with `totalreclaw_onboarding_start` as the fallback pointer for local-terminal-only users.
|
|
19
|
+
- **`docs/guides/openclaw-setup.md` — QR pairing is now documented as the default setup flow.** CLI wizard moved to a user-terminal-only subsection with a prominent "do NOT run this through an agent shell" warning.
|
|
20
|
+
|
|
21
|
+
### Tests
|
|
22
|
+
|
|
23
|
+
- **`phrase-safety-registry.test.ts`** — new. Text-scans `index.ts` for `api.registerTool({ name: '...' })` literals and asserts: (a) `totalreclaw_onboard` is NOT in the list; (b) `totalreclaw_pair` IS in the list; (c) no name contains phrase-adjacent tokens (`onboard_generate`, `generate_phrase`, `generate_mnemonic`, `restore_phrase`, `restore_mnemonic`, `mnemonic`). Runs as part of `npm test`.
|
|
24
|
+
|
|
7
25
|
## [3.3.1-rc.3] — 2026-04-22
|
|
8
26
|
|
|
9
27
|
Patch RC bundling two stability fixes, one new RC-gated tool, two SKILL.md addendums, and a configurable LLM retry budget. All prior rc.1 + rc.2 fixes are preserved.
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: totalreclaw
|
|
3
3
|
description: "End-to-end encrypted memory for AI agents — portable, yours forever. XChaCha20-Poly1305 E2EE: server never sees plaintext."
|
|
4
|
-
version: 3.3.1-rc.
|
|
4
|
+
version: 3.3.1-rc.4
|
|
5
5
|
author: TotalReclaw Team
|
|
6
6
|
license: MIT
|
|
7
7
|
homepage: https://totalreclaw.xyz
|
|
@@ -30,14 +30,26 @@ You are operating inside a host that has the TotalReclaw memory plugin loaded. T
|
|
|
30
30
|
|
|
31
31
|
Before any memory tool, check `~/.totalreclaw/credentials.json`:
|
|
32
32
|
|
|
33
|
-
- **Missing, empty, malformed, or no `mnemonic` key** -> NOT set up. You MUST NOT generate a phrase in chat, and you MUST NOT ask the user to paste one.
|
|
34
|
-
- Local gateway: call `totalreclaw_onboarding_start` (or `totalreclaw_onboard` if available); both return a pointer to `openclaw totalreclaw onboard`.
|
|
35
|
-
- Remote gateway (VPS / Docker / no TTY): call `totalreclaw_pair` (v3.3.1+), OR instruct the user to run `openclaw totalreclaw pair generate --json`. Present the returned `{url, pin, qr_ascii}` to the user. The phrase is entered in their browser, never in this conversation.
|
|
33
|
+
- **Missing, empty, malformed, or no `mnemonic` key** -> NOT set up. Follow the "Setting up a user's TotalReclaw recovery phrase" section below. You MUST NOT generate a phrase in chat, and you MUST NOT ask the user to paste one.
|
|
36
34
|
- **Present with a valid `mnemonic`** -> active. Use the tools below.
|
|
37
35
|
|
|
36
|
+
## Setting up a user's TotalReclaw recovery phrase
|
|
37
|
+
|
|
38
|
+
**Agent path (default, only secure path): call the `totalreclaw_pair` tool.** It returns a URL + 6-digit PIN. Tell the user:
|
|
39
|
+
|
|
40
|
+
> "Open <URL> in your browser, enter your phrase (or let the browser generate one), and confirm PIN <PIN>. Your phrase is encrypted in the browser and uploaded end-to-end-encrypted to this gateway — it never crosses this chat."
|
|
41
|
+
|
|
42
|
+
Browser-side crypto handles the phrase; this chat never sees it. After the user confirms "pairing complete" in their browser, the credentials are already on disk — no gateway restart needed for OpenClaw because routes are registered on plugin install and the pair HTTP handler writes credentials in-process.
|
|
43
|
+
|
|
44
|
+
**NEVER** invoke `openclaw totalreclaw onboard`, `totalreclaw setup`, or any phrase-generating CLI via your shell tool. Those commands are for users to run in their own terminal — not for you. Agent shell stdout is captured into LLM context; running them leaks the phrase.
|
|
45
|
+
|
|
46
|
+
**NEVER** ask the user to paste their phrase into chat. If they do, immediately tell them to rotate by generating a new wallet via `totalreclaw_pair` with `mode=generate`; the pasted phrase is compromised.
|
|
47
|
+
|
|
48
|
+
**If the user explicitly prefers local-terminal setup** (no browser, no QR URL to open), point them at `totalreclaw_onboarding_start` — which returns a pointer to the CLI wizard they run THEMSELVES. You do not run it.
|
|
49
|
+
|
|
38
50
|
## Core rules
|
|
39
51
|
|
|
40
|
-
1. **Phrase safety (NON-NEGOTIABLE).** The recovery phrase is a secret. Never generate one in chat, never echo one back, never log one in reasoning / tool payloads, never pass one to another tool.
|
|
52
|
+
1. **Phrase safety (NON-NEGOTIABLE, ABSOLUTE).** The recovery phrase is a secret. Never generate one in chat, never echo one back, never log one in reasoning / tool payloads, never pass one to another tool, never invoke a phrase-generating CLI via your shell tool. A leaked phrase = compromised vault, no rotation possible. Governed by `project_phrase_safety_rule.md` (memory file in p-diogo/totalreclaw-internal).
|
|
41
53
|
2. **Use the tools, not inline state.** `totalreclaw_remember` stores; `totalreclaw_recall` retrieves. Don't re-ask the user for past facts; don't paraphrase memories as your own recollection.
|
|
42
54
|
3. **Gateway restart is required after install.** If tools fail right after `openclaw plugins install @totalreclaw/totalreclaw`, tell the user to run `openclaw restart` or `docker restart openclaw-qa`.
|
|
43
55
|
|
|
@@ -87,8 +99,8 @@ Tools work only when credentials are active AND the gateway has been restarted p
|
|
|
87
99
|
| `totalreclaw_migrate` | optional `confirm` (dry-run by default) |
|
|
88
100
|
| `totalreclaw_import_from` / `totalreclaw_import_batch` | `source`, `file_path` or `content`, `dry_run` |
|
|
89
101
|
| `totalreclaw_consolidate` | optional `dry_run` |
|
|
90
|
-
| `totalreclaw_onboarding_start`
|
|
91
|
-
| `totalreclaw_pair` | optional `mode` (`generate` / `import`) — returns `{url, pin, qr_ascii, expires_at_ms}
|
|
102
|
+
| `totalreclaw_onboarding_start` | (none) — returns CLI pointer for users who prefer local-terminal setup |
|
|
103
|
+
| `totalreclaw_pair` | optional `mode` (`generate` / `import`) — returns `{url, pin, qr_ascii, expires_at_ms}`. CANONICAL setup surface |
|
|
92
104
|
|
|
93
105
|
## Taxonomy
|
|
94
106
|
|
package/index.ts
CHANGED
|
@@ -5049,143 +5049,34 @@ const plugin = {
|
|
|
5049
5049
|
);
|
|
5050
5050
|
|
|
5051
5051
|
// ---------------------------------------------------------------
|
|
5052
|
-
// Tool: totalreclaw_onboard
|
|
5052
|
+
// Tool: totalreclaw_onboard — REMOVED in 3.3.1-rc.4 (phrase-safety).
|
|
5053
5053
|
//
|
|
5054
|
-
//
|
|
5055
|
-
//
|
|
5056
|
-
//
|
|
5057
|
-
//
|
|
5054
|
+
// rc.3 shipped a `totalreclaw_onboard` agent tool that generated a
|
|
5055
|
+
// fresh BIP-39 mnemonic in-process, wrote it to credentials.json,
|
|
5056
|
+
// and returned `{scope_address, credentials_path}` to the agent.
|
|
5057
|
+
// `emitPhrase: false` kept the mnemonic OUT of the tool's return
|
|
5058
|
+
// payload, but NOTHING ARCHITECTURALLY PREVENTED leakage — a future
|
|
5059
|
+
// patch could regress the flag, a different code path could echo
|
|
5060
|
+
// the mnemonic in a log/error message the agent captures, or the
|
|
5061
|
+
// mere existence of the tool implied to agents that "generating a
|
|
5062
|
+
// phrase here is fine" (it isn't).
|
|
5058
5063
|
//
|
|
5059
|
-
//
|
|
5060
|
-
//
|
|
5061
|
-
//
|
|
5062
|
-
//
|
|
5063
|
-
//
|
|
5064
|
-
//
|
|
5065
|
-
//
|
|
5066
|
-
//
|
|
5064
|
+
// Per ``project_phrase_safety_rule.md``
|
|
5065
|
+
// (memory file in p-diogo/totalreclaw-internal — absolute rule:
|
|
5066
|
+
// "recovery phrase MUST NEVER cross the LLM context in ANY form"),
|
|
5067
|
+
// phrase-generating agent tools are forbidden. The ONLY approved
|
|
5068
|
+
// agent-facilitated setup surface is ``totalreclaw_pair`` (browser-
|
|
5069
|
+
// side crypto keeps the phrase out of the LLM round-trip by
|
|
5070
|
+
// construction). The underlying ``runNonInteractiveOnboard`` code
|
|
5071
|
+
// path is still reachable via the CLI ``openclaw totalreclaw onboard``
|
|
5072
|
+
// — that path runs in the user's own terminal, OUTSIDE any agent
|
|
5073
|
+
// shell, so phrase stdout never feeds back into LLM context.
|
|
5074
|
+
//
|
|
5075
|
+
// Audit assertion: ``tool-gating.test.ts`` enforces the removal —
|
|
5076
|
+
// any future re-registration of ``totalreclaw_onboard`` (or any
|
|
5077
|
+
// phrase-generating variant like ``totalreclaw_onboard_generate``,
|
|
5078
|
+
// ``totalreclaw_restore_phrase``) fails CI.
|
|
5067
5079
|
// ---------------------------------------------------------------
|
|
5068
|
-
api.registerTool(
|
|
5069
|
-
{
|
|
5070
|
-
name: 'totalreclaw_onboard',
|
|
5071
|
-
label: 'Onboard (generate new recovery phrase)',
|
|
5072
|
-
description:
|
|
5073
|
-
'Generate a NEW TotalReclaw recovery phrase on this machine without the user ' +
|
|
5074
|
-
'leaving chat. The phrase is written ONLY to ~/.totalreclaw/credentials.json (mode ' +
|
|
5075
|
-
'0600) and NEVER returned through this tool — the response contains just the derived ' +
|
|
5076
|
-
'Smart Account (scope) address and the credentials path so the user can retrieve ' +
|
|
5077
|
-
'their phrase locally (e.g. `cat ~/.totalreclaw/credentials.json | jq -r .mnemonic`).\n\n' +
|
|
5078
|
-
'Use when a fresh user asks you to set up TotalReclaw or enable memory. Refuses if ' +
|
|
5079
|
-
'onboarding is already active (the user can delete the credentials file to re-onboard, ' +
|
|
5080
|
-
'but we will NOT silently overwrite). For RESTORE (import an existing phrase), tell ' +
|
|
5081
|
-
'the user to run `openclaw totalreclaw onboard --mode restore` in their local ' +
|
|
5082
|
-
'terminal — this tool refuses to accept phrases through chat.',
|
|
5083
|
-
parameters: {
|
|
5084
|
-
type: 'object',
|
|
5085
|
-
properties: {
|
|
5086
|
-
mode: {
|
|
5087
|
-
type: 'string',
|
|
5088
|
-
enum: ['generate'],
|
|
5089
|
-
description:
|
|
5090
|
-
'Only "generate" is supported via this tool (creates a fresh 12-word BIP-39 ' +
|
|
5091
|
-
'phrase). "restore" requires the local CLI wizard because pasting a phrase ' +
|
|
5092
|
-
'through chat ships it to the LLM provider, defeating end-to-end encryption.',
|
|
5093
|
-
default: 'generate',
|
|
5094
|
-
},
|
|
5095
|
-
},
|
|
5096
|
-
additionalProperties: false,
|
|
5097
|
-
},
|
|
5098
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
5099
|
-
const mode = params?.mode;
|
|
5100
|
-
if (mode !== undefined && mode !== 'generate') {
|
|
5101
|
-
return {
|
|
5102
|
-
content: [{
|
|
5103
|
-
type: 'text',
|
|
5104
|
-
text:
|
|
5105
|
-
'Only mode="generate" is supported through chat. For RESTORE (import an ' +
|
|
5106
|
-
'existing recovery phrase), ask the user to run `openclaw totalreclaw onboard ' +
|
|
5107
|
-
'--mode restore` in their local terminal — the phrase is read from stdin and ' +
|
|
5108
|
-
'never touches the LLM provider or the transcript.',
|
|
5109
|
-
}],
|
|
5110
|
-
};
|
|
5111
|
-
}
|
|
5112
|
-
try {
|
|
5113
|
-
const result: NonInteractiveOnboardResult = await runNonInteractiveOnboard({
|
|
5114
|
-
credentialsPath: CREDENTIALS_PATH,
|
|
5115
|
-
statePath: CONFIG.onboardingStatePath,
|
|
5116
|
-
mode: 'generate',
|
|
5117
|
-
emitPhrase: false, // NEVER include the phrase in agent-visible output.
|
|
5118
|
-
deriveScopeAddress: async (mnemonic: string) => {
|
|
5119
|
-
try {
|
|
5120
|
-
return await deriveSmartAccountAddress(mnemonic, CONFIG.chainId);
|
|
5121
|
-
} catch (err) {
|
|
5122
|
-
api.logger.warn(
|
|
5123
|
-
`totalreclaw_onboard: scope-address derivation failed: ${
|
|
5124
|
-
err instanceof Error ? err.message : String(err)
|
|
5125
|
-
}`,
|
|
5126
|
-
);
|
|
5127
|
-
return undefined;
|
|
5128
|
-
}
|
|
5129
|
-
},
|
|
5130
|
-
});
|
|
5131
|
-
if (!result.ok) {
|
|
5132
|
-
// already-active, write-failed, etc. Never leaks the phrase.
|
|
5133
|
-
api.logger.info(`totalreclaw_onboard: ok=false error=${result.error}`);
|
|
5134
|
-
return {
|
|
5135
|
-
content: [{
|
|
5136
|
-
type: 'text',
|
|
5137
|
-
text:
|
|
5138
|
-
result.error === 'already-active'
|
|
5139
|
-
? 'TotalReclaw is already set up on this machine. Memory tools are unblocked. To re-onboard, delete ~/.totalreclaw/credentials.json first.'
|
|
5140
|
-
: `Onboard failed: ${result.error_detail ?? result.error}`,
|
|
5141
|
-
}],
|
|
5142
|
-
details: {
|
|
5143
|
-
ok: false,
|
|
5144
|
-
error: result.error,
|
|
5145
|
-
action: result.action,
|
|
5146
|
-
},
|
|
5147
|
-
};
|
|
5148
|
-
}
|
|
5149
|
-
api.logger.info(
|
|
5150
|
-
`totalreclaw_onboard: generated phrase, scope=${result.scope_address?.slice(0, 10) ?? 'unknown'}…, credentials=${result.credentials_path}`,
|
|
5151
|
-
);
|
|
5152
|
-
// Crucially: the mnemonic field is NEVER emitted in details
|
|
5153
|
-
// (emitPhrase=false enforces that on the result object).
|
|
5154
|
-
return {
|
|
5155
|
-
content: [{
|
|
5156
|
-
type: 'text',
|
|
5157
|
-
text:
|
|
5158
|
-
`TotalReclaw setup complete. A new 12-word recovery phrase was generated and ` +
|
|
5159
|
-
`saved to ${result.credentials_path} (mode 0600). ` +
|
|
5160
|
-
(result.scope_address
|
|
5161
|
-
? `Your on-chain scope (Smart Account) address is ${result.scope_address}. `
|
|
5162
|
-
: '') +
|
|
5163
|
-
`To view your recovery phrase, run on the user's local terminal:\n\n` +
|
|
5164
|
-
` cat ${result.credentials_path} | jq -r .mnemonic\n\n` +
|
|
5165
|
-
`IMPORTANT: the recovery phrase is the ONLY way to recover memories on another ` +
|
|
5166
|
-
`device. Tell the user to store it safely — a password manager or a paper backup. ` +
|
|
5167
|
-
`TotalReclaw cannot recover it if lost.`,
|
|
5168
|
-
}],
|
|
5169
|
-
details: {
|
|
5170
|
-
ok: true,
|
|
5171
|
-
action: 'generate',
|
|
5172
|
-
scope_address: result.scope_address,
|
|
5173
|
-
credentials_path: result.credentials_path,
|
|
5174
|
-
// Deliberately NO mnemonic field.
|
|
5175
|
-
},
|
|
5176
|
-
};
|
|
5177
|
-
} catch (err: unknown) {
|
|
5178
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
5179
|
-
api.logger.error(`totalreclaw_onboard failed: ${message}`);
|
|
5180
|
-
return {
|
|
5181
|
-
content: [{ type: 'text', text: `Onboard failed: ${humanizeError(message)}` }],
|
|
5182
|
-
details: { ok: false, error: 'unexpected', error_detail: message },
|
|
5183
|
-
};
|
|
5184
|
-
}
|
|
5185
|
-
},
|
|
5186
|
-
},
|
|
5187
|
-
{ name: 'totalreclaw_onboard' },
|
|
5188
|
-
);
|
|
5189
5080
|
|
|
5190
5081
|
// ---------------------------------------------------------------
|
|
5191
5082
|
// Tool: totalreclaw_pair (3.3.1-rc.2 — agent-callable pair-generate)
|
|
@@ -5206,10 +5097,12 @@ const plugin = {
|
|
|
5206
5097
|
'6-digit PIN, and an ASCII QR code that the agent relays to the user. The recovery ' +
|
|
5207
5098
|
'phrase itself is generated/entered in the BROWSER and uploaded end-to-end encrypted ' +
|
|
5208
5099
|
'to this gateway — it NEVER touches the LLM provider or the chat transcript.\n\n' +
|
|
5209
|
-
'
|
|
5210
|
-
'
|
|
5211
|
-
'
|
|
5212
|
-
'
|
|
5100
|
+
'This is the CANONICAL agent-facilitated setup surface — use it whenever the user ' +
|
|
5101
|
+
'asks you to set up TotalReclaw, regardless of whether they have terminal access. ' +
|
|
5102
|
+
'Browser-side crypto keeps the recovery phrase out of the LLM context entirely. ' +
|
|
5103
|
+
'If a user explicitly prefers local-terminal setup with no browser, point them at ' +
|
|
5104
|
+
'`totalreclaw_onboarding_start` (a pointer to the CLI wizard they run on their own ' +
|
|
5105
|
+
'terminal, NOT through your shell tool).',
|
|
5213
5106
|
parameters: {
|
|
5214
5107
|
type: 'object',
|
|
5215
5108
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totalreclaw/totalreclaw",
|
|
3
|
-
"version": "3.3.1-rc.
|
|
3
|
+
"version": "3.3.1-rc.4",
|
|
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": [
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"skill.json"
|
|
51
51
|
],
|
|
52
52
|
"scripts": {
|
|
53
|
-
"test": "npx tsx manifest-shape.test.ts && npx tsx config-schema.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 qa-bug-report.test.ts && npx tsx nonce-serialization.test.ts",
|
|
53
|
+
"test": "npx tsx manifest-shape.test.ts && npx tsx config-schema.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 qa-bug-report.test.ts && npx tsx nonce-serialization.test.ts && npx tsx phrase-safety-registry.test.ts",
|
|
54
54
|
"check-scanner": "node ../scripts/check-scanner.mjs",
|
|
55
55
|
"prepublishOnly": "node ../scripts/check-scanner.mjs"
|
|
56
56
|
},
|