@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.
Files changed (189) hide show
  1. package/.claude/helpers/intelligence.cjs +27 -6
  2. package/.claude/skills/agentic-jujutsu/SKILL.md +17 -15
  3. package/.claude/skills/hive-mind-advanced/SKILL.md +212 -559
  4. package/.claude/skills/hooks-automation/SKILL.md +1 -1
  5. package/.claude/skills/mastermind-createorg/SKILL.md +2 -1
  6. package/.claude/skills/pair-programming/SKILL.md +1 -1
  7. package/.claude/skills/performance-analysis/SKILL.md +228 -484
  8. package/.claude/skills/swarm-advanced/SKILL.md +2 -2
  9. package/.claude/skills/swarm-orchestration/SKILL.md +220 -150
  10. package/.claude/skills/verification-quality/SKILL.md +247 -571
  11. package/README.md +6 -2
  12. package/dist/src/autopilot-state.d.ts +16 -4
  13. package/dist/src/autopilot-state.d.ts.map +1 -1
  14. package/dist/src/autopilot-state.js +42 -19
  15. package/dist/src/autopilot-state.js.map +1 -1
  16. package/dist/src/commands/agent-lifecycle.js +3 -3
  17. package/dist/src/commands/agent-lifecycle.js.map +1 -1
  18. package/dist/src/commands/autopilot.d.ts.map +1 -1
  19. package/dist/src/commands/autopilot.js +113 -26
  20. package/dist/src/commands/autopilot.js.map +1 -1
  21. package/dist/src/commands/doc.d.ts.map +1 -1
  22. package/dist/src/commands/doc.js +14 -3
  23. package/dist/src/commands/doc.js.map +1 -1
  24. package/dist/src/commands/doctor-env-checks.d.ts +1 -1
  25. package/dist/src/commands/doctor-env-checks.d.ts.map +1 -1
  26. package/dist/src/commands/doctor.d.ts.map +1 -1
  27. package/dist/src/commands/doctor.js +30 -1
  28. package/dist/src/commands/doctor.js.map +1 -1
  29. package/dist/src/commands/hooks-routing-commands.d.ts.map +1 -1
  30. package/dist/src/commands/hooks-routing-commands.js +12 -26
  31. package/dist/src/commands/hooks-routing-commands.js.map +1 -1
  32. package/dist/src/commands/hooks-workers.js +8 -8
  33. package/dist/src/commands/hooks-workers.js.map +1 -1
  34. package/dist/src/commands/hooks.d.ts.map +1 -1
  35. package/dist/src/commands/hooks.js +96 -54
  36. package/dist/src/commands/hooks.js.map +1 -1
  37. package/dist/src/commands/init.d.ts.map +1 -1
  38. package/dist/src/commands/init.js +80 -7
  39. package/dist/src/commands/init.js.map +1 -1
  40. package/dist/src/commands/mcp.d.ts.map +1 -1
  41. package/dist/src/commands/mcp.js +78 -2
  42. package/dist/src/commands/mcp.js.map +1 -1
  43. package/dist/src/commands/memory-list.d.ts.map +1 -1
  44. package/dist/src/commands/memory-list.js +79 -36
  45. package/dist/src/commands/memory-list.js.map +1 -1
  46. package/dist/src/commands/org.d.ts.map +1 -1
  47. package/dist/src/commands/org.js +114 -1
  48. package/dist/src/commands/org.js.map +1 -1
  49. package/dist/src/commands/performance.js +1 -1
  50. package/dist/src/commands/performance.js.map +1 -1
  51. package/dist/src/commands/swarm.d.ts.map +1 -1
  52. package/dist/src/commands/swarm.js +144 -98
  53. package/dist/src/commands/swarm.js.map +1 -1
  54. package/dist/src/init/claudemd-generator.js +1 -1
  55. package/dist/src/init/executor.d.ts.map +1 -1
  56. package/dist/src/init/executor.js +13 -3
  57. package/dist/src/init/executor.js.map +1 -1
  58. package/dist/src/init/kimi-generator.d.ts +3 -2
  59. package/dist/src/init/kimi-generator.d.ts.map +1 -1
  60. package/dist/src/init/kimi-generator.js +45 -13
  61. package/dist/src/init/kimi-generator.js.map +1 -1
  62. package/dist/src/init/statusline-generator.d.ts +1 -1
  63. package/dist/src/init/statusline-generator.d.ts.map +1 -1
  64. package/dist/src/init/statusline-generator.js +48 -46
  65. package/dist/src/init/statusline-generator.js.map +1 -1
  66. package/dist/src/init/types.d.ts +1 -1
  67. package/dist/src/init/types.d.ts.map +1 -1
  68. package/dist/src/init/types.js +5 -1
  69. package/dist/src/init/types.js.map +1 -1
  70. package/dist/src/mcp-server.d.ts +6 -0
  71. package/dist/src/mcp-server.d.ts.map +1 -1
  72. package/dist/src/mcp-server.js +47 -22
  73. package/dist/src/mcp-server.js.map +1 -1
  74. package/dist/src/mcp-tools/hive-mind-tools.d.ts.map +1 -1
  75. package/dist/src/mcp-tools/hive-mind-tools.js +13 -11
  76. package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -1
  77. package/dist/src/mcp-tools/hooks-intelligence.d.ts.map +1 -1
  78. package/dist/src/mcp-tools/hooks-intelligence.js +97 -40
  79. package/dist/src/mcp-tools/hooks-intelligence.js.map +1 -1
  80. package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
  81. package/dist/src/mcp-tools/hooks-routing.js +406 -205
  82. package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
  83. package/dist/src/mcp-tools/monograph/impact-tools.d.ts.map +1 -1
  84. package/dist/src/mcp-tools/monograph/impact-tools.js +109 -44
  85. package/dist/src/mcp-tools/monograph/impact-tools.js.map +1 -1
  86. package/dist/src/mcp-tools/monograph/query-tools.d.ts.map +1 -1
  87. package/dist/src/mcp-tools/monograph/query-tools.js +48 -18
  88. package/dist/src/mcp-tools/monograph/query-tools.js.map +1 -1
  89. package/dist/src/mcp-tools/system-tools.d.ts.map +1 -1
  90. package/dist/src/mcp-tools/system-tools.js +100 -38
  91. package/dist/src/mcp-tools/system-tools.js.map +1 -1
  92. package/dist/src/memory/embedding-operations.d.ts +4 -0
  93. package/dist/src/memory/embedding-operations.d.ts.map +1 -1
  94. package/dist/src/memory/embedding-operations.js +125 -32
  95. package/dist/src/memory/embedding-operations.js.map +1 -1
  96. package/dist/src/memory/hnsw-operations.d.ts +1 -1
  97. package/dist/src/memory/hnsw-operations.js +1 -1
  98. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  99. package/dist/src/memory/memory-bridge.js +172 -69
  100. package/dist/src/memory/memory-bridge.js.map +1 -1
  101. package/dist/src/memory/memory-read.d.ts +1 -1
  102. package/dist/src/memory/memory-read.js +2 -2
  103. package/dist/src/memory/memory-read.js.map +1 -1
  104. package/dist/src/orgrt/checkpoint-ops.d.ts +11 -1
  105. package/dist/src/orgrt/checkpoint-ops.d.ts.map +1 -1
  106. package/dist/src/orgrt/checkpoint-ops.js +15 -1
  107. package/dist/src/orgrt/checkpoint-ops.js.map +1 -1
  108. package/dist/src/orgrt/checkpoint.js +1 -1
  109. package/dist/src/orgrt/checkpoint.js.map +1 -1
  110. package/dist/src/orgrt/daemon.d.ts +11 -3
  111. package/dist/src/orgrt/daemon.d.ts.map +1 -1
  112. package/dist/src/orgrt/daemon.js +317 -93
  113. package/dist/src/orgrt/daemon.js.map +1 -1
  114. package/dist/src/orgrt/decisions.d.ts +13 -0
  115. package/dist/src/orgrt/decisions.d.ts.map +1 -1
  116. package/dist/src/orgrt/decisions.js +95 -0
  117. package/dist/src/orgrt/decisions.js.map +1 -1
  118. package/dist/src/orgrt/remote.d.ts.map +1 -1
  119. package/dist/src/orgrt/remote.js +12 -6
  120. package/dist/src/orgrt/remote.js.map +1 -1
  121. package/dist/src/orgrt/server.d.ts.map +1 -1
  122. package/dist/src/orgrt/server.js +71 -14
  123. package/dist/src/orgrt/server.js.map +1 -1
  124. package/dist/src/orgrt/session.d.ts +27 -1
  125. package/dist/src/orgrt/session.d.ts.map +1 -1
  126. package/dist/src/orgrt/session.js +42 -3
  127. package/dist/src/orgrt/session.js.map +1 -1
  128. package/dist/src/orgrt/task-dag.d.ts +11 -1
  129. package/dist/src/orgrt/task-dag.d.ts.map +1 -1
  130. package/dist/src/orgrt/task-dag.js +93 -2
  131. package/dist/src/orgrt/task-dag.js.map +1 -1
  132. package/dist/src/orgrt/templates.d.ts.map +1 -1
  133. package/dist/src/orgrt/templates.js +24 -0
  134. package/dist/src/orgrt/templates.js.map +1 -1
  135. package/dist/src/orgrt/types.d.ts +70 -1
  136. package/dist/src/orgrt/types.d.ts.map +1 -1
  137. package/dist/src/orgrt/types.js +19 -0
  138. package/dist/src/orgrt/types.js.map +1 -1
  139. package/dist/src/orgrt/vercel-providers.d.ts.map +1 -1
  140. package/dist/src/orgrt/vercel-providers.js +9 -4
  141. package/dist/src/orgrt/vercel-providers.js.map +1 -1
  142. package/dist/src/orgrt/vercel-runner.d.ts +0 -23
  143. package/dist/src/orgrt/vercel-runner.d.ts.map +1 -1
  144. package/dist/src/orgrt/vercel-runner.js +26 -1
  145. package/dist/src/orgrt/vercel-runner.js.map +1 -1
  146. package/dist/src/types.d.ts +1 -1
  147. package/dist/src/types.d.ts.map +1 -1
  148. package/dist/src/ui/server.mjs +2647 -838
  149. package/dist/tsconfig.tsbuildinfo +1 -1
  150. package/package.json +7 -7
  151. package/.claude/commands/mastermind/approvev1.md +0 -94
  152. package/.claude/commands/mastermind/architect.md +0 -52
  153. package/.claude/commands/mastermind/autodev.md +0 -28
  154. package/.claude/commands/mastermind/build.md +0 -23
  155. package/.claude/commands/mastermind/finish.md +0 -17
  156. package/.claude/commands/mastermind/runorgv1.md +0 -159
  157. package/.claude/commands/mastermind/taskdev.md +0 -23
  158. package/.claude/commands/mastermind/tdd.md +0 -19
  159. package/.claude/commands/mastermind/verify.md +0 -19
  160. package/.claude/skills/mastermind-approvev1/SKILL.md +0 -191
  161. package/.claude/skills/mastermind-architect/SKILL.md +0 -862
  162. package/.claude/skills/mastermind-autodev/SKILL.md +0 -360
  163. package/.claude/skills/mastermind-build/SKILL.md +0 -169
  164. package/.claude/skills/mastermind-companies/SKILL.md +0 -256
  165. package/.claude/skills/mastermind-content/SKILL.md +0 -197
  166. package/.claude/skills/mastermind-costs/SKILL.md +0 -151
  167. package/.claude/skills/mastermind-finance/SKILL.md +0 -166
  168. package/.claude/skills/mastermind-finish/SKILL.md +0 -251
  169. package/.claude/skills/mastermind-heartbeatv1/SKILL.md +0 -167
  170. package/.claude/skills/mastermind-instance-settings/SKILL.md +0 -315
  171. package/.claude/skills/mastermind-marketing/SKILL.md +0 -228
  172. package/.claude/skills/mastermind-marketing/references/copywriting-frameworks.md +0 -181
  173. package/.claude/skills/mastermind-marketing/references/persuasion-psychology.md +0 -158
  174. package/.claude/skills/mastermind-ops/SKILL.md +0 -168
  175. package/.claude/skills/mastermind-org-chart/SKILL.md +0 -209
  176. package/.claude/skills/mastermind-project-detail/SKILL.md +0 -249
  177. package/.claude/skills/mastermind-project-workspace/SKILL.md +0 -244
  178. package/.claude/skills/mastermind-projects/SKILL.md +0 -167
  179. package/.claude/skills/mastermind-runorgv1/SKILL.md +0 -731
  180. package/.claude/skills/mastermind-sales/SKILL.md +0 -170
  181. package/.claude/skills/mastermind-taskdev/SKILL.md +0 -377
  182. package/.claude/skills/mastermind-taskdev/code-quality-reviewer-prompt.md +0 -60
  183. package/.claude/skills/mastermind-taskdev/final-reviewer-prompt.md +0 -144
  184. package/.claude/skills/mastermind-taskdev/implementer-prompt.md +0 -114
  185. package/.claude/skills/mastermind-taskdev/spec-reviewer-prompt.md +0 -80
  186. package/.claude/skills/mastermind-tdd/SKILL.md +0 -424
  187. package/.claude/skills/mastermind-verify/SKILL.md +0 -196
  188. package/.claude/skills/mastermind-wiki/SKILL.md +0 -314
  189. 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: { type: 'string', description: 'Type of operation (create, update, delete, refactor)' },
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 rawFilePath = params.filePath;
33
- const filePath = typeof rawFilePath === 'string' && rawFilePath.length > MAX_PRE_EDIT_PATH_LEN
34
- ? rawFilePath.slice(0, MAX_PRE_EDIT_PATH_LEN)
35
- : rawFilePath;
36
- const rawOperation = params.operation || 'update';
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 rawFilePath = params.filePath;
81
- const filePath = typeof rawFilePath === 'string' && rawFilePath.length > MAX_POST_EDIT_PATH_LEN
82
- ? rawFilePath.slice(0, MAX_POST_EDIT_PATH_LEN)
83
- : rawFilePath;
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 rawAgent = params.agent;
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
- recorded: feedbackResult.success,
113
- controller: feedbackResult.success ? 'lancedb' : 'unavailable',
114
- updates: feedbackResult.success ? 1 : 0,
115
- } : { recorded: false, controller: 'unavailable', updates: 0 },
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 rawCommand = params.command;
135
- const command = typeof rawCommand === 'string' && rawCommand.length > MAX_CMD_LEN
136
- ? rawCommand.slice(0, MAX_CMD_LEN)
137
- : rawCommand;
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 ? 'critical'
140
- : assessment.level >= 0.6 ? 'high'
141
- : assessment.level >= 0.3 ? 'medium'
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 rawPostCommand = params.command;
178
- const command = typeof rawPostCommand === 'string' && rawPostCommand.length > MAX_POST_CMD_LEN
179
- ? rawPostCommand.slice(0, MAX_POST_CMD_LEN)
180
- : rawPostCommand;
181
- const exitCode = params.exitCode || 0;
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 = 'lancedb';
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] = { key, value: JSON.stringify({ command, exitCode, success }), namespace: 'commands', createdAt: new Date().toISOString() };
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 { /* non-critical */ }
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: { type: 'boolean', description: 'Use semantic similarity routing (default: true)' },
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 rawTask = params.task;
249
- const task = typeof rawTask === 'string' && rawTask.length > MAX_ROUTE_TASK_LEN
250
- ? rawTask.slice(0, MAX_ROUTE_TASK_LEN)
251
- : rawTask;
252
- const rawContext = params.context;
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-lancedb';
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: 'lancedb',
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:lancedb: "${matchedPattern}" (${Math.round(routeConfidence * 100)}%)`,
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 - (0.1 * (i + 1))) * 100) / 100,
301
- reason: 'Alternative from lancedb',
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' ? '2-4 hours' : complexity === 'medium' ? '30-60 min' : '10-30 min',
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 ? { topology: 'hierarchical', agents, coordination: 'queen-led' } : null,
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 = [...intelSuggestion.agents, ...agents.filter(a => !new Set(intelSuggestion.agents).has(a))];
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 - (0.1 * (i + 1))) * 100) / 100,
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
- topology: 'hierarchical',
405
- agents,
406
- coordination: 'queen-led',
407
- } : null,
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: { type: 'boolean', description: 'Include all route scores in the response (default: false)' },
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 rawTask = params.task;
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 || typeof task !== 'string') {
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 || '24h';
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 rawTaskId = params.taskId;
572
- const taskId = typeof rawTaskId === 'string' && rawTaskId.length > MAX_TASK_ID_LEN
573
- ? rawTaskId.slice(0, MAX_TASK_ID_LEN)
574
- : rawTaskId;
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 rawDescription = params.description;
580
- const description = typeof rawDescription === 'string' && rawDescription.length > MAX_PRE_TASK_DESC_LEN
581
- ? rawDescription.slice(0, MAX_PRE_TASK_DESC_LEN)
582
- : rawDescription;
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') || descLower.includes('architecture') || description.length > 200
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 (heuristicResults?.results ?? [])) {
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 { /* skip malformed */ }
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 (gradientResults?.results ?? [])) {
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 { /* non-critical */ }
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 - (0.05 * i),
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 ? 'Consider using swarm coordination' : 'Single agent recommended',
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: { type: 'string', description: 'Task description text (used for learning keyword extraction)' },
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: { type: 'string', description: 'Route ID from a prior hooks_route call — joins the recommendation to this outcome' },
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 rawPostTaskId = params.taskId;
684
- const taskId = typeof rawPostTaskId === 'string' && rawPostTaskId.length > MAX_POST_TASK_ID_LEN
685
- ? rawPostTaskId.slice(0, MAX_POST_TASK_ID_LEN)
686
- : rawPostTaskId;
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 ? 'explicit' : 'unknown';
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 rawPostTaskAgent = params.agent;
719
- const agent = typeof rawPostTaskAgent === 'string' && rawPostTaskAgent.length > MAX_POST_TASK_AGENT_LEN
720
- ? rawPostTaskAgent.slice(0, MAX_POST_TASK_AGENT_LEN)
721
- : rawPostTaskAgent;
722
- const quality = params.quality || (success ? 0.85 : 0.3);
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 rawPostTask = params.task;
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' : (outcomeKnown ? 'failure' : 'partial'),
792
+ outcome: success ? 'success' : outcomeKnown ? 'failure' : 'partial',
739
793
  confidence: quality,
740
- metadata: { taskId, duration: params.duration || undefined, patterns: params.patterns || undefined },
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 && taskText && agent && agent.length <= 100 && /^[a-zA-Z0-9_-]+$/.test(agent)) {
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 { /* non-critical */ }
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
- await joinOutcome(getRouteOutcomesBaseDir(), params.routeId, outcome);
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 { /* non-critical */ }
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({ task: taskText, agent, success, quality, keywords: outcomeKeywords }),
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 { /* non-critical */ }
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 { /* non-critical */ }
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
- needed: true,
866
- suggestedAgents: [
867
- { role: 'diagnoser', description: 'Analyze root cause of task failure' },
868
- { role: 'critic-1', description: 'Critique from correctness angle (temperature 0.3)' },
869
- { role: 'critic-2', description: 'Critique from efficiency angle (temperature 0.8)' },
870
- { role: 'aggregator', description: 'Synthesize critiques into actionable reflection heuristic' },
871
- ],
872
- storeAs: 'heuristics',
873
- note: 'Spawn agents sequentially: Diagnoser → Critics in parallel → Aggregator',
874
- } : { needed: false };
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 ? 'lancedb' : 'none',
975
+ controller: feedbackResult?.success ? 'sqlite' : 'none',
886
976
  outcomePersisted,
887
977
  },
888
978
  quality,
889
- feedback: feedbackResult ? {
890
- recorded: feedbackResult.success,
891
- controller: feedbackResult.success ? 'lancedb' : 'unavailable',
892
- updates: feedbackResult.success ? 1 : 0,
893
- } : { recorded: false, controller: 'unavailable', updates: 0 },
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 rawExplainTask = params.task;
917
- const task = typeof rawExplainTask === 'string' && rawExplainTask.length > MAX_EXPLAIN_TASK_LEN
918
- ? rawExplainTask.slice(0, MAX_EXPLAIN_TASK_LEN)
919
- : rawExplainTask;
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
- { factor: 'Keyword Match', weight: 0.4, value: suggestion.confidence, impact: 'Primary routing signal' },
958
- { factor: 'Historical Success', weight: 0.3, value: historicalSuccess, impact: historicalNote },
959
- { factor: 'Agent Availability', weight: 0.2, value: null, impact: 'Agent availability tracking not implemented' },
960
- { factor: 'Task Complexity', weight: 0.1, value: task.length > 100 ? 0.8 : 0.3, impact: 'Complexity assessment' },
961
- ],
962
- patterns: matchedPatterns.length > 0 ? matchedPatterns : [
963
- { pattern: 'general-task', matchScore: 0.7, examples: ['Default pattern for unclassified tasks'] }
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 || 'medium';
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('.') ? entry.name.slice(entry.name.lastIndexOf('.')) : '';
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 ') || line.startsWith('from ') || line.startsWith('const ') && line.includes('require(')) {
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 { /* skip unreadable */ }
1175
+ catch {
1176
+ /* skip unreadable */
1177
+ }
1050
1178
  }
1051
1179
  }
1052
1180
  }
1053
1181
  }
1054
- catch { /* skip inaccessible dirs */ }
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({ filesAnalyzed, totalLines, topExtensions: Object.entries(extCounts).sort((a, b) => b[1] - a[1]).slice(0, 10), importPatterns: patterns.slice(0, 20) }),
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({ confidenceLearningRate: 0.002, maxTrajectorySize: patterns.length });
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({ type: 'action', content: pat, metadata: { source: 'pretrain', depth } });
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).sort((a, b) => b[1] - a[1]).slice(0, 15).map(([ext, count]) => ({ ext, count })),
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: { type: 'string', description: 'Focus area (v1-implementation, security, performance, all)' },
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 || './agents');
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 || 'all';
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 formatRaw = params.format || 'yaml';
1139
- if (!ALLOWED_FORMATS.has(formatRaw)) {
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
- { type: 'coder', configFile: join(outputDir, `coder.${format}`), capabilities: ['code-generation', 'refactoring', 'debugging'], optimizations: ['token-reduction', 'context-caching'] },
1146
- { type: 'architect', configFile: join(outputDir, `architect.${format}`), capabilities: ['system-design', 'api-design', 'documentation'], optimizations: ['context-caching', 'memory-persistence'] },
1147
- { type: 'tester', configFile: join(outputDir, `tester.${format}`), capabilities: ['unit-testing', 'integration-testing', 'coverage'], optimizations: ['parallel-execution'] },
1148
- { type: 'security-architect', configFile: join(outputDir, `security-architect.${format}`), capabilities: ['threat-modeling', 'vulnerability-analysis', 'security-review'], optimizations: ['pattern-matching'] },
1149
- { type: 'reviewer', configFile: join(outputDir, `reviewer.${format}`), capabilities: ['code-review', 'quality-analysis', 'best-practices'], optimizations: ['incremental-analysis'] },
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' ? agents :
1152
- focus === 'security' ? agents.filter(a => a.type.includes('security') || a.type === 'reviewer') :
1153
- focus === 'performance' ? agents.filter(a => ['coder', 'tester'].includes(a.type)) :
1154
- agents;
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
- const minConfidence = params.minConfidence || 0.7;
1207
- const filter = params.filter;
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) && statSync(sourceMemoryPath).size <= MAX_SOURCE_STORE_BYTES) {
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 || `session-${Date.now()}`;
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 ? 'lancedb' : 'none',
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
- id: `session-${Date.now() - 86400000}`,
1320
- tasksRestored: sessionMemory?.restoredPatterns || 0,
1321
- memoryRestored: sessionMemory?.restoredPatterns || 0,
1322
- } : null,
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: { type: 'boolean', description: 'Include a count of currently-live agents (default true)' },
1342
- restoreTasks: { type: 'boolean', description: 'Include a count of currently-live tasks (default true)' },
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 || 'latest';
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 ? 'lancedb' : 'none',
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 || 'balanced';
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', 'embeddings', 'trajectory-recording', 'claims', 'swarm-coordination',
1595
- 'hnsw-index', 'pattern-storage', 'keyword-routing'
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', 'flash-attention', 'lora-adapter',
1601
- 'native-sona-engine', 'native-router', 'native-attention',
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',