deepseeker-code 1.0.57 → 1.0.59
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/dist/cli.mjs +1306 -524
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -119,13 +119,16 @@ var init_strings = __esm({
|
|
|
119
119
|
planEditTitle: "\u270F\uFE0F \u7F16\u8F91\u65B9\u6848",
|
|
120
120
|
planEditHint: "Enter \u6309\u6B64\u65B9\u6848\u6267\u884C \xB7 Esc \u53D6\u6D88\u56DE\u5230\u9009\u9879",
|
|
121
121
|
planKeepPlanning: "\u270B \u5DF2\u7559\u5728\u8BA1\u5212\u6A21\u5F0F\uFF1A\u7EE7\u7EED\u8F93\u5165\u8865\u5145\u8981\u6C42\uFF0C\u6A21\u578B\u5C06\u4FEE\u8BA2\u65B9\u6848\u540E\u518D\u6B21\u63D0\u4EA4\uFF08/plan \u53EF\u9000\u51FA\uFF09\u3002",
|
|
122
|
+
planNoProposal: "\u2139\uFE0F \u672C\u8F6E\u672A\u6536\u5230\u65B9\u6848\uFF08\u6A21\u578B\u672A\u63D0\u4EA4 exit_plan_mode\uFF09\uFF0C\u4ECD\u5728\u8BA1\u5212\u6A21\u5F0F\uFF1A\u53EF\u7EE7\u7EED\u63CF\u8FF0\u9700\u6C42\uFF0C\u6216 /plan \u5173\u95ED\u8BA1\u5212\u6A21\u5F0F\u3002",
|
|
122
123
|
planEditEmpty: "\u65B9\u6848\u4E0D\u80FD\u4E3A\u7A7A",
|
|
123
124
|
planAutoExecute: "\u26A1 \u5DF2\u8FDB\u5165\u81EA\u52A8\u6267\u884C\uFF1A\u5B9E\u73B0\u9636\u6BB5\u5DE5\u5177\u5C06\u514D\u5BA1\u6279\u76F4\u63A5\u8FD0\u884C\u3002",
|
|
124
125
|
inputBadgePlan: "plan mode on\uFF08\u53EA\u8BFB\u8C03\u7814 \u2192 \u65B9\u6848\u5BA1\u6279 \u2192 \u5B9E\u73B0\uFF09",
|
|
125
126
|
inputBadgeAuto: "auto-accept edits on",
|
|
126
127
|
approvalTitle: "\u{1F510} \u64CD\u4F5C\u5BA1\u6279",
|
|
127
128
|
approvalPrompt: "\u2191\u2193 \u9009\u62E9\u540E Enter\uFF08Esc \u62D2\u7EDD\uFF09\uFF1A\u5141\u8BB8\u672C\u6B21 / \u603B\u662F\u5141\u8BB8\uFF08\u5199\u6301\u4E45\u89C4\u5219\uFF09/ \u62D2\u7EDD\u3002",
|
|
129
|
+
approvalFullHint: "\uFF08\u6309 f \u67E5\u770B/\u6536\u8D77\u5B8C\u6574\u5185\u5BB9\uFF09",
|
|
128
130
|
approvalAllow: "\u5141\u8BB8\u672C\u6B21",
|
|
131
|
+
approvalAllowSession: "\u672C\u4F1A\u8BDD\u5185\u5141\u8BB8\uFF08\u4E0D\u843D\u76D8\uFF09",
|
|
129
132
|
approvalAllowAlways: "\u603B\u662F\u5141\u8BB8\uFF08\u6B64\u9879\u76EE\uFF09",
|
|
130
133
|
approvalDeny: "\u62D2\u7EDD",
|
|
131
134
|
errAborted: "\u5DF2\u4E2D\u6B62\u5F53\u524D\u8F6E\u3002",
|
|
@@ -191,6 +194,17 @@ ${dim} \u8FD0\u884C npm i -g deepseeker-code@latest \u66F4\u65B0${reset}
|
|
|
191
194
|
forkEmpty: "\uFF08\u5F53\u524D\u4F1A\u8BDD\u6682\u65E0\u53EF\u5206\u53C9\u7684\u68C0\u67E5\u70B9\uFF09",
|
|
192
195
|
forkDone: (id, roundNo) => `\u5DF2\u4ECE\u7B2C ${roundNo} \u8F6E\u5206\u53C9 \u2192 \u65B0\u4F1A\u8BDD ${id}\uFF08\u5DF2\u8F7D\u5165\uFF0C\u540E\u7EED\u5BF9\u8BDD\u5199\u5165\u65B0\u4F1A\u8BDD\uFF0C\u539F\u4F1A\u8BDD\u4E0D\u53D8\uFF09`,
|
|
193
196
|
forkFailed: (e) => `\u5206\u53C9\u5931\u8D25\uFF1A${e}`,
|
|
197
|
+
cmdUndo: "\u64A4\u9500\u6700\u8FD1\u4E00\u6B21\u6587\u4EF6\u6539\u52A8\uFF08LIFO \u9010\u6B65\u56DE\u8D70\uFF1B\u56DE\u9000\u524D\u81EA\u52A8\u5907\u4EFD\uFF0C\u53EF\u9006\uFF09",
|
|
198
|
+
undoNoSession: "\u5F53\u524D\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD\uFF0C\u65E0\u53EF\u56DE\u9000\u9879\u3002",
|
|
199
|
+
cmdRewind: "\u6309\u68C0\u67E5\u70B9\u9009\u62E9\u56DE\u9000\u6587\u4EF6\u6539\u52A8\uFF08\u2191\u2193 \u9009\u62E9\uFF0CEnter \u56DE\u9000\uFF09",
|
|
200
|
+
rewindTitle: "\u23EA \u56DE\u9000\u6587\u4EF6\u6539\u52A8\uFF08\u2191\u2193 \u9009\u62E9 \xB7 Enter \u56DE\u9000 \xB7 Esc \u53D6\u6D88\uFF09",
|
|
201
|
+
rewindPrompt: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u56DE\u9000 \xB7 Esc \u53D6\u6D88",
|
|
202
|
+
rewindNoSession: "\u5F53\u524D\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD\uFF0C\u65E0\u53EF\u56DE\u9000\u9879\u3002",
|
|
203
|
+
rewindEmpty: "\u5F53\u524D\u4F1A\u8BDD\u6CA1\u6709\u53EF\u56DE\u9000\u7684\u6587\u4EF6\u6539\u52A8\u3002",
|
|
204
|
+
rewindFail: (e) => `\u56DE\u9000\u5931\u8D25\uFF1A${e}`,
|
|
205
|
+
cmdDiff: "\u672C run \u6587\u4EF6\u6539\u52A8\u7EA2\u7EFF\u6C47\u603B\uFF08\u5907\u4EFD vs \u5F53\u524D\u78C1\u76D8\u7684\u51C0\u53D8\u5316\uFF09",
|
|
206
|
+
diffTitle: "\u{1F4C4} \u672C run \u6587\u4EF6\u6539\u52A8\u6C47\u603B",
|
|
207
|
+
diffEmpty: "\u672C run \u6CA1\u6709\u6587\u4EF6\u6539\u52A8\u8BB0\u5F55\uFF08\u6539\u5B8C\u4E00\u8F6E\u540E\u518D\u8BD5 /diff\uFF09\u3002",
|
|
194
208
|
modelUsage: (current) => `\u5F53\u524D\u6A21\u578B\uFF1A${current || "\u9ED8\u8BA4\uFF08DEEP_SEEK_MODEL\uFF09"}
|
|
195
209
|
\u7528\u6CD5\uFF1A/model <\u6A21\u578Bid> \u76F4\u8F93\u5207\u6362\uFF0C\u6216 /switch \u5F39\u51FA\u5019\u9009\u9009\u62E9\u5668`,
|
|
196
210
|
modelPickerTitle: (current) => `\u{1F9E0} \u9009\u62E9\u6A21\u578B\uFF08\u5F53\u524D\uFF1A${current || "\u9ED8\u8BA4"}\uFF09`,
|
|
@@ -263,6 +277,9 @@ ${s}`,
|
|
|
263
277
|
"/sessions \u2014 \u9009\u62E9\u5E76\u8F7D\u5165\u5386\u53F2\u4F1A\u8BDD\uFF08\u7EED\u63A5\u5BF9\u8BDD\uFF09",
|
|
264
278
|
"/new \u2014 \u5F00\u542F\u65B0\u4F1A\u8BDD\uFF08\u65E7\u4F1A\u8BDD\u4FDD\u7559\uFF0C/sessions \u53EF\u627E\u56DE\uFF09",
|
|
265
279
|
"/fork \u2014 \u4ECE\u5F53\u524D\u4F1A\u8BDD\u7684\u67D0\u8F6E\u56DE\u590D\u5904\u5206\u53C9\u51FA\u65B0\u4F1A\u8BDD",
|
|
280
|
+
"/undo \u2014 \u64A4\u9500\u6700\u8FD1\u4E00\u6B21\u6587\u4EF6\u6539\u52A8\uFF08\u81EA\u52A8\u5907\u4EFD\uFF0C\u53EF\u9006\uFF09",
|
|
281
|
+
"/rewind \u2014 \u6309\u68C0\u67E5\u70B9\u9009\u62E9\u56DE\u9000\u6587\u4EF6\u6539\u52A8\uFF08\u2191\u2193 \u9009\u62E9\uFF09",
|
|
282
|
+
"/diff \u2014 \u672C run \u6587\u4EF6\u6539\u52A8\u7EA2\u7EFF\u6C47\u603B",
|
|
266
283
|
"/trust \u2014 \u7BA1\u7406\u5DF2\u4FE1\u4EFB\u76EE\u5F55\uFF08\u9879\u76EE\u7EA7 hooks/skills \u7B49\u4EC5\u5728\u4FE1\u4EFB\u76EE\u5F55\u52A0\u8F7D\uFF1BCI \u7528 --trust \u663E\u5F0F\u4FE1\u4EFB\uFF09",
|
|
267
284
|
"Ctrl+C \u9000\u51FA \xB7 Esc \u4E2D\u6B62/\u6E05\u8F93\u5165 \xB7 Ctrl+G \u4E2D\u6B62 \xB7 Ctrl+T \u5C55\u5F00/\u6536\u8D77\u601D\u8003"
|
|
268
285
|
].join("\n"),
|
|
@@ -301,13 +318,16 @@ ${s}`,
|
|
|
301
318
|
planEditTitle: "\u270F\uFE0F Edit plan",
|
|
302
319
|
planEditHint: "Enter to implement with this plan \xB7 Esc back to options",
|
|
303
320
|
planKeepPlanning: "\u270B Staying in plan mode: add feedback and the plan will be revised and resubmitted (/plan to exit).",
|
|
321
|
+
planNoProposal: "\u2139\uFE0F No plan was proposed this turn (exit_plan_mode not called) \u2014 still in plan mode: keep describing the task, or /plan to exit.",
|
|
304
322
|
planEditEmpty: "Plan cannot be empty",
|
|
305
323
|
planAutoExecute: "\u26A1 Auto-run: implementation tools will execute without approval.",
|
|
306
324
|
inputBadgePlan: "plan mode on (read-only research \u2192 approve \u2192 implement)",
|
|
307
325
|
inputBadgeAuto: "auto-accept edits on",
|
|
308
326
|
approvalTitle: "\u{1F510} Action approval",
|
|
309
327
|
approvalPrompt: "\u2191\u2193 then Enter (Esc to deny): allow once / always allow (persist rule) / deny.",
|
|
328
|
+
approvalFullHint: "(f to view/collapse full content)",
|
|
310
329
|
approvalAllow: "Allow once",
|
|
330
|
+
approvalAllowSession: "Allow for this session (in-memory)",
|
|
311
331
|
approvalAllowAlways: "Always allow (this project)",
|
|
312
332
|
approvalDeny: "Deny",
|
|
313
333
|
errAborted: "Aborted current turn.",
|
|
@@ -373,6 +393,17 @@ ${dim} Run npm i -g deepseeker-code@latest to update${reset}
|
|
|
373
393
|
forkEmpty: "(no checkpoints to fork from in this session)",
|
|
374
394
|
forkDone: (id, roundNo) => `Forked from round ${roundNo} \u2192 new session ${id} (loaded; new messages go to the fork, source untouched)`,
|
|
375
395
|
forkFailed: (e) => `Fork failed: ${e}`,
|
|
396
|
+
cmdUndo: "Undo the most recent file change (LIFO, repeatable; reverse-backed-up, reversible)",
|
|
397
|
+
undoNoSession: "No active session \u2014 nothing to undo.",
|
|
398
|
+
cmdRewind: "Rewind file changes \u2014 pick a checkpoint from the list",
|
|
399
|
+
rewindTitle: "\u23EA Rewind file changes (\u2191\u2193 select \xB7 Enter rewind \xB7 Esc cancel)",
|
|
400
|
+
rewindPrompt: "\u2191\u2193 select \xB7 Enter rewind \xB7 Esc cancel",
|
|
401
|
+
rewindNoSession: "No active session \u2014 nothing to rewind.",
|
|
402
|
+
rewindEmpty: "No rewindable file changes in this session.",
|
|
403
|
+
rewindFail: (e) => `Rewind failed: ${e}`,
|
|
404
|
+
cmdDiff: "Red/green summary of file changes in this run (backup vs current disk)",
|
|
405
|
+
diffTitle: "\u{1F4C4} File changes in this run",
|
|
406
|
+
diffEmpty: "No file changes recorded in this run (try /diff after a turn with edits).",
|
|
376
407
|
modelUsage: (current) => `Current model: ${current || "default (DEEP_SEEK_MODEL)"}
|
|
377
408
|
Usage: /model <model-id> to switch directly, or /switch for the picker`,
|
|
378
409
|
modelPickerTitle: (current) => `\u{1F9E0} Pick a model (current: ${current || "default"})`,
|
|
@@ -445,6 +476,9 @@ ${s}`,
|
|
|
445
476
|
"/sessions \u2014 Pick a past session to resume",
|
|
446
477
|
"/new \u2014 Start a new session (old ones remain via /sessions)",
|
|
447
478
|
"/fork \u2014 Fork the current session from a past round",
|
|
479
|
+
"/undo \u2014 Undo the most recent file change (auto-backed-up, reversible)",
|
|
480
|
+
"/rewind \u2014 Rewind file changes \u2014 pick a checkpoint from the list",
|
|
481
|
+
"/diff \u2014 Red/green summary of file changes in this run",
|
|
448
482
|
"/trust \u2014 Manage trusted dirs (project hooks/skills load only when trusted; CI uses --trust)",
|
|
449
483
|
"Ctrl+C exit \xB7 Esc abort/clear input \xB7 Ctrl+G abort \xB7 Ctrl+T toggle thinking"
|
|
450
484
|
].join("\n"),
|
|
@@ -465,7 +499,7 @@ ${s}`,
|
|
|
465
499
|
S = new Proxy({}, {
|
|
466
500
|
get: (_t, k) => typeof k === "string" ? STRINGS[locale][k] : void 0
|
|
467
501
|
});
|
|
468
|
-
LOCAL_COMMAND_NAMES = ["help", "status", "plan", "auto", "model", "switch", "thinking", "lang", "output-style", "new", "sessions", "fork", "usage", "budget", "context", "permissions", "mcp", "hooks", "trust", "debug", "clear", "exit"];
|
|
502
|
+
LOCAL_COMMAND_NAMES = ["help", "status", "plan", "auto", "model", "switch", "thinking", "lang", "output-style", "new", "sessions", "fork", "undo", "usage", "rewind", "diff", "budget", "context", "permissions", "mcp", "hooks", "trust", "debug", "clear", "exit"];
|
|
469
503
|
}
|
|
470
504
|
});
|
|
471
505
|
|
|
@@ -474,18 +508,31 @@ var config_exports = {};
|
|
|
474
508
|
__export(config_exports, {
|
|
475
509
|
ENGINE_OVERRIDE_KEYS: () => ENGINE_OVERRIDE_KEYS,
|
|
476
510
|
ENV_SOURCES: () => ENV_SOURCES,
|
|
511
|
+
MODEL_WINDOWS: () => MODEL_WINDOWS,
|
|
477
512
|
appConfig: () => appConfig,
|
|
478
|
-
configSources: () => configSources
|
|
513
|
+
configSources: () => configSources,
|
|
514
|
+
deriveWorkspaceKey: () => deriveWorkspaceKey,
|
|
515
|
+
resolveModelWindow: () => resolveModelWindow
|
|
479
516
|
});
|
|
480
517
|
import path3 from "path";
|
|
481
518
|
import os3 from "os";
|
|
482
519
|
import { readFileSync as readFileSync3 } from "fs";
|
|
483
520
|
import { createHash } from "crypto";
|
|
484
|
-
var _DataDir, appConfig, ENV_SOURCES, configSources, ENGINE_OVERRIDE_VALIDATORS, ENGINE_OVERRIDE_KEYS, applyEngineOverrides;
|
|
521
|
+
var _DataDir, deriveWorkspaceKey, appConfig, MODEL_WINDOWS, resolveModelWindow, ENV_SOURCES, configSources, ENGINE_OVERRIDE_VALIDATORS, ENGINE_OVERRIDE_KEYS, applyEngineOverrides;
|
|
485
522
|
var init_config = __esm({
|
|
486
523
|
"../core/src/config/index.ts"() {
|
|
487
524
|
"use strict";
|
|
488
525
|
_DataDir = process.env.DEEPSEEKER_CODE_DATA_DIR ? path3.resolve(process.env.DEEPSEEKER_CODE_DATA_DIR) : path3.join(os3.homedir(), ".deepseeker-code");
|
|
526
|
+
deriveWorkspaceKey = (dir) => {
|
|
527
|
+
let cwd = path3.resolve(dir).replace(/\\/g, "/");
|
|
528
|
+
if (/^[a-z]:/i.test(cwd)) {
|
|
529
|
+
cwd = cwd.charAt(0).toUpperCase() + cwd.slice(1);
|
|
530
|
+
}
|
|
531
|
+
const currentFolder = path3.basename(cwd);
|
|
532
|
+
const parentFolder = path3.basename(path3.dirname(cwd));
|
|
533
|
+
const hash = createHash("sha256").update(cwd).digest("hex").slice(0, 8);
|
|
534
|
+
return `${parentFolder}-${currentFolder}-${hash}`;
|
|
535
|
+
};
|
|
489
536
|
appConfig = {
|
|
490
537
|
dataDir: _DataDir,
|
|
491
538
|
maxReasoningRounds: 3,
|
|
@@ -513,16 +560,7 @@ var init_config = __esm({
|
|
|
513
560
|
* env RUN_COMMAND_AUTO_BG_MS 覆盖。勿把 IDLE 调到高于 DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS
|
|
514
561
|
* (空闲分支到期会产出降级消息喂流,不会饿死 collectToolResult 的 idle 熔断)。 */
|
|
515
562
|
runCommandAutoBgMs: Number(process.env.RUN_COMMAND_AUTO_BG_MS) || 12e4,
|
|
516
|
-
userWorkspaceDir: (()
|
|
517
|
-
let cwd = process.cwd().replace(/\\/g, "/");
|
|
518
|
-
if (/^[a-z]:/i.test(cwd)) {
|
|
519
|
-
cwd = cwd.charAt(0).toUpperCase() + cwd.slice(1);
|
|
520
|
-
}
|
|
521
|
-
const currentFolder = path3.basename(cwd);
|
|
522
|
-
const parentFolder = path3.basename(path3.dirname(cwd));
|
|
523
|
-
const hash = createHash("sha256").update(cwd).digest("hex").slice(0, 8);
|
|
524
|
-
return `${parentFolder}-${currentFolder}-${hash}`;
|
|
525
|
-
})(),
|
|
563
|
+
userWorkspaceDir: deriveWorkspaceKey(process.cwd()),
|
|
526
564
|
traceRetentionDays: 7,
|
|
527
565
|
/** Undo(文件回退)总开关:false 时跳过所有写前备份,紧急降级用。 */
|
|
528
566
|
undoEnabled: true,
|
|
@@ -539,7 +577,7 @@ var init_config = __esm({
|
|
|
539
577
|
* - 'allow':照常明文备份(隐私差,谨慎使用)。
|
|
540
578
|
*/
|
|
541
579
|
undoBackupSensitive: "skip",
|
|
542
|
-
/** web_search 搜索后端:环境变量 SEARCH_PROVIDER 可强制 "tavily"|"bing"|"ddg";不设则自动——有 TAVILY_API_KEY 用 Tavily,否则用 Bing(免注册,中国/全球可达)。 */
|
|
580
|
+
/** web_search 搜索后端:环境变量 SEARCH_PROVIDER 可强制 "deepseek"|"tavily"|"bing"|"ddg";不设则自动——有 DEEP_SEEK_API_KEY 用 DeepSeek 自家搜索(¥0.005/次进结算单),否则有 TAVILY_API_KEY 用 Tavily,否则用 Bing(免注册,中国/全球可达)。 */
|
|
543
581
|
searchProvider: process.env.SEARCH_PROVIDER || "",
|
|
544
582
|
/** Tavily 搜索 API 密钥(可选升级后端)。从环境变量 TAVILY_API_KEY 读取;未配置时 web_search 自动回退到免注册的 DuckDuckGo。 */
|
|
545
583
|
tavilyApiKey: process.env.TAVILY_API_KEY || "",
|
|
@@ -588,6 +626,12 @@ var init_config = __esm({
|
|
|
588
626
|
* 行为与改造前一致。env DEEP_SEEK_HOOK_REWRITE=0 回退。 */
|
|
589
627
|
hookRewrite: process.env.DEEP_SEEK_HOOK_REWRITE !== "0"
|
|
590
628
|
};
|
|
629
|
+
MODEL_WINDOWS = {};
|
|
630
|
+
resolveModelWindow = (model) => {
|
|
631
|
+
const id = model?.trim();
|
|
632
|
+
const hit = id ? MODEL_WINDOWS[id] : void 0;
|
|
633
|
+
return typeof hit === "number" && hit > 0 ? hit : appConfig.MAX_HISTORY_TOKENS;
|
|
634
|
+
};
|
|
591
635
|
ENV_SOURCES = {
|
|
592
636
|
dataDir: "DEEPSEEKER_CODE_DATA_DIR",
|
|
593
637
|
searchProvider: "SEARCH_PROVIDER",
|
|
@@ -1132,10 +1176,10 @@ var init_batchStrings = __esm({
|
|
|
1132
1176
|
const money = c.costYuan !== null ? `\xA5${c.costYuan.toFixed(4)}` : "\uFF08\u65E0\u4EF7\u6A21\u578B\uFF0C\u4E0D\u8BA1\u94B1\uFF09";
|
|
1133
1177
|
const hit = c.promptTokens > 0 ? Math.round(c.cachedTokens / c.promptTokens * 100) : 0;
|
|
1134
1178
|
const mins = Math.round(c.durationMs / 6e3) / 10;
|
|
1135
|
-
return `${c.rounds} \u8F6E \xB7 ${money} \xB7 \u7F13\u5B58\u547D\u4E2D ${hit}% \xB7 ${mins}min${c.partialPrice ? "\uFF08\u90E8\u5206\u8BB0\u5F55\u65E0\u4EF7\uFF0C\u94B1\u4E3A\u90E8\u5206\u548C\uFF09" : ""}${c.subRuns > 0 ? ` \xB7 \u5B50 run ${c.subRuns} \u4E2A\u5DF2\u8BA1\u5165` : ""}`;
|
|
1179
|
+
return `${c.rounds} \u8F6E \xB7 ${money} \xB7 \u7F13\u5B58\u547D\u4E2D ${hit}% \xB7 ${mins}min${c.partialPrice ? "\uFF08\u90E8\u5206\u8BB0\u5F55\u65E0\u4EF7\uFF0C\u94B1\u4E3A\u90E8\u5206\u548C\uFF09" : ""}${c.subRuns > 0 ? ` \xB7 \u5B50 run ${c.subRuns} \u4E2A\u5DF2\u8BA1\u5165` : ""}${c.toolCostYuan > 0 ? ` \xB7 \u8054\u7F51 \xA5${c.toolCostYuan.toFixed(4)}` : ""}${c.auxCostYuan > 0 ? ` \xB7 \u8F85\u52A9 \xA5${c.auxCostYuan.toFixed(4)}` : ""}`;
|
|
1136
1180
|
},
|
|
1137
1181
|
noReceipt: "\uFF08\u65E0\u7ED3\u7B97\u5355\u2014\u2014run \u672A\u5230\u6536\u5C3E\u5373\u88AB\u622A\u65AD\uFF0C\u82B1\u8D39\u4EE5\u4E0A\u65B9 usageLog \u805A\u5408\u4E3A\u51C6\uFF09",
|
|
1138
|
-
receiptLine: (r) => `${r.rounds} \u8F6E \xB7 ${r.costYuan !== null ? `\xA5${r.costYuan.toFixed(4)}` : "\u65E0\u4EF7"} \xB7 \u7F13\u5B58\u547D\u4E2D ${Math.round(r.cacheHitRate * 100)}% \xB7 ${(r.durationMs / 1e3).toFixed(0)}s`,
|
|
1182
|
+
receiptLine: (r) => `${r.rounds} \u8F6E \xB7 ${r.costYuan !== null ? `\xA5${r.costYuan.toFixed(4)}` : "\u65E0\u4EF7"} \xB7 \u7F13\u5B58\u547D\u4E2D ${Math.round(r.cacheHitRate * 100)}% \xB7 ${(r.durationMs / 1e3).toFixed(0)}s${r.toolCostYuan ? ` \xB7 \u8054\u7F51 \xA5${r.toolCostYuan.toFixed(4)}` : ""}${r.auxCostYuan ? ` \xB7 \u8F85\u52A9 \xA5${r.auxCostYuan.toFixed(4)}` : ""}`,
|
|
1139
1183
|
// —— 报告骨架 ——
|
|
1140
1184
|
rejectedTitle: "# \u6302\u673A\u4EFB\u52A1\u88AB\u62D2",
|
|
1141
1185
|
reportTitle: (title) => `# \u6302\u673A\u4EFB\u52A1\u62A5\u544A\uFF1A${title}`,
|
|
@@ -1232,10 +1276,10 @@ changes are harvested onto an isolated git worktree (temp branch), kept for manu
|
|
|
1232
1276
|
const money = c.costYuan !== null ? `\xA5${c.costYuan.toFixed(4)}` : "(unpriced model, no money counted)";
|
|
1233
1277
|
const hit = c.promptTokens > 0 ? Math.round(c.cachedTokens / c.promptTokens * 100) : 0;
|
|
1234
1278
|
const mins = Math.round(c.durationMs / 6e3) / 10;
|
|
1235
|
-
return `${c.rounds} rounds \xB7 ${money} \xB7 cache hit ${hit}% \xB7 ${mins}min${c.partialPrice ? " (some records unpriced; money is a partial sum)" : ""}${c.subRuns > 0 ? ` \xB7 ${c.subRuns} sub-runs included` : ""}`;
|
|
1279
|
+
return `${c.rounds} rounds \xB7 ${money} \xB7 cache hit ${hit}% \xB7 ${mins}min${c.partialPrice ? " (some records unpriced; money is a partial sum)" : ""}${c.subRuns > 0 ? ` \xB7 ${c.subRuns} sub-runs included` : ""}${c.toolCostYuan > 0 ? ` \xB7 web \xA5${c.toolCostYuan.toFixed(4)}` : ""}${c.auxCostYuan > 0 ? ` \xB7 aux \xA5${c.auxCostYuan.toFixed(4)}` : ""}`;
|
|
1236
1280
|
},
|
|
1237
1281
|
noReceipt: "(no receipt \u2014 the run was truncated before finishing; cost per the usageLog aggregate above)",
|
|
1238
|
-
receiptLine: (r) => `${r.rounds} rounds \xB7 ${r.costYuan !== null ? `\xA5${r.costYuan.toFixed(4)}` : "unpriced"} \xB7 cache hit ${Math.round(r.cacheHitRate * 100)}% \xB7 ${(r.durationMs / 1e3).toFixed(0)}s`,
|
|
1282
|
+
receiptLine: (r) => `${r.rounds} rounds \xB7 ${r.costYuan !== null ? `\xA5${r.costYuan.toFixed(4)}` : "unpriced"} \xB7 cache hit ${Math.round(r.cacheHitRate * 100)}% \xB7 ${(r.durationMs / 1e3).toFixed(0)}s${r.toolCostYuan ? ` \xB7 web \xA5${r.toolCostYuan.toFixed(4)}` : ""}${r.auxCostYuan ? ` \xB7 aux \xA5${r.auxCostYuan.toFixed(4)}` : ""}`,
|
|
1239
1283
|
rejectedTitle: "# Batch task rejected",
|
|
1240
1284
|
reportTitle: (title) => `# Batch task report: ${title}`,
|
|
1241
1285
|
statusEntry: (v) => `- Status: ${v}`,
|
|
@@ -1453,7 +1497,8 @@ var init_stream = __esm({
|
|
|
1453
1497
|
break;
|
|
1454
1498
|
}
|
|
1455
1499
|
}
|
|
1456
|
-
|
|
1500
|
+
const hasSplitToolTurn = messages.some((m, i) => i > 0 && m?.role === "assistant" && messages[i - 1]?.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.length > 0);
|
|
1501
|
+
if (lastNonSystem?.role === "assistant" || hasSplitToolTurn) return messages;
|
|
1457
1502
|
return messages.map((m) => {
|
|
1458
1503
|
const wire = m;
|
|
1459
1504
|
if (wire?.role !== "assistant" || typeof wire.reasoning_content !== "string") return m;
|
|
@@ -1565,6 +1610,10 @@ var init_deepseek = __esm({
|
|
|
1565
1610
|
const completion = await getModel().chat.completions.create(requestBody, {
|
|
1566
1611
|
signal: opts.signal
|
|
1567
1612
|
});
|
|
1613
|
+
try {
|
|
1614
|
+
opts.onAuxUsage?.(completion.usage, "compact");
|
|
1615
|
+
} catch {
|
|
1616
|
+
}
|
|
1568
1617
|
if (completion && "choices" in completion) return completion;
|
|
1569
1618
|
throw new Error("API \u54CD\u5E94\u5F02\u5E38\uFF0C\u672A\u5305\u542B choices \u7ED3\u6784");
|
|
1570
1619
|
} catch (error) {
|
|
@@ -1572,7 +1621,7 @@ var init_deepseek = __esm({
|
|
|
1572
1621
|
throw error;
|
|
1573
1622
|
}
|
|
1574
1623
|
};
|
|
1575
|
-
classifyRisk = async (toolName, args, detail, signal) => {
|
|
1624
|
+
classifyRisk = async (toolName, args, detail, signal, onAuxUsage) => {
|
|
1576
1625
|
const ac = new AbortController();
|
|
1577
1626
|
const timer = setTimeout(() => ac.abort(), 1e4);
|
|
1578
1627
|
if (signal) signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
@@ -1588,6 +1637,10 @@ var init_deepseek = __esm({
|
|
|
1588
1637
|
thinking: { type: "disabled" },
|
|
1589
1638
|
stream: false
|
|
1590
1639
|
}, { signal: ac.signal });
|
|
1640
|
+
try {
|
|
1641
|
+
onAuxUsage?.(completion.usage, "classify");
|
|
1642
|
+
} catch {
|
|
1643
|
+
}
|
|
1591
1644
|
const text = (completion.choices[0]?.message?.content ?? "").trim().toUpperCase();
|
|
1592
1645
|
return text.startsWith("SAFE") ? "safe" : "risky";
|
|
1593
1646
|
} catch {
|
|
@@ -1945,7 +1998,7 @@ var init_model = __esm({
|
|
|
1945
1998
|
};
|
|
1946
1999
|
activeProvider = resolveInitialProvider();
|
|
1947
2000
|
chatWithModelWithSummary = (messages, tools, opts) => activeProvider.summarize(messages, tools, opts);
|
|
1948
|
-
classifyToolRisk = (toolName, args, detail, signal) => activeProvider.classifyRisk(toolName, args, detail, signal);
|
|
2001
|
+
classifyToolRisk = (toolName, args, detail, signal, onAuxUsage) => activeProvider.classifyRisk(toolName, args, detail, signal, onAuxUsage);
|
|
1949
2002
|
}
|
|
1950
2003
|
});
|
|
1951
2004
|
|
|
@@ -2058,8 +2111,10 @@ var init_contextCore = __esm({
|
|
|
2058
2111
|
// ../core/src/session/store.ts
|
|
2059
2112
|
var store_exports = {};
|
|
2060
2113
|
__export(store_exports, {
|
|
2114
|
+
calibrationEntryFor: () => calibrationEntryFor,
|
|
2061
2115
|
deleteSession: () => deleteSession,
|
|
2062
2116
|
ensureSessionsDir: () => ensureSessionsDir,
|
|
2117
|
+
getCalibration: () => getCalibration,
|
|
2063
2118
|
getMostRecentSessionId: () => getMostRecentSessionId,
|
|
2064
2119
|
getOrCreateSessionId: () => getOrCreateSessionId,
|
|
2065
2120
|
getRollingState: () => getRollingState,
|
|
@@ -2088,7 +2143,9 @@ async function getRollingState(sessionId) {
|
|
|
2088
2143
|
archivedMessageCount: typeof store.archivedMessageCount === "number" && store.archivedMessageCount >= 0 ? store.archivedMessageCount : 0,
|
|
2089
2144
|
// 读取持久化的连续失败次数,若不存在则默认为 0
|
|
2090
2145
|
consecutiveFailures: typeof store.consecutiveFailures === "number" ? store.consecutiveFailures : 0,
|
|
2091
|
-
// ★ 跨 run 持久化的压缩校准状态(缺省 undefined,调用方按 ??
|
|
2146
|
+
// ★ 跨 run 持久化的压缩校准状态(缺省 undefined,调用方按 ?? 回落默认)。
|
|
2147
|
+
// P1-4 起这是「扁平镜像视图」:压缩主链路(runAgent/compactSessionNow)须走 getCalibration(sessionId, modelId)
|
|
2148
|
+
// 按 per-model 分键读——切模型后各模型校准互不污染,扁平字段只是最近写入模型的镜像。
|
|
2092
2149
|
calibRatio: typeof store.calibRatio === "number" ? store.calibRatio : void 0,
|
|
2093
2150
|
lastRealPromptTokens: typeof store.lastRealPromptTokens === "number" ? store.lastRealPromptTokens : void 0,
|
|
2094
2151
|
lastCachedTokens: typeof store.lastCachedTokens === "number" ? store.lastCachedTokens : void 0
|
|
@@ -2115,7 +2172,7 @@ async function setTodos(sessionId, todos) {
|
|
|
2115
2172
|
await writeStore(sessionId, store);
|
|
2116
2173
|
});
|
|
2117
2174
|
}
|
|
2118
|
-
var getSessionsDirPath, getWorkspaceSessionsDir, getStatePath, getTranscriptPath, ensuredSessionDirs, ensureSessionsDir, migrateLegacyFiles, writeStore, storeLocks, withStoreLock, getOrCreateSessionId, readStore, updateCalibration, listSessions, getMostRecentSessionId, renameSession, deleteSession;
|
|
2175
|
+
var getSessionsDirPath, getWorkspaceSessionsDir, getStatePath, getTranscriptPath, ensuredSessionDirs, ensureSessionsDir, migrateLegacyFiles, writeStore, storeLocks, withStoreLock, getOrCreateSessionId, readStore, calibrationEntryFor, getCalibration, updateCalibration, listSessions, getMostRecentSessionId, renameSession, deleteSession;
|
|
2119
2176
|
var init_store = __esm({
|
|
2120
2177
|
"../core/src/session/store.ts"() {
|
|
2121
2178
|
"use strict";
|
|
@@ -2222,8 +2279,11 @@ var init_store = __esm({
|
|
|
2222
2279
|
// 总条数消息
|
|
2223
2280
|
rollingSummary: "",
|
|
2224
2281
|
// 滚动总结摘要
|
|
2225
|
-
consecutiveFailures: 0
|
|
2282
|
+
consecutiveFailures: 0,
|
|
2226
2283
|
// 失败消息
|
|
2284
|
+
// ★ 项目根盖章(2026-10-21):记录会话归属项目,会话按项目分桶外的兜底元信息——
|
|
2285
|
+
// 多根工作区切根修复后分桶键即跟随项目,此章供诊断/未来跨桶工具用(旧会话无此字段)。
|
|
2286
|
+
projectRoot: process.cwd()
|
|
2227
2287
|
};
|
|
2228
2288
|
if (!isPersisted) {
|
|
2229
2289
|
await writeStore(entry.sessionId, entry);
|
|
@@ -2250,13 +2310,41 @@ var init_store = __esm({
|
|
|
2250
2310
|
throw err;
|
|
2251
2311
|
}
|
|
2252
2312
|
};
|
|
2253
|
-
|
|
2313
|
+
calibrationEntryFor = (store, modelId) => {
|
|
2314
|
+
if (modelId) {
|
|
2315
|
+
const byModel = store?.calibByModel;
|
|
2316
|
+
const hit = byModel && typeof byModel === "object" ? byModel[modelId] : void 0;
|
|
2317
|
+
if (hit && typeof hit === "object") {
|
|
2318
|
+
return {
|
|
2319
|
+
calibRatio: typeof hit.calibRatio === "number" ? hit.calibRatio : void 0,
|
|
2320
|
+
lastRealPromptTokens: typeof hit.lastRealPromptTokens === "number" ? hit.lastRealPromptTokens : void 0,
|
|
2321
|
+
lastCachedTokens: typeof hit.lastCachedTokens === "number" ? hit.lastCachedTokens : void 0
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
return {
|
|
2326
|
+
calibRatio: typeof store?.calibRatio === "number" ? store.calibRatio : void 0,
|
|
2327
|
+
lastRealPromptTokens: typeof store?.lastRealPromptTokens === "number" ? store.lastRealPromptTokens : void 0,
|
|
2328
|
+
lastCachedTokens: typeof store?.lastCachedTokens === "number" ? store.lastCachedTokens : void 0
|
|
2329
|
+
};
|
|
2330
|
+
};
|
|
2331
|
+
getCalibration = async (sessionId, modelId) => {
|
|
2332
|
+
const store = await readStore(sessionId);
|
|
2333
|
+
return calibrationEntryFor(store, modelId);
|
|
2334
|
+
};
|
|
2335
|
+
updateCalibration = async (sessionId, modelId, calib) => {
|
|
2254
2336
|
return withStoreLock(sessionId, async () => {
|
|
2255
2337
|
const store = await readStore(sessionId);
|
|
2256
2338
|
if (!store) return;
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2339
|
+
const id = modelId?.trim() || "default";
|
|
2340
|
+
const byModel = store.calibByModel && typeof store.calibByModel === "object" ? store.calibByModel : store.calibByModel = {};
|
|
2341
|
+
const cur = byModel[id] && typeof byModel[id] === "object" ? byModel[id] : byModel[id] = {};
|
|
2342
|
+
if (typeof calib.calibRatio === "number" && Number.isFinite(calib.calibRatio)) cur.calibRatio = calib.calibRatio;
|
|
2343
|
+
if (typeof calib.lastRealPromptTokens === "number") cur.lastRealPromptTokens = calib.lastRealPromptTokens;
|
|
2344
|
+
if (typeof calib.lastCachedTokens === "number") cur.lastCachedTokens = calib.lastCachedTokens;
|
|
2345
|
+
if (typeof cur.calibRatio === "number") store.calibRatio = cur.calibRatio;
|
|
2346
|
+
if (typeof cur.lastRealPromptTokens === "number") store.lastRealPromptTokens = cur.lastRealPromptTokens;
|
|
2347
|
+
if (typeof cur.lastCachedTokens === "number") store.lastCachedTokens = cur.lastCachedTokens;
|
|
2260
2348
|
await writeStore(sessionId, store);
|
|
2261
2349
|
});
|
|
2262
2350
|
};
|
|
@@ -2315,7 +2403,8 @@ var init_store = __esm({
|
|
|
2315
2403
|
createAt: state?.createAt,
|
|
2316
2404
|
updatedAt,
|
|
2317
2405
|
messageCount,
|
|
2318
|
-
preview: preview.slice(0, 80)
|
|
2406
|
+
preview: preview.slice(0, 80),
|
|
2407
|
+
projectRoot: typeof state?.projectRoot === "string" ? state.projectRoot : void 0
|
|
2319
2408
|
});
|
|
2320
2409
|
}
|
|
2321
2410
|
summaries.sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
|
|
@@ -2664,6 +2753,8 @@ var init_planMode = __esm({
|
|
|
2664
2753
|
type: "function",
|
|
2665
2754
|
function: {
|
|
2666
2755
|
name: "exit_plan_mode",
|
|
2756
|
+
// ★ P1-6 终结声明:调用即提交方案并终结本轮(调度层发 plan.proposed,上层呈现审批)
|
|
2757
|
+
terminal: "plan_exit",
|
|
2667
2758
|
description: "\u8BA1\u5212\u6A21\u5F0F\u4E13\u7528\uFF1A\u5B8C\u6210\u53EA\u8BFB\u8C03\u7814\u3001\u5F62\u6210\u660E\u786E\u5B9E\u73B0\u65B9\u6848\u540E\u8C03\u7528\u672C\u5DE5\u5177\u63D0\u4EA4\u65B9\u6848\u3002\u63D0\u4EA4\u540E\u65B9\u6848\u4F1A\u5448\u73B0\u7ED9\u7528\u6237\u5BA1\u6279\uFF1B\u5BA1\u6279\u901A\u8FC7\u540E\u624D\u8FDB\u5165\u5B9E\u73B0\u9636\u6BB5\uFF08\u5C4A\u65F6\u624D\u5141\u8BB8\u4FEE\u6539\u6587\u4EF6/\u6267\u884C\u547D\u4EE4\uFF09\u3002\u5728\u8BA1\u5212\u6A21\u5F0F\u4E0B\u4F60\u53EA\u80FD\u8BFB\u53D6\u4E0E\u641C\u7D22\uFF0C\u4E0D\u80FD\u505A\u4EFB\u4F55\u4FEE\u6539\u3002",
|
|
2668
2759
|
parameters: {
|
|
2669
2760
|
type: "object",
|
|
@@ -2678,6 +2769,8 @@ var init_planMode = __esm({
|
|
|
2678
2769
|
type: "function",
|
|
2679
2770
|
function: {
|
|
2680
2771
|
name: "enter_plan_mode",
|
|
2772
|
+
// ★ P1-6 终结声明:调用即请求进入计划模式并终结本轮(调度层发 plan.enterRequested)
|
|
2773
|
+
terminal: "plan_enter",
|
|
2681
2774
|
description: "\u81EA\u4E3B\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F\uFF1A\u5F53\u4EFB\u52A1\u662F\u975E\u5E73\u51E1\u7684\u5B9E\u73B0\u4EFB\u52A1\uFF08\u591A\u6587\u4EF6\u6539\u52A8\u3001\u67B6\u6784\u51B3\u7B56\u3001\u4E0D\u786E\u5B9A\u8DEF\u5F84\u3001\u9AD8\u98CE\u9669\u64CD\u4F5C\uFF09\u65F6\u8C03\u7528\u3002\u8C03\u7528\u540E\u672C\u8F6E\u7ED3\u675F\u5E76\u8FDB\u5165\u53EA\u8BFB\u8C03\u7814\u9636\u6BB5\u2014\u2014\u4F60\u53EA\u80FD\u8BFB\u53D6\u4E0E\u641C\u7D22\uFF0C\u4EA7\u51FA\u5B8C\u6574\u5B9E\u73B0\u65B9\u6848\uFF08\u8981\u6539\u54EA\u4E9B\u6587\u4EF6\u3001\u600E\u4E48\u6539\u3001\u4E3A\u4F55\u3001\u98CE\u9669\uFF09\u7ECF\u7528\u6237\u5BA1\u6279\u540E\u518D\u5B9E\u73B0\u3002\u7B80\u5355\u4EFB\u52A1\u65E0\u9700\u8C03\u7528\uFF0C\u76F4\u63A5\u5B9E\u73B0\u3002",
|
|
2682
2775
|
parameters: {
|
|
2683
2776
|
type: "object",
|
|
@@ -2712,7 +2805,12 @@ var init_systemPrompt = __esm({
|
|
|
2712
2805
|
- \u5DE5\u5177**\u6267\u884C\u5931\u8D25\u6216\u88AB\u7528\u6237\u62D2\u7EDD**\u65F6\uFF0C\u8BFB\u8FD4\u56DE\u7ED3\u679C\u5B9A\u4F4D\u539F\u56E0\uFF0C\u4FEE\u6B63\u53C2\u6570\u6216\u6362\u7B49\u6548\u65B9\u6848\u7EE7\u7EED\uFF0C\u4E0D\u8981\u9053\u6B49\u4E00\u4E24\u53E5\u5C31\u505C\u4E0B\uFF1B\u786E\u5B9E\u53D7\u963B\u9700\u8981\u7528\u6237\u51B3\u7B56\u65F6\uFF0C\u7528 ask_question \u8BF4\u660E**\u5177\u4F53**\u963B\u585E\u70B9\u3002**\u4E25\u7981**\u5728\u672A\u771F\u6B63\u6267\u884C\u6216\u672A\u9A8C\u8BC1\u7684\u60C5\u51B5\u4E0B\u58F0\u79F0"\u5DF2\u5B8C\u6210/\u5DF2\u4FEE\u6539/\u5DF2\u4FEE\u590D"\u2014\u2014\u6539\u52A8\u82E5\u53EF\u9A8C\u8BC1\uFF08\u91CD\u8BFB\u786E\u8BA4\u3001\u8DD1\u6D4B\u8BD5\u3001\u7C7B\u578B\u68C0\u67E5\uFF09\uFF0C\u5148\u9A8C\u8BC1\u518D\u6536\u5C3E\u3002
|
|
2713
2806
|
|
|
2714
2807
|
\u3010\u56DE\u7B54\u5F62\u5F0F\u3011
|
|
2715
|
-
- **\u5148\u7ED3\u8BBA\u540E\u7EC6\u8282**\uFF1A\u5148\u76F4\u63A5\u56DE\u7B54\u6216\u6C47\u62A5\u7ED3\u679C\uFF0C\u518D\u5C55\u5F00\u5FC5\u8981\u7684\u89E3\u91CA\
|
|
2808
|
+
- **\u5148\u7ED3\u8BBA\u540E\u7EC6\u8282**\uFF1A\u5148\u76F4\u63A5\u56DE\u7B54\u6216\u6C47\u62A5\u7ED3\u679C\uFF0C\u518D\u5C55\u5F00\u5FC5\u8981\u7684\u89E3\u91CA\u3002
|
|
2809
|
+
- **\u8BF4\u4EBA\u8BDD**\uFF1A\u6C47\u62A5\u548C\u6536\u5C3E\u7528\u901A\u4FD7\u8BED\u8A00\u2014\u2014\u5148\u8BB2\u505A\u4E86\u4EC0\u4E48\u3001\u6548\u679C\u662F\u4EC0\u4E48\uFF0C\u518D\u8BB2\u624B\u6BB5\uFF1B\u975E\u5FC5\u8981\u4E0D\u5806\u672F\u8BED\uFF0C\u7ED5\u4E0D\u5F00\u7684\u672F\u8BED\u7D27\u8DDF\u4E00\u53E5\u767D\u8BDD\u89E3\u91CA\uFF1B\u6280\u672F\u7EC6\u8282\u653E\u540E\u9762\uFF0C\u7528\u6237\u8FFD\u95EE\u518D\u5C55\u5F00\u3002
|
|
2810
|
+
- **\u6536\u5C3E\u81EA\u7136\u7B80\u77ED**\uFF1A\u4EFB\u52A1\u6536\u5C3E\u628A\u7ED3\u679C\u8BF4\u6E05\u695A\u5373\u53EF\u2014\u2014\u5148\u4E00\u53E5\u7ED3\u8BBA\uFF08\u505A\u5B8C\u4E86\u4EC0\u4E48\u3001\u6548\u679C\u5982\u4F55\uFF09\uFF0C\u518D\u6309\u9700\u7B80\u8FF0\u6539\u52A8\u8981\u70B9\u4E0E\u9A8C\u8BC1\u7ED3\u679C\uFF0C\u7BC7\u5E45\u4EE5\u8BF4\u6E05\u4E3A\u51C6\uFF08\u4E00\u822C\u51E0\u884C\uFF09\u3002\u4E0D\u5199\u957F\u7BC7\u7ED3\u9898\u62A5\u544A\u3001\u4E0D\u9010\u6587\u4EF6\u6D41\u6C34\u8D26\u3001\u4E0D\u590D\u8FF0\u6267\u884C\u8FC7\u7A0B\uFF1B\u7528\u6237\u8FFD\u95EE\u65F6\u518D\u5C55\u5F00\u3002\u957F\u5EA6\u53C2\u7167\u2014\u2014
|
|
2811
|
+
\u5DF2\u4FEE\u590D\uFF1A\u767B\u5F55\u8FC7\u671F\u540E\u73B0\u5728\u80FD\u6B63\u786E\u8DF3\u56DE\u767B\u5F55\u9875\uFF08\u4E4B\u524D\u4F1A\u505C\u5728\u7A7A\u767D\u9875\uFF09\u3002
|
|
2812
|
+
\u6539\u52A8\uFF1Asrc/auth/redirect.ts \u2014\u2014 \u8FC7\u671F\u5206\u652F\u8865\u4E0A return \u767B\u5F55\u9875\u8DEF\u5F84\u3002
|
|
2813
|
+
\u9A8C\u8BC1\uFF1A\u767B\u5F55\u6A21\u5757\u6D4B\u8BD5 23 \u4F8B\u5168\u90E8\u901A\u8FC7\u3002
|
|
2716
2814
|
- **\u4E0D\u56DE\u8D34\u5927\u6BB5\u4EE3\u7801**\uFF1A\u6539\u52A8\u5185\u5BB9\u7528\u6237\u5728\u7F16\u8F91\u5668/diff \u91CC\u770B\u5F97\u5230\uFF0C\u6B63\u6587\u53EA\u9700\u4E00\u53E5\u8BDD\u8BF4\u660E\u6539\u4E86\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\uFF0C\u6216\u5F15\u7528\u5173\u952E\u51E0\u884C\u3002
|
|
2717
2815
|
|
|
2718
2816
|
\u3010\u5DE5\u5177\u4F7F\u7528\u51C6\u5219\u3011
|
|
@@ -3056,6 +3154,7 @@ var init_truncate = __esm({
|
|
|
3056
3154
|
init_content();
|
|
3057
3155
|
init_systemPrompt();
|
|
3058
3156
|
init_model();
|
|
3157
|
+
init_createModel();
|
|
3059
3158
|
init_store();
|
|
3060
3159
|
init_transcript();
|
|
3061
3160
|
init_hooks();
|
|
@@ -3127,12 +3226,13 @@ var init_truncate = __esm({
|
|
|
3127
3226
|
${tail}`;
|
|
3128
3227
|
};
|
|
3129
3228
|
batchSummaryPrompt = (tailContext) => "\u6982\u62EC\u4EE5\u4E0A\u5BF9\u8BDD\u4E0E\u5DE5\u5177\u8C03\u7528\uFF0C\u6309\u300C\u5B8C\u6210\u4E86\u4EC0\u4E48 \u2192 \u6B63\u5728\u505A\u4EC0\u4E48 \u2192 \u4E0B\u4E00\u6B65/\u53D7\u963B\u300D\u7684\u987A\u5E8F\u5199\u6210\u4E00\u6BB5\u3002\u5FC5\u987B\u539F\u6837\u4FDD\u7559\u5173\u952E\u5B9E\u4F53\u540D\uFF08\u6587\u4EF6\u8DEF\u5F84\u3001\u51FD\u6570/\u7C7B\u540D\u3001\u62A5\u9519\u5173\u952E\u8BCD\uFF09\u4EE5\u4FBF\u540E\u7EED\u68C0\u7D22\uFF1B" + (tailContext ? "\u8FD9\u6279\u6458\u8981\u7D27\u90BB\u672A\u538B\u7F29\u7684\u4FDD\u7559\u533A\uFF0C\u8BF7\u989D\u5916\u5199\u660E\u5176\u4E2D\u5BF9\u7D27\u968F\u5176\u540E\u7684\u8FD1\u671F\u5DE5\u4F5C\u6709\u7528\u7684\u80CC\u666F\uFF08\u5DF2\u786E\u8BA4\u7684\u7EA6\u675F\u3001\u6570\u636E\u3001\u7ED3\u8BBA\uFF09\u3002" : "") + '\u5BF9\u8BB0\u4E0D\u51C6\u7684\u7EC6\u8282\u4E0D\u8981\u81C6\u6D4B\uFF0C\u6807\u6CE8"(\u7EC6\u8282\u5DF2\u5F52\u6863)"\u5373\u53EF\u3002\u4E0D\u8981\u8C03\u7528\u5DE5\u5177\u3002';
|
|
3130
|
-
compactBatch = async (batch, signal, tailContext) => {
|
|
3229
|
+
compactBatch = async (batch, signal, tailContext, onAuxUsage) => {
|
|
3131
3230
|
const resp = await chatWithModelWithSummary(
|
|
3132
3231
|
// ★ 辅助模型是 text-only:送前把 image part 降级为占位符——base64 绝不能进摘要批(烧钱且无意义)
|
|
3133
3232
|
[...batch.map(degradeImagesForAux), { role: "user", content: batchSummaryPrompt(tailContext) }],
|
|
3134
3233
|
[],
|
|
3135
|
-
|
|
3234
|
+
// ★ P0-2:aux usage 回传 run 级成本账(成本信任链:摘要调用不入账则预算被压缩打穿)
|
|
3235
|
+
{ signal, onAuxUsage }
|
|
3136
3236
|
);
|
|
3137
3237
|
return `- ${resp.choices[0].message.content || ""}`;
|
|
3138
3238
|
};
|
|
@@ -3218,7 +3318,7 @@ ${noteLine}` : noteLine : notes;
|
|
|
3218
3318
|
const hasPrevious = trimmedNotes.length > 0 && trimmedNotes !== SLOT_PLACEHOLDER;
|
|
3219
3319
|
return { index, hasPrevious };
|
|
3220
3320
|
};
|
|
3221
|
-
synthesizeSlotNarrative = async (slotContent, newLines, signal) => {
|
|
3321
|
+
synthesizeSlotNarrative = async (slotContent, newLines, signal, onAuxUsage) => {
|
|
3222
3322
|
const { index, hasPrevious } = parseSlotNarrativeState(slotContent);
|
|
3223
3323
|
const lines = newLines.trim();
|
|
3224
3324
|
const resp = await chatWithModelWithSummary(
|
|
@@ -3230,12 +3330,12 @@ ${lines}
|
|
|
3230
3330
|
` : ""}${checkpointPrompt(hasPrevious)}` }
|
|
3231
3331
|
],
|
|
3232
3332
|
[],
|
|
3233
|
-
{ signal }
|
|
3333
|
+
{ signal, onAuxUsage }
|
|
3234
3334
|
);
|
|
3235
3335
|
const newNotes = (resp.choices[0].message.content || "").trim();
|
|
3236
3336
|
return assembleSlot(index, newNotes);
|
|
3237
3337
|
};
|
|
3238
|
-
synthesizeSlotNarrativeFromBatch = async (slotContent, batch, signal) => {
|
|
3338
|
+
synthesizeSlotNarrativeFromBatch = async (slotContent, batch, signal, onAuxUsage) => {
|
|
3239
3339
|
const { index, hasPrevious } = parseSlotNarrativeState(slotContent);
|
|
3240
3340
|
const resp = await chatWithModelWithSummary(
|
|
3241
3341
|
[
|
|
@@ -3244,12 +3344,12 @@ ${lines}
|
|
|
3244
3344
|
{ role: "user", content: checkpointDirectPrompt(hasPrevious) }
|
|
3245
3345
|
],
|
|
3246
3346
|
[],
|
|
3247
|
-
{ signal }
|
|
3347
|
+
{ signal, onAuxUsage }
|
|
3248
3348
|
);
|
|
3249
3349
|
const newNotes = (resp.choices[0].message.content || "").trim();
|
|
3250
3350
|
return assembleSlot(index, newNotes);
|
|
3251
3351
|
};
|
|
3252
|
-
compactSlotNarrative = async (slotContent, _modelWindow, signal) => synthesizeSlotNarrative(slotContent, "", signal);
|
|
3352
|
+
compactSlotNarrative = async (slotContent, _modelWindow, signal, onAuxUsage) => synthesizeSlotNarrative(slotContent, "", signal, onAuxUsage);
|
|
3253
3353
|
MAX_BATCH_TOKENS = 16e3;
|
|
3254
3354
|
PRETRUNCATE_FLOOR_TOKENS = 512;
|
|
3255
3355
|
foldableTokensOf = (m) => Math.max(0, estimateTokens([m]) - 4);
|
|
@@ -3335,11 +3435,11 @@ ${lines}
|
|
|
3335
3435
|
if (batch.length > 0) batches.push(batch);
|
|
3336
3436
|
return batches;
|
|
3337
3437
|
};
|
|
3338
|
-
compactBatches = async (batches, signal, onUIEvent) => {
|
|
3438
|
+
compactBatches = async (batches, signal, onUIEvent, onAuxUsage) => {
|
|
3339
3439
|
if (onUIEvent && batches.length > 0) onUIEvent({ type: "compact.progress", done: 0, total: batches.length });
|
|
3340
3440
|
let done = 0;
|
|
3341
3441
|
const lines = await Promise.all(batches.map(
|
|
3342
|
-
(b, i) => compactBatch(b, signal, i === batches.length - 1).then((line) => {
|
|
3442
|
+
(b, i) => compactBatch(b, signal, i === batches.length - 1, onAuxUsage).then((line) => {
|
|
3343
3443
|
done++;
|
|
3344
3444
|
onUIEvent?.({ type: "compact.progress", done, total: batches.length });
|
|
3345
3445
|
return line;
|
|
@@ -3409,9 +3509,9 @@ ${lines}
|
|
|
3409
3509
|
if (toCompact.length > 0) {
|
|
3410
3510
|
summaryMsg.content = mergeSummarySlot(summaryMsg?.content || "", "", extractArchiveEntities(toCompact));
|
|
3411
3511
|
const batches = splitIntoBatches(toCompact);
|
|
3412
|
-
summaryMsg.content = batches.length === 1 ? await synthesizeSlotNarrativeFromBatch(summaryMsg.content, batches[0], event.signal) : await synthesizeSlotNarrative(summaryMsg.content, await compactBatches(batches, event.signal, event.depth === 0 ? event.onUIEvent : void 0), event.signal);
|
|
3512
|
+
summaryMsg.content = batches.length === 1 ? await synthesizeSlotNarrativeFromBatch(summaryMsg.content, batches[0], event.signal, event.onAuxUsage) : await synthesizeSlotNarrative(summaryMsg.content, await compactBatches(batches, event.signal, event.depth === 0 ? event.onUIEvent : void 0, event.onAuxUsage), event.signal, event.onAuxUsage);
|
|
3413
3513
|
if (estimateTokens([summaryMsg]) > SUMMARY_SELF_COMPACT_THRESHOLD) {
|
|
3414
|
-
summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal);
|
|
3514
|
+
summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal, event.onAuxUsage);
|
|
3415
3515
|
}
|
|
3416
3516
|
const endTime = performance.now();
|
|
3417
3517
|
event.messageArr.length = 0;
|
|
@@ -3438,7 +3538,7 @@ ${lines}
|
|
|
3438
3538
|
keep--;
|
|
3439
3539
|
continue;
|
|
3440
3540
|
} else if (summaryMsg?.content) {
|
|
3441
|
-
summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal);
|
|
3541
|
+
summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal, event.onAuxUsage);
|
|
3442
3542
|
event.messageArr.length = 0;
|
|
3443
3543
|
event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
|
|
3444
3544
|
const store = await getRollingState(event.sessionId);
|
|
@@ -3523,7 +3623,7 @@ ${lines}
|
|
|
3523
3623
|
// ../core/src/tool/permissions.ts
|
|
3524
3624
|
import fs10 from "fs/promises";
|
|
3525
3625
|
import path12 from "path";
|
|
3526
|
-
var rules2, trustedProject, globCache, globToRegex, DANGER_COMMAND_TOOLS, compileRule, readPermissionConfig, toolNameMatches, ruleMatches, initPermissions, checkPermission, listPermissionRules, buildScopedAllowRule, resolveSettingsPath, addPermissionRule;
|
|
3626
|
+
var rules2, trustedProject, sessionBuckets, SESSION_BUCKET_CAP, bucketFor, clearSessionPermissionRules, globCache, globToRegex, DANGER_COMMAND_TOOLS, compileRule, readPermissionConfig, toolNameMatches, ruleMatches, initPermissions, checkPermission, listPermissionRules, buildScopedAllowRule, resolveSettingsPath, addSessionPermissionRule, addPermissionRule;
|
|
3527
3627
|
var init_permissions = __esm({
|
|
3528
3628
|
"../core/src/tool/permissions.ts"() {
|
|
3529
3629
|
"use strict";
|
|
@@ -3531,6 +3631,23 @@ var init_permissions = __esm({
|
|
|
3531
3631
|
init_common();
|
|
3532
3632
|
rules2 = { allow: [], deny: [], ask: [] };
|
|
3533
3633
|
trustedProject = false;
|
|
3634
|
+
sessionBuckets = /* @__PURE__ */ new Map();
|
|
3635
|
+
SESSION_BUCKET_CAP = 64;
|
|
3636
|
+
bucketFor = (sessionId) => {
|
|
3637
|
+
let bucket = sessionBuckets.get(sessionId);
|
|
3638
|
+
if (!bucket) {
|
|
3639
|
+
if (sessionBuckets.size >= SESSION_BUCKET_CAP) {
|
|
3640
|
+
const oldest = sessionBuckets.keys().next().value;
|
|
3641
|
+
if (oldest !== void 0) sessionBuckets.delete(oldest);
|
|
3642
|
+
}
|
|
3643
|
+
bucket = { allow: [], deny: [], ask: [] };
|
|
3644
|
+
sessionBuckets.set(sessionId, bucket);
|
|
3645
|
+
}
|
|
3646
|
+
return bucket;
|
|
3647
|
+
};
|
|
3648
|
+
clearSessionPermissionRules = (sessionId) => {
|
|
3649
|
+
sessionBuckets.delete(sessionId);
|
|
3650
|
+
};
|
|
3534
3651
|
globCache = /* @__PURE__ */ new Map();
|
|
3535
3652
|
globToRegex = (glob) => {
|
|
3536
3653
|
const cached2 = globCache.get(glob);
|
|
@@ -3616,6 +3733,7 @@ var init_permissions = __esm({
|
|
|
3616
3733
|
};
|
|
3617
3734
|
initPermissions = async (includeProject) => {
|
|
3618
3735
|
trustedProject = includeProject;
|
|
3736
|
+
sessionBuckets.clear();
|
|
3619
3737
|
try {
|
|
3620
3738
|
rules2 = await readPermissionConfig(includeProject);
|
|
3621
3739
|
const total = rules2.allow.length + rules2.deny.length + rules2.ask.length;
|
|
@@ -3627,11 +3745,15 @@ var init_permissions = __esm({
|
|
|
3627
3745
|
rules2 = { allow: [], deny: [], ask: [] };
|
|
3628
3746
|
}
|
|
3629
3747
|
};
|
|
3630
|
-
checkPermission = (toolName, args, primaryArg) => {
|
|
3748
|
+
checkPermission = (toolName, args, primaryArg, sessionId) => {
|
|
3631
3749
|
try {
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3750
|
+
const bucket = sessionId ? sessionBuckets.get(sessionId) : void 0;
|
|
3751
|
+
const deny = bucket ? [...rules2.deny, ...bucket.deny] : rules2.deny;
|
|
3752
|
+
const ask = bucket ? [...rules2.ask, ...bucket.ask] : rules2.ask;
|
|
3753
|
+
const allow = bucket ? [...rules2.allow, ...bucket.allow] : rules2.allow;
|
|
3754
|
+
for (const r of deny) if (ruleMatches(r, toolName, args, primaryArg)) return "deny";
|
|
3755
|
+
for (const r of ask) if (ruleMatches(r, toolName, args, primaryArg)) return "ask";
|
|
3756
|
+
for (const r of allow) if (ruleMatches(r, toolName, args, primaryArg)) return "allow";
|
|
3635
3757
|
return null;
|
|
3636
3758
|
} catch {
|
|
3637
3759
|
return null;
|
|
@@ -3649,7 +3771,7 @@ var init_permissions = __esm({
|
|
|
3649
3771
|
const cmd = val.trim();
|
|
3650
3772
|
return cmd ? `${toolName}(${cmd})` : null;
|
|
3651
3773
|
}
|
|
3652
|
-
if (argKey === "path"
|
|
3774
|
+
if (argKey === "path") {
|
|
3653
3775
|
const norm = val.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^[a-zA-Z]:/, "");
|
|
3654
3776
|
const slashIdx = norm.indexOf("/");
|
|
3655
3777
|
if (slashIdx > 0) {
|
|
@@ -3661,6 +3783,13 @@ var init_permissions = __esm({
|
|
|
3661
3783
|
return `${toolName}(${val})`;
|
|
3662
3784
|
};
|
|
3663
3785
|
resolveSettingsPath = (scope) => scope === "global" ? path12.join(appConfig.dataDir, "settings.json") : path12.join(process.cwd(), ".deepseeker-code", "settings.json");
|
|
3786
|
+
addSessionPermissionRule = (ruleStr, kind = "allow", sessionId) => {
|
|
3787
|
+
const compiled2 = compileRule(ruleStr, "runtime:addSessionPermissionRule(session)", kind);
|
|
3788
|
+
if (!compiled2) return false;
|
|
3789
|
+
const target = sessionId ? bucketFor(sessionId) : rules2;
|
|
3790
|
+
if (!target[kind].some((r) => r.raw === compiled2.raw)) target[kind].push(compiled2);
|
|
3791
|
+
return true;
|
|
3792
|
+
};
|
|
3664
3793
|
addPermissionRule = async (scope, kind, ruleStr) => {
|
|
3665
3794
|
if (scope === "project" && !trustedProject) return false;
|
|
3666
3795
|
const compiled2 = compileRule(ruleStr, `runtime:addPermissionRule(${scope})`, kind);
|
|
@@ -3988,10 +4117,17 @@ var init_guard = __esm({
|
|
|
3988
4117
|
console.log(`\u23F3 [\u5BA1\u6279\u6302\u8D77] ${toolName} | \u4F1A\u8BDD=${ctx.sessionId} | \u51ED\u8BC1=${toolCallId}\uFF08\u4EA4\u7531\u5BBF\u4E3B\u5BA1\u6279\uFF09`);
|
|
3989
4118
|
await dispatch("PermissionRequest", { sessionId: ctx.sessionId, cwd: ctx.cwd, toolName, toolCallId, detail: safeDetail, safetyLevel, args }).catch(() => {
|
|
3990
4119
|
});
|
|
3991
|
-
const decision = await ctx.requestApproval(safeDetail, { toolName, toolCallId, sessionId: ctx.sessionId });
|
|
4120
|
+
const decision = await ctx.requestApproval(safeDetail, { toolName, toolCallId, sessionId: ctx.sessionId, fullDetail: detail });
|
|
3992
4121
|
const approved2 = decision !== "deny";
|
|
3993
4122
|
if (!approved2) {
|
|
3994
4123
|
ctx.onUIEvent?.({ type: "tool.denied", toolsId: toolCallId, toolName });
|
|
4124
|
+
} else if (decision === "allow-session") {
|
|
4125
|
+
const ruleStr = buildScopedAllowRule(toolName, args, primaryArg);
|
|
4126
|
+
if (ruleStr && addSessionPermissionRule(ruleStr, "allow", ctx.sessionId)) {
|
|
4127
|
+
console.log(`\u{1F4CC} [\u5BA1\u6279\u8BB0\u5FC6] \u672C\u4F1A\u8BDD\u5185\u514D\u5BA1\uFF1A${ruleStr}\uFF08\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u5931\u6548\uFF0C\u4E0D\u5199\u914D\u7F6E\u6587\u4EF6\uFF09`);
|
|
4128
|
+
} else {
|
|
4129
|
+
console.log(`\u267B\uFE0F [\u5BA1\u6279] ${toolName} \u672C\u6B21\u653E\u884C${safetyLevel ? ` (${safetyLevel})` : ""}\uFF08\u672A\u8BB0\u5FC6\uFF1A\u65E0\u6CD5\u5B89\u5168\u751F\u6210\u4F5C\u7528\u57DF\uFF09`);
|
|
4130
|
+
}
|
|
3995
4131
|
} else if (decision === "allow-always") {
|
|
3996
4132
|
const ruleStr = buildScopedAllowRule(toolName, args, primaryArg);
|
|
3997
4133
|
if (ruleStr) {
|
|
@@ -4264,6 +4400,11 @@ var init_background = __esm({
|
|
|
4264
4400
|
name: "run_in_background",
|
|
4265
4401
|
primaryArg: "command",
|
|
4266
4402
|
autoApproval: "command",
|
|
4403
|
+
// ★ P1-6 声明化:命令语义(灾难命令闸门/script-runner 供应链拦截按此识别;isSync:false 使其
|
|
4404
|
+
// 不享受只读免审);后台挂起不进子 agent(父收尾成孤儿)与挂机批处理(无人盯守)
|
|
4405
|
+
argKind: "command",
|
|
4406
|
+
subagentAllowed: false,
|
|
4407
|
+
batchAllowed: false,
|
|
4267
4408
|
description: "\u5728\u540E\u53F0\u542F\u52A8\u4E00\u4E2A\u957F\u65F6 shell \u547D\u4EE4\u5E76\u7ACB\u5373\u8FD4\u56DE task_id\uFF0C\u4E0D\u963B\u585E\u540E\u7EED\u63A8\u7406\u3002\u9002\u7528\u4E24\u7C7B\u573A\u666F\uFF1A\u2460 \u957F\u8FDE\u63A5/\u5E38\u9A7B\u8FDB\u7A0B\uFF08dev server\u3001watch\u3001tail -f\uFF09\u2014\u2014\u5148\u8D77\u670D\u52A1\u518D\u7EE7\u7EED\u5E72\u522B\u7684\u6D3B\uFF1B\u2461 \u9884\u8BA1\u8FD0\u884C\u6570\u5206\u949F\u4EE5\u4E0A\u3001\u65E0\u9700\u5B9E\u65F6\u76EF\u770B\u7684\u6709\u9650\u957F\u4EFB\u52A1\uFF08\u5168\u91CF\u6D4B\u8BD5 / \u5927\u6784\u5EFA / \u5927\u4F53\u79EF\u5B89\u88C5\u2014\u2014\u8DD1\u5B8C\u7528 get_background_output \u5E26 wait_seconds \u7B49\u7ED3\u679C\uFF0C\u4E0D\u8981\u7528\u524D\u53F0 run_command \u5E72\u7B49\uFF09\u3002\u7528 get_background_output \u67E5\u65E5\u5FD7\u4E0E\u72B6\u6001\uFF0Cstop_background_task \u7EC8\u6B62\u3002",
|
|
4268
4409
|
parameters: {
|
|
4269
4410
|
type: "object",
|
|
@@ -6907,7 +7048,9 @@ ${formattedCode}${hasMore ? `
|
|
|
6907
7048
|
function: {
|
|
6908
7049
|
name: "list_dir",
|
|
6909
7050
|
planAllowed: true,
|
|
6910
|
-
primaryArg:
|
|
7051
|
+
// ★ 第 17 项补丁(2026-10-21):原声明 primaryArg:'path' 系漂移——本工具参数表只有 max_depth,
|
|
7052
|
+
// 根本没有 path;且 SAFE 只读工具不走审批路径,allow-always 无意义。删除后由
|
|
7053
|
+
// tool-declaration-drift 的「声明 ⊆ parameters.properties」断言钉住。
|
|
6911
7054
|
description: "\u626B\u63CF\u5E76\u7CBE\u7B80\u5217\u51FA\u5F53\u524D\u9879\u76EE\u7684\u5DE5\u4F5C\u533A\u76EE\u5F55\u6811\u3002\u672C\u5DE5\u5177\u81EA\u52A8\u5408\u5E76\u901A\u7528\u5FFD\u7565\u89C4\u5219\u4E0E\u591A\u5C42\u5B50\u76EE\u5F55\u7EA7 .gitignore \u89C4\u8303\u3002\u2605 \u53EA\u8BFB\u5B89\u5168\uFF0C\u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
|
|
6912
7055
|
parameters: {
|
|
6913
7056
|
type: "object",
|
|
@@ -7095,6 +7238,9 @@ ${summaries.join("\n")}`;
|
|
|
7095
7238
|
triggersUndo: "delete",
|
|
7096
7239
|
autoApproval: "file",
|
|
7097
7240
|
primaryArg: "path",
|
|
7241
|
+
// ★ P1-6 声明化收权:递归删除不进子 agent(爆炸半径最大原语之一,SUBAGENT_DENYLIST 兜底同判)与挂机批处理
|
|
7242
|
+
subagentAllowed: false,
|
|
7243
|
+
batchAllowed: false,
|
|
7098
7244
|
description: "\u4ECE\u672C\u5730\u78C1\u76D8\u5185\u6C38\u4E45\u5220\u9664\u4E00\u4E2A\u6307\u5B9A\u7684\u6587\u4EF6\u6216\u8005\u4E00\u6574\u4E2A\u6587\u4EF6\u5939\u76EE\u5F55\u3002\u5982\u679C\u662F\u76EE\u5F55\uFF0C\u5DE5\u5177\u4F1A\u81EA\u52A8\u6267\u884C\u6DF1\u5EA6\u9012\u5F52\u5F3A\u884C\u9500\u6BC1\u3002\u6B64\u64CD\u4F5C\u4E0D\u53EF\u9006\uFF0C\u8BF7\u4E07\u5206\u5C0F\u5FC3\u3002",
|
|
7099
7245
|
parameters: {
|
|
7100
7246
|
type: "object",
|
|
@@ -7293,7 +7439,13 @@ ${summaries.join("\n")}`;
|
|
|
7293
7439
|
name: "move_file",
|
|
7294
7440
|
// ★ #8a 策略声明:双路径都受保护路径硬规则检查(P0-3 洞的声明化收口;特判已退役)。
|
|
7295
7441
|
// 刻意不声明 triggersUndo:move 语义可手动移回,维持「不在 Undo 回退范围」现状。
|
|
7296
|
-
|
|
7442
|
+
// ★ 第 17 项补丁(2026-10-21):主参数名原写 'src',与本工具实际参数 source/destination 不符
|
|
7443
|
+
// → args['src'] 恒 undefined → buildScopedAllowRule 返回 null → 恒降级 allow-once,
|
|
7444
|
+
// 用户点「总是允许」对 move_file 从不生效(安全侧 fail-closed 无风险,纯功能打折)。
|
|
7445
|
+
// 改为 'source' 后走 buildScopedAllowRule 末尾的【精确值】兜底分支(规则 = move_file(<精确源路径>)),
|
|
7446
|
+
// 刻意不并入 path 的顶层目录作用域(argKey === 'path' 分支):move 无 undo 兜底(见上条),
|
|
7447
|
+
// 作用域宁可窄——代价是该源路径逐次授权,move_file 非高频操作,可接受。
|
|
7448
|
+
primaryArg: "source",
|
|
7297
7449
|
autoApproval: "file",
|
|
7298
7450
|
pathArgs: ["source", "destination"],
|
|
7299
7451
|
description: "\u79FB\u52A8\u6216\u91CD\u547D\u540D\u6587\u4EF6/\u76EE\u5F55\uFF08\u540C\u5377\u539F\u5B50\u64CD\u4F5C\uFF09\u3002\u6E90\u4E0D\u5B58\u5728\u5219\u5931\u8D25\uFF1B\u76EE\u6807\u5DF2\u5B58\u5728\u5219\u62D2\u7EDD\uFF08\u9632\u8BEF\u8986\u76D6\uFF0C\u5982\u9700\u8986\u76D6\u8BF7\u5148 delete_path \u518D move_file\uFF09\u3002\u81EA\u52A8\u521B\u5EFA\u76EE\u6807\u7236\u76EE\u5F55\u3002\u26A0\uFE0F \u672C\u64CD\u4F5C\u6682\u4E0D\u5728 Undo \u56DE\u9000\u8303\u56F4\u5185\uFF08\u4E0D\u53EF\u64A4\u9500\uFF09\u3002",
|
|
@@ -7576,6 +7728,11 @@ var init_command = __esm({
|
|
|
7576
7728
|
name: "run_command",
|
|
7577
7729
|
primaryArg: "command",
|
|
7578
7730
|
autoApproval: "command",
|
|
7731
|
+
// ★ P1-6 声明化:命令语义(灾难命令闸门/script-runner 供应链拦截/只读免审按此识别)
|
|
7732
|
+
argKind: "command",
|
|
7733
|
+
// ★ P1-6 声明化收权:shell 执行不进子 agent(爆炸半径最大原语,SUBAGENT_DENYLIST 兜底同判)与挂机批处理
|
|
7734
|
+
subagentAllowed: false,
|
|
7735
|
+
batchAllowed: false,
|
|
7579
7736
|
description: "\u5728\u5DE5\u4F5C\u533A\u6267\u884C shell \u547D\u4EE4\uFF08\u77ED\u5E73\u5FEB\u7684\u6D4B\u8BD5 / \u6784\u5EFA / git \u7B49\uFF09\uFF0C\u6D41\u5F0F\u8FD4\u56DE stdout/stderr \u5E76\u9644\u5E26\u9000\u51FA\u7801\u3002\u5C5E\u4E8E\u9AD8\u5371\u64CD\u4F5C\uFF0C\u6BCF\u6B21\u6267\u884C\u90FD\u9700\u8981\u7528\u6237\u5BA1\u6279\u3002\u9884\u8BA1\u8FD0\u884C\u6570\u5206\u949F\u4EE5\u4E0A\u7684\u957F\u4EFB\u52A1\uFF08\u5168\u91CF\u6D4B\u8BD5 / \u5927\u6784\u5EFA / \u5927\u4F53\u79EF\u5B89\u88C5\uFF09\u8BF7\u6539\u7528 run_in_background \u540E\u53F0\u6267\u884C\uFF1B\u82E5\u672C\u5DE5\u5177\u6267\u884C\u8FC7\u4E45\u4ECD\u672A\u7ED3\u675F\uFF0C\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u628A\u8FDB\u7A0B\u8F6C\u5165\u540E\u53F0\u5E76\u8FD4\u56DE task_id\uFF08\u7528 get_background_output \u67E5\u7ED3\u679C\u3001stop_background_task \u7EC8\u6B62\uFF09\u3002",
|
|
7580
7737
|
parameters: {
|
|
7581
7738
|
type: "object",
|
|
@@ -8335,12 +8492,56 @@ function decodeEntities(s) {
|
|
|
8335
8492
|
return cp > 0 && cp <= 1114111 && !(cp >= 55296 && cp <= 57343) ? String.fromCodePoint(cp) : "";
|
|
8336
8493
|
}).replace(/&[a-z#0-9]+;/gi, (e) => named[e.toLowerCase()] ?? e);
|
|
8337
8494
|
}
|
|
8495
|
+
async function searchDeepseek(query, maxResults, signal) {
|
|
8496
|
+
const apiKey = dsApiKey();
|
|
8497
|
+
if (!apiKey) throw new Error("\u672A\u914D\u7F6E DEEP_SEEK_API_KEY\uFF08\u4E0E LLM \u8C03\u7528\u540C key\uFF0CDeepSeek \u63A7\u5236\u53F0\u7533\u8BF7\uFF09");
|
|
8498
|
+
const res = await fetch(`${dsToolsBase()}/tools/search`, {
|
|
8499
|
+
method: "POST",
|
|
8500
|
+
signal,
|
|
8501
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
|
|
8502
|
+
body: JSON.stringify({ query, count: Math.max(1, Math.min(maxResults, 50)) })
|
|
8503
|
+
});
|
|
8504
|
+
if (!res.ok) {
|
|
8505
|
+
const t = await res.text().catch(() => "");
|
|
8506
|
+
throw new Error(`DeepSeek Search HTTP ${res.status} ${res.statusText}${t ? ` | ${t.slice(0, 200)}` : ""}`);
|
|
8507
|
+
}
|
|
8508
|
+
const data = await res.json();
|
|
8509
|
+
const results = [];
|
|
8510
|
+
let budget = DS_SNIPPET_BUDGET_CHARS;
|
|
8511
|
+
for (const r of Array.isArray(data?.results) ? data.results : []) {
|
|
8512
|
+
const title = (r?.title ?? "").toString().trim();
|
|
8513
|
+
const url = (r?.url ?? "").toString().trim();
|
|
8514
|
+
if (!title || !url) continue;
|
|
8515
|
+
const snippet = (r?.summary ?? r?.snippet ?? "").toString().trim();
|
|
8516
|
+
if (results.length > 0 && snippet.length > budget) break;
|
|
8517
|
+
results.push({ title, url, snippet });
|
|
8518
|
+
budget -= snippet.length;
|
|
8519
|
+
}
|
|
8520
|
+
return { answer: "", results };
|
|
8521
|
+
}
|
|
8522
|
+
async function fetchDeepseek(url, signal) {
|
|
8523
|
+
const apiKey = dsApiKey();
|
|
8524
|
+
if (!apiKey) throw new Error("\u672A\u914D\u7F6E DEEP_SEEK_API_KEY\uFF08\u4E0E LLM \u8C03\u7528\u540C key\uFF0CDeepSeek \u63A7\u5236\u53F0\u7533\u8BF7\uFF09");
|
|
8525
|
+
const res = await fetch(`${dsToolsBase()}/tools/fetch`, {
|
|
8526
|
+
method: "POST",
|
|
8527
|
+
signal,
|
|
8528
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
|
|
8529
|
+
body: JSON.stringify({ url })
|
|
8530
|
+
});
|
|
8531
|
+
if (!res.ok) {
|
|
8532
|
+
const t = await res.text().catch(() => "");
|
|
8533
|
+
throw new Error(`DeepSeek Fetch HTTP ${res.status} ${res.statusText}${t ? ` | ${t.slice(0, 200)}` : ""}`);
|
|
8534
|
+
}
|
|
8535
|
+
const data = await res.json();
|
|
8536
|
+
return { title: (data?.title ?? "").toString().trim(), content: (data?.content ?? "").toString() };
|
|
8537
|
+
}
|
|
8338
8538
|
function pickSearchProvider() {
|
|
8339
8539
|
const forced = (process.env.SEARCH_PROVIDER || "").toLowerCase();
|
|
8540
|
+
if (forced === "deepseek" && dsApiKey()) return "deepseek";
|
|
8340
8541
|
if (forced === "tavily" && appConfig.tavilyApiKey) return "tavily";
|
|
8341
8542
|
if (forced === "ddg" || forced === "duckduckgo") return "duckduckgo";
|
|
8342
8543
|
if (forced === "bing") return "bing";
|
|
8343
|
-
return appConfig.tavilyApiKey ? "tavily" : "bing";
|
|
8544
|
+
return dsApiKey() ? "deepseek" : appConfig.tavilyApiKey ? "tavily" : "bing";
|
|
8344
8545
|
}
|
|
8345
8546
|
async function searchTavily(query, maxResults, signal) {
|
|
8346
8547
|
const apiKey = appConfig.tavilyApiKey;
|
|
@@ -8419,10 +8620,18 @@ async function searchDuckDuckGo(query, maxResults, signal) {
|
|
|
8419
8620
|
return { answer: "", results };
|
|
8420
8621
|
}
|
|
8421
8622
|
function formatSearchResults(query, provider, resp) {
|
|
8422
|
-
if (resp.results.length === 0)
|
|
8623
|
+
if (resp.results.length === 0) {
|
|
8624
|
+
const siteArg = query.match(/site:(\S+)/i)?.[1];
|
|
8625
|
+
if (siteArg?.includes("/")) {
|
|
8626
|
+
const host = siteArg.split("/")[0];
|
|
8627
|
+
return `[web_search | "${query}" | ${provider}]\uFF1A0 \u547D\u4E2D\u2014\u2014site: \u540E\u5E26\u4E86\u8DEF\u5F84\uFF08site: \u53EA\u63A5\u57DF\u540D\uFF0C\u5E26\u8DEF\u5F84\u6052 0 \u6761\uFF09\u3002\u6539\u7528\uFF1Asite:${host} \u9875\u9762\u6807\u9898\u6216\u4E3B\u9898\u5173\u952E\u8BCD`;
|
|
8628
|
+
}
|
|
8629
|
+
return `[web_search | "${query}" | ${provider}]\uFF1A\u672A\u627E\u5230\u76F8\u5173\u7ED3\u679C\u3002`;
|
|
8630
|
+
}
|
|
8631
|
+
const snippetCap = provider === "deepseek" ? DS_SUMMARY_SLICE_CHARS : 240;
|
|
8423
8632
|
const lines = resp.results.map((r, i) => `${i + 1}. ${r.title}
|
|
8424
8633
|
${r.url}
|
|
8425
|
-
${r.snippet.slice(0,
|
|
8634
|
+
${r.snippet.slice(0, snippetCap)}`);
|
|
8426
8635
|
const body = (resp.answer ? `\u{1F4A1} \u6458\u8981: ${resp.answer}
|
|
8427
8636
|
|
|
8428
8637
|
` : "") + lines.join("\n\n");
|
|
@@ -8473,7 +8682,7 @@ async function safeFetchFollow(startUrl, baseHeaders, signals, allowPrivate = fa
|
|
|
8473
8682
|
return res;
|
|
8474
8683
|
}
|
|
8475
8684
|
}
|
|
8476
|
-
var FETCH_TIMEOUT_MS2, SEARCH_TIMEOUT_MS, DEFAULT_MAX_CHARS, USER_AGENT, BROWSER_UA, TAVILY_ENDPOINT, formatHostBlockMessage, MAX_REDIRECTS, pinnedSsrfDispatcher, pinnedMetadataGuardDispatcher, readBodyCapped, webTools;
|
|
8685
|
+
var FETCH_TIMEOUT_MS2, SEARCH_TIMEOUT_MS, DEFAULT_MAX_CHARS, USER_AGENT, BROWSER_UA, TAVILY_ENDPOINT, formatHostBlockMessage, TOOL_CALL_PRICE_YUAN, dsToolsBase, dsApiKey, DS_SNIPPET_BUDGET_CHARS, DS_FETCH_TIMEOUT_MS, DS_SUMMARY_SLICE_CHARS, MAX_REDIRECTS, pinnedSsrfDispatcher, pinnedMetadataGuardDispatcher, readBodyCapped, webTools;
|
|
8477
8686
|
var init_web = __esm({
|
|
8478
8687
|
"../core/src/tool/registry/web.ts"() {
|
|
8479
8688
|
"use strict";
|
|
@@ -8503,6 +8712,12 @@ var init_web = __esm({
|
|
|
8503
8712
|
return `\u76EE\u6807\u4E3B\u673A ${h} DNS \u89E3\u6790\u5931\u8D25\uFF08${v.detail || "\u672A\u77E5\u9519\u8BEF"}\uFF09\uFF0C\u975E SSRF \u62E6\u622A\uFF1B\u5EFA\u8BAE\u68C0\u67E5\u7F51\u7EDC/DNS \u6216\u6539\u7528 web_search`;
|
|
8504
8713
|
}
|
|
8505
8714
|
};
|
|
8715
|
+
TOOL_CALL_PRICE_YUAN = { search: 5e-3, fetch: 5e-3 };
|
|
8716
|
+
dsToolsBase = () => (process.env.DEEP_SEEK_API_URL || "https://api.deepseek.com").replace(/\/+$/, "");
|
|
8717
|
+
dsApiKey = () => (process.env.DEEP_SEEK_API_KEY || "").trim();
|
|
8718
|
+
DS_SNIPPET_BUDGET_CHARS = 14e3;
|
|
8719
|
+
DS_FETCH_TIMEOUT_MS = 3e4;
|
|
8720
|
+
DS_SUMMARY_SLICE_CHARS = 3e3;
|
|
8506
8721
|
MAX_REDIRECTS = 5;
|
|
8507
8722
|
pinnedSsrfDispatcher = new Agent({
|
|
8508
8723
|
connect: {
|
|
@@ -8583,13 +8798,14 @@ var init_web = __esm({
|
|
|
8583
8798
|
planAllowed: true,
|
|
8584
8799
|
primaryArg: "url",
|
|
8585
8800
|
autoApproval: "aggressive",
|
|
8586
|
-
description:
|
|
8801
|
+
description: '\u6293\u53D6\u6307\u5B9A\u516C\u5F00 URL \u7684\u7F51\u9875\u5185\u5BB9\u5E76\u8F6C\u4E3A\u5E72\u51C0\u7684 Markdown \u6587\u672C\uFF0C\u7528\u4E8E\u67E5\u9605 API \u6587\u6863\u3001\u62A5\u9519\u8BF4\u660E\u3001\u5B98\u65B9 changelog \u7B49\u5916\u90E8\u8D44\u6599\u3002\u4EC5\u652F\u6301 http/https\uFF0C\u9ED8\u8BA4\u62E6\u622A\u5185\u7F51/\u56DE\u73AF/\u94FE\u8DEF\u672C\u5730\u5730\u5740\uFF08\u9632 SSRF\uFF09\uFF1B\u672C\u5730\u5F00\u53D1/\u81EA\u52A8\u5316\u6D4B\u8BD5\u9700\u6293 localhost \u670D\u52A1\u6216\u5185\u7F51\u9875\u9762\u65F6\uFF0C\u4F20 allow_private=true \u653E\u884C\uFF08\u4E91\u5143\u6570\u636E\u7AEF\u70B9\u4ECD\u62E6\uFF09\u3002\u53EF\u9009 backend="deepseek" \u8D70 DeepSeek \u670D\u52A1\u7AEF\u6293\u53D6\uFF08\u8FD4\u56DE markdown \u6B63\u6587\uFF1B\u8BE5\u540E\u7AEF\u4E0B allow_private \u65E0\u6548\u2014\u2014\u6293\u53D6\u4E0D\u7ECF\u8FC7\u672C\u673A\u7F51\u7EDC\uFF0CSSRF \u9632\u62A4\u662F\u672C\u673A\u8BED\u4E49\uFF09\u3002',
|
|
8587
8802
|
parameters: {
|
|
8588
8803
|
type: "object",
|
|
8589
8804
|
properties: {
|
|
8590
8805
|
url: { type: "string", description: "\u8981\u6293\u53D6\u7684\u5B8C\u6574 http(s) URL" },
|
|
8806
|
+
backend: { type: "string", enum: ["local", "deepseek"], description: "\u6293\u53D6\u540E\u7AEF\uFF1Alocal=\u672C\u673A\u76F4\u6293\uFF08\u7F3A\u7701\uFF0C\u5E26\u5168\u5957 SSRF \u9632\u62A4\uFF09\uFF1Bdeepseek=DeepSeek \u670D\u52A1\u7AEF\u6293\u53D6\uFF08\u53CD\u722C\u53D7\u9650\uFF0C\u5931\u8D25\u65F6\u53EF\u8BD5 web_search \u7528 site: \u8BED\u6CD5\u4ECE\u7D22\u5F15\u4FA7\u62FF\u6B63\u6587\uFF09\u3002" },
|
|
8591
8807
|
max_length: { type: "number", description: `\u8FD4\u56DE\u5185\u5BB9\u7684\u6700\u5927\u5B57\u7B26\u6570\uFF08\u9ED8\u8BA4 ${DEFAULT_MAX_CHARS}\uFF0C\u8D85\u51FA\u81EA\u52A8\u622A\u65AD\uFF09` },
|
|
8592
|
-
allow_private: { type: "boolean", description: "\u662F\u5426\u5141\u8BB8\u8BBF\u95EE\u5185\u7F51/\u56DE\u73AF\u5730\u5740\uFF08\u5982 localhost:5173 \u672C\u5730\u670D\u52A1\u3001\u5185\u7F51\u6587\u6863\uFF09\u3002\u9ED8\u8BA4 false\u3002\u4E5F\u53EF\u7528\u73AF\u5883\u53D8\u91CF WEB_FETCH_ALLOW_PRIVATE=1 \u5168\u5C40\u5F00\u542F\u3002\u5373\u4FBF\u5F00\u542F\u4ECD\u62E6\u622A\u4E91\u5143\u6570\u636E\u7AEF\u70B9\uFF08169.254.169.254 \u7B49\uFF09\u3002" }
|
|
8808
|
+
allow_private: { type: "boolean", description: "\u662F\u5426\u5141\u8BB8\u8BBF\u95EE\u5185\u7F51/\u56DE\u73AF\u5730\u5740\uFF08\u5982 localhost:5173 \u672C\u5730\u670D\u52A1\u3001\u5185\u7F51\u6587\u6863\uFF09\u3002\u9ED8\u8BA4 false\u3002\u4E5F\u53EF\u7528\u73AF\u5883\u53D8\u91CF WEB_FETCH_ALLOW_PRIVATE=1 \u5168\u5C40\u5F00\u542F\u3002\u5373\u4FBF\u5F00\u542F\u4ECD\u62E6\u622A\u4E91\u5143\u6570\u636E\u7AEF\u70B9\uFF08169.254.169.254 \u7B49\uFF09\u3002\u4EC5 local \u540E\u7AEF\u6709\u6548\uFF1Bbackend=deepseek \u65F6\u5FFD\u7565\u3002" }
|
|
8593
8809
|
},
|
|
8594
8810
|
required: ["url"]
|
|
8595
8811
|
},
|
|
@@ -8602,6 +8818,44 @@ var init_web = __esm({
|
|
|
8602
8818
|
\uFF08\u5C06\u53D1\u8D77\u5BF9\u5916\u7F51\u7EDC\u8BF7\u6C42\uFF0C\u4E14\u6293\u53D6\u5230\u7684\u5185\u5BB9\u4F1A\u8FDB\u5165\u4E91\u7AEF\u6A21\u578B\u4E0A\u4E0B\u6587\uFF1B\u8BF7\u786E\u8BA4 URL \u6765\u6E90\u53EF\u4FE1\u3001\u4E0D\u542B\u654F\u611F\u56DE\u4F20\u6570\u636E\uFF09`,
|
|
8603
8819
|
async execute(args, ctx) {
|
|
8604
8820
|
const maxChars = args.max_length && args.max_length > 0 ? args.max_length : DEFAULT_MAX_CHARS;
|
|
8821
|
+
if (args.backend === "deepseek") {
|
|
8822
|
+
let dsUrl;
|
|
8823
|
+
try {
|
|
8824
|
+
dsUrl = new URL(args.url);
|
|
8825
|
+
} catch {
|
|
8826
|
+
return toolFailure(`[\u6293\u53D6\u5931\u8D25]\uFF1AURL \u683C\u5F0F\u4E0D\u5408\u6CD5\uFF1A${args.url}`);
|
|
8827
|
+
}
|
|
8828
|
+
if (dsUrl.protocol !== "http:" && dsUrl.protocol !== "https:") {
|
|
8829
|
+
return toolFailure(`[\u5B89\u5168\u7194\u65AD]\uFF1A\u4EC5\u5141\u8BB8 http/https \u534F\u8BAE\uFF0C\u62D2\u7EDD ${dsUrl.protocol}`);
|
|
8830
|
+
}
|
|
8831
|
+
const dsSignals = [AbortSignal.timeout(DS_FETCH_TIMEOUT_MS)];
|
|
8832
|
+
if (ctx?.abortSignal) dsSignals.push(ctx.abortSignal);
|
|
8833
|
+
try {
|
|
8834
|
+
const { title, content } = await fetchDeepseek(dsUrl.href, AbortSignal.any(dsSignals));
|
|
8835
|
+
ctx?.toolCostSink?.(TOOL_CALL_PRICE_YUAN.fetch);
|
|
8836
|
+
const body = title ? `# ${title}
|
|
8837
|
+
|
|
8838
|
+
${content}` : content;
|
|
8839
|
+
if (body.length > maxChars) {
|
|
8840
|
+
return [
|
|
8841
|
+
`[web_fetch | deepseek | ${dsUrl.href} | \u5DF2\u622A\u65AD\u524D ${maxChars} \u5B57\u7B26\uFF0C\u539F\u6587\u5171 ${body.length} \u5B57\u7B26]`,
|
|
8842
|
+
body.slice(0, maxChars),
|
|
8843
|
+
`
|
|
8844
|
+
[... \u26A0\uFE0F \u5185\u5BB9\u8FC7\u957F\uFF0C\u5DF2\u9690\u85CF\u5269\u4F59 ${body.length - maxChars} \u5B57\u7B26\uFF0C\u53EF\u8C03\u5927 max_length ...]`
|
|
8845
|
+
].join("\n");
|
|
8846
|
+
}
|
|
8847
|
+
return `[web_fetch | deepseek | ${dsUrl.href}]
|
|
8848
|
+
${body}`;
|
|
8849
|
+
} catch (error) {
|
|
8850
|
+
if (error?.name === "TimeoutError") {
|
|
8851
|
+
return toolFailure(`[\u6293\u53D6\u8D85\u65F6]\uFF1A${DS_FETCH_TIMEOUT_MS / 1e3}s \u5185\u672A\u54CD\u5E94\uFF1A${dsUrl.href}`);
|
|
8852
|
+
}
|
|
8853
|
+
if (error?.name === "AbortError") {
|
|
8854
|
+
return `\u23F9\uFE0F [\u5DF2\u4E2D\u6B62]\uFF1A\u7528\u6237\u4E2D\u65AD\u4E86\u6293\u53D6\uFF1A${dsUrl.href}`;
|
|
8855
|
+
}
|
|
8856
|
+
return toolFailure(`[\u6293\u53D6\u5931\u8D25 | deepseek]\uFF1A${error.message}\uFF08\u8BE5\u540E\u7AEF\u5BF9\u53CD\u722C/\u56FD\u9645\u7AD9\u70B9\u540C\u6837\u53D7\u9650\uFF1B\u53EF\u6539\u7528 web_search \u4ECE\u7D22\u5F15\u4FA7\u62FF\u6B63\u6587\u2014\u2014site: \u53EA\u63A5\u57DF\u540D\u4E0D\u5E26\u8DEF\u5F84\uFF0C\u4F8B\uFF1Asite:${dsUrl.hostname} \u9875\u9762\u6807\u9898\u6216\u4E3B\u9898\u5173\u952E\u8BCD\uFF09`);
|
|
8857
|
+
}
|
|
8858
|
+
}
|
|
8605
8859
|
const allowPrivate = args.allow_private ?? appConfig.webFetchAllowPrivate;
|
|
8606
8860
|
let parsed;
|
|
8607
8861
|
try {
|
|
@@ -8666,7 +8920,7 @@ ${body}${truncNote}`;
|
|
|
8666
8920
|
planAllowed: true,
|
|
8667
8921
|
primaryArg: "query",
|
|
8668
8922
|
autoApproval: "aggressive",
|
|
8669
|
-
description: "\u8054\u7F51\u641C\u7D22\u5916\u90E8\u8D44\u6599\uFF08API \u6587\u6863\u3001\u62A5\u9519\u89E3\u6CD5\u3001\u5E93\u7528\u6CD5\u3001\u6700\u65B0\u4FE1\u606F\u7B49\uFF09\uFF0C\u8FD4\u56DE\u7ED3\u679C\u5217\u8868\uFF08\u6807\u9898/URL/\
|
|
8923
|
+
description: "\u8054\u7F51\u641C\u7D22\u5916\u90E8\u8D44\u6599\uFF08API \u6587\u6863\u3001\u62A5\u9519\u89E3\u6CD5\u3001\u5E93\u7528\u6CD5\u3001\u6700\u65B0\u4FE1\u606F\u7B49\uFF09\uFF0C\u8FD4\u56DE\u7ED3\u679C\u5217\u8868\uFF08\u6807\u9898/URL/\u6458\u8981\uFF09\u3002\u540E\u7AEF\u81EA\u52A8\u9009\u62E9\uFF1A\u914D\u7F6E\u4E86 DEEP_SEEK_API_KEY\uFF08\u901A\u5E38\u5DF2\u914D\uFF09\u9ED8\u8BA4\u8D70 DeepSeek \u81EA\u5BB6\u641C\u7D22\u5F15\u64CE\uFF08\u957F\u6458\u8981\u8D28\u91CF\u6700\u4F73\uFF0C\xA50.005/\u6B21\u6309\u6B21\u8BA1\u8D39\u8FDB\u7ED3\u7B97\u5355\uFF09\uFF1B\u5426\u5219\u6709 TAVILY_API_KEY \u7528 Tavily\uFF0C\u518D\u5426\u5219 Bing\uFF08\u514D\u6CE8\u518C\uFF09\u3002\u53EF\u7528 SEARCH_PROVIDER=deepseek|tavily|bing|ddg \u5F3A\u5236\u6307\u5B9A\uFF08deepseek \u5F02\u5E38\u65F6\u53EF\u4E34\u65F6\u5207 bing \u56DE\u9000\uFF09\u3002\u67E5\u8BE2\u964C\u751F\u5E93\u6216\u62A5\u9519\u65F6\u4F18\u5148\u7528\u672C\u5DE5\u5177\u66FF\u4EE3\u51ED\u8BB0\u5FC6\u4F5C\u7B54\u3002\u7ED3\u679C\u6309\u957F\u5EA6\u7194\u65AD\u3002",
|
|
8670
8924
|
parameters: {
|
|
8671
8925
|
type: "object",
|
|
8672
8926
|
properties: {
|
|
@@ -8689,7 +8943,17 @@ ${body}${truncNote}`;
|
|
|
8689
8943
|
if (ctx?.abortSignal) signals.push(ctx.abortSignal);
|
|
8690
8944
|
try {
|
|
8691
8945
|
const signal = AbortSignal.any(signals);
|
|
8692
|
-
|
|
8946
|
+
let resp;
|
|
8947
|
+
if (provider === "deepseek") {
|
|
8948
|
+
resp = await searchDeepseek(args.query, maxResults, signal);
|
|
8949
|
+
ctx?.toolCostSink?.(TOOL_CALL_PRICE_YUAN.search);
|
|
8950
|
+
} else if (provider === "tavily") {
|
|
8951
|
+
resp = await searchTavily(args.query, maxResults, signal);
|
|
8952
|
+
} else if (provider === "duckduckgo") {
|
|
8953
|
+
resp = await searchDuckDuckGo(args.query, maxResults, signal);
|
|
8954
|
+
} else {
|
|
8955
|
+
resp = await searchBing(args.query, maxResults, signal);
|
|
8956
|
+
}
|
|
8693
8957
|
return formatSearchResults(args.query, provider, resp);
|
|
8694
8958
|
} catch (error) {
|
|
8695
8959
|
if (error?.name === "TimeoutError") {
|
|
@@ -8698,7 +8962,8 @@ ${body}${truncNote}`;
|
|
|
8698
8962
|
if (error?.name === "AbortError") {
|
|
8699
8963
|
return `\u23F9\uFE0F [\u5DF2\u4E2D\u6B62]\uFF1A\u7528\u6237\u4E2D\u65AD\u4E86\u641C\u7D22\u3002`;
|
|
8700
8964
|
}
|
|
8701
|
-
|
|
8965
|
+
const hint = provider === "deepseek" ? "\uFF08\u53EF\u8BBE SEARCH_PROVIDER=bing \u4E34\u65F6\u5207\u56DE\u672C\u5730\u722C\u866B\u540E\u7AEF\uFF09" : "";
|
|
8966
|
+
return toolFailure(`[web_search \u5931\u8D25 | ${provider}]\uFF1A${error.message}${hint}`);
|
|
8702
8967
|
}
|
|
8703
8968
|
}
|
|
8704
8969
|
}
|
|
@@ -10162,6 +10427,8 @@ var init_ask = __esm({
|
|
|
10162
10427
|
type: "function",
|
|
10163
10428
|
function: {
|
|
10164
10429
|
name: "ask_question",
|
|
10430
|
+
// ★ P1-6 声明化:阻塞等用户作答,不参与并行调度(原 canParallelize 按名排除退役为声明否决)
|
|
10431
|
+
parallelizable: false,
|
|
10165
10432
|
description: [
|
|
10166
10433
|
"\u5411\u7528\u6237\u63D0\u51FA\u591A\u9009\u95EE\u9898\u5E76\u7B49\u5F85\u4F5C\u7B54\uFF1A\u6280\u672F\u65B9\u6848\u4E8C\u9009\u4E00\u3001\u8DEF\u5F84\u53D6\u820D\u3001\u8303\u56F4\u786E\u8BA4\u7B49\u9700\u7528\u6237\u62CD\u677F\u7684\u51B3\u7B56\u6216\u6B67\u4E49\u6F84\u6E05\u2014\u2014\u52FF\u7528\u4E8E\u80FD\u4ECE\u4EE3\u7801/\u4E0A\u4E0B\u6587\u81EA\u884C\u786E\u5B9A\u7684\u95EE\u9898\u3002",
|
|
10167
10434
|
"2-4 \u4E2A\u9009\u9879\uFF08label + \u53EF\u9009 description\uFF09\uFF1BmultiSelect=true \u591A\u9009\uFF08\u9ED8\u8BA4\u5355\u9009\uFF09\u3002\u7528\u6237\u53D6\u6D88\u8FD4\u56DE\u7A7A\u9009\u62E9\uFF08\u6539\u7528\u9ED8\u8BA4\u6216\u91CD\u65B0\u63D0\u95EE\uFF09\u3002",
|
|
@@ -11571,11 +11838,12 @@ var init_backgroundTool = __esm({
|
|
|
11571
11838
|
|
|
11572
11839
|
// ../core/src/tool/autoPermission.ts
|
|
11573
11840
|
import path35 from "path";
|
|
11574
|
-
var isMcpTool, BUILTIN_AUTO_DENY, matchBuiltinDeny, COMMAND_DENY, matchCommandDeny, hasShellMetachars, READONLY_HEADS, PKG_MGR, SCRIPT_RUNNER_HEADS, SCRIPT_RUNNER_READONLY, isScriptRunnerCommand, hasWriteModifier, SENSITIVE_FILE_ARG_RE, hasSensitiveFileArg, isReadOnlyCommand, isWithinWorkspace, runAutoCheck;
|
|
11841
|
+
var isMcpTool, BUILTIN_AUTO_DENY, matchBuiltinDeny, COMMAND_DENY, matchCommandDeny, hasShellMetachars, READONLY_HEADS, PKG_MGR, SCRIPT_RUNNER_HEADS, SCRIPT_RUNNER_READONLY, isScriptRunnerCommand, INTERP_EXE, INTERP_BARE_EXE, REPO_SCRIPT_TOKEN, isInterpreterRepoScript, hasWriteModifier, SENSITIVE_FILE_ARG_RE, hasSensitiveFileArg, isReadOnlyCommand, isWithinWorkspace, runAutoCheck;
|
|
11575
11842
|
var init_autoPermission = __esm({
|
|
11576
11843
|
"../core/src/tool/autoPermission.ts"() {
|
|
11577
11844
|
"use strict";
|
|
11578
11845
|
init_model();
|
|
11846
|
+
init_type();
|
|
11579
11847
|
isMcpTool = (name) => name.startsWith("mcp__");
|
|
11580
11848
|
BUILTIN_AUTO_DENY = [
|
|
11581
11849
|
/(^|[\\/])\.env(\.|$)/i,
|
|
@@ -11694,6 +11962,20 @@ var init_autoPermission = __esm({
|
|
|
11694
11962
|
if (SCRIPT_RUNNER_READONLY.some((re) => re.test(c))) return false;
|
|
11695
11963
|
return SCRIPT_RUNNER_HEADS.some((re) => re.test(c));
|
|
11696
11964
|
};
|
|
11965
|
+
INTERP_EXE = String.raw`\s*([^\s"'\`]*[\\/])?(node|deno|bun|tsx|ts-node|python3?|py|ruby|perl|php)(\.exe|\.cmd|\.bat|\.ps1)?\b`;
|
|
11966
|
+
INTERP_BARE_EXE = String.raw`\s*([^\s"'\`]*[\\/])?(tsc|make|cargo|go)(\.exe|\.cmd|\.bat|\.ps1)?\b`;
|
|
11967
|
+
REPO_SCRIPT_TOKEN = /^(?![A-Za-z]:)(?![\\/])(?:\.{1,2}[\\/])?(?:[^\\/: \t"']+[\\/])*[^\\/: \t"']+\.(?:js|mjs|cjs|ts|mts|cts|tsx|jsx|py|pyw|sh|bash|rb|pl|php)$/i;
|
|
11968
|
+
isInterpreterRepoScript = (cmd) => {
|
|
11969
|
+
const c = (cmd || "").trim();
|
|
11970
|
+
if (!c || hasShellMetachars(c)) return false;
|
|
11971
|
+
const head = new RegExp(`^${INTERP_EXE}`, "i");
|
|
11972
|
+
const bare = new RegExp(`^${INTERP_BARE_EXE}`, "i");
|
|
11973
|
+
if (bare.test(c)) return true;
|
|
11974
|
+
const m = head.exec(c);
|
|
11975
|
+
if (!m) return false;
|
|
11976
|
+
const rest = c.slice(m[0].length);
|
|
11977
|
+
return rest.split(/\s+/).filter(Boolean).some((t) => !t.startsWith("-") && REPO_SCRIPT_TOKEN.test(t));
|
|
11978
|
+
};
|
|
11697
11979
|
hasWriteModifier = (cmd) => {
|
|
11698
11980
|
if (!cmd) return false;
|
|
11699
11981
|
if (/^sed\b/.test(cmd) && /(^|\s)-i\b/.test(cmd)) return true;
|
|
@@ -11733,13 +12015,17 @@ var init_autoPermission = __esm({
|
|
|
11733
12015
|
if (!isWithinWorkspace(s, ctx.cwd)) return "ask";
|
|
11734
12016
|
if (matchBuiltinDeny(s)) return "deny";
|
|
11735
12017
|
}
|
|
12018
|
+
const fastPath = decl.safetyLevel !== "danger" /* DANGER */ && (decl.triggersUndo === "overwrite" || decl.triggersUndo === "create");
|
|
12019
|
+
return fastPath ? "allow" : "ask";
|
|
11736
12020
|
}
|
|
11737
12021
|
if (decl?.autoApproval === "command") {
|
|
11738
12022
|
const argKey = decl.primaryArg ?? "command";
|
|
11739
12023
|
const cmd = typeof args?.[argKey] === "string" ? args[argKey] : "";
|
|
11740
12024
|
if (matchCommandDeny(cmd)) return "deny";
|
|
11741
12025
|
}
|
|
11742
|
-
|
|
12026
|
+
ctx.emitProgress?.("\u{1F512} \u5B89\u5168\u5224\u5B9A\u4E2D\u2026\uFF08\u8F85\u52A9\u6A21\u578B\u5206\u7C7B\uFF0C\u6700\u957F 10s\uFF09");
|
|
12027
|
+
const v = await classifyToolRisk(name, args, "", ctx.abortSignal, ctx.onAuxUsage);
|
|
12028
|
+
ctx.emitProgress?.(v === "safe" ? "\u{1F512} \u5B89\u5168\u5224\u5B9A\uFF1A\u653E\u884C" : "\u{1F512} \u5B89\u5168\u5224\u5B9A\uFF1A\u672A\u653E\u884C\uFF08\u8F6C\u4EBA\u5DE5\u5BA1\u6279\uFF09");
|
|
11743
12029
|
return v === "safe" ? "allow" : "ask";
|
|
11744
12030
|
};
|
|
11745
12031
|
}
|
|
@@ -11747,7 +12033,7 @@ var init_autoPermission = __esm({
|
|
|
11747
12033
|
|
|
11748
12034
|
// ../core/src/agent/toolExecution.ts
|
|
11749
12035
|
import path36 from "path";
|
|
11750
|
-
var applyPrivacyMasking, scaleTruncBudget, processToolCall;
|
|
12036
|
+
var applyPrivacyMasking, scaleTruncBudget, failResult, emitValidationFailed, resolveEarlyOutcome, runSecurityGates, executeTool, finalizeOutcome, processToolCall;
|
|
11751
12037
|
var init_toolExecution = __esm({
|
|
11752
12038
|
"../core/src/agent/toolExecution.ts"() {
|
|
11753
12039
|
"use strict";
|
|
@@ -11787,47 +12073,24 @@ var init_toolExecution = __esm({
|
|
|
11787
12073
|
const factor = 1 - (fill - 0.5) / 0.35 * 0.6;
|
|
11788
12074
|
return Math.max(floor, Math.floor(base * factor));
|
|
11789
12075
|
};
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
requestApproval: hostRequestApproval,
|
|
11804
|
-
requestQuestion: hostRequestQuestion,
|
|
11805
|
-
ideAction,
|
|
11806
|
-
keepRecentUnits,
|
|
11807
|
-
compactRatio,
|
|
11808
|
-
modelWindow,
|
|
11809
|
-
parentSystemPrompt,
|
|
11810
|
-
readTracker,
|
|
11811
|
-
reportCost
|
|
11812
|
-
} = ctx;
|
|
11813
|
-
let calledName = "";
|
|
11814
|
-
let calledArgs = {};
|
|
11815
|
-
let parseFailed = false;
|
|
11816
|
-
if (toolCall.type === "function") {
|
|
11817
|
-
calledName = toolCall.function.name;
|
|
11818
|
-
try {
|
|
11819
|
-
calledArgs = JSON.parse(toolCall.function.arguments || "{}");
|
|
11820
|
-
} catch {
|
|
11821
|
-
parseFailed = true;
|
|
11822
|
-
}
|
|
11823
|
-
console.log(`\u{1F916} \u6A21\u578B\u8BF7\u6C42\u8C03\u7528\u5DE5\u5177: ${calledName}\uFF0C\u53C2\u6570:`, calledArgs);
|
|
11824
|
-
}
|
|
11825
|
-
if (calledName === "exit_plan_mode") {
|
|
12076
|
+
failResult = (state, text, cat = "runtime") => {
|
|
12077
|
+
state.result = text;
|
|
12078
|
+
state.resultStatus = "failed";
|
|
12079
|
+
state.errorCategory = cat;
|
|
12080
|
+
};
|
|
12081
|
+
emitValidationFailed = (ctx, toolCall, state) => {
|
|
12082
|
+
ctx.events({ sessionId: ctx.sessionId, eventType: "tool.validation.failed", metadata: { depth: ctx.depth, decisionSource: ctx.llmDecisionSource, durationMs: performance.now() - ctx.startTime, round: ctx.round, tools_id: toolCall.id, toolName: state.calledName, toolSource: "builtin", ok: false, attempt: ctx.round }, payload: { output: state.result } });
|
|
12083
|
+
};
|
|
12084
|
+
resolveEarlyOutcome = (ctx, toolCall, state) => {
|
|
12085
|
+
const { signal, planMode, planWriteRejections } = ctx;
|
|
12086
|
+
const { calledName, calledArgs, matchedTool, parseFailed } = state;
|
|
12087
|
+
const declaredTerminal = matchedTool?.function?.terminal;
|
|
12088
|
+
if (declaredTerminal === "plan_exit" || !declaredTerminal && calledName === "exit_plan_mode") {
|
|
11826
12089
|
const plan = typeof calledArgs?.plan === "string" ? calledArgs.plan : "";
|
|
11827
12090
|
const note = "\u2705 [\u8BA1\u5212\u6A21\u5F0F] \u5B9E\u73B0\u65B9\u6848\u5DF2\u63D0\u4EA4\uFF0C\u7B49\u5F85\u7528\u6237\u5BA1\u6279\u540E\u8FDB\u5165\u5B9E\u73B0\u9636\u6BB5\u3002";
|
|
11828
12091
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note, resultForUser: note, ok: true, terminal: { kind: "exit_plan_mode", plan } };
|
|
11829
12092
|
}
|
|
11830
|
-
if (calledName === "enter_plan_mode") {
|
|
12093
|
+
if (declaredTerminal === "plan_enter" || !declaredTerminal && calledName === "enter_plan_mode") {
|
|
11831
12094
|
if (planMode) {
|
|
11832
12095
|
const note2 = "\uFF08\u5DF2\u5728\u8BA1\u5212\u6A21\u5F0F\u4E2D\uFF0C\u65E0\u9700\u91CD\u590D\u8FDB\u5165\u3002\u8BF7\u7EE7\u7EED\u53EA\u8BFB\u8C03\u7814\uFF0C\u5B8C\u6210\u540E\u8C03 exit_plan_mode \u63D0\u4EA4\u65B9\u6848\u3002\uFF09";
|
|
11833
12096
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note2, resultForUser: note2, ok: true };
|
|
@@ -11840,227 +12103,294 @@ var init_toolExecution = __esm({
|
|
|
11840
12103
|
const placeholder = "\uFF08\u5DF2\u4E2D\u6B62\uFF0C\u672A\u6267\u884C\uFF09";
|
|
11841
12104
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: placeholder, resultForUser: placeholder, ok: false, aborted: true };
|
|
11842
12105
|
}
|
|
11843
|
-
|
|
11844
|
-
|
|
12106
|
+
if (!parseFailed && planMode && appConfig.planEnforcement === "runtime" && calledName !== "enter_plan_mode" && calledName !== "exit_plan_mode" && declaredTerminal !== "plan_enter" && declaredTerminal !== "plan_exit" && matchedTool?.function?.planAllowed !== true) {
|
|
12107
|
+
if (matchedTool && matchedTool.function.safetyLevel !== "safe" /* SAFE */) planWriteRejections?.(1);
|
|
11845
12108
|
const note = `\u274C [\u8BA1\u5212\u6A21\u5F0F] \u5F53\u524D\u4E3A\u53EA\u8BFB\u8C03\u7814\u9636\u6BB5\uFF0C\u7981\u6B62 ${calledName}\u3002\u5B8C\u6210\u65B9\u6848\u8BF7\u8C03 exit_plan_mode \u63D0\u4EA4\uFF0C\u7ECF\u7528\u6237\u5BA1\u6279\u540E\u8FDB\u5165\u5B9E\u73B0\u9636\u6BB5\u3002`;
|
|
11846
|
-
|
|
12109
|
+
state.result = note;
|
|
12110
|
+
emitValidationFailed(ctx, toolCall, state);
|
|
11847
12111
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note, resultForUser: note, ok: false };
|
|
11848
12112
|
}
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
const
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
let needApproval = level === "mutation" /* MUTATION */ || level === "danger" /* DANGER */;
|
|
11866
|
-
let denied = false;
|
|
11867
|
-
let preHooksAdvanced = false;
|
|
11868
|
-
if (appConfig.hookRewrite) {
|
|
11869
|
-
preHooksAdvanced = true;
|
|
11870
|
-
let veto;
|
|
11871
|
-
try {
|
|
11872
|
-
veto = await runPreHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, toolCtx);
|
|
11873
|
-
} catch (e) {
|
|
11874
|
-
veto = { deny: true, reason: `\u274C [Pre-hook \u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002` };
|
|
11875
|
-
}
|
|
11876
|
-
if (veto.deny) {
|
|
11877
|
-
denied = true;
|
|
11878
|
-
failResult(veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
|
|
11879
|
-
} else if (veto.argsOverride !== void 0 && veto.argsOverride !== null && typeof veto.argsOverride === "object" && !Array.isArray(veto.argsOverride)) {
|
|
11880
|
-
console.log(`\u270F\uFE0F [Pre-hook] [${calledName}] args \u5DF2\u88AB hook \u6539\u5199`);
|
|
11881
|
-
calledArgs = veto.argsOverride;
|
|
11882
|
-
}
|
|
12113
|
+
return null;
|
|
12114
|
+
};
|
|
12115
|
+
runSecurityGates = async (ctx, toolCall, state, toolCtx) => {
|
|
12116
|
+
const { sessionId, permissionMode } = ctx;
|
|
12117
|
+
const { calledName, calledArgs, matchedTool } = state;
|
|
12118
|
+
const level = matchedTool.function.safetyLevel;
|
|
12119
|
+
let needApproval = level === "mutation" /* MUTATION */ || level === "danger" /* DANGER */;
|
|
12120
|
+
let autoGateAsked = false;
|
|
12121
|
+
let preHooksAdvanced = false;
|
|
12122
|
+
if (appConfig.hookRewrite) {
|
|
12123
|
+
preHooksAdvanced = true;
|
|
12124
|
+
let veto;
|
|
12125
|
+
try {
|
|
12126
|
+
veto = await runPreHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, toolCtx);
|
|
12127
|
+
} catch (e) {
|
|
12128
|
+
veto = { deny: true, reason: `\u274C [Pre-hook \u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002` };
|
|
11883
12129
|
}
|
|
11884
|
-
{
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
|
|
12130
|
+
if (veto.deny) {
|
|
12131
|
+
state.denied = true;
|
|
12132
|
+
failResult(state, veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
|
|
12133
|
+
} else if (veto.argsOverride !== void 0 && veto.argsOverride !== null && typeof veto.argsOverride === "object" && !Array.isArray(veto.argsOverride)) {
|
|
12134
|
+
console.log(`\u270F\uFE0F [Pre-hook] [${calledName}] args \u5DF2\u88AB hook \u6539\u5199`);
|
|
12135
|
+
state.calledArgs = veto.argsOverride;
|
|
12136
|
+
}
|
|
12137
|
+
}
|
|
12138
|
+
{
|
|
12139
|
+
const paramsSchema = matchedTool.function.parameters;
|
|
12140
|
+
if (paramsSchema && typeof paramsSchema === "object" && !Array.isArray(paramsSchema)) {
|
|
12141
|
+
const verdict = validateToolArgs(paramsSchema, state.calledArgs);
|
|
12142
|
+
if (!verdict.ok) {
|
|
12143
|
+
state.denied = true;
|
|
12144
|
+
failResult(state, `\u274C \u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF1A${verdict.message}\uFF1B\u8BF7\u4FEE\u6B63\u53C2\u6570\u540E\u91CD\u8BD5\uFF08schema \u8981\u6C42\u89C1\u5DE5\u5177 parameters\uFF09\u3002`, "syntax");
|
|
12145
|
+
emitValidationFailed(ctx, toolCall, state);
|
|
11893
12146
|
}
|
|
11894
12147
|
}
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
12148
|
+
}
|
|
12149
|
+
const pathArgs = matchedTool.function.pathArgs ?? (matchedTool.function.triggersUndo ? ["path"] : []);
|
|
12150
|
+
const protectedPaths = pathArgs.map((k) => state.calledArgs?.[k]);
|
|
12151
|
+
const hitProtected = protectedPaths.find((p) => typeof p === "string" && isProtectedWrite(p, toolCtx.cwd));
|
|
12152
|
+
if (hitProtected) {
|
|
12153
|
+
state.denied = true;
|
|
12154
|
+
failResult(state, `\u274C [\u4FDD\u62A4\u8DEF\u5F84] \u7981\u6B62\u4FEE\u6539\u53D7\u4FDD\u62A4\u76EE\u5F55\uFF08.git/.ssh/.aws/.deepseeker-code \u7B49 VCS/\u51ED\u8BC1/\u914D\u7F6E\uFF09\uFF1A${hitProtected}\u3002`, "permission");
|
|
12155
|
+
}
|
|
12156
|
+
let perm = null;
|
|
12157
|
+
try {
|
|
12158
|
+
perm = checkPermission(resolveMcpPermissionName(calledName, state.calledArgs), state.calledArgs, matchedTool.function.primaryArg, sessionId);
|
|
12159
|
+
if (perm === "deny") {
|
|
12160
|
+
state.denied = true;
|
|
12161
|
+
failResult(state, `\u274C [\u6743\u9650\u89C4\u5219]\uFF1A[${calledName}] \u88AB\u6743\u9650\u89C4\u5219 deny \u62D2\u7EDD\u3002`, "permission");
|
|
12162
|
+
} else if (perm === "allow") {
|
|
12163
|
+
needApproval = false;
|
|
12164
|
+
} else if (perm === "ask") {
|
|
12165
|
+
needApproval = true;
|
|
11901
12166
|
}
|
|
11902
|
-
|
|
12167
|
+
} catch {
|
|
12168
|
+
perm = null;
|
|
12169
|
+
}
|
|
12170
|
+
const isCommandTool = matchedTool.function.argKind === "command";
|
|
12171
|
+
const isForegroundCommand = isCommandTool && matchedTool.function.isSync !== false;
|
|
12172
|
+
const cmdIsScriptRunner = isCommandTool && isScriptRunnerCommand(typeof state.calledArgs?.command === "string" ? state.calledArgs.command : "");
|
|
12173
|
+
const argCwd = typeof state.calledArgs?.cwd === "string" ? state.calledArgs.cwd.trim() : "";
|
|
12174
|
+
const samePath = (a, b) => {
|
|
11903
12175
|
try {
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
denied = true;
|
|
11907
|
-
failResult(`\u274C [\u6743\u9650\u89C4\u5219]\uFF1A[${calledName}] \u88AB\u6743\u9650\u89C4\u5219 deny \u62D2\u7EDD\u3002`, "permission");
|
|
11908
|
-
} else if (perm === "allow") {
|
|
11909
|
-
needApproval = false;
|
|
11910
|
-
} else if (perm === "ask") {
|
|
11911
|
-
needApproval = true;
|
|
11912
|
-
}
|
|
12176
|
+
const ra = path36.resolve(a), rb = path36.resolve(b);
|
|
12177
|
+
return process.platform === "win32" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;
|
|
11913
12178
|
} catch {
|
|
11914
|
-
|
|
12179
|
+
return false;
|
|
11915
12180
|
}
|
|
11916
|
-
|
|
11917
|
-
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
|
|
11924
|
-
|
|
11925
|
-
};
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
|
|
11929
|
-
|
|
12181
|
+
};
|
|
12182
|
+
const scriptRunnerCwdDrift = cmdIsScriptRunner && argCwd !== "" && !samePath(argCwd, toolCtx.cwd ?? process.cwd());
|
|
12183
|
+
const scriptRunnerBlocked = cmdIsScriptRunner && (perm === null || scriptRunnerCwdDrift);
|
|
12184
|
+
if (scriptRunnerCwdDrift) needApproval = true;
|
|
12185
|
+
if (perm === null && needApproval && !state.denied && !scriptRunnerBlocked && isForegroundCommand && isReadOnlyCommand(typeof state.calledArgs?.command === "string" ? state.calledArgs.command : "")) {
|
|
12186
|
+
needApproval = false;
|
|
12187
|
+
}
|
|
12188
|
+
if (isCommandTool && matchCommandDeny(typeof state.calledArgs?.command === "string" ? state.calledArgs.command : "")) {
|
|
12189
|
+
state.denied = true;
|
|
12190
|
+
failResult(state, `\u274C [\u5B89\u5168] \u707E\u96BE\u547D\u4EE4\u6E05\u5355\u62E6\u622A\uFF08\u4E0D\u53EF\u88AB allow \u89C4\u5219\u7ED5\u8FC7\uFF09\uFF1A[${calledName}] ${String(state.calledArgs?.command ?? "").slice(0, 100)}`, "permission");
|
|
12191
|
+
}
|
|
12192
|
+
state.isBgTool = matchedTool.function.isSync === false;
|
|
12193
|
+
state.lockKey = state.isBgTool ? computeLockKey(matchedTool.function.exclusiveLock, state.calledArgs, toolCtx) : null;
|
|
12194
|
+
if (state.lockKey && isLockHeld(state.lockKey)) {
|
|
12195
|
+
state.denied = true;
|
|
12196
|
+
failResult(state, `\u{1F512} [\u4E92\u65A5\u9501\u963B\u585E]\uFF1A\u5DF2\u6709\u540E\u53F0\u4EFB\u52A1\u6301\u6709\u9501 [${state.lockKey}]\uFF0C[${calledName}] \u8C03\u7528\u88AB\u8DF3\u8FC7\u3002`);
|
|
12197
|
+
}
|
|
12198
|
+
if (needApproval && !state.denied && !scriptRunnerBlocked) {
|
|
12199
|
+
const auto = await runAutoCheck(
|
|
12200
|
+
resolveMcpPermissionName(calledName, state.calledArgs),
|
|
12201
|
+
state.calledArgs,
|
|
12202
|
+
toolCtx,
|
|
12203
|
+
permissionMode === "auto",
|
|
12204
|
+
// ★ P0-3(裁定 B 方案):decl 补齐 safetyLevel/triggersUndo——快路径三条件缺一不可,
|
|
12205
|
+
// 两者都来自工具声明(勿在此处按名特判,名单已 #8a 声明化退役)。
|
|
12206
|
+
{ autoApproval: matchedTool.function.autoApproval, pathArgs: matchedTool.function.pathArgs, primaryArg: matchedTool.function.primaryArg, safetyLevel: matchedTool.function.safetyLevel, triggersUndo: matchedTool.function.triggersUndo }
|
|
12207
|
+
);
|
|
12208
|
+
if (auto === "allow") {
|
|
11930
12209
|
needApproval = false;
|
|
12210
|
+
} else if (auto === "deny") {
|
|
12211
|
+
state.denied = true;
|
|
12212
|
+
failResult(state, `\u274C [auto] \u5185\u7F6E\u9AD8\u5371\u6E05\u5355\u62E6\u622A\uFF1A[${calledName}] ${state.calledArgs?.path ?? ""}\u3002`, "permission");
|
|
12213
|
+
} else {
|
|
12214
|
+
autoGateAsked = true;
|
|
11931
12215
|
}
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
}
|
|
11936
|
-
|
|
11937
|
-
|
|
11938
|
-
|
|
11939
|
-
denied = true;
|
|
11940
|
-
failResult(`\
|
|
11941
|
-
}
|
|
11942
|
-
if (needApproval && !denied && !scriptRunnerBlocked) {
|
|
11943
|
-
const auto = await runAutoCheck(
|
|
11944
|
-
resolveMcpPermissionName(calledName, calledArgs),
|
|
11945
|
-
calledArgs,
|
|
11946
|
-
toolCtx,
|
|
11947
|
-
permissionMode === "auto",
|
|
11948
|
-
{ autoApproval: matchedTool.function.autoApproval, pathArgs: matchedTool.function.pathArgs, primaryArg: matchedTool.function.primaryArg }
|
|
11949
|
-
);
|
|
11950
|
-
if (auto === "allow") {
|
|
11951
|
-
needApproval = false;
|
|
11952
|
-
} else if (auto === "deny") {
|
|
11953
|
-
denied = true;
|
|
11954
|
-
failResult(`\u274C [auto] \u5185\u7F6E\u9AD8\u5371\u6E05\u5355\u62E6\u622A\uFF1A[${calledName}] ${calledArgs?.path ?? ""}\u3002`, "permission");
|
|
11955
|
-
}
|
|
12216
|
+
}
|
|
12217
|
+
if (needApproval && !state.denied) {
|
|
12218
|
+
const ra = matchedTool.function.requireApproval;
|
|
12219
|
+
let detail = `\u7533\u8BF7\u6267\u884C\u9AD8\u5371\u5DE5\u5177 [${calledName}]`;
|
|
12220
|
+
try {
|
|
12221
|
+
if (ra) detail = typeof ra === "function" ? await ra(state.calledArgs, toolCtx) : ra;
|
|
12222
|
+
} catch (e) {
|
|
12223
|
+
state.denied = true;
|
|
12224
|
+
failResult(state, `\u274C [\u5BA1\u6279\u63CF\u8FF0\u751F\u6210\u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
|
|
11956
12225
|
}
|
|
11957
|
-
if (
|
|
11958
|
-
|
|
11959
|
-
|
|
12226
|
+
if (autoGateAsked) detail += `
|
|
12227
|
+
\uFF08\u2139\uFE0F \u8BF4\u660E\uFF1A\u672C\u8C03\u7528\u672A\u83B7\u81EA\u52A8\u653E\u884C\u2014\u2014\u4E0D\u5728\u81EA\u52A8\u653E\u884C\u8303\u56F4 / \u5B89\u5168\u5206\u7C7B\u5668\u5224 risky / \u5224\u5B9A\u8D85\u65F6\uFF08\u5206\u7C7B\u5668\u4E0A\u9650 10s\uFF09\u6216\u5F02\u5E38\uFF0C\u6309 fail-closed \u8F6C\u4EBA\u5DE5\u5BA1\u6279\u3002\uFF09`;
|
|
12228
|
+
if (!state.denied) {
|
|
12229
|
+
let approved2 = false;
|
|
11960
12230
|
try {
|
|
11961
|
-
|
|
12231
|
+
approved2 = await requestApproval(calledName, toolCall.id, detail, toolCtx, level, state.calledArgs, matchedTool.function.primaryArg);
|
|
11962
12232
|
} catch (e) {
|
|
11963
|
-
denied = true;
|
|
11964
|
-
failResult(`\u274C [\u5BA1\u6279\
|
|
12233
|
+
state.denied = true;
|
|
12234
|
+
failResult(state, `\u274C [\u5BA1\u6279\u6D41\u7A0B\u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
|
|
11965
12235
|
}
|
|
11966
|
-
if (!denied) {
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
approved2 = await requestApproval(calledName, toolCall.id, detail, toolCtx, level, calledArgs, matchedTool.function.primaryArg);
|
|
11970
|
-
} catch (e) {
|
|
11971
|
-
denied = true;
|
|
11972
|
-
failResult(`\u274C [\u5BA1\u6279\u6D41\u7A0B\u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
|
|
11973
|
-
}
|
|
11974
|
-
if (!approved2 && !denied) {
|
|
11975
|
-
denied = true;
|
|
11976
|
-
failResult(`\u274C [\u5B89\u5168\u7194\u65AD]\uFF1A\u7528\u6237\u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u7533\u8BF7\u3002`, "permission");
|
|
11977
|
-
}
|
|
12236
|
+
if (!approved2 && !state.denied) {
|
|
12237
|
+
state.denied = true;
|
|
12238
|
+
failResult(state, `\u274C [\u5B89\u5168\u7194\u65AD]\uFF1A\u7528\u6237\u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u7533\u8BF7\u3002`, "permission");
|
|
11978
12239
|
}
|
|
11979
12240
|
}
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
}
|
|
11987
|
-
if (veto.deny) {
|
|
11988
|
-
denied = true;
|
|
11989
|
-
failResult(veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
|
|
11990
|
-
}
|
|
12241
|
+
}
|
|
12242
|
+
if (!state.denied && !preHooksAdvanced) {
|
|
12243
|
+
let veto;
|
|
12244
|
+
try {
|
|
12245
|
+
veto = await runPreHooks(resolveMcpPermissionName(calledName, state.calledArgs), state.calledArgs, toolCtx);
|
|
12246
|
+
} catch (e) {
|
|
12247
|
+
veto = { deny: true, reason: `\u274C [Pre-hook \u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002` };
|
|
11991
12248
|
}
|
|
11992
|
-
if (
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
} catch (e) {
|
|
11996
|
-
denied = true;
|
|
11997
|
-
failResult(`\u274C [Undo \u5907\u4EFD\u5931\u8D25\xB7\u5B89\u5168\u7194\u65AD]\uFF1A${e?.message ?? e}\u3002\u5199\u5165\u5DF2\u963B\u6B62\uFF08\u51E1\u6539\u5FC5\u53EF\u56DE\u9000\u539F\u5219\uFF09\u3002`);
|
|
11998
|
-
}
|
|
12249
|
+
if (veto.deny) {
|
|
12250
|
+
state.denied = true;
|
|
12251
|
+
failResult(state, veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
|
|
11999
12252
|
}
|
|
12000
|
-
|
|
12001
|
-
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12253
|
+
}
|
|
12254
|
+
const denied = state.denied;
|
|
12255
|
+
if (!denied && matchedTool.function.triggersUndo) {
|
|
12256
|
+
try {
|
|
12257
|
+
await beforeMutationBackup(matchedTool.function.triggersUndo, calledName, state.calledArgs, toolCall.id, sessionId);
|
|
12258
|
+
} catch (e) {
|
|
12259
|
+
state.denied = true;
|
|
12260
|
+
failResult(state, `\u274C [Undo \u5907\u4EFD\u5931\u8D25\xB7\u5B89\u5168\u7194\u65AD]\uFF1A${e?.message ?? e}\u3002\u5199\u5165\u5DF2\u963B\u6B62\uFF08\u51E1\u6539\u5FC5\u53EF\u56DE\u9000\u539F\u5219\uFF09\u3002`);
|
|
12261
|
+
}
|
|
12262
|
+
}
|
|
12263
|
+
};
|
|
12264
|
+
executeTool = async (ctx, toolCall, state, toolCtx) => {
|
|
12265
|
+
const { sessionId, depth, round, startTime, llmDecisionSource, signal, events } = ctx;
|
|
12266
|
+
const { calledName, calledArgs, matchedTool } = state;
|
|
12267
|
+
try {
|
|
12268
|
+
events({ sessionId, eventType: "tool.execute.start", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin" }, payload: { output: JSON.stringify(calledArgs) } });
|
|
12269
|
+
const execRet = matchedTool.function.execute(calledArgs, toolCtx);
|
|
12270
|
+
const collected = state.isBgTool ? await runBackgroundTool(execRet, state.lockKey, calledName, signal) : await collectToolResult(execRet, (chunk) => toolCtx.emitProgress?.(chunk), signal);
|
|
12271
|
+
state.result = collected.content;
|
|
12272
|
+
state.resultStatus = collected.status;
|
|
12273
|
+
state.imageAttachments = collected.images;
|
|
12274
|
+
if (collected.status === "failed") state.errorCategory = collected.errorCategory ?? "runtime";
|
|
12275
|
+
if (matchedTool.function.verifyResult) {
|
|
12276
|
+
const verdict = matchedTool.function.verifyResult(state.result, toolCtx);
|
|
12277
|
+
if (verdict.status === "failed" /* FAILED */) {
|
|
12278
|
+
state.result = `\u3010\u7CFB\u7EDF\u5224\u5B9A\uFF1A\u6267\u884C\u5931\u8D25\u3011${verdict.summary ?? ""}
|
|
12013
12279
|
\u8BF7\u6B63\u89C6\u4E0B\u65B9\u8F93\u51FA\uFF0C\u4E0D\u8981\u4E50\u89C2\u5047\u8BBE\u6210\u529F\u3002
|
|
12014
12280
|
|
|
12015
|
-
${result}`;
|
|
12016
|
-
|
|
12017
|
-
|
|
12018
|
-
}
|
|
12019
|
-
}
|
|
12020
|
-
if (resultStatus === "success") {
|
|
12021
|
-
events({ sessionId, eventType: "tool.execute.end", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: true }, payload: { output: result } });
|
|
12022
|
-
} else {
|
|
12023
|
-
events({ sessionId, eventType: "tool.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round, errorCategory: errorCategory ?? "unknown" }, payload: { output: result } });
|
|
12024
|
-
}
|
|
12025
|
-
} catch (err) {
|
|
12026
|
-
console.error(`\u274C \u6267\u884C\u5DE5\u5177 ${calledName} \u65F6\u53D1\u751F\u9519\u8BEF:`, err);
|
|
12027
|
-
failResult(`\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`);
|
|
12028
|
-
events({ sessionId, eventType: "tool.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round, errorCategory: errorCategory ?? "runtime" }, payload: { output: result } });
|
|
12029
|
-
}
|
|
12030
|
-
try {
|
|
12031
|
-
const post = await runPostHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, result, toolCtx);
|
|
12032
|
-
postHookOverride = post?.resultOverride;
|
|
12033
|
-
} catch (e) {
|
|
12034
|
-
console.warn(`\u26A0\uFE0F post-hook [${calledName}] \u5F02\u5E38\uFF08\u5DF2\u5FFD\u7565\uFF09:`, e?.message ?? e);
|
|
12281
|
+
${state.result}`;
|
|
12282
|
+
state.resultStatus = "failed";
|
|
12283
|
+
state.errorCategory = verdict.errorCategory ?? "unknown";
|
|
12035
12284
|
}
|
|
12036
12285
|
}
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12286
|
+
if (state.resultStatus === "success") {
|
|
12287
|
+
events({ sessionId, eventType: "tool.execute.end", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: true }, payload: { output: state.result } });
|
|
12288
|
+
} else {
|
|
12289
|
+
events({ sessionId, eventType: "tool.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round, errorCategory: state.errorCategory ?? "unknown" }, payload: { output: state.result } });
|
|
12290
|
+
}
|
|
12291
|
+
} catch (err) {
|
|
12292
|
+
console.error(`\u274C \u6267\u884C\u5DE5\u5177 ${calledName} \u65F6\u53D1\u751F\u9519\u8BEF:`, err);
|
|
12293
|
+
failResult(state, `\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`);
|
|
12294
|
+
events({ sessionId, eventType: "tool.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round, errorCategory: state.errorCategory ?? "runtime" }, payload: { output: state.result } });
|
|
12295
|
+
}
|
|
12296
|
+
try {
|
|
12297
|
+
const post = await runPostHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, state.result, toolCtx);
|
|
12298
|
+
state.postHookOverride = post?.resultOverride;
|
|
12299
|
+
} catch (e) {
|
|
12300
|
+
console.warn(`\u26A0\uFE0F post-hook [${calledName}] \u5F02\u5E38\uFF08\u5DF2\u5FFD\u7565\uFF09:`, e?.message ?? e);
|
|
12040
12301
|
}
|
|
12041
|
-
|
|
12042
|
-
|
|
12302
|
+
};
|
|
12303
|
+
finalizeOutcome = async (ctx, toolCall, state) => {
|
|
12304
|
+
const { sessionId, modelWindow, contextTokensEst } = ctx;
|
|
12305
|
+
const { calledName, calledArgs, matchedTool } = state;
|
|
12306
|
+
state.result = applyPrivacyMasking(matchedTool?.function?.privacyMaskingRules, calledArgs, state.result);
|
|
12307
|
+
const fill = contextTokensEst !== void 0 && modelWindow > 0 ? contextTokensEst / modelWindow : void 0;
|
|
12043
12308
|
const truncBudget = scaleTruncBudget(matchedTool?.function?.maxOutputCharacters ?? appConfig.MAX_TOOL_RESULT_CHARS, fill);
|
|
12044
|
-
const sidecarNote = result.length > truncBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), result) : void 0;
|
|
12045
|
-
result = truncateToolResult(result, truncBudget, sidecarNote);
|
|
12309
|
+
const sidecarNote = state.result.length > truncBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), state.result) : void 0;
|
|
12310
|
+
state.result = truncateToolResult(state.result, truncBudget, sidecarNote);
|
|
12311
|
+
const resultStatus = state.resultStatus;
|
|
12046
12312
|
const ok = resultStatus === "success";
|
|
12047
|
-
let resultForModel = result;
|
|
12048
|
-
let resultForUser = result;
|
|
12313
|
+
let resultForModel = state.result;
|
|
12314
|
+
let resultForUser = state.result;
|
|
12049
12315
|
if (matchedTool?.function?.outputFilter) {
|
|
12050
12316
|
try {
|
|
12051
|
-
const split = matchedTool.function.outputFilter(result);
|
|
12317
|
+
const split = matchedTool.function.outputFilter(state.result);
|
|
12052
12318
|
resultForModel = split.toModel;
|
|
12053
12319
|
resultForUser = split.toUser;
|
|
12054
12320
|
} catch {
|
|
12055
12321
|
}
|
|
12056
12322
|
}
|
|
12057
|
-
if (postHookOverride !== void 0) {
|
|
12323
|
+
if (state.postHookOverride !== void 0) {
|
|
12058
12324
|
console.log(`\u270F\uFE0F [Post-hook] [${calledName}] \u6A21\u578B\u89C6\u56FE\u7ED3\u679C\u5DF2\u88AB hook \u6539\u5199`);
|
|
12059
12325
|
const overrideBudget = scaleTruncBudget(matchedTool?.function?.maxOutputCharacters ?? appConfig.MAX_TOOL_RESULT_CHARS, fill);
|
|
12060
|
-
const overrideNote = postHookOverride.length > overrideBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), postHookOverride) : void 0;
|
|
12061
|
-
resultForModel = truncateToolResult(postHookOverride, overrideBudget, overrideNote);
|
|
12326
|
+
const overrideNote = state.postHookOverride.length > overrideBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), state.postHookOverride) : void 0;
|
|
12327
|
+
resultForModel = truncateToolResult(state.postHookOverride, overrideBudget, overrideNote);
|
|
12328
|
+
}
|
|
12329
|
+
const declaredTerminal = matchedTool?.function?.terminal;
|
|
12330
|
+
if (declaredTerminal === true) {
|
|
12331
|
+
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel, resultForUser, ok, imageAttachments: state.imageAttachments, terminal: { kind: "generic" } };
|
|
12332
|
+
}
|
|
12333
|
+
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel, resultForUser, ok, imageAttachments: state.imageAttachments };
|
|
12334
|
+
};
|
|
12335
|
+
processToolCall = async (toolCall, ctx) => {
|
|
12336
|
+
const state = {
|
|
12337
|
+
calledName: "",
|
|
12338
|
+
calledArgs: {},
|
|
12339
|
+
parseFailed: false,
|
|
12340
|
+
matchedTool: void 0,
|
|
12341
|
+
result: "",
|
|
12342
|
+
resultStatus: "success",
|
|
12343
|
+
errorCategory: void 0,
|
|
12344
|
+
denied: false,
|
|
12345
|
+
postHookOverride: void 0,
|
|
12346
|
+
imageAttachments: void 0,
|
|
12347
|
+
isBgTool: false,
|
|
12348
|
+
lockKey: null
|
|
12349
|
+
};
|
|
12350
|
+
if (toolCall.type === "function") {
|
|
12351
|
+
state.calledName = toolCall.function.name;
|
|
12352
|
+
try {
|
|
12353
|
+
state.calledArgs = JSON.parse(toolCall.function.arguments || "{}");
|
|
12354
|
+
} catch {
|
|
12355
|
+
state.parseFailed = true;
|
|
12356
|
+
}
|
|
12357
|
+
console.log(`\u{1F916} \u6A21\u578B\u8BF7\u6C42\u8C03\u7528\u5DE5\u5177: ${state.calledName}\uFF0C\u53C2\u6570:`, state.calledArgs);
|
|
12358
|
+
}
|
|
12359
|
+
state.matchedTool = ctx.rawTools.find((t) => t.function.name === state.calledName);
|
|
12360
|
+
const early = resolveEarlyOutcome(ctx, toolCall, state);
|
|
12361
|
+
if (early) return early;
|
|
12362
|
+
const {
|
|
12363
|
+
sessionId,
|
|
12364
|
+
depth,
|
|
12365
|
+
signal,
|
|
12366
|
+
events,
|
|
12367
|
+
permissionMode,
|
|
12368
|
+
onUIEvent,
|
|
12369
|
+
requestApproval: hostRequestApproval,
|
|
12370
|
+
requestQuestion: hostRequestQuestion,
|
|
12371
|
+
ideAction,
|
|
12372
|
+
keepRecentUnits,
|
|
12373
|
+
compactRatio,
|
|
12374
|
+
modelWindow,
|
|
12375
|
+
parentSystemPrompt,
|
|
12376
|
+
readTracker,
|
|
12377
|
+
reportCost,
|
|
12378
|
+
toolCostSink,
|
|
12379
|
+
onAuxUsage,
|
|
12380
|
+
budgetCheck
|
|
12381
|
+
} = ctx;
|
|
12382
|
+
const toolCtx = { sessionId, cwd: getActiveWorkspaceRoot(), abortSignal: signal, depth, keepRecentUnits, compactRatio, modelWindow, parentSystemPrompt, events, onUIEvent, requestApproval: hostRequestApproval, requestQuestion: hostRequestQuestion, ideAction, emitProgress: (m) => onUIEvent?.({ type: "tool.progress", toolsId: toolCall.id, toolName: state.calledName, message: m }), permissionMode, readTracker, reportCost, toolCostSink, onAuxUsage, budgetCheck };
|
|
12383
|
+
if (state.parseFailed) {
|
|
12384
|
+
failResult(state, `\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u6A21\u578B\u8FD4\u56DE\u7684 arguments \u4E0D\u662F\u5408\u6CD5 JSON${JSON.stringify(toolCall).slice(0, 300)}`, "syntax");
|
|
12385
|
+
emitValidationFailed(ctx, toolCall, state);
|
|
12386
|
+
} else if (state.matchedTool && typeof state.matchedTool.function.execute === "function") {
|
|
12387
|
+
await runSecurityGates(ctx, toolCall, state, toolCtx);
|
|
12388
|
+
if (!state.denied) await executeTool(ctx, toolCall, state, toolCtx);
|
|
12389
|
+
} else {
|
|
12390
|
+
failResult(state, `\u9519\u8BEF\uFF1A\u672A\u77E5\u5DE5\u5177 "${state.calledName}" \u6216\u8BE5\u5DE5\u5177\u65E0\u53EF\u6267\u884C\u51FD\u6570`, "syntax");
|
|
12391
|
+
emitValidationFailed(ctx, toolCall, state);
|
|
12062
12392
|
}
|
|
12063
|
-
return
|
|
12393
|
+
return await finalizeOutcome(ctx, toolCall, state);
|
|
12064
12394
|
};
|
|
12065
12395
|
}
|
|
12066
12396
|
});
|
|
@@ -12099,7 +12429,7 @@ var init_toolScheduling = __esm({
|
|
|
12099
12429
|
const fillRestPlaceholders = async (fromIdx) => {
|
|
12100
12430
|
const tcs2 = assistantMessage.tool_calls;
|
|
12101
12431
|
for (let j = fromIdx + 1; j < tcs2.length; j++) {
|
|
12102
|
-
const ph = "\uFF08\u5DF2\u8DF3\u8FC7\uFF1A\u7EC8\u7ED3\u7C7B\u5DE5\u5177
|
|
12432
|
+
const ph = "\uFF08\u5DF2\u8DF3\u8FC7\uFF1A\u7EC8\u7ED3\u7C7B\u5DE5\u5177\u4E4B\u540E\u7684\u5E76\u884C\u8C03\u7528\u672A\u6267\u884C\uFF09";
|
|
12103
12433
|
message.push({ role: "tool", tool_call_id: tcs2[j].id, content: ph });
|
|
12104
12434
|
await appendMessage({ sessionId, role: "tool", tool_call_id: tcs2[j].id, content: ph });
|
|
12105
12435
|
}
|
|
@@ -12118,14 +12448,15 @@ var init_toolScheduling = __esm({
|
|
|
12118
12448
|
};
|
|
12119
12449
|
const canParallelize = (name, args, parseFailed) => {
|
|
12120
12450
|
if (!parallelSafeToolsEnabled || parseFailed || signal?.aborted) return false;
|
|
12121
|
-
if (name === "exit_plan_mode" || name === "enter_plan_mode" || name === "ask_question") return false;
|
|
12122
12451
|
const matched = rawTools.find((t) => t.function.name === name);
|
|
12123
12452
|
if (!matched) return false;
|
|
12453
|
+
if (matched.function.terminal) return false;
|
|
12454
|
+
if (matched.function.parallelizable === false) return false;
|
|
12124
12455
|
if (matched.function.triggersUndo) return false;
|
|
12125
12456
|
if (matched.function.safetyLevel !== "safe" /* SAFE */) return false;
|
|
12126
12457
|
if (matched.function.isSync === false) return false;
|
|
12127
12458
|
try {
|
|
12128
|
-
const perm = checkPermission(name, args, matched.function.primaryArg);
|
|
12459
|
+
const perm = checkPermission(name, args, matched.function.primaryArg, sessionId);
|
|
12129
12460
|
if (perm === "ask" || perm === "deny") return false;
|
|
12130
12461
|
} catch {
|
|
12131
12462
|
return false;
|
|
@@ -12194,9 +12525,11 @@ var init_toolScheduling = __esm({
|
|
|
12194
12525
|
if (oc.terminal.kind === "exit_plan_mode") {
|
|
12195
12526
|
yield { type: "plan.proposed", plan: oc.terminal.plan || "" };
|
|
12196
12527
|
return { kind: "terminal", terminalText: oc.terminal.plan || oc.resultForUser };
|
|
12197
|
-
} else {
|
|
12528
|
+
} else if (oc.terminal.kind === "enter_plan_mode") {
|
|
12198
12529
|
yield { type: "plan.enterRequested", reason: oc.terminal.reason || "" };
|
|
12199
12530
|
return { kind: "terminal", terminalText: oc.terminal.reason ? `\u{1F4CB} \u6A21\u578B\u8BF7\u6C42\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F\uFF1A${oc.terminal.reason}` : oc.resultForUser };
|
|
12531
|
+
} else {
|
|
12532
|
+
return { kind: "terminal", terminalText: oc.resultForUser };
|
|
12200
12533
|
}
|
|
12201
12534
|
}
|
|
12202
12535
|
yield { type: "tool.end", toolCallId: oc.toolCallId, toolName: oc.calledName, result: oc.resultForUser, ok: oc.ok };
|
|
@@ -12409,7 +12742,8 @@ var init_streamInference = __esm({
|
|
|
12409
12742
|
depth,
|
|
12410
12743
|
signal,
|
|
12411
12744
|
toolsTokens,
|
|
12412
|
-
onUIEvent: ctx.onUIEvent
|
|
12745
|
+
onUIEvent: ctx.onUIEvent,
|
|
12746
|
+
onAuxUsage: ctx.onAuxUsage
|
|
12413
12747
|
});
|
|
12414
12748
|
inferenceMessages = withNudgeTail(message, nudgeMsg);
|
|
12415
12749
|
compactedThisRound = true;
|
|
@@ -12491,7 +12825,7 @@ var init_streamInference = __esm({
|
|
|
12491
12825
|
});
|
|
12492
12826
|
|
|
12493
12827
|
// ../core/src/agent/agentNudges.ts
|
|
12494
|
-
var NUDGE_EVERY, NUDGE_FENCE, PHANTOM_FENCE, PHANTOM_RETRY_MAX, EARLY_FINAL_FENCE, EARLY_FINAL_TURN_THRESHOLD, EARLY_FINAL_MAX, TOOL_DIGEST_FENCE, TOOL_DIGEST_MAX, TOOL_DIGEST_WINDOW, TOOL_DIGEST_TEXT_MAX_LEN, PLAN_FIRST_FENCE, REPEAT_FENCE, READ_REPEAT_THRESHOLD, GREP_REPEAT_THRESHOLD, REPEAT_NUDGE_MAX, TODO_INCOMPLETE_FENCE, TODO_INCOMPLETE_MAX, BUDGET_FENCE, BUDGET_SOFT_TEXT, PHANTOM_TEXT, EARLY_FINAL_TEXT, TOOL_DIGEST_TEXT, usageNoteText, NUDGE_TEXT, COMPLEX_VERBS, COMPLEX_OBJECTS, COMPLEX_MARKERS, QUERY_LEAD, looksComplex, PLAN_FIRST_TEXT, looksComplete, parseToolCall, normPath, normQuery, REPEAT_READ_TEXT, REPEAT_GREP_TEXT, TODO_INCOMPLETE_TEXT, createNudgeScheduler;
|
|
12828
|
+
var NUDGE_EVERY, NUDGE_FENCE, PHANTOM_FENCE, PHANTOM_RETRY_MAX, EARLY_FINAL_FENCE, EARLY_FINAL_TURN_THRESHOLD, EARLY_FINAL_MAX, TOOL_DIGEST_FENCE, TOOL_DIGEST_MAX, TOOL_DIGEST_WINDOW, TOOL_DIGEST_TEXT_MAX_LEN, PLAN_FIRST_FENCE, REPEAT_FENCE, READ_REPEAT_THRESHOLD, GREP_REPEAT_THRESHOLD, REPEAT_NUDGE_MAX, TODO_INCOMPLETE_FENCE, TODO_INCOMPLETE_MAX, BUDGET_FENCE, BUDGET_SOFT_TEXT, PLAN_REJECT_FENCE, PLAN_REJECT_NUDGE_AT, PLAN_REJECT_FORCE_AT, PLAN_REJECT_TEXT, PLAN_REJECT_FORCE_FINAL_TEXT, PHANTOM_TEXT, EARLY_FINAL_TEXT, TOOL_DIGEST_TEXT, usageNoteText, NUDGE_TEXT, COMPLEX_VERBS, COMPLEX_OBJECTS, COMPLEX_MARKERS, QUERY_LEAD, looksComplex, PLAN_FIRST_TEXT, looksComplete, parseToolCall, normPath, normQuery, REPEAT_READ_TEXT, REPEAT_GREP_TEXT, TODO_INCOMPLETE_TEXT, createNudgeScheduler;
|
|
12495
12829
|
var init_agentNudges = __esm({
|
|
12496
12830
|
"../core/src/agent/agentNudges.ts"() {
|
|
12497
12831
|
"use strict";
|
|
@@ -12518,9 +12852,14 @@ var init_agentNudges = __esm({
|
|
|
12518
12852
|
const pct = budget > 0 ? Math.round(spent / budget * 100) : 100;
|
|
12519
12853
|
return `\u5DF2\u82B1\u8D39 \xA5${spent.toFixed(3)} / \u9884\u7B97 \xA5${budget.toFixed(3)}\uFF08${pct}%\uFF09\uFF1A\u8BF7\u6536\u655B\u6536\u5C3E\uFF0C\u4E0D\u518D\u5C55\u5F00\u65B0\u63A2\u7D22\uFF1B\u82E5\u786E\u9700\u7EE7\u7EED\uFF0C\u7528\u4E00\u6BB5\u8BF4\u660E\u5269\u4F59\u5DE5\u4F5C\u540E\u5373\u6B62\u3002`;
|
|
12520
12854
|
};
|
|
12855
|
+
PLAN_REJECT_FENCE = "\u27E6DSC:PLAN_REJECT\u27E7";
|
|
12856
|
+
PLAN_REJECT_NUDGE_AT = 2;
|
|
12857
|
+
PLAN_REJECT_FORCE_AT = 4;
|
|
12858
|
+
PLAN_REJECT_TEXT = (n) => `\u8BA1\u5212\u6A21\u5F0F\u4E0B\u4F60\u8C03\u7528\u5199\u7C7B\u5DE5\u5177\u5DF2\u88AB\u62D2\u7EDD ${n} \u6B21\uFF1A\u8BA1\u5212\u6A21\u5F0F\u53EA\u5141\u8BB8\u53EA\u8BFB\u8C03\u7814\uFF0C\u5199\u7C7B\u64CD\u4F5C\u987B\u5F85\u65B9\u6848\u7ECF\u7528\u6237\u6279\u51C6\u540E\u624D\u80FD\u6267\u884C\uFF0C\u7EE7\u7EED\u91CD\u8BD5\u53EA\u4F1A\u88AB\u518D\u6B21\u62D2\u7EDD\u3002\u8BF7\u505C\u6B62\u5199\u64CD\u4F5C\uFF0C\u6539\u7528\u53EA\u8BFB\u5DE5\u5177\uFF08read_file / search_grep / web_fetch \u7B49\uFF09\u7EE7\u7EED\u5B8C\u5584\u8C03\u7814\uFF0C\u6216\u6574\u7406\u5DF2\u6709\u53D1\u73B0\uFF0C\u7528 exit_plan_mode \u63D0\u4EA4\u5B9E\u73B0\u65B9\u6848\u3002`;
|
|
12859
|
+
PLAN_REJECT_FORCE_FINAL_TEXT = (n) => `\u5DF2\u4E2D\u6B62\uFF1A\u8BA1\u5212\u6A21\u5F0F\u4E0B\u8FDE\u7EED ${n} \u6B21\u5199\u7C7B\u5DE5\u5177\u8C03\u7528\u88AB\u62D2\u7EDD\uFF0C\u6A21\u578B\u672A\u80FD\u4EA7\u51FA\u5B9E\u73B0\u65B9\u6848\u3002\u8BA1\u5212\u9636\u6BB5\u5230\u6B64\u4E3A\u6B62\uFF0C\u5DF2\u67E5\u660E\u7684\u4E8B\u5B9E\u4E0E\u8C03\u7814\u8BB0\u5F55\u4FDD\u7559\u5728\u4E0A\u4E0B\u6587\u4E2D\u2014\u2014\u4F60\u53EF\u4EE5\uFF1A\u2460\u8865\u5145\u8BF4\u660E\u9700\u6C42\u6216\u653E\u5BBD\u7EA6\u675F\u540E\u91CD\u8BD5\uFF1B\u2461\u8BA9 agent \u8DF3\u8FC7\u8BA1\u5212\u6A21\u5F0F\u76F4\u63A5\u5B9E\u73B0\uFF08\u9700\u81EA\u884C\u786E\u8BA4\u98CE\u9669\uFF09\uFF1B\u2462\u4EBA\u5DE5\u63A5\u624B\u5B8C\u6210\u5269\u4F59\u8C03\u7814\u3002`;
|
|
12521
12860
|
PHANTOM_TEXT = "\u4F60\u7684\u4E0A\u4E00\u6761\u56DE\u590D\u6CA1\u6709\u4EFB\u4F55\u5185\u5BB9\u3001\u4E5F\u6CA1\u6709\u8C03\u7528\u4EFB\u4F55\u5DE5\u5177\uFF0C\u4F46\u4EFB\u52A1\u5C1A\u672A\u5B8C\u6210\u3002\u8BF7\u7EE7\u7EED\u63A8\u8FDB\uFF08\u8C03\u7528\u5DE5\u5177\u6216\u7ED9\u51FA\u5B9E\u8D28\u56DE\u7B54\uFF09\uFF1B\u82E5\u786E\u5B9E\u53D7\u963B\u3001\u9700\u8981\u7528\u6237\u51B3\u7B56\uFF0C\u7528 ask_question \u8BF4\u660E\u5177\u4F53\u963B\u585E\u70B9\u3002\u4E0D\u8981\u8FD4\u56DE\u7A7A\u56DE\u590D\u3002";
|
|
12522
|
-
EARLY_FINAL_TEXT = (round) => `\u4F60\u4EC5 ${round} \u8F6E\u5C31\u51C6\u5907\u6536\u5C3E\uFF0C\u4E14\u56DE\u7B54\u4E2D\u6CA1\u6709\u660E\u786E\u7684\u5B8C\u6210\u58F0\u660E\u3002\u8BF7\u81EA\u68C0\uFF1A\u82E5\u7528\u6237\u8F93\u5165\u672C\u5C31\u65E0\u9700\u5DE5\u5177\uFF08\u5BD2\u6684/\u95F2\u804A/\u7EAF\u95EE\u7B54\uFF09\u3001\u6216\u4F60\u5728\u7B49\u7528\u6237\u6F84\u6E05\u51B3\u7B56\uFF0C\u76F4\u63A5\u6B63\u5E38\u56DE\u5E94\u5373\u53EF\uFF0C\u5FFD\u7565\u672C\u81EA\u68C0\u3001\u4E0D\u8981\u8F93\u51FA\u4EFB\u4F55\u81EA\u68C0\u8BF4\u660E\uFF1B\u82E5\u786E\u6709\u4EFB\u52A1\u672A\u843D\u5730\uFF08\u6240\u9700\u4FE1\u606F\u672A\u83B7\u53D6\u9F50 / \u8BE5\u6539\u7684\u6587\u4EF6\u672A\u6539\u5B8C / \u672A\u9A8C\u8BC1\uFF09\uFF0C\u7ACB\u5373\u7EE7\u7EED\u8C03\u7528\u5DE5\u5177\u63A8\u8FDB\uFF0C\u4E0D\u8981\u8349\u7387\u603B\u7ED3\u6536\u5C3E\uFF1B\u82E5\u786E\u5DF2\u5168\u90E8\u5B8C\u6210\uFF0C\u8BF7\u660E\u786E\u56DE\u590D\u300C\u5DF2\u5B8C\u6210\u300D\u5E76\u7B80\
|
|
12523
|
-
TOOL_DIGEST_TEXT = "\u4F60\u521A\u6267\u884C\u5B8C\u5DE5\u5177\u62FF\u5230\u7ED3\u679C\uFF0C\u5374\u672A\u57FA\u4E8E\u8BE5\u7ED3\u679C\u7ED9\u51FA\u5B9E\u8D28\u56DE\u5E94\u5C31\u51C6\u5907\u6536\u5C3E\u3002\u8BF7\u7ED3\u5408\u5DE5\u5177\u8FD4\u56DE\u7ED3\u679C\u7EE7\u7EED\u63A8\u8FDB\uFF1B\u82E5\u7ED3\u679C\u8868\u660E\u4EFB\u52A1\u5C1A\u672A\u5B8C\u6210\uFF08\u5982\u4ECD\u5728\u7F16\u8BD1/\u8FD0\u884C\u3001\u9700\u7EE7\u7EED\u8F6E\u8BE2\uFF09\uFF0C\u7ACB\u5373\u91C7\u53D6\u4E0B\u4E00\u6B65\u884C\u52A8\uFF0C\u4E0D\u8981\u7A7A\u624B\u6536\u5C3E\u3002\u82E5\u786E\u5DF2\u5168\u90E8\u5B8C\u6210\uFF0C\u8BF7\u660E\u786E\u56DE\u590D\u300C\u5DF2\u5B8C\u6210\u300D\u5E76\u7B80\
|
|
12861
|
+
EARLY_FINAL_TEXT = (round) => `\u4F60\u4EC5 ${round} \u8F6E\u5C31\u51C6\u5907\u6536\u5C3E\uFF0C\u4E14\u56DE\u7B54\u4E2D\u6CA1\u6709\u660E\u786E\u7684\u5B8C\u6210\u58F0\u660E\u3002\u8BF7\u81EA\u68C0\uFF1A\u82E5\u7528\u6237\u8F93\u5165\u672C\u5C31\u65E0\u9700\u5DE5\u5177\uFF08\u5BD2\u6684/\u95F2\u804A/\u7EAF\u95EE\u7B54\uFF09\u3001\u6216\u4F60\u5728\u7B49\u7528\u6237\u6F84\u6E05\u51B3\u7B56\uFF0C\u76F4\u63A5\u6B63\u5E38\u56DE\u5E94\u5373\u53EF\uFF0C\u5FFD\u7565\u672C\u81EA\u68C0\u3001\u4E0D\u8981\u8F93\u51FA\u4EFB\u4F55\u81EA\u68C0\u8BF4\u660E\uFF1B\u82E5\u786E\u6709\u4EFB\u52A1\u672A\u843D\u5730\uFF08\u6240\u9700\u4FE1\u606F\u672A\u83B7\u53D6\u9F50 / \u8BE5\u6539\u7684\u6587\u4EF6\u672A\u6539\u5B8C / \u672A\u9A8C\u8BC1\uFF09\uFF0C\u7ACB\u5373\u7EE7\u7EED\u8C03\u7528\u5DE5\u5177\u63A8\u8FDB\uFF0C\u4E0D\u8981\u8349\u7387\u603B\u7ED3\u6536\u5C3E\uFF1B\u82E5\u786E\u5DF2\u5168\u90E8\u5B8C\u6210\uFF0C\u8BF7\u660E\u786E\u56DE\u590D\u300C\u5DF2\u5B8C\u6210\u300D\u5E76\u7528\u7B80\u77ED\u81EA\u7136\u7684\u603B\u7ED3\u4EA4\u4EE3\u6210\u679C\uFF08\u7ED3\u8BBA\u4E00\u53E5 + \u6539\u52A8\u4E0E\u9A8C\u8BC1\u8981\u70B9\uFF09\uFF0C\u52FF\u5199\u957F\u7BC7\u62A5\u544A\u3002`;
|
|
12862
|
+
TOOL_DIGEST_TEXT = "\u4F60\u521A\u6267\u884C\u5B8C\u5DE5\u5177\u62FF\u5230\u7ED3\u679C\uFF0C\u5374\u672A\u57FA\u4E8E\u8BE5\u7ED3\u679C\u7ED9\u51FA\u5B9E\u8D28\u56DE\u5E94\u5C31\u51C6\u5907\u6536\u5C3E\u3002\u8BF7\u7ED3\u5408\u5DE5\u5177\u8FD4\u56DE\u7ED3\u679C\u7EE7\u7EED\u63A8\u8FDB\uFF1B\u82E5\u7ED3\u679C\u8868\u660E\u4EFB\u52A1\u5C1A\u672A\u5B8C\u6210\uFF08\u5982\u4ECD\u5728\u7F16\u8BD1/\u8FD0\u884C\u3001\u9700\u7EE7\u7EED\u8F6E\u8BE2\uFF09\uFF0C\u7ACB\u5373\u91C7\u53D6\u4E0B\u4E00\u6B65\u884C\u52A8\uFF0C\u4E0D\u8981\u7A7A\u624B\u6536\u5C3E\u3002\u82E5\u786E\u5DF2\u5168\u90E8\u5B8C\u6210\uFF0C\u8BF7\u660E\u786E\u56DE\u590D\u300C\u5DF2\u5B8C\u6210\u300D\u5E76\u7528\u7B80\u77ED\u81EA\u7136\u7684\u603B\u7ED3\u4EA4\u4EE3\u6210\u679C\uFF08\u7ED3\u8BBA\u4E00\u53E5 + \u6539\u52A8\u4E0E\u9A8C\u8BC1\u8981\u70B9\uFF09\uFF0C\u52FF\u5199\u957F\u7BC7\u62A5\u544A\u3002";
|
|
12524
12863
|
usageNoteText = (usage) => {
|
|
12525
12864
|
const pct = usage.windowTokens > 0 ? Math.min(100, Math.max(0, Math.round(usage.usedTokens / usage.windowTokens * 100))) : 0;
|
|
12526
12865
|
return `\u5F53\u524D\u4E0A\u4E0B\u6587\u5DF2\u7528\u7EA6 ${pct}%\uFF08\u4F30\u7B97 \u2248${usage.usedTokens}/${usage.windowTokens} token\uFF09\u3002\u82E5\u5360\u6BD4\u5DF2\u9AD8\uFF08>70%\uFF09\uFF1A\u4F18\u5148\u6536\u655B\u6536\u5C3E\u3001\u51CF\u5C0F\u5355\u6B21\u8BFB\u53D6\u91CF\uFF08search_grep \u5B9A\u4F4D\u540E\u5C40\u90E8\u8BFB\u53D6\uFF09\uFF0C\u907F\u514D\u89E6\u53D1\u5F3A\u5236\u538B\u7F29\u3002`;
|
|
@@ -12583,8 +12922,16 @@ var init_agentNudges = __esm({
|
|
|
12583
12922
|
let todoNudges = 0;
|
|
12584
12923
|
let todoPending = null;
|
|
12585
12924
|
let budgetSoftSent = false;
|
|
12925
|
+
let planRejectCount = 0;
|
|
12926
|
+
let planRejectNudged = false;
|
|
12927
|
+
let planRejectPending = null;
|
|
12586
12928
|
return {
|
|
12587
12929
|
pickNudge(round) {
|
|
12930
|
+
if (planRejectPending) {
|
|
12931
|
+
const m = planRejectPending;
|
|
12932
|
+
planRejectPending = null;
|
|
12933
|
+
return m;
|
|
12934
|
+
}
|
|
12588
12935
|
if (!planFirstSent && round === 1 && !planMode && looksComplex(firstPrompt)) {
|
|
12589
12936
|
planFirstSent = true;
|
|
12590
12937
|
return { role: "system", content: `${PLAN_FIRST_FENCE}
|
|
@@ -12710,6 +13057,45 @@ ${REPEAT_GREP_TEXT(q, n)}` };
|
|
|
12710
13057
|
continue;
|
|
12711
13058
|
}
|
|
12712
13059
|
}
|
|
13060
|
+
},
|
|
13061
|
+
notePlanWriteRejected(total) {
|
|
13062
|
+
planRejectCount = Math.max(planRejectCount, Math.floor(total) || 0);
|
|
13063
|
+
if (!planRejectNudged && planRejectCount >= PLAN_REJECT_NUDGE_AT) {
|
|
13064
|
+
planRejectNudged = true;
|
|
13065
|
+
planRejectPending = { role: "system", content: `${PLAN_REJECT_FENCE}
|
|
13066
|
+
${PLAN_REJECT_TEXT(planRejectCount)}` };
|
|
13067
|
+
}
|
|
13068
|
+
},
|
|
13069
|
+
planWriteRejections() {
|
|
13070
|
+
return planRejectCount;
|
|
13071
|
+
}
|
|
13072
|
+
};
|
|
13073
|
+
};
|
|
13074
|
+
}
|
|
13075
|
+
});
|
|
13076
|
+
|
|
13077
|
+
// ../core/src/agent/costRelay.ts
|
|
13078
|
+
var createCostRelay;
|
|
13079
|
+
var init_costRelay = __esm({
|
|
13080
|
+
"../core/src/agent/costRelay.ts"() {
|
|
13081
|
+
"use strict";
|
|
13082
|
+
createCostRelay = (sink) => {
|
|
13083
|
+
let reported = 0;
|
|
13084
|
+
return {
|
|
13085
|
+
/** 上报当前累计总额,向 sink 递送相对上次上报的正增量。 */
|
|
13086
|
+
flush(total) {
|
|
13087
|
+
if (!sink) return;
|
|
13088
|
+
if (!Number.isFinite(total)) return;
|
|
13089
|
+
const delta = total - reported;
|
|
13090
|
+
if (delta <= 1e-9) return;
|
|
13091
|
+
const r = Math.round(delta * 1e6) / 1e6;
|
|
13092
|
+
if (r <= 0) return;
|
|
13093
|
+
reported += r;
|
|
13094
|
+
sink(r);
|
|
13095
|
+
},
|
|
13096
|
+
/** 已累计上报给 sink 的金额(元),父级/调试可读。 */
|
|
13097
|
+
get reported() {
|
|
13098
|
+
return reported;
|
|
12713
13099
|
}
|
|
12714
13100
|
};
|
|
12715
13101
|
};
|
|
@@ -12833,12 +13219,17 @@ var init_prices = __esm({
|
|
|
12833
13219
|
idle: { input: 1, output: 4, cacheHit: 0.02 },
|
|
12834
13220
|
peak: { input: 2, output: 8, cacheHit: 0.04 }
|
|
12835
13221
|
},
|
|
13222
|
+
// 官网脚注明示旧名按 Flash 价格计费(2026-10-19 核实),非猜测——与 flash 同价条目
|
|
13223
|
+
"deepseek-flash-vision-exp": {
|
|
13224
|
+
idle: { input: 1, output: 4, cacheHit: 0.02 },
|
|
13225
|
+
peak: { input: 2, output: 8, cacheHit: 0.04 }
|
|
13226
|
+
},
|
|
12836
13227
|
"deepseek-v4-pro": {
|
|
12837
13228
|
idle: { input: 4.5, output: 13.5, cacheHit: 0.15 },
|
|
12838
13229
|
peak: { input: 9, output: 27, cacheHit: 0.3 }
|
|
12839
13230
|
}
|
|
12840
13231
|
};
|
|
12841
|
-
PRICES_VERIFIED_AT = "2026-10-
|
|
13232
|
+
PRICES_VERIFIED_AT = "2026-10-19";
|
|
12842
13233
|
PRICES_STALE_DAYS = 30;
|
|
12843
13234
|
pricesAgeDays = () => Math.max(0, Math.floor((Date.now() - Date.parse(`${PRICES_VERIFIED_AT}T00:00:00Z`)) / 864e5));
|
|
12844
13235
|
isBeijingPeak = (ts2 = /* @__PURE__ */ new Date()) => {
|
|
@@ -12901,18 +13292,16 @@ async function* runAgent(message, options) {
|
|
|
12901
13292
|
const keepRecentUnits = options.keepRecentUnits;
|
|
12902
13293
|
const compactRatio = options.compactRatio;
|
|
12903
13294
|
const parentSystemPrompt = options.parentSystemPrompt;
|
|
13295
|
+
const effectiveModel = options.model?.trim() || MODEL_NAME;
|
|
12904
13296
|
const { rawTools, cleanedToolSchemas, toolsTokens } = await prepareToolsAndInjections(message, options, events);
|
|
12905
13297
|
let round = 0;
|
|
12906
13298
|
let lastContent = "";
|
|
12907
13299
|
let stopReason = "normal";
|
|
12908
13300
|
let budgetTruncated = false;
|
|
12909
13301
|
const MAX_AGENT_ROUNDS = 500;
|
|
12910
|
-
const firstPrompt = (
|
|
12911
|
-
if (depth !== 0) return "";
|
|
12912
|
-
const m = message.find((x) => x?.role === "user");
|
|
12913
|
-
return msgText(m?.content);
|
|
12914
|
-
})();
|
|
13302
|
+
const firstPrompt = resolveFirstPrompt(message, options);
|
|
12915
13303
|
let budgetSpentYuan = 0;
|
|
13304
|
+
let subCostYuanAcc = 0;
|
|
12916
13305
|
let runCompactCount = 0;
|
|
12917
13306
|
const nudges = createNudgeScheduler({
|
|
12918
13307
|
firstPrompt,
|
|
@@ -12936,13 +13325,12 @@ async function* runAgent(message, options) {
|
|
|
12936
13325
|
});
|
|
12937
13326
|
};
|
|
12938
13327
|
const startTime = performance.now();
|
|
12939
|
-
const persistedCalib = await
|
|
13328
|
+
const persistedCalib = await getCalibration(sessionId, effectiveModel);
|
|
12940
13329
|
let calibRatio = persistedCalib.calibRatio ?? 1.4;
|
|
12941
13330
|
let lastRealPromptTokens = persistedCalib.lastRealPromptTokens;
|
|
12942
13331
|
let lastCachedTokens = persistedCalib.lastCachedTokens;
|
|
12943
13332
|
const breaker = createRepeatBreaker({ sessionId, depth, llmDecisionSource, startTime, events });
|
|
12944
13333
|
let usageSum;
|
|
12945
|
-
const effectiveModel = options.model?.trim() || MODEL_NAME;
|
|
12946
13334
|
let costYuanAcc = 0;
|
|
12947
13335
|
let costKnown = true;
|
|
12948
13336
|
const addUsage = (u) => {
|
|
@@ -12960,6 +13348,30 @@ async function* runAgent(message, options) {
|
|
|
12960
13348
|
budgetSpentYuan += cost;
|
|
12961
13349
|
}
|
|
12962
13350
|
};
|
|
13351
|
+
let toolCostYuanAcc = 0;
|
|
13352
|
+
const addToolCost = (yuan) => {
|
|
13353
|
+
if (!Number.isFinite(yuan) || yuan <= 0) return;
|
|
13354
|
+
toolCostYuanAcc += yuan;
|
|
13355
|
+
budgetSpentYuan += yuan;
|
|
13356
|
+
};
|
|
13357
|
+
const toolCostSink = process.env.DEEP_SEEK_TOOL_COST === "0" ? void 0 : addToolCost;
|
|
13358
|
+
let auxCostYuanAcc = 0;
|
|
13359
|
+
const reportAuxUsage = (usage, kind) => {
|
|
13360
|
+
if (usage) {
|
|
13361
|
+
const cost = calcCostYuan(AUX_MODEL_NAME, { promptTokens: usage.prompt_tokens, completionTokens: usage.completion_tokens, cachedTokens: usage.cached_tokens });
|
|
13362
|
+
if (cost != null && Number.isFinite(cost) && cost > 0) {
|
|
13363
|
+
auxCostYuanAcc += cost;
|
|
13364
|
+
budgetSpentYuan += cost;
|
|
13365
|
+
}
|
|
13366
|
+
}
|
|
13367
|
+
try {
|
|
13368
|
+
options.onAuxUsage?.(usage, kind);
|
|
13369
|
+
} catch {
|
|
13370
|
+
}
|
|
13371
|
+
};
|
|
13372
|
+
const costRelay = createCostRelay(options.costSink);
|
|
13373
|
+
const inheritedBudgetCheck = options.budgetCheck ?? (depth === 0 && options.budgetYuan != null && options.budgetYuan > 0 ? () => budgetSpentYuan >= options.budgetYuan * 1.2 : void 0);
|
|
13374
|
+
let planWriteRej = 0;
|
|
12963
13375
|
const toolCounts = {};
|
|
12964
13376
|
await appendEvent(sessionId, { dscEvent: "run.start", runId, depth });
|
|
12965
13377
|
try {
|
|
@@ -12996,6 +13408,12 @@ async function* runAgent(message, options) {
|
|
|
12996
13408
|
\uFF08\u{1F4B0} \u9884\u7B97\u622A\u65AD\uFF1A\u672C run \u5DF2\u82B1\u8D39 \xA5${budgetSpentYuan.toFixed(3)} / \u9884\u7B97 \xA5${options.budgetYuan.toFixed(3)}\u3002/budget \u8C03\u6574\u9884\u7B97\u540E\u56DE\u590D"\u7EE7\u7EED"\u5373\u53EF\u63A5\u7EED\u3002\uFF09` };
|
|
12997
13409
|
return;
|
|
12998
13410
|
}
|
|
13411
|
+
if (depth > 0 && options.budgetCheck?.()) {
|
|
13412
|
+
stopReason = "limit";
|
|
13413
|
+
yield { type: "final", text: (lastContent || "") + `
|
|
13414
|
+
\uFF08\u{1F4B0} \u9884\u7B97\u4E2D\u6B62\uFF1A\u7236\u7EA7\u9884\u7B97\u5DF2\u8017\u5C3D\uFF0C\u672C\u5B50\u4EFB\u52A1\u5728\u8F6E\u8FB9\u754C\u88AB\u7194\u65AD\u6536\u5C3E\uFF0C\u4EFB\u52A1\u672A\u5B8C\u6210\u3002\u5DF2\u5B8C\u6210\u8FDB\u5C55\u5982\u4E0A\uFF1B\u8BF7\u52FF\u91CD\u8BD5\u6216\u7EE7\u7EED\u6D3E\u751F\u5B50\u4EFB\u52A1\u3002\uFF09` };
|
|
13415
|
+
return;
|
|
13416
|
+
}
|
|
12999
13417
|
const inboxTexts = claimSessionInbox(sessionId);
|
|
13000
13418
|
if (inboxTexts.length > 0) {
|
|
13001
13419
|
for (const t of inboxTexts) {
|
|
@@ -13020,6 +13438,8 @@ async function* runAgent(message, options) {
|
|
|
13020
13438
|
lastRealPromptTokens,
|
|
13021
13439
|
lastCachedTokens,
|
|
13022
13440
|
toolsTokens,
|
|
13441
|
+
// ★ P0-2:压缩摘要(summarize)的 aux usage 回传计价(见 reportAuxUsage 声明处)
|
|
13442
|
+
onAuxUsage: reportAuxUsage,
|
|
13023
13443
|
// ★ H1:旁路数 compact.done 供 context.usage.compactCount(透传不拦截)
|
|
13024
13444
|
onUIEvent: (evt) => {
|
|
13025
13445
|
if (evt.type === "compact.done") runCompactCount++;
|
|
@@ -13058,7 +13478,8 @@ async function* runAgent(message, options) {
|
|
|
13058
13478
|
compactRatio,
|
|
13059
13479
|
modelWindow,
|
|
13060
13480
|
toolsTokens,
|
|
13061
|
-
onUIEvent: options.onUIEvent
|
|
13481
|
+
onUIEvent: options.onUIEvent,
|
|
13482
|
+
onAuxUsage: reportAuxUsage
|
|
13062
13483
|
});
|
|
13063
13484
|
if (infResult.kind === "aborted") {
|
|
13064
13485
|
yield { type: "final", text: infResult.partialText || lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
|
|
@@ -13088,6 +13509,7 @@ async function* runAgent(message, options) {
|
|
|
13088
13509
|
if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
|
|
13089
13510
|
await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
|
|
13090
13511
|
emitContextUsage();
|
|
13512
|
+
costRelay.flush(budgetSpentYuan);
|
|
13091
13513
|
const finalText = (typeof assistantMessage.content === "string" ? assistantMessage.content : "") || "";
|
|
13092
13514
|
const intercepted = nudges.interceptFinal(finalText, round);
|
|
13093
13515
|
if (intercepted) continue;
|
|
@@ -13140,8 +13562,23 @@ async function* runAgent(message, options) {
|
|
|
13140
13562
|
// ★ 子 run 成本回灌(成本信任链第二环 E1):runSubagent 经 toolCtx.reportCost 拿到闭包,
|
|
13141
13563
|
// 子 run 结束时把总花费记进本 run 预算累计器——父预算自动继承约束(子花费计入父 spent)。
|
|
13142
13564
|
reportCost: (yuan) => {
|
|
13143
|
-
if (Number.isFinite(yuan) && yuan > 0)
|
|
13144
|
-
|
|
13565
|
+
if (Number.isFinite(yuan) && yuan > 0) {
|
|
13566
|
+
budgetSpentYuan += yuan;
|
|
13567
|
+
subCostYuanAcc += yuan;
|
|
13568
|
+
}
|
|
13569
|
+
},
|
|
13570
|
+
// ★ 按次计费工具成本上报(DeepSeek Search/Fetch):web 工具经 toolCtx.toolCostSink 拿到闭包,
|
|
13571
|
+
// 成功调用后按次上报进本 run 累计器。
|
|
13572
|
+
toolCostSink,
|
|
13573
|
+
// ★ P0-2:auto 档风险分类器(classifyToolRisk)的 aux usage 回传计价(见 reportAuxUsage 声明处)
|
|
13574
|
+
onAuxUsage: reportAuxUsage,
|
|
13575
|
+
// ★ P1-7 计划拒绝熔断:计划期非 SAFE 工具被拒增量回传(计数 + 通知 nudge 调度器发转向文案)
|
|
13576
|
+
planWriteRejections: (delta) => {
|
|
13577
|
+
planWriteRej += delta;
|
|
13578
|
+
nudges.notePlanWriteRejected(planWriteRej);
|
|
13579
|
+
},
|
|
13580
|
+
// ★ P1-5 子任务预算熔断:runSubagent 经 toolCtx.budgetCheck 拿到探测闭包,子 runAgent 每轮边界自检
|
|
13581
|
+
budgetCheck: inheritedBudgetCheck
|
|
13145
13582
|
};
|
|
13146
13583
|
const scheduleResult = yield* scheduleToolCalls(assistantMessage, message, toolCallCtx);
|
|
13147
13584
|
if (scheduleResult.kind === "aborted") {
|
|
@@ -13154,6 +13591,12 @@ async function* runAgent(message, options) {
|
|
|
13154
13591
|
}
|
|
13155
13592
|
await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
|
|
13156
13593
|
emitContextUsage();
|
|
13594
|
+
costRelay.flush(budgetSpentYuan);
|
|
13595
|
+
if (options.planMode && planWriteRej >= PLAN_REJECT_FORCE_AT) {
|
|
13596
|
+
stopReason = "limit";
|
|
13597
|
+
yield { type: "final", text: PLAN_REJECT_FORCE_FINAL_TEXT(planWriteRej) };
|
|
13598
|
+
return;
|
|
13599
|
+
}
|
|
13157
13600
|
}
|
|
13158
13601
|
} catch (toolErr) {
|
|
13159
13602
|
if (signal?.aborted) {
|
|
@@ -13191,12 +13634,16 @@ async function* runAgent(message, options) {
|
|
|
13191
13634
|
totalTokens: usageSum?.total_tokens,
|
|
13192
13635
|
cachedTokens: usageSum?.cached_tokens,
|
|
13193
13636
|
costYuan: costKnown ? Math.round(costYuanAcc * 1e6) / 1e6 : void 0,
|
|
13637
|
+
toolCostYuan: toolCostYuanAcc > 0 ? Math.round(toolCostYuanAcc * 1e6) / 1e6 : void 0,
|
|
13638
|
+
// 按次计费工具花费(Search/Fetch)
|
|
13639
|
+
auxCostYuan: auxCostYuanAcc > 0 ? Math.round(auxCostYuanAcc * 1e6) / 1e6 : void 0,
|
|
13640
|
+
// 辅助模型花费(压缩摘要+风险分类器,P0-2)
|
|
13194
13641
|
tools: toolCounts
|
|
13195
13642
|
});
|
|
13196
13643
|
} catch {
|
|
13197
13644
|
}
|
|
13198
13645
|
try {
|
|
13199
|
-
|
|
13646
|
+
costRelay.flush(budgetSpentYuan);
|
|
13200
13647
|
} catch {
|
|
13201
13648
|
}
|
|
13202
13649
|
try {
|
|
@@ -13210,6 +13657,12 @@ async function* runAgent(message, options) {
|
|
|
13210
13657
|
cachedTokens: usageSum.cached_tokens ?? 0,
|
|
13211
13658
|
cacheHitRate: prompt > 0 ? (usageSum.cached_tokens ?? 0) / prompt : 0,
|
|
13212
13659
|
costYuan: costKnown ? Math.round(costYuanAcc * 1e6) / 1e6 : null,
|
|
13660
|
+
toolCostYuan: toolCostYuanAcc > 0 ? Math.round(toolCostYuanAcc * 1e6) / 1e6 : void 0,
|
|
13661
|
+
// 按次计费工具花费(undefined 自动省略)
|
|
13662
|
+
auxCostYuan: auxCostYuanAcc > 0 ? Math.round(auxCostYuanAcc * 1e6) / 1e6 : void 0,
|
|
13663
|
+
// 辅助模型花费(压缩摘要+风险分类器;undefined 自动省略)
|
|
13664
|
+
subCostYuan: subCostYuanAcc > 0 ? Math.round(subCostYuanAcc * 1e6) / 1e6 : void 0,
|
|
13665
|
+
// ★ Q12:子 run 花费并列展示(展示层补偿;undefined 自动省略)
|
|
13213
13666
|
durationMs: Math.round(performance.now() - startTime),
|
|
13214
13667
|
budgetTruncated: budgetTruncated || void 0
|
|
13215
13668
|
// 预算截断标注(E2;undefined 自动省略)
|
|
@@ -13224,12 +13677,13 @@ async function* runAgent(message, options) {
|
|
|
13224
13677
|
console.warn("\u26A0\uFE0F Stop hook \u5916\u5C42\u515C\u5E95\uFF08dispatch \u5185\u90E8\u5BB9\u9519\u5931\u6548\uFF0C\u5C5E\u5F02\u5E38\u4FE1\u53F7\uFF09:", e instanceof Error ? e.message : e);
|
|
13225
13678
|
}
|
|
13226
13679
|
try {
|
|
13227
|
-
await updateCalibration(sessionId, { calibRatio, lastRealPromptTokens, lastCachedTokens });
|
|
13680
|
+
await updateCalibration(sessionId, effectiveModel, { calibRatio, lastRealPromptTokens, lastCachedTokens });
|
|
13228
13681
|
} catch (e) {
|
|
13229
13682
|
console.warn("\u26A0\uFE0F \u538B\u7F29\u6821\u51C6\u72B6\u6001\u843D\u76D8\u5931\u8D25\uFF08\u4E0B run \u6821\u51C6\u91CD\u96F6\uFF0C\u7F13\u5B58\u611F\u77E5\u538B\u7F29\u9000\u5316\u56DE\u7F3A\u7701 1.4\uFF09:", e instanceof Error ? e.message : e);
|
|
13230
13683
|
}
|
|
13231
13684
|
}
|
|
13232
13685
|
}
|
|
13686
|
+
var resolveFirstPrompt;
|
|
13233
13687
|
var init_runAgent = __esm({
|
|
13234
13688
|
"../core/src/agent/runAgent.ts"() {
|
|
13235
13689
|
"use strict";
|
|
@@ -13246,9 +13700,23 @@ var init_runAgent = __esm({
|
|
|
13246
13700
|
init_toolScheduling();
|
|
13247
13701
|
init_streamInference();
|
|
13248
13702
|
init_agentNudges();
|
|
13703
|
+
init_costRelay();
|
|
13249
13704
|
init_usageLog();
|
|
13250
13705
|
init_prices();
|
|
13251
13706
|
init_createModel();
|
|
13707
|
+
init_client();
|
|
13708
|
+
resolveFirstPrompt = (message, options) => {
|
|
13709
|
+
if ((options.depth ?? 0) !== 0) return "";
|
|
13710
|
+
const explicit = options.currentUserText?.trim();
|
|
13711
|
+
if (explicit) return explicit;
|
|
13712
|
+
const arr = message;
|
|
13713
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
13714
|
+
if (arr[i]?.role !== "user") continue;
|
|
13715
|
+
const t = msgText(arr[i]?.content);
|
|
13716
|
+
if (t.trim()) return t;
|
|
13717
|
+
}
|
|
13718
|
+
return "";
|
|
13719
|
+
};
|
|
13252
13720
|
}
|
|
13253
13721
|
});
|
|
13254
13722
|
|
|
@@ -13306,11 +13774,12 @@ var init_lineage = __esm({
|
|
|
13306
13774
|
});
|
|
13307
13775
|
|
|
13308
13776
|
// ../core/src/agent/subagent.ts
|
|
13309
|
-
var SUBAGENT_DENYLIST, activeSubagents, failText, RESUME_NOTE, createProgressThrottler, runSubagent;
|
|
13777
|
+
var SUBAGENT_DENYLIST, filterToolsForSubagent, activeSubagents, failText, RESUME_NOTE, createProgressThrottler, runSubagent;
|
|
13310
13778
|
var init_subagent = __esm({
|
|
13311
13779
|
"../core/src/agent/subagent.ts"() {
|
|
13312
13780
|
"use strict";
|
|
13313
13781
|
init_runAgent();
|
|
13782
|
+
init_config();
|
|
13314
13783
|
init_content();
|
|
13315
13784
|
init_transcript();
|
|
13316
13785
|
init_common();
|
|
@@ -13318,7 +13787,14 @@ var init_subagent = __esm({
|
|
|
13318
13787
|
init_registry4();
|
|
13319
13788
|
init_hooks();
|
|
13320
13789
|
init_lineage();
|
|
13321
|
-
SUBAGENT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path"]);
|
|
13790
|
+
SUBAGENT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path", "run_in_background"]);
|
|
13791
|
+
filterToolsForSubagent = (allTools, manifest) => {
|
|
13792
|
+
if (manifest && manifest.tools.length > 0) {
|
|
13793
|
+
const wantsMcp = manifest.tools.some((t) => t === "mcp_call" || t === "mcp_list_tools" || t.startsWith("mcp__"));
|
|
13794
|
+
return allTools.filter((t) => manifest.tools.includes(t.function.name) || wantsMcp && (t.function.name === "mcp_call" || t.function.name === "mcp_list_tools"));
|
|
13795
|
+
}
|
|
13796
|
+
return allTools.filter((t) => t.function.subagentAllowed !== false && !SUBAGENT_DENYLIST.has(t.function.name));
|
|
13797
|
+
};
|
|
13322
13798
|
activeSubagents = /* @__PURE__ */ new Set();
|
|
13323
13799
|
failText = (msg) => toolFailure(msg).content;
|
|
13324
13800
|
RESUME_NOTE = [
|
|
@@ -13422,8 +13898,7 @@ var init_subagent = __esm({
|
|
|
13422
13898
|
...resuming ? [RESUME_NOTE] : []
|
|
13423
13899
|
].filter(Boolean).join("\n\n");
|
|
13424
13900
|
const allTools = getGlobalTools();
|
|
13425
|
-
const
|
|
13426
|
-
const toolSchemas = manifest && manifest.tools.length > 0 ? allTools.filter((t) => manifest.tools.includes(t.function.name) || wantsMcp && (t.function.name === "mcp_call" || t.function.name === "mcp_list_tools")) : allTools.filter((t) => !SUBAGENT_DENYLIST.has(t.function.name));
|
|
13901
|
+
const toolSchemas = filterToolsForSubagent(allTools, manifest);
|
|
13427
13902
|
console.log(`${resuming ? "\u{1F501} \u7EED\u8DD1" : "\u{1F423} \u6D3E\u751F"}\u5B50 Agent [\u6DF1\u5EA6: ${ctx.depth + 1}/${MAX_AGENT_DEPTH}][${subSessionId}]${manifest ? ` \u58F0\u660E\u5F0F=${manifest.name}` : ""} \u4EFB\u52A1: "${task.slice(0, 50)}..."`);
|
|
13428
13903
|
activeSubagents.add(subSessionId);
|
|
13429
13904
|
ctx.onUIEvent?.({ type: "subagent.count", running: activeSubagents.size, phase: "start", name: manifest?.name, depth: ctx.depth + 1 });
|
|
@@ -13438,7 +13913,9 @@ var init_subagent = __esm({
|
|
|
13438
13913
|
// ★ 透传父级工作目录,子 agent 的 hook/工具相对路径与父级一致
|
|
13439
13914
|
toolSchemas,
|
|
13440
13915
|
abortSignal: ctx.abortSignal,
|
|
13441
|
-
|
|
13916
|
+
// ★ P1-4:manifest.model 覆盖时窗口按该模型解析(MODEL_WINDOWS 表 / 全局回退);
|
|
13917
|
+
// 未覆盖(与父同模型)直接继承父窗口,行为不变。
|
|
13918
|
+
modelWindow: manifest?.model ? resolveModelWindow(manifest.model) : ctx.modelWindow,
|
|
13442
13919
|
depth: ctx.depth + 1,
|
|
13443
13920
|
keepRecentUnits: ctx.keepRecentUnits,
|
|
13444
13921
|
compactRatio: ctx.compactRatio,
|
|
@@ -13456,13 +13933,15 @@ var init_subagent = __esm({
|
|
|
13456
13933
|
// ★ 子 agent final 是交付父级的汇报,EARLY_FINAL 误推一轮纯浪费;父可经续跑纠错
|
|
13457
13934
|
model: manifest?.model,
|
|
13458
13935
|
// ★ per-agent 模型覆盖;undefined 时 model.ts 回退全局 MODEL_NAME
|
|
13459
|
-
costSink: ctx.reportCost
|
|
13936
|
+
costSink: ctx.reportCost,
|
|
13460
13937
|
// ★ 子 run 成本回灌父累计器(成本信任链第二环 E1):父预算自动继承约束
|
|
13938
|
+
budgetCheck: ctx.budgetCheck
|
|
13939
|
+
// ★ P1-5 预算熔断链式透传:子 runAgent 轮边界探测父预算,超支即收尾(不等烧完)
|
|
13461
13940
|
};
|
|
13462
13941
|
const startCtx = { sessionId: subSessionId, parentSessionId: ctx.sessionId, task, depth: ctx.depth + 1, name: manifest?.name, cwd: ctx.cwd };
|
|
13463
13942
|
await dispatch("SubagentStart", startCtx);
|
|
13464
13943
|
let result;
|
|
13465
|
-
const progress = createProgressThrottler((line) => ctx.onUIEvent?.({ type: "subagent.progress", text: line }));
|
|
13944
|
+
const progress = createProgressThrottler((line) => ctx.onUIEvent?.({ type: "subagent.progress", text: line, agentKey: subSessionId, name: manifest?.name }));
|
|
13466
13945
|
try {
|
|
13467
13946
|
for await (const e of runAgent(subMessages, subOptions)) {
|
|
13468
13947
|
if (ctx.abortSignal?.aborted) {
|
|
@@ -13486,7 +13965,7 @@ var init_subagent = __esm({
|
|
|
13486
13965
|
result = { ok: false, output: failText(`[\u5B50Agent\u5D29\u6E83]\uFF1A\u5B50 Agent \u5728\u8FED\u4EE3\u63A8\u7406\u4E3B\u5FAA\u73AF\u65F6\u906D\u9047\u5E95\u5C42\u5F02\u5E38: ${streamError.message}`), sessionId: subSessionId, manifestName: manifest?.name };
|
|
13487
13966
|
} finally {
|
|
13488
13967
|
try {
|
|
13489
|
-
ctx.onUIEvent?.({ type: "subagent.progress", text: "", done: true });
|
|
13968
|
+
ctx.onUIEvent?.({ type: "subagent.progress", text: "", done: true, agentKey: subSessionId });
|
|
13490
13969
|
} catch {
|
|
13491
13970
|
}
|
|
13492
13971
|
if (result) {
|
|
@@ -14732,14 +15211,15 @@ var createWebRequestApproval;
|
|
|
14732
15211
|
var init_webHost = __esm({
|
|
14733
15212
|
"../core/src/host/webHost.ts"() {
|
|
14734
15213
|
"use strict";
|
|
14735
|
-
createWebRequestApproval = (onUIEvent, abortSignal) => {
|
|
15214
|
+
createWebRequestApproval = (onUIEvent, abortSignal, opts) => {
|
|
14736
15215
|
return async (detail, meta) => {
|
|
14737
15216
|
onUIEvent?.({
|
|
14738
15217
|
type: "approval_request",
|
|
14739
15218
|
sessionId: meta.sessionId,
|
|
14740
15219
|
toolsId: meta.toolCallId,
|
|
14741
15220
|
toolName: meta.toolName,
|
|
14742
|
-
detail
|
|
15221
|
+
detail,
|
|
15222
|
+
...opts?.includeFullDetail && meta.fullDetail ? { fullDetail: meta.fullDetail } : {}
|
|
14743
15223
|
});
|
|
14744
15224
|
const { waitForUserApproval: waitForUserApproval2 } = await Promise.resolve().then(() => (init_approvalGate(), approvalGate_exports));
|
|
14745
15225
|
return waitForUserApproval2(meta.sessionId, meta.toolCallId, abortSignal);
|
|
@@ -14883,7 +15363,8 @@ ${envLines.join("\n")}`;
|
|
|
14883
15363
|
// 宿主可按需裁剪(挂机批处理);缺省全量,行为不变
|
|
14884
15364
|
abortSignal,
|
|
14885
15365
|
// ← 透传中止信号
|
|
14886
|
-
modelWindow:
|
|
15366
|
+
modelWindow: resolveModelWindow(opts?.model),
|
|
15367
|
+
// ★ P1-4:窗口按本轮模型解析(MODEL_WINDOWS 表 / 全局回退)
|
|
14887
15368
|
keepRecentUnits: appConfig.KEEP_RECENT_UNITS,
|
|
14888
15369
|
compactRatio: appConfig.COMPACT_RATIO,
|
|
14889
15370
|
parentSystemPrompt: sysPrompt,
|
|
@@ -14902,8 +15383,11 @@ ${envLines.join("\n")}`;
|
|
|
14902
15383
|
thinkingLevel: opts?.thinkingLevel,
|
|
14903
15384
|
locale: opts?.locale,
|
|
14904
15385
|
outputStyle: opts?.outputStyle,
|
|
14905
|
-
budgetYuan: opts?.budgetYuan
|
|
15386
|
+
budgetYuan: opts?.budgetYuan,
|
|
14906
15387
|
// ★ 预算制运行(成本信任链第二环 E1):主 run 传入,子 run 经 costSink 回灌
|
|
15388
|
+
// ★ P0-1:本轮用户输入原文(inbound.content 已过 slash 展开与 hook 注入定型),供 firstPrompt
|
|
15389
|
+
// 判定取「当前轮」而非消息数组首条(首条复杂 + 本轮寒暄曾误触发 PLAN_FIRST/EARLY_FINAL)。
|
|
15390
|
+
currentUserText: inbound.content
|
|
14907
15391
|
};
|
|
14908
15392
|
await runWithSessionContext(sessionId, async () => {
|
|
14909
15393
|
try {
|
|
@@ -15134,14 +15618,154 @@ ${s.slice(-half)}`;
|
|
|
15134
15618
|
}
|
|
15135
15619
|
});
|
|
15136
15620
|
|
|
15137
|
-
// src/
|
|
15138
|
-
var
|
|
15139
|
-
var
|
|
15140
|
-
"src/
|
|
15621
|
+
// src/lineDiff.ts
|
|
15622
|
+
var splitLines, lineDiff, collapseContext;
|
|
15623
|
+
var init_lineDiff = __esm({
|
|
15624
|
+
"src/lineDiff.ts"() {
|
|
15141
15625
|
"use strict";
|
|
15142
|
-
|
|
15143
|
-
|
|
15144
|
-
|
|
15626
|
+
splitLines = (s) => {
|
|
15627
|
+
const t = String(s ?? "").replace(/\r\n/g, "\n");
|
|
15628
|
+
return t === "" ? [] : t.split("\n");
|
|
15629
|
+
};
|
|
15630
|
+
lineDiff = (oldStr, newStr) => {
|
|
15631
|
+
const a = splitLines(oldStr);
|
|
15632
|
+
const b = splitLines(newStr);
|
|
15633
|
+
let s = 0;
|
|
15634
|
+
while (s < a.length && s < b.length && a[s] === b[s]) s++;
|
|
15635
|
+
let e = 0;
|
|
15636
|
+
while (e < a.length - s && e < b.length - s && a[a.length - 1 - e] === b[b.length - 1 - e]) e++;
|
|
15637
|
+
const m1 = a.slice(s, a.length - e);
|
|
15638
|
+
const m2 = b.slice(s, b.length - e);
|
|
15639
|
+
const rows = [];
|
|
15640
|
+
for (let i = 0; i < s; i++) rows.push({ t: "ctx", s: a[i] });
|
|
15641
|
+
const n = m1.length, m = m2.length;
|
|
15642
|
+
if (n === 0 && m === 0) {
|
|
15643
|
+
} else if (n * m > 4e6) {
|
|
15644
|
+
for (const l of m1) rows.push({ t: "del", s: l });
|
|
15645
|
+
for (const l of m2) rows.push({ t: "add", s: l });
|
|
15646
|
+
} else {
|
|
15647
|
+
const w = m + 1;
|
|
15648
|
+
const dp = new Uint32Array((n + 1) * w);
|
|
15649
|
+
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
15650
|
+
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
15651
|
+
dp[i2 * w + j2] = m1[i2] === m2[j2] ? dp[(i2 + 1) * w + j2 + 1] + 1 : Math.max(dp[(i2 + 1) * w + j2], dp[i2 * w + j2 + 1]);
|
|
15652
|
+
}
|
|
15653
|
+
}
|
|
15654
|
+
let i = 0, j = 0;
|
|
15655
|
+
while (i < n && j < m) {
|
|
15656
|
+
if (m1[i] === m2[j]) {
|
|
15657
|
+
rows.push({ t: "ctx", s: m1[i] });
|
|
15658
|
+
i++;
|
|
15659
|
+
j++;
|
|
15660
|
+
} else if (dp[(i + 1) * w + j] >= dp[i * w + j + 1]) {
|
|
15661
|
+
rows.push({ t: "del", s: m1[i] });
|
|
15662
|
+
i++;
|
|
15663
|
+
} else {
|
|
15664
|
+
rows.push({ t: "add", s: m2[j] });
|
|
15665
|
+
j++;
|
|
15666
|
+
}
|
|
15667
|
+
}
|
|
15668
|
+
while (i < n) {
|
|
15669
|
+
rows.push({ t: "del", s: m1[i] });
|
|
15670
|
+
i++;
|
|
15671
|
+
}
|
|
15672
|
+
while (j < m) {
|
|
15673
|
+
rows.push({ t: "add", s: m2[j] });
|
|
15674
|
+
j++;
|
|
15675
|
+
}
|
|
15676
|
+
}
|
|
15677
|
+
for (let k = 0; k < e; k++) rows.push({ t: "ctx", s: a[a.length - e + k] });
|
|
15678
|
+
return rows;
|
|
15679
|
+
};
|
|
15680
|
+
collapseContext = (rows, keep = 2) => {
|
|
15681
|
+
const out = [];
|
|
15682
|
+
let i = 0;
|
|
15683
|
+
while (i < rows.length) {
|
|
15684
|
+
if (rows[i].t !== "ctx") {
|
|
15685
|
+
out.push(rows[i]);
|
|
15686
|
+
i++;
|
|
15687
|
+
continue;
|
|
15688
|
+
}
|
|
15689
|
+
let j = i;
|
|
15690
|
+
while (j < rows.length && rows[j].t === "ctx") j++;
|
|
15691
|
+
const run = j - i;
|
|
15692
|
+
if (run > keep * 2 + 1) {
|
|
15693
|
+
for (let k = 0; k < keep; k++) out.push(rows[i + k]);
|
|
15694
|
+
out.push({ t: "ellip", n: run - keep * 2 });
|
|
15695
|
+
for (let k = run - keep; k < run; k++) out.push(rows[i + k]);
|
|
15696
|
+
} else {
|
|
15697
|
+
for (let k = 0; k < run; k++) out.push(rows[i + k]);
|
|
15698
|
+
}
|
|
15699
|
+
i = j;
|
|
15700
|
+
}
|
|
15701
|
+
return out;
|
|
15702
|
+
};
|
|
15703
|
+
}
|
|
15704
|
+
});
|
|
15705
|
+
|
|
15706
|
+
// src/diffSummary.ts
|
|
15707
|
+
import fs48 from "fs/promises";
|
|
15708
|
+
import path49 from "path";
|
|
15709
|
+
var readTextIfExists, buildRunDiff;
|
|
15710
|
+
var init_diffSummary = __esm({
|
|
15711
|
+
"src/diffSummary.ts"() {
|
|
15712
|
+
"use strict";
|
|
15713
|
+
init_lineDiff();
|
|
15714
|
+
init_store2();
|
|
15715
|
+
readTextIfExists = async (p) => {
|
|
15716
|
+
try {
|
|
15717
|
+
return await fs48.readFile(p, "utf-8");
|
|
15718
|
+
} catch {
|
|
15719
|
+
return null;
|
|
15720
|
+
}
|
|
15721
|
+
};
|
|
15722
|
+
buildRunDiff = async (sessionId, records) => {
|
|
15723
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
15724
|
+
for (const r of records) {
|
|
15725
|
+
if (r.restored) continue;
|
|
15726
|
+
const prev = byPath.get(r.relativePath);
|
|
15727
|
+
if (!prev || (r.timestamp || "") < (prev.timestamp || "")) byPath.set(r.relativePath, r);
|
|
15728
|
+
}
|
|
15729
|
+
const root = process.env.WORKSPACE_ROOT ?? process.cwd();
|
|
15730
|
+
const out = [];
|
|
15731
|
+
for (const r of [...byPath.values()].sort((a, b) => a.relativePath.localeCompare(b.relativePath))) {
|
|
15732
|
+
const abs = path49.join(root, ...r.relativePath.split("/"));
|
|
15733
|
+
if (r.backupKind === "directory_tree") {
|
|
15734
|
+
out.push({ path: r.relativePath, note: "\u25A4 \u76EE\u5F55\u6811\u6539\u52A8\uFF08\u5185\u5BB9 diff \u7565\uFF0C\u53EF /rewind \u56DE\u9000\uFF09", rows: null });
|
|
15735
|
+
continue;
|
|
15736
|
+
}
|
|
15737
|
+
const disk = await readTextIfExists(abs);
|
|
15738
|
+
if (disk === null) {
|
|
15739
|
+
out.push({ path: r.relativePath, note: "\uFF08\u6587\u4EF6\u5DF2\u4E0D\u5728\u78C1\u76D8\u2014\u2014\u5DF2\u88AB\u56DE\u9000\u6216\u624B\u52A8\u5220\u9664\uFF09", rows: null });
|
|
15740
|
+
continue;
|
|
15741
|
+
}
|
|
15742
|
+
let before;
|
|
15743
|
+
if (r.backupKind === "creation_marker") {
|
|
15744
|
+
before = "";
|
|
15745
|
+
} else {
|
|
15746
|
+
before = await readTextIfExists(path49.join(getUndoBackupRoot(sessionId), r.undoId, "content"));
|
|
15747
|
+
}
|
|
15748
|
+
if (before === null) {
|
|
15749
|
+
out.push({ path: r.relativePath, note: "\uFF08\u5907\u4EFD\u5DF2\u88AB\u6E05\u7406\uFF0C\u65E0\u6CD5\u751F\u6210\u7EA2\u7EFF\u5BF9\u6BD4\uFF09", rows: null });
|
|
15750
|
+
continue;
|
|
15751
|
+
}
|
|
15752
|
+
const rows = collapseContext(lineDiff(before, disk));
|
|
15753
|
+
if (!rows.some((c) => c.t === "add" || c.t === "del")) continue;
|
|
15754
|
+
out.push({ path: r.relativePath, rows });
|
|
15755
|
+
}
|
|
15756
|
+
return out;
|
|
15757
|
+
};
|
|
15758
|
+
}
|
|
15759
|
+
});
|
|
15760
|
+
|
|
15761
|
+
// src/cliHost.ts
|
|
15762
|
+
var createCliRequestApproval, createCliRequestQuestion;
|
|
15763
|
+
var init_cliHost = __esm({
|
|
15764
|
+
"src/cliHost.ts"() {
|
|
15765
|
+
"use strict";
|
|
15766
|
+
createCliRequestApproval = (askApproval) => {
|
|
15767
|
+
return async (detail, meta) => askApproval(detail, meta.toolName, meta.fullDetail);
|
|
15768
|
+
};
|
|
15145
15769
|
createCliRequestQuestion = (askQuestion) => {
|
|
15146
15770
|
return async (req) => askQuestion(req);
|
|
15147
15771
|
};
|
|
@@ -15332,6 +15956,9 @@ var init_useChatState = __esm({
|
|
|
15332
15956
|
init_guard();
|
|
15333
15957
|
init_createModel();
|
|
15334
15958
|
init_tool();
|
|
15959
|
+
init_permissions();
|
|
15960
|
+
init_restore();
|
|
15961
|
+
init_diffSummary();
|
|
15335
15962
|
init_cliHost();
|
|
15336
15963
|
init_prefs();
|
|
15337
15964
|
init_strings();
|
|
@@ -15357,6 +15984,7 @@ var init_useChatState = __esm({
|
|
|
15357
15984
|
const [pendingSessions, setPendingSessions] = useState(null);
|
|
15358
15985
|
const [pendingFork, setPendingFork] = useState(null);
|
|
15359
15986
|
const [pendingModel, setPendingModel] = useState(null);
|
|
15987
|
+
const [pendingRewind, setPendingRewind] = useState(null);
|
|
15360
15988
|
const nextId = useRef(1);
|
|
15361
15989
|
const assistantStreamingId = useRef(null);
|
|
15362
15990
|
const thinkingStreamingId = useRef(null);
|
|
@@ -15384,6 +16012,7 @@ var init_useChatState = __esm({
|
|
|
15384
16012
|
const budgetYuanRef = useRef(void 0);
|
|
15385
16013
|
const lastRunCacheHitRef = useRef(void 0);
|
|
15386
16014
|
const contextUsageRef = useRef(void 0);
|
|
16015
|
+
const lastSubmitAtRef = useRef(0);
|
|
15387
16016
|
const compactProgressIdRef = useRef(null);
|
|
15388
16017
|
const dropCompactProgressRow = useCallback(() => {
|
|
15389
16018
|
const pid = compactProgressIdRef.current;
|
|
@@ -15391,12 +16020,20 @@ var init_useChatState = __esm({
|
|
|
15391
16020
|
compactProgressIdRef.current = null;
|
|
15392
16021
|
setRows((prev) => prev.filter((r) => r.id !== pid));
|
|
15393
16022
|
}, []);
|
|
15394
|
-
const
|
|
15395
|
-
const dropSubagentProgressRow = useCallback(() => {
|
|
15396
|
-
const
|
|
15397
|
-
if (
|
|
15398
|
-
|
|
15399
|
-
|
|
16023
|
+
const subagentProgressRowsRef = useRef(/* @__PURE__ */ new Map());
|
|
16024
|
+
const dropSubagentProgressRow = useCallback((key) => {
|
|
16025
|
+
const m = subagentProgressRowsRef.current;
|
|
16026
|
+
if (key) {
|
|
16027
|
+
const rid = m.get(key);
|
|
16028
|
+
if (rid == null) return;
|
|
16029
|
+
m.delete(key);
|
|
16030
|
+
setRows((prev) => prev.filter((r) => r.id !== rid));
|
|
16031
|
+
return;
|
|
16032
|
+
}
|
|
16033
|
+
if (m.size === 0) return;
|
|
16034
|
+
const ids = new Set(m.values());
|
|
16035
|
+
m.clear();
|
|
16036
|
+
setRows((prev) => prev.filter((r) => !ids.has(r.id)));
|
|
15400
16037
|
}, []);
|
|
15401
16038
|
const outputStyleRef = useRef(void 0);
|
|
15402
16039
|
const lastUsageRef = useRef(null);
|
|
@@ -15502,6 +16139,24 @@ var init_useChatState = __esm({
|
|
|
15502
16139
|
scheduleFlush();
|
|
15503
16140
|
break;
|
|
15504
16141
|
}
|
|
16142
|
+
case "text.reset": {
|
|
16143
|
+
if (flushTimer.current) {
|
|
16144
|
+
clearTimeout(flushTimer.current);
|
|
16145
|
+
flushTimer.current = null;
|
|
16146
|
+
}
|
|
16147
|
+
textBuffer.current = "";
|
|
16148
|
+
thinkingBuffer.current = "";
|
|
16149
|
+
const raId = assistantStreamingId.current;
|
|
16150
|
+
const rthId = thinkingStreamingId.current;
|
|
16151
|
+
if (raId != null || rthId != null) {
|
|
16152
|
+
setRows((prev) => prev.map((row) => {
|
|
16153
|
+
if (row.id === raId && row.kind === "assistant") return { ...row, text: "" };
|
|
16154
|
+
if (row.id === rthId && row.kind === "thinking") return { ...row, text: "" };
|
|
16155
|
+
return row;
|
|
16156
|
+
}));
|
|
16157
|
+
}
|
|
16158
|
+
break;
|
|
16159
|
+
}
|
|
15505
16160
|
case "tool.start": {
|
|
15506
16161
|
closeStreaming();
|
|
15507
16162
|
const id = newRowId();
|
|
@@ -15570,16 +16225,20 @@ var init_useChatState = __esm({
|
|
|
15570
16225
|
const durMs = obj.durationMs ?? 0;
|
|
15571
16226
|
const durText = durMs >= 6e4 ? `${Math.floor(durMs / 6e4)}m${Math.round(durMs % 6e4 / 1e3)}s` : `${(durMs / 1e3).toFixed(1)}s`;
|
|
15572
16227
|
const cost = obj.costYuan;
|
|
16228
|
+
const toolCost = typeof obj.toolCostYuan === "number" && obj.toolCostYuan > 0 ? obj.toolCostYuan : null;
|
|
16229
|
+
const auxCost = typeof obj.auxCostYuan === "number" && obj.auxCostYuan > 0 ? obj.auxCostYuan : null;
|
|
16230
|
+
const subCost = typeof obj.subCostYuan === "number" && obj.subCostYuan > 0 ? obj.subCostYuan : null;
|
|
15573
16231
|
const tokTotal = promptT + completionT;
|
|
15574
16232
|
const tokText = tokTotal >= 1e4 ? `${(tokTotal / 1e3).toFixed(1)}k` : String(tokTotal);
|
|
15575
16233
|
const costText = typeof cost === "number" ? `\xA5${cost.toFixed(cost < 5e-3 ? 4 : 3)}` : `${tokText} tok`;
|
|
15576
16234
|
setRows((prev) => [...prev, {
|
|
15577
16235
|
id: newRowId(),
|
|
15578
16236
|
kind: "meta",
|
|
15579
|
-
text: `\u{1F4B0} \u672C\u6B21 ${rounds} \u8F6E \xB7 ${costText} \xB7 \u7F13\u5B58\u547D\u4E2D ${hitPct}% \xB7 ${durText}${obj.budgetTruncated ? " \xB7 \u9884\u7B97\u622A\u65AD" : ""}`
|
|
16237
|
+
text: `\u{1F4B0} \u672C\u6B21 ${rounds} \u8F6E \xB7 ${costText}${toolCost != null ? ` \xB7 \u8054\u7F51 \xA5${toolCost.toFixed(3)}` : ""}${auxCost != null ? ` \xB7 \u8F85\u52A9 \xA5${auxCost.toFixed(3)}` : ""}${subCost != null ? ` \xB7 \u5B50\u4EFB\u52A1 \xA5${subCost.toFixed(3)}` : ""} \xB7 \u7F13\u5B58\u547D\u4E2D ${hitPct}% \xB7 ${durText}${obj.budgetTruncated ? " \xB7 \u9884\u7B97\u622A\u65AD" : ""}`
|
|
15580
16238
|
}]);
|
|
15581
|
-
lastRunCostRef.current = typeof cost === "number" ? cost : void 0;
|
|
16239
|
+
lastRunCostRef.current = typeof cost === "number" ? cost + (auxCost ?? 0) : auxCost != null ? auxCost : void 0;
|
|
15582
16240
|
sessionCostRef.current = typeof cost === "number" ? (sessionCostRef.current ?? 0) + cost : sessionCostRef.current;
|
|
16241
|
+
if (auxCost != null) sessionCostRef.current = (sessionCostRef.current ?? 0) + auxCost;
|
|
15583
16242
|
lastRunCacheHitRef.current = typeof obj.cacheHitRate === "number" ? obj.cacheHitRate : void 0;
|
|
15584
16243
|
break;
|
|
15585
16244
|
}
|
|
@@ -15608,19 +16267,23 @@ var init_useChatState = __esm({
|
|
|
15608
16267
|
}
|
|
15609
16268
|
case "subagent.progress": {
|
|
15610
16269
|
flush();
|
|
16270
|
+
const agentKey = typeof obj.agentKey === "string" && obj.agentKey ? obj.agentKey : "__default__";
|
|
15611
16271
|
if (obj.done) {
|
|
15612
|
-
dropSubagentProgressRow();
|
|
16272
|
+
dropSubagentProgressRow(agentKey);
|
|
15613
16273
|
break;
|
|
15614
16274
|
}
|
|
15615
16275
|
const ptext = String(obj.text ?? "").slice(0, 160);
|
|
15616
16276
|
if (!ptext) break;
|
|
15617
|
-
|
|
16277
|
+
const agentName = typeof obj.name === "string" && obj.name ? `\u27E8${obj.name}\u27E9 ` : "";
|
|
16278
|
+
const pline = `${agentName}${ptext}`;
|
|
16279
|
+
const rowMap = subagentProgressRowsRef.current;
|
|
16280
|
+
const existingId = rowMap.get(agentKey);
|
|
16281
|
+
if (existingId == null) {
|
|
15618
16282
|
const id = newRowId();
|
|
15619
|
-
|
|
15620
|
-
setRows((prev) => [...prev, { id, kind: "subagent-progress", text:
|
|
16283
|
+
rowMap.set(agentKey, id);
|
|
16284
|
+
setRows((prev) => [...prev, { id, kind: "subagent-progress", text: pline }]);
|
|
15621
16285
|
} else {
|
|
15622
|
-
|
|
15623
|
-
setRows((prev) => prev.map((r) => r.id === pid && r.kind === "subagent-progress" ? { ...r, text: ptext } : r));
|
|
16286
|
+
setRows((prev) => prev.map((r) => r.id === existingId && r.kind === "subagent-progress" ? { ...r, text: pline } : r));
|
|
15624
16287
|
}
|
|
15625
16288
|
break;
|
|
15626
16289
|
}
|
|
@@ -15700,7 +16363,7 @@ var init_useChatState = __esm({
|
|
|
15700
16363
|
break;
|
|
15701
16364
|
}
|
|
15702
16365
|
}, [closeStreaming, flush, scheduleFlush, pushInfo]);
|
|
15703
|
-
const askApproval = useCallback((detail, toolName) => new Promise((resolve) => setPendingApproval({ detail, toolName, resolve })), []);
|
|
16366
|
+
const askApproval = useCallback((detail, toolName, fullDetail) => new Promise((resolve) => setPendingApproval({ detail, toolName, fullDetail, resolve })), []);
|
|
15704
16367
|
const resolveApproval = useCallback((v) => {
|
|
15705
16368
|
setPendingApproval((prev) => {
|
|
15706
16369
|
prev?.resolve(v);
|
|
@@ -15709,7 +16372,7 @@ var init_useChatState = __esm({
|
|
|
15709
16372
|
}, []);
|
|
15710
16373
|
const autoRequestApproval = useCallback(async (_detail, meta) => {
|
|
15711
16374
|
const t = findTool(meta.toolName);
|
|
15712
|
-
if (!t || t.function.safetyLevel === "danger" /* DANGER */) return askApproval(_detail, meta.toolName);
|
|
16375
|
+
if (!t || t.function.safetyLevel === "danger" /* DANGER */) return askApproval(_detail, meta.toolName, meta.fullDetail);
|
|
15713
16376
|
return "allow-once";
|
|
15714
16377
|
}, [askApproval]);
|
|
15715
16378
|
const askQuestion = useCallback((req) => new Promise((resolve) => setPendingQuestion({ req, resolve })), []);
|
|
@@ -15805,9 +16468,12 @@ ${finalPlan}`, false, res.autoExecute);
|
|
|
15805
16468
|
const runPlanStage = useCallback(async (sid, researchPrompt) => {
|
|
15806
16469
|
await runOnce(sid, researchPrompt, true);
|
|
15807
16470
|
const plan = takeProposedPlan();
|
|
15808
|
-
if (plan == null)
|
|
16471
|
+
if (plan == null) {
|
|
16472
|
+
pushInfo(S.planNoProposal);
|
|
16473
|
+
return false;
|
|
16474
|
+
}
|
|
15809
16475
|
return await approveAndImplement(sid, plan);
|
|
15810
|
-
}, [runOnce, takeProposedPlan, approveAndImplement]);
|
|
16476
|
+
}, [runOnce, takeProposedPlan, approveAndImplement, pushInfo]);
|
|
15811
16477
|
const queueInput = useCallback((content) => {
|
|
15812
16478
|
const text = content.trim();
|
|
15813
16479
|
const sid = sessionIdRef.current;
|
|
@@ -15822,6 +16488,7 @@ ${finalPlan}`, false, res.autoExecute);
|
|
|
15822
16488
|
if (!text || busy) return;
|
|
15823
16489
|
setBusy(true);
|
|
15824
16490
|
const myGen = ++turnGenRef.current;
|
|
16491
|
+
lastSubmitAtRef.current = Date.now();
|
|
15825
16492
|
try {
|
|
15826
16493
|
if (sessionIdRef.current == null) {
|
|
15827
16494
|
sessionIdRef.current = await getOrCreateSessionId(initialSessionId ?? void 0);
|
|
@@ -15892,6 +16559,8 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
15892
16559
|
toolRowByCallId.current.clear();
|
|
15893
16560
|
}, [flush]);
|
|
15894
16561
|
const loadSession = useCallback(async (id) => {
|
|
16562
|
+
const prev = sessionIdRef.current;
|
|
16563
|
+
if (prev && prev !== id) clearSessionPermissionRules(prev);
|
|
15895
16564
|
sessionIdRef.current = id;
|
|
15896
16565
|
lastRunCostRef.current = void 0;
|
|
15897
16566
|
sessionCostRef.current = void 0;
|
|
@@ -15922,6 +16591,8 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
15922
16591
|
if (picked) await loadSession(picked);
|
|
15923
16592
|
}, [pushInfo, loadSession]);
|
|
15924
16593
|
const newSession = useCallback(async () => {
|
|
16594
|
+
const prev = sessionIdRef.current;
|
|
16595
|
+
if (prev) clearSessionPermissionRules(prev);
|
|
15925
16596
|
sessionIdRef.current = await getOrCreateSessionId(void 0);
|
|
15926
16597
|
lastRunCostRef.current = void 0;
|
|
15927
16598
|
sessionCostRef.current = void 0;
|
|
@@ -15979,6 +16650,60 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
15979
16650
|
}
|
|
15980
16651
|
return picked;
|
|
15981
16652
|
}, [pushInfo, setModelOverride]);
|
|
16653
|
+
const resolveRewind = useCallback((undoId) => {
|
|
16654
|
+
setPendingRewind((prev) => {
|
|
16655
|
+
prev?.resolve(undoId);
|
|
16656
|
+
return null;
|
|
16657
|
+
});
|
|
16658
|
+
}, []);
|
|
16659
|
+
const openRewindPicker = useCallback(async () => {
|
|
16660
|
+
const sid = sessionIdRef.current;
|
|
16661
|
+
if (!sid) {
|
|
16662
|
+
pushInfo(S.rewindNoSession);
|
|
16663
|
+
return;
|
|
16664
|
+
}
|
|
16665
|
+
let records = [];
|
|
16666
|
+
try {
|
|
16667
|
+
records = await listUndoable(sid);
|
|
16668
|
+
} catch {
|
|
16669
|
+
}
|
|
16670
|
+
if (records.length === 0) {
|
|
16671
|
+
pushInfo(S.rewindEmpty);
|
|
16672
|
+
return;
|
|
16673
|
+
}
|
|
16674
|
+
const picked = await new Promise((resolve) => setPendingRewind({ records, resolve }));
|
|
16675
|
+
if (!picked) return;
|
|
16676
|
+
try {
|
|
16677
|
+
pushInfo(await restoreByUndoId(picked, sid));
|
|
16678
|
+
} catch (e) {
|
|
16679
|
+
pushInfo(S.rewindFail(e?.message ?? String(e)));
|
|
16680
|
+
}
|
|
16681
|
+
}, [pushInfo]);
|
|
16682
|
+
const openDiff = useCallback(async () => {
|
|
16683
|
+
const sid = sessionIdRef.current;
|
|
16684
|
+
const sinceMs = lastSubmitAtRef.current;
|
|
16685
|
+
if (!sid || !sinceMs) {
|
|
16686
|
+
pushInfo(S.diffEmpty);
|
|
16687
|
+
return;
|
|
16688
|
+
}
|
|
16689
|
+
let records = [];
|
|
16690
|
+
try {
|
|
16691
|
+
records = await listUndoable(sid);
|
|
16692
|
+
} catch {
|
|
16693
|
+
}
|
|
16694
|
+
const sinceIso = new Date(sinceMs).toISOString();
|
|
16695
|
+
const runRecords = records.filter((r) => (r.timestamp || "") >= sinceIso);
|
|
16696
|
+
if (runRecords.length === 0) {
|
|
16697
|
+
pushInfo(S.diffEmpty);
|
|
16698
|
+
return;
|
|
16699
|
+
}
|
|
16700
|
+
const files = await buildRunDiff(sid, runRecords);
|
|
16701
|
+
if (files.length === 0) {
|
|
16702
|
+
pushInfo(S.diffEmpty);
|
|
16703
|
+
return;
|
|
16704
|
+
}
|
|
16705
|
+
setRows((prev) => [...prev, { id: newRowId(), kind: "diff-summary", title: S.diffTitle, files }]);
|
|
16706
|
+
}, [pushInfo]);
|
|
15982
16707
|
const setPlanMode = useCallback((on) => {
|
|
15983
16708
|
planModeRef.current = on;
|
|
15984
16709
|
}, []);
|
|
@@ -16017,6 +16742,7 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
16017
16742
|
pendingSessions,
|
|
16018
16743
|
pendingFork,
|
|
16019
16744
|
pendingModel,
|
|
16745
|
+
pendingRewind,
|
|
16020
16746
|
sessionIdRef,
|
|
16021
16747
|
// 动作
|
|
16022
16748
|
submit,
|
|
@@ -16056,7 +16782,10 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
16056
16782
|
resolveFork,
|
|
16057
16783
|
newSession,
|
|
16058
16784
|
openModelPicker,
|
|
16059
|
-
resolveModel
|
|
16785
|
+
resolveModel,
|
|
16786
|
+
openRewindPicker,
|
|
16787
|
+
resolveRewind,
|
|
16788
|
+
openDiff
|
|
16060
16789
|
};
|
|
16061
16790
|
};
|
|
16062
16791
|
}
|
|
@@ -16439,91 +17168,6 @@ var init_ThinkingBlock = __esm({
|
|
|
16439
17168
|
}
|
|
16440
17169
|
});
|
|
16441
17170
|
|
|
16442
|
-
// src/lineDiff.ts
|
|
16443
|
-
var splitLines, lineDiff, collapseContext;
|
|
16444
|
-
var init_lineDiff = __esm({
|
|
16445
|
-
"src/lineDiff.ts"() {
|
|
16446
|
-
"use strict";
|
|
16447
|
-
splitLines = (s) => {
|
|
16448
|
-
const t = String(s ?? "").replace(/\r\n/g, "\n");
|
|
16449
|
-
return t === "" ? [] : t.split("\n");
|
|
16450
|
-
};
|
|
16451
|
-
lineDiff = (oldStr, newStr) => {
|
|
16452
|
-
const a = splitLines(oldStr);
|
|
16453
|
-
const b = splitLines(newStr);
|
|
16454
|
-
let s = 0;
|
|
16455
|
-
while (s < a.length && s < b.length && a[s] === b[s]) s++;
|
|
16456
|
-
let e = 0;
|
|
16457
|
-
while (e < a.length - s && e < b.length - s && a[a.length - 1 - e] === b[b.length - 1 - e]) e++;
|
|
16458
|
-
const m1 = a.slice(s, a.length - e);
|
|
16459
|
-
const m2 = b.slice(s, b.length - e);
|
|
16460
|
-
const rows = [];
|
|
16461
|
-
for (let i = 0; i < s; i++) rows.push({ t: "ctx", s: a[i] });
|
|
16462
|
-
const n = m1.length, m = m2.length;
|
|
16463
|
-
if (n === 0 && m === 0) {
|
|
16464
|
-
} else if (n * m > 4e6) {
|
|
16465
|
-
for (const l of m1) rows.push({ t: "del", s: l });
|
|
16466
|
-
for (const l of m2) rows.push({ t: "add", s: l });
|
|
16467
|
-
} else {
|
|
16468
|
-
const w = m + 1;
|
|
16469
|
-
const dp = new Uint32Array((n + 1) * w);
|
|
16470
|
-
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
16471
|
-
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
16472
|
-
dp[i2 * w + j2] = m1[i2] === m2[j2] ? dp[(i2 + 1) * w + j2 + 1] + 1 : Math.max(dp[(i2 + 1) * w + j2], dp[i2 * w + j2 + 1]);
|
|
16473
|
-
}
|
|
16474
|
-
}
|
|
16475
|
-
let i = 0, j = 0;
|
|
16476
|
-
while (i < n && j < m) {
|
|
16477
|
-
if (m1[i] === m2[j]) {
|
|
16478
|
-
rows.push({ t: "ctx", s: m1[i] });
|
|
16479
|
-
i++;
|
|
16480
|
-
j++;
|
|
16481
|
-
} else if (dp[(i + 1) * w + j] >= dp[i * w + j + 1]) {
|
|
16482
|
-
rows.push({ t: "del", s: m1[i] });
|
|
16483
|
-
i++;
|
|
16484
|
-
} else {
|
|
16485
|
-
rows.push({ t: "add", s: m2[j] });
|
|
16486
|
-
j++;
|
|
16487
|
-
}
|
|
16488
|
-
}
|
|
16489
|
-
while (i < n) {
|
|
16490
|
-
rows.push({ t: "del", s: m1[i] });
|
|
16491
|
-
i++;
|
|
16492
|
-
}
|
|
16493
|
-
while (j < m) {
|
|
16494
|
-
rows.push({ t: "add", s: m2[j] });
|
|
16495
|
-
j++;
|
|
16496
|
-
}
|
|
16497
|
-
}
|
|
16498
|
-
for (let k = 0; k < e; k++) rows.push({ t: "ctx", s: a[a.length - e + k] });
|
|
16499
|
-
return rows;
|
|
16500
|
-
};
|
|
16501
|
-
collapseContext = (rows, keep = 2) => {
|
|
16502
|
-
const out = [];
|
|
16503
|
-
let i = 0;
|
|
16504
|
-
while (i < rows.length) {
|
|
16505
|
-
if (rows[i].t !== "ctx") {
|
|
16506
|
-
out.push(rows[i]);
|
|
16507
|
-
i++;
|
|
16508
|
-
continue;
|
|
16509
|
-
}
|
|
16510
|
-
let j = i;
|
|
16511
|
-
while (j < rows.length && rows[j].t === "ctx") j++;
|
|
16512
|
-
const run = j - i;
|
|
16513
|
-
if (run > keep * 2 + 1) {
|
|
16514
|
-
for (let k = 0; k < keep; k++) out.push(rows[i + k]);
|
|
16515
|
-
out.push({ t: "ellip", n: run - keep * 2 });
|
|
16516
|
-
for (let k = run - keep; k < run; k++) out.push(rows[i + k]);
|
|
16517
|
-
} else {
|
|
16518
|
-
for (let k = 0; k < run; k++) out.push(rows[i + k]);
|
|
16519
|
-
}
|
|
16520
|
-
i = j;
|
|
16521
|
-
}
|
|
16522
|
-
return out;
|
|
16523
|
-
};
|
|
16524
|
-
}
|
|
16525
|
-
});
|
|
16526
|
-
|
|
16527
17171
|
// src/diffView.ts
|
|
16528
17172
|
var EDIT_TOOL_NAMES, extractEditPairs, pairsToDisplayRows, parseApprovalDiff;
|
|
16529
17173
|
var init_diffView = __esm({
|
|
@@ -16682,7 +17326,7 @@ var init_TodosPanel = __esm({
|
|
|
16682
17326
|
// src/components/ApprovalModal.tsx
|
|
16683
17327
|
import { Box as Box5, Text as Text7 } from "ink";
|
|
16684
17328
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
16685
|
-
var OPTIONS, MAX_DIFF_LINES, ApprovalModal;
|
|
17329
|
+
var OPTIONS, MAX_DIFF_LINES, MAX_FULL_LINES, ApprovalModal;
|
|
16686
17330
|
var init_ApprovalModal = __esm({
|
|
16687
17331
|
"src/components/ApprovalModal.tsx"() {
|
|
16688
17332
|
"use strict";
|
|
@@ -16692,14 +17336,20 @@ var init_ApprovalModal = __esm({
|
|
|
16692
17336
|
init_diffView();
|
|
16693
17337
|
OPTIONS = [
|
|
16694
17338
|
{ label: S.approvalAllow, value: "allow-once", color: THEME.ok },
|
|
17339
|
+
// ★ I-4:会话级记忆(对标 CC「本会话不再询问」)——纯内存,会话结束失效
|
|
17340
|
+
{ label: S.approvalAllowSession, value: "allow-session", color: THEME.ok },
|
|
16695
17341
|
{ label: S.approvalAllowAlways, value: "allow-always", color: THEME.warn },
|
|
16696
17342
|
{ label: S.approvalDeny, value: "deny", color: THEME.danger }
|
|
16697
17343
|
];
|
|
16698
17344
|
MAX_DIFF_LINES = 18;
|
|
16699
|
-
|
|
17345
|
+
MAX_FULL_LINES = 400;
|
|
17346
|
+
ApprovalModal = ({ toolName, detail, fullDetail, expanded, selectedIndex, wrapW }) => {
|
|
16700
17347
|
const contentW = Math.max(16, wrapW);
|
|
16701
|
-
const
|
|
17348
|
+
const showFull = expanded && !!fullDetail;
|
|
17349
|
+
const sourceText = showFull ? fullDetail : detail;
|
|
17350
|
+
const parsed = parseApprovalDiff(sourceText, toolName);
|
|
16702
17351
|
const diffRows = parsed ? pairsToDisplayRows(parsed.sections) : null;
|
|
17352
|
+
const cap = showFull ? MAX_FULL_LINES : MAX_DIFF_LINES;
|
|
16703
17353
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: THEME.warn, paddingX: 1, paddingY: 1, marginTop: 1, children: [
|
|
16704
17354
|
/* @__PURE__ */ jsxs5(Text7, { color: THEME.warn, bold: true, children: [
|
|
16705
17355
|
S.approvalTitle,
|
|
@@ -16708,9 +17358,10 @@ var init_ApprovalModal = __esm({
|
|
|
16708
17358
|
] }),
|
|
16709
17359
|
/* @__PURE__ */ jsx7(Box5, { flexDirection: "column", marginTop: 0.5, marginBottom: 0.5, children: parsed && diffRows ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
16710
17360
|
wrapText(parsed.header, contentW).map((line, j) => /* @__PURE__ */ jsx7(Text7, { color: THEME.white, children: line }, `h-${j}`)),
|
|
16711
|
-
diffRows.slice(0,
|
|
16712
|
-
diffRows.length >
|
|
16713
|
-
] }) : wrapText(
|
|
17361
|
+
diffRows.slice(0, cap).map((r, i) => /* @__PURE__ */ jsx7(Text7, { color: r.t === "del" ? THEME.danger : r.t === "add" ? THEME.ok : THEME.grayDim, children: r.t === "ellip" ? r.n > 0 ? ` \u22EF ${r.n} \u884C\u672A\u53D8` : " \u22EF" : `${r.t === "del" ? "- " : r.t === "add" ? "+ " : " "}${truncateMiddle(r.s, Math.max(20, contentW - 2))}` }, `d-${i}`)),
|
|
17362
|
+
diffRows.length > cap ? /* @__PURE__ */ jsx7(Text7, { color: THEME.grayDim, children: ` \u2026\uFF08\u53E6\u6709 ${diffRows.length - cap} \u884C\u672A\u663E\u793A${showFull ? "" : "\uFF0C\u6309 f \u67E5\u770B\u5B8C\u6574\u5185\u5BB9"}\uFF09` }) : null
|
|
17363
|
+
] }) : wrapText(sourceText || "(\u65E0\u8BF4\u660E)", contentW).map((line, j) => /* @__PURE__ */ jsx7(Text7, { color: THEME.white, children: line }, j)) }),
|
|
17364
|
+
fullDetail ? /* @__PURE__ */ jsx7(Text7, { color: THEME.grayDim, children: S.approvalFullHint }) : null,
|
|
16714
17365
|
/* @__PURE__ */ jsx7(Text7, { color: THEME.grayDim, children: S.approvalPrompt }),
|
|
16715
17366
|
OPTIONS.map((opt, i) => {
|
|
16716
17367
|
const active = i === selectedIndex;
|
|
@@ -17027,9 +17678,74 @@ var init_ModelPicker = __esm({
|
|
|
17027
17678
|
}
|
|
17028
17679
|
});
|
|
17029
17680
|
|
|
17030
|
-
// src/components/
|
|
17681
|
+
// src/components/RewindPicker.tsx
|
|
17031
17682
|
import { Box as Box13, Text as Text15 } from "ink";
|
|
17032
17683
|
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
17684
|
+
var PAGE_SIZE4, kindMark, RewindPicker;
|
|
17685
|
+
var init_RewindPicker = __esm({
|
|
17686
|
+
"src/components/RewindPicker.tsx"() {
|
|
17687
|
+
"use strict";
|
|
17688
|
+
init_theme();
|
|
17689
|
+
init_strings();
|
|
17690
|
+
init_util();
|
|
17691
|
+
PAGE_SIZE4 = 8;
|
|
17692
|
+
kindMark = (k) => k === "creation_marker" ? "\uFF0B" : k === "directory_tree" ? "\u25A4" : "~";
|
|
17693
|
+
RewindPicker = ({ records, selectedIndex, wrapW }) => {
|
|
17694
|
+
const start = Math.floor(Math.max(0, selectedIndex) / PAGE_SIZE4) * PAGE_SIZE4;
|
|
17695
|
+
const visible = records.slice(start, start + PAGE_SIZE4);
|
|
17696
|
+
const contentW = Math.max(24, wrapW);
|
|
17697
|
+
const metaSample = " \xB7 edit_file \xB7 59 \u5206\u949F\u524D";
|
|
17698
|
+
const pathW = Math.max(16, contentW - strWidth(metaSample) - 4);
|
|
17699
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", borderStyle: "round", borderColor: THEME.warn, paddingX: 1, paddingY: 1, marginTop: 1, children: [
|
|
17700
|
+
/* @__PURE__ */ jsx15(Text15, { color: THEME.warn, bold: true, children: S.rewindTitle }),
|
|
17701
|
+
/* @__PURE__ */ jsx15(Text15, { color: THEME.grayDim, children: S.rewindPrompt }),
|
|
17702
|
+
/* @__PURE__ */ jsx15(Box13, { flexDirection: "column", marginTop: 0.5, children: visible.map((r, i) => {
|
|
17703
|
+
const idx = start + i;
|
|
17704
|
+
const active = idx === selectedIndex;
|
|
17705
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", children: [
|
|
17706
|
+
/* @__PURE__ */ jsx15(Text15, { color: active ? THEME.coralBright : void 0, bold: true, children: active ? "\u203A " : " " }),
|
|
17707
|
+
/* @__PURE__ */ jsxs13(Text15, { color: THEME.grayDim, children: [
|
|
17708
|
+
kindMark(r.backupKind),
|
|
17709
|
+
" "
|
|
17710
|
+
] }),
|
|
17711
|
+
/* @__PURE__ */ jsx15(Text15, { color: active ? THEME.white : THEME.gray, bold: active, children: truncateMiddle(r.relativePath, pathW) }),
|
|
17712
|
+
/* @__PURE__ */ jsx15(Text15, { color: THEME.grayDim, children: ` \xB7 ${truncateMiddle(r.operationType, 20)} \xB7 ${S.relTime(r.timestamp)}` })
|
|
17713
|
+
] }, r.undoId);
|
|
17714
|
+
}) })
|
|
17715
|
+
] });
|
|
17716
|
+
};
|
|
17717
|
+
}
|
|
17718
|
+
});
|
|
17719
|
+
|
|
17720
|
+
// src/components/DiffSummaryCard.tsx
|
|
17721
|
+
import { Box as Box14, Text as Text16 } from "ink";
|
|
17722
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
17723
|
+
var MAX_ROWS_PER_FILE, DiffSummaryCard;
|
|
17724
|
+
var init_DiffSummaryCard = __esm({
|
|
17725
|
+
"src/components/DiffSummaryCard.tsx"() {
|
|
17726
|
+
"use strict";
|
|
17727
|
+
init_theme();
|
|
17728
|
+
init_util();
|
|
17729
|
+
MAX_ROWS_PER_FILE = 40;
|
|
17730
|
+
DiffSummaryCard = ({ title, files, wrapW }) => {
|
|
17731
|
+
const contentW = Math.max(16, wrapW);
|
|
17732
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: 1, children: [
|
|
17733
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, bold: true, children: title }),
|
|
17734
|
+
files.map((f) => /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: 0.5, children: [
|
|
17735
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.white, bold: true, children: truncateMiddle(f.path, Math.max(20, contentW)) }),
|
|
17736
|
+
f.note ? /* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: ` ${f.note}` }) : f.rows ? /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
17737
|
+
f.rows.slice(0, MAX_ROWS_PER_FILE).map((r, i) => /* @__PURE__ */ jsx16(Text16, { color: r.t === "del" ? THEME.danger : r.t === "add" ? THEME.ok : THEME.grayDim, children: r.t === "ellip" ? r.n > 0 ? ` \u22EF ${r.n} \u884C\u672A\u53D8` : " \u22EF" : `${r.t === "del" ? "- " : r.t === "add" ? "+ " : " "}${truncateMiddle(r.s, Math.max(20, contentW - 2))}` }, i)),
|
|
17738
|
+
f.rows.length > MAX_ROWS_PER_FILE ? /* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: ` \u2026\uFF08\u53E6\u6709 ${f.rows.length - MAX_ROWS_PER_FILE} \u884C\u672A\u663E\u793A\uFF09` }) : null
|
|
17739
|
+
] }) : null
|
|
17740
|
+
] }, f.path))
|
|
17741
|
+
] });
|
|
17742
|
+
};
|
|
17743
|
+
}
|
|
17744
|
+
});
|
|
17745
|
+
|
|
17746
|
+
// src/components/SlashMenu.tsx
|
|
17747
|
+
import { Box as Box15, Text as Text17 } from "ink";
|
|
17748
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
17033
17749
|
var MAX_ROWS, SlashMenu;
|
|
17034
17750
|
var init_SlashMenu = __esm({
|
|
17035
17751
|
"src/components/SlashMenu.tsx"() {
|
|
@@ -17041,22 +17757,22 @@ var init_SlashMenu = __esm({
|
|
|
17041
17757
|
const visible = entries.slice(start, start + MAX_ROWS);
|
|
17042
17758
|
const cmdW = Math.min(22, Math.max(8, ...visible.map((e) => e.name.length)) + 1);
|
|
17043
17759
|
const descMax = Math.max(12, cols - cmdW - 6);
|
|
17044
|
-
return /* @__PURE__ */
|
|
17760
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", borderStyle: "round", borderColor: THEME.coral, paddingX: 1, paddingY: 0, marginTop: 1, children: [
|
|
17045
17761
|
visible.map((e, i) => {
|
|
17046
17762
|
const active = i + start === selectedIndex;
|
|
17047
17763
|
const desc = e.description.length > descMax ? `${e.description.slice(0, descMax - 1)}\u2026` : e.description;
|
|
17048
17764
|
const gap = Math.max(1, cmdW - e.name.length);
|
|
17049
|
-
return /* @__PURE__ */
|
|
17050
|
-
/* @__PURE__ */
|
|
17051
|
-
/* @__PURE__ */
|
|
17765
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "row", children: [
|
|
17766
|
+
/* @__PURE__ */ jsx17(Text17, { color: active ? THEME.coralBright : void 0, bold: true, children: active ? "\u276F " : " " }),
|
|
17767
|
+
/* @__PURE__ */ jsxs15(Text17, { color: active ? THEME.white : THEME.gray, bold: active, children: [
|
|
17052
17768
|
"/",
|
|
17053
17769
|
e.name
|
|
17054
17770
|
] }),
|
|
17055
|
-
/* @__PURE__ */
|
|
17056
|
-
/* @__PURE__ */
|
|
17771
|
+
/* @__PURE__ */ jsx17(Text17, { color: THEME.grayDim, children: " ".repeat(gap) }),
|
|
17772
|
+
/* @__PURE__ */ jsx17(Text17, { color: active ? THEME.gray : THEME.grayDim, children: desc })
|
|
17057
17773
|
] }, e.name);
|
|
17058
17774
|
}),
|
|
17059
|
-
entries.length > MAX_ROWS ? /* @__PURE__ */
|
|
17775
|
+
entries.length > MAX_ROWS ? /* @__PURE__ */ jsxs15(Text17, { color: THEME.coralMuted, children: [
|
|
17060
17776
|
"\u2191\u2193 \u6EDA\u52A8 \xB7 ",
|
|
17061
17777
|
selectedIndex + 1,
|
|
17062
17778
|
"/",
|
|
@@ -17069,8 +17785,8 @@ var init_SlashMenu = __esm({
|
|
|
17069
17785
|
});
|
|
17070
17786
|
|
|
17071
17787
|
// src/components/StatusStrip.tsx
|
|
17072
|
-
import { Box as
|
|
17073
|
-
import { jsx as
|
|
17788
|
+
import { Box as Box16, Text as Text18 } from "ink";
|
|
17789
|
+
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
17074
17790
|
var StatusStrip;
|
|
17075
17791
|
var init_StatusStrip = __esm({
|
|
17076
17792
|
"src/components/StatusStrip.tsx"() {
|
|
@@ -17087,18 +17803,18 @@ var init_StatusStrip = __esm({
|
|
|
17087
17803
|
`state: ${stateText}`,
|
|
17088
17804
|
sessionShort ? `session: ${sessionShort}` : ""
|
|
17089
17805
|
].filter(Boolean);
|
|
17090
|
-
return /* @__PURE__ */
|
|
17091
|
-
/* @__PURE__ */
|
|
17092
|
-
/* @__PURE__ */
|
|
17093
|
-
/* @__PURE__ */
|
|
17806
|
+
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", flexShrink: 0, children: [
|
|
17807
|
+
/* @__PURE__ */ jsx18(Text18, { color: THEME.coralMuted, children: customLine && customLine.trim() ? customLine : bits.join(" \xB7 ") }),
|
|
17808
|
+
/* @__PURE__ */ jsx18(Text18, { color: stateColor, children: "" }),
|
|
17809
|
+
/* @__PURE__ */ jsx18(Text18, { color: THEME.grayDim, children: S.cmdHint })
|
|
17094
17810
|
] });
|
|
17095
17811
|
};
|
|
17096
17812
|
}
|
|
17097
17813
|
});
|
|
17098
17814
|
|
|
17099
17815
|
// src/components/TopPanel.tsx
|
|
17100
|
-
import { Box as
|
|
17101
|
-
import { jsx as
|
|
17816
|
+
import { Box as Box17, Text as Text19 } from "ink";
|
|
17817
|
+
import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
17102
17818
|
var ROBOT, INNER_H, TopPanel;
|
|
17103
17819
|
var init_TopPanel = __esm({
|
|
17104
17820
|
"src/components/TopPanel.tsx"() {
|
|
@@ -17118,36 +17834,36 @@ var init_TopPanel = __esm({
|
|
|
17118
17834
|
const leftW = Math.floor(innerW * 0.33);
|
|
17119
17835
|
const rightW = innerW - leftW - 1;
|
|
17120
17836
|
const ruleLen = Math.max(8, rightW - 2);
|
|
17121
|
-
const VDivider = /* @__PURE__ */
|
|
17122
|
-
return /* @__PURE__ */
|
|
17123
|
-
/* @__PURE__ */
|
|
17124
|
-
/* @__PURE__ */
|
|
17125
|
-
/* @__PURE__ */
|
|
17126
|
-
/* @__PURE__ */
|
|
17127
|
-
/* @__PURE__ */
|
|
17128
|
-
/* @__PURE__ */
|
|
17837
|
+
const VDivider = /* @__PURE__ */ jsx19(Box17, { flexDirection: "column", width: 1, flexShrink: 0, children: Array.from({ length: INNER_H }).map((_, i) => /* @__PURE__ */ jsx19(Text19, { color: THEME.coral, children: "\u2502" }, i)) });
|
|
17838
|
+
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", width: panelW, marginBottom: 1, children: [
|
|
17839
|
+
/* @__PURE__ */ jsxs17(Text19, { children: [
|
|
17840
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.coralBright, bold: true, children: "* Welcome to " }),
|
|
17841
|
+
/* @__PURE__ */ jsx19(Text19, { bold: true, color: THEME.white, children: "deep" }),
|
|
17842
|
+
/* @__PURE__ */ jsx19(Text19, { bold: true, color: THEME.coralBright, children: "Seek" }),
|
|
17843
|
+
/* @__PURE__ */ jsx19(Text19, { bold: true, color: THEME.white, children: "Code" }),
|
|
17844
|
+
/* @__PURE__ */ jsxs17(Text19, { color: THEME.coralMuted, children: [
|
|
17129
17845
|
" ",
|
|
17130
17846
|
VERSION
|
|
17131
17847
|
] })
|
|
17132
17848
|
] }),
|
|
17133
|
-
/* @__PURE__ */
|
|
17134
|
-
/* @__PURE__ */
|
|
17135
|
-
/* @__PURE__ */
|
|
17136
|
-
/* @__PURE__ */
|
|
17137
|
-
ROBOT.map((l, i) => /* @__PURE__ */
|
|
17138
|
-
/* @__PURE__ */
|
|
17139
|
-
/* @__PURE__ */
|
|
17849
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.grayDim, children: cwd }),
|
|
17850
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "row", borderStyle: "round", borderColor: THEME.coral, marginTop: 1, paddingX: 0, paddingY: 0, children: [
|
|
17851
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", width: leftW, height: INNER_H, alignItems: "center", justifyContent: "center", children: [
|
|
17852
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.white, bold: true, children: "Welcome back!" }),
|
|
17853
|
+
ROBOT.map((l, i) => /* @__PURE__ */ jsx19(Text19, { color: THEME.coralBright, children: l }, i)),
|
|
17854
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.grayDim, children: "Ctrl+C \u9000\u51FA \xB7 Esc \u4E2D\u6B62" }),
|
|
17855
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.grayDim, children: "\u8F93\u5165 / \u67E5\u770B\u547D\u4EE4" })
|
|
17140
17856
|
] }),
|
|
17141
17857
|
VDivider,
|
|
17142
|
-
/* @__PURE__ */
|
|
17143
|
-
/* @__PURE__ */
|
|
17144
|
-
/* @__PURE__ */
|
|
17145
|
-
/* @__PURE__ */
|
|
17858
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", width: rightW, height: INNER_H, justifyContent: "space-between", paddingX: 1, children: [
|
|
17859
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
17860
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.coralBright, bold: true, children: "Tips for getting started" }),
|
|
17861
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.white, children: "\u8F93\u5165\u9700\u6C42\u5F00\u59CB\uFF0C\u8F93\u5165 / \u5524\u51FA\u547D\u4EE4\u3002" })
|
|
17146
17862
|
] }),
|
|
17147
|
-
/* @__PURE__ */
|
|
17148
|
-
/* @__PURE__ */
|
|
17149
|
-
/* @__PURE__ */
|
|
17150
|
-
/* @__PURE__ */
|
|
17863
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.grayDim, children: "\u2500".repeat(ruleLen) }),
|
|
17864
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
17865
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.coralBright, bold: true, children: "What's new" }),
|
|
17866
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.white, children: "\u5DF2\u5BF9\u63A5\u672C\u5730 Tool Call \u667A\u80FD\u51FD\u6570\u8C03\u7528\u3002" })
|
|
17151
17867
|
] })
|
|
17152
17868
|
] })
|
|
17153
17869
|
] })
|
|
@@ -17157,7 +17873,7 @@ var init_TopPanel = __esm({
|
|
|
17157
17873
|
});
|
|
17158
17874
|
|
|
17159
17875
|
// ../core/src/observability/inspect.ts
|
|
17160
|
-
import
|
|
17876
|
+
import fs49 from "fs";
|
|
17161
17877
|
var OBS, obsT, CWD, fmt, readTrace, fmtK, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
|
|
17162
17878
|
var init_inspect = __esm({
|
|
17163
17879
|
"../core/src/observability/inspect.ts"() {
|
|
@@ -17280,7 +17996,7 @@ var init_inspect = __esm({
|
|
|
17280
17996
|
if (!sessionId) return [];
|
|
17281
17997
|
try {
|
|
17282
17998
|
const p = await getTraceStorePath(sessionId);
|
|
17283
|
-
const text = await
|
|
17999
|
+
const text = await fs49.promises.readFile(p, "utf8");
|
|
17284
18000
|
const out = [];
|
|
17285
18001
|
for (const line of text.split("\n")) {
|
|
17286
18002
|
if (!line.trim()) continue;
|
|
@@ -17389,7 +18105,7 @@ var init_inspect = __esm({
|
|
|
17389
18105
|
}
|
|
17390
18106
|
let sidecarCount = 0;
|
|
17391
18107
|
try {
|
|
17392
|
-
sidecarCount = (await
|
|
18108
|
+
sidecarCount = (await fs49.promises.readdir(getToolOutputSidecarDir(sessionId))).filter((f) => f.endsWith(".txt")).length;
|
|
17393
18109
|
} catch {
|
|
17394
18110
|
}
|
|
17395
18111
|
const window = appConfig.MAX_HISTORY_TOKENS;
|
|
@@ -17464,8 +18180,8 @@ __export(App_exports, {
|
|
|
17464
18180
|
App: () => App
|
|
17465
18181
|
});
|
|
17466
18182
|
import { useCallback as useCallback2, useEffect as useEffect4, useMemo, useRef as useRef2, useState as useState4 } from "react";
|
|
17467
|
-
import { Box as
|
|
17468
|
-
import { Fragment as
|
|
18183
|
+
import { Box as Box18, Static, Text as Text20, useApp, useInput as useInput2, useStdout } from "ink";
|
|
18184
|
+
import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
17469
18185
|
var CWD2, RowView, isDynamicRow, SPINNER_FRAMES, App;
|
|
17470
18186
|
var init_App = __esm({
|
|
17471
18187
|
"src/App.tsx"() {
|
|
@@ -17492,23 +18208,27 @@ var init_App = __esm({
|
|
|
17492
18208
|
init_SessionPicker();
|
|
17493
18209
|
init_ForkPicker();
|
|
17494
18210
|
init_ModelPicker();
|
|
18211
|
+
init_RewindPicker();
|
|
18212
|
+
init_DiffSummaryCard();
|
|
17495
18213
|
init_SlashMenu();
|
|
17496
18214
|
init_MultilineInput();
|
|
17497
18215
|
init_StatusStrip();
|
|
17498
18216
|
init_TopPanel();
|
|
17499
18217
|
init_inspect();
|
|
18218
|
+
init_restore();
|
|
17500
18219
|
CWD2 = process.cwd();
|
|
17501
18220
|
RowView = ({ row, wrapW, streamTail, showThinking }) => {
|
|
17502
|
-
if (row.kind === "todos") return /* @__PURE__ */
|
|
17503
|
-
if (row.kind === "tool") return /* @__PURE__ */
|
|
17504
|
-
if (row.kind === "
|
|
18221
|
+
if (row.kind === "todos") return /* @__PURE__ */ jsx20(TodosPanel, { todos: row.todos, wrapW });
|
|
18222
|
+
if (row.kind === "tool") return /* @__PURE__ */ jsx20(ToolCard, { toolName: row.toolName, args: row.args, result: row.result, ok: row.ok, status: row.status, progress: row.progress, wrapW });
|
|
18223
|
+
if (row.kind === "diff-summary") return /* @__PURE__ */ jsx20(DiffSummaryCard, { title: row.title, files: row.files, wrapW });
|
|
18224
|
+
if (row.kind === "thinking") return /* @__PURE__ */ jsx20(ThinkingBlock, { streaming: row.streaming ?? false, startedAt: row.startedAt, durationMs: row.durationMs, tokens: row.tokens, text: row.text, expanded: showThinking, wrapW });
|
|
17505
18225
|
if (row.kind === "user") {
|
|
17506
|
-
return /* @__PURE__ */
|
|
17507
|
-
/* @__PURE__ */
|
|
17508
|
-
/* @__PURE__ */
|
|
18226
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, children: [
|
|
18227
|
+
/* @__PURE__ */ jsx20(Text20, { color: THEME.grayDim, children: "\u2500".repeat(Math.max(8, wrapW)) }),
|
|
18228
|
+
/* @__PURE__ */ jsx20(MessageBlock, { row, wrapW, streamTail })
|
|
17509
18229
|
] });
|
|
17510
18230
|
}
|
|
17511
|
-
return /* @__PURE__ */
|
|
18231
|
+
return /* @__PURE__ */ jsx20(MessageBlock, { row, wrapW, streamTail });
|
|
17512
18232
|
};
|
|
17513
18233
|
isDynamicRow = (r) => r.kind === "assistant" && !!r.streaming || r.kind === "thinking" && !!r.streaming || r.kind === "tool" && r.status === "running" || r.kind === "todos" && !!r.active || r.kind === "compact-progress" || r.kind === "subagent-progress";
|
|
17514
18234
|
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -17537,6 +18257,7 @@ var init_App = __esm({
|
|
|
17537
18257
|
const [planEditing, setPlanEditing] = useState4(false);
|
|
17538
18258
|
const [planDraft, setPlanDraft] = useState4("");
|
|
17539
18259
|
const [planCursor, setPlanCursor] = useState4(0);
|
|
18260
|
+
const [approvalFull, setApprovalFull] = useState4(false);
|
|
17540
18261
|
const [qCursor, setQCursor] = useState4(0);
|
|
17541
18262
|
const [qChecked, setQChecked] = useState4(/* @__PURE__ */ new Set());
|
|
17542
18263
|
const [qEditing, setQEditing] = useState4(false);
|
|
@@ -17648,8 +18369,14 @@ var init_App = __esm({
|
|
|
17648
18369
|
return S.cmdSessions;
|
|
17649
18370
|
case "fork":
|
|
17650
18371
|
return S.cmdFork;
|
|
18372
|
+
case "undo":
|
|
18373
|
+
return S.cmdUndo;
|
|
17651
18374
|
case "usage":
|
|
17652
18375
|
return S.cmdUsage;
|
|
18376
|
+
case "rewind":
|
|
18377
|
+
return S.cmdRewind;
|
|
18378
|
+
case "diff":
|
|
18379
|
+
return S.cmdDiff;
|
|
17653
18380
|
case "budget":
|
|
17654
18381
|
return S.cmdBudget;
|
|
17655
18382
|
case "context":
|
|
@@ -17682,7 +18409,7 @@ var init_App = __esm({
|
|
|
17682
18409
|
const q = input.slice(1).toLowerCase();
|
|
17683
18410
|
return menuEntries.filter((c) => c.name.toLowerCase().startsWith(q));
|
|
17684
18411
|
}, [input, menuEntries]);
|
|
17685
|
-
const menuActive = state.pendingApproval != null || state.pendingQuestion != null || state.pendingPlan != null || state.pendingSessions != null || state.pendingFork != null || state.pendingModel != null;
|
|
18412
|
+
const menuActive = state.pendingApproval != null || state.pendingQuestion != null || state.pendingPlan != null || state.pendingSessions != null || state.pendingFork != null || state.pendingModel != null || state.pendingRewind != null;
|
|
17686
18413
|
const [busyClock, setBusyClock] = useState4({ frame: 0, sec: 0 });
|
|
17687
18414
|
const busyStartRef = useRef2(0);
|
|
17688
18415
|
useEffect4(() => {
|
|
@@ -17706,7 +18433,8 @@ var init_App = __esm({
|
|
|
17706
18433
|
useEffect4(() => {
|
|
17707
18434
|
setSelectIdx(0);
|
|
17708
18435
|
setPlanEditing(false);
|
|
17709
|
-
|
|
18436
|
+
setApprovalFull(false);
|
|
18437
|
+
}, [state.pendingApproval, state.pendingQuestion, state.pendingPlan, state.pendingSessions, state.pendingFork, state.pendingModel, state.pendingRewind, slashVisible, filteredCommands.length]);
|
|
17710
18438
|
useEffect4(() => {
|
|
17711
18439
|
setQCursor(0);
|
|
17712
18440
|
setQChecked(/* @__PURE__ */ new Set());
|
|
@@ -17744,6 +18472,21 @@ var init_App = __esm({
|
|
|
17744
18472
|
case "/fork":
|
|
17745
18473
|
void state.openForkPicker();
|
|
17746
18474
|
return true;
|
|
18475
|
+
case "/undo": {
|
|
18476
|
+
const sid = state.sessionIdRef.current;
|
|
18477
|
+
if (!sid) {
|
|
18478
|
+
state.pushInfo(S.undoNoSession);
|
|
18479
|
+
return true;
|
|
18480
|
+
}
|
|
18481
|
+
state.pushInfo(await restoreLast(sid));
|
|
18482
|
+
return true;
|
|
18483
|
+
}
|
|
18484
|
+
case "/rewind":
|
|
18485
|
+
void state.openRewindPicker();
|
|
18486
|
+
return true;
|
|
18487
|
+
case "/diff":
|
|
18488
|
+
void state.openDiff();
|
|
18489
|
+
return true;
|
|
17747
18490
|
case "/help":
|
|
17748
18491
|
state.pushInfo(S.helpText(modelDisplay, state.getThinkingLevel(), getLocale()));
|
|
17749
18492
|
return true;
|
|
@@ -17966,7 +18709,11 @@ ${listText}`);
|
|
|
17966
18709
|
return;
|
|
17967
18710
|
}
|
|
17968
18711
|
if (state.pendingApproval) {
|
|
17969
|
-
const APPROVAL_DECISIONS = ["allow-once", "allow-always", "deny"];
|
|
18712
|
+
const APPROVAL_DECISIONS = ["allow-once", "allow-session", "allow-always", "deny"];
|
|
18713
|
+
if (ch === "f" || ch === "F") {
|
|
18714
|
+
setApprovalFull((v) => !v);
|
|
18715
|
+
return;
|
|
18716
|
+
}
|
|
17970
18717
|
if (key.upArrow) setSelectIdx((i) => (i - 1 + APPROVAL_DECISIONS.length) % APPROVAL_DECISIONS.length);
|
|
17971
18718
|
else if (key.downArrow) setSelectIdx((i) => (i + 1) % APPROVAL_DECISIONS.length);
|
|
17972
18719
|
else if (key.return) state.resolveApproval(APPROVAL_DECISIONS[selectIdxRef.current] ?? "deny");
|
|
@@ -18060,6 +18807,16 @@ ${listText}`);
|
|
|
18060
18807
|
} else if (key.escape || key.ctrl && ch === "g") state.resolveModel(null);
|
|
18061
18808
|
return;
|
|
18062
18809
|
}
|
|
18810
|
+
if (state.pendingRewind) {
|
|
18811
|
+
const list = state.pendingRewind.records;
|
|
18812
|
+
if (key.upArrow) setSelectIdx((i) => (i - 1 + list.length) % list.length);
|
|
18813
|
+
else if (key.downArrow) setSelectIdx((i) => (i + 1) % list.length);
|
|
18814
|
+
else if (key.return) {
|
|
18815
|
+
const sel = list[Math.min(selectIdxRef.current, list.length - 1)];
|
|
18816
|
+
state.resolveRewind(sel?.undoId ?? null);
|
|
18817
|
+
} else if (key.escape || key.ctrl && ch === "g") state.resolveRewind(null);
|
|
18818
|
+
return;
|
|
18819
|
+
}
|
|
18063
18820
|
if (key.ctrl && ch === "t") {
|
|
18064
18821
|
state.toggleShowThinking();
|
|
18065
18822
|
return;
|
|
@@ -18100,14 +18857,14 @@ ${listText}`);
|
|
|
18100
18857
|
}
|
|
18101
18858
|
});
|
|
18102
18859
|
const sessionShort = state.sessionIdRef.current ? truncateMiddle(state.sessionIdRef.current, 12) : "";
|
|
18103
|
-
return /* @__PURE__ */
|
|
18104
|
-
/* @__PURE__ */
|
|
18105
|
-
/* @__PURE__ */
|
|
18106
|
-
menuActive ? null : /* @__PURE__ */
|
|
18107
|
-
dynamicRows.map((row) => /* @__PURE__ */
|
|
18108
|
-
state.busy ? /* @__PURE__ */
|
|
18860
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", width: cols, children: [
|
|
18861
|
+
/* @__PURE__ */ jsx20(Static, { items: staticItems, children: (item) => item.kind === "__header" ? /* @__PURE__ */ jsx20(TopPanel, { cwd: CWD2, cols }, "header") : /* @__PURE__ */ jsx20(RowView, { row: item, wrapW, streamTail, showThinking: state.showThinkingText }, item.id) }),
|
|
18862
|
+
/* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", width: cols, children: [
|
|
18863
|
+
menuActive ? null : /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", paddingX: 1, children: [
|
|
18864
|
+
dynamicRows.map((row) => /* @__PURE__ */ jsx20(RowView, { row, wrapW, streamTail, showThinking: state.showThinkingText }, row.id)),
|
|
18865
|
+
state.busy ? /* @__PURE__ */ jsx20(Box18, { marginTop: 0.5, children: /* @__PURE__ */ jsxs18(Text20, { color: THEME.coralBright, children: [
|
|
18109
18866
|
`${SPINNER_FRAMES[busyClock.frame]} ${S.thinkVerbs[Math.floor(busyClock.sec / 3) % S.thinkVerbs.length]}\u2026 (${busyClock.sec}s \xB7 ${S.escInterrupt})`,
|
|
18110
|
-
state.runningAgents > 0 ? /* @__PURE__ */
|
|
18867
|
+
state.runningAgents > 0 ? /* @__PURE__ */ jsxs18(Text20, { color: "green", children: [
|
|
18111
18868
|
" \xB7 \u25CF ",
|
|
18112
18869
|
state.runningAgents,
|
|
18113
18870
|
" agent",
|
|
@@ -18115,9 +18872,9 @@ ${listText}`);
|
|
|
18115
18872
|
] }) : null
|
|
18116
18873
|
] }) }) : null
|
|
18117
18874
|
] }),
|
|
18118
|
-
/* @__PURE__ */
|
|
18119
|
-
state.pendingApproval ? /* @__PURE__ */
|
|
18120
|
-
state.pendingQuestion ? /* @__PURE__ */
|
|
18875
|
+
/* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", paddingX: 1, flexShrink: 0, children: [
|
|
18876
|
+
state.pendingApproval ? /* @__PURE__ */ jsx20(ApprovalModal, { toolName: state.pendingApproval.toolName, detail: state.pendingApproval.detail, fullDetail: state.pendingApproval.fullDetail, expanded: approvalFull, selectedIndex: selectIdx, wrapW }) : null,
|
|
18877
|
+
state.pendingQuestion ? /* @__PURE__ */ jsx20(
|
|
18121
18878
|
QuestionModal,
|
|
18122
18879
|
{
|
|
18123
18880
|
question: state.pendingQuestion.req.question,
|
|
@@ -18136,7 +18893,7 @@ ${listText}`);
|
|
|
18136
18893
|
onDraftSubmit: confirmQuestionDraft
|
|
18137
18894
|
}
|
|
18138
18895
|
) : null,
|
|
18139
|
-
state.pendingPlan ? planEditing ? /* @__PURE__ */
|
|
18896
|
+
state.pendingPlan ? planEditing ? /* @__PURE__ */ jsx20(
|
|
18140
18897
|
PlanEditor,
|
|
18141
18898
|
{
|
|
18142
18899
|
value: planDraft,
|
|
@@ -18147,16 +18904,17 @@ ${listText}`);
|
|
|
18147
18904
|
},
|
|
18148
18905
|
onSubmit: confirmPlanEdit
|
|
18149
18906
|
}
|
|
18150
|
-
) : /* @__PURE__ */
|
|
18151
|
-
state.pendingSessions ? /* @__PURE__ */
|
|
18152
|
-
state.pendingFork ? /* @__PURE__ */
|
|
18153
|
-
state.pendingModel ? /* @__PURE__ */
|
|
18154
|
-
|
|
18907
|
+
) : /* @__PURE__ */ jsx20(PlanModal, { selectedIndex: selectIdx }) : null,
|
|
18908
|
+
state.pendingSessions ? /* @__PURE__ */ jsx20(SessionPicker, { sessions: state.pendingSessions.sessions, selectedIndex: selectIdx, wrapW }) : null,
|
|
18909
|
+
state.pendingFork ? /* @__PURE__ */ jsx20(ForkPicker, { anchors: state.pendingFork.anchors, selectedIndex: selectIdx, wrapW }) : null,
|
|
18910
|
+
state.pendingModel ? /* @__PURE__ */ jsx20(ModelPicker, { models: state.pendingModel.models, currentModel: modelDisplay, selectedIndex: selectIdx, wrapW }) : null,
|
|
18911
|
+
state.pendingRewind ? /* @__PURE__ */ jsx20(RewindPicker, { records: state.pendingRewind.records, selectedIndex: selectIdx, wrapW }) : null,
|
|
18912
|
+
slashVisible ? /* @__PURE__ */ jsx20(SlashMenu, { entries: filteredCommands, selectedIndex: selectIdx, cols }) : null
|
|
18155
18913
|
] }),
|
|
18156
|
-
/* @__PURE__ */
|
|
18914
|
+
/* @__PURE__ */ jsx20(Box18, { flexDirection: "column", paddingX: 1, marginTop: 1, flexShrink: 0, children: (() => {
|
|
18157
18915
|
const planOn = state.getPlanMode();
|
|
18158
18916
|
const autoOn = !planOn && state.getAutoMode();
|
|
18159
|
-
const inputEl = /* @__PURE__ */
|
|
18917
|
+
const inputEl = /* @__PURE__ */ jsx20(
|
|
18160
18918
|
MultilineInput,
|
|
18161
18919
|
{
|
|
18162
18920
|
value: input,
|
|
@@ -18174,17 +18932,17 @@ ${listText}`);
|
|
|
18174
18932
|
}
|
|
18175
18933
|
);
|
|
18176
18934
|
if (planOn || autoOn) {
|
|
18177
|
-
return /* @__PURE__ */
|
|
18178
|
-
/* @__PURE__ */
|
|
18935
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", borderStyle: "round", borderColor: planOn ? THEME.thinking : THEME.warn, paddingX: 1, children: [
|
|
18936
|
+
/* @__PURE__ */ jsx20(Text20, { color: planOn ? THEME.thinking : THEME.warn, children: planOn ? S.inputBadgePlan : S.inputBadgeAuto }),
|
|
18179
18937
|
inputEl
|
|
18180
18938
|
] });
|
|
18181
18939
|
}
|
|
18182
|
-
return /* @__PURE__ */
|
|
18183
|
-
/* @__PURE__ */
|
|
18940
|
+
return /* @__PURE__ */ jsxs18(Fragment4, { children: [
|
|
18941
|
+
/* @__PURE__ */ jsx20(Text20, { dimColor: true, color: THEME.gray, children: dimRule(cols) }),
|
|
18184
18942
|
inputEl
|
|
18185
18943
|
] });
|
|
18186
18944
|
})() }),
|
|
18187
|
-
/* @__PURE__ */
|
|
18945
|
+
/* @__PURE__ */ jsx20(Box18, { paddingX: 1, marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx20(StatusStrip, { model: modelDisplay, busy: state.busy, aborting: state.aborting, planMode: state.getPlanMode(), autoMode: state.getAutoMode(), sessionShort, cols, customLine: statusLineText }) })
|
|
18188
18946
|
] })
|
|
18189
18947
|
] });
|
|
18190
18948
|
};
|
|
@@ -18197,7 +18955,7 @@ __export(batchRunner_exports, {
|
|
|
18197
18955
|
parseWaitWindow: () => parseWaitWindow,
|
|
18198
18956
|
runBatchQueue: () => runBatchQueue
|
|
18199
18957
|
});
|
|
18200
|
-
import
|
|
18958
|
+
import fs50 from "fs";
|
|
18201
18959
|
import { spawn as spawn6 } from "child_process";
|
|
18202
18960
|
var BATCH_EDIT_TOOLS, BATCH_DEFAULT_DENYLIST, ACCEPT_TIMEOUT_MIN, ACCEPT_TAIL_LINES, emit2, ts, runAcceptCommand, collectSessionCost, buildReport, runOneTask, parseWaitWindow, nowMinutes, sleep, waitUntilWindow, runBatchQueue;
|
|
18203
18961
|
var init_batchRunner = __esm({
|
|
@@ -18205,6 +18963,7 @@ var init_batchRunner = __esm({
|
|
|
18205
18963
|
"use strict";
|
|
18206
18964
|
init_chatProcessing();
|
|
18207
18965
|
init_tool();
|
|
18966
|
+
init_autoPermission();
|
|
18208
18967
|
init_manager();
|
|
18209
18968
|
init_sessionRegistry();
|
|
18210
18969
|
init_background();
|
|
@@ -18216,7 +18975,7 @@ var init_batchRunner = __esm({
|
|
|
18216
18975
|
init_batchStrings();
|
|
18217
18976
|
init_strings();
|
|
18218
18977
|
BATCH_EDIT_TOOLS = /* @__PURE__ */ new Set(["edit_file", "create_file", "write_file", "move_file", "notebook_edit"]);
|
|
18219
|
-
BATCH_DEFAULT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path"]);
|
|
18978
|
+
BATCH_DEFAULT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path", "run_in_background"]);
|
|
18220
18979
|
ACCEPT_TIMEOUT_MIN = 10;
|
|
18221
18980
|
ACCEPT_TAIL_LINES = 60;
|
|
18222
18981
|
emit2 = (s = "") => {
|
|
@@ -18266,8 +19025,8 @@ ${lines.slice(-ACCEPT_TAIL_LINES).join("\n")}` : text;
|
|
|
18266
19025
|
});
|
|
18267
19026
|
};
|
|
18268
19027
|
collectSessionCost = async (sessionId) => {
|
|
18269
|
-
const recs = (await readUsageRecords(2)).filter((r) => r.sessionId === sessionId);
|
|
18270
|
-
const c = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: null, partialPrice: false, durationMs: 0 };
|
|
19028
|
+
const recs = (await readUsageRecords(2)).filter((r) => r.sessionId === sessionId || r.sessionId.startsWith(sessionId + "__sub__"));
|
|
19029
|
+
const c = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: null, toolCostYuan: 0, auxCostYuan: 0, partialPrice: false, durationMs: 0 };
|
|
18271
19030
|
for (const r of recs) {
|
|
18272
19031
|
c.rounds += r.rounds || 0;
|
|
18273
19032
|
if (r.depth === 0) c.mainRounds += r.rounds || 0;
|
|
@@ -18276,6 +19035,8 @@ ${lines.slice(-ACCEPT_TAIL_LINES).join("\n")}` : text;
|
|
|
18276
19035
|
c.completionTokens += r.completionTokens ?? 0;
|
|
18277
19036
|
c.cachedTokens += r.cachedTokens ?? 0;
|
|
18278
19037
|
c.durationMs += r.durationMs || 0;
|
|
19038
|
+
c.toolCostYuan += r.toolCostYuan ?? 0;
|
|
19039
|
+
c.auxCostYuan += r.auxCostYuan ?? 0;
|
|
18279
19040
|
if (typeof r.costYuan === "number") c.costYuan = (c.costYuan ?? 0) + r.costYuan;
|
|
18280
19041
|
else c.partialPrice = true;
|
|
18281
19042
|
}
|
|
@@ -18338,10 +19099,10 @@ ${diff}
|
|
|
18338
19099
|
};
|
|
18339
19100
|
runOneTask = async (task) => {
|
|
18340
19101
|
const T = batchT(getLocale());
|
|
18341
|
-
const source = await
|
|
19102
|
+
const source = await fs50.promises.readFile(task.path, "utf-8");
|
|
18342
19103
|
const parsed = parseBatchTicket(source, getLocale());
|
|
18343
19104
|
if (!parsed.ok) {
|
|
18344
|
-
const cost2 = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: 0, partialPrice: false, durationMs: 0 };
|
|
19105
|
+
const cost2 = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: 0, toolCostYuan: 0, auxCostYuan: 0, partialPrice: false, durationMs: 0 };
|
|
18345
19106
|
await finishTicket(task.id, `---
|
|
18346
19107
|
id: ${task.id}
|
|
18347
19108
|
title: ${task.id}
|
|
@@ -18360,10 +19121,31 @@ ${parsed.error}`);
|
|
|
18360
19121
|
emit2(`\u25B6 [${task.id}] ${t.title}`);
|
|
18361
19122
|
emit2(T.taskMeta(t.budgetYuan, timeoutMin, t.accept));
|
|
18362
19123
|
const unlocked = new Set(t.tools ?? []);
|
|
18363
|
-
const
|
|
18364
|
-
const toolSchemas = agentTools.filter((x) => !denySchemas.has(x?.function?.name));
|
|
19124
|
+
const toolSchemas = agentTools.filter((x) => unlocked.has(x?.function?.name) || x?.function?.batchAllowed !== false && !BATCH_DEFAULT_DENYLIST.has(x?.function?.name));
|
|
18365
19125
|
const allowApproval = /* @__PURE__ */ new Set([...BATCH_EDIT_TOOLS, ...unlocked]);
|
|
18366
19126
|
const deniedTools = [];
|
|
19127
|
+
const wrapInterpreterGate = (x) => {
|
|
19128
|
+
const name = x?.function?.name;
|
|
19129
|
+
if (name !== "run_command" && name !== "run_in_background") return x;
|
|
19130
|
+
const innerExecute = x.function.execute.bind(x.function);
|
|
19131
|
+
return {
|
|
19132
|
+
...x,
|
|
19133
|
+
function: {
|
|
19134
|
+
...x.function,
|
|
19135
|
+
execute: async (args, ctx) => {
|
|
19136
|
+
const cmd = typeof args?.command === "string" ? args.command : "";
|
|
19137
|
+
if (cmd && isInterpreterRepoScript(cmd) && !unlocked.has("interpreter")) {
|
|
19138
|
+
const why = `\u{1F6AB} [batch \u4F9B\u5E94\u94FE\u95F8\u95E8] \u89E3\u91CA\u5668\u76F4\u8DD1\u4ED3\u5E93\u5185\u811A\u672C\u5DF2\u62E6\u622A\uFF1A${cmd.slice(0, 120)}\u3002\u6267\u884C\u4ED3\u5E93\u4F5C\u8005\u63D0\u4EA4\u7684\u4EE3\u7801\u4E0E npm \u65CF\u540C\u6E90\u98CE\u9669\uFF0C\u6302\u673A\u573A\u666F\u9ED8\u8BA4\u62D2\u7EDD\uFF1B\u786E\u9700\u6267\u884C\u8BF7\u5728\u672C\u5DE5\u5355 tools: \u4E2D\u52A0 "interpreter" \u89E3\u9501\u3002`;
|
|
19139
|
+
deniedTools.push({ tool: name, detail: why.replace(/\n+/g, " ").slice(0, 140) });
|
|
19140
|
+
emit2(`\u{1F6AB} [${task.id}] \u62E6\u622A\u89E3\u91CA\u5668\u811A\u672C\uFF1A${cmd.slice(0, 80)}`);
|
|
19141
|
+
return why;
|
|
19142
|
+
}
|
|
19143
|
+
return innerExecute(args, ctx);
|
|
19144
|
+
}
|
|
19145
|
+
}
|
|
19146
|
+
};
|
|
19147
|
+
};
|
|
19148
|
+
const gatedSchemas = toolSchemas.map(wrapInterpreterGate);
|
|
18367
19149
|
let wt = null;
|
|
18368
19150
|
try {
|
|
18369
19151
|
wt = await createWorktree(sessionId, task.id);
|
|
@@ -18445,7 +19227,7 @@ ${s.output}
|
|
|
18445
19227
|
if (evt.type === "run.receipt") receipt = evt;
|
|
18446
19228
|
},
|
|
18447
19229
|
budgetYuan: t.budgetYuan,
|
|
18448
|
-
toolSchemas
|
|
19230
|
+
toolSchemas: gatedSchemas
|
|
18449
19231
|
}
|
|
18450
19232
|
);
|
|
18451
19233
|
} catch (e) {
|
|
@@ -18625,9 +19407,9 @@ init_updateCheck();
|
|
|
18625
19407
|
init_trust();
|
|
18626
19408
|
import { render } from "ink";
|
|
18627
19409
|
import os5 from "os";
|
|
18628
|
-
import
|
|
19410
|
+
import path50 from "path";
|
|
18629
19411
|
import { emitKeypressEvents } from "node:readline";
|
|
18630
|
-
import { jsx as
|
|
19412
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
18631
19413
|
var parseArgs = (argv) => {
|
|
18632
19414
|
const out = {};
|
|
18633
19415
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -18774,7 +19556,7 @@ var main = async () => {
|
|
|
18774
19556
|
process.stdout.write("\x1B[2J\x1B[3J\x1B[1;1H");
|
|
18775
19557
|
const cwdIsHome = () => {
|
|
18776
19558
|
try {
|
|
18777
|
-
return
|
|
19559
|
+
return path50.resolve(cwd).toLowerCase() === path50.resolve(os5.homedir()).toLowerCase();
|
|
18778
19560
|
} catch {
|
|
18779
19561
|
return false;
|
|
18780
19562
|
}
|
|
@@ -18793,7 +19575,7 @@ var main = async () => {
|
|
|
18793
19575
|
resumeId = await getMostRecentSessionId2() ?? void 0;
|
|
18794
19576
|
}
|
|
18795
19577
|
const { waitUntilExit } = render(
|
|
18796
|
-
/* @__PURE__ */
|
|
19578
|
+
/* @__PURE__ */ jsx21(App2, { resumeSessionId: resumeId, initialPlanMode: args.plan, initialAutoMode: args.auto, initialIncludeProject: includeProject, engineReady }),
|
|
18797
19579
|
// exitOnCtrlC:false:Ctrl+C 交由 App useInput 处理(统一退出/中止语义);
|
|
18798
19580
|
// patchConsole:false:避免 console 劫持与全屏重绘叠加闪屏。
|
|
18799
19581
|
{ exitOnCtrlC: false, patchConsole: false }
|