dominds 0.6.2 → 0.6.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 (149) hide show
  1. package/dist/access-control.js +2 -2
  2. package/dist/agent-priming.js +826 -92
  3. package/dist/cli/read.js +406 -12
  4. package/dist/dialog.js +4 -0
  5. package/dist/docs/design.md +1 -0
  6. package/dist/docs/design.zh.md +1 -0
  7. package/dist/docs/dialog-system.md +12 -7
  8. package/dist/docs/dialog-system.zh.md +7 -3
  9. package/dist/docs/dominds-agent-priming.md +10 -1
  10. package/dist/docs/dominds-agent-priming.zh.md +9 -1
  11. package/dist/docs/dominds-terminology.md +8 -8
  12. package/dist/docs/fbr-implementation.md +77 -0
  13. package/dist/docs/fbr-implementation.zh.md +77 -0
  14. package/dist/docs/fbr.md +142 -141
  15. package/dist/docs/fbr.zh.md +129 -123
  16. package/dist/docs/keep-going.zh.md +162 -0
  17. package/dist/docs/showing-by-doing.md +208 -0
  18. package/dist/docs/showing-by-doing.zh.md +177 -0
  19. package/dist/docs/tellask-collab.md +250 -0
  20. package/dist/docs/tellask-collab.zh.md +254 -0
  21. package/dist/docs/txt-editing-tools.md +2 -2
  22. package/dist/docs/txt-editing-tools.zh.md +2 -2
  23. package/dist/llm/defaults.yaml +82 -4
  24. package/dist/llm/driver.js +280 -104
  25. package/dist/llm/gen/codex.js +49 -2
  26. package/dist/log.js +385 -30
  27. package/dist/mcp/supervisor.js +113 -40
  28. package/dist/minds/builtin/pangu/persona.zh.md +2 -2
  29. package/dist/minds/load.js +49 -284
  30. package/dist/minds/minds-i18n.js +2 -2
  31. package/dist/minds/promptdocs.js +263 -0
  32. package/dist/minds/system-prompt-parts.js +231 -0
  33. package/dist/minds/system-prompt.js +190 -223
  34. package/dist/persistence.js +66 -1
  35. package/dist/server/websocket-handler.js +14 -0
  36. package/dist/shared/diligence.js +40 -6
  37. package/dist/shared/utils/inter-dialog-format.js +3 -5
  38. package/dist/showing-by-doing.js +34 -31
  39. package/dist/snippets/README.en.md +3 -0
  40. package/dist/static/assets/{_baseUniq-C9vbtHF9.js → _baseUniq-C7IpU2Uk.js} +2 -2
  41. package/dist/static/assets/{_baseUniq-C9vbtHF9.js.map → _baseUniq-C7IpU2Uk.js.map} +1 -1
  42. package/dist/static/assets/{arc-hulXG01i.js → arc-1bhQqjON.js} +2 -2
  43. package/dist/static/assets/{arc-hulXG01i.js.map → arc-1bhQqjON.js.map} +1 -1
  44. package/dist/static/assets/{architectureDiagram-VXUJARFQ-DdLIAMT5.js → architectureDiagram-VXUJARFQ-CkEi1QpB.js} +6 -6
  45. package/dist/static/assets/{architectureDiagram-VXUJARFQ-DdLIAMT5.js.map → architectureDiagram-VXUJARFQ-CkEi1QpB.js.map} +1 -1
  46. package/dist/static/assets/{blockDiagram-VD42YOAC-DACsx66C.js → blockDiagram-VD42YOAC-DaBQ5-pY.js} +7 -7
  47. package/dist/static/assets/{blockDiagram-VD42YOAC-DACsx66C.js.map → blockDiagram-VD42YOAC-DaBQ5-pY.js.map} +1 -1
  48. package/dist/static/assets/{c4Diagram-YG6GDRKO-Cd5xZlLy.js → c4Diagram-YG6GDRKO-ChUgpgkP.js} +3 -3
  49. package/dist/static/assets/{c4Diagram-YG6GDRKO-Cd5xZlLy.js.map → c4Diagram-YG6GDRKO-ChUgpgkP.js.map} +1 -1
  50. package/dist/static/assets/{channel-NQehis0Z.js → channel-CxvmwllM.js} +2 -2
  51. package/dist/static/assets/{channel-NQehis0Z.js.map → channel-CxvmwllM.js.map} +1 -1
  52. package/dist/static/assets/{chunk-4BX2VUAB-DZDPl76b.js → chunk-4BX2VUAB-CKsrU2yk.js} +2 -2
  53. package/dist/static/assets/{chunk-4BX2VUAB-DZDPl76b.js.map → chunk-4BX2VUAB-CKsrU2yk.js.map} +1 -1
  54. package/dist/static/assets/{chunk-55IACEB6-CFSRDUbl.js → chunk-55IACEB6-BAau9SFt.js} +2 -2
  55. package/dist/static/assets/{chunk-55IACEB6-CFSRDUbl.js.map → chunk-55IACEB6-BAau9SFt.js.map} +1 -1
  56. package/dist/static/assets/{chunk-B4BG7PRW-BqQQ9M_z.js → chunk-B4BG7PRW--IiJ7W1m.js} +5 -5
  57. package/dist/static/assets/{chunk-B4BG7PRW-BqQQ9M_z.js.map → chunk-B4BG7PRW--IiJ7W1m.js.map} +1 -1
  58. package/dist/static/assets/{chunk-DI55MBZ5-FiFzz1Gh.js → chunk-DI55MBZ5-B83KrPQj.js} +4 -4
  59. package/dist/static/assets/{chunk-DI55MBZ5-FiFzz1Gh.js.map → chunk-DI55MBZ5-B83KrPQj.js.map} +1 -1
  60. package/dist/static/assets/{chunk-FMBD7UC4-DqqtCyWK.js → chunk-FMBD7UC4-BlDXzeza.js} +2 -2
  61. package/dist/static/assets/{chunk-FMBD7UC4-DqqtCyWK.js.map → chunk-FMBD7UC4-BlDXzeza.js.map} +1 -1
  62. package/dist/static/assets/{chunk-QN33PNHL-F0laQQ-J.js → chunk-QN33PNHL-B596W_v7.js} +2 -2
  63. package/dist/static/assets/{chunk-QN33PNHL-F0laQQ-J.js.map → chunk-QN33PNHL-B596W_v7.js.map} +1 -1
  64. package/dist/static/assets/{chunk-QZHKN3VN-CWhEZPaV.js → chunk-QZHKN3VN-UBBCxgBb.js} +2 -2
  65. package/dist/static/assets/{chunk-QZHKN3VN-CWhEZPaV.js.map → chunk-QZHKN3VN-UBBCxgBb.js.map} +1 -1
  66. package/dist/static/assets/{chunk-TZMSLE5B-Dx9cnwUy.js → chunk-TZMSLE5B-D-wCX2wJ.js} +2 -2
  67. package/dist/static/assets/{chunk-TZMSLE5B-Dx9cnwUy.js.map → chunk-TZMSLE5B-D-wCX2wJ.js.map} +1 -1
  68. package/dist/static/assets/{classDiagram-2ON5EDUG-Dp-dyEGy.js → classDiagram-2ON5EDUG-DvtmzPcu.js} +6 -6
  69. package/dist/static/assets/{classDiagram-2ON5EDUG-Dp-dyEGy.js.map → classDiagram-2ON5EDUG-DvtmzPcu.js.map} +1 -1
  70. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-Dp-dyEGy.js → classDiagram-v2-WZHVMYZB-DvtmzPcu.js} +6 -6
  71. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-Dp-dyEGy.js.map → classDiagram-v2-WZHVMYZB-DvtmzPcu.js.map} +1 -1
  72. package/dist/static/assets/{clone-C6mKvxs5.js → clone-DgJ0ZR-k.js} +2 -2
  73. package/dist/static/assets/{clone-C6mKvxs5.js.map → clone-DgJ0ZR-k.js.map} +1 -1
  74. package/dist/static/assets/{cose-bilkent-S5V4N54A-Dbwh3GoX.js → cose-bilkent-S5V4N54A-DXMyFQvy.js} +2 -2
  75. package/dist/static/assets/{cose-bilkent-S5V4N54A-Dbwh3GoX.js.map → cose-bilkent-S5V4N54A-DXMyFQvy.js.map} +1 -1
  76. package/dist/static/assets/{dagre-6UL2VRFP-BD_6e0Uk.js → dagre-6UL2VRFP-BdaUG-j_.js} +7 -7
  77. package/dist/static/assets/{dagre-6UL2VRFP-BD_6e0Uk.js.map → dagre-6UL2VRFP-BdaUG-j_.js.map} +1 -1
  78. package/dist/static/assets/{diagram-PSM6KHXK-BWt7Q59-.js → diagram-PSM6KHXK-NLiqKBzn.js} +7 -7
  79. package/dist/static/assets/{diagram-PSM6KHXK-BWt7Q59-.js.map → diagram-PSM6KHXK-NLiqKBzn.js.map} +1 -1
  80. package/dist/static/assets/{diagram-QEK2KX5R-D0BvBR_a.js → diagram-QEK2KX5R-D-0fyvY_.js} +6 -6
  81. package/dist/static/assets/{diagram-QEK2KX5R-D0BvBR_a.js.map → diagram-QEK2KX5R-D-0fyvY_.js.map} +1 -1
  82. package/dist/static/assets/{diagram-S2PKOQOG-D8uRdKXp.js → diagram-S2PKOQOG-BQ_FU59m.js} +6 -6
  83. package/dist/static/assets/{diagram-S2PKOQOG-D8uRdKXp.js.map → diagram-S2PKOQOG-BQ_FU59m.js.map} +1 -1
  84. package/dist/static/assets/{erDiagram-Q2GNP2WA-CQoifjFq.js → erDiagram-Q2GNP2WA-DyftKeuC.js} +5 -5
  85. package/dist/static/assets/{erDiagram-Q2GNP2WA-CQoifjFq.js.map → erDiagram-Q2GNP2WA-DyftKeuC.js.map} +1 -1
  86. package/dist/static/assets/{flowDiagram-NV44I4VS-CGhdeaG8.js → flowDiagram-NV44I4VS-9SGefONA.js} +6 -6
  87. package/dist/static/assets/{flowDiagram-NV44I4VS-CGhdeaG8.js.map → flowDiagram-NV44I4VS-9SGefONA.js.map} +1 -1
  88. package/dist/static/assets/{ganttDiagram-JELNMOA3-D8W0wb9H.js → ganttDiagram-JELNMOA3-k_WLhf-r.js} +3 -3
  89. package/dist/static/assets/{ganttDiagram-JELNMOA3-D8W0wb9H.js.map → ganttDiagram-JELNMOA3-k_WLhf-r.js.map} +1 -1
  90. package/dist/static/assets/{gitGraphDiagram-NY62KEGX-ChHni_jP.js → gitGraphDiagram-NY62KEGX-3eoLlCOY.js} +7 -7
  91. package/dist/static/assets/{gitGraphDiagram-NY62KEGX-ChHni_jP.js.map → gitGraphDiagram-NY62KEGX-3eoLlCOY.js.map} +1 -1
  92. package/dist/static/assets/{graph-BWoi_FgC.js → graph-vUevIs4s.js} +3 -3
  93. package/dist/static/assets/{graph-BWoi_FgC.js.map → graph-vUevIs4s.js.map} +1 -1
  94. package/dist/static/assets/{index-th_praGg.js → index-BNBG2CE1.js} +399 -68
  95. package/dist/static/assets/index-BNBG2CE1.js.map +1 -0
  96. package/dist/static/assets/{infoDiagram-WHAUD3N6-B_XKKZTV.js → infoDiagram-WHAUD3N6-CwEhVxkU.js} +5 -5
  97. package/dist/static/assets/{infoDiagram-WHAUD3N6-B_XKKZTV.js.map → infoDiagram-WHAUD3N6-CwEhVxkU.js.map} +1 -1
  98. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-ChGuQ6T9.js → journeyDiagram-XKPGCS4Q-Dtdq4G4Q.js} +5 -5
  99. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-ChGuQ6T9.js.map → journeyDiagram-XKPGCS4Q-Dtdq4G4Q.js.map} +1 -1
  100. package/dist/static/assets/{kanban-definition-3W4ZIXB7-BjWe623u.js → kanban-definition-3W4ZIXB7-Bli-AycJ.js} +3 -3
  101. package/dist/static/assets/{kanban-definition-3W4ZIXB7-BjWe623u.js.map → kanban-definition-3W4ZIXB7-Bli-AycJ.js.map} +1 -1
  102. package/dist/static/assets/{layout-BPyT310w.js → layout-CGlA8c09.js} +5 -5
  103. package/dist/static/assets/{layout-BPyT310w.js.map → layout-CGlA8c09.js.map} +1 -1
  104. package/dist/static/assets/{linear-xUsVjXWq.js → linear-Da2jDWL3.js} +2 -2
  105. package/dist/static/assets/{linear-xUsVjXWq.js.map → linear-Da2jDWL3.js.map} +1 -1
  106. package/dist/static/assets/{min-xFt7zeOd.js → min-Co741hTV.js} +3 -3
  107. package/dist/static/assets/{min-xFt7zeOd.js.map → min-Co741hTV.js.map} +1 -1
  108. package/dist/static/assets/{mindmap-definition-VGOIOE7T-DT_dvf2c.js → mindmap-definition-VGOIOE7T-DvkIjoq8.js} +4 -4
  109. package/dist/static/assets/{mindmap-definition-VGOIOE7T-DT_dvf2c.js.map → mindmap-definition-VGOIOE7T-DvkIjoq8.js.map} +1 -1
  110. package/dist/static/assets/{pieDiagram-ADFJNKIX-B1DQ-OaG.js → pieDiagram-ADFJNKIX-BGuGhTu8.js} +7 -7
  111. package/dist/static/assets/{pieDiagram-ADFJNKIX-B1DQ-OaG.js.map → pieDiagram-ADFJNKIX-BGuGhTu8.js.map} +1 -1
  112. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-IHqyr3iT.js → quadrantDiagram-AYHSOK5B-DAZcrJMg.js} +3 -3
  113. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-IHqyr3iT.js.map → quadrantDiagram-AYHSOK5B-DAZcrJMg.js.map} +1 -1
  114. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-CKBpht7B.js → requirementDiagram-UZGBJVZJ-CXN0DxZs.js} +4 -4
  115. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-CKBpht7B.js.map → requirementDiagram-UZGBJVZJ-CXN0DxZs.js.map} +1 -1
  116. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-D2uGjv3i.js → sankeyDiagram-TZEHDZUN-B7-yAePZ.js} +2 -2
  117. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-D2uGjv3i.js.map → sankeyDiagram-TZEHDZUN-B7-yAePZ.js.map} +1 -1
  118. package/dist/static/assets/{sequenceDiagram-WL72ISMW-wLFRhAKd.js → sequenceDiagram-WL72ISMW-DfBNY6h_.js} +4 -4
  119. package/dist/static/assets/{sequenceDiagram-WL72ISMW-wLFRhAKd.js.map → sequenceDiagram-WL72ISMW-DfBNY6h_.js.map} +1 -1
  120. package/dist/static/assets/{stateDiagram-FKZM4ZOC-BFGQTbx5.js → stateDiagram-FKZM4ZOC-BLo1xRVY.js} +9 -9
  121. package/dist/static/assets/{stateDiagram-FKZM4ZOC-BFGQTbx5.js.map → stateDiagram-FKZM4ZOC-BLo1xRVY.js.map} +1 -1
  122. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-DF7AjJuk.js → stateDiagram-v2-4FDKWEC3-Dq7MAD0I.js} +5 -5
  123. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-DF7AjJuk.js.map → stateDiagram-v2-4FDKWEC3-Dq7MAD0I.js.map} +1 -1
  124. package/dist/static/assets/{timeline-definition-IT6M3QCI-ChHFOb0o.js → timeline-definition-IT6M3QCI-ySWyBF3b.js} +3 -3
  125. package/dist/static/assets/{timeline-definition-IT6M3QCI-ChHFOb0o.js.map → timeline-definition-IT6M3QCI-ySWyBF3b.js.map} +1 -1
  126. package/dist/static/assets/{treemap-KMMF4GRG-BxaNvQU4.js → treemap-KMMF4GRG-DOp4sqOh.js} +4 -4
  127. package/dist/static/assets/{treemap-KMMF4GRG-BxaNvQU4.js.map → treemap-KMMF4GRG-DOp4sqOh.js.map} +1 -1
  128. package/dist/static/assets/{xychartDiagram-PRI3JC2R-CrNKeY_-.js → xychartDiagram-PRI3JC2R-vkmh67qb.js} +3 -3
  129. package/dist/static/assets/{xychartDiagram-PRI3JC2R-CrNKeY_-.js.map → xychartDiagram-PRI3JC2R-vkmh67qb.js.map} +1 -1
  130. package/dist/static/index.html +1 -1
  131. package/dist/team.js +29 -6
  132. package/dist/tool.js +56 -0
  133. package/dist/tools/builtins.js +4 -2
  134. package/dist/tools/context-health.js +7 -7
  135. package/dist/tools/os.js +267 -30
  136. package/dist/tools/pending-tellask-reminder.js +185 -0
  137. package/dist/tools/plan.js +1 -0
  138. package/dist/tools/ripgrep.js +145 -4
  139. package/dist/tools/shell-tools.js +21 -0
  140. package/dist/tools/team-mgmt.js +4 -4
  141. package/dist/tools/toolset-manual.js +74 -0
  142. package/dist/utils/task-doc.js +16 -16
  143. package/package.json +1 -1
  144. package/dist/minds/builtin/cmdr/persona.md +0 -3
  145. package/dist/minds/builtin/dijiang/knowledge.md +0 -287
  146. package/dist/minds/builtin/dijiang/persona.md +0 -7
  147. package/dist/static/assets/index-th_praGg.js.map +0 -1
  148. package/dist/static/testing/dom-observation-utils.js +0 -425
  149. package/dist/static/testing/e2e-test-helper.js +0 -3119
