let-them-talk 3.3.2 → 3.4.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/dashboard.js CHANGED
@@ -228,6 +228,74 @@ function apiStatus(query) {
228
228
  };
229
229
  }
230
230
 
231
+ function apiStats(query) {
232
+ const projectPath = query.get('project') || null;
233
+ const history = readJsonl(filePath('history.jsonl', projectPath));
234
+ const agents = readJson(filePath('agents.json', projectPath));
235
+
236
+ // Per-agent stats
237
+ const perAgent = {};
238
+ let totalMessages = history.length;
239
+ const hourBuckets = new Array(24).fill(0);
240
+
241
+ for (let i = 0; i < history.length; i++) {
242
+ const m = history[i];
243
+ const from = m.from || 'unknown';
244
+ if (!perAgent[from]) {
245
+ perAgent[from] = { messages: 0, responseTimes: [], hours: new Array(24).fill(0) };
246
+ }
247
+ perAgent[from].messages++;
248
+ const ts = new Date(m.timestamp);
249
+ const hour = ts.getHours();
250
+ perAgent[from].hours[hour]++;
251
+ hourBuckets[hour]++;
252
+
253
+ // Compute response time if this is a reply
254
+ if (m.reply_to) {
255
+ for (let j = i - 1; j >= Math.max(0, i - 50); j--) {
256
+ if (history[j].id === m.reply_to) {
257
+ const delta = ts.getTime() - new Date(history[j].timestamp).getTime();
258
+ if (delta > 0 && delta < 3600000) perAgent[from].responseTimes.push(delta);
259
+ break;
260
+ }
261
+ }
262
+ }
263
+ }
264
+
265
+ // Build per-agent summary
266
+ const agentStats = {};
267
+ let busiestAgent = null;
268
+ let busiestCount = 0;
269
+ for (const [name, data] of Object.entries(perAgent)) {
270
+ const avgResponseMs = data.responseTimes.length
271
+ ? Math.round(data.responseTimes.reduce((a, b) => a + b, 0) / data.responseTimes.length)
272
+ : null;
273
+ const peakHour = data.hours.indexOf(Math.max(...data.hours));
274
+ agentStats[name] = {
275
+ messages: data.messages,
276
+ avg_response_ms: avgResponseMs,
277
+ peak_hour: peakHour,
278
+ };
279
+ if (data.messages > busiestCount) {
280
+ busiestCount = data.messages;
281
+ busiestAgent = name;
282
+ }
283
+ }
284
+
285
+ // Conversation velocity (messages per minute over last 10 minutes)
286
+ const tenMinAgo = Date.now() - 600000;
287
+ const recentCount = history.filter(m => new Date(m.timestamp).getTime() > tenMinAgo).length;
288
+ const velocity = Math.round((recentCount / 10) * 10) / 10;
289
+
290
+ return {
291
+ total_messages: totalMessages,
292
+ busiest_agent: busiestAgent,
293
+ velocity_per_min: velocity,
294
+ hour_distribution: hourBuckets,
295
+ agents: agentStats,
296
+ };
297
+ }
298
+
231
299
  function apiReset(query) {
232
300
  const projectPath = query.get('project') || null;
233
301
  const dataDir = resolveDataDir(projectPath);
@@ -528,9 +596,12 @@ function apiUpdateTask(body, query) {
528
596
  const task = tasks.find(t => t.id === body.task_id);
529
597
  if (!task) return { error: 'Task not found' };
530
598
 
599
+ const validStatuses = ['pending', 'in_progress', 'done', 'blocked'];
600
+ if (!validStatuses.includes(body.status)) return { error: 'Invalid status. Must be: ' + validStatuses.join(', ') };
531
601
  task.status = body.status;
532
602
  task.updated_at = new Date().toISOString();
533
603
  if (body.notes) {
604
+ if (!Array.isArray(task.notes)) task.notes = [];
534
605
  task.notes.push({ by: 'Dashboard', text: body.notes, at: new Date().toISOString() });
535
606
  }
536
607
 
@@ -544,8 +615,9 @@ function apiDiscover() {
544
615
  const checked = new Set();
545
616
  const existing = new Set(getProjects().map(p => p.path));
546
617
 
547
- function scanDir(dir, depth) {
548
- if (depth > 2 || checked.has(dir)) return;
618
+ function scanDir(dir, depth, maxDepth) {
619
+ maxDepth = maxDepth || 3;
620
+ if (depth > maxDepth || checked.has(dir)) return;
549
621
  checked.add(dir);
550
622
  try {
551
623
  const entries = fs.readdirSync(dir, { withFileTypes: true });
@@ -559,17 +631,26 @@ function apiDiscover() {
559
631
  if (!existing.has(projectPath)) {
560
632
  found.push({ name: path.basename(projectPath), path: projectPath, dataDir: fullPath });
561
633
  }
562
- } else if (depth < 2) {
563
- scanDir(fullPath, depth + 1);
634
+ } else if (depth < maxDepth) {
635
+ scanDir(fullPath, depth + 1, maxDepth);
564
636
  }
565
637
  }
566
638
  } catch {}
567
639
  }
568
640
 
569
- // Scan from cwd, parent, and home
641
+ // Scan from cwd, parent, home, Desktop, and common project locations
570
642
  const cwd = process.cwd();
643
+ const home = process.env.HOME || process.env.USERPROFILE || '';
571
644
  scanDir(cwd, 0);
572
645
  scanDir(path.dirname(cwd), 1);
646
+ if (home) {
647
+ scanDir(home, 0);
648
+ scanDir(path.join(home, 'Desktop'), 0);
649
+ scanDir(path.join(home, 'Documents'), 0);
650
+ scanDir(path.join(home, 'Projects'), 0);
651
+ scanDir(path.join(home, 'Desktop', 'Claude Projects'), 0);
652
+ scanDir(path.join(home, 'Desktop', 'Projects'), 0);
653
+ }
573
654
 
574
655
  return found;
575
656
  }
@@ -652,6 +733,233 @@ function apiLaunchAgent(body) {
652
733
  };
653
734
  }
654
735
 
736
+ // --- v3.4: Message Edit ---
737
+ function apiEditMessage(body, query) {
738
+ const projectPath = query.get('project') || null;
739
+ const { id, content } = body;
740
+ if (!id || !content) return { error: 'Missing "id" and/or "content" fields' };
741
+ if (content.length > 50000) return { error: 'Content too long (max 50000 chars)' };
742
+
743
+ const dataDir = resolveDataDir(projectPath);
744
+ const historyFile = path.join(dataDir, 'history.jsonl');
745
+ const messagesFile = path.join(dataDir, 'messages.jsonl');
746
+
747
+ let found = false;
748
+ const now = new Date().toISOString();
749
+
750
+ // Update in history.jsonl
751
+ if (fs.existsSync(historyFile)) {
752
+ const lines = fs.readFileSync(historyFile, 'utf8').trim().split('\n').filter(Boolean);
753
+ const updated = lines.map(line => {
754
+ try {
755
+ const msg = JSON.parse(line);
756
+ if (msg.id === id) {
757
+ found = true;
758
+ if (!msg.edit_history) msg.edit_history = [];
759
+ msg.edit_history.push({ content: msg.content, edited_at: now });
760
+ msg.content = content;
761
+ msg.edited = true;
762
+ msg.edited_at = now;
763
+ return JSON.stringify(msg);
764
+ }
765
+ return line;
766
+ } catch { return line; }
767
+ });
768
+ if (found) fs.writeFileSync(historyFile, updated.join('\n') + '\n');
769
+ }
770
+
771
+ // Also update in messages.jsonl (for agents that haven't consumed yet)
772
+ if (found && fs.existsSync(messagesFile)) {
773
+ const raw = fs.readFileSync(messagesFile, 'utf8').trim();
774
+ if (raw) {
775
+ const lines = raw.split('\n');
776
+ const updated = lines.map(line => {
777
+ try {
778
+ const msg = JSON.parse(line);
779
+ if (msg.id === id) {
780
+ msg.content = content;
781
+ msg.edited = true;
782
+ msg.edited_at = now;
783
+ return JSON.stringify(msg);
784
+ }
785
+ return line;
786
+ } catch { return line; }
787
+ });
788
+ fs.writeFileSync(messagesFile, updated.join('\n') + '\n');
789
+ }
790
+ }
791
+
792
+ if (!found) return { error: 'Message not found' };
793
+ return { success: true, id, edited_at: now };
794
+ }
795
+
796
+ // --- v3.4: Message Delete ---
797
+ function apiDeleteMessage(body, query) {
798
+ const projectPath = query.get('project') || null;
799
+ const { id } = body;
800
+ if (!id) return { error: 'Missing "id" field' };
801
+
802
+ const dataDir = resolveDataDir(projectPath);
803
+ const historyFile = path.join(dataDir, 'history.jsonl');
804
+ const messagesFile = path.join(dataDir, 'messages.jsonl');
805
+
806
+ let found = false;
807
+ let msgFrom = null;
808
+
809
+ // Find the message first to check permissions
810
+ if (fs.existsSync(historyFile)) {
811
+ const lines = fs.readFileSync(historyFile, 'utf8').trim().split('\n');
812
+ for (const line of lines) {
813
+ try {
814
+ const msg = JSON.parse(line);
815
+ if (msg.id === id) { found = true; msgFrom = msg.from; break; }
816
+ } catch {}
817
+ }
818
+ }
819
+
820
+ if (!found) return { error: 'Message not found' };
821
+
822
+ // Only allow deleting dashboard-injected or system messages
823
+ const allowed = ['Dashboard', 'dashboard', 'system', '__system__'];
824
+ if (!allowed.includes(msgFrom)) {
825
+ return { error: 'Can only delete messages sent from Dashboard or system' };
826
+ }
827
+
828
+ // Remove from history.jsonl
829
+ if (fs.existsSync(historyFile)) {
830
+ const lines = fs.readFileSync(historyFile, 'utf8').trim().split('\n');
831
+ const filtered = lines.filter(line => {
832
+ try { return JSON.parse(line).id !== id; } catch { return true; }
833
+ });
834
+ fs.writeFileSync(historyFile, filtered.join('\n') + (filtered.length ? '\n' : ''));
835
+ }
836
+
837
+ // Remove from messages.jsonl
838
+ if (fs.existsSync(messagesFile)) {
839
+ const lines = fs.readFileSync(messagesFile, 'utf8').trim().split('\n');
840
+ const filtered = lines.filter(line => {
841
+ try { return JSON.parse(line).id !== id; } catch { return true; }
842
+ });
843
+ fs.writeFileSync(messagesFile, filtered.join('\n') + (filtered.length ? '\n' : ''));
844
+ }
845
+
846
+ return { success: true, id };
847
+ }
848
+
849
+ // --- v3.4: Conversation Templates ---
850
+ function apiGetConversationTemplates() {
851
+ const templatesDir = path.join(__dirname, 'conversation-templates');
852
+ if (!fs.existsSync(templatesDir)) {
853
+ // Return built-in templates
854
+ return getBuiltInConversationTemplates();
855
+ }
856
+ const custom = fs.readdirSync(templatesDir)
857
+ .filter(f => f.endsWith('.json'))
858
+ .map(f => { try { return JSON.parse(fs.readFileSync(path.join(templatesDir, f), 'utf8')); } catch { return null; } })
859
+ .filter(Boolean);
860
+ return [...getBuiltInConversationTemplates(), ...custom];
861
+ }
862
+
863
+ function getBuiltInConversationTemplates() {
864
+ return [
865
+ {
866
+ id: 'code-review',
867
+ name: 'Code Review Pipeline',
868
+ description: 'Developer writes code, Reviewer checks it, Tester validates',
869
+ agents: [
870
+ { name: 'Developer', role: 'Developer', prompt: 'You are a developer. Write code as instructed. After completing, send your code to Reviewer for review.' },
871
+ { name: 'Reviewer', role: 'Code Reviewer', prompt: 'You are a code reviewer. Wait for code from Developer. Review it for bugs, style, and best practices. Send feedback back to Developer or approve and forward to Tester.' },
872
+ { name: 'Tester', role: 'QA Tester', prompt: 'You are a QA tester. Wait for approved code from Reviewer. Write and run tests. Report results back to the team.' }
873
+ ],
874
+ workflow: { name: 'Code Review', steps: ['Write Code', 'Review', 'Test', 'Approve'] }
875
+ },
876
+ {
877
+ id: 'debug-squad',
878
+ name: 'Debug Squad',
879
+ description: 'Investigator finds the bug, Fixer patches it, Verifier confirms the fix',
880
+ agents: [
881
+ { name: 'Investigator', role: 'Bug Investigator', prompt: 'You investigate bugs. Analyze error logs, trace code paths, and identify root causes. Send findings to Fixer.' },
882
+ { name: 'Fixer', role: 'Bug Fixer', prompt: 'You fix bugs. Wait for findings from Investigator. Implement fixes and send to Verifier for confirmation.' },
883
+ { name: 'Verifier', role: 'Fix Verifier', prompt: 'You verify bug fixes. Wait for patches from Fixer. Test the fix and confirm resolution or send back for more work.' }
884
+ ],
885
+ workflow: { name: 'Bug Fix', steps: ['Investigate', 'Fix', 'Verify', 'Close'] }
886
+ },
887
+ {
888
+ id: 'feature-build',
889
+ name: 'Feature Development',
890
+ description: 'Architect designs, Builder implements, Reviewer approves',
891
+ agents: [
892
+ { name: 'Architect', role: 'Software Architect', prompt: 'You are a software architect. Design the feature architecture, define interfaces, and create the implementation plan. Send the plan to Builder.' },
893
+ { name: 'Builder', role: 'Developer', prompt: 'You are a developer. Wait for architecture plans from Architect. Implement the feature following the design. Send completed code to Reviewer.' },
894
+ { name: 'Reviewer', role: 'Senior Reviewer', prompt: 'You are a senior reviewer. Review implementations from Builder against the architecture from Architect. Approve or request changes.' }
895
+ ],
896
+ workflow: { name: 'Feature Dev', steps: ['Design', 'Implement', 'Review', 'Ship'] }
897
+ },
898
+ {
899
+ id: 'research-write',
900
+ name: 'Research & Write',
901
+ description: 'Researcher gathers info, Writer creates content, Editor polishes',
902
+ agents: [
903
+ { name: 'Researcher', role: 'Researcher', prompt: 'You are a researcher. Gather information on the given topic. Organize findings and send a research brief to Writer.' },
904
+ { name: 'Writer', role: 'Writer', prompt: 'You are a writer. Wait for research from Researcher. Write clear, well-structured content based on the findings. Send to Editor.' },
905
+ { name: 'Editor', role: 'Editor', prompt: 'You are an editor. Review and polish content from Writer. Check for clarity, accuracy, and style. Send back final version or request revisions.' }
906
+ ],
907
+ workflow: { name: 'Content Pipeline', steps: ['Research', 'Draft', 'Edit', 'Publish'] }
908
+ }
909
+ ];
910
+ }
911
+
912
+ function apiLaunchConversationTemplate(body, query) {
913
+ const projectPath = query.get('project') || null;
914
+ const { template_id } = body;
915
+ if (!template_id) return { error: 'Missing template_id' };
916
+
917
+ const templates = apiGetConversationTemplates();
918
+ const template = templates.find(t => t.id === template_id);
919
+ if (!template) return { error: 'Template not found: ' + template_id };
920
+
921
+ // Return the template config for the frontend to display launch instructions
922
+ return {
923
+ success: true,
924
+ template,
925
+ instructions: template.agents.map(a => ({
926
+ agent_name: a.name,
927
+ role: a.role,
928
+ prompt: `You are "${a.name}" with role "${a.role}". ${a.prompt}\n\nFirst register yourself with: register(name="${a.name}"), then update_profile(role="${a.role}"). Then enter listen mode.`
929
+ }))
930
+ };
931
+ }
932
+
933
+ // --- v3.4: Agent Permissions ---
934
+ function apiUpdatePermissions(body, query) {
935
+ const projectPath = query.get('project') || null;
936
+ const dataDir = resolveDataDir(projectPath);
937
+ const permFile = path.join(dataDir, 'permissions.json');
938
+
939
+ const { agent, permissions } = body;
940
+ if (!agent || !permissions) return { error: 'Missing "agent" and/or "permissions" fields' };
941
+
942
+ let perms = {};
943
+ if (fs.existsSync(permFile)) {
944
+ try { perms = JSON.parse(fs.readFileSync(permFile, 'utf8')); } catch {}
945
+ }
946
+
947
+ // permissions: { can_read: [agents...] or "*", can_write_to: [agents...] or "*", is_admin: bool }
948
+ const allowed = {};
949
+ if (permissions.can_read !== undefined) allowed.can_read = permissions.can_read;
950
+ if (permissions.can_write_to !== undefined) allowed.can_write_to = permissions.can_write_to;
951
+ if (permissions.is_admin !== undefined) allowed.is_admin = !!permissions.is_admin;
952
+ perms[agent] = {
953
+ ...perms[agent],
954
+ ...allowed,
955
+ updated_at: new Date().toISOString()
956
+ };
957
+
958
+ if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
959
+ fs.writeFileSync(permFile, JSON.stringify(perms, null, 2));
960
+ return { success: true, agent, permissions: perms[agent] };
961
+ }
962
+
655
963
  // --- HTTP Server ---
656
964
 
657
965
  // Load HTML at startup (re-read on each request in dev for hot-reload)
@@ -686,7 +994,7 @@ const server = http.createServer(async (req, res) => {
686
994
  } else if (reqOrigin === allowedOrigin || reqOrigin === `http://127.0.0.1:${PORT}`) {
687
995
  res.setHeader('Access-Control-Allow-Origin', reqOrigin);
688
996
  }
689
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
997
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
690
998
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
691
999
 
692
1000
  if (req.method === 'OPTIONS') {
@@ -696,7 +1004,7 @@ const server = http.createServer(async (req, res) => {
696
1004
  }
697
1005
 
698
1006
  // CSRF + DNS rebinding protection: validate Host and Origin on mutating requests
699
- if (req.method === 'POST' || req.method === 'DELETE') {
1007
+ if (req.method === 'POST' || req.method === 'PUT' || req.method === 'DELETE') {
700
1008
  // Check Host header to block DNS rebinding attacks
701
1009
  const host = (req.headers.host || '').replace(/:\d+$/, '');
702
1010
  const validHosts = ['localhost', '127.0.0.1'];
@@ -741,12 +1049,15 @@ const server = http.createServer(async (req, res) => {
741
1049
  return;
742
1050
  }
743
1051
 
744
- // Serve dashboard HTML (re-read in dev mode for hot reload)
1052
+ // Serve dashboard HTML (always re-read for hot reload)
745
1053
  if (url.pathname === '/' || url.pathname === '/index.html') {
746
- const html = process.env.NODE_ENV === 'development'
747
- ? fs.readFileSync(HTML_FILE, 'utf8')
748
- : htmlContent;
749
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
1054
+ const html = fs.readFileSync(HTML_FILE, 'utf8');
1055
+ res.writeHead(200, {
1056
+ 'Content-Type': 'text/html; charset=utf-8',
1057
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
1058
+ 'Pragma': 'no-cache',
1059
+ 'Expires': '0'
1060
+ });
750
1061
  res.end(html);
751
1062
  }
752
1063
  // Existing APIs (now with ?project= param support)
@@ -762,6 +1073,10 @@ const server = http.createServer(async (req, res) => {
762
1073
  res.writeHead(200, { 'Content-Type': 'application/json' });
763
1074
  res.end(JSON.stringify(apiStatus(url.searchParams)));
764
1075
  }
1076
+ else if (url.pathname === '/api/stats' && req.method === 'GET') {
1077
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1078
+ res.end(JSON.stringify(apiStats(url.searchParams)));
1079
+ }
765
1080
  else if (url.pathname === '/api/reset' && req.method === 'POST') {
766
1081
  res.writeHead(200, { 'Content-Type': 'application/json' });
767
1082
  res.end(JSON.stringify(apiReset(url.searchParams)));
@@ -940,6 +1255,43 @@ const server = http.createServer(async (req, res) => {
940
1255
  res.writeHead(result.error ? 400 : 200, { 'Content-Type': 'application/json' });
941
1256
  res.end(JSON.stringify(result));
942
1257
  }
1258
+ // --- v3.4: Message Edit ---
1259
+ else if (url.pathname === '/api/message' && req.method === 'PUT') {
1260
+ const body = await parseBody(req);
1261
+ const result = apiEditMessage(body, url.searchParams);
1262
+ res.writeHead(result.error ? 400 : 200, { 'Content-Type': 'application/json' });
1263
+ res.end(JSON.stringify(result));
1264
+ }
1265
+ // --- v3.4: Message Delete ---
1266
+ else if (url.pathname === '/api/message' && req.method === 'DELETE') {
1267
+ const body = await parseBody(req);
1268
+ const result = apiDeleteMessage(body, url.searchParams);
1269
+ res.writeHead(result.error ? 400 : 200, { 'Content-Type': 'application/json' });
1270
+ res.end(JSON.stringify(result));
1271
+ }
1272
+ // --- v3.4: Conversation Templates ---
1273
+ else if (url.pathname === '/api/conversation-templates' && req.method === 'GET') {
1274
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1275
+ res.end(JSON.stringify(apiGetConversationTemplates()));
1276
+ }
1277
+ else if (url.pathname === '/api/conversation-templates/launch' && req.method === 'POST') {
1278
+ const body = await parseBody(req);
1279
+ const result = apiLaunchConversationTemplate(body, url.searchParams);
1280
+ res.writeHead(result.error ? 400 : 200, { 'Content-Type': 'application/json' });
1281
+ res.end(JSON.stringify(result));
1282
+ }
1283
+ // --- v3.4: Agent Permissions ---
1284
+ else if (url.pathname === '/api/permissions' && req.method === 'GET') {
1285
+ const projectPath = url.searchParams.get('project') || null;
1286
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1287
+ res.end(JSON.stringify(readJson(filePath('permissions.json', projectPath))));
1288
+ }
1289
+ else if (url.pathname === '/api/permissions' && req.method === 'POST') {
1290
+ const body = await parseBody(req);
1291
+ const result = apiUpdatePermissions(body, url.searchParams);
1292
+ res.writeHead(result.error ? 400 : 200, { 'Content-Type': 'application/json' });
1293
+ res.end(JSON.stringify(result));
1294
+ }
943
1295
  // Server info (LAN mode detection for frontend)
944
1296
  else if (url.pathname === '/api/server-info' && req.method === 'GET') {
945
1297
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -1068,7 +1420,7 @@ server.listen(PORT, LAN_MODE ? '0.0.0.0' : '127.0.0.1', () => {
1068
1420
  const dataDir = resolveDataDir();
1069
1421
  const lanIP = getLanIP();
1070
1422
  console.log('');
1071
- console.log(' Let Them Talk - Agent Bridge Dashboard v3.3.1');
1423
+ console.log(' Let Them Talk - Agent Bridge Dashboard v3.4.0');
1072
1424
  console.log(' ============================================');
1073
1425
  console.log(' Dashboard: http://localhost:' + PORT);
1074
1426
  if (LAN_MODE && lanIP) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "let-them-talk",
3
- "version": "3.3.2",
3
+ "version": "3.4.0",
4
4
  "description": "MCP message broker + web dashboard for inter-agent communication. Let AI CLI agents talk to each other.",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -21,6 +21,7 @@
21
21
  "dashboard.html",
22
22
  "cli.js",
23
23
  "templates/",
24
+ "conversation-templates/",
24
25
  "logo.png",
25
26
  "LICENSE",
26
27
  "SECURITY.md",
@@ -47,8 +48,8 @@
47
48
  "bugs": {
48
49
  "url": "https://github.com/Dekelelz/let-them-talk/issues"
49
50
  },
50
- "author": "Dekelelz",
51
- "license": "BSL-1.1",
51
+ "author": "Dekelelz <contact@talk.unrealai.studio>",
52
+ "license": "SEE LICENSE IN LICENSE",
52
53
  "dependencies": {
53
54
  "@modelcontextprotocol/sdk": "1.27.1"
54
55
  }
package/server.js CHANGED
@@ -2021,7 +2021,7 @@ async function main() {
2021
2021
  loadPlugins();
2022
2022
  const transport = new StdioServerTransport();
2023
2023
  await server.connect(transport);
2024
- console.error('Agent Bridge MCP server v3.3.1 running (' + (27 + loadedPlugins.length) + ' tools)');
2024
+ console.error('Agent Bridge MCP server v3.4.0 running (' + (27 + loadedPlugins.length) + ' tools)');
2025
2025
  }
2026
2026
 
2027
2027
  main().catch(console.error);