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.
- package/lib/heartbeat.js +105 -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.
|
|
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
|
-
|
|
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 ──
|