@@ -52,7 +52,7 @@
52
52
  padding: 20px;
53
53
  }
54
54
  </style>
55
- <script type="module" crossorigin src="/assets/index-th_praGg.js"></script>
55
+ <script type="module" crossorigin src="/assets/index-BNBG2CE1.js"></script>
56
56
  <link rel="stylesheet" crossorigin href="/assets/index-DaIsSzC_.css">
57
57
  </head>
58
58
  <body>
package/dist/team.js CHANGED
@@ -249,9 +249,10 @@ exports.Team = Team;
249
249
  * Honors declaration order of toolsets and tools. Logs warnings for duplicate tool names
250
250
  * that resolve to different Tool objects. Returns no duplicate tools per name.
251
251
  */
252
- listResolvedToolsetNames() {
252
+ listResolvedToolsetNames(options) {
253
253
  if (!this.toolsets)
254
254
  return [];
255
+ const onMissing = options?.onMissing ?? 'warn';
255
256
  const excludedToolsets = new Set();
256
257
  for (const entry of this.toolsets) {
257
258
  if (entry.startsWith('!') && entry.length > 1) {
@@ -273,7 +274,9 @@ exports.Team = Team;
273
274
  continue;
274
275
  const tools = (0, registry_1.getToolset)(resolvedToolsetName);
275
276
  if (!tools) {
276
- log_1.log.warn(`Toolset '${resolvedToolsetName}' not found in registry for member '${this.id}'`);
277
+ if (onMissing === 'warn') {
278
+ log_1.log.warn(`Toolset '${resolvedToolsetName}' not found in registry for member '${this.id}'`);
279
+ }
277
280
  continue;
278
281
  }
279
282
  resolved.push(resolvedToolsetName);
@@ -282,11 +285,13 @@ exports.Team = Team;
282
285
  }
283
286
  return resolved;
284
287
  }
285
- listTools() {
288
+ listTools(options) {
286
289
  const toolMap = new Map();
287
290
  const seenNames = new Set();
291
+ const onMissingToolset = options?.onMissingToolset ?? 'warn';
292
+ const onMissingTool = options?.onMissingTool ?? 'warn';
288
293
  // Process toolsets (in declaration order)
289
- for (const toolsetName of this.listResolvedToolsetNames()) {
294
+ for (const toolsetName of this.listResolvedToolsetNames({ onMissing: onMissingToolset })) {
290
295
  const tools = (0, registry_1.getToolset)(toolsetName);
291
296
  if (!tools)
292
297
  continue;
@@ -307,7 +312,9 @@ exports.Team = Team;
307
312
  for (const toolName of this.tools) {
308
313
  const tool = (0, registry_1.getTool)(toolName);
309
314
  if (!tool) {
310
- log_1.log.warn(`Tool '${toolName}' not found in registry for member '${this.id}'`);
315
+ if (onMissingTool === 'warn') {
316
+ log_1.log.warn(`Tool '${toolName}' not found in registry for member '${this.id}'`);
317
+ }
311
318
  continue;
312
319
  }
313
320
  if (seenNames.has(toolName)) {
@@ -331,6 +338,12 @@ exports.Team = Team;
331
338
  id: 'defaulter',
332
339
  name: 'Defaulter',
333
340
  fbr_effort: 3,
341
+ // FBR defaults to tool-less web search policy, but users can override via
342
+ // member_defaults.fbr_model_params / members.<id>.fbr_model_params.
343
+ fbr_model_params: {
344
+ codex: { web_search: 'disabled' },
345
+ openai: { web_search: 'disabled' },
346
+ },
334
347
  });
335
348
  const fuxi = new Team.Member({
336
349
  id: 'fuxi',
@@ -380,7 +393,7 @@ exports.Team = Team;
380
393
  }
381
394
  function listShellTools(member) {
382
395
  const out = [];
383
- for (const t of member.listTools()) {
396
+ for (const t of member.listTools({ onMissingToolset: 'silent', onMissingTool: 'silent' })) {
384
397
  if (t.type !== 'func')
385
398
  continue;
386
399
  if (!isShellToolName(t.name))
@@ -723,6 +736,7 @@ exports.Team = Team;
723
736
  'reasoning_effort',
724
737
  'verbosity',
725
738
  'parallel_tool_calls',
739
+ 'web_search',
726
740
  ];
727
741
  Team.TEAM_YAML_MODEL_PARAMS_CODEX_KEYS = Team.TEAM_YAML_MODEL_PARAMS_OPENAI_KEYS;
728
742
  Team.TEAM_YAML_MODEL_PARAMS_ANTHROPIC_KEYS = [
@@ -756,6 +770,7 @@ exports.Team = Team;
756
770
  reasoning_effort: `Did you mean \`${atPrefix}.model_params.codex.reasoning_effort\` (preferred for provider: codex) or \`${atPrefix}.model_params.openai.reasoning_effort\`? (not supported at ${atPrefix} root)`,
757
771
  verbosity: `Did you mean \`${atPrefix}.model_params.codex.verbosity\` (preferred for provider: codex) or \`${atPrefix}.model_params.openai.verbosity\`? (not supported at ${atPrefix} root)`,
758
772
  parallel_tool_calls: `Did you mean \`${atPrefix}.model_params.codex.parallel_tool_calls\` (preferred for provider: codex) or \`${atPrefix}.model_params.openai.parallel_tool_calls\`? (not supported at ${atPrefix} root)`,
773
+ web_search: `Did you mean \`${atPrefix}.model_params.codex.web_search\` (preferred for provider: codex) or \`${atPrefix}.model_params.openai.web_search\`? (not supported at ${atPrefix} root)`,
759
774
  };
760
775
  const unknownAtMember = listUnknownKeys(memberObj, Team.TEAM_YAML_MEMBER_KEYS);
761
776
  if (unknownAtMember.length > 0) {
@@ -776,6 +791,7 @@ exports.Team = Team;
776
791
  reasoning_effort: `Did you mean \`${modelParamsAt}.codex.reasoning_effort\` (preferred for provider: codex) or \`${modelParamsAt}.openai.reasoning_effort\`?`,
777
792
  verbosity: `Did you mean \`${modelParamsAt}.codex.verbosity\` (preferred for provider: codex) or \`${modelParamsAt}.openai.verbosity\`?`,
778
793
  parallel_tool_calls: `Did you mean \`${modelParamsAt}.codex.parallel_tool_calls\` (preferred for provider: codex) or \`${modelParamsAt}.openai.parallel_tool_calls\`?`,
794
+ web_search: `Did you mean \`${modelParamsAt}.codex.web_search\` (preferred for provider: codex) or \`${modelParamsAt}.openai.web_search\`?`,
779
795
  temperature: `Did you mean \`${modelParamsAt}.codex.temperature\` / \`${modelParamsAt}.openai.temperature\` (or \`${modelParamsAt}.anthropic.temperature\`)?`,
780
796
  top_p: `Did you mean \`${modelParamsAt}.codex.top_p\` / \`${modelParamsAt}.openai.top_p\` (or \`${modelParamsAt}.anthropic.top_p\`)?`,
781
797
  max_tokens: `Did you mean \`${modelParamsAt}.max_tokens\` / \`${modelParamsAt}.general.max_tokens\` (provider-agnostic), or \`${modelParamsAt}.codex.max_tokens\` / \`${modelParamsAt}.openai.max_tokens\` / \`${modelParamsAt}.anthropic.max_tokens\`?`,
@@ -1351,6 +1367,13 @@ exports.Team = Team;
1351
1367
  verbosity !== 'high') {
1352
1368
  throw new Error(`Invalid ${at2}.verbosity: expected low|medium|high (got ${describeValueType(verbosity)})`);
1353
1369
  }
1370
+ const webSearch = params.web_search;
1371
+ if (webSearch !== undefined &&
1372
+ webSearch !== 'disabled' &&
1373
+ webSearch !== 'cached' &&
1374
+ webSearch !== 'live') {
1375
+ throw new Error(`Invalid ${at2}.web_search: expected disabled|cached|live (got ${describeValueType(webSearch)})`);
1376
+ }
1354
1377
  };
1355
1378
  const codex = obj.codex === undefined ? undefined : asRecord(obj.codex, `${at}.codex`);
1356
1379
  const openai = obj.openai === undefined ? undefined : asRecord(obj.openai, `${at}.openai`);
package/dist/tool.js CHANGED
@@ -44,11 +44,67 @@ function validateArgs(schema, args) {
44
44
  }
45
45
  return { ok: true };
46
46
  }
47
+ function isJsonPrimitiveValue(value) {
48
+ return (typeof value === 'string' ||
49
+ typeof value === 'number' ||
50
+ typeof value === 'boolean' ||
51
+ value === null);
52
+ }
53
+ function formatJsonPrimitiveValue(value) {
54
+ return JSON.stringify(value);
55
+ }
56
+ function describeShortValue(value) {
57
+ if (isJsonPrimitiveValue(value))
58
+ return formatJsonPrimitiveValue(value);
59
+ if (Array.isArray(value))
60
+ return '[array]';
61
+ if (typeof value === 'object' && value !== null)
62
+ return '[object]';
63
+ return JSON.stringify(value);
64
+ }
65
+ function formatEnumAllowedList(values, maxShown) {
66
+ const shown = values.slice(0, Math.max(0, Math.floor(maxShown)));
67
+ const shownText = shown.map((v) => formatJsonPrimitiveValue(v)).join(', ');
68
+ const remaining = values.length - shown.length;
69
+ if (remaining <= 0)
70
+ return shownText;
71
+ return shownText.length > 0
72
+ ? `${shownText}, ... (+${remaining} more)`
73
+ : `... (+${remaining} more)`;
74
+ }
47
75
  function validateValue(schema, value, path) {
48
76
  if (!isRecord(schema)) {
49
77
  // Unknown schema shape: don't block execution.
50
78
  return { ok: true };
51
79
  }
80
+ // Best-effort const validation (only supports primitive const).
81
+ // For complex/object const, keep permissive behavior.
82
+ if ('const' in schema && isJsonPrimitiveValue(schema.const)) {
83
+ const expected = schema.const;
84
+ if (!isJsonPrimitiveValue(value) || value !== expected) {
85
+ return {
86
+ ok: false,
87
+ error: `Field ${path} must be ${formatJsonPrimitiveValue(expected)}; got ${describeShortValue(value)}`,
88
+ };
89
+ }
90
+ }
91
+ // Best-effort enum validation (only supports primitive enums).
92
+ // For complex/object enums, keep permissive behavior.
93
+ if ('enum' in schema && Array.isArray(schema.enum)) {
94
+ const enumValues = schema.enum;
95
+ const allPrimitive = enumValues.every((v) => isJsonPrimitiveValue(v));
96
+ if (allPrimitive) {
97
+ const allowedValues = enumValues;
98
+ const allowed = isJsonPrimitiveValue(value) && allowedValues.some((v) => v === value);
99
+ if (!allowed) {
100
+ const allowedText = formatEnumAllowedList(allowedValues, 10);
101
+ return {
102
+ ok: false,
103
+ error: `Field ${path} must be one of ${allowedText}; got ${describeShortValue(value)}`,
104
+ };
105
+ }
106
+ }
107
+ }
52
108
  const typeValue = 'type' in schema ? schema.type : undefined;
53
109
  const schemaType = typeof typeValue === 'string'
54
110
  ? typeValue
@@ -15,6 +15,7 @@ const fs_1 = require("./fs");
15
15
  const mcp_1 = require("./mcp");
16
16
  const mem_1 = require("./mem");
17
17
  const os_1 = require("./os");
18
+ const pending_tellask_reminder_1 = require("./pending-tellask-reminder");
18
19
  const plan_1 = require("./plan");
19
20
  const registry_1 = require("./registry");
20
21
  const ripgrep_1 = require("./ripgrep");
@@ -178,8 +179,8 @@ for (const tool of team_mgmt_1.teamMgmtTools) {
178
179
  zh: 'Codex 风格工具(apply_patch + readonly_shell + update_plan)',
179
180
  },
180
181
  promptI18n: {
181
- en: 'Use `apply_patch` (Codex-style patch format) to modify files. Use `readonly_shell` for simple rtws (runtime workspace) inspection via a small allowlist (cat/rg/sed/ls/nl/wc/head/tail/stat/file/uname/whoami/id/echo/pwd/which/date/diff/realpath/readlink/printf/cut/sort/uniq/tr/awk/shasum/sha256sum/md5sum/uuid/git show/git status/git diff/git log/git blame/find/tree/jq; also supports: git -C <relative-path> <show|status|diff|log|blame> ...; also supports: cd <relative-path> && <allowed command...> (or ||)). Use `update_plan` to record/update the task plan (stored as a reminder). You are explicitly authorized to call `readonly_shell` yourself; do not delegate it to a shell specialist. Avoid multi-line script-style commands; single-line is preferred (|, &&, || are ok). Paths must be relative to the rtws (runtime workspace). `apply_patch` enforces Dominds directory allow/deny lists.',
182
- zh: '使用 `apply_patch`(Codex 风格 patch 格式)修改文件;使用 `readonly_shell` 做少量只读命令行检查(仅允许 cat/rg/sed/ls/nl/wc/head/tail/stat/file/uname/whoami/id/echo/pwd/which/date/diff/realpath/readlink/printf/cut/sort/uniq/tr/awk/shasum/sha256sum/md5sum/uuid/git show/git status/git diff/git log/git blame/find/tree/jq;另支持:git -C <相对路径> <show|status|diff|log|blame> ...;另支持:cd <相对路径> && <允许命令...>(或 ||))。使用 `update_plan` 记录/更新任务计划(作为 reminder 存储)。你已被明确授权自行调用 `readonly_shell`,不要把它委派给 shell 专员。不建议多行脚本式命令,优先单行(允许 |、&&、||)。路径必须相对 rtws(运行时工作区)根目录。`apply_patch` 会按成员的目录权限(allow/deny)做访问控制。',
182
+ en: 'Use `apply_patch` (Codex-style patch format) to modify files. Use `readonly_shell` for simple rtws (runtime workspace) inspection via its small allowlist; commands outside the allowlist are rejected. For node/python, only exact version probes are allowed (no scripts). Chains via |/&&/|| are validated segment-by-segment. Use `update_plan` to record/update the task plan (stored as a reminder). You are explicitly authorized to call `readonly_shell` yourself; do not delegate it to a shell specialist. Avoid multi-line script-style commands; single-line is preferred (|, &&, || are ok). Paths must be relative to the rtws (runtime workspace). Hard denies: `readonly_shell` refuses rtws-root `.minds/` and `.dialogs/`; `apply_patch` is subject to the same access-control (including hard denies for `*.tsk/`, `.minds/`, and rtws-root `.dialogs/`).',
183
+ zh: '使用 `apply_patch`(Codex 风格 patch 格式)修改文件;使用 `readonly_shell` 做少量只读命令行检查,仅允许白名单命令前缀,白名单之外的命令会被拒绝。对 node/python 仅允许版本探针(不允许脚本执行)。通过 |/&&/|| 串联命令时会按子命令逐段校验。使用 `update_plan` 记录/更新任务计划(作为 reminder 存储)。你已被明确授权自行调用 `readonly_shell`,不要把它委派给 shell 专员。不建议多行脚本式命令,优先单行(允许 |、&&、||)。路径必须相对 rtws(运行时工作区)根目录。硬拒绝点:`readonly_shell` 无条件拒绝访问 rtws root 的 `.minds/` 与 `.dialogs/`;`apply_patch` 也受相同的访问控制约束(包含对 `*.tsk/`、`.minds/`、rtws root `.dialogs/` 的硬拒绝)。',
183
184
  },
184
185
  });
185
186
  (0, registry_1.registerToolset)('team-mgmt', [...team_mgmt_1.teamMgmtTools]);
@@ -196,3 +197,4 @@ for (const tool of team_mgmt_1.teamMgmtTools) {
196
197
  // Register ReminderOwners
197
198
  (0, registry_1.registerReminderOwner)(os_1.shellCmdReminderOwner);
198
199
  (0, registry_1.registerReminderOwner)(mcp_1.mcpLeaseReminderOwner);
200
+ (0, registry_1.registerReminderOwner)(pending_tellask_reminder_1.pendingTellaskReminderOwner);
@@ -39,12 +39,12 @@ function formatContextHealthOwnerHeader(args) {
39
39
  ];
40
40
  if (!snapshot) {
41
41
  lines.push('- 状态:未知(尚未获取上下文统计)');
42
- lines.push('- 现在就做:用提醒项收敛关键细节(update_reminder)→ change_mind(progress) → clear_mind');
42
+ lines.push('- 优先动作:change_mind(progress) → clear_mind');
43
43
  return lines.join('\n');
44
44
  }
45
45
  if (snapshot.kind !== 'available') {
46
46
  lines.push('- 状态:未知(token 统计不可用)');
47
- lines.push('- 现在就做:用提醒项收敛关键细节(update_reminder)→ change_mind(progress) → clear_mind');
47
+ lines.push('- 优先动作:change_mind(progress) → clear_mind');
48
48
  return lines.join('\n');
49
49
  }
50
50
  switch (snapshot.level) {
@@ -53,16 +53,16 @@ function formatContextHealthOwnerHeader(args) {
53
53
  return lines.join('\n');
54
54
  }
55
55
  case 'caution': {
56
- lines.push('- 状态:🟡 黄(必须尽快清理)');
57
- lines.push('- 硬规程:先 update_reminder 收敛工作集 → 再 change_mind(progress) → 然后 clear_mind');
56
+ lines.push('- 状态:🟡 黄(警告)');
57
+ lines.push('- 优先动作:change_mind(progress) → clear_mind');
58
58
  return lines.join('\n');
59
59
  }
60
60
  case 'critical': {
61
- lines.push('- 状态:🔴 红(硬闸门)');
61
+ lines.push('- 状态:🔴 红(危险)');
62
62
  if (remainingGenTurns !== undefined) {
63
- lines.push(`- 倒数:剩余 ${remainingGenTurns} 次生成机会;到 0 系统将被动开启新一轮/新回合以保持稳定性`);
63
+ lines.push(`- 倒数:剩余 ${remainingGenTurns} 次生成机会;到 0 系统将自动开启新一轮以保持稳定性`);
64
64
  }
65
- lines.push('- 禁止继续推进实现:先 update_reminder 收敛工作集 → 再 change_mind(progress) → 然后 clear_mind');
65
+ lines.push('- 立刻:change_mind(progress) → clear_mind');
66
66
  return lines.join('\n');
67
67
  }
68
68
  default: {
package/dist/tools/os.js CHANGED
@@ -290,7 +290,7 @@ const readonlyShellSchema = {
290
290
  properties: {
291
291
  command: {
292
292
  type: 'string',
293
- description: 'Read-only shell command (allowed prefixes: cat, rg, sed, ls, nl, wc, head, tail, stat, file, uname, whoami, id, echo, pwd, which, date, diff, realpath, readlink, printf, cut, sort, uniq, tr, awk, shasum, sha256sum, md5sum, uuid, git show, git status, git diff, git log, git blame, find, tree, jq; also allows: git -C <relative-path> <show|status|diff|log|blame> ...; also allows: cd <relative-path> && <allowed command...> (or ||))',
293
+ description: 'Read-only shell command (allowed prefixes: cat, rg, sed, ls, nl, wc, head, tail, stat, file, uname, whoami, id, echo, pwd, which, date, diff, realpath, readlink, printf, cut, sort, uniq, tr, awk, shasum, sha256sum, md5sum, uuid, git show, git status, git diff, git log, git blame, find, tree, jq, true; exact version probes: node --version|-v, python3 --version|-V; also allows: git -C <relative-path> <show|status|diff|log|blame> ...; also allows: cd <relative-path> && <allowed command...> (or ||); command chains via |/&&/|| are validated segment-by-segment)',
294
294
  },
295
295
  timeout_ms: {
296
296
  type: 'number',
@@ -591,23 +591,69 @@ const readonlyShellAllowedPrefixes = [
591
591
  'find',
592
592
  'tree',
593
593
  'jq',
594
+ 'true',
594
595
  ];
595
- function isAllowedReadonlyShellCommand(command) {
596
- const trimmed = command.trimStart();
597
- return isAllowedReadonlyShellCommandInternal(trimmed, 0);
598
- }
599
- function isAllowedReadonlyShellCommandInternal(command, depth) {
600
- if (depth > 8)
596
+ function isAllowedReadonlyShellVersionProbe(command) {
597
+ const tokens = splitShellTokens(command);
598
+ if (tokens.length !== 2)
601
599
  return false;
600
+ const cmd = tokens[0]?.text ?? '';
601
+ const flag = tokens[1]?.text ?? '';
602
+ if (cmd === 'node')
603
+ return flag === '--version' || flag === '-v';
604
+ if (cmd === 'python3')
605
+ return flag === '--version' || flag === '-V';
606
+ return false;
607
+ }
608
+ function validateReadonlyShellCommand(command) {
609
+ return validateReadonlyShellCommandInternal(command.trimStart(), 0);
610
+ }
611
+ function validateReadonlyShellCommandInternal(command, depth) {
612
+ if (depth > 8) {
613
+ return {
614
+ ok: false,
615
+ failure: {
616
+ reason: 'MAX_DEPTH',
617
+ rejectedSegment: command.trim() === '' ? command : command.trim(),
618
+ },
619
+ };
620
+ }
602
621
  const trimmed = command.trimStart();
603
622
  if (trimmed.startsWith('cd ')) {
604
623
  const parsed = parseCdChain(trimmed);
605
- if (!parsed)
606
- return false;
624
+ if (!parsed) {
625
+ return { ok: false, failure: { reason: 'INVALID_CD_SYNTAX', rejectedSegment: trimmed } };
626
+ }
607
627
  const dir = parsed.dir.replace(/^["']|["']$/g, '');
608
- if (!isSafeRelativePath(dir))
609
- return false;
610
- return isAllowedReadonlyShellCommandInternal(parsed.rest, depth + 1);
628
+ if (!isSafeRelativePath(dir)) {
629
+ return {
630
+ ok: false,
631
+ failure: {
632
+ reason: 'UNSAFE_RELATIVE_PATH',
633
+ rejectedSegment: `cd ${parsed.dir}`,
634
+ },
635
+ };
636
+ }
637
+ return validateReadonlyShellCommandInternal(parsed.rest, depth + 1);
638
+ }
639
+ const chainParsed = splitTopLevelReadonlyShellChain(trimmed);
640
+ if (!chainParsed.ok) {
641
+ return {
642
+ ok: false,
643
+ failure: {
644
+ reason: chainParsed.reason,
645
+ rejectedSegment: chainParsed.rejectedSegment,
646
+ },
647
+ };
648
+ }
649
+ if (chainParsed.segments.length > 1) {
650
+ for (const segment of chainParsed.segments) {
651
+ const segmentValidation = validateReadonlyShellCommandInternal(segment, depth + 1);
652
+ if (!segmentValidation.ok) {
653
+ return segmentValidation;
654
+ }
655
+ }
656
+ return { ok: true };
611
657
  }
612
658
  if (trimmed.startsWith('git -C ')) {
613
659
  // Allow a narrow, read-only subset of `git -C <dir> <subcommand> ...` as long as <dir> looks
@@ -619,23 +665,129 @@ function isAllowedReadonlyShellCommandInternal(command, depth) {
619
665
  const dirRaw = tokens[2] ?? '';
620
666
  const dir = dirRaw.replace(/^["']|["']$/g, '');
621
667
  const subcommand = tokens[3] ?? '';
622
- if (isSafeRelativePath(dir)) {
623
- if (subcommand === 'show' ||
624
- subcommand === 'status' ||
625
- subcommand === 'diff' ||
626
- subcommand === 'log' ||
627
- subcommand === 'blame') {
628
- return true;
629
- }
668
+ if (!isSafeRelativePath(dir)) {
669
+ return { ok: false, failure: { reason: 'GIT_C_UNSAFE_PATH', rejectedSegment: trimmed } };
670
+ }
671
+ if (subcommand === 'show' ||
672
+ subcommand === 'status' ||
673
+ subcommand === 'diff' ||
674
+ subcommand === 'log' ||
675
+ subcommand === 'blame') {
676
+ return { ok: true };
630
677
  }
678
+ return {
679
+ ok: false,
680
+ failure: { reason: 'GIT_C_UNSUPPORTED_SUBCOMMAND', rejectedSegment: trimmed },
681
+ };
631
682
  }
683
+ return { ok: false, failure: { reason: 'GIT_C_INVALID', rejectedSegment: trimmed } };
684
+ }
685
+ if (isAllowedReadonlyShellVersionProbe(trimmed)) {
686
+ return { ok: true };
632
687
  }
633
688
  for (const prefix of readonlyShellAllowedPrefixes) {
634
689
  if (trimmed === prefix || trimmed.startsWith(`${prefix} `)) {
635
- return true;
690
+ return { ok: true };
636
691
  }
637
692
  }
638
- return false;
693
+ return { ok: false, failure: { reason: 'COMMAND_NOT_ALLOWLISTED', rejectedSegment: trimmed } };
694
+ }
695
+ function splitTopLevelReadonlyShellChain(command) {
696
+ const segments = [];
697
+ let quote = null;
698
+ let escape = false;
699
+ let segmentStart = 0;
700
+ const pushSegment = (endExclusive) => {
701
+ const segment = command.slice(segmentStart, endExclusive).trim();
702
+ if (segment === '')
703
+ return false;
704
+ segments.push(segment);
705
+ return true;
706
+ };
707
+ for (let i = 0; i < command.length; i++) {
708
+ const ch = command[i] ?? '';
709
+ if (escape) {
710
+ escape = false;
711
+ continue;
712
+ }
713
+ if (quote) {
714
+ if (ch === quote) {
715
+ quote = null;
716
+ }
717
+ else if (ch === '\\' && quote === '"') {
718
+ escape = true;
719
+ }
720
+ continue;
721
+ }
722
+ if (ch === '\\') {
723
+ escape = true;
724
+ continue;
725
+ }
726
+ if (ch === "'" || ch === '"') {
727
+ quote = ch;
728
+ continue;
729
+ }
730
+ const next = command[i + 1] ?? '';
731
+ if ((ch === '&' && next === '&') || (ch === '|' && next === '|')) {
732
+ if (!pushSegment(i)) {
733
+ return {
734
+ ok: false,
735
+ reason: 'CHAIN_PARSE_EMPTY_SEGMENT',
736
+ rejectedSegment: command.trim(),
737
+ };
738
+ }
739
+ i += 1;
740
+ segmentStart = i + 1;
741
+ continue;
742
+ }
743
+ if (ch === '|') {
744
+ if (!pushSegment(i)) {
745
+ return {
746
+ ok: false,
747
+ reason: 'CHAIN_PARSE_EMPTY_SEGMENT',
748
+ rejectedSegment: command.trim(),
749
+ };
750
+ }
751
+ segmentStart = i + 1;
752
+ continue;
753
+ }
754
+ if (ch === ';') {
755
+ return {
756
+ ok: false,
757
+ reason: 'CHAIN_PARSE_UNSUPPORTED_OPERATOR',
758
+ rejectedSegment: command.slice(segmentStart).trim() || command.trim(),
759
+ };
760
+ }
761
+ if (ch === '&') {
762
+ return {
763
+ ok: false,
764
+ reason: 'CHAIN_PARSE_UNSUPPORTED_OPERATOR',
765
+ rejectedSegment: command.slice(segmentStart).trim() || command.trim(),
766
+ };
767
+ }
768
+ }
769
+ if (quote) {
770
+ return {
771
+ ok: false,
772
+ reason: 'CHAIN_PARSE_UNTERMINATED_QUOTE',
773
+ rejectedSegment: command.trim(),
774
+ };
775
+ }
776
+ if (escape) {
777
+ return {
778
+ ok: false,
779
+ reason: 'CHAIN_PARSE_TRAILING_ESCAPE',
780
+ rejectedSegment: command.trim(),
781
+ };
782
+ }
783
+ if (!pushSegment(command.length)) {
784
+ return {
785
+ ok: false,
786
+ reason: 'CHAIN_PARSE_EMPTY_SEGMENT',
787
+ rejectedSegment: command.trim(),
788
+ };
789
+ }
790
+ return { ok: true, segments };
639
791
  }
640
792
  function isSafeRelativePath(dir) {
641
793
  const hasParentTraversal = /(^|[\\/])\.\.([\\/]|$)/.test(dir);
@@ -722,6 +874,80 @@ function splitShellTokens(command) {
722
874
  push();
723
875
  return out;
724
876
  }
877
+ function firstReadonlyShellToken(segment) {
878
+ const tokens = splitShellTokens(segment.trim());
879
+ return tokens[0]?.text ?? '';
880
+ }
881
+ function getReadonlyShellSuggestionEn(failure) {
882
+ const token = firstReadonlyShellToken(failure.rejectedSegment);
883
+ if (failure.reason === 'CHAIN_PARSE_UNSUPPORTED_OPERATOR' ||
884
+ failure.reason === 'CHAIN_PARSE_EMPTY_SEGMENT') {
885
+ return 'Use only `|`, `&&`, `||` for chaining. Example: `ls || true`.';
886
+ }
887
+ if (failure.reason === 'CHAIN_PARSE_UNTERMINATED_QUOTE' ||
888
+ failure.reason === 'CHAIN_PARSE_TRAILING_ESCAPE') {
889
+ return 'Fix shell quoting first, then run an allowlisted segment (for example: `ls` or `rg <pattern>`).';
890
+ }
891
+ if (failure.reason === 'INVALID_CD_SYNTAX' || failure.reason === 'UNSAFE_RELATIVE_PATH') {
892
+ return 'Use `cd <relative-path> && <allowed command...>`.';
893
+ }
894
+ if (failure.reason === 'GIT_C_INVALID' ||
895
+ failure.reason === 'GIT_C_UNSAFE_PATH' ||
896
+ failure.reason === 'GIT_C_UNSUPPORTED_SUBCOMMAND') {
897
+ return 'Use `git -C <relative-path> <show|status|diff|log|blame> ...`.';
898
+ }
899
+ if (failure.reason === 'MAX_DEPTH') {
900
+ return 'Reduce nested chaining depth (for example split into smaller `readonly_shell` calls).';
901
+ }
902
+ if (token === 'node')
903
+ return 'Only version probes are allowed: `node --version || true`.';
904
+ if (token === 'python3' || token === 'python') {
905
+ return 'Only version probes are allowed: `python3 --version || true`.';
906
+ }
907
+ if (token === 'false')
908
+ return 'Use `true` as fallback (for example: `ls || true`).';
909
+ if (token === 'git') {
910
+ return 'Use `git <show|status|diff|log|blame> ...` or `git -C <relative-path> <show|status|diff|log|blame> ...`.';
911
+ }
912
+ if (token === 'cd')
913
+ return 'Use `cd <relative-path> && <allowed command...>`.';
914
+ return 'Use an allowlisted read-only segment (for example: `ls`, `rg <pattern>`, or `git status`).';
915
+ }
916
+ function getReadonlyShellSuggestionZh(failure) {
917
+ const token = firstReadonlyShellToken(failure.rejectedSegment);
918
+ if (failure.reason === 'CHAIN_PARSE_UNSUPPORTED_OPERATOR' ||
919
+ failure.reason === 'CHAIN_PARSE_EMPTY_SEGMENT') {
920
+ return '仅使用 `|`、`&&`、`||` 串联;示例:`ls || true`。';
921
+ }
922
+ if (failure.reason === 'CHAIN_PARSE_UNTERMINATED_QUOTE' ||
923
+ failure.reason === 'CHAIN_PARSE_TRAILING_ESCAPE') {
924
+ return '先修正引号/转义,再执行白名单子命令(例如:`ls` 或 `rg <pattern>`)。';
925
+ }
926
+ if (failure.reason === 'INVALID_CD_SYNTAX' || failure.reason === 'UNSAFE_RELATIVE_PATH') {
927
+ return '请使用 `cd <相对路径> && <允许命令...>`。';
928
+ }
929
+ if (failure.reason === 'GIT_C_INVALID' ||
930
+ failure.reason === 'GIT_C_UNSAFE_PATH' ||
931
+ failure.reason === 'GIT_C_UNSUPPORTED_SUBCOMMAND') {
932
+ return '请使用 `git -C <相对路径> <show|status|diff|log|blame> ...`。';
933
+ }
934
+ if (failure.reason === 'MAX_DEPTH') {
935
+ return '请降低链式嵌套深度(可拆成多次 `readonly_shell` 调用)。';
936
+ }
937
+ if (token === 'node')
938
+ return '仅允许版本探针:`node --version || true`。';
939
+ if (token === 'python3' || token === 'python') {
940
+ return '仅允许版本探针:`python3 --version || true`。';
941
+ }
942
+ if (token === 'false')
943
+ return '兜底请用 `true`(例如:`ls || true`)。';
944
+ if (token === 'git') {
945
+ return '可改为 `git <show|status|diff|log|blame> ...`,或 `git -C <相对路径> <show|status|diff|log|blame> ...`。';
946
+ }
947
+ if (token === 'cd')
948
+ return '可改为 `cd <相对路径> && <允许命令...>`。';
949
+ return '请改用白名单只读子命令(例如:`ls`、`rg <pattern>`、`git status`)。';
950
+ }
725
951
  function normalizeRelFromRtwsRoot(relPath) {
726
952
  return relPath.replace(/\\/g, '/').replace(/^\/+/, '');
727
953
  }
@@ -892,10 +1118,10 @@ function detectReadonlyShellForbiddenHiddenDirAccess(workspaceRootAbs, command)
892
1118
  exports.readonlyShellTool = {
893
1119
  type: 'func',
894
1120
  name: 'readonly_shell',
895
- description: 'Execute a read-only shell command from a small allowlist (cat, rg, sed, ls, nl, wc, head, tail, stat, file, uname, whoami, id, echo, pwd, which, date, diff, realpath, readlink, printf, cut, sort, uniq, tr, awk, shasum, sha256sum, md5sum, uuid, git show, git status, git diff, git log, git blame, find, tree, jq; also supports: git -C <relative-path> <show|status|diff|log|blame> ...; also supports: cd <relative-path> && <allowed command...> (or ||)). Commands outside the allowlist are rejected.',
1121
+ description: 'Execute a read-only shell command from a small allowlist. Only exact version probes are allowed for node/python (no scripts such as `node -e` or `python3 -c`). Command chains via |/&&/|| are validated segment-by-segment. Commands outside the allowlist are rejected.',
896
1122
  descriptionI18n: {
897
- en: 'Execute a read-only shell command from a small allowlist (cat, rg, sed, ls, nl, wc, head, tail, stat, file, uname, whoami, id, echo, pwd, which, date, diff, realpath, readlink, printf, cut, sort, uniq, tr, awk, shasum, sha256sum, md5sum, uuid, git show, git status, git diff, git log, git blame, find, tree, jq; also supports: git -C <relative-path> <show|status|diff|log|blame> ...; also supports: cd <relative-path> && <allowed command...> (or ||)). You are explicitly authorized to call this tool yourself (no delegation). Commands outside the allowlist are rejected.',
898
- zh: '执行只读 shell 命令(仅允许:cat、rg、sed、ls、nl、wc、head、tail、stat、file、uname、whoami、id、echo、pwd、which、date、diff、realpath、readlink、printf、cut、sort、uniq、tr、awk、shasum、sha256sum、md5sum、uuid、git show、git status、git diff、git log、git blame、find、tree、jq;另支持:git -C <相对路径> <show|status|diff|log|blame> ...;另支持:cd <相对路径> && <允许命令...>(或 ||))。你已被明确授权自行调用该工具(无需委派)。不在允许列表内的命令会被拒绝。',
1123
+ en: 'Execute a read-only shell command from a small allowlist. Only exact version probes are allowed for node/python (no scripts such as `node -e` or `python3 -c`). Command chains via |/&&/|| are validated segment-by-segment. You are explicitly authorized to call this tool yourself (no delegation). Commands outside the allowlist are rejected.',
1124
+ zh: '执行只读 shell 命令,仅允许少量白名单命令前缀。对 node/python 仅允许版本探针(不允许 `node -e` / `python3 -c` 这类脚本)。通过 |/&&/|| 串联时会按子命令逐段校验。你已被明确授权自行调用该工具(无需委派)。不在允许列表内的命令会被拒绝。',
899
1125
  },
900
1126
  parameters: readonlyShellSchema,
901
1127
  async call(dlg, caller, args) {
@@ -908,17 +1134,28 @@ exports.readonlyShellTool = {
908
1134
  ? `❌ readonly_shell 不建议执行多行脚本式命令(检测到换行符)。请用单行命令(允许 |、&&、||)。\n收到:${command}`
909
1135
  : `❌ readonly_shell does not allow multi-line script-style commands (newline detected). Use a single-line command (|, &&, || are allowed).\nGot: ${command}`;
910
1136
  }
911
- if (!isAllowedReadonlyShellCommand(command)) {
1137
+ const validation = validateReadonlyShellCommand(command);
1138
+ if (!validation.ok) {
912
1139
  const allowedList = readonlyShellAllowedPrefixes.join(', ');
1140
+ const rejectedSegment = validation.failure.rejectedSegment.trim();
1141
+ const rejectedSegmentOrCommand = rejectedSegment === '' ? command : rejectedSegment;
1142
+ const suggestion = language === 'zh'
1143
+ ? getReadonlyShellSuggestionZh(validation.failure)
1144
+ : getReadonlyShellSuggestionEn(validation.failure);
913
1145
  return language === 'zh'
914
- ? `❌ readonly_shell 仅允许以下命令前缀:${allowedList}\n另外允许:git -C <相对路径> <show|status|diff|log|blame> ...\n另外允许:cd <相对路径> && <允许命令...>(或 ||)\n收到:${command}`
915
- : `❌ readonly_shell only allows these command prefixes: ${allowedList}\nAlso allowed: git -C <relative-path> <show|status|diff|log|blame> ...\nAlso allowed: cd <relative-path> && <allowed command...> (or ||)\nGot: ${command}`;
1146
+ ? `❌ readonly_shell 仅允许以下命令前缀:${allowedList}\n另外允许(仅版本探针):node --version|-v、python3 --version|-V\n脚本执行(如 node -e / python3 -c)一律拒绝。\n另外允许:git -C <相对路径> <show|status|diff|log|blame> ...\n另外允许:cd <相对路径> && <允许命令...>(或 ||)\n说明:通过 |/&&/|| 串联时会按子命令逐段校验。\n被拒子命令段:${rejectedSegmentOrCommand}\n允许的等价写法:${suggestion}\n收到:${command}`
1147
+ : `❌ readonly_shell only allows these command prefixes: ${allowedList}\nAlso allowed (exact version probes only): node --version|-v, python3 --version|-V\nNode/python scripts (for example: node -e, python3 -c) are rejected.\nAlso allowed: git -C <relative-path> <show|status|diff|log|blame> ...\nAlso allowed: cd <relative-path> && <allowed command...> (or ||)\nNote: chains via |/&&/|| are validated segment-by-segment.\nRejected segment: ${rejectedSegmentOrCommand}\nAllowed equivalent: ${suggestion}\nGot: ${command}`;
916
1148
  }
917
1149
  const forbiddenHiddenDir = detectReadonlyShellForbiddenHiddenDirAccess(path_1.default.resolve(process.cwd()), command);
918
1150
  if (forbiddenHiddenDir) {
1151
+ if (forbiddenHiddenDir === '.minds') {
1152
+ return language === 'zh'
1153
+ ? `❌ **访问被拒绝**\n\n- 工具:\`readonly_shell\`\n- 路径:\`.minds/\`\n- 代码:\`ACCESS_DENIED\`\n\n说明:\`.minds/\` 是 rtws 根目录下的保留目录,readonly_shell 无条件拒绝访问。\n\n提示:\n- 若团队配置了 \`team-mgmt\` 工具集,请使用其中工具(\`team_mgmt_*\`)代管 \`.minds/**\`。\n- 若团队未配置该工具集或你不具备权限,请诉请具备 \`team-mgmt\` 权限的成员/团队管理员成员代管。\n- 若需要排查 Dominds,请在子目录 rtws 下复现(例如 \`ux-rtws/.dialogs/**\`)。`
1154
+ : `❌ **Access Denied**\n\n- Tool: \`readonly_shell\`\n- Path: \`.minds/\`\n- Code: \`ACCESS_DENIED\`\n\nNote: \`.minds/\` is a reserved directory at the rtws root; readonly_shell hard-denies access.\n\nHints:\n- If your team configured the \`team-mgmt\` toolset, use its tools (\`team_mgmt_*\`) to manage \`.minds/**\`.\n- If the toolset is not configured or you don\'t have permission, tellask a team-admin / a member with \`team-mgmt\` access to manage it for you.\n- For Dominds debugging, reproduce under a nested rtws (e.g. \`ux-rtws/.dialogs/**\`).`;
1155
+ }
919
1156
  return language === 'zh'
920
- ? `❌ **访问被拒绝**\n\n- 工具:\`readonly_shell\`\n- 路径:\`${forbiddenHiddenDir}/\`\n- 代码:\`ACCESS_DENIED\`\n\n说明:\`${forbiddenHiddenDir}/\` 是 rtws 根目录下的保留目录,readonly_shell 无条件拒绝访问。\n\n提示:\n- 若需要访问 \`.minds/**\`,请使用 \`team_mgmt_*\` 工具。\n- 若需要排查 Dominds,请在子目录 rtws 下复现(例如 \`ux-rtws/.dialogs/**\`)。`
921
- : `❌ **Access Denied**\n\n- Tool: \`readonly_shell\`\n- Path: \`${forbiddenHiddenDir}/\`\n- Code: \`ACCESS_DENIED\`\n\nNote: \`${forbiddenHiddenDir}/\` is a reserved directory at the rtws root; readonly_shell hard-denies access.\n\nHints:\n- To access \`.minds/**\`, use \`team_mgmt_*\` tools.\n- For Dominds debugging, reproduce under a nested rtws (e.g. \`ux-rtws/.dialogs/**\`).`;
1157
+ ? `❌ **访问被拒绝**\n\n- 工具:\`readonly_shell\`\n- 路径:\`.dialogs/\`\n- 代码:\`ACCESS_DENIED\`\n\n说明:\`.dialogs/\` 是 rtws 根目录下的保留目录,readonly_shell 无条件拒绝访问。\n\n提示:\n- 若需要排查 Dominds,请在子目录 rtws 下复现(例如 \`ux-rtws/.dialogs/**\`)。`
1158
+ : `❌ **Access Denied**\n\n- Tool: \`readonly_shell\`\n- Path: \`.dialogs/\`\n- Code: \`ACCESS_DENIED\`\n\nNote: \`.dialogs/\` is a reserved directory at the rtws root; readonly_shell hard-denies access.\n\nHints:\n- For Dominds debugging, reproduce under a nested rtws (e.g. \`ux-rtws/.dialogs/**\`).`;
922
1159
  }
923
1160
  const stdoutBuffer = new HeadTailByteBuffer(1024 * 1024);
924
1161
  const stderrBuffer = new HeadTailByteBuffer(1024 * 1024);