@monoes/monomindcli 1.15.5 → 1.15.7

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 (168) hide show
  1. package/.claude/agents/github/repo-architect.md +1 -1
  2. package/.claude/agents/specialists/integration-architect.md +6 -6
  3. package/.claude/commands/hive-mind/hive-mind-init.md +1 -1
  4. package/.claude/commands/hive-mind/hive-mind-memory.md +1 -1
  5. package/.claude/commands/mastermind/brain.md +11 -11
  6. package/.claude/commands/mastermind/master.md +4 -4
  7. package/.claude/commands/mastermind/memory.md +6 -6
  8. package/.claude/commands/memory/README.md +4 -4
  9. package/.claude/commands/truth/start.md +3 -3
  10. package/.claude/helpers/extras-registry.json +2 -2
  11. package/.claude/helpers/skill-registry.json +26 -26
  12. package/.claude/helpers/statusline.cjs +8 -8
  13. package/.claude/skills/agentic-jujutsu/SKILL.md +3 -3
  14. package/.claude/skills/mastermind/_protocol.md +8 -8
  15. package/README.md +6 -6
  16. package/dist/src/__tests__/browse-analyzer.test.js +18 -1
  17. package/dist/src/__tests__/browse-analyzer.test.js.map +1 -1
  18. package/dist/src/commands/agent.js +2 -2
  19. package/dist/src/commands/agent.js.map +1 -1
  20. package/dist/src/commands/autopilot.js +1 -1
  21. package/dist/src/commands/autopilot.js.map +1 -1
  22. package/dist/src/commands/completions.d.ts.map +1 -1
  23. package/dist/src/commands/completions.js +2 -21
  24. package/dist/src/commands/completions.js.map +1 -1
  25. package/dist/src/commands/config.js +1 -1
  26. package/dist/src/commands/hive-mind.js +1 -1
  27. package/dist/src/commands/hooks-coverage-commands.js +31 -31
  28. package/dist/src/commands/hooks-coverage-commands.js.map +1 -1
  29. package/dist/src/commands/hooks-routing-commands.js +1 -1
  30. package/dist/src/commands/hooks-routing-commands.js.map +1 -1
  31. package/dist/src/commands/hooks.js +1 -1
  32. package/dist/src/commands/hooks.js.map +1 -1
  33. package/dist/src/commands/index.d.ts +0 -1
  34. package/dist/src/commands/index.d.ts.map +1 -1
  35. package/dist/src/commands/index.js +0 -4
  36. package/dist/src/commands/index.js.map +1 -1
  37. package/dist/src/commands/init.js +8 -8
  38. package/dist/src/commands/init.js.map +1 -1
  39. package/dist/src/commands/memory.d.ts +1 -1
  40. package/dist/src/commands/memory.js +25 -25
  41. package/dist/src/commands/memory.js.map +1 -1
  42. package/dist/src/commands/migrate.js +2 -2
  43. package/dist/src/commands/neural.js +1 -1
  44. package/dist/src/commands/neural.js.map +1 -1
  45. package/dist/src/commands/swarm.js +1 -1
  46. package/dist/src/commands/swarm.js.map +1 -1
  47. package/dist/src/config-adapter.d.ts.map +1 -1
  48. package/dist/src/config-adapter.js +8 -8
  49. package/dist/src/config-adapter.js.map +1 -1
  50. package/dist/src/index.js +1 -1
  51. package/dist/src/index.js.map +1 -1
  52. package/dist/src/init/claudemd-generator.js +2 -2
  53. package/dist/src/init/executor.js +16 -16
  54. package/dist/src/init/executor.js.map +1 -1
  55. package/dist/src/init/shared-instructions-generator.d.ts +1 -1
  56. package/dist/src/init/shared-instructions-generator.js +1 -1
  57. package/dist/src/init/statusline-generator.d.ts +1 -1
  58. package/dist/src/init/statusline-generator.js +8 -8
  59. package/dist/src/init/types.d.ts +3 -3
  60. package/dist/src/init/types.d.ts.map +1 -1
  61. package/dist/src/init/types.js +3 -3
  62. package/dist/src/init/types.js.map +1 -1
  63. package/dist/src/mcp-client.d.ts.map +1 -1
  64. package/dist/src/mcp-client.js +4 -11
  65. package/dist/src/mcp-client.js.map +1 -1
  66. package/dist/src/mcp-tools/autopilot-tools.js +3 -3
  67. package/dist/src/mcp-tools/autopilot-tools.js.map +1 -1
  68. package/dist/src/mcp-tools/coherence-tools.d.ts +2 -2
  69. package/dist/src/mcp-tools/coherence-tools.js +2 -2
  70. package/dist/src/mcp-tools/daa-tools.js +13 -13
  71. package/dist/src/mcp-tools/daa-tools.js.map +1 -1
  72. package/dist/src/mcp-tools/guidance-tools.js +4 -4
  73. package/dist/src/mcp-tools/guidance-tools.js.map +1 -1
  74. package/dist/src/mcp-tools/hive-mind-tools.js +4 -4
  75. package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -1
  76. package/dist/src/mcp-tools/hooks-intelligence.d.ts.map +1 -1
  77. package/dist/src/mcp-tools/hooks-intelligence.js +1 -0
  78. package/dist/src/mcp-tools/hooks-intelligence.js.map +1 -1
  79. package/dist/src/mcp-tools/hooks-routing.js +23 -23
  80. package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
  81. package/dist/src/mcp-tools/index.d.ts +0 -1
  82. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  83. package/dist/src/mcp-tools/index.js +0 -2
  84. package/dist/src/mcp-tools/index.js.map +1 -1
  85. package/dist/src/mcp-tools/memory-tools.d.ts +22 -6
  86. package/dist/src/mcp-tools/memory-tools.d.ts.map +1 -1
  87. package/dist/src/mcp-tools/memory-tools.js +553 -505
  88. package/dist/src/mcp-tools/memory-tools.js.map +1 -1
  89. package/dist/src/mcp-tools/progress-tools.js +1 -1
  90. package/dist/src/mcp-tools/progress-tools.js.map +1 -1
  91. package/dist/src/mcp-tools/quality-tools.d.ts +2 -2
  92. package/dist/src/mcp-tools/quality-tools.js +3 -3
  93. package/dist/src/mcp-tools/quality-tools.js.map +1 -1
  94. package/dist/src/mcp-tools/system-tools.js +5 -5
  95. package/dist/src/mcp-tools/system-tools.js.map +1 -1
  96. package/dist/src/mcp-tools/transfer-tools.d.ts +1 -1
  97. package/dist/src/mcp-tools/transfer-tools.d.ts.map +1 -1
  98. package/dist/src/mcp-tools/transfer-tools.js +1 -156
  99. package/dist/src/mcp-tools/transfer-tools.js.map +1 -1
  100. package/dist/src/memory/embedding-operations.js +3 -3
  101. package/dist/src/memory/embedding-operations.js.map +1 -1
  102. package/dist/src/memory/hnsw-operations.js +5 -5
  103. package/dist/src/memory/hnsw-operations.js.map +1 -1
  104. package/dist/src/memory/intelligence.js +2 -2
  105. package/dist/src/memory/intelligence.js.map +1 -1
  106. package/dist/src/memory/memory-bridge.d.ts +86 -234
  107. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  108. package/dist/src/memory/memory-bridge.js +455 -1702
  109. package/dist/src/memory/memory-bridge.js.map +1 -1
  110. package/dist/src/memory/memory-crud.js +3 -3
  111. package/dist/src/memory/memory-crud.js.map +1 -1
  112. package/dist/src/memory/memory-initializer.d.ts +1 -1
  113. package/dist/src/memory/memory-initializer.js +5 -5
  114. package/dist/src/memory/memory-initializer.js.map +1 -1
  115. package/dist/src/memory/memory-read.js +4 -4
  116. package/dist/src/memory/memory-read.js.map +1 -1
  117. package/dist/src/suggest.js +0 -1
  118. package/dist/src/suggest.js.map +1 -1
  119. package/dist/src/types.d.ts +1 -1
  120. package/dist/src/ui/dashboard.html +41 -5
  121. package/dist/src/ui/orgs.html +91 -5
  122. package/dist/src/ui/server.mjs +44 -0
  123. package/dist/src/update/validator.d.ts.map +1 -1
  124. package/dist/src/update/validator.js +1 -3
  125. package/dist/src/update/validator.js.map +1 -1
  126. package/dist/tsconfig.tsbuildinfo +1 -1
  127. package/package.json +4 -4
  128. package/dist/src/commands/plugins.d.ts +0 -11
  129. package/dist/src/commands/plugins.d.ts.map +0 -1
  130. package/dist/src/commands/plugins.js +0 -799
  131. package/dist/src/commands/plugins.js.map +0 -1
  132. package/dist/src/plugins/manager.d.ts +0 -133
  133. package/dist/src/plugins/manager.d.ts.map +0 -1
  134. package/dist/src/plugins/manager.js +0 -498
  135. package/dist/src/plugins/manager.js.map +0 -1
  136. package/dist/src/plugins/store/discovery.d.ts +0 -88
  137. package/dist/src/plugins/store/discovery.d.ts.map +0 -1
  138. package/dist/src/plugins/store/discovery.js +0 -770
  139. package/dist/src/plugins/store/discovery.js.map +0 -1
  140. package/dist/src/plugins/store/index.d.ts +0 -76
  141. package/dist/src/plugins/store/index.d.ts.map +0 -1
  142. package/dist/src/plugins/store/index.js +0 -141
  143. package/dist/src/plugins/store/index.js.map +0 -1
  144. package/dist/src/plugins/store/search.d.ts +0 -46
  145. package/dist/src/plugins/store/search.d.ts.map +0 -1
  146. package/dist/src/plugins/store/search.js +0 -231
  147. package/dist/src/plugins/store/search.js.map +0 -1
  148. package/dist/src/plugins/store/types.d.ts +0 -274
  149. package/dist/src/plugins/store/types.d.ts.map +0 -1
  150. package/dist/src/plugins/store/types.js +0 -7
  151. package/dist/src/plugins/store/types.js.map +0 -1
  152. package/dist/src/plugins/tests/demo-plugin-store.d.ts +0 -7
  153. package/dist/src/plugins/tests/demo-plugin-store.d.ts.map +0 -1
  154. package/dist/src/plugins/tests/demo-plugin-store.js +0 -126
  155. package/dist/src/plugins/tests/demo-plugin-store.js.map +0 -1
  156. package/dist/src/plugins/tests/standalone-test.d.ts +0 -12
  157. package/dist/src/plugins/tests/standalone-test.d.ts.map +0 -1
  158. package/dist/src/plugins/tests/standalone-test.js +0 -188
  159. package/dist/src/plugins/tests/standalone-test.js.map +0 -1
  160. package/dist/src/plugins/tests/test-plugin-store.d.ts +0 -7
  161. package/dist/src/plugins/tests/test-plugin-store.d.ts.map +0 -1
  162. package/dist/src/plugins/tests/test-plugin-store.js +0 -206
  163. package/dist/src/plugins/tests/test-plugin-store.js.map +0 -1
  164. package/dist/src/services/registry-api.d.ts +0 -58
  165. package/dist/src/services/registry-api.d.ts.map +0 -1
  166. package/dist/src/services/registry-api.js +0 -199
  167. package/dist/src/services/registry-api.js.map +0 -1
  168. package/scripts/publish-registry.ts +0 -341
