dominds 1.17.2 → 1.17.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.
Files changed (125) hide show
  1. package/dist/apps-host/host.js +13 -2
  2. package/dist/apps-host/ipc-types.js +18 -1
  3. package/dist/dialog-fork.js +1 -0
  4. package/dist/dialog.js +5 -0
  5. package/dist/docs/mcp-support.md +4 -1
  6. package/dist/docs/team_mgmt-toolset.md +8 -7
  7. package/dist/docs/team_mgmt-toolset.zh.md +6 -5
  8. package/dist/mcp/manual-problems.d.ts +33 -0
  9. package/dist/mcp/manual-problems.js +323 -0
  10. package/dist/mcp/supervisor.js +23 -1
  11. package/dist/persistence.js +12 -0
  12. package/dist/priming.js +7 -0
  13. package/dist/shared-reminders.js +2 -0
  14. package/dist/tool.d.ts +5 -0
  15. package/dist/tool.js +66 -4
  16. package/dist/tools/app-reminders.js +20 -3
  17. package/dist/tools/ctrl.js +43 -4
  18. package/dist/tools/manual/output-limit.d.ts +9 -0
  19. package/dist/tools/manual/output-limit.js +12 -0
  20. package/dist/tools/os.js +173 -37
  21. package/dist/tools/pending-tellask-reminder.d.ts +1 -1
  22. package/dist/tools/pending-tellask-reminder.js +39 -19
  23. package/dist/tools/team_mgmt-manual.d.ts +2 -0
  24. package/dist/tools/team_mgmt-manual.js +160 -0
  25. package/dist/tools/team_mgmt-mcp-manual.d.ts +27 -0
  26. package/dist/tools/team_mgmt-mcp-manual.js +643 -0
  27. package/dist/tools/team_mgmt.d.ts +11 -1
  28. package/dist/tools/team_mgmt.js +22 -867
  29. package/dist/tools/toolset-manual.js +5 -5
  30. package/package.json +4 -4
  31. package/webapp/dist/assets/{_basePickBy-EK9iGcOl.js → _basePickBy-CgM-M_q8.js} +3 -3
  32. package/webapp/dist/assets/{_basePickBy-EK9iGcOl.js.map → _basePickBy-CgM-M_q8.js.map} +1 -1
  33. package/webapp/dist/assets/{_baseUniq-BHtz-XvO.js → _baseUniq-B06twih4.js} +2 -2
  34. package/webapp/dist/assets/{_baseUniq-BHtz-XvO.js.map → _baseUniq-B06twih4.js.map} +1 -1
  35. package/webapp/dist/assets/{arc-NqUmMwkS.js → arc-CoXJvjeB.js} +2 -2
  36. package/webapp/dist/assets/{arc-NqUmMwkS.js.map → arc-CoXJvjeB.js.map} +1 -1
  37. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CJ7Jb15a.js → architectureDiagram-2XIMDMQ5-BLFRWTKn.js} +7 -7
  38. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CJ7Jb15a.js.map → architectureDiagram-2XIMDMQ5-BLFRWTKn.js.map} +1 -1
  39. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-RNM7ujN4.js → blockDiagram-WCTKOSBZ-CYRE6deu.js} +7 -7
  40. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-RNM7ujN4.js.map → blockDiagram-WCTKOSBZ-CYRE6deu.js.map} +1 -1
  41. package/webapp/dist/assets/{c4Diagram-IC4MRINW-DKra5-za.js → c4Diagram-IC4MRINW-B26QTIJt.js} +3 -3
  42. package/webapp/dist/assets/{c4Diagram-IC4MRINW-DKra5-za.js.map → c4Diagram-IC4MRINW-B26QTIJt.js.map} +1 -1
  43. package/webapp/dist/assets/{channel-CxE9sL_E.js → channel-C5U2W0P9.js} +2 -2
  44. package/webapp/dist/assets/{channel-CxE9sL_E.js.map → channel-C5U2W0P9.js.map} +1 -1
  45. package/webapp/dist/assets/{chunk-4BX2VUAB-BlyIt9uv.js → chunk-4BX2VUAB-7z2PgnSv.js} +2 -2
  46. package/webapp/dist/assets/{chunk-4BX2VUAB-BlyIt9uv.js.map → chunk-4BX2VUAB-7z2PgnSv.js.map} +1 -1
  47. package/webapp/dist/assets/{chunk-55IACEB6-DsPShmjL.js → chunk-55IACEB6-6sRVmXqs.js} +2 -2
  48. package/webapp/dist/assets/{chunk-55IACEB6-DsPShmjL.js.map → chunk-55IACEB6-6sRVmXqs.js.map} +1 -1
  49. package/webapp/dist/assets/{chunk-FMBD7UC4-C2i0rEFF.js → chunk-FMBD7UC4-BwYp8OtY.js} +2 -2
  50. package/webapp/dist/assets/{chunk-FMBD7UC4-C2i0rEFF.js.map → chunk-FMBD7UC4-BwYp8OtY.js.map} +1 -1
  51. package/webapp/dist/assets/{chunk-JSJVCQXG-CK8inzJx.js → chunk-JSJVCQXG-CRq8LK53.js} +2 -2
  52. package/webapp/dist/assets/{chunk-JSJVCQXG-CK8inzJx.js.map → chunk-JSJVCQXG-CRq8LK53.js.map} +1 -1
  53. package/webapp/dist/assets/{chunk-KX2RTZJC-BjEK5_oI.js → chunk-KX2RTZJC-CzFE355P.js} +2 -2
  54. package/webapp/dist/assets/{chunk-KX2RTZJC-BjEK5_oI.js.map → chunk-KX2RTZJC-CzFE355P.js.map} +1 -1
  55. package/webapp/dist/assets/{chunk-NQ4KR5QH-Clf489xc.js → chunk-NQ4KR5QH-3cQSOzCt.js} +4 -4
  56. package/webapp/dist/assets/{chunk-NQ4KR5QH-Clf489xc.js.map → chunk-NQ4KR5QH-3cQSOzCt.js.map} +1 -1
  57. package/webapp/dist/assets/{chunk-QZHKN3VN-CMikir3s.js → chunk-QZHKN3VN-DWkpxb-w.js} +2 -2
  58. package/webapp/dist/assets/{chunk-QZHKN3VN-CMikir3s.js.map → chunk-QZHKN3VN-DWkpxb-w.js.map} +1 -1
  59. package/webapp/dist/assets/{chunk-WL4C6EOR-1gtCLicd.js → chunk-WL4C6EOR-DkpfoQzK.js} +6 -6
  60. package/webapp/dist/assets/{chunk-WL4C6EOR-1gtCLicd.js.map → chunk-WL4C6EOR-DkpfoQzK.js.map} +1 -1
  61. package/webapp/dist/assets/{classDiagram-VBA2DB6C-kzzlkQ_D.js → classDiagram-VBA2DB6C-mVfJeuZL.js} +7 -7
  62. package/webapp/dist/assets/{classDiagram-VBA2DB6C-kzzlkQ_D.js.map → classDiagram-VBA2DB6C-mVfJeuZL.js.map} +1 -1
  63. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-kzzlkQ_D.js → classDiagram-v2-RAHNMMFH-mVfJeuZL.js} +7 -7
  64. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-kzzlkQ_D.js.map → classDiagram-v2-RAHNMMFH-mVfJeuZL.js.map} +1 -1
  65. package/webapp/dist/assets/{clone-XglJh1R0.js → clone-5uLJc7AC.js} +2 -2
  66. package/webapp/dist/assets/{clone-XglJh1R0.js.map → clone-5uLJc7AC.js.map} +1 -1
  67. package/webapp/dist/assets/{cose-bilkent-S5V4N54A--gZrh2tG.js → cose-bilkent-S5V4N54A-CoiJzdQi.js} +2 -2
  68. package/webapp/dist/assets/{cose-bilkent-S5V4N54A--gZrh2tG.js.map → cose-bilkent-S5V4N54A-CoiJzdQi.js.map} +1 -1
  69. package/webapp/dist/assets/{dagre-KLK3FWXG-D_JMhNNL.js → dagre-KLK3FWXG-DU_3BSOq.js} +7 -7
  70. package/webapp/dist/assets/{dagre-KLK3FWXG-D_JMhNNL.js.map → dagre-KLK3FWXG-DU_3BSOq.js.map} +1 -1
  71. package/webapp/dist/assets/{diagram-E7M64L7V-HkYhqJDL.js → diagram-E7M64L7V-DgqOvF1U.js} +8 -8
  72. package/webapp/dist/assets/{diagram-E7M64L7V-HkYhqJDL.js.map → diagram-E7M64L7V-DgqOvF1U.js.map} +1 -1
  73. package/webapp/dist/assets/{diagram-IFDJBPK2-BFv5iU5U.js → diagram-IFDJBPK2-CFWMc1oD.js} +7 -7
  74. package/webapp/dist/assets/{diagram-IFDJBPK2-BFv5iU5U.js.map → diagram-IFDJBPK2-CFWMc1oD.js.map} +1 -1
  75. package/webapp/dist/assets/{diagram-P4PSJMXO-Dg46tTnk.js → diagram-P4PSJMXO-lrqvXDXp.js} +7 -7
  76. package/webapp/dist/assets/{diagram-P4PSJMXO-Dg46tTnk.js.map → diagram-P4PSJMXO-lrqvXDXp.js.map} +1 -1
  77. package/webapp/dist/assets/{erDiagram-INFDFZHY-BzJClUtq.js → erDiagram-INFDFZHY-C28KjRkA.js} +5 -5
  78. package/webapp/dist/assets/{erDiagram-INFDFZHY-BzJClUtq.js.map → erDiagram-INFDFZHY-C28KjRkA.js.map} +1 -1
  79. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-IBGgxeki.js → flowDiagram-PKNHOUZH-DkxGh-JF.js} +7 -7
  80. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-IBGgxeki.js.map → flowDiagram-PKNHOUZH-DkxGh-JF.js.map} +1 -1
  81. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-DdxgzFKe.js → ganttDiagram-A5KZAMGK-BmZnHD96.js} +3 -3
  82. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-DdxgzFKe.js.map → ganttDiagram-A5KZAMGK-BmZnHD96.js.map} +1 -1
  83. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-C14OpSSI.js → gitGraphDiagram-K3NZZRJ6-xiHqomZC.js} +8 -8
  84. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-C14OpSSI.js.map → gitGraphDiagram-K3NZZRJ6-xiHqomZC.js.map} +1 -1
  85. package/webapp/dist/assets/{graph-BrKKvSVx.js → graph-ozb0amP0.js} +3 -3
  86. package/webapp/dist/assets/{graph-BrKKvSVx.js.map → graph-ozb0amP0.js.map} +1 -1
  87. package/webapp/dist/assets/{index-BV_dDe3L.js → index-Cyx7eev_.js} +700 -165
  88. package/webapp/dist/assets/{index-BV_dDe3L.js.map → index-Cyx7eev_.js.map} +1 -1
  89. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-k0Yv94VI.js → infoDiagram-LFFYTUFH-fLl_TA1F.js} +6 -6
  90. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-k0Yv94VI.js.map → infoDiagram-LFFYTUFH-fLl_TA1F.js.map} +1 -1
  91. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-C3EYBxjN.js → ishikawaDiagram-PHBUUO56-ZL9tBKUr.js} +2 -2
  92. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-C3EYBxjN.js.map → ishikawaDiagram-PHBUUO56-ZL9tBKUr.js.map} +1 -1
  93. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-BnSlOrbq.js → journeyDiagram-4ABVD52K--aRyymZs.js} +5 -5
  94. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-BnSlOrbq.js.map → journeyDiagram-4ABVD52K--aRyymZs.js.map} +1 -1
  95. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-CNYH0HDF.js → kanban-definition-K7BYSVSG-BO_QdW_O.js} +3 -3
  96. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-CNYH0HDF.js.map → kanban-definition-K7BYSVSG-BO_QdW_O.js.map} +1 -1
  97. package/webapp/dist/assets/{layout-NtmBC9CZ.js → layout-Bu3Xw0z2.js} +5 -5
  98. package/webapp/dist/assets/{layout-NtmBC9CZ.js.map → layout-Bu3Xw0z2.js.map} +1 -1
  99. package/webapp/dist/assets/{linear-BrqwApt9.js → linear-Bq77itJm.js} +2 -2
  100. package/webapp/dist/assets/{linear-BrqwApt9.js.map → linear-Bq77itJm.js.map} +1 -1
  101. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-DcknQb8H.js → mindmap-definition-YRQLILUH-CHB8qv8L.js} +4 -4
  102. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-DcknQb8H.js.map → mindmap-definition-YRQLILUH-CHB8qv8L.js.map} +1 -1
  103. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-wydKHXzN.js → pieDiagram-SKSYHLDU-Cxg_wh4K.js} +8 -8
  104. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-wydKHXzN.js.map → pieDiagram-SKSYHLDU-Cxg_wh4K.js.map} +1 -1
  105. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-D6i3VaFO.js → quadrantDiagram-337W2JSQ-DFguuaS9.js} +3 -3
  106. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-D6i3VaFO.js.map → quadrantDiagram-337W2JSQ-DFguuaS9.js.map} +1 -1
  107. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-C0sBwH6H.js → requirementDiagram-Z7DCOOCP--tJ_dfsT.js} +4 -4
  108. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-C0sBwH6H.js.map → requirementDiagram-Z7DCOOCP--tJ_dfsT.js.map} +1 -1
  109. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-33jPg4PK.js → sankeyDiagram-WA2Y5GQK-f0zWimMc.js} +2 -2
  110. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-33jPg4PK.js.map → sankeyDiagram-WA2Y5GQK-f0zWimMc.js.map} +1 -1
  111. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-CJsKHnh5.js → sequenceDiagram-2WXFIKYE-dwXRRnyq.js} +4 -4
  112. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-CJsKHnh5.js.map → sequenceDiagram-2WXFIKYE-dwXRRnyq.js.map} +1 -1
  113. package/webapp/dist/assets/{stateDiagram-RAJIS63D-B6A5jTdU.js → stateDiagram-RAJIS63D-DToxcEC2.js} +9 -9
  114. package/webapp/dist/assets/{stateDiagram-RAJIS63D-B6A5jTdU.js.map → stateDiagram-RAJIS63D-DToxcEC2.js.map} +1 -1
  115. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-6YIjpVKr.js → stateDiagram-v2-FVOUBMTO-BY5hDUqz.js} +5 -5
  116. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-6YIjpVKr.js.map → stateDiagram-v2-FVOUBMTO-BY5hDUqz.js.map} +1 -1
  117. package/webapp/dist/assets/{timeline-definition-YZTLITO2-BQS5KHfj.js → timeline-definition-YZTLITO2-CT3WRcFt.js} +3 -3
  118. package/webapp/dist/assets/{timeline-definition-YZTLITO2-BQS5KHfj.js.map → timeline-definition-YZTLITO2-CT3WRcFt.js.map} +1 -1
  119. package/webapp/dist/assets/{treemap-KZPCXAKY-DB1uUX8l.js → treemap-KZPCXAKY-Lnkh2bpd.js} +5 -5
  120. package/webapp/dist/assets/{treemap-KZPCXAKY-DB1uUX8l.js.map → treemap-KZPCXAKY-Lnkh2bpd.js.map} +1 -1
  121. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-BtcfWZJx.js → vennDiagram-LZ73GAT5-CYSLSh1w.js} +2 -2
  122. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-BtcfWZJx.js.map → vennDiagram-LZ73GAT5-CYSLSh1w.js.map} +1 -1
  123. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-CfIdcI74.js → xychartDiagram-JWTSCODW-DgvaqrGO.js} +3 -3
  124. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-CfIdcI74.js.map → xychartDiagram-JWTSCODW-DgvaqrGO.js.map} +1 -1
  125. package/webapp/dist/index.html +1 -1
