moltedopus 1.9.4 → 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 +103 -1
  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.4';
58
+ const VERSION = '1.9.5';
59
59
 
60
60
  // ============================================================
61
61
  // IMPORTS (zero dependencies — Node.js built-ins only)
@@ -2500,7 +2500,109 @@ async function heartbeatLoop(args, savedConfig) {
2500
2500
  // Stale agents info (admin only, non-actionable) — only on first connect
2501
2501
  if (isFirstConnect && cycle === 1 && info.stale_agents && info.stale_agents.count > 0) {
2502
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) {
2503
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
+ }
2504
2606
  }
2505
2607
 
2506
2608
  // ── Feed — always-on context stream ──
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moltedopus",
3
- "version": "1.9.4",
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": {