@monoes/monomindcli 1.15.6 → 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.
- package/.claude/agents/github/repo-architect.md +1 -1
- package/.claude/agents/specialists/integration-architect.md +6 -6
- package/.claude/commands/hive-mind/hive-mind-init.md +1 -1
- package/.claude/commands/hive-mind/hive-mind-memory.md +1 -1
- package/.claude/commands/mastermind/brain.md +11 -11
- package/.claude/commands/mastermind/master.md +4 -4
- package/.claude/commands/mastermind/memory.md +6 -6
- package/.claude/commands/memory/README.md +4 -4
- package/.claude/commands/truth/start.md +3 -3
- package/.claude/helpers/extras-registry.json +2 -2
- package/.claude/helpers/skill-registry.json +26 -26
- package/.claude/helpers/statusline.cjs +8 -8
- package/.claude/skills/agentic-jujutsu/SKILL.md +3 -3
- package/.claude/skills/mastermind/_protocol.md +8 -8
- package/README.md +6 -6
- package/dist/src/__tests__/browse-analyzer.test.js +18 -1
- package/dist/src/__tests__/browse-analyzer.test.js.map +1 -1
- package/dist/src/commands/agent.js +2 -2
- package/dist/src/commands/agent.js.map +1 -1
- package/dist/src/commands/autopilot.js +1 -1
- package/dist/src/commands/autopilot.js.map +1 -1
- package/dist/src/commands/completions.d.ts.map +1 -1
- package/dist/src/commands/completions.js +2 -21
- package/dist/src/commands/completions.js.map +1 -1
- package/dist/src/commands/config.js +1 -1
- package/dist/src/commands/hive-mind.js +1 -1
- package/dist/src/commands/hooks-coverage-commands.js +31 -31
- package/dist/src/commands/hooks-coverage-commands.js.map +1 -1
- package/dist/src/commands/hooks-routing-commands.js +1 -1
- package/dist/src/commands/hooks-routing-commands.js.map +1 -1
- package/dist/src/commands/hooks.js +1 -1
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/index.d.ts +0 -1
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +0 -4
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/init.js +8 -8
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/memory.d.ts +1 -1
- package/dist/src/commands/memory.js +25 -25
- package/dist/src/commands/memory.js.map +1 -1
- package/dist/src/commands/migrate.js +2 -2
- package/dist/src/commands/neural.js +1 -1
- package/dist/src/commands/neural.js.map +1 -1
- package/dist/src/commands/swarm.js +1 -1
- package/dist/src/commands/swarm.js.map +1 -1
- package/dist/src/config-adapter.d.ts.map +1 -1
- package/dist/src/config-adapter.js +8 -8
- package/dist/src/config-adapter.js.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/init/claudemd-generator.js +2 -2
- package/dist/src/init/executor.js +16 -16
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/shared-instructions-generator.d.ts +1 -1
- package/dist/src/init/shared-instructions-generator.js +1 -1
- package/dist/src/init/statusline-generator.d.ts +1 -1
- package/dist/src/init/statusline-generator.js +8 -8
- package/dist/src/init/types.d.ts +3 -3
- package/dist/src/init/types.d.ts.map +1 -1
- package/dist/src/init/types.js +3 -3
- package/dist/src/init/types.js.map +1 -1
- package/dist/src/mcp-client.d.ts.map +1 -1
- package/dist/src/mcp-client.js +1 -8
- package/dist/src/mcp-client.js.map +1 -1
- package/dist/src/mcp-tools/autopilot-tools.js +3 -3
- package/dist/src/mcp-tools/autopilot-tools.js.map +1 -1
- package/dist/src/mcp-tools/daa-tools.js +13 -13
- package/dist/src/mcp-tools/daa-tools.js.map +1 -1
- package/dist/src/mcp-tools/guidance-tools.js +4 -4
- package/dist/src/mcp-tools/guidance-tools.js.map +1 -1
- package/dist/src/mcp-tools/hive-mind-tools.js +4 -4
- package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -1
- package/dist/src/mcp-tools/hooks-intelligence.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-intelligence.js +1 -0
- package/dist/src/mcp-tools/hooks-intelligence.js.map +1 -1
- package/dist/src/mcp-tools/hooks-routing.js +23 -23
- package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
- package/dist/src/mcp-tools/index.d.ts +0 -1
- package/dist/src/mcp-tools/index.d.ts.map +1 -1
- package/dist/src/mcp-tools/index.js +0 -2
- package/dist/src/mcp-tools/index.js.map +1 -1
- package/dist/src/mcp-tools/memory-tools.d.ts +22 -6
- package/dist/src/mcp-tools/memory-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/memory-tools.js +553 -505
- package/dist/src/mcp-tools/memory-tools.js.map +1 -1
- package/dist/src/mcp-tools/progress-tools.js +1 -1
- package/dist/src/mcp-tools/progress-tools.js.map +1 -1
- package/dist/src/mcp-tools/system-tools.js +5 -5
- package/dist/src/mcp-tools/system-tools.js.map +1 -1
- package/dist/src/mcp-tools/transfer-tools.d.ts +1 -1
- package/dist/src/mcp-tools/transfer-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/transfer-tools.js +1 -156
- package/dist/src/mcp-tools/transfer-tools.js.map +1 -1
- package/dist/src/memory/embedding-operations.js +3 -3
- package/dist/src/memory/embedding-operations.js.map +1 -1
- package/dist/src/memory/hnsw-operations.js +5 -5
- package/dist/src/memory/hnsw-operations.js.map +1 -1
- package/dist/src/memory/intelligence.js +2 -2
- package/dist/src/memory/intelligence.js.map +1 -1
- package/dist/src/memory/memory-bridge.d.ts +86 -234
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +455 -1702
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/memory/memory-crud.js +3 -3
- package/dist/src/memory/memory-crud.js.map +1 -1
- package/dist/src/memory/memory-initializer.d.ts +1 -1
- package/dist/src/memory/memory-initializer.js +5 -5
- package/dist/src/memory/memory-initializer.js.map +1 -1
- package/dist/src/memory/memory-read.js +4 -4
- package/dist/src/memory/memory-read.js.map +1 -1
- package/dist/src/suggest.js +0 -1
- package/dist/src/suggest.js.map +1 -1
- package/dist/src/types.d.ts +1 -1
- package/dist/src/ui/dashboard.html +41 -5
- package/dist/src/ui/orgs.html +91 -5
- package/dist/src/ui/server.mjs +44 -0
- package/dist/src/update/validator.d.ts.map +1 -1
- package/dist/src/update/validator.js +1 -3
- package/dist/src/update/validator.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/src/commands/plugins.d.ts +0 -11
- package/dist/src/commands/plugins.d.ts.map +0 -1
- package/dist/src/commands/plugins.js +0 -799
- package/dist/src/commands/plugins.js.map +0 -1
- package/dist/src/plugins/manager.d.ts +0 -133
- package/dist/src/plugins/manager.d.ts.map +0 -1
- package/dist/src/plugins/manager.js +0 -498
- package/dist/src/plugins/manager.js.map +0 -1
- package/dist/src/plugins/store/discovery.d.ts +0 -88
- package/dist/src/plugins/store/discovery.d.ts.map +0 -1
- package/dist/src/plugins/store/discovery.js +0 -650
- package/dist/src/plugins/store/discovery.js.map +0 -1
- package/dist/src/plugins/store/index.d.ts +0 -76
- package/dist/src/plugins/store/index.d.ts.map +0 -1
- package/dist/src/plugins/store/index.js +0 -141
- package/dist/src/plugins/store/index.js.map +0 -1
- package/dist/src/plugins/store/search.d.ts +0 -46
- package/dist/src/plugins/store/search.d.ts.map +0 -1
- package/dist/src/plugins/store/search.js +0 -231
- package/dist/src/plugins/store/search.js.map +0 -1
- package/dist/src/plugins/store/types.d.ts +0 -274
- package/dist/src/plugins/store/types.d.ts.map +0 -1
- package/dist/src/plugins/store/types.js +0 -7
- package/dist/src/plugins/store/types.js.map +0 -1
- package/dist/src/plugins/tests/demo-plugin-store.d.ts +0 -7
- package/dist/src/plugins/tests/demo-plugin-store.d.ts.map +0 -1
- package/dist/src/plugins/tests/demo-plugin-store.js +0 -126
- package/dist/src/plugins/tests/demo-plugin-store.js.map +0 -1
- package/dist/src/plugins/tests/standalone-test.d.ts +0 -12
- package/dist/src/plugins/tests/standalone-test.d.ts.map +0 -1
- package/dist/src/plugins/tests/standalone-test.js +0 -188
- package/dist/src/plugins/tests/standalone-test.js.map +0 -1
- package/dist/src/plugins/tests/test-plugin-store.d.ts +0 -7
- package/dist/src/plugins/tests/test-plugin-store.d.ts.map +0 -1
- package/dist/src/plugins/tests/test-plugin-store.js +0 -206
- package/dist/src/plugins/tests/test-plugin-store.js.map +0 -1
- package/dist/src/services/registry-api.d.ts +0 -58
- package/dist/src/services/registry-api.d.ts.map +0 -1
- package/dist/src/services/registry-api.js +0 -199
- package/dist/src/services/registry-api.js.map +0 -1
- package/scripts/publish-registry.ts +0 -339
|
@@ -1,548 +1,596 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Memory MCP Tools
|
|
2
|
+
* Memory MCP Tools — Phase 6 of ADR-053
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
23
|
-
|
|
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
|
|
26
|
-
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
return
|
|
75
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
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
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
-
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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
|