dominds 1.20.2 → 1.20.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 (154) hide show
  1. package/README.md +4 -4
  2. package/README.zh.md +2 -2
  3. package/dist/access-control.js +2 -2
  4. package/dist/cli/webui.d.ts +1 -1
  5. package/dist/cli/webui.js +29 -9
  6. package/dist/docs/OEC-philosophy.md +1 -1
  7. package/dist/docs/cli-usage.md +2 -1
  8. package/dist/docs/cli-usage.zh.md +6 -3
  9. package/dist/docs/context-health.md +2 -2
  10. package/dist/docs/context-health.zh.md +2 -2
  11. package/dist/docs/design.md +3 -3
  12. package/dist/docs/design.zh.md +3 -3
  13. package/dist/docs/dialog-system.md +18 -10
  14. package/dist/docs/dialog-system.zh.md +18 -10
  15. package/dist/docs/dominds-terminology.md +4 -4
  16. package/dist/docs/encapsulated-taskdoc.md +17 -10
  17. package/dist/docs/encapsulated-taskdoc.zh.md +18 -11
  18. package/dist/minds/load.js +15 -4
  19. package/dist/minds/minds-i18n.js +2 -2
  20. package/dist/minds/system-prompt-parts.js +11 -11
  21. package/dist/runtime/driver-messages.js +8 -4
  22. package/dist/server/api-routes.js +65 -0
  23. package/dist/server/port-selection.d.ts +19 -0
  24. package/dist/server/port-selection.js +57 -0
  25. package/dist/server/server-core.d.ts +2 -2
  26. package/dist/server/server-core.js +21 -7
  27. package/dist/server/static-server.js +1 -0
  28. package/dist/server.d.ts +3 -0
  29. package/dist/server.js +109 -44
  30. package/dist/team.d.ts +2 -3
  31. package/dist/team.js +17 -4
  32. package/dist/tools/builtins.js +5 -0
  33. package/dist/tools/ctrl.d.ts +11 -7
  34. package/dist/tools/ctrl.js +248 -34
  35. package/dist/tools/prompts/control/en/errors.md +2 -2
  36. package/dist/tools/prompts/control/en/index.md +1 -1
  37. package/dist/tools/prompts/control/en/principles.md +14 -12
  38. package/dist/tools/prompts/control/en/tools.md +40 -6
  39. package/dist/tools/prompts/control/zh/errors.md +2 -2
  40. package/dist/tools/prompts/control/zh/index.md +1 -1
  41. package/dist/tools/prompts/control/zh/principles.md +14 -12
  42. package/dist/tools/prompts/control/zh/tools.md +40 -6
  43. package/dist/tools/prompts/personal_memory/en/principles.md +1 -1
  44. package/dist/tools/prompts/personal_memory/zh/principles.md +1 -1
  45. package/dist/tools/prompts/team_memory/en/principles.md +1 -1
  46. package/dist/tools/prompts/team_memory/zh/principles.md +1 -1
  47. package/dist/tools/registry.d.ts +6 -0
  48. package/dist/tools/team_mgmt.js +5 -5
  49. package/dist/utils/task-package.d.ts +24 -1
  50. package/dist/utils/task-package.js +80 -5
  51. package/dist/utils/taskdoc-search-worker-client.d.ts +12 -0
  52. package/dist/utils/taskdoc-search-worker-client.js +320 -0
  53. package/dist/utils/taskdoc-search-worker.d.ts +1 -0
  54. package/dist/utils/taskdoc-search-worker.js +42 -0
  55. package/dist/utils/taskdoc-search.d.ts +28 -7
  56. package/dist/utils/taskdoc-search.js +62 -15
  57. package/dist/utils/taskdoc.js +60 -22
  58. package/package.json +3 -3
  59. package/webapp/dist/assets/{_basePickBy-B7M9Q0Fa.js → _basePickBy-BrC49DPW.js} +3 -3
  60. package/webapp/dist/assets/{_basePickBy-B7M9Q0Fa.js.map → _basePickBy-BrC49DPW.js.map} +1 -1
  61. package/webapp/dist/assets/{_baseUniq-DAeYoL6j.js → _baseUniq-C_Wcx-_S.js} +2 -2
  62. package/webapp/dist/assets/{_baseUniq-DAeYoL6j.js.map → _baseUniq-C_Wcx-_S.js.map} +1 -1
  63. package/webapp/dist/assets/{arc-Bh4nDbNR.js → arc-X5iIdpx0.js} +2 -2
  64. package/webapp/dist/assets/{arc-Bh4nDbNR.js.map → arc-X5iIdpx0.js.map} +1 -1
  65. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CxqmdsIm.js → architectureDiagram-2XIMDMQ5-DFfhMGkT.js} +7 -7
  66. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CxqmdsIm.js.map → architectureDiagram-2XIMDMQ5-DFfhMGkT.js.map} +1 -1
  67. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-CxIWLtpt.js → blockDiagram-WCTKOSBZ-BAt4BaxZ.js} +7 -7
  68. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-CxIWLtpt.js.map → blockDiagram-WCTKOSBZ-BAt4BaxZ.js.map} +1 -1
  69. package/webapp/dist/assets/{c4Diagram-IC4MRINW-1qErOIgG.js → c4Diagram-IC4MRINW-BYxKH5AA.js} +3 -3
  70. package/webapp/dist/assets/{c4Diagram-IC4MRINW-1qErOIgG.js.map → c4Diagram-IC4MRINW-BYxKH5AA.js.map} +1 -1
  71. package/webapp/dist/assets/{channel-DkgZHNUe.js → channel-CuYhL59H.js} +2 -2
  72. package/webapp/dist/assets/{channel-DkgZHNUe.js.map → channel-CuYhL59H.js.map} +1 -1
  73. package/webapp/dist/assets/{chunk-4BX2VUAB-BmdMbU9v.js → chunk-4BX2VUAB-Ch1PfLiD.js} +2 -2
  74. package/webapp/dist/assets/{chunk-4BX2VUAB-BmdMbU9v.js.map → chunk-4BX2VUAB-Ch1PfLiD.js.map} +1 -1
  75. package/webapp/dist/assets/{chunk-55IACEB6-D6LDTDBy.js → chunk-55IACEB6-B3rzakPd.js} +2 -2
  76. package/webapp/dist/assets/{chunk-55IACEB6-D6LDTDBy.js.map → chunk-55IACEB6-B3rzakPd.js.map} +1 -1
  77. package/webapp/dist/assets/{chunk-FMBD7UC4-C-BdCe4C.js → chunk-FMBD7UC4-CCZWJRJ5.js} +2 -2
  78. package/webapp/dist/assets/{chunk-FMBD7UC4-C-BdCe4C.js.map → chunk-FMBD7UC4-CCZWJRJ5.js.map} +1 -1
  79. package/webapp/dist/assets/{chunk-JSJVCQXG-WA_BLIm9.js → chunk-JSJVCQXG-Dqd_MOPD.js} +2 -2
  80. package/webapp/dist/assets/{chunk-JSJVCQXG-WA_BLIm9.js.map → chunk-JSJVCQXG-Dqd_MOPD.js.map} +1 -1
  81. package/webapp/dist/assets/{chunk-KX2RTZJC-CA7sDJO5.js → chunk-KX2RTZJC-DOpPZD_q.js} +2 -2
  82. package/webapp/dist/assets/{chunk-KX2RTZJC-CA7sDJO5.js.map → chunk-KX2RTZJC-DOpPZD_q.js.map} +1 -1
  83. package/webapp/dist/assets/{chunk-NQ4KR5QH-wlvxalE3.js → chunk-NQ4KR5QH-CUj2LNV0.js} +4 -4
  84. package/webapp/dist/assets/{chunk-NQ4KR5QH-wlvxalE3.js.map → chunk-NQ4KR5QH-CUj2LNV0.js.map} +1 -1
  85. package/webapp/dist/assets/{chunk-QZHKN3VN-Bo1VMcph.js → chunk-QZHKN3VN-DXeWDyzh.js} +2 -2
  86. package/webapp/dist/assets/{chunk-QZHKN3VN-Bo1VMcph.js.map → chunk-QZHKN3VN-DXeWDyzh.js.map} +1 -1
  87. package/webapp/dist/assets/{chunk-WL4C6EOR-B-Pk44be.js → chunk-WL4C6EOR-CqTowzs6.js} +6 -6
  88. package/webapp/dist/assets/{chunk-WL4C6EOR-B-Pk44be.js.map → chunk-WL4C6EOR-CqTowzs6.js.map} +1 -1
  89. package/webapp/dist/assets/{classDiagram-VBA2DB6C-BqKuyb49.js → classDiagram-VBA2DB6C-CvsxEN3D.js} +7 -7
  90. package/webapp/dist/assets/{classDiagram-VBA2DB6C-BqKuyb49.js.map → classDiagram-VBA2DB6C-CvsxEN3D.js.map} +1 -1
  91. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-BqKuyb49.js → classDiagram-v2-RAHNMMFH-CvsxEN3D.js} +7 -7
  92. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-BqKuyb49.js.map → classDiagram-v2-RAHNMMFH-CvsxEN3D.js.map} +1 -1
  93. package/webapp/dist/assets/{clone-BX5z8WVZ.js → clone-PGWIYNYc.js} +2 -2
  94. package/webapp/dist/assets/{clone-BX5z8WVZ.js.map → clone-PGWIYNYc.js.map} +1 -1
  95. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-B-s11SgN.js → cose-bilkent-S5V4N54A-Cf8RR_8j.js} +2 -2
  96. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-B-s11SgN.js.map → cose-bilkent-S5V4N54A-Cf8RR_8j.js.map} +1 -1
  97. package/webapp/dist/assets/{dagre-KLK3FWXG-DmQFV2qK.js → dagre-KLK3FWXG-C1yQDg-6.js} +7 -7
  98. package/webapp/dist/assets/{dagre-KLK3FWXG-DmQFV2qK.js.map → dagre-KLK3FWXG-C1yQDg-6.js.map} +1 -1
  99. package/webapp/dist/assets/{diagram-E7M64L7V-QRaBfST8.js → diagram-E7M64L7V-CrT4qBJh.js} +8 -8
  100. package/webapp/dist/assets/{diagram-E7M64L7V-QRaBfST8.js.map → diagram-E7M64L7V-CrT4qBJh.js.map} +1 -1
  101. package/webapp/dist/assets/{diagram-IFDJBPK2-lrWn1Obo.js → diagram-IFDJBPK2-B8WaCOcr.js} +7 -7
  102. package/webapp/dist/assets/{diagram-IFDJBPK2-lrWn1Obo.js.map → diagram-IFDJBPK2-B8WaCOcr.js.map} +1 -1
  103. package/webapp/dist/assets/{diagram-P4PSJMXO-sTU7Hh-Y.js → diagram-P4PSJMXO-BCZeNfu_.js} +7 -7
  104. package/webapp/dist/assets/{diagram-P4PSJMXO-sTU7Hh-Y.js.map → diagram-P4PSJMXO-BCZeNfu_.js.map} +1 -1
  105. package/webapp/dist/assets/{erDiagram-INFDFZHY-Cx6jc9Wq.js → erDiagram-INFDFZHY-BrYt0-mW.js} +5 -5
  106. package/webapp/dist/assets/{erDiagram-INFDFZHY-Cx6jc9Wq.js.map → erDiagram-INFDFZHY-BrYt0-mW.js.map} +1 -1
  107. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-DfGI49Dz.js → flowDiagram-PKNHOUZH-8lZ5d5y-.js} +7 -7
  108. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-DfGI49Dz.js.map → flowDiagram-PKNHOUZH-8lZ5d5y-.js.map} +1 -1
  109. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-nrcHWWaM.js → ganttDiagram-A5KZAMGK-CQ8pOLf-.js} +3 -3
  110. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-nrcHWWaM.js.map → ganttDiagram-A5KZAMGK-CQ8pOLf-.js.map} +1 -1
  111. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-D8ivAqd6.js → gitGraphDiagram-K3NZZRJ6-aOTOe0HP.js} +8 -8
  112. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-D8ivAqd6.js.map → gitGraphDiagram-K3NZZRJ6-aOTOe0HP.js.map} +1 -1
  113. package/webapp/dist/assets/{graph-R5G-y8tB.js → graph-CyMR1egR.js} +3 -3
  114. package/webapp/dist/assets/{graph-R5G-y8tB.js.map → graph-CyMR1egR.js.map} +1 -1
  115. package/webapp/dist/assets/{index--fy89xGh.js → index-B9TTmMv-.js} +550 -134
  116. package/webapp/dist/assets/index-B9TTmMv-.js.map +1 -0
  117. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-PIoZHr7s.js → infoDiagram-LFFYTUFH-CXR9XjRe.js} +6 -6
  118. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-PIoZHr7s.js.map → infoDiagram-LFFYTUFH-CXR9XjRe.js.map} +1 -1
  119. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-oCM-LYk1.js → ishikawaDiagram-PHBUUO56-zwe-cqRW.js} +2 -2
  120. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-oCM-LYk1.js.map → ishikawaDiagram-PHBUUO56-zwe-cqRW.js.map} +1 -1
  121. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-C2qidjQ5.js → journeyDiagram-4ABVD52K-BV-8X5hz.js} +5 -5
  122. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-C2qidjQ5.js.map → journeyDiagram-4ABVD52K-BV-8X5hz.js.map} +1 -1
  123. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-Du0TC8WS.js → kanban-definition-K7BYSVSG-E3W-szUv.js} +3 -3
  124. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-Du0TC8WS.js.map → kanban-definition-K7BYSVSG-E3W-szUv.js.map} +1 -1
  125. package/webapp/dist/assets/{layout-VmEo1OEB.js → layout-DvIjuV8I.js} +5 -5
  126. package/webapp/dist/assets/{layout-VmEo1OEB.js.map → layout-DvIjuV8I.js.map} +1 -1
  127. package/webapp/dist/assets/{linear-B662YHAc.js → linear-C-SIS0ki.js} +2 -2
  128. package/webapp/dist/assets/{linear-B662YHAc.js.map → linear-C-SIS0ki.js.map} +1 -1
  129. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-D7arZj95.js → mindmap-definition-YRQLILUH-wigkPJ2B.js} +4 -4
  130. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-D7arZj95.js.map → mindmap-definition-YRQLILUH-wigkPJ2B.js.map} +1 -1
  131. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-DvjPP4PA.js → pieDiagram-SKSYHLDU-BKrYJ0LE.js} +8 -8
  132. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-DvjPP4PA.js.map → pieDiagram-SKSYHLDU-BKrYJ0LE.js.map} +1 -1
  133. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-B_JUGMj_.js → quadrantDiagram-337W2JSQ-fjwd0gVY.js} +3 -3
  134. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-B_JUGMj_.js.map → quadrantDiagram-337W2JSQ-fjwd0gVY.js.map} +1 -1
  135. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-DF0mpvE3.js → requirementDiagram-Z7DCOOCP-vgTMObSA.js} +4 -4
  136. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-DF0mpvE3.js.map → requirementDiagram-Z7DCOOCP-vgTMObSA.js.map} +1 -1
  137. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-CoXlxv00.js → sankeyDiagram-WA2Y5GQK-B-ZV3LF7.js} +2 -2
  138. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-CoXlxv00.js.map → sankeyDiagram-WA2Y5GQK-B-ZV3LF7.js.map} +1 -1
  139. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-DYqT5Pg7.js → sequenceDiagram-2WXFIKYE-DPhZYHhW.js} +4 -4
  140. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-DYqT5Pg7.js.map → sequenceDiagram-2WXFIKYE-DPhZYHhW.js.map} +1 -1
  141. package/webapp/dist/assets/{stateDiagram-RAJIS63D-D9b1mN8-.js → stateDiagram-RAJIS63D-DLsw3SC8.js} +9 -9
  142. package/webapp/dist/assets/{stateDiagram-RAJIS63D-D9b1mN8-.js.map → stateDiagram-RAJIS63D-DLsw3SC8.js.map} +1 -1
  143. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-DNzgudL_.js → stateDiagram-v2-FVOUBMTO-DqXB3dNs.js} +5 -5
  144. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-DNzgudL_.js.map → stateDiagram-v2-FVOUBMTO-DqXB3dNs.js.map} +1 -1
  145. package/webapp/dist/assets/{timeline-definition-YZTLITO2-CkyKUY7A.js → timeline-definition-YZTLITO2-BFSqlDfo.js} +3 -3
  146. package/webapp/dist/assets/{timeline-definition-YZTLITO2-CkyKUY7A.js.map → timeline-definition-YZTLITO2-BFSqlDfo.js.map} +1 -1
  147. package/webapp/dist/assets/{treemap-KZPCXAKY-CZd09kF-.js → treemap-KZPCXAKY-sdfq6mHG.js} +5 -5
  148. package/webapp/dist/assets/{treemap-KZPCXAKY-CZd09kF-.js.map → treemap-KZPCXAKY-sdfq6mHG.js.map} +1 -1
  149. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-BxVF5Olo.js → vennDiagram-LZ73GAT5-D7Cioksz.js} +2 -2
  150. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-BxVF5Olo.js.map → vennDiagram-LZ73GAT5-D7Cioksz.js.map} +1 -1
  151. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-BRwRloPc.js → xychartDiagram-JWTSCODW-No65aXqH.js} +3 -3
  152. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-BRwRloPc.js.map → xychartDiagram-JWTSCODW-No65aXqH.js.map} +1 -1
  153. package/webapp/dist/index.html +1 -1
  154. package/webapp/dist/assets/index--fy89xGh.js.map +0 -1
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.buildTaskDocumentSuggestionRequestKey = buildTaskDocumentSuggestionRequestKey;
40
+ exports.searchTaskDocumentSuggestionsInWorker = searchTaskDocumentSuggestionsInWorker;
41
+ const fs_1 = __importDefault(require("fs"));
42
+ const module_1 = require("module");
43
+ const path = __importStar(require("path"));
44
+ const worker_threads_1 = require("worker_threads");
45
+ const requireFn = (0, module_1.createRequire)(__filename);
46
+ const TASKDOC_SUGGESTION_WORKER_TIMEOUT_MS = 10000;
47
+ const TASKDOC_SUGGESTION_MAX_WORKERS = 2;
48
+ const TASKDOC_SUGGESTION_MAX_PENDING_WORKERS = 8;
49
+ const TASKDOC_SUGGESTION_CACHE_TTL_MS = 3000;
50
+ const TASKDOC_SUGGESTION_CACHE_MAX_ENTRIES = 64;
51
+ let activeTaskdocSuggestionWorkers = 0;
52
+ const pendingTaskdocSuggestionWorkerSlots = [];
53
+ const taskdocSuggestionCache = new Map();
54
+ const taskdocSuggestionInFlightByCacheKey = new Map();
55
+ function isRecord(value) {
56
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
57
+ }
58
+ function normalizeTaskdocSuggestionLimit(limit) {
59
+ return typeof limit === 'number' && Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 50;
60
+ }
61
+ function normalizeTaskdocSuggestionQueryKey(query) {
62
+ const normalized = query.trim().toLowerCase();
63
+ return normalized.endsWith('.tsk') ? normalized.slice(0, -4) : normalized;
64
+ }
65
+ function buildTaskDocumentSuggestionRequestKey(payload) {
66
+ return normalizeTaskdocSuggestionQueryKey(payload.query);
67
+ }
68
+ function buildTaskDocumentSuggestionCacheKey(payload) {
69
+ return [
70
+ path.resolve(payload.rootDir ?? '.'),
71
+ String(normalizeTaskdocSuggestionLimit(payload.limit)),
72
+ buildTaskDocumentSuggestionRequestKey(payload),
73
+ ].join('\u0000');
74
+ }
75
+ function readTaskDocumentSuggestionCache(cacheKey) {
76
+ const cached = taskdocSuggestionCache.get(cacheKey);
77
+ if (!cached)
78
+ return undefined;
79
+ if (cached.expiresAtMs <= Date.now()) {
80
+ taskdocSuggestionCache.delete(cacheKey);
81
+ return undefined;
82
+ }
83
+ return cached.result;
84
+ }
85
+ function writeTaskDocumentSuggestionCache(cacheKey, result) {
86
+ if (result.kind !== 'ok')
87
+ return;
88
+ const now = Date.now();
89
+ for (const [key, cached] of taskdocSuggestionCache) {
90
+ if (cached.expiresAtMs <= now)
91
+ taskdocSuggestionCache.delete(key);
92
+ }
93
+ taskdocSuggestionCache.delete(cacheKey);
94
+ taskdocSuggestionCache.set(cacheKey, {
95
+ expiresAtMs: now + TASKDOC_SUGGESTION_CACHE_TTL_MS,
96
+ result,
97
+ });
98
+ while (taskdocSuggestionCache.size > TASKDOC_SUGGESTION_CACHE_MAX_ENTRIES) {
99
+ const oldestKey = taskdocSuggestionCache.keys().next().value;
100
+ if (typeof oldestKey !== 'string')
101
+ break;
102
+ taskdocSuggestionCache.delete(oldestKey);
103
+ }
104
+ }
105
+ async function waitForTaskDocumentSuggestionResult(promise, signal) {
106
+ if (!signal)
107
+ return await promise;
108
+ if (signal.aborted) {
109
+ return { kind: 'error', errorText: 'Taskdoc suggestion request aborted' };
110
+ }
111
+ return await new Promise((resolve) => {
112
+ let settled = false;
113
+ const settle = (result) => {
114
+ if (settled)
115
+ return;
116
+ settled = true;
117
+ signal.removeEventListener('abort', abortWait);
118
+ resolve(result);
119
+ };
120
+ const abortWait = () => {
121
+ settle({ kind: 'error', errorText: 'Taskdoc suggestion request aborted' });
122
+ };
123
+ signal.addEventListener('abort', abortWait, { once: true });
124
+ promise.then(settle, (error) => {
125
+ settle({
126
+ kind: 'error',
127
+ errorText: error instanceof Error ? error.message : 'Taskdoc suggestion worker failed',
128
+ });
129
+ });
130
+ });
131
+ }
132
+ function parseWorkerMessage(value) {
133
+ if (!isRecord(value))
134
+ throw new Error('Taskdoc suggestion worker returned non-object message');
135
+ const kind = value['kind'];
136
+ switch (kind) {
137
+ case 'ok': {
138
+ const suggestionsRaw = value['suggestions'];
139
+ if (!isRecord(suggestionsRaw)) {
140
+ throw new Error('Taskdoc suggestion worker ok message missing suggestions');
141
+ }
142
+ if (suggestionsRaw['kind'] === 'ok') {
143
+ const listRaw = suggestionsRaw['suggestions'];
144
+ if (!Array.isArray(listRaw)) {
145
+ throw new Error('Taskdoc suggestion worker ok result missing suggestions list');
146
+ }
147
+ return {
148
+ kind: 'ok',
149
+ suggestions: {
150
+ kind: 'ok',
151
+ suggestions: listRaw.map((item) => {
152
+ if (!isRecord(item)) {
153
+ throw new Error('Taskdoc suggestion worker returned invalid suggestion item');
154
+ }
155
+ const pathRaw = item['path'];
156
+ const relativePath = item['relativePath'];
157
+ const name = item['name'];
158
+ if (typeof pathRaw !== 'string' ||
159
+ typeof relativePath !== 'string' ||
160
+ typeof name !== 'string') {
161
+ throw new Error('Taskdoc suggestion worker returned malformed suggestion item');
162
+ }
163
+ return { path: pathRaw, relativePath, name };
164
+ }),
165
+ },
166
+ };
167
+ }
168
+ if (suggestionsRaw['kind'] === 'error') {
169
+ const errorText = suggestionsRaw['errorText'];
170
+ if (typeof errorText !== 'string') {
171
+ throw new Error('Taskdoc suggestion worker error result missing errorText');
172
+ }
173
+ return { kind: 'ok', suggestions: { kind: 'error', errorText } };
174
+ }
175
+ throw new Error('Taskdoc suggestion worker returned unknown suggestions kind');
176
+ }
177
+ case 'error': {
178
+ const errorText = value['errorText'];
179
+ if (typeof errorText !== 'string') {
180
+ throw new Error('Taskdoc suggestion worker error message missing errorText');
181
+ }
182
+ return { kind: 'error', errorText };
183
+ }
184
+ default:
185
+ throw new Error(`Taskdoc suggestion worker returned unknown message kind: ${String(kind)}`);
186
+ }
187
+ }
188
+ function resolveTaskdocSuggestionWorkerEntrypointAbs() {
189
+ const distCandidate = path.resolve(__dirname, 'taskdoc-search-worker.js');
190
+ if (fs_1.default.existsSync(distCandidate)) {
191
+ return { ok: true, kind: 'compiled_js', scriptAbs: distCandidate };
192
+ }
193
+ const tsCandidate = path.resolve(__dirname, 'taskdoc-search-worker.ts');
194
+ if (fs_1.default.existsSync(tsCandidate)) {
195
+ const tsxCjsRegisterAbs = requireFn.resolve('tsx/cjs');
196
+ return { ok: true, kind: 'tsx_cjs_bridge', scriptAbs: tsCandidate, tsxCjsRegisterAbs };
197
+ }
198
+ return {
199
+ ok: false,
200
+ errorText: `Cannot find taskdoc suggestion worker entrypoint at ${distCandidate} or ${tsCandidate}`,
201
+ };
202
+ }
203
+ async function acquireTaskdocSuggestionWorkerSlot() {
204
+ if (activeTaskdocSuggestionWorkers < TASKDOC_SUGGESTION_MAX_WORKERS) {
205
+ activeTaskdocSuggestionWorkers += 1;
206
+ return;
207
+ }
208
+ if (pendingTaskdocSuggestionWorkerSlots.length >= TASKDOC_SUGGESTION_MAX_PENDING_WORKERS) {
209
+ throw new Error('Taskdoc suggestion worker queue is full');
210
+ }
211
+ await new Promise((resolve) => {
212
+ const grantSlot = () => {
213
+ activeTaskdocSuggestionWorkers += 1;
214
+ resolve();
215
+ };
216
+ pendingTaskdocSuggestionWorkerSlots.push(grantSlot);
217
+ });
218
+ }
219
+ function releaseTaskdocSuggestionWorkerSlot() {
220
+ activeTaskdocSuggestionWorkers = Math.max(0, activeTaskdocSuggestionWorkers - 1);
221
+ const next = pendingTaskdocSuggestionWorkerSlots.shift();
222
+ if (next) {
223
+ next();
224
+ return;
225
+ }
226
+ }
227
+ async function runTaskDocumentSuggestionsWorker(payload) {
228
+ const entry = resolveTaskdocSuggestionWorkerEntrypointAbs();
229
+ if (!entry.ok)
230
+ return { kind: 'error', errorText: entry.errorText };
231
+ let acquiredWorkerSlot = false;
232
+ try {
233
+ await acquireTaskdocSuggestionWorkerSlot();
234
+ acquiredWorkerSlot = true;
235
+ const worker = entry.kind === 'compiled_js'
236
+ ? new worker_threads_1.Worker(entry.scriptAbs, { workerData: payload })
237
+ : new worker_threads_1.Worker('const { workerData } = require("worker_threads"); require(workerData.tsxCjsRegisterAbs); require(workerData.scriptAbs);', {
238
+ eval: true,
239
+ workerData: {
240
+ ...payload,
241
+ scriptAbs: entry.scriptAbs,
242
+ tsxCjsRegisterAbs: entry.tsxCjsRegisterAbs,
243
+ },
244
+ });
245
+ return await new Promise((resolve) => {
246
+ let settled = false;
247
+ const timeout = setTimeout(() => {
248
+ if (settled)
249
+ return;
250
+ settled = true;
251
+ releaseTaskdocSuggestionWorkerSlot();
252
+ void worker.terminate();
253
+ resolve({ kind: 'error', errorText: 'Taskdoc suggestion worker timed out' });
254
+ }, TASKDOC_SUGGESTION_WORKER_TIMEOUT_MS);
255
+ const settle = (result) => {
256
+ if (settled)
257
+ return;
258
+ settled = true;
259
+ clearTimeout(timeout);
260
+ releaseTaskdocSuggestionWorkerSlot();
261
+ void worker.terminate();
262
+ resolve(result);
263
+ };
264
+ worker.once('message', (raw) => {
265
+ try {
266
+ const message = parseWorkerMessage(raw);
267
+ if (message.kind === 'ok') {
268
+ settle(message.suggestions);
269
+ return;
270
+ }
271
+ settle({ kind: 'error', errorText: message.errorText });
272
+ }
273
+ catch (error) {
274
+ settle({
275
+ kind: 'error',
276
+ errorText: error instanceof Error ? error.message : 'Invalid taskdoc worker response',
277
+ });
278
+ }
279
+ });
280
+ worker.once('error', (error) => {
281
+ settle({ kind: 'error', errorText: error.message });
282
+ });
283
+ worker.once('exit', (code) => {
284
+ if (settled)
285
+ return;
286
+ settle({ kind: 'error', errorText: `Taskdoc suggestion worker exited with code ${code}` });
287
+ });
288
+ });
289
+ }
290
+ catch (error) {
291
+ if (acquiredWorkerSlot)
292
+ releaseTaskdocSuggestionWorkerSlot();
293
+ return {
294
+ kind: 'error',
295
+ errorText: error instanceof Error ? error.message : 'Failed to start taskdoc worker',
296
+ };
297
+ }
298
+ }
299
+ async function searchTaskDocumentSuggestionsInWorker(payload, options = {}) {
300
+ if (options.signal?.aborted) {
301
+ return { kind: 'error', errorText: 'Taskdoc suggestion request aborted' };
302
+ }
303
+ const cacheKey = buildTaskDocumentSuggestionCacheKey(payload);
304
+ const cached = readTaskDocumentSuggestionCache(cacheKey);
305
+ if (cached)
306
+ return cached;
307
+ let inFlight = taskdocSuggestionInFlightByCacheKey.get(cacheKey);
308
+ if (!inFlight) {
309
+ inFlight = runTaskDocumentSuggestionsWorker(payload)
310
+ .then((result) => {
311
+ writeTaskDocumentSuggestionCache(cacheKey, result);
312
+ return result;
313
+ })
314
+ .finally(() => {
315
+ taskdocSuggestionInFlightByCacheKey.delete(cacheKey);
316
+ });
317
+ taskdocSuggestionInFlightByCacheKey.set(cacheKey, inFlight);
318
+ }
319
+ return await waitForTaskDocumentSuggestionResult(inFlight, options.signal);
320
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const worker_threads_1 = require("worker_threads");
4
+ const taskdoc_search_1 = require("./taskdoc-search");
5
+ function isRecord(value) {
6
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7
+ }
8
+ function parseWorkerPayload(value) {
9
+ if (!isRecord(value)) {
10
+ throw new Error('Taskdoc suggestion worker payload must be an object');
11
+ }
12
+ const query = value['query'];
13
+ if (typeof query !== 'string') {
14
+ throw new Error('Taskdoc suggestion worker payload.query must be a string');
15
+ }
16
+ const rootDirRaw = value['rootDir'];
17
+ const limitRaw = value['limit'];
18
+ return {
19
+ query,
20
+ ...(typeof rootDirRaw === 'string' ? { rootDir: rootDirRaw } : {}),
21
+ ...(typeof limitRaw === 'number' ? { limit: limitRaw } : {}),
22
+ };
23
+ }
24
+ async function main() {
25
+ if (worker_threads_1.parentPort === null) {
26
+ throw new Error('Taskdoc suggestion worker requires a parentPort');
27
+ }
28
+ try {
29
+ const payload = parseWorkerPayload(worker_threads_1.workerData);
30
+ const suggestions = await (0, taskdoc_search_1.searchTaskDocumentSuggestionsInRtws)(payload);
31
+ const message = { kind: 'ok', suggestions };
32
+ worker_threads_1.parentPort.postMessage(message);
33
+ }
34
+ catch (error) {
35
+ const message = {
36
+ kind: 'error',
37
+ errorText: error instanceof Error ? error.message : 'Taskdoc suggestion worker failed',
38
+ };
39
+ worker_threads_1.parentPort.postMessage(message);
40
+ }
41
+ }
42
+ void main();
@@ -1,10 +1,19 @@
1
- export interface TaskDocumentSummary {
2
- path: string;
3
- relativePath: string;
4
- name: string;
5
- size: number;
6
- lastModified: string;
7
- }
1
+ /**
2
+ * Module: utils/taskdoc-search
3
+ *
4
+ * rtws (runtime workspace) search utilities for Taskdocs (`*.tsk/`).
5
+ *
6
+ * Taskdocs are encapsulated directories ending in `.tsk`. For search, we treat each `*.tsk/`
7
+ * directory as a single unit and do not recurse into it.
8
+ *
9
+ * Ignore rules:
10
+ * - Default ignore patterns skip common non-task directories (at any depth).
11
+ * - A `.taskdoc-ignore` file may exist in any directory. Its patterns apply to that directory's
12
+ * subtree only (like a simplified `.gitignore`).
13
+ * - Patterns are interpreted as paths relative to the `.taskdoc-ignore` directory (unless they
14
+ * start with `/`, in which case they are rtws-root-relative).
15
+ */
16
+ import type { TaskDocumentSuggestion, TaskDocumentSummary } from '@longrun-ai/kernel/types/taskdoc';
8
17
  export type ListTaskDocumentsResult = {
9
18
  kind: 'ok';
10
19
  taskDocuments: TaskDocumentSummary[];
@@ -12,8 +21,20 @@ export type ListTaskDocumentsResult = {
12
21
  kind: 'error';
13
22
  errorText: string;
14
23
  };
24
+ export type SearchTaskDocumentSuggestionsResult = {
25
+ kind: 'ok';
26
+ suggestions: TaskDocumentSuggestion[];
27
+ } | {
28
+ kind: 'error';
29
+ errorText: string;
30
+ };
15
31
  interface ListTaskDocumentsParams {
16
32
  rootDir?: string;
17
33
  }
34
+ interface SearchTaskDocumentSuggestionsParams extends ListTaskDocumentsParams {
35
+ query: string;
36
+ limit?: number;
37
+ }
18
38
  export declare function listTaskDocumentsInRtws(params?: ListTaskDocumentsParams): Promise<ListTaskDocumentsResult>;
39
+ export declare function searchTaskDocumentSuggestionsInRtws(params: SearchTaskDocumentSuggestionsParams): Promise<SearchTaskDocumentSuggestionsResult>;
19
40
  export {};
@@ -37,21 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.listTaskDocumentsInRtws = listTaskDocumentsInRtws;
40
- /**
41
- * Module: utils/taskdoc-search
42
- *
43
- * rtws (runtime workspace) search utilities for Taskdocs (`*.tsk/`).
44
- *
45
- * Taskdocs are encapsulated directories ending in `.tsk`. For search, we treat each `*.tsk/`
46
- * directory as a single unit and do not recurse into it.
47
- *
48
- * Ignore rules:
49
- * - Default ignore patterns skip common non-task directories (at any depth).
50
- * - A `.taskdoc-ignore` file may exist in any directory. Its patterns apply to that directory's
51
- * subtree only (like a simplified `.gitignore`).
52
- * - Patterns are interpreted as paths relative to the `.taskdoc-ignore` directory (unless they
53
- * start with `/`, in which case they are rtws-root-relative).
54
- */
40
+ exports.searchTaskDocumentSuggestionsInRtws = searchTaskDocumentSuggestionsInRtws;
55
41
  const time_1 = require("@longrun-ai/kernel/utils/time");
56
42
  const promises_1 = __importDefault(require("fs/promises"));
57
43
  const path = __importStar(require("path"));
@@ -64,6 +50,22 @@ const DEFAULT_IGNORE_PATTERNS = [
64
50
  function normalizePosixPath(p) {
65
51
  return p.replace(/\\/g, '/').replace(/^\.\//, '');
66
52
  }
53
+ function stripTaskdocSuffixForMatch(value) {
54
+ return value.endsWith('.tsk') ? value.slice(0, -4) : value;
55
+ }
56
+ function calculateTaskDocumentSuggestionScore(params) {
57
+ if (params.nameExactMatch)
58
+ return 100;
59
+ if (params.nameStartsWith)
60
+ return 90;
61
+ if (params.pathStartsWith)
62
+ return 80;
63
+ if (params.nameMatch)
64
+ return 70;
65
+ if (params.pathMatch)
66
+ return 60;
67
+ return 0;
68
+ }
67
69
  function escapeRegexLiteralChar(ch) {
68
70
  // Regex special chars: \ ^ $ . | ? * + ( ) [ ] { }
69
71
  if ('\\^$.|?*+()[]{}'.includes(ch))
@@ -293,3 +295,48 @@ async function listTaskDocumentsInRtws(params = {}) {
293
295
  return { kind: 'error', errorText: 'Failed to list Taskdocs' };
294
296
  }
295
297
  }
298
+ async function searchTaskDocumentSuggestionsInRtws(params) {
299
+ const normalizedQuery = stripTaskdocSuffixForMatch(params.query.trim().toLowerCase());
300
+ if (normalizedQuery === '')
301
+ return { kind: 'ok', suggestions: [] };
302
+ const listed = await listTaskDocumentsInRtws({ rootDir: params.rootDir });
303
+ if (listed.kind === 'error')
304
+ return listed;
305
+ const limit = typeof params.limit === 'number' && Number.isFinite(params.limit) && params.limit > 0
306
+ ? Math.floor(params.limit)
307
+ : 50;
308
+ const suggestions = listed.taskDocuments
309
+ .map((doc) => {
310
+ const normalizedRelativePathForMatch = stripTaskdocSuffixForMatch(doc.relativePath.toLowerCase());
311
+ const normalizedNameForMatch = stripTaskdocSuffixForMatch(doc.name.toLowerCase());
312
+ const nameMatch = normalizedNameForMatch.includes(normalizedQuery);
313
+ const pathMatch = normalizedRelativePathForMatch.includes(normalizedQuery);
314
+ if (!nameMatch && !pathMatch)
315
+ return null;
316
+ return {
317
+ doc,
318
+ score: calculateTaskDocumentSuggestionScore({
319
+ nameMatch,
320
+ pathMatch,
321
+ nameStartsWith: normalizedNameForMatch.startsWith(normalizedQuery),
322
+ pathStartsWith: normalizedRelativePathForMatch.startsWith(normalizedQuery),
323
+ nameExactMatch: normalizedNameForMatch === normalizedQuery,
324
+ }),
325
+ };
326
+ })
327
+ .filter((entry) => entry !== null)
328
+ .sort((a, b) => {
329
+ if (a.score !== b.score)
330
+ return b.score - a.score;
331
+ if (a.doc.name.length !== b.doc.name.length)
332
+ return a.doc.name.length - b.doc.name.length;
333
+ return a.doc.name.localeCompare(b.doc.name);
334
+ })
335
+ .slice(0, limit)
336
+ .map(({ doc }) => ({
337
+ path: doc.path,
338
+ relativePath: doc.relativePath,
339
+ name: doc.name,
340
+ }));
341
+ return { kind: 'ok', suggestions };
342
+ }
@@ -239,17 +239,40 @@ If you provided a regular file path (e.g. a \`.md\`), that is unexpected. Please
239
239
  .map((n) => `\`${n}\``)
240
240
  .join(', ')}`
241
241
  : '';
242
+ const missingSectionsZh = [
243
+ goalsStatus === 'missing' ? 'goals' : null,
244
+ constraintsStatus === 'missing' ? 'constraints' : null,
245
+ progressStatus === 'missing' ? 'progress' : null,
246
+ ].filter((section) => section !== null);
247
+ const presentSectionsZh = [
248
+ goalsStatus === 'present' ? 'goals' : null,
249
+ constraintsStatus === 'present' ? 'constraints' : null,
250
+ progressStatus === 'present' ? 'progress' : null,
251
+ ].filter((section) => section !== null);
252
+ const missingSectionLines = missingSectionsZh.length === 0
253
+ ? []
254
+ : [
255
+ ``,
256
+ `缺失的常驻分段请用函数工具 \`do_mind\` 创建(不要用通用文件工具):`,
257
+ ...missingSectionsZh.map((section) => `- \`do_mind({"selector":"${section}","content":"..."})\``),
258
+ ];
259
+ const presentSectionLine = presentSectionsZh.length === 0
260
+ ? []
261
+ : [
262
+ `- 已存在常驻分段(${presentSectionsZh.map((s) => `\`${s}\``).join(' / ')})需要追加少量条目时,使用 \`mind_more\`;需要整体改写/合并时,使用 \`change_mind\`。没有把握时不要覆盖,优先新增额外章节或追加小条目。`,
263
+ ];
242
264
  const maintenanceLine = isSideDialog
