@totalreclaw/totalreclaw 3.3.12-rc.16 → 3.3.12-rc.17

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: totalreclaw
3
3
  description: "End-to-end encrypted, decentralized memory for OpenClaw. A native kind:memory provider — recall is automatic via memory_search/memory_get, and facts are captured in the background. Trigger on 'install TotalReclaw', 'set up TotalReclaw', 'restore my recovery phrase', any recall request ('what do you remember about me', 'what's my X'), AND any explicit remember request ('remember X', 'save X')."
4
- version: 3.3.12-rc.16
4
+ version: 3.3.12-rc.17
5
5
  author: TotalReclaw Team
6
6
  license: MIT
7
7
  homepage: https://totalreclaw.xyz
@@ -132,6 +132,15 @@ export function makeCacheRequire(layout) {
132
132
  * entry — the `.mjs` file — and `import()` of that surfaces named
133
133
  * exports on every Node version we support (18, 20, 22, 24).
134
134
  *
135
+ * Post-import normalization (`normalizeImportNamespace`): even with
136
+ * the correct `.mjs` entry selected, Node 24 minor versions have
137
+ * shown regressions where the namespace comes back default-wrapped
138
+ * (cjs-module-lexer mis-parses the 1.3 MB bundled output, or the
139
+ * caller-context forces CJS resolution). The normalizer unwraps
140
+ * `.default` defensively so callers can always destructure named
141
+ * exports directly. See `normalizeImportNamespace` for the detection
142
+ * rule + edge cases.
143
+ *
135
144
  * Transitive deps resolve the same way: the imported module's own
136
145
  * internal `import`/`require` calls walk up from its URL and find the
137
146
  * cache's `node_modules` first.
@@ -168,9 +177,85 @@ export function makeCacheImport(layout) {
168
177
  // Step 3: native ESM dynamic import of the file URL — populates
169
178
  // named exports correctly for dual CJS/ESM and ESM-only packages.
170
179
  const fileUrl = pathToFileURL(esmEntry).href;
171
- return await import(fileUrl);
180
+ const mod = await import(fileUrl);
181
+ // Step 4: normalize the namespace across Node versions.
182
+ //
183
+ // Why this exists (issue #394 follow-up: `autoModel is not a function`
184
+ // PERSISTED on Node 24.16.0 with the real `@huggingface/transformers`
185
+ // v4.2.0 bundle even after #394 shipped `cacheImport`):
186
+ // The `resolveEsmEntryPath` correctly prefers the `.mjs` (ESM
187
+ // native) entry, and `import()` of an `.mjs` file surfaces real
188
+ // named exports on every Node version we tested locally (18, 20,
189
+ // 22). But on Node 24, two regressions have been observed in the
190
+ // wild for dual CJS/ESM packages with large bundled outputs:
191
+ //
192
+ // a) `cjs-module-lexer` (Node's static analyzer for CJS named
193
+ // export detection) silently times out / mis-parses the 1.3 MB
194
+ // `transformers.node.cjs` bundle, so a fallback `import()` of
195
+ // the `.cjs` file returns ONLY `{ default: module.exports }`
196
+ // with no named exports. `const { AutoModel } = mod` then
197
+ // yields `undefined`.
198
+ //
199
+ // b) Some Node 24 minor versions changed the ESM-CJS interop such
200
+ // that `import()` of a dual package resolves to the CJS entry
201
+ // under specific caller-context conditions (e.g. when the
202
+ // caller is itself a transitive CJS module loaded via
203
+ // `createRequire` from ESM), again producing a default-only
204
+ // namespace.
205
+ //
206
+ // We CANNOT reproduce (a)/(b) on Node 22 (the bug is Node-24-only
207
+ // and depends on cjs-module-lexer's behavior on the real 1.3 MB
208
+ // bundle), so this normalization is defensive: after the `import()`,
209
+ // if the namespace has NO own enumerable string keys other than
210
+ // `default` (and `default` is a non-null object), we unwrap `default`
211
+ // and return ITS keys as the namespace. This makes the loader robust
212
+ // to BOTH the ESM-native path (named exports come through untouched)
213
+ // AND the CJS-fallback path (named exports are recovered from
214
+ // `default`).
215
+ return normalizeImportNamespace(mod, specifier, esmEntry);
172
216
  };
173
217
  }
