@totalreclaw/totalreclaw 1.6.0 → 3.0.6
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/CLAWHUB.md +134 -0
- package/README.md +407 -64
- package/SKILL.md +1032 -0
- package/api-client.ts +5 -5
- package/claims-helper.ts +686 -0
- package/config.ts +211 -0
- package/consolidation.ts +141 -33
- package/contradiction-sync.ts +1389 -0
- package/crypto.ts +63 -261
- package/digest-sync.ts +516 -0
- package/embedding.ts +69 -46
- package/extractor.ts +1307 -84
- package/hot-cache-wrapper.ts +1 -1
- package/import-adapters/gemini-adapter.ts +243 -0
- package/import-adapters/index.ts +3 -0
- package/import-adapters/types.ts +1 -1
- package/index.ts +1887 -323
- package/llm-client.ts +106 -53
- package/lsh.ts +21 -210
- package/package.json +20 -7
- package/pin.ts +502 -0
- package/reranker.ts +96 -124
- package/skill.json +213 -0
- package/subgraph-search.ts +112 -5
- package/subgraph-store.ts +559 -275
- package/consolidation.test.ts +0 -356
- package/extractor-dedup.test.ts +0 -168
- package/import-adapters/import-adapters.test.ts +0 -1123
- package/lsh.test.ts +0 -463
- package/pocv2-e2e-test.ts +0 -917
- package/porter-stemmer.d.ts +0 -4
- package/reranker.test.ts +0 -594
- package/semantic-dedup.test.ts +0 -392
- package/setup.sh +0 -19
- package/store-dedup-wiring.test.ts +0 -186
package/subgraph-search.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import { getSubgraphConfig } from './subgraph-store.js';
|
|
24
|
+
import { CONFIG } from './config.js';
|
|
24
25
|
|
|
25
26
|
export interface SubgraphSearchFact {
|
|
26
27
|
id: string;
|
|
@@ -32,9 +33,9 @@ export interface SubgraphSearchFact {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/** Batch size for Phase 2 split queries. */
|
|
35
|
-
const TRAPDOOR_BATCH_SIZE =
|
|
36
|
+
const TRAPDOOR_BATCH_SIZE = CONFIG.trapdoorBatchSize;
|
|
36
37
|
/** Graph Studio / Graph Network hard limit on `first` argument. */
|
|
37
|
-
const PAGE_SIZE =
|
|
38
|
+
const PAGE_SIZE = CONFIG.pageSize;
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* Execute a single GraphQL query against the subgraph endpoint.
|
|
@@ -59,10 +60,18 @@ async function gqlQuery<T>(
|
|
|
59
60
|
headers,
|
|
60
61
|
body: JSON.stringify({ query, variables }),
|
|
61
62
|
});
|
|
62
|
-
if (!response.ok)
|
|
63
|
-
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
const body = await response.text().catch(() => '');
|
|
65
|
+
console.error(`[TotalReclaw] Subgraph query failed: HTTP ${response.status} — ${body.slice(0, 200)}`);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const json = await response.json() as { data?: T; error?: string; errors?: Array<{ message: string }> };
|
|
69
|
+
if (json.error || json.errors) {
|
|
70
|
+
console.error(`[TotalReclaw] Subgraph query error: ${json.error || json.errors?.map(e => e.message).join('; ')}`);
|
|
71
|
+
}
|
|
64
72
|
return json.data ?? null;
|
|
65
|
-
} catch {
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.error(`[TotalReclaw] Subgraph query exception: ${err instanceof Error ? err.message : String(err)}`);
|
|
66
75
|
return null;
|
|
67
76
|
}
|
|
68
77
|
}
|
|
@@ -83,6 +92,7 @@ const SEARCH_QUERY = `
|
|
|
83
92
|
encryptedEmbedding
|
|
84
93
|
decayScore
|
|
85
94
|
timestamp
|
|
95
|
+
createdAt
|
|
86
96
|
isActive
|
|
87
97
|
contentFp
|
|
88
98
|
sequenceId
|
|
@@ -107,6 +117,7 @@ const PAGINATE_QUERY = `
|
|
|
107
117
|
encryptedBlob
|
|
108
118
|
encryptedEmbedding
|
|
109
119
|
timestamp
|
|
120
|
+
createdAt
|
|
110
121
|
decayScore
|
|
111
122
|
isActive
|
|
112
123
|
contentFp
|
|
@@ -249,6 +260,102 @@ export async function searchSubgraph(
|
|
|
249
260
|
return Array.from(allResults.values());
|
|
250
261
|
}
|
|
251
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Broadened search: fetch recent active facts by owner without trapdoor filtering.
|
|
265
|
+
* Used as a fallback when trapdoor search returns 0 candidates (e.g., vague queries
|
|
266
|
+
* like "who am I?" where word trapdoors don't overlap with stored fact tokens).
|
|
267
|
+
*/
|
|
268
|
+
export async function searchSubgraphBroadened(
|
|
269
|
+
owner: string,
|
|
270
|
+
maxCandidates: number,
|
|
271
|
+
authKeyHex?: string,
|
|
272
|
+
): Promise<SubgraphSearchFact[]> {
|
|
273
|
+
const config = getSubgraphConfig();
|
|
274
|
+
const subgraphUrl = `${config.relayUrl}/v1/subgraph`;
|
|
275
|
+
|
|
276
|
+
const query = `
|
|
277
|
+
query BroadenedSearch($owner: Bytes!, $first: Int!) {
|
|
278
|
+
facts(
|
|
279
|
+
where: { owner: $owner, isActive: true }
|
|
280
|
+
first: $first
|
|
281
|
+
orderBy: timestamp
|
|
282
|
+
orderDirection: desc
|
|
283
|
+
) {
|
|
284
|
+
id
|
|
285
|
+
encryptedBlob
|
|
286
|
+
encryptedEmbedding
|
|
287
|
+
decayScore
|
|
288
|
+
timestamp
|
|
289
|
+
createdAt
|
|
290
|
+
isActive
|
|
291
|
+
contentFp
|
|
292
|
+
sequenceId
|
|
293
|
+
version
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
`;
|
|
297
|
+
|
|
298
|
+
const data = await gqlQuery<{ facts?: SubgraphSearchFact[] }>(
|
|
299
|
+
subgraphUrl,
|
|
300
|
+
query,
|
|
301
|
+
{ owner, first: Math.min(maxCandidates, 1000) },
|
|
302
|
+
authKeyHex,
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
return (data?.facts ?? []).filter(f => f.isActive !== false);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Fetch a single fact by its client-generated UUID.
|
|
310
|
+
*
|
|
311
|
+
* Used by the pin/unpin tools to retrieve a known fact's encrypted blob and
|
|
312
|
+
* metadata for re-encryption with an updated status. Returns null if the fact
|
|
313
|
+
* is not found, has been tombstoned (isActive=false), or the owner does not
|
|
314
|
+
* match the caller's Smart Account address (defense against stale IDs from
|
|
315
|
+
* another user's recall results).
|
|
316
|
+
*/
|
|
317
|
+
export async function fetchFactById(
|
|
318
|
+
owner: string,
|
|
319
|
+
factId: string,
|
|
320
|
+
authKeyHex?: string,
|
|
321
|
+
): Promise<(SubgraphSearchFact & { owner: string }) | null> {
|
|
322
|
+
const config = getSubgraphConfig();
|
|
323
|
+
const subgraphUrl = `${config.relayUrl}/v1/subgraph`;
|
|
324
|
+
|
|
325
|
+
const query = `
|
|
326
|
+
query GetFactById($id: ID!) {
|
|
327
|
+
fact(id: $id) {
|
|
328
|
+
id
|
|
329
|
+
owner
|
|
330
|
+
encryptedBlob
|
|
331
|
+
encryptedEmbedding
|
|
332
|
+
decayScore
|
|
333
|
+
timestamp
|
|
334
|
+
createdAt
|
|
335
|
+
isActive
|
|
336
|
+
contentFp
|
|
337
|
+
sequenceId
|
|
338
|
+
version
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
`;
|
|
342
|
+
|
|
343
|
+
const data = await gqlQuery<{ fact?: (SubgraphSearchFact & { owner: string }) | null }>(
|
|
344
|
+
subgraphUrl,
|
|
345
|
+
query,
|
|
346
|
+
{ id: factId },
|
|
347
|
+
authKeyHex,
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
const fact = data?.fact;
|
|
351
|
+
if (!fact) return null;
|
|
352
|
+
if (fact.isActive === false) return null;
|
|
353
|
+
if (typeof fact.owner === 'string' && fact.owner.toLowerCase() !== owner.toLowerCase()) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
return fact;
|
|
357
|
+
}
|
|
358
|
+
|
|
252
359
|
/**
|
|
253
360
|
* Get fact count from the subgraph for dynamic pool sizing.
|
|
254
361
|
* Uses the globalStates entity for a lightweight single-row lookup
|