@totalreclaw/totalreclaw 3.3.12 → 3.3.13-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.
Files changed (128) hide show
  1. package/SKILL.md +25 -8
  2. package/{billing-cache.ts → billing/billing-cache.ts} +1 -1
  3. package/{relay-headers.ts → billing/relay-headers.ts} +1 -1
  4. package/{tr-cli-export-helper.ts → cli/tr-cli-export-helper.ts} +5 -5
  5. package/{tr-cli.ts → cli/tr-cli.ts} +20 -20
  6. package/{contradiction-sync.ts → contradiction/contradiction-sync.ts} +13 -13
  7. package/{digest-sync.ts → digest/digest-sync.ts} +32 -2
  8. package/dist/{billing-cache.js → billing/billing-cache.js} +1 -1
  9. package/dist/{relay-headers.js → billing/relay-headers.js} +1 -1
  10. package/dist/{tr-cli-export-helper.js → cli/tr-cli-export-helper.js} +5 -5
  11. package/dist/{tr-cli.js → cli/tr-cli.js} +19 -19
  12. package/dist/{contradiction-sync.js → contradiction/contradiction-sync.js} +13 -13
  13. package/dist/{digest-sync.js → digest/digest-sync.js} +32 -2
  14. package/dist/{download-ux.js → embedding/download-ux.js} +1 -1
  15. package/dist/{reranker.js → embedding/reranker.js} +13 -4
  16. package/dist/{claims-helper.js → extraction/claims-helper.js} +54 -58
  17. package/dist/extraction/consolidation.js +170 -0
  18. package/dist/{extractor.js → extraction/extractor.js} +17 -17
  19. package/dist/extraction/importance-filter.js +38 -0
  20. package/dist/{semantic-dedup.js → extraction/semantic-dedup.js} +2 -2
  21. package/dist/{batch-gate.js → import/batch-gate.js} +1 -1
  22. package/dist/import/import-runtime.js +692 -0
  23. package/dist/import-adapters/chatgpt-adapter.js +7 -7
  24. package/dist/import-adapters/gemini-adapter.js +13 -13
  25. package/dist/import-adapters/mem0-adapter.js +10 -10
  26. package/dist/index.js +111 -1094
  27. package/dist/{llm-client.js → llm/llm-client.js} +2 -2
  28. package/dist/{hot-cache-wrapper.js → memory/hot-cache-wrapper.js} +5 -5
  29. package/dist/{memory-runtime.js → memory/memory-runtime.js} +1 -1
  30. package/dist/{pin.js → memory/pin.js} +18 -18
  31. package/dist/{retype-setscope.js → memory/retype-setscope.js} +19 -19
  32. package/dist/{credential-provider.js → pairing/credential-provider.js} +2 -2
  33. package/dist/{first-run.js → pairing/first-run.js} +1 -1
  34. package/dist/{onboarding-cli.js → pairing/onboarding-cli.js} +20 -20
  35. package/dist/{pair-cli-relay.js → pairing/pair-cli-relay.js} +10 -10
  36. package/dist/{pair-crypto.js → pairing/pair-crypto.js} +1 -1
  37. package/dist/{pair-http.js → pairing/pair-http.js} +3 -3
  38. package/dist/{pair-remote-client.js → pairing/pair-remote-client.js} +10 -10
  39. package/dist/{pair-session-store.js → pairing/pair-session-store.js} +4 -4
  40. package/dist/runtime/config-schema.js +59 -0
  41. package/dist/runtime/format-helpers.js +257 -0
  42. package/dist/runtime/types.js +11 -0
  43. package/dist/{confirm-indexed.js → subgraph/confirm-indexed.js} +1 -1
  44. package/dist/{subgraph-search.js → subgraph/subgraph-search.js} +14 -14
  45. package/dist/{subgraph-store.js → subgraph/subgraph-store.js} +6 -6
  46. package/{download-ux.ts → embedding/download-ux.ts} +1 -1
  47. package/{reranker.ts → embedding/reranker.ts} +13 -4
  48. package/{claims-helper.ts → extraction/claims-helper.ts} +52 -60
  49. package/{consolidation.ts → extraction/consolidation.ts} +63 -154
  50. package/{extractor.ts → extraction/extractor.ts} +18 -17
  51. package/extraction/importance-filter.ts +50 -0
  52. package/{semantic-dedup.ts → extraction/semantic-dedup.ts} +2 -2
  53. package/{batch-gate.ts → import/batch-gate.ts} +1 -1
  54. package/import/import-runtime.ts +853 -0
  55. package/import-adapters/chatgpt-adapter.ts +7 -7
  56. package/import-adapters/gemini-adapter.ts +13 -13
  57. package/import-adapters/mem0-adapter.ts +10 -10
  58. package/index.ts +129 -1377
  59. package/{llm-client.ts → llm/llm-client.ts} +2 -2
  60. package/{hot-cache-wrapper.ts → memory/hot-cache-wrapper.ts} +5 -5
  61. package/{memory-runtime.ts → memory/memory-runtime.ts} +1 -1
  62. package/{pin.ts → memory/pin.ts} +20 -20
  63. package/{retype-setscope.ts → memory/retype-setscope.ts} +21 -21
  64. package/{tool-gating.ts → memory/tool-gating.ts} +1 -1
  65. package/package.json +6 -6
  66. package/{credential-provider.ts → pairing/credential-provider.ts} +2 -2
  67. package/{first-run.ts → pairing/first-run.ts} +1 -1
  68. package/{onboarding-cli.ts → pairing/onboarding-cli.ts} +20 -20
  69. package/{pair-cli-relay.ts → pairing/pair-cli-relay.ts} +10 -10
  70. package/{pair-crypto.ts → pairing/pair-crypto.ts} +1 -1
  71. package/{pair-http.ts → pairing/pair-http.ts} +3 -3
  72. package/{pair-remote-client.ts → pairing/pair-remote-client.ts} +10 -10
  73. package/{pair-session-store.ts → pairing/pair-session-store.ts} +4 -4
  74. package/runtime/config-schema.ts +64 -0
  75. package/runtime/format-helpers.ts +293 -0
  76. package/runtime/types.ts +118 -0
  77. package/skill.json +44 -10
  78. package/{confirm-indexed.ts → subgraph/confirm-indexed.ts} +1 -1
  79. package/{subgraph-search.ts → subgraph/subgraph-search.ts} +14 -14
  80. package/{subgraph-store.ts → subgraph/subgraph-store.ts} +10 -10
  81. package/dist/consolidation.js +0 -249
  82. /package/{api-client.ts → billing/api-client.ts} +0 -0
  83. /package/{gateway-url.ts → billing/gateway-url.ts} +0 -0
  84. /package/{inbound-user-tracker.ts → billing/inbound-user-tracker.ts} +0 -0
  85. /package/{relay.ts → billing/relay.ts} +0 -0
  86. /package/{crypto.ts → crypto/crypto.ts} +0 -0
  87. /package/{vault-crypto.ts → crypto/vault-crypto.ts} +0 -0
  88. /package/dist/{api-client.js → billing/api-client.js} +0 -0
  89. /package/dist/{gateway-url.js → billing/gateway-url.js} +0 -0
  90. /package/dist/{inbound-user-tracker.js → billing/inbound-user-tracker.js} +0 -0
  91. /package/dist/{relay.js → billing/relay.js} +0 -0
  92. /package/dist/{crypto.js → crypto/crypto.js} +0 -0
  93. /package/dist/{vault-crypto.js → crypto/vault-crypto.js} +0 -0
  94. /package/dist/{embedder-cache.js → embedding/embedder-cache.js} +0 -0
  95. /package/dist/{embedder-loader.js → embedding/embedder-loader.js} +0 -0
  96. /package/dist/{embedder-network.js → embedding/embedder-network.js} +0 -0
  97. /package/dist/{embedding.js → embedding/embedding.js} +0 -0
  98. /package/dist/{lsh.js → embedding/lsh.js} +0 -0
  99. /package/dist/{import-state-manager.js → import/import-state-manager.js} +0 -0
  100. /package/dist/{llm-profile-reader.js → llm/llm-profile-reader.js} +0 -0
  101. /package/dist/{native-memory.js → memory/native-memory.js} +0 -0
  102. /package/dist/{tool-gating.js → memory/tool-gating.js} +0 -0
  103. /package/dist/{tools.js → memory/tools.js} +0 -0
  104. /package/dist/{generate-mnemonic.js → pairing/generate-mnemonic.js} +0 -0
  105. /package/dist/{pair-cli.js → pairing/pair-cli.js} +0 -0
  106. /package/dist/{pair-page.js → pairing/pair-page.js} +0 -0
  107. /package/dist/{pair-qr.js → pairing/pair-qr.js} +0 -0
  108. /package/dist/{restart-auth.js → pairing/restart-auth.js} +0 -0
  109. /package/dist/{qa-bug-report.js → setup/qa-bug-report.js} +0 -0
  110. /package/dist/{skill-register.js → setup/skill-register.js} +0 -0
  111. /package/dist/{trajectory-poller.js → subgraph/trajectory-poller.js} +0 -0
  112. /package/{embedder-cache.ts → embedding/embedder-cache.ts} +0 -0
  113. /package/{embedder-loader.ts → embedding/embedder-loader.ts} +0 -0
  114. /package/{embedder-network.ts → embedding/embedder-network.ts} +0 -0
  115. /package/{embedding.ts → embedding/embedding.ts} +0 -0
  116. /package/{lsh.ts → embedding/lsh.ts} +0 -0
  117. /package/{import-state-manager.ts → import/import-state-manager.ts} +0 -0
  118. /package/{llm-profile-reader.ts → llm/llm-profile-reader.ts} +0 -0
  119. /package/{native-memory.ts → memory/native-memory.ts} +0 -0
  120. /package/{tools.ts → memory/tools.ts} +0 -0
  121. /package/{generate-mnemonic.ts → pairing/generate-mnemonic.ts} +0 -0
  122. /package/{pair-cli.ts → pairing/pair-cli.ts} +0 -0
  123. /package/{pair-page.ts → pairing/pair-page.ts} +0 -0
  124. /package/{pair-qr.ts → pairing/pair-qr.ts} +0 -0
  125. /package/{restart-auth.ts → pairing/restart-auth.ts} +0 -0
  126. /package/{qa-bug-report.ts → setup/qa-bug-report.ts} +0 -0
  127. /package/{skill-register.ts → setup/skill-register.ts} +0 -0
  128. /package/{trajectory-poller.ts → subgraph/trajectory-poller.ts} +0 -0