218
+ /**
219
+ * Normalize the result of `import(specifier)` so callers can always
220
+ * destructure named exports directly, regardless of whether the resolved
221
+ * entry was ESM-native (named exports on the namespace) or CJS-via-ESM
222
+ * (named exports only reachable through `.default`).
223
+ *
224
+ * Detection rule: if the namespace has `default` AND zero non-`default`
225
+ * own enumerable string-keyed properties, treat it as a CJS-wrapped
226
+ * namespace and return the `default` object instead. Otherwise return
227
+ * the namespace unchanged (ESM-native or already-unwrapped).
228
+ *
229
+ * Edge case: a legitimately default-only ESM module (one that exports
230
+ * ONLY a default) would be mis-detected as CJS-wrapped. That is safe —
231
+ * the unwrapped `default` is itself the value the caller wants, and a
232
+ * default-only ESM module has no named exports to lose. The bundled
233
+ * packages this loader targets (`@huggingface/transformers`,
234
+ * `onnxruntime-node`) are named-export-heavy, so this edge case does
235
+ * not arise in practice.
236
+ */
237
+ function normalizeImportNamespace(mod, specifier, entryPath) {
238
+ if (!mod || typeof mod !== 'object')
239
+ return mod;
240
+ const ownKeys = Object.keys(mod).filter((k) => k !== 'default');
241
+ if (ownKeys.length > 0) {
242
+ // Named exports already present — ESM-native path. Return as-is.
243
+ return mod;
244
+ }
245
+ const def = mod.default;
246
+ if (def && typeof def === 'object') {
247
+ // CJS-wrapped namespace (Node 24 cjs-module-lexer regression).
248
+ // Unwrap so callers can destructure named exports directly.
249
+ return def;
250
+ }
251
+ // Neither named exports nor a usable default. Return as-is and let
252
+ // the caller's destructure yield `undefined` — but include a
253
+ // diagnostic marker the caller can surface in its error message.
254
+ throw new Error(`cacheImport("${specifier}") resolved ${entryPath} but the import() ` +
255
+ `returned a namespace with no named exports and no usable default ` +
256
+ `(keys: ${JSON.stringify(Object.keys(mod))}). The bundled package ` +
257
+ `may be corrupt or the platform's ESM-CJS interop is incompatible.`);
258
+ }
174
259
  /**
175
260
  * Walk up from `entryFile` to the nearest directory containing a
176
261
  * `package.json` whose `name` matches the specifier's package name.
package/dist/embedding.js CHANGED
@@ -140,8 +140,32 @@ export async function generateEmbedding(text, options) {
140
140
  // dynamic `import()` of the resolved entry file URL populates the named
141
141
  // exports correctly on every Node version we support. See
142
142
  // `makeCacheImport` in embedder-loader.ts.
143
+ //
144
+ // Defensive access (#394 follow-up): `cacheImport` normalizes the
145
+ // namespace so named exports are always top-level, but if the bundle
146
+ // is corrupt or a future Node version changes interop again, we want
147
+ // a CLEAR error here ("transformers bundle did not expose AutoModel")
148
+ // rather than the opaque downstream `autoModel is not a function`.
149
+ // The previous fix silently returned undefined and let the inference
150
+ // call site crash with a misleading message; this guard turns that
151
+ // into an actionable one.
143
152
  const transformers = await loaded.cacheImport('@huggingface/transformers');
144
- const { AutoTokenizer, AutoModel, pipeline } = transformers;
153
+ let AutoTokenizer = transformers.AutoTokenizer;
154
+ let AutoModel = transformers.AutoModel;
155
+ let pipeline = transformers.pipeline;
156
+ // Final `.default` fallback — covers any future regression where the
157
+ // loader's normalizer does not run (e.g. a hand-rolled caller).
158
+ if ((!AutoModel || !AutoTokenizer || !pipeline) && transformers.default) {
159
+ AutoTokenizer = AutoTokenizer ?? transformers.default.AutoTokenizer;
160
+ AutoModel = AutoModel ?? transformers.default.AutoModel;
161
+ pipeline = pipeline ?? transformers.default.pipeline;
162
+ }
163
+ if (typeof AutoModel !== 'function' && typeof AutoModel !== 'object') {
164
+ throw new Error(`transformers bundle did not expose AutoModel (typeof=${typeof AutoModel}). ` +
165
+ `Bundle may be corrupt or Node ${process.version} ESM-CJS interop ` +
166
+ `incompatible with the bundled @huggingface/transformers entry. ` +
167
+ `Cache at ${cfg.cacheRoot}/v1/.`);
168
+ }
145
169
  if (activeModel.pooling === 'sentence_embedding') {
146
170
  autoTokenizer = await AutoTokenizer.from_pretrained(activeModel.hfId);
147
171
  autoModel = await AutoModel.from_pretrained(activeModel.hfId, {
package/dist/tr-cli.js CHANGED
@@ -51,7 +51,7 @@ const STATE_PATH = CONFIG.onboardingStatePath;
51
51
  // Auto-synced by skill/scripts/sync-version.mjs from skill/plugin/package.json::version.
52
52
  // Do not edit by hand — running tests will catch drift but the publish workflow
53
53
  // rewrites this constant at the start of every npm/ClawHub publish.
54
- const PLUGIN_VERSION = '3.3.12-rc.16';
54
+ const PLUGIN_VERSION = '3.3.12-rc.17';
55
55
  function die(msg, code = 1) {
56
56
  process.stderr.write(`tr: ${msg}\n`);
57
57
  process.exit(code);
@@ -217,6 +217,15 @@ export function makeCacheRequire(layout: CacheLayout): NodeRequire {
217
217
  * entry — the `.mjs` file — and `import()` of that surfaces named
218
218
  * exports on every Node version we support (18, 20, 22, 24).
219
219
  *
220
+ * Post-import normalization (`normalizeImportNamespace`): even with
221
+ * the correct `.mjs` entry selected, Node 24 minor versions have
222
+ * shown regressions where the namespace comes back default-wrapped
223
+ * (cjs-module-lexer mis-parses the 1.3 MB bundled output, or the
224
+ * caller-context forces CJS resolution). The normalizer unwraps
225
+ * `.default` defensively so callers can always destructure named
226
+ * exports directly. See `normalizeImportNamespace` for the detection
227
+ * rule + edge cases.
228
+ *
220
229
  * Transitive deps resolve the same way: the imported module's own
221
230
  * internal `import`/`require` calls walk up from its URL and find the
222
231
  * cache's `node_modules` first.
@@ -255,10 +264,88 @@ export function makeCacheImport(layout: CacheLayout): (specifier: string) => Pro
255
264
  // Step 3: native ESM dynamic import of the file URL — populates
256
265
  // named exports correctly for dual CJS/ESM and ESM-only packages.
257
266
  const fileUrl = pathToFileURL(esmEntry).href;
258
- return await import(fileUrl);
267
+ const mod = await import(fileUrl);
268
+ // Step 4: normalize the namespace across Node versions.
269
+ //
270
+ // Why this exists (issue #394 follow-up: `autoModel is not a function`
271
+ // PERSISTED on Node 24.16.0 with the real `@huggingface/transformers`
272
+ // v4.2.0 bundle even after #394 shipped `cacheImport`):
273
+ // The `resolveEsmEntryPath` correctly prefers the `.mjs` (ESM
274
+ // native) entry, and `import()` of an `.mjs` file surfaces real
275
+ // named exports on every Node version we tested locally (18, 20,
276
+ // 22). But on Node 24, two regressions have been observed in the
277
+ // wild for dual CJS/ESM packages with large bundled outputs:
278
+ //
279
+ // a) `cjs-module-lexer` (Node's static analyzer for CJS named
280
+ // export detection) silently times out / mis-parses the 1.3 MB
281
+ // `transformers.node.cjs` bundle, so a fallback `import()` of
282
+ // the `.cjs` file returns ONLY `{ default: module.exports }`
283
+ // with no named exports. `const { AutoModel } = mod` then
284
+ // yields `undefined`.
285
+ //
286
+ // b) Some Node 24 minor versions changed the ESM-CJS interop such
287
+ // that `import()` of a dual package resolves to the CJS entry
288
+ // under specific caller-context conditions (e.g. when the
289
+ // caller is itself a transitive CJS module loaded via
290
+ // `createRequire` from ESM), again producing a default-only
291
+ // namespace.
292
+ //
293
+ // We CANNOT reproduce (a)/(b) on Node 22 (the bug is Node-24-only
294
+ // and depends on cjs-module-lexer's behavior on the real 1.3 MB
295
+ // bundle), so this normalization is defensive: after the `import()`,
296
+ // if the namespace has NO own enumerable string keys other than
297
+ // `default` (and `default` is a non-null object), we unwrap `default`
298
+ // and return ITS keys as the namespace. This makes the loader robust
299
+ // to BOTH the ESM-native path (named exports come through untouched)
300
+ // AND the CJS-fallback path (named exports are recovered from
301
+ // `default`).
302
+ return normalizeImportNamespace(mod, specifier, esmEntry);
259
303
  };
260
304
  }
261
305
 
306
+ /**
307
+ * Normalize the result of `import(specifier)` so callers can always
308
+ * destructure named exports directly, regardless of whether the resolved
309
+ * entry was ESM-native (named exports on the namespace) or CJS-via-ESM
310
+ * (named exports only reachable through `.default`).
311
+ *
312
+ * Detection rule: if the namespace has `default` AND zero non-`default`
313
+ * own enumerable string-keyed properties, treat it as a CJS-wrapped
314
+ * namespace and return the `default` object instead. Otherwise return
315
+ * the namespace unchanged (ESM-native or already-unwrapped).
316
+ *
317
+ * Edge case: a legitimately default-only ESM module (one that exports
318
+ * ONLY a default) would be mis-detected as CJS-wrapped. That is safe —
319
+ * the unwrapped `default` is itself the value the caller wants, and a
320
+ * default-only ESM module has no named exports to lose. The bundled
321
+ * packages this loader targets (`@huggingface/transformers`,
322
+ * `onnxruntime-node`) are named-export-heavy, so this edge case does
323
+ * not arise in practice.
324
+ */
325
+ function normalizeImportNamespace(mod: any, specifier: string, entryPath: string): any {
326
+ if (!mod || typeof mod !== 'object') return mod;
327
+ const ownKeys = Object.keys(mod).filter((k) => k !== 'default');
328
+ if (ownKeys.length > 0) {
329
+ // Named exports already present — ESM-native path. Return as-is.
330
+ return mod;
331
+ }
332
+ const def = (mod as { default?: unknown }).default;
333
+ if (def && typeof def === 'object') {
334
+ // CJS-wrapped namespace (Node 24 cjs-module-lexer regression).
335
+ // Unwrap so callers can destructure named exports directly.
336
+ return def;
337
+ }
338
+ // Neither named exports nor a usable default. Return as-is and let
339
+ // the caller's destructure yield `undefined` — but include a
340
+ // diagnostic marker the caller can surface in its error message.
341
+ throw new Error(
342
+ `cacheImport("${specifier}") resolved ${entryPath} but the import() ` +
343
+ `returned a namespace with no named exports and no usable default ` +
344
+ `(keys: ${JSON.stringify(Object.keys(mod))}). The bundled package ` +
345
+ `may be corrupt or the platform's ESM-CJS interop is incompatible.`
346
+ );
347
+ }
348
+
262
349
  /**
263
350
  * Walk up from `entryFile` to the nearest directory containing a
264
351
  * `package.json` whose `name` matches the specifier's package name.
package/embedding.ts CHANGED
@@ -187,8 +187,39 @@ export async function generateEmbedding(
187
187
  // dynamic `import()` of the resolved entry file URL populates the named
188
188
  // exports correctly on every Node version we support. See
189
189
  // `makeCacheImport` in embedder-loader.ts.
190
- const transformers = await loaded.cacheImport('@huggingface/transformers');
191
- const { AutoTokenizer, AutoModel, pipeline } = transformers as any;
190
+ //
191
+ // Defensive access (#394 follow-up): `cacheImport` normalizes the
192
+ // namespace so named exports are always top-level, but if the bundle
193
+ // is corrupt or a future Node version changes interop again, we want
194
+ // a CLEAR error here ("transformers bundle did not expose AutoModel")
195
+ // rather than the opaque downstream `autoModel is not a function`.
196
+ // The previous fix silently returned undefined and let the inference
197
+ // call site crash with a misleading message; this guard turns that
198
+ // into an actionable one.
199
+ const transformers = await loaded.cacheImport('@huggingface/transformers') as {
200
+ AutoTokenizer?: unknown;
201
+ AutoModel?: unknown;
202
+ pipeline?: unknown;
203
+ default?: { AutoTokenizer?: unknown; AutoModel?: unknown; pipeline?: unknown };
204
+ };
205
+ let AutoTokenizer = transformers.AutoTokenizer;
206
+ let AutoModel = transformers.AutoModel;
207
+ let pipeline = transformers.pipeline;
208
+ // Final `.default` fallback — covers any future regression where the
209
+ // loader's normalizer does not run (e.g. a hand-rolled caller).
210
+ if ((!AutoModel || !AutoTokenizer || !pipeline) && transformers.default) {
211
+ AutoTokenizer = AutoTokenizer ?? transformers.default.AutoTokenizer;
212
+ AutoModel = AutoModel ?? transformers.default.AutoModel;
213
+ pipeline = pipeline ?? transformers.default.pipeline;
214
+ }
215
+ if (typeof AutoModel !== 'function' && typeof AutoModel !== 'object') {
216
+ throw new Error(
217
+ `transformers bundle did not expose AutoModel (typeof=${typeof AutoModel}). ` +
218
+ `Bundle may be corrupt or Node ${process.version} ESM-CJS interop ` +
219
+ `incompatible with the bundled @huggingface/transformers entry. ` +
220
+ `Cache at ${cfg.cacheRoot}/v1/.`,
221
+ );
222
+ }
192
223
 
193
224
  if (activeModel.pooling === 'sentence_embedding') {
194
225
  autoTokenizer = await AutoTokenizer.from_pretrained(activeModel.hfId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totalreclaw/totalreclaw",
3
- "version": "3.3.12-rc.16",
3
+ "version": "3.3.12-rc.17",
4
4
  "description": "End-to-end encrypted, agent-portable memory for OpenClaw and any LLM-agent runtime. XChaCha20-Poly1305 with protobuf v4 + on-chain Memory Taxonomy v1 (claim / preference / directive / commitment / episode / summary).",
5
5
  "type": "module",
6
6
  "keywords": [
package/skill.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "totalreclaw",
3
- "version": "3.3.12-rc.16",
3
+ "version": "3.3.12-rc.17",
4
4
  "description": "End-to-end encrypted memory for AI agents — portable, yours forever. XChaCha20-Poly1305 E2EE: server never sees plaintext.",
5
5
  "author": "TotalReclaw Team",
6
6
  "license": "MIT",
package/tr-cli.ts CHANGED
@@ -68,7 +68,7 @@ const STATE_PATH = CONFIG.onboardingStatePath;
68
68
  // Auto-synced by skill/scripts/sync-version.mjs from skill/plugin/package.json::version.
69
69
  // Do not edit by hand — running tests will catch drift but the publish workflow
70
70
  // rewrites this constant at the start of every npm/ClawHub publish.
71
- const PLUGIN_VERSION = '3.3.12-rc.16';
71
+ const PLUGIN_VERSION = '3.3.12-rc.17';
72
72
 
73
73
  function die(msg: string, code = 1): never {
74
74
  process.stderr.write(`tr: ${msg}\n`);