dominds 1.9.2 → 1.9.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 (134) hide show
  1. package/dist/docs/memory-system.md +8 -1
  2. package/dist/docs/memory-system.zh.md +8 -1
  3. package/dist/docs/txt-editing-tools.md +2 -0
  4. package/dist/docs/txt-editing-tools.zh.md +1 -0
  5. package/dist/llm/kernel-driver/tellask-special.js +1 -3
  6. package/dist/minds/load.js +1 -1
  7. package/dist/minds/system-prompt-parts.js +7 -4
  8. package/dist/minds/system-prompt.js +2 -0
  9. package/dist/runtime/driver-messages.js +34 -35
  10. package/dist/runtime/inter-dialog-format.d.ts +10 -0
  11. package/dist/runtime/inter-dialog-format.js +21 -0
  12. package/dist/server/websocket-handler.js +21 -3
  13. package/dist/tools/app-reminders.js +50 -21
  14. package/dist/tools/builtins.js +34 -10
  15. package/dist/tools/ctrl.js +38 -8
  16. package/dist/tools/manual/spec.js +5 -6
  17. package/dist/tools/os.js +133 -32
  18. package/dist/tools/pending-tellask-reminder.js +36 -13
  19. package/dist/tools/plan.js +5 -4
  20. package/dist/tools/prompts/os/en/tools.md +7 -0
  21. package/dist/tools/prompts/os/zh/tools.md +7 -0
  22. package/dist/tools/prompts/team_mgmt/en/index.md +1 -0
  23. package/dist/tools/prompts/team_mgmt/en/principles.md +1 -0
  24. package/dist/tools/prompts/team_mgmt/en/scenarios.md +2 -0
  25. package/dist/tools/prompts/team_mgmt/zh/index.md +1 -0
  26. package/dist/tools/prompts/team_mgmt/zh/principles.md +1 -0
  27. package/dist/tools/prompts/team_mgmt/zh/scenarios.md +2 -0
  28. package/dist/tools/prompts/ws_mod/en/index.md +1 -0
  29. package/dist/tools/prompts/ws_mod/en/principles.md +1 -0
  30. package/dist/tools/prompts/ws_mod/zh/index.md +1 -0
  31. package/dist/tools/prompts/ws_mod/zh/principles.md +1 -0
  32. package/dist/tools/prompts/ws_mod.en.md +4 -0
  33. package/dist/tools/prompts/ws_mod.zh.md +4 -0
  34. package/dist/tools/team_mgmt.js +2 -2
  35. package/dist/tools/toolset-manual.d.ts +1 -1
  36. package/dist/tools/toolset-manual.js +73 -18
  37. package/dist/tools/txt.js +21 -11
  38. package/dist/utils/taskdoc.js +2 -0
  39. package/package.json +2 -2
  40. package/webapp/dist/assets/{_basePickBy-9Z1lbVTN.js → _basePickBy-D694K6Gh.js} +3 -3
  41. package/webapp/dist/assets/{_basePickBy-9Z1lbVTN.js.map → _basePickBy-D694K6Gh.js.map} +1 -1
  42. package/webapp/dist/assets/{_baseUniq-C3pRnD57.js → _baseUniq-ArneQk-G.js} +2 -2
  43. package/webapp/dist/assets/{_baseUniq-C3pRnD57.js.map → _baseUniq-ArneQk-G.js.map} +1 -1
  44. package/webapp/dist/assets/{arc-BvNng3O_.js → arc-8u9ipLr2.js} +2 -2
  45. package/webapp/dist/assets/{arc-BvNng3O_.js.map → arc-8u9ipLr2.js.map} +1 -1
  46. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-BF8vwQ4e.js → architectureDiagram-2XIMDMQ5-DFV-xGAa.js} +7 -7
  47. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-BF8vwQ4e.js.map → architectureDiagram-2XIMDMQ5-DFV-xGAa.js.map} +1 -1
  48. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-C7kfjZhf.js → blockDiagram-WCTKOSBZ-pk6LU06K.js} +7 -7
  49. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-C7kfjZhf.js.map → blockDiagram-WCTKOSBZ-pk6LU06K.js.map} +1 -1
  50. package/webapp/dist/assets/{c4Diagram-IC4MRINW-CaJ6bX75.js → c4Diagram-IC4MRINW-D4SBLYjK.js} +3 -3
  51. package/webapp/dist/assets/{c4Diagram-IC4MRINW-CaJ6bX75.js.map → c4Diagram-IC4MRINW-D4SBLYjK.js.map} +1 -1
  52. package/webapp/dist/assets/{channel-DDuIUO0R.js → channel-D8M5j8c2.js} +2 -2
  53. package/webapp/dist/assets/{channel-DDuIUO0R.js.map → channel-D8M5j8c2.js.map} +1 -1
  54. package/webapp/dist/assets/{chunk-4BX2VUAB-BQPkQh6v.js → chunk-4BX2VUAB-B-6IvMRI.js} +2 -2
  55. package/webapp/dist/assets/{chunk-4BX2VUAB-BQPkQh6v.js.map → chunk-4BX2VUAB-B-6IvMRI.js.map} +1 -1
  56. package/webapp/dist/assets/{chunk-55IACEB6-C2eZTkZ1.js → chunk-55IACEB6-DjEvYtPp.js} +2 -2
  57. package/webapp/dist/assets/{chunk-55IACEB6-C2eZTkZ1.js.map → chunk-55IACEB6-DjEvYtPp.js.map} +1 -1
  58. package/webapp/dist/assets/{chunk-FMBD7UC4-aXp5zApD.js → chunk-FMBD7UC4-CrfwnW9e.js} +2 -2
  59. package/webapp/dist/assets/{chunk-FMBD7UC4-aXp5zApD.js.map → chunk-FMBD7UC4-CrfwnW9e.js.map} +1 -1
  60. package/webapp/dist/assets/{chunk-JSJVCQXG-DXyaccrz.js → chunk-JSJVCQXG-DroABPma.js} +2 -2
  61. package/webapp/dist/assets/{chunk-JSJVCQXG-DXyaccrz.js.map → chunk-JSJVCQXG-DroABPma.js.map} +1 -1
  62. package/webapp/dist/assets/{chunk-KX2RTZJC-C-u000c0.js → chunk-KX2RTZJC--UaFwmvv.js} +2 -2
  63. package/webapp/dist/assets/{chunk-KX2RTZJC-C-u000c0.js.map → chunk-KX2RTZJC--UaFwmvv.js.map} +1 -1
  64. package/webapp/dist/assets/{chunk-NQ4KR5QH-BoYRoo4A.js → chunk-NQ4KR5QH-hGQ1BQy3.js} +4 -4
  65. package/webapp/dist/assets/{chunk-NQ4KR5QH-BoYRoo4A.js.map → chunk-NQ4KR5QH-hGQ1BQy3.js.map} +1 -1
  66. package/webapp/dist/assets/{chunk-QZHKN3VN-BAc_2ceG.js → chunk-QZHKN3VN-L1f-RVf4.js} +2 -2
  67. package/webapp/dist/assets/{chunk-QZHKN3VN-BAc_2ceG.js.map → chunk-QZHKN3VN-L1f-RVf4.js.map} +1 -1
  68. package/webapp/dist/assets/{chunk-WL4C6EOR-D6ws69Jx.js → chunk-WL4C6EOR-BS6MGl-Y.js} +6 -6
  69. package/webapp/dist/assets/{chunk-WL4C6EOR-D6ws69Jx.js.map → chunk-WL4C6EOR-BS6MGl-Y.js.map} +1 -1
  70. package/webapp/dist/assets/{classDiagram-VBA2DB6C-BIRrKhqs.js → classDiagram-VBA2DB6C-wDVeUi90.js} +7 -7
  71. package/webapp/dist/assets/{classDiagram-VBA2DB6C-BIRrKhqs.js.map → classDiagram-VBA2DB6C-wDVeUi90.js.map} +1 -1
  72. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-BIRrKhqs.js → classDiagram-v2-RAHNMMFH-wDVeUi90.js} +7 -7
  73. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-BIRrKhqs.js.map → classDiagram-v2-RAHNMMFH-wDVeUi90.js.map} +1 -1
  74. package/webapp/dist/assets/{clone-DweFyVW6.js → clone-C2MtRhfG.js} +2 -2
  75. package/webapp/dist/assets/{clone-DweFyVW6.js.map → clone-C2MtRhfG.js.map} +1 -1
  76. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-vgovmGKM.js → cose-bilkent-S5V4N54A-CEf3Y19l.js} +2 -2
  77. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-vgovmGKM.js.map → cose-bilkent-S5V4N54A-CEf3Y19l.js.map} +1 -1
  78. package/webapp/dist/assets/{dagre-KLK3FWXG-B-Ax3qBf.js → dagre-KLK3FWXG-Dy_y44Tz.js} +7 -7
  79. package/webapp/dist/assets/{dagre-KLK3FWXG-B-Ax3qBf.js.map → dagre-KLK3FWXG-Dy_y44Tz.js.map} +1 -1
  80. package/webapp/dist/assets/{diagram-E7M64L7V-B1sUlvtn.js → diagram-E7M64L7V-Bvv5WU0i.js} +8 -8
  81. package/webapp/dist/assets/{diagram-E7M64L7V-B1sUlvtn.js.map → diagram-E7M64L7V-Bvv5WU0i.js.map} +1 -1
  82. package/webapp/dist/assets/{diagram-IFDJBPK2-3IlyK_xp.js → diagram-IFDJBPK2-DgKslRxt.js} +7 -7
  83. package/webapp/dist/assets/{diagram-IFDJBPK2-3IlyK_xp.js.map → diagram-IFDJBPK2-DgKslRxt.js.map} +1 -1
  84. package/webapp/dist/assets/{diagram-P4PSJMXO-C3x0JkgF.js → diagram-P4PSJMXO-DKuN0W-K.js} +7 -7
  85. package/webapp/dist/assets/{diagram-P4PSJMXO-C3x0JkgF.js.map → diagram-P4PSJMXO-DKuN0W-K.js.map} +1 -1
  86. package/webapp/dist/assets/{erDiagram-INFDFZHY-mEp6kC6i.js → erDiagram-INFDFZHY-Buc2e1H3.js} +5 -5
  87. package/webapp/dist/assets/{erDiagram-INFDFZHY-mEp6kC6i.js.map → erDiagram-INFDFZHY-Buc2e1H3.js.map} +1 -1
  88. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-AsG_wdLN.js → flowDiagram-PKNHOUZH-J69A4hB0.js} +7 -7
  89. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-AsG_wdLN.js.map → flowDiagram-PKNHOUZH-J69A4hB0.js.map} +1 -1
  90. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-zv3UhEAO.js → ganttDiagram-A5KZAMGK-B_EUDSCl.js} +3 -3
  91. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-zv3UhEAO.js.map → ganttDiagram-A5KZAMGK-B_EUDSCl.js.map} +1 -1
  92. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-C1yyISy7.js → gitGraphDiagram-K3NZZRJ6-B1111A11.js} +8 -8
  93. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-C1yyISy7.js.map → gitGraphDiagram-K3NZZRJ6-B1111A11.js.map} +1 -1
  94. package/webapp/dist/assets/{graph-CkQycYq_.js → graph-CdxqCYb0.js} +3 -3
  95. package/webapp/dist/assets/{graph-CkQycYq_.js.map → graph-CdxqCYb0.js.map} +1 -1
  96. package/webapp/dist/assets/{index-CCGi5dDE.js → index-BHe1oHIa.js} +181 -49
  97. package/webapp/dist/assets/{index-CCGi5dDE.js.map → index-BHe1oHIa.js.map} +1 -1
  98. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-Dw1wAgpl.js → infoDiagram-LFFYTUFH-B0NHDULu.js} +6 -6
  99. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-Dw1wAgpl.js.map → infoDiagram-LFFYTUFH-B0NHDULu.js.map} +1 -1
  100. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-DY9fHLYz.js → ishikawaDiagram-PHBUUO56-zFZuCrGV.js} +2 -2
  101. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-DY9fHLYz.js.map → ishikawaDiagram-PHBUUO56-zFZuCrGV.js.map} +1 -1
  102. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-CSGjRykr.js → journeyDiagram-4ABVD52K-C-Y3JIvI.js} +5 -5
  103. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-CSGjRykr.js.map → journeyDiagram-4ABVD52K-C-Y3JIvI.js.map} +1 -1
  104. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-DDCQetMc.js → kanban-definition-K7BYSVSG-CAWm_s77.js} +3 -3
  105. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-DDCQetMc.js.map → kanban-definition-K7BYSVSG-CAWm_s77.js.map} +1 -1
  106. package/webapp/dist/assets/{layout-DpuTr6cl.js → layout-DAf8AacP.js} +5 -5
  107. package/webapp/dist/assets/{layout-DpuTr6cl.js.map → layout-DAf8AacP.js.map} +1 -1
  108. package/webapp/dist/assets/{linear-BQv5LuhW.js → linear-5V_bWmGQ.js} +2 -2
  109. package/webapp/dist/assets/{linear-BQv5LuhW.js.map → linear-5V_bWmGQ.js.map} +1 -1
  110. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-C5ajM9Hr.js → mindmap-definition-YRQLILUH-CbZFlmPZ.js} +4 -4
  111. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-C5ajM9Hr.js.map → mindmap-definition-YRQLILUH-CbZFlmPZ.js.map} +1 -1
  112. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-Daqms3DT.js → pieDiagram-SKSYHLDU-BdjfUPSq.js} +8 -8
  113. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-Daqms3DT.js.map → pieDiagram-SKSYHLDU-BdjfUPSq.js.map} +1 -1
  114. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-BMNWp18J.js → quadrantDiagram-337W2JSQ-B_roS40E.js} +3 -3
  115. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-BMNWp18J.js.map → quadrantDiagram-337W2JSQ-B_roS40E.js.map} +1 -1
  116. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-C1Mc4MX-.js → requirementDiagram-Z7DCOOCP-Cte4tBaS.js} +4 -4
  117. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-C1Mc4MX-.js.map → requirementDiagram-Z7DCOOCP-Cte4tBaS.js.map} +1 -1
  118. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-DTHGhOsp.js → sankeyDiagram-WA2Y5GQK-_7BuCxMO.js} +2 -2
  119. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-DTHGhOsp.js.map → sankeyDiagram-WA2Y5GQK-_7BuCxMO.js.map} +1 -1
  120. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-C6cA5ncc.js → sequenceDiagram-2WXFIKYE-DIrNFVXO.js} +4 -4
  121. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-C6cA5ncc.js.map → sequenceDiagram-2WXFIKYE-DIrNFVXO.js.map} +1 -1
  122. package/webapp/dist/assets/{stateDiagram-RAJIS63D-DRDqVPKY.js → stateDiagram-RAJIS63D-CleFEXbm.js} +9 -9
  123. package/webapp/dist/assets/{stateDiagram-RAJIS63D-DRDqVPKY.js.map → stateDiagram-RAJIS63D-CleFEXbm.js.map} +1 -1
  124. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-B-pby7Ke.js → stateDiagram-v2-FVOUBMTO-BPhDPWWG.js} +5 -5
  125. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-B-pby7Ke.js.map → stateDiagram-v2-FVOUBMTO-BPhDPWWG.js.map} +1 -1
  126. package/webapp/dist/assets/{timeline-definition-YZTLITO2-C-m1bG_Z.js → timeline-definition-YZTLITO2-DrrU-OCU.js} +3 -3
  127. package/webapp/dist/assets/{timeline-definition-YZTLITO2-C-m1bG_Z.js.map → timeline-definition-YZTLITO2-DrrU-OCU.js.map} +1 -1
  128. package/webapp/dist/assets/{treemap-KZPCXAKY-BWvLuAyU.js → treemap-KZPCXAKY-DShBAWxS.js} +5 -5
  129. package/webapp/dist/assets/{treemap-KZPCXAKY-BWvLuAyU.js.map → treemap-KZPCXAKY-DShBAWxS.js.map} +1 -1
  130. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-C8gZYuUq.js → vennDiagram-LZ73GAT5-DNQztUY0.js} +2 -2
  131. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-C8gZYuUq.js.map → vennDiagram-LZ73GAT5-DNQztUY0.js.map} +1 -1
  132. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-Bb3azvDM.js → xychartDiagram-JWTSCODW-q8D6fe1E.js} +3 -3
  133. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-Bb3azvDM.js.map → xychartDiagram-JWTSCODW-q8D6fe1E.js.map} +1 -1
  134. package/webapp/dist/index.html +1 -1
