@femtomc/mu-agent 26.3.2 → 26.3.4

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
@@ -77,7 +77,7 @@ Current stack:
77
77
 
78
78
  - `brandingExtension` — mu compact header/footer branding + default theme
79
79
  - `eventLogExtension` — event tail + watch widget
80
- - `uiExtension` — programmable `UiDoc` surface (`/mu ui ...`, `mu_ui`)
80
+ - `uiExtension` — programmable `UiDoc` surface (`/mu ui ...`, `mu_ui`) with session-resume persistence via `custom` entries (`mu-ui-state/v1`)
81
81
 
82
82
  Default operator UI theme is `mu-gruvbox-dark`.
83
83
 
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/extensions/ui.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAq/CpF,wBAAgB,WAAW,CAAC,EAAE,EAAE,YAAY,QAkO3C;AAED,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/extensions/ui.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AA0qDpF,wBAAgB,WAAW,CAAC,EAAE,EAAE,YAAY,QA4P3C;AAED,eAAe,WAAW,CAAC"}
@@ -1,4 +1,4 @@
1
- import { normalizeUiDocs, parseUiDoc, resolveUiStatusProfileName, uiStatusProfileWarnings, } from "@femtomc/mu-core";
1
+ import { normalizeUiDocs, parseUiDoc, resolveUiStatusProfileName, stableSerializeJson, uiStatusProfileWarnings, } from "@femtomc/mu-core";
2
2
  import { matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
3
3
  import { registerMuSubcommand } from "./mu-command-dispatcher.js";
4
4
  const UI_DISPLAY_DOCS_MAX = 16;
@@ -6,6 +6,8 @@ const UI_PICKER_COMPONENTS_MAX = 8;
6
6
  const UI_PICKER_LIST_ITEMS_MAX = 4;
7
7
  const UI_PICKER_KEYVALUE_ROWS_MAX = 4;
8
8
  const UI_SESSION_KEY_FALLBACK = "__mu_ui_active_session__";
9
+ const UI_STATE_CUSTOM_TYPE = "mu-ui-state/v1";
10
+ const UI_STATE_SCHEMA_VERSION = 1;
9
11
  const UI_PROMPT_PREVIEW_MAX = 160;
10
12
  const UI_INTERACT_SHORTCUT_PRIMARY = "ctrl+shift+u";
11
13
  const UI_INTERACT_SHORTCUT_FALLBACK = "alt+u";
@@ -46,6 +48,7 @@ function createState() {
46
48
  interactionDepth: 0,
47
49
  lastStatusText: null,
48
50
  lastWidgetSignature: null,
51
+ lastPersistSignature: null,
49
52
  };
50
53
  }
51
54
  function pruneStaleStates(nowMs) {
@@ -389,6 +392,164 @@ function parseDocListInput(value) {
389
392
  }
390
393
  return { ok: true, docs: normalizeUiDocs(docs, { maxDocs: UI_DISPLAY_DOCS_MAX }) };
391
394
  }
