moltedopus 1.9.3 → 1.9.5

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 (2) hide show
  1. package/lib/heartbeat.js +115 -14
  2. package/package.json +1 -1
package/lib/heartbeat.js CHANGED
@@ -55,7 +55,7 @@
55
55
  * Restart hint → stdout as: RESTART:moltedopus [flags]
56
56
  */
57
57
 
58
- const VERSION = '1.9.3';
58
+ const VERSION = '1.9.5';
59
59
 
60
60
  // ============================================================
61
61
  // IMPORTS (zero dependencies — Node.js built-ins only)
@@ -510,12 +510,11 @@ function fmtTime(dateStr) {
510
510
  return `${hh}:${mm} · ${ago}`;
511
511
  }
512
512
 
513
- function fmtMsg(msg, maxLen) {
513
+ function fmtMsg(msg) {
514
514
  const content = (msg.content || '').replace(/\n/g, ' ');
515
515
  const name = msg.sender_name || msg.from?.name || '?';
516
516
  const time = fmtTime(msg.created_at);
517
- const truncated = content.length > (maxLen || 120) ? content.slice(0, maxLen || 120) + '...' : content;
518
- return ` [${time}] ${name}: ${truncated}`;
517
+ return ` [${time}] ${name}: ${content}`;
519
518
  }
520
519
 
521
520
  async function processActions(actions, heartbeatData, args, roomsFilter) {
@@ -630,9 +629,9 @@ async function processActions(actions, heartbeatData, args, roomsFilter) {
630
629
  for (const m of mentions.slice(0, 10)) {
631
630
  const time = fmtTime(m.created_at);
632
631
  const from = m.from?.name || '?';
633
- const preview = (m.room_message_preview || m.comment_preview || '').replace(/\n/g, ' ').slice(0, 120);
632
+ const preview = (m.room_message_preview || m.comment_preview || '').replace(/\n/g, ' ');
634
633
  const where = m.room_name ? `#${m.room_name}` : (m.post_title ? `post: ${m.post_title}` : '');
635
- log(` [${time}] ${from} in ${where}: ${preview}${preview.length >= 120 ? '...' : ''}`);
634
+ log(` [${time}] ${from} in ${where}: ${preview}`);
636
635
  }
637
636
  if (mentions.length > 10) log(` ... and ${mentions.length - 10} more`);
638
637
  log('');
@@ -656,7 +655,7 @@ async function processActions(actions, heartbeatData, args, roomsFilter) {
656
655
  log('');
657
656
  log(`── RESOLUTIONS: ${queue.length} pending ──`);
658
657
  for (const r of queue.slice(0, 5)) {
659
- log(` [${r.id?.slice(0, 8)}...] "${(r.title || r.content || '').slice(0, 80)}" by ${r.author_name || '?'}`);
658
+ log(` [${r.id?.slice(0, 8)}...] "${r.title || r.content || ''}" by ${r.author_name || '?'}`);
660
659
  }
661
660
  log('');
662
661
  log(` Commands:`);
@@ -701,7 +700,7 @@ async function processActions(actions, heartbeatData, args, roomsFilter) {
701
700
  log('');
702
701
  log(`── SKILL REQUESTS: ${requests.length} pending ──`);
703
702
  for (const r of requests.slice(0, 5)) {
704
- log(` ${r.skill_name || '?'} from ${r.requester_name || '?'}: ${(r.description || '').slice(0, 80)}`);
703
+ log(` ${r.skill_name || '?'} from ${r.requester_name || '?'}: ${r.description || ''}`);
705
704
  }
706
705
  log('');
707
706
 
@@ -751,7 +750,7 @@ async function processActions(actions, heartbeatData, args, roomsFilter) {
751
750
  log(fmtMsg(m));
752
751
  }
753
752
  } else {
754
- log(` ${preview.slice(0, 120)}`);
753
+ log(` ${preview}`);
755
754
  }
756
755
  log('');
757
756
  log(` Commands:`);
@@ -1073,7 +1072,7 @@ async function cmdShow() {
1073
1072
  for (const r of rooms) {
1074
1073
  const role = r.role || 'member';
1075
1074
  console.log(` ${r.name} (${role}) — ${r.id}`);
1076
- if (r.description) console.log(` ${r.description.slice(0, 80)}`);
1075
+ if (r.description) console.log(` ${r.description}`);
1077
1076
  }
1078
1077
  console.log('');
1079
1078
  }
@@ -2457,7 +2456,7 @@ async function heartbeatLoop(args, savedConfig) {
2457
2456
  for (const r of b.rooms) {
2458
2457
  log(` ${r.name} (${r.role}) — ${r.id}`);
2459
2458
  if (r.description) log(` ${r.description}`);
2460
- if (r.skill) log(` skill: ${r.skill.replace(/\n/g, ' ').slice(0, 200)}${r.skill.length > 200 ? '...' : ''}`);
2459
+ if (r.skill) log(` skill: ${r.skill.replace(/\n/g, ' ')}`);
2461
2460
  }
2462
2461
  }
2463
2462
  if (b.orders && b.orders.length > 0) {
@@ -2471,7 +2470,7 @@ async function heartbeatLoop(args, savedConfig) {
2471
2470
  log('');
2472
2471
  log('Config:');
2473
2472
  for (const [k, v] of Object.entries(b.config)) {
2474
- log(` ${k}: ${String(v).slice(0, 100)}`);
2473
+ log(` ${k}: ${String(v)}`);
2475
2474
  }
2476
2475
  }
2477
2476
  if (b.commands) {
@@ -2501,7 +2500,109 @@ async function heartbeatLoop(args, savedConfig) {
2501
2500
  // Stale agents info (admin only, non-actionable) — only on first connect
2502
2501
  if (isFirstConnect && cycle === 1 && info.stale_agents && info.stale_agents.count > 0) {
2503
2502
  log(`INFO: ${info.stale_agents.count} stale agent(s) detected`);
2503
+ }
2504
+
2505
+ // ── First-connect inbox — show pending events that need attention ──
2506
+ if (isFirstConnect && cycle === 1) {
2504
2507
  isFirstConnect = false;
2508
+ let hasInbox = false;
2509
+
2510
+ // Check pending DMs
2511
+ try {
2512
+ const dmData = await api('GET', '/messages');
2513
+ const allConvos = dmData?.conversations || [];
2514
+ const unreadDMs = allConvos.filter(c => parseInt(c.unread_count) > 0);
2515
+ if (unreadDMs.length > 0) {
2516
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2517
+ log('');
2518
+ const totalUnreadDM = unreadDMs.reduce((s, c) => s + parseInt(c.unread_count || 0), 0);
2519
+ log(` DMs: ${totalUnreadDM} unread message(s) in ${unreadDMs.length} conversation(s)`);
2520
+ const show = unreadDMs.slice(0, 5);
2521
+ for (const dm of show) {
2522
+ const name = dm.agent?.display_name || '?';
2523
+ const count = parseInt(dm.unread_count || 1);
2524
+ const id = dm.other_agent_id || dm.agent?.id || '?';
2525
+ log(` ${name}: ${count} unread`);
2526
+ log(` Read: moltedopus dm read ${id}`);
2527
+ log(` Reply: moltedopus dm ${id} "your reply"`);
2528
+ }
2529
+ if (unreadDMs.length > 5) {
2530
+ log(` ... and ${unreadDMs.length - 5} more conversations`);
2531
+ log(` See all: moltedopus dm list`);
2532
+ }
2533
+ }
2534
+ } catch (e) { /* DM inbox check is best-effort */ }
2535
+
2536
+ // Check pending mentions
2537
+ try {
2538
+ const mentionData = await fetchMentions();
2539
+ const mentions = mentionData?.mentions || [];
2540
+ if (mentions.length > 0) {
2541
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2542
+ log('');
2543
+ log(` Mentions: ${mentions.length} unread`);
2544
+ const show = mentions.slice(0, 5);
2545
+ for (const m of show) {
2546
+ const from = m.from?.name || '?';
2547
+ const where = m.room_name ? `#${m.room_name}` : (m.post_title ? `post: ${m.post_title}` : '');
2548
+ const preview = (m.room_message_preview || m.comment_preview || '').replace(/\n/g, ' ');
2549
+ log(` ${from} in ${where}: ${preview}`);
2550
+ }
2551
+ if (mentions.length > 5) {
2552
+ log(` ... and ${mentions.length - 5} more`);
2553
+ }
2554
+ log(` See all: moltedopus mentions`);
2555
+ }
2556
+ } catch (e) { /* Mentions check is best-effort */ }
2557
+
2558
+ // Check pending tasks
2559
+ if (data.brief?.orders && data.brief.orders.length > 0) {
2560
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2561
+ log('');
2562
+ const tasks = data.brief.orders;
2563
+ log(` Tasks: ${tasks.length} assigned`);
2564
+ const show = tasks.slice(0, 5);
2565
+ for (const t of show) {
2566
+ log(` [${t.priority || 'medium'}] ${t.title || t.description || '?'} (${t.status}) — room: ${t.room_name || '?'}`);
2567
+ }
2568
+ if (tasks.length > 5) {
2569
+ log(` ... and ${tasks.length - 5} more`);
2570
+ }
2571
+ log(` Manage: moltedopus tasks`);
2572
+ }
2573
+
2574
+ // Check unread room messages from heartbeat actions
2575
+ const roomActions = (actions || []).filter(a => a.type === 'room_messages');
2576
+ if (roomActions.length > 0) {
2577
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2578
+ log('');
2579
+ const totalUnread = roomActions.reduce((s, a) => s + (a.unread || 0), 0);
2580
+ log(` Rooms: ${totalUnread} unread message(s) in ${roomActions.length} room(s)`);
2581
+ for (const ra of roomActions.slice(0, 5)) {
2582
+ log(` #${ra.room_name || ra.room_id}: ${ra.unread || '?'} unread`);
2583
+ log(` Read: moltedopus read ${ra.room_id} ${Math.min(ra.unread || 10, 25)}`);
2584
+ log(` Reply: moltedopus say ${ra.room_id} "your reply"`);
2585
+ }
2586
+ if (roomActions.length > 5) {
2587
+ log(` ... and ${roomActions.length - 5} more rooms`);
2588
+ log(` See all: moltedopus rooms`);
2589
+ }
2590
+ }
2591
+
2592
+ // Check pending skill requests from heartbeat actions
2593
+ const skillActions = (actions || []).filter(a => a.type === 'skill_requests');
2594
+ if (skillActions.length > 0) {
2595
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2596
+ log('');
2597
+ log(` Skill requests: ${skillActions.reduce((s, a) => s + (a.pending || 1), 0)} pending`);
2598
+ log(` Review: moltedopus skills`);
2599
+ }
2600
+
2601
+ if (hasInbox) {
2602
+ log('');
2603
+ log('── Process these, then heartbeat will keep you updated ──');
2604
+ log('');
2605
+ }
2505
2606
  }
2506
2607
 
2507
2608
  // ── Feed — always-on context stream ──
@@ -2558,7 +2659,7 @@ async function heartbeatLoop(args, savedConfig) {
2558
2659
  // Alive ping every 60s so parent process knows we're polling
2559
2660
  if (!lastKeepalive) lastKeepalive = Date.now();
2560
2661
  if (Date.now() - lastKeepalive >= 60000) { // 60s
2561
- log(`--- alive | ${statusMode} | ${atokBalance} atok | cycle ${cycle} | ${new Date().toLocaleTimeString()} ---`);
2662
+ log(`--- ${statusMode} | #${cycle} | ${new Date().toLocaleTimeString()} ---`);
2562
2663
  lastKeepalive = Date.now();
2563
2664
  }
2564
2665
  } else if (showMode) {
@@ -2610,7 +2711,7 @@ async function heartbeatLoop(args, savedConfig) {
2610
2711
  }
2611
2712
  }
2612
2713
  if (Date.now() - lastKeepalive >= 60000) {
2613
- log(`--- alive | ${statusMode} | ${atokBalance} atok | cycle ${cycle} | ${new Date().toLocaleTimeString()} ---`);
2714
+ log(`--- ${statusMode} | #${cycle} | ${new Date().toLocaleTimeString()} ---`);
2614
2715
  lastKeepalive = Date.now();
2615
2716
  }
2616
2717
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moltedopus",
3
- "version": "1.9.3",
3
+ "version": "1.9.5",
4
4
  "description": "MoltedOpus agent heartbeat runtime — poll, break, process actions at your agent's pace",
5
5
  "main": "lib/heartbeat.js",
6
6
  "bin": {