243
- ? `- 支线对话中不允许 \`mind_more\` / \`change_mind\`:需要更新时请诉请差遣牒维护人 @${taskdocMaintainerId} 执行更新,并提供要追加的条目或已合并好的“分段全文替换稿”(禁止覆盖/抹掉他人条目)。`
265
+ ? `- 支线对话中不允许 \`do_mind\` / \`mind_more\` / \`change_mind\` / \`never_mind\`:需要更新时请诉请差遣牒维护人 @${taskdocMaintainerId} 执行更新,并提供要新增的章节、要追加的条目、已合并好的“分段全文替换稿”或要删除的章节(禁止覆盖/抹掉他人条目)。`
244
266
  : `- 维护方式:少量新增条目优先用 \`mind_more({\"items\":[\"...\"]})\` 追加到 \`progress\`(也可指定 selector/category);需要删除陈旧项、重排或压缩时,用 \`change_mind\` 指定分段做整章替换。`;
245
267
  return [
246
268
  `**差遣牒结构(封装差遣牒 \`*.tsk/\`):**`,
247
269
  `- 我们的差遣牒是一个 \`*.tsk/\` 目录:顶层 3 个分段(\`goals\` / \`constraints\` / \`progress\`)一定会自动注入;\`bearinmind/\`(固定白名单)可选自动注入;其他章节不会自动注入,仅以“目录索引”形式提示并需用 \`recall_taskdoc\` 显式读取。`,
248
270
  `- 全队共享:三个分段对所有队友与支线对话可见。更新时禁止覆盖/抹掉他人条目;建议为自己维护的条目标注责任人(如 \`- [owner:@<id>] ...\`)。`,
249
271
  `- 章节语义约定:\`goals\` / \`constraints\` 是任务契约;\`progress\` 是全队共享、准实时、可扫读的任务公告牌,用于当前有效状态、关键决策、下一步与仍成立阻塞,不是“我当前在做什么”的个人笔记。`,
250
- `- 差遣牒维护人(负责执行 \`mind_more\` / \`change_mind\`):@${taskdocMaintainerId}`,
272
+ `- 差遣牒维护人(负责执行 \`do_mind\` / \`mind_more\` / \`change_mind\` / \`never_mind\`):@${taskdocMaintainerId}`,
251
273
  `- 重要:差遣牒内容已被系统以内联形式注入到上下文中(本程生成视角下即为最新)。请直接基于上下文里的差遣牒回顾与决策,不要试图用通用文件工具读取 \`*.tsk/\` 下的文件(会被拒绝)。`,
252
274
  maintenanceLine,
275
+ ...presentSectionLine,
253
276
  ``,
254
277
  `**分段状态:**`,
255
278
  `- \`goals.md\`:${goalsZh}`,
@@ -259,16 +282,34 @@ If you provided a regular file path (e.g. a \`.md\`), that is unexpected. Please
259
282
  ...(bearExtrasLine ? [bearExtrasLine] : []),
260
283
  ``,
261
284
  extraSectionsBlock,
262
- ``,
263
- `若某个分段缺失,请用函数工具 \`change_mind\` 创建(不要用通用文件工具):`,
264
- `- \`change_mind({\"selector\":\"goals\",\"content\":\"...\"})\``,
265
- `- \`change_mind({\"selector\":\"constraints\",\"content\":\"...\"})\``,
266
- `- \`change_mind({\"selector\":\"progress\",\"content\":\"...\"})\``,
285
+ ...missingSectionLines,
267
286
  ...(violationsBlock ? ['', violationsBlock] : []),
268
287
  ].join('\n');
269
288
  }