395
+ function normalizeStringArray(value) {
396
+ if (!Array.isArray(value)) {
397
+ return [];
398
+ }
399
+ const out = [];
400
+ const seen = new Set();
401
+ for (const entry of value) {
402
+ if (typeof entry !== "string") {
403
+ continue;
404
+ }
405
+ const trimmed = entry.trim();
406
+ if (!trimmed || seen.has(trimmed)) {
407
+ continue;
408
+ }
409
+ seen.add(trimmed);
410
+ out.push(trimmed);
411
+ }
412
+ return out;
413
+ }
414
+ function parsePersistedPendingPrompt(value) {
415
+ if (!isPlainObject(value)) {
416
+ return null;
417
+ }
418
+ const kind = value.kind === "action" || value.kind === "review" ? value.kind : null;
419
+ if (!kind) {
420
+ return null;
421
+ }
422
+ const uiIdRaw = typeof value.ui_id === "string" ? value.ui_id.trim() : "";
423
+ if (!uiIdRaw) {
424
+ return null;
425
+ }
426
+ const actionIdRaw = typeof value.action_id === "string" ? value.action_id.trim() : "";
427
+ if (kind === "review") {
428
+ return { kind, ui_id: uiIdRaw };
429
+ }
430
+ if (actionIdRaw) {
431
+ return { kind, ui_id: uiIdRaw, action_id: actionIdRaw };
432
+ }
433
+ return { kind, ui_id: uiIdRaw };
434
+ }
435
+ function parsePersistedUiState(value) {
436
+ if (!isPlainObject(value)) {
437
+ return null;
438
+ }
439
+ if (value.v !== UI_STATE_SCHEMA_VERSION) {
440
+ return null;
441
+ }
442
+ const parsedDocs = parseDocListInput(value.docs);
443
+ if (!parsedDocs.ok) {
444
+ return null;
445
+ }
446
+ const pendingPrompts = [];
447
+ const pendingRaw = Array.isArray(value.pending_prompts) ? value.pending_prompts : [];
448
+ for (const pending of pendingRaw) {
449
+ const parsed = parsePersistedPendingPrompt(pending);
450
+ if (parsed) {
451
+ pendingPrompts.push(parsed);
452
+ }
453
+ }
454
+ return {
455
+ v: 1,
456
+ docs: parsedDocs.docs,
457
+ pending_prompts: pendingPrompts,
458
+ prompted_revision_keys: normalizeStringArray(value.prompted_revision_keys),
459
+ awaiting_ui_ids: normalizeStringArray(value.awaiting_ui_ids),
460
+ };
461
+ }
462
+ function serializeUiState(state) {
463
+ retainPromptedRevisionKeysForActiveDocs(state);
464
+ retainAwaitingUiIdsForActiveDocs(state);
465
+ retainPendingPromptsForActiveDocs(state);
466
+ const docs = activeDocs(state, UI_DISPLAY_DOCS_MAX);
467
+ return {
468
+ v: 1,
469
+ docs,
470
+ pending_prompts: state.pendingPrompts.map((pending) => ({
471
+ kind: pending.kind,
472
+ ui_id: pending.uiId,
473
+ ...(pending.actionId ? { action_id: pending.actionId } : {}),
474
+ })),
475
+ prompted_revision_keys: [...state.promptedRevisionKeys].sort((left, right) => left.localeCompare(right)),
476
+ awaiting_ui_ids: [...state.awaitingUiIds].sort((left, right) => left.localeCompare(right)),
477
+ };
478
+ }
479
+ function uiStatePersistSignature(value) {
480
+ return stableSerializeJson(value);
481
+ }
482
+ function persistUiStateSnapshot(pi, state) {
483
+ const snapshot = serializeUiState(state);
484
+ const signature = uiStatePersistSignature(snapshot);
485
+ if (state.lastPersistSignature === signature) {
486
+ return;
487
+ }
488
+ pi.appendEntry(UI_STATE_CUSTOM_TYPE, snapshot);
489
+ state.lastPersistSignature = signature;
490
+ }
491
+ function applyPersistedUiState(state, persisted) {
492
+ state.docsById.clear();
493
+ state.pendingPrompts = [];
494
+ state.promptedRevisionKeys.clear();
495
+ state.awaitingUiIds.clear();
496
+ state.interactionDepth = 0;
497
+ state.lastStatusText = null;
498
+ state.lastWidgetSignature = null;
499
+ for (const doc of persisted.docs) {
500
+ state.docsById.set(doc.ui_id, doc);
501
+ }
502
+ for (const revisionKey of persisted.prompted_revision_keys) {
503
+ state.promptedRevisionKeys.add(revisionKey);
504
+ }
505
+ for (const uiId of persisted.awaiting_ui_ids) {
506
+ state.awaitingUiIds.add(uiId);
507
+ }
508
+ for (const pending of persisted.pending_prompts) {
509
+ enqueuePendingPrompt(state, {
510
+ kind: pending.kind,
511
+ uiId: pending.ui_id,
512
+ actionId: pending.action_id,
513
+ });
514
+ }
515
+ retainPromptedRevisionKeysForActiveDocs(state);
516
+ retainAwaitingUiIdsForActiveDocs(state);
517
+ retainPendingPromptsForActiveDocs(state);
518
+ state.lastPersistSignature = uiStatePersistSignature(serializeUiState(state));
519
+ }
520
+ function latestPersistedUiStateFromBranch(entries) {
521
+ let latest = null;
522
+ for (const entry of entries) {
523
+ if (!isPlainObject(entry)) {
524
+ continue;
525
+ }
526
+ if (entry.type !== "custom" || entry.customType !== UI_STATE_CUSTOM_TYPE) {
527
+ continue;
528
+ }
529
+ const parsed = parsePersistedUiState(entry.data);
530
+ if (!parsed) {
531
+ continue;
532
+ }
533
+ latest = parsed;
534
+ }
535
+ return latest;
536
+ }
537
+ function restoreStateFromSessionBranch(ctx, key) {
538
+ const manager = ctx.sessionManager;
539
+ const branch = typeof manager.getBranch === "function" ? manager.getBranch() : [];
540
+ const restored = createState();
541
+ const persisted = latestPersistedUiStateFromBranch(branch);
542
+ if (persisted) {
543
+ applyPersistedUiState(restored, persisted);
544
+ }
545
+ const nowMs = Date.now();
546
+ pruneStaleStates(nowMs);
547
+ STATE_BY_SESSION.set(key, {
548
+ state: restored,
549
+ lastAccessMs: nowMs,
550
+ });
551
+ return restored;
552
+ }
392
553
  function statusProfileWarningsExtraForDoc(doc) {
393
554
  const warnings = uiStatusProfileWarnings(doc);
394
555
  if (warnings.length === 0) {
@@ -1355,6 +1516,7 @@ export function uiExtension(pi) {
1355
1516
  removePendingPromptsForUiId(state, selectedDoc.ui_id);
1356
1517
  retainAwaitingUiIdsForActiveDocs(state);
1357
1518
  retainPendingPromptsForActiveDocs(state);
1519
+ persistUiStateSnapshot(pi, state);
1358
1520
  ctx.ui.notify(`Submitted prompt from ${selectedDoc.ui_id}/${selectedAction.id}.`, "info");
1359
1521
  });
1360
1522
  registerMuSubcommand(pi, {
@@ -1432,15 +1594,35 @@ export function uiExtension(pi) {
1432
1594
  if (ctx.hasUI && result.ok && result.changedUiIds && result.changedUiIds.length > 0) {
1433
1595
  armAutoPromptForUiDocs(state, result.changedUiIds);
1434
1596
  }
1597
+ const shouldPersistMutation = result.ok &&
1598
+ (result.action === "set" ||
1599
+ result.action === "update" ||
1600
+ result.action === "replace" ||
1601
+ result.action === "remove" ||
1602
+ result.action === "clear");
1603
+ if (shouldPersistMutation) {
1604
+ persistUiStateSnapshot(pi, state);
1605
+ }
1435
1606
  refreshUi(ctx);
1436
1607
  return buildToolResult({ state, ...result });
1437
1608
  },
1438
1609
  });
1439
- pi.on("session_start", (_event, ctx) => {
1610
+ const restoreAndRefresh = (ctx) => {
1611
+ const key = sessionKey(ctx);
1612
+ restoreStateFromSessionBranch(ctx, key);
1440
1613
  refreshUi(ctx);
1614
+ };
1615
+ pi.on("session_start", (_event, ctx) => {
1616
+ restoreAndRefresh(ctx);
1441
1617
  });
1442
1618
  pi.on("session_switch", (_event, ctx) => {
1443
- refreshUi(ctx);
1619
+ restoreAndRefresh(ctx);
1620
+ });
1621
+ pi.on("session_fork", (_event, ctx) => {
1622
+ restoreAndRefresh(ctx);
1623
+ });
1624
+ pi.on("session_tree", (_event, ctx) => {
1625
+ restoreAndRefresh(ctx);
1444
1626
  });
1445
1627
  pi.on("agent_end", async (_event, ctx) => {
1446
1628
  if (!ctx.hasUI) {
@@ -1453,6 +1635,7 @@ export function uiExtension(pi) {
1453
1635
  if (!pending) {
1454
1636
  return;
1455
1637
  }
1638
+ persistUiStateSnapshot(pi, state);
1456
1639
  if (pending.kind === "action") {
1457
1640
  ctx.ui.notify(`Agent requested input via ${pending.uiId}. Submit now or press ${UI_INTERACT_SHORTCUT_HINT} later.`, "info");
1458
1641
  }
@@ -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
@@ -173,16 +173,43 @@ function defaultSessionId() {
173
173
  function defaultTurnId() {
174
174
  return `turn-${crypto.randomUUID()}`;
175
175
  }
176
+ function classifyProviderFailureCodeFromMessage(message) {
177
+ if (!message) {
178
+ return null;
179
+ }
180
+ const normalized = message.trim().toLowerCase();
181
+ if (!normalized) {
182
+ return null;
183
+ }
184
+ if (normalized.includes("chatgpt usage limit") ||
185
+ normalized.includes("usage limit") ||
186
+ normalized.includes("insufficient_quota") ||
187
+ normalized.includes("quota exceeded") ||
188
+ normalized.includes("billing hard limit")) {
189
+ return "operator_provider_usage_limit";
190
+ }
191
+ if (normalized.includes("context_length_exceeded") ||
192
+ normalized.includes("context window") ||
193
+ normalized.includes("maximum context length") ||
194
+ normalized.includes("input exceeds the context window")) {
195
+ return "operator_provider_context_length_exceeded";
196
+ }
197
+ return null;
198
+ }
176
199
  function buildOperatorFailureFallbackMessage(code) {
177
200
  const reasonLine = code === "operator_timeout"
178
201
  ? "The operator turn exceeded the messaging timeout before a safe response was produced."
179
202
  : code === "operator_busy"
180
203
  ? "Another operator turn is already in progress for this conversation."
181
- : code === "operator_empty_response"
182
- ? "The operator completed without a usable response payload."
183
- : code === "operator_cancelled"
184
- ? "The operator turn was cancelled before completion."
185
- : "An internal operator runtime/formatting error interrupted the turn.";
204
+ : code === "operator_provider_usage_limit"
205
+ ? "The configured operator provider rejected the turn due to usage or quota limits."
206
+ : code === "operator_provider_context_length_exceeded"
207
+ ? "The configured operator provider rejected the turn because the context window was exceeded."
208
+ : code === "operator_empty_response"
209
+ ? "The operator completed without a usable response payload."
210
+ : code === "operator_cancelled"
211
+ ? "The operator turn was cancelled before completion."
212
+ : "An internal operator runtime/formatting error interrupted the turn.";
186
213
  return [
187
214
  "I could not complete that turn safely.",
188
215
  `Code: ${code}`,
@@ -198,6 +225,16 @@ function classifyBackendFailureCode(err) {
198
225
  if (message.includes("pi operator timeout") || message.includes("operator timeout")) {
199
226
  return "operator_timeout";
200
227
  }
228
+ if (message.includes("operator_provider_usage_limit")) {
229
+ return "operator_provider_usage_limit";
230
+ }
231
+ if (message.includes("operator_provider_context_length_exceeded")) {
232
+ return "operator_provider_context_length_exceeded";
233
+ }
234
+ const providerFailureCode = classifyProviderFailureCodeFromMessage(message);
235
+ if (providerFailureCode) {
236
+ return providerFailureCode;
237
+ }
201
238
  if (message.includes("operator_empty_response")) {
202
239
  return "operator_empty_response";
203
240
  }
@@ -914,6 +951,8 @@ export class PiMessagingOperatorBackend {
914
951
  const sessionRecord = await this.#resolveSession(input.sessionId, input.inbound.repo_root, overrides);
915
952
  const session = sessionRecord.session;
916
953
  let assistantText = "";
954
+ let assistantStopReason = null;
955
+ let assistantErrorMessage = null;
917
956
  let capturedCommand = null;
918
957
  let capturedUiDocs = [];
919
958
  const turnEnv = autonomousTurnEnvironment(input.inbound.metadata);
@@ -927,6 +966,8 @@ export class PiMessagingOperatorBackend {
927
966
  // Capture assistant text for fallback responses.
928
967
  if (event?.type === "message_end" && event?.message?.role === "assistant") {
929
968
  const msg = event.message;
969
+ assistantStopReason = typeof msg.stopReason === "string" ? msg.stopReason.trim().toLowerCase() : null;
970
+ assistantErrorMessage = typeof msg.errorMessage === "string" ? msg.errorMessage.trim() : null;
930
971
  if (typeof msg.text === "string") {
931
972
  assistantText = msg.text;
932
973
  }
@@ -993,6 +1034,8 @@ export class PiMessagingOperatorBackend {
993
1034
  }
994
1035
  await session.agent.waitForIdle();
995
1036
  assistantText = "";
1037
+ assistantStopReason = null;
1038
+ assistantErrorMessage = null;
996
1039
  capturedCommand = null;
997
1040
  await promptOnce();
998
1041
  }
@@ -1001,7 +1044,7 @@ export class PiMessagingOperatorBackend {
1001
1044
  await this.#auditTurn(input, {
1002
1045
  outcome: "error",
1003
1046
  reason: err instanceof Error ? err.message : "operator_backend_error",
1004
- messagePreview: assistantText,
1047
+ messagePreview: assistantText || assistantErrorMessage || undefined,
1005
1048
  });
1006
1049
  throw err;
1007
1050
  }
@@ -1027,11 +1070,14 @@ export class PiMessagingOperatorBackend {
1027
1070
  // Otherwise treat the assistant text as a plain response.
1028
1071
  const message = assistantText.trim();
1029
1072
  if (!message) {
1073
+ const providerFailureCode = classifyProviderFailureCodeFromMessage(assistantErrorMessage);
1074
+ const emptyResponseCode = providerFailureCode ?? (assistantStopReason === "aborted" ? "operator_cancelled" : null) ?? "operator_empty_response";
1030
1075
  await this.#auditTurn(input, {
1031
1076
  outcome: "error",
1032
- reason: "operator_empty_response",
1077
+ reason: emptyResponseCode,
1078
+ messagePreview: assistantErrorMessage || undefined,
1033
1079
  });
1034
- throw new Error("operator_empty_response");
1080
+ throw new Error(emptyResponseCode);
1035
1081
  }
1036
1082
  const responseMessage = message.slice(0, OPERATOR_RESPONSE_MAX_CHARS);
1037
1083
  await this.#auditTurn(input, {
package/package.json CHANGED
@@ -1,34 +1,34 @@
1
1
  {
2
- "name": "@femtomc/mu-agent",
3
- "version": "26.3.2",
4
- "description": "Shared operator runtime for mu assistant sessions and serve extensions.",
5
- "keywords": [
6
- "mu",
7
- "agent",
8
- "runtime",
9
- "chat",
10
- "extensions"
11
- ],
12
- "type": "module",
13
- "main": "./dist/index.js",
14
- "types": "./dist/index.d.ts",
15
- "exports": {
16
- ".": {
17
- "types": "./dist/index.d.ts",
18
- "default": "./dist/index.js"
19
- }
20
- },
21
- "files": [
22
- "dist/**",
23
- "prompts/**",
24
- "themes/**"
25
- ],
26
- "dependencies": {
27
- "@femtomc/mu-core": "26.3.2",
28
- "@mariozechner/pi-agent-core": "^0.54.2",
29
- "@mariozechner/pi-ai": "^0.54.2",
30
- "@mariozechner/pi-coding-agent": "^0.54.2",
31
- "@mariozechner/pi-tui": "^0.54.2",
32
- "zod": "^4.1.9"
33
- }
2
+ "name": "@femtomc/mu-agent",
3
+ "version": "26.3.4",
4
+ "description": "Shared operator runtime for mu assistant sessions and serve extensions.",
5
+ "keywords": [
6
+ "mu",
7
+ "agent",
8
+ "runtime",
9
+ "chat",
10
+ "extensions"
11
+ ],
12
+ "type": "module",
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist/**",
23
+ "prompts/**",
24
+ "themes/**"
25
+ ],
26
+ "dependencies": {
27
+ "@femtomc/mu-core": "26.3.4",
28
+ "@mariozechner/pi-agent-core": "^0.54.2",
29
+ "@mariozechner/pi-ai": "^0.54.2",
30
+ "@mariozechner/pi-coding-agent": "^0.54.2",
31
+ "@mariozechner/pi-tui": "^0.54.2",
32
+ "zod": "^4.1.9"
33
+ }
34
34
  }