@femtomc/mu-agent 26.2.95 → 26.2.97

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.
@@ -320,7 +320,7 @@ function renderPlanningUi(ctx, state) {
320
320
  const shownSteps = state.steps.slice(0, WIDGET_STEP_LIMIT);
321
321
  for (let index = 0; index < shownSteps.length; index += 1) {
322
322
  const step = shownSteps[index];
323
- const mark = step.done ? ctx.ui.theme.fg("success", "") : ctx.ui.theme.fg("muted", "");
323
+ const mark = step.done ? ctx.ui.theme.fg("success", "[x]") : ctx.ui.theme.fg("muted", "[ ]");
324
324
  const labelText = shortLabel(step.label, "(empty)", WIDGET_STEP_LABEL_MAX);
325
325
  const label = step.done ? ctx.ui.theme.fg("dim", labelText) : ctx.ui.theme.fg("text", labelText);
326
326
  lines.push(`${mark} ${ctx.ui.theme.fg("muted", `${index + 1}.`)} ${label}`);
@@ -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;AAu5BpF,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;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 fetchRecentActivity(opts) {
412
- if (!muServerUrl()) {
413
- return { lines: [], error: null };
414
- }
415
- let events;
416
- try {
417
- events = await fetchMuJson(`/api/control-plane/events?limit=${ACTIVITY_EVENT_LIMIT}`, {
418
- timeoutMs: 4_000,
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
- return { lines: [], error: `activity refresh failed: ${truncateOneLine(message, 60)}` };
427
- }
428
- if (!Array.isArray(events) || events.length === 0) {
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
- if (tracked.size > 0 && !tracked.has(rendered.issueId)) {
445
- continue;
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
- if (seenIssueIds.has(rendered.issueId)) {
448
- continue;
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
- seenIssueIds.add(rendered.issueId);
451
- lines.push(`${eventAgeLabel(event.ts_ms)} ${rendered.sentence}`);
452
- if (lines.length >= ACTIVITY_LINE_LIMIT) {
453
- break;
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
- return { lines, error: null };
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
- if (state.activeIssues.length > 0) {
662
- lines.push(ctx.ui.theme.fg("muted", "(no recent subagent updates yet)"));
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 = (state.activeIssues.length > 0 ? state.activeIssues : state.readyIssues)
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 });
@@ -1 +1 @@
1
- {"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"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"}
package/dist/operator.js CHANGED
@@ -5,7 +5,8 @@ import { z } from "zod";
5
5
  import { CommandContextResolver } from "./command_context.js";
6
6
  import { createMuSession } from "./session_factory.js";
7
7
  import { DEFAULT_OPERATOR_SYSTEM_PROMPT } from "./default_prompts.js";
8
- const SAFE_RESPONSE_RE = /^[\s\S]{1,2000}$/;
8
+ const OPERATOR_RESPONSE_MAX_CHARS = 12_000;
9
+ const SAFE_RESPONSE_RE = new RegExp(`^[\\s\\S]{1,${OPERATOR_RESPONSE_MAX_CHARS}}$`);
9
10
  export const OperatorApprovedCommandSchema = z.discriminatedUnion("kind", [
10
11
  z.object({ kind: z.literal("status") }),
11
12
  z.object({ kind: z.literal("ready") }),
@@ -40,7 +41,7 @@ export const OperatorApprovedCommandSchema = z.discriminatedUnion("kind", [
40
41
  }),
41
42
  ]);
42
43
  export const OperatorBackendTurnResultSchema = z.discriminatedUnion("kind", [
43
- z.object({ kind: z.literal("respond"), message: z.string().trim().min(1).max(2000) }),
44
+ z.object({ kind: z.literal("respond"), message: z.string().trim().min(1).max(OPERATOR_RESPONSE_MAX_CHARS) }),
44
45
  z.object({ kind: z.literal("command"), command: OperatorApprovedCommandSchema }),
45
46
  ]);
46
47
  function normalizeArg(arg) {
@@ -166,8 +167,31 @@ function buildOperatorFailureFallbackMessage(code) {
166
167
  "You can retry, or use an explicit /mu command (for example: /mu status or /mu issue list).",
167
168
  ].join("\n");
168
169
  }
170
+ function isAgentBusyError(err) {
171
+ if (!(err instanceof Error)) {
172
+ return false;
173
+ }
174
+ const text = err.message.trim().toLowerCase();
175
+ return text.includes("agent is already processing");
176
+ }
177
+ function slackThreadScope(inbound) {
178
+ if (inbound.channel !== "slack") {
179
+ return null;
180
+ }
181
+ const metadata = inbound.metadata;
182
+ const candidate = [metadata.slack_thread_ts, metadata.slack_message_ts, metadata.thread_ts].find((value) => typeof value === "string" && value.trim().length > 0);
183
+ if (typeof candidate !== "string") {
184
+ return null;
185
+ }
186
+ return candidate.trim();
187
+ }
169
188
  function conversationKey(inbound, binding) {
170
- return `${inbound.channel}:${inbound.channel_tenant_id}:${inbound.channel_conversation_id}:${binding.binding_id}`;
189
+ const base = `${inbound.channel}:${inbound.channel_tenant_id}:${inbound.channel_conversation_id}:${binding.binding_id}`;
190
+ const slackThread = slackThreadScope(inbound);
191
+ if (!slackThread) {
192
+ return base;
193
+ }
194
+ return `${base}:thread:${slackThread}`;
171
195
  }
172
196
  function asRecord(value) {
173
197
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -794,14 +818,26 @@ export class PiMessagingOperatorBackend {
794
818
  }
795
819
  }
796
820
  });
797
- const timeoutPromise = new Promise((_, reject) => {
798
- setTimeout(() => reject(new Error("pi operator timeout")), this.#timeoutMs);
799
- });
821
+ const promptText = buildOperatorPrompt(input);
822
+ const promptOnce = async () => {
823
+ const timeoutPromise = new Promise((_, reject) => {
824
+ setTimeout(() => reject(new Error("pi operator timeout")), this.#timeoutMs);
825
+ });
826
+ await Promise.race([session.prompt(promptText, { expandPromptTemplates: false }), timeoutPromise]);
827
+ };
800
828
  try {
801
- await Promise.race([
802
- session.prompt(buildOperatorPrompt(input), { expandPromptTemplates: false }),
803
- timeoutPromise,
804
- ]);
829
+ try {
830
+ await promptOnce();
831
+ }
832
+ catch (err) {
833
+ if (!isAgentBusyError(err)) {
834
+ throw err;
835
+ }
836
+ await session.agent.waitForIdle();
837
+ assistantText = "";
838
+ capturedCommand = null;
839
+ await promptOnce();
840
+ }
805
841
  }
806
842
  catch (err) {
807
843
  await this.#auditTurn(input, {
@@ -833,7 +869,7 @@ export class PiMessagingOperatorBackend {
833
869
  });
834
870
  throw new Error("operator_empty_response");
835
871
  }
836
- const responseMessage = message.slice(0, 2000);
872
+ const responseMessage = message.slice(0, OPERATOR_RESPONSE_MAX_CHARS);
837
873
  await this.#auditTurn(input, {
838
874
  outcome: "respond",
839
875
  messagePreview: responseMessage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@femtomc/mu-agent",
3
- "version": "26.2.95",
3
+ "version": "26.2.97",
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.95",
27
+ "@femtomc/mu-core": "26.2.97",
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",