devchain-cli 0.5.0 → 0.6.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 (72) hide show
  1. package/dist/drizzle/0022_guests.sql +16 -0
  2. package/dist/drizzle/0023_add_guest_description.sql +1 -0
  3. package/dist/drizzle/meta/0022_snapshot.json +3064 -0
  4. package/dist/drizzle/meta/0023_snapshot.json +3069 -0
  5. package/dist/drizzle/meta/_journal.json +15 -1
  6. package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
  7. package/dist/server/app.module.js +2 -0
  8. package/dist/server/app.module.js.map +1 -1
  9. package/dist/server/modules/agents/controllers/agents.controller.d.ts +14 -1
  10. package/dist/server/modules/agents/controllers/agents.controller.js +30 -4
  11. package/dist/server/modules/agents/controllers/agents.controller.js.map +1 -1
  12. package/dist/server/modules/events/catalog/guest.registered.d.ts +24 -0
  13. package/dist/server/modules/events/catalog/guest.registered.js +15 -0
  14. package/dist/server/modules/events/catalog/guest.registered.js.map +1 -0
  15. package/dist/server/modules/events/catalog/guest.unregistered.d.ts +24 -0
  16. package/dist/server/modules/events/catalog/guest.unregistered.js +15 -0
  17. package/dist/server/modules/events/catalog/guest.unregistered.js.map +1 -0
  18. package/dist/server/modules/events/catalog/index.d.ts +38 -0
  19. package/dist/server/modules/events/catalog/index.js +4 -0
  20. package/dist/server/modules/events/catalog/index.js.map +1 -1
  21. package/dist/server/modules/guests/constants.d.ts +3 -0
  22. package/dist/server/modules/guests/constants.js +7 -0
  23. package/dist/server/modules/guests/constants.js.map +1 -0
  24. package/dist/server/modules/guests/dtos/guest.dto.d.ts +21 -0
  25. package/dist/server/modules/guests/dtos/guest.dto.js +3 -0
  26. package/dist/server/modules/guests/dtos/guest.dto.js.map +1 -0
  27. package/dist/server/modules/guests/guests.module.d.ts +2 -0
  28. package/dist/server/modules/guests/guests.module.js +26 -0
  29. package/dist/server/modules/guests/guests.module.js.map +1 -0
  30. package/dist/server/modules/guests/services/guest-health.service.d.ts +21 -0
  31. package/dist/server/modules/guests/services/guest-health.service.js +125 -0
  32. package/dist/server/modules/guests/services/guest-health.service.js.map +1 -0
  33. package/dist/server/modules/guests/services/guests.service.d.ts +30 -0
  34. package/dist/server/modules/guests/services/guests.service.js +178 -0
  35. package/dist/server/modules/guests/services/guests.service.js.map +1 -0
  36. package/dist/server/modules/mcp/controllers/mcp-http.controller.js +22 -0
  37. package/dist/server/modules/mcp/controllers/mcp-http.controller.js.map +1 -1
  38. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js +22 -0
  39. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js.map +1 -1
  40. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +67 -5
  41. package/dist/server/modules/mcp/dtos/mcp.dto.js +13 -1
  42. package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
  43. package/dist/server/modules/mcp/mcp.module.js +2 -0
  44. package/dist/server/modules/mcp/mcp.module.js.map +1 -1
  45. package/dist/server/modules/mcp/services/mcp.service.d.ts +9 -1
  46. package/dist/server/modules/mcp/services/mcp.service.js +357 -71
  47. package/dist/server/modules/mcp/services/mcp.service.js.map +1 -1
  48. package/dist/server/modules/projects/controllers/projects.controller.d.ts +2 -0
  49. package/dist/server/modules/projects/controllers/projects.controller.js +11 -0
  50. package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -1
  51. package/dist/server/modules/projects/services/projects.service.d.ts +7 -0
  52. package/dist/server/modules/projects/services/projects.service.js +70 -2
  53. package/dist/server/modules/projects/services/projects.service.js.map +1 -1
  54. package/dist/server/modules/registry/services/template-upgrade.service.d.ts +3 -1
  55. package/dist/server/modules/registry/services/template-upgrade.service.js +66 -17
  56. package/dist/server/modules/registry/services/template-upgrade.service.js.map +1 -1
  57. package/dist/server/modules/storage/db/schema.d.ts +159 -0
  58. package/dist/server/modules/storage/db/schema.js +18 -1
  59. package/dist/server/modules/storage/db/schema.js.map +1 -1
  60. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +12 -1
  61. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  62. package/dist/server/modules/storage/local/local-storage.service.d.ts +12 -1
  63. package/dist/server/modules/storage/local/local-storage.service.js +194 -1
  64. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  65. package/dist/server/modules/storage/models/domain.models.d.ts +12 -0
  66. package/dist/server/modules/terminal/services/tmux.service.d.ts +2 -0
  67. package/dist/server/modules/terminal/services/tmux.service.js +74 -13
  68. package/dist/server/modules/terminal/services/tmux.service.js.map +1 -1
  69. package/dist/server/tsconfig.tsbuildinfo +1 -1
  70. package/dist/server/ui/assets/{index-CvNs6-rV.js → index-hB0e02VB.js} +165 -165
  71. package/dist/server/ui/index.html +1 -1
  72. package/package.json +9 -1
