@totalreclaw/totalreclaw 3.3.3-rc.1 → 3.3.4-rc.1
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/SKILL.md +18 -6
- package/dist/embedding.js +17 -1
- package/dist/fs-helpers.js +50 -4
- package/dist/index.js +125 -24
- package/dist/pair-cli-relay.js +278 -0
- package/dist/pair-cli.js +85 -17
- package/embedding.ts +18 -1
- package/fs-helpers.ts +49 -5
- package/index.ts +131 -26
- package/package.json +2 -2
- package/pair-cli-relay.ts +336 -0
- package/pair-cli.ts +118 -18
- package/skill.json +1 -1
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: totalreclaw
|
|
3
3
|
description: "Install + set up TotalReclaw encrypted memory for OpenClaw, then use totalreclaw_remember / totalreclaw_recall. Trigger on 'install TotalReclaw', 'set up TotalReclaw', 'restore my recovery phrase', or any remember / recall request."
|
|
4
|
-
version: 3.3.
|
|
4
|
+
version: 3.3.4-rc.1
|
|
5
5
|
author: TotalReclaw Team
|
|
6
6
|
license: MIT
|
|
7
7
|
homepage: https://totalreclaw.xyz
|
|
@@ -30,16 +30,28 @@ metadata:
|
|
|
30
30
|
|
|
31
31
|
If `totalreclaw_*` tools are already bound to your toolset, SKIP install and jump to step 4. Otherwise run all steps in order.
|
|
32
32
|
|
|
33
|
-
1. **Install skill
|
|
33
|
+
1. **Install plugin first, skill second.** Run via your shell tool, in this exact order:
|
|
34
34
|
```
|
|
35
|
-
openclaw skills install totalreclaw
|
|
36
35
|
openclaw plugins install @totalreclaw/totalreclaw
|
|
36
|
+
openclaw skills install totalreclaw
|
|
37
37
|
```
|
|
38
|
-
For RC: replace the
|
|
38
|
+
For RC: replace the first command with `openclaw plugins install @totalreclaw/totalreclaw@rc`. **Plugin BEFORE skill is deliberate** (3.3.4-rc.1 — issue from Pedro's QA on rc.3): the skill install triggers a config-driven SIGUSR1 gateway reload that can race a concurrent plugin install. Plugin-first lets the plugin land cleanly, then the skill's reload picks it up on the next gateway loop. If you accidentally do skill-first and the plugin install dies mid-flight, just retry `openclaw plugins install @totalreclaw/totalreclaw` once — it's idempotent.
|
|
39
|
+
|
|
40
|
+
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.
|
|
41
|
+
|
|
42
|
+
2. **Wait for auto-reload — then issue `/restart` autonomously if needed.** OpenClaw's default config (`gateway.reload.mode = "hybrid"`) auto-restarts the gateway via a file-watcher within 1-3 seconds. Wait ~5s, then run `openclaw plugins list | grep totalreclaw` and confirm `totalreclaw_pair` is in your toolset.
|
|
43
|
+
|
|
44
|
+
**If `totalreclaw_pair` is NOT visible after 10s:** issue OpenClaw's built-in `/restart` slash-command immediately and silently. **DO NOT ask the user "should I restart" / "do you want me to restart" / "let me check if the tool is bound" — just issue `/restart` and announce "Reloading gateway..." once.** This is in-process and drains active runs cleanly; your shell is NOT shelling out to `docker restart` or `openclaw gateway restart` (which would suicide the host). After `/restart`, wait ~3s and re-run `openclaw plugins list`.
|
|
45
|
+
|
|
46
|
+
**If `/restart` returns "You are not authorized to use this command":** the gateway's `channels.<your-channel>.allowFrom` allowlist is empty. **Do NOT keep retrying.** Surface this one-line user-facing fix and stop:
|
|
47
|
+
|
|
48
|
+
> Your channel needs to be allowlisted for slash commands. Run on the gateway host: `jq '.channels.<channel>.allowFrom = ["<your-id>"]' ~/.openclaw/openclaw.json | sponge ~/.openclaw/openclaw.json && docker restart <your-container-name>` (Docker self-host) — substitute `<channel>` with your channel name (e.g. `telegram`, `slack`), `<your-id>` with your user ID for that channel (Telegram: `@username` or numeric `chat_id`; Slack: workspace member ID), and `<your-container-name>` with your gateway container name (`docker ps` shows it). On a managed service, ask the service's support to enable slash commands for your channel. Reply `done` once it's back up.
|
|
49
|
+
|
|
50
|
+
For non-Docker / native deployments substitute `openclaw gateway restart` for the `docker restart` portion. As a session-level fallback while restart is gated, try `/new` (starts a fresh session within the same gateway process, may pick up freshly-bound tools without a full restart).
|
|
39
51
|
|
|
40
|
-
|
|
52
|
+
**Forbidden vocabulary** (these are anti-patterns when a tool is missing post-install): "Should I /restart", "Do you have a public URL", "Want me to restart", "Let me check if the tool is bound", "I need permission to restart". Just issue `/restart` immediately and announce.
|
|
41
53
|
|
|
42
|
-
3. **Verify `totalreclaw_pair` is bound.** If still missing after the user said `done
|
|
54
|
+
3. **Verify `totalreclaw_pair` is bound.** If still missing after `/restart` (or after the user fixed allowFrom and said `done`), tell them the restart didn't take effect and ask them to retry. Do not loop the install.
|
|
43
55
|
|
|
44
56
|
4. **Check for existing credentials.** Run `test -s ~/.totalreclaw/credentials.json`. If it exists and is non-empty, confirm *"TotalReclaw is already set up."* and stop.
|
|
45
57
|
|
package/dist/embedding.js
CHANGED
|
@@ -53,10 +53,26 @@ export function configureEmbedder(cfg) {
|
|
|
53
53
|
function defaultCacheRoot() {
|
|
54
54
|
return path.join(os.homedir(), '.totalreclaw', 'embedder');
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Last-known-good embedder bundle tag. Used ONLY as a hard-fallback when
|
|
58
|
+
* `configureEmbedder()` is never called by the orchestrator (defensive
|
|
59
|
+
* path — production code always wires it via index.ts register()).
|
|
60
|
+
*
|
|
61
|
+
* 3.3.4-rc.1 — pinned to v3.3.3-rc.1 because that is the most recent
|
|
62
|
+
* release at fix-time with a published `embedder-v1.tar.gz` asset. Earlier
|
|
63
|
+
* fallback `'0.0.0-dev'` (rc.22 → 3.3.3-rc.1) hard-coded a placeholder
|
|
64
|
+
* that resolved to a 404 GitHub Release URL; QA on 3.3.3-rc.1 (Pedro
|
|
65
|
+
* 2026-04-30) caught it because the cascade-cause (broken
|
|
66
|
+
* `readPluginVersion()` resolution) made the fallback fire on every cold
|
|
67
|
+
* start. Bumping this constant per RC is fine — the publish workflow auto-
|
|
68
|
+
* publishes the bundle for every RC tag (see scripts/build-embedder-
|
|
69
|
+
* bundle.mjs in the public repo).
|
|
70
|
+
*/
|
|
71
|
+
const LAST_KNOWN_GOOD_RC_TAG = '3.3.3-rc.1';
|
|
56
72
|
function activeRuntimeConfig() {
|
|
57
73
|
if (runtimeConfig)
|
|
58
74
|
return runtimeConfig;
|
|
59
|
-
return { cacheRoot: defaultCacheRoot(), rcTag:
|
|
75
|
+
return { cacheRoot: defaultCacheRoot(), rcTag: LAST_KNOWN_GOOD_RC_TAG };
|
|
60
76
|
}
|
|
61
77
|
/**
|
|
62
78
|
* 3.3.3-rc.1 (issue #187 — ONNX decouple): prefetch the embedder bundle
|
package/dist/fs-helpers.js
CHANGED
|
@@ -74,9 +74,17 @@ export function ensureMemoryHeaderFile(workspace, header, markerSubstring = 'Tot
|
|
|
74
74
|
* Read the plugin's own version string from `package.json`.
|
|
75
75
|
*
|
|
76
76
|
* Behaviour:
|
|
77
|
-
* -
|
|
77
|
+
* - Tries `package.json` next to the caller-provided directory first
|
|
78
78
|
* (typically `path.dirname(fileURLToPath(import.meta.url))` from the
|
|
79
|
-
* caller).
|
|
79
|
+
* caller — i.e., the directory of the running ESM module).
|
|
80
|
+
* - If that misses, walks up to 5 parent directories looking for a
|
|
81
|
+
* `package.json` whose `name` is `@totalreclaw/totalreclaw`. This
|
|
82
|
+
* covers the OpenClaw plugin sandbox case where the loaded module
|
|
83
|
+
* lives at `<pluginRoot>/dist/index.js` while `package.json` lives
|
|
84
|
+
* at `<pluginRoot>/package.json` (3.3.4-rc.1 fix — without this
|
|
85
|
+
* walk-up, the `.loaded.json` manifest gets `version=unknown` and
|
|
86
|
+
* all RC-gated logic that depends on the version string fails
|
|
87
|
+
* silently in production OpenClaw deployments).
|
|
80
88
|
* - Returns the `version` field, or `null` on any I/O / parse error.
|
|
81
89
|
*
|
|
82
90
|
* Used by the RC-gated `totalreclaw_report_qa_bug` tool registration in
|
|
@@ -87,13 +95,51 @@ export function ensureMemoryHeaderFile(workspace, header, markerSubstring = 'Tot
|
|
|
87
95
|
* helper — see the file-header guardrail.
|
|
88
96
|
*/
|
|
89
97
|
export function readPluginVersion(packageJsonDir) {
|
|
98
|
+
// Direct hit (source-tree dev path; tests).
|
|
99
|
+
const direct = tryReadPluginPackageJson(path.join(packageJsonDir, 'package.json'));
|
|
100
|
+
if (direct)
|
|
101
|
+
return direct;
|
|
102
|
+
// Walk up — the running ESM module typically lives at
|
|
103
|
+
// `<pluginRoot>/dist/index.js`, so `packageJsonDir` is `<pluginRoot>/dist`
|
|
104
|
+
// and `package.json` is one level up. Bound the walk so a misconfigured
|
|
105
|
+
// path doesn't traverse the entire filesystem; 5 levels is more than
|
|
106
|
+
// enough for any realistic plugin layout (dist/, dist/cjs/, build/lib/).
|
|
107
|
+
let current = packageJsonDir;
|
|
108
|
+
for (let depth = 0; depth < 5; depth++) {
|
|
109
|
+
const parent = path.dirname(current);
|
|
110
|
+
if (parent === current)
|
|
111
|
+
break; // root reached
|
|
112
|
+
const candidate = path.join(parent, 'package.json');
|
|
113
|
+
const version = tryReadPluginPackageJson(candidate);
|
|
114
|
+
if (version)
|
|
115
|
+
return version;
|
|
116
|
+
current = parent;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Try to read `package.json` at `pkgPath`. Returns the `version` only if
|
|
122
|
+
* the file's `name` field matches `@totalreclaw/totalreclaw` — guards
|
|
123
|
+
* against accidentally returning the version of an outer host-package
|
|
124
|
+
* (e.g. when the plugin is bundled inside a parent app's tree).
|
|
125
|
+
*
|
|
126
|
+
* If `name` is absent (legacy / minimal package.json), accept the version
|
|
127
|
+
* anyway as a fallback — this is the existing behaviour preserved for
|
|
128
|
+
* anyone who manually trimmed their package.json.
|
|
129
|
+
*/
|
|
130
|
+
function tryReadPluginPackageJson(pkgPath) {
|
|
90
131
|
try {
|
|
91
|
-
const pkgPath = path.join(packageJsonDir, 'package.json');
|
|
92
132
|
if (!fs.existsSync(pkgPath))
|
|
93
133
|
return null;
|
|
94
134
|
const raw = fs.readFileSync(pkgPath, 'utf-8');
|
|
95
135
|
const parsed = JSON.parse(raw);
|
|
96
|
-
|
|
136
|
+
if (typeof parsed.version !== 'string')
|
|
137
|
+
return null;
|
|
138
|
+
if (typeof parsed.name === 'string' && parsed.name !== '@totalreclaw/totalreclaw') {
|
|
139
|
+
// Wrong package — keep walking.
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return parsed.version;
|
|
97
143
|
}
|
|
98
144
|
catch {
|
|
99
145
|
return null;
|
package/dist/index.js
CHANGED
|
@@ -511,10 +511,26 @@ let firstRunWelcomeShown = false;
|
|
|
511
511
|
*
|
|
512
512
|
* Goal: a fresh QA tester can't accidentally use an RC build for real data
|
|
513
513
|
* without seeing a clear "RC = staging, no SLA, may be wiped" warning.
|
|
514
|
-
* One-shot at the first `before_agent_start`
|
|
515
|
-
*
|
|
514
|
+
* One-shot at the first `before_agent_start` whose `prependContext` actually
|
|
515
|
+
* lands on the LLM (3.3.4-rc.1 — see fix below). A fresh gateway restart
|
|
516
|
+
* re-fires it once.
|
|
517
|
+
*
|
|
518
|
+
* 3.3.4-rc.1 fix: through 3.3.3-rc.1 this flag was set to true as soon as
|
|
519
|
+
* the banner BLOCK was built — but multiple hook return paths returned
|
|
520
|
+
* `undefined` (zero-match cases), so the banner block was silently dropped
|
|
521
|
+
* AND the flag was flipped, suppressing all subsequent attempts. Now the
|
|
522
|
+
* flag flips ONLY when a return path actually includes the block in its
|
|
523
|
+
* `prependContext`, via the `markBannerDelivered()` closure.
|
|
516
524
|
*/
|
|
517
525
|
let stagingBannerShown = false;
|
|
526
|
+
/**
|
|
527
|
+
* 3.3.4-rc.1 — operator-facing "this is an RC build" log fires once per
|
|
528
|
+
* gateway process, independent of whether the user-facing banner has
|
|
529
|
+
* been delivered yet. Without this split, the warn-log was tied to the
|
|
530
|
+
* same flag as the user-facing banner and got dropped together when
|
|
531
|
+
* the hook returned `undefined`.
|
|
532
|
+
*/
|
|
533
|
+
let stagingBannerLogged = false;
|
|
518
534
|
/**
|
|
519
535
|
* Derive keys from the recovery phrase, load credentials, and register with
|
|
520
536
|
* the server if this is the first run.
|
|
@@ -2410,18 +2426,32 @@ const plugin = {
|
|
|
2410
2426
|
}
|
|
2411
2427
|
// 3.3.1-rc.22 — wire the lazy-embedder runtime config so the first
|
|
2412
2428
|
// `generateEmbedding()` call knows where to cache the bundle and
|
|
2413
|
-
// which RC's GitHub Release to fetch from.
|
|
2414
|
-
//
|
|
2415
|
-
//
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2429
|
+
// which RC's GitHub Release to fetch from.
|
|
2430
|
+
//
|
|
2431
|
+
// 3.3.4-rc.1 — when readPluginVersion() returns null (rare, but
|
|
2432
|
+
// possible if package.json is unreadable inside the OpenClaw
|
|
2433
|
+
// sandbox), we previously passed the literal `'0.0.0-dev'` which
|
|
2434
|
+
// resolves to a 404 GitHub Release URL. Now we let `embedding.ts`
|
|
2435
|
+
// fall back to its `LAST_KNOWN_GOOD_RC_TAG` constant by SKIPPING
|
|
2436
|
+
// the configure call entirely in the null case — the
|
|
2437
|
+
// `activeRuntimeConfig()` helper picks the constant up. This way
|
|
2438
|
+
// the constant lives in one place (embedding.ts) and the orch-
|
|
2439
|
+
// estrator just doesn't fight it.
|
|
2440
|
+
if (pluginVersion) {
|
|
2441
|
+
try {
|
|
2442
|
+
configureEmbedder({
|
|
2443
|
+
cacheRoot: CONFIG.embedderCachePath,
|
|
2444
|
+
rcTag: pluginVersion,
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
catch (err) {
|
|
2448
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2449
|
+
api.logger.warn(`TotalReclaw: configureEmbedder failed (will use defaults): ${msg}`);
|
|
2450
|
+
}
|
|
2421
2451
|
}
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2452
|
+
else {
|
|
2453
|
+
api.logger.warn('TotalReclaw: pluginVersion unresolved — embedder will fall back to LAST_KNOWN_GOOD_RC_TAG. ' +
|
|
2454
|
+
'Investigate package.json resolution; see fs-helpers.readPluginVersion docs.');
|
|
2425
2455
|
}
|
|
2426
2456
|
// 3.3.3-rc.1 (issue #187 — ONNX decouple): kick off a non-blocking
|
|
2427
2457
|
// bundle prefetch so the ~700 MB embedder tarball starts streaming
|
|
@@ -2576,11 +2606,36 @@ const plugin = {
|
|
|
2576
2606
|
});
|
|
2577
2607
|
// 3.3.0 — `openclaw totalreclaw pair [generate|import]` attaches
|
|
2578
2608
|
// alongside the existing `onboard` + `status` subcommands.
|
|
2609
|
+
//
|
|
2610
|
+
// 3.3.4-rc.1 — wire `runRelayPairCli` so the CLI defaults to the
|
|
2611
|
+
// same relay-brokered URL surface the agent tool uses. The local
|
|
2612
|
+
// (gateway-loopback) flow is still available via `--local`. See
|
|
2613
|
+
// pair-cli.ts header for the rationale.
|
|
2579
2614
|
const { registerPairCli } = await import('./pair-cli.js');
|
|
2580
2615
|
registerPairCli(program, {
|
|
2581
2616
|
sessionsPath: CONFIG.pairSessionsPath,
|
|
2582
2617
|
renderPairingUrl: (session) => buildPairingUrl(api, session),
|
|
2583
2618
|
logger: api.logger,
|
|
2619
|
+
runRelayPairCli: async (cliMode, runOpts) => {
|
|
2620
|
+
const { runRelayPairCli } = await import('./pair-cli-relay.js');
|
|
2621
|
+
return runRelayPairCli(cliMode, {
|
|
2622
|
+
relayBaseUrl: CONFIG.pairRelayUrl,
|
|
2623
|
+
credentialsPath: CREDENTIALS_PATH,
|
|
2624
|
+
onboardingStatePath: CONFIG.onboardingStatePath,
|
|
2625
|
+
logger: api.logger,
|
|
2626
|
+
pluginVersion: pluginVersion ?? '3.3.4-rc.1',
|
|
2627
|
+
deriveScopeAddress: async (mnemonic) => {
|
|
2628
|
+
try {
|
|
2629
|
+
return await deriveSmartAccountAddress(mnemonic, CONFIG.chainId);
|
|
2630
|
+
}
|
|
2631
|
+
catch (err) {
|
|
2632
|
+
api.logger.warn(`relay pair-cli: scope-address derivation failed (will retry lazily): ${err instanceof Error ? err.message : String(err)}`);
|
|
2633
|
+
return undefined;
|
|
2634
|
+
}
|
|
2635
|
+
},
|
|
2636
|
+
...runOpts,
|
|
2637
|
+
});
|
|
2638
|
+
},
|
|
2584
2639
|
});
|
|
2585
2640
|
}, { commands: ['totalreclaw'] });
|
|
2586
2641
|
}
|
|
@@ -4991,7 +5046,24 @@ const plugin = {
|
|
|
4991
5046
|
// Build a one-shot prefix when the bundled default points at staging
|
|
4992
5047
|
// AND the user hasn't overridden via env. This prefix is prepended
|
|
4993
5048
|
// to whichever context block the rest of the hook produces.
|
|
5049
|
+
//
|
|
5050
|
+
// 3.3.4-rc.1 — fix: previously `stagingBannerShown` was set to
|
|
5051
|
+
// `true` AS SOON AS the block was built. If the rest of the hook
|
|
5052
|
+
// then returned `undefined` (e.g. zero memory matches on the first
|
|
5053
|
+
// turn — multiple paths around lines 6103-6325 do this), the
|
|
5054
|
+
// banner block was silently discarded AND the flag was already
|
|
5055
|
+
// flipped, so subsequent before_agent_start invocations never
|
|
5056
|
+
// reconstructed it. Net effect: QA on 3.3.3-rc.1 (Pedro
|
|
5057
|
+
// 2026-04-30) saw NO banner emitted across an entire conversation
|
|
5058
|
+
// even though the build was bound to staging.
|
|
5059
|
+
//
|
|
5060
|
+
// Fix: build the block on every call until it is actually
|
|
5061
|
+
// delivered (i.e., until at least one return path included it
|
|
5062
|
+
// in `prependContext`). The flag flips at the bottom of this
|
|
5063
|
+
// hook in `markBannerDelivered()` once we know the prependContext
|
|
5064
|
+
// path was taken.
|
|
4994
5065
|
let stagingBannerBlock = '';
|
|
5066
|
+
let stagingBannerSuppressed = false;
|
|
4995
5067
|
if (!stagingBannerShown) {
|
|
4996
5068
|
try {
|
|
4997
5069
|
const usingStagingDefault = CONFIG.serverUrl.includes('api-staging.totalreclaw.xyz');
|
|
@@ -5004,9 +5076,11 @@ const plugin = {
|
|
|
5004
5076
|
'For production, install the stable release: `openclaw plugins install ' +
|
|
5005
5077
|
'@totalreclaw/totalreclaw` (no `@rc` suffix). To pin a custom server, set ' +
|
|
5006
5078
|
'`TOTALRECLAW_SERVER_URL=https://api.totalreclaw.xyz` in your env.\n\n';
|
|
5007
|
-
stagingBannerShown
|
|
5008
|
-
|
|
5009
|
-
|
|
5079
|
+
// Do NOT set stagingBannerShown=true here — see comment above.
|
|
5080
|
+
// Logger emits once per gateway process; this is fine to
|
|
5081
|
+
// gate on the same flag because the logger is operator-
|
|
5082
|
+
// facing, not user-facing.
|
|
5083
|
+
stagingBannerSuppressed = true;
|
|
5010
5084
|
}
|
|
5011
5085
|
else {
|
|
5012
5086
|
// Non-RC artifact OR user override — never fire the banner this
|
|
@@ -5019,6 +5093,33 @@ const plugin = {
|
|
|
5019
5093
|
stagingBannerShown = true;
|
|
5020
5094
|
}
|
|
5021
5095
|
}
|
|
5096
|
+
// Operator-facing log: once per process, when we DETECT the
|
|
5097
|
+
// staging build (banner-shown semantics are about user
|
|
5098
|
+
// delivery; this log is independent).
|
|
5099
|
+
if (stagingBannerSuppressed && !stagingBannerLogged) {
|
|
5100
|
+
stagingBannerLogged = true;
|
|
5101
|
+
api.logger.warn('TotalReclaw: RC/staging build active (api-staging.totalreclaw.xyz). ' +
|
|
5102
|
+
'See docs/guides/release-process.md for the RC=staging / stable=production rule.');
|
|
5103
|
+
}
|
|
5104
|
+
/**
|
|
5105
|
+
* Helper — invoked inline at any `prependContext` site that
|
|
5106
|
+
* wants to lead with the staging banner. Returns the banner
|
|
5107
|
+
* string AND atomically marks the banner as delivered, so
|
|
5108
|
+
* subsequent hook calls in the same gateway-process lifetime
|
|
5109
|
+
* skip re-emission. Returns '' (empty) when no banner is due
|
|
5110
|
+
* (stable build, user override, or already delivered).
|
|
5111
|
+
*
|
|
5112
|
+
* Use this at every prependContext callsite that takes the
|
|
5113
|
+
* banner; do NOT inline `stagingBannerBlock` on its own — the
|
|
5114
|
+
* 3.3.4-rc.1 bug fix requires the marker flip to be coupled
|
|
5115
|
+
* to the actual delivery.
|
|
5116
|
+
*/
|
|
5117
|
+
const consumeBannerForPrepend = () => {
|
|
5118
|
+
if (stagingBannerBlock === '')
|
|
5119
|
+
return '';
|
|
5120
|
+
stagingBannerShown = true;
|
|
5121
|
+
return stagingBannerBlock;
|
|
5122
|
+
};
|
|
5022
5123
|
await ensureInitialized(api.logger);
|
|
5023
5124
|
// 3.2.0 onboarding pending: emit a non-secret guidance banner so
|
|
5024
5125
|
// the LLM knows how to respond when the user asks about setup.
|
|
@@ -5047,7 +5148,7 @@ const plugin = {
|
|
|
5047
5148
|
api.logger.warn(`First-run welcome check failed: ${msg}`);
|
|
5048
5149
|
}
|
|
5049
5150
|
return {
|
|
5050
|
-
prependContext:
|
|
5151
|
+
prependContext: consumeBannerForPrepend() +
|
|
5051
5152
|
welcomeBlock +
|
|
5052
5153
|
'## TotalReclaw setup pending\n\n' +
|
|
5053
5154
|
'TotalReclaw encrypted memory is installed but not yet set up on this machine. ' +
|
|
@@ -5146,7 +5247,7 @@ const plugin = {
|
|
|
5146
5247
|
if (injectResult.promptText) {
|
|
5147
5248
|
api.logger.info(`Digest injection: state=${injectResult.state}`);
|
|
5148
5249
|
return {
|
|
5149
|
-
prependContext:
|
|
5250
|
+
prependContext: consumeBannerForPrepend() +
|
|
5150
5251
|
`## Your Memory\n\n${injectResult.promptText}` + welcomeBack + billingWarning,
|
|
5151
5252
|
};
|
|
5152
5253
|
}
|
|
@@ -5188,7 +5289,7 @@ const plugin = {
|
|
|
5188
5289
|
const querySimilarity = cosineSimilarity(queryEmbedding, lastQueryEmbedding);
|
|
5189
5290
|
if (querySimilarity > SEMANTIC_SKIP_THRESHOLD) {
|
|
5190
5291
|
const lines = cachedFacts.slice(0, 8).map((f, i) => `${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`);
|
|
5191
|
-
return { prependContext:
|
|
5292
|
+
return { prependContext: consumeBannerForPrepend() + `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5192
5293
|
}
|
|
5193
5294
|
}
|
|
5194
5295
|
// 3. Merge trapdoors — always include word trapdoors for small-dataset coverage.
|
|
@@ -5197,7 +5298,7 @@ const plugin = {
|
|
|
5197
5298
|
// If we have cached facts and no trapdoors, return cached facts.
|
|
5198
5299
|
if (allTrapdoors.length === 0 && cachedFacts.length > 0) {
|
|
5199
5300
|
const lines = cachedFacts.slice(0, 8).map((f, i) => `${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`);
|
|
5200
|
-
return { prependContext:
|
|
5301
|
+
return { prependContext: consumeBannerForPrepend() + `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5201
5302
|
}
|
|
5202
5303
|
if (allTrapdoors.length === 0)
|
|
5203
5304
|
return undefined;
|
|
@@ -5212,7 +5313,7 @@ const plugin = {
|
|
|
5212
5313
|
// Subgraph query failed -- fall back to cached facts if available.
|
|
5213
5314
|
if (cachedFacts.length > 0) {
|
|
5214
5315
|
const lines = cachedFacts.slice(0, 8).map((f, i) => `${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`);
|
|
5215
|
-
return { prependContext:
|
|
5316
|
+
return { prependContext: consumeBannerForPrepend() + `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5216
5317
|
}
|
|
5217
5318
|
return undefined;
|
|
5218
5319
|
}
|
|
@@ -5236,7 +5337,7 @@ const plugin = {
|
|
|
5236
5337
|
// If subgraph returned no results but we have cache, use cache.
|
|
5237
5338
|
if (subgraphResults.length === 0) {
|
|
5238
5339
|
const lines = cachedFacts.slice(0, 8).map((f, i) => `${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`);
|
|
5239
|
-
return { prependContext:
|
|
5340
|
+
return { prependContext: consumeBannerForPrepend() + `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5240
5341
|
}
|
|
5241
5342
|
// 5. Decrypt subgraph results and build reranker input.
|
|
5242
5343
|
const rerankerCandidates = [];
|
|
@@ -5311,7 +5412,7 @@ const plugin = {
|
|
|
5311
5412
|
return `${i + 1}. ${typeTag}${m.text} (importance: ${importance}/10, ${age})`;
|
|
5312
5413
|
});
|
|
5313
5414
|
const contextString = `## Relevant Memories\n\n${lines.join('\n')}`;
|
|
5314
|
-
return { prependContext:
|
|
5415
|
+
return { prependContext: consumeBannerForPrepend() + contextString + welcomeBack + billingWarning };
|
|
5315
5416
|
}
|
|
5316
5417
|
// --- Server mode (existing behavior) ---
|
|
5317
5418
|
// 1. Generate word trapdoors from the user prompt.
|
|
@@ -5393,7 +5494,7 @@ const plugin = {
|
|
|
5393
5494
|
return `${i + 1}. ${m.text} (importance: ${importance}/10, ${age})`;
|
|
5394
5495
|
});
|
|
5395
5496
|
const contextString = `## Relevant Memories\n\n${lines.join('\n')}`;
|
|
5396
|
-
return { prependContext:
|
|
5497
|
+
return { prependContext: consumeBannerForPrepend() + contextString + welcomeBack + billingWarning };
|
|
5397
5498
|
}
|
|
5398
5499
|
catch (err) {
|
|
5399
5500
|
// The hook must NEVER throw -- log and return undefined.
|