@@ -90,6 +90,35 @@ function isJsonValue(value) {
90
90
  function isJsonObject(value) {
91
91
  return isRecord(value) && Object.values(value).every((item) => isJsonValue(item));
92
92
  }
93
+ function getManagerTool(meta) {
94
+ if (!isRecord(meta)) {
95
+ return undefined;
96
+ }
97
+ const manager = meta['manager'];
98
+ if (!isRecord(manager)) {
99
+ return undefined;
100
+ }
101
+ const tool = manager['tool'];
102
+ return typeof tool === 'string' && tool.trim().length > 0 ? tool.trim() : undefined;
103
+ }
104
+ function getDeleteAltInstruction(meta) {
105
+ if (!isRecord(meta)) {
106
+ return undefined;
107
+ }
108
+ const deleteValue = meta['delete'];
109
+ if (!isRecord(deleteValue)) {
110
+ return undefined;
111
+ }
112
+ const altInstruction = deleteValue['altInstruction'];
113
+ return typeof altInstruction === 'string' && altInstruction.trim().length > 0
114
+ ? altInstruction.trim()
115
+ : undefined;
116
+ }
117
+ function formatManualDeleteBlockedError(language, altInstruction) {
118
+ return language === 'zh'
119
+ ? `错误:该提醒项不能用 delete_reminder 删除;请改为执行:${altInstruction}`
120
+ : `Error: This reminder cannot be deleted via delete_reminder. Use instead: ${altInstruction}`;
121
+ }
93
122
  function listNumberedReminderIndices(reminders) {
94
123
  const indices = [];
95
124
  for (let index = 0; index < reminders.length; index += 1) {
@@ -289,6 +318,10 @@ exports.deleteReminderTool = {
289
318
  if (targetIndex < 0) {
290
319
  return t.reminderDoesNotExist(String(reminderNoValue), numberedReminders.length);
291
320
  }
321
+ const deleteAltInstruction = getDeleteAltInstruction(targetReminder.meta);
322
+ if (deleteAltInstruction !== undefined) {
323
+ return formatManualDeleteBlockedError(language, deleteAltInstruction);
324
+ }
292
325
  dlg.deleteReminder(targetIndex);
293
326
  return (0, tool_result_messages_1.formatToolActionResult)(language, 'deleted');
294
327
  },
@@ -390,14 +423,11 @@ exports.updateReminderTool = {
390
423
  // `reminder.meta` is persisted JSON. Runtime shape checks are unavoidable here because tools
391
424
  // may attach arbitrary metadata for reminder ownership/management.
392
425
  const meta = reminder?.meta;
393
- if (isRecord(meta)) {
394
- const managedByToolValue = meta['managedByTool'];
395
- const managedByTool = typeof managedByToolValue === 'string' ? managedByToolValue.trim() : undefined;
396
- if (managedByTool && managedByTool.length > 0) {
397
- return language === 'zh'
398
- ? `错误:该提醒项由工具 ${managedByTool} 管理,不能用 update_reminder 修改;请使用 ${managedByTool} 更新。`
399
- : `Error: This reminder is managed by tool ${managedByTool}. Do not edit it via update_reminder; use ${managedByTool} instead.`;
400
- }
426
+ const managerTool = getManagerTool(meta);
427
+ if (managerTool !== undefined) {
428
+ return language === 'zh'
429
+ ? `错误:该提醒项由工具 ${managerTool} 管理,不能用 update_reminder 修改;请使用 ${managerTool} 更新。`
430
+ : `Error: This reminder is managed by tool ${managerTool}. Do not edit it via update_reminder; use ${managerTool} instead.`;
401
431
  }
402
432
  const contentValue = args['content'];
403
433
  const reminderContent = typeof contentValue === 'string' ? contentValue.trim() : '';
@@ -82,15 +82,14 @@ function shouldIncludeSchemaToolsSection(spec) {
82
82
  * No heuristics — `toolsetId` is an explicit parameter.
83
83
  */
84
84
  function builtinManualTopicPaths(toolsetId, language) {
85
- const suffix = language === 'en' ? '.en' : '';
86
85
  const langDir = language;
87
86
  const baseDir = `prompts/${toolsetId}`;
88
87
  return {
89
- index: path.join(baseDir, langDir, `index${suffix}.md`),
90
- principles: path.join(baseDir, langDir, `principles${suffix}.md`),
91
- tools: path.join(baseDir, langDir, `tools${suffix}.md`),
92
- scenarios: path.join(baseDir, langDir, `scenarios${suffix}.md`),
93
- errors: path.join(baseDir, langDir, `errors${suffix}.md`),
88
+ index: path.join(baseDir, langDir, 'index.md'),
89
+ principles: path.join(baseDir, langDir, 'principles.md'),
90
+ tools: path.join(baseDir, langDir, 'tools.md'),
91
+ scenarios: path.join(baseDir, langDir, 'scenarios.md'),
92
+ errors: path.join(baseDir, langDir, 'errors.md'),
94
93
  };
95
94
  }
96
95
  /**
package/dist/tools/os.js CHANGED
@@ -132,6 +132,55 @@ class HeadTailByteBuffer {
132
132
  }
133
133
  // Global registry for daemon processes
134
134
  const daemonProcesses = new Map();
135
+ let trackedDaemonShutdownSigtermSent = false;
136
+ let trackedDaemonShutdownSigkillSent = false;
137
+ function resolveBestEffortDaemonSignalTarget(daemon) {
138
+ if (process.platform !== 'win32' && daemon.processGroupId !== undefined) {
139
+ return -daemon.processGroupId;
140
+ }
141
+ return daemon.pid;
142
+ }
143
+ function signalTrackedDaemonsForProcessShutdown(signal) {
144
+ const alreadySent = signal === 'SIGTERM' ? trackedDaemonShutdownSigtermSent : trackedDaemonShutdownSigkillSent;
145
+ if (alreadySent)
146
+ return;
147
+ if (signal === 'SIGTERM') {
148
+ trackedDaemonShutdownSigtermSent = true;
149
+ }
150
+ else {
151
+ trackedDaemonShutdownSigkillSent = true;
152
+ }
153
+ for (const daemon of daemonProcesses.values()) {
154
+ const signalTarget = resolveBestEffortDaemonSignalTarget(daemon);
155
+ try {
156
+ process.kill(signalTarget, signal);
157
+ }
158
+ catch (error) {
159
+ console.error('[os] failed to signal tracked daemon during process shutdown', {
160
+ pid: daemon.pid,
161
+ processGroupId: daemon.processGroupId ?? null,
162
+ signal,
163
+ error: error instanceof Error ? error.message : String(error),
164
+ });
165
+ }
166
+ }
167
+ if (signal === 'SIGKILL') {
168
+ daemonProcesses.clear();
169
+ }
170
+ }
171
+ process.once('beforeExit', () => {
172
+ signalTrackedDaemonsForProcessShutdown('SIGTERM');
173
+ });
174
+ process.once('exit', () => {
175
+ signalTrackedDaemonsForProcessShutdown('SIGTERM');
176
+ signalTrackedDaemonsForProcessShutdown('SIGKILL');
177
+ });
178
+ process.once('SIGINT', () => {
179
+ signalTrackedDaemonsForProcessShutdown('SIGTERM');
180
+ });
181
+ process.once('SIGTERM', () => {
182
+ signalTrackedDaemonsForProcessShutdown('SIGTERM');
183
+ });
135
184
  function getOsToolMessages(language) {
136
185
  if (language === 'zh') {
137
186
  return {
@@ -249,7 +298,17 @@ function parseStopDaemonArgs(args) {
249
298
  if (typeof pid !== 'number') {
250
299
  throw new Error('stop_daemon.pid must be a number');
251
300
  }
252
- return { pid };
301
+ const entirePg = args.entire_pg;
302
+ if (entirePg !== undefined && typeof entirePg !== 'boolean') {
303
+ throw new Error('stop_daemon.entire_pg must be a boolean if provided');
304
+ }
305
+ if (process.platform === 'win32' && entirePg === true) {
306
+ throw new Error('stop_daemon.entire_pg=true is unsupported on Windows');
307
+ }
308
+ return {
309
+ pid,
310
+ entirePg: entirePg ?? process.platform !== 'win32',
311
+ };
253
312
  }
254
313
  function parseGetDaemonOutputArgs(args) {
255
314
  const pid = args.pid;
@@ -356,6 +415,10 @@ const stopDaemonSchema = {
356
415
  type: 'number',
357
416
  description: 'Process ID of the daemon to stop',
358
417
  },
418
+ entire_pg: {
419
+ type: 'boolean',
420
+ description: 'Whether to signal the entire process group instead of only the tracked PID (default: true on Unix-like systems; false on Windows)',
421
+ },
359
422
  },
360
423
  required: ['pid'],
361
424
  additionalProperties: false,
@@ -377,44 +440,70 @@ const getDaemonOutputSchema = {
377
440
  additionalProperties: false,
378
441
  };
379
442
  // Format daemon status for reminder display
380
- function formatDaemonStatus(daemon) {
443
+ function formatDaemonStatus(daemon, language) {
381
444
  const uptime = Math.floor((Date.now() - daemon.startTime.getTime()) / 1000);
382
- const status = daemon.isRunning
383
- ? 'running'
384
- : `exited (code: ${daemon.exitCode}, signal: ${daemon.exitSignal})`;
445
+ const status = language === 'zh'
446
+ ? daemon.isRunning
447
+ ? '运行中'
448
+ : `已退出(code: ${daemon.exitCode}, signal: ${daemon.exitSignal})`
449
+ : daemon.isRunning
450
+ ? 'running'
451
+ : `exited (code: ${daemon.exitCode}, signal: ${daemon.exitSignal})`;
385
452
  const stdoutInfo = daemon.stdoutBuffer.getScrollInfo();
386
453
  const stderrInfo = daemon.stderrBuffer.getScrollInfo();
387
454
  let scrollNotice = '';
388
455
  if (stdoutInfo.hasScrolledContent || stderrInfo.hasScrolledContent) {
389
456
  const scrolledLines = stdoutInfo.linesScrolledOut + stderrInfo.linesScrolledOut;
390
- scrollNotice = `\n⚠️ ${scrolledLines} lines have scrolled out of view`;
457
+ scrollNotice =
458
+ language === 'zh'
459
+ ? `\n注意:已有 ${scrolledLines} 行滚出当前保留缓冲区`
460
+ : `\nNote: ${scrolledLines} lines have scrolled out of the retained buffer`;
391
461
  }
392
462
  const stdoutContent = daemon.stdoutBuffer.isEmpty()
393
- ? '(no output)'
463
+ ? language === 'zh'
464
+ ? '(无输出)'
465
+ : '(no output)'
394
466
  : daemon.stdoutBuffer.getContent();
395
467
  const stderrContent = daemon.stderrBuffer.isEmpty()
396
- ? '(no errors)'
468
+ ? language === 'zh'
469
+ ? '(无 stderr 输出)'
470
+ : '(no stderr output)'
397
471
  : daemon.stderrBuffer.getContent();
398
472
  const fenceConsole = '```console';
399
473
  const fenceEnd = '```';
400
- return `🔄 Daemon Process ${daemon.pid}
401
- Command: ${daemon.command}
474
+ return language === 'zh'
475
+ ? `后台进程 PID: ${daemon.pid}
476
+ 命令: ${daemon.command}
402
477
  Shell: ${daemon.shell}
403
- Status: ${status}
404
- Uptime: ${uptime}s
405
- Started: ${(0, time_1.formatUnifiedTimestamp)(daemon.startTime)}${scrollNotice}
478
+ 生命周期状态: ${status}
479
+ 已运行: ${uptime}s
480
+ 启动时间: ${(0, time_1.formatUnifiedTimestamp)(daemon.startTime)}${scrollNotice}
406
481
 
407
- 📤 Latest stdout:
482
+ stdout 缓冲区快照:
408
483
  ${fenceConsole}
409
484
  ${stdoutContent}
410
485
  ${fenceEnd}
411
486
 
412
- 📤 Latest stderr:
487
+ stderr 缓冲区快照:
413
488
  ${fenceConsole}
414
489
  ${stderrContent}
490
+ ${fenceEnd}`
491
+ : `Daemon PID: ${daemon.pid}
492
+ Command: ${daemon.command}
493
+ Shell: ${daemon.shell}
494
+ Lifecycle status: ${status}
495
+ Uptime: ${uptime}s
496
+ Started at: ${(0, time_1.formatUnifiedTimestamp)(daemon.startTime)}${scrollNotice}
497
+
498
+ Stdout buffer snapshot:
499
+ ${fenceConsole}
500
+ ${stdoutContent}
415
501
  ${fenceEnd}
416
502
 
417
- 💡 Use stop_daemon({"pid": ${daemon.pid}}) to terminate this process`;
503
+ Stderr buffer snapshot:
504
+ ${fenceConsole}
505
+ ${stderrContent}
506
+ ${fenceEnd}`;
418
507
  }
419
508
  // ReminderOwner implementation for shell command tool
420
509
  exports.shellCmdReminderOwner = {
@@ -432,7 +521,7 @@ exports.shellCmdReminderOwner = {
432
521
  // Check if process has exited
433
522
  if (!daemon.isRunning) {
434
523
  // Process has exited, provide final status and drop reminder
435
- const finalStatus = formatDaemonStatus(daemon);
524
+ const finalStatus = formatDaemonStatus(daemon, (0, work_language_1.getWorkLanguage)());
436
525
  daemonProcesses.delete(pid);
437
526
  return {
438
527
  treatment: 'update',
@@ -457,7 +546,7 @@ exports.shellCmdReminderOwner = {
457
546
  daemon.exitSignal = 'UNKNOWN';
458
547
  }
459
548
  // Update the reminder with current daemon status
460
- const updatedContent = formatDaemonStatus(daemon);
549
+ const updatedContent = formatDaemonStatus(daemon, (0, work_language_1.getWorkLanguage)());
461
550
  return {
462
551
  treatment: 'update',
463
552
  updatedContent,
@@ -477,11 +566,11 @@ exports.shellCmdReminderOwner = {
477
566
  role: 'user',
478
567
  content: language === 'zh'
479
568
  ? `${prefix} 后台进程状态提醒 #${index + 1}
480
- 你正在查看系统维护的后台进程状态,不要把它当成你自己写的工作便签。该提醒会随进程生命周期自动更新或删除。
569
+ 这是系统维护的后台进程状态快照。把它当成环境信号,不是你自己写的工作便签。若它没有实质改变你的判断/计划/风险,则禁止做任何用户可见回应(禁止写“静默吸收”“已收到”等占位语句);只有它实际影响后续动作时,才在下一条有实质内容的回复中体现相关事实。该提醒会随进程生命周期自动更新或删除。
481
570
  ---
482
571
  ${reminder.content}`
483
572
  : `${prefix} Background process status reminder #${index + 1}
484
- You are looking at system-maintained background process state. Do not treat it as a self-authored work note. This reminder will update or disappear automatically with the process lifecycle.
573
+ This is a system-maintained background process snapshot. Treat it as an environment signal, not a self-authored work note. If it does not materially change your judgment/plan/risk, make no user-visible reply at all (do not send filler like “silently noted” or “received”); only reflect it inside the next substantive reply when it actually affects the next action. This reminder will update or disappear automatically with the process lifecycle.
485
574
  ---
486
575
  ${reminder.content}`,
487
576
  };
@@ -507,20 +596,20 @@ This daemon process has finished its lifecycle and is no longer running. This re
507
596
  : uptime < 3600
508
597
  ? `${Math.floor(uptime / 60)}m ${uptime % 60}s`
509
598
  : `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m`;
510
- const statusInfo = formatDaemonStatus(daemon);
599
+ const statusInfo = formatDaemonStatus(daemon, language);
511
600
  return {
512
601
  type: 'environment_msg',
513
602
  role: 'user',
514
603
  content: language === 'zh'
515
- ? `🔄 ${prefix} 运行中后台进程监控 #${index + 1} - PID ${pid}(已运行 ${uptimeStr})
516
- 你当前有一个仍在运行的后台进程。请按需要检查它的健康状态、资源占用和输出情况;这条提醒由系统自动维护,会随进程状态变化自动更新或删除。
604
+ ? `🔄 ${prefix} 运行中后台进程状态 #${index + 1} - PID ${pid}(已运行 ${uptimeStr})
605
+ 这是系统维护的状态快照,不是新的用户诉求,也不是默认需要单独汇报的事项。若下面的信息没有实质改变你的判断、计划、风险,且不需要调用守护进程相关工具,则禁止做任何用户可见回应;若它有实质影响,只在下一条有实质内容的回复中体现,禁止单独发送“静默吸收”“已收到”等占位语句。
517
606
 
518
- **当前状态:**
607
+ **状态快照:**
519
608
  ${statusInfo}`
520
- : `🔄 ${prefix} Active daemon monitor #${index + 1} - PID ${pid} (uptime: ${uptimeStr})
521
- You currently have a background process that is still running. Check its health, resource usage, and output as needed. This reminder is system-maintained and will update or disappear automatically as the process state changes.
609
+ : `🔄 ${prefix} Active daemon state #${index + 1} - PID ${pid} (uptime: ${uptimeStr})
610
+ This is a system-maintained snapshot, not a new user request and not something that normally deserves a standalone mention. If the information below does not materially change your judgment, plan, risk, or require a daemon-management action, make no user-visible reply at all; if it does matter, reflect it only inside the next substantive reply instead of sending filler like “silently noted” or “received”.
522
611
 
523
- **Current status:**
612
+ **State snapshot:**
524
613
  ${statusInfo}`,
525
614
  };
526
615
  },
@@ -546,6 +635,7 @@ exports.shellCmdTool = {
546
635
  return new Promise((resolve) => {
547
636
  const childProcess = (0, child_process_1.spawn)(spawnSpec.command, spawnSpec.args, {
548
637
  stdio: ['pipe', 'pipe', 'pipe'],
638
+ detached: process.platform !== 'win32',
549
639
  });
550
640
  const pid = childProcess.pid;
551
641
  const startTime = new Date();
@@ -564,6 +654,7 @@ exports.shellCmdTool = {
564
654
  command,
565
655
  shell: spawnSpec.shellLabel,
566
656
  process: childProcess,
657
+ processGroupId: process.platform === 'win32' ? undefined : pid,
567
658
  startTime,
568
659
  stdoutBuffer,
569
660
  stderrBuffer,
@@ -574,11 +665,14 @@ exports.shellCmdTool = {
574
665
  // Add reminder for daemon process
575
666
  const reminderContent = `[Daemon PID ${pid} - This content should not be visible, check dynamic rendering]`;
576
667
  dlg.addReminder(reminderContent, exports.shellCmdReminderOwner, {
577
- type: 'daemon',
668
+ kind: 'daemon',
578
669
  pid,
579
670
  command,
580
671
  shell: spawnSpec.shellLabel,
581
672
  startTime: (0, time_1.formatUnifiedTimestamp)(startTime),
673
+ delete: {
674
+ altInstruction: `stop_daemon({ "pid": ${pid} })`,
675
+ },
582
676
  });
583
677
  resolve(t.daemonStarted(pid, timeoutSeconds, command));
584
678
  }, timeoutSeconds * 1000);
@@ -1308,19 +1402,26 @@ exports.stopDaemonTool = {
1308
1402
  async call(dlg, caller, args) {
1309
1403
  const language = (0, work_language_1.getWorkLanguage)();
1310
1404
  const t = getOsToolMessages(language);
1311
- const { pid } = parseStopDaemonArgs(args);
1405
+ const { pid, entirePg } = parseStopDaemonArgs(args);
1312
1406
  const daemon = daemonProcesses.get(pid);
1313
1407
  if (!daemon) {
1314
1408
  return t.noDaemonFound(pid);
1315
1409
  }
1316
1410
  try {
1317
- // Kill the process
1318
- process.kill(pid, 'SIGTERM');
1411
+ let signalTarget = pid;
1412
+ if (entirePg) {
1413
+ if (daemon.processGroupId === undefined) {
1414
+ throw new Error('daemon has no isolated process group; rerun it after this update, or retry with entire_pg=false');
1415
+ }
1416
+ signalTarget = -daemon.processGroupId;
1417
+ }
1418
+ // Kill the tracked process or its entire process group.
1419
+ process.kill(signalTarget, 'SIGTERM');
1319
1420
  // Wait a bit for graceful shutdown
1320
1421
  await new Promise((resolve) => setTimeout(resolve, 1000));
1321
1422
  // Force kill if still running
1322
1423
  try {
1323
- process.kill(pid, 'SIGKILL');
1424
+ process.kill(signalTarget, 'SIGKILL');
1324
1425
  }
1325
1426
  catch (e) {
1326
1427
  // Process already terminated
@@ -22,19 +22,42 @@ function isPendingTellaskReminderMeta(value) {
22
22
  return false;
23
23
  return true;
24
24
  }
25
- function callTypeLabel(language, callType) {
25
+ function callKindLabel(language, view) {
26
+ if (view.callType === 'A') {
27
+ return language === 'zh' ? '回问诉请' : 'TellaskBack';
28
+ }
26
29
  if (language === 'zh') {
27
- if (callType === 'A')
28
- return '回问诉请';
29
- if (callType === 'B')
30
- return '长线诉请';
31
- return '一次性诉请';
30
+ switch (view.callName) {
31
+ case 'tellask':
32
+ return '长线诉请';
33
+ case 'tellaskSessionless':
34
+ return '一次性诉请';
35
+ case 'freshBootsReasoning':
36
+ return '扪心自问(FBR)';
37
+ }
38
+ }
39
+ switch (view.callName) {
40
+ case 'tellask':
41
+ return 'Tellask Session';
42
+ case 'tellaskSessionless':
43
+ return 'Fresh Tellask';
44
+ case 'freshBootsReasoning':
45
+ return 'Fresh Boots Reasoning (FBR)';
46
+ }
47
+ }
48
+ function pendingTargetLabel(language, view) {
49
+ if (view.callType === 'A') {
50
+ return language === 'zh'
51
+ ? `上游诉请者 @${view.targetAgentId}`
52
+ : `upstream requester @${view.targetAgentId}`;
53
+ }
54
+ switch (view.callName) {
55
+ case 'freshBootsReasoning':
56
+ return language === 'zh' ? '本对话自身' : 'this dialog itself';
57
+ case 'tellask':
58
+ case 'tellaskSessionless':
59
+ return `@${view.targetAgentId}`;
32
60
  }
33
- if (callType === 'A')
34
- return 'TellaskBack';
35
- if (callType === 'B')
36
- return 'Tellask Session';
37
- return 'Fresh Tellask';
38
61
  }
39
62
  function summarizeTellask(view) {
40
63
  const mentionPrefix = Array.isArray(view.mentionList) ? view.mentionList.join(' ') : '';
@@ -75,8 +98,8 @@ function buildReminderContent(language, pending) {
75
98
  : 'Only the Tellasks listed below are still in flight; besides them, no other Tellasks are currently executing. This reminder is auto-added/refreshed and not auto-deleted.';
76
99
  const lines = pending.map((p, idx) => {
77
100
  const base = language === 'zh'
78
- ? `${idx + 1}. @${p.targetAgentId} | ${callTypeLabel(language, p.callType)} | ${summarizeTellask(p)}`
79
- : `${idx + 1}. @${p.targetAgentId} | ${callTypeLabel(language, p.callType)} | ${summarizeTellask(p)}`;
101
+ ? `${idx + 1}. ${pendingTargetLabel(language, p)} | ${callKindLabel(language, p)} | ${summarizeTellask(p)}`
102
+ : `${idx + 1}. ${pendingTargetLabel(language, p)} | ${callKindLabel(language, p)} | ${summarizeTellask(p)}`;
80
103
  if (!p.sessionSlug)
81
104
  return base;
82
105
  return language === 'zh'
@@ -130,10 +130,11 @@ exports.updatePlanTool = {
130
130
  kind: 'plan',
131
131
  schemaVersion: 1,
132
132
  updatedAt: now,
133
- source: 'update_plan',
134
- managedByTool: 'update_plan',
135
- edit: {
136
- updateExample: 'update_plan({ "plan": [ { "step": "...", "status": "in_progress" } ] })',
133
+ manager: {
134
+ tool: 'update_plan',
135
+ },
136
+ update: {
137
+ altInstruction: 'update_plan({ "plan": [ { "step": "...", "status": "in_progress" } ] })',
137
138
  },
138
139
  };
139
140
  let existingIndex;
@@ -48,6 +48,7 @@ Stop daemon process.
48
48
  **Parameters:**
49
49
 
50
50
  - `pid` (required): Daemon process ID (number)
51
+ - `entire_pg` (optional): Whether to signal the entire process group (default: `true` on Unix-like systems, `false` on Windows; Windows does not support explicitly passing `true`)
51
52
 
52
53
  **Returns:**
53
54
 
@@ -151,6 +152,12 @@ shell_cmd({
151
152
  stop_daemon({
152
153
  pid: 12345,
153
154
  });
155
+
156
+ // On Unix-like systems, you can explicitly terminate the whole process group
157
+ stop_daemon({
158
+ pid: 12345,
159
+ entire_pg: true,
160
+ });
154
161
  ```
155
162
 
156
163
  ### Get Daemon Process Output
@@ -48,6 +48,7 @@ executed_at: <执行时间戳>
48
48
  **参数:**
49
49
 
50
50
  - `pid`(必需):守护进程 PID(数字)
51
+ - `entire_pg`(可选):是否对整个进程组发终止信号(默认:Linux/macOS 为 `true`,Windows 为 `false`;Windows 不支持显式传 `true`)
51
52
 
52
53
  **返回:**
53
54
 
@@ -151,6 +152,12 @@ shell_cmd({
151
152
  stop_daemon({
152
153
  pid: 12345,
153
154
  });
155
+
156
+ // Unix-like 平台可显式要求终止整个进程组
157
+ stop_daemon({
158
+ pid: 12345,
159
+ entire_pg: true,
160
+ });
154
161
  ```
155
162
 
156
163
  ### 获取守护进程输出
@@ -27,6 +27,7 @@
27
27
  team_mgmt is Dominds' toolset for managing `.minds/` (team configuration and rtws memory), using **prepare-first + single apply** architecture:
28
28
 
29
29
  - **Incremental edits (preferred)**: Use `team_mgmt_prepare_*` to generate reviewable YAML + diff + `hunk_id`, then write via `team_mgmt_apply_file_modification`
30
+ - **Hard ordering rule for the LLM**: `team_mgmt_prepare_*` only creates an in-memory preview and does not write the file; before `apply`, re-reading still returns the old content. If you want further edits based on the prepared result, you must call `team_mgmt_apply_file_modification` first, then read/prepare the next change
30
31
  - **Only operates in `.minds/`**: This toolset only operates within the `.minds/` subtree and should not touch other rtws files
31
32
  - **Shell guardrail**: toolset `os` includes `shell_cmd` / `stop_daemon` / `get_daemon_output`; any member with these shell tools must be listed in top-level `shell_specialists`
32
33
  - **Member assets recommended**: strongly recommend `persona/knowledge/lessons` files for every `members.<id>` to define ownership, boundaries, and reusable lessons
@@ -27,6 +27,7 @@
27
27
 
28
28
  - **Incremental edits (preferred)**: Use `team_mgmt_prepare_*` to generate reviewable YAML + diff + `hunk_id`, then write via `team_mgmt_apply_file_modification({ "hunk_id": "<hunk_id>" })`
29
29
  - **Parallelism constraint**: Multiple function tool calls in one generation step may run in parallel; **prepare → apply must be two steps**
30
+ - **LLM persistence semantics**: `team_mgmt_prepare_*` only stores a pending in-memory preview and does not modify the file before apply; a `team_mgmt_read_file` before apply still returns the old content. If you only want to revise that pending preview, overwrite the same hunk with `existing_hunk_id`; if you want the next edit based on this change, apply the current hunk first, then read/prepare again
30
31
  - **Minimum shell privilege**: toolset `os` includes `shell_cmd` / `stop_daemon` / `get_daemon_output`; grant it only to a small specialist set and list those member ids in top-level `shell_specialists`
31
32
  - **Exception (create)**: `team_mgmt_create_new_file` only creates a new file (empty content allowed). It does not do incremental edits and does not use prepare/apply; it refuses to overwrite existing files
32
33
  - **Exception (overwrite)**: `team_mgmt_overwrite_entire_file` writes immediately (no prepare/apply). It requires `known_old_total_lines/known_old_total_bytes` guardrails; use `team_mgmt_read_file` to read `total_lines/size_bytes` from the YAML header
@@ -52,6 +52,8 @@ Call the function tool `team_mgmt_apply_file_modification` with:
52
52
  { "hunk_id": "<hunk_id>" }
53
53
  ```
54
54
 
55
+ Important: before this apply, the prepared diff is not persisted yet; re-reading the file still returns the old content. If you want the next edit based on this change, apply the current hunk first, then prepare the next one.
56
+
55
57
  ### 4. Create New Mind File
56
58
 
57
59
  ```text
@@ -27,6 +27,7 @@
27
27
  team_mgmt 是 Dominds 用于管理 `.minds/`(团队配置与 rtws 记忆)的工具集,采用 **prepare-first + single apply** 架构:
28
28
 
29
29
  - **增量编辑(推荐)**:用 `team_mgmt_prepare_*` 先生成可复核的 YAML + diff + `hunk_id`,再用 `team_mgmt_apply_file_modification` 显式写入
30
+ - **LLM 顺序硬约束**:`team_mgmt_prepare_*` 只生成内存中的预览,不会写盘;在 `apply` 之前再次读取文件仍只能读到旧内容。若要基于本次改动继续修改,必须先 `team_mgmt_apply_file_modification`,再重新 read/prepare 新改动
30
31
  - **只操作 `.minds/`**:该 toolset 只允许操作 `.minds/` 子树,不会也不应触碰 rtws 其他文件
31
32
  - **shell 权限约束**:`os` toolset 包含 `shell_cmd` / `stop_daemon` / `get_daemon_output`;任何拿到这些工具的成员都必须出现在顶层 `shell_specialists`
32
33
  - **成员资产推荐**:强烈建议为每个 `members.<id>` 配置 `persona/knowledge/lessons` 资产文件,显式定义角色职责、边界和经验复用
@@ -27,6 +27,7 @@
27
27
 
28
28
  - **增量编辑(推荐)**:用 `team_mgmt_prepare_*` 先生成可复核的 YAML + diff + `hunk_id`,再用 `team_mgmt_apply_file_modification({ "hunk_id": "<hunk_id>" })` 显式写入
29
29
  - **并行约束**:同一轮生成中的多个工具调用可能并行执行;**prepare → apply 必须分两轮**
30
+ - **LLM 落盘语义**:`team_mgmt_prepare_*` 只保存待应用的内存预览,apply 前不会改动文件;此时再次 `team_mgmt_read_file` 看到的仍是旧内容。若只是修订同一个未落盘预览,可用同一 prepare 工具加 `existing_hunk_id` 覆写;若想继续下一笔修改,必须先 apply 当前 hunk,再重新 read/prepare
30
31
  - **shell 最小授权**:`os` toolset 包含 `shell_cmd` / `stop_daemon` / `get_daemon_output`;只授予少数专员成员,并在顶层 `shell_specialists` 显式列出这些成员 id
31
32
  - **例外(创建)**:`team_mgmt_create_new_file` 只负责创建新文件(允许空内容),不做增量编辑、不走 prepare/apply;若文件已存在会拒绝(避免误用覆盖写入语义)
32
33
  - **例外(整文件覆盖)**:`team_mgmt_overwrite_entire_file` 会直接写盘(不走 prepare/apply),必须提供 `known_old_total_lines/known_old_total_bytes` 作为对账护栏;建议先用 `team_mgmt_read_file` 从 YAML header 读取 `total_lines/size_bytes` 再填写
@@ -52,6 +52,8 @@ Call the function tool `team_mgmt_apply_file_modification` with:
52
52
  { "hunk_id": "<hunk_id>" }
53
53
  ```
54
54
 
55
+ 注意:这一步之前,prepare 结果还没有落盘;如果此时再次读取文件,读到的仍是旧内容。若要基于这次改动继续修改,先 apply 当前 hunk,再准备下一笔改动。
56
+
55
57
  ### 4. 创建新的 mind 文件
56
58
 
57
59
  ```text
@@ -28,6 +28,7 @@ ws_mod is Dominds' text editing toolset, using **prepare-first + single apply**
28
28
 
29
29
  - **prepare-first**: All incremental edits are planned first (output reviewable diff + evidence + hunk_id)
30
30
  - **single apply**: All planned edits are persisted only through `apply_file_modification`
31
+ - **Hard ordering rule for the LLM**: Before apply, a prepared hunk exists only in memory; re-reading still returns the old file content. If you want further edits based on that prepared result, apply the current hunk first, then prepare again
31
32
  - **Legacy tools removed**: `append_file` / `insert_after` / `insert_before` / `replace_block` / `apply_block_replace` are completely removed
32
33
 
33
34
  ## Quick Navigation
@@ -35,6 +35,7 @@ Therefore unified to:
35
35
 
36
36
  - **prepare-first**: All incremental edits are planned first (output reviewable diff + evidence + hunk_id)
37
37
  - **single apply**: All planned edits are persisted only through `apply_file_modification({ "hunk_id": "<hunk_id>" })`
38
+ - **LLM persistence semantics**: before apply, a prepared hunk exists only in memory and does not modify the file; a `read_file` at that point still returns the old content. If you only want to revise the same pending preview, overwrite it with the same prepare tool plus `existing_hunk_id`; if you want the next edit based on this change, apply the current hunk first, then read/prepare again
38
39
  - **Legacy tools removed**: `append_file` / `insert_after` / `insert_before` / `replace_block` / `apply_block_replace` are completely removed (no aliases, no compat layer)
39
40
 
40
41
  ## 2. Goals & Non-Goals
@@ -28,6 +28,7 @@ ws_mod 是 Dominds 的文本编辑工具集,采用 **prepare-first + single ap
28
28
 
29
29
  - **prepare-first**:所有增量编辑先规划(输出可审阅 diff + evidence + hunk_id)
30
30
  - **single apply**:所有计划类编辑仅通过 `apply_file_modification` 落盘
31
+ - **LLM 顺序硬约束**:prepare 结果在 apply 前只存在于内存里;此时再次读取文件仍只能读到旧内容。若要基于该结果继续修改,必须先 apply 当前 hunk,再重新 prepare
31
32
  - **移除旧工具**:`append_file` / `insert_after` / `insert_before` / `replace_block` / `apply_block_replace` 已彻底删除
32
33
 
33
34
  ## 快速导航
@@ -35,6 +35,7 @@
35
35
 
36
36
  - **prepare-first**:所有增量编辑先规划(输出可审阅 diff + evidence + hunk_id)
37
37
  - **single apply**:所有计划类编辑仅通过 `apply_file_modification({ "hunk_id": "<hunk_id>" })` 落盘
38
+ - **LLM 落盘语义**:prepare 结果在 apply 前只保存在内存中,不会改动文件;此时再次 `read_file` 看到的仍是旧内容。若只是修订同一个未落盘预览,可用同一 prepare 工具加 `existing_hunk_id` 覆写;若想继续下一笔修改,必须先 apply 当前 hunk,再重新 read/prepare
38
39
  - **移除旧工具**:`append_file` / `insert_after` / `insert_before` / `replace_block` / `apply_block_replace` 已彻底删除(无 alias、无兼容层)
39
40
 
40
41
  ## 2. 目标与非目标
@@ -5,6 +5,7 @@ You have read/write access to the rtws (runtime workspace), but **all incrementa
5
5
  ## Principles
6
6
 
7
7
  - Incremental edits: use `prepare_*` to generate an applyable hunk, then write via `apply_file_modification`.
8
+ - Hard ordering rule for the LLM: `prepare_*` only creates an in-memory preview and does not write the file; before `apply_file_modification`, re-reading still returns the old content. If you want further edits based on the prepared result, you must apply the current hunk first, then read/prepare the next change.
8
9
  - Legacy tools are removed (no compatibility layer): `append_file` / `insert_after` / `insert_before` / `replace_block` / `apply_block_replace`.
9
10
  - Constraint: paths under `*.tsk/` are encapsulated Taskdocs; file tools cannot access them.
10
11
  - Parallelism constraint: multiple function tool calls in one generation step may run in parallel; **prepare → apply must be two steps**.
@@ -35,6 +36,7 @@ You have read/write access to the rtws (runtime workspace), but **all incrementa
35
36
  - `prepare_*` generates `hunk_id` (TTL = 1 hour); apply can only use an unexpired hunk.
36
37
  - Expired/unused hunks have no side effects; they are cleaned up automatically.
37
38
  - Some prepare tools accept `existing_hunk_id` to overwrite the same prepared hunk; **custom new ids are not supported**.
39
+ - If you only want to revise the same not-yet-persisted preview, overwrite that hunk with the same prepare tool plus `existing_hunk_id`; if you want the next edit based on this change, apply the current hunk first, then prepare again.
38
40
 
39
41
  ## Apply semantics (context_match)
40
42
 
@@ -58,6 +60,8 @@ Call the function tool `apply_file_modification` with:
58
60
  { "hunk_id": "<hunk_id>" }
59
61
  ```
60
62
 
63
+ Before this step, the prepared diff is not persisted yet; a `read_file` at that point still returns the old content.
64
+
61
65
  ## Examples
62
66
 
63
67
  - Append to EOF: