monomind 2.1.9 → 2.3.0
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/package.json +2 -2
- package/packages/@monomind/cli/.claude/commands/mastermind/createorg.md +24 -16
- package/packages/@monomind/cli/.claude/commands/mastermind/master.md +23 -7
- package/packages/@monomind/cli/.claude/commands/mastermind/runorg.md +3 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/stoporg.md +6 -3
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/code-quality-reviewer-prompt.md +3 -3
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/createorg.md +19 -24
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/debug.md +49 -4
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/design.md +11 -10
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/final-reviewer-prompt.md +144 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/implementer-prompt.md +9 -4
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/org-settings.md +20 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgs.md +40 -19
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgstatus.md +90 -41
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/plan.md +19 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/skill-builder.md +171 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/spec-reviewer-prompt.md +19 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/stoporg.md +7 -5
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/taskdev.md +83 -15
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/tdd.md +30 -0
- package/packages/@monomind/cli/dist/src/autopilot-state.js +6 -4
- package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +4 -1
- package/packages/@monomind/cli/dist/src/capabilities/manager.js +3 -1
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +26 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +28 -46
- package/packages/@monomind/cli/dist/src/commands/cleanup.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/cleanup.js +120 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +13 -4
- package/packages/@monomind/cli/dist/src/commands/hooks-routing-commands.js +34 -18
- package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +8 -2
- package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/init-wizard.js +2 -2
- package/packages/@monomind/cli/dist/src/commands/init.js +54 -10
- package/packages/@monomind/cli/dist/src/commands/mcp.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +7 -1
- package/packages/@monomind/cli/dist/src/commands/org-observe.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/org-observe.js +295 -0
- package/packages/@monomind/cli/dist/src/commands/org.d.ts +9 -1
- package/packages/@monomind/cli/dist/src/commands/org.js +191 -9
- package/packages/@monomind/cli/dist/src/commands/performance.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/platforms.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/security-scan.js +8 -2
- package/packages/@monomind/cli/dist/src/commands/start.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/swarm.js +5 -4
- package/packages/@monomind/cli/dist/src/index.js +13 -3
- package/packages/@monomind/cli/dist/src/init/executor.js +19 -5
- package/packages/@monomind/cli/dist/src/init/settings-generator.js +8 -1
- package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +63 -20
- package/packages/@monomind/cli/dist/src/mcp-tools/claims-tools.js +3 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/config-tools.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/embeddings-tools.js +6 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +35 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +24 -7
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-embedding.js +13 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.js +9 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-routing.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-routing.js +112 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-tools.js +2 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +8 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/performance-tools.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/session-tools.js +15 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +4 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +6 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.js +38 -16
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.js +68 -24
- package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.js +6 -2
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +32 -10
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +16 -4
- package/packages/@monomind/cli/dist/src/memory/memory-crud.js +26 -2
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +3 -2
- package/packages/@monomind/cli/dist/src/memory/memory-migrations.js +2 -0
- package/packages/@monomind/cli/dist/src/monovector/command-outcomes.js +4 -1
- package/packages/@monomind/cli/dist/src/orgrt/bus.js +4 -1
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +74 -19
- package/packages/@monomind/cli/dist/src/orgrt/inbox.js +3 -1
- package/packages/@monomind/cli/dist/src/orgrt/reporting.d.ts +40 -0
- package/packages/@monomind/cli/dist/src/orgrt/reporting.js +127 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.js +10 -2
- package/packages/@monomind/cli/dist/src/orgrt/templates.d.ts +16 -0
- package/packages/@monomind/cli/dist/src/orgrt/templates.js +57 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.js +10 -1
- package/packages/@monomind/cli/dist/src/transfer/storage/gcs.js +6 -2
- package/packages/@monomind/cli/dist/src/ui/orgs.html +32 -0
- package/packages/@monomind/cli/dist/src/ui/server.mjs +25 -0
- package/packages/@monomind/cli/dist/src/update/executor.js +3 -1
- package/packages/@monomind/cli/dist/src/update/rate-limiter.js +3 -1
- package/packages/@monomind/cli/package.json +2 -2
|
@@ -97,8 +97,10 @@ export const hooksPostEdit = {
|
|
|
97
97
|
confidence: success ? 0.85 : 0.3,
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
|
-
catch {
|
|
100
|
+
catch (e) {
|
|
101
101
|
// Bridge not available — continue with basic response
|
|
102
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
103
|
+
console.error('[hooks-post-edit] memory bridge feedback failed:', e);
|
|
102
104
|
}
|
|
103
105
|
return {
|
|
104
106
|
recorded: true,
|
|
@@ -738,8 +740,10 @@ export const hooksPostTask = {
|
|
|
738
740
|
metadata: { taskId, duration: params.duration || undefined, patterns: params.patterns || undefined },
|
|
739
741
|
});
|
|
740
742
|
}
|
|
741
|
-
catch {
|
|
743
|
+
catch (e) {
|
|
742
744
|
// Bridge not available — continue with basic response
|
|
745
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
746
|
+
console.error('[hooks-post-task] memory bridge feedback failed:', e);
|
|
743
747
|
}
|
|
744
748
|
// Phase 3: Record causal edge (task → outcome)
|
|
745
749
|
try {
|
|
@@ -751,8 +755,10 @@ export const hooksPostTask = {
|
|
|
751
755
|
strength: quality,
|
|
752
756
|
});
|
|
753
757
|
}
|
|
754
|
-
catch {
|
|
758
|
+
catch (e) {
|
|
755
759
|
// Non-fatal
|
|
760
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
761
|
+
console.error('[hooks-post-task] causal edge record failed:', e);
|
|
756
762
|
}
|
|
757
763
|
// Persist routing outcome for runtime learning (file-based, always reliable).
|
|
758
764
|
// B1.3: also gate this sibling learning sink on a known outcome — an unverified
|
|
@@ -938,8 +944,10 @@ export const hooksExplain = {
|
|
|
938
944
|
}
|
|
939
945
|
}
|
|
940
946
|
}
|
|
941
|
-
catch {
|
|
942
|
-
// File unreadable; leave as null
|
|
947
|
+
catch (e) {
|
|
948
|
+
// File unreadable or corrupt; leave as null
|
|
949
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
950
|
+
console.error('[hooks-explain] routing outcomes file read/parse failed:', e);
|
|
943
951
|
}
|
|
944
952
|
return {
|
|
945
953
|
task,
|
|
@@ -1059,7 +1067,11 @@ export const hooksPretrain = {
|
|
|
1059
1067
|
});
|
|
1060
1068
|
patternsStored = patterns.length;
|
|
1061
1069
|
}
|
|
1062
|
-
catch {
|
|
1070
|
+
catch (e) {
|
|
1071
|
+
/* memory backend unavailable */
|
|
1072
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
1073
|
+
console.error('[hooks-pretrain] pattern store failed:', e);
|
|
1074
|
+
}
|
|
1063
1075
|
// Feed extracted import patterns into the neural training system so
|
|
1064
1076
|
// pretrain actually trains, not just scans.
|
|
1065
1077
|
let neuralPatternsLearned = 0;
|
|
@@ -1077,7 +1089,11 @@ export const hooksPretrain = {
|
|
|
1077
1089
|
intel.flushPatterns();
|
|
1078
1090
|
neuralPatternsLearned = steps.length;
|
|
1079
1091
|
}
|
|
1080
|
-
catch {
|
|
1092
|
+
catch (e) {
|
|
1093
|
+
/* intelligence not available */
|
|
1094
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
1095
|
+
console.error('[hooks-pretrain] intelligence training failed:', e);
|
|
1096
|
+
}
|
|
1081
1097
|
}
|
|
1082
1098
|
return {
|
|
1083
1099
|
success: true,
|
|
@@ -1215,8 +1231,10 @@ export const hooksTransfer = {
|
|
|
1215
1231
|
sourceStore = JSON.parse(readFileSync(sourceMemoryPath, 'utf-8'));
|
|
1216
1232
|
}
|
|
1217
1233
|
}
|
|
1218
|
-
catch {
|
|
1234
|
+
catch (e) {
|
|
1219
1235
|
// Fall back to empty store
|
|
1236
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
1237
|
+
console.error('[hooks-transfer] source memory store read/parse failed:', e);
|
|
1220
1238
|
}
|
|
1221
1239
|
const sourceEntries = Object.values(sourceStore.entries);
|
|
1222
1240
|
// Count patterns by type from source
|
|
@@ -1282,8 +1300,10 @@ export const hooksSessionStart = {
|
|
|
1282
1300
|
};
|
|
1283
1301
|
}
|
|
1284
1302
|
}
|
|
1285
|
-
catch {
|
|
1303
|
+
catch (e) {
|
|
1286
1304
|
// Bridge not available
|
|
1305
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
1306
|
+
console.error('[hooks-session-start] memory bridge failed:', e);
|
|
1287
1307
|
}
|
|
1288
1308
|
return {
|
|
1289
1309
|
sessionId,
|
|
@@ -1303,6 +1323,86 @@ export const hooksSessionStart = {
|
|
|
1303
1323
|
};
|
|
1304
1324
|
},
|
|
1305
1325
|
};
|
|
1326
|
+
// Session restore hook — hooks.ts's `hooks session-restore` (and its
|
|
1327
|
+
// `session-start` alias) called this tool name, but it was never registered
|
|
1328
|
+
// anywhere in the tool registry, so both always failed. This repo has no
|
|
1329
|
+
// per-session snapshot of agents/tasks to restore from, so "restore" here
|
|
1330
|
+
// means: report the currently-live (non-terminated/non-terminal) agents and
|
|
1331
|
+
// tasks as "carried forward", and reinitialize the memory-bridge session
|
|
1332
|
+
// context the same way hooks_session-start does — an honest, working
|
|
1333
|
+
// implementation rather than a fabricated one.
|
|
1334
|
+
export const hooksSessionRestore = {
|
|
1335
|
+
name: 'hooks_session-restore',
|
|
1336
|
+
description: 'Restore a previous session — reports currently-live agents/tasks and reinitializes memory-bridge session context',
|
|
1337
|
+
inputSchema: {
|
|
1338
|
+
type: 'object',
|
|
1339
|
+
properties: {
|
|
1340
|
+
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)' },
|
|
1343
|
+
},
|
|
1344
|
+
},
|
|
1345
|
+
handler: async (params) => {
|
|
1346
|
+
const originalSessionId = params.sessionId || 'latest';
|
|
1347
|
+
const newSessionId = `session-${Date.now()}`;
|
|
1348
|
+
const warnings = [];
|
|
1349
|
+
let agentsRestored = 0;
|
|
1350
|
+
if (params.restoreAgents !== false) {
|
|
1351
|
+
try {
|
|
1352
|
+
const { loadAgentStore } = await import('./agent-tools.js');
|
|
1353
|
+
const store = loadAgentStore();
|
|
1354
|
+
agentsRestored = Object.values(store.agents).filter((a) => a.status !== 'terminated').length;
|
|
1355
|
+
}
|
|
1356
|
+
catch (e) {
|
|
1357
|
+
warnings.push('Agent store unavailable — agent count not restored');
|
|
1358
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
1359
|
+
console.error('[hooks-session-restore] agent store read failed:', e);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
let tasksRestored = 0;
|
|
1363
|
+
if (params.restoreTasks !== false) {
|
|
1364
|
+
try {
|
|
1365
|
+
const { loadTaskStore } = await import('./task-tools.js');
|
|
1366
|
+
const store = loadTaskStore();
|
|
1367
|
+
tasksRestored = Object.values(store.tasks).filter((t) => t.status === 'pending' || t.status === 'in_progress').length;
|
|
1368
|
+
}
|
|
1369
|
+
catch (e) {
|
|
1370
|
+
warnings.push('Task store unavailable — task count not restored');
|
|
1371
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
1372
|
+
console.error('[hooks-session-restore] task store read failed:', e);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
// bridgeSessionStart only stores a new "session active" marker entry —
|
|
1376
|
+
// it has no pattern-restoration data to count, so there is no real
|
|
1377
|
+
// "memoryRestored" number to report. Track whether the bridge itself
|
|
1378
|
+
// came up instead of faking a count.
|
|
1379
|
+
let memoryBridgeInitialized = false;
|
|
1380
|
+
try {
|
|
1381
|
+
const bridge = await import('../memory/memory-bridge.js');
|
|
1382
|
+
const result = await bridge.bridgeSessionStart({
|
|
1383
|
+
sessionId: newSessionId,
|
|
1384
|
+
metadata: { context: 'restore previous session patterns' },
|
|
1385
|
+
});
|
|
1386
|
+
if (result) {
|
|
1387
|
+
memoryBridgeInitialized = result.success;
|
|
1388
|
+
}
|
|
1389
|
+
else {
|
|
1390
|
+
warnings.push('Memory bridge unavailable — pattern restoration skipped');
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
catch (e) {
|
|
1394
|
+
warnings.push('Memory bridge failed to initialize');
|
|
1395
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
1396
|
+
console.error('[hooks-session-restore] memory bridge failed:', e);
|
|
1397
|
+
}
|
|
1398
|
+
return {
|
|
1399
|
+
sessionId: newSessionId,
|
|
1400
|
+
originalSessionId,
|
|
1401
|
+
restoredState: { tasksRestored, agentsRestored, memoryBridgeInitialized },
|
|
1402
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
1403
|
+
};
|
|
1404
|
+
},
|
|
1405
|
+
};
|
|
1306
1406
|
// Session end hook - persists state
|
|
1307
1407
|
export const hooksSessionEnd = {
|
|
1308
1408
|
name: 'hooks_session-end',
|
|
@@ -1356,8 +1456,10 @@ export const hooksSessionEnd = {
|
|
|
1356
1456
|
};
|
|
1357
1457
|
}
|
|
1358
1458
|
}
|
|
1359
|
-
catch {
|
|
1459
|
+
catch (e) {
|
|
1360
1460
|
// Bridge not available
|
|
1461
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
1462
|
+
console.error('[hooks-session-end] memory bridge failed:', e);
|
|
1361
1463
|
}
|
|
1362
1464
|
return {
|
|
1363
1465
|
sessionId,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Thin registration module that aggregates all hooks MCP tools into a single array.
|
|
4
4
|
* Business logic lives in hooks-embedding.ts, hooks-routing.ts, and hooks-intelligence.ts.
|
|
5
5
|
*/
|
|
6
|
-
import { hooksPreEdit, hooksPostEdit, hooksPreCommand, hooksPostCommand, hooksRoute, hooksRouteSemantic, hooksMetrics, hooksList, hooksPreTask, hooksPostTask, hooksExplain, hooksPretrain, hooksBuildAgents, hooksTransfer, hooksSessionStart, hooksSessionEnd, hooksIntelligence, } from './hooks-routing.js';
|
|
6
|
+
import { hooksPreEdit, hooksPostEdit, hooksPreCommand, hooksPostCommand, hooksRoute, hooksRouteSemantic, hooksMetrics, hooksList, hooksPreTask, hooksPostTask, hooksExplain, hooksPretrain, hooksBuildAgents, hooksTransfer, hooksSessionStart, hooksSessionRestore, hooksSessionEnd, hooksIntelligence, } from './hooks-routing.js';
|
|
7
7
|
import { hooksIntelligenceReset, hooksTrajectoryStart, hooksTrajectoryStep, hooksTrajectoryEnd, hooksPatternStore, hooksPatternSearch, hooksIntelligenceStats, hooksIntelligenceLearn, hooksIntelligenceAttention, hooksModelRoute, hooksModelOutcome, hooksModelStats, } from './hooks-intelligence.js';
|
|
8
8
|
// Export all hooks tools
|
|
9
9
|
export const hooksTools = [
|
|
@@ -23,6 +23,7 @@ export const hooksTools = [
|
|
|
23
23
|
hooksBuildAgents,
|
|
24
24
|
hooksTransfer,
|
|
25
25
|
hooksSessionStart,
|
|
26
|
+
hooksSessionRestore,
|
|
26
27
|
hooksSessionEnd,
|
|
27
28
|
hooksIntelligence,
|
|
28
29
|
hooksIntelligenceReset,
|
|
@@ -639,7 +639,10 @@ const monographWatchTool = {
|
|
|
639
639
|
}
|
|
640
640
|
const watcher = new MonographWatcher(repoPath);
|
|
641
641
|
watcher.on('monograph:updated', (_paths) => {
|
|
642
|
-
import('@monoes/monograph').then(({ buildAsync }) => buildAsync(repoPath)).catch(() => {
|
|
642
|
+
import('@monoes/monograph').then(({ buildAsync }) => buildAsync(repoPath)).catch((e) => {
|
|
643
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
644
|
+
console.error('[monograph_watch] background rebuild failed:', e);
|
|
645
|
+
});
|
|
643
646
|
});
|
|
644
647
|
await watcher.start();
|
|
645
648
|
_activeWatchers.set(repoPath, watcher);
|
|
@@ -776,7 +779,10 @@ function triggerBackgroundBuildIfNeeded(repoPath, commitsBehind, threshold = STA
|
|
|
776
779
|
_buildInProgress = true;
|
|
777
780
|
void import('@monoes/monograph')
|
|
778
781
|
.then(({ buildAsync }) => buildAsync(repoPath, { codeOnly: true }))
|
|
779
|
-
.catch(() => {
|
|
782
|
+
.catch((e) => {
|
|
783
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
784
|
+
console.error('[triggerBackgroundBuildIfNeeded] staleness-triggered rebuild failed:', e);
|
|
785
|
+
})
|
|
780
786
|
.finally(() => { _buildInProgress = false; });
|
|
781
787
|
return true;
|
|
782
788
|
}
|
|
@@ -40,8 +40,9 @@ function loadPerfStore() {
|
|
|
40
40
|
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
catch {
|
|
44
|
-
|
|
43
|
+
catch (e) {
|
|
44
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
45
|
+
console.error('[performance-tools] failed to load perf store, using empty store:', e);
|
|
45
46
|
}
|
|
46
47
|
return { metrics: [], benchmarks: {}, version: '3.0.0' };
|
|
47
48
|
}
|
|
@@ -34,8 +34,9 @@ function loadSession(sessionId) {
|
|
|
34
34
|
return JSON.parse(data);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
catch {
|
|
38
|
-
|
|
37
|
+
catch (e) {
|
|
38
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
39
|
+
console.error('[session-tools] loadSession failed:', e);
|
|
39
40
|
}
|
|
40
41
|
return null;
|
|
41
42
|
}
|
|
@@ -90,7 +91,10 @@ function loadRelatedStores(options) {
|
|
|
90
91
|
data.memory = JSON.parse(readFileSync(memoryPath, 'utf-8'));
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
|
-
catch {
|
|
94
|
+
catch (e) {
|
|
95
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
96
|
+
console.error('[session-tools] failed to load memory store:', e);
|
|
97
|
+
}
|
|
94
98
|
}
|
|
95
99
|
if (options.includeTasks) {
|
|
96
100
|
try {
|
|
@@ -99,7 +103,10 @@ function loadRelatedStores(options) {
|
|
|
99
103
|
data.tasks = JSON.parse(readFileSync(taskPath, 'utf-8'));
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
|
-
catch {
|
|
106
|
+
catch (e) {
|
|
107
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
108
|
+
console.error('[session-tools] failed to load task store:', e);
|
|
109
|
+
}
|
|
103
110
|
}
|
|
104
111
|
if (options.includeAgents) {
|
|
105
112
|
try {
|
|
@@ -108,7 +115,10 @@ function loadRelatedStores(options) {
|
|
|
108
115
|
data.agents = JSON.parse(readFileSync(agentPath, 'utf-8'));
|
|
109
116
|
}
|
|
110
117
|
}
|
|
111
|
-
catch {
|
|
118
|
+
catch (e) {
|
|
119
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
120
|
+
console.error('[session-tools] failed to load agent store:', e);
|
|
121
|
+
}
|
|
112
122
|
}
|
|
113
123
|
return data;
|
|
114
124
|
}
|
|
@@ -36,7 +36,10 @@ function loadSwarmStore() {
|
|
|
36
36
|
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
catch {
|
|
39
|
+
catch (e) {
|
|
40
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
41
|
+
console.error('[swarm-tools] failed to parse swarm-state.json — resetting to empty store:', e);
|
|
42
|
+
}
|
|
40
43
|
return { swarms: {}, version: '3.0.0' };
|
|
41
44
|
}
|
|
42
45
|
function saveSwarmStore(store) {
|
|
@@ -51,8 +51,9 @@ function loadMetrics() {
|
|
|
51
51
|
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
catch {
|
|
55
|
-
|
|
54
|
+
catch (e) {
|
|
55
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
56
|
+
console.error('[loadMetrics] failed to read/parse metrics.json, using defaults:', e);
|
|
56
57
|
}
|
|
57
58
|
return {
|
|
58
59
|
startTime: new Date().toISOString(),
|
|
@@ -543,8 +544,9 @@ export const systemTools = [
|
|
|
543
544
|
tasks = Object.values(store.tasks || {});
|
|
544
545
|
}
|
|
545
546
|
}
|
|
546
|
-
catch {
|
|
547
|
-
|
|
547
|
+
catch (e) {
|
|
548
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
549
|
+
console.error('[task_summary] failed to read/parse task store:', e);
|
|
548
550
|
}
|
|
549
551
|
return {
|
|
550
552
|
total: tasks.length,
|
|
@@ -4,5 +4,25 @@
|
|
|
4
4
|
* Tool definitions for task management with file persistence.
|
|
5
5
|
*/
|
|
6
6
|
import { type MCPTool } from './types.js';
|
|
7
|
+
interface TaskRecord {
|
|
8
|
+
taskId: string;
|
|
9
|
+
type: string;
|
|
10
|
+
description: string;
|
|
11
|
+
priority: 'low' | 'normal' | 'high' | 'critical';
|
|
12
|
+
status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'cancelled';
|
|
13
|
+
progress: number;
|
|
14
|
+
assignedTo: string[];
|
|
15
|
+
tags: string[];
|
|
16
|
+
createdAt: string;
|
|
17
|
+
startedAt: string | null;
|
|
18
|
+
completedAt: string | null;
|
|
19
|
+
result?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
interface TaskStore {
|
|
22
|
+
tasks: Record<string, TaskRecord>;
|
|
23
|
+
version: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function loadTaskStore(): TaskStore;
|
|
7
26
|
export declare const taskTools: MCPTool[];
|
|
27
|
+
export {};
|
|
8
28
|
//# sourceMappingURL=task-tools.d.ts.map
|
|
@@ -23,7 +23,7 @@ function ensureTaskDir() {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
const MAX_TASK_STORE_BYTES = 50 * 1024 * 1024;
|
|
26
|
-
function loadTaskStore() {
|
|
26
|
+
export function loadTaskStore() {
|
|
27
27
|
try {
|
|
28
28
|
const path = getTaskPath();
|
|
29
29
|
if (existsSync(path)) {
|
|
@@ -36,8 +36,9 @@ function loadTaskStore() {
|
|
|
36
36
|
return parsed;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
catch {
|
|
40
|
-
|
|
39
|
+
catch (e) {
|
|
40
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
41
|
+
console.error('[loadTaskStore] failed to read/parse task store:', e);
|
|
41
42
|
}
|
|
42
43
|
return { tasks: {}, version: '3.0.0' };
|
|
43
44
|
}
|
|
@@ -273,8 +274,9 @@ export const taskTools = [
|
|
|
273
274
|
writeFileSync(tmpAgent1, JSON.stringify(agentStore, null, 2), 'utf-8');
|
|
274
275
|
renameSync(tmpAgent1, agentStorePath);
|
|
275
276
|
}
|
|
276
|
-
catch {
|
|
277
|
-
|
|
277
|
+
catch (e) {
|
|
278
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
279
|
+
console.error('[task_complete] agent store sync failed:', e);
|
|
278
280
|
}
|
|
279
281
|
}
|
|
280
282
|
return {
|
|
@@ -372,15 +374,30 @@ export const taskTools = [
|
|
|
372
374
|
return { taskId, error: 'Task not found' };
|
|
373
375
|
}
|
|
374
376
|
const previouslyAssigned = [...task.assignedTo];
|
|
375
|
-
// Load agent store to sync worker state
|
|
377
|
+
// Load agent store to sync worker state. Distinguish "file doesn't
|
|
378
|
+
// exist yet" (safe to proceed with an empty store) from "file exists
|
|
379
|
+
// but failed to read/parse" (a real store that's momentarily corrupt
|
|
380
|
+
// or oversized) — the latter must NOT fall through to the unconditional
|
|
381
|
+
// write-back below, or a transient read failure silently wipes out
|
|
382
|
+
// every agent's real on-disk state.
|
|
376
383
|
const agentStorePath = join(getMonomindDataRoot(), 'agents', 'store.json');
|
|
377
384
|
let agentStore = { agents: {} };
|
|
385
|
+
let agentStoreReadFailed = false;
|
|
378
386
|
try {
|
|
379
|
-
if (existsSync(agentStorePath)
|
|
380
|
-
|
|
387
|
+
if (existsSync(agentStorePath)) {
|
|
388
|
+
if (statSync(agentStorePath).size <= MAX_TASK_STORE_BYTES) {
|
|
389
|
+
agentStore = JSON.parse(readFileSync(agentStorePath, 'utf-8'));
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
agentStoreReadFailed = true;
|
|
393
|
+
}
|
|
381
394
|
}
|
|
382
395
|
}
|
|
383
|
-
catch {
|
|
396
|
+
catch (e) {
|
|
397
|
+
agentStoreReadFailed = true;
|
|
398
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
399
|
+
console.error('[task_assign] failed to read agent store — skipping agent-store sync to avoid overwriting it with an empty one:', e);
|
|
400
|
+
}
|
|
384
401
|
// Reject IDs that would mutate Object.prototype when used as a key in
|
|
385
402
|
// the JSON-loaded plain object `agentStore.agents`.
|
|
386
403
|
const FORBIDDEN_AGENT_IDS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
@@ -422,19 +439,24 @@ export const taskTools = [
|
|
|
422
439
|
}
|
|
423
440
|
}
|
|
424
441
|
saveTaskStore(store);
|
|
425
|
-
// Save agent store
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
442
|
+
// Save agent store — skipped when the read above failed, so a
|
|
443
|
+
// transient corrupt/oversized store.json is left alone instead of
|
|
444
|
+
// being overwritten with an empty one.
|
|
445
|
+
if (!agentStoreReadFailed) {
|
|
446
|
+
const agentDir = join(getMonomindDataRoot(), 'agents');
|
|
447
|
+
if (!existsSync(agentDir)) {
|
|
448
|
+
mkdirSync(agentDir, { recursive: true });
|
|
449
|
+
}
|
|
450
|
+
const tmpAgent2 = `${agentStorePath}.${process.pid}.${Date.now()}.tmp`;
|
|
451
|
+
writeFileSync(tmpAgent2, JSON.stringify(agentStore, null, 2), 'utf-8');
|
|
452
|
+
renameSync(tmpAgent2, agentStorePath);
|
|
429
453
|
}
|
|
430
|
-
const tmpAgent2 = `${agentStorePath}.${process.pid}.${Date.now()}.tmp`;
|
|
431
|
-
writeFileSync(tmpAgent2, JSON.stringify(agentStore, null, 2), 'utf-8');
|
|
432
|
-
renameSync(tmpAgent2, agentStorePath);
|
|
433
454
|
return {
|
|
434
455
|
taskId: task.taskId,
|
|
435
456
|
assignedTo: task.assignedTo,
|
|
436
457
|
previouslyAssigned,
|
|
437
458
|
status: task.status,
|
|
459
|
+
...(agentStoreReadFailed ? { agentStoreSyncSkipped: true } : {}),
|
|
438
460
|
};
|
|
439
461
|
},
|
|
440
462
|
},
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
* Terminal session management with real command execution.
|
|
5
5
|
*/
|
|
6
6
|
import type { MCPTool } from './types.js';
|
|
7
|
+
/** Returns a copy of `env` with secret-shaped variable names removed. */
|
|
8
|
+
export declare function filterSecretEnvVars(env: NodeJS.ProcessEnv): Record<string, string>;
|
|
7
9
|
export declare const terminalTools: MCPTool[];
|
|
8
10
|
//# sourceMappingURL=terminal-tools.d.ts.map
|
|
@@ -1,45 +1,80 @@
|
|
|
1
1
|
import { getProjectCwd } from './types.js';
|
|
2
|
-
import { existsSync, readFileSync, statSync
|
|
2
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
3
3
|
import { join, resolve } from 'node:path';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { execSync } from 'node:child_process';
|
|
6
6
|
import { randomBytes } from 'node:crypto';
|
|
7
|
+
import { writeJsonFileAtomic } from '../utils/json-file.js';
|
|
7
8
|
// Storage paths
|
|
8
9
|
const STORAGE_DIR = '.monomind';
|
|
9
10
|
const TERMINAL_DIR = 'terminals';
|
|
10
11
|
const TERMINAL_FILE = 'store.json';
|
|
12
|
+
// ── Secret-shaped env var filtering for terminal_execute ────────────────────
|
|
13
|
+
// terminal_execute runs arbitrary (metacharacter-denylisted) shell commands
|
|
14
|
+
// via execSync with `env: { ...process.env, ...session.env }` — the host
|
|
15
|
+
// process's real environment, which commonly holds provider API keys
|
|
16
|
+
// (ANTHROPIC_API_KEY, OPENAI_API_KEY, ...), CI/VCS tokens, and cloud
|
|
17
|
+
// credentials. A single unpiped command (the metacharacter denylist blocks
|
|
18
|
+
// piping/chaining, but not e.g. `curl` or `aws` invoked directly) can still
|
|
19
|
+
// read and exfiltrate whatever env vars it inherits. Stripping variables
|
|
20
|
+
// whose *names* match common secret conventions is defense-in-depth: it
|
|
21
|
+
// doesn't change legitimate terminal usage (PATH/HOME/etc. stay intact) but
|
|
22
|
+
// meaningfully shrinks what a malicious or hijacked command can read by
|
|
23
|
+
// default. Callers that genuinely need a specific secret can pass it via
|
|
24
|
+
// terminal_create's `env` option, which flows through untouched (session.env
|
|
25
|
+
// is layered on *after* the filtered process.env below).
|
|
26
|
+
const SECRET_ENV_NAME_PATTERN = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|_AUTH$|^AUTH_|PRIVATE_KEY|ACCESS_KEY)/i;
|
|
27
|
+
/** Returns a copy of `env` with secret-shaped variable names removed. */
|
|
28
|
+
export function filterSecretEnvVars(env) {
|
|
29
|
+
const filtered = {};
|
|
30
|
+
for (const [k, v] of Object.entries(env)) {
|
|
31
|
+
if (v === undefined)
|
|
32
|
+
continue;
|
|
33
|
+
if (SECRET_ENV_NAME_PATTERN.test(k))
|
|
34
|
+
continue;
|
|
35
|
+
filtered[k] = v;
|
|
36
|
+
}
|
|
37
|
+
return filtered;
|
|
38
|
+
}
|
|
11
39
|
function getTerminalDir() {
|
|
12
40
|
return join(getProjectCwd(), STORAGE_DIR, TERMINAL_DIR);
|
|
13
41
|
}
|
|
14
42
|
function getTerminalPath() {
|
|
15
43
|
return join(getTerminalDir(), TERMINAL_FILE);
|
|
16
44
|
}
|
|
17
|
-
function ensureTerminalDir() {
|
|
18
|
-
const dir = getTerminalDir();
|
|
19
|
-
if (!existsSync(dir)) {
|
|
20
|
-
mkdirSync(dir, { recursive: true });
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
45
|
const MAX_TERMINAL_STORE_BYTES = 10 * 1024 * 1024; // 10 MB
|
|
24
|
-
|
|
46
|
+
// Same hardened-loader convention as agent-tools.ts's loadAgentStoreOrNull:
|
|
47
|
+
// handlers that mutate and save must use the null-aware variant, since
|
|
48
|
+
// treating a corrupt/oversized store as empty and then saving that back
|
|
49
|
+
// would silently wipe every real session. Also guards against a __proto__
|
|
50
|
+
// key in the parsed JSON (agent-tools.ts's loader does the same). Can't
|
|
51
|
+
// delegate to utils/json-file.ts's readJsonFileSync here — it collapses
|
|
52
|
+
// "file absent" and "file corrupt" into the same fallback value, but this
|
|
53
|
+
// loader must tell them apart (absent → empty default is safe to build on
|
|
54
|
+
// and save; corrupt → null, refuse to save over it).
|
|
55
|
+
function loadTerminalStoreOrNull() {
|
|
25
56
|
try {
|
|
26
57
|
const path = getTerminalPath();
|
|
27
|
-
if (existsSync(path)
|
|
28
|
-
return
|
|
29
|
-
|
|
58
|
+
if (!existsSync(path))
|
|
59
|
+
return { sessions: {}, version: '3.0.0' };
|
|
60
|
+
if (statSync(path).size > MAX_TERMINAL_STORE_BYTES)
|
|
61
|
+
return null;
|
|
62
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
63
|
+
if (parsed && typeof parsed === 'object' && Object.prototype.hasOwnProperty.call(parsed, '__proto__'))
|
|
64
|
+
return null;
|
|
65
|
+
return parsed;
|
|
30
66
|
}
|
|
31
|
-
catch {
|
|
32
|
-
|
|
67
|
+
catch (e) {
|
|
68
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
69
|
+
console.error('[loadTerminalStore] terminal store unreadable/corrupt:', e);
|
|
70
|
+
return null;
|
|
33
71
|
}
|
|
34
|
-
|
|
72
|
+
}
|
|
73
|
+
function loadTerminalStore() {
|
|
74
|
+
return loadTerminalStoreOrNull() ?? { sessions: {}, version: '3.0.0' };
|
|
35
75
|
}
|
|
36
76
|
function saveTerminalStore(store) {
|
|
37
|
-
|
|
38
|
-
// Unique tmp filename so concurrent handler invocations cannot clobber each
|
|
39
|
-
// other's .tmp mid-write (which would produce a partial JSON on rename).
|
|
40
|
-
const tmpPath = `${getTerminalPath()}.${process.pid}.${Date.now()}.tmp`;
|
|
41
|
-
writeFileSync(tmpPath, JSON.stringify(store, null, 2), 'utf-8');
|
|
42
|
-
renameSync(tmpPath, getTerminalPath());
|
|
77
|
+
writeJsonFileAtomic(getTerminalPath(), store);
|
|
43
78
|
}
|
|
44
79
|
export const terminalTools = [
|
|
45
80
|
{
|
|
@@ -55,7 +90,10 @@ export const terminalTools = [
|
|
|
55
90
|
},
|
|
56
91
|
},
|
|
57
92
|
handler: async (input) => {
|
|
58
|
-
const store =
|
|
93
|
+
const store = loadTerminalStoreOrNull();
|
|
94
|
+
if (!store) {
|
|
95
|
+
return { success: false, error: 'Terminal store is unreadable/corrupt — refusing to create a session to avoid overwriting real session data.' };
|
|
96
|
+
}
|
|
59
97
|
const MAX_SESSIONS = 1000;
|
|
60
98
|
if (Object.keys(store.sessions).length >= MAX_SESSIONS) {
|
|
61
99
|
return { success: false, error: 'Session limit reached' };
|
|
@@ -140,7 +178,10 @@ export const terminalTools = [
|
|
|
140
178
|
required: ['command'],
|
|
141
179
|
},
|
|
142
180
|
handler: async (input) => {
|
|
143
|
-
const store =
|
|
181
|
+
const store = loadTerminalStoreOrNull();
|
|
182
|
+
if (!store) {
|
|
183
|
+
return { success: false, error: 'Terminal store is unreadable/corrupt — refusing to execute to avoid overwriting real session data.' };
|
|
184
|
+
}
|
|
144
185
|
const sessionId = input.sessionId;
|
|
145
186
|
// Cap command: the metacharacter regex check at line 220 is O(n), and the
|
|
146
187
|
// raw command is stored verbatim in session history (up to 200 entries).
|
|
@@ -200,7 +241,7 @@ export const terminalTools = [
|
|
|
200
241
|
timeout,
|
|
201
242
|
maxBuffer: 5 * 1024 * 1024,
|
|
202
243
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
203
|
-
env: { ...process.env, ...session.env },
|
|
244
|
+
env: { ...filterSecretEnvVars(process.env), ...session.env },
|
|
204
245
|
});
|
|
205
246
|
exitCode = 0;
|
|
206
247
|
}
|
|
@@ -286,7 +327,10 @@ export const terminalTools = [
|
|
|
286
327
|
required: ['sessionId'],
|
|
287
328
|
},
|
|
288
329
|
handler: async (input) => {
|
|
289
|
-
const store =
|
|
330
|
+
const store = loadTerminalStoreOrNull();
|
|
331
|
+
if (!store) {
|
|
332
|
+
return { success: false, error: 'Terminal store is unreadable/corrupt — refusing to close a session to avoid overwriting real session data.' };
|
|
333
|
+
}
|
|
290
334
|
const sessionId = input.sessionId;
|
|
291
335
|
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
292
336
|
if (!sessionId || FORBIDDEN_KEYS.has(sessionId)) {
|
|
@@ -70,8 +70,10 @@ export class EWCConsolidator {
|
|
|
70
70
|
this.initialized = true;
|
|
71
71
|
return true;
|
|
72
72
|
}
|
|
73
|
-
catch {
|
|
73
|
+
catch (e) {
|
|
74
74
|
// Start fresh if no persisted state
|
|
75
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
76
|
+
console.error('[ewc-consolidation] failed to load persisted state, starting fresh:', e);
|
|
75
77
|
this.initialized = true;
|
|
76
78
|
return true;
|
|
77
79
|
}
|
|
@@ -544,8 +546,10 @@ export class EWCConsolidator {
|
|
|
544
546
|
fs.writeFileSync(tmp, JSON.stringify(state, null, 2));
|
|
545
547
|
fs.renameSync(tmp, this.config.storagePath);
|
|
546
548
|
}
|
|
547
|
-
catch {
|
|
549
|
+
catch (e) {
|
|
548
550
|
// Silently fail - persistence is best-effort
|
|
551
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
552
|
+
console.error('[ewc-consolidation] failed to persist state:', e);
|
|
549
553
|
}
|
|
550
554
|
}
|
|
551
555
|
/**
|