@@ -0,0 +1,257 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Pure formatting / resolution helpers
3
+ // ---------------------------------------------------------------------------
4
+ //
5
+ // Extracted from index.ts. Every function here is a pure computation over its
6
+ // arguments (plus imported constants / stateless helpers) — none reads the
7
+ // plugin's mutable session state, so they carve cleanly out of the composing
8
+ // entry point. No environment-variable reads live here (they stay in config.ts /
9
+ // entry.ts per the OpenClaw env-harvesting scanner rule).
10
+ import { encrypt, decrypt } from '../crypto/crypto.js';
11
+ import { detectGatewayHost } from '../billing/gateway-url.js';
12
+ import { isRunningInDocker } from '../fs-helpers.js';
13
+ import { readBillingCache } from '../billing/billing-cache.js';
14
+ // ---------------------------------------------------------------------------
15
+ // Human-friendly error messages
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * Translate technical error messages from the on-chain submission pipeline
19
+ * into user-friendly messages. The original technical details are still
20
+ * logged via api.logger — this only affects what the agent sees.
21
+ */
22
+ export function humanizeError(rawMessage) {
23
+ if (rawMessage.includes('AA23')) {
24
+ return 'Memory storage temporarily unavailable. Will retry next time.';
25
+ }
26
+ if (rawMessage.includes('AA10')) {
27
+ return 'Please wait a moment before storing more memories.';
28
+ }
29
+ if (rawMessage.includes('AA25')) {
30
+ return 'Memory storage busy. Will retry.';
31
+ }
32
+ if (rawMessage.includes('pm_sponsorUserOperation')) {
33
+ return 'Memory storage service temporarily unavailable.';
34
+ }
35
+ if (/Relay returned HTTP\s*404/.test(rawMessage)) {
36
+ return 'Memory service is temporarily offline.';
37
+ }
38
+ if (/Relay returned HTTP\s*5\d\d/.test(rawMessage)) {
39
+ return 'Memory service encountered a temporary error. Will retry next time.';
40
+ }
41
+ // Pass through non-technical messages as-is.
42
+ return rawMessage;
43
+ }
44
+ // ---------------------------------------------------------------------------
45
+ // 3.3.0 — pairing URL resolution
46
+ // ---------------------------------------------------------------------------
47
+ /**
48
+ * Build the full pairing URL (including `#pk=` fragment) for a fresh
49
+ * pairing session. Pulls gateway config from `api.config.gateway`.
50
+ *
51
+ * Resolution order (3.3.1 — six-layer cascade):
52
+ * 1. `plugins.entries.totalreclaw.config.publicUrl` — explicit override
53
+ * 2. `gateway.remote.url` — OpenClaw's own remote-gateway URL
54
+ * 3. `gateway.bind === 'custom'` + `gateway.customBindHost` + port
55
+ * 4. Tailscale auto-detect — `tailscale status --json` → `https://<MagicDNS>`
56
+ * (assumes `tailscale serve` proxies to the gateway port on 443)
57
+ * 5. LAN auto-detect — first non-loopback, non-virtual IPv4 interface.
58
+ * Emits a warning: "only works on the same network".
59
+ * 6. Fallback `http://localhost:<port>` — warns with a pointer to
60
+ * configure `plugins.entries.totalreclaw.config.publicUrl`.
61
+ *
62
+ * Always returns a working URL string; never throws. The caller (CLI or
63
+ * JSON output) prints whatever we give it.
64
+ */
65
+ export function buildPairingUrl(api, session) {
66
+ const cfg = api.config;
67
+ const pluginCfg = (api.pluginConfig ?? {});
68
+ const tlsEnabled = cfg?.gateway?.tls?.enabled === true;
69
+ const scheme = tlsEnabled ? 'https' : 'http';
70
+ const port = cfg?.gateway?.port ?? 18789;
71
+ let base;
72
+ // Layer 1 — explicit user override
73
+ if (typeof pluginCfg.publicUrl === 'string' && pluginCfg.publicUrl.trim()) {
74
+ base = pluginCfg.publicUrl.replace(/\/+$/, '');
75
+ base = base.replace(/^wss:\/\//i, 'https://').replace(/^ws:\/\//i, 'http://');
76
+ }
77
+ // Layer 2 — OpenClaw gateway remote URL
78
+ else if (typeof cfg?.gateway?.remote?.url === 'string' && cfg.gateway.remote.url.trim()) {
79
+ base = cfg.gateway.remote.url.trim().replace(/\/+$/, '');
80
+ base = base.replace(/^wss:\/\//i, 'https://').replace(/^ws:\/\//i, 'http://');
81
+ }
82
+ // Layer 3 — gateway.bind = custom + explicit customBindHost
83
+ else if (cfg?.gateway?.bind === 'custom' && cfg.gateway.customBindHost) {
84
+ base = `${scheme}://${cfg.gateway.customBindHost}:${port}`;
85
+ }
86
+ // Layers 4 + 5 — auto-detect via gateway-url helper (Tailscale CGNAT, then LAN)
87
+ else {
88
+ let detected = null;
89
+ // issue #110 fix 4 — pass `isDocker` so LAN detection skips
90
+ // 172.16/12 bridge IPs that no external browser can reach.
91
+ let isDocker = false;
92
+ try {
93
+ isDocker = isRunningInDocker();
94
+ }
95
+ catch {
96
+ // Defensive: never block URL building on Docker sniff errors.
97
+ isDocker = false;
98
+ }
99
+ try {
100
+ detected = detectGatewayHost({ isDocker });
101
+ }
102
+ catch (err) {
103
+ api.logger.warn(`TotalReclaw: host autodetect crashed: ${err instanceof Error ? err.message : String(err)} — falling back to localhost`);
104
+ }
105
+ if (detected?.kind === 'tailscale') {
106
+ // 3.3.1-rc.2: we surface the raw Tailscale CGNAT IP because passive
107
+ // NIC detection (no subprocess) cannot resolve the MagicDNS name.
108
+ // Caller can override via `publicUrl` for a proper https://<magicdns>.
109
+ // The IP + port URL still works inside the tailnet (peers can reach
110
+ // each other by CGNAT IP directly). TLS defaults to the gateway's
111
+ // own config because we no longer assume `tailscale serve`.
112
+ base = `${scheme}://${detected.host}:${port}`;
113
+ api.logger.warn(`TotalReclaw: pairing URL using Tailscale CGNAT IP ${detected.host}:${port} — ` +
114
+ detected.note);
115
+ }
116
+ else if (detected?.kind === 'lan') {
117
+ base = `${scheme}://${detected.host}:${port}`;
118
+ api.logger.warn(`TotalReclaw: pairing URL using LAN host ${detected.host}:${port} — ` +
119
+ `this URL only works from the same network. ` +
120
+ `Set plugins.entries.totalreclaw.config.publicUrl for remote access.`);
121
+ }
122
+ else {
123
+ // Layer 6 — localhost fallback (or Docker-aware relay-pointer warning)
124
+ const bind = cfg?.gateway?.bind;
125
+ if (isDocker) {
126
+ // issue #110 fix 4: inside Docker the LAN IP is container-internal
127
+ // and useless. Loopback localhost only works for `docker exec`
128
+ // tests. The CORRECT pair URL for Docker is the relay-brokered
129
+ // path served by `tr pair` / the `/plugin/totalreclaw/pair/*` HTTP
130
+ // routes (CONFIG.pairMode === 'relay' since rc.11). The CLI-only
131
+ // path here cannot mint a relay session synchronously (the relay
132
+ // handshake needs a WS round-trip), so we emit the loopback URL
133
+ // with a LOUD warning pointing the operator at the pair CLI /
134
+ // publicUrl override.
135
+ api.logger.warn(`TotalReclaw: Docker container detected — pairing URL falling back to ` +
136
+ `http://localhost:${port}, which is unreachable from the host browser. ` +
137
+ `Run \`tr pair --url-pin\` (or \`openclaw totalreclaw pair generate --url-pin-only\`) ` +
138
+ `on the gateway host to mint a relay-brokered pair URL that reaches the host browser, ` +
139
+ `OR set plugins.entries.totalreclaw.config.publicUrl ` +
140
+ `to your gateway's host-reachable URL (e.g. http://<host-ip>:${port} when the ` +
141
+ `Docker port is published). Setting TOTALRECLAW_PAIR_MODE=relay is the default; ` +
142
+ `air-gapped operators on TOTALRECLAW_PAIR_MODE=local must publish a port + set publicUrl.`);
143
+ }
144
+ else if (bind === 'lan' || bind === 'tailnet') {
145
+ api.logger.warn(`TotalReclaw: pairing URL falling back to localhost because gateway.bind=${bind} could not be autodetected. ` +
146
+ 'Set plugins.entries.totalreclaw.config.publicUrl to override.');
147
+ }
148
+ else {
149
+ api.logger.warn(`TotalReclaw: pairing URL fell back to http://localhost:${port} — this URL only works on this machine. ` +
150
+ `Configure plugins.entries.totalreclaw.config.publicUrl for remote access.`);
151
+ }
152
+ base = `${scheme}://localhost:${port}`;
153
+ }
154
+ }
155
+ return `${base}/plugin/totalreclaw/pair/finish?sid=${encodeURIComponent(session.sid)}#pk=${encodeURIComponent(session.pkGatewayB64)}`;
156
+ }
157
+ /**
158
+ * Resolve whether this plugin is running on a `local` or `remote` gateway.
159
+ *
160
+ * Follows the same config surface `buildPairingUrl` uses:
161
+ * - `pluginConfig.publicUrl` set + non-localhost → remote
162
+ * - `gateway.remote.url` set + non-localhost → remote
163
+ * - `gateway.bind === 'lan' | 'tailnet' | 'custom'` → remote
164
+ * - anything else → local
165
+ *
166
+ * We treat a `publicUrl` or `remote.url` that points at `localhost` /
167
+ * `127.*` as local because that is what a dev-loopback override looks like;
168
+ * no one publishes a remote QR pairing for localhost.
169
+ */
170
+ export function resolveGatewayMode(api) {
171
+ const cfg = api.config;
172
+ const pluginCfg = (api.pluginConfig ?? {});
173
+ const looksLocal = (url) => {
174
+ if (!url)
175
+ return true;
176
+ const u = url.trim().toLowerCase();
177
+ if (u === '')
178
+ return true;
179
+ return /^(?:wss?:\/\/|https?:\/\/)?(?:localhost|127\.|0\.0\.0\.0)/.test(u);
180
+ };
181
+ if (typeof pluginCfg.publicUrl === 'string' && !looksLocal(pluginCfg.publicUrl)) {
182
+ return 'remote';
183
+ }
184
+ const remoteUrl = cfg?.gateway?.remote?.url;
185
+ if (typeof remoteUrl === 'string' && !looksLocal(remoteUrl)) {
186
+ return 'remote';
187
+ }
188
+ const bind = cfg?.gateway?.bind;
189
+ if (bind === 'lan' || bind === 'tailnet' || bind === 'custom') {
190
+ return 'remote';
191
+ }
192
+ return 'local';
193
+ }
194
+ // ---------------------------------------------------------------------------
195
+ // Candidate-pool sizing
196
+ // ---------------------------------------------------------------------------
197
+ /**
198
+ * Compute the candidate pool size from a fact count.
199
+ *
200
+ * Server-side config takes priority (from billing cache), then local fallback.
201
+ * The server computes the optimal pool based on vault size and tier caps.
202
+ *
203
+ * Local fallback formula: pool = min(max(factCount * 3, 400), 5000)
204
+ * - At least 400 candidates (even for tiny vaults)
205
+ * - At most 5000 candidates (to bound decryption + reranking cost)
206
+ * - 3x fact count in between
207
+ */
208
+ export function computeCandidatePool(factCount) {
209
+ const cache = readBillingCache();
210
+ if (cache?.features?.max_candidate_pool != null)
211
+ return cache.features.max_candidate_pool;
212
+ // Fallback to local formula if no server config
213
+ return Math.min(Math.max(factCount * 3, 400), 5000);
214
+ }
215
+ // ---------------------------------------------------------------------------
216
+ // Encrypt/decrypt hex helpers (on-chain blob encoding)
217
+ // ---------------------------------------------------------------------------
218
+ export function encryptToHex(plaintext, key) {
219
+ const b64 = encrypt(plaintext, key);
220
+ return Buffer.from(b64, 'base64').toString('hex');
221
+ }
222
+ export function decryptFromHex(hexBlob, key) {
223
+ const hex = hexBlob.startsWith('0x') ? hexBlob.slice(2) : hexBlob;
224
+ const b64 = Buffer.from(hex, 'hex').toString('base64');
225
+ return decrypt(b64, key);
226
+ }
227
+ // ---------------------------------------------------------------------------
228
+ // Lexical scoring / relative time
229
+ // ---------------------------------------------------------------------------
230
+ export function textScore(query, docText) {
231
+ const queryWords = new Set(query.toLowerCase().split(/\s+/).filter((w) => w.length >= 2));
232
+ const docWords = docText.toLowerCase().split(/\s+/);
233
+ let score = 0;
234
+ for (const word of docWords) {
235
+ if (queryWords.has(word))
236
+ score++;
237
+ }
238
+ return score;
239
+ }
240
+ /**
241
+ * Format a relative time string (e.g. "2 hours ago").
242
+ */
243
+ export function relativeTime(isoOrMs) {
244
+ const ms = typeof isoOrMs === 'number' ? isoOrMs : new Date(isoOrMs).getTime();
245
+ const diffMs = Date.now() - ms;
246
+ const seconds = Math.floor(diffMs / 1000);
247
+ if (seconds < 60)
248
+ return 'just now';
249
+ const minutes = Math.floor(seconds / 60);
250
+ if (minutes < 60)
251
+ return `${minutes}m ago`;
252
+ const hours = Math.floor(minutes / 60);
253
+ if (hours < 24)
254
+ return `${hours}h ago`;
255
+ const days = Math.floor(hours / 24);
256
+ return `${days}d ago`;
257
+ }
@@ -0,0 +1,11 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Shared plugin runtime types
3
+ // ---------------------------------------------------------------------------
4
+ //
5
+ // Extracted from index.ts so the composing entry point AND the domain modules
6
+ // carved out of it (runtime/format-helpers, import/import-runtime, …) can share
7
+ // one definition of the OpenClaw plugin-API surface and the internal row shapes
8
+ // without a circular import back through index.ts.
9
+ //
10
+ // Nothing here reads the environment or performs I/O — pure type declarations.
11
+ export {};
@@ -27,7 +27,7 @@
27
27
  */
28
28
  import { createRequire } from 'node:module';
29
29
  import { getSubgraphConfig } from './subgraph-store.js';
30
- import { buildRelayHeaders } from './relay-headers.js';
30
+ import { buildRelayHeaders } from '../billing/relay-headers.js';
31
31
  const requireWasm = createRequire(import.meta.url);
32
32
  let _wasm = null;
33
33
  function getWasm() {
@@ -20,9 +20,9 @@
20
20
  * - Large datasets: 1 + N batches + pagination queries
21
21
  */
22
22
  import { getSubgraphConfig } from './subgraph-store.js';
23
- import { CONFIG } from './config.js';
24
- import { buildRelayHeaders } from './relay-headers.js';
25
- import { relayFetch } from './relay.js';
23
+ import { CONFIG } from '../config.js';
24
+ import { buildRelayHeaders } from '../billing/relay-headers.js';
25
+ import { relayFetch } from '../billing/relay.js';
26
26
  /** Batch size for Phase 2 split queries. */
27
27
  const TRAPDOOR_BATCH_SIZE = CONFIG.trapdoorBatchSize;
28
28
  /** Graph Studio / Graph Network hard limit on `first` argument. */
@@ -126,8 +126,8 @@ function collectFacts(entries, allResults) {
126
126
  async function paginateChunk(subgraphUrl, chunk, owner, allResults, maxCandidates, authKeyHex) {
127
127
  let lastId = '';
128
128
  while (allResults.size < maxCandidates) {
129
- const data = await gqlQuery(subgraphUrl, PAGINATE_QUERY, { trapdoors: chunk, owner, first: PAGE_SIZE, lastId }, authKeyHex);
130
- const entries = data?.blindIndexes ?? [];
129
+ const subgraphResponse = await gqlQuery(subgraphUrl, PAGINATE_QUERY, { trapdoors: chunk, owner, first: PAGE_SIZE, lastId }, authKeyHex);
130
+ const entries = subgraphResponse?.blindIndexes ?? [];
131
131
  if (entries.length === 0)
132
132
  break;
133
133
  collectFacts(entries, allResults);
@@ -175,8 +175,8 @@ export async function searchSubgraph(owner, trapdoors, maxCandidates, authKeyHex
175
175
  chunks.push(trapdoors.slice(i, i + TRAPDOOR_BATCH_SIZE));
176
176
  }
177
177
  const batchResults = await Promise.all(chunks.map(async (chunk) => {
178
- const data = await gqlQuery(subgraphUrl, SEARCH_QUERY, { trapdoors: chunk, owner, first: PAGE_SIZE }, authKeyHex);
179
- return { chunk, entries: data?.blindIndexes ?? [] };
178
+ const subgraphResponse = await gqlQuery(subgraphUrl, SEARCH_QUERY, { trapdoors: chunk, owner, first: PAGE_SIZE }, authKeyHex);
179
+ return { chunk, entries: subgraphResponse?.blindIndexes ?? [] };
180
180
  }));
181
181
  const saturatedChunks = [];
182
182
  for (const { chunk, entries } of batchResults) {
@@ -224,8 +224,8 @@ export async function searchSubgraphBroadened(owner, maxCandidates, authKeyHex)
224
224
  }
225
225
  }
226
226
  `;
227
- const data = await gqlQuery(subgraphUrl, query, { owner, first: Math.min(maxCandidates, 1000) }, authKeyHex);
228
- return (data?.facts ?? []).filter(f => f.isActive !== false);
227
+ const subgraphResponse = await gqlQuery(subgraphUrl, query, { owner, first: Math.min(maxCandidates, 1000) }, authKeyHex);
228
+ return (subgraphResponse?.facts ?? []).filter(f => f.isActive !== false);
229
229
  }
230
230
  /**
231
231
  * Fetch a single fact by its client-generated UUID.
@@ -256,8 +256,8 @@ export async function fetchFactById(owner, factId, authKeyHex) {
256
256
  }
257
257
  }
258
258
  `;
259
- const data = await gqlQuery(subgraphUrl, query, { id: factId }, authKeyHex);
260
- const fact = data?.fact;
259
+ const subgraphResponse = await gqlQuery(subgraphUrl, query, { id: factId }, authKeyHex);
260
+ const fact = subgraphResponse?.fact;
261
261
  if (!fact)
262
262
  return null;
263
263
  if (fact.isActive === false)
@@ -282,9 +282,9 @@ export async function getSubgraphFactCount(owner, authKeyHex) {
282
282
  }
283
283
  }
284
284
  `;
285
- const data = await gqlQuery(subgraphUrl, query, {}, authKeyHex);
286
- if (data?.globalStates && data.globalStates.length > 0) {
287
- const count = parseInt(data.globalStates[0].totalFacts, 10);
285
+ const subgraphResponse = await gqlQuery(subgraphUrl, query, {}, authKeyHex);
286
+ if (subgraphResponse?.globalStates && subgraphResponse.globalStates.length > 0) {
287
+ const count = parseInt(subgraphResponse.globalStates[0].totalFacts, 10);
288
288
  return isNaN(count) ? 0 : count;
289
289
  }
290
290
  return 0;
@@ -21,10 +21,10 @@ function getWasm() {
21
21
  _wasm = requireWasm('@totalreclaw/core');
22
22
  return _wasm;
23
23
  }
24
- import { CONFIG, getDataEdgeAddressOverride } from './config.js';
25
- import { buildRelayHeaders } from './relay-headers.js';
26
- import { rpcRequest, rpcWithRetry } from './relay.js';
27
- import { signUserOp } from './vault-crypto.js';
24
+ import { CONFIG, getDataEdgeAddressOverride } from '../config.js';
25
+ import { buildRelayHeaders } from '../billing/relay-headers.js';
26
+ import { rpcRequest, rpcWithRetry } from '../billing/relay.js';
27
+ import { signUserOp } from '../crypto/vault-crypto.js';
28
28
  /** Legacy protobuf wrapper schema version (v0/v1-binary inner blob). */
29
29
  export const PROTOBUF_VERSION_LEGACY = 3;
30
30
  /** Memory Taxonomy v1 protobuf wrapper schema version. */
@@ -457,7 +457,7 @@ async function submitFactOnChainLocked(protobufPayload, config, eoa, sender) {
457
457
  break;
458
458
  }
459
459
  catch (err) {
460
- const msg = err?.message || '';
460
+ const msg = err instanceof Error ? err.message : String(err);
461
461
  // AA10 "sender already constructed" or AA25 invalid nonce → retry
462
462
  if (/AA25|AA10|invalid account nonce|already being processed/i.test(msg)) {
463
463
  lastErr = err;
@@ -658,7 +658,7 @@ async function submitFactBatchOnChainLocked(protobufPayloads, config, eoa, sende
658
658
  break;
659
659
  }
660
660
  catch (err) {
661
- const msg = err?.message || '';
661
+ const msg = err instanceof Error ? err.message : String(err);
662
662
  // AA10 "sender already constructed" or AA25 invalid nonce → retry
663
663
  if (/AA25|AA10|invalid account nonce|already being processed/i.test(msg)) {
664
664
  lastErr = err;
@@ -14,7 +14,7 @@
14
14
  * OpenClaw native integration plan, 2026-06-21).
15
15
  */
16
16
 
17
- import { envNumber } from './entry.js';
17
+ import { envNumber } from '../entry.js';
18
18
 
19
19
  const DEFAULT_DOWNLOAD_TIMEOUT_MS = 600_000;
20
20
  const KEEPALIVE_INTERVAL_MS = 60_000;
@@ -23,12 +23,16 @@
23
23
  * Compute cosine similarity between two vectors.
24
24
  *
25
25
  * Returns dot(a, b) / (||a|| * ||b||).
26
- * Returns 0 if either vector has zero magnitude (avoids division by zero).
26
+ * Returns 0 if either vector has zero magnitude (avoids division by zero),
27
+ * or if the vectors differ in length — matching the authoritative core impl
28
+ * `cosine_similarity_f32` (rust/totalreclaw-core), which rejects mismatched
29
+ * dimensions rather than truncating to the shorter length. Keeping this local
30
+ * fallback byte-for-byte with core preserves cross-runtime rerank parity.
27
31
  */
28
32
  export function cosineSimilarity(a: number[], b: number[]): number {
29
- if (a.length === 0 || b.length === 0) return 0;
33
+ if (a.length !== b.length || a.length === 0) return 0;
30
34
 
31
- const len = Math.min(a.length, b.length);
35
+ const len = a.length;
32
36
  let dot = 0;
33
37
  let normA = 0;
34
38
  let normB = 0;
@@ -301,9 +305,14 @@ const SOURCE_WEIGHTS: Record<string, number> = {
301
305
  const LEGACY_FALLBACK_WEIGHT = 0.85;
302
306
 
303
307
  export function getSourceWeight(source: string | undefined): number {
308
+ // Missing source → legacy fallback (0.85), matching core's None path.
304
309
  if (!source) return LEGACY_FALLBACK_WEIGHT;
305
310
  const w = SOURCE_WEIGHTS[source.toLowerCase()];
306
- return w ?? 0.85; // unknown source moderate penalty
311
+ // An unknown NON-EMPTY source string mirrors core's
312
+ // `MemorySource::from_str_lossy`, whose catch-all arm maps any unrecognized
313
+ // token to `user-inferred` (rust/totalreclaw-core/src/claims.rs) — so an
314
+ // unknown string weighs the same as user-inferred, NOT the legacy fallback.
315
+ return w ?? SOURCE_WEIGHTS['user-inferred'];
307
316
  }
308
317
 
309
318
  // ---------------------------------------------------------------------------