shieldcortex 3.0.3 → 3.0.4

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 (91) hide show
  1. package/dashboard/.next/standalone/dashboard/.next/BUILD_ID +1 -1
  2. package/dashboard/.next/standalone/dashboard/.next/build-manifest.json +2 -2
  3. package/dashboard/.next/standalone/dashboard/.next/prerender-manifest.json +3 -3
  4. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.html +2 -2
  5. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.rsc +1 -1
  6. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.html +1 -1
  13. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.rsc +2 -2
  14. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  15. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  17. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/dashboard/.next/standalone/dashboard/.next/server/app/index.html +1 -1
  21. package/dashboard/.next/standalone/dashboard/.next/server/app/index.rsc +3 -3
  22. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  23. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_full.segment.rsc +3 -3
  24. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
  25. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_index.segment.rsc +2 -2
  26. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  27. package/dashboard/.next/standalone/dashboard/.next/server/app/page/react-loadable-manifest.json +1 -1
  28. package/dashboard/.next/standalone/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  29. package/dashboard/.next/standalone/dashboard/.next/server/chunks/ssr/dashboard_3051539d._.js +1 -1
  30. package/dashboard/.next/standalone/dashboard/.next/server/pages/404.html +1 -1
  31. package/dashboard/.next/standalone/dashboard/.next/server/pages/500.html +2 -2
  32. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.js +1 -1
  33. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.json +1 -1
  34. package/dashboard/.next/standalone/dashboard/.next/static/chunks/313c0d327bbf244a.js +9 -0
  35. package/dashboard/.next/standalone/dashboard/.next/static/chunks/{fa5217550a8ab9a6.js → 49c1cec591af1460.js} +2 -2
  36. package/dashboard/.next/standalone/dashboard/.next/static/chunks/{f69fd1c5e71fbbfd.js → ca21f348cb163905.js} +1 -1
  37. package/dashboard/.next/standalone/dashboard/.next/static/chunks/f4ca424319f58dc7.css +3 -0
  38. package/dist/api/routes/admin.d.ts +12 -0
  39. package/dist/api/routes/admin.js +502 -0
  40. package/dist/api/routes/graph.d.ts +4 -0
  41. package/dist/api/routes/graph.js +333 -0
  42. package/dist/api/routes/incidents.d.ts +2 -0
  43. package/dist/api/routes/incidents.js +32 -0
  44. package/dist/api/routes/memories.d.ts +4 -0
  45. package/dist/api/routes/memories.js +659 -0
  46. package/dist/api/routes/recall.d.ts +4 -0
  47. package/dist/api/routes/recall.js +36 -0
  48. package/dist/api/routes/system.d.ts +9 -0
  49. package/dist/api/routes/system.js +201 -0
  50. package/dist/api/visualization-server.js +31 -1913
  51. package/dist/memory/search.d.ts +37 -0
  52. package/dist/memory/search.js +143 -0
  53. package/dist/memory/store.js +2 -166
  54. package/dist/tools/forget.d.ts +2 -2
  55. package/dist/tools/recall.d.ts +2 -2
  56. package/hooks/openclaw/cortex-memory/handler.ts +5 -141
  57. package/hooks/openclaw/cortex-memory/runtime.mjs +129 -0
  58. package/package.json +8 -4
  59. package/plugins/openclaw/dist/index.js +5 -39
  60. package/scripts/run-jest.mjs +25 -1
  61. package/dashboard/.next/standalone/dashboard/.next/static/chunks/be6970da20a17c0b.js +0 -9
  62. package/dashboard/.next/standalone/dashboard/.next/static/chunks/e63d2228780629dd.css +0 -3
  63. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_tsc.js +0 -133818
  64. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_tsserver.js +0 -659
  65. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_typingsInstaller.js +0 -222
  66. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +0 -2122
  67. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/de/diagnosticMessages.generated.json +0 -2122
  68. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/es/diagnosticMessages.generated.json +0 -2122
  69. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +0 -2122
  70. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/it/diagnosticMessages.generated.json +0 -2122
  71. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +0 -2122
  72. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +0 -2122
  73. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +0 -2122
  74. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +0 -2122
  75. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +0 -2122
  76. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +0 -2122
  77. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsc.js +0 -8
  78. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsserver.js +0 -8
  79. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsserverlibrary.js +0 -21
  80. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typesMap.json +0 -497
  81. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typescript.js +0 -200276
  82. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typingsInstaller.js +0 -8
  83. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/watchGuard.js +0 -53
  84. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +0 -2122
  85. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +0 -2122
  86. package/dashboard/.next/standalone/dashboard/node_modules/typescript/package.json +0 -120
  87. package/scripts/start-dashboard.sh +0 -41
  88. package/scripts/stop-dashboard.sh +0 -21
  89. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → BEvyMAX62LQMyt5iSb-F9}/_buildManifest.js +0 -0
  90. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → BEvyMAX62LQMyt5iSb-F9}/_clientMiddlewareManifest.json +0 -0
  91. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → BEvyMAX62LQMyt5iSb-F9}/_ssgManifest.js +0 -0