@@ -1,548 +1,596 @@
1
1
  /**
2
- * Memory MCP Tools for CLI - V1 with sql.js/HNSW Backend
2
+ * Memory MCP Tools Phase 6 of ADR-053
3
3
  *
4
- * UPGRADED: Now uses the advanced sql.js + HNSW backend for:
5
- * - 150x-12,500x faster semantic search
6
- * - Vector embeddings with cosine similarity
7
- * - Persistent SQLite storage (WASM)
8
- * - Backward compatible with legacy JSON storage (auto-migrates)
4
+ * Exposes Memory backend operations as MCP tools.
5
+ * Provides direct access to ReasoningBank, CausalGraph, SkillLibrary,
6
+ * AttestationLog, and bridge health through the MCP protocol.
7
+ *
8
+ * Security: All handlers validate input types, enforce length bounds,
9
+ * and sanitize error messages before returning to MCP callers.
9
10
  *
10
11
  * @module v1/cli/mcp-tools/memory-tools
11
12
  */
12
- import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from 'fs';
13
- import { join } from 'path';
14
- import { getMonomindDataRoot } from './types.js';
15
- // Paths relative to the git-safe data root
16
- const MEMORY_SUBDIR = 'memory';
17
- const LEGACY_MEMORY_FILE = 'store.json';
18
- const MIGRATION_MARKER = '.migrated-to-sqlite';
19
- function getMemoryDir() {
20
- return join(getMonomindDataRoot(), MEMORY_SUBDIR);
13
+ // ===== Shared validation helpers =====
14
+ const MAX_STRING_LENGTH = 100_000; // 100KB max for any string input
15
+ const MAX_BATCH_SIZE = 500; // Max entries per batch operation
16
+ const MAX_TOP_K = 100; // Max results per query
17
+ // Reject NUL and C0 control chars except \t \n \r. NUL truncates strings at
18
+ // the C-API boundary in some bridge backends (key collision); ANSI/control
19
+ // chars enable terminal injection when values are echoed back; \r/\n in
20
+ // values fed to log files breaks log-line integrity.
21
+ const CONTROL_CHAR_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F]/;
22
+ function validateString(value, name, maxLen = MAX_STRING_LENGTH) {
23
+ if (typeof value !== 'string' || value.length === 0)
24
+ return null;
25
+ if (value.length > maxLen)
26
+ return null;
27
+ if (CONTROL_CHAR_RE.test(value))
28
+ return null;
29
+ return value;
21
30
  }