@@ -19,8 +19,10 @@ const chat_service_1 = require("../../chat/services/chat.service");
19
19
  const sessions_service_1 = require("../../sessions/services/sessions.service");
20
20
  const sessions_message_pool_service_1 = require("../../sessions/services/sessions-message-pool.service");
21
21
  const terminal_gateway_1 = require("../../terminal/gateways/terminal.gateway");
22
+ const tmux_service_1 = require("../../terminal/services/tmux.service");
22
23
  const epics_service_1 = require("../../epics/services/epics.service");
23
24
  const settings_service_1 = require("../../settings/services/settings.service");
25
+ const guests_service_1 = require("../../guests/services/guests.service");
24
26
  const logger_1 = require("../../../common/logging/logger");
25
27
  const mcp_dto_1 = require("../dtos/mcp.dto");
26
28
  const instructions_resolver_1 = require("./instructions-resolver");
@@ -28,6 +30,19 @@ const error_types_1 = require("../../../common/errors/error-types");
28
30
  const common_2 = require("@nestjs/common");
29
31
  const zod_1 = require("zod");
30
32
  const logger = (0, logger_1.createLogger)('McpService');
33
+ function getActorFromContext(ctx) {
34
+ if (ctx.type === 'agent') {
35
+ return ctx.agent;
36
+ }
37
+ else if (ctx.type === 'guest') {
38
+ return {
39
+ id: ctx.guest.id,
40
+ name: ctx.guest.name,
41
+ projectId: ctx.guest.projectId,
42
+ };
43
+ }
44
+ return null;
45
+ }
31
46
  function redactSessionId(sessionId) {
32
47
  if (!sessionId)
33
48
  return '(none)';
@@ -43,14 +58,16 @@ function redactParams(params) {
43
58
  return params;
44
59
  }
45
60
  let McpService = class McpService {
46
- constructor(storage, chatService, sessionsService, messagePoolService, terminalGateway, epicsService, settingsService) {
61
+ constructor(storage, chatService, sessionsService, messagePoolService, terminalGateway, tmuxService, epicsService, settingsService, guestsService) {
47
62
  this.storage = storage;
48
63
  this.chatService = chatService;
49
64
  this.sessionsService = sessionsService;
50
65
  this.messagePoolService = messagePoolService;
51
66
  this.terminalGateway = terminalGateway;
67
+ this.tmuxService = tmuxService;
52
68
  this.epicsService = epicsService;
53
69
  this.settingsService = settingsService;
70
+ this.guestsService = guestsService;
54
71
  this.DEFAULT_INLINE_MAX_BYTES = 64 * 1024;
55
72
  logger.info('McpService initialized');
56
73
  this.featureFlags = this.storage.getFeatureFlags();
@@ -145,6 +162,8 @@ let McpService = class McpService {
145
162
  return await this.activityFinish(params);
146
163
  case 'devchain_list_sessions':
147
164
  return await this.listSessions();
165
+ case 'devchain_register_guest':
166
+ return await this.registerGuest(params);
148
167
  default:
149
168
  logger.warn({ tool: normalizedTool }, 'Unknown MCP tool');
150
169
  return {
@@ -525,21 +544,49 @@ let McpService = class McpService {
525
544
  const limit = validated.limit ?? 100;
526
545
  const offset = validated.offset ?? 0;
527
546
  const normalizedQuery = validated.q?.toLowerCase();
528
- const fetchOptions = normalizedQuery ? { limit: limit + offset, offset: 0 } : { limit, offset };
529
- const result = await this.storage.listAgents(project.id, fetchOptions);
530
- let agents = result.items;
547
+ const MAX_COMBINED_FETCH = 1000;
548
+ const [agentsResult, guests] = await Promise.all([
549
+ this.storage.listAgents(project.id, { limit: MAX_COMBINED_FETCH, offset: 0 }),
550
+ this.storage.listGuests(project.id),
551
+ ]);
552
+ const [agentPresence, tmuxSessions] = await Promise.all([
553
+ this.sessionsService
554
+ ? this.sessionsService.getAgentPresence(project.id)
555
+ : Promise.resolve(new Map()),
556
+ this.tmuxService
557
+ ? this.tmuxService.listAllSessionNames()
558
+ : Promise.resolve(new Set()),
559
+ ]);
560
+ const agentItems = agentsResult.items.map((agent) => ({
561
+ id: agent.id,
562
+ name: agent.name,
563
+ profileId: agent.profileId,
564
+ description: agent.description,
565
+ type: 'agent',
566
+ online: agentPresence.get(agent.id)?.online ?? false,
567
+ }));
568
+ const guestItems = guests.map((guest) => ({
569
+ id: guest.id,
570
+ name: guest.name,
571
+ profileId: null,
572
+ description: guest.description,
573
+ type: 'guest',
574
+ online: tmuxSessions.has(guest.tmuxSessionId),
575
+ }));
576
+ let allItems = [...agentItems, ...guestItems].sort((a, b) => {
577
+ const nameCompare = a.name.localeCompare(b.name);
578
+ if (nameCompare !== 0)
579
+ return nameCompare;
580
+ return a.type === 'agent' ? -1 : 1;
581
+ });
531
582
  if (normalizedQuery) {
532
- agents = agents.filter((agent) => agent.name.toLowerCase().includes(normalizedQuery));
583
+ allItems = allItems.filter((item) => item.name.toLowerCase().includes(normalizedQuery));
533
584
  }
534
- const paginatedAgents = normalizedQuery ? agents.slice(offset, offset + limit) : agents;
585
+ const total = allItems.length;
586
+ const paginatedItems = allItems.slice(offset, offset + limit);
535
587
  const response = {
536
- agents: paginatedAgents.map((agent) => ({
537
- id: agent.id,
538
- name: agent.name,
539
- profileId: agent.profileId,
540
- description: agent.description,
541
- })),
542
- total: normalizedQuery ? agents.length : result.total,
588
+ agents: paginatedItems,
589
+ total,
543
590
  limit,
544
591
  offset,
545
592
  };
@@ -1038,16 +1085,18 @@ let McpService = class McpService {
1038
1085
  const ctx = await this.resolveSessionContext(validated.sessionId);
1039
1086
  if (!ctx.success)
1040
1087
  return ctx;
1041
- const { agent: authorAgent, project } = ctx.data;
1042
- if (!authorAgent) {
1088
+ const sessionCtx = ctx.data;
1089
+ const authorActor = getActorFromContext(sessionCtx);
1090
+ if (!authorActor) {
1043
1091
  return {
1044
1092
  success: false,
1045
1093
  error: {
1046
1094
  code: 'AGENT_REQUIRED',
1047
- message: 'Session must be associated with an agent to add comments',
1095
+ message: 'Session must be associated with an agent or guest to add comments',
1048
1096
  },
1049
1097
  };
1050
1098
  }
1099
+ const project = sessionCtx.project;
1051
1100
  if (!project) {
1052
1101
  return {
1053
1102
  success: false,
@@ -1084,7 +1133,7 @@ let McpService = class McpService {
1084
1133
  }
1085
1134
  const comment = await this.storage.createEpicComment({
1086
1135
  epicId: validated.epicId,
1087
- authorName: authorAgent.name,
1136
+ authorName: authorActor.name,
1088
1137
  content: validated.content,
1089
1138
  });
1090
1139
  const response = {
@@ -1307,6 +1356,40 @@ let McpService = class McpService {
1307
1356
  };
1308
1357
  return { success: true, data: response };
1309
1358
  }
1359
+ async resolveRecipientByName(projectId, name) {
1360
+ try {
1361
+ const agent = await this.storage.getAgentByName(projectId, name);
1362
+ return {
1363
+ type: 'agent',
1364
+ id: agent.id,
1365
+ name: agent.name,
1366
+ };
1367
+ }
1368
+ catch (error) {
1369
+ if (!(error instanceof error_types_1.NotFoundError)) {
1370
+ throw error;
1371
+ }
1372
+ }
1373
+ const guest = await this.storage.getGuestByName(projectId, name);
1374
+ if (guest) {
1375
+ return {
1376
+ type: 'guest',
1377
+ id: guest.id,
1378
+ name: guest.name,
1379
+ tmuxSessionId: guest.tmuxSessionId,
1380
+ };
1381
+ }
1382
+ return null;
1383
+ }
1384
+ async getAvailableRecipientNames(projectId) {
1385
+ const [agentsResult, guests] = await Promise.all([
1386
+ this.storage.listAgents(projectId, { limit: 100, offset: 0 }),
1387
+ this.storage.listGuests(projectId),
1388
+ ]);
1389
+ const agentNames = agentsResult.items.map((a) => a.name);
1390
+ const guestNames = guests.map((g) => `${g.name} (guest)`);
1391
+ return [...agentNames, ...guestNames];
1392
+ }
1310
1393
  async sendMessage(params) {
1311
1394
  if (!this.sessionsService) {
1312
1395
  return {
@@ -1321,13 +1404,15 @@ let McpService = class McpService {
1321
1404
  const ctx = await this.resolveSessionContext(validated.sessionId);
1322
1405
  if (!ctx.success)
1323
1406
  return ctx;
1324
- const { agent: senderAgent, project } = ctx.data;
1325
- if (!senderAgent) {
1407
+ const sessionCtx = ctx.data;
1408
+ const sender = getActorFromContext(sessionCtx);
1409
+ const project = sessionCtx.project;
1410
+ if (!sender) {
1326
1411
  return {
1327
1412
  success: false,
1328
1413
  error: {
1329
1414
  code: 'AGENT_REQUIRED',
1330
- message: 'Session must be associated with an agent to send messages',
1415
+ message: 'Session must be associated with an agent or guest to send messages',
1331
1416
  },
1332
1417
  };
1333
1418
  }
@@ -1340,20 +1425,53 @@ let McpService = class McpService {
1340
1425
  },
1341
1426
  };
1342
1427
  }
1428
+ if (sessionCtx.type === 'guest') {
1429
+ if (validated.threadId) {
1430
+ return {
1431
+ success: false,
1432
+ error: {
1433
+ code: 'GUEST_THREAD_NOT_ALLOWED',
1434
+ message: 'Guests cannot use threaded messaging. Use recipientAgentNames for direct messaging.',
1435
+ },
1436
+ };
1437
+ }
1438
+ if (validated.recipient === 'user') {
1439
+ return {
1440
+ success: false,
1441
+ error: {
1442
+ code: 'GUEST_USER_DM_NOT_ALLOWED',
1443
+ message: 'Guests cannot send direct messages to users.',
1444
+ },
1445
+ };
1446
+ }
1447
+ }
1343
1448
  try {
1344
1449
  const autoLaunchSessions = process.env.NODE_ENV !== 'test';
1345
- const senderAgentId = senderAgent.id;
1346
- const senderAgentName = senderAgent.name;
1450
+ const senderId = sender.id;
1451
+ const senderName = sender.name;
1452
+ const senderType = sessionCtx.type;
1347
1453
  const recipientType = validated.recipient ?? 'agents';
1348
- let normalizedRecipientIds = [];
1454
+ const resolvedRecipients = [];
1349
1455
  if (validated.recipientAgentNames && validated.recipientAgentNames.length > 0) {
1350
1456
  for (const name of validated.recipientAgentNames) {
1351
- const agent = await this.storage.getAgentByName(project.id, name);
1352
- normalizedRecipientIds.push(agent.id);
1457
+ const recipient = await this.resolveRecipientByName(project.id, name);
1458
+ if (!recipient) {
1459
+ const availableNames = await this.getAvailableRecipientNames(project.id);
1460
+ return {
1461
+ success: false,
1462
+ error: {
1463
+ code: 'RECIPIENT_NOT_FOUND',
1464
+ message: `Recipient "${name}" not found. Available: ${availableNames.join(', ') || 'none'}`,
1465
+ },
1466
+ };
1467
+ }
1468
+ if (recipient.id !== senderId) {
1469
+ resolvedRecipients.push(recipient);
1470
+ }
1353
1471
  }
1354
1472
  }
1355
- normalizedRecipientIds = Array.from(new Set(normalizedRecipientIds.filter((id) => id && id !== senderAgentId)));
1356
- if (!validated.threadId && senderAgentId && recipientType !== 'user') {
1473
+ const uniqueRecipients = resolvedRecipients.filter((r, i, arr) => arr.findIndex((x) => x.id === r.id) === i);
1474
+ if (!validated.threadId && senderId && recipientType !== 'user') {
1357
1475
  if (!this.messagePoolService || !this.settingsService) {
1358
1476
  return {
1359
1477
  success: false,
@@ -1363,12 +1481,12 @@ let McpService = class McpService {
1363
1481
  },
1364
1482
  };
1365
1483
  }
1366
- if (normalizedRecipientIds.length === 0) {
1484
+ if (uniqueRecipients.length === 0) {
1367
1485
  return {
1368
1486
  success: false,
1369
1487
  error: {
1370
1488
  code: 'RECIPIENTS_REQUIRED',
1371
- message: 'Recipient agents must be provided when sending agent-to-agent without threadId.',
1489
+ message: 'Recipients must be provided when sending without threadId.',
1372
1490
  },
1373
1491
  };
1374
1492
  }
@@ -1377,35 +1495,77 @@ let McpService = class McpService {
1377
1495
  const activeSessions = this.sessionsService
1378
1496
  ? await this.sessionsService.listActiveSessions()
1379
1497
  : [];
1380
- for (const agentId of normalizedRecipientIds) {
1381
- const agent = await this.storage.getAgent(agentId);
1382
- let session = activeSessions.find((s) => s.agentId === agentId);
1383
- let wasLaunched = false;
1384
- if (!session && autoLaunchSessions && this.sessionsService) {
1385
- try {
1386
- const launched = await this.sessionsService.launchSession({
1387
- projectId: project.id,
1388
- agentId,
1498
+ for (const recipient of uniqueRecipients) {
1499
+ const injectionText = `\n[This message is sent from "${senderName}" ${senderType} use devchain_send_message tool for communication]\n${validated.message}\n`;
1500
+ if (recipient.type === 'agent') {
1501
+ let session = activeSessions.find((s) => s.agentId === recipient.id);
1502
+ let wasLaunched = false;
1503
+ if (!session && autoLaunchSessions && this.sessionsService) {
1504
+ try {
1505
+ const launched = await this.sessionsService.launchSession({
1506
+ projectId: project.id,
1507
+ agentId: recipient.id,
1508
+ });
1509
+ session = launched;
1510
+ activeSessions.push(launched);
1511
+ wasLaunched = true;
1512
+ }
1513
+ catch {
1514
+ }
1515
+ }
1516
+ await this.messagePoolService.enqueue(recipient.id, injectionText, {
1517
+ source: 'mcp.send_message',
1518
+ submitKeys: ['Enter'],
1519
+ senderAgentId: senderId,
1520
+ projectId: project.id,
1521
+ agentName: recipient.name,
1522
+ });
1523
+ queued.push({
1524
+ name: recipient.name,
1525
+ type: 'agent',
1526
+ status: wasLaunched ? 'launched' : 'queued',
1527
+ });
1528
+ }
1529
+ else {
1530
+ const isOnline = this.tmuxService
1531
+ ? await this.tmuxService.hasSession(recipient.tmuxSessionId)
1532
+ : false;
1533
+ if (!isOnline) {
1534
+ queued.push({
1535
+ name: recipient.name,
1536
+ type: 'guest',
1537
+ status: 'failed',
1538
+ error: 'Recipient offline',
1389
1539
  });
1390
- session = launched;
1391
- activeSessions.push(launched);
1392
- wasLaunched = true;
1393
1540
  }
1394
- catch {
1541
+ else if (this.tmuxService) {
1542
+ try {
1543
+ await this.tmuxService.pasteAndSubmit(recipient.tmuxSessionId, injectionText);
1544
+ queued.push({
1545
+ name: recipient.name,
1546
+ type: 'guest',
1547
+ status: 'delivered',
1548
+ });
1549
+ }
1550
+ catch (error) {
1551
+ logger.warn({ guestId: recipient.id, tmuxSessionId: recipient.tmuxSessionId, error }, 'Failed to deliver message to guest');
1552
+ queued.push({
1553
+ name: recipient.name,
1554
+ type: 'guest',
1555
+ status: 'failed',
1556
+ error: error instanceof Error ? error.message : 'Delivery failed',
1557
+ });
1558
+ }
1559
+ }
1560
+ else {
1561
+ queued.push({
1562
+ name: recipient.name,
1563
+ type: 'guest',
1564
+ status: 'failed',
1565
+ error: 'Tmux service unavailable',
1566
+ });
1395
1567
  }
1396
1568
  }
1397
- const injectionText = `\n[This message is sent from "${senderAgentName}" agent use devchain_send_message tool for communication]\n${validated.message}\n`;
1398
- await this.messagePoolService.enqueue(agentId, injectionText, {
1399
- source: 'mcp.send_message',
1400
- submitKeys: ['Enter'],
1401
- senderAgentId: senderAgentId,
1402
- projectId: project.id,
1403
- agentName: agent.name,
1404
- });
1405
- queued.push({
1406
- agentName: agent.name,
1407
- status: wasLaunched ? 'launched' : 'queued',
1408
- });
1409
1569
  }
1410
1570
  const response = {
1411
1571
  mode: 'pooled',
@@ -1425,11 +1585,11 @@ let McpService = class McpService {
1425
1585
  };
1426
1586
  }
1427
1587
  let threadId = validated.threadId;
1428
- if (!threadId && senderAgentId) {
1588
+ if (!threadId && senderId) {
1429
1589
  if (recipientType === 'user') {
1430
1590
  const direct = await this.chatService.createDirectThread({
1431
1591
  projectId: project.id,
1432
- agentId: senderAgentId,
1592
+ agentId: senderId,
1433
1593
  });
1434
1594
  threadId = direct.id;
1435
1595
  }
@@ -1448,15 +1608,12 @@ let McpService = class McpService {
1448
1608
  const thread = await this.chatService.getThread(threadId);
1449
1609
  const message = await this.chatService.createMessage(threadId, {
1450
1610
  authorType: 'agent',
1451
- authorAgentId: senderAgentId,
1611
+ authorAgentId: senderId,
1452
1612
  content: validated.message,
1453
1613
  });
1454
- let targetAgentIds = normalizedRecipientIds;
1455
- if (senderAgentId &&
1456
- thread.members &&
1457
- thread.members.length > 1 &&
1458
- targetAgentIds.length === 0) {
1459
- targetAgentIds = thread.members.filter((id) => id !== senderAgentId);
1614
+ let targetAgentIds = uniqueRecipients.filter((r) => r.type === 'agent').map((r) => r.id);
1615
+ if (senderId && thread.members && thread.members.length > 1 && targetAgentIds.length === 0) {
1616
+ targetAgentIds = thread.members.filter((id) => id !== senderId);
1460
1617
  }
1461
1618
  const activeSessions = await this.sessionsService.listActiveSessions();
1462
1619
  const delivered = [];
@@ -1479,7 +1636,7 @@ let McpService = class McpService {
1479
1636
  delivered.push({ agentId, agentName: agent.name, sessionId: '', status: 'queued' });
1480
1637
  continue;
1481
1638
  }
1482
- const injectionText = `\n[CHAT] From: ${senderAgentName} • Thread: ${threadId}\n${validated.message}\n[ACK] tools/call { name: "devchain_chat_ack", arguments: { sessionId: "${session.id}", thread_id: "${threadId}", message_id: "${message.id}" } }\n`;
1639
+ const injectionText = `\n[CHAT] From: ${senderName} • Thread: ${threadId}\n${validated.message}\n[ACK] tools/call { name: "devchain_chat_ack", arguments: { sessionId: "${session.id}", thread_id: "${threadId}", message_id: "${message.id}" } }\n`;
1483
1640
  await this.sessionsService.injectTextIntoSession(session.id, injectionText);
1484
1641
  delivered.push({
1485
1642
  agentId,
@@ -1523,7 +1680,8 @@ let McpService = class McpService {
1523
1680
  const ctx = await this.resolveSessionContext(validated.sessionId);
1524
1681
  if (!ctx.success)
1525
1682
  return ctx;
1526
- const { agent } = ctx.data;
1683
+ const sessionCtx = ctx.data;
1684
+ const agent = getActorFromContext(sessionCtx);
1527
1685
  if (!agent) {
1528
1686
  return {
1529
1687
  success: false,
@@ -1757,7 +1915,18 @@ let McpService = class McpService {
1757
1915
  const ctx = await this.resolveSessionContext(validated.sessionId);
1758
1916
  if (!ctx.success)
1759
1917
  return ctx;
1760
- const { project, agent } = ctx.data;
1918
+ const sessionCtx = ctx.data;
1919
+ const project = sessionCtx.project;
1920
+ const agent = getActorFromContext(sessionCtx);
1921
+ if (sessionCtx.type === 'guest') {
1922
+ return {
1923
+ success: false,
1924
+ error: {
1925
+ code: 'GUEST_ACTIVITY_NOT_ALLOWED',
1926
+ message: 'Guests cannot use activity tools.',
1927
+ },
1928
+ };
1929
+ }
1761
1930
  if (!project) {
1762
1931
  return {
1763
1932
  success: false,
@@ -1818,7 +1987,18 @@ let McpService = class McpService {
1818
1987
  const ctx = await this.resolveSessionContext(validated.sessionId);
1819
1988
  if (!ctx.success)
1820
1989
  return ctx;
1821
- const { project, agent } = ctx.data;
1990
+ const sessionCtx = ctx.data;
1991
+ const project = sessionCtx.project;
1992
+ const agent = getActorFromContext(sessionCtx);
1993
+ if (sessionCtx.type === 'guest') {
1994
+ return {
1995
+ success: false,
1996
+ error: {
1997
+ code: 'GUEST_ACTIVITY_NOT_ALLOWED',
1998
+ message: 'Guests cannot use activity tools.',
1999
+ },
2000
+ };
2001
+ }
1822
2002
  if (!project) {
1823
2003
  return {
1824
2004
  success: false,
@@ -1961,11 +2141,15 @@ let McpService = class McpService {
1961
2141
  matchingSessions = activeSessions.filter((s) => s.id.startsWith(sessionId));
1962
2142
  }
1963
2143
  if (matchingSessions.length === 0) {
2144
+ const guestContext = await this.tryResolveGuestContext(sessionId);
2145
+ if (guestContext) {
2146
+ return { success: true, data: guestContext };
2147
+ }
1964
2148
  return {
1965
2149
  success: false,
1966
2150
  error: {
1967
2151
  code: 'SESSION_NOT_FOUND',
1968
- message: `No active session found matching '${sessionId}'`,
2152
+ message: `No active session or guest found matching '${sessionId}'`,
1969
2153
  },
1970
2154
  };
1971
2155
  }
@@ -2013,6 +2197,7 @@ let McpService = class McpService {
2013
2197
  }
2014
2198
  }
2015
2199
  const context = {
2200
+ type: 'agent',
2016
2201
  session: {
2017
2202
  id: session.id,
2018
2203
  agentId: session.agentId,
@@ -2035,6 +2220,103 @@ let McpService = class McpService {
2035
2220
  };
2036
2221
  }
2037
2222
  }
2223
+ async tryResolveGuestContext(guestId) {
2224
+ if (!this.guestsService || !this.tmuxService) {
2225
+ return null;
2226
+ }
2227
+ try {
2228
+ let guest;
2229
+ if (guestId.length === 36) {
2230
+ guest = await this.storage.getGuest(guestId);
2231
+ }
2232
+ else {
2233
+ const matches = await this.storage.getGuestsByIdPrefix(guestId);
2234
+ if (matches.length === 1) {
2235
+ guest = matches[0];
2236
+ }
2237
+ else {
2238
+ return null;
2239
+ }
2240
+ }
2241
+ const sessionAlive = await this.tmuxService.hasSession(guest.tmuxSessionId);
2242
+ if (!sessionAlive) {
2243
+ logger.warn({ guestId: guest.id, tmuxSessionId: guest.tmuxSessionId }, 'Guest tmux session no longer exists');
2244
+ return null;
2245
+ }
2246
+ const project = await this.storage.getProject(guest.projectId);
2247
+ const context = {
2248
+ type: 'guest',
2249
+ guest: {
2250
+ id: guest.id,
2251
+ name: guest.name,
2252
+ projectId: guest.projectId,
2253
+ tmuxSessionId: guest.tmuxSessionId,
2254
+ },
2255
+ project: {
2256
+ id: project.id,
2257
+ name: project.name,
2258
+ rootPath: project.rootPath,
2259
+ },
2260
+ };
2261
+ return context;
2262
+ }
2263
+ catch (error) {
2264
+ logger.debug({ guestId: redactSessionId(guestId), error: String(error) }, 'Failed to resolve guest context');
2265
+ return null;
2266
+ }
2267
+ }
2268
+ async registerGuest(params) {
2269
+ if (!this.guestsService) {
2270
+ return {
2271
+ success: false,
2272
+ error: {
2273
+ code: 'SERVICE_UNAVAILABLE',
2274
+ message: 'Guest registration requires full app context',
2275
+ },
2276
+ };
2277
+ }
2278
+ const validated = mcp_dto_1.RegisterGuestParamsSchema.parse(params);
2279
+ try {
2280
+ const result = await this.guestsService.register({
2281
+ name: validated.name,
2282
+ tmuxSessionId: validated.tmuxSessionId,
2283
+ description: validated.description,
2284
+ });
2285
+ const response = {
2286
+ guestId: result.guestId,
2287
+ name: validated.name,
2288
+ projectId: result.projectId,
2289
+ projectName: result.projectName,
2290
+ isSandbox: result.isSandbox,
2291
+ registeredAt: new Date().toISOString(),
2292
+ };
2293
+ logger.info({ guestId: result.guestId, projectId: result.projectId, isSandbox: result.isSandbox }, 'Guest registered successfully');
2294
+ return { success: true, data: response };
2295
+ }
2296
+ catch (error) {
2297
+ if (error instanceof error_types_1.ValidationError) {
2298
+ return {
2299
+ success: false,
2300
+ error: {
2301
+ code: 'VALIDATION_ERROR',
2302
+ message: error.message,
2303
+ data: error.details,
2304
+ },
2305
+ };
2306
+ }
2307
+ if (error instanceof Error && error.name === 'ConflictError') {
2308
+ return {
2309
+ success: false,
2310
+ error: {
2311
+ code: 'CONFLICT',
2312
+ message: error.message,
2313
+ data: error.data,
2314
+ },
2315
+ };
2316
+ }
2317
+ throw error;
2318
+ }
2319
+ }
2038
2320
  mapStatusSummary(status) {
2039
2321
  return {
2040
2322
  id: status.id,
@@ -2364,13 +2646,17 @@ exports.McpService = McpService = __decorate([
2364
2646
  __param(2, (0, common_1.Inject)((0, common_1.forwardRef)(() => sessions_service_1.SessionsService))),
2365
2647
  __param(3, (0, common_1.Inject)((0, common_1.forwardRef)(() => sessions_message_pool_service_1.SessionsMessagePoolService))),
2366
2648
  __param(4, (0, common_1.Inject)((0, common_1.forwardRef)(() => terminal_gateway_1.TerminalGateway))),
2367
- __param(5, (0, common_1.Inject)((0, common_1.forwardRef)(() => epics_service_1.EpicsService))),
2368
- __param(6, (0, common_1.Inject)((0, common_1.forwardRef)(() => settings_service_1.SettingsService))),
2649
+ __param(5, (0, common_1.Inject)((0, common_1.forwardRef)(() => tmux_service_1.TmuxService))),
2650
+ __param(6, (0, common_1.Inject)((0, common_1.forwardRef)(() => epics_service_1.EpicsService))),
2651
+ __param(7, (0, common_1.Inject)((0, common_1.forwardRef)(() => settings_service_1.SettingsService))),
2652
+ __param(8, (0, common_1.Inject)((0, common_1.forwardRef)(() => guests_service_1.GuestsService))),
2369
2653
  __metadata("design:paramtypes", [Object, chat_service_1.ChatService,
2370
2654
  sessions_service_1.SessionsService,
2371
2655
  sessions_message_pool_service_1.SessionsMessagePoolService,
2372
2656
  terminal_gateway_1.TerminalGateway,
2657
+ tmux_service_1.TmuxService,
2373
2658
  epics_service_1.EpicsService,
2374
- settings_service_1.SettingsService])
2659
+ settings_service_1.SettingsService,
2660
+ guests_service_1.GuestsService])
2375
2661
  ], McpService);
2376
2662
  //# sourceMappingURL=mcp.service.js.map