dominds 1.19.3 → 1.20.2

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 (195) hide show
  1. package/README.md +1 -0
  2. package/dist/access-control.js +2 -2
  3. package/dist/dialog.d.ts +6 -1
  4. package/dist/dialog.js +13 -4
  5. package/dist/docs/context-health.md +39 -13
  6. package/dist/docs/context-health.zh.md +14 -7
  7. package/dist/docs/idle-reminder-wake.md +227 -0
  8. package/dist/docs/idle-reminder-wake.zh.md +227 -0
  9. package/dist/llm/client.d.ts +2 -0
  10. package/dist/llm/client.js +32 -2
  11. package/dist/llm/defaults.yaml +51 -10
  12. package/dist/llm/gen/codex.js +15 -0
  13. package/dist/llm/gen/failure-classifier.js +22 -1
  14. package/dist/llm/kernel-driver/drive.js +6 -0
  15. package/dist/llm/kernel-driver/flow.js +9 -0
  16. package/dist/llm/kernel-driver/idle-reminder-wake.d.ts +4 -0
  17. package/dist/llm/kernel-driver/idle-reminder-wake.js +351 -0
  18. package/dist/llm/kernel-driver/types.d.ts +1 -1
  19. package/dist/minds/load.js +2 -1
  20. package/dist/minds/minds-i18n.js +2 -2
  21. package/dist/minds/system-prompt-parts.js +56 -20
  22. package/dist/minds/system-prompt.js +10 -10
  23. package/dist/persistence.js +1 -1
  24. package/dist/runtime/driver-messages.d.ts +3 -0
  25. package/dist/runtime/driver-messages.js +91 -8
  26. package/dist/server/setup-routes.js +5 -5
  27. package/dist/tool.d.ts +8 -0
  28. package/dist/tools/builtins.js +4 -2
  29. package/dist/tools/ctrl.d.ts +2 -0
  30. package/dist/tools/ctrl.js +255 -68
  31. package/dist/tools/os.js +198 -0
  32. package/dist/tools/prompts/control/en/errors.md +2 -2
  33. package/dist/tools/prompts/control/en/index.md +1 -1
  34. package/dist/tools/prompts/control/en/principles.md +21 -14
  35. package/dist/tools/prompts/control/en/scenarios.md +12 -4
  36. package/dist/tools/prompts/control/en/tools.md +45 -5
  37. package/dist/tools/prompts/control/zh/errors.md +2 -2
  38. package/dist/tools/prompts/control/zh/index.md +1 -1
  39. package/dist/tools/prompts/control/zh/principles.md +21 -14
  40. package/dist/tools/prompts/control/zh/scenarios.md +10 -4
  41. package/dist/tools/prompts/control/zh/tools.md +40 -5
  42. package/dist/tools/prompts/personal_memory/en/principles.md +1 -1
  43. package/dist/tools/prompts/personal_memory/zh/principles.md +1 -1
  44. package/dist/tools/prompts/team_memory/en/principles.md +1 -1
  45. package/dist/tools/prompts/team_memory/zh/principles.md +1 -1
  46. package/dist/tools/team_mgmt.js +5 -8
  47. package/dist/utils/task-package.d.ts +7 -0
  48. package/dist/utils/task-package.js +65 -28
  49. package/dist/utils/taskdoc.js +21 -17
  50. package/package.json +4 -4
  51. package/webapp/dist/assets/{_basePickBy-Dnh413xT.js → _basePickBy-B7M9Q0Fa.js} +3 -3
  52. package/webapp/dist/assets/_basePickBy-B7M9Q0Fa.js.map +1 -0
  53. package/webapp/dist/assets/{_baseUniq-DWzYqpN_.js → _baseUniq-DAeYoL6j.js} +2 -2
  54. package/webapp/dist/assets/_baseUniq-DAeYoL6j.js.map +1 -0
  55. package/webapp/dist/assets/{arc-vfBkNCOx.js → arc-Bh4nDbNR.js} +2 -2
  56. package/webapp/dist/assets/arc-Bh4nDbNR.js.map +1 -0
  57. package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-DiUEBXOa.js → architectureDiagram-2XIMDMQ5-CxqmdsIm.js} +26 -8
  58. package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-CxqmdsIm.js.map +1 -0
  59. package/webapp/dist/assets/{blockDiagram-VD42YOAC-BqK1KM2m.js → blockDiagram-WCTKOSBZ-CxIWLtpt.js} +187 -170
  60. package/webapp/dist/assets/blockDiagram-WCTKOSBZ-CxIWLtpt.js.map +1 -0
  61. package/webapp/dist/assets/{c4Diagram-YG6GDRKO-ClHNu1Uo.js → c4Diagram-IC4MRINW-1qErOIgG.js} +4 -4
  62. package/webapp/dist/assets/c4Diagram-IC4MRINW-1qErOIgG.js.map +1 -0
  63. package/webapp/dist/assets/{channel-BbWLVc8W.js → channel-DkgZHNUe.js} +2 -2
  64. package/webapp/dist/assets/channel-DkgZHNUe.js.map +1 -0
  65. package/webapp/dist/assets/{chunk-4BX2VUAB-CItdSmZH.js → chunk-4BX2VUAB-BmdMbU9v.js} +2 -2
  66. package/webapp/dist/assets/chunk-4BX2VUAB-BmdMbU9v.js.map +1 -0
  67. package/webapp/dist/assets/{chunk-55IACEB6-DSCX9WCf.js → chunk-55IACEB6-D6LDTDBy.js} +2 -2
  68. package/webapp/dist/assets/chunk-55IACEB6-D6LDTDBy.js.map +1 -0
  69. package/webapp/dist/assets/{chunk-FMBD7UC4-BJ1vT2se.js → chunk-FMBD7UC4-C-BdCe4C.js} +2 -2
  70. package/webapp/dist/assets/chunk-FMBD7UC4-C-BdCe4C.js.map +1 -0
  71. package/webapp/dist/assets/{chunk-TZMSLE5B-D2g6Tj7Z.js → chunk-JSJVCQXG-WA_BLIm9.js} +14 -6
  72. package/webapp/dist/assets/chunk-JSJVCQXG-WA_BLIm9.js.map +1 -0
  73. package/webapp/dist/assets/{chunk-QN33PNHL-CGyezTSD.js → chunk-KX2RTZJC-CA7sDJO5.js} +2 -2
  74. package/webapp/dist/assets/chunk-KX2RTZJC-CA7sDJO5.js.map +1 -0
  75. package/webapp/dist/assets/{chunk-DI55MBZ5-CRMf6XZu.js → chunk-NQ4KR5QH-wlvxalE3.js} +9 -7
  76. package/webapp/dist/assets/chunk-NQ4KR5QH-wlvxalE3.js.map +1 -0
  77. package/webapp/dist/assets/{chunk-QZHKN3VN-9xs15j8C.js → chunk-QZHKN3VN-Bo1VMcph.js} +2 -2
  78. package/webapp/dist/assets/chunk-QZHKN3VN-Bo1VMcph.js.map +1 -0
  79. package/webapp/dist/assets/{chunk-B4BG7PRW-5CRXFeD9.js → chunk-WL4C6EOR-B-Pk44be.js} +171 -121
  80. package/webapp/dist/assets/chunk-WL4C6EOR-B-Pk44be.js.map +1 -0
  81. package/webapp/dist/assets/{classDiagram-2ON5EDUG-BQFGGJNm.js → classDiagram-VBA2DB6C-BqKuyb49.js} +7 -6
  82. package/webapp/dist/assets/classDiagram-VBA2DB6C-BqKuyb49.js.map +1 -0
  83. package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-BQFGGJNm.js → classDiagram-v2-RAHNMMFH-BqKuyb49.js} +7 -6
  84. package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-BqKuyb49.js.map +1 -0
  85. package/webapp/dist/assets/{clone-DOfPd4cz.js → clone-BX5z8WVZ.js} +2 -2
  86. package/webapp/dist/assets/clone-BX5z8WVZ.js.map +1 -0
  87. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-BYN-vqm8.js → cose-bilkent-S5V4N54A-B-s11SgN.js} +2 -2
  88. package/webapp/dist/assets/cose-bilkent-S5V4N54A-B-s11SgN.js.map +1 -0
  89. package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
  90. package/webapp/dist/assets/{dagre-6UL2VRFP-ClEaFABE.js → dagre-KLK3FWXG-DmQFV2qK.js} +7 -7
  91. package/webapp/dist/assets/dagre-KLK3FWXG-DmQFV2qK.js.map +1 -0
  92. package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
  93. package/webapp/dist/assets/{diagram-PSM6KHXK-CM4hLE_0.js → diagram-E7M64L7V-QRaBfST8.js} +10 -10
  94. package/webapp/dist/assets/diagram-E7M64L7V-QRaBfST8.js.map +1 -0
  95. package/webapp/dist/assets/{diagram-QEK2KX5R-BZjGFX-2.js → diagram-IFDJBPK2-lrWn1Obo.js} +9 -8
  96. package/webapp/dist/assets/diagram-IFDJBPK2-lrWn1Obo.js.map +1 -0
  97. package/webapp/dist/assets/{diagram-S2PKOQOG-Bvw01OOG.js → diagram-P4PSJMXO-sTU7Hh-Y.js} +8 -8
  98. package/webapp/dist/assets/diagram-P4PSJMXO-sTU7Hh-Y.js.map +1 -0
  99. package/webapp/dist/assets/{erDiagram-Q2GNP2WA-ctHu5zQL.js → erDiagram-INFDFZHY-Cx6jc9Wq.js} +96 -75
  100. package/webapp/dist/assets/erDiagram-INFDFZHY-Cx6jc9Wq.js.map +1 -0
  101. package/webapp/dist/assets/{flowDiagram-NV44I4VS-m7ofIhri.js → flowDiagram-PKNHOUZH-DfGI49Dz.js} +98 -81
  102. package/webapp/dist/assets/flowDiagram-PKNHOUZH-DfGI49Dz.js.map +1 -0
  103. package/webapp/dist/assets/{ganttDiagram-JELNMOA3-D9wS5Veb.js → ganttDiagram-A5KZAMGK-nrcHWWaM.js} +28 -3
  104. package/webapp/dist/assets/ganttDiagram-A5KZAMGK-nrcHWWaM.js.map +1 -0
  105. package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-B86qqJx7.js → gitGraphDiagram-K3NZZRJ6-D8ivAqd6.js} +38 -46
  106. package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-D8ivAqd6.js.map +1 -0
  107. package/webapp/dist/assets/graph-R5G-y8tB.js +782 -0
  108. package/webapp/dist/assets/graph-R5G-y8tB.js.map +1 -0
  109. package/webapp/dist/assets/{index-tinPEZoH.js → index--fy89xGh.js} +1034 -1059
  110. package/webapp/dist/assets/{index-tinPEZoH.js.map → index--fy89xGh.js.map} +1 -1
  111. package/webapp/dist/assets/{index-BGdI3lWA.css → index-DZFkLLVz.css} +1 -1
  112. package/webapp/dist/assets/{infoDiagram-HS3SLOUP-DwRPUctP.js → infoDiagram-LFFYTUFH-PIoZHr7s.js} +7 -7
  113. package/webapp/dist/assets/infoDiagram-LFFYTUFH-PIoZHr7s.js.map +1 -0
  114. package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
  115. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-oCM-LYk1.js +966 -0
  116. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-oCM-LYk1.js.map +1 -0
  117. package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-B91ZO-ec.js → journeyDiagram-4ABVD52K-C2qidjQ5.js} +5 -5
  118. package/webapp/dist/assets/journeyDiagram-4ABVD52K-C2qidjQ5.js.map +1 -0
  119. package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-CoogrZ07.js → kanban-definition-K7BYSVSG-Du0TC8WS.js} +5 -3
  120. package/webapp/dist/assets/kanban-definition-K7BYSVSG-Du0TC8WS.js.map +1 -0
  121. package/webapp/dist/assets/{layout-BrzQmqFJ.js → layout-VmEo1OEB.js} +5 -5
  122. package/webapp/dist/assets/layout-VmEo1OEB.js.map +1 -0
  123. package/webapp/dist/assets/{linear-C6H7K9Zy.js → linear-B662YHAc.js} +2 -2
  124. package/webapp/dist/assets/linear-B662YHAc.js.map +1 -0
  125. package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-UDHZQkNZ.js → mindmap-definition-YRQLILUH-D7arZj95.js} +7 -5
  126. package/webapp/dist/assets/mindmap-definition-YRQLILUH-D7arZj95.js.map +1 -0
  127. package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
  128. package/webapp/dist/assets/{pieDiagram-ADFJNKIX-M81uyQ1J.js → pieDiagram-SKSYHLDU-DvjPP4PA.js} +8 -8
  129. package/webapp/dist/assets/pieDiagram-SKSYHLDU-DvjPP4PA.js.map +1 -0
  130. package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-ClzIh9Gb.js → quadrantDiagram-337W2JSQ-B_JUGMj_.js} +3 -3
  131. package/webapp/dist/assets/quadrantDiagram-337W2JSQ-B_JUGMj_.js.map +1 -0
  132. package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-DLK3A-pn.js → requirementDiagram-Z7DCOOCP-DF0mpvE3.js} +16 -6
  133. package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-DF0mpvE3.js.map +1 -0
  134. package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-CYqju8I1.js → sankeyDiagram-WA2Y5GQK-CoXlxv00.js} +2 -2
  135. package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-CoXlxv00.js.map +1 -0
  136. package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-2guv6eOd.js → sequenceDiagram-2WXFIKYE-DYqT5Pg7.js} +601 -201
  137. package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-DYqT5Pg7.js.map +1 -0
  138. package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-iujqSp0X.js → stateDiagram-RAJIS63D-D9b1mN8-.js} +9 -9
  139. package/webapp/dist/assets/stateDiagram-RAJIS63D-D9b1mN8-.js.map +1 -0
  140. package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-BxzY81ky.js → stateDiagram-v2-FVOUBMTO-DNzgudL_.js} +5 -5
  141. package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-DNzgudL_.js.map +1 -0
  142. package/webapp/dist/assets/{timeline-definition-IT6M3QCI-DjCFSC8d.js → timeline-definition-YZTLITO2-CkyKUY7A.js} +3 -3
  143. package/webapp/dist/assets/timeline-definition-YZTLITO2-CkyKUY7A.js.map +1 -0
  144. package/webapp/dist/assets/{treemap-GDKQZRPO-iaBzDWCP.js → treemap-KZPCXAKY-CZd09kF-.js} +37 -24
  145. package/webapp/dist/assets/treemap-KZPCXAKY-CZd09kF-.js.map +1 -0
  146. package/webapp/dist/assets/vennDiagram-LZ73GAT5-BxVF5Olo.js +2487 -0
  147. package/webapp/dist/assets/vennDiagram-LZ73GAT5-BxVF5Olo.js.map +1 -0
  148. package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-R6Jl1c89.js → xychartDiagram-JWTSCODW-BRwRloPc.js} +4 -4
  149. package/webapp/dist/assets/xychartDiagram-JWTSCODW-BRwRloPc.js.map +1 -0
  150. package/webapp/dist/index.html +2 -2
  151. package/webapp/dist/assets/_basePickBy-Dnh413xT.js.map +0 -1
  152. package/webapp/dist/assets/_baseUniq-DWzYqpN_.js.map +0 -1
  153. package/webapp/dist/assets/arc-vfBkNCOx.js.map +0 -1
  154. package/webapp/dist/assets/architectureDiagram-VXUJARFQ-DiUEBXOa.js.map +0 -1
  155. package/webapp/dist/assets/blockDiagram-VD42YOAC-BqK1KM2m.js.map +0 -1
  156. package/webapp/dist/assets/c4Diagram-YG6GDRKO-ClHNu1Uo.js.map +0 -1
  157. package/webapp/dist/assets/channel-BbWLVc8W.js.map +0 -1
  158. package/webapp/dist/assets/chunk-4BX2VUAB-CItdSmZH.js.map +0 -1
  159. package/webapp/dist/assets/chunk-55IACEB6-DSCX9WCf.js.map +0 -1
  160. package/webapp/dist/assets/chunk-B4BG7PRW-5CRXFeD9.js.map +0 -1
  161. package/webapp/dist/assets/chunk-DI55MBZ5-CRMf6XZu.js.map +0 -1
  162. package/webapp/dist/assets/chunk-FMBD7UC4-BJ1vT2se.js.map +0 -1
  163. package/webapp/dist/assets/chunk-QN33PNHL-CGyezTSD.js.map +0 -1
  164. package/webapp/dist/assets/chunk-QZHKN3VN-9xs15j8C.js.map +0 -1
  165. package/webapp/dist/assets/chunk-TZMSLE5B-D2g6Tj7Z.js.map +0 -1
  166. package/webapp/dist/assets/classDiagram-2ON5EDUG-BQFGGJNm.js.map +0 -1
  167. package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-BQFGGJNm.js.map +0 -1
  168. package/webapp/dist/assets/clone-DOfPd4cz.js.map +0 -1
  169. package/webapp/dist/assets/cose-bilkent-S5V4N54A-BYN-vqm8.js.map +0 -1
  170. package/webapp/dist/assets/dagre-6UL2VRFP-ClEaFABE.js.map +0 -1
  171. package/webapp/dist/assets/diagram-PSM6KHXK-CM4hLE_0.js.map +0 -1
  172. package/webapp/dist/assets/diagram-QEK2KX5R-BZjGFX-2.js.map +0 -1
  173. package/webapp/dist/assets/diagram-S2PKOQOG-Bvw01OOG.js.map +0 -1
  174. package/webapp/dist/assets/erDiagram-Q2GNP2WA-ctHu5zQL.js.map +0 -1
  175. package/webapp/dist/assets/flowDiagram-NV44I4VS-m7ofIhri.js.map +0 -1
  176. package/webapp/dist/assets/ganttDiagram-JELNMOA3-D9wS5Veb.js.map +0 -1
  177. package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-B86qqJx7.js.map +0 -1
  178. package/webapp/dist/assets/graph-u844GGQC.js +0 -425
  179. package/webapp/dist/assets/graph-u844GGQC.js.map +0 -1
  180. package/webapp/dist/assets/infoDiagram-HS3SLOUP-DwRPUctP.js.map +0 -1
  181. package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-B91ZO-ec.js.map +0 -1
  182. package/webapp/dist/assets/kanban-definition-3W4ZIXB7-CoogrZ07.js.map +0 -1
  183. package/webapp/dist/assets/layout-BrzQmqFJ.js.map +0 -1
  184. package/webapp/dist/assets/linear-C6H7K9Zy.js.map +0 -1
  185. package/webapp/dist/assets/mindmap-definition-VGOIOE7T-UDHZQkNZ.js.map +0 -1
  186. package/webapp/dist/assets/pieDiagram-ADFJNKIX-M81uyQ1J.js.map +0 -1
  187. package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-ClzIh9Gb.js.map +0 -1
  188. package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-DLK3A-pn.js.map +0 -1
  189. package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-CYqju8I1.js.map +0 -1
  190. package/webapp/dist/assets/sequenceDiagram-WL72ISMW-2guv6eOd.js.map +0 -1
  191. package/webapp/dist/assets/stateDiagram-FKZM4ZOC-iujqSp0X.js.map +0 -1
  192. package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-BxzY81ky.js.map +0 -1
  193. package/webapp/dist/assets/timeline-definition-IT6M3QCI-DjCFSC8d.js.map +0 -1
  194. package/webapp/dist/assets/treemap-GDKQZRPO-iaBzDWCP.js.map +0 -1
  195. package/webapp/dist/assets/xychartDiagram-PRI3JC2R-R6Jl1c89.js.map +0 -1
