@monoes/monomindcli 2.9.3 → 2.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/helpers/intelligence.cjs +27 -6
- package/.claude/skills/agentic-jujutsu/SKILL.md +17 -15
- package/.claude/skills/hive-mind-advanced/SKILL.md +212 -559
- package/.claude/skills/hooks-automation/SKILL.md +1 -1
- package/.claude/skills/mastermind-createorg/SKILL.md +2 -1
- package/.claude/skills/pair-programming/SKILL.md +1 -1
- package/.claude/skills/performance-analysis/SKILL.md +228 -484
- package/.claude/skills/swarm-advanced/SKILL.md +2 -2
- package/.claude/skills/swarm-orchestration/SKILL.md +220 -150
- package/.claude/skills/verification-quality/SKILL.md +247 -571
- package/README.md +6 -2
- 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 +3 -3
- 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 +113 -26
- package/dist/src/commands/autopilot.js.map +1 -1
- package/dist/src/commands/doc.d.ts.map +1 -1
- package/dist/src/commands/doc.js +14 -3
- package/dist/src/commands/doc.js.map +1 -1
- package/dist/src/commands/doctor-env-checks.d.ts +1 -1
- package/dist/src/commands/doctor-env-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +30 -1
- package/dist/src/commands/doctor.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.js +8 -8
- 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/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +80 -7
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/mcp.d.ts.map +1 -1
- package/dist/src/commands/mcp.js +78 -2
- package/dist/src/commands/mcp.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.d.ts.map +1 -1
- package/dist/src/commands/org.js +114 -1
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/commands/performance.js +1 -1
- package/dist/src/commands/performance.js.map +1 -1
- package/dist/src/commands/swarm.d.ts.map +1 -1
- package/dist/src/commands/swarm.js +144 -98
- package/dist/src/commands/swarm.js.map +1 -1
- package/dist/src/init/claudemd-generator.js +1 -1
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +13 -3
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/kimi-generator.d.ts +3 -2
- package/dist/src/init/kimi-generator.d.ts.map +1 -1
- package/dist/src/init/kimi-generator.js +45 -13
- package/dist/src/init/kimi-generator.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts +1 -1
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +48 -46
- 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/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 +97 -40
- 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 +406 -205
- 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/monograph/query-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/monograph/query-tools.js +48 -18
- package/dist/src/mcp-tools/monograph/query-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/embedding-operations.d.ts +4 -0
- package/dist/src/memory/embedding-operations.d.ts.map +1 -1
- package/dist/src/memory/embedding-operations.js +125 -32
- package/dist/src/memory/embedding-operations.js.map +1 -1
- package/dist/src/memory/hnsw-operations.d.ts +1 -1
- package/dist/src/memory/hnsw-operations.js +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/memory/memory-read.d.ts +1 -1
- package/dist/src/memory/memory-read.js +2 -2
- package/dist/src/memory/memory-read.js.map +1 -1
- package/dist/src/orgrt/checkpoint-ops.d.ts +11 -1
- package/dist/src/orgrt/checkpoint-ops.d.ts.map +1 -1
- package/dist/src/orgrt/checkpoint-ops.js +15 -1
- package/dist/src/orgrt/checkpoint-ops.js.map +1 -1
- package/dist/src/orgrt/checkpoint.js +1 -1
- package/dist/src/orgrt/checkpoint.js.map +1 -1
- package/dist/src/orgrt/daemon.d.ts +11 -3
- package/dist/src/orgrt/daemon.d.ts.map +1 -1
- package/dist/src/orgrt/daemon.js +317 -93
- package/dist/src/orgrt/daemon.js.map +1 -1
- package/dist/src/orgrt/decisions.d.ts +13 -0
- package/dist/src/orgrt/decisions.d.ts.map +1 -1
- package/dist/src/orgrt/decisions.js +95 -0
- package/dist/src/orgrt/decisions.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/orgrt/session.d.ts +27 -1
- package/dist/src/orgrt/session.d.ts.map +1 -1
- package/dist/src/orgrt/session.js +42 -3
- package/dist/src/orgrt/session.js.map +1 -1
- package/dist/src/orgrt/task-dag.d.ts +11 -1
- package/dist/src/orgrt/task-dag.d.ts.map +1 -1
- package/dist/src/orgrt/task-dag.js +93 -2
- package/dist/src/orgrt/task-dag.js.map +1 -1
- package/dist/src/orgrt/templates.d.ts.map +1 -1
- package/dist/src/orgrt/templates.js +24 -0
- package/dist/src/orgrt/templates.js.map +1 -1
- package/dist/src/orgrt/types.d.ts +70 -1
- package/dist/src/orgrt/types.d.ts.map +1 -1
- package/dist/src/orgrt/types.js +19 -0
- package/dist/src/orgrt/types.js.map +1 -1
- package/dist/src/orgrt/vercel-providers.d.ts.map +1 -1
- package/dist/src/orgrt/vercel-providers.js +9 -4
- package/dist/src/orgrt/vercel-providers.js.map +1 -1
- package/dist/src/orgrt/vercel-runner.d.ts +0 -23
- package/dist/src/orgrt/vercel-runner.d.ts.map +1 -1
- package/dist/src/orgrt/vercel-runner.js +26 -1
- package/dist/src/orgrt/vercel-runner.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
- package/.claude/commands/mastermind/approvev1.md +0 -94
- package/.claude/commands/mastermind/architect.md +0 -52
- package/.claude/commands/mastermind/autodev.md +0 -28
- package/.claude/commands/mastermind/build.md +0 -23
- package/.claude/commands/mastermind/finish.md +0 -17
- package/.claude/commands/mastermind/runorgv1.md +0 -159
- package/.claude/commands/mastermind/taskdev.md +0 -23
- package/.claude/commands/mastermind/tdd.md +0 -19
- package/.claude/commands/mastermind/verify.md +0 -19
- package/.claude/skills/mastermind-approvev1/SKILL.md +0 -191
- package/.claude/skills/mastermind-architect/SKILL.md +0 -862
- package/.claude/skills/mastermind-autodev/SKILL.md +0 -360
- package/.claude/skills/mastermind-build/SKILL.md +0 -169
- package/.claude/skills/mastermind-companies/SKILL.md +0 -256
- package/.claude/skills/mastermind-content/SKILL.md +0 -197
- package/.claude/skills/mastermind-costs/SKILL.md +0 -151
- package/.claude/skills/mastermind-finance/SKILL.md +0 -166
- package/.claude/skills/mastermind-finish/SKILL.md +0 -251
- package/.claude/skills/mastermind-heartbeatv1/SKILL.md +0 -167
- package/.claude/skills/mastermind-instance-settings/SKILL.md +0 -315
- package/.claude/skills/mastermind-marketing/SKILL.md +0 -228
- package/.claude/skills/mastermind-marketing/references/copywriting-frameworks.md +0 -181
- package/.claude/skills/mastermind-marketing/references/persuasion-psychology.md +0 -158
- package/.claude/skills/mastermind-ops/SKILL.md +0 -168
- package/.claude/skills/mastermind-org-chart/SKILL.md +0 -209
- package/.claude/skills/mastermind-project-detail/SKILL.md +0 -249
- package/.claude/skills/mastermind-project-workspace/SKILL.md +0 -244
- package/.claude/skills/mastermind-projects/SKILL.md +0 -167
- package/.claude/skills/mastermind-runorgv1/SKILL.md +0 -731
- package/.claude/skills/mastermind-sales/SKILL.md +0 -170
- package/.claude/skills/mastermind-taskdev/SKILL.md +0 -377
- package/.claude/skills/mastermind-taskdev/code-quality-reviewer-prompt.md +0 -60
- package/.claude/skills/mastermind-taskdev/final-reviewer-prompt.md +0 -144
- package/.claude/skills/mastermind-taskdev/implementer-prompt.md +0 -114
- package/.claude/skills/mastermind-taskdev/spec-reviewer-prompt.md +0 -80
- package/.claude/skills/mastermind-tdd/SKILL.md +0 -424
- package/.claude/skills/mastermind-verify/SKILL.md +0 -196
- package/.claude/skills/mastermind-wiki/SKILL.md +0 -314
- package/.claude/skills/monolean-review/SKILL.md +0 -57
|
@@ -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',
|
|
@@ -235,7 +245,10 @@ export const hooksRoute = {
|
|
|
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,14 +258,11 @@ 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;
|
|
257
267
|
// Phase 5: Try memory backend SemanticRouter / LearningSystem first
|
|
258
268
|
if (useSemanticRouter) {
|
|
@@ -265,7 +275,7 @@ export const hooksRoute = {
|
|
|
265
275
|
if (routeConfidence > 0.5) {
|
|
266
276
|
const agents = memoryRoute.routes.map((r) => r.agentType);
|
|
267
277
|
const complexity = task.length > 200 ? 'high' : task.length < 50 ? 'low' : 'medium';
|
|
268
|
-
const memoryMethod = 'memory-
|
|
278
|
+
const memoryMethod = 'memory-sqlite';
|
|
269
279
|
const memoryConfidence = Math.round(routeConfidence * 100) / 100;
|
|
270
280
|
const matchedPattern = topRoute.pattern ?? task.slice(0, 60);
|
|
271
281
|
// Record the route recommendation so post-task can join the actual outcome
|
|
@@ -284,7 +294,7 @@ export const hooksRoute = {
|
|
|
284
294
|
task,
|
|
285
295
|
routing: {
|
|
286
296
|
method: memoryMethod,
|
|
287
|
-
backend: '
|
|
297
|
+
backend: 'sqlite',
|
|
288
298
|
latencyMs: 0,
|
|
289
299
|
throughput: 'N/A',
|
|
290
300
|
},
|
|
@@ -293,19 +303,25 @@ export const hooksRoute = {
|
|
|
293
303
|
primaryAgent: {
|
|
294
304
|
type: agents[0],
|
|
295
305
|
confidence: memoryConfidence,
|
|
296
|
-
reason: `memory:
|
|
306
|
+
reason: `memory:sqlite: "${matchedPattern}" (${Math.round(routeConfidence * 100)}%)`,
|
|
297
307
|
},
|
|
298
308
|
alternativeAgents: agents.slice(1).map((agent, i) => ({
|
|
299
309
|
type: agent,
|
|
300
|
-
confidence: Math.round((routeConfidence -
|
|
301
|
-
reason: 'Alternative from
|
|
310
|
+
confidence: Math.round((routeConfidence - 0.1 * (i + 1)) * 100) / 100,
|
|
311
|
+
reason: 'Alternative from sqlite',
|
|
302
312
|
})),
|
|
303
313
|
estimatedMetrics: {
|
|
304
314
|
successProbability: memoryConfidence,
|
|
305
|
-
estimatedDuration: complexity === 'high'
|
|
315
|
+
estimatedDuration: complexity === 'high'
|
|
316
|
+
? '2-4 hours'
|
|
317
|
+
: complexity === 'medium'
|
|
318
|
+
? '30-60 min'
|
|
319
|
+
: '10-30 min',
|
|
306
320
|
complexity,
|
|
307
321
|
},
|
|
308
|
-
swarmRecommendation: agents.length > 2
|
|
322
|
+
swarmRecommendation: agents.length > 2
|
|
323
|
+
? { topology: 'hierarchical', agents, coordination: 'queen-led' }
|
|
324
|
+
: null,
|
|
309
325
|
};
|
|
310
326
|
}
|
|
311
327
|
}
|
|
@@ -338,8 +354,11 @@ export const hooksRoute = {
|
|
|
338
354
|
if (intelSuggestion && intelSuggestion.confidence > 0.5) {
|
|
339
355
|
// Prepend neural agents (deduped) and boost confidence
|
|
340
356
|
const existingSet = new Set(agents);
|
|
341
|
-
const neuralOnly = intelSuggestion.agents.filter(a => !existingSet.has(a));
|
|
342
|
-
agents = [
|
|
357
|
+
const neuralOnly = intelSuggestion.agents.filter((a) => !existingSet.has(a));
|
|
358
|
+
agents = [
|
|
359
|
+
...intelSuggestion.agents,
|
|
360
|
+
...agents.filter((a) => !new Set(intelSuggestion.agents).has(a)),
|
|
361
|
+
];
|
|
343
362
|
const neuralWeight = intelSuggestion.confidence > 0.7 ? 0.65 : 0.5;
|
|
344
363
|
const keywordWeight = 1 - neuralWeight;
|
|
345
364
|
confidence = Math.min(0.95, intelSuggestion.confidence * neuralWeight +
|
|
@@ -379,7 +398,7 @@ export const hooksRoute = {
|
|
|
379
398
|
throughput: routingLatencyMs > 0 ? `${Math.round(1000 / routingLatencyMs)} routes/s` : 'N/A',
|
|
380
399
|
},
|
|
381
400
|
matchedPattern,
|
|
382
|
-
semanticMatches: semanticResult.slice(0, 3).map(r => ({
|
|
401
|
+
semanticMatches: semanticResult.slice(0, 3).map((r) => ({
|
|
383
402
|
pattern: r.intent,
|
|
384
403
|
score: Math.round(r.score * 100) / 100,
|
|
385
404
|
})),
|
|
@@ -392,7 +411,7 @@ export const hooksRoute = {
|
|
|
392
411
|
},
|
|
393
412
|
alternativeAgents: agents.slice(1).map((agent, i) => ({
|
|
394
413
|
type: agent,
|
|
395
|
-
confidence: Math.round((confidence -
|
|
414
|
+
confidence: Math.round((confidence - 0.1 * (i + 1)) * 100) / 100,
|
|
396
415
|
reason: `Alternative agent for ${agent} capabilities`,
|
|
397
416
|
})),
|
|
398
417
|
estimatedMetrics: {
|
|
@@ -400,11 +419,13 @@ export const hooksRoute = {
|
|
|
400
419
|
estimatedDuration: complexity === 'high' ? '2-4 hours' : complexity === 'medium' ? '30-60 min' : '10-30 min',
|
|
401
420
|
complexity,
|
|
402
421
|
},
|
|
403
|
-
swarmRecommendation: agents.length > 2
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
422
|
+
swarmRecommendation: agents.length > 2
|
|
423
|
+
? {
|
|
424
|
+
topology: 'hierarchical',
|
|
425
|
+
agents,
|
|
426
|
+
coordination: 'queen-led',
|
|
427
|
+
}
|
|
428
|
+
: null,
|
|
408
429
|
};
|
|
409
430
|
},
|
|
410
431
|
};
|
|
@@ -419,19 +440,19 @@ export const hooksRouteSemantic = {
|
|
|
419
440
|
type: 'object',
|
|
420
441
|
properties: {
|
|
421
442
|
task: { type: 'string', description: 'Task description' },
|
|
422
|
-
debug: {
|
|
443
|
+
debug: {
|
|
444
|
+
type: 'boolean',
|
|
445
|
+
description: 'Include all route scores in the response (default: false)',
|
|
446
|
+
},
|
|
423
447
|
},
|
|
424
448
|
required: ['task'],
|
|
425
449
|
},
|
|
426
450
|
handler: async (params) => {
|
|
427
451
|
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;
|
|
452
|
+
const task = validateMcpString(params.task, 'task', MAX_ROUTE_TASK_LEN);
|
|
432
453
|
const debug = params.debug === true;
|
|
433
|
-
if (!task
|
|
434
|
-
throw new Error('task is required');
|
|
454
|
+
if (!task) {
|
|
455
|
+
throw new Error('task is required (non-empty string, no control chars, max 2000 chars)');
|
|
435
456
|
}
|
|
436
457
|
const { createConfiguredRouteLayer } = await import('../routing/route-layer-factory.js');
|
|
437
458
|
const layer = await createConfiguredRouteLayer({ debug });
|
|
@@ -445,7 +466,9 @@ export const hooksRouteSemantic = {
|
|
|
445
466
|
routingMethod: `routing-pkg:${result.method}`,
|
|
446
467
|
confidence: result.confidence,
|
|
447
468
|
learningMode: 'js',
|
|
448
|
-
}).catch(() => {
|
|
469
|
+
}).catch(() => {
|
|
470
|
+
/* non-fatal — outcome joining is best-effort */
|
|
471
|
+
});
|
|
449
472
|
return { routeId, task, ...result };
|
|
450
473
|
},
|
|
451
474
|
};
|
|
@@ -460,14 +483,14 @@ export const hooksMetrics = {
|
|
|
460
483
|
},
|
|
461
484
|
},
|
|
462
485
|
handler: async (params) => {
|
|
463
|
-
const period = params.period
|
|
486
|
+
const period = validateMcpString(params.period, 'period', 32) ?? '24h';
|
|
464
487
|
// Try to read real counts from memory store
|
|
465
488
|
const store = loadMemoryStore();
|
|
466
489
|
const entries = Object.values(store.entries);
|
|
467
490
|
// 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'));
|
|
491
|
+
const patternEntries = entries.filter((e) => e.key.includes('pattern'));
|
|
492
|
+
const routingEntries = entries.filter((e) => e.key.includes('route') || e.key.includes('routing'));
|
|
493
|
+
const taskEntries = entries.filter((e) => e.key.includes('task'));
|
|
471
494
|
if (entries.length === 0) {
|
|
472
495
|
return {
|
|
473
496
|
_real: true,
|
|
@@ -568,23 +591,25 @@ export const hooksPreTask = {
|
|
|
568
591
|
// in causal-graph edges persisted to SQLite. An uncapped ID can inflate the DB key
|
|
569
592
|
// column and every JSON payload that includes the ID.
|
|
570
593
|
const MAX_TASK_ID_LEN = 256;
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
594
|
+
const taskId = validateMcpString(params.taskId, 'taskId', MAX_TASK_ID_LEN);
|
|
595
|
+
if (!taskId) {
|
|
596
|
+
return { error: 'taskId is required (non-empty string, no control chars, max 256 chars)' };
|
|
597
|
+
}
|
|
575
598
|
// Cap description: it is forwarded to generateEmbedding twice (ERL heuristics
|
|
576
599
|
// + TextGrad gradient queries) and used in O(n) keyword extraction.
|
|
577
600
|
// 16 KB matches the cap applied in hooks_route and hooksPatternSearch.
|
|
578
601
|
const MAX_PRE_TASK_DESC_LEN = 16 * 1024;
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
const filePath = params.filePath;
|
|
602
|
+
const description = validateMcpString(params.description, 'description', MAX_PRE_TASK_DESC_LEN);
|
|
603
|
+
if (!description) {
|
|
604
|
+
return { error: 'description is required (non-empty string, no control chars, max 16KB)' };
|
|
605
|
+
}
|
|
606
|
+
const filePath = validateMcpString(params.filePath, 'filePath', 4 * 1024) ?? undefined;
|
|
584
607
|
const suggestion = suggestAgentsForTask(description);
|
|
585
608
|
// Determine complexity
|
|
586
609
|
const descLower = description.toLowerCase();
|
|
587
|
-
const complexity = descLower.includes('complex') ||
|
|
610
|
+
const complexity = descLower.includes('complex') ||
|
|
611
|
+
descLower.includes('architecture') ||
|
|
612
|
+
description.length > 200
|
|
588
613
|
? 'high'
|
|
589
614
|
: descLower.includes('simple') || descLower.includes('fix') || description.length < 50
|
|
590
615
|
? 'low'
|
|
@@ -603,14 +628,16 @@ export const hooksPreTask = {
|
|
|
603
628
|
limit: 3,
|
|
604
629
|
threshold: 0.6,
|
|
605
630
|
});
|
|
606
|
-
for (const r of
|
|
631
|
+
for (const r of heuristicResults?.results ?? []) {
|
|
607
632
|
try {
|
|
608
633
|
const h = JSON.parse(r.content ?? '{}');
|
|
609
634
|
if (h.action && h.confidence !== undefined && h.confidence >= 0.6) {
|
|
610
635
|
erlHints.push(`ERL hint (conf=${h.confidence.toFixed(2)}): use "${h.action}" for tasks involving "${h.condition ?? 'similar context'}"`);
|
|
611
636
|
}
|
|
612
637
|
}
|
|
613
|
-
catch {
|
|
638
|
+
catch {
|
|
639
|
+
/* skip malformed */
|
|
640
|
+
}
|
|
614
641
|
}
|
|
615
642
|
// TextGrad: also inject relevant past failure gradients to guide away from known pitfalls
|
|
616
643
|
// Source: https://arxiv.org/abs/2406.07496
|
|
@@ -620,7 +647,7 @@ export const hooksPreTask = {
|
|
|
620
647
|
limit: 2,
|
|
621
648
|
threshold: 0.55,
|
|
622
649
|
});
|
|
623
|
-
for (const r of
|
|
650
|
+
for (const r of gradientResults?.results ?? []) {
|
|
624
651
|
const critique = r.content ?? '';
|
|
625
652
|
if (critique && critique.length > 10) {
|
|
626
653
|
erlHints.push(`TextGrad warning: ${critique.slice(0, 120)}`);
|
|
@@ -628,18 +655,36 @@ export const hooksPreTask = {
|
|
|
628
655
|
}
|
|
629
656
|
}
|
|
630
657
|
}
|
|
631
|
-
catch {
|
|
658
|
+
catch {
|
|
659
|
+
/* non-critical */
|
|
660
|
+
}
|
|
632
661
|
// NOTE: a LATS planning pass used to be attempted here via
|
|
633
662
|
// `import('@monoes/hooks').buildLATSPlan` — that function never existed
|
|
634
663
|
// in the package (the planning module was removed), so the import failed
|
|
635
664
|
// silently on every call. The dead block was removed.
|
|
636
665
|
let plan;
|
|
666
|
+
// P2-15: Retrieve Reflexion reflections for this task — past failures
|
|
667
|
+
// on similar tasks are injected as recommendations so the agent avoids
|
|
668
|
+
// repeating mistakes. This closes the self-learning loop.
|
|
669
|
+
let reflexionWarnings = [];
|
|
670
|
+
try {
|
|
671
|
+
const hooksPkg = await import('@monoes/hooks').catch(() => null);
|
|
672
|
+
const fn = hooksPkg?.getReflectionsForTask;
|
|
673
|
+
if (typeof fn === 'function') {
|
|
674
|
+
const cwd = process.cwd();
|
|
675
|
+
const reflections = await fn(cwd, description, 3);
|
|
676
|
+
reflexionWarnings = reflections.map(r => `⚠ Past failure: ${r.reflection.slice(0, 200)}`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
catch {
|
|
680
|
+
/* non-critical — reflexion store may not exist yet */
|
|
681
|
+
}
|
|
637
682
|
return {
|
|
638
683
|
taskId,
|
|
639
684
|
description,
|
|
640
685
|
suggestedAgents: suggestion.agents.map((agent, i) => ({
|
|
641
686
|
type: agent,
|
|
642
|
-
confidence: suggestion.confidence -
|
|
687
|
+
confidence: suggestion.confidence - 0.05 * i,
|
|
643
688
|
reason: i === 0
|
|
644
689
|
? `Primary agent for ${agent} tasks based on learned patterns`
|
|
645
690
|
: `Alternative agent with ${agent} capabilities`,
|
|
@@ -649,8 +694,11 @@ export const hooksPreTask = {
|
|
|
649
694
|
risks: complexity === 'high' ? ['Complex task may require multiple iterations'] : [],
|
|
650
695
|
recommendations: [
|
|
651
696
|
`Use ${suggestion.agents[0]} as primary agent`,
|
|
652
|
-
suggestion.agents.length > 2
|
|
697
|
+
suggestion.agents.length > 2
|
|
698
|
+
? 'Consider using swarm coordination'
|
|
699
|
+
: 'Single agent recommended',
|
|
653
700
|
...erlHints,
|
|
701
|
+
...reflexionWarnings,
|
|
654
702
|
],
|
|
655
703
|
modelRouting,
|
|
656
704
|
plan,
|
|
@@ -668,9 +716,15 @@ export const hooksPostTask = {
|
|
|
668
716
|
success: { type: 'boolean', description: 'Whether task was successful' },
|
|
669
717
|
agent: { type: 'string', description: 'Agent that completed the task' },
|
|
670
718
|
quality: { type: 'number', description: 'Quality score (0-1)' },
|
|
671
|
-
task: {
|
|
719
|
+
task: {
|
|
720
|
+
type: 'string',
|
|
721
|
+
description: 'Task description text (used for learning keyword extraction)',
|
|
722
|
+
},
|
|
672
723
|
storeDecisions: { type: 'boolean', description: 'Also store routing decision in memory DB' },
|
|
673
|
-
routeId: {
|
|
724
|
+
routeId: {
|
|
725
|
+
type: 'string',
|
|
726
|
+
description: 'Route ID from a prior hooks_route call — joins the recommendation to this outcome',
|
|
727
|
+
},
|
|
674
728
|
},
|
|
675
729
|
required: ['taskId'],
|
|
676
730
|
},
|
|
@@ -680,10 +734,10 @@ export const hooksPostTask = {
|
|
|
680
734
|
// into causal-graph edge IDs persisted to the DB. Without a cap an attacker can
|
|
681
735
|
// inflate every row that stores the raw ID.
|
|
682
736
|
const MAX_POST_TASK_ID_LEN = 256;
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
737
|
+
const taskId = validateMcpString(params.taskId, 'taskId', MAX_POST_TASK_ID_LEN);
|
|
738
|
+
if (!taskId) {
|
|
739
|
+
return { error: 'taskId is required (non-empty string, no control chars, max 256 chars)' };
|
|
740
|
+
}
|
|
687
741
|
// The success flag, when the caller asserts it (--success true), is taken as
|
|
688
742
|
// ground truth. But callers usually do NOT pass it. Rather than treating every
|
|
689
743
|
// unverified task as "unknown" (and thus excluding it from learning), we now
|
|
@@ -702,7 +756,9 @@ export const hooksPostTask = {
|
|
|
702
756
|
const explicitSuccess = typeof params.success === 'boolean';
|
|
703
757
|
let outcomeKnown = explicitSuccess;
|
|
704
758
|
let success = params.success !== false;
|
|
705
|
-
let successSource = explicitSuccess
|
|
759
|
+
let successSource = explicitSuccess
|
|
760
|
+
? 'explicit'
|
|
761
|
+
: 'unknown';
|
|
706
762
|
if (!explicitSuccess) {
|
|
707
763
|
const derived = await deriveRecentSuccess(getRouteOutcomesBaseDir());
|
|
708
764
|
if (derived !== null) {
|
|
@@ -715,19 +771,17 @@ export const hooksPostTask = {
|
|
|
715
771
|
// feedback record and used as a tag string in the JSON store. An uncapped
|
|
716
772
|
// agent value inflates the on-disk store entry.
|
|
717
773
|
const MAX_POST_TASK_AGENT_LEN = 256;
|
|
718
|
-
const
|
|
719
|
-
const
|
|
720
|
-
?
|
|
721
|
-
:
|
|
722
|
-
|
|
774
|
+
const agent = validateMcpString(params.agent, 'agent', MAX_POST_TASK_AGENT_LEN) ?? undefined;
|
|
775
|
+
const quality = typeof params.quality === 'number' && Number.isFinite(params.quality)
|
|
776
|
+
? Math.max(0, Math.min(1, params.quality))
|
|
777
|
+
: success
|
|
778
|
+
? 0.85
|
|
779
|
+
: 0.3;
|
|
723
780
|
const startTime = Date.now();
|
|
724
781
|
// Cap task description: passed to generateEmbedding via bridgeRecordFeedback
|
|
725
782
|
// and persisted to route-outcomes.jsonl. 16 KB matches hooks_route cap.
|
|
726
783
|
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;
|
|
784
|
+
const cappedPostTask = validateMcpString(params.task, 'task', MAX_POST_TASK_LEN) ?? undefined;
|
|
731
785
|
// Phase 3: Wire recordFeedback through bridge → LearningSystem + ReasoningBank
|
|
732
786
|
let feedbackResult = null;
|
|
733
787
|
try {
|
|
@@ -735,9 +789,19 @@ export const hooksPostTask = {
|
|
|
735
789
|
feedbackResult = await bridge.bridgeRecordFeedback({
|
|
736
790
|
taskType: agent ?? 'task',
|
|
737
791
|
action: cappedPostTask?.slice(0, 80) ?? taskId,
|
|
738
|
-
outcome: success ? 'success' :
|
|
792
|
+
outcome: success ? 'success' : outcomeKnown ? 'failure' : 'partial',
|
|
739
793
|
confidence: quality,
|
|
740
|
-
metadata: {
|
|
794
|
+
metadata: {
|
|
795
|
+
taskId,
|
|
796
|
+
duration: typeof params.duration === 'number' && Number.isFinite(params.duration)
|
|
797
|
+
? params.duration
|
|
798
|
+
: undefined,
|
|
799
|
+
patterns: Array.isArray(params.patterns)
|
|
800
|
+
? params.patterns
|
|
801
|
+
.filter((p) => typeof p === 'string' && p.length > 0 && p.length <= 200)
|
|
802
|
+
.slice(0, 50)
|
|
803
|
+
: undefined,
|
|
804
|
+
},
|
|
741
805
|
});
|
|
742
806
|
}
|
|
743
807
|
catch (e) {
|
|
@@ -767,7 +831,11 @@ export const hooksPostTask = {
|
|
|
767
831
|
const taskText = cappedPostTask || '';
|
|
768
832
|
const outcomeKeywords = extractKeywords(taskText);
|
|
769
833
|
let outcomePersisted = false;
|
|
770
|
-
if (outcomeKnown &&
|
|
834
|
+
if (outcomeKnown &&
|
|
835
|
+
taskText &&
|
|
836
|
+
agent &&
|
|
837
|
+
agent.length <= 100 &&
|
|
838
|
+
/^[a-zA-Z0-9_-]+$/.test(agent)) {
|
|
771
839
|
try {
|
|
772
840
|
const outcomes = loadRoutingOutcomes();
|
|
773
841
|
outcomes.push({
|
|
@@ -781,7 +849,9 @@ export const hooksPostTask = {
|
|
|
781
849
|
saveRoutingOutcomes(outcomes);
|
|
782
850
|
outcomePersisted = true;
|
|
783
851
|
}
|
|
784
|
-
catch {
|
|
852
|
+
catch {
|
|
853
|
+
/* non-critical */
|
|
854
|
+
}
|
|
785
855
|
}
|
|
786
856
|
// Join this outcome back onto the original route recommendation. This is the
|
|
787
857
|
// recommendation→actual→success link that routing-accuracy metrics and SONA
|
|
@@ -797,7 +867,10 @@ export const hooksPostTask = {
|
|
|
797
867
|
quality: typeof params.quality === 'number' ? params.quality : undefined,
|
|
798
868
|
};
|
|
799
869
|
if (params.routeId) {
|
|
800
|
-
|
|
870
|
+
const routeId = validateMcpString(params.routeId, 'routeId', 256);
|
|
871
|
+
if (routeId) {
|
|
872
|
+
await joinOutcome(getRouteOutcomesBaseDir(), routeId, outcome);
|
|
873
|
+
}
|
|
801
874
|
}
|
|
802
875
|
else {
|
|
803
876
|
await joinLatestUnresolved(getRouteOutcomesBaseDir(), outcome);
|
|
@@ -822,7 +895,9 @@ export const hooksPostTask = {
|
|
|
822
895
|
});
|
|
823
896
|
}
|
|
824
897
|
}
|
|
825
|
-
catch {
|
|
898
|
+
catch {
|
|
899
|
+
/* non-critical */
|
|
900
|
+
}
|
|
826
901
|
}
|
|
827
902
|
// Optionally store in memory DB for cross-session vector retrieval
|
|
828
903
|
if (params.storeDecisions && taskText && agent) {
|
|
@@ -832,12 +907,20 @@ export const hooksPostTask = {
|
|
|
832
907
|
await storeFn({
|
|
833
908
|
key: `routing-decision:${taskId}`,
|
|
834
909
|
namespace: 'patterns',
|
|
835
|
-
value: JSON.stringify({
|
|
910
|
+
value: JSON.stringify({
|
|
911
|
+
task: taskText,
|
|
912
|
+
agent,
|
|
913
|
+
success,
|
|
914
|
+
quality,
|
|
915
|
+
keywords: outcomeKeywords,
|
|
916
|
+
}),
|
|
836
917
|
tags: ['routing-decision'],
|
|
837
918
|
});
|
|
838
919
|
}
|
|
839
920
|
}
|
|
840
|
-
catch {
|
|
921
|
+
catch {
|
|
922
|
+
/* non-critical */
|
|
923
|
+
}
|
|
841
924
|
}
|
|
842
925
|
const duration = Date.now() - startTime;
|
|
843
926
|
// TextGrad: Store textual gradient critique for failed tasks
|
|
@@ -857,21 +940,28 @@ export const hooksPostTask = {
|
|
|
857
940
|
});
|
|
858
941
|
}
|
|
859
942
|
}
|
|
860
|
-
catch {
|
|
943
|
+
catch {
|
|
944
|
+
/* non-critical */
|
|
945
|
+
}
|
|
861
946
|
}
|
|
862
947
|
// MAR: Structured multi-agent reflection on failure
|
|
863
948
|
// Source: https://arxiv.org/html/2512.20845 (MAR — December 2025)
|
|
864
|
-
const marReflection = !success
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
949
|
+
const marReflection = !success
|
|
950
|
+
? {
|
|
951
|
+
needed: true,
|
|
952
|
+
suggestedAgents: [
|
|
953
|
+
{ role: 'diagnoser', description: 'Analyze root cause of task failure' },
|
|
954
|
+
{ role: 'critic-1', description: 'Critique from correctness angle (temperature 0.3)' },
|
|
955
|
+
{ role: 'critic-2', description: 'Critique from efficiency angle (temperature 0.8)' },
|
|
956
|
+
{
|
|
957
|
+
role: 'aggregator',
|
|
958
|
+
description: 'Synthesize critiques into actionable reflection heuristic',
|
|
959
|
+
},
|
|
960
|
+
],
|
|
961
|
+
storeAs: 'heuristics',
|
|
962
|
+
note: 'Spawn agents sequentially: Diagnoser → Critics in parallel → Aggregator',
|
|
963
|
+
}
|
|
964
|
+
: { needed: false };
|
|
875
965
|
return {
|
|
876
966
|
taskId,
|
|
877
967
|
success,
|
|
@@ -882,15 +972,17 @@ export const hooksPostTask = {
|
|
|
882
972
|
patternsUpdated: feedbackResult?.success ? (success ? 2 : 1) : 0,
|
|
883
973
|
newPatterns: success ? 1 : 0,
|
|
884
974
|
trajectoryId: `traj-${Date.now()}`,
|
|
885
|
-
controller: feedbackResult?.success ? '
|
|
975
|
+
controller: feedbackResult?.success ? 'sqlite' : 'none',
|
|
886
976
|
outcomePersisted,
|
|
887
977
|
},
|
|
888
978
|
quality,
|
|
889
|
-
feedback: feedbackResult
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
979
|
+
feedback: feedbackResult
|
|
980
|
+
? {
|
|
981
|
+
recorded: feedbackResult.success,
|
|
982
|
+
controller: feedbackResult.success ? 'sqlite' : 'unavailable',
|
|
983
|
+
updates: feedbackResult.success ? 1 : 0,
|
|
984
|
+
}
|
|
985
|
+
: { recorded: false, controller: 'unavailable', updates: 0 },
|
|
894
986
|
marReflection,
|
|
895
987
|
timestamp: new Date().toISOString(),
|
|
896
988
|
};
|
|
@@ -913,10 +1005,10 @@ export const hooksExplain = {
|
|
|
913
1005
|
// Cap task: forwarded to suggestAgentsForTask (O(n) keyword loop + extractKeywords),
|
|
914
1006
|
// .toLowerCase() (O(n)), and reflected verbatim in the response.
|
|
915
1007
|
const MAX_EXPLAIN_TASK_LEN = 16 * 1024;
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
1008
|
+
const task = validateMcpString(params.task, 'task', MAX_EXPLAIN_TASK_LEN);
|
|
1009
|
+
if (!task) {
|
|
1010
|
+
return { error: 'task is required (non-empty string, no control chars, max 16KB)' };
|
|
1011
|
+
}
|
|
920
1012
|
const suggestion = suggestAgentsForTask(task);
|
|
921
1013
|
const taskLower = task.toLowerCase();
|
|
922
1014
|
// Determine matched patterns
|
|
@@ -939,7 +1031,7 @@ export const hooksExplain = {
|
|
|
939
1031
|
const data = JSON.parse(readFileSync(outcomesPath, 'utf-8'));
|
|
940
1032
|
const outcomes = data.outcomes || [];
|
|
941
1033
|
if (outcomes.length > 0) {
|
|
942
|
-
historicalSuccess = outcomes.filter(o => o.success).length / outcomes.length;
|
|
1034
|
+
historicalSuccess = outcomes.filter((o) => o.success).length / outcomes.length;
|
|
943
1035
|
historicalNote = `Calculated from ${outcomes.length} recorded outcomes`;
|
|
944
1036
|
}
|
|
945
1037
|
}
|
|
@@ -954,14 +1046,40 @@ export const hooksExplain = {
|
|
|
954
1046
|
explanation: `The routing decision was made based on keyword analysis of the task description. ` +
|
|
955
1047
|
`The task contains keywords that match the "${suggestion.agents[0]}" specialization with ${(suggestion.confidence * 100).toFixed(0)}% confidence.`,
|
|
956
1048
|
factors: [
|
|
957
|
-
{
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
{
|
|
1049
|
+
{
|
|
1050
|
+
factor: 'Keyword Match',
|
|
1051
|
+
weight: 0.4,
|
|
1052
|
+
value: suggestion.confidence,
|
|
1053
|
+
impact: 'Primary routing signal',
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
factor: 'Historical Success',
|
|
1057
|
+
weight: 0.3,
|
|
1058
|
+
value: historicalSuccess,
|
|
1059
|
+
impact: historicalNote,
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
factor: 'Agent Availability',
|
|
1063
|
+
weight: 0.2,
|
|
1064
|
+
value: null,
|
|
1065
|
+
impact: 'Agent availability tracking not implemented',
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
factor: 'Task Complexity',
|
|
1069
|
+
weight: 0.1,
|
|
1070
|
+
value: task.length > 100 ? 0.8 : 0.3,
|
|
1071
|
+
impact: 'Complexity assessment',
|
|
1072
|
+
},
|
|
964
1073
|
],
|
|
1074
|
+
patterns: matchedPatterns.length > 0
|
|
1075
|
+
? matchedPatterns
|
|
1076
|
+
: [
|
|
1077
|
+
{
|
|
1078
|
+
pattern: 'general-task',
|
|
1079
|
+
matchScore: 0.7,
|
|
1080
|
+
examples: ['Default pattern for unclassified tasks'],
|
|
1081
|
+
},
|
|
1082
|
+
],
|
|
965
1083
|
decision: {
|
|
966
1084
|
agent: suggestion.agents[0],
|
|
967
1085
|
confidence: suggestion.confidence,
|
|
@@ -990,12 +1108,16 @@ export const hooksPretrain = {
|
|
|
990
1108
|
},
|
|
991
1109
|
},
|
|
992
1110
|
handler: async (params) => {
|
|
993
|
-
const repoPath = resolve(params.path
|
|
1111
|
+
const repoPath = resolve(validateMcpString(params.path, 'path', 4 * 1024) ?? '.');
|
|
994
1112
|
const projectRoot = getProjectCwd();
|
|
995
1113
|
if (repoPath !== projectRoot && !repoPath.startsWith(projectRoot + sep)) {
|
|
996
1114
|
return { error: 'Invalid path: must be within the project directory.' };
|
|
997
1115
|
}
|
|
998
|
-
const depth = params.depth
|
|
1116
|
+
const depth = validateMcpString(params.depth, 'depth', 16) ?? 'medium';
|
|
1117
|
+
const allowedDepths = new Set(['shallow', 'medium', 'deep']);
|
|
1118
|
+
if (!allowedDepths.has(depth)) {
|
|
1119
|
+
return { error: 'Invalid depth: must be shallow, medium, or deep' };
|
|
1120
|
+
}
|
|
999
1121
|
const startTime = performance.now();
|
|
1000
1122
|
// Real file scanning — count files by extension, extract patterns
|
|
1001
1123
|
const { readdirSync, statSync } = await import('node:fs');
|
|
@@ -1017,7 +1139,9 @@ export const hooksPretrain = {
|
|
|
1017
1139
|
scan(full, currentDepth + 1);
|
|
1018
1140
|
}
|
|
1019
1141
|
else if (entry.isFile()) {
|
|
1020
|
-
const ext = entry.name.includes('.')
|
|
1142
|
+
const ext = entry.name.includes('.')
|
|
1143
|
+
? entry.name.slice(entry.name.lastIndexOf('.'))
|
|
1144
|
+
: '';
|
|
1021
1145
|
if (ext)
|
|
1022
1146
|
extCounts[ext] = (extCounts[ext] || 0) + 1;
|
|
1023
1147
|
filesAnalyzed++;
|
|
@@ -1036,7 +1160,9 @@ export const hooksPretrain = {
|
|
|
1036
1160
|
// Extract import patterns (first 50 files max for performance)
|
|
1037
1161
|
if (filesAnalyzed <= 50) {
|
|
1038
1162
|
for (const line of lines.slice(0, 30)) {
|
|
1039
|
-
if (line.startsWith('import ') ||
|
|
1163
|
+
if (line.startsWith('import ') ||
|
|
1164
|
+
line.startsWith('from ') ||
|
|
1165
|
+
(line.startsWith('const ') && line.includes('require('))) {
|
|
1040
1166
|
const trimmed = line.trim();
|
|
1041
1167
|
if (trimmed.length < 120 && !patterns.includes(trimmed))
|
|
1042
1168
|
patterns.push(trimmed);
|
|
@@ -1046,12 +1172,16 @@ export const hooksPretrain = {
|
|
|
1046
1172
|
}
|
|
1047
1173
|
}
|
|
1048
1174
|
}
|
|
1049
|
-
catch {
|
|
1175
|
+
catch {
|
|
1176
|
+
/* skip unreadable */
|
|
1177
|
+
}
|
|
1050
1178
|
}
|
|
1051
1179
|
}
|
|
1052
1180
|
}
|
|
1053
1181
|
}
|
|
1054
|
-
catch {
|
|
1182
|
+
catch {
|
|
1183
|
+
/* skip inaccessible dirs */
|
|
1184
|
+
}
|
|
1055
1185
|
};
|
|
1056
1186
|
scan(repoPath, 0);
|
|
1057
1187
|
const elapsed = Math.round(performance.now() - startTime);
|
|
@@ -1061,7 +1191,14 @@ export const hooksPretrain = {
|
|
|
1061
1191
|
const bridge = await import('../memory/memory-bridge.js');
|
|
1062
1192
|
await bridge.bridgeStoreEntry({
|
|
1063
1193
|
key: `pretrain-${Date.now()}`,
|
|
1064
|
-
value: JSON.stringify({
|
|
1194
|
+
value: JSON.stringify({
|
|
1195
|
+
filesAnalyzed,
|
|
1196
|
+
totalLines,
|
|
1197
|
+
topExtensions: Object.entries(extCounts)
|
|
1198
|
+
.sort((a, b) => b[1] - a[1])
|
|
1199
|
+
.slice(0, 10),
|
|
1200
|
+
importPatterns: patterns.slice(0, 20),
|
|
1201
|
+
}),
|
|
1065
1202
|
namespace: 'pretrain',
|
|
1066
1203
|
tags: ['pretrain', depth],
|
|
1067
1204
|
});
|
|
@@ -1078,13 +1215,20 @@ export const hooksPretrain = {
|
|
|
1078
1215
|
if (patterns.length > 0) {
|
|
1079
1216
|
try {
|
|
1080
1217
|
const intel = await import('../memory/intelligence.js');
|
|
1081
|
-
await intel.initializeIntelligence({
|
|
1218
|
+
await intel.initializeIntelligence({
|
|
1219
|
+
confidenceLearningRate: 0.002,
|
|
1220
|
+
maxTrajectorySize: patterns.length,
|
|
1221
|
+
});
|
|
1082
1222
|
// Record each extracted pattern as an action step
|
|
1083
1223
|
for (const pat of patterns.slice(0, 50)) {
|
|
1084
|
-
await intel.recordStep({
|
|
1224
|
+
await intel.recordStep({
|
|
1225
|
+
type: 'action',
|
|
1226
|
+
content: pat,
|
|
1227
|
+
metadata: { source: 'pretrain', depth },
|
|
1228
|
+
});
|
|
1085
1229
|
}
|
|
1086
1230
|
// Record the entire scan as a completed trajectory
|
|
1087
|
-
const steps = patterns.slice(0, 50).map(p => ({ type: 'action', content: p }));
|
|
1231
|
+
const steps = patterns.slice(0, 50).map((p) => ({ type: 'action', content: p }));
|
|
1088
1232
|
await intel.recordTrajectory(steps, 'success');
|
|
1089
1233
|
intel.flushPatterns();
|
|
1090
1234
|
neuralPatternsLearned = steps.length;
|
|
@@ -1107,7 +1251,10 @@ export const hooksPretrain = {
|
|
|
1107
1251
|
patternsExtracted: patterns.length,
|
|
1108
1252
|
patternsStored,
|
|
1109
1253
|
neuralPatternsLearned,
|
|
1110
|
-
fileTypes: Object.entries(extCounts)
|
|
1254
|
+
fileTypes: Object.entries(extCounts)
|
|
1255
|
+
.sort((a, b) => b[1] - a[1])
|
|
1256
|
+
.slice(0, 15)
|
|
1257
|
+
.map(([ext, count]) => ({ ext, count })),
|
|
1111
1258
|
},
|
|
1112
1259
|
};
|
|
1113
1260
|
},
|
|
@@ -1120,38 +1267,68 @@ export const hooksBuildAgents = {
|
|
|
1120
1267
|
type: 'object',
|
|
1121
1268
|
properties: {
|
|
1122
1269
|
outputDir: { type: 'string', description: 'Output directory for configs' },
|
|
1123
|
-
focus: {
|
|
1270
|
+
focus: {
|
|
1271
|
+
type: 'string',
|
|
1272
|
+
description: 'Focus area (v1-implementation, security, performance, all)',
|
|
1273
|
+
},
|
|
1124
1274
|
format: { type: 'string', description: 'Config format (yaml, json)' },
|
|
1125
1275
|
persist: { type: 'boolean', description: 'Write configs to disk' },
|
|
1126
1276
|
},
|
|
1127
1277
|
},
|
|
1128
1278
|
handler: async (params) => {
|
|
1129
|
-
const rawOutputDir = resolve(params.outputDir
|
|
1279
|
+
const rawOutputDir = resolve(validateMcpString(params.outputDir, 'outputDir', 4 * 1024) ?? './agents');
|
|
1130
1280
|
const outputDir = rawOutputDir;
|
|
1131
1281
|
if (!outputDir.startsWith(getProjectCwd() + sep) && outputDir !== getProjectCwd()) {
|
|
1132
1282
|
return { error: 'Invalid outputDir: must be within the project directory.' };
|
|
1133
1283
|
}
|
|
1134
|
-
const focus = params.focus
|
|
1284
|
+
const focus = validateMcpString(params.focus, 'focus', 64) ?? 'all';
|
|
1135
1285
|
// Strict allowlist on `format` — without this, `format = "yaml/../../../etc/cron.d/x"`
|
|
1136
1286
|
// collapses through `join` and lets writes escape the validated outputDir.
|
|
1137
1287
|
const ALLOWED_FORMATS = new Set(['yaml', 'json']);
|
|
1138
|
-
const
|
|
1139
|
-
if (!ALLOWED_FORMATS.has(
|
|
1288
|
+
const format = validateMcpString(params.format, 'format', 16) ?? 'yaml';
|
|
1289
|
+
if (!ALLOWED_FORMATS.has(format)) {
|
|
1140
1290
|
return { error: 'Invalid format: must be yaml or json' };
|
|
1141
1291
|
}
|
|
1142
|
-
const format = formatRaw;
|
|
1143
1292
|
const persist = params.persist !== false; // Default to true
|
|
1144
1293
|
const agents = [
|
|
1145
|
-
{
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1294
|
+
{
|
|
1295
|
+
type: 'coder',
|
|
1296
|
+
configFile: join(outputDir, `coder.${format}`),
|
|
1297
|
+
capabilities: ['code-generation', 'refactoring', 'debugging'],
|
|
1298
|
+
optimizations: ['token-reduction', 'context-caching'],
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
type: 'architect',
|
|
1302
|
+
configFile: join(outputDir, `architect.${format}`),
|
|
1303
|
+
capabilities: ['system-design', 'api-design', 'documentation'],
|
|
1304
|
+
optimizations: ['context-caching', 'memory-persistence'],
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
type: 'tester',
|
|
1308
|
+
configFile: join(outputDir, `tester.${format}`),
|
|
1309
|
+
capabilities: ['unit-testing', 'integration-testing', 'coverage'],
|
|
1310
|
+
optimizations: ['parallel-execution'],
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
type: 'security-architect',
|
|
1314
|
+
configFile: join(outputDir, `security-architect.${format}`),
|
|
1315
|
+
capabilities: ['threat-modeling', 'vulnerability-analysis', 'security-review'],
|
|
1316
|
+
optimizations: ['pattern-matching'],
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
type: 'reviewer',
|
|
1320
|
+
configFile: join(outputDir, `reviewer.${format}`),
|
|
1321
|
+
capabilities: ['code-review', 'quality-analysis', 'best-practices'],
|
|
1322
|
+
optimizations: ['incremental-analysis'],
|
|
1323
|
+
},
|
|
1150
1324
|
];
|
|
1151
|
-
const filteredAgents = focus === 'all'
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1325
|
+
const filteredAgents = focus === 'all'
|
|
1326
|
+
? agents
|
|
1327
|
+
: focus === 'security'
|
|
1328
|
+
? agents.filter((a) => a.type.includes('security') || a.type === 'reviewer')
|
|
1329
|
+
: focus === 'performance'
|
|
1330
|
+
? agents.filter((a) => ['coder', 'tester'].includes(a.type))
|
|
1331
|
+
: agents;
|
|
1155
1332
|
// Persist configs to disk if requested
|
|
1156
1333
|
if (persist) {
|
|
1157
1334
|
// Ensure output directory exists
|
|
@@ -1169,7 +1346,7 @@ export const hooksBuildAgents = {
|
|
|
1169
1346
|
};
|
|
1170
1347
|
const content = format === 'json'
|
|
1171
1348
|
? 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`;
|
|
1349
|
+
: `# ${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
1350
|
const _cftmp = agent.configFile + '.tmp';
|
|
1174
1351
|
writeFileSync(_cftmp, content, 'utf-8');
|
|
1175
1352
|
renameSync(_cftmp, agent.configFile);
|
|
@@ -1202,9 +1379,14 @@ export const hooksTransfer = {
|
|
|
1202
1379
|
required: ['sourcePath'],
|
|
1203
1380
|
},
|
|
1204
1381
|
handler: async (params) => {
|
|
1205
|
-
const sourcePath = params.sourcePath;
|
|
1206
|
-
|
|
1207
|
-
|
|
1382
|
+
const sourcePath = validateMcpString(params.sourcePath, 'sourcePath', 4 * 1024);
|
|
1383
|
+
if (!sourcePath) {
|
|
1384
|
+
return { error: 'sourcePath is required (non-empty string, no control chars, max 4KB)' };
|
|
1385
|
+
}
|
|
1386
|
+
const minConfidence = typeof params.minConfidence === 'number' && Number.isFinite(params.minConfidence)
|
|
1387
|
+
? Math.max(0, Math.min(1, params.minConfidence))
|
|
1388
|
+
: 0.7;
|
|
1389
|
+
const filter = validateMcpString(params.filter, 'filter', 64) ?? undefined;
|
|
1208
1390
|
// Validate sourcePath is an existing directory before reading from it
|
|
1209
1391
|
const resolvedSource = resolve(sourcePath);
|
|
1210
1392
|
const { statSync } = await import('fs');
|
|
@@ -1227,7 +1409,8 @@ export const hooksTransfer = {
|
|
|
1227
1409
|
let sourceStore = { entries: {}, version: '3.0.0' };
|
|
1228
1410
|
const MAX_SOURCE_STORE_BYTES = 50 * 1024 * 1024; // 50 MB — matches other store readers
|
|
1229
1411
|
try {
|
|
1230
|
-
if (existsSync(sourceMemoryPath) &&
|
|
1412
|
+
if (existsSync(sourceMemoryPath) &&
|
|
1413
|
+
statSync(sourceMemoryPath).size <= MAX_SOURCE_STORE_BYTES) {
|
|
1231
1414
|
sourceStore = JSON.parse(readFileSync(sourceMemoryPath, 'utf-8'));
|
|
1232
1415
|
}
|
|
1233
1416
|
}
|
|
@@ -1239,13 +1422,13 @@ export const hooksTransfer = {
|
|
|
1239
1422
|
const sourceEntries = Object.values(sourceStore.entries);
|
|
1240
1423
|
// Count patterns by type from source
|
|
1241
1424
|
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,
|
|
1425
|
+
'file-patterns': sourceEntries.filter((e) => e.key.includes('file') || e.metadata?.type === 'file-pattern').length,
|
|
1426
|
+
'task-routing': sourceEntries.filter((e) => e.key.includes('routing') || e.metadata?.type === 'routing').length,
|
|
1427
|
+
'command-risk': sourceEntries.filter((e) => e.key.includes('command') || e.metadata?.type === 'command-risk').length,
|
|
1428
|
+
'agent-success': sourceEntries.filter((e) => e.key.includes('agent') || e.metadata?.type === 'agent-success').length,
|
|
1246
1429
|
};
|
|
1247
1430
|
// If source has no patterns, report honestly instead of substituting demo data
|
|
1248
|
-
if (Object.values(byType).every(v => v === 0)) {
|
|
1431
|
+
if (Object.values(byType).every((v) => v === 0)) {
|
|
1249
1432
|
return {
|
|
1250
1433
|
success: false,
|
|
1251
1434
|
message: 'No patterns found in source project',
|
|
@@ -1254,7 +1437,7 @@ export const hooksTransfer = {
|
|
|
1254
1437
|
};
|
|
1255
1438
|
}
|
|
1256
1439
|
if (filter) {
|
|
1257
|
-
Object.keys(byType).forEach(key => {
|
|
1440
|
+
Object.keys(byType).forEach((key) => {
|
|
1258
1441
|
if (!key.includes(filter))
|
|
1259
1442
|
delete byType[key];
|
|
1260
1443
|
});
|
|
@@ -1283,8 +1466,8 @@ export const hooksSessionStart = {
|
|
|
1283
1466
|
},
|
|
1284
1467
|
},
|
|
1285
1468
|
handler: async (params) => {
|
|
1286
|
-
const sessionId = params.sessionId
|
|
1287
|
-
const restoreLatest = params.restoreLatest;
|
|
1469
|
+
const sessionId = validateMcpString(params.sessionId, 'sessionId', 256) ?? `session-${Date.now()}`;
|
|
1470
|
+
const restoreLatest = params.restoreLatest === true;
|
|
1288
1471
|
// Phase 5: Wire ReflexionMemory session start via bridge
|
|
1289
1472
|
let sessionMemory = null;
|
|
1290
1473
|
try {
|
|
@@ -1295,7 +1478,7 @@ export const hooksSessionStart = {
|
|
|
1295
1478
|
});
|
|
1296
1479
|
if (result) {
|
|
1297
1480
|
sessionMemory = {
|
|
1298
|
-
controller: result.success ? '
|
|
1481
|
+
controller: result.success ? 'sqlite' : 'none',
|
|
1299
1482
|
restoredPatterns: 0,
|
|
1300
1483
|
};
|
|
1301
1484
|
}
|
|
@@ -1315,11 +1498,13 @@ export const hooksSessionStart = {
|
|
|
1315
1498
|
memoryPersistence: true,
|
|
1316
1499
|
},
|
|
1317
1500
|
sessionMemory: sessionMemory || { controller: 'none', restoredPatterns: 0 },
|
|
1318
|
-
previousSession: restoreLatest
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1501
|
+
previousSession: restoreLatest
|
|
1502
|
+
? {
|
|
1503
|
+
id: `session-${Date.now() - 86400000}`,
|
|
1504
|
+
tasksRestored: sessionMemory?.restoredPatterns || 0,
|
|
1505
|
+
memoryRestored: sessionMemory?.restoredPatterns || 0,
|
|
1506
|
+
}
|
|
1507
|
+
: null,
|
|
1323
1508
|
};
|
|
1324
1509
|
},
|
|
1325
1510
|
};
|
|
@@ -1338,12 +1523,18 @@ export const hooksSessionRestore = {
|
|
|
1338
1523
|
type: 'object',
|
|
1339
1524
|
properties: {
|
|
1340
1525
|
sessionId: { type: 'string', description: 'Session ID to restore, or "latest"' },
|
|
1341
|
-
restoreAgents: {
|
|
1342
|
-
|
|
1526
|
+
restoreAgents: {
|
|
1527
|
+
type: 'boolean',
|
|
1528
|
+
description: 'Include a count of currently-live agents (default true)',
|
|
1529
|
+
},
|
|
1530
|
+
restoreTasks: {
|
|
1531
|
+
type: 'boolean',
|
|
1532
|
+
description: 'Include a count of currently-live tasks (default true)',
|
|
1533
|
+
},
|
|
1343
1534
|
},
|
|
1344
1535
|
},
|
|
1345
1536
|
handler: async (params) => {
|
|
1346
|
-
const originalSessionId = params.sessionId
|
|
1537
|
+
const originalSessionId = validateMcpString(params.sessionId, 'sessionId', 256) ?? 'latest';
|
|
1347
1538
|
const newSessionId = `session-${Date.now()}`;
|
|
1348
1539
|
const warnings = [];
|
|
1349
1540
|
let agentsRestored = 0;
|
|
@@ -1424,9 +1615,9 @@ export const hooksSessionEnd = {
|
|
|
1424
1615
|
// Read actual counts from stores
|
|
1425
1616
|
const store = loadMemoryStore();
|
|
1426
1617
|
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;
|
|
1618
|
+
const taskCount = allEntries.filter((e) => e.key.includes('task')).length;
|
|
1619
|
+
const agentCount = allEntries.filter((e) => e.key.includes('agent')).length;
|
|
1620
|
+
const patternCount = allEntries.filter((e) => e.key.includes('pattern')).length;
|
|
1430
1621
|
const trajectoryCount = activeTrajectories.size;
|
|
1431
1622
|
// Check for pending-insights.jsonl
|
|
1432
1623
|
let insightCount = 0;
|
|
@@ -1451,7 +1642,7 @@ export const hooksSessionEnd = {
|
|
|
1451
1642
|
});
|
|
1452
1643
|
if (result) {
|
|
1453
1644
|
sessionPersistence = {
|
|
1454
|
-
controller: result.success ? '
|
|
1645
|
+
controller: result.success ? 'sqlite' : 'none',
|
|
1455
1646
|
persisted: result.success,
|
|
1456
1647
|
};
|
|
1457
1648
|
}
|
|
@@ -1521,7 +1712,7 @@ export const hooksIntelligence = {
|
|
|
1521
1712
|
},
|
|
1522
1713
|
},
|
|
1523
1714
|
handler: async (params) => {
|
|
1524
|
-
const mode = params.mode
|
|
1715
|
+
const mode = validateMcpString(params.mode, 'mode', 32) ?? 'balanced';
|
|
1525
1716
|
const enableSona = params.enableSona !== false;
|
|
1526
1717
|
const enableMoe = params.enableMoe !== false;
|
|
1527
1718
|
const enableHnsw = params.enableHnsw !== false;
|
|
@@ -1591,14 +1782,24 @@ export const hooksIntelligence = {
|
|
|
1591
1782
|
},
|
|
1592
1783
|
implementationStatus: {
|
|
1593
1784
|
working: [
|
|
1594
|
-
'memory-store',
|
|
1595
|
-
'
|
|
1785
|
+
'memory-store',
|
|
1786
|
+
'embeddings',
|
|
1787
|
+
'trajectory-recording',
|
|
1788
|
+
'claims',
|
|
1789
|
+
'swarm-coordination',
|
|
1790
|
+
'hnsw-index',
|
|
1791
|
+
'pattern-storage',
|
|
1792
|
+
'keyword-routing',
|
|
1596
1793
|
],
|
|
1597
1794
|
partial: [],
|
|
1598
1795
|
notImplemented: [],
|
|
1599
1796
|
removed: [
|
|
1600
|
-
'moe-routing',
|
|
1601
|
-
'
|
|
1797
|
+
'moe-routing',
|
|
1798
|
+
'flash-attention',
|
|
1799
|
+
'lora-adapter',
|
|
1800
|
+
'native-sona-engine',
|
|
1801
|
+
'native-router',
|
|
1802
|
+
'native-attention',
|
|
1602
1803
|
],
|
|
1603
1804
|
},
|
|
1604
1805
|
version: '3.0.0-alpha.102',
|