@totalreclaw/totalreclaw 3.3.1-rc.1 → 3.3.1-rc.3

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,159 @@ 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.3] — 2026-04-22
8
+
9
+ 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.
10
+
11
+ ### Changed
12
+
13
+ - **`llm-client.ts` — configurable `ZAI_BASE_URL` + auto-fallback on "Insufficient balance" 429.** rc.2 QA surfaced that GLM Coding Plan keys hitting the STANDARD zai endpoint (and PAYG keys hitting CODING) return HTTP 429 with body `"Insufficient balance or no resource package. Please recharge."` — misleading because the key itself is valid. rc.3: (a) accepts `ZAI_BASE_URL` env override via `config.ts` / `getZaiBaseUrl()`; (b) auto-detects the error signature and flips CODING ↔ STANDARD once per call (logged at INFO). SKILL.md now documents "GLM Coding Plan → leave unset; PAYG → set `ZAI_BASE_URL=https://api.z.ai/api/paas/v4`."
14
+ - **`llm-client.ts` — retry budget 7s → ~62s (configurable).** rc.1/rc.2 QA: 5–9 of 10 extraction windows returned 0 facts against multi-minute upstream 429 storms. The 3-attempt 1s/2s/4s backoff couldn't outlast a 9-minute outage. rc.3: 5 attempts, 2s/4s/8s/16s/32s backoff, total ~62s. Configurable via `TOTALRECLAW_LLM_RETRY_BUDGET_MS` env (default 60_000). First retry logs at INFO, rest at DEBUG (debounced — no spam during long outages). On exhaustion throws `LLMUpstreamOutageError` (structured, `attempts` + `lastStatus`) so extraction callers can recognise vs bail silently. Non-retryable errors (401/403/404/parse) still propagate as plain `Error`.
15
+ - **`subgraph-store.ts` — per-account submission mutex.** rc.2 logged 16 AA25 `invalid account nonce` events from concurrent `submitFactBatchOnChain` / `submitFactOnChain` calls racing at the `eth_call getNonce(sender, 0)` step. rc.3 wraps both submission entry points in a per-`sender` `Map<scopeAddress, Promise>` chain so only one UserOp is in flight per Smart Account at a time. The existing AA25-retry-with-fresh-nonce path is unchanged and still catches relay-side zombie UserOps.
16
+
17
+ ### Added
18
+
19
+ - **`totalreclaw_report_qa_bug`** (RC-gated tool) — lets agents file structured QA-bug issues to `p-diogo/totalreclaw-internal` without the maintainer opening a fresh issue per RC finding. Only registered when the plugin version matches the `-rc.` token (via `readPluginVersion` in `fs-helpers.ts` + `isRcBuild` in the new `qa-bug-report.ts`). Handler POSTs to `https://api.github.com/repos/.../issues` with `Authorization: Bearer <token>` where `token = CONFIG.qaGithubToken` (reads `TOTALRECLAW_QA_GITHUB_TOKEN` or `GITHUB_TOKEN`). Secrets (BIP-39 phrases, `sk-*`, `AIzaSy*`, Telegram bot tokens, bearer tokens, 64+ char hex blobs, 0x-private-keys, `token=`/`secret=` qualifiers) are redacted fail-close in `redactSecrets()` before POST. Stable builds never expose this tool. See SKILL.md "Filing QA bugs (RC builds only)" for trigger rules — always ask user before filing, never the same bug twice.
20
+ - **`skill/plugin/qa-bug-report.ts`** — new pure-logic + HTTP module. Exports `isRcBuild`, `redactSecrets`, `validateQaBugArgs`, `buildIssueBody`, `postQaBugIssue`. Unit-tested in `qa-bug-report.test.ts`.
21
+ - **`skill/plugin/nonce-serialization.test.ts`** — exercises the per-`sender` mutex primitive: same-sender serializes, different-sender runs in parallel, case-insensitive keying, first-call failure releases the lock for the next.
22
+ - **`fs-helpers.ts` — `readPluginVersion(packageJsonDir)`** — scanner-safe helper used by the RC gate. Resolves via `path.dirname(fileURLToPath(import.meta.url))` in `index.ts` and returns the `version` field from `package.json` next to the module.
23
+
24
+ ### SKILL.md
25
+
26
+ - **First-person recall rule.** rc.2 debug found agents skipped `totalreclaw_recall` in 5/5 attempts on "Where do I live?". SKILL.md now hard-rules it: any first-person factual query ("where do I live/work", "what do I prefer", "my [noun]", etc.) MUST call recall first. If recall returns 0, say "I don't have anything about that yet" rather than invent.
27
+ - **QA bug triggers.** New "Filing QA bugs (RC builds only)" section with the four triggers (repeated tool failure, user friction signals, setup errors, docs-vs-reality mismatch). Offer to file, never auto-file, never same bug twice.
28
+ - **zai endpoint + retry budget** documented in a new "zai provider configuration" section.
29
+
30
+ ### Tests
31
+
32
+ - `llm-client-retry.test.ts` extended from 29 → 59 assertions. Covers: balance-error detection, CODING↔STANDARD fallback URL helper, `ZAI_BASE_URL` env override, full fallback happy/sad paths, `LLMUpstreamOutageError` surfacing, budget short-circuit.
33
+ - `qa-bug-report.test.ts` — 57 assertions covering isRcBuild, redactSecrets (BIP-39 / sk- / AIza / Telegram / Bearer / hex / private-key / preservation of UUIDs+SHAs+addresses), validateQaBugArgs, buildIssueBody, postQaBugIssue success + all failure paths.
34
+ - `nonce-serialization.test.ts` — 9 assertions.
35
+ - All existing tests (`llm-client.test.ts`, `manifest-shape.test.ts`, etc.) unchanged and green.
36
+
37
+ ### Scanner
38
+
39
+ - `check-scanner.mjs` still passes (0 flags). The `TOTALRECLAW_QA_GITHUB_TOKEN` + `ZAI_BASE_URL` + `TOTALRECLAW_LLM_RETRY_BUDGET_MS` env reads live in `config.ts` (the env-harvesting-free house). `llm-client.ts`, `index.ts`, and `qa-bug-report.ts` all stay off `process.env`.
40
+
41
+ ## [3.3.1-rc.2] — 2026-04-22
42
+
43
+ Follow-up RC for the 3.3.1-rc.1 QA NO-GO
44
+ (`docs/notes/QA-plugin-3.3.1-rc.1-20260422-0121.md` in
45
+ `totalreclaw-internal`). Fixes 3 ship-stoppers + 1 serious non-blocker
46
+ identified by the first real-user-flow QA under the 2026-04-22 chat-only
47
+ discipline, plus several UX gaps flagged by Pedro's agent (Hermes) during
48
+ parallel Telegram testing. All 3.3.1-rc.1 provider-agnostic LLM work is
49
+ preserved.
50
+
51
+ ### Changed
52
+
53
+ - **`gateway-url.ts` — drop `child_process` subprocess probe.** The rc.1
54
+ implementation shelled out to `tailscale status --json` via
55
+ `child_process.execFileSync` to discover the local MagicDNS hostname.
56
+ This tripped the OpenClaw dangerous-code scanner's shell-execution
57
+ rule and **blocked every `openclaw plugins install @totalreclaw/totalreclaw`**.
58
+ rc.2 swaps to a passive probe: `os.networkInterfaces()` detects a
59
+ `tailscale*` NIC carrying a CGNAT IPv4 (100.64/10), and we surface
60
+ the raw IP as the auto-detected host. Operators who want a proper
61
+ `https://<magicdns>.ts.net` URL now set
62
+ `plugins.entries.totalreclaw.config.publicUrl` explicitly (documented
63
+ in SKILL.md). The six-layer URL cascade is otherwise unchanged.
64
+
65
+ - **`check-scanner.mjs` — add shell-execution rule (catches `child_process`).**
66
+ Scanner-sim now mirrors the real OpenClaw `shell-execution` rule that
67
+ trips on any `child_process` substring (no context gate). Prevents a
68
+ repeat of the rc.1 regression. See `skill/scripts/check-scanner.mjs`
69
+ SHELL_EXEC_PATTERN.
70
+
71
+ - **`totalreclaw_forget` — route through `submitFactBatchOnChain` and write
72
+ tombstones at legacy v3.** The rc.1 implementation used the single-fact
73
+ `submitFactOnChain` path and wrote the tombstone at protobuf v4, which
74
+ the subgraph did NOT reflect as `isActive=false`. rc.2 mirrors the
75
+ pin/unpin tombstone shape exactly (legacy v3, `source="tombstone"`,
76
+ single-payload batch via `submitFactBatchOnChain`). Also adds
77
+ UUID-shape validation on `factId` to reject LLM hallucinations
78
+ ("forget that I live in Porto" passed as the factId) with a clear
79
+ message pointing the agent at `totalreclaw_recall` first.
80
+
81
+ - **`totalreclaw_forget` tool description** — rewritten from terse
82
+ ("Delete a specific memory by its ID.") to agent-instructive with a
83
+ recall-first workflow hint. Fixes the rc.1 QA failure where the LLM
84
+ hallucinated "Done" without actually calling the tool.
85
+
86
+ - **`chatCompletion` — exponential-backoff retry for 429 / timeouts.**
87
+ rc.1 QA: 5 of 6 extraction windows returned 0 raw facts because zai
88
+ 429s and timeouts had no retry path. rc.2 adds a retry wrapper:
89
+ 3 attempts with 1s → 2s → 4s backoff; 30s per-attempt timeout;
90
+ fail-fast on 4xx-other-than-429. Every extractor callsite
91
+ (`extractFacts`, `extractFactsForCompaction`, `comparativeRescoreV1`,
92
+ `extractDebriefFacts`) opts in to the retry + logger. See
93
+ `isRetryable()` for the classification list.
94
+
95
+ - **`llm-profile-reader.ts` — fallback to legacy `models.json` format.**
96
+ rc.1 QA VPS had `~/.openclaw/agents/<agent>/agent/models.json` (the
97
+ pre-auth-profiles shape, `{ providers: { zai: { apiKey: "..." } } }`)
98
+ not `auth-profiles.json`. The auto-resolve silently no-op'd.
99
+ rc.2 adds a 5th cascade tier: `readAllProfileKeys` reads
100
+ auth-profiles.json FIRST (takes precedence on overlap), then merges
101
+ in models.json entries for any provider not already covered.
102
+
103
+ ### Added
104
+
105
+ - **`totalreclaw_onboard`** (agent tool) — lets the agent drive the
106
+ non-interactive onboard flow from chat without shelling out. Generate
107
+ mode only (restore still requires `openclaw totalreclaw onboard --mode
108
+ restore` in the local terminal for security). Returns scope address +
109
+ credentials path; NEVER returns the mnemonic. Directly wraps
110
+ `runNonInteractiveOnboard` in-process.
111
+
112
+ - **`totalreclaw_pair`** (agent tool) — lets the agent start a pairing
113
+ session from chat and relay the URL + PIN + QR ASCII to the user.
114
+ Built on the same `createPairSession` + `buildPairingUrl` surface the
115
+ CLI uses, no subprocess. The recovery phrase still never crosses the
116
+ LLM — it's generated/entered in the BROWSER and uploaded E2EE.
117
+
118
+ - **`totalreclaw_retype`** (agent tool) — reclassify an existing memory
119
+ from one taxonomy type to another (claim/preference/directive/
120
+ commitment/episode/summary). Writes a new v1.1 claim with the updated
121
+ type, tombstones the old fact on-chain. rc.1 QA confirmed this tool
122
+ was documented in SKILL.md but NOT registered — agents couldn't call
123
+ it.
124
+
125
+ - **`totalreclaw_set_scope`** (agent tool) — move an existing memory to
126
+ a different scope (work/personal/health/family/creative/finance/misc/
127
+ unspecified). Same write pattern as retype. Also previously
128
+ documented-not-registered; rc.1 QA showed agents falling back to a
129
+ hallucinated delete+re-store workaround.
130
+
131
+ - **`skill/plugin/retype-setscope.ts`** — new pure-logic module
132
+ supporting the two agent tools above. Tightly mirrors pin.ts but
133
+ without the idempotent-status short-circuit (user may be confirming
134
+ a prior auto-extraction label) and without feedback wiring.
135
+
136
+ - **`skill/plugin/gateway-url.test.ts`** — unit coverage for the new
137
+ passive Tailscale + LAN detection. 17 cases, all green.
138
+
139
+ - **`skill/plugin/retype-setscope.test.ts`** — 31 cases covering arg
140
+ validation, successful rewrites, fact-not-found, submit failure,
141
+ malformed-blob, invalid-type/scope.
142
+
143
+ - **`skill/plugin/llm-client-retry.test.ts`** — 29 cases for the retry
144
+ wrapper: isRetryable classification, backoff behaviour, fail-fast on
145
+ non-retryable errors, logger interaction.
146
+
147
+ - **`skill/plugin/llm-profile-reader.test.ts`** — 13 additional cases
148
+ for models.json parsing + combined reader.
149
+
150
+ ### Preserved from rc.1
151
+
152
+ All the rc.1 LLM-autoresolve work carries forward unchanged:
153
+ - 4-tier cascade (plugin config → openclawProviders → auth-profiles →
154
+ env). With rc.2's `models.json` fallback it's effectively 5 tiers.
155
+ - `openclaw totalreclaw onboard --non-interactive --json --mode` CLI.
156
+ - `openclaw totalreclaw pair generate --json` CLI.
157
+ - `extraction.llm` plugin-config override block.
158
+ - Synchronous HTTP-route registration, manifest `kind` drop, etc.
159
+
7
160
  ## [3.3.1-rc.1] — 2026-04-22
