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.
- package/lib/heartbeat.js +115 -14
- 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.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
|
|
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
|
-
|
|
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, ' ')
|
|
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}
|
|
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)}...] "${
|
|
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 || '?'}: ${
|
|
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
|
|
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
|
|
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, ' ')
|
|
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)
|
|
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(`---
|
|
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(`---
|
|
2714
|
+
log(`--- ${statusMode} | #${cycle} | ${new Date().toLocaleTimeString()} ---`);
|
|
2614
2715
|
lastKeepalive = Date.now();
|
|
2615
2716
|
}
|
|
2616
2717
|
} else {
|