@femtomc/mu-agent 26.2.89 → 26.2.90
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/README.md +2 -2
- package/dist/extensions/branding.d.ts.map +1 -1
- package/dist/extensions/branding.js +11 -0
- package/dist/extensions/hud-mode.d.ts +8 -0
- package/dist/extensions/hud-mode.d.ts.map +1 -0
- package/dist/extensions/hud-mode.js +21 -0
- package/dist/extensions/planning-ui.d.ts.map +1 -1
- package/dist/extensions/planning-ui.js +17 -0
- package/dist/extensions/subagents-ui.d.ts.map +1 -1
- package/dist/extensions/subagents-ui.js +224 -68
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -48,8 +48,8 @@ Current stack:
|
|
|
48
48
|
|
|
49
49
|
- `brandingExtension` — mu compact header/footer branding + default theme
|
|
50
50
|
- `eventLogExtension` — event tail + watch widget
|
|
51
|
-
- `planningUiExtension` — planning HUD
|
|
52
|
-
- `subagentsUiExtension` —
|
|
51
|
+
- `planningUiExtension` — planning mode: compact HUD for next-step/approval flow plus footer-ready incidental status metadata (`/mu plan ...`)
|
|
52
|
+
- `subagentsUiExtension` — subagents mode: compact HUD with activity sentences from issue/forum events plus footer-ready queue/health metadata (`/mu subagents ...`)
|
|
53
53
|
|
|
54
54
|
Default operator UI theme is `mu-gruvbox-dark`.
|
|
55
55
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../../src/extensions/branding.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../../src/extensions/branding.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAyEpF,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,YAAY,QA8OjD;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { basename } from "node:path";
|
|
10
10
|
import { MU_DEFAULT_THEME_NAME, MU_VERSION } from "../ui_defaults.js";
|
|
11
|
+
import { getActiveHudMode } from "./hud-mode.js";
|
|
11
12
|
import { registerMuSubcommand } from "./mu-command-dispatcher.js";
|
|
12
13
|
import { fetchMuStatus, muServerUrl } from "./shared.js";
|
|
13
14
|
const EMPTY_SNAPSHOT = {
|
|
@@ -127,6 +128,16 @@ export function brandingExtension(pi) {
|
|
|
127
128
|
const barColor = pct >= 80 ? "warning" : pct >= 60 ? "muted" : "dim";
|
|
128
129
|
parts.push(theme.fg("muted", "·"), theme.fg(barColor, `ctx ${pct}%`), theme.fg(barColor, contextBar(pct, 10)));
|
|
129
130
|
}
|
|
131
|
+
const activeHudMode = getActiveHudMode();
|
|
132
|
+
if (activeHudMode) {
|
|
133
|
+
const extensionStatuses = footerData.getExtensionStatuses();
|
|
134
|
+
const modeMetaKey = activeHudMode === "planning" ? "mu-planning-meta" : "mu-subagents-meta";
|
|
135
|
+
const modeMeta = extensionStatuses.get(modeMetaKey) ?? "";
|
|
136
|
+
parts.push(theme.fg("muted", "·"), theme.fg("accent", `hud:${activeHudMode}`));
|
|
137
|
+
if (modeMeta.length > 0) {
|
|
138
|
+
parts.push(theme.fg("muted", "·"), theme.fg("dim", truncateToWidth(modeMeta, 42)));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
130
141
|
if (snapshot.openCount > 0 || snapshot.readyCount > 0) {
|
|
131
142
|
parts.push(theme.fg("muted", "·"), theme.fg("dim", `open ${snapshot.openCount} ready ${snapshot.readyCount}`));
|
|
132
143
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
export type MuHudMode = "planning" | "subagents";
|
|
3
|
+
export declare function getActiveHudMode(): MuHudMode | null;
|
|
4
|
+
export declare function resetHudMode(): void;
|
|
5
|
+
export declare function setActiveHudMode(mode: MuHudMode | null): void;
|
|
6
|
+
export declare function clearHudMode(mode: MuHudMode): void;
|
|
7
|
+
export declare function syncHudModeStatus(ctx: ExtensionContext): void;
|
|
8
|
+
//# sourceMappingURL=hud-mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hud-mode.d.ts","sourceRoot":"","sources":["../../src/extensions/hud-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;AAIjD,wBAAgB,gBAAgB,IAAI,SAAS,GAAG,IAAI,CAEnD;AAED,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAE7D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAIlD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAK7D"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
let activeHudMode = null;
|
|
2
|
+
export function getActiveHudMode() {
|
|
3
|
+
return activeHudMode;
|
|
4
|
+
}
|
|
5
|
+
export function resetHudMode() {
|
|
6
|
+
activeHudMode = null;
|
|
7
|
+
}
|
|
8
|
+
export function setActiveHudMode(mode) {
|
|
9
|
+
activeHudMode = mode;
|
|
10
|
+
}
|
|
11
|
+
export function clearHudMode(mode) {
|
|
12
|
+
if (activeHudMode === mode) {
|
|
13
|
+
activeHudMode = null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function syncHudModeStatus(ctx) {
|
|
17
|
+
if (!ctx.hasUI) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
ctx.ui.setStatus("mu-hud-mode", activeHudMode ? `hud:${activeHudMode}` : undefined);
|
|
21
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planning-ui.d.ts","sourceRoot":"","sources":["../../src/extensions/planning-ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"planning-ui.d.ts","sourceRoot":"","sources":["../../src/extensions/planning-ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAigBpF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,QAuenD;AAED,eAAe,mBAAmB,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearHudMode, setActiveHudMode, syncHudModeStatus } from "./hud-mode.js";
|
|
1
2
|
import { registerMuSubcommand } from "./mu-command-dispatcher.js";
|
|
2
3
|
const DEFAULT_STEPS = [
|
|
3
4
|
"Investigate relevant code/docs/state",
|
|
@@ -252,6 +253,7 @@ function renderPlanningUi(ctx, state) {
|
|
|
252
253
|
}
|
|
253
254
|
if (!state.enabled) {
|
|
254
255
|
ctx.ui.setStatus("mu-planning", undefined);
|
|
256
|
+
ctx.ui.setStatus("mu-planning-meta", undefined);
|
|
255
257
|
ctx.ui.setWidget("mu-planning", undefined);
|
|
256
258
|
return;
|
|
257
259
|
}
|
|
@@ -276,6 +278,7 @@ function renderPlanningUi(ctx, state) {
|
|
|
276
278
|
ctx.ui.theme.fg(waitingColor, `wait:${waitingLabel}`),
|
|
277
279
|
ctx.ui.theme.fg("muted", `root:${rootCompact}`),
|
|
278
280
|
].join(` ${ctx.ui.theme.fg("muted", "·")} `));
|
|
281
|
+
ctx.ui.setStatus("mu-planning-meta", `phase:${phase} steps:${done}/${total} wait:${waitingLabel} conf:${state.confidence}`);
|
|
279
282
|
const lines = [
|
|
280
283
|
[
|
|
281
284
|
ctx.ui.theme.fg("accent", ctx.ui.theme.bold("Planning")),
|
|
@@ -387,6 +390,16 @@ export function planningUiExtension(pi) {
|
|
|
387
390
|
const refresh = (ctx) => {
|
|
388
391
|
renderPlanningUi(ctx, state);
|
|
389
392
|
};
|
|
393
|
+
const syncPlanningMode = (ctx, action) => {
|
|
394
|
+
const passiveAction = action === "status" || action === "snapshot";
|
|
395
|
+
if (!state.enabled) {
|
|
396
|
+
clearHudMode("planning");
|
|
397
|
+
}
|
|
398
|
+
else if (!passiveAction) {
|
|
399
|
+
setActiveHudMode("planning");
|
|
400
|
+
}
|
|
401
|
+
syncHudModeStatus(ctx);
|
|
402
|
+
};
|
|
390
403
|
const applyPlanningAction = (params) => {
|
|
391
404
|
switch (params.action) {
|
|
392
405
|
case "status":
|
|
@@ -647,9 +660,11 @@ export function planningUiExtension(pi) {
|
|
|
647
660
|
};
|
|
648
661
|
pi.on("session_start", async (_event, ctx) => {
|
|
649
662
|
refresh(ctx);
|
|
663
|
+
syncHudModeStatus(ctx);
|
|
650
664
|
});
|
|
651
665
|
pi.on("session_switch", async (_event, ctx) => {
|
|
652
666
|
refresh(ctx);
|
|
667
|
+
syncHudModeStatus(ctx);
|
|
653
668
|
});
|
|
654
669
|
registerMuSubcommand(pi, {
|
|
655
670
|
subcommand: "plan",
|
|
@@ -733,6 +748,7 @@ export function planningUiExtension(pi) {
|
|
|
733
748
|
notify(ctx, result.message, result.level ?? "error");
|
|
734
749
|
return;
|
|
735
750
|
}
|
|
751
|
+
syncPlanningMode(ctx, params.action);
|
|
736
752
|
ctx.ui.notify(result.message, result.level ?? "info");
|
|
737
753
|
},
|
|
738
754
|
});
|
|
@@ -823,6 +839,7 @@ export function planningUiExtension(pi) {
|
|
|
823
839
|
if (!result.ok) {
|
|
824
840
|
return planningToolError(result.message);
|
|
825
841
|
}
|
|
842
|
+
syncPlanningMode(ctx, params.action);
|
|
826
843
|
return {
|
|
827
844
|
content: [{ type: "text", text: `${result.message}\n\n${planningStatusSummary(state)}` }],
|
|
828
845
|
details: {
|
|
@@ -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;AAw4BpF,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,QAkoBpD;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { fetchMuJson, muServerUrl } from "./shared.js";
|
|
2
|
+
import { clearHudMode, setActiveHudMode, syncHudModeStatus } from "./hud-mode.js";
|
|
1
3
|
import { registerMuSubcommand } from "./mu-command-dispatcher.js";
|
|
2
4
|
const DEFAULT_PREFIX = "mu-sub-";
|
|
3
5
|
const DEFAULT_ROLE_TAG = "role:worker";
|
|
@@ -10,6 +12,12 @@ const MAX_REFRESH_SECONDS = 120;
|
|
|
10
12
|
const DEFAULT_STALE_AFTER_MS = 60_000;
|
|
11
13
|
const MIN_STALE_SECONDS = 10;
|
|
12
14
|
const MAX_STALE_SECONDS = 3_600;
|
|
15
|
+
const WIDGET_SCOPE_MAX = 52;
|
|
16
|
+
const WIDGET_PREFIX_MAX = 20;
|
|
17
|
+
const WIDGET_SUMMARY_MAX = 76;
|
|
18
|
+
const WIDGET_ERROR_MAX = 72;
|
|
19
|
+
const ACTIVITY_EVENT_LIMIT = 180;
|
|
20
|
+
const ACTIVITY_LINE_LIMIT = 4;
|
|
13
21
|
function shellQuote(value) {
|
|
14
22
|
return `'${value.replaceAll("'", "'\"'\"'")}'`;
|
|
15
23
|
}
|
|
@@ -99,6 +107,8 @@ function createDefaultState() {
|
|
|
99
107
|
staleAfterMs: DEFAULT_STALE_AFTER_MS,
|
|
100
108
|
spawnPaused: false,
|
|
101
109
|
spawnMode: DEFAULT_SPAWN_MODE,
|
|
110
|
+
activityLines: [],
|
|
111
|
+
activityError: null,
|
|
102
112
|
};
|
|
103
113
|
}
|
|
104
114
|
function truncateOneLine(input, maxLen = 68) {
|
|
@@ -295,14 +305,6 @@ async function listIssueSlices(rootId, roleTag) {
|
|
|
295
305
|
error: null,
|
|
296
306
|
};
|
|
297
307
|
}
|
|
298
|
-
function formatIssueLine(ctx, issue, opts = {}) {
|
|
299
|
-
const marker = opts.marker ?? "•";
|
|
300
|
-
const tone = opts.tone ?? "accent";
|
|
301
|
-
const id = ctx.ui.theme.fg("dim", issue.id);
|
|
302
|
-
const priority = ctx.ui.theme.fg("muted", `p${issue.priority}`);
|
|
303
|
-
const title = ctx.ui.theme.fg("text", truncateOneLine(issue.title));
|
|
304
|
-
return ` ${ctx.ui.theme.fg(tone, marker)} ${id} ${priority} ${title}`;
|
|
305
|
-
}
|
|
306
308
|
function queueMeter(value, total, width = 10) {
|
|
307
309
|
if (width <= 0 || total <= 0) {
|
|
308
310
|
return "";
|
|
@@ -333,6 +335,127 @@ function isRefreshStale(lastUpdatedMs, staleAfterMs) {
|
|
|
333
335
|
}
|
|
334
336
|
return Date.now() - lastUpdatedMs > staleAfterMs;
|
|
335
337
|
}
|
|
338
|
+
function asRecord(value) {
|
|
339
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
return value;
|
|
343
|
+
}
|
|
344
|
+
function issueIdFromEvent(event) {
|
|
345
|
+
const issueId = typeof event.issue_id === "string" ? event.issue_id.trim() : "";
|
|
346
|
+
return issueId.length > 0 ? issueId : null;
|
|
347
|
+
}
|
|
348
|
+
function eventAgeLabel(tsMs) {
|
|
349
|
+
if (typeof tsMs !== "number" || !Number.isFinite(tsMs)) {
|
|
350
|
+
return "now";
|
|
351
|
+
}
|
|
352
|
+
const ageSeconds = Math.max(0, Math.round((Date.now() - tsMs) / 1_000));
|
|
353
|
+
if (ageSeconds < 60) {
|
|
354
|
+
return `${ageSeconds}s`;
|
|
355
|
+
}
|
|
356
|
+
const mins = Math.floor(ageSeconds / 60);
|
|
357
|
+
if (mins < 60) {
|
|
358
|
+
return `${mins}m`;
|
|
359
|
+
}
|
|
360
|
+
const hours = Math.floor(mins / 60);
|
|
361
|
+
return `${hours}h`;
|
|
362
|
+
}
|
|
363
|
+
function renderActivitySentence(event) {
|
|
364
|
+
const issueId = issueIdFromEvent(event);
|
|
365
|
+
if (!issueId) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
const eventType = typeof event.type === "string" ? event.type : "";
|
|
369
|
+
const payload = asRecord(event.payload);
|
|
370
|
+
if (eventType === "forum.post") {
|
|
371
|
+
const message = asRecord(payload?.message);
|
|
372
|
+
const body = typeof message?.body === "string" ? message.body.trim() : "";
|
|
373
|
+
const author = typeof message?.author === "string" ? message.author.trim() : "worker";
|
|
374
|
+
if (body.length === 0) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
issueId,
|
|
379
|
+
sentence: `${issueId} ${author}: ${truncateOneLine(body, 54)}`,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
if (eventType === "issue.claim") {
|
|
383
|
+
const ok = payload?.ok === true;
|
|
384
|
+
if (ok) {
|
|
385
|
+
return { issueId, sentence: `${issueId} claimed and started work` };
|
|
386
|
+
}
|
|
387
|
+
const reason = typeof payload?.reason === "string" ? payload.reason : "claim failed";
|
|
388
|
+
return { issueId, sentence: `${issueId} claim failed (${truncateOneLine(reason, 36)})` };
|
|
389
|
+
}
|
|
390
|
+
if (eventType === "issue.close") {
|
|
391
|
+
const outcome = typeof payload?.outcome === "string" ? payload.outcome : "closed";
|
|
392
|
+
return { issueId, sentence: `${issueId} closed (${outcome})` };
|
|
393
|
+
}
|
|
394
|
+
if (eventType === "issue.update") {
|
|
395
|
+
const changed = asRecord(payload?.changed);
|
|
396
|
+
const changedKeys = changed ? Object.keys(changed) : [];
|
|
397
|
+
if (changedKeys.includes("status")) {
|
|
398
|
+
const statusChange = asRecord(changed?.status);
|
|
399
|
+
const from = typeof statusChange?.from === "string" ? statusChange.from : "?";
|
|
400
|
+
const to = typeof statusChange?.to === "string" ? statusChange.to : "?";
|
|
401
|
+
return { issueId, sentence: `${issueId} status ${from} → ${to}` };
|
|
402
|
+
}
|
|
403
|
+
if (changedKeys.length > 0) {
|
|
404
|
+
return {
|
|
405
|
+
issueId,
|
|
406
|
+
sentence: `${issueId} updated ${truncateOneLine(changedKeys.join(","), 28)}`,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (eventType === "issue.open") {
|
|
411
|
+
return { issueId, sentence: `${issueId} reopened` };
|
|
412
|
+
}
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
async function fetchRecentActivity(opts) {
|
|
416
|
+
if (!muServerUrl()) {
|
|
417
|
+
return { lines: [], error: null };
|
|
418
|
+
}
|
|
419
|
+
let events;
|
|
420
|
+
try {
|
|
421
|
+
events = await fetchMuJson(`/api/control-plane/events?limit=${ACTIVITY_EVENT_LIMIT}`, {
|
|
422
|
+
timeoutMs: 4_000,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
catch (err) {
|
|
426
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
427
|
+
return { lines: [], error: `activity refresh failed: ${truncateOneLine(message, 60)}` };
|
|
428
|
+
}
|
|
429
|
+
if (!Array.isArray(events) || events.length === 0) {
|
|
430
|
+
return { lines: [], error: null };
|
|
431
|
+
}
|
|
432
|
+
const tracked = new Set(opts.issueIds.map((issueId) => issueId.trim()).filter((issueId) => issueId.length > 0));
|
|
433
|
+
const seenIssueIds = new Set();
|
|
434
|
+
const lines = [];
|
|
435
|
+
const sorted = [...events].sort((left, right) => {
|
|
436
|
+
const leftTs = typeof left.ts_ms === "number" ? left.ts_ms : 0;
|
|
437
|
+
const rightTs = typeof right.ts_ms === "number" ? right.ts_ms : 0;
|
|
438
|
+
return rightTs - leftTs;
|
|
439
|
+
});
|
|
440
|
+
for (const event of sorted) {
|
|
441
|
+
const rendered = renderActivitySentence(event);
|
|
442
|
+
if (!rendered) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (tracked.size > 0 && !tracked.has(rendered.issueId)) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (seenIssueIds.has(rendered.issueId)) {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
seenIssueIds.add(rendered.issueId);
|
|
452
|
+
lines.push(`${eventAgeLabel(event.ts_ms)} ${rendered.sentence}`);
|
|
453
|
+
if (lines.length >= ACTIVITY_LINE_LIMIT) {
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return { lines, error: null };
|
|
458
|
+
}
|
|
336
459
|
function computeQueueDrift(sessions, activeIssues) {
|
|
337
460
|
const activeWithoutSessionIds = activeIssues
|
|
338
461
|
.filter((issue) => !issueHasSession(sessions, issue.id))
|
|
@@ -406,6 +529,7 @@ function subagentsSnapshot(state, format) {
|
|
|
406
529
|
`spawn_paused: ${paused}`,
|
|
407
530
|
`queues: ${state.readyIssues.length} ready / ${state.activeIssues.length} active`,
|
|
408
531
|
`sessions: ${state.sessions.length}`,
|
|
532
|
+
`activity_lines: ${state.activityLines.length}`,
|
|
409
533
|
`drift_active_without_session: ${drift.activeWithoutSessionIds.length}`,
|
|
410
534
|
`drift_orphan_sessions: ${drift.orphanSessions.length}`,
|
|
411
535
|
`refresh_age: ${refreshAge}`,
|
|
@@ -425,6 +549,7 @@ function subagentsSnapshot(state, format) {
|
|
|
425
549
|
`active=${state.activeIssues.length}`,
|
|
426
550
|
`sessions=${state.sessions.length}`,
|
|
427
551
|
`drift=${staleCount}`,
|
|
552
|
+
`activity=${state.activityLines.length}`,
|
|
428
553
|
`refresh=${refreshAge}`,
|
|
429
554
|
].join(" · ");
|
|
430
555
|
}
|
|
@@ -434,11 +559,14 @@ function renderSubagentsUi(ctx, state) {
|
|
|
434
559
|
}
|
|
435
560
|
if (!state.enabled) {
|
|
436
561
|
ctx.ui.setStatus("mu-subagents", undefined);
|
|
562
|
+
ctx.ui.setStatus("mu-subagents-meta", undefined);
|
|
437
563
|
ctx.ui.setWidget("mu-subagents", undefined);
|
|
438
564
|
return;
|
|
439
565
|
}
|
|
440
566
|
const issueScope = state.issueRootId ? `root:${state.issueRootId}` : "all-roots";
|
|
441
567
|
const roleScope = state.issueRoleTag ? state.issueRoleTag : "(all roles)";
|
|
568
|
+
const scopeCompact = truncateOneLine(`${issueScope} · ${roleScope}`, WIDGET_SCOPE_MAX);
|
|
569
|
+
const prefixCompact = truncateOneLine(state.prefix || "(all sessions)", WIDGET_PREFIX_MAX);
|
|
442
570
|
const refreshStale = isRefreshStale(state.lastUpdatedMs, state.staleAfterMs);
|
|
443
571
|
const drift = computeQueueDrift(state.sessions, state.activeIssues);
|
|
444
572
|
const staleCount = drift.activeWithoutSessionIds.length + drift.orphanSessions.length;
|
|
@@ -452,82 +580,84 @@ function renderSubagentsUi(ctx, state) {
|
|
|
452
580
|
const pausedColor = state.spawnPaused ? "warning" : "dim";
|
|
453
581
|
const refreshSeconds = Math.round(state.refreshIntervalMs / 1_000);
|
|
454
582
|
const staleAfterSeconds = Math.round(state.staleAfterMs / 1_000);
|
|
583
|
+
const activityLines = state.activityLines.slice(0, ACTIVITY_LINE_LIMIT);
|
|
455
584
|
ctx.ui.setStatus("mu-subagents", [
|
|
456
585
|
ctx.ui.theme.fg("dim", "subagents"),
|
|
457
586
|
ctx.ui.theme.fg(healthColor, healthLabel),
|
|
458
|
-
ctx.ui.theme.fg("dim",
|
|
587
|
+
ctx.ui.theme.fg("dim", `mode:${state.spawnMode}`),
|
|
459
588
|
ctx.ui.theme.fg(pausedColor, `paused:${pausedLabel}`),
|
|
460
|
-
ctx.ui.theme.fg("dim",
|
|
461
|
-
ctx.ui.theme.fg("dim",
|
|
462
|
-
ctx.ui.theme.fg("
|
|
589
|
+
ctx.ui.theme.fg("dim", `q:${state.readyIssues.length}/${state.activeIssues.length}`),
|
|
590
|
+
ctx.ui.theme.fg("dim", `tmux:${state.sessions.length}`),
|
|
591
|
+
ctx.ui.theme.fg(staleCount > 0 ? "warning" : "dim", `drift:${staleCount}`),
|
|
592
|
+
ctx.ui.theme.fg("muted", truncateOneLine(issueScope, 18)),
|
|
463
593
|
].join(` ${ctx.ui.theme.fg("muted", "·")} `));
|
|
594
|
+
ctx.ui.setStatus("mu-subagents-meta", `health:${healthLabel} q:${state.readyIssues.length}/${state.activeIssues.length} tmux:${state.sessions.length} drift:${staleCount} refresh:${refreshAge}`);
|
|
464
595
|
const lines = [
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
596
|
+
[
|
|
597
|
+
ctx.ui.theme.fg("accent", ctx.ui.theme.bold("Subagents")),
|
|
598
|
+
ctx.ui.theme.fg("muted", "·"),
|
|
599
|
+
ctx.ui.theme.fg(healthColor, healthLabel),
|
|
600
|
+
ctx.ui.theme.fg("muted", "·"),
|
|
601
|
+
ctx.ui.theme.fg("accent", `mode:${state.spawnMode}`),
|
|
602
|
+
ctx.ui.theme.fg("muted", "·"),
|
|
603
|
+
ctx.ui.theme.fg(pausedColor, `paused:${pausedLabel}`),
|
|
604
|
+
].join(" "),
|
|
605
|
+
`${ctx.ui.theme.fg("muted", "scope:")} ${ctx.ui.theme.fg("dim", scopeCompact)} ${ctx.ui.theme.fg("muted", "· prefix:")} ${ctx.ui.theme.fg("dim", prefixCompact)}`,
|
|
606
|
+
[
|
|
607
|
+
ctx.ui.theme.fg("muted", "queues:"),
|
|
608
|
+
ctx.ui.theme.fg("accent", `${state.readyIssues.length}r`),
|
|
609
|
+
ctx.ui.theme.fg("muted", "/"),
|
|
610
|
+
ctx.ui.theme.fg("warning", `${state.activeIssues.length}a`),
|
|
611
|
+
ctx.ui.theme.fg("dim", queueBar),
|
|
612
|
+
ctx.ui.theme.fg("muted", "·"),
|
|
613
|
+
ctx.ui.theme.fg("muted", "tmux:"),
|
|
614
|
+
ctx.ui.theme.fg("dim", `${state.sessions.length}`),
|
|
615
|
+
ctx.ui.theme.fg("muted", "·"),
|
|
616
|
+
ctx.ui.theme.fg(staleCount > 0 ? "warning" : "dim", `drift:${staleCount}`),
|
|
617
|
+
].join(" "),
|
|
618
|
+
[
|
|
619
|
+
ctx.ui.theme.fg("muted", "refresh:"),
|
|
620
|
+
ctx.ui.theme.fg(refreshStale ? "warning" : "dim", refreshAge),
|
|
621
|
+
ctx.ui.theme.fg("muted", "·"),
|
|
622
|
+
ctx.ui.theme.fg("muted", "every:"),
|
|
623
|
+
ctx.ui.theme.fg("dim", `${refreshSeconds}s`),
|
|
624
|
+
ctx.ui.theme.fg("muted", "·"),
|
|
625
|
+
ctx.ui.theme.fg("muted", "stale:"),
|
|
626
|
+
ctx.ui.theme.fg("dim", `${staleAfterSeconds}s`),
|
|
627
|
+
].join(" "),
|
|
476
628
|
];
|
|
629
|
+
if (state.issueError) {
|
|
630
|
+
lines.push(ctx.ui.theme.fg("warning", `issue_error: ${truncateOneLine(state.issueError, WIDGET_ERROR_MAX)}`));
|
|
631
|
+
}
|
|
477
632
|
if (state.sessionError) {
|
|
478
|
-
lines.push(ctx.ui.theme.fg("warning", `
|
|
633
|
+
lines.push(ctx.ui.theme.fg("warning", `tmux_error: ${truncateOneLine(state.sessionError, WIDGET_ERROR_MAX)}`));
|
|
479
634
|
}
|
|
480
|
-
|
|
481
|
-
lines.push(ctx.ui.theme.fg("
|
|
635
|
+
if (refreshStale) {
|
|
636
|
+
lines.push(ctx.ui.theme.fg("warning", `warning: refresh stale (>${staleAfterSeconds}s since last successful refresh)`));
|
|
482
637
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
lines.push(` ${ctx.ui.theme.fg("success", "●")} ${ctx.ui.theme.fg("text", name)}`);
|
|
486
|
-
}
|
|
487
|
-
if (state.sessions.length > 8) {
|
|
488
|
-
lines.push(ctx.ui.theme.fg("muted", ` ... +${state.sessions.length - 8} more tmux sessions`));
|
|
489
|
-
}
|
|
638
|
+
if (drift.activeWithoutSessionIds.length > 0) {
|
|
639
|
+
lines.push(ctx.ui.theme.fg("warning", truncateOneLine(`drift_missing: ${drift.activeWithoutSessionIds.slice(0, 4).join(", ")}${drift.activeWithoutSessionIds.length > 4 ? " ..." : ""}`, WIDGET_ERROR_MAX)));
|
|
490
640
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
lines.push(ctx.ui.theme.fg("warning", `issue error: ${state.issueError}`));
|
|
641
|
+
if (drift.orphanSessions.length > 0) {
|
|
642
|
+
lines.push(ctx.ui.theme.fg("warning", truncateOneLine(`drift_orphan: ${drift.orphanSessions.slice(0, 4).join(", ")}${drift.orphanSessions.length > 4 ? " ..." : ""}`, WIDGET_ERROR_MAX)));
|
|
494
643
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
504
|
-
if (state.readyIssues.length > 6) {
|
|
505
|
-
lines.push(ctx.ui.theme.fg("muted", ` ... +${state.readyIssues.length - 6} more ready issues`));
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
lines.push(ctx.ui.theme.fg("accent", `active queue (${state.activeIssues.length})`));
|
|
509
|
-
if (state.activeIssues.length === 0) {
|
|
510
|
-
lines.push(ctx.ui.theme.fg("muted", " (no in-progress issues)"));
|
|
644
|
+
lines.push(ctx.ui.theme.fg("dim", "────────────────────────────"));
|
|
645
|
+
lines.push(ctx.ui.theme.fg("accent", "activity"));
|
|
646
|
+
if (state.activityError) {
|
|
647
|
+
lines.push(ctx.ui.theme.fg("warning", truncateOneLine(state.activityError, WIDGET_ERROR_MAX)));
|
|
648
|
+
}
|
|
649
|
+
else if (activityLines.length === 0) {
|
|
650
|
+
if (state.activeIssues.length > 0) {
|
|
651
|
+
lines.push(ctx.ui.theme.fg("muted", "(no recent subagent updates yet)"));
|
|
511
652
|
}
|
|
512
653
|
else {
|
|
513
|
-
|
|
514
|
-
lines.push(formatIssueLine(ctx, issue, { marker: "●", tone: "warning" }));
|
|
515
|
-
}
|
|
516
|
-
if (state.activeIssues.length > 6) {
|
|
517
|
-
lines.push(ctx.ui.theme.fg("muted", ` ... +${state.activeIssues.length - 6} more active issues`));
|
|
518
|
-
}
|
|
654
|
+
lines.push(ctx.ui.theme.fg("muted", "(no active workers)"));
|
|
519
655
|
}
|
|
520
656
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
lines.push(ctx.ui.theme.fg("warning", `drift warning: active issues without tmux sessions (${drift.activeWithoutSessionIds.length})`));
|
|
526
|
-
lines.push(ctx.ui.theme.fg("warning", ` missing sessions for: ${drift.activeWithoutSessionIds.slice(0, 8).join(", ")}${drift.activeWithoutSessionIds.length > 8 ? " ..." : ""}`));
|
|
527
|
-
}
|
|
528
|
-
if (drift.orphanSessions.length > 0) {
|
|
529
|
-
lines.push(ctx.ui.theme.fg("warning", `drift warning: tmux sessions without active issues (${drift.orphanSessions.length})`));
|
|
530
|
-
lines.push(ctx.ui.theme.fg("warning", ` orphan sessions: ${drift.orphanSessions.slice(0, 8).join(", ")}${drift.orphanSessions.length > 8 ? " ..." : ""}`));
|
|
657
|
+
else {
|
|
658
|
+
for (const line of activityLines) {
|
|
659
|
+
lines.push(`${ctx.ui.theme.fg("muted", "•")} ${ctx.ui.theme.fg("text", truncateOneLine(line, WIDGET_SUMMARY_MAX))}`);
|
|
660
|
+
}
|
|
531
661
|
}
|
|
532
662
|
ctx.ui.setWidget("mu-subagents", lines, { placement: "belowEditor" });
|
|
533
663
|
}
|
|
@@ -564,6 +694,8 @@ function subagentsDetails(state) {
|
|
|
564
694
|
refresh_stale: isRefreshStale(state.lastUpdatedMs, state.staleAfterMs),
|
|
565
695
|
issue_error: state.issueError,
|
|
566
696
|
session_error: state.sessionError,
|
|
697
|
+
activity_lines: [...state.activityLines],
|
|
698
|
+
activity_error: state.activityError,
|
|
567
699
|
last_updated_ms: state.lastUpdatedMs,
|
|
568
700
|
snapshot_compact: subagentsSnapshot(state, "compact"),
|
|
569
701
|
snapshot_multiline: subagentsSnapshot(state, "multiline"),
|
|
@@ -597,6 +729,12 @@ export function subagentsUiExtension(pi) {
|
|
|
597
729
|
state.readyIssues = issues.ready;
|
|
598
730
|
state.activeIssues = issues.active;
|
|
599
731
|
state.issueError = issues.error;
|
|
732
|
+
const trackedIssueIds = (state.activeIssues.length > 0 ? state.activeIssues : state.readyIssues)
|
|
733
|
+
.slice(0, 8)
|
|
734
|
+
.map((issue) => issue.id);
|
|
735
|
+
const activity = await fetchRecentActivity({ issueIds: trackedIssueIds });
|
|
736
|
+
state.activityLines = activity.lines;
|
|
737
|
+
state.activityError = activity.error;
|
|
600
738
|
state.lastUpdatedMs = Date.now();
|
|
601
739
|
renderSubagentsUi(ctx, state);
|
|
602
740
|
};
|
|
@@ -628,6 +766,16 @@ export function subagentsUiExtension(pi) {
|
|
|
628
766
|
const notify = (ctx, message, level = "info") => {
|
|
629
767
|
ctx.ui.notify(`${message}\n\n${subagentsUsageText()}`, level);
|
|
630
768
|
};
|
|
769
|
+
const syncSubagentsMode = (ctx, action) => {
|
|
770
|
+
const passiveAction = action === "status" || action === "snapshot";
|
|
771
|
+
if (!state.enabled) {
|
|
772
|
+
clearHudMode("subagents");
|
|
773
|
+
}
|
|
774
|
+
else if (!passiveAction) {
|
|
775
|
+
setActiveHudMode("subagents");
|
|
776
|
+
}
|
|
777
|
+
syncHudModeStatus(ctx);
|
|
778
|
+
};
|
|
631
779
|
const statusSummary = () => {
|
|
632
780
|
const when = state.lastUpdatedMs == null ? "never" : new Date(state.lastUpdatedMs).toLocaleTimeString();
|
|
633
781
|
const status = state.enabled ? "enabled" : "disabled";
|
|
@@ -637,6 +785,7 @@ export function subagentsUiExtension(pi) {
|
|
|
637
785
|
const refreshStale = isRefreshStale(state.lastUpdatedMs, state.staleAfterMs);
|
|
638
786
|
const issueError = state.issueError ? `\nissue_error: ${state.issueError}` : "";
|
|
639
787
|
const tmuxError = state.sessionError ? `\ntmux_error: ${state.sessionError}` : "";
|
|
788
|
+
const activityError = state.activityError ? `\nactivity_error: ${state.activityError}` : "";
|
|
640
789
|
const driftInfo = drift.activeWithoutSessionIds.length > 0 || drift.orphanSessions.length > 0
|
|
641
790
|
? `\ndrift_active_without_session: ${drift.activeWithoutSessionIds.length}\ndrift_orphan_sessions: ${drift.orphanSessions.length}`
|
|
642
791
|
: "";
|
|
@@ -644,6 +793,7 @@ export function subagentsUiExtension(pi) {
|
|
|
644
793
|
return {
|
|
645
794
|
level: state.issueError ||
|
|
646
795
|
state.sessionError ||
|
|
796
|
+
state.activityError ||
|
|
647
797
|
refreshStale ||
|
|
648
798
|
drift.activeWithoutSessionIds.length > 0 ||
|
|
649
799
|
drift.orphanSessions.length > 0
|
|
@@ -661,10 +811,12 @@ export function subagentsUiExtension(pi) {
|
|
|
661
811
|
`sessions: ${state.sessions.length}`,
|
|
662
812
|
`ready_issues: ${state.readyIssues.length}`,
|
|
663
813
|
`active_issues: ${state.activeIssues.length}`,
|
|
814
|
+
`activity_updates: ${state.activityLines.length}`,
|
|
664
815
|
`last refresh: ${when}`,
|
|
665
816
|
].join("\n") +
|
|
666
817
|
issueError +
|
|
667
818
|
tmuxError +
|
|
819
|
+
activityError +
|
|
668
820
|
driftInfo +
|
|
669
821
|
staleInfo,
|
|
670
822
|
};
|
|
@@ -984,6 +1136,7 @@ export function subagentsUiExtension(pi) {
|
|
|
984
1136
|
ensurePolling();
|
|
985
1137
|
}
|
|
986
1138
|
await refresh(ctx);
|
|
1139
|
+
syncHudModeStatus(ctx);
|
|
987
1140
|
});
|
|
988
1141
|
pi.on("session_switch", async (_event, ctx) => {
|
|
989
1142
|
activeCtx = ctx;
|
|
@@ -991,6 +1144,7 @@ export function subagentsUiExtension(pi) {
|
|
|
991
1144
|
ensurePolling();
|
|
992
1145
|
}
|
|
993
1146
|
await refresh(ctx);
|
|
1147
|
+
syncHudModeStatus(ctx);
|
|
994
1148
|
});
|
|
995
1149
|
pi.on("session_shutdown", async () => {
|
|
996
1150
|
stopPolling();
|
|
@@ -1072,6 +1226,7 @@ export function subagentsUiExtension(pi) {
|
|
|
1072
1226
|
notify(ctx, result.message, result.level);
|
|
1073
1227
|
return;
|
|
1074
1228
|
}
|
|
1229
|
+
syncSubagentsMode(ctx, params.action);
|
|
1075
1230
|
ctx.ui.notify(result.message, result.level);
|
|
1076
1231
|
},
|
|
1077
1232
|
});
|
|
@@ -1127,6 +1282,7 @@ export function subagentsUiExtension(pi) {
|
|
|
1127
1282
|
if (!result.ok) {
|
|
1128
1283
|
return subagentsToolError(result.message, state);
|
|
1129
1284
|
}
|
|
1285
|
+
syncSubagentsMode(ctx, params.action);
|
|
1130
1286
|
return {
|
|
1131
1287
|
content: [{ type: "text", text: result.message }],
|
|
1132
1288
|
details: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@femtomc/mu-agent",
|
|
3
|
-
"version": "26.2.
|
|
3
|
+
"version": "26.2.90",
|
|
4
4
|
"description": "Shared agent runtime for mu assistant sessions, orchestration roles, 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.90",
|
|
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",
|