@vibecheckai/cli 3.2.6 → 3.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.
Files changed (84) hide show
  1. package/bin/registry.js +192 -5
  2. package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
  3. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  4. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  5. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  6. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  7. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  8. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  9. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  10. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  11. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  12. package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
  13. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
  14. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
  15. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  16. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  17. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  18. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  19. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  20. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  21. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  22. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  23. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  24. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  25. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  26. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  27. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  28. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  29. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  30. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  31. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  32. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  33. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  34. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  35. package/bin/runners/lib/analyzers.js +81 -18
  36. package/bin/runners/lib/authority-badge.js +425 -0
  37. package/bin/runners/lib/cli-output.js +7 -1
  38. package/bin/runners/lib/error-handler.js +16 -9
  39. package/bin/runners/lib/exit-codes.js +275 -0
  40. package/bin/runners/lib/global-flags.js +37 -0
  41. package/bin/runners/lib/help-formatter.js +413 -0
  42. package/bin/runners/lib/logger.js +38 -0
  43. package/bin/runners/lib/unified-cli-output.js +604 -0
  44. package/bin/runners/lib/upsell.js +148 -0
  45. package/bin/runners/runApprove.js +1200 -0
  46. package/bin/runners/runAuth.js +324 -95
  47. package/bin/runners/runCheckpoint.js +39 -21
  48. package/bin/runners/runClassify.js +859 -0
  49. package/bin/runners/runContext.js +136 -24
  50. package/bin/runners/runDoctor.js +108 -68
  51. package/bin/runners/runFix.js +6 -5
  52. package/bin/runners/runGuard.js +212 -118
  53. package/bin/runners/runInit.js +3 -2
  54. package/bin/runners/runMcp.js +130 -52
  55. package/bin/runners/runPolish.js +43 -20
  56. package/bin/runners/runProve.js +1 -2
  57. package/bin/runners/runReport.js +3 -2
  58. package/bin/runners/runScan.js +63 -44
  59. package/bin/runners/runShip.js +3 -4
  60. package/bin/runners/runValidate.js +19 -2
  61. package/bin/runners/runWatch.js +104 -53
  62. package/bin/vibecheck.js +106 -19
  63. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  64. package/mcp-server/agent-firewall-interceptor.js +367 -31
  65. package/mcp-server/authority-tools.js +569 -0
  66. package/mcp-server/conductor/conflict-resolver.js +588 -0
  67. package/mcp-server/conductor/execution-planner.js +544 -0
  68. package/mcp-server/conductor/index.js +377 -0
  69. package/mcp-server/conductor/lock-manager.js +615 -0
  70. package/mcp-server/conductor/request-queue.js +550 -0
  71. package/mcp-server/conductor/session-manager.js +500 -0
  72. package/mcp-server/conductor/tools.js +510 -0
  73. package/mcp-server/index.js +1149 -243
  74. package/mcp-server/lib/{api-client.js → api-client.cjs} +40 -4
  75. package/mcp-server/lib/logger.cjs +30 -0
  76. package/mcp-server/logger.js +173 -0
  77. package/mcp-server/package.json +2 -2
  78. package/mcp-server/premium-tools.js +2 -2
  79. package/mcp-server/tier-auth.js +245 -35
  80. package/mcp-server/truth-firewall-tools.js +145 -15
  81. package/mcp-server/vibecheck-tools.js +2 -2
  82. package/package.json +2 -3
  83. package/mcp-server/index.old.js +0 -4137
  84. package/mcp-server/package-lock.json +0 -165
