@femtomc/mu-agent 26.2.91 → 26.2.93

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 CHANGED
@@ -45,7 +45,7 @@ Current stack:
45
45
 
46
46
  - `brandingExtension` — mu compact header/footer branding + default theme
47
47
  - `eventLogExtension` — event tail + watch widget
48
- - `planningUiExtension` — planning mode: compact HUD for next-step/approval flow plus footer-ready incidental status metadata (`/mu plan ...`)
48
+ - `planningUiExtension` — planning mode: compact HUD for next-step/approval flow in widget/status surfaces while keeping the branding footer unchanged (`/mu plan ...`)
49
49
  - `subagentsUiExtension` — subagents mode: compact HUD with activity sentences from issue/forum events plus footer-ready queue/health metadata (`/mu subagents ...`)
50
50
 
51
51
  Default operator UI theme is `mu-gruvbox-dark`.
@@ -55,7 +55,7 @@ Default operator UI theme is `mu-gruvbox-dark`.
55
55
  - `/mu events [n]` / `/mu events tail [n]` — event log tail
56
56
  - `/mu events watch on|off` — toggle event watch widget
57
57
  - `/mu brand on|off|toggle` — enable/disable UI branding
58
- - `/mu plan ...` — planning HUD (phases, checklist editing, communication state, snapshots)
58
+ - `/mu plan ...` — planning HUD (phases, checklist editing, communication state, snapshots); does not inject planning metadata into branding footer
59
59
  - `/mu subagents ...` — tmux + issue queue monitor/spawner (profiles, spawn pause, stale/refresh controls, snapshots)
60
60
  - `/mu help` — dispatcher catalog of registered `/mu` subcommands
61
61
 
@@ -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;AAyEpF,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,YAAY,QA8OjD;AAED,eAAe,iBAAiB,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,QA6OjD;AAED,eAAe,iBAAiB,CAAC"}
@@ -129,11 +129,10 @@ export function brandingExtension(pi) {
129
129
  parts.push(theme.fg("muted", "·"), theme.fg(barColor, `ctx ${pct}%`), theme.fg(barColor, contextBar(pct, 10)));
130
130
  }
131
131
  const activeHudMode = getActiveHudMode();