289
+ const missingSectionsEn = [
290
+ goalsStatus === 'missing' ? 'goals' : null,
291
+ constraintsStatus === 'missing' ? 'constraints' : null,
292
+ progressStatus === 'missing' ? 'progress' : null,
293
+ ].filter((section) => section !== null);
294
+ const presentSectionsEn = [
295
+ goalsStatus === 'present' ? 'goals' : null,
296
+ constraintsStatus === 'present' ? 'constraints' : null,
297
+ progressStatus === 'present' ? 'progress' : null,
298
+ ].filter((section) => section !== null);
299
+ const missingSectionLines = missingSectionsEn.length === 0
300
+ ? []
301
+ : [
302
+ ``,
303
+ `Create missing resident sections with the function tool \`do_mind\` (never via general file tools):`,
304
+ ...missingSectionsEn.map((section) => `- \`do_mind({"selector":"${section}","content":"..."})\``),
305
+ ];
306
+ const presentSectionLine = presentSectionsEn.length === 0
307
+ ? []
308
+ : [
309
+ `- For existing resident sections (${presentSectionsEn.map((s) => `\`${s}\``).join(' / ')}), use \`mind_more\` to append small notes, and use \`change_mind\` for full-section rewrite/merge. If unsure, do not overwrite; prefer creating an extra section or appending small notes.`,
310
+ ];
270
311
  const maintenanceLine = isSideDialog
