@totalreclaw/totalreclaw 3.3.1-rc.2 → 3.3.1-rc.21
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 +330 -0
- package/SKILL.md +50 -83
- package/api-client.ts +18 -11
- package/config.ts +117 -3
- package/crypto.ts +10 -2
- package/dist/api-client.js +226 -0
- package/dist/billing-cache.js +100 -0
- package/dist/claims-helper.js +606 -0
- package/dist/config.js +280 -0
- package/dist/consolidation.js +258 -0
- package/dist/contradiction-sync.js +1034 -0
- package/dist/crypto.js +138 -0
- package/dist/digest-sync.js +361 -0
- package/dist/download-ux.js +63 -0
- package/dist/embedding.js +86 -0
- package/dist/extractor.js +1225 -0
- package/dist/first-run.js +103 -0
- package/dist/fs-helpers.js +563 -0
- package/dist/gateway-url.js +197 -0
- package/dist/generate-mnemonic.js +13 -0
- package/dist/hot-cache-wrapper.js +101 -0
- package/dist/import-adapters/base-adapter.js +64 -0
- package/dist/import-adapters/chatgpt-adapter.js +238 -0
- package/dist/import-adapters/claude-adapter.js +114 -0
- package/dist/import-adapters/gemini-adapter.js +201 -0
- package/dist/import-adapters/index.js +26 -0
- package/dist/import-adapters/mcp-memory-adapter.js +219 -0
- package/dist/import-adapters/mem0-adapter.js +158 -0
- package/dist/import-adapters/types.js +1 -0
- package/dist/index.js +5348 -0
- package/dist/llm-client.js +686 -0
- package/dist/llm-profile-reader.js +346 -0
- package/dist/lsh.js +62 -0
- package/dist/onboarding-cli.js +750 -0
- package/dist/pair-cli.js +344 -0
- package/dist/pair-crypto.js +359 -0
- package/dist/pair-http.js +404 -0
- package/dist/pair-page.js +826 -0
- package/dist/pair-qr.js +107 -0
- package/dist/pair-remote-client.js +410 -0
- package/dist/pair-session-store.js +566 -0
- package/dist/pin.js +542 -0
- package/dist/qa-bug-report.js +301 -0
- package/dist/relay-headers.js +44 -0
- package/dist/reranker.js +442 -0
- package/dist/retype-setscope.js +348 -0
- package/dist/semantic-dedup.js +75 -0
- package/dist/subgraph-search.js +289 -0
- package/dist/subgraph-store.js +694 -0
- package/dist/tool-gating.js +58 -0
- package/download-ux.ts +91 -0
- package/embedding.ts +32 -9
- package/fs-helpers.ts +124 -0
- package/gateway-url.ts +57 -9
- package/index.ts +586 -357
- package/llm-client.ts +211 -23
- package/lsh.ts +7 -2
- package/onboarding-cli.ts +114 -1
- package/package.json +19 -5
- package/pair-cli.ts +76 -8
- package/pair-crypto.ts +34 -24
- package/pair-page.ts +28 -17
- package/pair-qr.ts +152 -0
- package/pair-remote-client.ts +540 -0
- package/qa-bug-report.ts +381 -0
- package/relay-headers.ts +50 -0
- package/reranker.ts +73 -0
- package/retype-setscope.ts +12 -0
- package/subgraph-search.ts +4 -3
- package/subgraph-store.ts +109 -16
package/subgraph-store.ts
CHANGED
|
@@ -10,13 +10,18 @@
|
|
|
10
10
|
* and chain RPCs. No viem, no permissionless.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
// Lazy-load WASM
|
|
13
|
+
// Lazy-load WASM via createRequire — the shipped bundle is ESM-only and
|
|
14
|
+
// the bare `require` global is undefined there (issue #124). Same pattern
|
|
15
|
+
// as crypto / lsh / claims-helper / consolidation / digest-sync.
|
|
16
|
+
import { createRequire } from 'node:module';
|
|
17
|
+
const requireWasm = createRequire(import.meta.url);
|
|
14
18
|
let _wasm: typeof import('@totalreclaw/core') | null = null;
|
|
15
19
|
function getWasm() {
|
|
16
|
-
if (!_wasm) _wasm =
|
|
20
|
+
if (!_wasm) _wasm = requireWasm('@totalreclaw/core');
|
|
17
21
|
return _wasm;
|
|
18
22
|
}
|
|
19
23
|
import { CONFIG } from './config.js';
|
|
24
|
+
import { buildRelayHeaders } from './relay-headers.js';
|
|
20
25
|
|
|
21
26
|
// ---------------------------------------------------------------------------
|
|
22
27
|
// Pimlico 429 retry helper
|
|
@@ -231,6 +236,68 @@ export async function deriveSmartAccountAddress(mnemonic: string, chainId?: numb
|
|
|
231
236
|
*/
|
|
232
237
|
const deployedAccounts = new Set<string>();
|
|
233
238
|
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// Per-account submission mutex — 3.3.1-rc.3 AA25 serialization
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
//
|
|
243
|
+
// Concurrent `submitFactOnChain` / `submitFactBatchOnChain` calls for the
|
|
244
|
+
// SAME Smart Account used to race at the nonce-fetch step:
|
|
245
|
+
// - Call A: getNonce()=5, build UserOp, submit, wait for receipt.
|
|
246
|
+
// - Call B: getNonce()=5 (A not mined yet), build UserOp, submit → AA25.
|
|
247
|
+
//
|
|
248
|
+
// The fix: chain submissions per `sender` address through a single promise.
|
|
249
|
+
// Each call awaits the previous in-flight submission before starting its
|
|
250
|
+
// own nonce fetch. Fallback to public RPC for getNonce continues to work
|
|
251
|
+
// because by the time B fetches, A's UserOp has been bundled AND mined.
|
|
252
|
+
//
|
|
253
|
+
// 16 AA25 occurrences were logged in rc.2 QA; this lock eliminates the
|
|
254
|
+
// race condition at the plugin layer. Subsequent AA25s would indicate
|
|
255
|
+
// nonce rot from another process (e.g. relay retrying the same UserOp)
|
|
256
|
+
// and are handled by the existing single-retry with fresh-nonce path.
|
|
257
|
+
const _senderSubmissionLocks = new Map<string, Promise<unknown>>();
|
|
258
|
+
|
|
259
|
+
async function withSenderLock<T>(sender: string, fn: () => Promise<T>): Promise<T> {
|
|
260
|
+
const key = sender.toLowerCase();
|
|
261
|
+
const prev = _senderSubmissionLocks.get(key) ?? Promise.resolve();
|
|
262
|
+
let release: () => void = () => {};
|
|
263
|
+
const thisCallGate = new Promise<void>((resolve) => { release = resolve; });
|
|
264
|
+
_senderSubmissionLocks.set(key, prev.then(() => thisCallGate));
|
|
265
|
+
try {
|
|
266
|
+
await prev; // wait for previous submission to settle (success OR failure)
|
|
267
|
+
} catch {
|
|
268
|
+
// Prior submission threw — that's the caller's problem, not ours.
|
|
269
|
+
// The lock is still released below; we re-enter the chain.
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
return await fn();
|
|
273
|
+
} finally {
|
|
274
|
+
release();
|
|
275
|
+
// If we're the tail of the chain, clean up to avoid unbounded memory.
|
|
276
|
+
// Use `===` to ensure we don't clobber a newer lock that joined while
|
|
277
|
+
// we were running.
|
|
278
|
+
const current = _senderSubmissionLocks.get(key);
|
|
279
|
+
// The lock we set above was `prev.then(() => thisCallGate)` — when
|
|
280
|
+
// `thisCallGate` resolves, the whole promise resolves. If nothing
|
|
281
|
+
// queued behind us, remove the entry.
|
|
282
|
+
if (current) {
|
|
283
|
+
current.then(() => {
|
|
284
|
+
if (_senderSubmissionLocks.get(key) === current) {
|
|
285
|
+
_senderSubmissionLocks.delete(key);
|
|
286
|
+
}
|
|
287
|
+
}).catch(() => {
|
|
288
|
+
if (_senderSubmissionLocks.get(key) === current) {
|
|
289
|
+
_senderSubmissionLocks.delete(key);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Exposed for tests — reset the per-account lock map. */
|
|
297
|
+
export function __resetSenderLocksForTests(): void {
|
|
298
|
+
_senderSubmissionLocks.clear();
|
|
299
|
+
}
|
|
300
|
+
|
|
234
301
|
/**
|
|
235
302
|
* Check if a Smart Account is deployed and return factory/factoryData if not.
|
|
236
303
|
*
|
|
@@ -303,22 +370,36 @@ export async function submitFactOnChain(
|
|
|
303
370
|
throw new Error('Recovery phrase (TOTALRECLAW_RECOVERY_PHRASE) is required for on-chain submission');
|
|
304
371
|
}
|
|
305
372
|
|
|
373
|
+
// Resolve sender up-front so we can serialize concurrent submissions for
|
|
374
|
+
// the SAME Smart Account (rc.3 AA25 fix). Derivation is CREATE2, so we
|
|
375
|
+
// don't need to hit the chain — WASM does it.
|
|
376
|
+
const eoa = getWasm().deriveEoa(config.mnemonic) as { private_key: string; address: string };
|
|
377
|
+
const sender = config.walletAddress || await deriveSmartAccountAddress(config.mnemonic, config.chainId);
|
|
378
|
+
|
|
379
|
+
return withSenderLock(sender, () => submitFactOnChainLocked(
|
|
380
|
+
protobufPayload, config, eoa, sender,
|
|
381
|
+
));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async function submitFactOnChainLocked(
|
|
385
|
+
protobufPayload: Buffer,
|
|
386
|
+
config: SubgraphStoreConfig,
|
|
387
|
+
eoa: { private_key: string; address: string },
|
|
388
|
+
sender: string,
|
|
389
|
+
): Promise<{ txHash: string; userOpHash: string; success: boolean }> {
|
|
306
390
|
const bundlerUrl = `${config.relayUrl}/v1/bundler`;
|
|
307
|
-
const
|
|
391
|
+
const overrides: Record<string, string> = {
|
|
308
392
|
'Content-Type': 'application/json',
|
|
309
|
-
'X-TotalReclaw-Client': 'openclaw-plugin',
|
|
310
393
|
};
|
|
311
|
-
if (config.authKeyHex)
|
|
312
|
-
if (config.walletAddress)
|
|
394
|
+
if (config.authKeyHex) overrides['Authorization'] = `Bearer ${config.authKeyHex}`;
|
|
395
|
+
if (config.walletAddress) overrides['X-Wallet-Address'] = config.walletAddress;
|
|
396
|
+
const headers = buildRelayHeaders(overrides);
|
|
313
397
|
|
|
314
398
|
// Helper for JSON-RPC calls to relay bundler (with 429 retry)
|
|
315
399
|
async function rpc(method: string, params: unknown[]): Promise<any> {
|
|
316
400
|
return rpcWithRetry(bundlerUrl, headers, method, params);
|
|
317
401
|
}
|
|
318
402
|
|
|
319
|
-
// 1. Derive EOA from mnemonic
|
|
320
|
-
const eoa = getWasm().deriveEoa(config.mnemonic) as { private_key: string; address: string };
|
|
321
|
-
const sender = config.walletAddress || await deriveSmartAccountAddress(config.mnemonic, config.chainId);
|
|
322
403
|
const entryPoint = config.entryPointAddress || getWasm().getEntryPointAddress();
|
|
323
404
|
|
|
324
405
|
// 2. Encode calldata (SimpleAccount.execute → DataEdge fallback)
|
|
@@ -508,21 +589,33 @@ export async function submitFactBatchOnChain(
|
|
|
508
589
|
throw new Error('Recovery phrase (TOTALRECLAW_RECOVERY_PHRASE) is required for on-chain submission');
|
|
509
590
|
}
|
|
510
591
|
|
|
592
|
+
// Resolve sender up-front for the per-account mutex (rc.3 AA25 fix).
|
|
593
|
+
const eoa = getWasm().deriveEoa(config.mnemonic) as { private_key: string; address: string };
|
|
594
|
+
const sender = config.walletAddress || await deriveSmartAccountAddress(config.mnemonic, config.chainId);
|
|
595
|
+
|
|
596
|
+
return withSenderLock(sender, () => submitFactBatchOnChainLocked(
|
|
597
|
+
protobufPayloads, config, eoa, sender,
|
|
598
|
+
));
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
async function submitFactBatchOnChainLocked(
|
|
602
|
+
protobufPayloads: Buffer[],
|
|
603
|
+
config: SubgraphStoreConfig,
|
|
604
|
+
eoa: { private_key: string; address: string },
|
|
605
|
+
sender: string,
|
|
606
|
+
): Promise<{ txHash: string; userOpHash: string; success: boolean; batchSize: number }> {
|
|
511
607
|
const bundlerUrl = `${config.relayUrl}/v1/bundler`;
|
|
512
|
-
const
|
|
608
|
+
const overrides: Record<string, string> = {
|
|
513
609
|
'Content-Type': 'application/json',
|
|
514
|
-
'X-TotalReclaw-Client': 'openclaw-plugin',
|
|
515
610
|
};
|
|
516
|
-
if (config.authKeyHex)
|
|
517
|
-
if (config.walletAddress)
|
|
611
|
+
if (config.authKeyHex) overrides['Authorization'] = `Bearer ${config.authKeyHex}`;
|
|
612
|
+
if (config.walletAddress) overrides['X-Wallet-Address'] = config.walletAddress;
|
|
613
|
+
const headers = buildRelayHeaders(overrides);
|
|
518
614
|
|
|
519
615
|
// Helper for JSON-RPC calls to relay bundler (with 429 retry)
|
|
520
616
|
async function rpc(method: string, params: unknown[]): Promise<any> {
|
|
521
617
|
return rpcWithRetry(bundlerUrl, headers, method, params);
|
|
522
618
|
}
|
|
523
|
-
|
|
524
|
-
const eoa = getWasm().deriveEoa(config.mnemonic) as { private_key: string; address: string };
|
|
525
|
-
const sender = config.walletAddress || await deriveSmartAccountAddress(config.mnemonic, config.chainId);
|
|
526
619
|
const entryPoint = config.entryPointAddress || getWasm().getEntryPointAddress();
|
|
527
620
|
|
|
528
621
|
// Encode batch calldata (SimpleAccount.executeBatch)
|