@@ -0,0 +1,37 @@
1
+ import type Database from 'better-sqlite3';
2
+ import { Memory, MemoryCategory, MemoryConfig, SearchResult } from './types.js';
3
+ export interface SearchExecutionOptions {
4
+ enableSideEffects: boolean;
5
+ includeExplanation: boolean;
6
+ }
7
+ export interface SearchScoringContext {
8
+ db: Database.Database;
9
+ config: MemoryConfig;
10
+ detectedCategory: MemoryCategory | null;
11
+ queryTags: string[];
12
+ vectorResults: Map<number, number>;
13
+ query: string;
14
+ }
15
+ export interface SearchScoreValues {
16
+ ftsScore: number;
17
+ vectorSimilarity: number;
18
+ vectorBoost: number;
19
+ decayedScore: number;
20
+ priorityBoost: number;
21
+ recencyBoost: number;
22
+ categoryBoost: number;
23
+ linkBoost: number;
24
+ tagBoost: number;
25
+ activationBoost: number;
26
+ finalScore: number;
27
+ }
28
+ export type MemoryRowConverter = (row: Record<string, unknown>) => Memory;
29
+ export declare function detectQueryCategory(query: string): MemoryCategory | null;
30
+ export declare function calculateLinkBoost(memoryId: number, db: Database.Database): number;
31
+ export declare function calculateTagScore(queryTags: string[], memoryTags: string[]): number;
32
+ export declare function extractQueryTags(query: string): string[];
33
+ export declare function vectorSearch(db: Database.Database, rowToMemory: MemoryRowConverter, queryEmbedding: Float32Array, limit: number, project?: string, includeGlobal?: boolean): Array<{
34
+ memory: Memory;
35
+ similarity: number;
36
+ }>;
37
+ export declare function buildSearchExplanation(memory: Memory, context: SearchScoringContext, values: SearchScoreValues): SearchResult['explanation'];
@@ -0,0 +1,143 @@
1
+ import { cosineSimilarity } from '../embeddings/index.js';
2
+ export function detectQueryCategory(query) {
3
+ const lower = query.toLowerCase();
4
+ if (/architect|design|structure|pattern|system|schema|model/.test(lower)) {
5
+ return 'architecture';
6
+ }
7
+ if (/error|bug|fix|issue|crash|exception|fail|problem/.test(lower)) {
8
+ return 'error';
9
+ }
10
+ if (/prefer|always|never|style|convention|like|want/.test(lower)) {
11
+ return 'preference';
12
+ }
13
+ if (/learn|discover|realiz|found\s+out|turns?\s+out/.test(lower)) {
14
+ return 'learning';
15
+ }
16
+ if (/todo|task|pending|need\s+to|should\s+do/.test(lower)) {
17
+ return 'todo';
18
+ }
19
+ if (/relation|depend|connect|link|reference/.test(lower)) {
20
+ return 'relationship';
21
+ }
22
+ return null;
23
+ }
24
+ export function calculateLinkBoost(memoryId, db) {
25
+ try {
26
+ const linked = db.prepare(`
27
+ SELECT m.salience, ml.strength
28
+ FROM memory_links ml
29
+ JOIN memories m ON (m.id = ml.target_id OR m.id = ml.source_id)
30
+ WHERE (ml.source_id = ? OR ml.target_id = ?)
31
+ AND m.id != ?
32
+ `).all(memoryId, memoryId, memoryId);
33
+ if (linked.length === 0)
34
+ return 0;
35
+ const totalWeight = linked.reduce((sum, link) => sum + link.strength, 0);
36
+ if (totalWeight === 0)
37
+ return 0;
38
+ const weightedSalience = linked.reduce((sum, link) => sum + link.salience * link.strength, 0) / totalWeight;
39
+ return Math.min(0.15, weightedSalience * 0.2);
40
+ }
41
+ catch {
42
+ return 0;
43
+ }
44
+ }
45
+ export function calculateTagScore(queryTags, memoryTags) {
46
+ if (queryTags.length === 0 || memoryTags.length === 0)
47
+ return 0;
48
+ let matches = 0;
49
+ for (const queryTag of queryTags) {
50
+ const lowerQueryTag = queryTag.toLowerCase();
51
+ if (memoryTags.some((memoryTag) => {
52
+ const lowerMemoryTag = memoryTag.toLowerCase();
53
+ return lowerMemoryTag.includes(lowerQueryTag) || lowerQueryTag.includes(lowerMemoryTag);
54
+ })) {
55
+ matches++;
56
+ }
57
+ }
58
+ return (matches / queryTags.length) * 0.1;
59
+ }
60
+ export function extractQueryTags(query) {
61
+ const words = query.toLowerCase().split(/\s+/);
62
+ return words.filter((word) => word.length > 2 &&
63
+ /^[a-z][a-z0-9-]*$/.test(word) &&
64
+ !['the', 'and', 'for', 'with', 'how', 'what', 'when', 'where', 'why'].includes(word));
65
+ }
66
+ export function vectorSearch(db, rowToMemory, queryEmbedding, limit, project, includeGlobal = true) {
67
+ let query = `
68
+ SELECT * FROM memories
69
+ WHERE embedding IS NOT NULL
70
+ `;
71
+ const params = [];
72
+ if (project && includeGlobal) {
73
+ query += ` AND (project = ? OR scope = 'global')`;
74
+ params.push(project);
75
+ }
76
+ else if (project) {
77
+ query += ` AND project = ?`;
78
+ params.push(project);
79
+ }
80
+ const rows = db.prepare(query).all(...params);
81
+ return rows
82
+ .map((row) => {
83
+ const embeddingBuffer = row.embedding;
84
+ const embedding = new Float32Array(embeddingBuffer.buffer, embeddingBuffer.byteOffset, embeddingBuffer.length / 4);
85
+ return {
86
+ memory: rowToMemory(row),
87
+ similarity: cosineSimilarity(queryEmbedding, embedding),
88
+ };
89
+ })
90
+ .filter((result) => result.similarity > 0.3)
91
+ .sort((a, b) => b.similarity - a.similarity)
92
+ .slice(0, limit);
93
+ }
94
+ export function buildSearchExplanation(memory, context, values) {
95
+ const matchedTags = context.queryTags.filter((queryTag) => memory.tags.some((memoryTag) => {
96
+ const lowerMemoryTag = memoryTag.toLowerCase();
97
+ return lowerMemoryTag.includes(queryTag) || queryTag.includes(lowerMemoryTag);
98
+ }));
99
+ const reasons = [];
100
+ if (values.vectorSimilarity > 0) {
101
+ reasons.push(`Semantic similarity ${(values.vectorSimilarity * 100).toFixed(0)}%`);
102
+ }
103
+ if (values.ftsScore > 0.3) {
104
+ reasons.push('Strong keyword match');
105
+ }
106
+ if (values.categoryBoost > 0 && context.detectedCategory) {
107
+ reasons.push(`Matches ${context.detectedCategory} category intent`);
108
+ }
109
+ if (matchedTags.length > 0) {
110
+ reasons.push(`Shared tags: ${matchedTags.slice(0, 3).join(', ')}`);
111
+ }
112
+ if (values.recencyBoost > 0) {
113
+ reasons.push('Recently accessed');
114
+ }
115
+ if (values.linkBoost > 0) {
116
+ reasons.push('Connected to related memories');
117
+ }
118
+ if (values.activationBoost > 0) {
119
+ reasons.push('Activated by recent recall activity');
120
+ }
121
+ if (reasons.length === 0) {
122
+ reasons.push('Ranked by salience and base recall heuristics');
123
+ }
124
+ return {
125
+ query: context.query,
126
+ reasons,
127
+ breakdown: {
128
+ ftsScore: values.ftsScore,
129
+ vectorSimilarity: values.vectorSimilarity,
130
+ vectorBoost: values.vectorBoost,
131
+ decayedScore: values.decayedScore,
132
+ priorityBoost: values.priorityBoost,
133
+ recencyBoost: values.recencyBoost,
134
+ categoryBoost: values.categoryBoost,
135
+ linkBoost: values.linkBoost,
136
+ tagBoost: values.tagBoost,
137
+ activationBoost: values.activationBoost,
138
+ finalScore: values.finalScore,
139
+ matchedTags,
140
+ matchedCategory: values.categoryBoost > 0 ? context.detectedCategory : null,
141
+ },
142
+ };
143
+ }
@@ -23,6 +23,7 @@ import { scoreSource } from '../defence/trust/source-scorer.js';
23
23
  import { logAudit } from '../defence/audit/logger.js';