271
- ? `- Side Dialogs cannot call \`mind_more\` / \`change_mind\`: ask the Taskdoc maintainer @${taskdocMaintainerId} to apply updates, and provide entries to append or a fully merged full-section replacement draft (do not overwrite/delete other contributors).`
312
+ ? `- Side Dialogs cannot call \`do_mind\` / \`mind_more\` / \`change_mind\` / \`never_mind\`: ask the Taskdoc maintainer @${taskdocMaintainerId} to apply updates, and provide the new section to create, entries to append, a fully merged full-section replacement draft, or the section to delete (do not overwrite/delete other contributors).`
272
313
  : `- Maintenance: for small additions, prefer \`mind_more({\"items\":[\"...\"]})\` to append to \`progress\` (selector/category may be specified); when stale entries must be removed, reordered, or compressed, use \`change_mind\` for a full-section replacement.`;
273
314
  const bearEn = bearInMindStatus === 'absent'
274
315
  ? 'absent'
@@ -285,9 +326,10 @@ If you provided a regular file path (e.g. a \`.md\`), that is unexpected. Please
285
326
  `- Our Taskdoc is a \`*.tsk/\` directory: it always auto-injects the 3 top-level sections (\`goals\` / \`constraints\` / \`progress\`); it may auto-inject \`bearinmind/\` (fixed whitelist); any other sections are NOT auto-injected and must be read via \`recall_taskdoc\` (only an index is shown).`,
286
327
  `- Team-shared: all 3 sections are visible to teammates and sideDialogs. Do not overwrite/delete other contributors; add an owner marker for entries you maintain (e.g. \`- [owner:@<id>] ...\`).`,
287
328
  `- Section semantics: \`goals\` / \`constraints\` are the task contract; \`progress\` is the team-shared, quasi-real-time, scannable task bulletin board for current effective state, key decisions, next steps, and still-active blockers, not a personal “what I'm doing now” notebook.`,
288
- `- Taskdoc maintainer (runs \`mind_more\` / \`change_mind\`): @${taskdocMaintainerId}`,
329
+ `- Taskdoc maintainer (runs \`do_mind\` / \`mind_more\` / \`change_mind\` / \`never_mind\`): @${taskdocMaintainerId}`,
289
330
  `- Important: Taskdoc content is injected inline into the context (the latest as of this generation). Review the injected Taskdoc; do not try to read files under \`*.tsk/\` via general file tools (they will be rejected).`,
290
331
  maintenanceLine,
332
+ ...presentSectionLine,
291
333
  ``,
292
334
  `**Sections:**`,
293
335
  `- \`goals.md\`: ${goalsStatus}`,
@@ -297,11 +339,7 @@ If you provided a regular file path (e.g. a \`.md\`), that is unexpected. Please
297
339
  ...(bearExtrasLine ? [bearExtrasLine] : []),
298
340
  ``,
299
341
  extraSectionsBlock,
300
- ``,
301
- `If any section is missing, create it with the function tool \`change_mind\` (never via general file tools):`,
302
- `- \`change_mind({\"selector\":\"goals\",\"content\":\"...\"})\``,
303
- `- \`change_mind({\"selector\":\"constraints\",\"content\":\"...\"})\``,
304
- `- \`change_mind({\"selector\":\"progress\",\"content\":\"...\"})\``,
342
+ ...missingSectionLines,
305
343
  ...(violationsBlock ? ['', violationsBlock] : []),
306
344
  ].join('\n');
307
345
  })();