@@ -82,7 +82,16 @@ function parseReminderRequest(value) {
82
82
  if (echoback === null) {
83
83
  throw new Error('Invalid app tool reminder request: upsert.echoback must be boolean');
84
84
  }
85
- return { kind: 'upsert', ownerRef, content, meta, position, echoback };
85
+ const renderModeRaw = value['renderMode'];
86
+ const renderMode = renderModeRaw === undefined
87
+ ? undefined
88
+ : renderModeRaw === 'plain' || renderModeRaw === 'markdown'
89
+ ? renderModeRaw
90
+ : null;
91
+ if (renderMode === null) {
92
+ throw new Error('Invalid app tool reminder request: upsert.renderMode must be plain|markdown');
93
+ }
94
+ return { kind: 'upsert', ownerRef, content, meta, position, echoback, renderMode };
86
95
  }
87
96
  throw new Error(`Invalid app tool reminder request kind: ${String(kind)}`);
88
97
  }
@@ -138,7 +147,9 @@ function isReminderState(value) {
138
147
  if (meta !== undefined && !isJsonValue(meta))
139
148
  return false;
140
149
  const echoback = value['echoback'];
141
- return echoback === undefined || typeof echoback === 'boolean';
150
+ const renderMode = value['renderMode'];
151
+ return ((echoback === undefined || typeof echoback === 'boolean') &&
152
+ (renderMode === undefined || renderMode === 'plain' || renderMode === 'markdown'));
142
153
  }