@@ -15,6 +15,7 @@
15
15
  * - update_reminder: Update reminder content
16
16
  * - clear_mind: Start a new course, optionally add a reminder
17
17
  * - change_mind: Update a `.tsk/` Taskdoc section without starting a new course
18
+ * - mind_more: Append entries to a `.tsk/` Taskdoc section without starting a new course
18
19
  * - recall_taskdoc: Read a Taskdoc section from `*.tsk/` by (category, selector)
19
20
  *
20
21
  * USAGE CONTEXT:
@@ -60,7 +61,7 @@ var __importStar = (this && this.__importStar) || (function () {
60
61
  };
61
62
  })();
62
63
  Object.defineProperty(exports, "__esModule", { value: true });
63
- exports.recallTaskdocTool = exports.changeMindTool = exports.clearMindTool = exports.updateReminderTool = exports.addReminderTool = exports.deleteReminderTool = void 0;
64
+ exports.recallTaskdocTool = exports.mindMoreTool = exports.changeMindTool = exports.clearMindTool = exports.updateReminderTool = exports.addReminderTool = exports.deleteReminderTool = void 0;
64
65
  const fs = __importStar(require("fs"));
65
66
  const path = __importStar(require("path"));
66
67
  const dialog_1 = require("../dialog");
@@ -98,6 +99,10 @@ function isJsonValue(value) {
98
99
  function isJsonObject(value) {
99
100
  return isRecord(value) && Object.values(value).every((item) => isJsonValue(item));
100
101
  }
102
+ function isPathWithinDirectory(childPath, parentDir) {
103
+ const relative = path.relative(parentDir, childPath);
104
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
105
+ }
101
106
  function getManagerTool(meta) {
102
107
  if (!isRecord(meta)) {
103
108
  return undefined;
@@ -251,11 +256,112 @@ async function resolveReminderTarget(dlg, reminderIdRaw) {
251
256
  }
252
257
  return { ok: true, target };
253
258
  }
259
+ function runReminderIndexMutation(mutation) {
260
+ try {
261
+ mutation();
262
+ return true;
263
+ }
264
+ catch (error) {
265
+ if (error instanceof dialog_1.InvalidReminderIndexError) {
266
+ return false;
267
+ }
268
+ throw error;
269
+ }
270
+ }
271
+ function findReminderIndexById(sourceLabel, reminders, reminderId) {
272
+ let foundIndex = null;
273
+ for (let index = 0; index < reminders.length; index += 1) {
274
+ const reminder = reminders[index];
275
+ if (reminder?.id !== reminderId)
276
+ continue;
277
+ if (foundIndex !== null) {
278
+ throw new Error(`Duplicate ${sourceLabel} reminder_id detected: ${reminderId}`);
279
+ }
280
+ foundIndex = index;
281
+ }
282
+ return foundIndex;
283
+ }
284
+ async function deleteSharedReminderById(agentId, reminderId) {
285
+ return (0, shared_reminders_1.mutateAgentSharedReminders)(agentId, (reminders) => {
286
+ const index = findReminderIndexById('shared', reminders, reminderId);
287
+ if (index === null)
288
+ return false;
289
+ reminders.splice(index, 1);
290
+ return true;
291
+ });
292
+ }
293
+ async function updateSharedReminderById(agentId, reminderId, updateReminder) {
294
+ return (0, shared_reminders_1.mutateAgentSharedReminders)(agentId, (reminders) => {
295
+ const index = findReminderIndexById('shared', reminders, reminderId);
296
+ if (index === null)
297
+ return false;
298
+ const reminder = reminders[index];
299
+ if (reminder === undefined) {
300
+ throw new Error(`Shared reminder index ${index} disappeared while updating reminder_id ${reminderId}`);
301
+ }
302
+ reminders[index] = updateReminder(reminder);
303
+ return true;
304
+ });
305
+ }
306
+ function replaceReminderContent(reminder, content, meta, renderMode) {
307
+ return (0, tool_1.materializeReminder)({
308
+ id: reminder.id,
309
+ content,
310
+ owner: reminder.owner,
311
+ meta: meta !== undefined ? meta : reminder.meta,
312
+ echoback: reminder.echoback,
313
+ scope: reminder.scope,
314
+ createdAt: reminder.createdAt,
315
+ priority: reminder.priority,
316
+ renderMode: renderMode ?? reminder.renderMode,
317
+ });
318
+ }
319
+ async function deleteResolvedReminderTarget(dlg, target) {
320
+ switch (target.source) {
321
+ case 'dialog': {
322
+ const index = findReminderIndexById('dialog', dlg.reminders, target.reminder.id);
323
+ if (index === null)
324
+ return false;
325
+ return runReminderIndexMutation(() => {
326
+ dlg.deleteReminder(index);
327
+ });
328
+ }
329
+ case 'agent_shared': {
330
+ const deleted = await deleteSharedReminderById(target.agentId, target.reminder.id);
331
+ if (deleted)
332
+ dlg.touchReminders();
333
+ return deleted;
334
+ }
335
+ }
336
+ const _exhaustive = target;
337
+ return _exhaustive;
338
+ }
339
+ async function updateResolvedReminderTarget(dlg, target, content, meta, renderMode) {
340
+ switch (target.source) {
341
+ case 'dialog': {
342
+ const index = findReminderIndexById('dialog', dlg.reminders, target.reminder.id);
343
+ if (index === null)
344
+ return false;
345
+ return runReminderIndexMutation(() => {
346
+ dlg.updateReminder(index, content, meta, { renderMode });
347
+ });
348
+ }
349
+ case 'agent_shared': {
350
+ const updated = await updateSharedReminderById(target.agentId, target.reminder.id, (current) => replaceReminderContent(current, content, meta, renderMode));
351
+ if (updated)
352
+ dlg.touchReminders();
353
+ return updated;
354
+ }
355
+ }
356
+ const _exhaustive = target;
357
+ return _exhaustive;
358
+ }
254
359
  function getCtrlMessages(language) {
255
360
  if (language === 'zh') {
256
361
  return {
257
362
  invalidFormatDelete: '参数格式不对。用法:delete_reminder({ reminder_id: string })',
258
363
  reminderDoesNotExist: (reminderId) => `提醒项 '${reminderId}' 不存在。`,
364
+ reminderTargetChanged: '错误:提醒项列表已变化,这条提醒项当前不在可见列表中。请重新查看提醒项列表后,用当前的 reminder_id 重试。',
259
365
  invalidFormatAdd: '参数格式不对。用法:add_reminder({ content: string, position?: number, scope?: "dialog" | "personal" })(省略 position 表示追加)',
260
366
  personalPositionUnsupported: 'personal 范围提醒当前只支持追加,不能指定 position。',
261
367
  reminderContentEmpty: '提醒内容不能为空',
@@ -263,6 +369,9 @@ function getCtrlMessages(language) {
263
369
  invalidFormatUpdate: '参数格式不对。用法:update_reminder({ reminder_id: string, content: string })',
264
370
  invalidFormatChangeMind: '参数格式不对。用法:change_mind({ selector: string, category?: string, content: string })',
265
371
  tooManyArgsChangeMind: '参数格式不对。用法:change_mind({ selector: string, category?: string, content: string })',
372
+ invalidFormatMindMore: '参数格式不对。用法:mind_more({ items: string[], sep?: string, selector?: string, category?: string })(selector 默认 progress)',
373
+ mindMoreItemsRequired: '需要提供要追加的条目(items),且每一项都必须是非空字符串。\n' +
374
+ '示例:mind_more({"items":["- 下一步:复核验证结果(详见 <文档路径>#<章节>)","- 阻塞:等待 API 验收口径确认"]})',
266
375
  invalidFormatRecallTaskdoc: '参数格式不对。用法:recall_taskdoc({ category: string, selector: string })',
267
376
  taskDocContentRequired: '需要提供差遣牒内容(content)。\n' +
268
377
  '示例:\n' +
@@ -282,13 +391,14 @@ function getCtrlMessages(language) {
282
391
  invalidCategorySelector: (selector) => `选择器 '${selector}' 无效。需匹配 ^[a-zA-Z][a-zA-Z0-9_-]*(\\.[a-zA-Z0-9_-]*)*$。`,
283
392
  topLevelSelectorRequiresNoCategory: (category, selector) => `选择器 '${selector}' 是顶层保留分段(goals/constraints/progress),不能与 category='${category}' 一起用。`,
284
393
  bearInMindSelectorRequiresBearInMindCategory: (category, selector) => `选择器 '${selector}' 只能在 category='bearinmind' 下用(当前 category='${category}')。`,
285
- taskDocSectionMissing: (relativePath) => `未找到:${relativePath}。\n\n change_mind 创建或更新:\n- change_mind({"category":string,"selector":string,"content":string})`,
394
+ taskDocSectionMissing: (relativePath) => `未找到:${relativePath}。\n\n少量追加/创建可用 mind_more;整章创建/替换用 change_mind:\n- mind_more({"category":"<category>","selector":"<selector>","items":["..."]})\n- change_mind({"category":"<category>","selector":"<selector>","content":"..."})`,
286
395
  clearedCoursePrompt: (nextCourse) => (0, driver_messages_1.formatNewCourseStartPrompt)('zh', { nextCourse, source: 'clear_mind' }),
287
396
  };
288
397
  }
289
398
  return {
290
399
  invalidFormatDelete: 'Error: Invalid args. Use: delete_reminder({ reminder_id: string })',
291
400
  reminderDoesNotExist: (reminderId) => `Error: Reminder '${reminderId}' does not exist.`,
401
+ reminderTargetChanged: 'Error: The reminder list changed, and this reminder is no longer visible. Refresh the reminders and retry with the current reminder_id.',
292
402
  invalidFormatAdd: 'Error: Invalid args. Use: add_reminder({ content: string, position?: number, scope?: "dialog" | "personal" }) (omit position to append).',
293
403
  personalPositionUnsupported: 'Error: personal-scope reminders currently support append only; do not pass position.',
294
404
  reminderContentEmpty: 'Error: Reminder content cannot be empty',
@@ -296,6 +406,9 @@ function getCtrlMessages(language) {
296
406
  invalidFormatUpdate: 'Error: Invalid args. Use: update_reminder({ reminder_id: string, content: string })',
297
407
  invalidFormatChangeMind: 'Error: Invalid args. Use: change_mind({ selector: string, category?: string, content: string })',
298
408
  tooManyArgsChangeMind: 'Error: Invalid args. Use: change_mind({ selector: string, category?: string, content: string })',
409
+ invalidFormatMindMore: 'Error: Invalid args. Use: mind_more({ items: string[], sep?: string, selector?: string, category?: string }) (selector defaults to progress).',
410
+ mindMoreItemsRequired: 'Error: items are required, and every item must be a non-empty string.\n' +
411
+ 'Example: mind_more({"items":["- Next: review verification results (details: <doc-path>#<section>)","- Blocker: API acceptance criteria pending"]})',
299
412
  invalidFormatRecallTaskdoc: 'Error: Invalid args. Use: recall_taskdoc({ category: string, selector: string })',
300
413
  taskDocContentRequired: 'Error: Taskdoc content is required (content).\n' +
301
414
  'Copy/paste example:\n' +
@@ -315,7 +428,7 @@ function getCtrlMessages(language) {
315
428
  invalidCategorySelector: (selector) => `Error: Invalid category selector '${selector}'. Must match \`^[a-zA-Z][a-zA-Z0-9_-]*(\\.[a-zA-Z0-9_-]+)*$\`.`,
316
429
  topLevelSelectorRequiresNoCategory: (category, selector) => `Error: Selector '${selector}' is reserved for top-level sections (goals/constraints/progress) and must not be used with category='${category}'.`,
317
430
  bearInMindSelectorRequiresBearInMindCategory: (category, selector) => `Error: Selector '${selector}' is only valid under category='bearinmind' (got category='${category}').`,
318
- taskDocSectionMissing: (relativePath) => `Not found: \`${relativePath}\`.\n\nUse \`change_mind\` to create/update it (whole-section replace):\n- \`change_mind({\"category\":\"<category>\",\"selector\":\"<selector>\",\"content\":\"...\"})\``,
431
+ taskDocSectionMissing: (relativePath) => `Not found: \`${relativePath}\`.\n\nUse \`mind_more\` for small append/create updates, or \`change_mind\` for full-section create/replace:\n- \`mind_more({\"category\":\"<category>\",\"selector\":\"<selector>\",\"items\":[\"...\"]})\`\n- \`change_mind({\"category\":\"<category>\",\"selector\":\"<selector>\",\"content\":\"...\"})\``,
319
432
  clearedCoursePrompt: (nextCourse) => (0, driver_messages_1.formatNewCourseStartPrompt)('en', { nextCourse, source: 'clear_mind' }),
320
433
  };
321
434
  }
@@ -351,18 +464,10 @@ exports.deleteReminderTool = {
351
464
  if (deleteAltInstruction !== undefined) {
352
465
  return (0, tool_1.toolFailure)(formatManualDeleteBlockedError(language, deleteAltInstruction));
353
466
  }
354
- if (resolved.target.source === 'dialog') {
355
- dlg.deleteReminder(resolved.target.index);
356
- return (0, tool_result_messages_1.formatToolActionResult)(language, 'deleted');
357
- }
358
- if (resolved.target.source === 'agent_shared') {
359
- await (0, shared_reminders_1.mutateAgentSharedReminders)(resolved.target.agentId, (reminders) => {
360
- reminders.splice(resolved.target.index, 1);
361
- });
362
- dlg.touchReminders();
363
- return (0, tool_result_messages_1.formatToolActionResult)(language, 'deleted');
364
- }
365
- return (0, tool_1.toolFailure)(t.invalidFormatDelete);
467
+ const deleted = await deleteResolvedReminderTarget(dlg, resolved.target);
468
+ if (!deleted)
469
+ return (0, tool_1.toolFailure)(t.reminderTargetChanged);
470
+ return (0, tool_result_messages_1.formatToolActionResult)(language, 'deleted');
366
471
  },
367
472
  };
368
473
  exports.addReminderTool = {
@@ -532,39 +637,14 @@ exports.updateReminderTool = {
532
637
  if (contextHealthLevel === undefined) {
533
638
  const stripResult = removeContinuationPackageReminderMeta(reminder?.meta);
534
639
  if (stripResult.changed) {
535
- if (resolved.target.source === 'dialog') {
536
- dlg.updateReminder(resolved.target.index, reminderContent, stripResult.nextMeta, {
537
- renderMode: reminderRenderMode,
538
- });
539
- }
540
- else {
541
- await (0, shared_reminders_1.mutateAgentSharedReminders)(resolved.target.agentId, (reminders) => {
542
- reminders[resolved.target.index] = {
543
- ...reminders[resolved.target.index],
544
- content: reminderContent,
545
- meta: stripResult.nextMeta,
546
- renderMode: reminderRenderMode ?? reminders[resolved.target.index]?.renderMode,
547
- };
548
- });
549
- dlg.touchReminders();
550
- }
640
+ const updated = await updateResolvedReminderTarget(dlg, resolved.target, reminderContent, stripResult.nextMeta, reminderRenderMode);
641
+ if (!updated)
642
+ return (0, tool_1.toolFailure)(t.reminderTargetChanged);
551
643
  return (0, tool_result_messages_1.formatToolActionResult)(language, 'updated');
552
644
  }
553
- if (resolved.target.source === 'dialog') {
554
- dlg.updateReminder(resolved.target.index, reminderContent, undefined, {
555
- renderMode: reminderRenderMode,
556
- });
557
- }
558
- else {
559
- await (0, shared_reminders_1.mutateAgentSharedReminders)(resolved.target.agentId, (reminders) => {
560
- reminders[resolved.target.index] = {
561
- ...reminders[resolved.target.index],
562
- content: reminderContent,
563
- renderMode: reminderRenderMode ?? reminders[resolved.target.index]?.renderMode,
564
- };
565
- });
566
- dlg.touchReminders();
567
- }
645
+ const updated = await updateResolvedReminderTarget(dlg, resolved.target, reminderContent, undefined, reminderRenderMode);
646
+ if (!updated)
647
+ return (0, tool_1.toolFailure)(t.reminderTargetChanged);
568
648
  return (0, tool_result_messages_1.formatToolActionResult)(language, 'updated');
569
649
  }
570
650
  const reminderMeta = buildContinuationPackageReminderMeta({
@@ -572,22 +652,9 @@ exports.updateReminderTool = {
572
652
  createdBy: 'context_health',
573
653
  contextHealthLevel,
574
654
  });
575
- if (resolved.target.source === 'dialog') {
576
- dlg.updateReminder(resolved.target.index, reminderContent, reminderMeta, {
577
- renderMode: reminderRenderMode,
578
- });
579
- }
580
- else {
581
- await (0, shared_reminders_1.mutateAgentSharedReminders)(resolved.target.agentId, (reminders) => {
582
- reminders[resolved.target.index] = {
583
- ...reminders[resolved.target.index],
584
- content: reminderContent,
585
- meta: reminderMeta,
586
- renderMode: reminderRenderMode ?? reminders[resolved.target.index]?.renderMode,
587
- };
588
- });
589
- dlg.touchReminders();
590
- }
655
+ const updated = await updateResolvedReminderTarget(dlg, resolved.target, reminderContent, reminderMeta, reminderRenderMode);
656
+ if (!updated)
657
+ return (0, tool_1.toolFailure)(t.reminderTargetChanged);
591
658
  return (0, tool_result_messages_1.formatToolActionResult)(language, 'updated');
592
659
  },
593
660
  };
@@ -663,10 +730,10 @@ exports.changeMindTool = {
663
730
  const maintainerId = dlg instanceof dialog_1.SideDialog ? dlg.mainDialog.agentId : dlg.agentId;
664
731
  if (language === 'zh') {
665
732
  return (0, tool_1.toolFailure)(`错误:\`change_mind\` 仅允许在主线对话中使用(支线对话中不可用)。\n` +
666
- `请诉请差遣牒维护人 @${maintainerId} 在其对话中执行 \`change_mind\`,并提供你已合并好的“分段全文替换稿”(禁止覆盖/抹掉他人条目)。`);
733
+ `请诉请差遣牒维护人 @${maintainerId} 在其对话中执行 \`mind_more\` 或 \`change_mind\`,并提供要追加的条目或已合并好的“分段全文替换稿”(禁止覆盖/抹掉他人条目)。`);
667
734
  }
668
735
  return (0, tool_1.toolFailure)(`Error: \`change_mind\` is only available in the Main Dialog (not in Side Dialogs).\n` +
669
- `Ask the Taskdoc maintainer @${maintainerId} to run \`change_mind\` and provide a fully merged full-section replacement draft (do not overwrite/delete other contributors).`);
736
+ `Ask the Taskdoc maintainer @${maintainerId} to run \`mind_more\` or \`change_mind\` with entries to append or a fully merged full-section replacement draft (do not overwrite/delete other contributors).`);
670
737
  }
671
738
  const selectorValue = args['selector'];
672
739
  const selector = typeof selectorValue === 'string' ? selectorValue.trim() : '';
@@ -684,8 +751,9 @@ exports.changeMindTool = {
684
751
  return (0, tool_1.toolFailure)(t.noTaskDocPathConfigured);
685
752
  const workspaceRoot = path.resolve(process.cwd());
686
753
  const fullPath = path.resolve(workspaceRoot, taskDocPath);
687
- if (!fullPath.startsWith(workspaceRoot))
754
+ if (!isPathWithinDirectory(fullPath, workspaceRoot)) {
688
755
  return (0, tool_1.toolFailure)(t.pathMustBeWithinWorkspace);
756
+ }
689
757
  if (!(0, task_package_1.isTaskPackagePath)(taskDocPath))
690
758
  return (0, tool_1.toolFailure)(t.invalidTaskDocPath(taskDocPath));
691
759
  const parsed = (0, task_package_1.parseTaskPackageChangeMindTarget)({ selector, category });
@@ -721,6 +789,123 @@ exports.changeMindTool = {
721
789
  return (0, tool_result_messages_1.formatToolActionResult)(language, 'mindChanged');
722
790
  },
723
791
  };
792
+ exports.mindMoreTool = {
793
+ type: 'func',
794
+ name: 'mind_more',
795
+ description: 'Append entries to one shared Taskdoc section in the Main Dialog.',
796
+ descriptionI18n: {
797
+ en: 'Append entries to one shared Taskdoc section in the Main Dialog.',
798
+ zh: '在主线对话中向一段共享差遣牒章节追加条目。',
799
+ },
800
+ parameters: {
801
+ type: 'object',
802
+ additionalProperties: false,
803
+ required: ['items'],
804
+ properties: {
805
+ items: {
806
+ type: 'array',
807
+ description: 'Entries to append. Each entry must be a non-empty string.',
808
+ items: { type: 'string' },
809
+ },
810
+ sep: {
811
+ type: 'string',
812
+ description: 'Separator inserted between existing content and new entries, and between entries. Defaults to "\\n".',
813
+ },
814
+ selector: {
815
+ type: 'string',
816
+ description: 'Target section selector. Defaults to progress. Top-level: goals|constraints|progress. Under category="bearinmind": contracts|acceptance|grants|runbook|decisions|risks. For other categories: any identifier.',
817
+ },
818
+ category: {
819
+ type: 'string',
820
+ description: 'Optional category directory within the Taskdoc package. When present, selector targets <category>/<selector>.md.',
821
+ },
822
+ },
823
+ },
824
+ argsValidation: 'dominds',
825
+ async call(dlg, caller, args) {
826
+ const language = (0, work_language_1.getWorkLanguage)();
827
+ const t = getCtrlMessages(language);
828
+ if (dlg.askerDialog !== undefined) {
829
+ const maintainerId = dlg instanceof dialog_1.SideDialog ? dlg.mainDialog.agentId : dlg.agentId;
830
+ if (language === 'zh') {
831
+ return (0, tool_1.toolFailure)(`错误:\`mind_more\` 仅允许在主线对话中使用(支线对话中不可用)。\n` +
832
+ `请诉请差遣牒维护人 @${maintainerId} 在其对话中执行 \`mind_more\` 或 \`change_mind\`,并提供要追加或已合并好的内容(禁止覆盖/抹掉他人条目)。`);
833
+ }
834
+ return (0, tool_1.toolFailure)(`Error: \`mind_more\` is only available in the Main Dialog (not in Side Dialogs).\n` +
835
+ `Ask the Taskdoc maintainer @${maintainerId} to run \`mind_more\` or \`change_mind\` with the entries to append or a merged draft (do not overwrite/delete other contributors).`);
836
+ }
837
+ const rawItems = args['items'];
838
+ if (!Array.isArray(rawItems) || rawItems.length === 0) {
839
+ return (0, tool_1.toolFailure)(t.mindMoreItemsRequired);
840
+ }
841
+ const items = [];
842
+ for (const item of rawItems) {
843
+ if (typeof item !== 'string')
844
+ return (0, tool_1.toolFailure)(t.mindMoreItemsRequired);
845
+ const normalized = item.trim();
846
+ if (!normalized)
847
+ return (0, tool_1.toolFailure)(t.mindMoreItemsRequired);
848
+ items.push(normalized);
849
+ }
850
+ const sepValue = args['sep'];
851
+ const sep = sepValue === undefined ? '\n' : typeof sepValue === 'string' ? sepValue : null;
852
+ if (sep === null)
853
+ return (0, tool_1.toolFailure)(t.invalidFormatMindMore);
854
+ const selectorValue = args['selector'];
855
+ const selector = selectorValue === undefined
856
+ ? 'progress'
857
+ : typeof selectorValue === 'string'
858
+ ? selectorValue.trim()
859
+ : '';
860
+ if (!selector)
861
+ return (0, tool_1.toolFailure)(t.selectorRequired);
862
+ const categoryValue = args['category'];
863
+ const category = typeof categoryValue === 'string' ? categoryValue.trim() : undefined;
864
+ // Taskdoc path is immutable for the dialog lifecycle.
865
+ const taskDocPath = dlg.taskDocPath;
866
+ if (!taskDocPath)
867
+ return (0, tool_1.toolFailure)(t.noTaskDocPathConfigured);
868
+ const workspaceRoot = path.resolve(process.cwd());
869
+ const fullPath = path.resolve(workspaceRoot, taskDocPath);
870
+ if (!isPathWithinDirectory(fullPath, workspaceRoot)) {
871
+ return (0, tool_1.toolFailure)(t.pathMustBeWithinWorkspace);
872
+ }
873
+ if (!(0, task_package_1.isTaskPackagePath)(taskDocPath))
874
+ return (0, tool_1.toolFailure)(t.invalidTaskDocPath(taskDocPath));
875
+ const parsed = (0, task_package_1.parseTaskPackageChangeMindTarget)({ selector, category });
876
+ if (parsed.kind !== 'ok') {
877
+ const e = parsed.error;
878
+ switch (e.kind) {
879
+ case 'selector_required':
880
+ return (0, tool_1.toolFailure)(t.selectorRequired);
881
+ case 'invalid_category_name':
882
+ return (0, tool_1.toolFailure)(t.invalidCategory(e.category));
883
+ case 'invalid_category_selector':
884
+ return (0, tool_1.toolFailure)(t.invalidCategorySelector(e.selector));
885
+ case 'invalid_top_level_selector':
886
+ return (0, tool_1.toolFailure)(t.invalidSelector(e.selector));
887
+ case 'invalid_bearinmind_selector':
888
+ return (0, tool_1.toolFailure)(t.invalidSelector(e.selector));
889
+ case 'top_level_selector_requires_no_category':
890
+ return (0, tool_1.toolFailure)(t.topLevelSelectorRequiresNoCategory(e.category, e.selector));
891
+ case 'bearinmind_selector_requires_bearinmind_category':
892
+ return (0, tool_1.toolFailure)(t.bearInMindSelectorRequiresBearInMindCategory(e.category, e.selector));
893
+ default: {
894
+ const _exhaustive = e;
895
+ return (0, tool_1.toolFailure)(String(_exhaustive));
896
+ }
897
+ }
898
+ }
899
+ await (0, task_package_1.appendTaskPackageByChangeMindTarget)({
900
+ taskPackageDirFullPath: fullPath,
901
+ target: parsed.target,
902
+ content: items.join(sep),
903
+ sep,
904
+ updatedBy: caller.id,
905
+ });
906
+ return (0, tool_result_messages_1.formatToolActionResult)(language, 'mindChanged');
907
+ },
908
+ };
724
909
  exports.recallTaskdocTool = {
725
910
  type: 'func',
726
911
  name: 'recall_taskdoc',
@@ -756,8 +941,9 @@ exports.recallTaskdocTool = {
756
941
  return (0, tool_1.toolFailure)(t.noTaskDocPathConfigured);
757
942
  const workspaceRoot = path.resolve(process.cwd());
758
943
  const fullPath = path.resolve(workspaceRoot, taskDocPath);
759
- if (!fullPath.startsWith(workspaceRoot))
944
+ if (!isPathWithinDirectory(fullPath, workspaceRoot)) {
760
945
  return (0, tool_1.toolFailure)(t.pathMustBeWithinWorkspace);
946
+ }
761
947
  if (!(0, task_package_1.isTaskPackagePath)(taskDocPath))
762
948
  return (0, tool_1.toolFailure)(t.invalidTaskDocPath(taskDocPath));
763
949
  const parsed = (0, task_package_1.parseTaskPackageChangeMindTarget)({ selector, category });
@@ -803,7 +989,7 @@ exports.recallTaskdocTool = {
803
989
  return (0, tool_1.toolFailure)(t.invalidFormatRecallTaskdoc);
804
990
  }
805
991
  const sectionPath = path.resolve(fullPath, relPath);
806
- if (!sectionPath.startsWith(fullPath)) {
992
+ if (!isPathWithinDirectory(sectionPath, fullPath)) {
807
993
  return (0, tool_1.toolFailure)(t.pathMustBeWithinWorkspace);
808
994
  }
809
995
  try {
@@ -830,6 +1016,7 @@ exports.recallTaskdocTool = {
830
1016
  ? `\n\n⚠️ 已截断:内容过大(${bytes} bytes),仅回显前 ${maxSize} bytes。`
831
1017
  : `\n\n⚠️ Truncated: content is too large (${bytes} bytes); showing first ${maxSize} bytes.`
832
1018
  : '';
833
- return (0, tool_1.toolSuccess)(`**recall_taskdoc:** \`${relPath}\`\n\n---\n${clipped}\n---${note}`);
1019
+ const closingSeparatorPrefix = clipped.endsWith('\n') ? '' : '\n';
1020
+ return (0, tool_1.toolSuccess)(`**recall_taskdoc:** \`${relPath}\`\n\n---\n${clipped}${closingSeparatorPrefix}---${note}`);
834
1021
  },
835
1022
  };