dominds 1.3.1 → 1.4.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.
- package/README.md +9 -2
- package/README.zh.md +9 -2
- package/dist/apps/app-lock-file.js +33 -57
- package/dist/apps/configuration-file.js +267 -0
- package/dist/apps/enabled-apps.js +217 -252
- package/dist/apps/manifest.js +132 -0
- package/dist/apps/resolution-file.js +80 -192
- package/dist/apps/workspace-app-state.js +8 -0
- package/dist/cli/disable.js +18 -22
- package/dist/cli/enable.js +20 -58
- package/dist/cli/install.js +74 -80
- package/dist/cli/uninstall.js +48 -25
- package/dist/cli/update.js +36 -80
- package/dist/docs/app-constitution.md +12 -7
- package/dist/docs/app-constitution.zh.md +13 -8
- package/dist/llm/kernel-driver/engine.js +10 -4
- package/dist/llm/kernel-driver/flow.js +93 -0
- package/dist/llm/kernel-driver/loop.js +4 -1
- package/dist/llm/kernel-driver/subdialog.js +5 -1
- package/dist/llm/kernel-driver/tellask-special.js +48 -6
- package/dist/server/server-core.js +19 -4
- package/dist/server/websocket-handler.js +26 -6
- package/dist/static/assets/{_basePickBy-CmziIRM9.js → _basePickBy-B2o4z1Hf.js} +3 -3
- package/dist/static/assets/{_basePickBy-CmziIRM9.js.map → _basePickBy-B2o4z1Hf.js.map} +1 -1
- package/dist/static/assets/{_baseUniq-CgDZxcD0.js → _baseUniq-CLmcxjdl.js} +2 -2
- package/dist/static/assets/{_baseUniq-CgDZxcD0.js.map → _baseUniq-CLmcxjdl.js.map} +1 -1
- package/dist/static/assets/{arc-Df9rjNNk.js → arc-CymD_KN7.js} +2 -2
- package/dist/static/assets/{arc-Df9rjNNk.js.map → arc-CymD_KN7.js.map} +1 -1
- package/dist/static/assets/{architectureDiagram-VXUJARFQ-Bif8topC.js → architectureDiagram-VXUJARFQ-DJQfSJUH.js} +7 -7
- package/dist/static/assets/{architectureDiagram-VXUJARFQ-Bif8topC.js.map → architectureDiagram-VXUJARFQ-DJQfSJUH.js.map} +1 -1
- package/dist/static/assets/{blockDiagram-VD42YOAC-D9Egoflr.js → blockDiagram-VD42YOAC-pHVz60D0.js} +7 -7
- package/dist/static/assets/{blockDiagram-VD42YOAC-D9Egoflr.js.map → blockDiagram-VD42YOAC-pHVz60D0.js.map} +1 -1
- package/dist/static/assets/{c4Diagram-YG6GDRKO-DBf1NeBf.js → c4Diagram-YG6GDRKO-B0WnCfAT.js} +3 -3
- package/dist/static/assets/{c4Diagram-YG6GDRKO-DBf1NeBf.js.map → c4Diagram-YG6GDRKO-B0WnCfAT.js.map} +1 -1
- package/dist/static/assets/{channel-Dc34yAJl.js → channel-CX9BlKil.js} +2 -2
- package/dist/static/assets/{channel-Dc34yAJl.js.map → channel-CX9BlKil.js.map} +1 -1
- package/dist/static/assets/{chunk-4BX2VUAB-B65G1dJI.js → chunk-4BX2VUAB-lXArRj3o.js} +2 -2
- package/dist/static/assets/{chunk-4BX2VUAB-B65G1dJI.js.map → chunk-4BX2VUAB-lXArRj3o.js.map} +1 -1
- package/dist/static/assets/{chunk-55IACEB6-CSDEOGl2.js → chunk-55IACEB6-CdqwynH9.js} +2 -2
- package/dist/static/assets/{chunk-55IACEB6-CSDEOGl2.js.map → chunk-55IACEB6-CdqwynH9.js.map} +1 -1
- package/dist/static/assets/{chunk-B4BG7PRW-Cb6W3QWJ.js → chunk-B4BG7PRW-Y-uXcJst.js} +5 -5
- package/dist/static/assets/{chunk-B4BG7PRW-Cb6W3QWJ.js.map → chunk-B4BG7PRW-Y-uXcJst.js.map} +1 -1
- package/dist/static/assets/{chunk-DI55MBZ5-ZAJWeVWH.js → chunk-DI55MBZ5-C5xSbRST.js} +4 -4
- package/dist/static/assets/{chunk-DI55MBZ5-ZAJWeVWH.js.map → chunk-DI55MBZ5-C5xSbRST.js.map} +1 -1
- package/dist/static/assets/{chunk-FMBD7UC4-DiwRlImb.js → chunk-FMBD7UC4-5uefwCjI.js} +2 -2
- package/dist/static/assets/{chunk-FMBD7UC4-DiwRlImb.js.map → chunk-FMBD7UC4-5uefwCjI.js.map} +1 -1
- package/dist/static/assets/{chunk-QN33PNHL-wilj7fb5.js → chunk-QN33PNHL-DzWVcvpI.js} +2 -2
- package/dist/static/assets/{chunk-QN33PNHL-wilj7fb5.js.map → chunk-QN33PNHL-DzWVcvpI.js.map} +1 -1
- package/dist/static/assets/{chunk-QZHKN3VN-DGmviJfR.js → chunk-QZHKN3VN-BrrvAZdP.js} +2 -2
- package/dist/static/assets/{chunk-QZHKN3VN-DGmviJfR.js.map → chunk-QZHKN3VN-BrrvAZdP.js.map} +1 -1
- package/dist/static/assets/{chunk-TZMSLE5B-Nm5wTXa_.js → chunk-TZMSLE5B-DyKOlPTY.js} +2 -2
- package/dist/static/assets/{chunk-TZMSLE5B-Nm5wTXa_.js.map → chunk-TZMSLE5B-DyKOlPTY.js.map} +1 -1
- package/dist/static/assets/{classDiagram-2ON5EDUG-BvUbXD6H.js → classDiagram-2ON5EDUG-FCrnlCWC.js} +6 -6
- package/dist/static/assets/{classDiagram-2ON5EDUG-BvUbXD6H.js.map → classDiagram-2ON5EDUG-FCrnlCWC.js.map} +1 -1
- package/dist/static/assets/{classDiagram-v2-WZHVMYZB-BvUbXD6H.js → classDiagram-v2-WZHVMYZB-FCrnlCWC.js} +6 -6
- package/dist/static/assets/{classDiagram-v2-WZHVMYZB-BvUbXD6H.js.map → classDiagram-v2-WZHVMYZB-FCrnlCWC.js.map} +1 -1
- package/dist/static/assets/{clone-0VLS7GaA.js → clone-BlI81KqZ.js} +2 -2
- package/dist/static/assets/{clone-0VLS7GaA.js.map → clone-BlI81KqZ.js.map} +1 -1
- package/dist/static/assets/{cose-bilkent-S5V4N54A-anaPs-75.js → cose-bilkent-S5V4N54A-yM7S2atz.js} +2 -2
- package/dist/static/assets/{cose-bilkent-S5V4N54A-anaPs-75.js.map → cose-bilkent-S5V4N54A-yM7S2atz.js.map} +1 -1
- package/dist/static/assets/{dagre-6UL2VRFP-YwdsZ11r.js → dagre-6UL2VRFP-BcweuZHt.js} +7 -7
- package/dist/static/assets/{dagre-6UL2VRFP-YwdsZ11r.js.map → dagre-6UL2VRFP-BcweuZHt.js.map} +1 -1
- package/dist/static/assets/{diagram-PSM6KHXK-5KQCf3h2.js → diagram-PSM6KHXK-D4-QwLW1.js} +8 -8
- package/dist/static/assets/{diagram-PSM6KHXK-5KQCf3h2.js.map → diagram-PSM6KHXK-D4-QwLW1.js.map} +1 -1
- package/dist/static/assets/{diagram-QEK2KX5R-Mf24XxZL.js → diagram-QEK2KX5R-BVbuejJn.js} +7 -7
- package/dist/static/assets/{diagram-QEK2KX5R-Mf24XxZL.js.map → diagram-QEK2KX5R-BVbuejJn.js.map} +1 -1
- package/dist/static/assets/{diagram-S2PKOQOG-DyQjD4D5.js → diagram-S2PKOQOG-pB6N6Tq_.js} +7 -7
- package/dist/static/assets/{diagram-S2PKOQOG-DyQjD4D5.js.map → diagram-S2PKOQOG-pB6N6Tq_.js.map} +1 -1
- package/dist/static/assets/{erDiagram-Q2GNP2WA-CEzTKw1u.js → erDiagram-Q2GNP2WA-DLKmthuw.js} +5 -5
- package/dist/static/assets/{erDiagram-Q2GNP2WA-CEzTKw1u.js.map → erDiagram-Q2GNP2WA-DLKmthuw.js.map} +1 -1
- package/dist/static/assets/{flowDiagram-NV44I4VS-DT821XSz.js → flowDiagram-NV44I4VS-BsBhWukh.js} +6 -6
- package/dist/static/assets/{flowDiagram-NV44I4VS-DT821XSz.js.map → flowDiagram-NV44I4VS-BsBhWukh.js.map} +1 -1
- package/dist/static/assets/{ganttDiagram-JELNMOA3-DlmeVsGg.js → ganttDiagram-JELNMOA3-Debz-J-C.js} +3 -3
- package/dist/static/assets/{ganttDiagram-JELNMOA3-DlmeVsGg.js.map → ganttDiagram-JELNMOA3-Debz-J-C.js.map} +1 -1
- package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-yAfyBG_d.js → gitGraphDiagram-V2S2FVAM-BnAPFBGR.js} +8 -8
- package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-yAfyBG_d.js.map → gitGraphDiagram-V2S2FVAM-BnAPFBGR.js.map} +1 -1
- package/dist/static/assets/{graph-BYv8vyWe.js → graph-DbzWiBNK.js} +3 -3
- package/dist/static/assets/{graph-BYv8vyWe.js.map → graph-DbzWiBNK.js.map} +1 -1
- package/dist/static/assets/{index-DMbwqOm6.js → index-B-8J28g7.js} +987 -1049
- package/dist/static/assets/index-B-8J28g7.js.map +1 -0
- package/dist/static/assets/{index-BiNcBn_U.css → index-CD5wtC_i.css} +1 -1
- package/dist/static/assets/{infoDiagram-HS3SLOUP-DaadramQ.js → infoDiagram-HS3SLOUP-CZ5hWoxV.js} +6 -6
- package/dist/static/assets/{infoDiagram-HS3SLOUP-DaadramQ.js.map → infoDiagram-HS3SLOUP-CZ5hWoxV.js.map} +1 -1
- package/dist/static/assets/{journeyDiagram-XKPGCS4Q-ftN8hxu3.js → journeyDiagram-XKPGCS4Q-CKN3oSxk.js} +5 -5
- package/dist/static/assets/{journeyDiagram-XKPGCS4Q-ftN8hxu3.js.map → journeyDiagram-XKPGCS4Q-CKN3oSxk.js.map} +1 -1
- package/dist/static/assets/{kanban-definition-3W4ZIXB7-BIXETQ-C.js → kanban-definition-3W4ZIXB7-BQCMklfJ.js} +3 -3
- package/dist/static/assets/{kanban-definition-3W4ZIXB7-BIXETQ-C.js.map → kanban-definition-3W4ZIXB7-BQCMklfJ.js.map} +1 -1
- package/dist/static/assets/{layout-OTbrj0Ye.js → layout-C5B58szc.js} +5 -5
- package/dist/static/assets/{layout-OTbrj0Ye.js.map → layout-C5B58szc.js.map} +1 -1
- package/dist/static/assets/{linear-CVfOC669.js → linear-_32fut6G.js} +2 -2
- package/dist/static/assets/{linear-CVfOC669.js.map → linear-_32fut6G.js.map} +1 -1
- package/dist/static/assets/{mindmap-definition-VGOIOE7T-D2zv5uI9.js → mindmap-definition-VGOIOE7T-C_goMzjx.js} +4 -4
- package/dist/static/assets/{mindmap-definition-VGOIOE7T-D2zv5uI9.js.map → mindmap-definition-VGOIOE7T-C_goMzjx.js.map} +1 -1
- package/dist/static/assets/{pieDiagram-ADFJNKIX-DaUXTsv7.js → pieDiagram-ADFJNKIX-BQ2n0cOB.js} +8 -8
- package/dist/static/assets/{pieDiagram-ADFJNKIX-DaUXTsv7.js.map → pieDiagram-ADFJNKIX-BQ2n0cOB.js.map} +1 -1
- package/dist/static/assets/{quadrantDiagram-AYHSOK5B-B7O2wPX9.js → quadrantDiagram-AYHSOK5B-BLg7_neg.js} +3 -3
- package/dist/static/assets/{quadrantDiagram-AYHSOK5B-B7O2wPX9.js.map → quadrantDiagram-AYHSOK5B-BLg7_neg.js.map} +1 -1
- package/dist/static/assets/{requirementDiagram-UZGBJVZJ-DpauVPY1.js → requirementDiagram-UZGBJVZJ-DwkJt0zi.js} +4 -4
- package/dist/static/assets/{requirementDiagram-UZGBJVZJ-DpauVPY1.js.map → requirementDiagram-UZGBJVZJ-DwkJt0zi.js.map} +1 -1
- package/dist/static/assets/{sankeyDiagram-TZEHDZUN-B3Hbfvad.js → sankeyDiagram-TZEHDZUN-DmxmatUB.js} +2 -2
- package/dist/static/assets/{sankeyDiagram-TZEHDZUN-B3Hbfvad.js.map → sankeyDiagram-TZEHDZUN-DmxmatUB.js.map} +1 -1
- package/dist/static/assets/{sequenceDiagram-WL72ISMW-KdbWByWT.js → sequenceDiagram-WL72ISMW-KHU_eApU.js} +4 -4
- package/dist/static/assets/{sequenceDiagram-WL72ISMW-KdbWByWT.js.map → sequenceDiagram-WL72ISMW-KHU_eApU.js.map} +1 -1
- package/dist/static/assets/{stateDiagram-FKZM4ZOC-yDOCVezC.js → stateDiagram-FKZM4ZOC-B3DBCxAL.js} +9 -9
- package/dist/static/assets/{stateDiagram-FKZM4ZOC-yDOCVezC.js.map → stateDiagram-FKZM4ZOC-B3DBCxAL.js.map} +1 -1
- package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-CpCJhvQO.js → stateDiagram-v2-4FDKWEC3-C-uIk7gh.js} +5 -5
- package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-CpCJhvQO.js.map → stateDiagram-v2-4FDKWEC3-C-uIk7gh.js.map} +1 -1
- package/dist/static/assets/{timeline-definition-IT6M3QCI-CHxuEjhV.js → timeline-definition-IT6M3QCI-SysEcQCC.js} +3 -3
- package/dist/static/assets/{timeline-definition-IT6M3QCI-CHxuEjhV.js.map → timeline-definition-IT6M3QCI-SysEcQCC.js.map} +1 -1
- package/dist/static/assets/{treemap-GDKQZRPO-Bsqu3wIy.js → treemap-GDKQZRPO-d0AbKEc4.js} +5 -5
- package/dist/static/assets/{treemap-GDKQZRPO-Bsqu3wIy.js.map → treemap-GDKQZRPO-d0AbKEc4.js.map} +1 -1
- package/dist/static/assets/{xychartDiagram-PRI3JC2R-RZy33lFF.js → xychartDiagram-PRI3JC2R-CmSQMxUh.js} +3 -3
- package/dist/static/assets/{xychartDiagram-PRI3JC2R-RZy33lFF.js.map → xychartDiagram-PRI3JC2R-CmSQMxUh.js.map} +1 -1
- package/dist/static/index.html +2 -2
- package/dist/tools/fs.js +1 -1
- package/package.json +9 -7
- package/dist/agent-priming.js +0 -2051
- package/dist/apps/installed-file.js +0 -207
- package/dist/apps/runtime-port.js +0 -91
- package/dist/docs/dominds-agent-priming.md +0 -218
- package/dist/docs/dominds-agent-priming.zh.md +0 -196
- package/dist/docs/drive-logic-context-refactor-plan.zh.md +0 -338
- package/dist/docs/keep-going.md +0 -176
- package/dist/docs/keep-going.zh.md +0 -162
- package/dist/docs/kernel-app-architecture.md +0 -286
- package/dist/docs/kernel-app-architecture.zh.md +0 -285
- package/dist/docs/showing-by-doing.md +0 -208
- package/dist/docs/showing-by-doing.zh.md +0 -177
- package/dist/docs/team-mgmt-toolset.md +0 -482
- package/dist/docs/team-mgmt-toolset.zh.md +0 -426
- package/dist/llm/driver-entry.js +0 -28
- package/dist/llm/driver-v2/context-health.js +0 -121
- package/dist/llm/driver-v2/context.js +0 -56
- package/dist/llm/driver-v2/core.js +0 -1545
- package/dist/llm/driver-v2/index.js +0 -26
- package/dist/llm/driver-v2/orchestrator.js +0 -158
- package/dist/llm/driver-v2/policy.js +0 -129
- package/dist/llm/driver-v2/restore-dialog-hierarchy.js +0 -73
- package/dist/llm/driver-v2/round.js +0 -366
- package/dist/llm/driver-v2/runtime-utils.js +0 -365
- package/dist/llm/driver-v2/saying-events.js +0 -20
- package/dist/llm/driver-v2/subdialog-txn.js +0 -42
- package/dist/llm/driver-v2/supdialog-response.js +0 -400
- package/dist/llm/driver-v2/tellask-bridge.js +0 -1148
- package/dist/llm/driver-v2/types.js +0 -10
- package/dist/llm/driver-v2-ref-only/context-health.js +0 -121
- package/dist/llm/driver-v2-ref-only/context.js +0 -17
- package/dist/llm/driver-v2-ref-only/core.js +0 -1710
- package/dist/llm/driver-v2-ref-only/index.js +0 -26
- package/dist/llm/driver-v2-ref-only/orchestrator.js +0 -158
- package/dist/llm/driver-v2-ref-only/policy.js +0 -129
- package/dist/llm/driver-v2-ref-only/restore-dialog-hierarchy.js +0 -73
- package/dist/llm/driver-v2-ref-only/round.js +0 -366
- package/dist/llm/driver-v2-ref-only/runtime-utils.js +0 -473
- package/dist/llm/driver-v2-ref-only/saying-events.js +0 -18
- package/dist/llm/driver-v2-ref-only/subdialog-txn.js +0 -42
- package/dist/llm/driver-v2-ref-only/supdialog-response.js +0 -453
- package/dist/llm/driver-v2-ref-only/tellask-bridge.js +0 -1178
- package/dist/llm/driver-v2-ref-only/types.js +0 -10
- package/dist/llm/driver.js +0 -4093
- package/dist/minds/promptdocs.js +0 -263
- package/dist/server/prompts-routes.js +0 -545
- package/dist/shared/team-mgmt-manual.js +0 -120
- package/dist/shared/types/prompts.js +0 -2
- package/dist/shared/types/tellask.js +0 -8
- package/dist/showing-by-doing.js +0 -1091
- package/dist/snippets/README.en.md +0 -3
- package/dist/snippets/README.md +0 -4
- package/dist/static/assets/index-DMbwqOm6.js.map +0 -1
- package/dist/tellask.js +0 -439
- package/dist/tools/context-health.js +0 -177
- package/dist/tools/diag.js +0 -583
- package/dist/tools/prompts/memory/en/errors.md +0 -155
- package/dist/tools/prompts/memory/en/index.md +0 -47
- package/dist/tools/prompts/memory/en/principles.md +0 -87
- package/dist/tools/prompts/memory/en/scenarios.md +0 -174
- package/dist/tools/prompts/memory/en/tools.md +0 -129
- package/dist/tools/prompts/memory/zh/errors.md +0 -155
- package/dist/tools/prompts/memory/zh/index.md +0 -47
- package/dist/tools/prompts/memory/zh/principles.md +0 -89
- package/dist/tools/prompts/memory/zh/scenarios.md +0 -174
- package/dist/tools/prompts/memory/zh/tools.md +0 -129
- package/dist/tools/team-mgmt.js +0 -3487
- package/dist/utils/task-doc.js +0 -236
package/dist/tools/team-mgmt.js
DELETED
|
@@ -1,3487 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Module: tools/team-mgmt
|
|
4
|
-
*
|
|
5
|
-
* Team management tooling scoped strictly to `.minds/**`.
|
|
6
|
-
*
|
|
7
|
-
* Goals:
|
|
8
|
-
* - Allow a dedicated team manager (e.g. a shadow/hidden member) to manage `.minds/` without granting
|
|
9
|
-
* broad rtws (runtime workspace) permissions (e.g. `ws_mod`).
|
|
10
|
-
* - Enforce static scoping to `.minds/**` and reject anything outside that subtree.
|
|
11
|
-
*/
|
|
12
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
-
};
|
|
15
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.teamMgmtTools = exports.teamMgmtManualTool = exports.teamMgmtValidateTeamCfgTool = exports.teamMgmtRmDirTool = exports.teamMgmtRmFileTool = exports.teamMgmtRipgrepSearchTool = exports.teamMgmtRipgrepFixedTool = exports.teamMgmtRipgrepCountTool = exports.teamMgmtRipgrepSnippetsTool = exports.teamMgmtRipgrepFilesTool = exports.teamMgmtMoveDirTool = exports.teamMgmtMoveFileTool = exports.teamMgmtMkDirTool = exports.teamMgmtApplyFileModificationTool = exports.teamMgmtPrepareFileRangeEditTool = exports.teamMgmtPrepareBlockReplaceTool = exports.teamMgmtPrepareInsertBeforeTool = exports.teamMgmtPrepareInsertAfterTool = exports.teamMgmtPrepareFileAppendTool = exports.teamMgmtOverwriteEntireFileTool = exports.teamMgmtCreateNewFileTool = exports.teamMgmtReadFileTool = exports.teamMgmtListDirTool = exports.teamMgmtListModelsTool = exports.teamMgmtListProvidersTool = exports.teamMgmtCheckProviderTool = void 0;
|
|
17
|
-
exports.splitCommandArgs = splitCommandArgs;
|
|
18
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
19
|
-
const path_1 = __importDefault(require("path"));
|
|
20
|
-
const yaml_1 = __importDefault(require("yaml"));
|
|
21
|
-
const client_1 = require("../llm/client");
|
|
22
|
-
const registry_1 = require("../llm/gen/registry");
|
|
23
|
-
const problems_1 = require("../problems");
|
|
24
|
-
const team_mgmt_manual_1 = require("../shared/team-mgmt-manual");
|
|
25
|
-
const time_1 = require("../shared/utils/time");
|
|
26
|
-
const team_1 = require("../team");
|
|
27
|
-
const fs_1 = require("./fs");
|
|
28
|
-
const registry_2 = require("./registry");
|
|
29
|
-
const ripgrep_1 = require("./ripgrep");
|
|
30
|
-
const txt_1 = require("./txt");
|
|
31
|
-
const MINDS_ALLOW = ['.minds/**'];
|
|
32
|
-
const MINDS_DIR = '.minds';
|
|
33
|
-
const TEAM_YAML_REL = `${MINDS_DIR}/team.yaml`;
|
|
34
|
-
const TEAM_YAML_PROBLEM_PREFIX = 'team/team_yaml_error/';
|
|
35
|
-
function ok(result, messages) {
|
|
36
|
-
void messages;
|
|
37
|
-
return result;
|
|
38
|
-
}
|
|
39
|
-
function fail(result, messages) {
|
|
40
|
-
void messages;
|
|
41
|
-
return result;
|
|
42
|
-
}
|
|
43
|
-
function toolCallOutputToString(output) {
|
|
44
|
-
return typeof output === 'string' ? output : output.content;
|
|
45
|
-
}
|
|
46
|
-
function yamlQuote(value) {
|
|
47
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
48
|
-
}
|
|
49
|
-
function formatYamlCodeBlock(yaml) {
|
|
50
|
-
return `\`\`\`yaml\n${yaml}\n\`\`\``;
|
|
51
|
-
}
|
|
52
|
-
function normalizeFileWriteBody(inputBody) {
|
|
53
|
-
if (inputBody === '' || inputBody.endsWith('\n')) {
|
|
54
|
-
return { normalizedBody: inputBody, addedTrailingNewlineToContent: false };
|
|
55
|
-
}
|
|
56
|
-
return { normalizedBody: `${inputBody}\n`, addedTrailingNewlineToContent: true };
|
|
57
|
-
}
|
|
58
|
-
function isEmptyLine(line) {
|
|
59
|
-
return line.trim() === '';
|
|
60
|
-
}
|
|
61
|
-
function lintTeamYamlStyle(raw) {
|
|
62
|
-
const out = [];
|
|
63
|
-
const lines = raw.split(/\r?\n/);
|
|
64
|
-
// 1) File should end with exactly one trailing newline (split leaves last empty).
|
|
65
|
-
if (raw !== '' && !raw.endsWith('\n')) {
|
|
66
|
-
out.push('- team.yaml should end with a trailing newline.');
|
|
67
|
-
}
|
|
68
|
-
// 2) Warn about large runs of blank lines (prefer single blank line between blocks).
|
|
69
|
-
let maxBlankRun = 0;
|
|
70
|
-
let cur = 0;
|
|
71
|
-
for (const line of lines) {
|
|
72
|
-
if (isEmptyLine(line)) {
|
|
73
|
-
cur++;
|
|
74
|
-
maxBlankRun = Math.max(maxBlankRun, cur);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
cur = 0;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (maxBlankRun >= 3) {
|
|
81
|
-
out.push('- team.yaml has 3+ consecutive blank lines; prefer a single blank line between blocks.');
|
|
82
|
-
}
|
|
83
|
-
// 3) Warn if there is no blank line separating adjacent top-level member blocks.
|
|
84
|
-
// This is a best-effort heuristic: we treat " <id>:" (2-space indent) as a member key.
|
|
85
|
-
const memberKeyRe = /^ [A-Za-z0-9_-]+:\s*$/;
|
|
86
|
-
let prevMemberLine = null;
|
|
87
|
-
for (let i = 0; i < lines.length; i++) {
|
|
88
|
-
const line = lines[i] ?? '';
|
|
89
|
-
if (!memberKeyRe.test(line))
|
|
90
|
-
continue;
|
|
91
|
-
if (prevMemberLine !== null) {
|
|
92
|
-
const between = lines.slice(prevMemberLine + 1, i);
|
|
93
|
-
const hasBlank = between.some((l) => isEmptyLine(l));
|
|
94
|
-
if (!hasBlank) {
|
|
95
|
-
out.push(`- team.yaml: members blocks should be separated by a blank line (between lines ${prevMemberLine + 1} and ${i + 1}).`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
prevMemberLine = i;
|
|
99
|
-
}
|
|
100
|
-
return out;
|
|
101
|
-
}
|
|
102
|
-
async function lintTeamYamlStyleProblems() {
|
|
103
|
-
const cwd = path_1.default.resolve(process.cwd());
|
|
104
|
-
const teamYamlAbs = path_1.default.resolve(cwd, TEAM_YAML_REL);
|
|
105
|
-
try {
|
|
106
|
-
const st = await promises_1.default.stat(teamYamlAbs);
|
|
107
|
-
if (!st.isFile())
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
catch (err) {
|
|
111
|
-
if (isFsErrWithCode(err) && err.code === 'ENOENT')
|
|
112
|
-
return;
|
|
113
|
-
throw err;
|
|
114
|
-
}
|
|
115
|
-
const raw = await promises_1.default.readFile(teamYamlAbs, 'utf8');
|
|
116
|
-
const warnings = lintTeamYamlStyle(raw);
|
|
117
|
-
const STYLE_PREFIX = TEAM_YAML_PROBLEM_PREFIX + 'style/';
|
|
118
|
-
if (warnings.length === 0) {
|
|
119
|
-
(0, problems_1.reconcileProblemsByPrefix)(STYLE_PREFIX, []);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const now = (0, time_1.formatUnifiedTimestamp)(new Date());
|
|
123
|
-
(0, problems_1.reconcileProblemsByPrefix)(STYLE_PREFIX, [
|
|
124
|
-
{
|
|
125
|
-
kind: 'team_workspace_config_error',
|
|
126
|
-
source: 'team',
|
|
127
|
-
id: STYLE_PREFIX + 'formatting',
|
|
128
|
-
severity: 'warning',
|
|
129
|
-
timestamp: now,
|
|
130
|
-
message: `Style warnings in ${TEAM_YAML_REL}.`,
|
|
131
|
-
detail: { filePath: TEAM_YAML_REL, errorText: warnings.join('\n') },
|
|
132
|
-
},
|
|
133
|
-
]);
|
|
134
|
-
}
|
|
135
|
-
function countLogicalLines(text) {
|
|
136
|
-
if (text === '')
|
|
137
|
-
return 0;
|
|
138
|
-
const parts = text.split('\n');
|
|
139
|
-
if (parts.length > 0 && parts[parts.length - 1] === '') {
|
|
140
|
-
parts.pop();
|
|
141
|
-
}
|
|
142
|
-
return parts.length;
|
|
143
|
-
}
|
|
144
|
-
function normalizePathToken(raw) {
|
|
145
|
-
return raw.trim().replace(/\\/g, '/').replace(/^\/+/, '');
|
|
146
|
-
}
|
|
147
|
-
function toMindsRelativePath(raw) {
|
|
148
|
-
const token = normalizePathToken(raw);
|
|
149
|
-
if (token === '' || token === '.')
|
|
150
|
-
return MINDS_DIR;
|
|
151
|
-
if (token === MINDS_DIR)
|
|
152
|
-
return MINDS_DIR;
|
|
153
|
-
if (token.startsWith(`${MINDS_DIR}/`))
|
|
154
|
-
return token;
|
|
155
|
-
if (token.startsWith(`./${MINDS_DIR}/`))
|
|
156
|
-
return token.slice(2);
|
|
157
|
-
if (token.startsWith(`./${MINDS_DIR}`))
|
|
158
|
-
return token.slice(2);
|
|
159
|
-
return `${MINDS_DIR}/${token.replace(/^\.\/+/, '')}`;
|
|
160
|
-
}
|
|
161
|
-
function ensureMindsScopedPath(rel) {
|
|
162
|
-
const cwd = path_1.default.resolve(process.cwd());
|
|
163
|
-
const mindsAbs = path_1.default.resolve(cwd, MINDS_DIR);
|
|
164
|
-
const abs = path_1.default.resolve(cwd, rel);
|
|
165
|
-
const isInside = abs === mindsAbs || abs.startsWith(mindsAbs + path_1.default.sep);
|
|
166
|
-
if (!isInside) {
|
|
167
|
-
throw new Error(`Path must be within ${MINDS_DIR}/`);
|
|
168
|
-
}
|
|
169
|
-
return { rel: path_1.default.relative(cwd, abs).replace(/\\/g, '/'), abs };
|
|
170
|
-
}
|
|
171
|
-
function getUserLang(dlg) {
|
|
172
|
-
try {
|
|
173
|
-
return dlg.getLastUserLanguageCode();
|
|
174
|
-
}
|
|
175
|
-
catch {
|
|
176
|
-
return 'en';
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
function makeMindsOnlyAccessMember(caller) {
|
|
180
|
-
return new team_1.Team.Member({
|
|
181
|
-
id: caller.id,
|
|
182
|
-
name: caller.name,
|
|
183
|
-
read_dirs: [...MINDS_ALLOW],
|
|
184
|
-
write_dirs: [...MINDS_ALLOW],
|
|
185
|
-
internal_allow_minds: true,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
function splitCommandArgs(raw) {
|
|
189
|
-
const args = [];
|
|
190
|
-
let current = '';
|
|
191
|
-
let inSingle = false;
|
|
192
|
-
let inDouble = false;
|
|
193
|
-
let escape = false;
|
|
194
|
-
const flush = () => {
|
|
195
|
-
if (current === '')
|
|
196
|
-
return;
|
|
197
|
-
args.push(current);
|
|
198
|
-
current = '';
|
|
199
|
-
};
|
|
200
|
-
for (let i = 0; i < raw.length; i++) {
|
|
201
|
-
const ch = raw[i] ?? '';
|
|
202
|
-
if (escape) {
|
|
203
|
-
current += ch;
|
|
204
|
-
escape = false;
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (!inSingle && ch === '\\') {
|
|
208
|
-
escape = true;
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
if (!inDouble && ch === "'" && !inSingle) {
|
|
212
|
-
inSingle = true;
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
if (!inDouble && ch === "'" && inSingle) {
|
|
216
|
-
inSingle = false;
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
if (!inSingle && ch === '"' && !inDouble) {
|
|
220
|
-
inDouble = true;
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
if (!inSingle && ch === '"' && inDouble) {
|
|
224
|
-
inDouble = false;
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
if (!inSingle && !inDouble && /\s/.test(ch)) {
|
|
228
|
-
flush();
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
current += ch;
|
|
232
|
-
}
|
|
233
|
-
flush();
|
|
234
|
-
return args;
|
|
235
|
-
}
|
|
236
|
-
function isFsErrWithCode(err) {
|
|
237
|
-
return typeof err === 'object' && err !== null && 'code' in err;
|
|
238
|
-
}
|
|
239
|
-
async function getMindsDirState() {
|
|
240
|
-
const cwd = path_1.default.resolve(process.cwd());
|
|
241
|
-
const abs = path_1.default.resolve(cwd, MINDS_DIR);
|
|
242
|
-
try {
|
|
243
|
-
const st = await promises_1.default.stat(abs);
|
|
244
|
-
if (!st.isDirectory())
|
|
245
|
-
return { kind: 'not_directory', abs };
|
|
246
|
-
return { kind: 'present' };
|
|
247
|
-
}
|
|
248
|
-
catch (err) {
|
|
249
|
-
if (isFsErrWithCode(err) && err.code === 'ENOENT')
|
|
250
|
-
return { kind: 'missing' };
|
|
251
|
-
throw err;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
function formatMindsMissingNotice(language) {
|
|
255
|
-
if (language === 'zh') {
|
|
256
|
-
return [
|
|
257
|
-
`注意:当前 rtws(运行时工作区)未初始化 \`${MINDS_DIR}/\`(这是正常情况)。`,
|
|
258
|
-
`因此当前在 \`${MINDS_DIR}/\` 下没有可读取/可列出的团队配置。`,
|
|
259
|
-
``,
|
|
260
|
-
`如果要初始化团队配置,请先创建目录:\`team_mgmt_mk_dir({ \"path\": \"${MINDS_DIR}\", \"parents\": true })\`。`,
|
|
261
|
-
].join('\n');
|
|
262
|
-
}
|
|
263
|
-
return [
|
|
264
|
-
`Note: \`${MINDS_DIR}/\` is not present in this rtws (runtime workspace) (this is normal).`,
|
|
265
|
-
`So there is currently no team configuration to read/list under \`${MINDS_DIR}/\`.`,
|
|
266
|
-
``,
|
|
267
|
-
`If you want to initialize team configuration, create the directory first: \`team_mgmt_mk_dir({ \"path\": \"${MINDS_DIR}\", \"parents\": true })\`.`,
|
|
268
|
-
].join('\n');
|
|
269
|
-
}
|
|
270
|
-
async function ensureMindsRootDirExists() {
|
|
271
|
-
const cwd = path_1.default.resolve(process.cwd());
|
|
272
|
-
const abs = path_1.default.resolve(cwd, MINDS_DIR);
|
|
273
|
-
await promises_1.default.mkdir(abs, { recursive: true });
|
|
274
|
-
}
|
|
275
|
-
function listTeamYamlProblems(problems) {
|
|
276
|
-
const out = [];
|
|
277
|
-
for (const p of problems) {
|
|
278
|
-
if (p.source !== 'team')
|
|
279
|
-
continue;
|
|
280
|
-
if (p.id.startsWith(TEAM_YAML_PROBLEM_PREFIX)) {
|
|
281
|
-
out.push(p);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
out.sort((a, b) => a.id.localeCompare(b.id));
|
|
285
|
-
return out;
|
|
286
|
-
}
|
|
287
|
-
function formatModelCheckResult(r) {
|
|
288
|
-
if (r.status === 'pass')
|
|
289
|
-
return `- ${r.model}: ✅ ok`;
|
|
290
|
-
return `- ${r.model}: ❌ ${r.details ?? 'failed'}`;
|
|
291
|
-
}
|
|
292
|
-
function escapeRegexChar(ch) {
|
|
293
|
-
// Escape characters with special meaning in JS RegExp patterns.
|
|
294
|
-
return /[\\^$.*+?()[\]{}|]/.test(ch) ? `\\${ch}` : ch;
|
|
295
|
-
}
|
|
296
|
-
function wildcardMatch(value, pattern) {
|
|
297
|
-
const p = pattern.trim() === '' ? '*' : pattern.trim();
|
|
298
|
-
let re = '^';
|
|
299
|
-
for (const ch of p) {
|
|
300
|
-
if (ch === '*')
|
|
301
|
-
re += '.*';
|
|
302
|
-
else if (ch === '?')
|
|
303
|
-
re += '.';
|
|
304
|
-
else
|
|
305
|
-
re += escapeRegexChar(ch);
|
|
306
|
-
}
|
|
307
|
-
re += '$';
|
|
308
|
-
return new RegExp(re).test(value);
|
|
309
|
-
}
|
|
310
|
-
function isNonEmptyString(value) {
|
|
311
|
-
return typeof value === 'string' && value.trim().length > 0;
|
|
312
|
-
}
|
|
313
|
-
function isInteger(value) {
|
|
314
|
-
return typeof value === 'number' && Number.isInteger(value);
|
|
315
|
-
}
|
|
316
|
-
function isEnvVarConfigured(envVar) {
|
|
317
|
-
const raw = process.env[envVar];
|
|
318
|
-
return typeof raw === 'string' && raw.trim().length > 0;
|
|
319
|
-
}
|
|
320
|
-
function getProviderModelsForListing(providerCfg) {
|
|
321
|
-
const rec = providerCfg;
|
|
322
|
-
const modelsUnknown = rec['models'];
|
|
323
|
-
if (typeof modelsUnknown !== 'object' || modelsUnknown === null)
|
|
324
|
-
return {};
|
|
325
|
-
if (Array.isArray(modelsUnknown))
|
|
326
|
-
return {};
|
|
327
|
-
return modelsUnknown;
|
|
328
|
-
}
|
|
329
|
-
function formatProviderEnvStatusLine(providerCfg) {
|
|
330
|
-
const envVar = providerCfg.apiKeyEnvVar;
|
|
331
|
-
const configured = isEnvVarConfigured(envVar);
|
|
332
|
-
if (configured)
|
|
333
|
-
return `apiKeyEnvVar: ${envVar} (configured)`;
|
|
334
|
-
if (providerCfg.apiType === 'codex') {
|
|
335
|
-
return `apiKeyEnvVar: ${envVar} (not set; may still work for codex via default ~/.codex)`;
|
|
336
|
-
}
|
|
337
|
-
return `apiKeyEnvVar: ${envVar} (NOT set)`;
|
|
338
|
-
}
|
|
339
|
-
function listModelIds(models, maxModels) {
|
|
340
|
-
const ids = Object.keys(models).sort((a, b) => a.localeCompare(b));
|
|
341
|
-
if (ids.length === 0)
|
|
342
|
-
return '(none)';
|
|
343
|
-
const max = maxModels > 0 ? maxModels : 30;
|
|
344
|
-
const head = ids.slice(0, max);
|
|
345
|
-
return `${head.join(', ')}${ids.length > head.length ? ', ...' : ''}`;
|
|
346
|
-
}
|
|
347
|
-
async function loadBuiltinLlmProviders() {
|
|
348
|
-
const defaultsPath = path_1.default.join(__dirname, '..', 'llm', 'defaults.yaml');
|
|
349
|
-
const raw = await promises_1.default.readFile(defaultsPath, 'utf-8');
|
|
350
|
-
const parsed = yaml_1.default.parse(raw);
|
|
351
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
352
|
-
throw new Error('Invalid defaults.yaml');
|
|
353
|
-
}
|
|
354
|
-
const rec = parsed;
|
|
355
|
-
const providersUnknown = rec['providers'];
|
|
356
|
-
if (typeof providersUnknown !== 'object' || providersUnknown === null) {
|
|
357
|
-
throw new Error('Invalid defaults.yaml (missing providers)');
|
|
358
|
-
}
|
|
359
|
-
return providersUnknown;
|
|
360
|
-
}
|
|
361
|
-
async function loadRtwsLlmProviders() {
|
|
362
|
-
const cfgPath = `${MINDS_DIR}/llm.yaml`;
|
|
363
|
-
try {
|
|
364
|
-
await promises_1.default.access(cfgPath);
|
|
365
|
-
}
|
|
366
|
-
catch (err) {
|
|
367
|
-
if (isFsErrWithCode(err) && err.code === 'ENOENT')
|
|
368
|
-
return { kind: 'missing' };
|
|
369
|
-
return { kind: 'invalid', error: err instanceof Error ? err.message : String(err) };
|
|
370
|
-
}
|
|
371
|
-
try {
|
|
372
|
-
const raw = await promises_1.default.readFile(cfgPath, 'utf-8');
|
|
373
|
-
const parsed = yaml_1.default.parse(raw);
|
|
374
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
375
|
-
return { kind: 'invalid', error: 'Invalid llm.yaml (expected root object)' };
|
|
376
|
-
}
|
|
377
|
-
const rec = parsed;
|
|
378
|
-
const providersUnknown = rec['providers'];
|
|
379
|
-
if (typeof providersUnknown !== 'object' || providersUnknown === null) {
|
|
380
|
-
return { kind: 'invalid', error: 'Invalid llm.yaml (missing providers object)' };
|
|
381
|
-
}
|
|
382
|
-
return { kind: 'present', providers: providersUnknown };
|
|
383
|
-
}
|
|
384
|
-
catch (err) {
|
|
385
|
-
return { kind: 'invalid', error: err instanceof Error ? err.message : String(err) };
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
function formatModelInfoSummary(info) {
|
|
389
|
-
if (typeof info !== 'object' || info === null)
|
|
390
|
-
return '';
|
|
391
|
-
const rec = info;
|
|
392
|
-
const parts = [];
|
|
393
|
-
const nameUnknown = rec['name'];
|
|
394
|
-
if (isNonEmptyString(nameUnknown))
|
|
395
|
-
parts.push(`name=${nameUnknown}`);
|
|
396
|
-
const contextLengthUnknown = rec['context_length'];
|
|
397
|
-
if (typeof contextLengthUnknown === 'number')
|
|
398
|
-
parts.push(`ctx=${contextLengthUnknown}`);
|
|
399
|
-
const inputLengthUnknown = rec['input_length'];
|
|
400
|
-
if (typeof inputLengthUnknown === 'number')
|
|
401
|
-
parts.push(`in=${inputLengthUnknown}`);
|
|
402
|
-
const outputLengthUnknown = rec['output_length'];
|
|
403
|
-
if (typeof outputLengthUnknown === 'number')
|
|
404
|
-
parts.push(`out=${outputLengthUnknown}`);
|
|
405
|
-
const optimalMaxTokensUnknown = rec['optimal_max_tokens'];
|
|
406
|
-
if (typeof optimalMaxTokensUnknown === 'number')
|
|
407
|
-
parts.push(`optimal_max_tokens=${optimalMaxTokensUnknown}`);
|
|
408
|
-
const criticalMaxTokensUnknown = rec['critical_max_tokens'];
|
|
409
|
-
if (typeof criticalMaxTokensUnknown === 'number')
|
|
410
|
-
parts.push(`critical_max_tokens=${criticalMaxTokensUnknown}`);
|
|
411
|
-
const contextWindowUnknown = rec['context_window'];
|
|
412
|
-
if (isNonEmptyString(contextWindowUnknown))
|
|
413
|
-
parts.push(`context_window=${contextWindowUnknown}`);
|
|
414
|
-
return parts.join(' ');
|
|
415
|
-
}
|
|
416
|
-
function formatModelParamOptionLine(paramName, opt, language) {
|
|
417
|
-
const extras = [];
|
|
418
|
-
if (opt.prominent === true)
|
|
419
|
-
extras.push(language === 'zh' ? 'prominent' : 'prominent');
|
|
420
|
-
switch (opt.type) {
|
|
421
|
-
case 'number': {
|
|
422
|
-
const range = `${opt.min ?? ''}..${opt.max ?? ''}`.trim();
|
|
423
|
-
if (range !== '..')
|
|
424
|
-
extras.push(range);
|
|
425
|
-
if (typeof opt.default === 'number')
|
|
426
|
-
extras.push(`default=${opt.default}`);
|
|
427
|
-
break;
|
|
428
|
-
}
|
|
429
|
-
case 'integer': {
|
|
430
|
-
const range = `${opt.min ?? ''}..${opt.max ?? ''}`.trim();
|
|
431
|
-
if (range !== '..')
|
|
432
|
-
extras.push(range);
|
|
433
|
-
if (typeof opt.default === 'number')
|
|
434
|
-
extras.push(`default=${opt.default}`);
|
|
435
|
-
break;
|
|
436
|
-
}
|
|
437
|
-
case 'boolean': {
|
|
438
|
-
if (typeof opt.default === 'boolean')
|
|
439
|
-
extras.push(`default=${opt.default ? 'true' : 'false'}`);
|
|
440
|
-
break;
|
|
441
|
-
}
|
|
442
|
-
case 'string': {
|
|
443
|
-
if (typeof opt.default === 'string')
|
|
444
|
-
extras.push(`default=${opt.default}`);
|
|
445
|
-
break;
|
|
446
|
-
}
|
|
447
|
-
case 'string_array': {
|
|
448
|
-
if (Array.isArray(opt.default) && opt.default.every((v) => typeof v === 'string')) {
|
|
449
|
-
extras.push(`default=[${opt.default.join(',')}]`);
|
|
450
|
-
}
|
|
451
|
-
break;
|
|
452
|
-
}
|
|
453
|
-
case 'record_number': {
|
|
454
|
-
if (typeof opt.default === 'object' && opt.default !== null) {
|
|
455
|
-
const entries = Object.entries(opt.default).filter(([, v]) => typeof v === 'number');
|
|
456
|
-
if (entries.length > 0) {
|
|
457
|
-
extras.push(`default={${entries
|
|
458
|
-
.slice(0, 6)
|
|
459
|
-
.map(([k, v]) => `${k}:${v}`)
|
|
460
|
-
.join(',')}}`);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
break;
|
|
464
|
-
}
|
|
465
|
-
case 'enum': {
|
|
466
|
-
extras.push(opt.values.join('|'));
|
|
467
|
-
if (typeof opt.default === 'string')
|
|
468
|
-
extras.push(`default=${opt.default}`);
|
|
469
|
-
break;
|
|
470
|
-
}
|
|
471
|
-
default: {
|
|
472
|
-
const _exhaustive = opt;
|
|
473
|
-
throw new Error(`Unhandled ModelParamOption: ${String(_exhaustive)}`);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
const desc = opt.description.trim();
|
|
477
|
-
const descShort = desc.length > 180 ? `${desc.slice(0, 180)}…` : desc;
|
|
478
|
-
const suffix = extras.length > 0 ? ` (${extras.join(', ')})` : '';
|
|
479
|
-
return `- \`${paramName}\`: \`${opt.type}\`${suffix}${descShort ? ` — ${descShort}` : ''}`;
|
|
480
|
-
}
|
|
481
|
-
exports.teamMgmtCheckProviderTool = {
|
|
482
|
-
type: 'func',
|
|
483
|
-
name: 'team_mgmt_check_provider',
|
|
484
|
-
description: 'Validate an LLM provider configuration (and optionally test models).',
|
|
485
|
-
descriptionI18n: {
|
|
486
|
-
en: 'Validate an LLM provider configuration (and optionally test models).',
|
|
487
|
-
zh: '校验 LLM provider 配置(可选对模型做实际连通性测试)。',
|
|
488
|
-
},
|
|
489
|
-
parameters: {
|
|
490
|
-
type: 'object',
|
|
491
|
-
additionalProperties: false,
|
|
492
|
-
required: ['provider_key'],
|
|
493
|
-
properties: {
|
|
494
|
-
provider_key: { type: 'string' },
|
|
495
|
-
model: { type: 'string' },
|
|
496
|
-
all_models: { type: 'boolean' },
|
|
497
|
-
live: { type: 'boolean' },
|
|
498
|
-
max_models: { type: 'integer' },
|
|
499
|
-
},
|
|
500
|
-
},
|
|
501
|
-
argsValidation: 'dominds',
|
|
502
|
-
async call(dlg, _caller, args) {
|
|
503
|
-
const language = getUserLang(dlg);
|
|
504
|
-
try {
|
|
505
|
-
const providerKeyValue = args['provider_key'];
|
|
506
|
-
const providerKey = typeof providerKeyValue === 'string' ? providerKeyValue.trim() : '';
|
|
507
|
-
if (!providerKey)
|
|
508
|
-
throw new Error('Provider key required');
|
|
509
|
-
const modelValue = args['model'];
|
|
510
|
-
const model = typeof modelValue === 'string' && modelValue.trim() !== '' ? modelValue.trim() : undefined;
|
|
511
|
-
const allModelsValue = args['all_models'];
|
|
512
|
-
const allModels = allModelsValue === true ? true : false;
|
|
513
|
-
if (allModelsValue !== undefined && typeof allModelsValue !== 'boolean') {
|
|
514
|
-
throw new Error('Invalid all_models (expected boolean)');
|
|
515
|
-
}
|
|
516
|
-
const liveValue = args['live'];
|
|
517
|
-
const live = liveValue === true ? true : false;
|
|
518
|
-
if (liveValue !== undefined && typeof liveValue !== 'boolean') {
|
|
519
|
-
throw new Error('Invalid live (expected boolean)');
|
|
520
|
-
}
|
|
521
|
-
const maxModelsValue = args['max_models'];
|
|
522
|
-
// Codex provider requires all func args to be schema-required; use max_models=0 as sentinel for default.
|
|
523
|
-
const maxModels = typeof maxModelsValue === 'number' && Number.isInteger(maxModelsValue) && maxModelsValue > 0
|
|
524
|
-
? maxModelsValue
|
|
525
|
-
: 10;
|
|
526
|
-
if (maxModelsValue !== undefined &&
|
|
527
|
-
(typeof maxModelsValue !== 'number' ||
|
|
528
|
-
!Number.isInteger(maxModelsValue) ||
|
|
529
|
-
maxModelsValue < 0)) {
|
|
530
|
-
throw new Error('Invalid max_models (expected positive integer or 0 for default)');
|
|
531
|
-
}
|
|
532
|
-
if (model !== undefined && allModels) {
|
|
533
|
-
throw new Error('Use either `model` or `all_models` (not both)');
|
|
534
|
-
}
|
|
535
|
-
const llmCfg = await client_1.LlmConfig.load();
|
|
536
|
-
const providerCfg = llmCfg.getProvider(providerKey);
|
|
537
|
-
if (!providerCfg) {
|
|
538
|
-
const msg = language === 'zh'
|
|
539
|
-
? `Provider 不存在:\`${providerKey}\`。请检查 \`.minds/llm.yaml\`(或内置 defaults)。也可先用 \`team_mgmt_list_providers({})\` 查看当前可用 provider keys。`
|
|
540
|
-
: `Provider not found: \`${providerKey}\`. Check \`.minds/llm.yaml\` (or built-in defaults). You can also run \`team_mgmt_list_providers({})\` to see available provider keys.`;
|
|
541
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
542
|
-
}
|
|
543
|
-
const envVar = providerCfg.apiKeyEnvVar;
|
|
544
|
-
const rawEnvValue = process.env[envVar];
|
|
545
|
-
const envConfigured = typeof rawEnvValue === 'string' && rawEnvValue.trim().length > 0 ? true : false;
|
|
546
|
-
const isCodexLike = providerCfg.apiType === 'codex';
|
|
547
|
-
const envStatusLine = envConfigured
|
|
548
|
-
? `apiKeyEnvVar: ${envVar} (configured)`
|
|
549
|
-
: isCodexLike
|
|
550
|
-
? `apiKeyEnvVar: ${envVar} (not set; may still work for codex via default ~/.codex)`
|
|
551
|
-
: `apiKeyEnvVar: ${envVar} (NOT set)`;
|
|
552
|
-
const models = Object.keys(providerCfg.models);
|
|
553
|
-
const modelHeader = models.length > 0
|
|
554
|
-
? `models: ${models.slice(0, 30).join(', ')}${models.length > 30 ? ', ...' : ''}`
|
|
555
|
-
: 'models: (none)';
|
|
556
|
-
if (!envConfigured && !isCodexLike) {
|
|
557
|
-
const msg = language === 'zh'
|
|
558
|
-
? [
|
|
559
|
-
fmtHeader('Provider 校验失败'),
|
|
560
|
-
fmtList([
|
|
561
|
-
`provider: \`${providerKey}\` (apiType: \`${providerCfg.apiType}\`)`,
|
|
562
|
-
envStatusLine,
|
|
563
|
-
'该 provider 的环境变量未配置,强烈建议先配置 env var 再修改 team 配置。',
|
|
564
|
-
]),
|
|
565
|
-
].join('')
|
|
566
|
-
: [
|
|
567
|
-
fmtHeader('Provider Check Failed'),
|
|
568
|
-
fmtList([
|
|
569
|
-
`provider: \`${providerKey}\` (apiType: \`${providerCfg.apiType}\`)`,
|
|
570
|
-
envStatusLine,
|
|
571
|
-
'Provider env var is not configured. Configure it before changing team config to avoid bricking.',
|
|
572
|
-
]),
|
|
573
|
-
].join('');
|
|
574
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
575
|
-
}
|
|
576
|
-
const modelsToCheck = model !== undefined ? [model] : allModels ? models : models.length > 0 ? [models[0]] : [];
|
|
577
|
-
if (model !== undefined && !Object.prototype.hasOwnProperty.call(providerCfg.models, model)) {
|
|
578
|
-
const msg = language === 'zh'
|
|
579
|
-
? `Model 不存在:\`${model}\` 不在 provider \`${providerKey}\` 的 models 列表中。请先更新 \`.minds/llm.yaml\` 或选择一个已配置的 model key。也可用 \`team_mgmt_list_models({ provider_pattern: \"${providerKey}\", model_pattern: \"*\" })\` 查看该 provider 下已有模型。`
|
|
580
|
-
: `Model not found: \`${model}\` is not in provider \`${providerKey}\` models. Update \`.minds/llm.yaml\` or choose a configured model key. You can also run \`team_mgmt_list_models({ provider_pattern: \"${providerKey}\", model_pattern: \"*\" })\` to see configured models under that provider.`;
|
|
581
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
582
|
-
}
|
|
583
|
-
const results = [];
|
|
584
|
-
if (live && modelsToCheck.length > 0) {
|
|
585
|
-
const llmGen = (0, registry_1.getLlmGenerator)(providerCfg.apiType);
|
|
586
|
-
if (!llmGen) {
|
|
587
|
-
const msg = language === 'zh'
|
|
588
|
-
? `该 provider 的生成器不存在:apiType=\`${providerCfg.apiType}\`。`
|
|
589
|
-
: `LLM generator not found for apiType=\`${providerCfg.apiType}\`.`;
|
|
590
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
591
|
-
}
|
|
592
|
-
const modelsLimited = allModels && modelsToCheck.length > maxModels
|
|
593
|
-
? modelsToCheck.slice(0, maxModels)
|
|
594
|
-
: modelsToCheck;
|
|
595
|
-
for (const modelKey of modelsLimited) {
|
|
596
|
-
const agent = new team_1.Team.Member({
|
|
597
|
-
id: 'team_mgmt_checker',
|
|
598
|
-
name: 'TeamMgmtChecker',
|
|
599
|
-
provider: providerKey,
|
|
600
|
-
model: modelKey,
|
|
601
|
-
model_params: {
|
|
602
|
-
max_tokens: 16,
|
|
603
|
-
openai: { temperature: 0 },
|
|
604
|
-
anthropic: { temperature: 0 },
|
|
605
|
-
},
|
|
606
|
-
});
|
|
607
|
-
let out = '';
|
|
608
|
-
let sawFuncCall = false;
|
|
609
|
-
const receiver = {
|
|
610
|
-
thinkingStart: async () => { },
|
|
611
|
-
thinkingChunk: async (_chunk) => { },
|
|
612
|
-
thinkingFinish: async () => { },
|
|
613
|
-
sayingStart: async () => { },
|
|
614
|
-
sayingChunk: async (chunk) => {
|
|
615
|
-
out += chunk;
|
|
616
|
-
},
|
|
617
|
-
sayingFinish: async () => { },
|
|
618
|
-
funcCall: async (_callId, _name, _args) => {
|
|
619
|
-
sawFuncCall = true;
|
|
620
|
-
},
|
|
621
|
-
};
|
|
622
|
-
const context = [
|
|
623
|
-
{ type: 'environment_msg', role: 'user', content: 'ping' },
|
|
624
|
-
];
|
|
625
|
-
const systemPrompt = 'Connectivity check: reply with a short confirmation (e.g. "ok").';
|
|
626
|
-
try {
|
|
627
|
-
await llmGen.genToReceiver(providerCfg, agent, systemPrompt, [], context, receiver, 0);
|
|
628
|
-
const details = out.trim().length > 0
|
|
629
|
-
? out.trim().slice(0, 120)
|
|
630
|
-
: sawFuncCall
|
|
631
|
-
? 'provider-native tool call emitted'
|
|
632
|
-
: undefined;
|
|
633
|
-
results.push({ model: modelKey, status: 'pass', details });
|
|
634
|
-
}
|
|
635
|
-
catch (err) {
|
|
636
|
-
const details = err instanceof Error ? err.message : String(err);
|
|
637
|
-
results.push({ model: modelKey, status: 'fail', details: details.slice(0, 200) });
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
if (allModels && modelsToCheck.length > modelsLimited.length) {
|
|
641
|
-
results.push({
|
|
642
|
-
model: `(skipped ${modelsToCheck.length - modelsLimited.length} models)`,
|
|
643
|
-
status: 'pass',
|
|
644
|
-
details: `use max_models to adjust`,
|
|
645
|
-
});
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
const headerTitle = language === 'zh' ? 'Provider 校验结果' : 'Provider Check';
|
|
649
|
-
const lines = [];
|
|
650
|
-
lines.push(fmtHeader(headerTitle));
|
|
651
|
-
lines.push(fmtList([
|
|
652
|
-
`provider: \`${providerKey}\` (apiType: \`${providerCfg.apiType}\`)`,
|
|
653
|
-
envStatusLine,
|
|
654
|
-
modelHeader,
|
|
655
|
-
live
|
|
656
|
-
? 'live: true (performed a real generation call)'
|
|
657
|
-
: 'live: false (config/env validation only)',
|
|
658
|
-
]));
|
|
659
|
-
if (!envConfigured && isCodexLike) {
|
|
660
|
-
const caution = language === 'zh'
|
|
661
|
-
? '注意:codex provider 在某些环境下可能仍可工作,但为了稳定性,建议配置对应 env var(通常是 CODEX_HOME)。'
|
|
662
|
-
: 'Note: codex may still work without the env var, but for stability you should configure it (usually CODEX_HOME).';
|
|
663
|
-
lines.push(caution + '\n');
|
|
664
|
-
}
|
|
665
|
-
if (live && results.length > 0) {
|
|
666
|
-
const title = language === 'zh' ? '模型连通性(live)' : 'Model Connectivity (live)';
|
|
667
|
-
lines.push(fmtHeader(title));
|
|
668
|
-
lines.push(results.map(formatModelCheckResult).join('\n') + '\n');
|
|
669
|
-
}
|
|
670
|
-
else if (!live) {
|
|
671
|
-
const hint = language === 'zh'
|
|
672
|
-
? `提示:如需做真实连通性测试,设置 \`live: true\`。例如:\`team_mgmt_check_provider({ provider_key: \"${providerKey}\", model: \"<modelKey>\", all_models: false, live: true, max_models: 0 })\``
|
|
673
|
-
: `Tip: to perform a real connectivity test, set \`live: true\`. Example: \`team_mgmt_check_provider({ provider_key: \"${providerKey}\", model: \"<modelKey>\", all_models: false, live: true, max_models: 0 })\``;
|
|
674
|
-
lines.push(hint + '\n');
|
|
675
|
-
}
|
|
676
|
-
const content = lines.join('');
|
|
677
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
678
|
-
}
|
|
679
|
-
catch (err) {
|
|
680
|
-
const msg = language === 'zh'
|
|
681
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
682
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
683
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
684
|
-
}
|
|
685
|
-
},
|
|
686
|
-
};
|
|
687
|
-
exports.teamMgmtListProvidersTool = {
|
|
688
|
-
type: 'func',
|
|
689
|
-
name: 'team_mgmt_list_providers',
|
|
690
|
-
description: 'List built-in and rtws LLM providers, their env-var readiness, and configured models.',
|
|
691
|
-
descriptionI18n: {
|
|
692
|
-
en: 'List built-in and rtws LLM providers, their env-var readiness, and configured models.',
|
|
693
|
-
zh: '列出内置与 rtws(运行时工作区)LLM providers,并显示 env var 是否已配置、以及该 provider 下有哪些模型。',
|
|
694
|
-
},
|
|
695
|
-
parameters: {
|
|
696
|
-
type: 'object',
|
|
697
|
-
additionalProperties: false,
|
|
698
|
-
properties: {
|
|
699
|
-
provider_pattern: { type: 'string' },
|
|
700
|
-
include_builtin: { type: 'boolean' },
|
|
701
|
-
include_rtws: { type: 'boolean' },
|
|
702
|
-
show_models: { type: 'boolean' },
|
|
703
|
-
max_models: { type: 'integer' },
|
|
704
|
-
},
|
|
705
|
-
},
|
|
706
|
-
argsValidation: 'dominds',
|
|
707
|
-
async call(dlg, _caller, args) {
|
|
708
|
-
const language = getUserLang(dlg);
|
|
709
|
-
try {
|
|
710
|
-
const providerPatternValue = args['provider_pattern'];
|
|
711
|
-
const providerPattern = typeof providerPatternValue === 'string' && providerPatternValue.trim() !== ''
|
|
712
|
-
? providerPatternValue.trim()
|
|
713
|
-
: '*';
|
|
714
|
-
const includeBuiltinValue = args['include_builtin'];
|
|
715
|
-
const includeBuiltin = includeBuiltinValue === undefined ? true : includeBuiltinValue === true;
|
|
716
|
-
if (includeBuiltinValue !== undefined && typeof includeBuiltinValue !== 'boolean') {
|
|
717
|
-
throw new Error('Invalid include_builtin (expected boolean)');
|
|
718
|
-
}
|
|
719
|
-
const includeRtwsValue = args['include_rtws'];
|
|
720
|
-
const includeRtws = includeRtwsValue === undefined ? true : includeRtwsValue === true;
|
|
721
|
-
if (includeRtwsValue !== undefined && typeof includeRtwsValue !== 'boolean') {
|
|
722
|
-
throw new Error('Invalid include_rtws (expected boolean)');
|
|
723
|
-
}
|
|
724
|
-
const showModelsValue = args['show_models'];
|
|
725
|
-
const showModels = showModelsValue === undefined ? true : showModelsValue === true;
|
|
726
|
-
if (showModelsValue !== undefined && typeof showModelsValue !== 'boolean') {
|
|
727
|
-
throw new Error('Invalid show_models (expected boolean)');
|
|
728
|
-
}
|
|
729
|
-
const maxModelsValue = args['max_models'];
|
|
730
|
-
const maxModels = isInteger(maxModelsValue) && maxModelsValue > 0 ? maxModelsValue : 30;
|
|
731
|
-
if (maxModelsValue !== undefined && (!isInteger(maxModelsValue) || maxModelsValue < 0)) {
|
|
732
|
-
throw new Error('Invalid max_models (expected integer >= 0)');
|
|
733
|
-
}
|
|
734
|
-
const builtinProviders = includeBuiltin ? await loadBuiltinLlmProviders() : {};
|
|
735
|
-
const rtwsProvidersResult = includeRtws
|
|
736
|
-
? await loadRtwsLlmProviders()
|
|
737
|
-
: { kind: 'missing' };
|
|
738
|
-
const rtwsProviders = rtwsProvidersResult.kind === 'present' ? rtwsProvidersResult.providers : {};
|
|
739
|
-
const contentLines = [];
|
|
740
|
-
const title = language === 'zh' ? 'LLM Provider 列表' : 'LLM Providers';
|
|
741
|
-
contentLines.push(fmtHeader(title));
|
|
742
|
-
contentLines.push(language === 'zh'
|
|
743
|
-
? '说明:rtws(运行时工作区)`.minds/llm.yaml` 的同名 provider key 会覆盖内置 defaults。\n'
|
|
744
|
-
: 'Note: rtws (runtime workspace) `.minds/llm.yaml` overrides built-in defaults when provider keys match.\n');
|
|
745
|
-
if (includeRtws) {
|
|
746
|
-
contentLines.push(fmtSubHeader(language === 'zh' ? 'rtws(.minds/llm.yaml)' : 'rtws (.minds/llm.yaml)'));
|
|
747
|
-
if (rtwsProvidersResult.kind === 'missing') {
|
|
748
|
-
contentLines.push(language === 'zh'
|
|
749
|
-
? `(未发现 \`${MINDS_DIR}/llm.yaml\`;仅列出内置 defaults)\n`
|
|
750
|
-
: `(\`${MINDS_DIR}/llm.yaml\` not found; showing built-in defaults only)\n`);
|
|
751
|
-
}
|
|
752
|
-
else if (rtwsProvidersResult.kind === 'invalid') {
|
|
753
|
-
contentLines.push(language === 'zh'
|
|
754
|
-
? `(解析失败:${rtwsProvidersResult.error})\n`
|
|
755
|
-
: `(Parse failed: ${rtwsProvidersResult.error})\n`);
|
|
756
|
-
}
|
|
757
|
-
else {
|
|
758
|
-
const keys = Object.keys(rtwsProviders).sort((a, b) => a.localeCompare(b));
|
|
759
|
-
if (keys.length === 0) {
|
|
760
|
-
contentLines.push(language === 'zh' ? '(空)\n' : '(empty)\n');
|
|
761
|
-
}
|
|
762
|
-
else {
|
|
763
|
-
const items = [];
|
|
764
|
-
for (const providerKey of keys) {
|
|
765
|
-
if (!wildcardMatch(providerKey, providerPattern))
|
|
766
|
-
continue;
|
|
767
|
-
const providerCfg = rtwsProviders[providerKey];
|
|
768
|
-
const envLine = formatProviderEnvStatusLine(providerCfg);
|
|
769
|
-
const overridesBuiltin = Object.prototype.hasOwnProperty.call(builtinProviders, providerKey);
|
|
770
|
-
const models = getProviderModelsForListing(providerCfg);
|
|
771
|
-
const modelCount = Object.keys(models).length;
|
|
772
|
-
const modelsText = showModels ? listModelIds(models, maxModels) : '';
|
|
773
|
-
const modelsSuffix = showModels
|
|
774
|
-
? `models(${modelCount}): ${modelsText}`
|
|
775
|
-
: `models(${modelCount})`;
|
|
776
|
-
items.push(`\`${providerKey}\` (apiType: \`${providerCfg.apiType}\`) — ${envLine} — ${modelsSuffix}${overridesBuiltin
|
|
777
|
-
? language === 'zh'
|
|
778
|
-
? ' — 覆盖内置 defaults'
|
|
779
|
-
: ' — overrides built-in'
|
|
780
|
-
: ''}`);
|
|
781
|
-
}
|
|
782
|
-
contentLines.push(fmtList(items));
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
if (includeBuiltin) {
|
|
787
|
-
contentLines.push(fmtSubHeader(language === 'zh'
|
|
788
|
-
? '内置(dominds/main/llm/defaults.yaml)'
|
|
789
|
-
: 'Built-in (dominds/main/llm/defaults.yaml)'));
|
|
790
|
-
const keys = Object.keys(builtinProviders).sort((a, b) => a.localeCompare(b));
|
|
791
|
-
if (keys.length === 0) {
|
|
792
|
-
contentLines.push(language === 'zh' ? '(空)\n' : '(empty)\n');
|
|
793
|
-
}
|
|
794
|
-
else {
|
|
795
|
-
const items = [];
|
|
796
|
-
for (const providerKey of keys) {
|
|
797
|
-
if (!wildcardMatch(providerKey, providerPattern))
|
|
798
|
-
continue;
|
|
799
|
-
const providerCfg = builtinProviders[providerKey];
|
|
800
|
-
const envLine = formatProviderEnvStatusLine(providerCfg);
|
|
801
|
-
const overriddenByRtws = rtwsProvidersResult.kind === 'present' &&
|
|
802
|
-
Object.prototype.hasOwnProperty.call(rtwsProviders, providerKey);
|
|
803
|
-
const models = getProviderModelsForListing(providerCfg);
|
|
804
|
-
const modelCount = Object.keys(models).length;
|
|
805
|
-
const modelsText = showModels ? listModelIds(models, maxModels) : '';
|
|
806
|
-
const modelsSuffix = showModels
|
|
807
|
-
? `models(${modelCount}): ${modelsText}`
|
|
808
|
-
: `models(${modelCount})`;
|
|
809
|
-
items.push(`\`${providerKey}\` (apiType: \`${providerCfg.apiType}\`) — ${envLine} — ${modelsSuffix}${overriddenByRtws
|
|
810
|
-
? language === 'zh'
|
|
811
|
-
? ' — 被 rtws 覆盖'
|
|
812
|
-
: ' — overridden by rtws'
|
|
813
|
-
: ''}`);
|
|
814
|
-
}
|
|
815
|
-
contentLines.push(fmtList(items));
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
const content = contentLines.join('');
|
|
819
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
820
|
-
}
|
|
821
|
-
catch (err) {
|
|
822
|
-
const msg = language === 'zh'
|
|
823
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
824
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
825
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
826
|
-
}
|
|
827
|
-
},
|
|
828
|
-
};
|
|
829
|
-
exports.teamMgmtListModelsTool = {
|
|
830
|
-
type: 'func',
|
|
831
|
-
name: 'team_mgmt_list_models',
|
|
832
|
-
description: 'List models filtered by provider/model wildcard, and show model info + provider model-parameter options.',
|
|
833
|
-
descriptionI18n: {
|
|
834
|
-
en: 'List models filtered by provider/model wildcard, and show model info + provider model-parameter options.',
|
|
835
|
-
zh: '按 provider/model 通配符过滤列出模型,并展示模型信息与该 provider 的 model_param_options(模型参数说明)。',
|
|
836
|
-
},
|
|
837
|
-
parameters: {
|
|
838
|
-
type: 'object',
|
|
839
|
-
additionalProperties: false,
|
|
840
|
-
properties: {
|
|
841
|
-
source: { type: 'string', enum: ['effective', 'builtin', 'rtws'] },
|
|
842
|
-
provider_pattern: { type: 'string' },
|
|
843
|
-
model_pattern: { type: 'string' },
|
|
844
|
-
include_param_options: { type: 'boolean' },
|
|
845
|
-
max_models: { type: 'integer' },
|
|
846
|
-
max_models_per_provider: { type: 'integer' },
|
|
847
|
-
max_params: { type: 'integer' },
|
|
848
|
-
},
|
|
849
|
-
},
|
|
850
|
-
argsValidation: 'dominds',
|
|
851
|
-
async call(dlg, _caller, args) {
|
|
852
|
-
const language = getUserLang(dlg);
|
|
853
|
-
try {
|
|
854
|
-
const sourceValue = args['source'];
|
|
855
|
-
const source = sourceValue === 'builtin' || sourceValue === 'rtws' || sourceValue === 'effective'
|
|
856
|
-
? sourceValue
|
|
857
|
-
: 'effective';
|
|
858
|
-
if (sourceValue !== undefined && typeof sourceValue !== 'string') {
|
|
859
|
-
throw new Error('Invalid source (expected string)');
|
|
860
|
-
}
|
|
861
|
-
const providerPatternValue = args['provider_pattern'];
|
|
862
|
-
const providerPattern = typeof providerPatternValue === 'string' && providerPatternValue.trim() !== ''
|
|
863
|
-
? providerPatternValue.trim()
|
|
864
|
-
: '*';
|
|
865
|
-
const modelPatternValue = args['model_pattern'];
|
|
866
|
-
const modelPattern = typeof modelPatternValue === 'string' && modelPatternValue.trim() !== ''
|
|
867
|
-
? modelPatternValue.trim()
|
|
868
|
-
: '*';
|
|
869
|
-
const includeParamOptionsValue = args['include_param_options'];
|
|
870
|
-
const includeParamOptions = includeParamOptionsValue === undefined ? true : includeParamOptionsValue === true;
|
|
871
|
-
if (includeParamOptionsValue !== undefined && typeof includeParamOptionsValue !== 'boolean') {
|
|
872
|
-
throw new Error('Invalid include_param_options (expected boolean)');
|
|
873
|
-
}
|
|
874
|
-
const maxModelsValue = args['max_models'];
|
|
875
|
-
const maxModels = isInteger(maxModelsValue) && maxModelsValue > 0 ? maxModelsValue : 200;
|
|
876
|
-
if (maxModelsValue !== undefined && (!isInteger(maxModelsValue) || maxModelsValue < 0)) {
|
|
877
|
-
throw new Error('Invalid max_models (expected integer >= 0)');
|
|
878
|
-
}
|
|
879
|
-
const maxModelsPerProviderValue = args['max_models_per_provider'];
|
|
880
|
-
const maxModelsPerProvider = isInteger(maxModelsPerProviderValue) && maxModelsPerProviderValue > 0
|
|
881
|
-
? maxModelsPerProviderValue
|
|
882
|
-
: 50;
|
|
883
|
-
if (maxModelsPerProviderValue !== undefined &&
|
|
884
|
-
(!isInteger(maxModelsPerProviderValue) || maxModelsPerProviderValue < 0)) {
|
|
885
|
-
throw new Error('Invalid max_models_per_provider (expected integer >= 0)');
|
|
886
|
-
}
|
|
887
|
-
const maxParamsValue = args['max_params'];
|
|
888
|
-
const maxParams = isInteger(maxParamsValue) && maxParamsValue > 0 ? maxParamsValue : 80;
|
|
889
|
-
if (maxParamsValue !== undefined && (!isInteger(maxParamsValue) || maxParamsValue < 0)) {
|
|
890
|
-
throw new Error('Invalid max_params (expected integer >= 0)');
|
|
891
|
-
}
|
|
892
|
-
let providers = {};
|
|
893
|
-
let sourceLabel = source;
|
|
894
|
-
if (source === 'effective') {
|
|
895
|
-
const cfg = await client_1.LlmConfig.load();
|
|
896
|
-
providers = cfg.providers;
|
|
897
|
-
}
|
|
898
|
-
else if (source === 'builtin') {
|
|
899
|
-
providers = await loadBuiltinLlmProviders();
|
|
900
|
-
}
|
|
901
|
-
else {
|
|
902
|
-
const rtws = await loadRtwsLlmProviders();
|
|
903
|
-
if (rtws.kind === 'missing') {
|
|
904
|
-
const msg = language === 'zh'
|
|
905
|
-
? `未发现 \`${MINDS_DIR}/llm.yaml\`,无法列出 source=rtws 的 models。`
|
|
906
|
-
: `\`${MINDS_DIR}/llm.yaml\` not found; cannot list models for source=rtws.`;
|
|
907
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
908
|
-
}
|
|
909
|
-
if (rtws.kind === 'invalid') {
|
|
910
|
-
const msg = language === 'zh'
|
|
911
|
-
? `解析 \`${MINDS_DIR}/llm.yaml\` 失败:${rtws.error}`
|
|
912
|
-
: `Failed to parse \`${MINDS_DIR}/llm.yaml\`: ${rtws.error}`;
|
|
913
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
914
|
-
}
|
|
915
|
-
providers = rtws.providers;
|
|
916
|
-
sourceLabel = 'rtws';
|
|
917
|
-
}
|
|
918
|
-
const providerKeys = Object.keys(providers).sort((a, b) => a.localeCompare(b));
|
|
919
|
-
const lines = [];
|
|
920
|
-
lines.push(fmtHeader(language === 'zh' ? 'LLM 模型列表' : 'LLM Models'));
|
|
921
|
-
lines.push(fmtList([
|
|
922
|
-
`source: \`${sourceLabel}\``,
|
|
923
|
-
`provider_pattern: \`${providerPattern}\``,
|
|
924
|
-
`model_pattern: \`${modelPattern}\``,
|
|
925
|
-
]));
|
|
926
|
-
let totalListed = 0;
|
|
927
|
-
let providerMatched = 0;
|
|
928
|
-
for (const providerKey of providerKeys) {
|
|
929
|
-
if (!wildcardMatch(providerKey, providerPattern))
|
|
930
|
-
continue;
|
|
931
|
-
const providerCfg = providers[providerKey];
|
|
932
|
-
providerMatched++;
|
|
933
|
-
const envLine = formatProviderEnvStatusLine(providerCfg);
|
|
934
|
-
const models = getProviderModelsForListing(providerCfg);
|
|
935
|
-
const modelIds = Object.keys(models).sort((a, b) => a.localeCompare(b));
|
|
936
|
-
const matchedModelIds = modelIds.filter((m) => wildcardMatch(m, modelPattern));
|
|
937
|
-
lines.push(fmtSubHeader(`provider: ${providerKey}`));
|
|
938
|
-
lines.push(fmtList([
|
|
939
|
-
`apiType: \`${providerCfg.apiType}\``,
|
|
940
|
-
envLine,
|
|
941
|
-
`models_total: ${modelIds.length}`,
|
|
942
|
-
`models_matched: ${matchedModelIds.length}`,
|
|
943
|
-
]));
|
|
944
|
-
if (matchedModelIds.length === 0) {
|
|
945
|
-
lines.push(language === 'zh' ? '(无匹配模型)\n' : '(no matching models)\n');
|
|
946
|
-
continue;
|
|
947
|
-
}
|
|
948
|
-
const perProviderLimit = maxModelsPerProvider > 0 ? maxModelsPerProvider : matchedModelIds.length;
|
|
949
|
-
const remainingGlobal = maxModels > 0 ? Math.max(0, maxModels - totalListed) : matchedModelIds.length;
|
|
950
|
-
const limit = Math.min(perProviderLimit, remainingGlobal, matchedModelIds.length);
|
|
951
|
-
const toShow = matchedModelIds.slice(0, limit);
|
|
952
|
-
const modelLines = [];
|
|
953
|
-
for (const modelKey of toShow) {
|
|
954
|
-
const infoUnknown = models[modelKey];
|
|
955
|
-
const summary = formatModelInfoSummary(infoUnknown);
|
|
956
|
-
modelLines.push(summary ? `\`${modelKey}\` — ${summary}` : `\`${modelKey}\``);
|
|
957
|
-
}
|
|
958
|
-
lines.push(fmtList(modelLines));
|
|
959
|
-
totalListed += toShow.length;
|
|
960
|
-
if (limit < matchedModelIds.length) {
|
|
961
|
-
const skipped = matchedModelIds.length - limit;
|
|
962
|
-
lines.push(language === 'zh'
|
|
963
|
-
? `(该 provider 省略 ${skipped} 个模型;可调大 max_models_per_provider / max_models)\n`
|
|
964
|
-
: `(skipped ${skipped} models for this provider; raise max_models_per_provider / max_models)\n`);
|
|
965
|
-
}
|
|
966
|
-
if (maxModels > 0 && totalListed >= maxModels) {
|
|
967
|
-
lines.push(language === 'zh'
|
|
968
|
-
? `(达到 max_models=${maxModels} 上限,已停止列出更多模型)\n`
|
|
969
|
-
: `(hit max_models=${maxModels} limit; stopped listing more models)\n`);
|
|
970
|
-
break;
|
|
971
|
-
}
|
|
972
|
-
if (includeParamOptions) {
|
|
973
|
-
const mpo = providerCfg.model_param_options;
|
|
974
|
-
const general = mpo ? mpo.general : undefined;
|
|
975
|
-
let specific;
|
|
976
|
-
if (mpo) {
|
|
977
|
-
if (providerCfg.apiType === 'codex')
|
|
978
|
-
specific = mpo.codex;
|
|
979
|
-
else if (providerCfg.apiType === 'openai' ||
|
|
980
|
-
providerCfg.apiType === 'openai-compatible')
|
|
981
|
-
specific = mpo.openai;
|
|
982
|
-
else if (providerCfg.apiType === 'anthropic')
|
|
983
|
-
specific = mpo.anthropic;
|
|
984
|
-
else
|
|
985
|
-
specific = undefined;
|
|
986
|
-
}
|
|
987
|
-
lines.push(fmtSubHeader(language === 'zh' ? 'model_param_options(模型参数说明)' : 'model_param_options'));
|
|
988
|
-
if (!general && !specific) {
|
|
989
|
-
lines.push(language === 'zh' ? '(未配置)\n' : '(not configured)\n');
|
|
990
|
-
}
|
|
991
|
-
else {
|
|
992
|
-
if (general) {
|
|
993
|
-
const keys = Object.keys(general).sort((a, b) => a.localeCompare(b));
|
|
994
|
-
const limited = maxParams > 0 ? keys.slice(0, maxParams) : keys;
|
|
995
|
-
const items = limited.map((k) => formatModelParamOptionLine(k, general[k], language));
|
|
996
|
-
lines.push(fmtSubHeader(language === 'zh' ? 'general(通用)' : 'general'));
|
|
997
|
-
lines.push(items.join('\n') + '\n');
|
|
998
|
-
if (limited.length < keys.length) {
|
|
999
|
-
lines.push(language === 'zh'
|
|
1000
|
-
? `(general 省略 ${keys.length - limited.length} 个参数;可调大 max_params)\n`
|
|
1001
|
-
: `(general skipped ${keys.length - limited.length} params; raise max_params)\n`);
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
if (specific) {
|
|
1005
|
-
const keys = Object.keys(specific).sort((a, b) => a.localeCompare(b));
|
|
1006
|
-
const limited = maxParams > 0 ? keys.slice(0, maxParams) : keys;
|
|
1007
|
-
const items = limited.map((k) => formatModelParamOptionLine(k, specific[k], language));
|
|
1008
|
-
lines.push(fmtSubHeader(language === 'zh'
|
|
1009
|
-
? `${providerCfg.apiType}(provider 专有)`
|
|
1010
|
-
: `${providerCfg.apiType}`));
|
|
1011
|
-
lines.push(items.join('\n') + '\n');
|
|
1012
|
-
if (limited.length < keys.length) {
|
|
1013
|
-
lines.push(language === 'zh'
|
|
1014
|
-
? `(${providerCfg.apiType} 省略 ${keys.length - limited.length} 个参数;可调大 max_params)\n`
|
|
1015
|
-
: `(${providerCfg.apiType} skipped ${keys.length - limited.length} params; raise max_params)\n`);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
if (providerMatched === 0) {
|
|
1022
|
-
lines.push(language === 'zh' ? '(没有匹配的 provider)\n' : '(no matching providers)\n');
|
|
1023
|
-
}
|
|
1024
|
-
const summaryTitle = language === 'zh' ? 'Summary' : 'Summary';
|
|
1025
|
-
lines.push(fmtSubHeader(summaryTitle) +
|
|
1026
|
-
fmtList([`providers_matched: ${providerMatched}`, `models_listed: ${totalListed}`]));
|
|
1027
|
-
const content = lines.join('');
|
|
1028
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1029
|
-
}
|
|
1030
|
-
catch (err) {
|
|
1031
|
-
const msg = language === 'zh'
|
|
1032
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1033
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1034
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1035
|
-
}
|
|
1036
|
-
},
|
|
1037
|
-
};
|
|
1038
|
-
exports.teamMgmtListDirTool = {
|
|
1039
|
-
type: 'func',
|
|
1040
|
-
name: 'team_mgmt_list_dir',
|
|
1041
|
-
description: `List directory contents under ${MINDS_DIR}/.`,
|
|
1042
|
-
descriptionI18n: {
|
|
1043
|
-
en: `List directory contents under ${MINDS_DIR}/.`,
|
|
1044
|
-
zh: `列出 ${MINDS_DIR}/ 下的目录内容。`,
|
|
1045
|
-
},
|
|
1046
|
-
parameters: {
|
|
1047
|
-
type: 'object',
|
|
1048
|
-
additionalProperties: false,
|
|
1049
|
-
properties: { path: { type: 'string' } },
|
|
1050
|
-
},
|
|
1051
|
-
argsValidation: 'dominds',
|
|
1052
|
-
async call(dlg, caller, args) {
|
|
1053
|
-
const language = getUserLang(dlg);
|
|
1054
|
-
try {
|
|
1055
|
-
const mindsState = await getMindsDirState();
|
|
1056
|
-
if (mindsState.kind === 'missing') {
|
|
1057
|
-
const msg = formatMindsMissingNotice(language);
|
|
1058
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1059
|
-
}
|
|
1060
|
-
if (mindsState.kind === 'not_directory') {
|
|
1061
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1062
|
-
}
|
|
1063
|
-
const pathValue = args['path'];
|
|
1064
|
-
const rel = toMindsRelativePath(typeof pathValue === 'string' ? pathValue : '.');
|
|
1065
|
-
ensureMindsScopedPath(rel);
|
|
1066
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1067
|
-
const output = await fs_1.listDirTool.call(dlg, proxyCaller, { path: rel });
|
|
1068
|
-
const content = toolCallOutputToString(output);
|
|
1069
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1070
|
-
}
|
|
1071
|
-
catch (err) {
|
|
1072
|
-
const msg = language === 'zh'
|
|
1073
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1074
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1075
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1076
|
-
}
|
|
1077
|
-
},
|
|
1078
|
-
};
|
|
1079
|
-
exports.teamMgmtReadFileTool = {
|
|
1080
|
-
type: 'func',
|
|
1081
|
-
name: 'team_mgmt_read_file',
|
|
1082
|
-
description: `Read a text file under ${MINDS_DIR}/.`,
|
|
1083
|
-
descriptionI18n: {
|
|
1084
|
-
en: `Read a text file under ${MINDS_DIR}/.`,
|
|
1085
|
-
zh: `读取 ${MINDS_DIR}/ 下的文本文件。`,
|
|
1086
|
-
},
|
|
1087
|
-
parameters: {
|
|
1088
|
-
type: 'object',
|
|
1089
|
-
additionalProperties: false,
|
|
1090
|
-
required: ['path'],
|
|
1091
|
-
properties: {
|
|
1092
|
-
path: { type: 'string' },
|
|
1093
|
-
range: { type: 'string' },
|
|
1094
|
-
max_lines: { type: 'integer' },
|
|
1095
|
-
show_linenos: { type: 'boolean' },
|
|
1096
|
-
},
|
|
1097
|
-
},
|
|
1098
|
-
argsValidation: 'dominds',
|
|
1099
|
-
async call(dlg, caller, args) {
|
|
1100
|
-
const language = getUserLang(dlg);
|
|
1101
|
-
try {
|
|
1102
|
-
const mindsState = await getMindsDirState();
|
|
1103
|
-
if (mindsState.kind === 'missing') {
|
|
1104
|
-
const msg = formatMindsMissingNotice(language);
|
|
1105
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1106
|
-
}
|
|
1107
|
-
if (mindsState.kind === 'not_directory') {
|
|
1108
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1109
|
-
}
|
|
1110
|
-
const pathValue = args['path'];
|
|
1111
|
-
const rawPath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
1112
|
-
if (!rawPath)
|
|
1113
|
-
throw new Error('Path required');
|
|
1114
|
-
const rel = toMindsRelativePath(rawPath);
|
|
1115
|
-
ensureMindsScopedPath(rel);
|
|
1116
|
-
const rangeValue = args['range'];
|
|
1117
|
-
const range = rangeValue === undefined
|
|
1118
|
-
? undefined
|
|
1119
|
-
: typeof rangeValue === 'string'
|
|
1120
|
-
? rangeValue
|
|
1121
|
-
: undefined;
|
|
1122
|
-
if (rangeValue !== undefined && typeof rangeValue !== 'string') {
|
|
1123
|
-
throw new Error('Invalid range (expected string)');
|
|
1124
|
-
}
|
|
1125
|
-
const maxLinesValue = args['max_lines'];
|
|
1126
|
-
const maxLines = typeof maxLinesValue === 'number' && Number.isInteger(maxLinesValue)
|
|
1127
|
-
? maxLinesValue
|
|
1128
|
-
: undefined;
|
|
1129
|
-
if (maxLinesValue !== undefined &&
|
|
1130
|
-
(typeof maxLinesValue !== 'number' || !Number.isInteger(maxLinesValue))) {
|
|
1131
|
-
throw new Error('Invalid max_lines (expected integer)');
|
|
1132
|
-
}
|
|
1133
|
-
const showLinenosValue = args['show_linenos'];
|
|
1134
|
-
const showLinenos = showLinenosValue === undefined
|
|
1135
|
-
? undefined
|
|
1136
|
-
: typeof showLinenosValue === 'boolean'
|
|
1137
|
-
? showLinenosValue
|
|
1138
|
-
: undefined;
|
|
1139
|
-
if (showLinenosValue !== undefined && typeof showLinenosValue !== 'boolean') {
|
|
1140
|
-
throw new Error('Invalid show_linenos (expected boolean)');
|
|
1141
|
-
}
|
|
1142
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1143
|
-
const output = await txt_1.readFileTool.call(dlg, proxyCaller, {
|
|
1144
|
-
path: rel,
|
|
1145
|
-
...(range ? { range } : {}),
|
|
1146
|
-
...(maxLines !== undefined ? { max_lines: maxLines } : {}),
|
|
1147
|
-
...(showLinenos !== undefined ? { show_linenos: showLinenos } : {}),
|
|
1148
|
-
});
|
|
1149
|
-
const content = toolCallOutputToString(output);
|
|
1150
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1151
|
-
}
|
|
1152
|
-
catch (err) {
|
|
1153
|
-
const msg = language === 'zh'
|
|
1154
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1155
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1156
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1157
|
-
}
|
|
1158
|
-
},
|
|
1159
|
-
};
|
|
1160
|
-
exports.teamMgmtCreateNewFileTool = {
|
|
1161
|
-
type: 'func',
|
|
1162
|
-
name: 'team_mgmt_create_new_file',
|
|
1163
|
-
description: `Create a new file under ${MINDS_DIR}/ (no prepare/apply). Refuses to overwrite existing files.`,
|
|
1164
|
-
descriptionI18n: {
|
|
1165
|
-
en: `Create a new file under ${MINDS_DIR}/ (no prepare/apply). Refuses to overwrite existing files.`,
|
|
1166
|
-
zh: `在 ${MINDS_DIR}/ 下创建一个新文件(不走 prepare/apply)。若文件已存在则拒绝覆写。`,
|
|
1167
|
-
},
|
|
1168
|
-
parameters: {
|
|
1169
|
-
type: 'object',
|
|
1170
|
-
additionalProperties: false,
|
|
1171
|
-
required: ['path'],
|
|
1172
|
-
properties: {
|
|
1173
|
-
path: { type: 'string' },
|
|
1174
|
-
content: { type: 'string' },
|
|
1175
|
-
},
|
|
1176
|
-
},
|
|
1177
|
-
argsValidation: 'dominds',
|
|
1178
|
-
async call(dlg, _caller, args) {
|
|
1179
|
-
const language = getUserLang(dlg);
|
|
1180
|
-
const t = language === 'zh'
|
|
1181
|
-
? {
|
|
1182
|
-
invalidArgs: (msg) => `参数不正确:${msg}`,
|
|
1183
|
-
fileExists: '文件已存在,拒绝创建。',
|
|
1184
|
-
notAFile: '路径已存在但不是文件(可能是目录),拒绝创建。',
|
|
1185
|
-
nextOverwrite: '下一步:先用 team_mgmt_read_file 获取 total_lines/size_bytes,然后再调用 team_mgmt_overwrite_entire_file 覆盖写入。',
|
|
1186
|
-
ok: '已创建新文件。',
|
|
1187
|
-
}
|
|
1188
|
-
: {
|
|
1189
|
-
invalidArgs: (msg) => `Invalid args: ${msg}`,
|
|
1190
|
-
fileExists: 'File already exists; refusing to create.',
|
|
1191
|
-
notAFile: 'Path exists but is not a file (e.g. a directory); refusing to create.',
|
|
1192
|
-
nextOverwrite: 'Next: call team_mgmt_read_file to get total_lines/size_bytes, then use team_mgmt_overwrite_entire_file to overwrite.',
|
|
1193
|
-
ok: 'Created new file.',
|
|
1194
|
-
};
|
|
1195
|
-
try {
|
|
1196
|
-
const mindsState = await getMindsDirState();
|
|
1197
|
-
if (mindsState.kind === 'not_directory') {
|
|
1198
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1199
|
-
}
|
|
1200
|
-
await ensureMindsRootDirExists();
|
|
1201
|
-
const pathValue = args['path'];
|
|
1202
|
-
const rawPath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
1203
|
-
if (!rawPath) {
|
|
1204
|
-
const content = formatYamlCodeBlock([
|
|
1205
|
-
`status: error`,
|
|
1206
|
-
`mode: create_new_file`,
|
|
1207
|
-
`error: INVALID_ARGS`,
|
|
1208
|
-
`summary: ${yamlQuote(t.invalidArgs('Path required'))}`,
|
|
1209
|
-
].join('\n'));
|
|
1210
|
-
return fail(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1211
|
-
}
|
|
1212
|
-
const rel = toMindsRelativePath(rawPath);
|
|
1213
|
-
const { abs } = ensureMindsScopedPath(rel);
|
|
1214
|
-
if (rel === MINDS_DIR) {
|
|
1215
|
-
const content = formatYamlCodeBlock([
|
|
1216
|
-
`status: error`,
|
|
1217
|
-
`mode: create_new_file`,
|
|
1218
|
-
`path: ${yamlQuote(rel)}`,
|
|
1219
|
-
`error: NOT_A_FILE`,
|
|
1220
|
-
`summary: ${yamlQuote(t.notAFile)}`,
|
|
1221
|
-
].join('\n'));
|
|
1222
|
-
return fail(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1223
|
-
}
|
|
1224
|
-
const contentValue = args['content'];
|
|
1225
|
-
if (contentValue !== undefined && typeof contentValue !== 'string') {
|
|
1226
|
-
throw new Error('Invalid content (expected string)');
|
|
1227
|
-
}
|
|
1228
|
-
const initialContent = typeof contentValue === 'string' ? contentValue : '';
|
|
1229
|
-
try {
|
|
1230
|
-
const st = await promises_1.default.stat(abs);
|
|
1231
|
-
if (!st.isFile()) {
|
|
1232
|
-
const out = formatYamlCodeBlock([
|
|
1233
|
-
`status: error`,
|
|
1234
|
-
`mode: create_new_file`,
|
|
1235
|
-
`path: ${yamlQuote(rel)}`,
|
|
1236
|
-
`error: NOT_A_FILE`,
|
|
1237
|
-
`summary: ${yamlQuote(t.notAFile)}`,
|
|
1238
|
-
].join('\n'));
|
|
1239
|
-
return fail(out, [{ type: 'environment_msg', role: 'user', content: out }]);
|
|
1240
|
-
}
|
|
1241
|
-
const out = formatYamlCodeBlock([
|
|
1242
|
-
`status: error`,
|
|
1243
|
-
`mode: create_new_file`,
|
|
1244
|
-
`path: ${yamlQuote(rel)}`,
|
|
1245
|
-
`error: FILE_EXISTS`,
|
|
1246
|
-
`summary: ${yamlQuote(t.fileExists)}`,
|
|
1247
|
-
`next: ${yamlQuote(t.nextOverwrite)}`,
|
|
1248
|
-
].join('\n'));
|
|
1249
|
-
return fail(out, [{ type: 'environment_msg', role: 'user', content: out }]);
|
|
1250
|
-
}
|
|
1251
|
-
catch (err) {
|
|
1252
|
-
if (!isFsErrWithCode(err) || err.code !== 'ENOENT')
|
|
1253
|
-
throw err;
|
|
1254
|
-
}
|
|
1255
|
-
const { normalizedBody, addedTrailingNewlineToContent } = normalizeFileWriteBody(initialContent);
|
|
1256
|
-
await promises_1.default.mkdir(path_1.default.dirname(abs), { recursive: true });
|
|
1257
|
-
await promises_1.default.writeFile(abs, normalizedBody, 'utf8');
|
|
1258
|
-
const newTotalBytes = Buffer.byteLength(normalizedBody, 'utf8');
|
|
1259
|
-
const newTotalLines = countLogicalLines(normalizedBody);
|
|
1260
|
-
const normalizedNewlineAdded = addedTrailingNewlineToContent && normalizedBody !== '';
|
|
1261
|
-
const summary = language === 'zh'
|
|
1262
|
-
? `${t.ok} path=${rel}; new_total_lines=${newTotalLines}; new_total_bytes=${newTotalBytes}.`
|
|
1263
|
-
: `${t.ok} path=${rel}; new_total_lines=${newTotalLines}; new_total_bytes=${newTotalBytes}.`;
|
|
1264
|
-
const out = formatYamlCodeBlock([
|
|
1265
|
-
`status: ok`,
|
|
1266
|
-
`mode: create_new_file`,
|
|
1267
|
-
`path: ${yamlQuote(rel)}`,
|
|
1268
|
-
`new_total_lines: ${newTotalLines}`,
|
|
1269
|
-
`new_total_bytes: ${newTotalBytes}`,
|
|
1270
|
-
`normalized_trailing_newline_added: ${normalizedNewlineAdded}`,
|
|
1271
|
-
`summary: ${yamlQuote(summary)}`,
|
|
1272
|
-
].join('\n'));
|
|
1273
|
-
return ok(out, [{ type: 'environment_msg', role: 'user', content: out }]);
|
|
1274
|
-
}
|
|
1275
|
-
catch (err) {
|
|
1276
|
-
const msg = language === 'zh'
|
|
1277
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1278
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1279
|
-
const out = formatYamlCodeBlock([
|
|
1280
|
-
`status: error`,
|
|
1281
|
-
`mode: create_new_file`,
|
|
1282
|
-
`error: FAILED`,
|
|
1283
|
-
`summary: ${yamlQuote(msg)}`,
|
|
1284
|
-
].join('\n'));
|
|
1285
|
-
return fail(out, [{ type: 'environment_msg', role: 'user', content: out }]);
|
|
1286
|
-
}
|
|
1287
|
-
},
|
|
1288
|
-
};
|
|
1289
|
-
exports.teamMgmtOverwriteEntireFileTool = {
|
|
1290
|
-
type: 'func',
|
|
1291
|
-
name: 'team_mgmt_overwrite_entire_file',
|
|
1292
|
-
description: `Overwrite an existing file under ${MINDS_DIR}/ (writes immediately; guarded).`,
|
|
1293
|
-
descriptionI18n: {
|
|
1294
|
-
en: `Overwrite an existing file under ${MINDS_DIR}/ (writes immediately; guarded).`,
|
|
1295
|
-
zh: `整体覆盖写入 ${MINDS_DIR}/ 下的已存在文件(直接写盘,带护栏)。`,
|
|
1296
|
-
},
|
|
1297
|
-
parameters: {
|
|
1298
|
-
type: 'object',
|
|
1299
|
-
additionalProperties: false,
|
|
1300
|
-
required: ['path', 'known_old_total_lines', 'known_old_total_bytes', 'content'],
|
|
1301
|
-
properties: {
|
|
1302
|
-
path: { type: 'string' },
|
|
1303
|
-
known_old_total_lines: { type: 'integer' },
|
|
1304
|
-
known_old_total_bytes: { type: 'integer' },
|
|
1305
|
-
content_format: { type: 'string' },
|
|
1306
|
-
content: { type: 'string' },
|
|
1307
|
-
},
|
|
1308
|
-
},
|
|
1309
|
-
argsValidation: 'dominds',
|
|
1310
|
-
async call(dlg, caller, args) {
|
|
1311
|
-
const language = getUserLang(dlg);
|
|
1312
|
-
try {
|
|
1313
|
-
const mindsState = await getMindsDirState();
|
|
1314
|
-
if (mindsState.kind === 'not_directory') {
|
|
1315
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1316
|
-
}
|
|
1317
|
-
await ensureMindsRootDirExists();
|
|
1318
|
-
const pathValue = args['path'];
|
|
1319
|
-
const rawPath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
1320
|
-
if (!rawPath)
|
|
1321
|
-
throw new Error('Path required');
|
|
1322
|
-
const knownLinesValue = args['known_old_total_lines'];
|
|
1323
|
-
if (typeof knownLinesValue !== 'number' || !Number.isInteger(knownLinesValue)) {
|
|
1324
|
-
throw new Error(language === 'zh'
|
|
1325
|
-
? 'known_old_total_lines 需要为整数。'
|
|
1326
|
-
: 'known_old_total_lines must be an integer.');
|
|
1327
|
-
}
|
|
1328
|
-
const knownBytesValue = args['known_old_total_bytes'];
|
|
1329
|
-
if (typeof knownBytesValue !== 'number' || !Number.isInteger(knownBytesValue)) {
|
|
1330
|
-
throw new Error(language === 'zh'
|
|
1331
|
-
? 'known_old_total_bytes 需要为整数。'
|
|
1332
|
-
: 'known_old_total_bytes must be an integer.');
|
|
1333
|
-
}
|
|
1334
|
-
const contentValue = args['content'];
|
|
1335
|
-
if (typeof contentValue !== 'string') {
|
|
1336
|
-
throw new Error(language === 'zh' ? 'content 需要为字符串。' : 'content must be a string.');
|
|
1337
|
-
}
|
|
1338
|
-
const contentFormatValue = args['content_format'];
|
|
1339
|
-
const contentFormat = contentFormatValue === undefined
|
|
1340
|
-
? undefined
|
|
1341
|
-
: typeof contentFormatValue === 'string'
|
|
1342
|
-
? contentFormatValue
|
|
1343
|
-
: undefined;
|
|
1344
|
-
if (contentFormatValue !== undefined && typeof contentFormatValue !== 'string') {
|
|
1345
|
-
throw new Error(language === 'zh' ? 'content_format 需要为字符串。' : 'content_format must be a string.');
|
|
1346
|
-
}
|
|
1347
|
-
const rel = toMindsRelativePath(rawPath);
|
|
1348
|
-
ensureMindsScopedPath(rel);
|
|
1349
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1350
|
-
const output = await txt_1.overwriteEntireFileTool.call(dlg, proxyCaller, {
|
|
1351
|
-
path: rel,
|
|
1352
|
-
known_old_total_lines: knownLinesValue,
|
|
1353
|
-
known_old_total_bytes: knownBytesValue,
|
|
1354
|
-
content: contentValue,
|
|
1355
|
-
...(contentFormat ? { content_format: contentFormat } : {}),
|
|
1356
|
-
});
|
|
1357
|
-
const result = toolCallOutputToString(output);
|
|
1358
|
-
return ok(result, [{ type: 'environment_msg', role: 'user', content: result }]);
|
|
1359
|
-
}
|
|
1360
|
-
catch (err) {
|
|
1361
|
-
const msg = language === 'zh'
|
|
1362
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1363
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1364
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1365
|
-
}
|
|
1366
|
-
},
|
|
1367
|
-
};
|
|
1368
|
-
exports.teamMgmtPrepareFileAppendTool = {
|
|
1369
|
-
type: 'func',
|
|
1370
|
-
name: 'team_mgmt_prepare_file_append',
|
|
1371
|
-
description: `Prepare an append-to-EOF modification under ${MINDS_DIR}/ (does not write yet).`,
|
|
1372
|
-
descriptionI18n: {
|
|
1373
|
-
en: `Prepare an append-to-EOF modification under ${MINDS_DIR}/ (does not write yet).`,
|
|
1374
|
-
zh: `规划 ${MINDS_DIR}/ 下“末尾追加”修改(不会立刻写入)。`,
|
|
1375
|
-
},
|
|
1376
|
-
parameters: {
|
|
1377
|
-
type: 'object',
|
|
1378
|
-
additionalProperties: false,
|
|
1379
|
-
required: ['path', 'content'],
|
|
1380
|
-
properties: {
|
|
1381
|
-
path: { type: 'string' },
|
|
1382
|
-
create: { type: 'boolean' },
|
|
1383
|
-
existing_hunk_id: { type: 'string' },
|
|
1384
|
-
content: { type: 'string' },
|
|
1385
|
-
},
|
|
1386
|
-
},
|
|
1387
|
-
argsValidation: 'dominds',
|
|
1388
|
-
async call(dlg, caller, args) {
|
|
1389
|
-
const language = getUserLang(dlg);
|
|
1390
|
-
try {
|
|
1391
|
-
const mindsState = await getMindsDirState();
|
|
1392
|
-
if (mindsState.kind === 'not_directory') {
|
|
1393
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1394
|
-
}
|
|
1395
|
-
await ensureMindsRootDirExists();
|
|
1396
|
-
const pathValue = args['path'];
|
|
1397
|
-
const rawPath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
1398
|
-
if (!rawPath)
|
|
1399
|
-
throw new Error('Path required');
|
|
1400
|
-
const rel = toMindsRelativePath(rawPath);
|
|
1401
|
-
ensureMindsScopedPath(rel);
|
|
1402
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1403
|
-
const createValue = args['create'];
|
|
1404
|
-
const create = createValue === undefined ? undefined : createValue === true ? true : false;
|
|
1405
|
-
if (createValue !== undefined && typeof createValue !== 'boolean') {
|
|
1406
|
-
throw new Error('Invalid create (expected boolean)');
|
|
1407
|
-
}
|
|
1408
|
-
const existingHunkIdValue = args['existing_hunk_id'];
|
|
1409
|
-
const existingHunkId = existingHunkIdValue === undefined
|
|
1410
|
-
? undefined
|
|
1411
|
-
: typeof existingHunkIdValue === 'string'
|
|
1412
|
-
? existingHunkIdValue
|
|
1413
|
-
: undefined;
|
|
1414
|
-
if (existingHunkIdValue !== undefined && typeof existingHunkIdValue !== 'string') {
|
|
1415
|
-
throw new Error('Invalid existing_hunk_id (expected string)');
|
|
1416
|
-
}
|
|
1417
|
-
const contentValue = args['content'];
|
|
1418
|
-
if (typeof contentValue !== 'string')
|
|
1419
|
-
throw new Error('Invalid content (expected string)');
|
|
1420
|
-
const output = await txt_1.prepareFileAppendTool.call(dlg, proxyCaller, {
|
|
1421
|
-
path: rel,
|
|
1422
|
-
...(create !== undefined ? { create } : {}),
|
|
1423
|
-
...(existingHunkId ? { existing_hunk_id: existingHunkId } : {}),
|
|
1424
|
-
content: contentValue,
|
|
1425
|
-
});
|
|
1426
|
-
const content = toolCallOutputToString(output);
|
|
1427
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1428
|
-
}
|
|
1429
|
-
catch (err) {
|
|
1430
|
-
const msg = language === 'zh'
|
|
1431
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1432
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1433
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1434
|
-
}
|
|
1435
|
-
},
|
|
1436
|
-
};
|
|
1437
|
-
exports.teamMgmtPrepareInsertAfterTool = {
|
|
1438
|
-
type: 'func',
|
|
1439
|
-
name: 'team_mgmt_prepare_file_insert_after',
|
|
1440
|
-
description: `Prepare an insertion after an anchor under ${MINDS_DIR}/ (does not write yet).`,
|
|
1441
|
-
descriptionI18n: {
|
|
1442
|
-
en: `Prepare an insertion after an anchor under ${MINDS_DIR}/ (does not write yet).`,
|
|
1443
|
-
zh: `按锚点规划 ${MINDS_DIR}/ 下“在其后插入”修改(不会立刻写入)。`,
|
|
1444
|
-
},
|
|
1445
|
-
parameters: {
|
|
1446
|
-
type: 'object',
|
|
1447
|
-
additionalProperties: false,
|
|
1448
|
-
required: ['path', 'anchor', 'content'],
|
|
1449
|
-
properties: {
|
|
1450
|
-
path: { type: 'string' },
|
|
1451
|
-
anchor: { type: 'string' },
|
|
1452
|
-
occurrence: { type: ['integer', 'string'] },
|
|
1453
|
-
match: { type: 'string' },
|
|
1454
|
-
existing_hunk_id: { type: 'string' },
|
|
1455
|
-
content: { type: 'string' },
|
|
1456
|
-
},
|
|
1457
|
-
},
|
|
1458
|
-
argsValidation: 'dominds',
|
|
1459
|
-
async call(dlg, caller, args) {
|
|
1460
|
-
const language = getUserLang(dlg);
|
|
1461
|
-
try {
|
|
1462
|
-
const mindsState = await getMindsDirState();
|
|
1463
|
-
if (mindsState.kind === 'missing') {
|
|
1464
|
-
const msg = formatMindsMissingNotice(language);
|
|
1465
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1466
|
-
}
|
|
1467
|
-
if (mindsState.kind === 'not_directory') {
|
|
1468
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1469
|
-
}
|
|
1470
|
-
const pathValue = args['path'];
|
|
1471
|
-
const anchorValue = args['anchor'];
|
|
1472
|
-
const rawPath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
1473
|
-
const anchor = typeof anchorValue === 'string' ? anchorValue : '';
|
|
1474
|
-
if (!rawPath)
|
|
1475
|
-
throw new Error('Path required');
|
|
1476
|
-
if (!anchor)
|
|
1477
|
-
throw new Error('Anchor is required');
|
|
1478
|
-
const rel = toMindsRelativePath(rawPath);
|
|
1479
|
-
ensureMindsScopedPath(rel);
|
|
1480
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1481
|
-
const occurrenceValue = args['occurrence'];
|
|
1482
|
-
if (occurrenceValue !== undefined &&
|
|
1483
|
-
typeof occurrenceValue !== 'number' &&
|
|
1484
|
-
typeof occurrenceValue !== 'string') {
|
|
1485
|
-
throw new Error("Invalid occurrence (expected integer or 'last')");
|
|
1486
|
-
}
|
|
1487
|
-
const matchValue = args['match'];
|
|
1488
|
-
if (matchValue !== undefined && typeof matchValue !== 'string') {
|
|
1489
|
-
throw new Error("Invalid match (expected 'contains'|'equals')");
|
|
1490
|
-
}
|
|
1491
|
-
const existingHunkIdValue = args['existing_hunk_id'];
|
|
1492
|
-
if (existingHunkIdValue !== undefined && typeof existingHunkIdValue !== 'string') {
|
|
1493
|
-
throw new Error('Invalid existing_hunk_id (expected string)');
|
|
1494
|
-
}
|
|
1495
|
-
const contentValue = args['content'];
|
|
1496
|
-
if (typeof contentValue !== 'string')
|
|
1497
|
-
throw new Error('Invalid content (expected string)');
|
|
1498
|
-
const output = await txt_1.prepareFileInsertAfterTool.call(dlg, proxyCaller, {
|
|
1499
|
-
path: rel,
|
|
1500
|
-
anchor,
|
|
1501
|
-
...(occurrenceValue !== undefined ? { occurrence: occurrenceValue } : {}),
|
|
1502
|
-
...(matchValue !== undefined ? { match: matchValue } : {}),
|
|
1503
|
-
...(existingHunkIdValue ? { existing_hunk_id: existingHunkIdValue } : {}),
|
|
1504
|
-
content: contentValue,
|
|
1505
|
-
});
|
|
1506
|
-
const content = toolCallOutputToString(output);
|
|
1507
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1508
|
-
}
|
|
1509
|
-
catch (err) {
|
|
1510
|
-
const msg = language === 'zh'
|
|
1511
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1512
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1513
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1514
|
-
}
|
|
1515
|
-
},
|
|
1516
|
-
};
|
|
1517
|
-
exports.teamMgmtPrepareInsertBeforeTool = {
|
|
1518
|
-
type: 'func',
|
|
1519
|
-
name: 'team_mgmt_prepare_file_insert_before',
|
|
1520
|
-
description: `Prepare an insertion before an anchor under ${MINDS_DIR}/ (does not write yet).`,
|
|
1521
|
-
descriptionI18n: {
|
|
1522
|
-
en: `Prepare an insertion before an anchor under ${MINDS_DIR}/ (does not write yet).`,
|
|
1523
|
-
zh: `按锚点规划 ${MINDS_DIR}/ 下“在其前插入”修改(不会立刻写入)。`,
|
|
1524
|
-
},
|
|
1525
|
-
parameters: {
|
|
1526
|
-
type: 'object',
|
|
1527
|
-
additionalProperties: false,
|
|
1528
|
-
required: ['path', 'anchor', 'content'],
|
|
1529
|
-
properties: {
|
|
1530
|
-
path: { type: 'string' },
|
|
1531
|
-
anchor: { type: 'string' },
|
|
1532
|
-
occurrence: { type: ['integer', 'string'] },
|
|
1533
|
-
match: { type: 'string' },
|
|
1534
|
-
existing_hunk_id: { type: 'string' },
|
|
1535
|
-
content: { type: 'string' },
|
|
1536
|
-
},
|
|
1537
|
-
},
|
|
1538
|
-
argsValidation: 'dominds',
|
|
1539
|
-
async call(dlg, caller, args) {
|
|
1540
|
-
const language = getUserLang(dlg);
|
|
1541
|
-
try {
|
|
1542
|
-
const mindsState = await getMindsDirState();
|
|
1543
|
-
if (mindsState.kind === 'missing') {
|
|
1544
|
-
const msg = formatMindsMissingNotice(language);
|
|
1545
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1546
|
-
}
|
|
1547
|
-
if (mindsState.kind === 'not_directory') {
|
|
1548
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1549
|
-
}
|
|
1550
|
-
const pathValue = args['path'];
|
|
1551
|
-
const anchorValue = args['anchor'];
|
|
1552
|
-
const rawPath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
1553
|
-
const anchor = typeof anchorValue === 'string' ? anchorValue : '';
|
|
1554
|
-
if (!rawPath)
|
|
1555
|
-
throw new Error('Path required');
|
|
1556
|
-
if (!anchor)
|
|
1557
|
-
throw new Error('Anchor is required');
|
|
1558
|
-
const rel = toMindsRelativePath(rawPath);
|
|
1559
|
-
ensureMindsScopedPath(rel);
|
|
1560
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1561
|
-
const occurrenceValue = args['occurrence'];
|
|
1562
|
-
if (occurrenceValue !== undefined &&
|
|
1563
|
-
typeof occurrenceValue !== 'number' &&
|
|
1564
|
-
typeof occurrenceValue !== 'string') {
|
|
1565
|
-
throw new Error("Invalid occurrence (expected integer or 'last')");
|
|
1566
|
-
}
|
|
1567
|
-
const matchValue = args['match'];
|
|
1568
|
-
if (matchValue !== undefined && typeof matchValue !== 'string') {
|
|
1569
|
-
throw new Error("Invalid match (expected 'contains'|'equals')");
|
|
1570
|
-
}
|
|
1571
|
-
const existingHunkIdValue = args['existing_hunk_id'];
|
|
1572
|
-
if (existingHunkIdValue !== undefined && typeof existingHunkIdValue !== 'string') {
|
|
1573
|
-
throw new Error('Invalid existing_hunk_id (expected string)');
|
|
1574
|
-
}
|
|
1575
|
-
const contentValue = args['content'];
|
|
1576
|
-
if (typeof contentValue !== 'string')
|
|
1577
|
-
throw new Error('Invalid content (expected string)');
|
|
1578
|
-
const output = await txt_1.prepareFileInsertBeforeTool.call(dlg, proxyCaller, {
|
|
1579
|
-
path: rel,
|
|
1580
|
-
anchor,
|
|
1581
|
-
...(occurrenceValue !== undefined ? { occurrence: occurrenceValue } : {}),
|
|
1582
|
-
...(matchValue !== undefined ? { match: matchValue } : {}),
|
|
1583
|
-
...(existingHunkIdValue ? { existing_hunk_id: existingHunkIdValue } : {}),
|
|
1584
|
-
content: contentValue,
|
|
1585
|
-
});
|
|
1586
|
-
const content = toolCallOutputToString(output);
|
|
1587
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1588
|
-
}
|
|
1589
|
-
catch (err) {
|
|
1590
|
-
const msg = language === 'zh'
|
|
1591
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1592
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1593
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1594
|
-
}
|
|
1595
|
-
},
|
|
1596
|
-
};
|
|
1597
|
-
exports.teamMgmtPrepareBlockReplaceTool = {
|
|
1598
|
-
type: 'func',
|
|
1599
|
-
name: 'team_mgmt_prepare_file_block_replace',
|
|
1600
|
-
description: `Prepare a block replacement between anchors in a file under ${MINDS_DIR}/ (does not write yet).`,
|
|
1601
|
-
descriptionI18n: {
|
|
1602
|
-
en: `Prepare a block replacement between anchors in a file under ${MINDS_DIR}/ (does not write yet).`,
|
|
1603
|
-
zh: `按锚点规划 ${MINDS_DIR}/ 下文件的块替换(不会立刻写入)。`,
|
|
1604
|
-
},
|
|
1605
|
-
parameters: {
|
|
1606
|
-
type: 'object',
|
|
1607
|
-
additionalProperties: false,
|
|
1608
|
-
required: ['path', 'start_anchor', 'end_anchor', 'content'],
|
|
1609
|
-
properties: {
|
|
1610
|
-
path: { type: 'string' },
|
|
1611
|
-
start_anchor: { type: 'string' },
|
|
1612
|
-
end_anchor: { type: 'string' },
|
|
1613
|
-
occurrence: { type: ['integer', 'string'] },
|
|
1614
|
-
include_anchors: { type: 'boolean' },
|
|
1615
|
-
match: { type: 'string' },
|
|
1616
|
-
require_unique: { type: 'boolean' },
|
|
1617
|
-
strict: { type: 'boolean' },
|
|
1618
|
-
existing_hunk_id: { type: 'string' },
|
|
1619
|
-
content: { type: 'string' },
|
|
1620
|
-
},
|
|
1621
|
-
},
|
|
1622
|
-
argsValidation: 'dominds',
|
|
1623
|
-
async call(dlg, caller, args) {
|
|
1624
|
-
const language = getUserLang(dlg);
|
|
1625
|
-
try {
|
|
1626
|
-
const mindsState = await getMindsDirState();
|
|
1627
|
-
if (mindsState.kind === 'missing') {
|
|
1628
|
-
const msg = formatMindsMissingNotice(language);
|
|
1629
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1630
|
-
}
|
|
1631
|
-
if (mindsState.kind === 'not_directory') {
|
|
1632
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1633
|
-
}
|
|
1634
|
-
const pathValue = args['path'];
|
|
1635
|
-
const startAnchorValue = args['start_anchor'];
|
|
1636
|
-
const endAnchorValue = args['end_anchor'];
|
|
1637
|
-
const rawPath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
1638
|
-
const startAnchor = typeof startAnchorValue === 'string' ? startAnchorValue : '';
|
|
1639
|
-
const endAnchor = typeof endAnchorValue === 'string' ? endAnchorValue : '';
|
|
1640
|
-
if (!rawPath)
|
|
1641
|
-
throw new Error('Path required');
|
|
1642
|
-
if (!startAnchor || !endAnchor)
|
|
1643
|
-
throw new Error('start_anchor and end_anchor are required');
|
|
1644
|
-
const rel = toMindsRelativePath(rawPath);
|
|
1645
|
-
ensureMindsScopedPath(rel);
|
|
1646
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1647
|
-
const occurrenceValue = args['occurrence'];
|
|
1648
|
-
if (occurrenceValue !== undefined &&
|
|
1649
|
-
typeof occurrenceValue !== 'number' &&
|
|
1650
|
-
typeof occurrenceValue !== 'string') {
|
|
1651
|
-
throw new Error("Invalid occurrence (expected integer or 'last')");
|
|
1652
|
-
}
|
|
1653
|
-
const includeAnchorsValue = args['include_anchors'];
|
|
1654
|
-
if (includeAnchorsValue !== undefined && typeof includeAnchorsValue !== 'boolean') {
|
|
1655
|
-
throw new Error('Invalid include_anchors (expected boolean)');
|
|
1656
|
-
}
|
|
1657
|
-
const matchValue = args['match'];
|
|
1658
|
-
if (matchValue !== undefined && typeof matchValue !== 'string') {
|
|
1659
|
-
throw new Error("Invalid match (expected 'contains'|'equals')");
|
|
1660
|
-
}
|
|
1661
|
-
const requireUniqueValue = args['require_unique'];
|
|
1662
|
-
if (requireUniqueValue !== undefined && typeof requireUniqueValue !== 'boolean') {
|
|
1663
|
-
throw new Error('Invalid require_unique (expected boolean)');
|
|
1664
|
-
}
|
|
1665
|
-
const strictValue = args['strict'];
|
|
1666
|
-
if (strictValue !== undefined && typeof strictValue !== 'boolean') {
|
|
1667
|
-
throw new Error('Invalid strict (expected boolean)');
|
|
1668
|
-
}
|
|
1669
|
-
const existingHunkIdValue = args['existing_hunk_id'];
|
|
1670
|
-
if (existingHunkIdValue !== undefined && typeof existingHunkIdValue !== 'string') {
|
|
1671
|
-
throw new Error('Invalid existing_hunk_id (expected string)');
|
|
1672
|
-
}
|
|
1673
|
-
const contentValue = args['content'];
|
|
1674
|
-
if (typeof contentValue !== 'string')
|
|
1675
|
-
throw new Error('Invalid content (expected string)');
|
|
1676
|
-
const output = await txt_1.prepareFileBlockReplaceTool.call(dlg, proxyCaller, {
|
|
1677
|
-
path: rel,
|
|
1678
|
-
start_anchor: startAnchor,
|
|
1679
|
-
end_anchor: endAnchor,
|
|
1680
|
-
...(occurrenceValue !== undefined ? { occurrence: occurrenceValue } : {}),
|
|
1681
|
-
...(includeAnchorsValue !== undefined ? { include_anchors: includeAnchorsValue } : {}),
|
|
1682
|
-
...(matchValue !== undefined ? { match: matchValue } : {}),
|
|
1683
|
-
...(requireUniqueValue !== undefined ? { require_unique: requireUniqueValue } : {}),
|
|
1684
|
-
...(strictValue !== undefined ? { strict: strictValue } : {}),
|
|
1685
|
-
...(existingHunkIdValue ? { existing_hunk_id: existingHunkIdValue } : {}),
|
|
1686
|
-
content: contentValue,
|
|
1687
|
-
});
|
|
1688
|
-
const content = toolCallOutputToString(output);
|
|
1689
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1690
|
-
}
|
|
1691
|
-
catch (err) {
|
|
1692
|
-
const msg = language === 'zh'
|
|
1693
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1694
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1695
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1696
|
-
}
|
|
1697
|
-
},
|
|
1698
|
-
};
|
|
1699
|
-
exports.teamMgmtPrepareFileRangeEditTool = {
|
|
1700
|
-
type: 'func',
|
|
1701
|
-
name: 'team_mgmt_prepare_file_range_edit',
|
|
1702
|
-
description: `Prepare a single-file modification under ${MINDS_DIR}/ (does not write yet).`,
|
|
1703
|
-
descriptionI18n: {
|
|
1704
|
-
en: `Prepare a single-file modification under ${MINDS_DIR}/ (does not write yet).`,
|
|
1705
|
-
zh: `按行号范围规划 ${MINDS_DIR}/ 下的单文件修改(不会立刻写入)。`,
|
|
1706
|
-
},
|
|
1707
|
-
parameters: {
|
|
1708
|
-
type: 'object',
|
|
1709
|
-
additionalProperties: false,
|
|
1710
|
-
required: ['path', 'range'],
|
|
1711
|
-
properties: {
|
|
1712
|
-
path: { type: 'string' },
|
|
1713
|
-
range: { type: 'string' },
|
|
1714
|
-
existing_hunk_id: { type: 'string' },
|
|
1715
|
-
content: { type: 'string' },
|
|
1716
|
-
},
|
|
1717
|
-
},
|
|
1718
|
-
argsValidation: 'dominds',
|
|
1719
|
-
async call(dlg, caller, args) {
|
|
1720
|
-
const language = getUserLang(dlg);
|
|
1721
|
-
try {
|
|
1722
|
-
const mindsState = await getMindsDirState();
|
|
1723
|
-
if (mindsState.kind === 'not_directory') {
|
|
1724
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1725
|
-
}
|
|
1726
|
-
await ensureMindsRootDirExists();
|
|
1727
|
-
const pathValue = args['path'];
|
|
1728
|
-
const rangeValue = args['range'];
|
|
1729
|
-
const filePath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
1730
|
-
const rangeSpec = typeof rangeValue === 'string' ? rangeValue.trim() : '';
|
|
1731
|
-
if (!filePath)
|
|
1732
|
-
throw new Error('Path required');
|
|
1733
|
-
if (!rangeSpec)
|
|
1734
|
-
throw new Error('Range required (e.g. 10~20 or ~)');
|
|
1735
|
-
const existingHunkIdValue = args['existing_hunk_id'];
|
|
1736
|
-
if (existingHunkIdValue !== undefined && typeof existingHunkIdValue !== 'string') {
|
|
1737
|
-
throw new Error('Invalid existing_hunk_id (expected string)');
|
|
1738
|
-
}
|
|
1739
|
-
const contentValue = args['content'];
|
|
1740
|
-
if (contentValue !== undefined && typeof contentValue !== 'string') {
|
|
1741
|
-
throw new Error('Invalid content (expected string)');
|
|
1742
|
-
}
|
|
1743
|
-
const rel = toMindsRelativePath(filePath);
|
|
1744
|
-
ensureMindsScopedPath(rel);
|
|
1745
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1746
|
-
const output = await txt_1.prepareFileRangeEditTool.call(dlg, proxyCaller, {
|
|
1747
|
-
path: rel,
|
|
1748
|
-
range: rangeSpec,
|
|
1749
|
-
...(existingHunkIdValue ? { existing_hunk_id: existingHunkIdValue } : {}),
|
|
1750
|
-
...(typeof contentValue === 'string' ? { content: contentValue } : {}),
|
|
1751
|
-
});
|
|
1752
|
-
const content = toolCallOutputToString(output);
|
|
1753
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1754
|
-
}
|
|
1755
|
-
catch (err) {
|
|
1756
|
-
const msg = language === 'zh'
|
|
1757
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1758
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1759
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1760
|
-
}
|
|
1761
|
-
},
|
|
1762
|
-
};
|
|
1763
|
-
exports.teamMgmtApplyFileModificationTool = {
|
|
1764
|
-
type: 'func',
|
|
1765
|
-
name: 'team_mgmt_apply_file_modification',
|
|
1766
|
-
description: `Apply a previously planned file modification under ${MINDS_DIR}/ by hunk id.`,
|
|
1767
|
-
descriptionI18n: {
|
|
1768
|
-
en: `Apply a previously planned file modification under ${MINDS_DIR}/ by hunk id.`,
|
|
1769
|
-
zh: `按 hunk id 应用之前规划的 ${MINDS_DIR}/ 下的单文件修改。`,
|
|
1770
|
-
},
|
|
1771
|
-
parameters: {
|
|
1772
|
-
type: 'object',
|
|
1773
|
-
additionalProperties: false,
|
|
1774
|
-
required: ['hunk_id'],
|
|
1775
|
-
properties: { hunk_id: { type: 'string' } },
|
|
1776
|
-
},
|
|
1777
|
-
argsValidation: 'dominds',
|
|
1778
|
-
async call(dlg, caller, args) {
|
|
1779
|
-
const language = getUserLang(dlg);
|
|
1780
|
-
try {
|
|
1781
|
-
const mindsState = await getMindsDirState();
|
|
1782
|
-
if (mindsState.kind === 'not_directory') {
|
|
1783
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1784
|
-
}
|
|
1785
|
-
await ensureMindsRootDirExists();
|
|
1786
|
-
const hunkIdValue = args['hunk_id'];
|
|
1787
|
-
const id = typeof hunkIdValue === 'string' ? hunkIdValue.trim() : '';
|
|
1788
|
-
if (!id)
|
|
1789
|
-
throw new Error('Hunk id required (e.g. a1b2c3d4)');
|
|
1790
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1791
|
-
const output = await txt_1.applyFileModificationTool.call(dlg, proxyCaller, { hunk_id: id });
|
|
1792
|
-
const content = toolCallOutputToString(output);
|
|
1793
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1794
|
-
}
|
|
1795
|
-
catch (err) {
|
|
1796
|
-
const msg = language === 'zh'
|
|
1797
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1798
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1799
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1800
|
-
}
|
|
1801
|
-
},
|
|
1802
|
-
};
|
|
1803
|
-
exports.teamMgmtMkDirTool = {
|
|
1804
|
-
type: 'func',
|
|
1805
|
-
name: 'team_mgmt_mk_dir',
|
|
1806
|
-
description: `Create a directory under ${MINDS_DIR}/.`,
|
|
1807
|
-
descriptionI18n: {
|
|
1808
|
-
en: `Create a directory under ${MINDS_DIR}/.`,
|
|
1809
|
-
zh: `创建 ${MINDS_DIR}/ 下目录。`,
|
|
1810
|
-
},
|
|
1811
|
-
parameters: {
|
|
1812
|
-
type: 'object',
|
|
1813
|
-
additionalProperties: false,
|
|
1814
|
-
required: ['path'],
|
|
1815
|
-
properties: { path: { type: 'string' }, parents: { type: 'boolean' } },
|
|
1816
|
-
},
|
|
1817
|
-
argsValidation: 'dominds',
|
|
1818
|
-
async call(dlg, caller, args) {
|
|
1819
|
-
const language = getUserLang(dlg);
|
|
1820
|
-
try {
|
|
1821
|
-
const mindsState = await getMindsDirState();
|
|
1822
|
-
if (mindsState.kind === 'not_directory') {
|
|
1823
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1824
|
-
}
|
|
1825
|
-
await ensureMindsRootDirExists();
|
|
1826
|
-
const pathValue = args['path'];
|
|
1827
|
-
const rawPath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
1828
|
-
if (!rawPath)
|
|
1829
|
-
throw new Error('Path required');
|
|
1830
|
-
const rel = toMindsRelativePath(rawPath);
|
|
1831
|
-
ensureMindsScopedPath(rel);
|
|
1832
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1833
|
-
const parentsValue = args['parents'];
|
|
1834
|
-
const parents = parentsValue === undefined ? undefined : parentsValue === true ? true : false;
|
|
1835
|
-
if (parentsValue !== undefined && typeof parentsValue !== 'boolean') {
|
|
1836
|
-
throw new Error('Invalid parents (expected boolean)');
|
|
1837
|
-
}
|
|
1838
|
-
const toolArgs = parents === undefined ? { path: rel } : { path: rel, parents };
|
|
1839
|
-
const output = await fs_1.mkDirTool.call(dlg, proxyCaller, toolArgs);
|
|
1840
|
-
const content = toolCallOutputToString(output);
|
|
1841
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1842
|
-
}
|
|
1843
|
-
catch (err) {
|
|
1844
|
-
const msg = language === 'zh'
|
|
1845
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1846
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1847
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1848
|
-
}
|
|
1849
|
-
},
|
|
1850
|
-
};
|
|
1851
|
-
exports.teamMgmtMoveFileTool = {
|
|
1852
|
-
type: 'func',
|
|
1853
|
-
name: 'team_mgmt_move_file',
|
|
1854
|
-
description: `Move/rename a file under ${MINDS_DIR}/.`,
|
|
1855
|
-
descriptionI18n: {
|
|
1856
|
-
en: `Move/rename a file under ${MINDS_DIR}/.`,
|
|
1857
|
-
zh: `移动/重命名 ${MINDS_DIR}/ 下文件。`,
|
|
1858
|
-
},
|
|
1859
|
-
parameters: {
|
|
1860
|
-
type: 'object',
|
|
1861
|
-
additionalProperties: false,
|
|
1862
|
-
required: ['from', 'to'],
|
|
1863
|
-
properties: { from: { type: 'string' }, to: { type: 'string' } },
|
|
1864
|
-
},
|
|
1865
|
-
argsValidation: 'dominds',
|
|
1866
|
-
async call(dlg, caller, args) {
|
|
1867
|
-
const language = getUserLang(dlg);
|
|
1868
|
-
try {
|
|
1869
|
-
const mindsState = await getMindsDirState();
|
|
1870
|
-
if (mindsState.kind === 'missing') {
|
|
1871
|
-
const msg = formatMindsMissingNotice(language);
|
|
1872
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1873
|
-
}
|
|
1874
|
-
if (mindsState.kind === 'not_directory') {
|
|
1875
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1876
|
-
}
|
|
1877
|
-
const fromValue = args['from'];
|
|
1878
|
-
const toValue = args['to'];
|
|
1879
|
-
const rawFrom = typeof fromValue === 'string' ? fromValue.trim() : '';
|
|
1880
|
-
const rawTo = typeof toValue === 'string' ? toValue.trim() : '';
|
|
1881
|
-
if (!rawFrom || !rawTo)
|
|
1882
|
-
throw new Error('From/to required');
|
|
1883
|
-
const fromRel = toMindsRelativePath(rawFrom);
|
|
1884
|
-
const toRel = toMindsRelativePath(rawTo);
|
|
1885
|
-
ensureMindsScopedPath(fromRel);
|
|
1886
|
-
ensureMindsScopedPath(toRel);
|
|
1887
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1888
|
-
const output = await fs_1.moveFileTool.call(dlg, proxyCaller, { from: fromRel, to: toRel });
|
|
1889
|
-
const content = toolCallOutputToString(output);
|
|
1890
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1891
|
-
}
|
|
1892
|
-
catch (err) {
|
|
1893
|
-
const msg = language === 'zh'
|
|
1894
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1895
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1896
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1897
|
-
}
|
|
1898
|
-
},
|
|
1899
|
-
};
|
|
1900
|
-
exports.teamMgmtMoveDirTool = {
|
|
1901
|
-
type: 'func',
|
|
1902
|
-
name: 'team_mgmt_move_dir',
|
|
1903
|
-
description: `Move/rename a directory under ${MINDS_DIR}/.`,
|
|
1904
|
-
descriptionI18n: {
|
|
1905
|
-
en: `Move/rename a directory under ${MINDS_DIR}/.`,
|
|
1906
|
-
zh: `移动/重命名 ${MINDS_DIR}/ 下目录。`,
|
|
1907
|
-
},
|
|
1908
|
-
parameters: {
|
|
1909
|
-
type: 'object',
|
|
1910
|
-
additionalProperties: false,
|
|
1911
|
-
required: ['from', 'to'],
|
|
1912
|
-
properties: { from: { type: 'string' }, to: { type: 'string' } },
|
|
1913
|
-
},
|
|
1914
|
-
argsValidation: 'dominds',
|
|
1915
|
-
async call(dlg, caller, args) {
|
|
1916
|
-
const language = getUserLang(dlg);
|
|
1917
|
-
try {
|
|
1918
|
-
const mindsState = await getMindsDirState();
|
|
1919
|
-
if (mindsState.kind === 'missing') {
|
|
1920
|
-
const msg = formatMindsMissingNotice(language);
|
|
1921
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1922
|
-
}
|
|
1923
|
-
if (mindsState.kind === 'not_directory') {
|
|
1924
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1925
|
-
}
|
|
1926
|
-
const fromValue = args['from'];
|
|
1927
|
-
const toValue = args['to'];
|
|
1928
|
-
const rawFrom = typeof fromValue === 'string' ? fromValue.trim() : '';
|
|
1929
|
-
const rawTo = typeof toValue === 'string' ? toValue.trim() : '';
|
|
1930
|
-
if (!rawFrom || !rawTo)
|
|
1931
|
-
throw new Error('From/to required');
|
|
1932
|
-
const fromRel = toMindsRelativePath(rawFrom);
|
|
1933
|
-
const toRel = toMindsRelativePath(rawTo);
|
|
1934
|
-
ensureMindsScopedPath(fromRel);
|
|
1935
|
-
ensureMindsScopedPath(toRel);
|
|
1936
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
1937
|
-
const output = await fs_1.moveDirTool.call(dlg, proxyCaller, { from: fromRel, to: toRel });
|
|
1938
|
-
const content = toolCallOutputToString(output);
|
|
1939
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
1940
|
-
}
|
|
1941
|
-
catch (err) {
|
|
1942
|
-
const msg = language === 'zh'
|
|
1943
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
1944
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1945
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1946
|
-
}
|
|
1947
|
-
},
|
|
1948
|
-
};
|
|
1949
|
-
exports.teamMgmtRipgrepFilesTool = {
|
|
1950
|
-
type: 'func',
|
|
1951
|
-
name: 'team_mgmt_ripgrep_files',
|
|
1952
|
-
description: `Search within ${MINDS_DIR}/ using ripgrep_files.`,
|
|
1953
|
-
descriptionI18n: {
|
|
1954
|
-
en: `Search within ${MINDS_DIR}/ using ripgrep_files.`,
|
|
1955
|
-
zh: `在 ${MINDS_DIR}/ 下用 ripgrep_files 搜索。`,
|
|
1956
|
-
},
|
|
1957
|
-
parameters: {
|
|
1958
|
-
type: 'object',
|
|
1959
|
-
additionalProperties: false,
|
|
1960
|
-
required: ['pattern'],
|
|
1961
|
-
properties: {
|
|
1962
|
-
pattern: { type: 'string' },
|
|
1963
|
-
path: { type: 'string' },
|
|
1964
|
-
globs: { type: 'array', items: { type: 'string' } },
|
|
1965
|
-
case: { type: 'string' },
|
|
1966
|
-
fixed_strings: { type: 'boolean' },
|
|
1967
|
-
max_files: { type: 'integer' },
|
|
1968
|
-
include_hidden: { type: 'boolean' },
|
|
1969
|
-
follow_symlinks: { type: 'boolean' },
|
|
1970
|
-
},
|
|
1971
|
-
},
|
|
1972
|
-
argsValidation: 'dominds',
|
|
1973
|
-
async call(dlg, caller, args) {
|
|
1974
|
-
const language = getUserLang(dlg);
|
|
1975
|
-
try {
|
|
1976
|
-
const mindsState = await getMindsDirState();
|
|
1977
|
-
if (mindsState.kind === 'missing') {
|
|
1978
|
-
const msg = formatMindsMissingNotice(language);
|
|
1979
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
1980
|
-
}
|
|
1981
|
-
if (mindsState.kind === 'not_directory') {
|
|
1982
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
1983
|
-
}
|
|
1984
|
-
const patternValue = args['pattern'];
|
|
1985
|
-
const pattern = typeof patternValue === 'string' ? patternValue.trim() : '';
|
|
1986
|
-
if (!pattern)
|
|
1987
|
-
throw new Error('Pattern required');
|
|
1988
|
-
const pathValue = args['path'];
|
|
1989
|
-
const rawPath = typeof pathValue === 'string' && pathValue.trim() !== '' ? pathValue.trim() : MINDS_DIR;
|
|
1990
|
-
const rel = toMindsRelativePath(rawPath);
|
|
1991
|
-
ensureMindsScopedPath(rel);
|
|
1992
|
-
const toolArgs = { pattern, path: rel };
|
|
1993
|
-
const globsValue = args['globs'];
|
|
1994
|
-
if (globsValue !== undefined) {
|
|
1995
|
-
if (!Array.isArray(globsValue) || !globsValue.every((v) => typeof v === 'string')) {
|
|
1996
|
-
throw new Error('Invalid globs (expected string[])');
|
|
1997
|
-
}
|
|
1998
|
-
toolArgs['globs'] = globsValue;
|
|
1999
|
-
}
|
|
2000
|
-
const caseValue = args['case'];
|
|
2001
|
-
if (caseValue !== undefined) {
|
|
2002
|
-
if (typeof caseValue !== 'string')
|
|
2003
|
-
throw new Error('Invalid case (expected string)');
|
|
2004
|
-
toolArgs['case'] = caseValue;
|
|
2005
|
-
}
|
|
2006
|
-
const fixedStringsValue = args['fixed_strings'];
|
|
2007
|
-
if (fixedStringsValue !== undefined) {
|
|
2008
|
-
if (typeof fixedStringsValue !== 'boolean')
|
|
2009
|
-
throw new Error('Invalid fixed_strings (expected boolean)');
|
|
2010
|
-
toolArgs['fixed_strings'] = fixedStringsValue;
|
|
2011
|
-
}
|
|
2012
|
-
const includeHiddenValue = args['include_hidden'];
|
|
2013
|
-
if (includeHiddenValue !== undefined) {
|
|
2014
|
-
if (typeof includeHiddenValue !== 'boolean')
|
|
2015
|
-
throw new Error('Invalid include_hidden (expected boolean)');
|
|
2016
|
-
toolArgs['include_hidden'] = includeHiddenValue;
|
|
2017
|
-
}
|
|
2018
|
-
const followSymlinksValue = args['follow_symlinks'];
|
|
2019
|
-
if (followSymlinksValue !== undefined) {
|
|
2020
|
-
if (typeof followSymlinksValue !== 'boolean')
|
|
2021
|
-
throw new Error('Invalid follow_symlinks (expected boolean)');
|
|
2022
|
-
toolArgs['follow_symlinks'] = followSymlinksValue;
|
|
2023
|
-
}
|
|
2024
|
-
const maxFilesValue = args['max_files'];
|
|
2025
|
-
if (maxFilesValue !== undefined) {
|
|
2026
|
-
if (typeof maxFilesValue !== 'number' || !Number.isInteger(maxFilesValue))
|
|
2027
|
-
throw new Error('Invalid max_files (expected integer)');
|
|
2028
|
-
toolArgs['max_files'] = maxFilesValue;
|
|
2029
|
-
}
|
|
2030
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
2031
|
-
const output = await ripgrep_1.ripgrepFilesTool.call(dlg, proxyCaller, toolArgs);
|
|
2032
|
-
const content = toolCallOutputToString(output);
|
|
2033
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
2034
|
-
}
|
|
2035
|
-
catch (err) {
|
|
2036
|
-
const msg = language === 'zh'
|
|
2037
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
2038
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
2039
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2040
|
-
}
|
|
2041
|
-
},
|
|
2042
|
-
};
|
|
2043
|
-
exports.teamMgmtRipgrepSnippetsTool = {
|
|
2044
|
-
type: 'func',
|
|
2045
|
-
name: 'team_mgmt_ripgrep_snippets',
|
|
2046
|
-
description: `Search within ${MINDS_DIR}/ using ripgrep_snippets.`,
|
|
2047
|
-
descriptionI18n: {
|
|
2048
|
-
en: `Search within ${MINDS_DIR}/ using ripgrep_snippets.`,
|
|
2049
|
-
zh: `在 ${MINDS_DIR}/ 下用 ripgrep_snippets 搜索。`,
|
|
2050
|
-
},
|
|
2051
|
-
parameters: {
|
|
2052
|
-
type: 'object',
|
|
2053
|
-
additionalProperties: false,
|
|
2054
|
-
required: ['pattern'],
|
|
2055
|
-
properties: {
|
|
2056
|
-
pattern: { type: 'string' },
|
|
2057
|
-
path: { type: 'string' },
|
|
2058
|
-
globs: { type: 'array', items: { type: 'string' } },
|
|
2059
|
-
case: { type: 'string' },
|
|
2060
|
-
fixed_strings: { type: 'boolean' },
|
|
2061
|
-
context_before: { type: 'integer' },
|
|
2062
|
-
context_after: { type: 'integer' },
|
|
2063
|
-
max_results: { type: 'integer' },
|
|
2064
|
-
include_hidden: { type: 'boolean' },
|
|
2065
|
-
follow_symlinks: { type: 'boolean' },
|
|
2066
|
-
},
|
|
2067
|
-
},
|
|
2068
|
-
argsValidation: 'dominds',
|
|
2069
|
-
async call(dlg, caller, args) {
|
|
2070
|
-
const language = getUserLang(dlg);
|
|
2071
|
-
try {
|
|
2072
|
-
const mindsState = await getMindsDirState();
|
|
2073
|
-
if (mindsState.kind === 'missing') {
|
|
2074
|
-
const msg = formatMindsMissingNotice(language);
|
|
2075
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2076
|
-
}
|
|
2077
|
-
if (mindsState.kind === 'not_directory') {
|
|
2078
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
2079
|
-
}
|
|
2080
|
-
const patternValue = args['pattern'];
|
|
2081
|
-
const pattern = typeof patternValue === 'string' ? patternValue.trim() : '';
|
|
2082
|
-
if (!pattern)
|
|
2083
|
-
throw new Error('Pattern required');
|
|
2084
|
-
const pathValue = args['path'];
|
|
2085
|
-
const rawPath = typeof pathValue === 'string' && pathValue.trim() !== '' ? pathValue.trim() : MINDS_DIR;
|
|
2086
|
-
const rel = toMindsRelativePath(rawPath);
|
|
2087
|
-
ensureMindsScopedPath(rel);
|
|
2088
|
-
const toolArgs = { pattern, path: rel };
|
|
2089
|
-
const globsValue = args['globs'];
|
|
2090
|
-
if (globsValue !== undefined) {
|
|
2091
|
-
if (!Array.isArray(globsValue) || !globsValue.every((v) => typeof v === 'string')) {
|
|
2092
|
-
throw new Error('Invalid globs (expected string[])');
|
|
2093
|
-
}
|
|
2094
|
-
toolArgs['globs'] = globsValue;
|
|
2095
|
-
}
|
|
2096
|
-
const caseValue = args['case'];
|
|
2097
|
-
if (caseValue !== undefined) {
|
|
2098
|
-
if (typeof caseValue !== 'string')
|
|
2099
|
-
throw new Error('Invalid case (expected string)');
|
|
2100
|
-
toolArgs['case'] = caseValue;
|
|
2101
|
-
}
|
|
2102
|
-
const fixedStringsValue = args['fixed_strings'];
|
|
2103
|
-
if (fixedStringsValue !== undefined) {
|
|
2104
|
-
if (typeof fixedStringsValue !== 'boolean')
|
|
2105
|
-
throw new Error('Invalid fixed_strings (expected boolean)');
|
|
2106
|
-
toolArgs['fixed_strings'] = fixedStringsValue;
|
|
2107
|
-
}
|
|
2108
|
-
const contextBeforeValue = args['context_before'];
|
|
2109
|
-
if (contextBeforeValue !== undefined) {
|
|
2110
|
-
if (typeof contextBeforeValue !== 'number' || !Number.isInteger(contextBeforeValue)) {
|
|
2111
|
-
throw new Error('Invalid context_before (expected integer)');
|
|
2112
|
-
}
|
|
2113
|
-
toolArgs['context_before'] = contextBeforeValue;
|
|
2114
|
-
}
|
|
2115
|
-
const contextAfterValue = args['context_after'];
|
|
2116
|
-
if (contextAfterValue !== undefined) {
|
|
2117
|
-
if (typeof contextAfterValue !== 'number' || !Number.isInteger(contextAfterValue)) {
|
|
2118
|
-
throw new Error('Invalid context_after (expected integer)');
|
|
2119
|
-
}
|
|
2120
|
-
toolArgs['context_after'] = contextAfterValue;
|
|
2121
|
-
}
|
|
2122
|
-
const maxResultsValue = args['max_results'];
|
|
2123
|
-
if (maxResultsValue !== undefined) {
|
|
2124
|
-
if (typeof maxResultsValue !== 'number' || !Number.isInteger(maxResultsValue)) {
|
|
2125
|
-
throw new Error('Invalid max_results (expected integer)');
|
|
2126
|
-
}
|
|
2127
|
-
toolArgs['max_results'] = maxResultsValue;
|
|
2128
|
-
}
|
|
2129
|
-
const includeHiddenValue = args['include_hidden'];
|
|
2130
|
-
if (includeHiddenValue !== undefined) {
|
|
2131
|
-
if (typeof includeHiddenValue !== 'boolean')
|
|
2132
|
-
throw new Error('Invalid include_hidden (expected boolean)');
|
|
2133
|
-
toolArgs['include_hidden'] = includeHiddenValue;
|
|
2134
|
-
}
|
|
2135
|
-
const followSymlinksValue = args['follow_symlinks'];
|
|
2136
|
-
if (followSymlinksValue !== undefined) {
|
|
2137
|
-
if (typeof followSymlinksValue !== 'boolean')
|
|
2138
|
-
throw new Error('Invalid follow_symlinks (expected boolean)');
|
|
2139
|
-
toolArgs['follow_symlinks'] = followSymlinksValue;
|
|
2140
|
-
}
|
|
2141
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
2142
|
-
const output = await ripgrep_1.ripgrepSnippetsTool.call(dlg, proxyCaller, toolArgs);
|
|
2143
|
-
const content = toolCallOutputToString(output);
|
|
2144
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
2145
|
-
}
|
|
2146
|
-
catch (err) {
|
|
2147
|
-
const msg = language === 'zh'
|
|
2148
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
2149
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
2150
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2151
|
-
}
|
|
2152
|
-
},
|
|
2153
|
-
};
|
|
2154
|
-
exports.teamMgmtRipgrepCountTool = {
|
|
2155
|
-
type: 'func',
|
|
2156
|
-
name: 'team_mgmt_ripgrep_count',
|
|
2157
|
-
description: `Count matches within ${MINDS_DIR}/ using ripgrep_count.`,
|
|
2158
|
-
descriptionI18n: {
|
|
2159
|
-
en: `Count matches within ${MINDS_DIR}/ using ripgrep_count.`,
|
|
2160
|
-
zh: `在 ${MINDS_DIR}/ 下用 ripgrep_count 计数。`,
|
|
2161
|
-
},
|
|
2162
|
-
parameters: {
|
|
2163
|
-
type: 'object',
|
|
2164
|
-
additionalProperties: false,
|
|
2165
|
-
required: ['pattern'],
|
|
2166
|
-
properties: {
|
|
2167
|
-
pattern: { type: 'string' },
|
|
2168
|
-
path: { type: 'string' },
|
|
2169
|
-
globs: { type: 'array', items: { type: 'string' } },
|
|
2170
|
-
case: { type: 'string' },
|
|
2171
|
-
fixed_strings: { type: 'boolean' },
|
|
2172
|
-
max_files: { type: 'integer' },
|
|
2173
|
-
include_hidden: { type: 'boolean' },
|
|
2174
|
-
follow_symlinks: { type: 'boolean' },
|
|
2175
|
-
},
|
|
2176
|
-
},
|
|
2177
|
-
argsValidation: 'dominds',
|
|
2178
|
-
async call(dlg, caller, args) {
|
|
2179
|
-
const language = getUserLang(dlg);
|
|
2180
|
-
try {
|
|
2181
|
-
const mindsState = await getMindsDirState();
|
|
2182
|
-
if (mindsState.kind === 'missing') {
|
|
2183
|
-
const msg = formatMindsMissingNotice(language);
|
|
2184
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2185
|
-
}
|
|
2186
|
-
if (mindsState.kind === 'not_directory') {
|
|
2187
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
2188
|
-
}
|
|
2189
|
-
const patternValue = args['pattern'];
|
|
2190
|
-
const pattern = typeof patternValue === 'string' ? patternValue.trim() : '';
|
|
2191
|
-
if (!pattern)
|
|
2192
|
-
throw new Error('Pattern required');
|
|
2193
|
-
const pathValue = args['path'];
|
|
2194
|
-
const rawPath = typeof pathValue === 'string' && pathValue.trim() !== '' ? pathValue.trim() : MINDS_DIR;
|
|
2195
|
-
const rel = toMindsRelativePath(rawPath);
|
|
2196
|
-
ensureMindsScopedPath(rel);
|
|
2197
|
-
const toolArgs = { pattern, path: rel };
|
|
2198
|
-
const globsValue = args['globs'];
|
|
2199
|
-
if (globsValue !== undefined) {
|
|
2200
|
-
if (!Array.isArray(globsValue) || !globsValue.every((v) => typeof v === 'string')) {
|
|
2201
|
-
throw new Error('Invalid globs (expected string[])');
|
|
2202
|
-
}
|
|
2203
|
-
toolArgs['globs'] = globsValue;
|
|
2204
|
-
}
|
|
2205
|
-
const caseValue = args['case'];
|
|
2206
|
-
if (caseValue !== undefined) {
|
|
2207
|
-
if (typeof caseValue !== 'string')
|
|
2208
|
-
throw new Error('Invalid case (expected string)');
|
|
2209
|
-
toolArgs['case'] = caseValue;
|
|
2210
|
-
}
|
|
2211
|
-
const fixedStringsValue = args['fixed_strings'];
|
|
2212
|
-
if (fixedStringsValue !== undefined) {
|
|
2213
|
-
if (typeof fixedStringsValue !== 'boolean')
|
|
2214
|
-
throw new Error('Invalid fixed_strings (expected boolean)');
|
|
2215
|
-
toolArgs['fixed_strings'] = fixedStringsValue;
|
|
2216
|
-
}
|
|
2217
|
-
const includeHiddenValue = args['include_hidden'];
|
|
2218
|
-
if (includeHiddenValue !== undefined) {
|
|
2219
|
-
if (typeof includeHiddenValue !== 'boolean')
|
|
2220
|
-
throw new Error('Invalid include_hidden (expected boolean)');
|
|
2221
|
-
toolArgs['include_hidden'] = includeHiddenValue;
|
|
2222
|
-
}
|
|
2223
|
-
const followSymlinksValue = args['follow_symlinks'];
|
|
2224
|
-
if (followSymlinksValue !== undefined) {
|
|
2225
|
-
if (typeof followSymlinksValue !== 'boolean')
|
|
2226
|
-
throw new Error('Invalid follow_symlinks (expected boolean)');
|
|
2227
|
-
toolArgs['follow_symlinks'] = followSymlinksValue;
|
|
2228
|
-
}
|
|
2229
|
-
const maxFilesValue = args['max_files'];
|
|
2230
|
-
if (maxFilesValue !== undefined) {
|
|
2231
|
-
if (typeof maxFilesValue !== 'number' || !Number.isInteger(maxFilesValue))
|
|
2232
|
-
throw new Error('Invalid max_files (expected integer)');
|
|
2233
|
-
toolArgs['max_files'] = maxFilesValue;
|
|
2234
|
-
}
|
|
2235
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
2236
|
-
const output = await ripgrep_1.ripgrepCountTool.call(dlg, proxyCaller, toolArgs);
|
|
2237
|
-
const content = toolCallOutputToString(output);
|
|
2238
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
2239
|
-
}
|
|
2240
|
-
catch (err) {
|
|
2241
|
-
const msg = language === 'zh'
|
|
2242
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
2243
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
2244
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2245
|
-
}
|
|
2246
|
-
},
|
|
2247
|
-
};
|
|
2248
|
-
exports.teamMgmtRipgrepFixedTool = {
|
|
2249
|
-
type: 'func',
|
|
2250
|
-
name: 'team_mgmt_ripgrep_fixed',
|
|
2251
|
-
description: `Fixed-string ripgrep within ${MINDS_DIR}/.`,
|
|
2252
|
-
descriptionI18n: {
|
|
2253
|
-
en: `Fixed-string ripgrep within ${MINDS_DIR}/.`,
|
|
2254
|
-
zh: `在 ${MINDS_DIR}/ 下固定字符串搜索。`,
|
|
2255
|
-
},
|
|
2256
|
-
parameters: {
|
|
2257
|
-
type: 'object',
|
|
2258
|
-
additionalProperties: false,
|
|
2259
|
-
required: ['literal'],
|
|
2260
|
-
properties: {
|
|
2261
|
-
literal: { type: 'string' },
|
|
2262
|
-
path: { type: 'string' },
|
|
2263
|
-
mode: { type: 'string' },
|
|
2264
|
-
globs: { type: 'array', items: { type: 'string' } },
|
|
2265
|
-
case: { type: 'string' },
|
|
2266
|
-
max_files: { type: 'integer' },
|
|
2267
|
-
max_results: { type: 'integer' },
|
|
2268
|
-
context_before: { type: 'integer' },
|
|
2269
|
-
context_after: { type: 'integer' },
|
|
2270
|
-
include_hidden: { type: 'boolean' },
|
|
2271
|
-
follow_symlinks: { type: 'boolean' },
|
|
2272
|
-
},
|
|
2273
|
-
},
|
|
2274
|
-
argsValidation: 'dominds',
|
|
2275
|
-
async call(dlg, caller, args) {
|
|
2276
|
-
const language = getUserLang(dlg);
|
|
2277
|
-
try {
|
|
2278
|
-
const mindsState = await getMindsDirState();
|
|
2279
|
-
if (mindsState.kind === 'missing') {
|
|
2280
|
-
const msg = formatMindsMissingNotice(language);
|
|
2281
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2282
|
-
}
|
|
2283
|
-
if (mindsState.kind === 'not_directory') {
|
|
2284
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
2285
|
-
}
|
|
2286
|
-
const literalValue = args['literal'];
|
|
2287
|
-
const literal = typeof literalValue === 'string' ? literalValue.trim() : '';
|
|
2288
|
-
if (!literal)
|
|
2289
|
-
throw new Error('Literal required');
|
|
2290
|
-
const pathValue = args['path'];
|
|
2291
|
-
const rawPath = typeof pathValue === 'string' && pathValue.trim() !== '' ? pathValue.trim() : MINDS_DIR;
|
|
2292
|
-
const rel = toMindsRelativePath(rawPath);
|
|
2293
|
-
ensureMindsScopedPath(rel);
|
|
2294
|
-
const toolArgs = { literal, path: rel };
|
|
2295
|
-
const modeValue = args['mode'];
|
|
2296
|
-
if (modeValue !== undefined) {
|
|
2297
|
-
if (typeof modeValue !== 'string')
|
|
2298
|
-
throw new Error('Invalid mode (expected string)');
|
|
2299
|
-
toolArgs['mode'] = modeValue;
|
|
2300
|
-
}
|
|
2301
|
-
const caseValue = args['case'];
|
|
2302
|
-
if (caseValue !== undefined) {
|
|
2303
|
-
if (typeof caseValue !== 'string')
|
|
2304
|
-
throw new Error('Invalid case (expected string)');
|
|
2305
|
-
toolArgs['case'] = caseValue;
|
|
2306
|
-
}
|
|
2307
|
-
const globsValue = args['globs'];
|
|
2308
|
-
if (globsValue !== undefined) {
|
|
2309
|
-
if (!Array.isArray(globsValue) || !globsValue.every((v) => typeof v === 'string')) {
|
|
2310
|
-
throw new Error('Invalid globs (expected string[])');
|
|
2311
|
-
}
|
|
2312
|
-
toolArgs['globs'] = globsValue;
|
|
2313
|
-
}
|
|
2314
|
-
const maxFilesValue = args['max_files'];
|
|
2315
|
-
if (maxFilesValue !== undefined) {
|
|
2316
|
-
if (typeof maxFilesValue !== 'number' || !Number.isInteger(maxFilesValue))
|
|
2317
|
-
throw new Error('Invalid max_files (expected integer)');
|
|
2318
|
-
toolArgs['max_files'] = maxFilesValue;
|
|
2319
|
-
}
|
|
2320
|
-
const maxResultsValue = args['max_results'];
|
|
2321
|
-
if (maxResultsValue !== undefined) {
|
|
2322
|
-
if (typeof maxResultsValue !== 'number' || !Number.isInteger(maxResultsValue))
|
|
2323
|
-
throw new Error('Invalid max_results (expected integer)');
|
|
2324
|
-
toolArgs['max_results'] = maxResultsValue;
|
|
2325
|
-
}
|
|
2326
|
-
const contextBeforeValue = args['context_before'];
|
|
2327
|
-
if (contextBeforeValue !== undefined) {
|
|
2328
|
-
if (typeof contextBeforeValue !== 'number' || !Number.isInteger(contextBeforeValue))
|
|
2329
|
-
throw new Error('Invalid context_before (expected integer)');
|
|
2330
|
-
toolArgs['context_before'] = contextBeforeValue;
|
|
2331
|
-
}
|
|
2332
|
-
const contextAfterValue = args['context_after'];
|
|
2333
|
-
if (contextAfterValue !== undefined) {
|
|
2334
|
-
if (typeof contextAfterValue !== 'number' || !Number.isInteger(contextAfterValue))
|
|
2335
|
-
throw new Error('Invalid context_after (expected integer)');
|
|
2336
|
-
toolArgs['context_after'] = contextAfterValue;
|
|
2337
|
-
}
|
|
2338
|
-
const includeHiddenValue = args['include_hidden'];
|
|
2339
|
-
if (includeHiddenValue !== undefined) {
|
|
2340
|
-
if (typeof includeHiddenValue !== 'boolean')
|
|
2341
|
-
throw new Error('Invalid include_hidden (expected boolean)');
|
|
2342
|
-
toolArgs['include_hidden'] = includeHiddenValue;
|
|
2343
|
-
}
|
|
2344
|
-
const followSymlinksValue = args['follow_symlinks'];
|
|
2345
|
-
if (followSymlinksValue !== undefined) {
|
|
2346
|
-
if (typeof followSymlinksValue !== 'boolean')
|
|
2347
|
-
throw new Error('Invalid follow_symlinks (expected boolean)');
|
|
2348
|
-
toolArgs['follow_symlinks'] = followSymlinksValue;
|
|
2349
|
-
}
|
|
2350
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
2351
|
-
const output = await ripgrep_1.ripgrepFixedTool.call(dlg, proxyCaller, toolArgs);
|
|
2352
|
-
const content = toolCallOutputToString(output);
|
|
2353
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
2354
|
-
}
|
|
2355
|
-
catch (err) {
|
|
2356
|
-
const msg = language === 'zh'
|
|
2357
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
2358
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
2359
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2360
|
-
}
|
|
2361
|
-
},
|
|
2362
|
-
};
|
|
2363
|
-
exports.teamMgmtRipgrepSearchTool = {
|
|
2364
|
-
type: 'func',
|
|
2365
|
-
name: 'team_mgmt_ripgrep_search',
|
|
2366
|
-
description: `Escape hatch ripgrep_search within ${MINDS_DIR}/.`,
|
|
2367
|
-
descriptionI18n: {
|
|
2368
|
-
en: `Escape hatch ripgrep_search within ${MINDS_DIR}/.`,
|
|
2369
|
-
zh: `在 ${MINDS_DIR}/ 下使用 ripgrep_search 逃生舱。`,
|
|
2370
|
-
},
|
|
2371
|
-
parameters: {
|
|
2372
|
-
type: 'object',
|
|
2373
|
-
additionalProperties: false,
|
|
2374
|
-
required: ['pattern'],
|
|
2375
|
-
properties: {
|
|
2376
|
-
pattern: { type: 'string' },
|
|
2377
|
-
path: { type: 'string' },
|
|
2378
|
-
rg_args: { type: 'array', items: { type: 'string' } },
|
|
2379
|
-
},
|
|
2380
|
-
},
|
|
2381
|
-
argsValidation: 'dominds',
|
|
2382
|
-
async call(dlg, caller, args) {
|
|
2383
|
-
const language = getUserLang(dlg);
|
|
2384
|
-
try {
|
|
2385
|
-
const mindsState = await getMindsDirState();
|
|
2386
|
-
if (mindsState.kind === 'missing') {
|
|
2387
|
-
const msg = formatMindsMissingNotice(language);
|
|
2388
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2389
|
-
}
|
|
2390
|
-
if (mindsState.kind === 'not_directory') {
|
|
2391
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
2392
|
-
}
|
|
2393
|
-
const patternValue = args['pattern'];
|
|
2394
|
-
const pattern = typeof patternValue === 'string' ? patternValue.trim() : '';
|
|
2395
|
-
if (!pattern)
|
|
2396
|
-
throw new Error('Pattern required');
|
|
2397
|
-
const pathValue = args['path'];
|
|
2398
|
-
const rawPath = typeof pathValue === 'string' && pathValue.trim() !== '' ? pathValue.trim() : MINDS_DIR;
|
|
2399
|
-
const rel = toMindsRelativePath(rawPath);
|
|
2400
|
-
ensureMindsScopedPath(rel);
|
|
2401
|
-
const rgArgsValue = args['rg_args'];
|
|
2402
|
-
if (rgArgsValue !== undefined) {
|
|
2403
|
-
if (!Array.isArray(rgArgsValue) || !rgArgsValue.every((v) => typeof v === 'string')) {
|
|
2404
|
-
throw new Error('Invalid rg_args (expected string[])');
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
const toolArgs = {
|
|
2408
|
-
pattern,
|
|
2409
|
-
path: rel,
|
|
2410
|
-
...(rgArgsValue !== undefined ? { rg_args: rgArgsValue } : {}),
|
|
2411
|
-
};
|
|
2412
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
2413
|
-
const output = await ripgrep_1.ripgrepSearchTool.call(dlg, proxyCaller, toolArgs);
|
|
2414
|
-
const content = toolCallOutputToString(output);
|
|
2415
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
2416
|
-
}
|
|
2417
|
-
catch (err) {
|
|
2418
|
-
const msg = language === 'zh'
|
|
2419
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
2420
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
2421
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2422
|
-
}
|
|
2423
|
-
},
|
|
2424
|
-
};
|
|
2425
|
-
exports.teamMgmtRmFileTool = {
|
|
2426
|
-
type: 'func',
|
|
2427
|
-
name: 'team_mgmt_rm_file',
|
|
2428
|
-
description: `Remove a file under ${MINDS_DIR}/.`,
|
|
2429
|
-
descriptionI18n: {
|
|
2430
|
-
en: `Remove a file under ${MINDS_DIR}/.`,
|
|
2431
|
-
zh: `删除 ${MINDS_DIR}/ 下的文件。`,
|
|
2432
|
-
},
|
|
2433
|
-
parameters: {
|
|
2434
|
-
type: 'object',
|
|
2435
|
-
additionalProperties: false,
|
|
2436
|
-
required: ['path'],
|
|
2437
|
-
properties: { path: { type: 'string' } },
|
|
2438
|
-
},
|
|
2439
|
-
argsValidation: 'dominds',
|
|
2440
|
-
async call(dlg, caller, args) {
|
|
2441
|
-
const language = getUserLang(dlg);
|
|
2442
|
-
try {
|
|
2443
|
-
const mindsState = await getMindsDirState();
|
|
2444
|
-
if (mindsState.kind === 'missing') {
|
|
2445
|
-
const msg = formatMindsMissingNotice(language);
|
|
2446
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2447
|
-
}
|
|
2448
|
-
if (mindsState.kind === 'not_directory') {
|
|
2449
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
2450
|
-
}
|
|
2451
|
-
const pathValue = args['path'];
|
|
2452
|
-
const filePath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
2453
|
-
if (!filePath)
|
|
2454
|
-
throw new Error('Path required');
|
|
2455
|
-
const rel = toMindsRelativePath(filePath);
|
|
2456
|
-
ensureMindsScopedPath(rel);
|
|
2457
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
2458
|
-
const output = await fs_1.rmFileTool.call(dlg, proxyCaller, { path: rel });
|
|
2459
|
-
const content = toolCallOutputToString(output);
|
|
2460
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
2461
|
-
}
|
|
2462
|
-
catch (err) {
|
|
2463
|
-
const msg = language === 'zh'
|
|
2464
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
2465
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
2466
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2467
|
-
}
|
|
2468
|
-
},
|
|
2469
|
-
};
|
|
2470
|
-
exports.teamMgmtRmDirTool = {
|
|
2471
|
-
type: 'func',
|
|
2472
|
-
name: 'team_mgmt_rm_dir',
|
|
2473
|
-
description: `Remove a directory under ${MINDS_DIR}/.`,
|
|
2474
|
-
descriptionI18n: {
|
|
2475
|
-
en: `Remove a directory under ${MINDS_DIR}/.`,
|
|
2476
|
-
zh: `删除 ${MINDS_DIR}/ 下的目录。`,
|
|
2477
|
-
},
|
|
2478
|
-
parameters: {
|
|
2479
|
-
type: 'object',
|
|
2480
|
-
additionalProperties: false,
|
|
2481
|
-
required: ['path'],
|
|
2482
|
-
properties: { path: { type: 'string' }, recursive: { type: 'boolean' } },
|
|
2483
|
-
},
|
|
2484
|
-
argsValidation: 'dominds',
|
|
2485
|
-
async call(dlg, caller, args) {
|
|
2486
|
-
const language = getUserLang(dlg);
|
|
2487
|
-
try {
|
|
2488
|
-
const mindsState = await getMindsDirState();
|
|
2489
|
-
if (mindsState.kind === 'missing') {
|
|
2490
|
-
const msg = formatMindsMissingNotice(language);
|
|
2491
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2492
|
-
}
|
|
2493
|
-
if (mindsState.kind === 'not_directory') {
|
|
2494
|
-
throw new Error(`${MINDS_DIR} exists but is not a directory: ${mindsState.abs}`);
|
|
2495
|
-
}
|
|
2496
|
-
const pathValue = args['path'];
|
|
2497
|
-
const rawPath = typeof pathValue === 'string' ? pathValue.trim() : '';
|
|
2498
|
-
if (!rawPath)
|
|
2499
|
-
throw new Error('Path required');
|
|
2500
|
-
const rel = toMindsRelativePath(rawPath);
|
|
2501
|
-
ensureMindsScopedPath(rel);
|
|
2502
|
-
const proxyCaller = makeMindsOnlyAccessMember(caller);
|
|
2503
|
-
const recursiveValue = args['recursive'];
|
|
2504
|
-
const recursive = recursiveValue === undefined ? undefined : recursiveValue === true ? true : false;
|
|
2505
|
-
if (recursiveValue !== undefined && typeof recursiveValue !== 'boolean') {
|
|
2506
|
-
throw new Error('Invalid recursive (expected boolean)');
|
|
2507
|
-
}
|
|
2508
|
-
const toolArgs = recursive === undefined ? { path: rel } : { path: rel, recursive };
|
|
2509
|
-
const output = await fs_1.rmDirTool.call(dlg, proxyCaller, toolArgs);
|
|
2510
|
-
const content = toolCallOutputToString(output);
|
|
2511
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
2512
|
-
}
|
|
2513
|
-
catch (err) {
|
|
2514
|
-
const msg = language === 'zh'
|
|
2515
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
2516
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
2517
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
2518
|
-
}
|
|
2519
|
-
},
|
|
2520
|
-
};
|
|
2521
|
-
function fmtHeader(title) {
|
|
2522
|
-
return `# ${title}\n`;
|
|
2523
|
-
}
|
|
2524
|
-
function fmtList(items) {
|
|
2525
|
-
return (items
|
|
2526
|
-
.filter((s) => s.trim() !== '')
|
|
2527
|
-
.map((s) => `- ${s}`)
|
|
2528
|
-
.join('\n') + '\n');
|
|
2529
|
-
}
|
|
2530
|
-
function fmtCodeBlock(lang, lines) {
|
|
2531
|
-
const body = lines.join('\n');
|
|
2532
|
-
return `\n\n\`\`\`${lang}\n${body}\n\`\`\`\n`;
|
|
2533
|
-
}
|
|
2534
|
-
function fmtSubHeader(title) {
|
|
2535
|
-
return `\n## ${title}\n`;
|
|
2536
|
-
}
|
|
2537
|
-
function fmtKeyList(keys) {
|
|
2538
|
-
return keys.map((k) => `\`${k}\``).join(' / ');
|
|
2539
|
-
}
|
|
2540
|
-
async function loadBuiltinLlmDefaultsText() {
|
|
2541
|
-
const defaultsPath = path_1.default.join(__dirname, '..', 'llm', 'defaults.yaml');
|
|
2542
|
-
const raw = await promises_1.default.readFile(defaultsPath, 'utf-8');
|
|
2543
|
-
const parsed = yaml_1.default.parse(raw);
|
|
2544
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
2545
|
-
return 'Invalid defaults.yaml';
|
|
2546
|
-
}
|
|
2547
|
-
const rec = parsed;
|
|
2548
|
-
const providersUnknown = rec['providers'];
|
|
2549
|
-
if (typeof providersUnknown !== 'object' || providersUnknown === null) {
|
|
2550
|
-
return 'Invalid defaults.yaml (missing providers)';
|
|
2551
|
-
}
|
|
2552
|
-
const providers = providersUnknown;
|
|
2553
|
-
const lines = [];
|
|
2554
|
-
for (const [providerId, pv] of Object.entries(providers)) {
|
|
2555
|
-
if (typeof pv !== 'object' || pv === null)
|
|
2556
|
-
continue;
|
|
2557
|
-
const provider = pv;
|
|
2558
|
-
const modelsUnknown = provider['models'];
|
|
2559
|
-
const modelIds = typeof modelsUnknown === 'object' && modelsUnknown !== null
|
|
2560
|
-
? Object.keys(modelsUnknown)
|
|
2561
|
-
: [];
|
|
2562
|
-
lines.push(`- ${providerId}: ${modelIds.slice(0, 30).join(', ')}${modelIds.length > 30 ? ', ...' : ''}`);
|
|
2563
|
-
}
|
|
2564
|
-
return lines.join('\n');
|
|
2565
|
-
}
|
|
2566
|
-
async function loadBuiltinLlmModelParamOptionsText() {
|
|
2567
|
-
const defaultsPath = path_1.default.join(__dirname, '..', 'llm', 'defaults.yaml');
|
|
2568
|
-
const raw = await promises_1.default.readFile(defaultsPath, 'utf-8');
|
|
2569
|
-
const parsed = yaml_1.default.parse(raw);
|
|
2570
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
2571
|
-
return 'Invalid defaults.yaml';
|
|
2572
|
-
}
|
|
2573
|
-
const rec = parsed;
|
|
2574
|
-
const providersUnknown = rec['providers'];
|
|
2575
|
-
if (typeof providersUnknown !== 'object' || providersUnknown === null) {
|
|
2576
|
-
return 'Invalid defaults.yaml (missing providers)';
|
|
2577
|
-
}
|
|
2578
|
-
const providers = providersUnknown;
|
|
2579
|
-
const lines = [];
|
|
2580
|
-
const summarizeSection = (section) => {
|
|
2581
|
-
const parts = [];
|
|
2582
|
-
for (const [paramName, paramUnknown] of Object.entries(section)) {
|
|
2583
|
-
if (typeof paramUnknown !== 'object' || paramUnknown === null)
|
|
2584
|
-
continue;
|
|
2585
|
-
const opt = paramUnknown;
|
|
2586
|
-
const prominent = opt['prominent'] === true;
|
|
2587
|
-
const typeUnknown = opt['type'];
|
|
2588
|
-
const type = typeof typeUnknown === 'string' ? typeUnknown : undefined;
|
|
2589
|
-
const valuesUnknown = opt['values'];
|
|
2590
|
-
const values = Array.isArray(valuesUnknown) && valuesUnknown.every((v) => typeof v === 'string')
|
|
2591
|
-
? valuesUnknown
|
|
2592
|
-
: undefined;
|
|
2593
|
-
const minUnknown = opt['min'];
|
|
2594
|
-
const min = typeof minUnknown === 'number' ? minUnknown : undefined;
|
|
2595
|
-
const maxUnknown = opt['max'];
|
|
2596
|
-
const max = typeof maxUnknown === 'number' ? maxUnknown : undefined;
|
|
2597
|
-
const defaultUnknown = opt['default'];
|
|
2598
|
-
const extras = [];
|
|
2599
|
-
if (type)
|
|
2600
|
-
extras.push(type);
|
|
2601
|
-
if (values && values.length > 0)
|
|
2602
|
-
extras.push(values.join('|'));
|
|
2603
|
-
if (min !== undefined || max !== undefined) {
|
|
2604
|
-
extras.push(`${min !== undefined ? min : ''}..${max !== undefined ? max : ''}`.trim());
|
|
2605
|
-
}
|
|
2606
|
-
if (defaultUnknown !== undefined) {
|
|
2607
|
-
let defaultText = null;
|
|
2608
|
-
if (type === 'enum' && typeof defaultUnknown === 'string') {
|
|
2609
|
-
defaultText = defaultUnknown;
|
|
2610
|
-
}
|
|
2611
|
-
else if ((type === 'number' || type === 'integer') &&
|
|
2612
|
-
typeof defaultUnknown === 'number') {
|
|
2613
|
-
defaultText = String(defaultUnknown);
|
|
2614
|
-
}
|
|
2615
|
-
else if (type === 'boolean' && typeof defaultUnknown === 'boolean') {
|
|
2616
|
-
defaultText = defaultUnknown ? 'true' : 'false';
|
|
2617
|
-
}
|
|
2618
|
-
else if (type === 'string' && typeof defaultUnknown === 'string') {
|
|
2619
|
-
defaultText = defaultUnknown;
|
|
2620
|
-
}
|
|
2621
|
-
else if (type === 'string_array' &&
|
|
2622
|
-
Array.isArray(defaultUnknown) &&
|
|
2623
|
-
defaultUnknown.every((v) => typeof v === 'string')) {
|
|
2624
|
-
defaultText = defaultUnknown.join('|');
|
|
2625
|
-
}
|
|
2626
|
-
else if (type === 'record_number' &&
|
|
2627
|
-
typeof defaultUnknown === 'object' &&
|
|
2628
|
-
defaultUnknown) {
|
|
2629
|
-
const rec = defaultUnknown;
|
|
2630
|
-
const entries = Object.entries(rec).filter(([, v]) => typeof v === 'number');
|
|
2631
|
-
if (entries.length > 0) {
|
|
2632
|
-
defaultText = entries
|
|
2633
|
-
.slice(0, 6)
|
|
2634
|
-
.map(([k, v]) => `${k}=${v}`)
|
|
2635
|
-
.join('|');
|
|
2636
|
-
}
|
|
2637
|
-
}
|
|
2638
|
-
if (defaultText)
|
|
2639
|
-
extras.push(`default=${defaultText}`);
|
|
2640
|
-
}
|
|
2641
|
-
const name = prominent ? `${paramName} [prominent]` : paramName;
|
|
2642
|
-
parts.push(extras.length > 0 ? `${name} (${extras.join(', ')})` : name);
|
|
2643
|
-
}
|
|
2644
|
-
return parts.join(', ');
|
|
2645
|
-
};
|
|
2646
|
-
for (const [providerId, providerUnknown] of Object.entries(providers)) {
|
|
2647
|
-
if (typeof providerUnknown !== 'object' || providerUnknown === null)
|
|
2648
|
-
continue;
|
|
2649
|
-
const provider = providerUnknown;
|
|
2650
|
-
const mpoUnknown = provider['model_param_options'];
|
|
2651
|
-
if (typeof mpoUnknown !== 'object' || mpoUnknown === null)
|
|
2652
|
-
continue;
|
|
2653
|
-
const mpo = mpoUnknown;
|
|
2654
|
-
const sections = [];
|
|
2655
|
-
for (const [sectionName, sectionUnknown] of Object.entries(mpo)) {
|
|
2656
|
-
if (typeof sectionUnknown !== 'object' || sectionUnknown === null)
|
|
2657
|
-
continue;
|
|
2658
|
-
const section = sectionUnknown;
|
|
2659
|
-
const summary = summarizeSection(section);
|
|
2660
|
-
if (!summary)
|
|
2661
|
-
continue;
|
|
2662
|
-
sections.push(`${sectionName}: ${summary}`);
|
|
2663
|
-
}
|
|
2664
|
-
if (sections.length === 0)
|
|
2665
|
-
continue;
|
|
2666
|
-
lines.push(`- ${providerId}: ${sections.join(' | ')}`);
|
|
2667
|
-
}
|
|
2668
|
-
return lines.length > 0 ? lines.join('\n') : '- (none)';
|
|
2669
|
-
}
|
|
2670
|
-
function renderMemberProperties(language) {
|
|
2671
|
-
const memberKeys = fmtKeyList(team_1.Team.TEAM_YAML_MEMBER_KEYS);
|
|
2672
|
-
if (language === 'zh') {
|
|
2673
|
-
return (fmtHeader('成员字段(members.<id>)') +
|
|
2674
|
-
fmtList([
|
|
2675
|
-
`字段白名单(以当前实现为准):${memberKeys}`,
|
|
2676
|
-
'`name` / `icon` / `gofor`',
|
|
2677
|
-
'`gofor`:该长期 agent 的职责速记卡(建议 5 行内),用于快速路由/提醒:写清“负责什么 / 不负责什么 / 主要交付物 / 优先级”。推荐用 YAML list(3–6 条);也支持 YAML object(单对象多键值,value 必须是 string),string 仅适合单句。对象的渲染顺序跟 YAML key 写入顺序一致(当前实现/依赖)。详细规范请写入 `.minds/team/<id>/*` 或 `.minds/team/domains/*.md` 等 Markdown 资产。',
|
|
2678
|
-
'`provider` / `model` / `model_params`',
|
|
2679
|
-
'`toolsets` / `tools`(两者可同时配置;多数情况下推荐用 toolsets 做粗粒度授权,用 tools 做少量补充/收敛。具体冲突/合并规则以当前实现为准)',
|
|
2680
|
-
'`diligence-push-max`:鞭策 上限(number)。也接受兼容别名 `diligence_push_max`,但请优先用 `diligence-push-max`。',
|
|
2681
|
-
'`streaming`:是否启用流式输出。注意:若该成员解析后的 provider 的 `apiType` 是 `codex`,则 `streaming: false` 属于配置错误(Codex 仅支持流式);会在 team 校验与运行期被视为严重问题并中止请求。',
|
|
2682
|
-
'`hidden`(影子/隐藏成员:不出现在系统提示的团队目录里,但仍可被诉请)',
|
|
2683
|
-
'`read_dirs` / `write_dirs` / `no_read_dirs` / `no_write_dirs`(冲突规则见 `team_mgmt_manual({ topics: ["permissions"] })`;read 与 write 是独立控制,别默认 write implies read)',
|
|
2684
|
-
]));
|
|
2685
|
-
}
|
|
2686
|
-
return (fmtHeader('Member Properties (members.<id>)') +
|
|
2687
|
-
fmtList([
|
|
2688
|
-
`Allow-list (per current implementation): ${memberKeys}`,
|
|
2689
|
-
'`name` / `icon` / `gofor`',
|
|
2690
|
-
'`gofor`: a short responsibility flashcard (≤ 5 lines) for a long-lived agent; use it for fast routing/reminders (owns / does-not-own / key deliverables / priorities). Prefer a YAML list (3–6 items); YAML object is also allowed (single object with multiple keys, string values only). Object rendering order follows the YAML key order (implementation-dependent). Use a string only for a single sentence. Put detailed specs in Markdown assets like `.minds/team/<id>/*` or `.minds/team/domains/*.md`.',
|
|
2691
|
-
'`provider` / `model` / `model_params`',
|
|
2692
|
-
'`toolsets` / `tools`(两者可同时配置;多数情况下推荐用 toolsets 做粗粒度授权,用 tools 做少量补充/收敛。具体冲突/合并规则以当前实现为准)',
|
|
2693
|
-
'`diligence-push-max`: Diligence Push cap (number). Compatibility alias `diligence_push_max` is accepted, but prefer `diligence-push-max`.',
|
|
2694
|
-
'`streaming`: whether to enable streaming output. Note: if the member resolves to a provider whose `apiType` is `codex`, then `streaming: false` is a configuration error (Codex is streaming-only); it is treated as a severe issue during validation/runtime and the request will be aborted.',
|
|
2695
|
-
'`hidden` (shadow/hidden member: excluded from system-prompt team directory, but callable)',
|
|
2696
|
-
'`read_dirs` / `write_dirs` / `no_read_dirs` / `no_write_dirs`(冲突规则见 `team_mgmt_manual({ topics: ["permissions"] })`;read 与 write 是独立控制,别默认 write implies read)',
|
|
2697
|
-
]));
|
|
2698
|
-
}
|
|
2699
|
-
function renderTeamManual(language) {
|
|
2700
|
-
const common = [
|
|
2701
|
-
'member_defaults: strongly recommended to set provider/model explicitly (omitting may fall back to built-in defaults)',
|
|
2702
|
-
'members: per-agent overrides inherit from member_defaults via prototype fallback',
|
|
2703
|
-
'after every modification to `.minds/team.yaml`: you must run `team_mgmt_validate_team_cfg({})` and resolve any Problems panel errors before proceeding to avoid runtime issues (e.g., wrong field types, missing fields, or broken path bindings)',
|
|
2704
|
-
'when changing provider/model: validate provider exists + env var is configured (use `team_mgmt_check_provider({ provider_key: "<providerKey>", model: "", all_models: false, live: false, max_models: 0 })`)',
|
|
2705
|
-
'to discover providers/models: use `team_mgmt_list_providers({})` and `team_mgmt_list_models({ provider_pattern: "*", model_pattern: "*" })`',
|
|
2706
|
-
'streaming: Codex providers (apiType=codex) are streaming-only. Setting members.<id>.streaming=false with a Codex provider is a config error and will abort requests.',
|
|
2707
|
-
'do not write built-in members (e.g. fuxi/pangu) into `.minds/team.yaml` (define only rtws members)',
|
|
2708
|
-
'`shell_specialists`: optional allow-list of member ids permitted to have shell tools. If any member has shell tools (e.g. toolset `os` / tools like `shell_exec`), they must be listed in shell_specialists; null/empty means “no shell specialists”.',
|
|
2709
|
-
'hidden: true marks a shadow member (not listed in system prompt)',
|
|
2710
|
-
];
|
|
2711
|
-
if (language === 'zh') {
|
|
2712
|
-
return (fmtHeader('.minds/team.yaml') +
|
|
2713
|
-
fmtList([
|
|
2714
|
-
'团队定义入口文件是 `.minds/team.yaml`(当前没有 `.minds/team.yml` / `.minds/team.json` 等别名;也不使用 `.minds/team.yaml` 以外的“等效入口”)。',
|
|
2715
|
-
'强烈建议显式设置 `member_defaults.provider` 与 `member_defaults.model`:如果省略,可能会使用实现内置的默认值(以当前实现为准),但可移植性/可复现性会变差,也更容易在环境变量未配置时把系统刷成板砖。',
|
|
2716
|
-
'每次修改 `.minds/team.yaml` 必须运行 `team_mgmt_validate_team_cfg({})`,并在继续之前先清空 Problems 面板里的 team.yaml 相关错误,避免潜在错误进入运行期(例如字段类型错误/字段缺失/路径绑定错误)。',
|
|
2717
|
-
'角色职责(Markdown)通过 `.minds/team/<id>/{persona,knowledge,lessons}.*.md` 绑定到 `members.<id>`:同一个 `<id>` 必须在 `team.yaml` 的 `members` 里出现,且在 `.minds/team/<id>/` 下存在对应的 mind 文件。',
|
|
2718
|
-
'团队机制默认范式是“长期 agent”(long-lived teammates):`members` 列表表示稳定存在、可随时被诉请的队友,并非“按需子角色/临时 sub-role”。这是产品机制,而非部署/运行偏好。\n如需切换当前由谁执行/扮演,用 CLI/TUI 的 `-m/--member <id>` 显式选择。\n`members.<id>.gofor` 用于写该长期 agent 的“职责速记卡/工作边界/交付物摘要”(建议 5 行内):用于快速路由与提醒;更完整的规范请写入 `.minds/team/<id>/*` 或 `.minds/team/domains/*.md` 等 Markdown 资产。\n示例(gofor):\n```yaml\nmembers:\n qa_guard:\n name: QA Guard\n gofor:\n - Own release regression checklist and pass/fail gate\n - Maintain script-style smoke tests and how to run them\n - Reject changes that break lint/types/tests (or request fixes)\n - Track high-risk areas and required manual verification\n```\n示例(gofor, object;按 YAML key 顺序渲染):\n```yaml\nmembers:\n qa_guard:\n name: QA Guard\n gofor:\n Scope: release regression gate\n Deliverables: checklist + runnable scripts\n Non-goals: feature dev\n Interfaces: coordinates with server/webui owners\n```',
|
|
2719
|
-
'`members.<id>.gofor` 推荐用 YAML list(3–6 条)而不是长字符串;string 仅适合单句。建议用下面 5 行模板维度(每条尽量短):\n```yaml\ngofor:\n - Scope: ...\n - Interfaces: ...\n - Deliverables: ...\n - Non-goals: ...\n - Regression: ...\n```',
|
|
2720
|
-
'如何为不同角色指定默认模型:用 `member_defaults.provider/model` 设全局默认;对特定成员在 `members.<id>.provider/model` 里覆盖即可。例如:默认用 `gpt-5.2`,代码编写域成员用 `gpt-5.2-codex`。',
|
|
2721
|
-
'模型参数(例如 `reasoning_effort` / `verbosity` / `temperature`)应写在 `member_defaults.model_params.codex.*` 或 `members.<id>.model_params.codex.*` 下(对内置 `codex` provider)。不要把这些参数直接写在 `member_defaults`/`members.<id>` 根上。',
|
|
2722
|
-
'重要:Codex provider(`apiType=codex`)仅支持流式输出。若成员解析后的 provider 是 Codex,则 `members.<id>.streaming: false` 属于配置错误,会在校验/运行时作为严重问题上报并中止请求。',
|
|
2723
|
-
'`shell_specialists`:可选,列出允许拥有 shell 工具的成员 id(string|string[]|null)。如某成员获得了 shell 工具(例如 toolset `os` 或 tools 里的 `shell_exec` 等),则该成员必须出现在 `shell_specialists`;否则会在 Problems 面板提示(运行期 fail-open,但你仍应修复)。',
|
|
2724
|
-
'风格提醒:保持 `team.yaml` 的可读性。推荐用空行分隔段落/成员块,避免连续多行空行;每次修改后运行 `team_mgmt_validate_team_cfg({})` 以便在 Problems 面板看到错误与风格提醒。',
|
|
2725
|
-
'默认策略(可被用户覆盖):\n' +
|
|
2726
|
-
'1) 新增成员时,`diligence-push-max` 默认设为 `3`(除非用户明确要求其他值)。\n' +
|
|
2727
|
-
'2) 切换成员的 LLM `provider/model` 时,默认保留 `ws_read` / `ws_mod` 作为基线;当目标是 `provider: codex` 时,在基线上追加 `codex_style_tools`(而不是替代),除非用户明确要求其他组合。',
|
|
2728
|
-
'成员配置通过 prototype 继承 `member_defaults`(省略字段会继承默认值)。',
|
|
2729
|
-
'修改 provider/model 前请务必确认该 provider 可用(至少 env var 已配置)。可用 `team_mgmt_check_provider({ provider_key: \"<providerKey>\", model: \"\", all_models: false, live: false, max_models: 0 })` 做检查,避免把系统刷成板砖。',
|
|
2730
|
-
'想快速查看有哪些 provider / models / model_param_options:用 `team_mgmt_list_providers({})` 和 `team_mgmt_list_models({ provider_pattern: \"*\", model_pattern: \"*\" })`。',
|
|
2731
|
-
'不要把内置成员(例如 `fuxi` / `pangu`)的定义写入 `.minds/team.yaml`(这里只定义 rtws(运行时工作区)自己的成员):内置成员通常带有特殊权限/目录访问边界;重复定义可能引入冲突、权限误配或行为不一致。',
|
|
2732
|
-
'`hidden: true` 表示影子/隐藏成员:不会出现在系统提示的团队目录里,但仍然可以通过 tellask-special 函数诉请。',
|
|
2733
|
-
'修改文件推荐流程:先 `team_mgmt_read_file({ path: \"team.yaml\", range: \"<start~end>\", max_lines: 0, show_linenos: true })` 定位行号;小改动用 `team_mgmt_prepare_file_range_edit({ path: \"team.yaml\", range: \"<line~range>\", existing_hunk_id: \"\", content: \"<new content>\" })` 生成 diff(工具会返回 hunk_id),再用 `team_mgmt_apply_file_modification({ hunk_id: \"<hunk_id>\" })` 显式确认写入;如需修订同一个预览,可再次调用 `team_mgmt_prepare_file_range_edit({ path: \"team.yaml\", range: \"<line~range>\", existing_hunk_id: \"<hunk_id>\", content: \"<new content>\" })` 覆写;如确实需要整文件覆盖:先 `team_mgmt_read_file({ path: \"team.yaml\", range: \"\", max_lines: 0, show_linenos: true })` 从 YAML header 获取 total_lines/size_bytes,再用 `team_mgmt_overwrite_entire_file({ path: \"team.yaml\", known_old_total_lines: <n>, known_old_total_bytes: <n>, content_format: \"\", content: \"...\" })`。',
|
|
2734
|
-
'部署/组织建议(可选):如果你不希望出现显在“团队管理者”,可由一个影子/隐藏成员持有 `team-mgmt` 负责维护 `.minds/**`(尤其 `team.yaml`),由人类在需要时触发其执行(例如初始化/调整权限/更新模型)。Dominds 不强制这种组织方式;你也可以让显在成员拥有 `team-mgmt` 或由人类直接维护文件。',
|
|
2735
|
-
]) +
|
|
2736
|
-
fmtSubHeader('Schema Snapshot(自动生成,来自当前解析器白名单)') +
|
|
2737
|
-
fmtList([
|
|
2738
|
-
`顶层字段(root):${fmtKeyList(team_1.Team.TEAM_YAML_ROOT_KEYS)}`,
|
|
2739
|
-
`成员字段(members.<id>):${fmtKeyList(team_1.Team.TEAM_YAML_MEMBER_KEYS)}`,
|
|
2740
|
-
`model_params 顶层字段:${fmtKeyList(team_1.Team.TEAM_YAML_MODEL_PARAMS_ROOT_KEYS)}`,
|
|
2741
|
-
`model_params.codex 字段:${fmtKeyList(team_1.Team.TEAM_YAML_MODEL_PARAMS_CODEX_KEYS)}`,
|
|
2742
|
-
`model_params.openai 字段:${fmtKeyList(team_1.Team.TEAM_YAML_MODEL_PARAMS_OPENAI_KEYS)}`,
|
|
2743
|
-
`model_params.anthropic 字段:${fmtKeyList(team_1.Team.TEAM_YAML_MODEL_PARAMS_ANTHROPIC_KEYS)}`,
|
|
2744
|
-
]) +
|
|
2745
|
-
'\n' +
|
|
2746
|
-
'最小模板:\n' +
|
|
2747
|
-
'```yaml\n' +
|
|
2748
|
-
'# 这里只放 rtws(运行时工作区)自己的成员;不要把内置成员(例如 fuxi/pangu)写进来。\n' +
|
|
2749
|
-
'member_defaults:\n' +
|
|
2750
|
-
' provider: codex\n' +
|
|
2751
|
-
' model: gpt-5.2\n' +
|
|
2752
|
-
'\n' +
|
|
2753
|
-
'default_responder: primary\n' +
|
|
2754
|
-
'\n' +
|
|
2755
|
-
'members:\n' +
|
|
2756
|
-
' team_manager:\n' +
|
|
2757
|
-
' hidden: true\n' +
|
|
2758
|
-
" toolsets: ['team-mgmt']\n" +
|
|
2759
|
-
' primary:\n' +
|
|
2760
|
-
' hidden: true\n' +
|
|
2761
|
-
' toolsets:\n' +
|
|
2762
|
-
' - ws_read\n' +
|
|
2763
|
-
' - ws_mod\n' +
|
|
2764
|
-
' - codex_style_tools\n' +
|
|
2765
|
-
" no_read_dirs: ['.minds/**']\n" +
|
|
2766
|
-
" no_write_dirs: ['.minds/**']\n" +
|
|
2767
|
-
' qa_guard:\n' +
|
|
2768
|
-
' name: QA Guard\n' +
|
|
2769
|
-
' gofor:\n' +
|
|
2770
|
-
' - Own release regression checklist and pass/fail gate\n' +
|
|
2771
|
-
' - Maintain runnable smoke tests and docs\n' +
|
|
2772
|
-
' - Flag high-risk changes and required manual checks\n' +
|
|
2773
|
-
' coder:\n' +
|
|
2774
|
-
' name: Coder\n' +
|
|
2775
|
-
' provider: codex\n' +
|
|
2776
|
-
' model: gpt-5.2-codex\n' +
|
|
2777
|
-
'```\n');
|
|
2778
|
-
}
|
|
2779
|
-
return (fmtHeader('.minds/team.yaml') +
|
|
2780
|
-
fmtList(common.concat([
|
|
2781
|
-
'The team definition entrypoint is `.minds/team.yaml` (no `.minds/team.yml` alias today).',
|
|
2782
|
-
'Role responsibilities (Markdown) live under `.minds/team/<id>/{persona,knowledge,lessons}.*.md` and are linked by member id: the same `<id>` must exist in `members.<id>` in `team.yaml`.',
|
|
2783
|
-
'The team mechanism default is long-lived agents (long-lived teammates): `members` is a stable roster of callable teammates, not “on-demand sub-roles”. This is a product mechanism, not a deployment preference.\nTo pick who acts, use `-m/--member <id>` in CLI/TUI.\n`members.<id>.gofor` is a responsibility flashcard / scope / deliverables summary (≤ 5 lines). Use it for fast routing/reminders; put detailed specs in Markdown assets like `.minds/team/<id>/*` or `.minds/team/domains/*.md`.\nExample (`gofor`):\n```yaml\nmembers:\n qa_guard:\n name: QA Guard\n gofor:\n - Own release regression checklist and pass/fail gate\n - Maintain runnable smoke tests and docs\n - Flag high-risk changes and required manual checks\n```\nExample (`gofor`, object; rendered in YAML key order):\n```yaml\nmembers:\n qa_guard:\n name: QA Guard\n gofor:\n Scope: release regression gate\n Deliverables: checklist + runnable scripts\n Non-goals: feature dev\n Interfaces: coordinates with server/webui owners\n```',
|
|
2784
|
-
'Per-role default models: set global defaults via `member_defaults.provider/model`, then override `members.<id>.provider/model` per member (e.g. use `gpt-5.2` by default, and `gpt-5.2-codex` for code-writing members).',
|
|
2785
|
-
'Model params (e.g. `reasoning_effort` / `verbosity` / `temperature`) must be nested under `member_defaults.model_params.codex.*` or `members.<id>.model_params.codex.*` (for the built-in `codex` provider). Do not put them directly under `member_defaults`/`members.<id>` root.',
|
|
2786
|
-
'Style reminder: keep `team.yaml` readable. Prefer single blank lines between sections/member blocks; avoid long runs of blank lines. Run `team_mgmt_validate_team_cfg({})` after edits to surface errors and style warnings in the Problems panel.',
|
|
2787
|
-
'Default policy (override only when requested):\n1) When adding a member, set `diligence-push-max` to `3` unless the user explicitly asks otherwise.\n2) When switching a member’s LLM `provider/model`, keep `ws_read` / `ws_mod` as the baseline; when the target is `provider: codex`, add `codex_style_tools` on top (not as a replacement), unless the user explicitly asks for a different combination.',
|
|
2788
|
-
'Deployment/org suggestion (optional): if you do not want a visible team manager, keep `team-mgmt` only on a hidden/shadow member and have a human trigger it when needed; Dominds does not require this organizational setup.',
|
|
2789
|
-
'Recommended editing workflow: use `team_mgmt_read_file({ path: \"team.yaml\", range: \"<start~end>\", max_lines: 0, show_linenos: true })` to find line numbers; for small edits, run `team_mgmt_prepare_file_range_edit({ path: \"team.yaml\", range: \"<line~range>\", existing_hunk_id: \"\", content: \"<new content>\" })` to get a diff (the tool returns hunk_id), then confirm with `team_mgmt_apply_file_modification({ hunk_id: \"<hunk_id>\" })`; to revise the same prepared diff, call `team_mgmt_prepare_file_range_edit({ path: \"team.yaml\", range: \"<line~range>\", existing_hunk_id: \"<hunk_id>\", content: \"<new content>\" })` again; if you truly need a full overwrite: first `team_mgmt_read_file({ path: \"team.yaml\", range: \"\", max_lines: 0, show_linenos: true })` and read total_lines/size_bytes from the YAML header, then use `team_mgmt_overwrite_entire_file({ path: \"team.yaml\", known_old_total_lines: <n>, known_old_total_bytes: <n>, content_format: \"\", content: \"...\" })`.',
|
|
2790
|
-
])) +
|
|
2791
|
-
fmtSubHeader('Schema Snapshot (generated from parser allow-list)') +
|
|
2792
|
-
fmtList([
|
|
2793
|
-
`Root keys: ${fmtKeyList(team_1.Team.TEAM_YAML_ROOT_KEYS)}`,
|
|
2794
|
-
`members.<id> keys: ${fmtKeyList(team_1.Team.TEAM_YAML_MEMBER_KEYS)}`,
|
|
2795
|
-
`model_params keys: ${fmtKeyList(team_1.Team.TEAM_YAML_MODEL_PARAMS_ROOT_KEYS)}`,
|
|
2796
|
-
`model_params.codex keys: ${fmtKeyList(team_1.Team.TEAM_YAML_MODEL_PARAMS_CODEX_KEYS)}`,
|
|
2797
|
-
`model_params.openai keys: ${fmtKeyList(team_1.Team.TEAM_YAML_MODEL_PARAMS_OPENAI_KEYS)}`,
|
|
2798
|
-
`model_params.anthropic keys: ${fmtKeyList(team_1.Team.TEAM_YAML_MODEL_PARAMS_ANTHROPIC_KEYS)}`,
|
|
2799
|
-
]) +
|
|
2800
|
-
'\n' +
|
|
2801
|
-
'Minimal template:\n' +
|
|
2802
|
-
'```yaml\n' +
|
|
2803
|
-
'# Define only rtws members here (do not copy built-in members like fuxi/pangu).\n' +
|
|
2804
|
-
'member_defaults:\n' +
|
|
2805
|
-
' provider: codex\n' +
|
|
2806
|
-
' model: gpt-5.2\n' +
|
|
2807
|
-
'\n' +
|
|
2808
|
-
'default_responder: primary\n' +
|
|
2809
|
-
'\n' +
|
|
2810
|
-
'members:\n' +
|
|
2811
|
-
' team_manager:\n' +
|
|
2812
|
-
' hidden: true\n' +
|
|
2813
|
-
" toolsets: ['team-mgmt']\n" +
|
|
2814
|
-
' primary:\n' +
|
|
2815
|
-
' hidden: true\n' +
|
|
2816
|
-
' toolsets:\n' +
|
|
2817
|
-
' - ws_read\n' +
|
|
2818
|
-
' - ws_mod\n' +
|
|
2819
|
-
' - codex_style_tools\n' +
|
|
2820
|
-
" no_read_dirs: ['.minds/**']\n" +
|
|
2821
|
-
" no_write_dirs: ['.minds/**']\n" +
|
|
2822
|
-
'```\n');
|
|
2823
|
-
}
|
|
2824
|
-
function renderMcpManual(language) {
|
|
2825
|
-
if (language === 'zh') {
|
|
2826
|
-
return (fmtHeader('.minds/mcp.yaml') +
|
|
2827
|
-
fmtList([
|
|
2828
|
-
'每个 MCP `serverId` 注册一个 toolset,toolset 名称 = `serverId`(不加 `mcp_` 前缀)。成员通过 `members.<id>.toolsets` 选择能用哪些 MCP toolset。',
|
|
2829
|
-
'支持热重载:编辑 `.minds/mcp.yaml` 后通常无需重启 Dominds;必要时用 `mcp_restart`。',
|
|
2830
|
-
'默认按“每个对话租用一个 MCP client”运行(更安全):首次使用该 toolset 会产生 sticky reminder,完成后用 `mcp_release` 释放;如确实是无状态服务器,可配置 `truely-stateless: true` 允许跨对话共享。',
|
|
2831
|
-
'用 `tools.whitelist/blacklist` 控制暴露的工具,用 `transform` 做命名变换。',
|
|
2832
|
-
'常见坑:stdio transport 需要可执行命令路径/工作目录正确,且受成员目录权限(`read_dirs/write_dirs/no_*`)约束;HTTP transport 需要服务可达(url/端口/网络)。',
|
|
2833
|
-
'高频坑(stdio 路径):相对路径会受 `cwd` 影响而失败;推荐用绝对路径,或显式设置 `cwd` 来固定相对路径的解析。',
|
|
2834
|
-
'最小诊断流程(建议顺序):1) 先用 `team_mgmt_check_provider({ provider_key: \"<providerKey>\", model: \"\", all_models: false, live: false, max_models: 0 })` 确认 LLM provider 可用;2) 再检查该成员的目录权限(`team_mgmt_manual({ topics: [\"permissions\"] })`);3) 最后检查 MCP 侧报错(Problems 面板/相关日志提示),必要时 `mcp_restart`,用完记得 `mcp_release`。',
|
|
2835
|
-
]) +
|
|
2836
|
-
fmtCodeBlock('yaml', [
|
|
2837
|
-
'# 最小模板(stdio)',
|
|
2838
|
-
'version: 1',
|
|
2839
|
-
'servers:',
|
|
2840
|
-
' sdk_stdio:',
|
|
2841
|
-
' truely-stateless: false',
|
|
2842
|
-
' transport: stdio',
|
|
2843
|
-
' command:',
|
|
2844
|
-
' - node',
|
|
2845
|
-
' - ./path/to/mcp-server.js',
|
|
2846
|
-
' cwd: "./"',
|
|
2847
|
-
' env: {}',
|
|
2848
|
-
' tools: { whitelist: [], blacklist: [] }',
|
|
2849
|
-
' transform: []',
|
|
2850
|
-
]) +
|
|
2851
|
-
fmtCodeBlock('yaml', [
|
|
2852
|
-
'# stdio 路径示例(最小)',
|
|
2853
|
-
'# 相对路径:cwd 变化会失败',
|
|
2854
|
-
'command:',
|
|
2855
|
-
' - node',
|
|
2856
|
-
' - ./mcp/server.js',
|
|
2857
|
-
'cwd: "/absolute/path/to/project"',
|
|
2858
|
-
'',
|
|
2859
|
-
'# 绝对路径:不依赖 cwd',
|
|
2860
|
-
'command:',
|
|
2861
|
-
' - node',
|
|
2862
|
-
' - /absolute/path/to/mcp/server.js',
|
|
2863
|
-
]) +
|
|
2864
|
-
fmtCodeBlock('yaml', [
|
|
2865
|
-
'# 最小模板(HTTP)',
|
|
2866
|
-
'version: 1',
|
|
2867
|
-
'servers:',
|
|
2868
|
-
' sdk_http:',
|
|
2869
|
-
' truely-stateless: false',
|
|
2870
|
-
' transport: streamable_http',
|
|
2871
|
-
' url: http://127.0.0.1:3000/mcp',
|
|
2872
|
-
' tools: { whitelist: [], blacklist: [] }',
|
|
2873
|
-
' transform: []',
|
|
2874
|
-
]));
|
|
2875
|
-
}
|
|
2876
|
-
return (fmtHeader('.minds/mcp.yaml') +
|
|
2877
|
-
fmtList([
|
|
2878
|
-
'Each MCP `serverId` registers one toolset, and the toolset name is exactly `serverId` (no `mcp_` prefix). Members choose MCP access via `members.<id>.toolsets`.',
|
|
2879
|
-
'Hot reload: edits usually apply without restarting Dominds; use `mcp_restart` when needed.',
|
|
2880
|
-
"Default is per-dialog MCP client leasing (safer): first use adds a sticky reminder; call `mcp_release` when you're sure you won't need the toolset soon. If the server is truly stateless, set `truely-stateless: true` to allow cross-dialog sharing.",
|
|
2881
|
-
'Use `tools.whitelist/blacklist` for exposure control and `transform` for naming transforms.',
|
|
2882
|
-
'Common pitfalls: stdio transport needs a correct executable/command path and working directory, and is subject to member directory permissions (`read_dirs/write_dirs/no_*`); HTTP transport requires the server URL to be reachable.',
|
|
2883
|
-
'High-frequency pitfall (stdio paths): relative paths depend on `cwd` and can break; prefer absolute paths, or set `cwd` explicitly to make relative paths stable.',
|
|
2884
|
-
'Minimal diagnostic flow: 1) run `team_mgmt_check_provider({ provider_key: \"<providerKey>\", model: \"\", all_models: false, live: false, max_models: 0 })` to confirm the LLM provider works; 2) review member directory permissions (`team_mgmt_manual({ topics: [\"permissions\"] })`); 3) check MCP-side errors (Problems panel / logs), use `mcp_restart` if needed, and `mcp_release` when done.',
|
|
2885
|
-
]) +
|
|
2886
|
-
fmtCodeBlock('yaml', [
|
|
2887
|
-
'# Minimal template (stdio)',
|
|
2888
|
-
'version: 1',
|
|
2889
|
-
'servers:',
|
|
2890
|
-
' sdk_stdio:',
|
|
2891
|
-
' truely-stateless: false',
|
|
2892
|
-
' transport: stdio',
|
|
2893
|
-
' command:',
|
|
2894
|
-
' - node',
|
|
2895
|
-
' - ./path/to/mcp-server.js',
|
|
2896
|
-
' cwd: "./"',
|
|
2897
|
-
' env: {}',
|
|
2898
|
-
' tools: { whitelist: [], blacklist: [] }',
|
|
2899
|
-
' transform: []',
|
|
2900
|
-
]) +
|
|
2901
|
-
fmtCodeBlock('yaml', [
|
|
2902
|
-
'# stdio path example (minimal)',
|
|
2903
|
-
'# Relative path: depends on cwd',
|
|
2904
|
-
'command:',
|
|
2905
|
-
' - node',
|
|
2906
|
-
' - ./mcp/server.js',
|
|
2907
|
-
'cwd: "/absolute/path/to/project"',
|
|
2908
|
-
'',
|
|
2909
|
-
'# Absolute path: independent of cwd',
|
|
2910
|
-
'command:',
|
|
2911
|
-
' - node',
|
|
2912
|
-
' - /absolute/path/to/mcp/server.js',
|
|
2913
|
-
]) +
|
|
2914
|
-
fmtCodeBlock('yaml', [
|
|
2915
|
-
'# Minimal template (HTTP)',
|
|
2916
|
-
'version: 1',
|
|
2917
|
-
'servers:',
|
|
2918
|
-
' sdk_http:',
|
|
2919
|
-
' truely-stateless: false',
|
|
2920
|
-
' transport: streamable_http',
|
|
2921
|
-
' url: http://127.0.0.1:3000/mcp',
|
|
2922
|
-
' tools: { whitelist: [], blacklist: [] }',
|
|
2923
|
-
' transform: []',
|
|
2924
|
-
]));
|
|
2925
|
-
}
|
|
2926
|
-
function renderPermissionsManual(language) {
|
|
2927
|
-
if (language === 'zh') {
|
|
2928
|
-
return (fmtHeader('目录权限(read_dirs / write_dirs)') +
|
|
2929
|
-
fmtList([
|
|
2930
|
-
'权限字段:`read_dirs` / `write_dirs` / `no_read_dirs` / `no_write_dirs`。',
|
|
2931
|
-
'deny-list(no_*)优先于 allow-list(*_dirs)。',
|
|
2932
|
-
'若未配置 allow-list,则默认允许(在 deny-list 不命中的前提下)。这很方便,但也更容易“权限过大”;如需最小权限,建议显式收敛 allow-list 并对敏感目录加 deny-list。',
|
|
2933
|
-
'`read_dirs` 与 `write_dirs` 是独立控制:不要默认 write implies read(以当前实现的权限检查为准)。',
|
|
2934
|
-
'模式支持 `*` 和 `**`,按“目录范围”语义匹配(按目录/路径前缀范围来理解)。',
|
|
2935
|
-
'示例:`dominds/**` 会匹配 `dominds/README.md`、`dominds/main/server.ts`、`dominds/webapp/src/...` 等路径。',
|
|
2936
|
-
'示例:`.minds/**` 会匹配 `.minds/team.yaml`、`.minds/team/<id>/persona.zh.md` 等;常用于限制普通成员访问 minds 资产。',
|
|
2937
|
-
'`*.tsk/` 是封装差遣牒:只能用函数工具 `change_mind` 维护。任何通用文件工具都无法访问该目录树(硬编码无条件拒绝)。',
|
|
2938
|
-
'`.minds/**` 是 rtws(运行时工作区)的“团队配置/记忆/资产”目录:任何通用文件工具都无法访问(硬编码无条件拒绝)。只有专用的 `.minds/` 工具集(例如 `team-mgmt`)可访问它。',
|
|
2939
|
-
'说明:如果你在 `team.yaml` 的 allow-list(`read_dirs`/`write_dirs`)里写了 `.minds/**` 或 `*.tsk/**` 试图绕过限制,运行时会忽略并上报 err 级别问题。',
|
|
2940
|
-
]) +
|
|
2941
|
-
fmtCodeBlock('yaml', [
|
|
2942
|
-
'# 最小权限写法示例(仅示意)',
|
|
2943
|
-
'members:',
|
|
2944
|
-
' coder:',
|
|
2945
|
-
' read_dirs: ["dominds/**"]',
|
|
2946
|
-
' write_dirs: ["dominds/**"]',
|
|
2947
|
-
' no_read_dirs: [".minds/**"]',
|
|
2948
|
-
' no_write_dirs: [".minds/**"]',
|
|
2949
|
-
]));
|
|
2950
|
-
}
|
|
2951
|
-
return (fmtHeader('Directory Permissions (read_dirs / write_dirs)') +
|
|
2952
|
-
fmtList([
|
|
2953
|
-
'Fields: `read_dirs` / `write_dirs` / `no_read_dirs` / `no_write_dirs`.',
|
|
2954
|
-
'Deny-lists (no_*) override allow-lists (*_dirs).',
|
|
2955
|
-
'If no allow-list is configured, access defaults to allow (after deny-list check). This is convenient but can be overly permissive; for least privilege, explicitly narrow allow-lists and deny sensitive directories.',
|
|
2956
|
-
'`read_dirs` and `write_dirs` are controlled independently (do not assume write implies read; follow current implementation).',
|
|
2957
|
-
'Patterns support `*` and `**` with directory-scope semantics (think directory/path-range matching).',
|
|
2958
|
-
'Example: `dominds/**` matches `dominds/README.md`, `dominds/main/server.ts`, `dominds/webapp/src/...`, etc.',
|
|
2959
|
-
'Example: `.minds/**` matches `.minds/team.yaml` and `.minds/team/<id>/persona.*.md`; commonly used to restrict normal members from minds assets.',
|
|
2960
|
-
'`*.tsk/` is an encapsulated Taskdoc: it must be maintained via the function tool `change_mind` only. It is hard-denied for all general file tools.',
|
|
2961
|
-
'`.minds/**` stores rtws (runtime workspace) team config/memory/assets: it is hard-denied for all general file tools. Only dedicated `.minds/`-scoped toolsets (e.g. `team-mgmt`) may access it.',
|
|
2962
|
-
'Note: If you try to whitelist `.minds/**` or `*.tsk/**` via `read_dirs`/`write_dirs`, the runtime ignores it and reports an error-level Problem.',
|
|
2963
|
-
]) +
|
|
2964
|
-
fmtCodeBlock('yaml', [
|
|
2965
|
-
'# Least-privilege example (illustrative)',
|
|
2966
|
-
'members:',
|
|
2967
|
-
' coder:',
|
|
2968
|
-
' read_dirs: ["dominds/**"]',
|
|
2969
|
-
' write_dirs: ["dominds/**"]',
|
|
2970
|
-
' no_read_dirs: [".minds/**"]',
|
|
2971
|
-
' no_write_dirs: [".minds/**"]',
|
|
2972
|
-
]));
|
|
2973
|
-
}
|
|
2974
|
-
function renderMindsManual(language) {
|
|
2975
|
-
if (language === 'zh') {
|
|
2976
|
-
return (fmtHeader('.minds/team/<id>/*') +
|
|
2977
|
-
fmtList([
|
|
2978
|
-
'最小要求:每个 `members.<id>` 建议至少提供 `persona.*.md`(否则该成员将缺少可发现的角色设定;具体忽略/回退/报错行为以当前实现为准)。',
|
|
2979
|
-
'persona.*.md:角色设定(稳定的工作方式与职责)。',
|
|
2980
|
-
'knowledge.*.md:领域知识(可维护)。',
|
|
2981
|
-
'lessons.*.md:经验教训(可维护)。',
|
|
2982
|
-
'语言文件命名:优先按工作语言提供 `persona.zh.md` / `persona.en.md` 等;是否回退到 `persona.md`/其他语言版本,以当前实现为准。',
|
|
2983
|
-
]) +
|
|
2984
|
-
fmtCodeBlock('text', [
|
|
2985
|
-
'.minds/',
|
|
2986
|
-
' team/',
|
|
2987
|
-
' qa_guard/',
|
|
2988
|
-
' persona.zh.md',
|
|
2989
|
-
' knowledge.zh.md',
|
|
2990
|
-
' lessons.zh.md',
|
|
2991
|
-
]));
|
|
2992
|
-
}
|
|
2993
|
-
return (fmtHeader('.minds/team/<id>/*') +
|
|
2994
|
-
fmtList([
|
|
2995
|
-
'Minimum: for each `members.<id>`, provide at least `persona.*.md` (otherwise the member may lack a discoverable persona; ignore/fallback/error behavior follows current implementation).',
|
|
2996
|
-
'persona.*.md: persona and operating style.',
|
|
2997
|
-
'knowledge.*.md: domain knowledge (maintainable).',
|
|
2998
|
-
'lessons.*.md: lessons learned (maintainable).',
|
|
2999
|
-
'Language variants: prefer working-language files like `persona.en.md` / `persona.zh.md`; whether it falls back to `persona.md` or other languages follows current implementation.',
|
|
3000
|
-
]) +
|
|
3001
|
-
fmtCodeBlock('text', [
|
|
3002
|
-
'.minds/',
|
|
3003
|
-
' team/',
|
|
3004
|
-
' qa_guard/',
|
|
3005
|
-
' persona.en.md',
|
|
3006
|
-
' knowledge.en.md',
|
|
3007
|
-
' lessons.en.md',
|
|
3008
|
-
]));
|
|
3009
|
-
}
|
|
3010
|
-
function renderEnvManual(language) {
|
|
3011
|
-
if (language === 'zh') {
|
|
3012
|
-
return (fmtHeader('.minds/env.*.md(运行环境提示)') +
|
|
3013
|
-
fmtList([
|
|
3014
|
-
'用途:为“当前 rtws 的运行环境”提供一段稳定的介绍文案。Dominds 会将其注入到 agent 的 system prompt 中,注入位置在“团队目录(Team Directory)”之前。',
|
|
3015
|
-
'文件位置:写在当前 rtws 的 `.minds/` 下;切换 rtws(例如 `-C ux-rtws`)时,应在对应 rtws 的 `.minds/` 下分别维护。',
|
|
3016
|
-
'推荐文件名:`env.zh.md`(中文语义基准)与 `env.en.md`(英文对齐)。',
|
|
3017
|
-
'回退规则:优先按工作语言读取 `env.<lang>.md`;如不存在,可回退到 `env.md`(以当前实现为准)。空文件/仅空白会被当作“无提示”。',
|
|
3018
|
-
'i18n 约定:`zh` 为语义基准。不要把 `zh` 通过翻译 `en` 来更新;应让 `en` 追随 `zh` 的语义。',
|
|
3019
|
-
'管理者提醒:若发现缺失/质量不佳/与实际环境不符,应与人类用户讨论并确认措辞,然后再写入/更新对应的 `env.*.md`(避免“凭空编造”的环境描述)。',
|
|
3020
|
-
]) +
|
|
3021
|
-
fmtCodeBlock('text', [
|
|
3022
|
-
'# 示例(片段;请按你的 rtws 真实环境改写)',
|
|
3023
|
-
'## 本 rtws 的 Dominds 运行环境说明',
|
|
3024
|
-
'',
|
|
3025
|
-
'- 本 rtws 用于 Dominds 自我开发与联调。',
|
|
3026
|
-
'- Dominds 程序来源:本机全局 link 的 `dominds`,由 `./dominds/` 构建产物提供。',
|
|
3027
|
-
'- WebUI dev/UX:`./dev-server.sh` 使用 `ux-rtws/` 作为 rtws(避免污染根 rtws)。',
|
|
3028
|
-
]));
|
|
3029
|
-
}
|
|
3030
|
-
return (fmtHeader('.minds/env.*.md (runtime environment intro)') +
|
|
3031
|
-
fmtList([
|
|
3032
|
-
'Purpose: provide a stable intro note describing the “current rtws runtime environment”. Dominds injects it into the agent system prompt, positioned before “Team Directory”.',
|
|
3033
|
-
'Location: place it under the current rtws `.minds/`. If you switch rtws (e.g. `-C ux-rtws`), maintain a separate `env.*.md` under that rtws’s `.minds/`.',
|
|
3034
|
-
'Recommended filenames: `env.zh.md` (canonical semantics) and `env.en.md` (English aligned to zh).',
|
|
3035
|
-
'Fallback behavior: prefer `env.<lang>.md` by working language; if missing, it may fall back to `env.md` (per current implementation). Empty/whitespace-only content is treated as “no intro”.',
|
|
3036
|
-
'i18n rule: `zh` is canonical. Do not update `zh` by translating from `en`; update `en` to match `zh` semantics.',
|
|
3037
|
-
'Manager reminder: if the file is missing / inaccurate / low quality, discuss wording with the human user and then write/update `env.*.md` (avoid fabricating environment details).',
|
|
3038
|
-
]) +
|
|
3039
|
-
fmtCodeBlock('text', [
|
|
3040
|
-
'# Example (snippet; tailor to your real rtws)',
|
|
3041
|
-
'## Dominds runtime environment notes',
|
|
3042
|
-
'',
|
|
3043
|
-
'- This rtws is used for Dominds self-development and integration.',
|
|
3044
|
-
'- Program source: a globally linked `dominds` built from `./dominds/`.',
|
|
3045
|
-
'- WebUI dev/UX: `./dev-server.sh` uses `ux-rtws/` as rtws (keeps root rtws clean).',
|
|
3046
|
-
]));
|
|
3047
|
-
}
|
|
3048
|
-
function renderTroubleshooting(language) {
|
|
3049
|
-
if (language === 'zh') {
|
|
3050
|
-
return (fmtHeader('排障(症状 → 原因 → 解决步骤)') +
|
|
3051
|
-
fmtList([
|
|
3052
|
-
'改 provider/model 前总是先做:先用 `team_mgmt_list_providers({})` / `team_mgmt_list_models({ provider_pattern: \"*\", model_pattern: \"*\" })` 确认 key 是否存在,再运行 `team_mgmt_check_provider({ provider_key: \"<providerKey>\", model: \"\", all_models: false, live: true, max_models: 0 })` 做可用性检查(env + 可选 live)。',
|
|
3053
|
-
'症状:提示“缺少 provider/model” → 原因:`member_defaults` 或成员覆盖缺失 → 步骤:检查 `.minds/team.yaml` 的 `member_defaults.provider/model`(以及 `members.<id>.provider/model` 是否写错)。',
|
|
3054
|
-
'症状:提示“Provider not found” → 原因:provider key 未定义/拼写错误/未按预期合并 defaults → 步骤:检查 `.minds/llm.yaml` 的 provider keys,并确认 `.minds/team.yaml` 引用的 key 存在。',
|
|
3055
|
-
'症状:提示“Model not found” → 原因:model key 未定义/拼写错误/不在该 provider 下 → 步骤:用 `team_mgmt_list_models({ provider_pattern: \"<providerKey>\", model_pattern: \"*\" })` 查已有模型 key,再修正 `.minds/team.yaml` 引用或补全 `.minds/llm.yaml`。',
|
|
3056
|
-
'症状:提示“permission denied / forbidden / not allowed” → 原因:目录权限(read/write/no_*)命中 deny-list 或未被 allow-list 覆盖 → 步骤:用 `team_mgmt_manual({ topics: [\"permissions\"] })` 复核规则,并检查该成员的 `read_dirs/write_dirs/no_*` 配置。',
|
|
3057
|
-
'症状:MCP 不生效 → 原因:mcp 配置错误/服务不可用/租用未释放 → 步骤:打开 Problems 面板查看错误;必要时用 `mcp_restart`;完成后用 `mcp_release` 释放租用。',
|
|
3058
|
-
]));
|
|
3059
|
-
}
|
|
3060
|
-
return (fmtHeader('Troubleshooting (symptom → cause → steps)') +
|
|
3061
|
-
fmtList([
|
|
3062
|
-
'Before changing provider/model: use `team_mgmt_list_providers({})` / `team_mgmt_list_models({ provider_pattern: \"*\", model_pattern: \"*\" })` to confirm keys exist, then run `team_mgmt_check_provider({ provider_key: \"<providerKey>\", model: \"\", all_models: false, live: true, max_models: 0 })` for a readiness check (env + optional live).',
|
|
3063
|
-
'Symptom: "Missing provider/model" → Cause: missing `member_defaults` or member overrides → Steps: check `.minds/team.yaml` `member_defaults.provider/model` (and `members.<id>.provider/model`).',
|
|
3064
|
-
'Symptom: "Provider not found" → Cause: provider key not defined / typo / unexpected merge with defaults → Steps: check `.minds/llm.yaml` provider keys and ensure `.minds/team.yaml` references an existing key.',
|
|
3065
|
-
'Symptom: "Model not found" → Cause: model key not defined / typo / not under that provider → Steps: run `team_mgmt_list_models({ provider_pattern: \"<providerKey>\", model_pattern: \"*\" })` and fix `.minds/team.yaml` references or update `.minds/llm.yaml`.',
|
|
3066
|
-
'Symptom: "permission denied / forbidden / not allowed" → Cause: directory permissions (read/write/no_*) hit deny-list or not covered by allow-list → Steps: review `team_mgmt_manual({ topics: [\"permissions\"] })` and the member `read_dirs/write_dirs/no_*` config.',
|
|
3067
|
-
'Symptom: MCP not working → Cause: bad config / server down / leasing issues → Steps: check Problems panel; use `mcp_restart`; call `mcp_release` when done.',
|
|
3068
|
-
]));
|
|
3069
|
-
}
|
|
3070
|
-
async function renderModelParamsManual(language) {
|
|
3071
|
-
const header = language === 'zh'
|
|
3072
|
-
? fmtHeader('model_params(成员模型参数)')
|
|
3073
|
-
: fmtHeader('model_params (member model parameters)');
|
|
3074
|
-
const summary = await loadBuiltinLlmModelParamOptionsText();
|
|
3075
|
-
if (language === 'zh') {
|
|
3076
|
-
return (header +
|
|
3077
|
-
fmtList([
|
|
3078
|
-
'`model_params` 写在 `.minds/team.yaml` 的 `member_defaults` 或 `members.<id>` 下,用来控制采样/推理/输出风格。',
|
|
3079
|
-
'`model_params` 是运行时参数;`model_param_options`(在 `.minds/llm.yaml` 或内置 defaults 中)是文档/说明用途,用来描述可用参数范围(不保证强制校验)。',
|
|
3080
|
-
'想查看某个 provider 的“有效配置” `model_param_options`:优先用 `team_mgmt_list_models({ source: \"effective\", provider_pattern: \"<providerKey>\", model_pattern: \"*\", include_param_options: true })`(会把 general + provider 专有参数一起列出)。',
|
|
3081
|
-
'常见参数示例(不同 provider 支持不同):例如 `reasoning_effort`、`verbosity`、`temperature`、`max_tokens` 等。对内置 `codex` provider,这些参数应写在 `model_params.codex.*` 下。',
|
|
3082
|
-
'常见坑:不要把 `reasoning_effort` / `verbosity` 直接写在 `member_defaults` 或 `members.<id>` 根上(会被忽略,并会被 team.yaml 校验提示);应写在 `model_params.codex.*` 下。',
|
|
3083
|
-
'`model_param_options.<ns>.<param>.prominent: true`:表示“初始化/团队管理时应显式讨论并选定”的参数。不要依赖 provider/model 的隐含默认值。',
|
|
3084
|
-
'`model_param_options.<ns>.<param>.default`:为该参数提供建议默认值;`/setup` 会将其作为预选值展示(仍建议与用户确认是否需要调整)。',
|
|
3085
|
-
'最低要求:当 `member_defaults.provider` 选中某 provider 时,至少确保其 prominent=true 的参数在 `member_defaults.model_params.<ns>.*` 下都有明确取值;再进一步讨论是否需要对不同成员(`members.<id>.model_params...`)做差异化。',
|
|
3086
|
-
'风险提示:部分参数可能影响成本/延迟/输出稳定性(例如 temperature、max tokens 等)。参数是否透传/是否会被校验或裁剪,以当前实现为准。',
|
|
3087
|
-
]) +
|
|
3088
|
-
'\n' +
|
|
3089
|
-
'示例:\n' +
|
|
3090
|
-
'```yaml\n' +
|
|
3091
|
-
'member_defaults:\n' +
|
|
3092
|
-
' provider: codex\n' +
|
|
3093
|
-
' model: gpt-5.2\n' +
|
|
3094
|
-
' model_params:\n' +
|
|
3095
|
-
' codex:\n' +
|
|
3096
|
-
' reasoning_effort: high\n' +
|
|
3097
|
-
' verbosity: low\n' +
|
|
3098
|
-
' temperature: 0\n' +
|
|
3099
|
-
'```\n' +
|
|
3100
|
-
'\n' +
|
|
3101
|
-
'内置 provider 的 `model_param_options` 摘要(来自 `dominds/main/llm/defaults.yaml`):\n' +
|
|
3102
|
-
summary +
|
|
3103
|
-
'\n');
|
|
3104
|
-
}
|
|
3105
|
-
return (header +
|
|
3106
|
-
fmtList([
|
|
3107
|
-
'`model_params` lives in `.minds/team.yaml` under `member_defaults` or `members.<id>` to control sampling/reasoning/output style.',
|
|
3108
|
-
'`model_params` is runtime config; `model_param_options` (in `.minds/llm.yaml` or built-in defaults) is documentation-only to describe supported knobs (not guaranteed to be strictly validated).',
|
|
3109
|
-
'To inspect a provider’s effective `model_param_options`, prefer `team_mgmt_list_models({ source: \"effective\", provider_pattern: \"<providerKey>\", model_pattern: \"*\", include_param_options: true })` (lists both general and provider-specific options).',
|
|
3110
|
-
'Common examples (provider-dependent): e.g. `reasoning_effort`, `verbosity`, `temperature`, `max_tokens`, etc. For the built-in `codex` provider, these go under `model_params.codex.*`.',
|
|
3111
|
-
'Common pitfall: do not put `reasoning_effort` / `verbosity` directly under `member_defaults` or `members.<id>` (they are ignored and will be flagged by team.yaml validation); put them under `model_params.codex.*`.',
|
|
3112
|
-
'`model_param_options.<ns>.<param>.prominent: true` means “discuss and pick explicitly during bootstrap/team management”. Do not rely on implicit provider/model defaults.',
|
|
3113
|
-
'`model_param_options.<ns>.<param>.default` provides a recommended default; `/setup` may preselect it (still discuss with the user if it needs to change).',
|
|
3114
|
-
'Minimum: when `member_defaults.provider` selects a provider, ensure all `prominent: true` params are explicitly set under `member_defaults.model_params.<ns>.*`. Then decide if you need per-member overrides (`members.<id>.model_params...`).',
|
|
3115
|
-
'Risk note: some knobs may affect cost/latency/output stability (e.g. temperature, max tokens). Whether params are passed through / validated / clamped follows current implementation.',
|
|
3116
|
-
]) +
|
|
3117
|
-
'\n' +
|
|
3118
|
-
'Example:\n' +
|
|
3119
|
-
'```yaml\n' +
|
|
3120
|
-
'member_defaults:\n' +
|
|
3121
|
-
' provider: codex\n' +
|
|
3122
|
-
' model: gpt-5.2\n' +
|
|
3123
|
-
' model_params:\n' +
|
|
3124
|
-
' codex:\n' +
|
|
3125
|
-
' reasoning_effort: high\n' +
|
|
3126
|
-
' verbosity: low\n' +
|
|
3127
|
-
' temperature: 0\n' +
|
|
3128
|
-
'```\n' +
|
|
3129
|
-
'\n' +
|
|
3130
|
-
'Built-in provider `model_param_options` summary (from `dominds/main/llm/defaults.yaml`):\n' +
|
|
3131
|
-
summary +
|
|
3132
|
-
'\n');
|
|
3133
|
-
}
|
|
3134
|
-
async function renderToolsets(language) {
|
|
3135
|
-
const ids = Object.keys((0, registry_2.listToolsets)()).filter((id) => id !== 'control');
|
|
3136
|
-
const header = language === 'zh' ? fmtHeader('已注册 toolsets') : fmtHeader('Registered toolsets');
|
|
3137
|
-
const intro = language === 'zh'
|
|
3138
|
-
? fmtList([
|
|
3139
|
-
'`control`:对话控制类工具属于“内建必备能力”,运行时会自动包含给所有成员;因此不需要(也不建议)在 `members.<id>.toolsets` 里显式列出,本页也默认不展示它。',
|
|
3140
|
-
'`diag`:诊断类工具集不应默认授予任何成员;仅当用户明确要求“诊断/排查/验证解析/流式分段”等能力时才添加。',
|
|
3141
|
-
'多数情况下推荐用 `members.<id>.toolsets` 做粗粒度授权;`members.<id>.tools` 更适合做少量补充/收敛。',
|
|
3142
|
-
'按 provider 选择匹配的 toolsets:默认把 `ws_read` / `ws_mod` 作为通用基线;当 `provider: codex`(偏 Codex CLI 风格提示/工具名)时,在基线上追加 `codex_style_tools`(`apply_patch` 等),不是替换 `ws_read` / `ws_mod`。如果还需要“读/探测 rtws”,通常要再给 `os`(`shell_cmd`)并严格限制在少数专员成员手里。',
|
|
3143
|
-
'最佳实践:把 `os`(尤其 `shell_cmd`)只授予具备良好纪律/风控意识的人设成员(例如 “cmdr/ops”)。对不具备 shell 工具的成员,系统提示会明确要求其将 shell 执行委派给这类专员,并提供可审查的命令提案与理由。',
|
|
3144
|
-
'常见三种模式(示例写在 `.minds/team.yaml` 的 `members.<id>.toolsets` 下):',
|
|
3145
|
-
])
|
|
3146
|
-
: fmtList([
|
|
3147
|
-
'`control`: dialog-control tools are intrinsic and automatically included for all members at runtime; you do not need (and should not) list it under `members.<id>.toolsets`. It is omitted from the list below.',
|
|
3148
|
-
'`diag`: diagnostics tools should not be granted by default; only add it when the user explicitly asks for diagnostics/troubleshooting/streaming-parse verification.',
|
|
3149
|
-
'Typically use `members.<id>.toolsets` for coarse-grained access; use `members.<id>.tools` for a small number of additions/limits.',
|
|
3150
|
-
'Pick toolsets to match the provider: keep `ws_read` / `ws_mod` as the general baseline; for `provider: codex` (Codex CLI-style prompts/tool names), add `codex_style_tools` (`apply_patch`, etc.) on top rather than replacing `ws_read` / `ws_mod`. If you also need to read/probe the rtws, you typically must grant `os` (`shell_cmd`) and keep it restricted to a small number of specialist operators.',
|
|
3151
|
-
'Best practice: grant `os` (especially `shell_cmd`) only to a disciplined, risk-aware operator persona (e.g. “cmdr/ops”). For members without shell tools, the system prompt explicitly tells them to delegate shell execution to such a specialist, with a reviewable command proposal and justification.',
|
|
3152
|
-
'Three common patterns (in `.minds/team.yaml` under `members.<id>.toolsets`):',
|
|
3153
|
-
]);
|
|
3154
|
-
const patterns = fmtCodeBlock('yaml', [
|
|
3155
|
-
'# Recommended: explicit allow-list (most common)',
|
|
3156
|
-
'toolsets:',
|
|
3157
|
-
' - ws_read',
|
|
3158
|
-
' - ws_mod',
|
|
3159
|
-
' - codex_style_tools',
|
|
3160
|
-
'',
|
|
3161
|
-
'# Team manager (explicit, minimal)',
|
|
3162
|
-
'toolsets:',
|
|
3163
|
-
' - team-mgmt',
|
|
3164
|
-
'',
|
|
3165
|
-
'# Operator / DevOps (explicit; higher risk)',
|
|
3166
|
-
'toolsets:',
|
|
3167
|
-
' - ws_read',
|
|
3168
|
-
' - ws_mod',
|
|
3169
|
-
' - os',
|
|
3170
|
-
' - mcp_admin',
|
|
3171
|
-
]);
|
|
3172
|
-
const list = fmtList(ids.map((id) => `\`${id}\``));
|
|
3173
|
-
return header + intro + patterns + '\n' + list;
|
|
3174
|
-
}
|
|
3175
|
-
async function renderBuiltinDefaults(language) {
|
|
3176
|
-
const header = language === 'zh'
|
|
3177
|
-
? fmtHeader('内置 LLM Defaults(摘要)')
|
|
3178
|
-
: fmtHeader('Built-in LLM Defaults (summary)');
|
|
3179
|
-
const body = await loadBuiltinLlmDefaultsText();
|
|
3180
|
-
const explain = language === 'zh'
|
|
3181
|
-
? fmtList([
|
|
3182
|
-
'这份列表来自 Dominds 内置的 LLM defaults(实现内置)。当你没有在 `.minds/llm.yaml` 里显式覆盖某些 provider/model key 时,这些 defaults 可能会生效(以当前实现的合并规则为准)。',
|
|
3183
|
-
'在 `.minds/llm.yaml` 里新增/覆盖 provider key,通常只会影响同名 key 的解析,不表示“禁用其他内置 provider”。建议用 `team_mgmt_check_provider({ provider_key: \"<providerKey>\", model: \"\", all_models: false, live: true, max_models: 0 })` 验证配置。',
|
|
3184
|
-
])
|
|
3185
|
-
: fmtList([
|
|
3186
|
-
'This list comes from Dominds built-in LLM defaults (implementation-provided). If you do not explicitly override certain provider/model keys in `.minds/llm.yaml`, these defaults may be used (per current merge rules).',
|
|
3187
|
-
'Adding/overriding a provider key in `.minds/llm.yaml` typically affects that key only; it does not imply disabling other built-in providers. Use `team_mgmt_check_provider({ provider_key: \"<providerKey>\", model: \"\", all_models: false, live: true, max_models: 0 })` to verify.',
|
|
3188
|
-
]);
|
|
3189
|
-
return header + explain + '\n' + body + '\n';
|
|
3190
|
-
}
|
|
3191
|
-
exports.teamMgmtValidateTeamCfgTool = {
|
|
3192
|
-
type: 'func',
|
|
3193
|
-
name: 'team_mgmt_validate_team_cfg',
|
|
3194
|
-
description: `Validate ${TEAM_YAML_REL} and surface issues to the WebUI Problems panel.`,
|
|
3195
|
-
descriptionI18n: {
|
|
3196
|
-
en: `Validate ${TEAM_YAML_REL} and surface issues to the WebUI Problems panel.`,
|
|
3197
|
-
zh: `校验 ${TEAM_YAML_REL},并将问题上报到 WebUI 的 Problems 面板。`,
|
|
3198
|
-
},
|
|
3199
|
-
parameters: { type: 'object', additionalProperties: false, properties: {} },
|
|
3200
|
-
argsValidation: 'dominds',
|
|
3201
|
-
async call(dlg, _caller, _args) {
|
|
3202
|
-
const language = getUserLang(dlg);
|
|
3203
|
-
try {
|
|
3204
|
-
const minds = await getMindsDirState();
|
|
3205
|
-
if (minds.kind === 'missing') {
|
|
3206
|
-
const msg = formatMindsMissingNotice(language) +
|
|
3207
|
-
(language === 'zh'
|
|
3208
|
-
? `\n\n当前无法校验 \`${TEAM_YAML_REL}\`。`
|
|
3209
|
-
: `\n\nCannot validate \`${TEAM_YAML_REL}\` yet.`);
|
|
3210
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
3211
|
-
}
|
|
3212
|
-
if (minds.kind === 'not_directory') {
|
|
3213
|
-
const msg = language === 'zh'
|
|
3214
|
-
? `错误:\`${MINDS_DIR}/\` 存在但不是目录:\`${minds.abs}\``
|
|
3215
|
-
: `Error: \`${MINDS_DIR}/\` exists but is not a directory: \`${minds.abs}\``;
|
|
3216
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
3217
|
-
}
|
|
3218
|
-
const cwd = path_1.default.resolve(process.cwd());
|
|
3219
|
-
const teamYamlAbs = path_1.default.resolve(cwd, TEAM_YAML_REL);
|
|
3220
|
-
try {
|
|
3221
|
-
const st = await promises_1.default.stat(teamYamlAbs);
|
|
3222
|
-
if (!st.isFile()) {
|
|
3223
|
-
const msg = language === 'zh'
|
|
3224
|
-
? `错误:\`${TEAM_YAML_REL}\` 存在但不是文件。`
|
|
3225
|
-
: `Error: \`${TEAM_YAML_REL}\` exists but is not a file.`;
|
|
3226
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
3227
|
-
}
|
|
3228
|
-
}
|
|
3229
|
-
catch (err) {
|
|
3230
|
-
if (isFsErrWithCode(err) && err.code === 'ENOENT') {
|
|
3231
|
-
const msg = language === 'zh'
|
|
3232
|
-
? `未发现 \`${TEAM_YAML_REL}\`,无需校验。`
|
|
3233
|
-
: `\`${TEAM_YAML_REL}\` not found; nothing to validate.`;
|
|
3234
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
3235
|
-
}
|
|
3236
|
-
throw err;
|
|
3237
|
-
}
|
|
3238
|
-
// Team.load() is fail-open (always returns a usable team) and publishes any team.yaml issues
|
|
3239
|
-
// to the Problems panel.
|
|
3240
|
-
await team_1.Team.load();
|
|
3241
|
-
// Non-blocking style lint (keeps team.yaml readable).
|
|
3242
|
-
await lintTeamYamlStyleProblems();
|
|
3243
|
-
const snapshot = (0, problems_1.getProblemsSnapshot)();
|
|
3244
|
-
const teamProblems = listTeamYamlProblems(snapshot.problems);
|
|
3245
|
-
if (teamProblems.length === 0) {
|
|
3246
|
-
const msg = language === 'zh'
|
|
3247
|
-
? fmtHeader('team.yaml 校验通过') +
|
|
3248
|
-
fmtList([
|
|
3249
|
-
`\`${TEAM_YAML_REL}\`:✅ 未检测到问题`,
|
|
3250
|
-
'提示:每次修改 team.yaml 后都应运行本工具,避免“坏成员配置被静默跳过”。',
|
|
3251
|
-
])
|
|
3252
|
-
: fmtHeader('team.yaml Validation Passed') +
|
|
3253
|
-
fmtList([
|
|
3254
|
-
`\`${TEAM_YAML_REL}\`: ✅ no issues detected`,
|
|
3255
|
-
'Tip: run this after every team.yaml change to avoid silent omission of broken members.',
|
|
3256
|
-
]);
|
|
3257
|
-
return ok(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
3258
|
-
}
|
|
3259
|
-
const issueLines = [];
|
|
3260
|
-
for (const p of teamProblems) {
|
|
3261
|
-
issueLines.push(`- ${p.id}: ${p.message}`);
|
|
3262
|
-
issueLines.push(' ' + p.detail.errorText.split('\n').join('\n '));
|
|
3263
|
-
}
|
|
3264
|
-
const msg = language === 'zh'
|
|
3265
|
-
? fmtHeader('team.yaml 校验失败') +
|
|
3266
|
-
fmtList([
|
|
3267
|
-
`\`${TEAM_YAML_REL}\`:❌ 检测到 ${teamProblems.length} 个问题(详见 Problems 面板)`,
|
|
3268
|
-
'说明:坏的成员配置可能会在运行时被跳过或在使用时失败(为了保持 Team 可用),但你仍应立即修复以免行为偏离预期。',
|
|
3269
|
-
]) +
|
|
3270
|
-
'\n' +
|
|
3271
|
-
issueLines.join('\n')
|
|
3272
|
-
: fmtHeader('team.yaml Validation Failed') +
|
|
3273
|
-
fmtList([
|
|
3274
|
-
`\`${TEAM_YAML_REL}\`: ❌ ${teamProblems.length} issue(s) detected (see Problems panel)`,
|
|
3275
|
-
'Note: invalid member configs may be omitted at runtime or fail when used (to keep the Team usable), but you should fix them immediately.',
|
|
3276
|
-
]) +
|
|
3277
|
-
'\n' +
|
|
3278
|
-
issueLines.join('\n');
|
|
3279
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
3280
|
-
}
|
|
3281
|
-
catch (err) {
|
|
3282
|
-
const msg = language === 'zh'
|
|
3283
|
-
? `校验失败:${err instanceof Error ? err.message : String(err)}`
|
|
3284
|
-
: `Validation failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
3285
|
-
return fail(msg, [{ type: 'environment_msg', role: 'user', content: msg }]);
|
|
3286
|
-
}
|
|
3287
|
-
},
|
|
3288
|
-
};
|
|
3289
|
-
exports.teamMgmtManualTool = {
|
|
3290
|
-
type: 'func',
|
|
3291
|
-
name: 'team_mgmt_manual',
|
|
3292
|
-
description: `Team management manual for ${MINDS_DIR}/.`,
|
|
3293
|
-
descriptionI18n: {
|
|
3294
|
-
en: `Team management manual for ${MINDS_DIR}/.`,
|
|
3295
|
-
zh: `${MINDS_DIR}/ 的团队管理手册。`,
|
|
3296
|
-
},
|
|
3297
|
-
parameters: {
|
|
3298
|
-
type: 'object',
|
|
3299
|
-
additionalProperties: false,
|
|
3300
|
-
properties: {
|
|
3301
|
-
topics: {
|
|
3302
|
-
type: 'array',
|
|
3303
|
-
items: { type: 'string' },
|
|
3304
|
-
description: 'Manual topics to render. Empty/omitted renders the index. Examples: ["team"], ["team","member-properties"].',
|
|
3305
|
-
},
|
|
3306
|
-
},
|
|
3307
|
-
},
|
|
3308
|
-
argsValidation: 'dominds',
|
|
3309
|
-
async call(dlg, _caller, args) {
|
|
3310
|
-
const language = getUserLang(dlg);
|
|
3311
|
-
const topicsValue = args['topics'];
|
|
3312
|
-
const topicsRaw = topicsValue === undefined
|
|
3313
|
-
? []
|
|
3314
|
-
: Array.isArray(topicsValue) && topicsValue.every((v) => typeof v === 'string')
|
|
3315
|
-
? topicsValue
|
|
3316
|
-
: (() => {
|
|
3317
|
-
throw new Error('Invalid topics (expected string[])');
|
|
3318
|
-
})();
|
|
3319
|
-
const topics = [];
|
|
3320
|
-
for (const token0 of topicsRaw) {
|
|
3321
|
-
const token = token0.trim().startsWith('!') ? token0.trim().slice(1) : token0.trim();
|
|
3322
|
-
if (token === '')
|
|
3323
|
-
continue;
|
|
3324
|
-
if ((0, team_mgmt_manual_1.isTeamMgmtManualTopicKey)(token)) {
|
|
3325
|
-
topics.push(token);
|
|
3326
|
-
continue;
|
|
3327
|
-
}
|
|
3328
|
-
throw new Error(`Unknown topic: ${token0}`);
|
|
3329
|
-
}
|
|
3330
|
-
const msgPrefix = language === 'zh'
|
|
3331
|
-
? `(生成时间:${(0, time_1.formatUnifiedTimestamp)(new Date())})\n\n`
|
|
3332
|
-
: `(Generated: ${(0, time_1.formatUnifiedTimestamp)(new Date())})\n\n`;
|
|
3333
|
-
const renderIndex = () => {
|
|
3334
|
-
const topicTitle = (key) => (0, team_mgmt_manual_1.getTeamMgmtManualTopicTitle)(language, key);
|
|
3335
|
-
if (language === 'zh') {
|
|
3336
|
-
return (fmtHeader('Team Management Manual') +
|
|
3337
|
-
msgPrefix +
|
|
3338
|
-
fmtList([
|
|
3339
|
-
`\`team_mgmt_manual({ topics: ["topics"] })\`:${topicTitle('topics')}(你在这里)`,
|
|
3340
|
-
'新手最常见流程:先 `team_mgmt_list_providers({})` / `team_mgmt_list_models({ provider_pattern: "*", model_pattern: "*" })` 确认 provider/model keys → 再写 `.minds/team.yaml` → 再写 `.minds/team/<id>/persona.*.md` → 再跑 `team_mgmt_check_provider({ provider_key: "<providerKey>", model: "", all_models: false, live: false, max_models: 0 })`。',
|
|
3341
|
-
'',
|
|
3342
|
-
`\`team_mgmt_manual({ topics: ["team"] })\`:${topicTitle('team')} — .minds/team.yaml(团队花名册、工具集、目录权限入口)`,
|
|
3343
|
-
`\`team_mgmt_manual({ topics: ["minds"] })\`:${topicTitle('minds')} — .minds/team/<id>/*(persona/knowledge/lessons 资产怎么写)`,
|
|
3344
|
-
`\`team_mgmt_manual({ topics: ["env"] })\`:${topicTitle('env')} — .minds/env.*.md(运行环境提示:在团队介绍之前注入)`,
|
|
3345
|
-
`\`team_mgmt_manual({ topics: ["permissions"] })\`:${topicTitle('permissions')} — 目录权限(read_dirs/write_dirs/no_* 语义与冲突规则)`,
|
|
3346
|
-
`\`team_mgmt_manual({ topics: ["toolsets"] })\`:${topicTitle('toolsets')} — toolsets 列表(当前已注册 toolsets;常见三种授权模式)`,
|
|
3347
|
-
`\`team_mgmt_manual({ topics: ["llm"] })\`:${topicTitle('llm')} — .minds/llm.yaml(provider key 如何定义/引用;env var 安全边界)`,
|
|
3348
|
-
`\`team_mgmt_manual({ topics: ["mcp"] })\`:${topicTitle('mcp')} — .minds/mcp.yaml(MCP serverId→toolset;热重载与租用;可复制最小模板)`,
|
|
3349
|
-
`\`team_mgmt_manual({ topics: ["troubleshooting"] })\`:${topicTitle('troubleshooting')} — 按症状定位;优先 list_providers/list_models → check_provider`,
|
|
3350
|
-
'',
|
|
3351
|
-
`\`team_mgmt_manual({ topics: ["team","member-properties"] })\`:${topicTitle('team')} + ${topicTitle('member-properties')} — 成员字段表(members.<id> 字段参考)`,
|
|
3352
|
-
`\`team_mgmt_manual({ topics: ["llm","builtin-defaults"] })\`:${topicTitle('llm')} + ${topicTitle('builtin-defaults')} — 内置 defaults 摘要(内置 provider/model 概览与合并语义)`,
|
|
3353
|
-
`\`team_mgmt_manual({ topics: ["llm","model-params"] })\`:${topicTitle('llm')} + ${topicTitle('model-params')} — 模型参数参考(model_params / model_param_options)`,
|
|
3354
|
-
]));
|
|
3355
|
-
}
|
|
3356
|
-
return (fmtHeader('Team Management Manual') +
|
|
3357
|
-
msgPrefix +
|
|
3358
|
-
fmtList([
|
|
3359
|
-
`\`team_mgmt_manual({ topics: ["topics"] })\`: ${topicTitle('topics')} (you are here)`,
|
|
3360
|
-
'Common starter flow: run `team_mgmt_list_providers({})` / `team_mgmt_list_models({ provider_pattern: \"*\", model_pattern: \"*\" })` to confirm provider/model keys → write `.minds/team.yaml` → write `.minds/team/<id>/persona.*.md` → run `team_mgmt_check_provider({ provider_key: "<providerKey>", model: "", all_models: false, live: false, max_models: 0 })`. ',
|
|
3361
|
-
'',
|
|
3362
|
-
`\`team_mgmt_manual({ topics: ["team"] })\`: ${topicTitle('team')} — .minds/team.yaml (roster/toolsets/permissions entrypoint)`,
|
|
3363
|
-
`\`team_mgmt_manual({ topics: ["minds"] })\`: ${topicTitle('minds')} — .minds/team/<id>/* (persona/knowledge/lessons assets)`,
|
|
3364
|
-
`\`team_mgmt_manual({ topics: ["env"] })\`: ${topicTitle('env')} — .minds/env.*.md (runtime intro injected before Team Directory)`,
|
|
3365
|
-
`\`team_mgmt_manual({ topics: ["permissions"] })\`: ${topicTitle('permissions')} — directory permissions (semantics + conflict rules)`,
|
|
3366
|
-
`\`team_mgmt_manual({ topics: ["toolsets"] })\`: ${topicTitle('toolsets')} — toolsets list (registered toolsets + common patterns)`,
|
|
3367
|
-
`\`team_mgmt_manual({ topics: ["llm"] })\`: ${topicTitle('llm')} — .minds/llm.yaml (provider keys, env var boundaries)`,
|
|
3368
|
-
`\`team_mgmt_manual({ topics: ["mcp"] })\`: ${topicTitle('mcp')} — .minds/mcp.yaml (serverId→toolset, hot reload, leasing, minimal templates)`,
|
|
3369
|
-
`\`team_mgmt_manual({ topics: ["troubleshooting"] })\`: ${topicTitle('troubleshooting')} — symptom → steps; start with list_providers/list_models, then check_provider`,
|
|
3370
|
-
'',
|
|
3371
|
-
`\`team_mgmt_manual({ topics: ["team","member-properties"] })\`: ${topicTitle('team')} + ${topicTitle('member-properties')} — member field reference (members.<id>)`,
|
|
3372
|
-
`\`team_mgmt_manual({ topics: ["llm","builtin-defaults"] })\`: ${topicTitle('llm')} + ${topicTitle('builtin-defaults')} — built-in defaults summary (what/when/merge behavior)`,
|
|
3373
|
-
`\`team_mgmt_manual({ topics: ["llm","model-params"] })\`: ${topicTitle('llm')} + ${topicTitle('model-params')} — \`model_params\` and \`model_param_options\` reference`,
|
|
3374
|
-
]));
|
|
3375
|
-
};
|
|
3376
|
-
const want = (t) => topics.includes(t);
|
|
3377
|
-
try {
|
|
3378
|
-
if (topics.length === 0) {
|
|
3379
|
-
const content = renderIndex();
|
|
3380
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3381
|
-
}
|
|
3382
|
-
if (want('topics')) {
|
|
3383
|
-
const content = renderIndex();
|
|
3384
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3385
|
-
}
|
|
3386
|
-
if (want('team') && want('member-properties')) {
|
|
3387
|
-
const content = renderMemberProperties(language);
|
|
3388
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3389
|
-
}
|
|
3390
|
-
if (want('team')) {
|
|
3391
|
-
const content = renderTeamManual(language);
|
|
3392
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3393
|
-
}
|
|
3394
|
-
if (want('llm') && want('builtin-defaults')) {
|
|
3395
|
-
const content = await renderBuiltinDefaults(language);
|
|
3396
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3397
|
-
}
|
|
3398
|
-
if (want('llm') && want('model-params')) {
|
|
3399
|
-
const content = await renderModelParamsManual(language);
|
|
3400
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3401
|
-
}
|
|
3402
|
-
if (want('llm')) {
|
|
3403
|
-
const llmText = language === 'zh'
|
|
3404
|
-
? fmtHeader('.minds/llm.yaml') +
|
|
3405
|
-
fmtList([
|
|
3406
|
-
'定义 provider key → model 映射(用于 `.minds/team.yaml` 的 `member_defaults.provider` / `members.<id>.provider` 引用)。',
|
|
3407
|
-
'快速自检:用 `team_mgmt_list_providers({})` 列出内置/rtws provider keys、env var 是否配置;用 `team_mgmt_list_models({ source: \"effective\", provider_pattern: \"*\", model_pattern: \"*\" })` 列出“合并后”的模型与 `model_param_options`。',
|
|
3408
|
-
'最小示例:\n```yaml\nproviders:\n my_provider:\n apiKeyEnvVar: MY_PROVIDER_API_KEY\n models:\n my_model: { name: "my-model-id" }\n```\n然后在 `.minds/team.yaml` 里引用 `provider: my_provider` / `model: my_model`。',
|
|
3409
|
-
'覆盖/合并语义:`.minds/llm.yaml` 会在内置 defaults 之上做覆盖(以当前实现为准);定义一个 provider key 并不意味着“禁用其他内置 provider”。',
|
|
3410
|
-
'不要在文件里存 API key,使用环境变量(apiKeyEnvVar)。',
|
|
3411
|
-
'member_defaults.provider/model 需要引用这里的 key。',
|
|
3412
|
-
'`model_param_options` 可选:用于记录该 provider 支持的 `.minds/team.yaml model_params` 选项(文档用途)。',
|
|
3413
|
-
])
|
|
3414
|
-
: fmtHeader('.minds/llm.yaml') +
|
|
3415
|
-
fmtList([
|
|
3416
|
-
'Defines provider keys → model keys (referenced by `.minds/team.yaml` via `member_defaults.provider` / `members.<id>.provider`).',
|
|
3417
|
-
'Quick checks: use `team_mgmt_list_providers({})` to list built-in/rtws providers + env-var readiness; use `team_mgmt_list_models({ source: \"effective\", provider_pattern: \"*\", model_pattern: \"*\" })` to list merged models and `model_param_options`.',
|
|
3418
|
-
'Minimal example:\n```yaml\nproviders:\n my_provider:\n apiKeyEnvVar: MY_PROVIDER_API_KEY\n models:\n my_model: { name: "my-model-id" }\n```\nThen reference `provider: my_provider` and `model: my_model` in `.minds/team.yaml`.',
|
|
3419
|
-
'Merge/override: `.minds/llm.yaml` overrides built-in defaults (per current implementation); defining one provider does not imply disabling other built-in providers.',
|
|
3420
|
-
'Do not store API keys in the file; use env vars via apiKeyEnvVar.',
|
|
3421
|
-
'member_defaults.provider/model must reference these keys.',
|
|
3422
|
-
'Optional: `model_param_options` documents `.minds/team.yaml model_params` knobs (documentation only).',
|
|
3423
|
-
]);
|
|
3424
|
-
return ok(llmText, [{ type: 'environment_msg', role: 'user', content: llmText }]);
|
|
3425
|
-
}
|
|
3426
|
-
if (want('mcp')) {
|
|
3427
|
-
const content = renderMcpManual(language);
|
|
3428
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3429
|
-
}
|
|
3430
|
-
if (want('minds')) {
|
|
3431
|
-
const content = renderMindsManual(language);
|
|
3432
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3433
|
-
}
|
|
3434
|
-
if (want('env')) {
|
|
3435
|
-
const content = renderEnvManual(language);
|
|
3436
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3437
|
-
}
|
|
3438
|
-
if (want('permissions')) {
|
|
3439
|
-
const content = renderPermissionsManual(language);
|
|
3440
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3441
|
-
}
|
|
3442
|
-
if (want('toolsets')) {
|
|
3443
|
-
const content = await renderToolsets(language);
|
|
3444
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3445
|
-
}
|
|
3446
|
-
if (want('troubleshooting')) {
|
|
3447
|
-
const content = renderTroubleshooting(language);
|
|
3448
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3449
|
-
}
|
|
3450
|
-
const content = renderIndex();
|
|
3451
|
-
return ok(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3452
|
-
}
|
|
3453
|
-
catch (err) {
|
|
3454
|
-
const content = language === 'zh'
|
|
3455
|
-
? `错误:${err instanceof Error ? err.message : String(err)}`
|
|
3456
|
-
: `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
3457
|
-
return fail(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3458
|
-
}
|
|
3459
|
-
},
|
|
3460
|
-
};
|
|
3461
|
-
exports.teamMgmtTools = [
|
|
3462
|
-
exports.teamMgmtManualTool,
|
|
3463
|
-
exports.teamMgmtCheckProviderTool,
|
|
3464
|
-
exports.teamMgmtListProvidersTool,
|
|
3465
|
-
exports.teamMgmtListModelsTool,
|
|
3466
|
-
exports.teamMgmtValidateTeamCfgTool,
|
|
3467
|
-
exports.teamMgmtListDirTool,
|
|
3468
|
-
exports.teamMgmtReadFileTool,
|
|
3469
|
-
exports.teamMgmtCreateNewFileTool,
|
|
3470
|
-
exports.teamMgmtOverwriteEntireFileTool,
|
|
3471
|
-
exports.teamMgmtPrepareFileAppendTool,
|
|
3472
|
-
exports.teamMgmtPrepareInsertAfterTool,
|
|
3473
|
-
exports.teamMgmtPrepareInsertBeforeTool,
|
|
3474
|
-
exports.teamMgmtPrepareBlockReplaceTool,
|
|
3475
|
-
exports.teamMgmtPrepareFileRangeEditTool,
|
|
3476
|
-
exports.teamMgmtApplyFileModificationTool,
|
|
3477
|
-
exports.teamMgmtMkDirTool,
|
|
3478
|
-
exports.teamMgmtMoveFileTool,
|
|
3479
|
-
exports.teamMgmtMoveDirTool,
|
|
3480
|
-
exports.teamMgmtRipgrepFilesTool,
|
|
3481
|
-
exports.teamMgmtRipgrepSnippetsTool,
|
|
3482
|
-
exports.teamMgmtRipgrepCountTool,
|
|
3483
|
-
exports.teamMgmtRipgrepFixedTool,
|
|
3484
|
-
exports.teamMgmtRipgrepSearchTool,
|
|
3485
|
-
exports.teamMgmtRmFileTool,
|
|
3486
|
-
exports.teamMgmtRmDirTool,
|
|
3487
|
-
];
|