143
154
  function isReminderApplyResult(value) {
144
155
  if (!isRecord(value))
@@ -46,7 +46,15 @@ function parseReminderState(v, at) {
46
46
  const echoback = echobackRaw === undefined ? undefined : typeof echobackRaw === 'boolean' ? echobackRaw : null;
47
47
  if (echoback === null)
48
48
  throw new Error(`Invalid ${at}.echoback: must be boolean`);
49
- return { content, meta: metaRaw, echoback };
49
+ const renderModeRaw = v['renderMode'];
50
+ const renderMode = renderModeRaw === undefined
51
+ ? undefined
52
+ : renderModeRaw === 'plain' || renderModeRaw === 'markdown'
53
+ ? renderModeRaw
54
+ : null;
55
+ if (renderMode === null)
56
+ throw new Error(`Invalid ${at}.renderMode: must be plain|markdown`);
57
+ return { content, meta: metaRaw, echoback, renderMode };
50
58
  }
51
59
  function parseReminderApplyRequest(v, at) {
52
60
  if (!isRecord(v))
@@ -75,6 +83,14 @@ function parseReminderApplyRequest(v, at) {
75
83
  const echoback = echobackRaw === undefined ? undefined : typeof echobackRaw === 'boolean' ? echobackRaw : null;
76
84
  if (echoback === null)
77
85
  throw new Error(`Invalid ${at}.echoback: must be boolean`);
86
+ const renderModeRaw = v['renderMode'];
87
+ const renderMode = renderModeRaw === undefined
88
+ ? undefined
89
+ : renderModeRaw === 'plain' || renderModeRaw === 'markdown'
90
+ ? renderModeRaw
91
+ : null;
92
+ if (renderMode === null)
93
+ throw new Error(`Invalid ${at}.renderMode: must be plain|markdown`);
78
94
  return {
79
95
  kind: 'upsert',
80
96
  ownerRef,
@@ -82,6 +98,7 @@ function parseReminderApplyRequest(v, at) {
82
98
  meta: metaRaw,
83
99
  position,
84
100
  echoback,
101
+ renderMode,
85
102
  };
86
103
  }
87
104
  if (kind === 'delete') {
@@ -38,6 +38,7 @@ function cloneReminderSnapshot(snapshot) {
38
38
  meta: snapshot.meta,
39
39
  echoback: snapshot.echoback,
40
40
  scope: snapshot.scope ?? 'dialog',
41
+ renderMode: snapshot.renderMode ?? 'markdown',
41
42
  createdAt: snapshot.createdAt,
42
43
  priority: snapshot.priority,
43
44
  });
package/dist/dialog.js CHANGED
@@ -374,6 +374,7 @@ class Dialog {
374
374
  meta,
375
375
  echoback: options?.echoback,
376
376
  scope: options?.scope ?? 'dialog',
377
+ renderMode: options?.renderMode,
377
378
  });
378
379
  const insertIndex = position !== undefined ? position : this.reminders.length;
379
380
  if (insertIndex < 0 || insertIndex > this.reminders.length) {
@@ -405,6 +406,7 @@ class Dialog {
405
406
  scope: oldReminder.scope,
406
407
  createdAt: oldReminder.createdAt,
407
408
  priority: oldReminder.priority,
409
+ renderMode: options?.renderMode ?? oldReminder.renderMode,
408
410
  });
409
411
  this.reminders[index] = updatedReminder;
410
412
  this.touchReminders();
@@ -479,6 +481,7 @@ class Dialog {
479
481
  scope: reminder.scope,
480
482
  createdAt: reminder.createdAt,
481
483
  priority: reminder.priority,
484
+ renderMode: reminder.renderMode,
482
485
  });
483
486
  const contentChanged = updatedReminder.content !== reminder.content;
484
487
  const metaChanged = updatedReminder.meta !== reminder.meta;
@@ -539,8 +542,10 @@ class Dialog {
539
542
  content: r.content,
540
543
  meta: r.meta,
541
544
  reminder_id: r.id,
545
+ renderRevision: (0, tool_1.computeReminderRenderRevision)(r),
542
546
  echoback: (0, tool_1.reminderEchoBackEnabled)(r),
543
547
  scope: r.scope,
548
+ renderMode: r.renderMode ?? 'markdown',
544
549
  }));
