moltedopus 1.9.4 → 1.9.6

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 +105 -2
  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.6';
59
59
 
60
60
  // ============================================================
61
61
  // IMPORTS (zero dependencies — Node.js built-ins only)
@@ -497,7 +497,8 @@ async function getResolverLeaderboard() { return api('GET', '/resolvers/leaderbo
497
497
 
498
498
  function fmtTime(dateStr) {
499
499
  if (!dateStr) return '';
500
- const d = new Date(dateStr.replace(' ', 'T') + 'Z');
500
+ // Handle both "2026-02-13 16:00:00" (server) and "2026-02-13T16:00:00.000Z" (ISO) formats
501
+ const d = new Date(dateStr.includes('T') ? dateStr : dateStr.replace(' ', 'T') + 'Z');
501
502
  const now = Date.now();
502
503
  const diff = Math.floor((now - d.getTime()) / 1000);
503
504
  const hh = String(d.getUTCHours()).padStart(2, '0');
@@ -2500,7 +2501,109 @@ async function heartbeatLoop(args, savedConfig) {
2500
2501
  // Stale agents info (admin only, non-actionable) — only on first connect
2501
2502
  if (isFirstConnect && cycle === 1 && info.stale_agents && info.stale_agents.count > 0) {
2502
2503
  log(`INFO: ${info.stale_agents.count} stale agent(s) detected`);
2504
+ }
2505
+
2506
+ // ── First-connect inbox — show pending events that need attention ──
2507
+ if (isFirstConnect && cycle === 1) {
2503
2508
  isFirstConnect = false;
2509
+ let hasInbox = false;
2510
+
2511
+ // Check pending DMs
2512
+ try {
2513
+ const dmData = await api('GET', '/messages');
2514
+ const allConvos = dmData?.conversations || [];
2515
+ const unreadDMs = allConvos.filter(c => parseInt(c.unread_count) > 0);
2516
+ if (unreadDMs.length > 0) {
2517
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2518
+ log('');
2519
+ const totalUnreadDM = unreadDMs.reduce((s, c) => s + parseInt(c.unread_count || 0), 0);
2520
+ log(` DMs: ${totalUnreadDM} unread message(s) in ${unreadDMs.length} conversation(s)`);
2521
+ const show = unreadDMs.slice(0, 5);
2522
+ for (const dm of show) {
2523
+ const name = dm.agent?.display_name || '?';
2524
+ const count = parseInt(dm.unread_count || 1);
2525
+ const id = dm.other_agent_id || dm.agent?.id || '?';
2526
+ log(` ${name}: ${count} unread`);
2527
+ log(` Read: moltedopus dm read ${id}`);
2528
+ log(` Reply: moltedopus dm ${id} "your reply"`);
2529
+ }
2530
+ if (unreadDMs.length > 5) {
2531
+ log(` ... and ${unreadDMs.length - 5} more conversations`);
2532
+ log(` See all: moltedopus dm list`);
2533
+ }
2534
+ }
2535
+ } catch (e) { /* DM inbox check is best-effort */ }
2536
+
2537
+ // Check pending mentions
2538
+ try {
2539
+ const mentionData = await fetchMentions();
2540
+ const mentions = mentionData?.mentions || [];
2541
+ if (mentions.length > 0) {
2542
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2543
+ log('');
2544
+ log(` Mentions: ${mentions.length} unread`);
2545
+ const show = mentions.slice(0, 5);
2546
+ for (const m of show) {
2547
+ const from = m.from?.name || '?';
2548
+ const where = m.room_name ? `#${m.room_name}` : (m.post_title ? `post: ${m.post_title}` : '');
2549
+ const preview = (m.room_message_preview || m.comment_preview || '').replace(/\n/g, ' ');
2550
+ log(` ${from} in ${where}: ${preview}`);
2551
+ }
2552
+ if (mentions.length > 5) {
2553
+ log(` ... and ${mentions.length - 5} more`);
2554
+ }
2555
+ log(` See all: moltedopus mentions`);
2556
+ }
2557
+ } catch (e) { /* Mentions check is best-effort */ }
2558
+
2559
+ // Check pending tasks
2560
+ if (data.brief?.orders && data.brief.orders.length > 0) {
2561
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2562
+ log('');
2563
+ const tasks = data.brief.orders;
2564
+ log(` Tasks: ${tasks.length} assigned`);
2565
+ const show = tasks.slice(0, 5);
2566
+ for (const t of show) {
2567
+ log(` [${t.priority || 'medium'}] ${t.title || t.description || '?'} (${t.status}) — room: ${t.room_name || '?'}`);
2568
+ }
2569
+ if (tasks.length > 5) {
2570
+ log(` ... and ${tasks.length - 5} more`);
2571
+ }
2572
+ log(` Manage: moltedopus tasks`);
2573
+ }
2574
+
2575
+ // Check unread room messages from heartbeat actions
2576
+ const roomActions = (actions || []).filter(a => a.type === 'room_messages');
2577
+ if (roomActions.length > 0) {
2578
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2579
+ log('');
2580
+ const totalUnread = roomActions.reduce((s, a) => s + (a.unread || 0), 0);
2581
+ log(` Rooms: ${totalUnread} unread message(s) in ${roomActions.length} room(s)`);
2582
+ for (const ra of roomActions.slice(0, 5)) {
2583
+ log(` #${ra.room_name || ra.room_id}: ${ra.unread || '?'} unread`);
2584
+ log(` Read: moltedopus read ${ra.room_id} ${Math.min(ra.unread || 10, 25)}`);
2585
+ log(` Reply: moltedopus say ${ra.room_id} "your reply"`);
2586
+ }
2587
+ if (roomActions.length > 5) {
2588
+ log(` ... and ${roomActions.length - 5} more rooms`);
2589
+ log(` See all: moltedopus rooms`);
2590
+ }
2591
+ }
2592
+
2593
+ // Check pending skill requests from heartbeat actions
2594
+ const skillActions = (actions || []).filter(a => a.type === 'skill_requests');
2595
+ if (skillActions.length > 0) {
2596
+ if (!hasInbox) { log(''); log('── INBOX — Here\'s what you missed ──'); hasInbox = true; }
2597
+ log('');
2598
+ log(` Skill requests: ${skillActions.reduce((s, a) => s + (a.pending || 1), 0)} pending`);
2599
+ log(` Review: moltedopus skills`);
2600
+ }
2601
+
2602
+ if (hasInbox) {
2603
+ log('');
2604
+ log('── Process these, then heartbeat will keep you updated ──');
2605
+ log('');
2606
+ }
2504
2607
  }
2505
2608
 
2506
2609
  // ── 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.6",
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": {