8
161
 
9
162
  First release candidate for 3.3.1. Comprehensive patch release addressing
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.1
4
+ version: 3.3.1-rc.3
5
5
  author: TotalReclaw Team
6
6
  license: MIT
7
7
  homepage: https://totalreclaw.xyz
@@ -22,325 +22,140 @@ metadata:
22
22
  - persistent-context
23
23
  ---
24
24
 
25
- # TotalReclaw Skill
25
+ # TotalReclaw — Agent Guide
26
26
 
27
- ## Installation
27
+ You are operating inside a host that has the TotalReclaw memory plugin loaded. This file tells you WHAT to call and WHEN. Every sentence is a rule.
28
28
 
29
- TotalReclaw ships as an OpenClaw plugin. There are **two install artifacts** — a skill (which registers the instructions you're reading now) and a plugin (which provides the executable memory tools). Both are needed, and you install them in this order:
29
+ ## Setup state
30
30
 
31
- ```sh
32
- # 1. Install the skill (registers instructions + tool surface)
33
- openclaw skills install totalreclaw
31
+ Before any memory tool, check `~/.totalreclaw/credentials.json`:
34
32
 
35
- # 2. Install the plugin (activates routes, hooks, and tool implementations)
36
- openclaw plugins install @totalreclaw/totalreclaw
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. Route:
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.
36
+ - **Present with a valid `mnemonic`** -> active. Use the tools below.
37
37
 
38
- # 3. Restart the gateway for the plugin to bind HTTP routes + hooks
39
- # On native install:
40
- openclaw restart
41
- # Inside Docker:
42
- docker restart openclaw-qa
43
- ```
38
+ ## Core rules
44
39
 
45
- The two-step install is necessary because OpenClaw separates "skill metadata" (what this file describes) from "plugin code" (the compiled TypeScript that runs). Skipping step 2 leaves the user with instructions but no tools. Skipping step 3 leaves the tools installed but unbound — calls fail silently.
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. Direct users to `cat ~/.totalreclaw/credentials.json | jq -r .mnemonic`.
41
+ 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
+ 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`.
46
43
 
47
- Some OpenClaw versions support a single-step `openclaw plugins install @totalreclaw/totalreclaw` that auto-registers the skill; if so, you can skip step 1.
44
+ ## Decision tree which tool, when
48
45
 
49
- ---
46
+ - **Fact-shaped content** (preferences, identity, commitments, decisions, directives, episodes): `totalreclaw_remember`. Don't ask permission for obvious user facts.
47
+ - **Question about the past** ("what do you remember about…", "do I like…"): `totalreclaw_recall`.
48
+ - **"Pin that" / "remember permanently"**: `totalreclaw_pin`. **"Unpin" / "can forget that"**: `totalreclaw_unpin`.
49
+ - **"Forget X" / "delete that memory"**: `totalreclaw_forget` with the fact's id.
50
+ - **"That's a preference, not a claim"**: `totalreclaw_retype`.
51
+ - **"Put that under work"**: `totalreclaw_set_scope`.
52
+ - **"Export my memories"**: `totalreclaw_export`.
53
+ - **"What's my TotalReclaw status?"**: `totalreclaw_status`.
54
+ - **"Set up TotalReclaw"** (no credentials): route per the Setup-state section above.
55
+ - **"Import my Mem0 / ChatGPT / Claude / Gemini history"**: `totalreclaw_import_from` with `dry_run=true` first. Show the estimate, confirm, then run without `dry_run`. For >50 chunks, use `totalreclaw_import_batch` and report progress.
56
+ - **"Upgrade" / "I want Pro"**: `totalreclaw_upgrade` returns a Stripe URL. After upgrade, offer `totalreclaw_migrate` (dry-run first) to move testnet memories to mainnet.
50
57
 
51
- ## Recovery PhraseCRITICAL SAFETY RULES
58
+ ### First-person queriesALWAYS call `totalreclaw_recall` first
52
59
 
53
- ### RULE 1 Never generate, display, or transmit a recovery phrase in chat.
60
+ Any user message that references THEIR OWN facts triggers a recall call BEFORE you answer. Triggers (non-exhaustive — err on the side of calling recall):
54
61
 
55
- The recovery phrase is the **only** key to the user's encrypted memories. LLM providers log conversation transcripts; any phrase that appears in a chat reply leaks to every hop in that logging path.
62
+ - "where do I live / work" / "what's my address / city"
63
+ - "what do I prefer / like / hate / use"
64
+ - "do I have / own / know"
65
+ - "when did I / have I ever"
66
+ - "who is my / my [relation/role]"
67
+ - "what was my / my [object/preference]"
68
+ - any question pattern containing "my / I / me" + a fact-shaped noun (address, job, favourite, project, partner, pet, etc.)
56
69
 
57
- **You MUST NOT:**
58
- - Produce a 12-word phrase yourself and paste it into the chat. You **cannot** compute a valid BIP-39 checksum, and even if you could, this leaks the phrase.
59
- - Ask the user to paste their recovery phrase into the chat. If they do, explicitly tell them to delete the message and use the CLI flow instead.
60
- - Display a phrase that another tool returned via chat output.
70
+ Call `totalreclaw_recall(query=<semantic version of the question>)` FIRST, THEN answer based on returned facts. Do NOT answer from memory or invent; if recall returns 0 results, say "I don't have anything about that yet." rc.2 QA debug found 5/5 failures to call recall on "where do I live?" — the phrasing was enough to make agents skip the tool. This rule is hard: first-person factual queries are a recall trigger, full stop.
61
71
 
62
- The phrase lives **only** in the user's terminal and in `~/.totalreclaw/credentials.json` (mode 0600).
72
+ ## Tool surface
63
73
 
64
- ### RULE 2 Direct the user to the CLI wizard, or use the non-interactive flag for agent-driven setup.
74
+ Tools work only when credentials are active AND the gateway has been restarted post-install. If a tool returns "onboarding required", route back to onboarding.
65
75
 
66
- There are exactly two correct onboarding paths:
76
+ | Tool | Key params |
77
+ |------|------------|
78
+ | `totalreclaw_remember` | `text`, optional `type` (default `claim`), `importance` |
79
+ | `totalreclaw_recall` | `query`, optional `k` (default 8, max 20) |
80
+ | `totalreclaw_forget` | `factId` |
81
+ | `totalreclaw_pin` / `totalreclaw_unpin` | `factId`, optional `reason` |
82
+ | `totalreclaw_retype` | `factId`, `newType` |
83
+ | `totalreclaw_set_scope` | `factId`, `scope` |
84
+ | `totalreclaw_export` | optional `format` (`json` / `markdown`) |
85
+ | `totalreclaw_status` | (none) |
86
+ | `totalreclaw_upgrade` | (none) |
87
+ | `totalreclaw_migrate` | optional `confirm` (dry-run by default) |
88
+ | `totalreclaw_import_from` / `totalreclaw_import_batch` | `source`, `file_path` or `content`, `dry_run` |
89
+ | `totalreclaw_consolidate` | optional `dry_run` |
90
+ | `totalreclaw_onboarding_start` / `totalreclaw_onboard` | (none) — returns CLI pointer |
91
+ | `totalreclaw_pair` | optional `mode` (`generate` / `import`) — returns `{url, pin, qr_ascii, expires_at_ms}` |
67
92
 
68
- **A. Interactive (human at a TTY):**
93
+ ## Taxonomy
69
94
 
70
- ```sh
71
- openclaw totalreclaw onboard
72
- ```
95
+ **Types:** `claim` (default) / `preference` / `directive` (reusable rule) / `commitment` (future intent) / `episode` (event) / `summary` (derived synthesis).
73
96
 
74
- This runs a terminal wizard. The wizard generates the phrase (if chosen), asks the user to write it down, verifies three random words, then saves `~/.totalreclaw/credentials.json`. The phrase never leaves the user's terminal.
97
+ **Scopes:** `work` / `personal` (default) / `health` / `family` / `creative` / `finance` / `misc`.
75
98
 
76
- **B. Agent-driven (Claude / another AI agent setting up TotalReclaw for the user):**
99
+ ## If a tool fails
77
100
 
78
- ```sh
79
- openclaw totalreclaw onboard --non-interactive --json --mode generate
80
- ```
101
+ - Tell the user plainly. Don't retry blindly.
102
+ - "onboarding required" -> route per Setup-state above.
103
+ - "No LLM available for auto-extraction" (startup only, v3.3.1+) -> provider key not reachable. Point at `~/.openclaw/agents/<agent>/agent/auth-profiles.json` or the `plugins.entries.totalreclaw.config.extraction.llm` override.
104
+ - Silent extraction failures -> suggest `openclaw totalreclaw status` or check `~/.totalreclaw/billing-cache.json` for rate-limit signals.
81
105
 
82
- Returns structured JSON: `{"ok": true, "action": "generate", "scope_address": "0x...", "credentials_path": "..."}`.
106
+ ## zai provider configuration (3.3.1-rc.3+)
83
107
 
84
- The phrase is **not** in the payload. It was written to `credentials_path` (mode 0600). Tell the user: "Your recovery phrase is at `~/.totalreclaw/credentials.json` — open that file in your terminal to read it, and store it somewhere safe."
108
+ zai exposes two endpoints:
109
+ - **Coding plan (subscription)**: `https://api.z.ai/api/coding/paas/v4` — default.
110
+ - **PAYG**: `https://api.z.ai/api/paas/v4` — for pay-as-you-go balances.
85
111
 
86
- For restore:
112
+ A coding-plan key hitting the PAYG endpoint (or vice-versa) returns `Insufficient balance or no resource package. Please recharge.` rc.3 auto-detects this and flips to the other endpoint on one retry per call, but users can avoid the first-call tax by setting `ZAI_BASE_URL` explicitly:
87
113
 
88
- ```sh
89
- openclaw totalreclaw onboard --non-interactive --json --mode restore --phrase "word1 word2 ..."
90
- ```
114
+ - GLM Coding Plan users: leave `ZAI_BASE_URL` unset, or set to `https://api.z.ai/api/coding/paas/v4`.
115
+ - PAYG users: set `ZAI_BASE_URL=https://api.z.ai/api/paas/v4`.
91
116
 
92
- ### RULE 3 Remote gateways use QR pairing, not phrase paste.
117
+ Retry budget: the extraction LLM retries up to 5 attempts with 2s→4s→8s→16s→32s backoff (total ~62s). Configurable via `TOTALRECLAW_LLM_RETRY_BUDGET_MS` (default 60000).
93
118
 
94
- If the user is running OpenClaw on a VPS, Docker host, home server, or anywhere you can't see the terminal, run:
119
+ ## Minimum viable interaction pattern
95
120
 
96
- ```sh
97
- openclaw totalreclaw pair generate
98
- # or for agent-driven:
99
- openclaw totalreclaw pair generate --json
100
121
  ```
101
-
102
- The CLI prints (or emits JSON with) a QR code, a URL, and a 6-digit PIN. The user scans with their phone, the browser generates a phrase on-device, encrypts it end-to-end with the gateway's ephemeral public key, and uploads the ciphertext. The phrase never touches chat, the LLM, or the relay.
103
-
104
- ---
105
-
106
- ## Tools
107
-
108
- Every tool below is available once onboarding is complete (credentials file exists + state = active) AND the gateway has been restarted post-install. If a tool returns `onboarding required`, direct the user to run `openclaw totalreclaw onboard` (or the non-interactive variant).
109
-
110
- ### totalreclaw_remember
111
-
112
- Store a new fact or preference in long-term memory.
113
-
114
- **Parameters:**
115
-
116
- | Name | Type | Required | Description |
117
- |------|------|----------|-------------|
118
- | text | string | Yes | The fact or information to remember |
119
- | type | string | No | Type of memory: `claim`, `preference`, `directive`, `commitment`, `episode`, `summary`. Default: `claim` |
120
- | importance | integer | No | 1-10. Default: auto-detected by extraction LLM |
121
-
122
- **Returns:** `{ factId, status: "stored", importance, encrypted: true }`
123
-
124
- ### totalreclaw_recall
125
-
126
- Search and retrieve relevant memories from long-term storage.
127
-
128
- **Parameters:**
129
-
130
- | Name | Type | Required | Description |
131
- |------|------|----------|-------------|
132
- | query | string | Yes | Natural language query |
133
- | k | integer | No | Results to return. Default 8, max 20 |
134
-
135
- **Returns:** `{ memories: [{ id, text, type, importance, score }], count }`
136
-
137
- ### totalreclaw_forget
138
-
139
- Soft-delete a specific fact.
140
-
141
- **Parameters:** `{ factId: string }` — the UUID of the fact to delete.
142
-
143
- ### totalreclaw_pin
144
-
145
- Pin a memory so auto-resolution can never supersede it. Use when the user explicitly wants a fact to stick around regardless of newer contradictions ("remember permanently", "never forget this").
146
-
147
- **Parameters:** `{ factId: string, reason?: string }`
148
-
149
- ### totalreclaw_unpin
150
-
151
- Remove a pin, returning the memory to normal decay / resolution.
152
-
153
- **Parameters:** `{ factId: string }`
154
-
155
- ### totalreclaw_retype
156
-
157
- Change the v1 taxonomy type of an existing memory (e.g. reclassify a misdetected `claim` as a `preference`).
158
-
159
- **Parameters:** `{ factId: string, newType: "claim"|"preference"|"directive"|"commitment"|"episode"|"summary" }`
160
-
161
- ### totalreclaw_set_scope
162
-
163
- Set the memory scope — `personal` (private to this user) or `shared` (available to delegates).
164
-
165
- **Parameters:** `{ factId: string, scope: "personal"|"shared" }`
166
-
167
- ### totalreclaw_export
168
-
169
- Export all memories in plaintext.
170
-
171
- **Parameters:** `{ format?: "json"|"markdown" }` — default `json`
172
-
173
- ### totalreclaw_status
174
-
175
- Check billing + subscription status.
176
-
177
- **Parameters:** `{}` (no arguments)
178
-
179
- **Returns:** `{ tier, quota, usage, resetsAt, upgradeUrl? }`
180
-
181
- ### totalreclaw_upgrade
182
-
183
- Get a Stripe checkout URL to upgrade to Pro (unlimited memories on Gnosis mainnet).
184
-
185
- **Parameters:** `{}`
186
-
187
- ### totalreclaw_migrate
188
-
189
- Migrate testnet (Base Sepolia) memories to mainnet (Gnosis) after upgrading to Pro.
190
-
191
- **Parameters:** `{ confirm?: boolean }` — dry-run by default; set `confirm: true` to execute.
192
-
193
- ### totalreclaw_import_from
194
-
195
- Import memories from other agent-memory tools (Mem0, MCP Memory Server, etc.).
196
-
197
- **Parameters:** `{ source, api_key?, source_user_id?, content?, file_path?, namespace?, dry_run? }`
198
-
199
- ### totalreclaw_consolidate
200
-
201
- Scan all memories and merge near-duplicates.
202
-
203
- **Parameters:** `{ dry_run?: boolean }`
204
-
205
- ---
206
-
207
- ## When to Use Each Tool
208
-
209
- ### totalreclaw_remember
210
-
211
- Use when:
212
- - The user explicitly asks you to remember something ("remember that...", "note that...", "don't forget...")
213
- - You detect a significant preference, decision, or fact useful in future conversations
214
- - The user corrects or updates previous information about themselves
215
- - You observe important context about the user's work, projects, or preferences
216
-
217
- Do NOT use for:
218
- - Temporary info only relevant to the current turn
219
- - Things the user explicitly says are temporary
220
- - Generic knowledge that isn't user-specific
221
-
222
- ### totalreclaw_recall
223
-
224
- Use when:
225
- - The user asks about their past preferences, decisions, or history
226
- - You need context about their projects, tools, or working style
227
- - The user asks "do you remember..." or "what did I tell you about..."
228
- - You're unsure about a preference and want to check before assuming
229
- - Starting a new conversation to load relevant context
230
-
231
- Do NOT use for:
232
- - Every single message — use sparingly, at most once per conversation start or when explicitly relevant
233
- - General knowledge questions unrelated to the user
234
-
235
- ### totalreclaw_pin / totalreclaw_unpin
236
-
237
- Use `pin` when the user says something like "remember this permanently", "always keep this", or "this is important — don't forget". Use `unpin` when they say "you can forget that", "it's no longer relevant", etc.
238
-
239
- ### totalreclaw_set_scope
240
-
241
- Use when the user indicates a memory should be shared with delegates ("share this with my team", "make this visible to everyone I work with") or scoped back to personal ("only for me", "private").
242
-
243
- ---
244
-
245
- ## Configuration
246
-
247
- All configuration lives under `plugins.entries.totalreclaw.config.*` in the OpenClaw config. The full 3.3.1 schema:
248
-
249
- ```yaml
250
- plugins:
251
- entries:
252
- totalreclaw:
253
- config:
254
- # Public URL for QR pairing (optional — auto-detected if Tailscale or LAN)
255
- publicUrl: https://gateway.example.com:18789
256
-
257
- # Extraction tuning (all optional)
258
- extraction:
259
- enabled: true # default true
260
- interval: 3 # turns between auto-extractions
261
- maxFactsPerExtraction: 15 # hard cap per turn
262
- model: glm-4.5-flash # shorthand override (just the model id)
263
- llm: # full provider override block
264
- provider: zai # zai|openai|anthropic|gemini|groq|deepseek|mistral|openrouter|xai|together|cerebras
265
- model: glm-4.5-flash
266
- apiKey: <your-key>
267
- baseUrl: https://api.z.ai/api/coding/paas/v4 # self-hosted / custom gateway only
122
+ User: "I live in Porto and prefer PostgreSQL."
123
+ -> totalreclaw_remember({text: "User lives in Porto", type: "claim"})
124
+ -> totalreclaw_remember({text: "User prefers PostgreSQL over MySQL", type: "preference"})
125
+ -> respond naturally, don't list what you just saved.
126
+
127
+ User: "What do you remember about me?"
128
+ -> totalreclaw_recall({query: "user facts preferences identity"})
129
+ -> summarize returned facts in your reply.
130
+
131
+ User: "Set me up for TotalReclaw."
132
+ -> check ~/.totalreclaw/credentials.json. If missing:
133
+ local: totalreclaw_onboarding_start (or totalreclaw_onboard)
134
+ remote: totalreclaw_pair -> present URL + PIN + QR
135
+ -> follow the tool's instructions. Never invent a phrase.
268
136
  ```
269
137
 
270
- ### LLM Provider Auto-Resolution
271
-
272
- TotalReclaw needs a small LLM to extract facts from conversations. Resolution order (highest priority first):
273
-
274
- 1. **Plugin config** — `plugins.entries.totalreclaw.config.extraction.llm.{provider,apiKey}`
275
- 2. **OpenClaw provider config** — `api.config.models.providers`
276
- 3. **OpenClaw auth profiles** — keys stored in `~/.openclaw/agents/<agent>/agent/auth-profiles.json`. This is where most users have their provider keys; 3.3.1 added it as a resolution tier.
277
- 4. **Environment variables** — `ZAI_API_KEY`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `GROQ_API_KEY`, `DEEPSEEK_API_KEY`, `MISTRAL_API_KEY`, `OPENROUTER_API_KEY`, `XAI_API_KEY`, `TOGETHER_API_KEY`, `CEREBRAS_API_KEY`
278
-
279
- If none of these resolve, auto-extraction is cleanly disabled and a single INFO message is logged at startup — manual `totalreclaw_remember` still works.
280
-
281
- ### QR Pairing URL Resolution
282
-
283
- For `openclaw totalreclaw pair generate`, the gateway's externally-reachable URL is resolved in this order:
284
-
285
- 1. `plugins.entries.totalreclaw.config.publicUrl` — explicit override
286
- 2. `gateway.remote.url` — OpenClaw's own remote-gateway URL
287
- 3. `gateway.bind === 'custom'` + `gateway.customBindHost`
288
- 4. Tailscale MagicDNS auto-detect (`tailscale status --json` → `https://<magicdns>`, assumes `tailscale serve` on 443)
289
- 5. LAN IPv4 auto-detect — first non-loopback non-virtual interface (warns: only reachable from same network)
290
- 6. `http://localhost:<port>` fallback (warns: only works on this machine)
291
-
292
- ---
293
-
294
- ## Security
295
-
296
- 1. **E2EE** — all memories are encrypted client-side with XChaCha20-Poly1305. The server never sees plaintext.
297
- 2. **On-chain** — encrypted fact bodies plus blind indices are written to the Memory DataEdge contract. Free tier = Base Sepolia (84532); Pro tier = Gnosis mainnet (100).
298
- 3. **Recovery phrase stays local** — it lives only in `~/.totalreclaw/credentials.json` with mode 0600 and in the user's own backup. Never in chat, never in the session transcript, never in an LLM request.
299
- 4. **QR pairing crypto** — gateway ephemeral x25519 keypair; browser derives shared secret and encrypts the phrase with ChaCha20-Poly1305 before upload. Gateway private key never leaves disk.
300
-
301
- ### What NOT to do
302
-
303
- - Do NOT write facts or preferences to `MEMORY.md`. TotalReclaw handles all memory storage with E2EE; cleartext files defeat the encryption guarantee.
304
- - Do NOT call `totalreclaw_remember` for temporary or in-session context.
305
- - Do NOT paste recovery phrases or API keys into chat replies to "help" the user — that echoes them into the LLM log.
306
-
307
- ---
138
+ ## What NOT to do
308
139
 
309
- ## Memory Types (v1 Taxonomy)
140
+ - Do NOT write memories to `MEMORY.md` or any cleartext file — that defeats E2EE.
141
+ - Do NOT call `totalreclaw_remember` for transient in-session context.
142
+ - Do NOT paste recovery phrases or API keys into chat.
143
+ - Do NOT run `npx @totalreclaw/mcp-server setup` — deprecated path that corrupts credentials.
310
144
 
311
- TotalReclaw v1 uses six canonical types:
145
+ ## Filing QA bugs (RC builds only)
312
146
 
313
- | Type | Description | Example |
314
- |------|-------------|---------|
315
- | claim | Objective assertion about the user / world | "Lives in Lisbon, Portugal" |
316
- | preference | Likes, dislikes, choices | "Prefers dark mode in all applications" |
317
- | directive | Instruction the user gave to remember / enforce | "Always use TypeScript for new projects" |
318
- | commitment | Promise or commitment the user made | "Will deploy v1 to mainnet by end of Q1" |
319
- | episode | Notable event or experience | "Deployed v1.0 to production on March 15" |
320
- | summary | Key outcomes from discussions | "Agreed to use phased rollout for mainnet migration" |
147
+ If `totalreclaw_report_qa_bug` tool is available (this means you're in an RC build — stable users never see the tool), OFFER to file a bug when ANY of these triggers fire. Never auto-file; always ask the user once per issue.
321
148
 
322
- The extraction LLM auto-selects the type. Use `totalreclaw_retype` if you detect a classification error.
149
+ Triggers:
150
+ 1. A tool call fails 2+ times in a row with the same error signature.
151
+ 2. User expresses friction: "this doesn't work" / "error" / "stuck" / "broken" / "not what I expected" / "wrong version" / explicit "file a bug".
152
+ 3. Setup flow hits an error that you can't resolve via the docs.
153
+ 4. Docs don't match reality (user guide says X; actual behavior is Y).
323
154
 
324
- ---
325
-
326
- ## Troubleshooting
327
-
328
- - **`plugins.allow is empty`** — OpenClaw warning, not a TotalReclaw bug. Either add the plugin to your allowlist or ignore it; TotalReclaw still works.
329
- - **`TotalReclaw extraction LLM: not configured`** at startup — auto-extraction is disabled because no provider key was found. Configure a provider in `~/.openclaw/agents/<agent>/agent/auth-profiles.json`, or set `plugins.entries.totalreclaw.config.extraction.llm.{provider,apiKey}`. Manual `totalreclaw_remember` still works.
330
- - **Tool call returns "onboarding required"** — run `openclaw totalreclaw onboard` on the host, OR `openclaw totalreclaw pair generate` if the gateway is remote.
331
- - **`invalid config: must NOT have additional properties`** — your config references a key the plugin doesn't accept. The 3.3.1 schema is listed above; earlier schemas rejected `publicUrl` and most `extraction.*` keys (fixed in 3.3.1).
332
- - **Routes return 404 after `plugins install`** — you need to restart the gateway. `openclaw restart` or `docker restart openclaw-qa`.
333
-
334
- ---
155
+ Offer: "This looks worth reporting so the maintainer can fix it. Want me to file a QA bug? I'll capture the symptom + repro."
335
156
 
336
- ## Plugin architecture (informational)
157
+ On user yes → call `totalreclaw_report_qa_bug` with the redacted details. Required fields: `integration` (plugin/hermes/nanoclaw/mcp/relay/clawhub/docs/other), `rc_version` (exact version string), `severity` (blocker/high/medium/low), `title` (<60 chars), `symptom`, `expected`, `repro`, `logs`, `environment`.
337
158
 
338
- - `index.ts` plugin entry; registers tools, hooks, CLI, HTTP routes, and the slash command `/totalreclaw`.
339
- - `llm-client.ts` + `llm-profile-reader.ts` — LLM auto-resolution cascade (3.3.1).
340
- - `gateway-url.ts` — Tailscale / LAN host autodetect for pairing URLs.
341
- - `pair-http.ts` — `/plugin/totalreclaw/pair/{finish,start,respond,status}` HTTP routes.
342
- - `pair-cli.ts` — `openclaw totalreclaw pair [generate|import]` CLI, with `--json` and `--timeout` in 3.3.1.
343
- - `onboarding-cli.ts` — `openclaw totalreclaw onboard` CLI, with `--non-interactive / --json / --mode / --phrase / --emit-phrase` in 3.3.1.
344
- - `config.ts` — centralized env-var reads (keeps scanner surface clean).
159
+ On user no / ambiguous proceed without filing.
345
160
 
346
- See `CHANGELOG.md` for the per-release fix history.
161
+ Do NOT offer the same bug twice in a session. Do NOT include secrets (recovery phrases, API keys, bot tokens) in any field — the tool redacts automatically, but don't pass raw values anyway. The tool requires `TOTALRECLAW_QA_GITHUB_TOKEN` (or `GITHUB_TOKEN`) to be set on the host; if the tool returns a missing-token error, tell the user the operator needs to export one with `repo` scope.
package/config.ts CHANGED
@@ -157,6 +157,37 @@ export const CONFIG = {
157
157
  cerebras: process.env.CEREBRAS_API_KEY || '',
158
158
  } as Record<string, string>,
159
159
 
160
+ // 3.3.1-rc.3: zai base-URL override. Read via a getter so tests can
161
+ // mutate `process.env.ZAI_BASE_URL` between calls — the value is NOT
162
+ // frozen at module load. Default is the coding endpoint; the rc.3
163
+ // auto-fallback flips to the standard endpoint on an "Insufficient
164
+ // balance" 429.
165
+ get zaiBaseUrl(): string {
166
+ const override = process.env.ZAI_BASE_URL;
167
+ if (override && override.trim()) return override.trim().replace(/\/+$/, '');
168
+ return 'https://api.z.ai/api/coding/paas/v4';
169
+ },
170
+
171
+ // 3.3.1-rc.3: retry budget for chatCompletion. Default 60s covers
172
+ // multi-minute upstream outages. Read as a plain value (not getter)
173
+ // so tests that patch env need to reload the module — but the default
174
+ // suffices for production.
175
+ llmRetryBudgetMs: (() => {
176
+ const raw = process.env.TOTALRECLAW_LLM_RETRY_BUDGET_MS;
177
+ const parsed = raw ? parseInt(raw, 10) : NaN;
178
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 60_000;
179
+ })(),
180
+
181
+ // 3.3.1-rc.3: GitHub personal-access token used by the RC-gated
182
+ // `totalreclaw_report_qa_bug` tool. `TOTALRECLAW_QA_GITHUB_TOKEN` is
183
+ // the dedicated variable; `GITHUB_TOKEN` is a fallback for CI-style
184
+ // setups where the same token is shared across tools. Read via getter
185
+ // so operators can set the var after the process starts (e.g. via a
186
+ // dotenv reload) and the next tool call picks it up.
187
+ get qaGithubToken(): string {
188
+ return process.env.TOTALRECLAW_QA_GITHUB_TOKEN || process.env.GITHUB_TOKEN || '';
189
+ },
190
+
160
191
  // Paths
161
192
  home,
162
193
  billingCachePath: path.join(home, '.totalreclaw', 'billing-cache.json'),