@appoly/multiagent-chat 1.0.6 → 1.0.7

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 (3) hide show
  1. package/main.js +87 -93
  2. package/package.json +1 -1
  3. package/renderer.js +7 -1
package/main.js CHANGED
@@ -47,6 +47,13 @@ let agentColors = {}; // Map of agent name -> color
47
47
  let sessionBaseCommit = null; // Git commit hash at session start for diff baseline
48
48
  let currentSessionId = null; // Current active session ID
49
49
 
50
+ function sendToRenderer(channel, payload) {
51
+ if (!mainWindow || mainWindow.isDestroyed()) return;
52
+ const wc = mainWindow.webContents;
53
+ if (!wc || wc.isDestroyed()) return;
54
+ wc.send(channel, payload);
55
+ }
56
+
50
57
  // ═══════════════════════════════════════════════════════════
51
58
  // Session Storage Functions
52
59
  // ═══════════════════════════════════════════════════════════
@@ -538,6 +545,9 @@ function createWindow() {
538
545
  mainWindow.once('ready-to-show', () => {
539
546
  mainWindow.show();
540
547
  });
548
+ mainWindow.on('closed', () => {
549
+ mainWindow = null;
550
+ });
541
551
 
542
552
  if (process.platform === 'darwin' && app.dock) {
543
553
  app.dock.setIcon(iconPath);
@@ -743,13 +753,11 @@ class AgentProcess {
743
753
  this.process.onData((data) => {
744
754
  const output = data.toString();
745
755
  this.outputBuffer.push(output);
746
- if (mainWindow) {
747
- mainWindow.webContents.send('agent-output', {
748
- agentName: this.name,
749
- output: output,
750
- isPty: true
751
- });
752
- }
756
+ sendToRenderer('agent-output', {
757
+ agentName: this.name,
758
+ output: output,
759
+ isPty: true
760
+ });
753
761
  });
754
762
 
755
763
  this.process.onExit(({ exitCode, signal }) => {
@@ -778,17 +786,13 @@ class AgentProcess {
778
786
  this.process.stdout.on('data', (data) => {
779
787
  const output = data.toString();
780
788
  this.outputBuffer.push(output);
781
- if (mainWindow) {
782
- mainWindow.webContents.send('agent-output', { agentName: this.name, output, isPty: false });
783
- }
789
+ sendToRenderer('agent-output', { agentName: this.name, output, isPty: false });
784
790
  });
785
791
 
786
792
  this.process.stderr.on('data', (data) => {
787
793
  const output = data.toString();
788
794
  this.outputBuffer.push(`[stderr] ${output}`);
789
- if (mainWindow) {
790
- mainWindow.webContents.send('agent-output', { agentName: this.name, output: `[stderr] ${output}`, isPty: false });
791
- }
795
+ sendToRenderer('agent-output', { agentName: this.name, output: `[stderr] ${output}`, isPty: false });
792
796
  });
793
797
 
794
798
  this.process.on('close', (code) => {
@@ -809,13 +813,11 @@ class AgentProcess {
809
813
  handleExit(exitCode) {
810
814
  if (this.intentionalStop) {
811
815
  console.log(`Agent ${this.name} stopped intentionally`);
812
- if (mainWindow) {
813
- mainWindow.webContents.send('agent-status', {
814
- agentName: this.name,
815
- status: 'stopped',
816
- exitCode: exitCode
817
- });
818
- }
816
+ sendToRenderer('agent-status', {
817
+ agentName: this.name,
818
+ status: 'stopped',
819
+ exitCode: exitCode
820
+ });
819
821
  return;
820
822
  }
821
823
 
@@ -824,47 +826,39 @@ class AgentProcess {
824
826
  this.restartCount++;
825
827
  console.log(`Agent ${this.name} exited unexpectedly (code ${exitCode}), restarting (attempt ${this.restartCount}/${this.maxRestarts})...`);
826
828
 
827
- if (mainWindow) {
828
- mainWindow.webContents.send('agent-status', {
829
- agentName: this.name,
830
- status: 'restarting',
831
- exitCode: exitCode,
832
- restartCount: this.restartCount
833
- });
834
- }
829
+ sendToRenderer('agent-status', {
830
+ agentName: this.name,
831
+ status: 'restarting',
832
+ exitCode: exitCode,
833
+ restartCount: this.restartCount
834
+ });
835
835
 
836
836
  // Wait a moment before relaunching (give time for auto-updates etc.)
837
837
  const delay = 2000 * this.restartCount;
838
838
  setTimeout(() => {
839
839
  this.start(this.lastPrompt).then(() => {
840
840
  console.log(`Agent ${this.name} restarted successfully (attempt ${this.restartCount})`);
841
- if (mainWindow) {
842
- mainWindow.webContents.send('agent-status', {
843
- agentName: this.name,
844
- status: 'running'
845
- });
846
- }
841
+ sendToRenderer('agent-status', {
842
+ agentName: this.name,
843
+ status: 'running'
844
+ });
847
845
  }).catch(err => {
848
846
  console.error(`Failed to restart agent ${this.name}:`, err);
849
- if (mainWindow) {
850
- mainWindow.webContents.send('agent-status', {
851
- agentName: this.name,
852
- status: 'stopped',
853
- exitCode: exitCode,
854
- error: `Restart failed: ${err.message}`
855
- });
856
- }
847
+ sendToRenderer('agent-status', {
848
+ agentName: this.name,
849
+ status: 'stopped',
850
+ exitCode: exitCode,
851
+ error: `Restart failed: ${err.message}`
852
+ });
857
853
  });
858
854
  }, delay);
859
855
  } else {
860
856
  console.log(`Agent ${this.name} exited (code ${exitCode}), max restarts reached or no prompt stored`);
861
- if (mainWindow) {
862
- mainWindow.webContents.send('agent-status', {
863
- agentName: this.name,
864
- status: 'stopped',
865
- exitCode: exitCode
866
- });
867
- }
857
+ sendToRenderer('agent-status', {
858
+ agentName: this.name,
859
+ status: 'stopped',
860
+ exitCode: exitCode
861
+ });
868
862
  }
869
863
  }
870
864
 
@@ -918,10 +912,11 @@ function getOutboxRelativePath(agentName) {
918
912
  }
919
913
 
920
914
  function sendMessageToOtherAgents(senderName, message) {
915
+ const timestamp = new Date().toISOString();
921
916
  for (const agent of agents) {
922
917
  if (agent.name.toLowerCase() !== senderName.toLowerCase()) {
923
918
  const outboxFile = getOutboxRelativePath(agent.name);
924
- const formattedMessage = `\n---\n📨 MESSAGE FROM ${senderName.toUpperCase()}:\n\n${message}\n\n---\n(Respond via: cat << 'EOF' > ${outboxFile})\n`;
919
+ const formattedMessage = `\n---\n📨 MESSAGE FROM ${senderName.toUpperCase()}:\n🕐 ${timestamp}\n\n${message}\n\n---\n(Respond via: cat << 'EOF' > ${outboxFile})\n`;
925
920
  console.log(`Delivering message from ${senderName} to ${agent.name}`);
926
921
  agent.sendMessage(formattedMessage);
927
922
  }
@@ -929,9 +924,10 @@ function sendMessageToOtherAgents(senderName, message) {
929
924
  }
930
925
 
931
926
  function sendMessageToAllAgents(message) {
927
+ const timestamp = new Date().toISOString();
932
928
  for (const agent of agents) {
933
929
  const outboxFile = getOutboxRelativePath(agent.name);
934
- const formattedMessage = `\n---\n📨 MESSAGE FROM USER:\n\n${message}\n\n---\n(Respond via: cat << 'EOF' > ${outboxFile})\n`;
930
+ const formattedMessage = `\n---\n📨 MESSAGE FROM USER:\n🕐 ${timestamp}\n\n${message}\n\n---\n(Respond via: cat << 'EOF' > ${outboxFile})\n`;
935
931
  console.log(`Delivering user message to ${agent.name}`);
936
932
  agent.sendMessage(formattedMessage);
937
933
  }
@@ -963,21 +959,17 @@ async function startAgents(challenge) {
963
959
  await agent.start(prompt);
964
960
  console.log(`Started agent: ${agent.name}`);
965
961
 
966
- if (mainWindow) {
967
- mainWindow.webContents.send('agent-status', {
968
- agentName: agent.name,
969
- status: 'running'
970
- });
971
- }
962
+ sendToRenderer('agent-status', {
963
+ agentName: agent.name,
964
+ status: 'running'
965
+ });
972
966
  } catch (error) {
973
967
  console.error(`Failed to start agent ${agent.name}:`, error);
974
- if (mainWindow) {
975
- mainWindow.webContents.send('agent-status', {
976
- agentName: agent.name,
977
- status: 'error',
978
- error: error.message
979
- });
980
- }
968
+ sendToRenderer('agent-status', {
969
+ agentName: agent.name,
970
+ status: 'error',
971
+ error: error.message
972
+ });
981
973
  }
982
974
  }
983
975
  }
@@ -997,9 +989,7 @@ function startFileWatcher() {
997
989
  fileWatcher.on('change', async () => {
998
990
  try {
999
991
  const messages = await getChatContent();
1000
- if (mainWindow) {
1001
- mainWindow.webContents.send('chat-updated', messages);
1002
- }
992
+ sendToRenderer('chat-updated', messages);
1003
993
  } catch (error) {
1004
994
  console.error('Error reading chat file:', error);
1005
995
  }
@@ -1066,9 +1056,7 @@ function startOutboxWatcher() {
1066
1056
  }).catch(e => console.warn('Failed to update session index:', e.message));
1067
1057
  }
1068
1058
 
1069
- if (mainWindow) {
1070
- mainWindow.webContents.send('chat-message', message);
1071
- }
1059
+ sendToRenderer('chat-message', message);
1072
1060
  } catch (error) {
1073
1061
  console.error(`Error processing outbox file ${filePath}:`, error);
1074
1062
  } finally {
@@ -1118,9 +1106,7 @@ async function sendUserMessage(messageText) {
1118
1106
  }).catch(e => console.warn('Failed to update session index:', e.message));
1119
1107
  }
1120
1108
 
1121
- if (mainWindow) {
1122
- mainWindow.webContents.send('chat-message', message);
1123
- }
1109
+ sendToRenderer('chat-message', message);
1124
1110
  } catch (error) {
1125
1111
  console.error('Error appending user message:', error);
1126
1112
  throw error;
@@ -1373,21 +1359,17 @@ ipcMain.handle('resume-session', async (event, { projectRoot, sessionId, newMess
1373
1359
  await agent.start(prompt);
1374
1360
  console.log(`Started agent: ${agent.name} (resumed)`);
1375
1361
 
1376
- if (mainWindow) {
1377
- mainWindow.webContents.send('agent-status', {
1378
- agentName: agent.name,
1379
- status: 'running'
1380
- });
1381
- }
1362
+ sendToRenderer('agent-status', {
1363
+ agentName: agent.name,
1364
+ status: 'running'
1365
+ });
1382
1366
  } catch (error) {
1383
1367
  console.error(`Failed to start agent ${agent.name}:`, error);
1384
- if (mainWindow) {
1385
- mainWindow.webContents.send('agent-status', {
1386
- agentName: agent.name,
1387
- status: 'error',
1388
- error: error.message
1389
- });
1390
- }
1368
+ sendToRenderer('agent-status', {
1369
+ agentName: agent.name,
1370
+ status: 'error',
1371
+ error: error.message
1372
+ });
1391
1373
  }
1392
1374
  }
1393
1375
 
@@ -1436,6 +1418,20 @@ ipcMain.handle('start-session', async (event, { challenge, workspace: selectedWo
1436
1418
  currentSessionId = sessionId;
1437
1419
  const sessionDir = await setupSessionDirectory(resolvedRoot, sessionId);
1438
1420
  workspacePath = sessionDir;
1421
+ const initialTimestamp = new Date().toISOString();
1422
+
1423
+ // Seed chat history with the initial user challenge so it appears in UI immediately.
1424
+ messageSequence = 1;
1425
+ const initialMessage = {
1426
+ seq: messageSequence,
1427
+ type: 'user',
1428
+ agent: 'User',
1429
+ timestamp: initialTimestamp,
1430
+ content: challenge,
1431
+ color: agentColors['user'] || '#a0aec0'
1432
+ };
1433
+ const chatPath = path.join(workspacePath, config.chat_file || 'chat.jsonl');
1434
+ await fs.appendFile(chatPath, JSON.stringify(initialMessage) + '\n');
1439
1435
 
1440
1436
  // Add to session index
1441
1437
  const sessionMeta = {
@@ -1443,16 +1439,13 @@ ipcMain.handle('start-session', async (event, { challenge, workspace: selectedWo
1443
1439
  title: generateSessionTitle(challenge),
1444
1440
  firstPrompt: challenge.slice(0, 200),
1445
1441
  workspace: resolvedRoot,
1446
- createdAt: new Date().toISOString(),
1447
- lastActiveAt: new Date().toISOString(),
1448
- messageCount: 0,
1442
+ createdAt: initialTimestamp,
1443
+ lastActiveAt: initialTimestamp,
1444
+ messageCount: 1,
1449
1445
  status: 'active'
1450
1446
  };
1451
1447
  await addSessionToIndex(resolvedRoot, sessionMeta);
1452
1448
 
1453
- // Reset message sequence
1454
- messageSequence = 0;
1455
-
1456
1449
  initializeAgents();
1457
1450
  await startAgents(challenge);
1458
1451
  startFileWatcher();
@@ -1466,7 +1459,8 @@ ipcMain.handle('start-session', async (event, { challenge, workspace: selectedWo
1466
1459
  agents: agents.map(a => ({ name: a.name, use_pty: a.use_pty })),
1467
1460
  workspace: agentCwd,
1468
1461
  colors: agentColors,
1469
- sessionId: sessionId
1462
+ sessionId: sessionId,
1463
+ initialMessage
1470
1464
  };
1471
1465
  } catch (error) {
1472
1466
  console.error('Error starting session:', error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appoly/multiagent-chat",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Multi-agent chat for collaborative AI discussions",
5
5
  "main": "main.js",
6
6
  "bin": {
package/renderer.js CHANGED
@@ -311,7 +311,13 @@ async function handleNewSession(challenge) {
311
311
  if (result.success) {
312
312
  currentSessionId = result.sessionId;
313
313
  agentColors = result.colors || {};
314
- chatMessages = [];
314
+ if (result.initialMessage) {
315
+ chatMessages = [result.initialMessage];
316
+ } else if (Array.isArray(result.messages)) {
317
+ chatMessages = result.messages;
318
+ } else {
319
+ chatMessages = [];
320
+ }
315
321
  sessionState = 'active';
316
322
 
317
323
  result.agents.forEach(agent => {