@zqagent/core 0.2.20 → 0.3.7
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 +1 -1
- package/README.oss.en.md +3 -3
- package/README.oss.md +8 -3
- package/README.oss.zh-TW.md +3 -3
- package/dist/McpClient-CEQBUPuC.d.cts +145 -0
- package/dist/McpClient-CEQBUPuC.d.ts +145 -0
- package/dist/{ToolRegistry-C3pTFiaJ.d.cts → ToolRegistry-Cy0fQxrb.d.ts} +2 -111
- package/dist/{ToolRegistry-C3pTFiaJ.d.ts → ToolRegistry-DouDvn9E.d.cts} +2 -111
- package/dist/{explore-RtZtb-V6.d.ts → TurnEngine-BPKhWuSI.d.cts} +108 -73
- package/dist/{explore-BgRhj6SP.d.cts → TurnEngine-DtGEuVLT.d.ts} +108 -73
- package/dist/conflictGuard-BBOOIxxK.d.cts +42 -0
- package/dist/conflictGuard-BBOOIxxK.d.ts +42 -0
- package/dist/index.cjs +5930 -1114
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +577 -195
- package/dist/index.d.ts +577 -195
- package/dist/index.js +5761 -979
- package/dist/index.js.map +1 -1
- package/dist/internal/crypto/secrets.cjs +25 -0
- package/dist/internal/crypto/secrets.cjs.map +1 -0
- package/dist/internal/crypto/secrets.d.cts +2 -0
- package/dist/internal/crypto/secrets.d.ts +2 -0
- package/dist/internal/crypto/secrets.js +3 -0
- package/dist/internal/crypto/secrets.js.map +1 -0
- package/dist/internal/engine/FileLogHook.cjs +179 -0
- package/dist/internal/engine/FileLogHook.cjs.map +1 -0
- package/dist/internal/engine/FileLogHook.d.cts +67 -0
- package/dist/internal/engine/FileLogHook.d.ts +67 -0
- package/dist/internal/engine/FileLogHook.js +144 -0
- package/dist/internal/engine/FileLogHook.js.map +1 -0
- package/dist/internal/engine/approvalView.cjs +25 -0
- package/dist/internal/engine/approvalView.cjs.map +1 -0
- package/dist/internal/engine/approvalView.d.cts +1 -0
- package/dist/internal/engine/approvalView.d.ts +1 -0
- package/dist/internal/engine/approvalView.js +3 -0
- package/dist/internal/engine/approvalView.js.map +1 -0
- package/dist/internal/engine/limits.cjs +59 -0
- package/dist/internal/engine/limits.cjs.map +1 -0
- package/dist/internal/engine/limits.d.cts +32 -0
- package/dist/internal/engine/limits.d.ts +32 -0
- package/dist/internal/engine/limits.js +31 -0
- package/dist/internal/engine/limits.js.map +1 -0
- package/dist/internal/engine/sessionNaming.cjs +47 -0
- package/dist/internal/engine/sessionNaming.cjs.map +1 -0
- package/dist/internal/engine/sessionNaming.d.cts +25 -0
- package/dist/internal/engine/sessionNaming.d.ts +25 -0
- package/dist/internal/engine/sessionNaming.js +20 -0
- package/dist/internal/engine/sessionNaming.js.map +1 -0
- package/dist/internal/llmConfig.cjs +561 -0
- package/dist/internal/llmConfig.cjs.map +1 -0
- package/dist/internal/llmConfig.d.cts +75 -0
- package/dist/internal/llmConfig.d.ts +75 -0
- package/dist/internal/llmConfig.js +535 -0
- package/dist/internal/llmConfig.js.map +1 -0
- package/dist/internal/mcp/builtin/index.cjs +685 -0
- package/dist/internal/mcp/builtin/index.cjs.map +1 -0
- package/dist/internal/mcp/builtin/index.d.cts +21 -0
- package/dist/internal/mcp/builtin/index.d.ts +21 -0
- package/dist/internal/mcp/builtin/index.js +652 -0
- package/dist/internal/mcp/builtin/index.js.map +1 -0
- package/dist/internal/mcp/config.cjs +278 -0
- package/dist/internal/mcp/config.cjs.map +1 -0
- package/dist/internal/mcp/config.d.cts +61 -0
- package/dist/internal/mcp/config.d.ts +61 -0
- package/dist/internal/mcp/config.js +235 -0
- package/dist/internal/mcp/config.js.map +1 -0
- package/dist/internal/mcp/presets.cjs +184 -0
- package/dist/internal/mcp/presets.cjs.map +1 -0
- package/dist/internal/mcp/presets.d.cts +39 -0
- package/dist/internal/mcp/presets.d.ts +39 -0
- package/dist/internal/mcp/presets.js +158 -0
- package/dist/internal/mcp/presets.js.map +1 -0
- package/dist/internal/node-name.cjs +45 -0
- package/dist/internal/node-name.cjs.map +1 -0
- package/dist/internal/node-name.d.cts +3 -0
- package/dist/internal/node-name.d.ts +3 -0
- package/dist/internal/node-name.js +10 -0
- package/dist/internal/node-name.js.map +1 -0
- package/dist/internal/paths.cjs +63 -0
- package/dist/internal/paths.cjs.map +1 -0
- package/dist/internal/paths.d.cts +17 -0
- package/dist/internal/paths.d.ts +17 -0
- package/dist/internal/paths.js +25 -0
- package/dist/internal/paths.js.map +1 -0
- package/dist/internal/providers/diagnostics.cjs +288 -0
- package/dist/internal/providers/diagnostics.cjs.map +1 -0
- package/dist/internal/providers/diagnostics.d.cts +103 -0
- package/dist/internal/providers/diagnostics.d.ts +103 -0
- package/dist/internal/providers/diagnostics.js +250 -0
- package/dist/internal/providers/diagnostics.js.map +1 -0
- package/dist/internal/providers/models.cjs +699 -0
- package/dist/internal/providers/models.cjs.map +1 -0
- package/dist/internal/providers/models.d.cts +136 -0
- package/dist/internal/providers/models.d.ts +136 -0
- package/dist/internal/providers/models.js +654 -0
- package/dist/internal/providers/models.js.map +1 -0
- package/dist/internal/terminal.cjs +161 -0
- package/dist/internal/terminal.cjs.map +1 -0
- package/dist/internal/terminal.d.cts +58 -0
- package/dist/internal/terminal.d.ts +58 -0
- package/dist/internal/terminal.js +128 -0
- package/dist/internal/terminal.js.map +1 -0
- package/dist/internal/tools/execAsync.cjs +187 -0
- package/dist/internal/tools/execAsync.cjs.map +1 -0
- package/dist/internal/tools/execAsync.d.cts +30 -0
- package/dist/internal/tools/execAsync.d.ts +30 -0
- package/dist/internal/tools/execAsync.js +162 -0
- package/dist/internal/tools/execAsync.js.map +1 -0
- package/dist/internal/tools/git.cjs +935 -0
- package/dist/internal/tools/git.cjs.map +1 -0
- package/dist/internal/tools/git.d.cts +206 -0
- package/dist/internal/tools/git.d.ts +206 -0
- package/dist/internal/tools/git.js +883 -0
- package/dist/internal/tools/git.js.map +1 -0
- package/dist/internal/tools/pathSafety.cjs +99 -0
- package/dist/internal/tools/pathSafety.cjs.map +1 -0
- package/dist/internal/tools/pathSafety.d.cts +44 -0
- package/dist/internal/tools/pathSafety.d.ts +44 -0
- package/dist/internal/tools/pathSafety.js +61 -0
- package/dist/internal/tools/pathSafety.js.map +1 -0
- package/dist/internal/tools/plan.cjs +146 -0
- package/dist/internal/tools/plan.cjs.map +1 -0
- package/dist/internal/tools/plan.d.cts +48 -0
- package/dist/internal/tools/plan.d.ts +48 -0
- package/dist/internal/tools/plan.js +117 -0
- package/dist/internal/tools/plan.js.map +1 -0
- package/dist/internal/tools/ripgrep.cjs +118 -0
- package/dist/internal/tools/ripgrep.cjs.map +1 -0
- package/dist/internal/tools/ripgrep.d.cts +6 -0
- package/dist/internal/tools/ripgrep.d.ts +6 -0
- package/dist/internal/tools/ripgrep.js +89 -0
- package/dist/internal/tools/ripgrep.js.map +1 -0
- package/dist/internal/tools/sandbox.cjs +253 -0
- package/dist/internal/tools/sandbox.cjs.map +1 -0
- package/dist/internal/tools/sandbox.d.cts +67 -0
- package/dist/internal/tools/sandbox.d.ts +67 -0
- package/dist/internal/tools/sandbox.js +213 -0
- package/dist/internal/tools/sandbox.js.map +1 -0
- package/dist/internal/tools/shell.cjs +771 -0
- package/dist/internal/tools/shell.cjs.map +1 -0
- package/dist/internal/tools/shell.d.cts +47 -0
- package/dist/internal/tools/shell.d.ts +47 -0
- package/dist/internal/tools/shell.js +731 -0
- package/dist/internal/tools/shell.js.map +1 -0
- package/dist/internal/tools/shellRuntime.cjs +220 -0
- package/dist/internal/tools/shellRuntime.cjs.map +1 -0
- package/dist/internal/tools/shellRuntime.d.cts +67 -0
- package/dist/internal/tools/shellRuntime.d.ts +67 -0
- package/dist/internal/tools/shellRuntime.js +181 -0
- package/dist/internal/tools/shellRuntime.js.map +1 -0
- package/dist/internal/update/controller.cjs +1035 -0
- package/dist/internal/update/controller.cjs.map +1 -0
- package/dist/internal/update/controller.d.cts +150 -0
- package/dist/internal/update/controller.d.ts +150 -0
- package/dist/internal/update/controller.js +991 -0
- package/dist/internal/update/controller.js.map +1 -0
- package/dist/internal/update/platform.cjs +234 -0
- package/dist/internal/update/platform.cjs.map +1 -0
- package/dist/internal/update/platform.d.cts +77 -0
- package/dist/internal/update/platform.d.ts +77 -0
- package/dist/internal/update/platform.js +184 -0
- package/dist/internal/update/platform.js.map +1 -0
- package/dist/tools/ask.d.cts +3 -1
- package/dist/tools/ask.d.ts +3 -1
- package/dist/tools/diff.cjs +17 -6
- package/dist/tools/diff.cjs.map +1 -1
- package/dist/tools/diff.d.cts +5 -2
- package/dist/tools/diff.d.ts +5 -2
- package/dist/tools/diff.js +17 -6
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/directories.d.cts +3 -1
- package/dist/tools/directories.d.ts +3 -1
- package/dist/tools/explore.cjs +1778 -204
- package/dist/tools/explore.cjs.map +1 -1
- package/dist/tools/explore.d.cts +82 -2
- package/dist/tools/explore.d.ts +82 -2
- package/dist/tools/explore.js +1780 -204
- package/dist/tools/explore.js.map +1 -1
- package/dist/tools/files.cjs +56 -21
- package/dist/tools/files.cjs.map +1 -1
- package/dist/tools/files.d.cts +6 -3
- package/dist/tools/files.d.ts +6 -3
- package/dist/tools/files.js +56 -21
- package/dist/tools/files.js.map +1 -1
- package/dist/tools/git.cjs +34 -25
- package/dist/tools/git.cjs.map +1 -1
- package/dist/tools/git.d.cts +4 -204
- package/dist/tools/git.d.ts +4 -204
- package/dist/tools/git.js +34 -25
- package/dist/tools/git.js.map +1 -1
- package/dist/tools/media.cjs +280 -3
- package/dist/tools/media.cjs.map +1 -1
- package/dist/tools/media.d.cts +3 -1
- package/dist/tools/media.d.ts +3 -1
- package/dist/tools/media.js +278 -1
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/plan.cjs +62 -0
- package/dist/tools/plan.cjs.map +1 -1
- package/dist/tools/plan.d.cts +4 -17
- package/dist/tools/plan.d.ts +4 -17
- package/dist/tools/plan.js +59 -0
- package/dist/tools/plan.js.map +1 -1
- package/dist/tools/search.cjs +3 -0
- package/dist/tools/search.cjs.map +1 -1
- package/dist/tools/search.d.cts +3 -1
- package/dist/tools/search.d.ts +3 -1
- package/dist/tools/search.js +3 -0
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/shell.cjs +76 -11
- package/dist/tools/shell.cjs.map +1 -1
- package/dist/tools/shell.d.cts +5 -39
- package/dist/tools/shell.d.ts +5 -39
- package/dist/tools/shell.js +75 -11
- package/dist/tools/shell.js.map +1 -1
- package/dist/tools/todo.d.cts +3 -1
- package/dist/tools/todo.d.ts +3 -1
- package/dist/tools/web.cjs +279 -5
- package/dist/tools/web.cjs.map +1 -1
- package/dist/tools/web.d.cts +3 -1
- package/dist/tools/web.d.ts +3 -1
- package/dist/tools/web.js +275 -3
- package/dist/tools/web.js.map +1 -1
- package/dist/types-BYyXWWPW.d.cts +237 -0
- package/dist/types-BYyXWWPW.d.ts +237 -0
- package/package.json +26 -37
- package/bin/zqagent +0 -2
- package/dist/cli/index.cjs +0 -25218
- package/dist/cli/index.cjs.map +0 -1
- package/dist/cli/index.d.cts +0 -2
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -25201
- package/dist/cli/index.js.map +0 -1
- package/dist/server/index.cjs +0 -20510
- package/dist/server/index.cjs.map +0 -1
- package/dist/server/index.d.cts +0 -2
- package/dist/server/index.d.ts +0 -2
- package/dist/server/index.js +0 -20493
- package/dist/server/index.js.map +0 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/tools/plan.ts
|
|
21
|
+
var plan_exports = {};
|
|
22
|
+
__export(plan_exports, {
|
|
23
|
+
interpretPlanReply: () => interpretPlanReply,
|
|
24
|
+
isAffirmativeText: () => isAffirmativeText,
|
|
25
|
+
isRejectionText: () => isRejectionText,
|
|
26
|
+
registerPlanTool: () => registerPlanTool,
|
|
27
|
+
setPlanHandler: () => setPlanHandler
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(plan_exports);
|
|
30
|
+
var APPROVE_WORDS = /* @__PURE__ */ new Set([
|
|
31
|
+
"y",
|
|
32
|
+
"yes",
|
|
33
|
+
"approve",
|
|
34
|
+
"approved",
|
|
35
|
+
"ok",
|
|
36
|
+
"true",
|
|
37
|
+
"a",
|
|
38
|
+
"1",
|
|
39
|
+
"\u597D",
|
|
40
|
+
"\u597D\u7684",
|
|
41
|
+
"\u884C",
|
|
42
|
+
"\u55EF"
|
|
43
|
+
]);
|
|
44
|
+
var APPROVE_PREFIX = /^(yes|approve|同意|批准|允许|确定|可以|好的|确认)/;
|
|
45
|
+
var REJECT_WORDS = /* @__PURE__ */ new Set([
|
|
46
|
+
"d",
|
|
47
|
+
"n",
|
|
48
|
+
"no",
|
|
49
|
+
"reject",
|
|
50
|
+
"rejected",
|
|
51
|
+
"deny",
|
|
52
|
+
"false",
|
|
53
|
+
"0",
|
|
54
|
+
"\u4E0D",
|
|
55
|
+
"\u4E0D\u884C",
|
|
56
|
+
"\u4E0D\u7528",
|
|
57
|
+
"\u62D2\u7EDD",
|
|
58
|
+
"\u4E0D\u540C\u610F",
|
|
59
|
+
"\u53D6\u6D88"
|
|
60
|
+
]);
|
|
61
|
+
function isAffirmativeText(answer) {
|
|
62
|
+
const a = answer.trim().toLowerCase();
|
|
63
|
+
if (!a) return false;
|
|
64
|
+
if (APPROVE_WORDS.has(a)) return true;
|
|
65
|
+
return APPROVE_PREFIX.test(a);
|
|
66
|
+
}
|
|
67
|
+
function isRejectionText(answer) {
|
|
68
|
+
const a = answer.trim().toLowerCase().replace(/[。.!!]$/, "");
|
|
69
|
+
return REJECT_WORDS.has(a);
|
|
70
|
+
}
|
|
71
|
+
function interpretPlanReply(answer) {
|
|
72
|
+
const text = answer.trim();
|
|
73
|
+
if (!text) return { kind: "empty" };
|
|
74
|
+
if (isAffirmativeText(text)) return { kind: "approve" };
|
|
75
|
+
if (isRejectionText(text)) return { kind: "reject" };
|
|
76
|
+
return { kind: "revise", feedback: text };
|
|
77
|
+
}
|
|
78
|
+
var _planHandler = null;
|
|
79
|
+
function setPlanHandler(handler) {
|
|
80
|
+
_planHandler = handler;
|
|
81
|
+
}
|
|
82
|
+
function makeProposePlan() {
|
|
83
|
+
return async function propose_plan(args) {
|
|
84
|
+
const plan = String(args.plan ?? "");
|
|
85
|
+
if (!plan.trim()) {
|
|
86
|
+
return { approved: false, error: "\u8BA1\u5212\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
87
|
+
}
|
|
88
|
+
if (!_planHandler) {
|
|
89
|
+
return { approved: false, error: "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u8BA1\u5212\u5BA1\u6279" };
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const result = await _planHandler(plan);
|
|
93
|
+
if (result.approved) {
|
|
94
|
+
return {
|
|
95
|
+
approved: true,
|
|
96
|
+
mode: result.mode ?? "interactive",
|
|
97
|
+
note: "\u8BA1\u5212\u5DF2\u6279\u51C6 \u2014 \u73B0\u5728\u5F00\u59CB\u5B9E\u65BD"
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (result.revised) {
|
|
101
|
+
return {
|
|
102
|
+
approved: false,
|
|
103
|
+
revised: true,
|
|
104
|
+
feedback: result.feedback ?? "\u7528\u6237\u8981\u6C42\u8865\u5145\u4FEE\u6B63\u8BA1\u5212",
|
|
105
|
+
note: "\u7528\u6237\u6CA1\u6709\u6279\u51C6\uFF0C\u800C\u662F\u5BF9\u8BA1\u5212\u63D0\u51FA\u4E86\u8865\u5145\u8981\u6C42\u3002\u8BF7\u628A\u8FD9\u4E9B\u8981\u6C42\u5408\u5E76\u8FDB\u539F\u8BA1\u5212\uFF08\u4FDD\u7559\u4ECD\u7136\u6709\u6548\u7684\u90E8\u5206\uFF09\uFF0C\u7136\u540E\u518D\u6B21\u8C03\u7528 propose_plan \u63D0\u4EA4\u4FEE\u8BA2\u540E\u7684\u8BA1\u5212\uFF0C\u4E0D\u8981\u5F00\u59CB\u5B9E\u65BD\uFF0C\u4E5F\u4E0D\u8981\u4ECE\u5934\u91CD\u5199\u6574\u4E2A\u65B9\u6848\u3002"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
approved: false,
|
|
110
|
+
feedback: result.feedback ?? "\u8BA1\u5212\u88AB\u62D2\u7EDD"
|
|
111
|
+
};
|
|
112
|
+
} catch {
|
|
113
|
+
return { approved: false, error: "\u8BA1\u5212\u5BA1\u6279\u5DF2\u53D6\u6D88" };
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function registerPlanTool(registry) {
|
|
118
|
+
const planFunc = makeProposePlan();
|
|
119
|
+
registry.register(
|
|
120
|
+
(args) => planFunc(args),
|
|
121
|
+
{ category: "planning", riskLevel: "low", capabilities: ["plan"], requiresApproval: false },
|
|
122
|
+
{
|
|
123
|
+
type: "function",
|
|
124
|
+
function: {
|
|
125
|
+
name: "propose_plan",
|
|
126
|
+
description: "\u5C06\u4F60\u7684\u5B9E\u65BD\u8BA1\u5212\u5448\u73B0\u7ED9\u7528\u6237\u5BA1\u6279\u3002\u5728\u63A2\u7D22\u5145\u5206\u3001\u786E\u5B9A\u4E86\u4E00\u4E2A\u65B9\u6848\u540E\u4F7F\u7528\u6B64\u5DE5\u5177\uFF1A\u603B\u7ED3\u4F60\u5C06\u66F4\u6539\u4EC0\u4E48\u3001\u5728\u54EA\u4E9B\u6587\u4EF6\u4E2D\u3001\u4EE5\u53CA\u4F60\u5C06\u5982\u4F55\u9A8C\u8BC1\u5B83\u3002\u5982\u679C\u88AB\u6279\u51C6\uFF0C\u4F1A\u8BDD\u5C06\u9000\u51FA\u53EA\u8BFB\u8BA1\u5212\u6A21\u5F0F\uFF0C\u4F60\u6765\u5B9E\u65BD\u8BA1\u5212\uFF1B\u5982\u679C\u88AB\u62D2\u7EDD\uFF0C\u4F7F\u7528\u7ED3\u679C\u4E2D\u7684\u53CD\u9988\u610F\u89C1\u4FEE\u6539\u8BA1\u5212\u3002\u4E0D\u8981\u5728\u8FD8\u6CA1\u6709\u63D0\u6848\u4E4B\u524D\u5C31\u63CF\u8FF0\u5B9E\u65BD\u6B65\u9AA4\u2014\u2014\u5148\u63D0\u6848\u3002",
|
|
127
|
+
parameters: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: {
|
|
130
|
+
plan: { type: "string", description: "\u5B8C\u6574\u7684\u5B9E\u65BD\u8BA1\u5212\u63CF\u8FF0\uFF08\u5305\u542B\u5C06\u66F4\u6539\u4EC0\u4E48\u3001\u5728\u54EA\u4E9B\u6587\u4EF6\u4E2D\u3001\u5982\u4F55\u9A8C\u8BC1\uFF09" }
|
|
131
|
+
},
|
|
132
|
+
required: ["plan"]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
139
|
+
0 && (module.exports = {
|
|
140
|
+
interpretPlanReply,
|
|
141
|
+
isAffirmativeText,
|
|
142
|
+
isRejectionText,
|
|
143
|
+
registerPlanTool,
|
|
144
|
+
setPlanHandler
|
|
145
|
+
});
|
|
146
|
+
//# sourceMappingURL=plan.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/tools/plan.ts"],"sourcesContent":["import { ToolRegistry } from \"./ToolRegistry.js\";\nimport { Mode } from \"../permissions/PermissionEngine.js\";\n\n/**\n * propose_plan — 计划提案工具\n * 对齐 Python 版 tools/plan.py\n * 在 CLI 模式下通过 readline 审批实现\n */\n\n/**\n * 肯定回答(批准 / 授权):单词或句首。\n * 中文口语的「好 / 行 / 嗯」也要认:否则用户敲一个「好」,会被当成补充要求。\n */\nconst APPROVE_WORDS = new Set([\n \"y\",\n \"yes\",\n \"approve\",\n \"approved\",\n \"ok\",\n \"true\",\n \"a\",\n \"1\",\n \"好\",\n \"好的\",\n \"行\",\n \"嗯\",\n]);\nconst APPROVE_PREFIX = /^(yes|approve|同意|批准|允许|确定|可以|好的|确认)/;\n\n/** 明确的拒绝词(Web 端点「拒绝」按钮提交的就是 reject) */\nconst REJECT_WORDS = new Set([\n \"d\",\n \"n\",\n \"no\",\n \"reject\",\n \"rejected\",\n \"deny\",\n \"false\",\n \"0\",\n \"不\",\n \"不行\",\n \"不用\",\n \"拒绝\",\n \"不同意\",\n \"取消\",\n]);\n\n/** 是否肯定回答(批准计划 / 授权目录) */\nexport function isAffirmativeText(answer: string): boolean {\n const a = answer.trim().toLowerCase();\n if (!a) return false;\n if (APPROVE_WORDS.has(a)) return true;\n return APPROVE_PREFIX.test(a);\n}\n\n/**\n * 是否明确的拒绝:只有整句就是拒绝词才算。\n * 「不要改 A,改用 B」这类是修改意见,不能当成否决。\n */\nexport function isRejectionText(answer: string): boolean {\n const a = answer.trim().toLowerCase().replace(/[。.!!]$/, \"\");\n return REJECT_WORDS.has(a);\n}\n\n/**\n * 解析用户对计划提案的一句话回应。\n *\n * CLI 与 Web 共用同一套判定,避免两处词表各自漂移:\n * - approve:批准;\n * - reject:否决;\n * - revise:既没批准也没拒绝 → 视为补充要求,合并进计划后重新提案;\n * - empty:空输入(CLI 重新提示,不改变任何状态)。\n */\nexport type PlanReply =\n | { kind: \"approve\" }\n | { kind: \"reject\" }\n | { kind: \"revise\"; feedback: string }\n | { kind: \"empty\" };\n\nexport function interpretPlanReply(answer: string): PlanReply {\n const text = answer.trim();\n if (!text) return { kind: \"empty\" };\n if (isAffirmativeText(text)) return { kind: \"approve\" };\n if (isRejectionText(text)) return { kind: \"reject\" };\n return { kind: \"revise\", feedback: text };\n}\n\n/**\n * 提案审批结果。\n *\n * `revised` 表示「用户没有批准也没有拒绝,而是提出了补充要求」:把 feedback\n * 合并进原计划后重新提案即可,不要开始实施,也不要把它当成否决而放弃方案。\n */\nexport type PlanHandler = (plan: string) => Promise<{\n approved: boolean;\n mode?: string;\n feedback?: string;\n revised?: boolean;\n}>;\n\nlet _planHandler: PlanHandler | null = null;\n\nexport function setPlanHandler(handler: PlanHandler): void {\n _planHandler = handler;\n}\n\nfunction makeProposePlan() {\n return async function propose_plan(args: Record<string, unknown>) {\n const plan = String(args.plan ?? \"\");\n\n if (!plan.trim()) {\n return { approved: false, error: \"计划内容不能为空\" };\n }\n\n if (!_planHandler) {\n return { approved: false, error: \"当前环境不支持计划审批\" };\n }\n\n try {\n const result = await _planHandler(plan);\n if (result.approved) {\n return {\n approved: true,\n mode: result.mode ?? \"interactive\",\n note: \"计划已批准 — 现在开始实施\",\n };\n }\n /*\n * 用户提出补充要求(既没批准也没拒绝):这是「修订」,不是否决。\n * 明确告诉模型下一步该做什么——把反馈合并进计划、再次调用 propose_plan。\n * 否则模型容易把它当作拒绝而从零重来,或者直接开始动手。\n */\n if (result.revised) {\n return {\n approved: false,\n revised: true,\n feedback: result.feedback ?? \"用户要求补充修正计划\",\n note:\n \"用户没有批准,而是对计划提出了补充要求。请把这些要求合并进原计划\" +\n \"(保留仍然有效的部分),然后再次调用 propose_plan 提交修订后的计划,\" +\n \"不要开始实施,也不要从头重写整个方案。\",\n };\n }\n return {\n approved: false,\n feedback: result.feedback ?? \"计划被拒绝\",\n };\n } catch {\n return { approved: false, error: \"计划审批已取消\" };\n }\n };\n}\n\n/** 注册 propose_plan 工具 */\nexport function registerPlanTool(registry: ToolRegistry): void {\n const planFunc = makeProposePlan();\n\n registry.register(\n (args) => planFunc(args),\n { category: \"planning\", riskLevel: \"low\", capabilities: [\"plan\"], requiresApproval: false },\n {\n type: \"function\",\n function: {\n name: \"propose_plan\",\n description: \"将你的实施计划呈现给用户审批。在探索充分、确定了一个方案后使用此工具:总结你将更改什么、在哪些文件中、以及你将如何验证它。如果被批准,会话将退出只读计划模式,你来实施计划;如果被拒绝,使用结果中的反馈意见修改计划。不要在还没有提案之前就描述实施步骤——先提案。\",\n parameters: {\n type: \"object\",\n properties: {\n plan: { type: \"string\", description: \"完整的实施计划描述(包含将更改什么、在哪些文件中、如何验证)\" },\n },\n required: [\"plan\"],\n },\n },\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,IAAM,gBAAgB,oBAAI,IAAI;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,iBAAiB;AAGvB,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,kBAAkB,QAAyB;AACzD,QAAM,IAAI,OAAO,KAAK,EAAE,YAAY;AACpC,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,cAAc,IAAI,CAAC,EAAG,QAAO;AACjC,SAAO,eAAe,KAAK,CAAC;AAC9B;AAMO,SAAS,gBAAgB,QAAyB;AACvD,QAAM,IAAI,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,WAAW,EAAE;AAC3D,SAAO,aAAa,IAAI,CAAC;AAC3B;AAiBO,SAAS,mBAAmB,QAA2B;AAC5D,QAAM,OAAO,OAAO,KAAK;AACzB,MAAI,CAAC,KAAM,QAAO,EAAE,MAAM,QAAQ;AAClC,MAAI,kBAAkB,IAAI,EAAG,QAAO,EAAE,MAAM,UAAU;AACtD,MAAI,gBAAgB,IAAI,EAAG,QAAO,EAAE,MAAM,SAAS;AACnD,SAAO,EAAE,MAAM,UAAU,UAAU,KAAK;AAC1C;AAeA,IAAI,eAAmC;AAEhC,SAAS,eAAe,SAA4B;AACzD,iBAAe;AACjB;AAEA,SAAS,kBAAkB;AACzB,SAAO,eAAe,aAAa,MAA+B;AAChE,UAAM,OAAO,OAAO,KAAK,QAAQ,EAAE;AAEnC,QAAI,CAAC,KAAK,KAAK,GAAG;AAChB,aAAO,EAAE,UAAU,OAAO,OAAO,mDAAW;AAAA,IAC9C;AAEA,QAAI,CAAC,cAAc;AACjB,aAAO,EAAE,UAAU,OAAO,OAAO,qEAAc;AAAA,IACjD;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,aAAa,IAAI;AACtC,UAAI,OAAO,UAAU;AACnB,eAAO;AAAA,UACL,UAAU;AAAA,UACV,MAAM,OAAO,QAAQ;AAAA,UACrB,MAAM;AAAA,QACR;AAAA,MACF;AAMA,UAAI,OAAO,SAAS;AAClB,eAAO;AAAA,UACL,UAAU;AAAA,UACV,SAAS;AAAA,UACT,UAAU,OAAO,YAAY;AAAA,UAC7B,MACE;AAAA,QAGJ;AAAA,MACF;AACA,aAAO;AAAA,QACL,UAAU;AAAA,QACV,UAAU,OAAO,YAAY;AAAA,MAC/B;AAAA,IACF,QAAQ;AACN,aAAO,EAAE,UAAU,OAAO,OAAO,6CAAU;AAAA,IAC7C;AAAA,EACF;AACF;AAGO,SAAS,iBAAiB,UAA8B;AAC7D,QAAM,WAAW,gBAAgB;AAEjC,WAAS;AAAA,IACP,CAAC,SAAS,SAAS,IAAI;AAAA,IACvB,EAAE,UAAU,YAAY,WAAW,OAAO,cAAc,CAAC,MAAM,GAAG,kBAAkB,MAAM;AAAA,IAC1F;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,QACR,MAAM;AAAA,QACN,aAAa;AAAA,QACb,YAAY;AAAA,UACV,MAAM;AAAA,UACN,YAAY;AAAA,YACV,MAAM,EAAE,MAAM,UAAU,aAAa,uLAAiC;AAAA,UACxE;AAAA,UACA,UAAU,CAAC,MAAM;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { T as ToolRegistry } from '../../ToolRegistry-DouDvn9E.cjs';
|
|
2
|
+
import '../../types-BYyXWWPW.cjs';
|
|
3
|
+
import '@zqagent/shared/model-capabilities';
|
|
4
|
+
|
|
5
|
+
/** 是否肯定回答(批准计划 / 授权目录) */
|
|
6
|
+
declare function isAffirmativeText(answer: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* 是否明确的拒绝:只有整句就是拒绝词才算。
|
|
9
|
+
* 「不要改 A,改用 B」这类是修改意见,不能当成否决。
|
|
10
|
+
*/
|
|
11
|
+
declare function isRejectionText(answer: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* 解析用户对计划提案的一句话回应。
|
|
14
|
+
*
|
|
15
|
+
* CLI 与 Web 共用同一套判定,避免两处词表各自漂移:
|
|
16
|
+
* - approve:批准;
|
|
17
|
+
* - reject:否决;
|
|
18
|
+
* - revise:既没批准也没拒绝 → 视为补充要求,合并进计划后重新提案;
|
|
19
|
+
* - empty:空输入(CLI 重新提示,不改变任何状态)。
|
|
20
|
+
*/
|
|
21
|
+
type PlanReply = {
|
|
22
|
+
kind: "approve";
|
|
23
|
+
} | {
|
|
24
|
+
kind: "reject";
|
|
25
|
+
} | {
|
|
26
|
+
kind: "revise";
|
|
27
|
+
feedback: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "empty";
|
|
30
|
+
};
|
|
31
|
+
declare function interpretPlanReply(answer: string): PlanReply;
|
|
32
|
+
/**
|
|
33
|
+
* 提案审批结果。
|
|
34
|
+
*
|
|
35
|
+
* `revised` 表示「用户没有批准也没有拒绝,而是提出了补充要求」:把 feedback
|
|
36
|
+
* 合并进原计划后重新提案即可,不要开始实施,也不要把它当成否决而放弃方案。
|
|
37
|
+
*/
|
|
38
|
+
type PlanHandler = (plan: string) => Promise<{
|
|
39
|
+
approved: boolean;
|
|
40
|
+
mode?: string;
|
|
41
|
+
feedback?: string;
|
|
42
|
+
revised?: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
declare function setPlanHandler(handler: PlanHandler): void;
|
|
45
|
+
/** 注册 propose_plan 工具 */
|
|
46
|
+
declare function registerPlanTool(registry: ToolRegistry): void;
|
|
47
|
+
|
|
48
|
+
export { type PlanHandler, type PlanReply, interpretPlanReply, isAffirmativeText, isRejectionText, registerPlanTool, setPlanHandler };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { T as ToolRegistry } from '../../ToolRegistry-Cy0fQxrb.js';
|
|
2
|
+
import '../../types-BYyXWWPW.js';
|
|
3
|
+
import '@zqagent/shared/model-capabilities';
|
|
4
|
+
|
|
5
|
+
/** 是否肯定回答(批准计划 / 授权目录) */
|
|
6
|
+
declare function isAffirmativeText(answer: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* 是否明确的拒绝:只有整句就是拒绝词才算。
|
|
9
|
+
* 「不要改 A,改用 B」这类是修改意见,不能当成否决。
|
|
10
|
+
*/
|
|
11
|
+
declare function isRejectionText(answer: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* 解析用户对计划提案的一句话回应。
|
|
14
|
+
*
|
|
15
|
+
* CLI 与 Web 共用同一套判定,避免两处词表各自漂移:
|
|
16
|
+
* - approve:批准;
|
|
17
|
+
* - reject:否决;
|
|
18
|
+
* - revise:既没批准也没拒绝 → 视为补充要求,合并进计划后重新提案;
|
|
19
|
+
* - empty:空输入(CLI 重新提示,不改变任何状态)。
|
|
20
|
+
*/
|
|
21
|
+
type PlanReply = {
|
|
22
|
+
kind: "approve";
|
|
23
|
+
} | {
|
|
24
|
+
kind: "reject";
|
|
25
|
+
} | {
|
|
26
|
+
kind: "revise";
|
|
27
|
+
feedback: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "empty";
|
|
30
|
+
};
|
|
31
|
+
declare function interpretPlanReply(answer: string): PlanReply;
|
|
32
|
+
/**
|
|
33
|
+
* 提案审批结果。
|
|
34
|
+
*
|
|
35
|
+
* `revised` 表示「用户没有批准也没有拒绝,而是提出了补充要求」:把 feedback
|
|
36
|
+
* 合并进原计划后重新提案即可,不要开始实施,也不要把它当成否决而放弃方案。
|
|
37
|
+
*/
|
|
38
|
+
type PlanHandler = (plan: string) => Promise<{
|
|
39
|
+
approved: boolean;
|
|
40
|
+
mode?: string;
|
|
41
|
+
feedback?: string;
|
|
42
|
+
revised?: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
declare function setPlanHandler(handler: PlanHandler): void;
|
|
45
|
+
/** 注册 propose_plan 工具 */
|
|
46
|
+
declare function registerPlanTool(registry: ToolRegistry): void;
|
|
47
|
+
|
|
48
|
+
export { type PlanHandler, type PlanReply, interpretPlanReply, isAffirmativeText, isRejectionText, registerPlanTool, setPlanHandler };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// src/tools/plan.ts
|
|
2
|
+
var APPROVE_WORDS = /* @__PURE__ */ new Set([
|
|
3
|
+
"y",
|
|
4
|
+
"yes",
|
|
5
|
+
"approve",
|
|
6
|
+
"approved",
|
|
7
|
+
"ok",
|
|
8
|
+
"true",
|
|
9
|
+
"a",
|
|
10
|
+
"1",
|
|
11
|
+
"\u597D",
|
|
12
|
+
"\u597D\u7684",
|
|
13
|
+
"\u884C",
|
|
14
|
+
"\u55EF"
|
|
15
|
+
]);
|
|
16
|
+
var APPROVE_PREFIX = /^(yes|approve|同意|批准|允许|确定|可以|好的|确认)/;
|
|
17
|
+
var REJECT_WORDS = /* @__PURE__ */ new Set([
|
|
18
|
+
"d",
|
|
19
|
+
"n",
|
|
20
|
+
"no",
|
|
21
|
+
"reject",
|
|
22
|
+
"rejected",
|
|
23
|
+
"deny",
|
|
24
|
+
"false",
|
|
25
|
+
"0",
|
|
26
|
+
"\u4E0D",
|
|
27
|
+
"\u4E0D\u884C",
|
|
28
|
+
"\u4E0D\u7528",
|
|
29
|
+
"\u62D2\u7EDD",
|
|
30
|
+
"\u4E0D\u540C\u610F",
|
|
31
|
+
"\u53D6\u6D88"
|
|
32
|
+
]);
|
|
33
|
+
function isAffirmativeText(answer) {
|
|
34
|
+
const a = answer.trim().toLowerCase();
|
|
35
|
+
if (!a) return false;
|
|
36
|
+
if (APPROVE_WORDS.has(a)) return true;
|
|
37
|
+
return APPROVE_PREFIX.test(a);
|
|
38
|
+
}
|
|
39
|
+
function isRejectionText(answer) {
|
|
40
|
+
const a = answer.trim().toLowerCase().replace(/[。.!!]$/, "");
|
|
41
|
+
return REJECT_WORDS.has(a);
|
|
42
|
+
}
|
|
43
|
+
function interpretPlanReply(answer) {
|
|
44
|
+
const text = answer.trim();
|
|
45
|
+
if (!text) return { kind: "empty" };
|
|
46
|
+
if (isAffirmativeText(text)) return { kind: "approve" };
|
|
47
|
+
if (isRejectionText(text)) return { kind: "reject" };
|
|
48
|
+
return { kind: "revise", feedback: text };
|
|
49
|
+
}
|
|
50
|
+
var _planHandler = null;
|
|
51
|
+
function setPlanHandler(handler) {
|
|
52
|
+
_planHandler = handler;
|
|
53
|
+
}
|
|
54
|
+
function makeProposePlan() {
|
|
55
|
+
return async function propose_plan(args) {
|
|
56
|
+
const plan = String(args.plan ?? "");
|
|
57
|
+
if (!plan.trim()) {
|
|
58
|
+
return { approved: false, error: "\u8BA1\u5212\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
59
|
+
}
|
|
60
|
+
if (!_planHandler) {
|
|
61
|
+
return { approved: false, error: "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u8BA1\u5212\u5BA1\u6279" };
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const result = await _planHandler(plan);
|
|
65
|
+
if (result.approved) {
|
|
66
|
+
return {
|
|
67
|
+
approved: true,
|
|
68
|
+
mode: result.mode ?? "interactive",
|
|
69
|
+
note: "\u8BA1\u5212\u5DF2\u6279\u51C6 \u2014 \u73B0\u5728\u5F00\u59CB\u5B9E\u65BD"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (result.revised) {
|
|
73
|
+
return {
|
|
74
|
+
approved: false,
|
|
75
|
+
revised: true,
|
|
76
|
+
feedback: result.feedback ?? "\u7528\u6237\u8981\u6C42\u8865\u5145\u4FEE\u6B63\u8BA1\u5212",
|
|
77
|
+
note: "\u7528\u6237\u6CA1\u6709\u6279\u51C6\uFF0C\u800C\u662F\u5BF9\u8BA1\u5212\u63D0\u51FA\u4E86\u8865\u5145\u8981\u6C42\u3002\u8BF7\u628A\u8FD9\u4E9B\u8981\u6C42\u5408\u5E76\u8FDB\u539F\u8BA1\u5212\uFF08\u4FDD\u7559\u4ECD\u7136\u6709\u6548\u7684\u90E8\u5206\uFF09\uFF0C\u7136\u540E\u518D\u6B21\u8C03\u7528 propose_plan \u63D0\u4EA4\u4FEE\u8BA2\u540E\u7684\u8BA1\u5212\uFF0C\u4E0D\u8981\u5F00\u59CB\u5B9E\u65BD\uFF0C\u4E5F\u4E0D\u8981\u4ECE\u5934\u91CD\u5199\u6574\u4E2A\u65B9\u6848\u3002"
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
approved: false,
|
|
82
|
+
feedback: result.feedback ?? "\u8BA1\u5212\u88AB\u62D2\u7EDD"
|
|
83
|
+
};
|
|
84
|
+
} catch {
|
|
85
|
+
return { approved: false, error: "\u8BA1\u5212\u5BA1\u6279\u5DF2\u53D6\u6D88" };
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function registerPlanTool(registry) {
|
|
90
|
+
const planFunc = makeProposePlan();
|
|
91
|
+
registry.register(
|
|
92
|
+
(args) => planFunc(args),
|
|
93
|
+
{ category: "planning", riskLevel: "low", capabilities: ["plan"], requiresApproval: false },
|
|
94
|
+
{
|
|
95
|
+
type: "function",
|
|
96
|
+
function: {
|
|
97
|
+
name: "propose_plan",
|
|
98
|
+
description: "\u5C06\u4F60\u7684\u5B9E\u65BD\u8BA1\u5212\u5448\u73B0\u7ED9\u7528\u6237\u5BA1\u6279\u3002\u5728\u63A2\u7D22\u5145\u5206\u3001\u786E\u5B9A\u4E86\u4E00\u4E2A\u65B9\u6848\u540E\u4F7F\u7528\u6B64\u5DE5\u5177\uFF1A\u603B\u7ED3\u4F60\u5C06\u66F4\u6539\u4EC0\u4E48\u3001\u5728\u54EA\u4E9B\u6587\u4EF6\u4E2D\u3001\u4EE5\u53CA\u4F60\u5C06\u5982\u4F55\u9A8C\u8BC1\u5B83\u3002\u5982\u679C\u88AB\u6279\u51C6\uFF0C\u4F1A\u8BDD\u5C06\u9000\u51FA\u53EA\u8BFB\u8BA1\u5212\u6A21\u5F0F\uFF0C\u4F60\u6765\u5B9E\u65BD\u8BA1\u5212\uFF1B\u5982\u679C\u88AB\u62D2\u7EDD\uFF0C\u4F7F\u7528\u7ED3\u679C\u4E2D\u7684\u53CD\u9988\u610F\u89C1\u4FEE\u6539\u8BA1\u5212\u3002\u4E0D\u8981\u5728\u8FD8\u6CA1\u6709\u63D0\u6848\u4E4B\u524D\u5C31\u63CF\u8FF0\u5B9E\u65BD\u6B65\u9AA4\u2014\u2014\u5148\u63D0\u6848\u3002",
|
|
99
|
+
parameters: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
plan: { type: "string", description: "\u5B8C\u6574\u7684\u5B9E\u65BD\u8BA1\u5212\u63CF\u8FF0\uFF08\u5305\u542B\u5C06\u66F4\u6539\u4EC0\u4E48\u3001\u5728\u54EA\u4E9B\u6587\u4EF6\u4E2D\u3001\u5982\u4F55\u9A8C\u8BC1\uFF09" }
|
|
103
|
+
},
|
|
104
|
+
required: ["plan"]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
interpretPlanReply,
|
|
112
|
+
isAffirmativeText,
|
|
113
|
+
isRejectionText,
|
|
114
|
+
registerPlanTool,
|
|
115
|
+
setPlanHandler
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/tools/plan.ts"],"sourcesContent":["import { ToolRegistry } from \"./ToolRegistry.js\";\nimport { Mode } from \"../permissions/PermissionEngine.js\";\n\n/**\n * propose_plan — 计划提案工具\n * 对齐 Python 版 tools/plan.py\n * 在 CLI 模式下通过 readline 审批实现\n */\n\n/**\n * 肯定回答(批准 / 授权):单词或句首。\n * 中文口语的「好 / 行 / 嗯」也要认:否则用户敲一个「好」,会被当成补充要求。\n */\nconst APPROVE_WORDS = new Set([\n \"y\",\n \"yes\",\n \"approve\",\n \"approved\",\n \"ok\",\n \"true\",\n \"a\",\n \"1\",\n \"好\",\n \"好的\",\n \"行\",\n \"嗯\",\n]);\nconst APPROVE_PREFIX = /^(yes|approve|同意|批准|允许|确定|可以|好的|确认)/;\n\n/** 明确的拒绝词(Web 端点「拒绝」按钮提交的就是 reject) */\nconst REJECT_WORDS = new Set([\n \"d\",\n \"n\",\n \"no\",\n \"reject\",\n \"rejected\",\n \"deny\",\n \"false\",\n \"0\",\n \"不\",\n \"不行\",\n \"不用\",\n \"拒绝\",\n \"不同意\",\n \"取消\",\n]);\n\n/** 是否肯定回答(批准计划 / 授权目录) */\nexport function isAffirmativeText(answer: string): boolean {\n const a = answer.trim().toLowerCase();\n if (!a) return false;\n if (APPROVE_WORDS.has(a)) return true;\n return APPROVE_PREFIX.test(a);\n}\n\n/**\n * 是否明确的拒绝:只有整句就是拒绝词才算。\n * 「不要改 A,改用 B」这类是修改意见,不能当成否决。\n */\nexport function isRejectionText(answer: string): boolean {\n const a = answer.trim().toLowerCase().replace(/[。.!!]$/, \"\");\n return REJECT_WORDS.has(a);\n}\n\n/**\n * 解析用户对计划提案的一句话回应。\n *\n * CLI 与 Web 共用同一套判定,避免两处词表各自漂移:\n * - approve:批准;\n * - reject:否决;\n * - revise:既没批准也没拒绝 → 视为补充要求,合并进计划后重新提案;\n * - empty:空输入(CLI 重新提示,不改变任何状态)。\n */\nexport type PlanReply =\n | { kind: \"approve\" }\n | { kind: \"reject\" }\n | { kind: \"revise\"; feedback: string }\n | { kind: \"empty\" };\n\nexport function interpretPlanReply(answer: string): PlanReply {\n const text = answer.trim();\n if (!text) return { kind: \"empty\" };\n if (isAffirmativeText(text)) return { kind: \"approve\" };\n if (isRejectionText(text)) return { kind: \"reject\" };\n return { kind: \"revise\", feedback: text };\n}\n\n/**\n * 提案审批结果。\n *\n * `revised` 表示「用户没有批准也没有拒绝,而是提出了补充要求」:把 feedback\n * 合并进原计划后重新提案即可,不要开始实施,也不要把它当成否决而放弃方案。\n */\nexport type PlanHandler = (plan: string) => Promise<{\n approved: boolean;\n mode?: string;\n feedback?: string;\n revised?: boolean;\n}>;\n\nlet _planHandler: PlanHandler | null = null;\n\nexport function setPlanHandler(handler: PlanHandler): void {\n _planHandler = handler;\n}\n\nfunction makeProposePlan() {\n return async function propose_plan(args: Record<string, unknown>) {\n const plan = String(args.plan ?? \"\");\n\n if (!plan.trim()) {\n return { approved: false, error: \"计划内容不能为空\" };\n }\n\n if (!_planHandler) {\n return { approved: false, error: \"当前环境不支持计划审批\" };\n }\n\n try {\n const result = await _planHandler(plan);\n if (result.approved) {\n return {\n approved: true,\n mode: result.mode ?? \"interactive\",\n note: \"计划已批准 — 现在开始实施\",\n };\n }\n /*\n * 用户提出补充要求(既没批准也没拒绝):这是「修订」,不是否决。\n * 明确告诉模型下一步该做什么——把反馈合并进计划、再次调用 propose_plan。\n * 否则模型容易把它当作拒绝而从零重来,或者直接开始动手。\n */\n if (result.revised) {\n return {\n approved: false,\n revised: true,\n feedback: result.feedback ?? \"用户要求补充修正计划\",\n note:\n \"用户没有批准,而是对计划提出了补充要求。请把这些要求合并进原计划\" +\n \"(保留仍然有效的部分),然后再次调用 propose_plan 提交修订后的计划,\" +\n \"不要开始实施,也不要从头重写整个方案。\",\n };\n }\n return {\n approved: false,\n feedback: result.feedback ?? \"计划被拒绝\",\n };\n } catch {\n return { approved: false, error: \"计划审批已取消\" };\n }\n };\n}\n\n/** 注册 propose_plan 工具 */\nexport function registerPlanTool(registry: ToolRegistry): void {\n const planFunc = makeProposePlan();\n\n registry.register(\n (args) => planFunc(args),\n { category: \"planning\", riskLevel: \"low\", capabilities: [\"plan\"], requiresApproval: false },\n {\n type: \"function\",\n function: {\n name: \"propose_plan\",\n description: \"将你的实施计划呈现给用户审批。在探索充分、确定了一个方案后使用此工具:总结你将更改什么、在哪些文件中、以及你将如何验证它。如果被批准,会话将退出只读计划模式,你来实施计划;如果被拒绝,使用结果中的反馈意见修改计划。不要在还没有提案之前就描述实施步骤——先提案。\",\n parameters: {\n type: \"object\",\n properties: {\n plan: { type: \"string\", description: \"完整的实施计划描述(包含将更改什么、在哪些文件中、如何验证)\" },\n },\n required: [\"plan\"],\n },\n },\n },\n );\n}\n"],"mappings":";AAaA,IAAM,gBAAgB,oBAAI,IAAI;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,iBAAiB;AAGvB,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,kBAAkB,QAAyB;AACzD,QAAM,IAAI,OAAO,KAAK,EAAE,YAAY;AACpC,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,cAAc,IAAI,CAAC,EAAG,QAAO;AACjC,SAAO,eAAe,KAAK,CAAC;AAC9B;AAMO,SAAS,gBAAgB,QAAyB;AACvD,QAAM,IAAI,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,WAAW,EAAE;AAC3D,SAAO,aAAa,IAAI,CAAC;AAC3B;AAiBO,SAAS,mBAAmB,QAA2B;AAC5D,QAAM,OAAO,OAAO,KAAK;AACzB,MAAI,CAAC,KAAM,QAAO,EAAE,MAAM,QAAQ;AAClC,MAAI,kBAAkB,IAAI,EAAG,QAAO,EAAE,MAAM,UAAU;AACtD,MAAI,gBAAgB,IAAI,EAAG,QAAO,EAAE,MAAM,SAAS;AACnD,SAAO,EAAE,MAAM,UAAU,UAAU,KAAK;AAC1C;AAeA,IAAI,eAAmC;AAEhC,SAAS,eAAe,SAA4B;AACzD,iBAAe;AACjB;AAEA,SAAS,kBAAkB;AACzB,SAAO,eAAe,aAAa,MAA+B;AAChE,UAAM,OAAO,OAAO,KAAK,QAAQ,EAAE;AAEnC,QAAI,CAAC,KAAK,KAAK,GAAG;AAChB,aAAO,EAAE,UAAU,OAAO,OAAO,mDAAW;AAAA,IAC9C;AAEA,QAAI,CAAC,cAAc;AACjB,aAAO,EAAE,UAAU,OAAO,OAAO,qEAAc;AAAA,IACjD;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,aAAa,IAAI;AACtC,UAAI,OAAO,UAAU;AACnB,eAAO;AAAA,UACL,UAAU;AAAA,UACV,MAAM,OAAO,QAAQ;AAAA,UACrB,MAAM;AAAA,QACR;AAAA,MACF;AAMA,UAAI,OAAO,SAAS;AAClB,eAAO;AAAA,UACL,UAAU;AAAA,UACV,SAAS;AAAA,UACT,UAAU,OAAO,YAAY;AAAA,UAC7B,MACE;AAAA,QAGJ;AAAA,MACF;AACA,aAAO;AAAA,QACL,UAAU;AAAA,QACV,UAAU,OAAO,YAAY;AAAA,MAC/B;AAAA,IACF,QAAQ;AACN,aAAO,EAAE,UAAU,OAAO,OAAO,6CAAU;AAAA,IAC7C;AAAA,EACF;AACF;AAGO,SAAS,iBAAiB,UAA8B;AAC7D,QAAM,WAAW,gBAAgB;AAEjC,WAAS;AAAA,IACP,CAAC,SAAS,SAAS,IAAI;AAAA,IACvB,EAAE,UAAU,YAAY,WAAW,OAAO,cAAc,CAAC,MAAM,GAAG,kBAAkB,MAAM;AAAA,IAC1F;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,QACR,MAAM;AAAA,QACN,aAAa;AAAA,QACb,YAAY;AAAA,UACV,MAAM;AAAA,UACN,YAAY;AAAA,YACV,MAAM,EAAE,MAAM,UAAU,aAAa,uLAAiC;AAAA,UACxE;AAAA,UACA,UAAU,CAAC,MAAM;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/tools/ripgrep.ts
|
|
31
|
+
var ripgrep_exports = {};
|
|
32
|
+
__export(ripgrep_exports, {
|
|
33
|
+
resetRipgrepCache: () => resetRipgrepCache,
|
|
34
|
+
resolveRipgrep: () => resolveRipgrep
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(ripgrep_exports);
|
|
37
|
+
var fs = __toESM(require("fs"), 1);
|
|
38
|
+
var os = __toESM(require("os"), 1);
|
|
39
|
+
var path2 = __toESM(require("path"), 1);
|
|
40
|
+
var import_node_module = require("module");
|
|
41
|
+
|
|
42
|
+
// src/tools/shellRuntime.ts
|
|
43
|
+
var path = __toESM(require("path"), 1);
|
|
44
|
+
var WINDOWS_SUFFIXES = [".exe", ".cmd", ".bat", ".com", ""];
|
|
45
|
+
function pathApi(platform) {
|
|
46
|
+
return platform === "win32" ? path.win32 : path.posix;
|
|
47
|
+
}
|
|
48
|
+
function joinPath(platform, ...parts) {
|
|
49
|
+
return pathApi(platform).join(...parts);
|
|
50
|
+
}
|
|
51
|
+
function findExecutable(name, ctx) {
|
|
52
|
+
if (name.includes("/") || name.includes("\\")) {
|
|
53
|
+
return ctx.fileExists(name) ? name : null;
|
|
54
|
+
}
|
|
55
|
+
const suffixes = ctx.platform === "win32" ? WINDOWS_SUFFIXES : [""];
|
|
56
|
+
const delimiter = ctx.platform === "win32" ? ";" : ":";
|
|
57
|
+
const dirs = (ctx.env.PATH ?? "").split(delimiter).filter((d) => d.length > 0);
|
|
58
|
+
for (const dir of dirs) {
|
|
59
|
+
for (const suffix of suffixes) {
|
|
60
|
+
const candidate = joinPath(ctx.platform, dir, name + suffix);
|
|
61
|
+
if (ctx.fileExists(candidate)) return candidate;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/tools/ripgrep.ts
|
|
68
|
+
var cached;
|
|
69
|
+
function extractBundledRipgrep() {
|
|
70
|
+
try {
|
|
71
|
+
const req = typeof require === "function" ? require : (0, import_node_module.createRequire)(path2.join(process.cwd(), "__zqagent__.js"));
|
|
72
|
+
const sea = req("node:sea");
|
|
73
|
+
if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
|
|
74
|
+
const asset = sea.getAsset("rg");
|
|
75
|
+
if (!asset) return null;
|
|
76
|
+
const dir = fs.mkdtempSync(path2.join(os.tmpdir(), "zqagent-rg-"));
|
|
77
|
+
const file = path2.join(dir, process.platform === "win32" ? "rg.exe" : "rg");
|
|
78
|
+
fs.writeFileSync(file, Buffer.from(asset));
|
|
79
|
+
fs.chmodSync(file, 493);
|
|
80
|
+
return file;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function resolveRipgrep() {
|
|
86
|
+
if (cached !== void 0) return cached;
|
|
87
|
+
const explicit = (process.env.ZQAGENT_RG ?? "").trim();
|
|
88
|
+
if (explicit && fs.existsSync(explicit)) {
|
|
89
|
+
cached = explicit;
|
|
90
|
+
return cached;
|
|
91
|
+
}
|
|
92
|
+
const bundled = extractBundledRipgrep();
|
|
93
|
+
if (bundled) {
|
|
94
|
+
cached = bundled;
|
|
95
|
+
return cached;
|
|
96
|
+
}
|
|
97
|
+
cached = findExecutable("rg", {
|
|
98
|
+
platform: process.platform,
|
|
99
|
+
env: process.env,
|
|
100
|
+
fileExists: (p) => {
|
|
101
|
+
try {
|
|
102
|
+
return fs.statSync(p).isFile();
|
|
103
|
+
} catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return cached;
|
|
109
|
+
}
|
|
110
|
+
function resetRipgrepCache() {
|
|
111
|
+
cached = void 0;
|
|
112
|
+
}
|
|
113
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
114
|
+
0 && (module.exports = {
|
|
115
|
+
resetRipgrepCache,
|
|
116
|
+
resolveRipgrep
|
|
117
|
+
});
|
|
118
|
+
//# sourceMappingURL=ripgrep.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/tools/ripgrep.ts","../../../src/tools/shellRuntime.ts"],"sourcesContent":["/**\n * ripgrep (rg) 定位\n *\n * 优先级:\n * 1. 环境变量 ZQAGENT_RG 显式指定的路径;\n * 2. SEA 单文件内嵌的 rg 资源(构建时由 build_cli_binary.mjs 作为 asset 打包,\n * 运行时用 node:sea 的 getAsset 释放到临时目录);\n * 3. PATH 上的 rg。\n * 都拿不到时返回 null,由调用方回退到纯 Node 扫描器(nodeGrep.ts)。\n */\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { findExecutable } from \"./shellRuntime.js\";\n\n/** 已解析结果缓存(null 表示确认没有,勿重复探测) */\nlet cached: string | null | undefined;\n\n/** 把 SEA 内嵌的 rg 资源释放到临时目录,返回可执行文件路径 */\nfunction extractBundledRipgrep(): string | null {\n try {\n // CJS(SEA 单文件)下 require 可用;ESM 构建下用 createRequire 兜底。\n // 以 cwd 为基准构造 createRequire,避免依赖 import.meta(CJS 产物里不稳定)。\n const req =\n typeof require === \"function\"\n ? require\n : createRequire(path.join(process.cwd(), \"__zqagent__.js\"));\n const sea = req(\"node:sea\") as\n | { isSea?: () => boolean; getAsset?: (key: string) => ArrayBuffer | undefined }\n | undefined;\n if (!sea?.isSea?.() || typeof sea.getAsset !== \"function\") return null;\n const asset = sea.getAsset(\"rg\");\n if (!asset) return null;\n const dir = fs.mkdtempSync(path.join(os.tmpdir(), \"zqagent-rg-\"));\n const file = path.join(dir, process.platform === \"win32\" ? \"rg.exe\" : \"rg\");\n fs.writeFileSync(file, Buffer.from(asset));\n fs.chmodSync(file, 0o755);\n return file;\n } catch {\n // 未内嵌 rg(如 npm 安装、普通 node 运行)或 API 不可用\n return null;\n }\n}\n\n/** 解析可用的 rg 路径;无则返回 null(结果带缓存) */\nexport function resolveRipgrep(): string | null {\n if (cached !== undefined) return cached;\n\n const explicit = (process.env.ZQAGENT_RG ?? \"\").trim();\n if (explicit && fs.existsSync(explicit)) {\n cached = explicit;\n return cached;\n }\n\n const bundled = extractBundledRipgrep();\n if (bundled) {\n cached = bundled;\n return cached;\n }\n\n cached = findExecutable(\"rg\", {\n platform: process.platform,\n env: process.env,\n fileExists: (p) => {\n try {\n return fs.statSync(p).isFile();\n } catch {\n return false;\n }\n },\n });\n return cached;\n}\n\n/** 测试用:清除解析缓存 */\nexport function resetRipgrepCache(): void {\n cached = undefined;\n}\n","/**\n * shell 运行时探测\n *\n * 决定 run_shell 用哪个 shell 执行命令,解决三件事:\n * - POSIX:沿用 Node 默认 /bin/sh,不改变既有语义;\n * - Windows:cmd.exe 没有 ls/grep/rm/cat,优先挑一个 POSIX 风格的 shell\n * (BusyBox-w32 / Git Bash / PowerShell),都没有时才回退到 %ComSpec%;\n * - 允许用 ZQAGENT_SHELL / ZQAGENT_BUSYBOX 显式覆盖,便于在装了非常规\n * shell(如内网自带的 busybox)的机器上接入。\n *\n * 本模块只做解析,不执行命令;platform / env / 文件探测均可注入,\n * 便于在 CI(Linux)上覆盖 Windows 分支。\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\n/** shell 家族,决定命令行参数怎么拼 */\nexport type ShellKind = \"sh\" | \"busybox\" | \"powershell\" | \"cmd\" | \"default\";\n\nexport interface ShellSpec {\n kind: ShellKind;\n /** 人类可读标签,用于工具描述 */\n label: string;\n /** 可执行文件路径;null 表示交给 Node 默认(POSIX /bin/sh 或 %ComSpec%) */\n file: string | null;\n /** 拼出实际 argv(含命令本身) */\n buildArgs: (command: string) => string[];\n}\n\nexport interface ResolveShellOptions {\n platform?: NodeJS.Platform;\n env?: NodeJS.ProcessEnv;\n /** 判断文件是否存在,便于测试注入 */\n fileExists?: (p: string) => boolean;\n /** 用于定位「产物同目录」的 busybox,默认 process.execPath */\n execPath?: string;\n}\n\nexport interface ProbeContext {\n platform: NodeJS.Platform;\n env: NodeJS.ProcessEnv;\n fileExists: (p: string) => boolean;\n}\n\n/** Windows 可执行文件后缀(按优先级) */\nconst WINDOWS_SUFFIXES = [\".exe\", \".cmd\", \".bat\", \".com\", \"\"];\n\nfunction pathApi(platform: NodeJS.Platform) {\n return platform === \"win32\" ? path.win32 : path.posix;\n}\n\nfunction defaultFileExists(p: string): boolean {\n try {\n return fs.statSync(p).isFile();\n } catch {\n return false;\n }\n}\n\n/** 按目标平台(而非宿主)拼路径 */\nfunction joinPath(platform: NodeJS.Platform, ...parts: string[]): string {\n return pathApi(platform).join(...parts);\n}\n\n/**\n * 在 PATH 上查找可执行文件(同步,不 spawn 进程)。\n * name 含路径分隔符时按路径直接判定。Windows 会按 WINDOWS_SUFFIXES 补后缀。\n */\nexport function findExecutable(name: string, ctx: ProbeContext): string | null {\n if (name.includes(\"/\") || name.includes(\"\\\\\")) {\n return ctx.fileExists(name) ? name : null;\n }\n const suffixes = ctx.platform === \"win32\" ? WINDOWS_SUFFIXES : [\"\"];\n const delimiter = ctx.platform === \"win32\" ? \";\" : \":\";\n const dirs = (ctx.env.PATH ?? \"\").split(delimiter).filter((d) => d.length > 0);\n for (const dir of dirs) {\n for (const suffix of suffixes) {\n const candidate = joinPath(ctx.platform, dir, name + suffix);\n if (ctx.fileExists(candidate)) return candidate;\n }\n }\n return null;\n}\n\n/** 按文件名推断 shell 家族 */\nfunction kindFromBasename(base: string): ShellKind {\n const b = base.toLowerCase().replace(/\\.(exe|cmd|bat|com)$/, \"\");\n if (b === \"busybox\") return \"busybox\";\n if (b === \"powershell\" || b === \"pwsh\") return \"powershell\";\n if (b === \"cmd\") return \"cmd\";\n // sh/bash/dash/zsh/ash 以及不认识的名字(如 busybox 的 sh.exe 符号链接)都按 sh 处理\n return \"sh\";\n}\n\n/**\n * busybox 按 argv[0] 决定 applet:`busybox.exe` 需要显式给 applet 名\n * (`busybox.exe sh -c \"...\"`),而重命名/硬链接出来的 `sh.exe` 本身就是 ash,\n * 多传一个 \"sh\" 会被当成脚本文件名。\n */\nfunction busyboxArgs(file: string | null, command: string): string[] {\n const base = (file ? path.win32.basename(file) : \"busybox\")\n .toLowerCase()\n .replace(/\\.(exe|cmd|bat|com)$/, \"\");\n return base === \"busybox\" ? [\"sh\", \"-c\", command] : [\"-c\", command];\n}\n\nfunction argsFor(kind: ShellKind, command: string): string[] {\n switch (kind) {\n case \"busybox\":\n return [\"sh\", \"-c\", command];\n case \"powershell\":\n return [\"-NoProfile\", \"-NonInteractive\", \"-Command\", command];\n case \"cmd\":\n return [\"/d\", \"/s\", \"/c\", command];\n default:\n return [\"-c\", command];\n }\n}\n\nfunction makeSpec(kind: ShellKind, file: string | null, label: string): ShellSpec {\n return {\n kind,\n file,\n label,\n buildArgs: (command) =>\n kind === \"busybox\" ? busyboxArgs(file, command) : argsFor(kind, command),\n };\n}\n\nfunction defaultLabelFor(kind: ShellKind): string {\n switch (kind) {\n case \"busybox\":\n return \"busybox ash\";\n case \"powershell\":\n return \"PowerShell\";\n case \"cmd\":\n return \"cmd.exe\";\n case \"default\":\n return \"系统默认 shell\";\n default:\n return \"sh\";\n }\n}\n\n/**\n * 查找 BusyBox-w32。\n * 显式 ZQAGENT_BUSYBOX 优先;否则看 CLI 产物同目录及其 bin/ 子目录\n * (兼容 SEA 单文件、npm 安装与桌面 sidecar 三种布局,其中 sh.exe 是\n * busybox.exe 的重命名副本 —— busybox 按 argv[0] 决定 applet)。\n */\nfunction findBusybox(\n ctx: ProbeContext,\n execPath: string,\n): string | null {\n const explicit = (ctx.env.ZQAGENT_BUSYBOX ?? \"\").trim();\n if (explicit) return ctx.fileExists(explicit) ? explicit : null;\n\n const exeDir = pathApi(ctx.platform).dirname(execPath);\n const candidates = [\n joinPath(ctx.platform, exeDir, \"busybox.exe\"),\n joinPath(ctx.platform, exeDir, \"sh.exe\"),\n joinPath(ctx.platform, exeDir, \"bin\", \"busybox.exe\"),\n joinPath(ctx.platform, exeDir, \"bin\", \"sh.exe\"),\n ];\n for (const candidate of candidates) {\n if (ctx.fileExists(candidate)) return candidate;\n }\n return null;\n}\n\n/** 解析 ZQAGENT_SHELL 的显式取值(关键字或路径) */\nfunction resolveRequested(requested: string, ctx: ProbeContext, execPath: string): ShellSpec {\n const lower = requested.toLowerCase();\n\n if (lower === \"cmd\") {\n const file =\n findExecutable(\"cmd\", ctx) ?? ctx.env.ComSpec ?? ctx.env.COMSPEC ?? \"cmd.exe\";\n return makeSpec(\"cmd\", file, \"cmd.exe\");\n }\n if (lower === \"powershell\" || lower === \"pwsh\") {\n const file =\n findExecutable(lower, ctx) ?? findExecutable(\"powershell\", ctx) ?? `${lower}.exe`;\n return makeSpec(\"powershell\", file, \"PowerShell\");\n }\n if (lower === \"busybox\") {\n const found = findBusybox(ctx, execPath) ?? findExecutable(\"busybox\", ctx);\n return makeSpec(\"busybox\", found ?? requested, \"busybox ash\");\n }\n if ([\"sh\", \"bash\", \"dash\", \"zsh\", \"ash\"].includes(lower)) {\n return makeSpec(\"sh\", findExecutable(lower, ctx) ?? lower, lower);\n }\n\n // 视为路径(可能含空格,如 C:\\Program Files\\Git\\bin\\bash.exe)\n const base = pathApi(ctx.platform).basename(requested);\n const kind = kindFromBasename(base);\n return makeSpec(kind, requested, base || defaultLabelFor(kind));\n}\n\n/**\n * 解析当前应使用的 shell。\n * 返回值 file === null 时表示「不显式指定」,由 Node 走平台默认 shell\n * (POSIX /bin/sh / Windows %ComSpec%),即改造前的行为。\n */\nexport function resolveShellSpec(opts: ResolveShellOptions = {}): ShellSpec {\n const platform = opts.platform ?? process.platform;\n const env = opts.env ?? process.env;\n const execPath = opts.execPath ?? process.execPath;\n const ctx: ProbeContext = {\n platform,\n env,\n fileExists: opts.fileExists ?? defaultFileExists,\n };\n\n const requested = (env.ZQAGENT_SHELL ?? \"\").trim();\n if (requested && requested.toLowerCase() !== \"auto\") {\n return resolveRequested(requested, ctx, execPath);\n }\n\n // 非 Windows:保持 Node 默认(不改变既有行为)\n if (platform !== \"win32\") {\n return makeSpec(\"default\", null, \"/bin/sh\");\n }\n\n // Windows:优先 POSIX 风格 shell —— cmd.exe 缺 ls/grep/rm/cat,模型产出的\n // POSIX 命令在它下面几乎全废。\n const busybox = findBusybox(ctx, execPath);\n if (busybox) return makeSpec(\"busybox\", busybox, \"busybox ash (POSIX)\");\n\n const bash = findExecutable(\"bash\", ctx);\n if (bash) return makeSpec(\"sh\", bash, \"bash (Git for Windows)\");\n\n // PowerShell 排在 cmd 之前:它有 ls/cat/rm/cp/mv/pwd 别名,更接近模型习惯\n const shell = findExecutable(\"pwsh\", ctx) ?? findExecutable(\"powershell\", ctx);\n if (shell) return makeSpec(\"powershell\", shell, \"PowerShell\");\n\n return makeSpec(\"default\", null, env.ComSpec ?? env.COMSPEC ?? \"cmd.exe\");\n}\n\n/**\n * 为命令套上「编码设置 + 退出码传播」外壳,按 shell 家族区分。\n *\n * - PowerShell:先设置控制台输出编码为 UTF-8(同一次调用内即对后续命令生效,\n * 规避 `chcp 65001` 要等下一次调用才生效的问题),命令结束后若存在\n * `$LASTEXITCODE` 就以它作为进程退出码 —— 否则 PowerShell 对 `cmd /c ...`\n * 这类原生命令一律返回 0,工具拿到的 exit_code 不可信。\n * - cmd.exe(含 Windows 下未探测到其它 shell 时的 default):`chcp 65001` 切换\n * 代码页,避免中文被压成 `?`。\n * - POSIX sh / busybox:现代环境默认 UTF-8,不改动。\n */\nexport function wrapCommand(\n kind: ShellKind,\n command: string,\n platform: NodeJS.Platform = process.platform,\n): string {\n if (kind === \"powershell\") {\n return (\n \"$OutputEncoding = [Console]::OutputEncoding = [Text.Encoding]::UTF8; \" +\n command +\n \"\\nif ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }\"\n );\n }\n if (kind === \"cmd\" || (kind === \"default\" && platform === \"win32\")) {\n return `chcp 65001>nul & ${command}`;\n }\n return command;\n}\n\n/** 生成给模型的运行环境提示,追加到 run_shell 描述里 */\nexport function describeShell(spec: ShellSpec, platform: NodeJS.Platform): string {\n if (spec.kind === \"sh\" || spec.kind === \"busybox\") {\n return `当前平台 ${platform},shell=${spec.label}:请使用 POSIX 风格命令。`;\n }\n if (spec.kind === \"powershell\") {\n return (\n `当前平台 ${platform},shell=PowerShell:ls/cat/rm/cp/mv/pwd 等别名可用,` +\n `也可用 cmdlet;路径分隔符为 \"\\\\\"。`\n );\n }\n if (spec.kind === \"default\" && platform !== \"win32\") {\n return `当前平台 ${platform},shell=${spec.label}:请使用 POSIX 风格命令。`;\n }\n return (\n `当前平台 ${platform},shell=${spec.label}:请使用 Windows 命令` +\n `(dir/type/del/copy/move/findstr),多条命令用 & 或 && 连接;` +\n `不要使用 ls/cat/rm/grep 等 POSIX 命令。`\n );\n}\n\n/**\n * 解析命令里的「切目录」意图,返回目录与剩余命令。\n *\n * 识别 POSIX 的 `cd dir`、PowerShell 的 `Set-Location`/`sl`、cmd 的 `cd /d dir`,\n * 以及 `pushd`/`Push-Location`(只跟踪目标目录;popd 的栈语义不在此处理)。\n * 带 `&&` 时(如 `cd /d D:\\x && dir`)只把切目录部分交给 cwd 跟踪,剩余命令\n * 在新目录下执行 —— 否则目录变更只影响那一个子 shell,无法跨调用保持。\n *\n * target 为 null 时调用方应原样执行命令(未识别到切目录意图)。\n */\nexport function parseCdChain(\n command: string,\n opts: { platform?: NodeJS.Platform } = {},\n): { target: string | null; rest: string | null } {\n const platform = opts.platform ?? process.platform;\n const trimmed = command.trim();\n\n const chain = /^([\\s\\S]*?)\\s*&&\\s*([\\s\\S]+)$/.exec(trimmed);\n const head = (chain ? chain[1] : trimmed).trim();\n const rest = chain ? chain[2].trim() : null;\n\n const m = /^(?:cd|set-location|sl|pushd|push-location)\\b\\s*([\\s\\S]*)$/i.exec(head);\n if (!m) return { target: null, rest: null };\n\n let target = m[1].trim();\n // PowerShell 显式参数与 cmd 的盘符切换\n target = target.replace(/^-(?:path|literalpath)\\s+/i, \"\");\n if (platform === \"win32\") target = target.replace(/^\\/d\\s+/i, \"\");\n // 去引号\n target = target.replace(/^\"(.*)\"$/, \"$1\").replace(/^'(.*)'$/, \"$1\").trim();\n\n // 裸 `cd` / `cd -` 不跟踪(无法确定目标目录)\n if (!target || target === \"-\") return { target: null, rest: null };\n return { target, rest };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,SAAoB;AACpB,SAAoB;AACpB,IAAAA,QAAsB;AACtB,yBAA8B;;;ACC9B,WAAsB;AA+BtB,IAAM,mBAAmB,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,EAAE;AAE5D,SAAS,QAAQ,UAA2B;AAC1C,SAAO,aAAa,UAAe,aAAa;AAClD;AAWA,SAAS,SAAS,aAA8B,OAAyB;AACvE,SAAO,QAAQ,QAAQ,EAAE,KAAK,GAAG,KAAK;AACxC;AAMO,SAAS,eAAe,MAAc,KAAkC;AAC7E,MAAI,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,IAAI,GAAG;AAC7C,WAAO,IAAI,WAAW,IAAI,IAAI,OAAO;AAAA,EACvC;AACA,QAAM,WAAW,IAAI,aAAa,UAAU,mBAAmB,CAAC,EAAE;AAClE,QAAM,YAAY,IAAI,aAAa,UAAU,MAAM;AACnD,QAAM,QAAQ,IAAI,IAAI,QAAQ,IAAI,MAAM,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC7E,aAAW,OAAO,MAAM;AACtB,eAAW,UAAU,UAAU;AAC7B,YAAM,YAAY,SAAS,IAAI,UAAU,KAAK,OAAO,MAAM;AAC3D,UAAI,IAAI,WAAW,SAAS,EAAG,QAAO;AAAA,IACxC;AAAA,EACF;AACA,SAAO;AACT;;;ADjEA,IAAI;AAGJ,SAAS,wBAAuC;AAC9C,MAAI;AAGF,UAAM,MACJ,OAAO,YAAY,aACf,cACA,kCAAmB,WAAK,QAAQ,IAAI,GAAG,gBAAgB,CAAC;AAC9D,UAAM,MAAM,IAAI,UAAU;AAG1B,QAAI,CAAC,KAAK,QAAQ,KAAK,OAAO,IAAI,aAAa,WAAY,QAAO;AAClE,UAAM,QAAQ,IAAI,SAAS,IAAI;AAC/B,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,MAAS,eAAiB,WAAQ,UAAO,GAAG,aAAa,CAAC;AAChE,UAAM,OAAY,WAAK,KAAK,QAAQ,aAAa,UAAU,WAAW,IAAI;AAC1E,IAAG,iBAAc,MAAM,OAAO,KAAK,KAAK,CAAC;AACzC,IAAG,aAAU,MAAM,GAAK;AACxB,WAAO;AAAA,EACT,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,iBAAgC;AAC9C,MAAI,WAAW,OAAW,QAAO;AAEjC,QAAM,YAAY,QAAQ,IAAI,cAAc,IAAI,KAAK;AACrD,MAAI,YAAe,cAAW,QAAQ,GAAG;AACvC,aAAS;AACT,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,sBAAsB;AACtC,MAAI,SAAS;AACX,aAAS;AACT,WAAO;AAAA,EACT;AAEA,WAAS,eAAe,MAAM;AAAA,IAC5B,UAAU,QAAQ;AAAA,IAClB,KAAK,QAAQ;AAAA,IACb,YAAY,CAAC,MAAM;AACjB,UAAI;AACF,eAAU,YAAS,CAAC,EAAE,OAAO;AAAA,MAC/B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAGO,SAAS,oBAA0B;AACxC,WAAS;AACX;","names":["path"]}
|