@monoes/monomindcli 2.9.3 → 2.9.5
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/skills/mastermind-createorg/SKILL.md +2 -1
- package/README.md +9 -5
- package/dist/src/autopilot-state.d.ts +16 -4
- package/dist/src/autopilot-state.d.ts.map +1 -1
- package/dist/src/autopilot-state.js +42 -19
- package/dist/src/autopilot-state.js.map +1 -1
- package/dist/src/commands/agent-lifecycle.js +4 -4
- package/dist/src/commands/agent-lifecycle.js.map +1 -1
- package/dist/src/commands/autopilot.d.ts.map +1 -1
- package/dist/src/commands/autopilot.js +106 -25
- package/dist/src/commands/autopilot.js.map +1 -1
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +20 -3
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/commands/hooks-coverage-commands.d.ts.map +1 -1
- package/dist/src/commands/hooks-coverage-commands.js +67 -75
- package/dist/src/commands/hooks-coverage-commands.js.map +1 -1
- package/dist/src/commands/hooks-routing-commands.d.ts.map +1 -1
- package/dist/src/commands/hooks-routing-commands.js +12 -26
- package/dist/src/commands/hooks-routing-commands.js.map +1 -1
- package/dist/src/commands/hooks-workers.d.ts.map +1 -1
- package/dist/src/commands/hooks-workers.js +16 -47
- package/dist/src/commands/hooks-workers.js.map +1 -1
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +96 -54
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/memory-list.d.ts.map +1 -1
- package/dist/src/commands/memory-list.js +79 -36
- package/dist/src/commands/memory-list.js.map +1 -1
- package/dist/src/commands/org.js +2 -2
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/commands/security-cve.d.ts.map +1 -1
- package/dist/src/commands/security-cve.js +11 -1
- package/dist/src/commands/security-cve.js.map +1 -1
- package/dist/src/commands/security-misc.d.ts +9 -0
- package/dist/src/commands/security-misc.d.ts.map +1 -1
- package/dist/src/commands/security-misc.js +54 -33
- package/dist/src/commands/security-misc.js.map +1 -1
- package/dist/src/commands/security-scan.d.ts.map +1 -1
- package/dist/src/commands/security-scan.js +11 -3
- package/dist/src/commands/security-scan.js.map +1 -1
- package/dist/src/commands/swarm.d.ts.map +1 -1
- package/dist/src/commands/swarm.js +137 -96
- package/dist/src/commands/swarm.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +47 -45
- package/dist/src/init/statusline-generator.js.map +1 -1
- package/dist/src/init/types.d.ts +1 -1
- package/dist/src/init/types.d.ts.map +1 -1
- package/dist/src/init/types.js +5 -1
- package/dist/src/init/types.js.map +1 -1
- package/dist/src/mcp-server.d.ts +6 -0
- package/dist/src/mcp-server.d.ts.map +1 -1
- package/dist/src/mcp-server.js +47 -22
- package/dist/src/mcp-server.js.map +1 -1
- package/dist/src/mcp-tools/embeddings-tools.js +2 -2
- package/dist/src/mcp-tools/embeddings-tools.js.map +1 -1
- package/dist/src/mcp-tools/hive-mind-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hive-mind-tools.js +13 -11
- 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 +99 -68
- package/dist/src/mcp-tools/hooks-intelligence.js.map +1 -1
- package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-routing.js +403 -215
- package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
- package/dist/src/mcp-tools/monograph/impact-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/monograph/impact-tools.js +109 -44
- package/dist/src/mcp-tools/monograph/impact-tools.js.map +1 -1
- package/dist/src/mcp-tools/performance-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/performance-tools.js +10 -15
- package/dist/src/mcp-tools/performance-tools.js.map +1 -1
- package/dist/src/mcp-tools/system-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/system-tools.js +100 -38
- package/dist/src/mcp-tools/system-tools.js.map +1 -1
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +172 -69
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/monovector/diff-classifier.js +3 -3
- package/dist/src/monovector/diff-classifier.js.map +1 -1
- package/dist/src/orgrt/daemon.d.ts +5 -3
- package/dist/src/orgrt/daemon.d.ts.map +1 -1
- package/dist/src/orgrt/daemon.js +291 -93
- package/dist/src/orgrt/daemon.js.map +1 -1
- package/dist/src/orgrt/kimicode-runner.js +1 -1
- package/dist/src/orgrt/kimicode-runner.js.map +1 -1
- package/dist/src/orgrt/opencode-runner.js +1 -1
- package/dist/src/orgrt/opencode-runner.js.map +1 -1
- package/dist/src/orgrt/remote.d.ts.map +1 -1
- package/dist/src/orgrt/remote.js +12 -6
- package/dist/src/orgrt/remote.js.map +1 -1
- package/dist/src/orgrt/server.d.ts.map +1 -1
- package/dist/src/orgrt/server.js +71 -14
- package/dist/src/orgrt/server.js.map +1 -1
- package/dist/src/types.d.ts +1 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/ui/server.mjs +2647 -838
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* build-agents, transfer, session, list, metrics, pre-task, post-task, intelligence.
|
|
5
5
|
* Extracted from hooks-tools.ts.
|
|
6
6
|
*/
|
|
7
|
-
import { mkdirSync, writeFileSync, renameSync, existsSync, readFileSync } from 'fs';
|
|
7
|
+
import { mkdirSync, writeFileSync, renameSync, existsSync, readFileSync, } from 'fs';
|
|
8
8
|
import { join, resolve, sep } from 'path';
|
|
9
9
|
import { getProjectCwd } from './types.js';
|
|
10
|
+
import { validateMcpString } from '../utils/input-guards.js';
|
|
10
11
|
import { randomUUID } from 'node:crypto';
|
|
11
12
|
import { recordRoute, joinOutcome, joinLatestUnresolved } from '../monovector/route-outcomes.js';
|
|
12
13
|
import { recordCommand, deriveRecentSuccess } from '../monovector/command-outcomes.js';
|
|
@@ -19,7 +20,10 @@ export const hooksPreEdit = {
|
|
|
19
20
|
type: 'object',
|
|
20
21
|
properties: {
|
|
21
22
|
filePath: { type: 'string', description: 'Path to the file being edited' },
|
|
22
|
-
operation: {
|
|
23
|
+
operation: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Type of operation (create, update, delete, refactor)',
|
|
26
|
+
},
|
|
23
27
|
context: { type: 'string', description: 'Additional context' },
|
|
24
28
|
},
|
|
25
29
|
required: ['filePath'],
|
|
@@ -29,14 +33,11 @@ export const hooksPreEdit = {
|
|
|
29
33
|
// response. Cap operation to prevent oversized strings in recommendations.
|
|
30
34
|
const MAX_PRE_EDIT_PATH_LEN = 4 * 1024;
|
|
31
35
|
const MAX_PRE_EDIT_OP_LEN = 64;
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const operation = typeof rawOperation === 'string' && rawOperation.length > MAX_PRE_EDIT_OP_LEN
|
|
38
|
-
? rawOperation.slice(0, MAX_PRE_EDIT_OP_LEN)
|
|
39
|
-
: rawOperation;
|
|
36
|
+
const filePath = validateMcpString(params.filePath, 'filePath', MAX_PRE_EDIT_PATH_LEN);
|
|
37
|
+
if (!filePath) {
|
|
38
|
+
return { error: 'filePath is required (non-empty string, no control chars, max 4KB)' };
|
|
39
|
+
}
|
|
40
|
+
const operation = validateMcpString(params.operation, 'operation', MAX_PRE_EDIT_OP_LEN) ?? 'update';
|
|
40
41
|
const suggestedAgents = suggestAgentsForFile(filePath);
|
|
41
42
|
const ext = getFileExtension(filePath);
|
|
42
43
|
return {
|
|
@@ -47,9 +48,7 @@ export const hooksPreEdit = {
|
|
|
47
48
|
fileType: ext || 'unknown',
|
|
48
49
|
relatedFiles: [],
|
|
49
50
|
suggestedAgents,
|
|
50
|
-
patterns: [
|
|
51
|
-
{ pattern: `${ext} file editing`, confidence: 0.85 },
|
|
52
|
-
],
|
|
51
|
+
patterns: [{ pattern: `${ext} file editing`, confidence: 0.85 }],
|
|
53
52
|
risks: operation === 'delete' ? ['File deletion is irreversible'] : [],
|
|
54
53
|
},
|
|
55
54
|
recommendations: [
|
|
@@ -77,15 +76,12 @@ export const hooksPostEdit = {
|
|
|
77
76
|
// Cap agent: stored in feedback record and forwarded to bridge.
|
|
78
77
|
const MAX_POST_EDIT_PATH_LEN = 4 * 1024;
|
|
79
78
|
const MAX_POST_EDIT_AGENT_LEN = 256;
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
const filePath = validateMcpString(params.filePath, 'filePath', MAX_POST_EDIT_PATH_LEN);
|
|
80
|
+
if (!filePath) {
|
|
81
|
+
return { error: 'filePath is required (non-empty string, no control chars, max 4KB)' };
|
|
82
|
+
}
|
|
84
83
|
const success = params.success !== false;
|
|
85
|
-
const
|
|
86
|
-
const agent = typeof rawAgent === 'string' && rawAgent.length > MAX_POST_EDIT_AGENT_LEN
|
|
87
|
-
? rawAgent.slice(0, MAX_POST_EDIT_AGENT_LEN)
|
|
88
|
-
: rawAgent;
|
|
84
|
+
const agent = validateMcpString(params.agent, 'agent', MAX_POST_EDIT_AGENT_LEN) ?? undefined;
|
|
89
85
|
// Wire recordFeedback through bridge (issue #1209)
|
|
90
86
|
let feedbackResult = null;
|
|
91
87
|
try {
|
|
@@ -108,11 +104,13 @@ export const hooksPostEdit = {
|
|
|
108
104
|
success,
|
|
109
105
|
timestamp: new Date().toISOString(),
|
|
110
106
|
learningUpdate: success ? 'pattern_reinforced' : 'pattern_adjusted',
|
|
111
|
-
feedback: feedbackResult
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
107
|
+
feedback: feedbackResult
|
|
108
|
+
? {
|
|
109
|
+
recorded: feedbackResult.success,
|
|
110
|
+
controller: feedbackResult.success ? 'sqlite' : 'unavailable',
|
|
111
|
+
updates: feedbackResult.success ? 1 : 0,
|
|
112
|
+
}
|
|
113
|
+
: { recorded: false, controller: 'unavailable', updates: 0 },
|
|
116
114
|
};
|
|
117
115
|
},
|
|
118
116
|
};
|
|
@@ -131,14 +129,17 @@ export const hooksPreCommand = {
|
|
|
131
129
|
// raw command is reflected verbatim in the response. Limit to 4 KB which
|
|
132
130
|
// is far beyond any realistic shell command.
|
|
133
131
|
const MAX_CMD_LEN = 4 * 1024;
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
const command = validateMcpString(params.command, 'command', MAX_CMD_LEN);
|
|
133
|
+
if (!command) {
|
|
134
|
+
return { error: 'command is required (non-empty string, no control chars, max 4KB)' };
|
|
135
|
+
}
|
|
138
136
|
const assessment = assessCommandRisk(command);
|
|
139
|
-
const riskLevel = assessment.level >= 0.8
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
const riskLevel = assessment.level >= 0.8
|
|
138
|
+
? 'critical'
|
|
139
|
+
: assessment.level >= 0.6
|
|
140
|
+
? 'high'
|
|
141
|
+
: assessment.level >= 0.3
|
|
142
|
+
? 'medium'
|
|
142
143
|
: 'low';
|
|
143
144
|
return {
|
|
144
145
|
command,
|
|
@@ -174,11 +175,13 @@ export const hooksPostCommand = {
|
|
|
174
175
|
// already caps to 200 chars; apply a consistent 4 KB cap here that still
|
|
175
176
|
// covers any realistic shell command.
|
|
176
177
|
const MAX_POST_CMD_LEN = 4 * 1024;
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const exitCode = params.exitCode
|
|
178
|
+
const command = validateMcpString(params.command, 'command', MAX_POST_CMD_LEN);
|
|
179
|
+
if (!command) {
|
|
180
|
+
return { error: 'command is required (non-empty string, no control chars, max 4KB)' };
|
|
181
|
+
}
|
|
182
|
+
const exitCode = typeof params.exitCode === 'number' && Number.isFinite(params.exitCode)
|
|
183
|
+
? Math.floor(params.exitCode)
|
|
184
|
+
: 0;
|
|
182
185
|
const success = exitCode === 0;
|
|
183
186
|
// Record the real exit code in the time-windowed command-outcome store so
|
|
184
187
|
// post-task can derive a MEASURED success signal (grounded in actual exit
|
|
@@ -198,14 +201,19 @@ export const hooksPostCommand = {
|
|
|
198
201
|
namespace: 'commands',
|
|
199
202
|
tags: [success ? 'success' : 'error'],
|
|
200
203
|
});
|
|
201
|
-
_storedIn = '
|
|
204
|
+
_storedIn = 'sqlite';
|
|
202
205
|
}
|
|
203
206
|
catch {
|
|
204
207
|
// memory backend unavailable — store in JSON
|
|
205
208
|
try {
|
|
206
209
|
const store = loadMemoryStore();
|
|
207
210
|
const key = `cmd-${Date.now()}`;
|
|
208
|
-
store.entries[key] = {
|
|
211
|
+
store.entries[key] = {
|
|
212
|
+
key,
|
|
213
|
+
value: JSON.stringify({ command, exitCode, success }),
|
|
214
|
+
namespace: 'commands',
|
|
215
|
+
createdAt: new Date().toISOString(),
|
|
216
|
+
};
|
|
209
217
|
const memDir = join(getProjectCwd(), MEMORY_DIR);
|
|
210
218
|
if (!existsSync(memDir))
|
|
211
219
|
mkdirSync(memDir, { recursive: true });
|
|
@@ -215,7 +223,9 @@ export const hooksPostCommand = {
|
|
|
215
223
|
renameSync(_mptmp, _mp);
|
|
216
224
|
_storedIn = 'json-store';
|
|
217
225
|
}
|
|
218
|
-
catch {
|
|
226
|
+
catch {
|
|
227
|
+
/* non-critical */
|
|
228
|
+
}
|
|
219
229
|
}
|
|
220
230
|
return {
|
|
221
231
|
recorded: _storedIn !== 'none',
|
|
@@ -229,13 +239,16 @@ export const hooksPostCommand = {
|
|
|
229
239
|
};
|
|
230
240
|
export const hooksRoute = {
|
|
231
241
|
name: 'hooks_route',
|
|
232
|
-
description: 'Route task to optimal agent using
|
|
242
|
+
description: 'Route task to optimal agent using keyword matching and optional memory-backend semantic search',
|
|
233
243
|
inputSchema: {
|
|
234
244
|
type: 'object',
|
|
235
245
|
properties: {
|
|
236
246
|
task: { type: 'string', description: 'Task description' },
|
|
237
247
|
context: { type: 'string', description: 'Additional context' },
|
|
238
|
-
useSemanticRouter: {
|
|
248
|
+
useSemanticRouter: {
|
|
249
|
+
type: 'boolean',
|
|
250
|
+
description: 'Use semantic similarity routing (default: true)',
|
|
251
|
+
},
|
|
239
252
|
},
|
|
240
253
|
required: ['task'],
|
|
241
254
|
},
|
|
@@ -245,15 +258,13 @@ export const hooksRoute = {
|
|
|
245
258
|
// route-outcomes.jsonl. 16 KB matches the cap in hooksPatternSearch.
|
|
246
259
|
const MAX_ROUTE_TASK_LEN = 16 * 1024;
|
|
247
260
|
const MAX_ROUTE_CTX_LEN = 4 * 1024;
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
const context = typeof rawContext === 'string' && rawContext.length > MAX_ROUTE_CTX_LEN
|
|
254
|
-
? rawContext.slice(0, MAX_ROUTE_CTX_LEN)
|
|
255
|
-
: rawContext;
|
|
261
|
+
const task = validateMcpString(params.task, 'task', MAX_ROUTE_TASK_LEN);
|
|
262
|
+
if (!task) {
|
|
263
|
+
return { error: 'task is required (non-empty string, no control chars, max 16KB)' };
|
|
264
|
+
}
|
|
265
|
+
const context = validateMcpString(params.context, 'context', MAX_ROUTE_CTX_LEN) ?? undefined;
|
|
256
266
|
const useSemanticRouter = params.useSemanticRouter !== false;
|
|
267
|
+
const routeStart = Date.now();
|
|
257
268
|
// Phase 5: Try memory backend SemanticRouter / LearningSystem first
|
|
258
269
|
if (useSemanticRouter) {
|
|
259
270
|
try {
|
|
@@ -265,7 +276,7 @@ export const hooksRoute = {
|
|
|
265
276
|
if (routeConfidence > 0.5) {
|
|
266
277
|
const agents = memoryRoute.routes.map((r) => r.agentType);
|
|
267
278
|
const complexity = task.length > 200 ? 'high' : task.length < 50 ? 'low' : 'medium';
|
|
268
|
-
const memoryMethod = 'memory-
|
|
279
|
+
const memoryMethod = 'memory-sqlite';
|
|
269
280
|
const memoryConfidence = Math.round(routeConfidence * 100) / 100;
|
|
270
281
|
const matchedPattern = topRoute.pattern ?? task.slice(0, 60);
|
|
271
282
|
// Record the route recommendation so post-task can join the actual outcome
|
|
@@ -284,8 +295,8 @@ export const hooksRoute = {
|
|
|
284
295
|
task,
|
|
285
296
|
routing: {
|
|
286
297
|
method: memoryMethod,
|
|
287
|
-
backend: '
|
|
288
|
-
latencyMs:
|
|
298
|
+
backend: 'sqlite',
|
|
299
|
+
latencyMs: Date.now() - routeStart,
|
|
289
300
|
throughput: 'N/A',
|
|
290
301
|
},
|
|
291
302
|
matchedPattern,
|
|
@@ -293,19 +304,25 @@ export const hooksRoute = {
|
|
|
293
304
|
primaryAgent: {
|
|
294
305
|
type: agents[0],
|
|
295
306
|
confidence: memoryConfidence,
|
|
296
|
-
reason: `memory:
|
|
307
|
+
reason: `memory:sqlite: "${matchedPattern}" (${Math.round(routeConfidence * 100)}%)`,
|
|
297
308
|
},
|
|
298
309
|
alternativeAgents: agents.slice(1).map((agent, i) => ({
|
|
299
310
|
type: agent,
|
|
300
|
-
confidence: Math.round((routeConfidence -
|
|
301
|
-
reason: 'Alternative from
|
|
311
|
+
confidence: Math.round((routeConfidence - 0.1 * (i + 1)) * 100) / 100,
|
|
312
|
+
reason: 'Alternative from sqlite',
|
|
302
313
|
})),
|
|
303
|
-
|
|
314
|
+
heuristics: {
|
|
304
315
|
successProbability: memoryConfidence,
|
|
305
|
-
estimatedDuration: complexity === 'high'
|
|
316
|
+
estimatedDuration: complexity === 'high'
|
|
317
|
+
? '2-4 hours'
|
|
318
|
+
: complexity === 'medium'
|
|
319
|
+
? '30-60 min'
|
|
320
|
+
: '10-30 min',
|
|
306
321
|
complexity,
|
|
307
322
|
},
|
|
308
|
-
swarmRecommendation: agents.length > 2
|
|
323
|
+
swarmRecommendation: agents.length > 2
|
|
324
|
+
? { topology: 'hierarchical', agents, coordination: 'queen-led' }
|
|
325
|
+
: null,
|
|
309
326
|
};
|
|
310
327
|
}
|
|
311
328
|
}
|
|
@@ -317,7 +334,7 @@ export const hooksRoute = {
|
|
|
317
334
|
// Deterministic keyword routing is the baseline (and only) local path.
|
|
318
335
|
const semanticResult = [];
|
|
319
336
|
let routingMethod = 'keyword';
|
|
320
|
-
const routingLatencyMs =
|
|
337
|
+
const routingLatencyMs = Date.now() - routeStart;
|
|
321
338
|
let backendInfo = '';
|
|
322
339
|
// Get agents from keyword routing
|
|
323
340
|
let agents;
|
|
@@ -338,8 +355,11 @@ export const hooksRoute = {
|
|
|
338
355
|
if (intelSuggestion && intelSuggestion.confidence > 0.5) {
|
|
339
356
|
// Prepend neural agents (deduped) and boost confidence
|
|
340
357
|
const existingSet = new Set(agents);
|
|
341
|
-
const neuralOnly = intelSuggestion.agents.filter(a => !existingSet.has(a));
|
|
342
|
-
agents = [
|
|
358
|
+
const neuralOnly = intelSuggestion.agents.filter((a) => !existingSet.has(a));
|
|
359
|
+
agents = [
|
|
360
|
+
...intelSuggestion.agents,
|
|
361
|
+
...agents.filter((a) => !new Set(intelSuggestion.agents).has(a)),
|
|
362
|
+
];
|
|
343
363
|
const neuralWeight = intelSuggestion.confidence > 0.7 ? 0.65 : 0.5;
|
|
344
364
|
const keywordWeight = 1 - neuralWeight;
|
|
345
365
|
confidence = Math.min(0.95, intelSuggestion.confidence * neuralWeight +
|
|
@@ -379,7 +399,7 @@ export const hooksRoute = {
|
|
|
379
399
|
throughput: routingLatencyMs > 0 ? `${Math.round(1000 / routingLatencyMs)} routes/s` : 'N/A',
|
|
380
400
|
},
|
|
381
401
|
matchedPattern,
|
|
382
|
-
semanticMatches: semanticResult.slice(0, 3).map(r => ({
|
|
402
|
+
semanticMatches: semanticResult.slice(0, 3).map((r) => ({
|
|
383
403
|
pattern: r.intent,
|
|
384
404
|
score: Math.round(r.score * 100) / 100,
|
|
385
405
|
})),
|
|
@@ -392,19 +412,21 @@ export const hooksRoute = {
|
|
|
392
412
|
},
|
|
393
413
|
alternativeAgents: agents.slice(1).map((agent, i) => ({
|
|
394
414
|
type: agent,
|
|
395
|
-
confidence: Math.round((confidence -
|
|
415
|
+
confidence: Math.round((confidence - 0.1 * (i + 1)) * 100) / 100,
|
|
396
416
|
reason: `Alternative agent for ${agent} capabilities`,
|
|
397
417
|
})),
|
|
398
|
-
|
|
418
|
+
heuristics: {
|
|
399
419
|
successProbability: Math.round(confidence * 100) / 100,
|
|
400
420
|
estimatedDuration: complexity === 'high' ? '2-4 hours' : complexity === 'medium' ? '30-60 min' : '10-30 min',
|
|
401
421
|
complexity,
|
|
402
422
|
},
|
|
403
|
-
swarmRecommendation: agents.length > 2
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
423
|
+
swarmRecommendation: agents.length > 2
|
|
424
|
+
? {
|
|
425
|
+
topology: 'hierarchical',
|
|
426
|
+
agents,
|
|
427
|
+
coordination: 'queen-led',
|
|
428
|
+
}
|
|
429
|
+
: null,
|
|
408
430
|
};
|
|
409
431
|
},
|
|
410
432
|
};
|
|
@@ -419,19 +441,19 @@ export const hooksRouteSemantic = {
|
|
|
419
441
|
type: 'object',
|
|
420
442
|
properties: {
|
|
421
443
|
task: { type: 'string', description: 'Task description' },
|
|
422
|
-
debug: {
|
|
444
|
+
debug: {
|
|
445
|
+
type: 'boolean',
|
|
446
|
+
description: 'Include all route scores in the response (default: false)',
|
|
447
|
+
},
|
|
423
448
|
},
|
|
424
449
|
required: ['task'],
|
|
425
450
|
},
|
|
426
451
|
handler: async (params) => {
|
|
427
452
|
const MAX_ROUTE_TASK_LEN = 2000; // matches route-layer-factory's MAX_TASK_LENGTH
|
|
428
|
-
const
|
|
429
|
-
const task = typeof rawTask === 'string' && rawTask.length > MAX_ROUTE_TASK_LEN
|
|
430
|
-
? rawTask.slice(0, MAX_ROUTE_TASK_LEN)
|
|
431
|
-
: rawTask;
|
|
453
|
+
const task = validateMcpString(params.task, 'task', MAX_ROUTE_TASK_LEN);
|
|
432
454
|
const debug = params.debug === true;
|
|
433
|
-
if (!task
|
|
434
|
-
throw new Error('task is required');
|
|
455
|
+
if (!task) {
|
|
456
|
+
throw new Error('task is required (non-empty string, no control chars, max 2000 chars)');
|
|
435
457
|
}
|
|
436
458
|
const { createConfiguredRouteLayer } = await import('../routing/route-layer-factory.js');
|
|
437
459
|
const layer = await createConfiguredRouteLayer({ debug });
|
|
@@ -445,7 +467,9 @@ export const hooksRouteSemantic = {
|
|
|
445
467
|
routingMethod: `routing-pkg:${result.method}`,
|
|
446
468
|
confidence: result.confidence,
|
|
447
469
|
learningMode: 'js',
|
|
448
|
-
}).catch(() => {
|
|
470
|
+
}).catch(() => {
|
|
471
|
+
/* non-fatal — outcome joining is best-effort */
|
|
472
|
+
});
|
|
449
473
|
return { routeId, task, ...result };
|
|
450
474
|
},
|
|
451
475
|
};
|
|
@@ -460,14 +484,14 @@ export const hooksMetrics = {
|
|
|
460
484
|
},
|
|
461
485
|
},
|
|
462
486
|
handler: async (params) => {
|
|
463
|
-
const period = params.period
|
|
487
|
+
const period = validateMcpString(params.period, 'period', 32) ?? '24h';
|
|
464
488
|
// Try to read real counts from memory store
|
|
465
489
|
const store = loadMemoryStore();
|
|
466
490
|
const entries = Object.values(store.entries);
|
|
467
491
|
// Count patterns by looking at stored pattern entries
|
|
468
|
-
const patternEntries = entries.filter(e => e.key.includes('pattern'));
|
|
469
|
-
const routingEntries = entries.filter(e => e.key.includes('route') || e.key.includes('routing'));
|
|
470
|
-
const taskEntries = entries.filter(e => e.key.includes('task'));
|
|
492
|
+
const patternEntries = entries.filter((e) => e.key.includes('pattern'));
|
|
493
|
+
const routingEntries = entries.filter((e) => e.key.includes('route') || e.key.includes('routing'));
|
|
494
|
+
const taskEntries = entries.filter((e) => e.key.includes('task'));
|
|
471
495
|
if (entries.length === 0) {
|
|
472
496
|
return {
|
|
473
497
|
_real: true,
|
|
@@ -568,23 +592,25 @@ export const hooksPreTask = {
|
|
|
568
592
|
// in causal-graph edges persisted to SQLite. An uncapped ID can inflate the DB key
|
|
569
593
|
// column and every JSON payload that includes the ID.
|
|
570
594
|
const MAX_TASK_ID_LEN = 256;
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
595
|
+
const taskId = validateMcpString(params.taskId, 'taskId', MAX_TASK_ID_LEN);
|
|
596
|
+
if (!taskId) {
|
|
597
|
+
return { error: 'taskId is required (non-empty string, no control chars, max 256 chars)' };
|
|
598
|
+
}
|
|
575
599
|
// Cap description: it is forwarded to generateEmbedding twice (ERL heuristics
|
|
576
600
|
// + TextGrad gradient queries) and used in O(n) keyword extraction.
|
|
577
601
|
// 16 KB matches the cap applied in hooks_route and hooksPatternSearch.
|
|
578
602
|
const MAX_PRE_TASK_DESC_LEN = 16 * 1024;
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
const filePath = params.filePath;
|
|
603
|
+
const description = validateMcpString(params.description, 'description', MAX_PRE_TASK_DESC_LEN);
|
|
604
|
+
if (!description) {
|
|
605
|
+
return { error: 'description is required (non-empty string, no control chars, max 16KB)' };
|
|
606
|
+
}
|
|
607
|
+
const filePath = validateMcpString(params.filePath, 'filePath', 4 * 1024) ?? undefined;
|
|
584
608
|
const suggestion = suggestAgentsForTask(description);
|
|
585
609
|
// Determine complexity
|
|
586
610
|
const descLower = description.toLowerCase();
|
|
587
|
-
const complexity = descLower.includes('complex') ||
|
|
611
|
+
const complexity = descLower.includes('complex') ||
|
|
612
|
+
descLower.includes('architecture') ||
|
|
613
|
+
description.length > 200
|
|
588
614
|
? 'high'
|
|
589
615
|
: descLower.includes('simple') || descLower.includes('fix') || description.length < 50
|
|
590
616
|
? 'low'
|
|
@@ -603,14 +629,16 @@ export const hooksPreTask = {
|
|
|
603
629
|
limit: 3,
|
|
604
630
|
threshold: 0.6,
|
|
605
631
|
});
|
|
606
|
-
for (const r of
|
|
632
|
+
for (const r of heuristicResults?.results ?? []) {
|
|
607
633
|
try {
|
|
608
634
|
const h = JSON.parse(r.content ?? '{}');
|
|
609
635
|
if (h.action && h.confidence !== undefined && h.confidence >= 0.6) {
|
|
610
636
|
erlHints.push(`ERL hint (conf=${h.confidence.toFixed(2)}): use "${h.action}" for tasks involving "${h.condition ?? 'similar context'}"`);
|
|
611
637
|
}
|
|
612
638
|
}
|
|
613
|
-
catch {
|
|
639
|
+
catch {
|
|
640
|
+
/* skip malformed */
|
|
641
|
+
}
|
|
614
642
|
}
|
|
615
643
|
// TextGrad: also inject relevant past failure gradients to guide away from known pitfalls
|
|
616
644
|
// Source: https://arxiv.org/abs/2406.07496
|
|
@@ -620,7 +648,7 @@ export const hooksPreTask = {
|
|
|
620
648
|
limit: 2,
|
|
621
649
|
threshold: 0.55,
|
|
622
650
|
});
|
|
623
|
-
for (const r of
|
|
651
|
+
for (const r of gradientResults?.results ?? []) {
|
|
624
652
|
const critique = r.content ?? '';
|
|
625
653
|
if (critique && critique.length > 10) {
|
|
626
654
|
erlHints.push(`TextGrad warning: ${critique.slice(0, 120)}`);
|
|
@@ -628,7 +656,9 @@ export const hooksPreTask = {
|
|
|
628
656
|
}
|
|
629
657
|
}
|
|
630
658
|
}
|
|
631
|
-
catch {
|
|
659
|
+
catch {
|
|
660
|
+
/* non-critical */
|
|
661
|
+
}
|
|
632
662
|
// NOTE: a LATS planning pass used to be attempted here via
|
|
633
663
|
// `import('@monoes/hooks').buildLATSPlan` — that function never existed
|
|
634
664
|
// in the package (the planning module was removed), so the import failed
|
|
@@ -639,7 +669,7 @@ export const hooksPreTask = {
|
|
|
639
669
|
description,
|
|
640
670
|
suggestedAgents: suggestion.agents.map((agent, i) => ({
|
|
641
671
|
type: agent,
|
|
642
|
-
confidence: suggestion.confidence -
|
|
672
|
+
confidence: suggestion.confidence - 0.05 * i,
|
|
643
673
|
reason: i === 0
|
|
644
674
|
? `Primary agent for ${agent} tasks based on learned patterns`
|
|
645
675
|
: `Alternative agent with ${agent} capabilities`,
|
|
@@ -649,7 +679,9 @@ export const hooksPreTask = {
|
|
|
649
679
|
risks: complexity === 'high' ? ['Complex task may require multiple iterations'] : [],
|
|
650
680
|
recommendations: [
|
|
651
681
|
`Use ${suggestion.agents[0]} as primary agent`,
|
|
652
|
-
suggestion.agents.length > 2
|
|
682
|
+
suggestion.agents.length > 2
|
|
683
|
+
? 'Consider using swarm coordination'
|
|
684
|
+
: 'Single agent recommended',
|
|
653
685
|
...erlHints,
|
|
654
686
|
],
|
|
655
687
|
modelRouting,
|
|
@@ -668,9 +700,15 @@ export const hooksPostTask = {
|
|
|
668
700
|
success: { type: 'boolean', description: 'Whether task was successful' },
|
|
669
701
|
agent: { type: 'string', description: 'Agent that completed the task' },
|
|
670
702
|
quality: { type: 'number', description: 'Quality score (0-1)' },
|
|
671
|
-
task: {
|
|
703
|
+
task: {
|
|
704
|
+
type: 'string',
|
|
705
|
+
description: 'Task description text (used for learning keyword extraction)',
|
|
706
|
+
},
|
|
672
707
|
storeDecisions: { type: 'boolean', description: 'Also store routing decision in memory DB' },
|
|
673
|
-
routeId: {
|
|
708
|
+
routeId: {
|
|
709
|
+
type: 'string',
|
|
710
|
+
description: 'Route ID from a prior hooks_route call — joins the recommendation to this outcome',
|
|
711
|
+
},
|
|
674
712
|
},
|
|
675
713
|
required: ['taskId'],
|
|
676
714
|
},
|
|
@@ -680,10 +718,10 @@ export const hooksPostTask = {
|
|
|
680
718
|
// into causal-graph edge IDs persisted to the DB. Without a cap an attacker can
|
|
681
719
|
// inflate every row that stores the raw ID.
|
|
682
720
|
const MAX_POST_TASK_ID_LEN = 256;
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
721
|
+
const taskId = validateMcpString(params.taskId, 'taskId', MAX_POST_TASK_ID_LEN);
|
|
722
|
+
if (!taskId) {
|
|
723
|
+
return { error: 'taskId is required (non-empty string, no control chars, max 256 chars)' };
|
|
724
|
+
}
|
|
687
725
|
// The success flag, when the caller asserts it (--success true), is taken as
|
|
688
726
|
// ground truth. But callers usually do NOT pass it. Rather than treating every
|
|
689
727
|
// unverified task as "unknown" (and thus excluding it from learning), we now
|
|
@@ -702,7 +740,9 @@ export const hooksPostTask = {
|
|
|
702
740
|
const explicitSuccess = typeof params.success === 'boolean';
|
|
703
741
|
let outcomeKnown = explicitSuccess;
|
|
704
742
|
let success = params.success !== false;
|
|
705
|
-
let successSource = explicitSuccess
|
|
743
|
+
let successSource = explicitSuccess
|
|
744
|
+
? 'explicit'
|
|
745
|
+
: 'unknown';
|
|
706
746
|
if (!explicitSuccess) {
|
|
707
747
|
const derived = await deriveRecentSuccess(getRouteOutcomesBaseDir());
|
|
708
748
|
if (derived !== null) {
|
|
@@ -715,19 +755,17 @@ export const hooksPostTask = {
|
|
|
715
755
|
// feedback record and used as a tag string in the JSON store. An uncapped
|
|
716
756
|
// agent value inflates the on-disk store entry.
|
|
717
757
|
const MAX_POST_TASK_AGENT_LEN = 256;
|
|
718
|
-
const
|
|
719
|
-
const
|
|
720
|
-
?
|
|
721
|
-
:
|
|
722
|
-
|
|
758
|
+
const agent = validateMcpString(params.agent, 'agent', MAX_POST_TASK_AGENT_LEN) ?? undefined;
|
|
759
|
+
const quality = typeof params.quality === 'number' && Number.isFinite(params.quality)
|
|
760
|
+
? Math.max(0, Math.min(1, params.quality))
|
|
761
|
+
: success
|
|
762
|
+
? 0.85
|
|
763
|
+
: 0.3;
|
|
723
764
|
const startTime = Date.now();
|
|
724
765
|
// Cap task description: passed to generateEmbedding via bridgeRecordFeedback
|
|
725
766
|
// and persisted to route-outcomes.jsonl. 16 KB matches hooks_route cap.
|
|
726
767
|
const MAX_POST_TASK_LEN = 16 * 1024;
|
|
727
|
-
const
|
|
728
|
-
const cappedPostTask = typeof rawPostTask === 'string' && rawPostTask.length > MAX_POST_TASK_LEN
|
|
729
|
-
? rawPostTask.slice(0, MAX_POST_TASK_LEN)
|
|
730
|
-
: rawPostTask;
|
|
768
|
+
const cappedPostTask = validateMcpString(params.task, 'task', MAX_POST_TASK_LEN) ?? undefined;
|
|
731
769
|
// Phase 3: Wire recordFeedback through bridge → LearningSystem + ReasoningBank
|
|
732
770
|
let feedbackResult = null;
|
|
733
771
|
try {
|
|
@@ -735,9 +773,19 @@ export const hooksPostTask = {
|
|
|
735
773
|
feedbackResult = await bridge.bridgeRecordFeedback({
|
|
736
774
|
taskType: agent ?? 'task',
|
|
737
775
|
action: cappedPostTask?.slice(0, 80) ?? taskId,
|
|
738
|
-
outcome: success ? 'success' :
|
|
776
|
+
outcome: success ? 'success' : outcomeKnown ? 'failure' : 'partial',
|
|
739
777
|
confidence: quality,
|
|
740
|
-
metadata: {
|
|
778
|
+
metadata: {
|
|
779
|
+
taskId,
|
|
780
|
+
duration: typeof params.duration === 'number' && Number.isFinite(params.duration)
|
|
781
|
+
? params.duration
|
|
782
|
+
: undefined,
|
|
783
|
+
patterns: Array.isArray(params.patterns)
|
|
784
|
+
? params.patterns
|
|
785
|
+
.filter((p) => typeof p === 'string' && p.length > 0 && p.length <= 200)
|
|
786
|
+
.slice(0, 50)
|
|
787
|
+
: undefined,
|
|
788
|
+
},
|
|
741
789
|
});
|
|
742
790
|
}
|
|
743
791
|
catch (e) {
|
|
@@ -767,7 +815,11 @@ export const hooksPostTask = {
|
|
|
767
815
|
const taskText = cappedPostTask || '';
|
|
768
816
|
const outcomeKeywords = extractKeywords(taskText);
|
|
769
817
|
let outcomePersisted = false;
|
|
770
|
-
if (outcomeKnown &&
|
|
818
|
+
if (outcomeKnown &&
|
|
819
|
+
taskText &&
|
|
820
|
+
agent &&
|
|
821
|
+
agent.length <= 100 &&
|
|
822
|
+
/^[a-zA-Z0-9_-]+$/.test(agent)) {
|
|
771
823
|
try {
|
|
772
824
|
const outcomes = loadRoutingOutcomes();
|
|
773
825
|
outcomes.push({
|
|
@@ -781,7 +833,9 @@ export const hooksPostTask = {
|
|
|
781
833
|
saveRoutingOutcomes(outcomes);
|
|
782
834
|
outcomePersisted = true;
|
|
783
835
|
}
|
|
784
|
-
catch {
|
|
836
|
+
catch {
|
|
837
|
+
/* non-critical */
|
|
838
|
+
}
|
|
785
839
|
}
|
|
786
840
|
// Join this outcome back onto the original route recommendation. This is the
|
|
787
841
|
// recommendation→actual→success link that routing-accuracy metrics and SONA
|
|
@@ -797,7 +851,10 @@ export const hooksPostTask = {
|
|
|
797
851
|
quality: typeof params.quality === 'number' ? params.quality : undefined,
|
|
798
852
|
};
|
|
799
853
|
if (params.routeId) {
|
|
800
|
-
|
|
854
|
+
const routeId = validateMcpString(params.routeId, 'routeId', 256);
|
|
855
|
+
if (routeId) {
|
|
856
|
+
await joinOutcome(getRouteOutcomesBaseDir(), routeId, outcome);
|
|
857
|
+
}
|
|
801
858
|
}
|
|
802
859
|
else {
|
|
803
860
|
await joinLatestUnresolved(getRouteOutcomesBaseDir(), outcome);
|
|
@@ -822,7 +879,9 @@ export const hooksPostTask = {
|
|
|
822
879
|
});
|
|
823
880
|
}
|
|
824
881
|
}
|
|
825
|
-
catch {
|
|
882
|
+
catch {
|
|
883
|
+
/* non-critical */
|
|
884
|
+
}
|
|
826
885
|
}
|
|
827
886
|
// Optionally store in memory DB for cross-session vector retrieval
|
|
828
887
|
if (params.storeDecisions && taskText && agent) {
|
|
@@ -832,12 +891,20 @@ export const hooksPostTask = {
|
|
|
832
891
|
await storeFn({
|
|
833
892
|
key: `routing-decision:${taskId}`,
|
|
834
893
|
namespace: 'patterns',
|
|
835
|
-
value: JSON.stringify({
|
|
894
|
+
value: JSON.stringify({
|
|
895
|
+
task: taskText,
|
|
896
|
+
agent,
|
|
897
|
+
success,
|
|
898
|
+
quality,
|
|
899
|
+
keywords: outcomeKeywords,
|
|
900
|
+
}),
|
|
836
901
|
tags: ['routing-decision'],
|
|
837
902
|
});
|
|
838
903
|
}
|
|
839
904
|
}
|
|
840
|
-
catch {
|
|
905
|
+
catch {
|
|
906
|
+
/* non-critical */
|
|
907
|
+
}
|
|
841
908
|
}
|
|
842
909
|
const duration = Date.now() - startTime;
|
|
843
910
|
// TextGrad: Store textual gradient critique for failed tasks
|
|
@@ -857,21 +924,28 @@ export const hooksPostTask = {
|
|
|
857
924
|
});
|
|
858
925
|
}
|
|
859
926
|
}
|
|
860
|
-
catch {
|
|
927
|
+
catch {
|
|
928
|
+
/* non-critical */
|
|
929
|
+
}
|
|
861
930
|
}
|
|
862
931
|
// MAR: Structured multi-agent reflection on failure
|
|
863
932
|
// Source: https://arxiv.org/html/2512.20845 (MAR — December 2025)
|
|
864
|
-
const marReflection = !success
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
933
|
+
const marReflection = !success
|
|
934
|
+
? {
|
|
935
|
+
needed: true,
|
|
936
|
+
suggestedAgents: [
|
|
937
|
+
{ role: 'diagnoser', description: 'Analyze root cause of task failure' },
|
|
938
|
+
{ role: 'critic-1', description: 'Critique from correctness angle (temperature 0.3)' },
|
|
939
|
+
{ role: 'critic-2', description: 'Critique from efficiency angle (temperature 0.8)' },
|
|
940
|
+
{
|
|
941
|
+
role: 'aggregator',
|
|
942
|
+
description: 'Synthesize critiques into actionable reflection heuristic',
|
|
943
|
+
},
|
|
944
|
+
],
|
|
945
|
+
storeAs: 'heuristics',
|
|
946
|
+
note: 'Spawn agents sequentially: Diagnoser → Critics in parallel → Aggregator',
|
|
947
|
+
}
|
|
948
|
+
: { needed: false };
|
|
875
949
|
return {
|
|
876
950
|
taskId,
|
|
877
951
|
success,
|
|
@@ -882,15 +956,17 @@ export const hooksPostTask = {
|
|
|
882
956
|
patternsUpdated: feedbackResult?.success ? (success ? 2 : 1) : 0,
|
|
883
957
|
newPatterns: success ? 1 : 0,
|
|
884
958
|
trajectoryId: `traj-${Date.now()}`,
|
|
885
|
-
controller: feedbackResult?.success ? '
|
|
959
|
+
controller: feedbackResult?.success ? 'sqlite' : 'none',
|
|
886
960
|
outcomePersisted,
|
|
887
961
|
},
|
|
888
962
|
quality,
|
|
889
|
-
feedback: feedbackResult
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
963
|
+
feedback: feedbackResult
|
|
964
|
+
? {
|
|
965
|
+
recorded: feedbackResult.success,
|
|
966
|
+
controller: feedbackResult.success ? 'sqlite' : 'unavailable',
|
|
967
|
+
updates: feedbackResult.success ? 1 : 0,
|
|
968
|
+
}
|
|
969
|
+
: { recorded: false, controller: 'unavailable', updates: 0 },
|
|
894
970
|
marReflection,
|
|
895
971
|
timestamp: new Date().toISOString(),
|
|
896
972
|
};
|
|
@@ -913,10 +989,10 @@ export const hooksExplain = {
|
|
|
913
989
|
// Cap task: forwarded to suggestAgentsForTask (O(n) keyword loop + extractKeywords),
|
|
914
990
|
// .toLowerCase() (O(n)), and reflected verbatim in the response.
|
|
915
991
|
const MAX_EXPLAIN_TASK_LEN = 16 * 1024;
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
992
|
+
const task = validateMcpString(params.task, 'task', MAX_EXPLAIN_TASK_LEN);
|
|
993
|
+
if (!task) {
|
|
994
|
+
return { error: 'task is required (non-empty string, no control chars, max 16KB)' };
|
|
995
|
+
}
|
|
920
996
|
const suggestion = suggestAgentsForTask(task);
|
|
921
997
|
const taskLower = task.toLowerCase();
|
|
922
998
|
// Determine matched patterns
|
|
@@ -939,7 +1015,7 @@ export const hooksExplain = {
|
|
|
939
1015
|
const data = JSON.parse(readFileSync(outcomesPath, 'utf-8'));
|
|
940
1016
|
const outcomes = data.outcomes || [];
|
|
941
1017
|
if (outcomes.length > 0) {
|
|
942
|
-
historicalSuccess = outcomes.filter(o => o.success).length / outcomes.length;
|
|
1018
|
+
historicalSuccess = outcomes.filter((o) => o.success).length / outcomes.length;
|
|
943
1019
|
historicalNote = `Calculated from ${outcomes.length} recorded outcomes`;
|
|
944
1020
|
}
|
|
945
1021
|
}
|
|
@@ -954,14 +1030,44 @@ export const hooksExplain = {
|
|
|
954
1030
|
explanation: `The routing decision was made based on keyword analysis of the task description. ` +
|
|
955
1031
|
`The task contains keywords that match the "${suggestion.agents[0]}" specialization with ${(suggestion.confidence * 100).toFixed(0)}% confidence.`,
|
|
956
1032
|
factors: [
|
|
957
|
-
{
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1033
|
+
{
|
|
1034
|
+
factor: 'Keyword Match',
|
|
1035
|
+
weight: 0.4,
|
|
1036
|
+
heuristic: true,
|
|
1037
|
+
value: suggestion.confidence,
|
|
1038
|
+
impact: 'Primary routing signal',
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
factor: 'Historical Success',
|
|
1042
|
+
weight: 0.3,
|
|
1043
|
+
heuristic: true,
|
|
1044
|
+
value: historicalSuccess,
|
|
1045
|
+
impact: historicalNote,
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
factor: 'Agent Availability',
|
|
1049
|
+
weight: 0.2,
|
|
1050
|
+
heuristic: true,
|
|
1051
|
+
value: null,
|
|
1052
|
+
impact: 'Agent availability tracking not implemented',
|
|
1053
|
+
},
|
|
1054
|
+
{
|
|
1055
|
+
factor: 'Task Complexity',
|
|
1056
|
+
weight: 0.1,
|
|
1057
|
+
heuristic: true,
|
|
1058
|
+
value: task.length > 100 ? 0.8 : 0.3,
|
|
1059
|
+
impact: 'Complexity assessment',
|
|
1060
|
+
},
|
|
964
1061
|
],
|
|
1062
|
+
patterns: matchedPatterns.length > 0
|
|
1063
|
+
? matchedPatterns
|
|
1064
|
+
: [
|
|
1065
|
+
{
|
|
1066
|
+
pattern: 'general-task',
|
|
1067
|
+
matchScore: 0.7,
|
|
1068
|
+
examples: ['Default pattern for unclassified tasks'],
|
|
1069
|
+
},
|
|
1070
|
+
],
|
|
965
1071
|
decision: {
|
|
966
1072
|
agent: suggestion.agents[0],
|
|
967
1073
|
confidence: suggestion.confidence,
|
|
@@ -990,12 +1096,16 @@ export const hooksPretrain = {
|
|
|
990
1096
|
},
|
|
991
1097
|
},
|
|
992
1098
|
handler: async (params) => {
|
|
993
|
-
const repoPath = resolve(params.path
|
|
1099
|
+
const repoPath = resolve(validateMcpString(params.path, 'path', 4 * 1024) ?? '.');
|
|
994
1100
|
const projectRoot = getProjectCwd();
|
|
995
1101
|
if (repoPath !== projectRoot && !repoPath.startsWith(projectRoot + sep)) {
|
|
996
1102
|
return { error: 'Invalid path: must be within the project directory.' };
|
|
997
1103
|
}
|
|
998
|
-
const depth = params.depth
|
|
1104
|
+
const depth = validateMcpString(params.depth, 'depth', 16) ?? 'medium';
|
|
1105
|
+
const allowedDepths = new Set(['shallow', 'medium', 'deep']);
|
|
1106
|
+
if (!allowedDepths.has(depth)) {
|
|
1107
|
+
return { error: 'Invalid depth: must be shallow, medium, or deep' };
|
|
1108
|
+
}
|
|
999
1109
|
const startTime = performance.now();
|
|
1000
1110
|
// Real file scanning — count files by extension, extract patterns
|
|
1001
1111
|
const { readdirSync, statSync } = await import('node:fs');
|
|
@@ -1017,7 +1127,9 @@ export const hooksPretrain = {
|
|
|
1017
1127
|
scan(full, currentDepth + 1);
|
|
1018
1128
|
}
|
|
1019
1129
|
else if (entry.isFile()) {
|
|
1020
|
-
const ext = entry.name.includes('.')
|
|
1130
|
+
const ext = entry.name.includes('.')
|
|
1131
|
+
? entry.name.slice(entry.name.lastIndexOf('.'))
|
|
1132
|
+
: '';
|
|
1021
1133
|
if (ext)
|
|
1022
1134
|
extCounts[ext] = (extCounts[ext] || 0) + 1;
|
|
1023
1135
|
filesAnalyzed++;
|
|
@@ -1036,7 +1148,9 @@ export const hooksPretrain = {
|
|
|
1036
1148
|
// Extract import patterns (first 50 files max for performance)
|
|
1037
1149
|
if (filesAnalyzed <= 50) {
|
|
1038
1150
|
for (const line of lines.slice(0, 30)) {
|
|
1039
|
-
if (line.startsWith('import ') ||
|
|
1151
|
+
if (line.startsWith('import ') ||
|
|
1152
|
+
line.startsWith('from ') ||
|
|
1153
|
+
(line.startsWith('const ') && line.includes('require('))) {
|
|
1040
1154
|
const trimmed = line.trim();
|
|
1041
1155
|
if (trimmed.length < 120 && !patterns.includes(trimmed))
|
|
1042
1156
|
patterns.push(trimmed);
|
|
@@ -1046,12 +1160,16 @@ export const hooksPretrain = {
|
|
|
1046
1160
|
}
|
|
1047
1161
|
}
|
|
1048
1162
|
}
|
|
1049
|
-
catch {
|
|
1163
|
+
catch {
|
|
1164
|
+
/* skip unreadable */
|
|
1165
|
+
}
|
|
1050
1166
|
}
|
|
1051
1167
|
}
|
|
1052
1168
|
}
|
|
1053
1169
|
}
|
|
1054
|
-
catch {
|
|
1170
|
+
catch {
|
|
1171
|
+
/* skip inaccessible dirs */
|
|
1172
|
+
}
|
|
1055
1173
|
};
|
|
1056
1174
|
scan(repoPath, 0);
|
|
1057
1175
|
const elapsed = Math.round(performance.now() - startTime);
|
|
@@ -1061,7 +1179,14 @@ export const hooksPretrain = {
|
|
|
1061
1179
|
const bridge = await import('../memory/memory-bridge.js');
|
|
1062
1180
|
await bridge.bridgeStoreEntry({
|
|
1063
1181
|
key: `pretrain-${Date.now()}`,
|
|
1064
|
-
value: JSON.stringify({
|
|
1182
|
+
value: JSON.stringify({
|
|
1183
|
+
filesAnalyzed,
|
|
1184
|
+
totalLines,
|
|
1185
|
+
topExtensions: Object.entries(extCounts)
|
|
1186
|
+
.sort((a, b) => b[1] - a[1])
|
|
1187
|
+
.slice(0, 10),
|
|
1188
|
+
importPatterns: patterns.slice(0, 20),
|
|
1189
|
+
}),
|
|
1065
1190
|
namespace: 'pretrain',
|
|
1066
1191
|
tags: ['pretrain', depth],
|
|
1067
1192
|
});
|
|
@@ -1078,13 +1203,20 @@ export const hooksPretrain = {
|
|
|
1078
1203
|
if (patterns.length > 0) {
|
|
1079
1204
|
try {
|
|
1080
1205
|
const intel = await import('../memory/intelligence.js');
|
|
1081
|
-
await intel.initializeIntelligence({
|
|
1206
|
+
await intel.initializeIntelligence({
|
|
1207
|
+
confidenceLearningRate: 0.002,
|
|
1208
|
+
maxTrajectorySize: patterns.length,
|
|
1209
|
+
});
|
|
1082
1210
|
// Record each extracted pattern as an action step
|
|
1083
1211
|
for (const pat of patterns.slice(0, 50)) {
|
|
1084
|
-
await intel.recordStep({
|
|
1212
|
+
await intel.recordStep({
|
|
1213
|
+
type: 'action',
|
|
1214
|
+
content: pat,
|
|
1215
|
+
metadata: { source: 'pretrain', depth },
|
|
1216
|
+
});
|
|
1085
1217
|
}
|
|
1086
1218
|
// Record the entire scan as a completed trajectory
|
|
1087
|
-
const steps = patterns.slice(0, 50).map(p => ({ type: 'action', content: p }));
|
|
1219
|
+
const steps = patterns.slice(0, 50).map((p) => ({ type: 'action', content: p }));
|
|
1088
1220
|
await intel.recordTrajectory(steps, 'success');
|
|
1089
1221
|
intel.flushPatterns();
|
|
1090
1222
|
neuralPatternsLearned = steps.length;
|
|
@@ -1107,7 +1239,10 @@ export const hooksPretrain = {
|
|
|
1107
1239
|
patternsExtracted: patterns.length,
|
|
1108
1240
|
patternsStored,
|
|
1109
1241
|
neuralPatternsLearned,
|
|
1110
|
-
fileTypes: Object.entries(extCounts)
|
|
1242
|
+
fileTypes: Object.entries(extCounts)
|
|
1243
|
+
.sort((a, b) => b[1] - a[1])
|
|
1244
|
+
.slice(0, 15)
|
|
1245
|
+
.map(([ext, count]) => ({ ext, count })),
|
|
1111
1246
|
},
|
|
1112
1247
|
};
|
|
1113
1248
|
},
|
|
@@ -1120,38 +1255,68 @@ export const hooksBuildAgents = {
|
|
|
1120
1255
|
type: 'object',
|
|
1121
1256
|
properties: {
|
|
1122
1257
|
outputDir: { type: 'string', description: 'Output directory for configs' },
|
|
1123
|
-
focus: {
|
|
1258
|
+
focus: {
|
|
1259
|
+
type: 'string',
|
|
1260
|
+
description: 'Focus area (v1-implementation, security, performance, all)',
|
|
1261
|
+
},
|
|
1124
1262
|
format: { type: 'string', description: 'Config format (yaml, json)' },
|
|
1125
1263
|
persist: { type: 'boolean', description: 'Write configs to disk' },
|
|
1126
1264
|
},
|
|
1127
1265
|
},
|
|
1128
1266
|
handler: async (params) => {
|
|
1129
|
-
const rawOutputDir = resolve(params.outputDir
|
|
1267
|
+
const rawOutputDir = resolve(validateMcpString(params.outputDir, 'outputDir', 4 * 1024) ?? './agents');
|
|
1130
1268
|
const outputDir = rawOutputDir;
|
|
1131
1269
|
if (!outputDir.startsWith(getProjectCwd() + sep) && outputDir !== getProjectCwd()) {
|
|
1132
1270
|
return { error: 'Invalid outputDir: must be within the project directory.' };
|
|
1133
1271
|
}
|
|
1134
|
-
const focus = params.focus
|
|
1272
|
+
const focus = validateMcpString(params.focus, 'focus', 64) ?? 'all';
|
|
1135
1273
|
// Strict allowlist on `format` — without this, `format = "yaml/../../../etc/cron.d/x"`
|
|
1136
1274
|
// collapses through `join` and lets writes escape the validated outputDir.
|
|
1137
1275
|
const ALLOWED_FORMATS = new Set(['yaml', 'json']);
|
|
1138
|
-
const
|
|
1139
|
-
if (!ALLOWED_FORMATS.has(
|
|
1276
|
+
const format = validateMcpString(params.format, 'format', 16) ?? 'yaml';
|
|
1277
|
+
if (!ALLOWED_FORMATS.has(format)) {
|
|
1140
1278
|
return { error: 'Invalid format: must be yaml or json' };
|
|
1141
1279
|
}
|
|
1142
|
-
const format = formatRaw;
|
|
1143
1280
|
const persist = params.persist !== false; // Default to true
|
|
1144
1281
|
const agents = [
|
|
1145
|
-
{
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1282
|
+
{
|
|
1283
|
+
type: 'coder',
|
|
1284
|
+
configFile: join(outputDir, `coder.${format}`),
|
|
1285
|
+
capabilities: ['code-generation', 'refactoring', 'debugging'],
|
|
1286
|
+
optimizations: ['token-reduction', 'context-caching'],
|
|
1287
|
+
},
|
|
1288
|
+
{
|
|
1289
|
+
type: 'architect',
|
|
1290
|
+
configFile: join(outputDir, `architect.${format}`),
|
|
1291
|
+
capabilities: ['system-design', 'api-design', 'documentation'],
|
|
1292
|
+
optimizations: ['context-caching', 'memory-persistence'],
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
type: 'tester',
|
|
1296
|
+
configFile: join(outputDir, `tester.${format}`),
|
|
1297
|
+
capabilities: ['unit-testing', 'integration-testing', 'coverage'],
|
|
1298
|
+
optimizations: ['parallel-execution'],
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
type: 'security-architect',
|
|
1302
|
+
configFile: join(outputDir, `security-architect.${format}`),
|
|
1303
|
+
capabilities: ['threat-modeling', 'vulnerability-analysis', 'security-review'],
|
|
1304
|
+
optimizations: ['pattern-matching'],
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
type: 'reviewer',
|
|
1308
|
+
configFile: join(outputDir, `reviewer.${format}`),
|
|
1309
|
+
capabilities: ['code-review', 'quality-analysis', 'best-practices'],
|
|
1310
|
+
optimizations: ['incremental-analysis'],
|
|
1311
|
+
},
|
|
1150
1312
|
];
|
|
1151
|
-
const filteredAgents = focus === 'all'
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1313
|
+
const filteredAgents = focus === 'all'
|
|
1314
|
+
? agents
|
|
1315
|
+
: focus === 'security'
|
|
1316
|
+
? agents.filter((a) => a.type.includes('security') || a.type === 'reviewer')
|
|
1317
|
+
: focus === 'performance'
|
|
1318
|
+
? agents.filter((a) => ['coder', 'tester'].includes(a.type))
|
|
1319
|
+
: agents;
|
|
1155
1320
|
// Persist configs to disk if requested
|
|
1156
1321
|
if (persist) {
|
|
1157
1322
|
// Ensure output directory exists
|
|
@@ -1169,7 +1334,7 @@ export const hooksBuildAgents = {
|
|
|
1169
1334
|
};
|
|
1170
1335
|
const content = format === 'json'
|
|
1171
1336
|
? JSON.stringify(config, null, 2)
|
|
1172
|
-
: `# ${agent.type} agent configuration\ntype: ${agent.type}\nversion: "3.0.0"\ncapabilities:\n${agent.capabilities.map(c => ` - ${c}`).join('\n')}\noptimizations:\n${agent.optimizations.map(o => ` - ${o}`).join('\n')}\ncreatedAt: "${config.createdAt}"\n`;
|
|
1337
|
+
: `# ${agent.type} agent configuration\ntype: ${agent.type}\nversion: "3.0.0"\ncapabilities:\n${agent.capabilities.map((c) => ` - ${c}`).join('\n')}\noptimizations:\n${agent.optimizations.map((o) => ` - ${o}`).join('\n')}\ncreatedAt: "${config.createdAt}"\n`;
|
|
1173
1338
|
const _cftmp = agent.configFile + '.tmp';
|
|
1174
1339
|
writeFileSync(_cftmp, content, 'utf-8');
|
|
1175
1340
|
renameSync(_cftmp, agent.configFile);
|
|
@@ -1202,9 +1367,14 @@ export const hooksTransfer = {
|
|
|
1202
1367
|
required: ['sourcePath'],
|
|
1203
1368
|
},
|
|
1204
1369
|
handler: async (params) => {
|
|
1205
|
-
const sourcePath = params.sourcePath;
|
|
1206
|
-
|
|
1207
|
-
|
|
1370
|
+
const sourcePath = validateMcpString(params.sourcePath, 'sourcePath', 4 * 1024);
|
|
1371
|
+
if (!sourcePath) {
|
|
1372
|
+
return { error: 'sourcePath is required (non-empty string, no control chars, max 4KB)' };
|
|
1373
|
+
}
|
|
1374
|
+
const minConfidence = typeof params.minConfidence === 'number' && Number.isFinite(params.minConfidence)
|
|
1375
|
+
? Math.max(0, Math.min(1, params.minConfidence))
|
|
1376
|
+
: 0.7;
|
|
1377
|
+
const filter = validateMcpString(params.filter, 'filter', 64) ?? undefined;
|
|
1208
1378
|
// Validate sourcePath is an existing directory before reading from it
|
|
1209
1379
|
const resolvedSource = resolve(sourcePath);
|
|
1210
1380
|
const { statSync } = await import('fs');
|
|
@@ -1227,7 +1397,8 @@ export const hooksTransfer = {
|
|
|
1227
1397
|
let sourceStore = { entries: {}, version: '3.0.0' };
|
|
1228
1398
|
const MAX_SOURCE_STORE_BYTES = 50 * 1024 * 1024; // 50 MB — matches other store readers
|
|
1229
1399
|
try {
|
|
1230
|
-
if (existsSync(sourceMemoryPath) &&
|
|
1400
|
+
if (existsSync(sourceMemoryPath) &&
|
|
1401
|
+
statSync(sourceMemoryPath).size <= MAX_SOURCE_STORE_BYTES) {
|
|
1231
1402
|
sourceStore = JSON.parse(readFileSync(sourceMemoryPath, 'utf-8'));
|
|
1232
1403
|
}
|
|
1233
1404
|
}
|
|
@@ -1239,13 +1410,13 @@ export const hooksTransfer = {
|
|
|
1239
1410
|
const sourceEntries = Object.values(sourceStore.entries);
|
|
1240
1411
|
// Count patterns by type from source
|
|
1241
1412
|
const byType = {
|
|
1242
|
-
'file-patterns': sourceEntries.filter(e => e.key.includes('file') || e.metadata?.type === 'file-pattern').length,
|
|
1243
|
-
'task-routing': sourceEntries.filter(e => e.key.includes('routing') || e.metadata?.type === 'routing').length,
|
|
1244
|
-
'command-risk': sourceEntries.filter(e => e.key.includes('command') || e.metadata?.type === 'command-risk').length,
|
|
1245
|
-
'agent-success': sourceEntries.filter(e => e.key.includes('agent') || e.metadata?.type === 'agent-success').length,
|
|
1413
|
+
'file-patterns': sourceEntries.filter((e) => e.key.includes('file') || e.metadata?.type === 'file-pattern').length,
|
|
1414
|
+
'task-routing': sourceEntries.filter((e) => e.key.includes('routing') || e.metadata?.type === 'routing').length,
|
|
1415
|
+
'command-risk': sourceEntries.filter((e) => e.key.includes('command') || e.metadata?.type === 'command-risk').length,
|
|
1416
|
+
'agent-success': sourceEntries.filter((e) => e.key.includes('agent') || e.metadata?.type === 'agent-success').length,
|
|
1246
1417
|
};
|
|
1247
1418
|
// If source has no patterns, report honestly instead of substituting demo data
|
|
1248
|
-
if (Object.values(byType).every(v => v === 0)) {
|
|
1419
|
+
if (Object.values(byType).every((v) => v === 0)) {
|
|
1249
1420
|
return {
|
|
1250
1421
|
success: false,
|
|
1251
1422
|
message: 'No patterns found in source project',
|
|
@@ -1254,7 +1425,7 @@ export const hooksTransfer = {
|
|
|
1254
1425
|
};
|
|
1255
1426
|
}
|
|
1256
1427
|
if (filter) {
|
|
1257
|
-
Object.keys(byType).forEach(key => {
|
|
1428
|
+
Object.keys(byType).forEach((key) => {
|
|
1258
1429
|
if (!key.includes(filter))
|
|
1259
1430
|
delete byType[key];
|
|
1260
1431
|
});
|
|
@@ -1283,8 +1454,8 @@ export const hooksSessionStart = {
|
|
|
1283
1454
|
},
|
|
1284
1455
|
},
|
|
1285
1456
|
handler: async (params) => {
|
|
1286
|
-
const sessionId = params.sessionId
|
|
1287
|
-
const restoreLatest = params.restoreLatest;
|
|
1457
|
+
const sessionId = validateMcpString(params.sessionId, 'sessionId', 256) ?? `session-${Date.now()}`;
|
|
1458
|
+
const restoreLatest = params.restoreLatest === true;
|
|
1288
1459
|
// Phase 5: Wire ReflexionMemory session start via bridge
|
|
1289
1460
|
let sessionMemory = null;
|
|
1290
1461
|
try {
|
|
@@ -1295,7 +1466,7 @@ export const hooksSessionStart = {
|
|
|
1295
1466
|
});
|
|
1296
1467
|
if (result) {
|
|
1297
1468
|
sessionMemory = {
|
|
1298
|
-
controller: result.success ? '
|
|
1469
|
+
controller: result.success ? 'sqlite' : 'none',
|
|
1299
1470
|
restoredPatterns: 0,
|
|
1300
1471
|
};
|
|
1301
1472
|
}
|
|
@@ -1315,11 +1486,13 @@ export const hooksSessionStart = {
|
|
|
1315
1486
|
memoryPersistence: true,
|
|
1316
1487
|
},
|
|
1317
1488
|
sessionMemory: sessionMemory || { controller: 'none', restoredPatterns: 0 },
|
|
1318
|
-
previousSession: restoreLatest
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1489
|
+
previousSession: restoreLatest && sessionMemory && sessionMemory.restoredPatterns > 0
|
|
1490
|
+
? {
|
|
1491
|
+
id: sessionId,
|
|
1492
|
+
tasksRestored: sessionMemory.restoredPatterns,
|
|
1493
|
+
memoryRestored: sessionMemory.restoredPatterns,
|
|
1494
|
+
}
|
|
1495
|
+
: null,
|
|
1323
1496
|
};
|
|
1324
1497
|
},
|
|
1325
1498
|
};
|
|
@@ -1338,12 +1511,18 @@ export const hooksSessionRestore = {
|
|
|
1338
1511
|
type: 'object',
|
|
1339
1512
|
properties: {
|
|
1340
1513
|
sessionId: { type: 'string', description: 'Session ID to restore, or "latest"' },
|
|
1341
|
-
restoreAgents: {
|
|
1342
|
-
|
|
1514
|
+
restoreAgents: {
|
|
1515
|
+
type: 'boolean',
|
|
1516
|
+
description: 'Include a count of currently-live agents (default true)',
|
|
1517
|
+
},
|
|
1518
|
+
restoreTasks: {
|
|
1519
|
+
type: 'boolean',
|
|
1520
|
+
description: 'Include a count of currently-live tasks (default true)',
|
|
1521
|
+
},
|
|
1343
1522
|
},
|
|
1344
1523
|
},
|
|
1345
1524
|
handler: async (params) => {
|
|
1346
|
-
const originalSessionId = params.sessionId
|
|
1525
|
+
const originalSessionId = validateMcpString(params.sessionId, 'sessionId', 256) ?? 'latest';
|
|
1347
1526
|
const newSessionId = `session-${Date.now()}`;
|
|
1348
1527
|
const warnings = [];
|
|
1349
1528
|
let agentsRestored = 0;
|
|
@@ -1424,9 +1603,9 @@ export const hooksSessionEnd = {
|
|
|
1424
1603
|
// Read actual counts from stores
|
|
1425
1604
|
const store = loadMemoryStore();
|
|
1426
1605
|
const allEntries = Object.values(store.entries);
|
|
1427
|
-
const taskCount = allEntries.filter(e => e.key.includes('task')).length;
|
|
1428
|
-
const agentCount = allEntries.filter(e => e.key.includes('agent')).length;
|
|
1429
|
-
const patternCount = allEntries.filter(e => e.key.includes('pattern')).length;
|
|
1606
|
+
const taskCount = allEntries.filter((e) => e.key.includes('task')).length;
|
|
1607
|
+
const agentCount = allEntries.filter((e) => e.key.includes('agent')).length;
|
|
1608
|
+
const patternCount = allEntries.filter((e) => e.key.includes('pattern')).length;
|
|
1430
1609
|
const trajectoryCount = activeTrajectories.size;
|
|
1431
1610
|
// Check for pending-insights.jsonl
|
|
1432
1611
|
let insightCount = 0;
|
|
@@ -1451,7 +1630,7 @@ export const hooksSessionEnd = {
|
|
|
1451
1630
|
});
|
|
1452
1631
|
if (result) {
|
|
1453
1632
|
sessionPersistence = {
|
|
1454
|
-
controller: result.success ? '
|
|
1633
|
+
controller: result.success ? 'sqlite' : 'none',
|
|
1455
1634
|
persisted: result.success,
|
|
1456
1635
|
};
|
|
1457
1636
|
}
|
|
@@ -1521,7 +1700,7 @@ export const hooksIntelligence = {
|
|
|
1521
1700
|
},
|
|
1522
1701
|
},
|
|
1523
1702
|
handler: async (params) => {
|
|
1524
|
-
const mode = params.mode
|
|
1703
|
+
const mode = validateMcpString(params.mode, 'mode', 32) ?? 'balanced';
|
|
1525
1704
|
const enableSona = params.enableSona !== false;
|
|
1526
1705
|
const enableMoe = params.enableMoe !== false;
|
|
1527
1706
|
const enableHnsw = params.enableHnsw !== false;
|
|
@@ -1551,11 +1730,11 @@ export const hooksIntelligence = {
|
|
|
1551
1730
|
},
|
|
1552
1731
|
hnsw: {
|
|
1553
1732
|
enabled: enableHnsw,
|
|
1554
|
-
status: enableHnsw ? '
|
|
1555
|
-
implemented:
|
|
1733
|
+
status: enableHnsw ? 'fallback-only' : 'disabled',
|
|
1734
|
+
implemented: false,
|
|
1556
1735
|
indexSize: realStats.memory.indexSize,
|
|
1557
1736
|
memorySizeBytes: realStats.memory.memorySizeBytes,
|
|
1558
|
-
note: 'Pure-JS HNSW
|
|
1737
|
+
note: 'Pure-JS HNSW is a fallback path only, not on the default search path',
|
|
1559
1738
|
},
|
|
1560
1739
|
flashAttention: {
|
|
1561
1740
|
enabled: false,
|
|
@@ -1580,7 +1759,7 @@ export const hooksIntelligence = {
|
|
|
1580
1759
|
model: 'Xenova/all-MiniLM-L6-v2',
|
|
1581
1760
|
dimension: 384,
|
|
1582
1761
|
implemented: true,
|
|
1583
|
-
note: '
|
|
1762
|
+
note: 'ONNX embeddings via Xenova/all-MiniLM-L6-v2 when available; deterministic hash fallback otherwise',
|
|
1584
1763
|
},
|
|
1585
1764
|
},
|
|
1586
1765
|
realMetrics: {
|
|
@@ -1591,14 +1770,23 @@ export const hooksIntelligence = {
|
|
|
1591
1770
|
},
|
|
1592
1771
|
implementationStatus: {
|
|
1593
1772
|
working: [
|
|
1594
|
-
'memory-store',
|
|
1595
|
-
'
|
|
1773
|
+
'memory-store',
|
|
1774
|
+
'embeddings',
|
|
1775
|
+
'trajectory-recording',
|
|
1776
|
+
'claims',
|
|
1777
|
+
'swarm-coordination',
|
|
1778
|
+
'pattern-storage',
|
|
1779
|
+
'keyword-routing',
|
|
1596
1780
|
],
|
|
1597
|
-
partial: [],
|
|
1781
|
+
partial: ['hnsw-index'],
|
|
1598
1782
|
notImplemented: [],
|
|
1599
1783
|
removed: [
|
|
1600
|
-
'moe-routing',
|
|
1601
|
-
'
|
|
1784
|
+
'moe-routing',
|
|
1785
|
+
'flash-attention',
|
|
1786
|
+
'lora-adapter',
|
|
1787
|
+
'native-sona-engine',
|
|
1788
|
+
'native-router',
|
|
1789
|
+
'native-attention',
|
|
1602
1790
|
],
|
|
1603
1791
|
},
|
|
1604
1792
|
version: '3.0.0-alpha.102',
|