24
24
  import { dispatchWebhook } from '../events/webhooks.js';
25
25
  import { getCachedQueryEmbedding, findSimilarMemories } from './embedding.js';
26
+ import { buildSearchExplanation, calculateLinkBoost, calculateTagScore, detectQueryCategory, extractQueryTags, vectorSearch, } from './search.js';
26
27
  // Anti-bloat: Maximum content size per memory (10KB)
27
28
  const MAX_CONTENT_SIZE = 10 * 1024;
28
29
  // Track truncation info globally for the last addMemory call
@@ -776,171 +777,6 @@ export function updateDecayScores() {
776
777
  }
777
778
  return updated;
778
779
  }
779
- /**
780
- * Detect the likely category a query is asking about
781
- */
782
- function detectQueryCategory(query) {
783
- const lower = query.toLowerCase();
784
- if (/architect|design|structure|pattern|system|schema|model/.test(lower)) {
785
- return 'architecture';
786
- }
787
- if (/error|bug|fix|issue|crash|exception|fail|problem/.test(lower)) {
788
- return 'error';
789
- }
790
- if (/prefer|always|never|style|convention|like|want/.test(lower)) {
791
- return 'preference';
792
- }
793
- if (/learn|discover|realiz|found\s+out|turns?\s+out/.test(lower)) {
794
- return 'learning';
795
- }
796
- if (/todo|task|pending|need\s+to|should\s+do/.test(lower)) {
797
- return 'todo';
798
- }
799
- if (/relation|depend|connect|link|reference/.test(lower)) {
800
- return 'relationship';
801
- }
802
- return null;
803
- }
804
- /**
805
- * Calculate a boost for memories linked to high-salience memories
806
- */
807
- function calculateLinkBoost(memoryId, db) {
808
- try {
809
- // Get linked memories and their salience
810
- const linked = db.prepare(`
811
- SELECT m.salience, ml.strength
812
- FROM memory_links ml
813
- JOIN memories m ON (m.id = ml.target_id OR m.id = ml.source_id)
814
- WHERE (ml.source_id = ? OR ml.target_id = ?)
815
- AND m.id != ?
816
- `).all(memoryId, memoryId, memoryId);
817
- if (linked.length === 0)
818
- return 0;
819
- // Calculate weighted average of linked memory salience
820
- const totalWeight = linked.reduce((sum, l) => sum + l.strength, 0);
821
- if (totalWeight === 0)
822
- return 0;
823
- const weightedSalience = linked.reduce((sum, l) => sum + l.salience * l.strength, 0) / totalWeight;
824
- // Cap boost at 0.15
825
- return Math.min(0.15, weightedSalience * 0.2);
826
- }
827
- catch {
828
- return 0;
829
- }
830
- }
831
- /**
832
- * Calculate partial tag match score
833
- */
834
- function calculateTagScore(queryTags, memoryTags) {
835
- if (queryTags.length === 0 || memoryTags.length === 0)
836
- return 0;
837
- // Count partial matches (substring matching)
838
- let matches = 0;
839
- for (const qt of queryTags) {
840
- const qtLower = qt.toLowerCase();
841
- if (memoryTags.some(mt => mt.toLowerCase().includes(qtLower) || qtLower.includes(mt.toLowerCase()))) {
842
- matches++;
843
- }
844
- }
845
- return (matches / queryTags.length) * 0.1;
846
- }
847
- /**
848
- * Extract potential tags from a query string
849
- */
850
- function extractQueryTags(query) {
851
- // Extract words that might be tags (tech terms, project-specific terms)
852
- const words = query.toLowerCase().split(/\s+/);
853
- return words.filter(w => w.length > 2 &&
854
- /^[a-z][a-z0-9-]*$/.test(w) &&
855
- !['the', 'and', 'for', 'with', 'how', 'what', 'when', 'where', 'why'].includes(w));
856
- }
857
- /**
858
- * Search memories by vector similarity
859
- * Returns memories sorted by cosine similarity to the query embedding
860
- */
861
- function vectorSearch(queryEmbedding, limit, project, includeGlobal = true) {
862
- const db = getDatabase();
863
- // Get memories with embeddings
864
- let query = `
865
- SELECT * FROM memories
866
- WHERE embedding IS NOT NULL
867
- `;
868
- const params = [];
869
- if (project && includeGlobal) {
870
- query += ` AND (project = ? OR scope = 'global')`;
871
- params.push(project);
872
- }
873
- else if (project) {
874
- query += ` AND project = ?`;
875
- params.push(project);
876
- }
877
- const rows = db.prepare(query).all(...params);
878
- // Calculate similarities
879
- const results = rows
880
- .map(row => {
881
- const embeddingBuffer = row.embedding;
882
- const embedding = new Float32Array(embeddingBuffer.buffer, embeddingBuffer.byteOffset, embeddingBuffer.length / 4);
883
- const similarity = cosineSimilarity(queryEmbedding, embedding);
884
- return {
885
- memory: rowToMemory(row),
886
- similarity,
887
- };
888
- })
889
- .filter(r => r.similarity > 0.3) // Threshold for relevance
890
- .sort((a, b) => b.similarity - a.similarity)
891
- .slice(0, limit);
892
- return results;
893
- }
894
- function buildSearchExplanation(memory, context, values) {
895
- const matchedTags = context.queryTags.filter((queryTag) => memory.tags.some((memoryTag) => {
896
- const lowerMemoryTag = memoryTag.toLowerCase();
897
- return lowerMemoryTag.includes(queryTag) || queryTag.includes(lowerMemoryTag);
898
- }));
899
- const reasons = [];
900
- if (values.vectorSimilarity > 0) {
901
- reasons.push(`Semantic similarity ${(values.vectorSimilarity * 100).toFixed(0)}%`);
902
- }
903
- if (values.ftsScore > 0.3) {
904
- reasons.push('Strong keyword match');
905
- }
906
- if (values.categoryBoost > 0 && context.detectedCategory) {
907
- reasons.push(`Matches ${context.detectedCategory} category intent`);
908
- }
909
- if (matchedTags.length > 0) {
910
- reasons.push(`Shared tags: ${matchedTags.slice(0, 3).join(', ')}`);
911
- }
912
- if (values.recencyBoost > 0) {
913
- reasons.push('Recently accessed');
914
- }
915
- if (values.linkBoost > 0) {
916
- reasons.push('Connected to related memories');
917
- }
918
- if (values.activationBoost > 0) {
919
- reasons.push('Activated by recent recall activity');
920
- }
921
- if (reasons.length === 0) {
922
- reasons.push('Ranked by salience and base recall heuristics');
923
- }
924
- return {
925
- query: context.query,
926
- reasons,
927
- breakdown: {
928
- ftsScore: values.ftsScore,
929
- vectorSimilarity: values.vectorSimilarity,
930
- vectorBoost: values.vectorBoost,
931
- decayedScore: values.decayedScore,
932
- priorityBoost: values.priorityBoost,
933
- recencyBoost: values.recencyBoost,
934
- categoryBoost: values.categoryBoost,
935
- linkBoost: values.linkBoost,
936
- tagBoost: values.tagBoost,
937
- activationBoost: values.activationBoost,
938
- finalScore: values.finalScore,
939
- matchedTags,
940
- matchedCategory: values.categoryBoost > 0 ? context.detectedCategory : null,
941
- },
942
- };
943
- }
944
780
  async function searchMemoriesInternal(options, config, source, execution) {
945
781
  if (++searchCount % 100 === 0) {
946
782
  pruneActivationCache();
@@ -958,7 +794,7 @@ async function searchMemoriesInternal(options, config, source, execution) {
958
794
  if (!queryEmbedding) {
959
795
  throw new Error('query embedding unavailable');
960
796
  }
961
- const vectorHits = vectorSearch(queryEmbedding, limit * 2, options.project, includeGlobal);
797
+ const vectorHits = vectorSearch(db, rowToMemory, queryEmbedding, limit * 2, options.project, includeGlobal);
962
798
  for (const hit of vectorHits) {
963
799
  vectorResults.set(hit.memory.id, hit.similarity);
964
800
  }
@@ -32,8 +32,8 @@ export declare const forgetSchema: z.ZodObject<{
32
32
  } | undefined;
33
33
  project?: string | undefined;
34
34
  id?: number | undefined;
35
- category?: "architecture" | "pattern" | "preference" | "error" | "context" | "learning" | "todo" | "note" | "relationship" | "custom" | undefined;
36
35
  query?: string | undefined;
36
+ category?: "architecture" | "pattern" | "preference" | "error" | "context" | "learning" | "todo" | "note" | "relationship" | "custom" | undefined;
37
37
  olderThan?: number | undefined;
38
38
  belowSalience?: number | undefined;
39
39
  }, {
@@ -43,8 +43,8 @@ export declare const forgetSchema: z.ZodObject<{
43
43
  } | undefined;
44
44
  project?: string | undefined;
45
45
  id?: number | undefined;
46
- category?: "architecture" | "pattern" | "preference" | "error" | "context" | "learning" | "todo" | "note" | "relationship" | "custom" | undefined;
47
46
  query?: string | undefined;
47
+ category?: "architecture" | "pattern" | "preference" | "error" | "context" | "learning" | "todo" | "note" | "relationship" | "custom" | undefined;
48
48
  olderThan?: number | undefined;
49
49
  belowSalience?: number | undefined;
50
50
  dryRun?: boolean | undefined;
@@ -37,9 +37,9 @@ export declare const recallSchema: z.ZodObject<{
37
37
  } | undefined;
38
38
  project?: string | undefined;
39
39
  type?: "short_term" | "long_term" | "episodic" | undefined;
40
+ query?: string | undefined;
40
41
  category?: "architecture" | "pattern" | "preference" | "error" | "context" | "learning" | "todo" | "note" | "relationship" | "custom" | undefined;
41
42
  tags?: string[] | undefined;
42
- query?: string | undefined;
43
43
  }, {
44
44
  source?: {
45
45
  type: "api" | "user" | "cli" | "hook" | "email" | "web" | "agent" | "file" | "tool_response";
@@ -49,9 +49,9 @@ export declare const recallSchema: z.ZodObject<{
49
49
  mode?: "search" | "important" | "recent" | undefined;
50
50
  type?: "short_term" | "long_term" | "episodic" | undefined;
51
51
  limit?: number | undefined;
52
+ query?: string | undefined;
52
53
  category?: "architecture" | "pattern" | "preference" | "error" | "context" | "learning" | "todo" | "note" | "relationship" | "custom" | undefined;
53
54
  tags?: string[] | undefined;
54
- query?: string | undefined;
55
55
  includeDecayed?: boolean | undefined;
56
56
  includeGlobal?: boolean | undefined;
57
57
  }>;
@@ -6,158 +6,22 @@
6
6
  * - Context injection on agent bootstrap
7
7
  * - Keyword-triggered memory saves
8
8
  */
9
- import { execFile } from "node:child_process";
10
9
  import { createHash } from "node:crypto";
11
10
  import fs from "node:fs/promises";
12
11
  import { homedir } from "node:os";
13
12
  import path from "node:path";
13
+ import { createOpenClawRuntime } from "./runtime.mjs";
14
14
 
15
15
  // ==================== SERVER COMMAND RESOLUTION ====================
16
16
 
17
- let _shieldConfig = null;
18
17
  let _autoMemoryNoticeShown = false;
19
-
20
- async function loadShieldConfig() {
21
- if (_shieldConfig) return _shieldConfig;
22
-
23
- try {
24
- const configPath = path.join(homedir(), ".shieldcortex", "config.json");
25
- _shieldConfig = JSON.parse(await fs.readFile(configPath, "utf-8"));
26
- } catch {
27
- _shieldConfig = {};
28
- }
29
-
30
- return _shieldConfig;
31
- }
18
+ const runtime = createOpenClawRuntime({ logPrefix: "[cortex-memory]" });
19
+ const loadShieldConfig = runtime.loadShieldConfig;
20
+ const callCortex = runtime.callCortex;
32
21
 
33
22
  async function isOpenClawAutoMemoryEnabled() {
34
23
  const config = await loadShieldConfig();
35
- // Default OFF — OpenClaw has its own memory system. User must explicitly opt in.
36
- return config?.openclawAutoMemory === true;
37
- }
38
-
39
- /**
40
- * Resolve the fastest way to invoke shieldcortex:
41
- * 1. ~/.shieldcortex/config.json "binaryPath" override
42
- * 2. Global install detected via `which shieldcortex`
43
- * 3. Fallback to `npx -y shieldcortex` (slow on ARM64)
44
- */
45
- let _resolvedServerCmd = null;
46
-
47
- async function resolveServerCmd() {
48
- if (_resolvedServerCmd) return _resolvedServerCmd;
49
-
50
- // 1. Check config for explicit binaryPath
51
- try {
52
- const config = await loadShieldConfig();
53
- if (config?.binaryPath) {
54
- await fs.access(config.binaryPath);
55
- _resolvedServerCmd = config.binaryPath;
56
- console.log(`[cortex-memory] Using configured binary: ${config.binaryPath}`);
57
- return _resolvedServerCmd;
58
- }
59
- } catch { /* Config not found, no binaryPath, or path invalid */ }
60
-
61
- // 2. Try to find global install via `which`
62
- try {
63
- const { execFileSync } = await import("node:child_process");
64
- const bin = execFileSync("which", ["shieldcortex"], {
65
- encoding: "utf-8",
66
- timeout: 3000,
67
- }).trim();
68
- if (bin) {
69
- _resolvedServerCmd = bin;
70
- console.log(`[cortex-memory] Using global install: ${bin}`);
71
- return _resolvedServerCmd;
72
- }
73
- } catch { /* Not in PATH */ }
74
-
75
- // 3. Fall back to npx (slow but always works)
76
- _resolvedServerCmd = "npx -y shieldcortex";
77
- console.log("[cortex-memory] Falling back to npx -y shieldcortex (slow path)");
78
- return _resolvedServerCmd;
79
- }
80
-
81
- // ==================== CORTEX MCP HELPER ====================
82
-
83
- let _lastCallErrorType = null;
84
-
85
- function classifyCallError(err) {
86
- if (err.killed || err.code === "ETIMEDOUT" || err.signal === "SIGTERM") return "timeout";
87
- if (/ENOENT|not found|command not found/i.test(err.message || "")) return "not-found";
88
- if (/mcporter/i.test(err.message || "")) return "mcporter";
89
- return "unknown";
90
- }
91
-
92
- /**
93
- * Call a ShieldCortex MCP tool via mcporter
94
- * @param {string} tool - Tool name (e.g., "remember", "recall", "get_context")
95
- * @param {Record<string, string>} args - Tool arguments as key:value pairs
96
- * @param {object} options - Options { retries: number, timeout: number }
97
- * @returns {Promise<string|null>} Raw stdout or null on error
98
- */
99
- async function callCortex(tool, args = {}, options = { retries: 1, timeout: 15000 }) {
100
- const serverCmd = await resolveServerCmd();
101
-
102
- return new Promise((resolve) => {
103
- const cmdArgs = [
104
- "mcporter", "call", "--stdio",
105
- serverCmd,
106
- tool,
107
- ];
108
- for (const [key, value] of Object.entries(args)) {
109
- // Escape single quotes by doubling them (FTS5-safe)
110
- const safe = String(value).replace(/'/g, "''");
111
- cmdArgs.push(`${key}:${safe}`);
112
- }
113
-
114
- let attempts = 0;
115
- const maxAttempts = (options.retries || 0) + 1;
116
-
117
- function attempt() {
118
- attempts++;
119
- execFile("npx", cmdArgs, {
120
- timeout: options.timeout || 15000,
121
- maxBuffer: 1024 * 256,
122
- }, (err, stdout) => {
123
- if (err) {
124
- // Distinguish timeout from other errors
125
- const isTimeout = err.killed || err.code === "ETIMEDOUT" || err.signal === "SIGTERM";
126
- const errorType = isTimeout ? "TIMEOUT" : "ERROR";
127
-
128
- if (attempts < maxAttempts) {
129
- console.warn(`[cortex-memory] ${errorType} on ${tool} (attempt ${attempts}/${maxAttempts}), retrying...`);
130
- setTimeout(attempt, 1000); // 1s delay before retry
131
- return;
132
- }
133
-
134
- // One-time categorised warning per error type (prevents spam)
135
- const category = classifyCallError(err);
136
- if (category !== _lastCallErrorType) {
137
- _lastCallErrorType = category;
138
- switch (category) {
139
- case "timeout":
140
- console.warn("[cortex-memory] ShieldCortex call timed out (15s). Memory may be under heavy load.");
141
- break;
142
- case "not-found":
143
- console.warn("[cortex-memory] ShieldCortex binary not found. Run: npm install -g shieldcortex");
144
- break;
145
- case "mcporter":
146
- console.warn("[cortex-memory] mcporter failed to reach ShieldCortex MCP server. Is it configured?");
147
- break;
148
- default:
149
- console.warn(`[cortex-memory] ShieldCortex call failed: ${err.message}`);
150
- }
151
- }
152
- resolve(null);
153
- return;
154
- }
155
- resolve(stdout?.trim() || null);
156
- });
157
- }
158
-
159
- attempt();
160
- });
24
+ return runtime.isOpenClawAutoMemoryEnabled(config);
161
25
  }
162
26
 
163
27
  // ==================== NOVELTY / DEDUPE GATE ====================