clawmem 0.1.0

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 (50) hide show
  1. package/AGENTS.md +660 -0
  2. package/CLAUDE.md +660 -0
  3. package/LICENSE +21 -0
  4. package/README.md +993 -0
  5. package/SKILL.md +717 -0
  6. package/bin/clawmem +75 -0
  7. package/package.json +72 -0
  8. package/src/amem.ts +797 -0
  9. package/src/beads.ts +263 -0
  10. package/src/clawmem.ts +1849 -0
  11. package/src/collections.ts +405 -0
  12. package/src/config.ts +178 -0
  13. package/src/consolidation.ts +123 -0
  14. package/src/directory-context.ts +248 -0
  15. package/src/errors.ts +41 -0
  16. package/src/formatter.ts +427 -0
  17. package/src/graph-traversal.ts +247 -0
  18. package/src/hooks/context-surfacing.ts +317 -0
  19. package/src/hooks/curator-nudge.ts +89 -0
  20. package/src/hooks/decision-extractor.ts +639 -0
  21. package/src/hooks/feedback-loop.ts +214 -0
  22. package/src/hooks/handoff-generator.ts +345 -0
  23. package/src/hooks/postcompact-inject.ts +226 -0
  24. package/src/hooks/precompact-extract.ts +314 -0
  25. package/src/hooks/pretool-inject.ts +79 -0
  26. package/src/hooks/session-bootstrap.ts +324 -0
  27. package/src/hooks/staleness-check.ts +130 -0
  28. package/src/hooks.ts +367 -0
  29. package/src/indexer.ts +327 -0
  30. package/src/intent.ts +294 -0
  31. package/src/limits.ts +26 -0
  32. package/src/llm.ts +1175 -0
  33. package/src/mcp.ts +2138 -0
  34. package/src/memory.ts +336 -0
  35. package/src/mmr.ts +93 -0
  36. package/src/observer.ts +269 -0
  37. package/src/openclaw/engine.ts +283 -0
  38. package/src/openclaw/index.ts +221 -0
  39. package/src/openclaw/plugin.json +83 -0
  40. package/src/openclaw/shell.ts +207 -0
  41. package/src/openclaw/tools.ts +304 -0
  42. package/src/profile.ts +346 -0
  43. package/src/promptguard.ts +218 -0
  44. package/src/retrieval-gate.ts +106 -0
  45. package/src/search-utils.ts +127 -0
  46. package/src/server.ts +783 -0
  47. package/src/splitter.ts +325 -0
  48. package/src/store.ts +4062 -0
  49. package/src/validation.ts +67 -0
  50. package/src/watcher.ts +58 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * ClawMem Search Utilities — Shared enrichment and fusion functions
3
+ *
4
+ * Consolidates duplicated code from clawmem.ts, mcp.ts, and context-surfacing.ts.
5
+ */
6
+
7
+ import type { Store, SearchResult } from "./store.ts";
8
+ import type { EnrichedResult } from "./memory.ts";
9
+
10
+ // =============================================================================
11
+ // Result Enrichment
12
+ // =============================================================================
13
+
14
+ /**
15
+ * Join search results with SAME metadata from the documents table.
16
+ * Adds content_type, modified_at, access_count, confidence to each result.
17
+ */
18
+ export function enrichResults(
19
+ store: Store,
20
+ results: SearchResult[],
21
+ _query: string
22
+ ): EnrichedResult[] {
23
+ return results.map(r => {
24
+ const row = store.db.prepare(`
25
+ SELECT content_type, modified_at, access_count, confidence, domain, workstream, tags,
26
+ quality_score, pinned, last_accessed_at, duplicate_count, revision_count
27
+ FROM documents
28
+ WHERE active = 1 AND (collection || '/' || path) = ?
29
+ LIMIT 1
30
+ `).get(r.displayPath) as any | null;
31
+
32
+ return {
33
+ ...r,
34
+ contentType: row?.content_type ?? "note",
35
+ modifiedAt: row?.modified_at ?? r.modifiedAt,
36
+ accessCount: row?.access_count ?? 0,
37
+ confidence: row?.confidence ?? 0.5,
38
+ qualityScore: row?.quality_score ?? 0.5,
39
+ pinned: !!(row?.pinned),
40
+ lastAccessedAt: row?.last_accessed_at ?? null,
41
+ duplicateCount: row?.duplicate_count ?? 1,
42
+ revisionCount: row?.revision_count ?? 1,
43
+ } as EnrichedResult;
44
+ });
45
+ }
46
+
47
+ // =============================================================================
48
+ // Ranked Result Type (for RRF)
49
+ // =============================================================================
50
+
51
+ export type RankedResult = {
52
+ file: string;
53
+ displayPath: string;
54
+ title: string;
55
+ body: string;
56
+ score: number;
57
+ };
58
+
59
+ // =============================================================================
60
+ // Reciprocal Rank Fusion
61
+ // =============================================================================
62
+
63
+ /**
64
+ * Merge multiple ranked result lists using Reciprocal Rank Fusion.
65
+ * k=60 is the standard RRF constant. Top-rank bonuses reward results
66
+ * that appear at rank 0 (+0.05) or rank 1-2 (+0.02).
67
+ */
68
+ export function reciprocalRankFusion(
69
+ resultLists: RankedResult[][],
70
+ weights: number[],
71
+ k: number = 60
72
+ ): RankedResult[] {
73
+ // Validate weights match result lists when explicitly provided
74
+ if (weights.length > 0 && weights.length !== resultLists.length) {
75
+ throw new Error(
76
+ `weights length (${weights.length}) must match resultLists length (${resultLists.length})`
77
+ );
78
+ }
79
+
80
+ // Validate k is finite and positive
81
+ if (!Number.isFinite(k) || k <= 0) k = 60;
82
+
83
+ // Validate all weights are finite and non-negative
84
+ for (let w = 0; w < weights.length; w++) {
85
+ if (!Number.isFinite(weights[w]) || weights[w]! < 0) {
86
+ weights[w] = 1;
87
+ }
88
+ }
89
+
90
+ const scores = new Map<string, { score: number; result: RankedResult }>();
91
+
92
+ for (let i = 0; i < resultLists.length; i++) {
93
+ const list = resultLists[i]!;
94
+ const weight = weights[i] ?? 1;
95
+ if (weight === 0) continue; // Skip zero-weight lists entirely
96
+ for (let rank = 0; rank < list.length; rank++) {
97
+ const r = list[rank]!;
98
+ const existing = scores.get(r.file);
99
+ const rrfScore = weight / (k + rank + 1);
100
+ const bonus = rank === 0 ? 0.05 : rank <= 2 ? 0.02 : 0;
101
+ const total = rrfScore + bonus;
102
+
103
+ if (existing) {
104
+ existing.score += total;
105
+ } else {
106
+ scores.set(r.file, { score: total, result: r });
107
+ }
108
+ }
109
+ }
110
+
111
+ return [...scores.values()]
112
+ .sort((a, b) => b.score - a.score)
113
+ .map(v => ({ ...v.result, score: v.score }));
114
+ }
115
+
116
+ /**
117
+ * Convert a SearchResult to a RankedResult for use in RRF.
118
+ */
119
+ export function toRanked(r: SearchResult): RankedResult {
120
+ return {
121
+ file: r.filepath,
122
+ displayPath: r.displayPath,
123
+ title: r.title,
124
+ body: r.body || "",
125
+ score: r.score,
126
+ };
127
+ }