22
- function getLegacyPath() {
23
- return join(getMonomindDataRoot(), MEMORY_SUBDIR, LEGACY_MEMORY_FILE);
31
+ function validatePositiveInt(value, defaultVal, max) {
32
+ if (typeof value !== 'number' || !Number.isFinite(value))
33
+ return defaultVal;
34
+ const n = Math.floor(value);
35
+ return n > 0 ? Math.min(n, max) : defaultVal;
24
36
  }
25
- function getMigrationMarkerPath() {
26
- return join(getMonomindDataRoot(), MEMORY_SUBDIR, MIGRATION_MARKER);
37
+ function validateScore(value, defaultVal) {
38
+ if (typeof value !== 'number' || !Number.isFinite(value))
39
+ return defaultVal;
40
+ return Math.max(0, Math.min(1, value));
27
41
  }
28
- function ensureMemoryDir() {
29
- const dir = getMemoryDir();
30
- if (!existsSync(dir)) {
31
- mkdirSync(dir, { recursive: true });
42
+ function sanitizeError(error) {
43
+ if (error instanceof Error) {
44
+ // Strip filesystem paths from error messages — match path components even
45
+ // when no trailing slash (path at end of message before whitespace, colon, EOL)
46
+ return error.message
47
+ .replace(/\/[^\s:]+(\/|(?=\s|:|$))/g, '<path>/')
48
+ .substring(0, 500);
32
49
  }
50
+ return 'Internal error';
33
51
  }
34
- // D-2: Input bounds for memory parameters
35
- const MAX_KEY_LENGTH = 1024;
36
- const MAX_VALUE_SIZE = 1024 * 1024; // 1MB
37
- const MAX_QUERY_LENGTH = 4096;
38
- const MAX_NAMESPACE_LENGTH = 256;
39
- const MAX_AGENT_ID_LENGTH = 256;
40
- const MAX_TAGS_COUNT = 50;
41
- const MAX_TAG_LENGTH = 256;
42
- function validateMemoryInput(key, value, query) {
43
- if (key && key.length > MAX_KEY_LENGTH) {
44
- throw new Error(`Key exceeds maximum length of ${MAX_KEY_LENGTH} characters`);
45
- }
46
- if (value && value.length > MAX_VALUE_SIZE) {
47
- throw new Error(`Value exceeds maximum size of ${MAX_VALUE_SIZE} bytes`);
52
+ // Lazy-cached bridge module
53
+ let bridgeModule = null;
54
+ async function getBridge() {
55
+ if (!bridgeModule) {
56
+ bridgeModule = await import('../memory/memory-bridge.js');
48
57
  }
49
- if (query && query.length > MAX_QUERY_LENGTH) {
50
- throw new Error(`Query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`);
51
- }
52
- }
53
- /**
54
- * Check if legacy JSON store exists and needs migration
55
- */
56
- function hasLegacyStore() {
57
- const legacyPath = getLegacyPath();
58
- const migrationMarker = getMigrationMarkerPath();
59
- return existsSync(legacyPath) && !existsSync(migrationMarker);
58
+ return bridgeModule;
60
59
  }
61
- /**
62
- * Load legacy JSON store for migration
63
- */
64
- const MAX_LEGACY_STORE_BYTES = 50 * 1024 * 1024;
65
- function loadLegacyStore() {
66
- try {
67
- const path = getLegacyPath();
68
- if (existsSync(path)) {
69
- if (statSync(path).size > MAX_LEGACY_STORE_BYTES)
70
- return null;
71
- const data = readFileSync(path, 'utf-8');
72
- const parsed = JSON.parse(data);
73
- if (parsed && typeof parsed === 'object' && Object.prototype.hasOwnProperty.call(parsed, '__proto__'))
74
- return null;
75
- return parsed;
60
+ // ===== memory_health — Controller health check =====
61
+ export const memoryHealth = {
62
+ name: 'memory_health',
63
+ description: 'Get Memory backend health status including cache stats and attestation count',
64
+ inputSchema: {
65
+ type: 'object',
66
+ properties: {},
67
+ },
68
+ handler: async () => {
69
+ try {
70
+ const bridge = await getBridge();
71
+ const health = await bridge.bridgeHealthCheck();
72
+ if (!health)
73
+ return { available: false, error: 'Memory bridge not available' };
74
+ return health;
76
75
  }
77
- }
78
- catch {
79
- // Return null on error
80
- }
81
- return null;
82
- }
83
- /**
84
- * Mark migration as complete
85
- */
86
- function markMigrationComplete() {
87
- ensureMemoryDir();
88
- const dest = getMigrationMarkerPath();
89
- const tmp = dest + '.tmp';
90
- writeFileSync(tmp, JSON.stringify({
91
- migratedAt: new Date().toISOString(),
92
- version: '3.0.0',
93
- }), 'utf-8');
94
- renameSync(tmp, dest);
95
- }
96
- /**
97
- * Lazy-load memory initializer functions to avoid circular deps
98
- */
99
- async function getMemoryFunctions() {
100
- const { storeEntry, searchEntries, listEntries, getEntry, deleteEntry, initializeMemoryDatabase, checkMemoryInitialization, } = await import('../memory/memory-initializer.js');
101
- return {
102
- storeEntry,
103
- searchEntries,
104
- listEntries,
105
- getEntry,
106
- deleteEntry,
107
- initializeMemoryDatabase,
108
- checkMemoryInitialization,
109
- };
110
- }
111
- /**
112
- * Ensure memory database is initialized and migrate legacy data if needed
113
- */
114
- async function ensureInitialized() {
115
- const { initializeMemoryDatabase, checkMemoryInitialization, storeEntry } = await getMemoryFunctions();
116
- // Check if already initialized
117
- const status = await checkMemoryInitialization();
118
- if (!status.initialized) {
119
- await initializeMemoryDatabase({ force: false, verbose: false });
120
- }
121
- // Migrate legacy JSON data if exists
122
- if (hasLegacyStore()) {
123
- const legacyStore = loadLegacyStore();
124
- if (legacyStore && Object.keys(legacyStore.entries).length > 0) {
125
- console.error('[MCP Memory] Migrating legacy JSON store to sql.js...');
126
- let migrated = 0;
127
- for (const [key, entry] of Object.entries(legacyStore.entries)) {
128
- try {
129
- // Convert value to string for storage
130
- const value = typeof entry.value === 'string' ? entry.value : JSON.stringify(entry.value);
131
- await storeEntry({
132
- key,
133
- value,
134
- namespace: 'default',
135
- generateEmbeddingFlag: true,
136
- });
137
- migrated++;
138
- }
139
- catch (e) {
140
- console.error(`[MCP Memory] Failed to migrate key "${key}":`, e);
141
- }
142
- }
143
- console.error(`[MCP Memory] Migrated ${migrated}/${Object.keys(legacyStore.entries).length} entries`);
144
- markMigrationComplete();
76
+ catch (error) {
77
+ return { available: false, error: sanitizeError(error) };
145
78
  }
146
- }
147
- }
148
- export const memoryTools = [
149
- {
150
- name: 'memory_store',
151
- description: 'Store a value in memory with vector embedding for semantic search (sql.js + HNSW backend). Use upsert=true to update existing keys.',
152
- category: 'memory',
153
- inputSchema: {
154
- type: 'object',
155
- properties: {
156
- key: { type: 'string', description: 'Memory key (unique within namespace)' },
157
- value: { description: 'Value to store (string or object)' },
158
- namespace: { type: 'string', description: 'Namespace for organization (default: "default")' },
159
- tags: {
160
- type: 'array',
161
- items: { type: 'string' },
162
- description: 'Optional tags for filtering',
163
- },
164
- ttl: { type: 'number', description: 'Time-to-live in seconds (optional)' },
165
- upsert: { type: 'boolean', description: 'If true, update existing key instead of failing (default: false)' },
166
- },
167
- required: ['key', 'value'],
79
+ },
80
+ };
81
+ // ===== memory_controllers List all controllers =====
82
+ export const memoryControllers = {
83
+ name: 'memory_controllers',
84
+ description: 'List all Memory backends and their initialization status',
85
+ inputSchema: {
86
+ type: 'object',
87
+ properties: {},
88
+ },
89
+ handler: async () => {
90
+ try {
91
+ const bridge = await getBridge();
92
+ const controllers = await bridge.bridgeListControllers();
93
+ if (!controllers)
94
+ return { available: false, controllers: [], error: 'Memory bridge not available — @monomind/memory not installed or missing controller-registry. Use memory_store/memory_search tools instead.' };
95
+ return {
96
+ available: true,
97
+ controllers,
98
+ total: controllers.length,
99
+ active: controllers.filter((c) => c.enabled).length,
100
+ };
101
+ }
102
+ catch (error) {
103
+ return { available: false, error: sanitizeError(error) };
104
+ }
105
+ },
106
+ };
107
+ // ===== memory_pattern_store — Store via ReasoningBank =====
108
+ export const memoryPatternStore = {
109
+ name: 'memory_pattern-store',
110
+ description: 'Store a pattern directly via ReasoningBank controller',
111
+ inputSchema: {
112
+ type: 'object',
113
+ properties: {
114
+ pattern: { type: 'string', description: 'Pattern description' },
115
+ type: { type: 'string', description: 'Pattern type (e.g., task-routing, error-recovery)' },
116
+ confidence: { type: 'number', description: 'Confidence score (0-1)' },
168
117
  },
169
- handler: async (input) => {
170
- await ensureInitialized();
171
- const { storeEntry } = await getMemoryFunctions();
172
- const key = input.key;
173
- const rawNamespace = input.namespace || 'default';
174
- const namespace = typeof rawNamespace === 'string' && rawNamespace.length > MAX_NAMESPACE_LENGTH
175
- ? rawNamespace.slice(0, MAX_NAMESPACE_LENGTH) : rawNamespace;
176
- const rawValue = input.value;
177
- const value = typeof rawValue === 'string' ? rawValue : (rawValue !== undefined ? JSON.stringify(rawValue) : '');
178
- const rawTags = input.tags || [];
179
- // Cap tags count and individual tag length to prevent store inflation
180
- const tags = rawTags.slice(0, MAX_TAGS_COUNT).map(t => typeof t === 'string' && t.length > MAX_TAG_LENGTH ? t.slice(0, MAX_TAG_LENGTH) : t);
181
- const ttl = input.ttl;
182
- const upsert = input.upsert || false;
183
- if (!value) {
184
- return {
185
- success: false,
186
- key,
187
- stored: false,
188
- hasEmbedding: false,
189
- error: 'Value is required and cannot be empty',
190
- };
191
- }
192
- validateMemoryInput(key, value);
193
- const startTime = performance.now();
194
- try {
195
- const result = await storeEntry({
196
- key,
197
- value,
198
- namespace,
199
- generateEmbeddingFlag: true,
200
- tags,
201
- ttl,
202
- upsert,
203
- });
204
- const duration = performance.now() - startTime;
205
- return {
206
- success: result.success,
207
- key,
208
- namespace,
209
- stored: result.success,
210
- storedAt: new Date().toISOString(),
211
- hasEmbedding: !!result.embedding,
212
- embeddingDimensions: result.embedding?.dimensions || null,
213
- backend: 'sql.js + HNSW',
214
- storeTime: `${duration.toFixed(2)}ms`,
215
- error: result.error,
216
- };
217
- }
218
- catch (error) {
219
- return {
220
- success: false,
221
- key,
222
- error: error instanceof Error ? error.message : 'Unknown error',
223
- };
224
- }
118
+ required: ['pattern'],
119
+ },
120
+ handler: async (params) => {
121
+ try {
122
+ const pattern = validateString(params.pattern, 'pattern');
123
+ if (!pattern)
124
+ return { success: false, error: 'pattern is required (non-empty string, max 100KB)' };
125
+ const bridge = await getBridge();
126
+ const result = await bridge.bridgeStorePattern({
127
+ pattern,
128
+ type: validateString(params.type, 'type', 200) ?? 'general',
129
+ confidence: validateScore(params.confidence, 0.8),
130
+ });
131
+ return result ?? { success: false, error: 'Memory bridge not available. Use memory_store/memory_search instead.' };
132
+ }
133
+ catch (error) {
134
+ return { success: false, error: sanitizeError(error) };
135
+ }
136
+ },
137
+ };
138
+ // ===== memory_pattern_search Search via ReasoningBank =====
139
+ export const memoryPatternSearch = {
140
+ name: 'memory_pattern-search',
141
+ description: 'Search patterns via ReasoningBank controller with BM25+semantic hybrid',
142
+ inputSchema: {
143
+ type: 'object',
144
+ properties: {
145
+ query: { type: 'string', description: 'Search query' },
146
+ topK: { type: 'number', description: 'Number of results (default: 5)' },
147
+ minConfidence: { type: 'number', description: 'Minimum score threshold (0-1)' },
225
148
  },
149
+ required: ['query'],
226
150
  },
227
- {
228
- name: 'memory_retrieve',
229
- description: 'Retrieve a value from memory by key',
230
- category: 'memory',
231
- inputSchema: {
232
- type: 'object',
233
- properties: {
234
- key: { type: 'string', description: 'Memory key' },
235
- namespace: { type: 'string', description: 'Namespace (default: "default")' },
236
- agentId: { type: 'string', description: 'Caller agent ID (enables collaborative memory promotion)' },
237
- },
238
- required: ['key'],
151
+ handler: async (params) => {
152
+ try {
153
+ const query = validateString(params.query, 'query', 10_000);
154
+ if (!query)
155
+ return { results: [], error: 'query is required (non-empty string, max 10KB)' };
156
+ const bridge = await getBridge();
157
+ const result = await bridge.bridgeSearchPatterns({
158
+ query,
159
+ topK: validatePositiveInt(params.topK, 5, MAX_TOP_K),
160
+ minConfidence: validateScore(params.minConfidence, 0.3),
161
+ });
162
+ return result ?? { results: [], controller: 'unavailable' };
163
+ }
164
+ catch (error) {
165
+ return { results: [], error: sanitizeError(error) };
166
+ }
167
+ },
168
+ };
169
+ // ===== memory_feedback — Record task feedback =====
170
+ export const memoryFeedback = {
171
+ name: 'memory_feedback',
172
+ description: 'Record task feedback for learning via LearningSystem + ReasoningBank controllers',
173
+ inputSchema: {
174
+ type: 'object',
175
+ properties: {
176
+ taskId: { type: 'string', description: 'Task identifier' },
177
+ success: { type: 'boolean', description: 'Whether task succeeded' },
178
+ quality: { type: 'number', description: 'Quality score (0-1)' },
179
+ agent: { type: 'string', description: 'Agent that performed the task' },
239
180
  },
240
- handler: async (input) => {
241
- await ensureInitialized();
242
- const { getEntry } = await getMemoryFunctions();
243
- const key = input.key;
244
- const rawNs = input.namespace || 'default';
245
- const namespace = typeof rawNs === 'string' && rawNs.length > MAX_NAMESPACE_LENGTH
246
- ? rawNs.slice(0, MAX_NAMESPACE_LENGTH) : rawNs;
247
- const rawAgentId = input.agentId;
248
- const agentId = typeof rawAgentId === 'string' && rawAgentId.length > MAX_AGENT_ID_LENGTH
249
- ? rawAgentId.slice(0, MAX_AGENT_ID_LENGTH) : rawAgentId;
250
- validateMemoryInput(key);
251
- try {
252
- const result = await getEntry({ key, namespace, agentId });
253
- if (result.found && result.entry) {
254
- // Try to parse JSON value
255
- let value = result.entry.content;
256
- try {
257
- value = JSON.parse(result.entry.content);
258
- }
259
- catch {
260
- // Keep as string
261
- }
262
- return {
263
- key,
264
- namespace,
265
- value,
266
- tags: result.entry.tags,
267
- storedAt: result.entry.createdAt,
268
- updatedAt: result.entry.updatedAt,
269
- accessCount: result.entry.accessCount,
270
- hasEmbedding: result.entry.hasEmbedding,
271
- found: true,
272
- backend: 'sql.js + HNSW',
273
- };
274
- }
275
- return {
276
- key,
277
- namespace,
278
- value: null,
279
- found: false,
280
- };
281
- }
282
- catch (error) {
283
- return {
284
- key,
285
- namespace,
286
- value: null,
287
- found: false,
288
- error: error instanceof Error ? error.message : 'Unknown error',
289
- };
290
- }
181
+ required: ['taskId'],
182
+ },
183
+ handler: async (params) => {
184
+ try {
185
+ const taskId = validateString(params.taskId, 'taskId', 500);
186
+ if (!taskId)
187
+ return { success: false, error: 'taskId is required (non-empty string, max 500 chars)' };
188
+ const bridge = await getBridge();
189
+ const result = await bridge.bridgeRecordFeedback({
190
+ taskId,
191
+ success: params.success === true,
192
+ quality: validateScore(params.quality, 0.85),
193
+ agent: validateString(params.agent, 'agent', 200) ?? undefined,
194
+ });
195
+ return result ?? { success: false, error: 'Memory bridge not available. Use memory_store/memory_search instead.' };
196
+ }
197
+ catch (error) {
198
+ return { success: false, error: sanitizeError(error) };
199
+ }
200
+ },
201
+ };
202
+ // ===== memory_causal_edge — Record causal relationships =====
203
+ export const memoryCausalEdge = {
204
+ name: 'memory_causal-edge',
205
+ description: 'Record a causal edge between two memory entries via CausalMemoryGraph',
206
+ inputSchema: {
207
+ type: 'object',
208
+ properties: {
209
+ sourceId: { type: 'string', description: 'Source entry ID' },
210
+ targetId: { type: 'string', description: 'Target entry ID' },
211
+ relation: { type: 'string', description: 'Relationship type (e.g., caused, preceded, succeeded)' },
212
+ weight: { type: 'number', description: 'Edge weight (0-1)' },
291
213
  },
214
+ required: ['sourceId', 'targetId', 'relation'],
292
215
  },
293
- {
294
- name: 'memory_search',
295
- description: 'Semantic vector search using HNSW index (150x-12,500x faster than keyword search)',
296
- category: 'memory',
297
- inputSchema: {
298
- type: 'object',
299
- properties: {
300
- query: { type: 'string', description: 'Search query (semantic similarity)' },
301
- namespace: { type: 'string', description: 'Namespace to search (default: "default")' },
302
- limit: { type: 'number', description: 'Maximum results (default: 10)' },
303
- threshold: { type: 'number', description: 'Minimum similarity threshold 0-1 (default: 0.3)' },
304
- },
305
- required: ['query'],
216
+ handler: async (params) => {
217
+ try {
218
+ const sourceId = validateString(params.sourceId, 'sourceId', 500);
219
+ const targetId = validateString(params.targetId, 'targetId', 500);
220
+ const relation = validateString(params.relation, 'relation', 200);
221
+ if (!sourceId)
222
+ return { success: false, error: 'sourceId is required (non-empty string)' };
223
+ if (!targetId)
224
+ return { success: false, error: 'targetId is required (non-empty string)' };
225
+ if (!relation)
226
+ return { success: false, error: 'relation is required (non-empty string)' };
227
+ const bridge = await getBridge();
228
+ const result = await bridge.bridgeRecordCausalEdge({
229
+ sourceId,
230
+ targetId,
231
+ relation,
232
+ weight: typeof params.weight === 'number' ? validateScore(params.weight, 0.5) : undefined,
233
+ });
234
+ return result ?? { success: false, error: 'Memory bridge not available. Use memory_store/memory_search instead.' };
235
+ }
236
+ catch (error) {
237
+ return { success: false, error: sanitizeError(error) };
238
+ }
239
+ },
240
+ };
241
+ // ===== memory_route — Route via SemanticRouter =====
242
+ export const memoryRoute = {
243
+ name: 'memory_route',
244
+ description: 'Route a task via SemanticRouter or LearningSystem recommendAlgorithm',
245
+ inputSchema: {
246
+ type: 'object',
247
+ properties: {
248
+ task: { type: 'string', description: 'Task description to route' },
249
+ context: { type: 'string', description: 'Additional context' },
306
250
  },
307
- handler: async (input) => {
308
- await ensureInitialized();
309
- const { searchEntries } = await getMemoryFunctions();
310
- const query = input.query;
311
- const rawSearchNs = input.namespace || 'default';
312
- const namespace = typeof rawSearchNs === 'string' && rawSearchNs.length > MAX_NAMESPACE_LENGTH
313
- ? rawSearchNs.slice(0, MAX_NAMESPACE_LENGTH) : rawSearchNs;
314
- const limit = Math.min(Math.max(input.limit || 10, 1), 1000);
315
- const threshold = input.threshold || 0.3;
316
- validateMemoryInput(undefined, undefined, query);
317
- const startTime = performance.now();
318
- try {
319
- const result = await searchEntries({
320
- query,
321
- namespace,
322
- limit,
323
- threshold,
324
- });
325
- const duration = performance.now() - startTime;
326
- // Parse JSON values in results
327
- const results = result.results.map(r => {
328
- let value = r.content;
329
- try {
330
- value = JSON.parse(r.content);
331
- }
332
- catch {
333
- // Keep as string
334
- }
335
- return {
336
- key: r.key,
337
- namespace: r.namespace,
338
- value,
339
- similarity: r.score,
340
- };
341
- });
342
- return {
343
- query,
344
- results,
345
- total: results.length,
346
- searchTime: `${duration.toFixed(2)}ms`,
347
- backend: 'HNSW + sql.js',
348
- };
349
- }
350
- catch (error) {
351
- return {
352
- query,
353
- results: [],
354
- total: 0,
355
- error: error instanceof Error ? error.message : 'Unknown error',
356
- };
357
- }
251
+ required: ['task'],
252
+ },
253
+ handler: async (params) => {
254
+ try {
255
+ const task = validateString(params.task, 'task', 10_000);
256
+ if (!task)
257
+ return { route: 'general', confidence: 0.5, agents: ['coder'], controller: 'error', error: 'task is required (non-empty string)' };
258
+ const bridge = await getBridge();
259
+ const result = await bridge.bridgeRouteTask({
260
+ task,
261
+ context: validateString(params.context, 'context', 10_000) ?? undefined,
262
+ });
263
+ return result ?? { route: 'general', confidence: 0.5, agents: ['coder'], controller: 'fallback' };
264
+ }
265
+ catch (error) {
266
+ return { route: 'general', confidence: 0.5, agents: ['coder'], controller: 'error', error: sanitizeError(error) };
267
+ }
268
+ },
269
+ };
270
+ // ===== memory_session_start Session with ReflexionMemory =====
271
+ export const memorySessionStart = {
272
+ name: 'memory_session-start',
273
+ description: 'Start a session with ReflexionMemory episodic replay',
274
+ inputSchema: {
275
+ type: 'object',
276
+ properties: {
277
+ sessionId: { type: 'string', description: 'Session identifier' },
278
+ context: { type: 'string', description: 'Session context for pattern retrieval' },
279
+ },
280
+ required: ['sessionId'],
281
+ },
282
+ handler: async (params) => {
283
+ try {
284
+ const sessionId = validateString(params.sessionId, 'sessionId', 500);
285
+ if (!sessionId)
286
+ return { success: false, error: 'sessionId is required (non-empty string)' };
287
+ const bridge = await getBridge();
288
+ const result = await bridge.bridgeSessionStart({
289
+ sessionId,
290
+ context: validateString(params.context, 'context', 10_000) ?? undefined,
291
+ });
292
+ return result ?? { success: false, error: 'Memory bridge not available. Use memory_store/memory_search instead.' };
293
+ }
294
+ catch (error) {
295
+ return { success: false, error: sanitizeError(error) };
296
+ }
297
+ },
298
+ };
299
+ // ===== memory_session_end End session + NightlyLearner =====
300
+ export const memorySessionEnd = {
301
+ name: 'memory_session-end',
302
+ description: 'End session, persist to ReflexionMemory, trigger NightlyLearner consolidation',
303
+ inputSchema: {
304
+ type: 'object',
305
+ properties: {
306
+ sessionId: { type: 'string', description: 'Session identifier' },
307
+ summary: { type: 'string', description: 'Session summary' },
308
+ tasksCompleted: { type: 'number', description: 'Number of tasks completed' },
358
309
  },
310
+ required: ['sessionId'],
359
311
  },
360
- {
361
- name: 'memory_delete',
362
- description: 'Delete a memory entry by key',
363
- category: 'memory',
364
- inputSchema: {
365
- type: 'object',
366
- properties: {
367
- key: { type: 'string', description: 'Memory key' },
368
- namespace: { type: 'string', description: 'Namespace (default: "default")' },
312
+ handler: async (params) => {
313
+ try {
314
+ const sessionId = validateString(params.sessionId, 'sessionId', 500);
315
+ if (!sessionId)
316
+ return { success: false, error: 'sessionId is required (non-empty string)' };
317
+ const bridge = await getBridge();
318
+ const result = await bridge.bridgeSessionEnd({
319
+ sessionId,
320
+ summary: validateString(params.summary, 'summary', 50_000) ?? undefined,
321
+ tasksCompleted: validatePositiveInt(params.tasksCompleted, 0, 10_000),
322
+ });
323
+ return result ?? { success: false, error: 'Memory bridge not available. Use memory_store/memory_search instead.' };
324
+ }
325
+ catch (error) {
326
+ return { success: false, error: sanitizeError(error) };
327
+ }
328
+ },
329
+ };
330
+ // ===== memory_hierarchical_store — Store to hierarchical memory =====
331
+ export const memoryHierarchicalStore = {
332
+ name: 'memory_hierarchical-store',
333
+ description: 'Store to hierarchical memory with tier (working, episodic, semantic)',
334
+ inputSchema: {
335
+ type: 'object',
336
+ properties: {
337
+ key: { type: 'string', description: 'Memory entry key' },
338
+ value: { type: 'string', description: 'Memory entry value' },
339
+ tier: {
340
+ type: 'string',
341
+ description: 'Memory tier (working, episodic, semantic)',
342
+ enum: ['working', 'episodic', 'semantic'],
343
+ default: 'working',
369
344
  },
370
- required: ['key'],
371
345
  },
372
- handler: async (input) => {
373
- await ensureInitialized();
374
- const { deleteEntry } = await getMemoryFunctions();
375
- const key = input.key;
376
- const rawDelNs = input.namespace || 'default';
377
- const namespace = typeof rawDelNs === 'string' && rawDelNs.length > MAX_NAMESPACE_LENGTH
378
- ? rawDelNs.slice(0, MAX_NAMESPACE_LENGTH) : rawDelNs;
379
- validateMemoryInput(key);
380
- try {
381
- const result = await deleteEntry({ key, namespace });
382
- return {
383
- success: result.deleted,
384
- key,
385
- namespace,
386
- deleted: result.deleted,
387
- hnswIndexInvalidated: result.deleted,
388
- backend: 'sql.js + HNSW',
389
- };
346
+ required: ['key', 'value'],
347
+ },
348
+ handler: async (params) => {
349
+ try {
350
+ const key = validateString(params.key, 'key', 1000);
351
+ const value = validateString(params.value, 'value');
352
+ if (!key)
353
+ return { success: false, error: 'key is required (non-empty string, max 1KB)' };
354
+ if (!value)
355
+ return { success: false, error: 'value is required (non-empty string, max 100KB)' };
356
+ const tier = validateString(params.tier, 'tier', 20) ?? 'working';
357
+ if (!['working', 'episodic', 'semantic'].includes(tier)) {
358
+ return { success: false, error: `Invalid tier: ${tier}. Must be working, episodic, or semantic` };
390
359
  }
391
- catch (error) {
392
- return {
393
- success: false,
394
- key,
395
- namespace,
396
- deleted: false,
397
- error: error instanceof Error ? error.message : 'Unknown error',
398
- };
360
+ const bridge = await getBridge();
361
+ const result = await bridge.bridgeHierarchicalStore({ key, value, tier });
362
+ return result ?? { success: false, error: 'Memory bridge not available. Use memory_store/memory_search instead.' };
363
+ }
364
+ catch (error) {
365
+ return { success: false, error: sanitizeError(error) };
366
+ }
367
+ },
368
+ };
369
+ // ===== memory_hierarchical_recall — Recall from hierarchical memory =====
370
+ export const memoryHierarchicalRecall = {
371
+ name: 'memory_hierarchical-recall',
372
+ description: 'Recall from hierarchical memory with optional tier filter',
373
+ inputSchema: {
374
+ type: 'object',
375
+ properties: {
376
+ query: { type: 'string', description: 'Recall query' },
377
+ tier: { type: 'string', description: 'Filter by tier (working, episodic, semantic)' },
378
+ topK: { type: 'number', description: 'Number of results (default: 5)' },
379
+ },
380
+ required: ['query'],
381
+ },
382
+ handler: async (params) => {
383
+ try {
384
+ const query = validateString(params.query, 'query', 10_000);
385
+ if (!query)
386
+ return { results: [], error: 'query is required (non-empty string, max 10KB)' };
387
+ const tier = validateString(params.tier, 'tier', 20);
388
+ if (tier && !['working', 'episodic', 'semantic'].includes(tier)) {
389
+ return { results: [], error: `Invalid tier: ${tier}. Must be working, episodic, or semantic` };
399
390
  }
391
+ const bridge = await getBridge();
392
+ const result = await bridge.bridgeHierarchicalRecall({
393
+ query,
394
+ tier: tier ?? undefined,
395
+ topK: validatePositiveInt(params.topK, 5, MAX_TOP_K),
396
+ });
397
+ return result ?? { results: [], error: 'Memory bridge not available. Use memory_search instead.' };
398
+ }
399
+ catch (error) {
400
+ return { results: [], error: sanitizeError(error) };
401
+ }
402
+ },
403
+ };
404
+ // ===== memory_consolidate — Run memory consolidation =====
405
+ export const memoryConsolidate = {
406
+ name: 'memory_consolidate',
407
+ description: 'Run memory consolidation to promote entries across tiers and compress old data',
408
+ inputSchema: {
409
+ type: 'object',
410
+ properties: {
411
+ minAge: { type: 'number', description: 'Minimum age in hours since store (optional)' },
412
+ maxEntries: { type: 'number', description: 'Maximum entries to consolidate (optional)' },
400
413
  },
401
414
  },
402
- {
403
- name: 'memory_list',
404
- description: 'List memory entries with optional filtering',
405
- category: 'memory',
406
- inputSchema: {
407
- type: 'object',
408
- properties: {
409
- namespace: { type: 'string', description: 'Filter by namespace' },
410
- limit: { type: 'number', description: 'Maximum results (default: 50)' },
411
- offset: { type: 'number', description: 'Offset for pagination (default: 0)' },
415
+ handler: async (params) => {
416
+ try {
417
+ const bridge = await getBridge();
418
+ // Reject NaN and Infinity. typeof === 'number' returns true for both.
419
+ // NaN propagates through arithmetic and corrupts consolidation accounting;
420
+ // Infinity makes `entry.age >= minAge` always false, silently no-op.
421
+ const minAge = typeof params.minAge === 'number' && Number.isFinite(params.minAge)
422
+ ? Math.max(0, Math.min(params.minAge, 24 * 365 * 10))
423
+ : undefined;
424
+ const result = await bridge.bridgeConsolidate({
425
+ minAge,
426
+ maxEntries: params.maxEntries !== undefined
427
+ ? validatePositiveInt(params.maxEntries, 1000, 10_000)
428
+ : undefined,
429
+ });
430
+ return result ?? { success: false, error: 'Memory bridge not available. Use memory_store/memory_search instead.' };
431
+ }
432
+ catch (error) {
433
+ return { success: false, error: sanitizeError(error) };
434
+ }
435
+ },
436
+ };
437
+ // ===== memory_batch — Batch operations (insert, update, delete) =====
438
+ export const memoryBatch = {
439
+ name: 'memory_batch',
440
+ description: 'Batch operations on memory entries (insert, update, delete)',
441
+ inputSchema: {
442
+ type: 'object',
443
+ properties: {
444
+ operation: {
445
+ type: 'string',
446
+ description: 'Batch operation type',
447
+ enum: ['insert', 'update', 'delete'],
448
+ },
449
+ entries: {
450
+ type: 'array',
451
+ description: 'Array of {key, value} entries to operate on',
452
+ items: {
453
+ type: 'object',
454
+ properties: {
455
+ key: { type: 'string' },
456
+ value: { type: 'string' },
457
+ },
458
+ required: ['key'],
459
+ },
412
460
  },
413
461
  },
414
- handler: async (input) => {
415
- await ensureInitialized();
416
- const { listEntries } = await getMemoryFunctions();
417
- const rawListNs = input.namespace;
418
- const namespace = typeof rawListNs === 'string' && rawListNs.length > MAX_NAMESPACE_LENGTH
419
- ? rawListNs.slice(0, MAX_NAMESPACE_LENGTH) : rawListNs;
420
- const limit = Math.min(Math.max(input.limit || 50, 1), 1000);
421
- const offset = input.offset || 0;
422
- try {
423
- const result = await listEntries({
424
- namespace,
425
- limit,
426
- offset,
427
- });
428
- const entries = result.entries.map(e => ({
429
- key: e.key,
430
- namespace: e.namespace,
431
- storedAt: e.createdAt,
432
- updatedAt: e.updatedAt,
433
- accessCount: e.accessCount,
434
- hasEmbedding: e.hasEmbedding,
435
- size: e.size,
436
- }));
437
- return {
438
- entries,
439
- total: result.total,
440
- limit,
441
- offset,
442
- backend: 'sql.js + HNSW',
443
- };
462
+ required: ['operation', 'entries'],
463
+ },
464
+ handler: async (params) => {
465
+ try {
466
+ const operation = validateString(params.operation, 'operation', 20);
467
+ if (!operation)
468
+ return { success: false, error: 'operation is required (string)' };
469
+ if (!['insert', 'update', 'delete'].includes(operation)) {
470
+ return { success: false, error: `Invalid operation: ${operation}. Must be insert, update, or delete` };
444
471
  }
445
- catch (error) {
446
- return {
447
- entries: [],
448
- total: 0,
449
- limit,
450
- offset,
451
- error: error instanceof Error ? error.message : 'Unknown error',
452
- };
472
+ if (!Array.isArray(params.entries) || params.entries.length === 0) {
473
+ return { success: false, error: 'entries is required (non-empty array)' };
453
474
  }
454
- },
455
- },
456
- {
457
- name: 'memory_stats',
458
- description: 'Get memory storage statistics including HNSW index status',
459
- category: 'memory',
460
- inputSchema: {
461
- type: 'object',
462
- properties: {},
463
- },
464
- handler: async () => {
465
- await ensureInitialized();
466
- const { checkMemoryInitialization, listEntries } = await getMemoryFunctions();
467
- try {
468
- const status = await checkMemoryInitialization();
469
- // Cap stats sample at 1000 entries — the previous 100000 limit could
470
- // load up to 100 GB of resident memory (each entry value capped at 1 MB)
471
- // just to compute aggregate counts. Counts above 1000 are reported as
472
- // approximate via the dedicated `total` field.
473
- const allEntries = await listEntries({ limit: 1000 });
474
- // Count by namespace (sample-based for large stores)
475
- const namespaces = {};
476
- let withEmbeddings = 0;
477
- for (const entry of allEntries.entries) {
478
- namespaces[entry.namespace] = (namespaces[entry.namespace] || 0) + 1;
479
- if (entry.hasEmbedding)
480
- withEmbeddings++;
481
- }
482
- return {
483
- initialized: status.initialized,
484
- totalEntries: allEntries.total,
485
- entriesWithEmbeddings: withEmbeddings,
486
- embeddingCoverage: allEntries.total > 0
487
- ? `${((withEmbeddings / allEntries.total) * 100).toFixed(1)}%`
488
- : '0%',
489
- namespaces,
490
- backend: 'sql.js + HNSW',
491
- version: status.version || '3.0.0',
492
- features: status.features || {
493
- vectorEmbeddings: true,
494
- hnswIndex: true,
495
- semanticSearch: true,
496
- },
497
- };
475
+ if (params.entries.length > MAX_BATCH_SIZE) {
476
+ return { success: false, error: `Too many entries: ${params.entries.length}. Max is ${MAX_BATCH_SIZE}` };
498
477
  }
499
- catch (error) {
500
- return {
501
- initialized: false,
502
- error: error instanceof Error ? error.message : 'Unknown error',
503
- };
478
+ // Validate each entry. Aggregate-byte cap prevents 500 entries × 100KB
479
+ // values = 50MB single-call payloads from spiking Node heap to ~200MB
480
+ // (UTF-16 doubling + downstream copies in the bridge layer).
481
+ const MAX_BATCH_BYTES = 1_048_576; // 1 MiB total
482
+ let totalBytes = 0;
483
+ const validatedEntries = [];
484
+ for (let i = 0; i < params.entries.length; i++) {
485
+ const entry = params.entries[i];
486
+ if (!entry || typeof entry !== 'object') {
487
+ return { success: false, error: `entries[${i}] must be an object` };
488
+ }
489
+ const key = validateString(entry.key, `entries[${i}].key`, 1000);
490
+ if (!key)
491
+ return { success: false, error: `entries[${i}].key is required (non-empty string)` };
492
+ const value = validateString(entry.value, `entries[${i}].value`);
493
+ totalBytes += key.length + (value?.length ?? 0);
494
+ if (totalBytes > MAX_BATCH_BYTES) {
495
+ return { success: false, error: `Batch payload exceeds ${MAX_BATCH_BYTES} bytes` };
496
+ }
497
+ validatedEntries.push({ key, value: value ?? undefined });
504
498
  }
505
- },
499
+ const bridge = await getBridge();
500
+ const result = await bridge.bridgeBatchOperation({
501
+ operation,
502
+ entries: validatedEntries,
503
+ });
504
+ return result ?? { success: false, error: 'Memory bridge not available. Use memory_store/memory_search instead.' };
505
+ }
506
+ catch (error) {
507
+ return { success: false, error: sanitizeError(error) };
508
+ }
506
509
  },
507
- {
508
- name: 'memory_migrate',
509
- description: 'Manually trigger migration from legacy JSON store to sql.js',
510
- category: 'memory',
511
- inputSchema: {
512
- type: 'object',
513
- properties: {
514
- force: { type: 'boolean', description: 'Force re-migration even if already done' },
515
- },
510
+ };
511
+ // ===== memory_context_synthesize — Synthesize context from memories =====
512
+ export const memoryContextSynthesize = {
513
+ name: 'memory_context-synthesize',
514
+ description: 'Synthesize context from stored memories for a given query',
515
+ inputSchema: {
516
+ type: 'object',
517
+ properties: {
518
+ query: { type: 'string', description: 'Query to synthesize context for' },
519
+ maxEntries: { type: 'number', description: 'Maximum entries to include (default: 10)' },
516
520
  },
517
- handler: async (input) => {
518
- const force = input.force;
519
- // Remove migration marker if forcing
520
- if (force) {
521
- const markerPath = getMigrationMarkerPath();
522
- if (existsSync(markerPath)) {
523
- unlinkSync(markerPath);
521
+ required: ['query'],
522
+ },
523
+ handler: async (params) => {
524
+ try {
525
+ const query = validateString(params.query, 'query', 10_000);
526
+ if (!query)
527
+ return { success: false, error: 'query is required (non-empty string, max 10KB)' };
528
+ // validateExternalContent: guard against prompt injection in synthesized context
529
+ // Source: https://arxiv.org/abs/2302.12173, https://arxiv.org/abs/2310.12815
530
+ try {
531
+ const secMod = await import('@monomind/security').catch(() => null);
532
+ const validateExternalContent = secMod?.validateExternalContent;
533
+ if (validateExternalContent) {
534
+ const check = await validateExternalContent(query, 'memory_context-synthesize query');
535
+ if (!check.safe) {
536
+ return { success: false, error: `Injection guard: ${check.reason}`, injectionDetected: true };
537
+ }
524
538
  }
525
539
  }
526
- // Check if migration was already completed before running it
527
- const alreadyMigrated = existsSync(getMigrationMarkerPath());
528
- // Check for legacy data
529
- const legacyStore = loadLegacyStore();
530
- if (!legacyStore || Object.keys(legacyStore.entries).length === 0) {
531
- return {
532
- success: true,
533
- message: 'No legacy data to migrate',
534
- migrated: 0,
535
- };
536
- }
537
- // Run migration via ensureInitialized
538
- await ensureInitialized();
539
- return {
540
- success: true,
541
- message: alreadyMigrated ? 'Already migrated — use force=true to re-run' : 'Migration completed',
542
- migrated: alreadyMigrated ? 0 : Object.keys(legacyStore.entries).length,
543
- backend: 'sql.js + HNSW',
544
- };
540
+ catch { /* security module optional */ }
541
+ const bridge = await getBridge();
542
+ const result = await bridge.bridgeContextSynthesize({
543
+ query,
544
+ maxEntries: validatePositiveInt(params.maxEntries, 10, MAX_TOP_K),
545
+ });
546
+ return result ?? { success: false, error: 'Memory bridge not available. Use memory_store/memory_search instead.' };
547
+ }
548
+ catch (error) {
549
+ return { success: false, error: sanitizeError(error) };
550
+ }
551
+ },
552
+ };
553
+ // ===== memory_semantic_route — Route via SemanticRouter =====
554
+ export const memorySemanticRoute = {
555
+ name: 'memory_semantic-route',
556
+ description: 'Route an input via SemanticRouter for intent classification',
557
+ inputSchema: {
558
+ type: 'object',
559
+ properties: {
560
+ input: { type: 'string', description: 'Input text to route' },
545
561
  },
562
+ required: ['input'],
546
563
  },
564
+ handler: async (params) => {
565
+ try {
566
+ const input = validateString(params.input, 'input', 10_000);
567
+ if (!input)
568
+ return { route: null, error: 'input is required (non-empty string, max 10KB)' };
569
+ const bridge = await getBridge();
570
+ const result = await bridge.bridgeSemanticRoute({ input });
571
+ return result ?? { route: null, error: 'Memory bridge not available. Use hooks route instead.' };
572
+ }
573
+ catch (error) {
574
+ return { route: null, error: sanitizeError(error) };
575
+ }
576
+ },
577
+ };
578
+ // ===== Export all tools =====
579
+ export const memoryTools = [
580
+ memoryHealth,
581
+ memoryControllers,
582
+ memoryPatternStore,
583
+ memoryPatternSearch,
584
+ memoryFeedback,
585
+ memoryCausalEdge,
586
+ memoryRoute,
587
+ memorySessionStart,
588
+ memorySessionEnd,
589
+ memoryHierarchicalStore,
590
+ memoryHierarchicalRecall,
591
+ memoryConsolidate,
592
+ memoryBatch,
593
+ memoryContextSynthesize,
594
+ memorySemanticRoute,
547
595
  ];
548
596
  //# sourceMappingURL=memory-tools.js.map