545
550
  // Emit full_reminders_update event with complete reminder list including metadata
546
551
  const fullRemindersEvt = {
@@ -423,9 +423,12 @@ servers:
423
423
  # Tool name transforms (optional)
424
424
  transform: []
425
425
 
426
- # Optional per-toolset manual shown to agents
426
+ # Optional manual information
427
+ # - contentFile: formal runtime manual for man({ "toolsetId": "<serverId>" })
428
+ # - content/sections: extra inline team-management guidance
427
429
  # (missing manual does NOT make the toolset unavailable)
428
430
  manual:
431
+ contentFile: ".minds/manuals/<serverId>"
429
432
  content: "What this MCP toolset is for"
430
433
  sections:
431
434
  - title: "When To Use"
@@ -385,15 +385,15 @@ Notes:
385
385
  - MCP tool names are global across all toolsets (built-in + MCP). Collisions cause tools to be
386
386
  skipped and should surface via Problems + logs.
387
387
  - `mcp_admin` is a built-in toolset that contains `mcp_restart` (best-effort per-server restart).
388
- - Optional per-toolset manual can live in `.minds/mcp.yaml` at `servers.<serverId>.manual` using:
389
- - `content`: overview text
390
- - `sections`: chapterized guidance (`[{ title, content }]` or `{ "<title>": "<content>" }`)
388
+ - Optional manual information can live in `.minds/mcp.yaml` at `servers.<serverId>.manual`:
389
+ - `contentFile`: the topic-file directory prefix for the formal runtime manual; the final `man({ "toolsetId": "<serverId>" })` content shown to the LLM is loaded from here
390
+ - `content` / `sections`: inline team-management guidance shown in the `team_mgmt` MCP chapter (`sections` supports `[{ title, content }]` or `{ "<title>": "<content>" }`)
391
391
  - Missing manual does **not** mean the toolset is unavailable; it means team-manager documentation
392
392
  is incomplete. Agents should continue by reading each tool’s own description/arguments.
393
393
  - Team-manager recommendation: after MCP config validation passes, carefully read each exposed tool
394
- description, discuss intended rtws usage with the human user, then write
395
- `servers.<serverId>.manual` that captures typical usage patterns, primary intent directions, and
396
- unavailable-case business handling rules.
394
+ description, discuss intended rtws usage with the human user, then write the formal manual into
395
+ `manual.contentFile`; if extra team-management interpretation is still useful, add inline
396
+ `content + sections` alongside it.
397
397
  - Use a semi-structured chapter shape: high-value sections often include `When To Use`,
398
398
  `Guardrails`, and `Business Handling When Unavailable`, but do not force every toolset into one
399
399
  fixed template. Start from the real business goal, then decide which sections deserve depth,
@@ -431,8 +431,9 @@ servers:
431
431
  # Tool name transforms
432
432
  transform: [] # optional
433
433
 
434
- # Optional toolset manual for agents
434
+ # Optional manual information
435
435
  manual:
436
+ contentFile: ".minds/manuals/<serverId>" # formal runtime manual for man({ "toolsetId": "<serverId>" })
436
437
  content: "What this MCP toolset is for"
437
438
  sections:
438
439
  - title: "When To Use"
@@ -322,11 +322,11 @@ members:
322
322
 
323
323
  - MCP 工具名称在所有工具集(内置 + MCP)中是全局的。冲突导致工具被跳过,应通过问题 + 日志暴露
324
324
  - `mcp_admin` 是一个内置工具集,包含 `mcp_restart`(每个服务器最佳努力重启)
325
- - 可选手册:可在 `.minds/mcp.yaml` 的 `servers.<serverId>.manual` 为每个 MCP toolset 提供手册:
326
- - `content`:总说明
327
- - `sections`:章节化指导(`[{ title, content }]` 或 `{ "<title>": "<content>" }`)
325
+ - 可选手册:可在 `.minds/mcp.yaml` 的 `servers.<serverId>.manual` 提供手册相关信息:
326
+ - `contentFile`:正式 runtime 手册的 topic 文件目录前缀;`man({ "toolsetId": "<serverId>" })` 最终给 LLM 看的正文从这里加载
327
+ - `content` / `sections`:补充给 `team_mgmt` MCP 章节看的 inline 团队管理说明(`sections` 支持 `[{ title, content }]` 或 `{ "<title>": "<content>" }`)
328
328
  - 没有手册 **不代表** 该 toolset 不可用;这只表示团队管理文档覆盖不足。智能体应继续依据每个工具自身的 description/参数来使用。
329
- - 建议团队管理者在 MCP 配置验证通过后:先精读该 server 暴露的每个工具说明,再与人类用户讨论本 rtws 的使用意图,最后把“典型用法 + 主要意图方向 + 不可用时业务处置规约”沉淀为 `servers.<serverId>.manual`。
329
+ - 建议团队管理者在 MCP 配置验证通过后:先精读该 server 暴露的每个工具说明,再与人类用户讨论本 rtws 的使用意图,然后把正式手册沉淀到 `manual.contentFile`;如仍需额外写团队管理层解释,再补 inline `content + sections`。
330
330
  - 章节组织建议采用“半结构化”:可优先考虑 `何时使用`、`安全边界`、`不可用时业务处置` 这类高价值章节,但不要求所有 toolset 都照抄同一模板。应从真实业务目标出发,决定哪些章节需要展开、哪些只需一句话、哪些可以合并或改名成更贴切的标题。
331
331
  - 对每个 MCP toolset,建议刻意写明“不可用时业务处置规约”,至少回答:
332
332
  - 当前 toolset 暂不可达时,是否必须找协调者/专员接手
@@ -360,8 +360,9 @@ servers:
360
360
  # 工具名称转换
361
361
  transform: [] # 可选
362
362
 
363
- # 可选:给智能体看的 toolset 手册
363
+ # 可选:手册相关信息
364
364
  manual:
365
+ contentFile: ".minds/manuals/<serverId>" # 正式 runtime 手册(供 man({ "toolsetId": "<serverId>" }) 读取)
365
366
  content: "该 MCP toolset 的用途说明"
366
367
  sections:
367
368
  - title: "何时使用"
@@ -0,0 +1,33 @@
1
+ export type McpToolsetManualSection = {
2
+ title: string;
3
+ content: string;
4
+ };
5
+ export type McpToolsetManual = {
6
+ content?: string;
7
+ contentFile?: string;
8
+ sections: ReadonlyArray<McpToolsetManualSection>;
9
+ };
10
+ export type McpToolsetManualState = {
11
+ kind: 'missing';
12
+ } | {
13
+ kind: 'invalid';
14
+ errorText: string;
15
+ } | {
16
+ kind: 'present';
17
+ manual: McpToolsetManual;
18
+ };
19
+ export type McpToolsetManualByServer = {
20
+ manualByServerId: ReadonlyMap<string, McpToolsetManual>;
21
+ invalidByServerId: ReadonlyMap<string, string>;
22
+ warningTextByServerId: ReadonlyMap<string, string>;
23
+ };
24
+ export declare function emptyMcpToolsetManualByServer(): McpToolsetManualByServer;
25
+ export declare function mcpToolsetManualProblemPrefix(serverId: string): string;
26
+ export declare function mcpWorkspaceManualProblemPrefix(): string;
27
+ export declare function parseMcpManualByServer(rawText: string): McpToolsetManualByServer;
28
+ export declare function reconcileMcpToolsetManualProblems(params: {
29
+ serverIds: ReadonlyArray<string>;
30
+ manualInfo: McpToolsetManualByServer;
31
+ measureRenderedWorkspaceMcpTopicRawChars: () => Promise<number>;
32
+ workspaceManualPath: string;
33
+ }): Promise<void>;
@@ -0,0 +1,323 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.emptyMcpToolsetManualByServer = emptyMcpToolsetManualByServer;
7
+ exports.mcpToolsetManualProblemPrefix = mcpToolsetManualProblemPrefix;
8
+ exports.mcpWorkspaceManualProblemPrefix = mcpWorkspaceManualProblemPrefix;
9
+ exports.parseMcpManualByServer = parseMcpManualByServer;
10
+ exports.reconcileMcpToolsetManualProblems = reconcileMcpToolsetManualProblems;
11
+ const time_1 = require("@longrun-ai/kernel/utils/time");
12
+ const yaml_1 = __importDefault(require("yaml"));
13
+ const problems_1 = require("../problems");
14
+ const output_limit_1 = require("../tools/manual/output-limit");
15
+ const render_1 = require("../tools/manual/render");
16
+ const registry_1 = require("../tools/registry");
17
+ const MCP_SERVER_PROBLEM_PREFIX = 'mcp/server/';
18
+ function isObjectRecord(value) {
19
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
20
+ }
21
+ function isNonEmptyString(value) {
22
+ return typeof value === 'string' && value.trim() !== '';
23
+ }
24
+ function emptyMcpToolsetManualByServer() {
25
+ return {
26
+ manualByServerId: new Map(),
27
+ invalidByServerId: new Map(),
28
+ warningTextByServerId: new Map(),
29
+ };
30
+ }
31
+ function mcpToolsetManualProblemPrefix(serverId) {
32
+ return `${MCP_SERVER_PROBLEM_PREFIX}${serverId}/toolset_manual_`;
33
+ }
34
+ function mcpWorkspaceManualProblemPrefix() {
35
+ return 'mcp/workspace_manual_';
36
+ }
37
+ function parseMcpManualByServer(rawText) {
38
+ const manualByServerId = new Map();
39
+ const invalidByServerId = new Map();
40
+ const warningTextByServerId = new Map();
41
+ let parsedRoot;
42
+ try {
43
+ parsedRoot = yaml_1.default.parse(rawText);
44
+ }
45
+ catch {
46
+ return { manualByServerId, invalidByServerId, warningTextByServerId };
47
+ }
48
+ if (!isObjectRecord(parsedRoot)) {
49
+ return { manualByServerId, invalidByServerId, warningTextByServerId };
50
+ }
51
+ const serversVal = parsedRoot['servers'];
52
+ if (!isObjectRecord(serversVal)) {
53
+ return { manualByServerId, invalidByServerId, warningTextByServerId };
54
+ }
55
+ for (const [serverId, serverVal] of Object.entries(serversVal)) {
56
+ if (!isObjectRecord(serverVal))
57
+ continue;
58
+ const manualVal = serverVal['manual'];
59
+ if (manualVal === undefined || manualVal === null)
60
+ continue;
61
+ const parsed = parseMcpManualField(serverId, manualVal);
62
+ if (parsed.warningText !== undefined) {
63
+ warningTextByServerId.set(serverId, parsed.warningText);
64
+ }
65
+ if (parsed.kind === 'present') {
66
+ manualByServerId.set(serverId, parsed.manual);
67
+ continue;
68
+ }
69
+ invalidByServerId.set(serverId, parsed.errorText);
70
+ }
71
+ return { manualByServerId, invalidByServerId, warningTextByServerId };
72
+ }
73
+ function parseMcpManualField(serverId, value) {
74
+ const fieldPath = `servers.${serverId}.manual`;
75
+ if (typeof value === 'string') {
76
+ const text = value.trim();
77
+ if (text === '') {
78
+ return { kind: 'invalid', errorText: `${fieldPath} must not be empty` };
79
+ }
80
+ return { kind: 'present', manual: { content: text, sections: [] } };
81
+ }
82
+ if (!isObjectRecord(value)) {
83
+ return {
84
+ kind: 'invalid',
85
+ errorText: `${fieldPath} must be either a string, or an object with optional content, contentFile, sections`,
86
+ };
87
+ }
88
+ const supportedKeys = new Set(['content', 'contentFile', 'sections']);
89
+ const unknownKeys = Object.keys(value).filter((key) => !supportedKeys.has(key));
90
+ const warningText = unknownKeys.length > 0
91
+ ? `${fieldPath} contains unsupported extra field(s): ${unknownKeys
92
+ .map((key) => `${fieldPath}.${key}`)
93
+ .join(', ')} (only content, contentFile, sections are used)`
94
+ : undefined;
95
+ const contentVal = value['content'];
96
+ let content;
97
+ if (contentVal !== undefined) {
98
+ if (!isNonEmptyString(contentVal)) {
99
+ return {
100
+ kind: 'invalid',
101
+ errorText: `${fieldPath}.content must be a non-empty string when provided`,
102
+ ...(warningText !== undefined ? { warningText } : {}),
103
+ };
104
+ }
105
+ content = contentVal.trim();
106
+ }
107
+ const contentFileVal = value['contentFile'];
108
+ let contentFile;
109
+ if (contentFileVal !== undefined) {
110
+ if (!isNonEmptyString(contentFileVal)) {
111
+ return {
112
+ kind: 'invalid',
113
+ errorText: `${fieldPath}.contentFile must be a non-empty string when provided`,
114
+ ...(warningText !== undefined ? { warningText } : {}),
115
+ };
116
+ }
117
+ contentFile = contentFileVal.trim();
118
+ }
119
+ const sectionsVal = value['sections'];
120
+ const sections = [];
121
+ if (sectionsVal !== undefined) {
122
+ if (Array.isArray(sectionsVal)) {
123
+ for (let i = 0; i < sectionsVal.length; i++) {
124
+ const sectionVal = sectionsVal[i];
125
+ if (!isObjectRecord(sectionVal)) {
126
+ return {
127
+ kind: 'invalid',
128
+ errorText: `${fieldPath}.sections[${i}] must be an object with title/content non-empty strings`,
129
+ ...(warningText !== undefined ? { warningText } : {}),
130
+ };
131
+ }
132
+ const title = sectionVal['title'];
133
+ const sectionContent = sectionVal['content'];
134
+ if (!isNonEmptyString(title) || !isNonEmptyString(sectionContent)) {
135
+ return {
136
+ kind: 'invalid',
137
+ errorText: `${fieldPath}.sections[${i}] must provide non-empty title/content strings`,
138
+ ...(warningText !== undefined ? { warningText } : {}),
139
+ };
140
+ }
141
+ sections.push({ title: title.trim(), content: sectionContent.trim() });
142
+ }
143
+ }
144
+ else if (isObjectRecord(sectionsVal)) {
145
+ for (const [sectionTitle, sectionContent] of Object.entries(sectionsVal)) {
146
+ if (!isNonEmptyString(sectionTitle) || !isNonEmptyString(sectionContent)) {
147
+ return {
148
+ kind: 'invalid',
149
+ errorText: `${fieldPath}.sections object entries must be non-empty string -> string`,
150
+ ...(warningText !== undefined ? { warningText } : {}),
151
+ };
152
+ }
153
+ sections.push({ title: sectionTitle.trim(), content: sectionContent.trim() });
154
+ }
155
+ }
156
+ else {
157
+ return {
158
+ kind: 'invalid',
159
+ errorText: `${fieldPath}.sections must be either [{ title, content }] or { "<title>": "<content>" }`,
160
+ ...(warningText !== undefined ? { warningText } : {}),
161
+ };
162
+ }
163
+ }
164
+ if (content === undefined && contentFile === undefined && sections.length === 0) {
165
+ return {
166
+ kind: 'invalid',
167
+ errorText: `${fieldPath} must provide at least one of content, contentFile, or sections`,
168
+ ...(warningText !== undefined ? { warningText } : {}),
169
+ };
170
+ }
171
+ return {
172
+ kind: 'present',
173
+ manual: { content, contentFile, sections },
174
+ ...(warningText !== undefined ? { warningText } : {}),
175
+ };
176
+ }
177
+ function buildMcpToolsetManualInvalidProblem(serverId, errorText) {
178
+ return {
179
+ kind: 'mcp_server_error',
180
+ source: 'mcp',
181
+ id: `${mcpToolsetManualProblemPrefix(serverId)}error`,
182
+ severity: 'error',
183
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
184
+ message: `MCP server '${serverId}' manual declaration is invalid`,
185
+ messageI18n: {
186
+ en: `MCP server '${serverId}' manual declaration is invalid`,
187
+ zh: `MCP server '${serverId}' 的手册声明无效`,
188
+ },
189
+ detailTextI18n: {
190
+ en: errorText,
191
+ zh: errorText,
192
+ },
193
+ detail: { serverId, errorText },
194
+ };
195
+ }
196
+ function buildMcpToolsetManualUnknownFieldsProblem(serverId, warningText) {
197
+ return {
198
+ kind: 'mcp_server_error',
199
+ source: 'mcp',
200
+ id: `${mcpToolsetManualProblemPrefix(serverId)}unknown_fields`,
201
+ severity: 'warning',
202
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
203
+ message: `MCP server '${serverId}' manual declaration has unsupported extra fields`,
204
+ messageI18n: {
205
+ en: `MCP server '${serverId}' manual declaration has unsupported extra fields`,
206
+ zh: `MCP server '${serverId}' 的手册声明含有不支持的附加字段`,
207
+ },
208
+ detailTextI18n: {
209
+ en: `${warningText}\nThese extra fields are ignored by the manual loader. Keep only content, contentFile, and sections under \`servers.${serverId}.manual\`.`,
210
+ zh: `${warningText}\n这些附加字段会被手册加载器忽略。请仅在 \`servers.${serverId}.manual\` 下保留 content、contentFile、sections。`,
211
+ },
212
+ detail: { serverId, errorText: warningText },
213
+ };
214
+ }
215
+ function buildMcpToolsetManualTooLargeProblem(args) {
216
+ const errorText = `man({ "toolsetId": "${args.serverId}" }) rendered manual exceeds limit (${args.renderedChars} chars > ${args.limitChars} chars)`;
217
+ return {
218
+ kind: 'mcp_server_error',
219
+ source: 'mcp',
220
+ id: `${mcpToolsetManualProblemPrefix(args.serverId)}too_large`,
221
+ severity: 'error',
222
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
223
+ message: `MCP server '${args.serverId}' manual result is too large`,
224
+ messageI18n: {
225
+ en: `MCP server '${args.serverId}' manual result is too large`,
226
+ zh: `MCP server '${args.serverId}' 的手册结果过长`,
227
+ },
228
+ detailTextI18n: {
229
+ en: `${errorText}\nThe actual MCP toolset manual shown to the LLM is too large. Split the handbook by business topic so the default \`man({ "toolsetId": "${args.serverId}" })\` result stays within ${args.limitChars} chars.`,
230
+ zh: `\`man({ "toolsetId": "${args.serverId}" })\` 的实际手册结果过长(${args.renderedChars} chars > ${args.limitChars} chars)\n请按业务主题拆小手册,保证默认 \`man({ "toolsetId": "${args.serverId}" })\` 结果不超过 ${args.limitChars} chars。`,
231
+ },
232
+ detail: { serverId: args.serverId, errorText },
233
+ };
234
+ }
235
+ function buildMcpWorkspaceManualTooLargeProblem(args) {
236
+ const errorText = `man({ "toolsetId": "team_mgmt", "topics": ["mcp"] }) rendered manual exceeds limit (${args.renderedChars} chars > ${args.limitChars} chars)`;
237
+ return {
238
+ kind: 'mcp_workspace_config_error',
239
+ source: 'mcp',
240
+ id: `${mcpWorkspaceManualProblemPrefix()}team_mgmt_mcp_too_large`,
241
+ severity: 'error',
242
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
243
+ message: 'Rendered MCP handbook topic is too large',
244
+ messageI18n: {
245
+ en: 'Rendered MCP handbook topic is too large',
246
+ zh: 'MCP 手册主题的渲染结果过长',
247
+ },
248
+ detailTextI18n: {
249
+ en: `${errorText}\nThis check measures the final handbook content shown to the LLM. Split MCP handbook content so \`man({ "toolsetId": "team_mgmt", "topics": ["mcp"] })\` stays within ${args.limitChars} chars.`,
250
+ zh: `\`man({ "toolsetId": "team_mgmt", "topics": ["mcp"] })\` 的实际手册结果过长(${args.renderedChars} chars > ${args.limitChars} chars)\n该检查衡量的是最终给 LLM 看的手册内容。请拆小 MCP 手册内容,保证该结果不超过 ${args.limitChars} chars。`,
251
+ },
252
+ detail: { filePath: args.workspaceManualPath, errorText },
253
+ };
254
+ }
255
+ function measureRenderedToolsetManualRawChars(serverId) {
256
+ const tools = (0, registry_1.getToolset)(serverId);
257
+ const meta = (0, registry_1.getToolsetMeta)(serverId);
258
+ if (!tools || meta?.source !== 'mcp')
259
+ return null;
260
+ const availableToolNames = new Set(tools.map((tool) => tool.name));
261
+ const zh = (0, render_1.renderToolsetManual)({
262
+ toolsetId: serverId,
263
+ language: 'zh',
264
+ request: {},
265
+ availableToolNames,
266
+ });
267
+ const en = (0, render_1.renderToolsetManual)({
268
+ toolsetId: serverId,
269
+ language: 'en',
270
+ request: {},
271
+ availableToolNames,
272
+ });
273
+ if (!zh.foundToolset || !en.foundToolset)
274
+ return null;
275
+ return Math.max(zh.content.length, en.content.length);
276
+ }
277
+ async function reconcileMcpToolsetManualProblems(params) {
278
+ const desiredServerIds = new Set(params.serverIds);
279
+ const stalePrefixes = new Set();
280
+ for (const problem of (0, problems_1.listProblems)({ source: 'mcp' })) {
281
+ const match = problem.id.match(/^(mcp\/server\/[^/]+\/toolset_manual_)/);
282
+ if (!match || typeof match[1] !== 'string')
283
+ continue;
284
+ const serverId = problem.kind === 'mcp_server_error' ? problem.detail.serverId : null;
285
+ if (serverId !== null && desiredServerIds.has(serverId))
286
+ continue;
287
+ stalePrefixes.add(match[1]);
288
+ }
289
+ for (const prefix of stalePrefixes) {
290
+ (0, problems_1.reconcileProblemsByPrefix)(prefix, []);
291
+ }
292
+ const workspaceDesired = [];
293
+ const teamMgmtMcpTopicChars = await params.measureRenderedWorkspaceMcpTopicRawChars();
294
+ if (teamMgmtMcpTopicChars > output_limit_1.MANUAL_SINGLE_REQUEST_CHAR_LIMIT) {
295
+ workspaceDesired.push(buildMcpWorkspaceManualTooLargeProblem({
296
+ renderedChars: teamMgmtMcpTopicChars,
297
+ limitChars: output_limit_1.MANUAL_SINGLE_REQUEST_CHAR_LIMIT,
298
+ workspaceManualPath: params.workspaceManualPath,
299
+ }));
300
+ }
301
+ (0, problems_1.reconcileProblemsByPrefix)(mcpWorkspaceManualProblemPrefix(), workspaceDesired);
302
+ for (const serverId of params.serverIds) {
303
+ const desired = [];
304
+ const invalid = params.manualInfo.invalidByServerId.get(serverId);
305
+ if (invalid !== undefined) {
306
+ desired.push(buildMcpToolsetManualInvalidProblem(serverId, invalid));
307
+ }
308
+ const warningText = params.manualInfo.warningTextByServerId.get(serverId);
309
+ if (warningText !== undefined) {
310
+ desired.push(buildMcpToolsetManualUnknownFieldsProblem(serverId, warningText));
311
+ }
312
+ const renderedToolsetManualChars = measureRenderedToolsetManualRawChars(serverId);
313
+ if (renderedToolsetManualChars !== null &&
314
+ renderedToolsetManualChars > output_limit_1.MANUAL_SINGLE_REQUEST_CHAR_LIMIT) {
315
+ desired.push(buildMcpToolsetManualTooLargeProblem({
316
+ serverId,
317
+ renderedChars: renderedToolsetManualChars,
318
+ limitChars: output_limit_1.MANUAL_SINGLE_REQUEST_CHAR_LIMIT,
319
+ }));
320
+ }
321
+ (0, problems_1.reconcileProblemsByPrefix)(mcpToolsetManualProblemPrefix(serverId), desired);
322
+ }
323
+ }
@@ -51,7 +51,9 @@ const work_language_1 = require("../runtime/work-language");
51
51
  const tool_1 = require("../tool");
52
52
  const spec_1 = require("../tools/manual/spec");
53
53
  const registry_1 = require("../tools/registry");
54
+ const team_mgmt_mcp_manual_1 = require("../tools/team_mgmt-mcp-manual");
54
55
  const config_1 = require("./config");
56
+ const manual_problems_1 = require("./manual-problems");
55
57
  const sdk_client_1 = require("./sdk-client");
56
58
  const server_runtime_1 = require("./server-runtime");
57
59
  const tool_names_1 = require("./tool-names");
@@ -533,20 +535,24 @@ async function reloadNow(reason) {
533
535
  // Deletion is treated as empty config.
534
536
  await applyWorkspaceConfig({ version: 1, servers: {} }, [], [], [], `missing file (${reason})`);
535
537
  clearWorkspaceConfigProblem();
538
+ await reconcileMcpManualProblemsForRuntime([], null);
536
539
  return;
537
540
  }
538
541
  clearDeclaredServerRuntimeCatalog();
542
+ await reconcileMcpManualProblemsForRuntime([], null);
539
543
  upsertWorkspaceConfigProblem(`Failed to read ${MCP_YAML_PATH}: ${String(err)}`);
540
544
  return;
541
545
  }
