@femtomc/mu-agent 26.2.95 → 26.2.96
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagents-ui.d.ts","sourceRoot":"","sources":["../../src/extensions/subagents-ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"subagents-ui.d.ts","sourceRoot":"","sources":["../../src/extensions/subagents-ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AA2gCpF,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,QAkoBpD;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -18,6 +18,7 @@ const WIDGET_SUMMARY_MAX = 76;
|
|
|
18
18
|
const WIDGET_ERROR_MAX = 72;
|
|
19
19
|
const ACTIVITY_EVENT_LIMIT = 180;
|
|
20
20
|
const ACTIVITY_LINE_LIMIT = 4;
|
|
21
|
+
const FORUM_ACTIVITY_ISSUE_LIMIT = 8;
|
|
21
22
|
function shellQuote(value) {
|
|
22
23
|
return `'${value.replaceAll("'", "'\"'\"'")}'`;
|
|
23
24
|
}
|
|
@@ -177,6 +178,46 @@ function parseIssueArray(label, jsonText) {
|
|
|
177
178
|
});
|
|
178
179
|
return { issues, error: null };
|
|
179
180
|
}
|
|
181
|
+
function parseForumReadLatest(label, jsonText) {
|
|
182
|
+
const trimmed = jsonText.trim();
|
|
183
|
+
if (trimmed.length === 0) {
|
|
184
|
+
return { message: null, error: null };
|
|
185
|
+
}
|
|
186
|
+
let parsed = null;
|
|
187
|
+
try {
|
|
188
|
+
parsed = JSON.parse(trimmed);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return { message: null, error: `${label}: invalid JSON output from mu forum read` };
|
|
192
|
+
}
|
|
193
|
+
if (!Array.isArray(parsed)) {
|
|
194
|
+
return { message: null, error: `${label}: expected JSON array output from mu forum read` };
|
|
195
|
+
}
|
|
196
|
+
const first = parsed[0];
|
|
197
|
+
if (!first || typeof first !== "object" || Array.isArray(first)) {
|
|
198
|
+
return { message: null, error: null };
|
|
199
|
+
}
|
|
200
|
+
const row = first;
|
|
201
|
+
const body = typeof row.body === "string" ? row.body.trim() : "";
|
|
202
|
+
if (body.length === 0) {
|
|
203
|
+
return { message: null, error: null };
|
|
204
|
+
}
|
|
205
|
+
const authorRaw = typeof row.author === "string" ? row.author.trim() : "";
|
|
206
|
+
const createdAtRaw = row.created_at;
|
|
207
|
+
const createdAtSec = typeof createdAtRaw === "number" && Number.isFinite(createdAtRaw)
|
|
208
|
+
? Math.trunc(createdAtRaw)
|
|
209
|
+
: typeof createdAtRaw === "string" && /^\d+$/.test(createdAtRaw.trim())
|
|
210
|
+
? Number.parseInt(createdAtRaw.trim(), 10)
|
|
211
|
+
: null;
|
|
212
|
+
return {
|
|
213
|
+
message: {
|
|
214
|
+
author: authorRaw.length > 0 ? authorRaw : "operator",
|
|
215
|
+
body,
|
|
216
|
+
createdAtMs: createdAtSec != null && Number.isFinite(createdAtSec) ? createdAtSec * 1_000 : null,
|
|
217
|
+
},
|
|
218
|
+
error: null,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
180
221
|
async function runMuCli(args) {
|
|
181
222
|
let proc = null;
|
|
182
223
|
try {
|
|
@@ -408,52 +449,85 @@ function isActivityEndpointUnavailable(errorMessage) {
|
|
|
408
449
|
const normalized = errorMessage.toLowerCase();
|
|
409
450
|
return normalized.includes("mu server 404") && normalized.includes("not found");
|
|
410
451
|
}
|
|
411
|
-
async function
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
catch (err) {
|
|
422
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
423
|
-
if (isActivityEndpointUnavailable(message)) {
|
|
424
|
-
return { lines: [], error: null };
|
|
452
|
+
async function fetchRecentForumActivity(issueIds) {
|
|
453
|
+
const uniqueIssueIds = Array.from(new Set(issueIds.map((issueId) => issueId.trim()).filter((issueId) => issueId.length > 0))).slice(0, FORUM_ACTIVITY_ISSUE_LIMIT);
|
|
454
|
+
if (uniqueIssueIds.length === 0) {
|
|
455
|
+
return [];
|
|
456
|
+
}
|
|
457
|
+
const reads = await Promise.all(uniqueIssueIds.map(async (issueId) => {
|
|
458
|
+
const outcome = await runMuCli(["forum", "read", `issue:${issueId}`, "--limit", "1", "--json"]);
|
|
459
|
+
if (outcome.exitCode !== 0 || outcome.error || outcome.timedOut) {
|
|
460
|
+
return null;
|
|
425
461
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
return { lines: [], error: null };
|
|
430
|
-
}
|
|
431
|
-
const tracked = new Set(opts.issueIds.map((issueId) => issueId.trim()).filter((issueId) => issueId.length > 0));
|
|
432
|
-
const seenIssueIds = new Set();
|
|
433
|
-
const lines = [];
|
|
434
|
-
const sorted = [...events].sort((left, right) => {
|
|
435
|
-
const leftTs = typeof left.ts_ms === "number" ? left.ts_ms : 0;
|
|
436
|
-
const rightTs = typeof right.ts_ms === "number" ? right.ts_ms : 0;
|
|
437
|
-
return rightTs - leftTs;
|
|
438
|
-
});
|
|
439
|
-
for (const event of sorted) {
|
|
440
|
-
const rendered = renderActivitySentence(event);
|
|
441
|
-
if (!rendered) {
|
|
442
|
-
continue;
|
|
462
|
+
const parsed = parseForumReadLatest(`forum:${issueId}`, outcome.stdout);
|
|
463
|
+
if (parsed.error || !parsed.message) {
|
|
464
|
+
return null;
|
|
443
465
|
}
|
|
444
|
-
|
|
445
|
-
|
|
466
|
+
return {
|
|
467
|
+
issueId,
|
|
468
|
+
tsMs: parsed.message.createdAtMs ?? 0,
|
|
469
|
+
line: `${eventAgeLabel(parsed.message.createdAtMs ?? undefined)} ${issueId} ${parsed.message.author}: ${truncateOneLine(parsed.message.body, 54)}`,
|
|
470
|
+
};
|
|
471
|
+
}));
|
|
472
|
+
return reads
|
|
473
|
+
.filter((row) => row != null)
|
|
474
|
+
.sort((left, right) => right.tsMs - left.tsMs)
|
|
475
|
+
.slice(0, ACTIVITY_LINE_LIMIT)
|
|
476
|
+
.map((row) => row.line);
|
|
477
|
+
}
|
|
478
|
+
async function fetchRecentActivity(opts) {
|
|
479
|
+
let endpointError = null;
|
|
480
|
+
const hasServerUrl = Boolean(muServerUrl());
|
|
481
|
+
if (hasServerUrl) {
|
|
482
|
+
let events;
|
|
483
|
+
try {
|
|
484
|
+
events = await fetchMuJson(`/api/control-plane/events?limit=${ACTIVITY_EVENT_LIMIT}`, {
|
|
485
|
+
timeoutMs: 4_000,
|
|
486
|
+
});
|
|
446
487
|
}
|
|
447
|
-
|
|
448
|
-
|
|
488
|
+
catch (err) {
|
|
489
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
490
|
+
if (!isActivityEndpointUnavailable(message)) {
|
|
491
|
+
endpointError = `activity refresh failed: ${truncateOneLine(message, 60)}`;
|
|
492
|
+
}
|
|
493
|
+
events = [];
|
|
449
494
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
495
|
+
if (Array.isArray(events) && events.length > 0) {
|
|
496
|
+
const tracked = new Set(opts.issueIds.map((issueId) => issueId.trim()).filter((issueId) => issueId.length > 0));
|
|
497
|
+
const seenIssueIds = new Set();
|
|
498
|
+
const lines = [];
|
|
499
|
+
const sorted = [...events].sort((left, right) => {
|
|
500
|
+
const leftTs = typeof left.ts_ms === "number" ? left.ts_ms : 0;
|
|
501
|
+
const rightTs = typeof right.ts_ms === "number" ? right.ts_ms : 0;
|
|
502
|
+
return rightTs - leftTs;
|
|
503
|
+
});
|
|
504
|
+
for (const event of sorted) {
|
|
505
|
+
const rendered = renderActivitySentence(event);
|
|
506
|
+
if (!rendered) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (tracked.size > 0 && !tracked.has(rendered.issueId)) {
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (seenIssueIds.has(rendered.issueId)) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
seenIssueIds.add(rendered.issueId);
|
|
516
|
+
lines.push(`${eventAgeLabel(event.ts_ms)} ${rendered.sentence}`);
|
|
517
|
+
if (lines.length >= ACTIVITY_LINE_LIMIT) {
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (lines.length > 0) {
|
|
522
|
+
return { lines, error: null };
|
|
523
|
+
}
|
|
454
524
|
}
|
|
455
525
|
}
|
|
456
|
-
|
|
526
|
+
const forumLines = await fetchRecentForumActivity(opts.issueIds);
|
|
527
|
+
if (forumLines.length > 0) {
|
|
528
|
+
return { lines: forumLines, error: null };
|
|
529
|
+
}
|
|
530
|
+
return { lines: [], error: endpointError };
|
|
457
531
|
}
|
|
458
532
|
function computeQueueDrift(sessions, activeIssues) {
|
|
459
533
|
const activeWithoutSessionIds = activeIssues
|
|
@@ -465,6 +539,30 @@ function computeQueueDrift(sessions, activeIssues) {
|
|
|
465
539
|
orphanSessions,
|
|
466
540
|
};
|
|
467
541
|
}
|
|
542
|
+
function queueFallbackActivityLines(state) {
|
|
543
|
+
const lines = [];
|
|
544
|
+
for (const issue of state.activeIssues) {
|
|
545
|
+
if (lines.length >= ACTIVITY_LINE_LIMIT) {
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
lines.push(`active ${issue.id}: ${truncateOneLine(issue.title, 52)}`);
|
|
549
|
+
}
|
|
550
|
+
for (const issue of state.readyIssues) {
|
|
551
|
+
if (lines.length >= ACTIVITY_LINE_LIMIT) {
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
lines.push(`ready ${issue.id}: ${truncateOneLine(issue.title, 52)}`);
|
|
555
|
+
}
|
|
556
|
+
if (lines.length === 0 && state.sessions.length > 0) {
|
|
557
|
+
const sessionPreview = state.sessions
|
|
558
|
+
.slice(0, 2)
|
|
559
|
+
.map((sessionName) => truncateOneLine(sessionName, 28))
|
|
560
|
+
.join(", ");
|
|
561
|
+
const suffix = state.sessions.length > 2 ? ` +${state.sessions.length - 2} more` : "";
|
|
562
|
+
lines.push(`tmux active: ${sessionPreview}${suffix}`);
|
|
563
|
+
}
|
|
564
|
+
return lines;
|
|
565
|
+
}
|
|
468
566
|
function normalizeIssueTag(raw) {
|
|
469
567
|
const trimmed = raw.trim();
|
|
470
568
|
if (!trimmed || trimmed.toLowerCase() === "clear") {
|
|
@@ -658,8 +756,11 @@ function renderSubagentsUi(ctx, state) {
|
|
|
658
756
|
lines.push(ctx.ui.theme.fg("warning", truncateOneLine(state.activityError, WIDGET_ERROR_MAX)));
|
|
659
757
|
}
|
|
660
758
|
else if (activityLines.length === 0) {
|
|
661
|
-
|
|
662
|
-
|
|
759
|
+
const fallbackLines = queueFallbackActivityLines(state);
|
|
760
|
+
if (fallbackLines.length > 0) {
|
|
761
|
+
for (const line of fallbackLines) {
|
|
762
|
+
lines.push(`${ctx.ui.theme.fg("muted", "•")} ${ctx.ui.theme.fg("text", truncateOneLine(line, WIDGET_SUMMARY_MAX))}`);
|
|
763
|
+
}
|
|
663
764
|
}
|
|
664
765
|
else {
|
|
665
766
|
lines.push(ctx.ui.theme.fg("muted", "(no active operators)"));
|
|
@@ -740,7 +841,7 @@ export function subagentsUiExtension(pi) {
|
|
|
740
841
|
state.readyIssues = issues.ready;
|
|
741
842
|
state.activeIssues = issues.active;
|
|
742
843
|
state.issueError = issues.error;
|
|
743
|
-
const trackedIssueIds =
|
|
844
|
+
const trackedIssueIds = [...state.activeIssues, ...state.readyIssues]
|
|
744
845
|
.slice(0, 8)
|
|
745
846
|
.map((issue) => issue.id);
|
|
746
847
|
const activity = await fetchRecentActivity({ issueIds: trackedIssueIds });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@femtomc/mu-agent",
|
|
3
|
-
"version": "26.2.
|
|
3
|
+
"version": "26.2.96",
|
|
4
4
|
"description": "Shared operator runtime for mu assistant sessions and serve extensions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mu",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"themes/**"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@femtomc/mu-core": "26.2.
|
|
27
|
+
"@femtomc/mu-core": "26.2.96",
|
|
28
28
|
"@mariozechner/pi-agent-core": "^0.53.0",
|
|
29
29
|
"@mariozechner/pi-ai": "^0.53.0",
|
|
30
30
|
"@mariozechner/pi-coding-agent": "^0.53.0",
|