@@ -311,8 +349,8 @@ If you provided a regular file path (e.g. a \`.md\`), that is unexpected. Please
311
349
  if (bytes > maxSize) {
312
350
  if (language === 'zh') {
313
351
  const howToUpdate = isSideDialog
314
- ? `⚠️ **注意:** 差遣牒是封装的。不要用文件工具去读/写/列目录 \`*.tsk/\` 下的任何路径。\n支线对话中不允许 \`mind_more\` / \`change_mind\`:请诉请差遣牒维护人 @${taskdocMaintainerId} 执行更新,并提供要追加的条目或合并好的“分段全文替换稿”(禁止覆盖/抹掉他人条目)。`
315
- : `⚠️ **注意:** 差遣牒是封装的。不要用文件工具去读/写/列目录 \`*.tsk/\` 下的任何路径。\n请在当前对话中用函数工具 \`mind_more\` 追加少量条目,或用 \`change_mind\` 做整章替换;禁止覆盖/抹掉他人条目。`;
352
+ ? `⚠️ **注意:** 差遣牒是封装的。不要用文件工具去读/写/列目录 \`*.tsk/\` 下的任何路径。\n支线对话中不允许 \`do_mind\` / \`mind_more\` / \`change_mind\` / \`never_mind\`:请诉请差遣牒维护人 @${taskdocMaintainerId} 执行更新,并提供要新增的章节、要追加的条目、合并好的“分段全文替换稿”或要删除的章节(禁止覆盖/抹掉他人条目)。`
353
+ : `⚠️ **注意:** 差遣牒是封装的。不要用文件工具去读/写/列目录 \`*.tsk/\` 下的任何路径。\n请在当前对话中用函数工具 \`mind_more\` 追加少量条目,或用 \`change_mind\` 做整章替换;缺失章节用 \`do_mind\` 创建;禁止覆盖/抹掉他人条目。`;
316
354
  return {
317
355
  type: 'environment_msg',
318
356
  role: 'user',
@@ -326,8 +364,8 @@ ${howToUpdate}`,
326
364
  };
327
365
  }
328
366
  const howToUpdate = isSideDialog
329
- ? `⚠️ **Note:** Taskdocs are encapsulated. Do not use file tools to read/write/list anything under \`*.tsk/\`.\nSide Dialogs cannot call \`mind_more\` / \`change_mind\`; ask the Taskdoc maintainer @${taskdocMaintainerId} with entries to append or a merged full-section replacement draft (do not overwrite/delete other contributors).`
330
- : `⚠️ **Note:** Taskdocs are encapsulated. Do not use file tools to read/write/list anything under \`*.tsk/\`.\nIn this dialog, use \`mind_more\` for small append-only updates, or \`change_mind\` for full-section replacements; do not overwrite/delete other contributors.`;
367
+ ? `⚠️ **Note:** Taskdocs are encapsulated. Do not use file tools to read/write/list anything under \`*.tsk/\`.\nSide Dialogs cannot call \`do_mind\` / \`mind_more\` / \`change_mind\` / \`never_mind\`; ask the Taskdoc maintainer @${taskdocMaintainerId} with the new section to create, entries to append, a merged full-section replacement draft, or the section to delete (do not overwrite/delete other contributors).`
368
+ : `⚠️ **Note:** Taskdocs are encapsulated. Do not use file tools to read/write/list anything under \`*.tsk/\`.\nIn this dialog, use \`mind_more\` for small append-only updates, or \`change_mind\` for full-section replacements; create missing sections with \`do_mind\`; do not overwrite/delete other contributors.`;
331
369
  return {
332
370
  type: 'environment_msg',
333
371
  role: 'user',
@@ -342,8 +380,8 @@ ${howToUpdate}`,
342
380
  }
343
381
  if (language === 'zh') {
344
382
  const footerLine = isSideDialog
345
- ? `*支线对话中不允许 \`mind_more\` / \`change_mind\`:请诉请差遣牒维护人 @${taskdocMaintainerId} 执行更新,并提供要追加的条目或合并好的“分段全文替换稿”(禁止覆盖/抹掉他人条目)。*`
346
- : `*在当前对话中用 \`mind_more\` 追加少量条目,或用 \`change_mind\` 替换整段;更新时禁止覆盖他人条目。*`;
383
+ ? `*支线对话中不允许 \`do_mind\` / \`mind_more\` / \`change_mind\` / \`never_mind\`:请诉请差遣牒维护人 @${taskdocMaintainerId} 执行更新,并提供要新增的章节、要追加的条目、合并好的“分段全文替换稿”或要删除的章节(禁止覆盖/抹掉他人条目)。*`
384
+ : `*在当前对话中用 \`mind_more\` 追加少量条目,或用 \`change_mind\` 替换整段;缺失章节用 \`do_mind\` 创建;更新时禁止覆盖他人条目。*`;
347
385
  return {
348
386
  type: 'environment_msg',
349
387
  role: 'user',
@@ -365,8 +403,8 @@ ${footerLine}
365
403
  };
366
404
  }
367
405
  const footerLine = isSideDialog
368
- ? `*Side Dialogs cannot call \`mind_more\` / \`change_mind\`; ask the Taskdoc maintainer @${taskdocMaintainerId} with entries to append or a merged full-section replacement draft (do not overwrite/delete other contributors).*`
369
- : `*In this dialog, use \`mind_more\` for small append-only updates, or \`change_mind\` for full-section replacements; do not overwrite other contributors.*`;
406
+ ? `*Side Dialogs cannot call \`do_mind\` / \`mind_more\` / \`change_mind\` / \`never_mind\`; ask the Taskdoc maintainer @${taskdocMaintainerId} with the new section to create, entries to append, a merged full-section replacement draft, or the section to delete (do not overwrite/delete other contributors).*`
407
+ : `*In this dialog, use \`mind_more\` for small append-only updates, or \`change_mind\` for full-section replacements; create missing sections with \`do_mind\`; do not overwrite other contributors.*`;
370
408
  return {
371
409
  type: 'environment_msg',
372
410
  role: 'user',