542
546
  const parsed = (0, config_1.parseMcpYaml)(rawText);
543
547
  if (!parsed.ok) {
544
548
  clearDeclaredServerRuntimeCatalog();
549
+ await reconcileMcpManualProblemsForRuntime([], null);
545
550
  upsertWorkspaceConfigProblem(parsed.errorText);
546
551
  return;
547
552
  }
548
553
  clearWorkspaceConfigProblem();
549
554
  await applyWorkspaceConfig(parsed.config, parsed.invalidServers, parsed.serverIdsInYamlOrder, parsed.validServerIdsInYamlOrder, reason);
555
+ await reconcileMcpManualProblemsForRuntime(parsed.serverIdsInYamlOrder, rawText);
550
556
  }
551
557
  async function restartServerNow(serverId) {
552
558
  let rawText;
@@ -558,13 +564,16 @@ async function restartServerNow(serverId) {
558
564
  if (code === 'ENOENT') {
559
565
  // Deletion is treated as empty config, so restart cannot proceed.
560
566
  clearWorkspaceConfigProblem();
567
+ await reconcileMcpManualProblemsForRuntime([], null);
561
568
  return { ok: false, errorText: `Cannot restart '${serverId}': ${MCP_YAML_PATH} is missing` };
562
569
  }
570
+ await reconcileMcpManualProblemsForRuntime([], null);
563
571
  upsertWorkspaceConfigProblem(`Failed to read ${MCP_YAML_PATH}: ${String(err)}`);
564
572
  return { ok: false, errorText: `Failed to read ${MCP_YAML_PATH}: ${String(err)}` };
565
573
  }
566
574
  const parsed = (0, config_1.parseMcpYaml)(rawText);
567
575
  if (!parsed.ok) {
576
+ await reconcileMcpManualProblemsForRuntime([], null);
568
577
  upsertWorkspaceConfigProblem(parsed.errorText);
569
578
  return { ok: false, errorText: parsed.errorText };
570
579
  }
@@ -572,11 +581,13 @@ async function restartServerNow(serverId) {
572
581
  const invalid = parsed.invalidServers.find((s) => s.serverId === serverId);
573
582
  if (invalid) {
574
583
  replaceDeclaredServerRuntimeCatalog(parsed.config, parsed.invalidServers, parsed.serverIdsInYamlOrder);
584
+ await reconcileMcpManualProblemsForRuntime(parsed.serverIdsInYamlOrder, rawText);
575
585
  upsertMcpServerConfigInvalidProblem(serverId, invalid.errorText);
576
586
  return { ok: false, errorText: invalid.errorText };
577
587
  }
578
588
  const serverCfg = parsed.config.servers[serverId];
579
589
  if (!serverCfg) {
590
+ await reconcileMcpManualProblemsForRuntime(parsed.serverIdsInYamlOrder, rawText);
580
591
  return {
581
592
  ok: false,
582
593
  errorText: `MCP server '${serverId}' is not configured in ${MCP_YAML_PATH}`,
@@ -588,6 +599,7 @@ async function restartServerNow(serverId) {
588
599
  replaceDeclaredServerRuntimeCatalog(parsed.config, parsed.invalidServers, parsed.serverIdsInYamlOrder);
589
600
  const res = await tryBuildServerState(serverCfg, desiredToolsetName, fingerprint);
590
601
  if (!res.ok) {
602
+ await reconcileMcpManualProblemsForRuntime(parsed.serverIdsInYamlOrder, rawText);
591
603
  upsertDeclaredServerRuntimeError(serverId, res.errorText);
592
604
  upsertMcpServerRuntimeUnavailableProblem(serverId, res.errorText, res.detailTextI18n);
593
605
  return { ok: false, errorText: res.errorText };
@@ -602,8 +614,18 @@ async function restartServerNow(serverId) {
602
614
  serverStateById.set(serverId, res.state);
603
615
  (0, problems_1.reconcileProblemsByPrefix)(problemPrefixForServer(serverId), res.state.problems);
604
616
  reorderMcpToolsetsInRegistry(parsed.serverIdsInYamlOrder);
617
+ await reconcileMcpManualProblemsForRuntime(parsed.serverIdsInYamlOrder, rawText);
605
618
  return { ok: true };
606
619
  }
620
+ async function reconcileMcpManualProblemsForRuntime(serverIdsInYamlOrder, rawText) {
621
+ const manualInfo = rawText === null ? (0, manual_problems_1.emptyMcpToolsetManualByServer)() : (0, manual_problems_1.parseMcpManualByServer)(rawText);
622
+ await (0, manual_problems_1.reconcileMcpToolsetManualProblems)({
623
+ serverIds: serverIdsInYamlOrder,
624
+ manualInfo,
625
+ measureRenderedWorkspaceMcpTopicRawChars: team_mgmt_mcp_manual_1.measureRenderedTeamMgmtMcpTopicRawChars,
626
+ workspaceManualPath: MCP_YAML_PATH,
627
+ });
628
+ }
607
629
  function upsertWorkspaceConfigProblem(errorText) {
608
630
  (0, problems_1.upsertProblem)({
609
631
  kind: 'mcp_workspace_config_error',
@@ -707,7 +729,7 @@ async function applyWorkspaceConfig(config, invalidServers, serverIdsInYamlOrder
707
729
  unregisterServer(state);
708
730
  state.dispatch.requestStop();
709
731
  serverStateById.delete(serverId);
710
- (0, problems_1.removeProblemsByPrefix)(problemPrefixForServer(serverId));
732
+ (0, problems_1.reconcileProblemsByPrefix)(problemPrefixForServer(serverId), []);
711
733
  }
712
734
  // Surface invalid server config errors (while keeping last-known-good runtimes registered).
713
735
  for (const s of invalidServers) {