132
- if (activeHudMode) {
132
+ if (activeHudMode === "subagents") {
133
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}`));
134
+ const modeMeta = extensionStatuses.get("mu-subagents-meta") ?? "";
135
+ parts.push(theme.fg("muted", "·"), theme.fg("accent", "hud:subagents"));
137
136
  if (modeMeta.length > 0) {
138
137
  parts.push(theme.fg("muted", "·"), theme.fg("dim", truncateToWidth(modeMeta, 42)));
139
138
  }
@@ -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;AAigBpF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,QAuenD;AAED,eAAe,mBAAmB,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;AA0gBpF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,QAuenD;AAED,eAAe,mBAAmB,CAAC"}
@@ -9,9 +9,9 @@ const DEFAULT_STEPS = [
9
9
  const BAR_CHARS = ["░", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"];
10
10
  const WIDGET_STEP_LIMIT = 4;
11
11
  const WIDGET_STEP_LABEL_MAX = 60;
12
- const WIDGET_ROOT_MAX = 20;
13
- const WIDGET_NEXT_MAX = 56;
14
- const WIDGET_BLOCKER_MAX = 56;
12
+ const WIDGET_ROOT_MAX = 24;
13
+ const WIDGET_NEXT_MAX = 76;
14
+ const WIDGET_BLOCKER_MAX = 76;
15
15
  function phaseTone(phase) {
16
16
  switch (phase) {
17
17
  case "investigating":
@@ -262,41 +262,53 @@ function renderPlanningUi(ctx, state) {
262
262
  const phase = summarizePhase(state.phase);
263
263
  const phaseColor = phaseTone(state.phase);
264
264
  const confidenceColor = confidenceTone(state.confidence);
265
- const rootLabel = state.rootIssueId ?? "(unset)";
266
265
  const meter = progressBar(done, total, 10);
267
- const waitingLabel = state.waitingOnUser ? "yes" : "no";
266
+ const waitingLabel = state.waitingOnUser ? "awaiting-user" : "active";
268
267
  const waitingColor = state.waitingOnUser ? "warning" : "dim";
269
- const rootCompact = shortLabel(rootLabel, "(unset)", WIDGET_ROOT_MAX);
270
- const nextCompact = shortLabel(state.nextAction, "(unset)", WIDGET_NEXT_MAX);
268
+ const rootCompact = state.rootIssueId ? shortLabel(state.rootIssueId, "", WIDGET_ROOT_MAX) : null;
269
+ const nextLabel = state.waitingOnUser ? "ask" : "next";
270
+ const nextFallback = state.waitingOnUser ? "(describe required user input)" : "(unset)";
271
+ const nextTone = state.waitingOnUser ? "warning" : "dim";
272
+ const nextCompact = shortLabel(state.nextAction, nextFallback, WIDGET_NEXT_MAX);
271
273
  const blockerCompact = shortLabel(state.blocker, "(none)", WIDGET_BLOCKER_MAX);
272
274
  const blockerColor = state.blocker ? "warning" : "dim";
273
- ctx.ui.setStatus("mu-planning", [
275
+ const statusParts = [
274
276
  ctx.ui.theme.fg("dim", "plan"),
275
277
  ctx.ui.theme.fg(phaseColor, phase),
276
278
  ctx.ui.theme.fg("dim", `${done}/${total}`),
277
279
  ctx.ui.theme.fg(phaseColor, meter),
278
- ctx.ui.theme.fg(waitingColor, `wait:${waitingLabel}`),
279
- ctx.ui.theme.fg("muted", `root:${rootCompact}`),
280
- ].join(` ${ctx.ui.theme.fg("muted", "·")} `));
281
- ctx.ui.setStatus("mu-planning-meta", `phase:${phase} steps:${done}/${total} wait:${waitingLabel} conf:${state.confidence}`);
280
+ ];
281
+ if (state.waitingOnUser) {
282
+ statusParts.push(ctx.ui.theme.fg("warning", "ask:user"));
283
+ }
284
+ if (state.blocker) {
285
+ statusParts.push(ctx.ui.theme.fg("warning", "blocked"));
286
+ }
287
+ ctx.ui.setStatus("mu-planning", statusParts.join(` ${ctx.ui.theme.fg("muted", "·")} `));
288
+ ctx.ui.setStatus("mu-planning-meta", undefined);
282
289
  const lines = [
283
290
  [
284
291
  ctx.ui.theme.fg("accent", ctx.ui.theme.bold("Planning")),
285
292
  ctx.ui.theme.fg("muted", "·"),
286
293
  ctx.ui.theme.fg(phaseColor, phase),
287
294
  ctx.ui.theme.fg("muted", "·"),
295
+ ctx.ui.theme.fg(waitingColor, waitingLabel),
296
+ ctx.ui.theme.fg("muted", "·"),
288
297
  ctx.ui.theme.fg("dim", `${done}/${total}`),
289
298
  ctx.ui.theme.fg(phaseColor, meter),
290
299
  ].join(" "),
291
- [
292
- ctx.ui.theme.fg("muted", `root:${rootCompact}`),
293
- ctx.ui.theme.fg("muted", "·"),
294
- ctx.ui.theme.fg(waitingColor, `wait:${waitingLabel}`),
295
- ctx.ui.theme.fg("muted", "·"),
296
- ctx.ui.theme.fg(confidenceColor, `conf:${state.confidence}`),
297
- ].join(" "),
298
- `${ctx.ui.theme.fg("muted", "next:")} ${ctx.ui.theme.fg("dim", nextCompact)}`,
299
300
  ];
301
+ const chips = [];
302
+ if (rootCompact) {
303
+ chips.push(ctx.ui.theme.fg("muted", `root:${rootCompact}`));
304
+ }
305
+ if (state.confidence !== "medium") {
306
+ chips.push(ctx.ui.theme.fg(confidenceColor, `conf:${state.confidence}`));
307
+ }
308
+ if (chips.length > 0) {
309
+ lines.push(chips.join(` ${ctx.ui.theme.fg("muted", "·")} `));
310
+ }
311
+ lines.push(`${ctx.ui.theme.fg("muted", `${nextLabel}:`)} ${ctx.ui.theme.fg(nextTone, nextCompact)}`);
300
312
  if (state.blocker) {
301
313
  lines.push(`${ctx.ui.theme.fg("muted", "blocker:")} ${ctx.ui.theme.fg(blockerColor, blockerCompact)}`);
302
314
  }
@@ -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;AA63BpF,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,QAkoBpD;AAED,eAAe,oBAAoB,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"}
@@ -404,6 +404,10 @@ function renderActivitySentence(event) {
404
404
  }
405
405
  return null;
406
406
  }
407
+ function isActivityEndpointUnavailable(errorMessage) {
408
+ const normalized = errorMessage.toLowerCase();
409
+ return normalized.includes("mu server 404") && normalized.includes("not found");
410
+ }
407
411
  async function fetchRecentActivity(opts) {
408
412
  if (!muServerUrl()) {
409
413
  return { lines: [], error: null };
@@ -416,6 +420,9 @@ async function fetchRecentActivity(opts) {
416
420
  }
417
421
  catch (err) {
418
422
  const message = err instanceof Error ? err.message : String(err);
423
+ if (isActivityEndpointUnavailable(message)) {
424
+ return { lines: [], error: null };
425
+ }
419
426
  return { lines: [], error: `activity refresh failed: ${truncateOneLine(message, 60)}` };
420
427
  }
421
428
  if (!Array.isArray(events) || events.length === 0) {
@@ -502,7 +509,9 @@ function subagentsSnapshot(state, format) {
502
509
  const drift = computeQueueDrift(state.sessions, state.activeIssues);
503
510
  const refreshStale = isRefreshStale(state.lastUpdatedMs, state.staleAfterMs);
504
511
  const staleCount = drift.activeWithoutSessionIds.length + drift.orphanSessions.length;
505
- const health = state.issueError || state.sessionError || refreshStale || staleCount > 0 ? "degraded" : "healthy";
512
+ const health = state.issueError || state.sessionError || state.activityError || refreshStale || staleCount > 0
513
+ ? "degraded"
514
+ : "healthy";
506
515
  const refreshAge = formatRefreshAge(state.lastUpdatedMs);
507
516
  const paused = state.spawnPaused ? "yes" : "no";
508
517
  const refreshSeconds = Math.round(state.refreshIntervalMs / 1_000);
@@ -559,7 +568,7 @@ function renderSubagentsUi(ctx, state) {
559
568
  const refreshStale = isRefreshStale(state.lastUpdatedMs, state.staleAfterMs);
560
569
  const drift = computeQueueDrift(state.sessions, state.activeIssues);
561
570
  const staleCount = drift.activeWithoutSessionIds.length + drift.orphanSessions.length;
562
- const hasError = Boolean(state.sessionError || state.issueError || refreshStale || staleCount > 0);
571
+ const hasError = Boolean(state.sessionError || state.issueError || state.activityError || refreshStale || staleCount > 0);
563
572
  const healthColor = hasError ? "warning" : "success";
564
573
  const healthLabel = hasError ? "degraded" : "healthy";
565
574
  const queueTotal = state.readyIssues.length + state.activeIssues.length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@femtomc/mu-agent",
3
- "version": "26.2.91",
3
+ "version": "26.2.93",
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.91",
27
+ "@femtomc/mu-core": "26.2.93",
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",
@@ -241,9 +241,9 @@ for issue_id in $(mu issues ready --root <root-id> --tag proto:subagents-v1 --js
241
241
  done
242
242
  ```
243
243
 
244
- ## Subagents HUD (optional board)
244
+ ## Subagents HUD
245
245
 
246
- Use HUD for visibility and bounded spawning. Protocol truth still lives in issues/forum.
246
+ Use HUD to communicate with your user for visibility. Truth should still live entirely in issues/forum.
247
247
 
248
248
  ```text
249
249
  /mu subagents on