@@ -0,0 +1,500 @@
1
+ /**
2
+ * Conductor Session Manager
3
+ *
4
+ * Tracks active AI agent sessions per project.
5
+ * Enables multi-agent coordination by knowing which agents are active.
6
+ *
7
+ * Codename: Conductor
8
+ */
9
+
10
+ "use strict";
11
+
12
+ import fs from "fs";
13
+ import path from "path";
14
+ import crypto from "crypto";
15
+ import { conductorLogger as log, getErrorMessage } from "../logger.js";
16
+
17
+ /**
18
+ * @typedef {Object} AgentSession
19
+ * @property {string} agentId - Agent identifier (cursor, copilot, etc.)
20
+ * @property {string} sessionId - Unique session ID
21
+ * @property {string} tier - User tier (FREE, STARTER, PRO, ENTERPRISE)
22
+ * @property {string} trust - Trust level (low, medium, high)
23
+ * @property {Date} startTime - Session start timestamp
24
+ * @property {Date} lastActivity - Last activity timestamp
25
+ * @property {string[]} workingFiles - Files this agent is working on
26
+ * @property {string[]} pendingProposals - Pending proposal IDs
27
+ * @property {string} projectRoot - Project root directory
28
+ */
29
+
30
+ /**
31
+ * Default session timeout (30 minutes)
32
+ */
33
+ const DEFAULT_SESSION_TIMEOUT_MS = 30 * 60 * 1000;
34
+
35
+ /**
36
+ * Trust level mapping based on tier
37
+ */
38
+ const TIER_TRUST_MAP = {
39
+ FREE: "low",
40
+ STARTER: "medium",
41
+ PRO: "high",
42
+ ENTERPRISE: "high",
43
+ };
44
+
45
+ /**
46
+ * Session Manager class
47
+ */
48
+ class SessionManager {
49
+ constructor(options = {}) {
50
+ this.sessions = new Map(); // sessionId -> AgentSession
51
+ this.agentSessions = new Map(); // agentId -> Set<sessionId>
52
+ this.projectSessions = new Map(); // projectRoot -> Set<sessionId>
53
+ this.sessionTimeout = options.sessionTimeout || DEFAULT_SESSION_TIMEOUT_MS;
54
+ this.persistPath = options.persistPath || null;
55
+
56
+ // Start cleanup interval
57
+ this.cleanupInterval = setInterval(() => {
58
+ this.cleanupExpiredSessions();
59
+ }, 60000); // Check every minute
60
+
61
+ // Load persisted state if available
62
+ if (this.persistPath) {
63
+ this.loadState();
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Generate a unique session ID
69
+ * @returns {string} Session ID
70
+ */
71
+ generateSessionId() {
72
+ return `ses_${crypto.randomBytes(12).toString("hex")}`;
73
+ }
74
+
75
+ /**
76
+ * Register a new agent session
77
+ * @param {Object} params - Session parameters
78
+ * @returns {AgentSession} Created session
79
+ */
80
+ registerSession({
81
+ agentId,
82
+ tier = "FREE",
83
+ projectRoot,
84
+ workingFiles = [],
85
+ }) {
86
+ const sessionId = this.generateSessionId();
87
+ const now = new Date();
88
+
89
+ const session = {
90
+ agentId,
91
+ sessionId,
92
+ tier,
93
+ trust: TIER_TRUST_MAP[tier] || "low",
94
+ startTime: now,
95
+ lastActivity: now,
96
+ workingFiles,
97
+ pendingProposals: [],
98
+ projectRoot: path.resolve(projectRoot),
99
+ };
100
+
101
+ // Store session
102
+ this.sessions.set(sessionId, session);
103
+
104
+ // Index by agent
105
+ if (!this.agentSessions.has(agentId)) {
106
+ this.agentSessions.set(agentId, new Set());
107
+ }
108
+ this.agentSessions.get(agentId).add(sessionId);
109
+
110
+ // Index by project
111
+ const normalizedRoot = path.resolve(projectRoot);
112
+ if (!this.projectSessions.has(normalizedRoot)) {
113
+ this.projectSessions.set(normalizedRoot, new Set());
114
+ }
115
+ this.projectSessions.get(normalizedRoot).add(sessionId);
116
+
117
+ // Persist state
118
+ this.saveState();
119
+
120
+ return session;
121
+ }
122
+
123
+ /**
124
+ * Get a session by ID
125
+ * @param {string} sessionId - Session ID
126
+ * @returns {AgentSession|null} Session or null
127
+ */
128
+ getSession(sessionId) {
129
+ const session = this.sessions.get(sessionId);
130
+ if (!session) return null;
131
+
132
+ // Check if expired
133
+ if (this.isSessionExpired(session)) {
134
+ this.terminateSession(sessionId);
135
+ return null;
136
+ }
137
+
138
+ return session;
139
+ }
140
+
141
+ /**
142
+ * Get all sessions for an agent
143
+ * @param {string} agentId - Agent ID
144
+ * @returns {AgentSession[]} Sessions
145
+ */
146
+ getAgentSessions(agentId) {
147
+ const sessionIds = this.agentSessions.get(agentId);
148
+ if (!sessionIds) return [];
149
+
150
+ const sessions = [];
151
+ for (const sessionId of sessionIds) {
152
+ const session = this.getSession(sessionId);
153
+ if (session) {
154
+ sessions.push(session);
155
+ }
156
+ }
157
+ return sessions;
158
+ }
159
+
160
+ /**
161
+ * Get all sessions for a project
162
+ * @param {string} projectRoot - Project root
163
+ * @returns {AgentSession[]} Sessions
164
+ */
165
+ getProjectSessions(projectRoot) {
166
+ const normalizedRoot = path.resolve(projectRoot);
167
+ const sessionIds = this.projectSessions.get(normalizedRoot);
168
+ if (!sessionIds) return [];
169
+
170
+ const sessions = [];
171
+ for (const sessionId of sessionIds) {
172
+ const session = this.getSession(sessionId);
173
+ if (session) {
174
+ sessions.push(session);
175
+ }
176
+ }
177
+ return sessions;
178
+ }
179
+
180
+ /**
181
+ * Get all active agents for a project
182
+ * @param {string} projectRoot - Project root
183
+ * @returns {Object[]} Active agents with their sessions
184
+ */
185
+ getActiveAgents(projectRoot) {
186
+ const sessions = this.getProjectSessions(projectRoot);
187
+ const agentMap = new Map();
188
+
189
+ for (const session of sessions) {
190
+ if (!agentMap.has(session.agentId)) {
191
+ agentMap.set(session.agentId, {
192
+ agentId: session.agentId,
193
+ tier: session.tier,
194
+ trust: session.trust,
195
+ sessions: [],
196
+ workingFiles: new Set(),
197
+ });
198
+ }
199
+
200
+ const agent = agentMap.get(session.agentId);
201
+ agent.sessions.push(session);
202
+ session.workingFiles.forEach(f => agent.workingFiles.add(f));
203
+ }
204
+
205
+ // Convert sets to arrays
206
+ return Array.from(agentMap.values()).map(agent => ({
207
+ ...agent,
208
+ workingFiles: Array.from(agent.workingFiles),
209
+ sessionCount: agent.sessions.length,
210
+ }));
211
+ }
212
+
213
+ /**
214
+ * Update session activity
215
+ * @param {string} sessionId - Session ID
216
+ * @param {Object} updates - Updates to apply
217
+ * @returns {AgentSession|null} Updated session
218
+ */
219
+ updateSession(sessionId, updates = {}) {
220
+ const session = this.sessions.get(sessionId);
221
+ if (!session) return null;
222
+
223
+ // Update last activity
224
+ session.lastActivity = new Date();
225
+
226
+ // Apply updates
227
+ if (updates.workingFiles) {
228
+ session.workingFiles = updates.workingFiles;
229
+ }
230
+ if (updates.pendingProposals) {
231
+ session.pendingProposals = updates.pendingProposals;
232
+ }
233
+
234
+ // Persist state
235
+ this.saveState();
236
+
237
+ return session;
238
+ }
239
+
240
+ /**
241
+ * Add working file to session
242
+ * @param {string} sessionId - Session ID
243
+ * @param {string} filePath - File path
244
+ */
245
+ addWorkingFile(sessionId, filePath) {
246
+ const session = this.sessions.get(sessionId);
247
+ if (!session) return;
248
+
249
+ if (!session.workingFiles.includes(filePath)) {
250
+ session.workingFiles.push(filePath);
251
+ session.lastActivity = new Date();
252
+ this.saveState();
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Remove working file from session
258
+ * @param {string} sessionId - Session ID
259
+ * @param {string} filePath - File path
260
+ */
261
+ removeWorkingFile(sessionId, filePath) {
262
+ const session = this.sessions.get(sessionId);
263
+ if (!session) return;
264
+
265
+ const index = session.workingFiles.indexOf(filePath);
266
+ if (index !== -1) {
267
+ session.workingFiles.splice(index, 1);
268
+ session.lastActivity = new Date();
269
+ this.saveState();
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Add pending proposal to session
275
+ * @param {string} sessionId - Session ID
276
+ * @param {string} proposalId - Proposal ID
277
+ */
278
+ addPendingProposal(sessionId, proposalId) {
279
+ const session = this.sessions.get(sessionId);
280
+ if (!session) return;
281
+
282
+ if (!session.pendingProposals.includes(proposalId)) {
283
+ session.pendingProposals.push(proposalId);
284
+ session.lastActivity = new Date();
285
+ this.saveState();
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Remove pending proposal from session
291
+ * @param {string} sessionId - Session ID
292
+ * @param {string} proposalId - Proposal ID
293
+ */
294
+ removePendingProposal(sessionId, proposalId) {
295
+ const session = this.sessions.get(sessionId);
296
+ if (!session) return;
297
+
298
+ const index = session.pendingProposals.indexOf(proposalId);
299
+ if (index !== -1) {
300
+ session.pendingProposals.splice(index, 1);
301
+ session.lastActivity = new Date();
302
+ this.saveState();
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Check if session is expired
308
+ * @param {AgentSession} session - Session to check
309
+ * @returns {boolean} Is expired
310
+ */
311
+ isSessionExpired(session) {
312
+ const elapsed = Date.now() - new Date(session.lastActivity).getTime();
313
+ return elapsed > this.sessionTimeout;
314
+ }
315
+
316
+ /**
317
+ * Terminate a session
318
+ * @param {string} sessionId - Session ID to terminate
319
+ * @returns {boolean} Success
320
+ */
321
+ terminateSession(sessionId) {
322
+ const session = this.sessions.get(sessionId);
323
+ if (!session) return false;
324
+
325
+ // Remove from sessions
326
+ this.sessions.delete(sessionId);
327
+
328
+ // Remove from agent index
329
+ const agentSessions = this.agentSessions.get(session.agentId);
330
+ if (agentSessions) {
331
+ agentSessions.delete(sessionId);
332
+ if (agentSessions.size === 0) {
333
+ this.agentSessions.delete(session.agentId);
334
+ }
335
+ }
336
+
337
+ // Remove from project index
338
+ const projectSessions = this.projectSessions.get(session.projectRoot);
339
+ if (projectSessions) {
340
+ projectSessions.delete(sessionId);
341
+ if (projectSessions.size === 0) {
342
+ this.projectSessions.delete(session.projectRoot);
343
+ }
344
+ }
345
+
346
+ // Persist state
347
+ this.saveState();
348
+
349
+ return true;
350
+ }
351
+
352
+ /**
353
+ * Cleanup expired sessions
354
+ */
355
+ cleanupExpiredSessions() {
356
+ const expiredIds = [];
357
+
358
+ for (const [sessionId, session] of this.sessions) {
359
+ if (this.isSessionExpired(session)) {
360
+ expiredIds.push(sessionId);
361
+ }
362
+ }
363
+
364
+ for (const sessionId of expiredIds) {
365
+ this.terminateSession(sessionId);
366
+ }
367
+
368
+ if (expiredIds.length > 0) {
369
+ log.info(`Cleaned up ${expiredIds.length} expired sessions`);
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Get session statistics
375
+ * @returns {Object} Statistics
376
+ */
377
+ getStatistics() {
378
+ const stats = {
379
+ totalSessions: this.sessions.size,
380
+ activeAgents: this.agentSessions.size,
381
+ activeProjects: this.projectSessions.size,
382
+ byTier: { FREE: 0, STARTER: 0, PRO: 0, ENTERPRISE: 0 },
383
+ byTrust: { low: 0, medium: 0, high: 0 },
384
+ };
385
+
386
+ for (const session of this.sessions.values()) {
387
+ stats.byTier[session.tier] = (stats.byTier[session.tier] || 0) + 1;
388
+ stats.byTrust[session.trust] = (stats.byTrust[session.trust] || 0) + 1;
389
+ }
390
+
391
+ return stats;
392
+ }
393
+
394
+ /**
395
+ * Save state to disk
396
+ */
397
+ saveState() {
398
+ if (!this.persistPath) return;
399
+
400
+ try {
401
+ const dir = path.dirname(this.persistPath);
402
+ if (!fs.existsSync(dir)) {
403
+ fs.mkdirSync(dir, { recursive: true });
404
+ }
405
+
406
+ const state = {
407
+ sessions: Array.from(this.sessions.entries()),
408
+ timestamp: new Date().toISOString(),
409
+ };
410
+
411
+ fs.writeFileSync(this.persistPath, JSON.stringify(state, null, 2));
412
+ } catch (error) {
413
+ log.warn(`Failed to save state: ${getErrorMessage(error)}`);
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Load state from disk
419
+ */
420
+ loadState() {
421
+ if (!this.persistPath || !fs.existsSync(this.persistPath)) return;
422
+
423
+ try {
424
+ const content = fs.readFileSync(this.persistPath, "utf-8");
425
+ const state = JSON.parse(content);
426
+
427
+ // Restore sessions
428
+ for (const [sessionId, session] of state.sessions || []) {
429
+ // Convert date strings back to dates
430
+ session.startTime = new Date(session.startTime);
431
+ session.lastActivity = new Date(session.lastActivity);
432
+
433
+ // Only restore non-expired sessions
434
+ if (!this.isSessionExpired(session)) {
435
+ this.sessions.set(sessionId, session);
436
+
437
+ // Rebuild indexes
438
+ if (!this.agentSessions.has(session.agentId)) {
439
+ this.agentSessions.set(session.agentId, new Set());
440
+ }
441
+ this.agentSessions.get(session.agentId).add(sessionId);
442
+
443
+ if (!this.projectSessions.has(session.projectRoot)) {
444
+ this.projectSessions.set(session.projectRoot, new Set());
445
+ }
446
+ this.projectSessions.get(session.projectRoot).add(sessionId);
447
+ }
448
+ }
449
+
450
+ log.info(`Restored ${this.sessions.size} sessions from disk`);
451
+ } catch (error) {
452
+ log.warn(`Failed to load state: ${getErrorMessage(error)}`);
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Shutdown the session manager
458
+ */
459
+ shutdown() {
460
+ if (this.cleanupInterval) {
461
+ clearInterval(this.cleanupInterval);
462
+ }
463
+ this.saveState();
464
+ }
465
+ }
466
+
467
+ /**
468
+ * Create a session manager instance
469
+ * @param {Object} options - Options
470
+ * @returns {SessionManager} Manager instance
471
+ */
472
+ function createSessionManager(options = {}) {
473
+ return new SessionManager(options);
474
+ }
475
+
476
+ // Default instance
477
+ let defaultManager = null;
478
+
479
+ /**
480
+ * Get the default session manager
481
+ * @param {string} projectRoot - Project root for persist path
482
+ * @returns {SessionManager} Default manager
483
+ */
484
+ function getSessionManager(projectRoot) {
485
+ if (!defaultManager) {
486
+ const persistPath = projectRoot
487
+ ? path.join(projectRoot, ".vibecheck", "conductor", "sessions.json")
488
+ : null;
489
+ defaultManager = createSessionManager({ persistPath });
490
+ }
491
+ return defaultManager;
492
+ }
493
+
494
+ export {
495
+ SessionManager,
496
+ createSessionManager,
497
+ getSessionManager,
498
+ TIER_TRUST_MAP,
499
+ DEFAULT_SESSION_TIMEOUT_MS,
500
+ };