deepseeker-code 1.0.58 → 1.0.60
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 +1446 -746
- 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,
|
|
@@ -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` : ""}${c.toolCostYuan > 0 ? ` \xB7 \u8054\u7F51 \xA5${c.toolCostYuan.toFixed(4)}` : ""}`;
|
|
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${r.toolCostYuan ? ` \xB7 \u8054\u7F51 \xA5${r.toolCostYuan.toFixed(4)}` : ""}`,
|
|
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` : ""}${c.toolCostYuan > 0 ? ` \xB7 web \xA5${c.toolCostYuan.toFixed(4)}` : ""}`;
|
|
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${r.toolCostYuan ? ` \xB7 web \xA5${r.toolCostYuan.toFixed(4)}` : ""}`,
|
|
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",
|
|
@@ -10270,6 +10427,8 @@ var init_ask = __esm({
|
|
|
10270
10427
|
type: "function",
|
|
10271
10428
|
function: {
|
|
10272
10429
|
name: "ask_question",
|
|
10430
|
+
// ★ P1-6 声明化:阻塞等用户作答,不参与并行调度(原 canParallelize 按名排除退役为声明否决)
|
|
10431
|
+
parallelizable: false,
|
|
10273
10432
|
description: [
|
|
10274
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",
|
|
10275
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",
|
|
@@ -11679,11 +11838,12 @@ var init_backgroundTool = __esm({
|
|
|
11679
11838
|
|
|
11680
11839
|
// ../core/src/tool/autoPermission.ts
|
|
11681
11840
|
import path35 from "path";
|
|
11682
|
-
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;
|
|
11683
11842
|
var init_autoPermission = __esm({
|
|
11684
11843
|
"../core/src/tool/autoPermission.ts"() {
|
|
11685
11844
|
"use strict";
|
|
11686
11845
|
init_model();
|
|
11846
|
+
init_type();
|
|
11687
11847
|
isMcpTool = (name) => name.startsWith("mcp__");
|
|
11688
11848
|
BUILTIN_AUTO_DENY = [
|
|
11689
11849
|
/(^|[\\/])\.env(\.|$)/i,
|
|
@@ -11802,6 +11962,20 @@ var init_autoPermission = __esm({
|
|
|
11802
11962
|
if (SCRIPT_RUNNER_READONLY.some((re) => re.test(c))) return false;
|
|
11803
11963
|
return SCRIPT_RUNNER_HEADS.some((re) => re.test(c));
|
|
11804
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
|
+
};
|
|
11805
11979
|
hasWriteModifier = (cmd) => {
|
|
11806
11980
|
if (!cmd) return false;
|
|
11807
11981
|
if (/^sed\b/.test(cmd) && /(^|\s)-i\b/.test(cmd)) return true;
|
|
@@ -11841,13 +12015,17 @@ var init_autoPermission = __esm({
|
|
|
11841
12015
|
if (!isWithinWorkspace(s, ctx.cwd)) return "ask";
|
|
11842
12016
|
if (matchBuiltinDeny(s)) return "deny";
|
|
11843
12017
|
}
|
|
12018
|
+
const fastPath = decl.safetyLevel !== "danger" /* DANGER */ && (decl.triggersUndo === "overwrite" || decl.triggersUndo === "create");
|
|
12019
|
+
return fastPath ? "allow" : "ask";
|
|
11844
12020
|
}
|
|
11845
12021
|
if (decl?.autoApproval === "command") {
|
|
11846
12022
|
const argKey = decl.primaryArg ?? "command";
|
|
11847
12023
|
const cmd = typeof args?.[argKey] === "string" ? args[argKey] : "";
|
|
11848
12024
|
if (matchCommandDeny(cmd)) return "deny";
|
|
11849
12025
|
}
|
|
11850
|
-
|
|
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");
|
|
11851
12029
|
return v === "safe" ? "allow" : "ask";
|
|
11852
12030
|
};
|
|
11853
12031
|
}
|
|
@@ -11855,7 +12033,7 @@ var init_autoPermission = __esm({
|
|
|
11855
12033
|
|
|
11856
12034
|
// ../core/src/agent/toolExecution.ts
|
|
11857
12035
|
import path36 from "path";
|
|
11858
|
-
var applyPrivacyMasking, scaleTruncBudget, processToolCall;
|
|
12036
|
+
var applyPrivacyMasking, scaleTruncBudget, failResult, emitValidationFailed, resolveEarlyOutcome, runSecurityGates, executeTool, finalizeOutcome, processToolCall;
|
|
11859
12037
|
var init_toolExecution = __esm({
|
|
11860
12038
|
"../core/src/agent/toolExecution.ts"() {
|
|
11861
12039
|
"use strict";
|
|
@@ -11895,48 +12073,24 @@ var init_toolExecution = __esm({
|
|
|
11895
12073
|
const factor = 1 - (fill - 0.5) / 0.35 * 0.6;
|
|
11896
12074
|
return Math.max(floor, Math.floor(base * factor));
|
|
11897
12075
|
};
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
requestApproval: hostRequestApproval,
|
|
11912
|
-
requestQuestion: hostRequestQuestion,
|
|
11913
|
-
ideAction,
|
|
11914
|
-
keepRecentUnits,
|
|
11915
|
-
compactRatio,
|
|
11916
|
-
modelWindow,
|
|
11917
|
-
parentSystemPrompt,
|
|
11918
|
-
readTracker,
|
|
11919
|
-
reportCost,
|
|
11920
|
-
toolCostSink
|
|
11921
|
-
} = ctx;
|
|
11922
|
-
let calledName = "";
|
|
11923
|
-
let calledArgs = {};
|
|
11924
|
-
let parseFailed = false;
|
|
11925
|
-
if (toolCall.type === "function") {
|
|
11926
|
-
calledName = toolCall.function.name;
|
|
11927
|
-
try {
|
|
11928
|
-
calledArgs = JSON.parse(toolCall.function.arguments || "{}");
|
|
11929
|
-
} catch {
|
|
11930
|
-
parseFailed = true;
|
|
11931
|
-
}
|
|
11932
|
-
console.log(`\u{1F916} \u6A21\u578B\u8BF7\u6C42\u8C03\u7528\u5DE5\u5177: ${calledName}\uFF0C\u53C2\u6570:`, calledArgs);
|
|
11933
|
-
}
|
|
11934
|
-
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") {
|
|
11935
12089
|
const plan = typeof calledArgs?.plan === "string" ? calledArgs.plan : "";
|
|
11936
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";
|
|
11937
12091
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note, resultForUser: note, ok: true, terminal: { kind: "exit_plan_mode", plan } };
|
|
11938
12092
|
}
|
|
11939
|
-
if (calledName === "enter_plan_mode") {
|
|
12093
|
+
if (declaredTerminal === "plan_enter" || !declaredTerminal && calledName === "enter_plan_mode") {
|
|
11940
12094
|
if (planMode) {
|
|
11941
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";
|
|
11942
12096
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note2, resultForUser: note2, ok: true };
|
|
@@ -11949,227 +12103,294 @@ var init_toolExecution = __esm({
|
|
|
11949
12103
|
const placeholder = "\uFF08\u5DF2\u4E2D\u6B62\uFF0C\u672A\u6267\u884C\uFF09";
|
|
11950
12104
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: placeholder, resultForUser: placeholder, ok: false, aborted: true };
|
|
11951
12105
|
}
|
|
11952
|
-
|
|
11953
|
-
|
|
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);
|
|
11954
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`;
|
|
11955
|
-
|
|
12109
|
+
state.result = note;
|
|
12110
|
+
emitValidationFailed(ctx, toolCall, state);
|
|
11956
12111
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note, resultForUser: note, ok: false };
|
|
11957
12112
|
}
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
const
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
let needApproval = level === "mutation" /* MUTATION */ || level === "danger" /* DANGER */;
|
|
11975
|
-
let denied = false;
|
|
11976
|
-
let preHooksAdvanced = false;
|
|
11977
|
-
if (appConfig.hookRewrite) {
|
|
11978
|
-
preHooksAdvanced = true;
|
|
11979
|
-
let veto;
|
|
11980
|
-
try {
|
|
11981
|
-
veto = await runPreHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, toolCtx);
|
|
11982
|
-
} catch (e) {
|
|
11983
|
-
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` };
|
|
11984
|
-
}
|
|
11985
|
-
if (veto.deny) {
|
|
11986
|
-
denied = true;
|
|
11987
|
-
failResult(veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
|
|
11988
|
-
} else if (veto.argsOverride !== void 0 && veto.argsOverride !== null && typeof veto.argsOverride === "object" && !Array.isArray(veto.argsOverride)) {
|
|
11989
|
-
console.log(`\u270F\uFE0F [Pre-hook] [${calledName}] args \u5DF2\u88AB hook \u6539\u5199`);
|
|
11990
|
-
calledArgs = veto.argsOverride;
|
|
11991
|
-
}
|
|
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` };
|
|
11992
12129
|
}
|
|
11993
|
-
{
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
|
|
12000
|
-
|
|
12001
|
-
|
|
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);
|
|
12002
12146
|
}
|
|
12003
12147
|
}
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
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;
|
|
12010
12166
|
}
|
|
12011
|
-
|
|
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) => {
|
|
12012
12175
|
try {
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
denied = true;
|
|
12016
|
-
failResult(`\u274C [\u6743\u9650\u89C4\u5219]\uFF1A[${calledName}] \u88AB\u6743\u9650\u89C4\u5219 deny \u62D2\u7EDD\u3002`, "permission");
|
|
12017
|
-
} else if (perm === "allow") {
|
|
12018
|
-
needApproval = false;
|
|
12019
|
-
} else if (perm === "ask") {
|
|
12020
|
-
needApproval = true;
|
|
12021
|
-
}
|
|
12176
|
+
const ra = path36.resolve(a), rb = path36.resolve(b);
|
|
12177
|
+
return process.platform === "win32" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;
|
|
12022
12178
|
} catch {
|
|
12023
|
-
|
|
12179
|
+
return false;
|
|
12024
12180
|
}
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
};
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
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") {
|
|
12039
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;
|
|
12040
12215
|
}
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
}
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
denied = true;
|
|
12049
|
-
failResult(`\
|
|
12050
|
-
}
|
|
12051
|
-
if (needApproval && !denied && !scriptRunnerBlocked) {
|
|
12052
|
-
const auto = await runAutoCheck(
|
|
12053
|
-
resolveMcpPermissionName(calledName, calledArgs),
|
|
12054
|
-
calledArgs,
|
|
12055
|
-
toolCtx,
|
|
12056
|
-
permissionMode === "auto",
|
|
12057
|
-
{ autoApproval: matchedTool.function.autoApproval, pathArgs: matchedTool.function.pathArgs, primaryArg: matchedTool.function.primaryArg }
|
|
12058
|
-
);
|
|
12059
|
-
if (auto === "allow") {
|
|
12060
|
-
needApproval = false;
|
|
12061
|
-
} else if (auto === "deny") {
|
|
12062
|
-
denied = true;
|
|
12063
|
-
failResult(`\u274C [auto] \u5185\u7F6E\u9AD8\u5371\u6E05\u5355\u62E6\u622A\uFF1A[${calledName}] ${calledArgs?.path ?? ""}\u3002`, "permission");
|
|
12064
|
-
}
|
|
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");
|
|
12065
12225
|
}
|
|
12066
|
-
if (
|
|
12067
|
-
|
|
12068
|
-
|
|
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;
|
|
12069
12230
|
try {
|
|
12070
|
-
|
|
12231
|
+
approved2 = await requestApproval(calledName, toolCall.id, detail, toolCtx, level, state.calledArgs, matchedTool.function.primaryArg);
|
|
12071
12232
|
} catch (e) {
|
|
12072
|
-
denied = true;
|
|
12073
|
-
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");
|
|
12074
12235
|
}
|
|
12075
|
-
if (!denied) {
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
approved2 = await requestApproval(calledName, toolCall.id, detail, toolCtx, level, calledArgs, matchedTool.function.primaryArg);
|
|
12079
|
-
} catch (e) {
|
|
12080
|
-
denied = true;
|
|
12081
|
-
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");
|
|
12082
|
-
}
|
|
12083
|
-
if (!approved2 && !denied) {
|
|
12084
|
-
denied = true;
|
|
12085
|
-
failResult(`\u274C [\u5B89\u5168\u7194\u65AD]\uFF1A\u7528\u6237\u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u7533\u8BF7\u3002`, "permission");
|
|
12086
|
-
}
|
|
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");
|
|
12087
12239
|
}
|
|
12088
12240
|
}
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
}
|
|
12096
|
-
if (veto.deny) {
|
|
12097
|
-
denied = true;
|
|
12098
|
-
failResult(veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
|
|
12099
|
-
}
|
|
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` };
|
|
12100
12248
|
}
|
|
12101
|
-
if (
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
} catch (e) {
|
|
12105
|
-
denied = true;
|
|
12106
|
-
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`);
|
|
12107
|
-
}
|
|
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");
|
|
12108
12252
|
}
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
|
|
12117
|
-
|
|
12118
|
-
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
|
|
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 ?? ""}
|
|
12122
12279
|
\u8BF7\u6B63\u89C6\u4E0B\u65B9\u8F93\u51FA\uFF0C\u4E0D\u8981\u4E50\u89C2\u5047\u8BBE\u6210\u529F\u3002
|
|
12123
12280
|
|
|
12124
|
-
${result}`;
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
}
|
|
12128
|
-
}
|
|
12129
|
-
if (resultStatus === "success") {
|
|
12130
|
-
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 } });
|
|
12131
|
-
} else {
|
|
12132
|
-
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 } });
|
|
12133
|
-
}
|
|
12134
|
-
} catch (err) {
|
|
12135
|
-
console.error(`\u274C \u6267\u884C\u5DE5\u5177 ${calledName} \u65F6\u53D1\u751F\u9519\u8BEF:`, err);
|
|
12136
|
-
failResult(`\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`);
|
|
12137
|
-
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 } });
|
|
12138
|
-
}
|
|
12139
|
-
try {
|
|
12140
|
-
const post = await runPostHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, result, toolCtx);
|
|
12141
|
-
postHookOverride = post?.resultOverride;
|
|
12142
|
-
} catch (e) {
|
|
12143
|
-
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";
|
|
12144
12284
|
}
|
|
12145
12285
|
}
|
|
12146
|
-
|
|
12147
|
-
|
|
12148
|
-
|
|
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);
|
|
12149
12301
|
}
|
|
12150
|
-
|
|
12151
|
-
|
|
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;
|
|
12152
12308
|
const truncBudget = scaleTruncBudget(matchedTool?.function?.maxOutputCharacters ?? appConfig.MAX_TOOL_RESULT_CHARS, fill);
|
|
12153
|
-
const sidecarNote = result.length > truncBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), result) : void 0;
|
|
12154
|
-
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;
|
|
12155
12312
|
const ok = resultStatus === "success";
|
|
12156
|
-
let resultForModel = result;
|
|
12157
|
-
let resultForUser = result;
|
|
12313
|
+
let resultForModel = state.result;
|
|
12314
|
+
let resultForUser = state.result;
|
|
12158
12315
|
if (matchedTool?.function?.outputFilter) {
|
|
12159
12316
|
try {
|
|
12160
|
-
const split = matchedTool.function.outputFilter(result);
|
|
12317
|
+
const split = matchedTool.function.outputFilter(state.result);
|
|
12161
12318
|
resultForModel = split.toModel;
|
|
12162
12319
|
resultForUser = split.toUser;
|
|
12163
12320
|
} catch {
|
|
12164
12321
|
}
|
|
12165
12322
|
}
|
|
12166
|
-
if (postHookOverride !== void 0) {
|
|
12323
|
+
if (state.postHookOverride !== void 0) {
|
|
12167
12324
|
console.log(`\u270F\uFE0F [Post-hook] [${calledName}] \u6A21\u578B\u89C6\u56FE\u7ED3\u679C\u5DF2\u88AB hook \u6539\u5199`);
|
|
12168
12325
|
const overrideBudget = scaleTruncBudget(matchedTool?.function?.maxOutputCharacters ?? appConfig.MAX_TOOL_RESULT_CHARS, fill);
|
|
12169
|
-
const overrideNote = postHookOverride.length > overrideBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), postHookOverride) : void 0;
|
|
12170
|
-
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);
|
|
12171
12392
|
}
|
|
12172
|
-
return
|
|
12393
|
+
return await finalizeOutcome(ctx, toolCall, state);
|
|
12173
12394
|
};
|
|
12174
12395
|
}
|
|
12175
12396
|
});
|
|
@@ -12208,7 +12429,7 @@ var init_toolScheduling = __esm({
|
|
|
12208
12429
|
const fillRestPlaceholders = async (fromIdx) => {
|
|
12209
12430
|
const tcs2 = assistantMessage.tool_calls;
|
|
12210
12431
|
for (let j = fromIdx + 1; j < tcs2.length; j++) {
|
|
12211
|
-
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";
|
|
12212
12433
|
message.push({ role: "tool", tool_call_id: tcs2[j].id, content: ph });
|
|
12213
12434
|
await appendMessage({ sessionId, role: "tool", tool_call_id: tcs2[j].id, content: ph });
|
|
12214
12435
|
}
|
|
@@ -12227,14 +12448,15 @@ var init_toolScheduling = __esm({
|
|
|
12227
12448
|
};
|
|
12228
12449
|
const canParallelize = (name, args, parseFailed) => {
|
|
12229
12450
|
if (!parallelSafeToolsEnabled || parseFailed || signal?.aborted) return false;
|
|
12230
|
-
if (name === "exit_plan_mode" || name === "enter_plan_mode" || name === "ask_question") return false;
|
|
12231
12451
|
const matched = rawTools.find((t) => t.function.name === name);
|
|
12232
12452
|
if (!matched) return false;
|
|
12453
|
+
if (matched.function.terminal) return false;
|
|
12454
|
+
if (matched.function.parallelizable === false) return false;
|
|
12233
12455
|
if (matched.function.triggersUndo) return false;
|
|
12234
12456
|
if (matched.function.safetyLevel !== "safe" /* SAFE */) return false;
|
|
12235
12457
|
if (matched.function.isSync === false) return false;
|
|
12236
12458
|
try {
|
|
12237
|
-
const perm = checkPermission(name, args, matched.function.primaryArg);
|
|
12459
|
+
const perm = checkPermission(name, args, matched.function.primaryArg, sessionId);
|
|
12238
12460
|
if (perm === "ask" || perm === "deny") return false;
|
|
12239
12461
|
} catch {
|
|
12240
12462
|
return false;
|
|
@@ -12303,9 +12525,11 @@ var init_toolScheduling = __esm({
|
|
|
12303
12525
|
if (oc.terminal.kind === "exit_plan_mode") {
|
|
12304
12526
|
yield { type: "plan.proposed", plan: oc.terminal.plan || "" };
|
|
12305
12527
|
return { kind: "terminal", terminalText: oc.terminal.plan || oc.resultForUser };
|
|
12306
|
-
} else {
|
|
12528
|
+
} else if (oc.terminal.kind === "enter_plan_mode") {
|
|
12307
12529
|
yield { type: "plan.enterRequested", reason: oc.terminal.reason || "" };
|
|
12308
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 };
|
|
12309
12533
|
}
|
|
12310
12534
|
}
|
|
12311
12535
|
yield { type: "tool.end", toolCallId: oc.toolCallId, toolName: oc.calledName, result: oc.resultForUser, ok: oc.ok };
|
|
@@ -12518,7 +12742,8 @@ var init_streamInference = __esm({
|
|
|
12518
12742
|
depth,
|
|
12519
12743
|
signal,
|
|
12520
12744
|
toolsTokens,
|
|
12521
|
-
onUIEvent: ctx.onUIEvent
|
|
12745
|
+
onUIEvent: ctx.onUIEvent,
|
|
12746
|
+
onAuxUsage: ctx.onAuxUsage
|
|
12522
12747
|
});
|
|
12523
12748
|
inferenceMessages = withNudgeTail(message, nudgeMsg);
|
|
12524
12749
|
compactedThisRound = true;
|
|
@@ -12578,7 +12803,6 @@ var init_streamInference = __esm({
|
|
|
12578
12803
|
payload: { input: msgText(message[message.length - 1].content) }
|
|
12579
12804
|
// ★ 多模态:trace 只记文本视图
|
|
12580
12805
|
});
|
|
12581
|
-
console.log(`[DSC-DIAG] streamInference round ${round} \u5B8C\u6210: content=${contentBuf.length}chars reasoning=${reasoningBuf.length}chars toolCalls=${toolCallsBuf.size}`);
|
|
12582
12806
|
return { kind: "completed", assistantMessage, usage: lastUsage };
|
|
12583
12807
|
} catch (error) {
|
|
12584
12808
|
if (signal?.aborted) {
|
|
@@ -12592,7 +12816,7 @@ var init_streamInference = __esm({
|
|
|
12592
12816
|
payload: { input: msgText(message[message.length - 1].content), output: err.message }
|
|
12593
12817
|
// ★ 多模态:文本视图
|
|
12594
12818
|
});
|
|
12595
|
-
console.
|
|
12819
|
+
console.error(`\u274C \u63A8\u7406\u5931\u8D25\uFF08round ${round}\uFF09\uFF1A${err.message}`);
|
|
12596
12820
|
return { kind: "error", error: err };
|
|
12597
12821
|
}
|
|
12598
12822
|
};
|
|
@@ -12600,7 +12824,7 @@ var init_streamInference = __esm({
|
|
|
12600
12824
|
});
|
|
12601
12825
|
|
|
12602
12826
|
// ../core/src/agent/agentNudges.ts
|
|
12603
|
-
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;
|
|
12827
|
+
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;
|
|
12604
12828
|
var init_agentNudges = __esm({
|
|
12605
12829
|
"../core/src/agent/agentNudges.ts"() {
|
|
12606
12830
|
"use strict";
|
|
@@ -12627,9 +12851,14 @@ var init_agentNudges = __esm({
|
|
|
12627
12851
|
const pct = budget > 0 ? Math.round(spent / budget * 100) : 100;
|
|
12628
12852
|
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`;
|
|
12629
12853
|
};
|
|
12854
|
+
PLAN_REJECT_FENCE = "\u27E6DSC:PLAN_REJECT\u27E7";
|
|
12855
|
+
PLAN_REJECT_NUDGE_AT = 2;
|
|
12856
|
+
PLAN_REJECT_FORCE_AT = 4;
|
|
12857
|
+
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`;
|
|
12858
|
+
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`;
|
|
12630
12859
|
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";
|
|
12631
|
-
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\
|
|
12632
|
-
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\
|
|
12860
|
+
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`;
|
|
12861
|
+
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";
|
|
12633
12862
|
usageNoteText = (usage) => {
|
|
12634
12863
|
const pct = usage.windowTokens > 0 ? Math.min(100, Math.max(0, Math.round(usage.usedTokens / usage.windowTokens * 100))) : 0;
|
|
12635
12864
|
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`;
|
|
@@ -12692,8 +12921,16 @@ var init_agentNudges = __esm({
|
|
|
12692
12921
|
let todoNudges = 0;
|
|
12693
12922
|
let todoPending = null;
|
|
12694
12923
|
let budgetSoftSent = false;
|
|
12924
|
+
let planRejectCount = 0;
|
|
12925
|
+
let planRejectNudged = false;
|
|
12926
|
+
let planRejectPending = null;
|
|
12695
12927
|
return {
|
|
12696
12928
|
pickNudge(round) {
|
|
12929
|
+
if (planRejectPending) {
|
|
12930
|
+
const m = planRejectPending;
|
|
12931
|
+
planRejectPending = null;
|
|
12932
|
+
return m;
|
|
12933
|
+
}
|
|
12697
12934
|
if (!planFirstSent && round === 1 && !planMode && looksComplex(firstPrompt)) {
|
|
12698
12935
|
planFirstSent = true;
|
|
12699
12936
|
return { role: "system", content: `${PLAN_FIRST_FENCE}
|
|
@@ -12819,137 +13056,41 @@ ${REPEAT_GREP_TEXT(q, n)}` };
|
|
|
12819
13056
|
continue;
|
|
12820
13057
|
}
|
|
12821
13058
|
}
|
|
13059
|
+
},
|
|
13060
|
+
notePlanWriteRejected(total) {
|
|
13061
|
+
planRejectCount = Math.max(planRejectCount, Math.floor(total) || 0);
|
|
13062
|
+
if (!planRejectNudged && planRejectCount >= PLAN_REJECT_NUDGE_AT) {
|
|
13063
|
+
planRejectNudged = true;
|
|
13064
|
+
planRejectPending = { role: "system", content: `${PLAN_REJECT_FENCE}
|
|
13065
|
+
${PLAN_REJECT_TEXT(planRejectCount)}` };
|
|
13066
|
+
}
|
|
13067
|
+
},
|
|
13068
|
+
planWriteRejections() {
|
|
13069
|
+
return planRejectCount;
|
|
12822
13070
|
}
|
|
12823
13071
|
};
|
|
12824
13072
|
};
|
|
12825
13073
|
}
|
|
12826
13074
|
});
|
|
12827
13075
|
|
|
12828
|
-
// ../core/src/
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
var init_usageLog = __esm({
|
|
12833
|
-
"../core/src/observability/usageLog.ts"() {
|
|
13076
|
+
// ../core/src/config/prices.ts
|
|
13077
|
+
var MODEL_PRICES, PRICES_VERIFIED_AT, PRICES_STALE_DAYS, pricesAgeDays, isBeijingPeak, envPrice, ENV_OVERRIDE, priceFor, calcCostYuan;
|
|
13078
|
+
var init_prices = __esm({
|
|
13079
|
+
"../core/src/config/prices.ts"() {
|
|
12834
13080
|
"use strict";
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
ensuredDir = dir;
|
|
12849
|
-
}
|
|
12850
|
-
await fs34.appendFile(getUsageLogPath(month), JSON.stringify(full) + "\n", "utf-8");
|
|
12851
|
-
} catch (e) {
|
|
12852
|
-
console.warn("\u26A0\uFE0F [usageLog] \u4F7F\u7528\u65E5\u5FD7\u843D\u76D8\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u4E3B\u4E1A\u52A1\uFF09:", e instanceof Error ? e.message : e);
|
|
12853
|
-
}
|
|
12854
|
-
};
|
|
12855
|
-
USAGE_FILE_RE = /^usage-\d{4}-\d{2}\.jsonl$/;
|
|
12856
|
-
SYNTHETIC_SESSION_RE = /^(selftest-|eval-|e2e-)/;
|
|
12857
|
-
readUsageRecords = async (days) => {
|
|
12858
|
-
let files;
|
|
12859
|
-
try {
|
|
12860
|
-
files = (await fs34.readdir(usageDir())).filter((f) => USAGE_FILE_RE.test(f)).sort();
|
|
12861
|
-
} catch {
|
|
12862
|
-
return [];
|
|
12863
|
-
}
|
|
12864
|
-
let cutoff = null;
|
|
12865
|
-
if (days && days > 0) {
|
|
12866
|
-
cutoff = /* @__PURE__ */ new Date();
|
|
12867
|
-
cutoff.setHours(0, 0, 0, 0);
|
|
12868
|
-
cutoff.setDate(cutoff.getDate() - (days - 1));
|
|
12869
|
-
}
|
|
12870
|
-
const out = [];
|
|
12871
|
-
for (const f of files) {
|
|
12872
|
-
let text;
|
|
12873
|
-
try {
|
|
12874
|
-
text = await fs34.readFile(path37.join(usageDir(), f), "utf-8");
|
|
12875
|
-
} catch {
|
|
12876
|
-
continue;
|
|
12877
|
-
}
|
|
12878
|
-
for (const line of text.split("\n")) {
|
|
12879
|
-
if (!line.trim()) continue;
|
|
12880
|
-
let rec;
|
|
12881
|
-
try {
|
|
12882
|
-
rec = JSON.parse(line);
|
|
12883
|
-
} catch {
|
|
12884
|
-
continue;
|
|
12885
|
-
}
|
|
12886
|
-
if (!rec || typeof rec !== "object" || !rec.sessionId || !rec.ts) continue;
|
|
12887
|
-
if (SYNTHETIC_SESSION_RE.test(rec.sessionId)) continue;
|
|
12888
|
-
if (cutoff && new Date(rec.ts) < cutoff) continue;
|
|
12889
|
-
out.push(rec);
|
|
12890
|
-
}
|
|
12891
|
-
}
|
|
12892
|
-
return out.sort((a, b) => String(a.ts).localeCompare(String(b.ts)));
|
|
12893
|
-
};
|
|
12894
|
-
summarizeUsage = (records) => {
|
|
12895
|
-
const s = {
|
|
12896
|
-
runs: 0,
|
|
12897
|
-
mainRuns: 0,
|
|
12898
|
-
subRuns: 0,
|
|
12899
|
-
stopReasons: { normal: 0, aborted: 0, error: 0, repeat: 0, limit: 0 },
|
|
12900
|
-
rounds: 0,
|
|
12901
|
-
mainRounds: 0,
|
|
12902
|
-
durationMs: 0,
|
|
12903
|
-
promptTokens: 0,
|
|
12904
|
-
completionTokens: 0,
|
|
12905
|
-
totalTokens: 0,
|
|
12906
|
-
cachedTokens: 0,
|
|
12907
|
-
toolCalls: 0,
|
|
12908
|
-
tools: {}
|
|
12909
|
-
};
|
|
12910
|
-
for (const r of records) {
|
|
12911
|
-
s.runs++;
|
|
12912
|
-
if (r.depth > 0) s.subRuns++;
|
|
12913
|
-
else s.mainRuns++;
|
|
12914
|
-
if (r.stopReason in s.stopReasons) s.stopReasons[r.stopReason]++;
|
|
12915
|
-
s.rounds += r.rounds || 0;
|
|
12916
|
-
if (r.depth === 0) s.mainRounds += r.rounds || 0;
|
|
12917
|
-
s.durationMs += r.durationMs || 0;
|
|
12918
|
-
s.promptTokens += r.promptTokens ?? 0;
|
|
12919
|
-
s.completionTokens += r.completionTokens ?? 0;
|
|
12920
|
-
s.totalTokens += r.totalTokens ?? 0;
|
|
12921
|
-
s.cachedTokens += r.cachedTokens ?? 0;
|
|
12922
|
-
for (const [name, n] of Object.entries(r.tools ?? {})) {
|
|
12923
|
-
s.tools[name] = (s.tools[name] ?? 0) + (n || 0);
|
|
12924
|
-
s.toolCalls += n || 0;
|
|
12925
|
-
}
|
|
12926
|
-
if (!s.firstTs || r.ts < s.firstTs) s.firstTs = r.ts;
|
|
12927
|
-
if (!s.lastTs || r.ts > s.lastTs) s.lastTs = r.ts;
|
|
12928
|
-
}
|
|
12929
|
-
return s;
|
|
12930
|
-
};
|
|
12931
|
-
topTools = (tools, n) => Object.entries(tools).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, n);
|
|
12932
|
-
}
|
|
12933
|
-
});
|
|
12934
|
-
|
|
12935
|
-
// ../core/src/config/prices.ts
|
|
12936
|
-
var MODEL_PRICES, PRICES_VERIFIED_AT, PRICES_STALE_DAYS, pricesAgeDays, isBeijingPeak, envPrice, ENV_OVERRIDE, priceFor, calcCostYuan;
|
|
12937
|
-
var init_prices = __esm({
|
|
12938
|
-
"../core/src/config/prices.ts"() {
|
|
12939
|
-
"use strict";
|
|
12940
|
-
MODEL_PRICES = {
|
|
12941
|
-
"deepseek-flash": {
|
|
12942
|
-
idle: { input: 1, output: 4, cacheHit: 0.02 },
|
|
12943
|
-
peak: { input: 2, output: 8, cacheHit: 0.04 }
|
|
12944
|
-
},
|
|
12945
|
-
// 官网脚注明示旧名按 Flash 价格计费(2026-10-19 核实),非猜测——与 flash 同价条目
|
|
12946
|
-
"deepseek-flash-vision-exp": {
|
|
12947
|
-
idle: { input: 1, output: 4, cacheHit: 0.02 },
|
|
12948
|
-
peak: { input: 2, output: 8, cacheHit: 0.04 }
|
|
12949
|
-
},
|
|
12950
|
-
"deepseek-v4-pro": {
|
|
12951
|
-
idle: { input: 4.5, output: 13.5, cacheHit: 0.15 },
|
|
12952
|
-
peak: { input: 9, output: 27, cacheHit: 0.3 }
|
|
13081
|
+
MODEL_PRICES = {
|
|
13082
|
+
"deepseek-flash": {
|
|
13083
|
+
idle: { input: 1, output: 4, cacheHit: 0.02 },
|
|
13084
|
+
peak: { input: 2, output: 8, cacheHit: 0.04 }
|
|
13085
|
+
},
|
|
13086
|
+
// 官网脚注明示旧名按 Flash 价格计费(2026-10-19 核实),非猜测——与 flash 同价条目
|
|
13087
|
+
"deepseek-flash-vision-exp": {
|
|
13088
|
+
idle: { input: 1, output: 4, cacheHit: 0.02 },
|
|
13089
|
+
peak: { input: 2, output: 8, cacheHit: 0.04 }
|
|
13090
|
+
},
|
|
13091
|
+
"deepseek-v4-pro": {
|
|
13092
|
+
idle: { input: 4.5, output: 13.5, cacheHit: 0.15 },
|
|
13093
|
+
peak: { input: 9, output: 27, cacheHit: 0.3 }
|
|
12953
13094
|
}
|
|
12954
13095
|
};
|
|
12955
13096
|
PRICES_VERIFIED_AT = "2026-10-19";
|
|
@@ -12996,6 +13137,234 @@ var init_prices = __esm({
|
|
|
12996
13137
|
}
|
|
12997
13138
|
});
|
|
12998
13139
|
|
|
13140
|
+
// ../core/src/agent/costRelay.ts
|
|
13141
|
+
var createCostRelay;
|
|
13142
|
+
var init_costRelay = __esm({
|
|
13143
|
+
"../core/src/agent/costRelay.ts"() {
|
|
13144
|
+
"use strict";
|
|
13145
|
+
createCostRelay = (sink) => {
|
|
13146
|
+
let reported = 0;
|
|
13147
|
+
return {
|
|
13148
|
+
/** 上报当前累计总额,向 sink 递送相对上次上报的正增量。 */
|
|
13149
|
+
flush(total) {
|
|
13150
|
+
if (!sink) return;
|
|
13151
|
+
if (!Number.isFinite(total)) return;
|
|
13152
|
+
const delta = total - reported;
|
|
13153
|
+
if (delta <= 1e-9) return;
|
|
13154
|
+
const r = Math.round(delta * 1e6) / 1e6;
|
|
13155
|
+
if (r <= 0) return;
|
|
13156
|
+
reported += r;
|
|
13157
|
+
sink(r);
|
|
13158
|
+
},
|
|
13159
|
+
/** 已累计上报给 sink 的金额(元),父级/调试可读。 */
|
|
13160
|
+
get reported() {
|
|
13161
|
+
return reported;
|
|
13162
|
+
}
|
|
13163
|
+
};
|
|
13164
|
+
};
|
|
13165
|
+
}
|
|
13166
|
+
});
|
|
13167
|
+
|
|
13168
|
+
// ../core/src/agent/runLedger.ts
|
|
13169
|
+
var BUDGET_HARD_LIMIT_RATIO, roundYuan6, createRunLedger;
|
|
13170
|
+
var init_runLedger = __esm({
|
|
13171
|
+
"../core/src/agent/runLedger.ts"() {
|
|
13172
|
+
"use strict";
|
|
13173
|
+
init_prices();
|
|
13174
|
+
init_costRelay();
|
|
13175
|
+
init_client();
|
|
13176
|
+
BUDGET_HARD_LIMIT_RATIO = 1.2;
|
|
13177
|
+
roundYuan6 = (y) => Math.round(y * 1e6) / 1e6;
|
|
13178
|
+
createRunLedger = (opts) => {
|
|
13179
|
+
let budgetSpentYuan = 0;
|
|
13180
|
+
let usageSum;
|
|
13181
|
+
let costYuanAcc = 0;
|
|
13182
|
+
let costKnown = true;
|
|
13183
|
+
let toolCostYuanAcc = 0;
|
|
13184
|
+
let auxCostYuanAcc = 0;
|
|
13185
|
+
let subCostYuanAcc = 0;
|
|
13186
|
+
const relay = createCostRelay(opts.costSink);
|
|
13187
|
+
const addUsage = (u) => {
|
|
13188
|
+
if (!u) return;
|
|
13189
|
+
usageSum = {
|
|
13190
|
+
prompt_tokens: (usageSum?.prompt_tokens ?? 0) + (u.prompt_tokens ?? 0),
|
|
13191
|
+
completion_tokens: (usageSum?.completion_tokens ?? 0) + (u.completion_tokens ?? 0),
|
|
13192
|
+
total_tokens: (usageSum?.total_tokens ?? 0) + (u.total_tokens ?? 0),
|
|
13193
|
+
cached_tokens: (usageSum?.cached_tokens ?? 0) + (u.cached_tokens ?? 0)
|
|
13194
|
+
};
|
|
13195
|
+
const cost = calcCostYuan(opts.model, { promptTokens: u.prompt_tokens, completionTokens: u.completion_tokens, cachedTokens: u.cached_tokens });
|
|
13196
|
+
if (cost == null) costKnown = false;
|
|
13197
|
+
else {
|
|
13198
|
+
costYuanAcc += cost;
|
|
13199
|
+
budgetSpentYuan += cost;
|
|
13200
|
+
}
|
|
13201
|
+
};
|
|
13202
|
+
const addToolCost = (yuan) => {
|
|
13203
|
+
if (!Number.isFinite(yuan) || yuan <= 0) return;
|
|
13204
|
+
toolCostYuanAcc += yuan;
|
|
13205
|
+
budgetSpentYuan += yuan;
|
|
13206
|
+
};
|
|
13207
|
+
const reportAuxUsage = (usage, kind) => {
|
|
13208
|
+
if (usage) {
|
|
13209
|
+
const cost = calcCostYuan(AUX_MODEL_NAME, { promptTokens: usage.prompt_tokens, completionTokens: usage.completion_tokens, cachedTokens: usage.cached_tokens });
|
|
13210
|
+
if (cost != null && Number.isFinite(cost) && cost > 0) {
|
|
13211
|
+
auxCostYuanAcc += cost;
|
|
13212
|
+
budgetSpentYuan += cost;
|
|
13213
|
+
}
|
|
13214
|
+
}
|
|
13215
|
+
try {
|
|
13216
|
+
opts.onAuxUsage?.(usage, kind);
|
|
13217
|
+
} catch {
|
|
13218
|
+
}
|
|
13219
|
+
};
|
|
13220
|
+
const reportSubCost = (yuan) => {
|
|
13221
|
+
if (Number.isFinite(yuan) && yuan > 0) {
|
|
13222
|
+
budgetSpentYuan += yuan;
|
|
13223
|
+
subCostYuanAcc += yuan;
|
|
13224
|
+
}
|
|
13225
|
+
};
|
|
13226
|
+
const snapshot = () => opts.budgetYuan != null ? { spent: budgetSpentYuan, budget: opts.budgetYuan } : null;
|
|
13227
|
+
return {
|
|
13228
|
+
addUsage,
|
|
13229
|
+
addToolCost,
|
|
13230
|
+
reportAuxUsage,
|
|
13231
|
+
reportSubCost,
|
|
13232
|
+
/** 预算 soft 判定读数(nudge 管线消费):未武装返回 null。O(1) 闭包读数。 */
|
|
13233
|
+
getBudgetStatus: snapshot,
|
|
13234
|
+
/** 预算硬线判定(E2):spent >= budget×1.2(>= 语义与收拢前逐字节等价,恰 1.2× 即触发)。
|
|
13235
|
+
* 自武装探测(inheritedBudgetCheck 兜底)与轮边界硬截断共用同一判定。 */
|
|
13236
|
+
overHardLimit: () => opts.budgetYuan != null && budgetSpentYuan >= opts.budgetYuan * BUDGET_HARD_LIMIT_RATIO,
|
|
13237
|
+
/** 子 run 成本中继 flush:基数=预算圈实时读数(子树完整口径,文件头★中继段)。主 run 无 sink 时 no-op。 */
|
|
13238
|
+
flushRelay: () => relay.flush(budgetSpentYuan),
|
|
13239
|
+
/** run.end 事件行与 receipt token 段共用的用量合计快照。 */
|
|
13240
|
+
usageSummary: () => usageSum,
|
|
13241
|
+
/** 预算读数快照:硬截断 final 文案金额源。未武装=null(调用方结构上只在已武装分支取,勿 ?? 0 兜底)。 */
|
|
13242
|
+
budgetSnapshot: snapshot,
|
|
13243
|
+
/** usageLog 落盘字段(recordUsageRun 入参金额段;costYuan 无价=undefined、各账 ≤0 省略——自动缺省容错)。 */
|
|
13244
|
+
usageLogFields: () => ({
|
|
13245
|
+
costYuan: costKnown ? roundYuan6(costYuanAcc) : void 0,
|
|
13246
|
+
toolCostYuan: toolCostYuanAcc > 0 ? roundYuan6(toolCostYuanAcc) : void 0,
|
|
13247
|
+
auxCostYuan: auxCostYuanAcc > 0 ? roundYuan6(auxCostYuanAcc) : void 0
|
|
13248
|
+
}),
|
|
13249
|
+
/** run.receipt 结算单金额段:costYuan 无价=null(显式「不知钱」,绝不编数);各账 ≤0 省略字段。 */
|
|
13250
|
+
receiptFields: () => ({
|
|
13251
|
+
costYuan: costKnown ? roundYuan6(costYuanAcc) : null,
|
|
13252
|
+
toolCostYuan: toolCostYuanAcc > 0 ? roundYuan6(toolCostYuanAcc) : void 0,
|
|
13253
|
+
auxCostYuan: auxCostYuanAcc > 0 ? roundYuan6(auxCostYuanAcc) : void 0,
|
|
13254
|
+
subCostYuan: subCostYuanAcc > 0 ? roundYuan6(subCostYuanAcc) : void 0
|
|
13255
|
+
})
|
|
13256
|
+
};
|
|
13257
|
+
};
|
|
13258
|
+
}
|
|
13259
|
+
});
|
|
13260
|
+
|
|
13261
|
+
// ../core/src/observability/usageLog.ts
|
|
13262
|
+
import fs34 from "fs/promises";
|
|
13263
|
+
import path37 from "path";
|
|
13264
|
+
var USAGE_DISABLED, usageDir, ensuredDir, getUsageLogPath, recordUsageRun, USAGE_FILE_RE, SYNTHETIC_SESSION_RE, readUsageRecords, summarizeUsage, topTools;
|
|
13265
|
+
var init_usageLog = __esm({
|
|
13266
|
+
"../core/src/observability/usageLog.ts"() {
|
|
13267
|
+
"use strict";
|
|
13268
|
+
init_config();
|
|
13269
|
+
USAGE_DISABLED = process.env.DEEPSEEKER_CODE_USAGE_LOG === "0";
|
|
13270
|
+
usageDir = () => path37.join(appConfig.dataDir, "usage");
|
|
13271
|
+
ensuredDir = null;
|
|
13272
|
+
getUsageLogPath = (month) => path37.join(usageDir(), `usage-${month}.jsonl`);
|
|
13273
|
+
recordUsageRun = async (rec) => {
|
|
13274
|
+
if (USAGE_DISABLED) return;
|
|
13275
|
+
try {
|
|
13276
|
+
const full = { workspace: appConfig.userWorkspaceDir, ...rec };
|
|
13277
|
+
const month = (full.ts || (/* @__PURE__ */ new Date()).toISOString()).slice(0, 7);
|
|
13278
|
+
const dir = usageDir();
|
|
13279
|
+
if (ensuredDir !== dir) {
|
|
13280
|
+
await fs34.mkdir(dir, { recursive: true });
|
|
13281
|
+
ensuredDir = dir;
|
|
13282
|
+
}
|
|
13283
|
+
await fs34.appendFile(getUsageLogPath(month), JSON.stringify(full) + "\n", "utf-8");
|
|
13284
|
+
} catch (e) {
|
|
13285
|
+
console.warn("\u26A0\uFE0F [usageLog] \u4F7F\u7528\u65E5\u5FD7\u843D\u76D8\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u4E3B\u4E1A\u52A1\uFF09:", e instanceof Error ? e.message : e);
|
|
13286
|
+
}
|
|
13287
|
+
};
|
|
13288
|
+
USAGE_FILE_RE = /^usage-\d{4}-\d{2}\.jsonl$/;
|
|
13289
|
+
SYNTHETIC_SESSION_RE = /^(selftest-|eval-|e2e-)/;
|
|
13290
|
+
readUsageRecords = async (days) => {
|
|
13291
|
+
let files;
|
|
13292
|
+
try {
|
|
13293
|
+
files = (await fs34.readdir(usageDir())).filter((f) => USAGE_FILE_RE.test(f)).sort();
|
|
13294
|
+
} catch {
|
|
13295
|
+
return [];
|
|
13296
|
+
}
|
|
13297
|
+
let cutoff = null;
|
|
13298
|
+
if (days && days > 0) {
|
|
13299
|
+
cutoff = /* @__PURE__ */ new Date();
|
|
13300
|
+
cutoff.setHours(0, 0, 0, 0);
|
|
13301
|
+
cutoff.setDate(cutoff.getDate() - (days - 1));
|
|
13302
|
+
}
|
|
13303
|
+
const out = [];
|
|
13304
|
+
for (const f of files) {
|
|
13305
|
+
let text;
|
|
13306
|
+
try {
|
|
13307
|
+
text = await fs34.readFile(path37.join(usageDir(), f), "utf-8");
|
|
13308
|
+
} catch {
|
|
13309
|
+
continue;
|
|
13310
|
+
}
|
|
13311
|
+
for (const line of text.split("\n")) {
|
|
13312
|
+
if (!line.trim()) continue;
|
|
13313
|
+
let rec;
|
|
13314
|
+
try {
|
|
13315
|
+
rec = JSON.parse(line);
|
|
13316
|
+
} catch {
|
|
13317
|
+
continue;
|
|
13318
|
+
}
|
|
13319
|
+
if (!rec || typeof rec !== "object" || !rec.sessionId || !rec.ts) continue;
|
|
13320
|
+
if (SYNTHETIC_SESSION_RE.test(rec.sessionId)) continue;
|
|
13321
|
+
if (cutoff && new Date(rec.ts) < cutoff) continue;
|
|
13322
|
+
out.push(rec);
|
|
13323
|
+
}
|
|
13324
|
+
}
|
|
13325
|
+
return out.sort((a, b) => String(a.ts).localeCompare(String(b.ts)));
|
|
13326
|
+
};
|
|
13327
|
+
summarizeUsage = (records) => {
|
|
13328
|
+
const s = {
|
|
13329
|
+
runs: 0,
|
|
13330
|
+
mainRuns: 0,
|
|
13331
|
+
subRuns: 0,
|
|
13332
|
+
stopReasons: { normal: 0, aborted: 0, error: 0, repeat: 0, limit: 0 },
|
|
13333
|
+
rounds: 0,
|
|
13334
|
+
mainRounds: 0,
|
|
13335
|
+
durationMs: 0,
|
|
13336
|
+
promptTokens: 0,
|
|
13337
|
+
completionTokens: 0,
|
|
13338
|
+
totalTokens: 0,
|
|
13339
|
+
cachedTokens: 0,
|
|
13340
|
+
toolCalls: 0,
|
|
13341
|
+
tools: {}
|
|
13342
|
+
};
|
|
13343
|
+
for (const r of records) {
|
|
13344
|
+
s.runs++;
|
|
13345
|
+
if (r.depth > 0) s.subRuns++;
|
|
13346
|
+
else s.mainRuns++;
|
|
13347
|
+
if (r.stopReason in s.stopReasons) s.stopReasons[r.stopReason]++;
|
|
13348
|
+
s.rounds += r.rounds || 0;
|
|
13349
|
+
if (r.depth === 0) s.mainRounds += r.rounds || 0;
|
|
13350
|
+
s.durationMs += r.durationMs || 0;
|
|
13351
|
+
s.promptTokens += r.promptTokens ?? 0;
|
|
13352
|
+
s.completionTokens += r.completionTokens ?? 0;
|
|
13353
|
+
s.totalTokens += r.totalTokens ?? 0;
|
|
13354
|
+
s.cachedTokens += r.cachedTokens ?? 0;
|
|
13355
|
+
for (const [name, n] of Object.entries(r.tools ?? {})) {
|
|
13356
|
+
s.tools[name] = (s.tools[name] ?? 0) + (n || 0);
|
|
13357
|
+
s.toolCalls += n || 0;
|
|
13358
|
+
}
|
|
13359
|
+
if (!s.firstTs || r.ts < s.firstTs) s.firstTs = r.ts;
|
|
13360
|
+
if (!s.lastTs || r.ts > s.lastTs) s.lastTs = r.ts;
|
|
13361
|
+
}
|
|
13362
|
+
return s;
|
|
13363
|
+
};
|
|
13364
|
+
topTools = (tools, n) => Object.entries(tools).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, n);
|
|
13365
|
+
}
|
|
13366
|
+
});
|
|
13367
|
+
|
|
12999
13368
|
// ../core/src/agent/runAgent.ts
|
|
13000
13369
|
async function* runAgent(message, options) {
|
|
13001
13370
|
const sessionId = options.sessionId;
|
|
@@ -13015,25 +13384,30 @@ async function* runAgent(message, options) {
|
|
|
13015
13384
|
const keepRecentUnits = options.keepRecentUnits;
|
|
13016
13385
|
const compactRatio = options.compactRatio;
|
|
13017
13386
|
const parentSystemPrompt = options.parentSystemPrompt;
|
|
13387
|
+
const effectiveModel = options.model?.trim() || MODEL_NAME;
|
|
13018
13388
|
const { rawTools, cleanedToolSchemas, toolsTokens } = await prepareToolsAndInjections(message, options, events);
|
|
13019
13389
|
let round = 0;
|
|
13020
13390
|
let lastContent = "";
|
|
13021
13391
|
let stopReason = "normal";
|
|
13022
13392
|
let budgetTruncated = false;
|
|
13023
13393
|
const MAX_AGENT_ROUNDS = 500;
|
|
13024
|
-
const firstPrompt = (
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13029
|
-
|
|
13394
|
+
const firstPrompt = resolveFirstPrompt(message, options);
|
|
13395
|
+
const ledger = createRunLedger({
|
|
13396
|
+
model: effectiveModel,
|
|
13397
|
+
budgetYuan: depth === 0 && options.budgetYuan != null && options.budgetYuan > 0 ? options.budgetYuan : void 0,
|
|
13398
|
+
costSink: options.costSink,
|
|
13399
|
+
onAuxUsage: options.onAuxUsage
|
|
13400
|
+
});
|
|
13401
|
+
const reportAuxUsage = ledger.reportAuxUsage;
|
|
13402
|
+
const toolCostSink = process.env.DEEP_SEEK_TOOL_COST === "0" ? void 0 : ledger.addToolCost;
|
|
13030
13403
|
let runCompactCount = 0;
|
|
13031
13404
|
const nudges = createNudgeScheduler({
|
|
13032
13405
|
firstPrompt,
|
|
13033
13406
|
planMode: !!options.planMode,
|
|
13034
13407
|
noEarlyFinal: !!options.noEarlyFinal,
|
|
13035
13408
|
getContextUsage: () => ({ usedTokens: Math.round(estimateTokens(message) * calibRatio + toolsTokens), windowTokens: modelWindow }),
|
|
13036
|
-
getBudgetStatus:
|
|
13409
|
+
getBudgetStatus: ledger.getBudgetStatus
|
|
13410
|
+
// ★ E2 预算 soft 读数(闭包在账本内 O(1) 读数;未武装返回 null,管线自判)
|
|
13037
13411
|
});
|
|
13038
13412
|
const readTracker = /* @__PURE__ */ new Map();
|
|
13039
13413
|
const userDecisionSource = depth > 0 ? "spawn_agent" : "user";
|
|
@@ -13050,37 +13424,13 @@ async function* runAgent(message, options) {
|
|
|
13050
13424
|
});
|
|
13051
13425
|
};
|
|
13052
13426
|
const startTime = performance.now();
|
|
13053
|
-
const persistedCalib = await
|
|
13427
|
+
const persistedCalib = await getCalibration(sessionId, effectiveModel);
|
|
13054
13428
|
let calibRatio = persistedCalib.calibRatio ?? 1.4;
|
|
13055
13429
|
let lastRealPromptTokens = persistedCalib.lastRealPromptTokens;
|
|
13056
13430
|
let lastCachedTokens = persistedCalib.lastCachedTokens;
|
|
13057
13431
|
const breaker = createRepeatBreaker({ sessionId, depth, llmDecisionSource, startTime, events });
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
let costYuanAcc = 0;
|
|
13061
|
-
let costKnown = true;
|
|
13062
|
-
const addUsage = (u) => {
|
|
13063
|
-
if (!u) return;
|
|
13064
|
-
usageSum = {
|
|
13065
|
-
prompt_tokens: (usageSum?.prompt_tokens ?? 0) + (u.prompt_tokens ?? 0),
|
|
13066
|
-
completion_tokens: (usageSum?.completion_tokens ?? 0) + (u.completion_tokens ?? 0),
|
|
13067
|
-
total_tokens: (usageSum?.total_tokens ?? 0) + (u.total_tokens ?? 0),
|
|
13068
|
-
cached_tokens: (usageSum?.cached_tokens ?? 0) + (u.cached_tokens ?? 0)
|
|
13069
|
-
};
|
|
13070
|
-
const cost = calcCostYuan(effectiveModel, { promptTokens: u.prompt_tokens, completionTokens: u.completion_tokens, cachedTokens: u.cached_tokens });
|
|
13071
|
-
if (cost == null) costKnown = false;
|
|
13072
|
-
else {
|
|
13073
|
-
costYuanAcc += cost;
|
|
13074
|
-
budgetSpentYuan += cost;
|
|
13075
|
-
}
|
|
13076
|
-
};
|
|
13077
|
-
let toolCostYuanAcc = 0;
|
|
13078
|
-
const addToolCost = (yuan) => {
|
|
13079
|
-
if (!Number.isFinite(yuan) || yuan <= 0) return;
|
|
13080
|
-
toolCostYuanAcc += yuan;
|
|
13081
|
-
budgetSpentYuan += yuan;
|
|
13082
|
-
};
|
|
13083
|
-
const toolCostSink = process.env.DEEP_SEEK_TOOL_COST === "0" ? void 0 : addToolCost;
|
|
13432
|
+
const inheritedBudgetCheck = options.budgetCheck ?? (depth === 0 ? ledger.overHardLimit : void 0);
|
|
13433
|
+
let planWriteRej = 0;
|
|
13084
13434
|
const toolCounts = {};
|
|
13085
13435
|
await appendEvent(sessionId, { dscEvent: "run.start", runId, depth });
|
|
13086
13436
|
try {
|
|
@@ -13110,11 +13460,18 @@ async function* runAgent(message, options) {
|
|
|
13110
13460
|
yield { type: "final", text: lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
|
|
13111
13461
|
return;
|
|
13112
13462
|
}
|
|
13113
|
-
if (depth === 0 &&
|
|
13463
|
+
if (depth === 0 && ledger.overHardLimit()) {
|
|
13464
|
+
const snap = ledger.budgetSnapshot();
|
|
13114
13465
|
stopReason = "limit";
|
|
13115
13466
|
budgetTruncated = true;
|
|
13116
13467
|
yield { type: "final", text: (lastContent || "") + `
|
|
13117
|
-
\uFF08\u{1F4B0} \u9884\u7B97\u622A\u65AD\uFF1A\u672C run \u5DF2\u82B1\u8D39 \xA5${
|
|
13468
|
+
\uFF08\u{1F4B0} \u9884\u7B97\u622A\u65AD\uFF1A\u672C run \u5DF2\u82B1\u8D39 \xA5${snap.spent.toFixed(3)} / \u9884\u7B97 \xA5${snap.budget.toFixed(3)}\u3002/budget \u8C03\u6574\u9884\u7B97\u540E\u56DE\u590D"\u7EE7\u7EED"\u5373\u53EF\u63A5\u7EED\u3002\uFF09` };
|
|
13469
|
+
return;
|
|
13470
|
+
}
|
|
13471
|
+
if (depth > 0 && options.budgetCheck?.()) {
|
|
13472
|
+
stopReason = "limit";
|
|
13473
|
+
yield { type: "final", text: (lastContent || "") + `
|
|
13474
|
+
\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` };
|
|
13118
13475
|
return;
|
|
13119
13476
|
}
|
|
13120
13477
|
const inboxTexts = claimSessionInbox(sessionId);
|
|
@@ -13141,6 +13498,8 @@ async function* runAgent(message, options) {
|
|
|
13141
13498
|
lastRealPromptTokens,
|
|
13142
13499
|
lastCachedTokens,
|
|
13143
13500
|
toolsTokens,
|
|
13501
|
+
// ★ P0-2:压缩摘要(summarize)的 aux usage 回传计价(见 reportAuxUsage 声明处)
|
|
13502
|
+
onAuxUsage: reportAuxUsage,
|
|
13144
13503
|
// ★ H1:旁路数 compact.done 供 context.usage.compactCount(透传不拦截)
|
|
13145
13504
|
onUIEvent: (evt) => {
|
|
13146
13505
|
if (evt.type === "compact.done") runCompactCount++;
|
|
@@ -13179,7 +13538,8 @@ async function* runAgent(message, options) {
|
|
|
13179
13538
|
compactRatio,
|
|
13180
13539
|
modelWindow,
|
|
13181
13540
|
toolsTokens,
|
|
13182
|
-
onUIEvent: options.onUIEvent
|
|
13541
|
+
onUIEvent: options.onUIEvent,
|
|
13542
|
+
onAuxUsage: reportAuxUsage
|
|
13183
13543
|
});
|
|
13184
13544
|
if (infResult.kind === "aborted") {
|
|
13185
13545
|
yield { type: "final", text: infResult.partialText || lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
|
|
@@ -13194,12 +13554,12 @@ async function* runAgent(message, options) {
|
|
|
13194
13554
|
const estAtInfer = estimateTokens(message) + toolsTokens;
|
|
13195
13555
|
if (estAtInfer > 1e3) {
|
|
13196
13556
|
const observed = infResult.usage.prompt_tokens / estAtInfer;
|
|
13197
|
-
calibRatio = calibRatio *
|
|
13557
|
+
calibRatio = calibRatio * CALIB_EMA_KEEP + observed * CALIB_EMA_NEW;
|
|
13198
13558
|
}
|
|
13199
13559
|
lastRealPromptTokens = infResult.usage.prompt_tokens;
|
|
13200
13560
|
lastCachedTokens = infResult.usage.cached_tokens;
|
|
13201
13561
|
}
|
|
13202
|
-
addUsage(infResult.usage);
|
|
13562
|
+
ledger.addUsage(infResult.usage);
|
|
13203
13563
|
const assistantMessage = infResult.assistantMessage;
|
|
13204
13564
|
message.push(assistantMessage);
|
|
13205
13565
|
await appendMessage({ ...assistantMessage, sessionId });
|
|
@@ -13209,6 +13569,7 @@ async function* runAgent(message, options) {
|
|
|
13209
13569
|
if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
|
|
13210
13570
|
await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
|
|
13211
13571
|
emitContextUsage();
|
|
13572
|
+
ledger.flushRelay();
|
|
13212
13573
|
const finalText = (typeof assistantMessage.content === "string" ? assistantMessage.content : "") || "";
|
|
13213
13574
|
const intercepted = nudges.interceptFinal(finalText, round);
|
|
13214
13575
|
if (intercepted) continue;
|
|
@@ -13258,14 +13619,21 @@ async function* runAgent(message, options) {
|
|
|
13258
13619
|
contextTokensEst: Math.round(estimateTokens(message) * calibRatio + toolsTokens),
|
|
13259
13620
|
readTracker,
|
|
13260
13621
|
// ★ P3-8:run 级读取追踪(同一引用透传,跨轮累计)
|
|
13261
|
-
// ★ 子 run 成本回灌(成本信任链第二环 E1):runSubagent 经 toolCtx.reportCost
|
|
13262
|
-
//
|
|
13263
|
-
reportCost:
|
|
13264
|
-
if (Number.isFinite(yuan) && yuan > 0) budgetSpentYuan += yuan;
|
|
13265
|
-
},
|
|
13622
|
+
// ★ 子 run 成本回灌(成本信任链第二环 E1):runSubagent 经 toolCtx.reportCost 把子 run 花费
|
|
13623
|
+
// 记进账本预算圈 + 展示账——父预算自动继承约束(守卫与分账语义在 runLedger.reportSubCost)。
|
|
13624
|
+
reportCost: ledger.reportSubCost,
|
|
13266
13625
|
// ★ 按次计费工具成本上报(DeepSeek Search/Fetch):web 工具经 toolCtx.toolCostSink 拿到闭包,
|
|
13267
13626
|
// 成功调用后按次上报进本 run 累计器。
|
|
13268
|
-
toolCostSink
|
|
13627
|
+
toolCostSink,
|
|
13628
|
+
// ★ P0-2:auto 档风险分类器(classifyToolRisk)的 aux usage 回传计价(见 reportAuxUsage 声明处)
|
|
13629
|
+
onAuxUsage: reportAuxUsage,
|
|
13630
|
+
// ★ P1-7 计划拒绝熔断:计划期非 SAFE 工具被拒增量回传(计数 + 通知 nudge 调度器发转向文案)
|
|
13631
|
+
planWriteRejections: (delta) => {
|
|
13632
|
+
planWriteRej += delta;
|
|
13633
|
+
nudges.notePlanWriteRejected(planWriteRej);
|
|
13634
|
+
},
|
|
13635
|
+
// ★ P1-5 子任务预算熔断:runSubagent 经 toolCtx.budgetCheck 拿到探测闭包,子 runAgent 每轮边界自检
|
|
13636
|
+
budgetCheck: inheritedBudgetCheck
|
|
13269
13637
|
};
|
|
13270
13638
|
const scheduleResult = yield* scheduleToolCalls(assistantMessage, message, toolCallCtx);
|
|
13271
13639
|
if (scheduleResult.kind === "aborted") {
|
|
@@ -13278,6 +13646,12 @@ async function* runAgent(message, options) {
|
|
|
13278
13646
|
}
|
|
13279
13647
|
await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
|
|
13280
13648
|
emitContextUsage();
|
|
13649
|
+
ledger.flushRelay();
|
|
13650
|
+
if (options.planMode && planWriteRej >= PLAN_REJECT_FORCE_AT) {
|
|
13651
|
+
stopReason = "limit";
|
|
13652
|
+
yield { type: "final", text: PLAN_REJECT_FORCE_FINAL_TEXT(planWriteRej) };
|
|
13653
|
+
return;
|
|
13654
|
+
}
|
|
13281
13655
|
}
|
|
13282
13656
|
} catch (toolErr) {
|
|
13283
13657
|
if (signal?.aborted) {
|
|
@@ -13296,10 +13670,11 @@ async function* runAgent(message, options) {
|
|
|
13296
13670
|
} catch {
|
|
13297
13671
|
}
|
|
13298
13672
|
try {
|
|
13299
|
-
await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage:
|
|
13673
|
+
await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage: ledger.usageSummary() });
|
|
13300
13674
|
} catch {
|
|
13301
13675
|
}
|
|
13302
13676
|
try {
|
|
13677
|
+
const us = ledger.usageSummary();
|
|
13303
13678
|
void recordUsageRun({
|
|
13304
13679
|
v: 1,
|
|
13305
13680
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13310,35 +13685,33 @@ async function* runAgent(message, options) {
|
|
|
13310
13685
|
stopReason: signal?.aborted ? "aborted" : stopReason,
|
|
13311
13686
|
rounds: round,
|
|
13312
13687
|
durationMs: Math.round(performance.now() - startTime),
|
|
13313
|
-
promptTokens:
|
|
13314
|
-
completionTokens:
|
|
13315
|
-
totalTokens:
|
|
13316
|
-
cachedTokens:
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
// 按次计费工具花费(Search/Fetch)
|
|
13688
|
+
promptTokens: us?.prompt_tokens,
|
|
13689
|
+
completionTokens: us?.completion_tokens,
|
|
13690
|
+
totalTokens: us?.total_tokens,
|
|
13691
|
+
cachedTokens: us?.cached_tokens,
|
|
13692
|
+
...ledger.usageLogFields(),
|
|
13693
|
+
// costYuan/toolCostYuan/auxCostYuan(口径与舍入在 runLedger)
|
|
13320
13694
|
tools: toolCounts
|
|
13321
13695
|
});
|
|
13322
13696
|
} catch {
|
|
13323
13697
|
}
|
|
13324
13698
|
try {
|
|
13325
|
-
|
|
13326
|
-
if (options.costSink && spentTotal > 0) options.costSink(Math.round(spentTotal * 1e6) / 1e6);
|
|
13699
|
+
ledger.flushRelay();
|
|
13327
13700
|
} catch {
|
|
13328
13701
|
}
|
|
13329
13702
|
try {
|
|
13330
|
-
|
|
13331
|
-
|
|
13703
|
+
const us = ledger.usageSummary();
|
|
13704
|
+
if (depth === 0 && options.onUIEvent && us) {
|
|
13705
|
+
const prompt = us.prompt_tokens ?? 0;
|
|
13332
13706
|
options.onUIEvent({
|
|
13333
13707
|
type: "run.receipt",
|
|
13334
13708
|
rounds: round,
|
|
13335
13709
|
promptTokens: prompt,
|
|
13336
|
-
completionTokens:
|
|
13337
|
-
cachedTokens:
|
|
13338
|
-
cacheHitRate: prompt > 0 ? (
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
// 按次计费工具花费(undefined 自动省略)
|
|
13710
|
+
completionTokens: us.completion_tokens ?? 0,
|
|
13711
|
+
cachedTokens: us.cached_tokens ?? 0,
|
|
13712
|
+
cacheHitRate: prompt > 0 ? (us.cached_tokens ?? 0) / prompt : 0,
|
|
13713
|
+
...ledger.receiptFields(),
|
|
13714
|
+
// costYuan(null=无价)/toolCostYuan/auxCostYuan/subCostYuan(口径与舍入在 runLedger)
|
|
13342
13715
|
durationMs: Math.round(performance.now() - startTime),
|
|
13343
13716
|
budgetTruncated: budgetTruncated || void 0
|
|
13344
13717
|
// 预算截断标注(E2;undefined 自动省略)
|
|
@@ -13353,12 +13726,13 @@ async function* runAgent(message, options) {
|
|
|
13353
13726
|
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);
|
|
13354
13727
|
}
|
|
13355
13728
|
try {
|
|
13356
|
-
await updateCalibration(sessionId, { calibRatio, lastRealPromptTokens, lastCachedTokens });
|
|
13729
|
+
await updateCalibration(sessionId, effectiveModel, { calibRatio, lastRealPromptTokens, lastCachedTokens });
|
|
13357
13730
|
} catch (e) {
|
|
13358
13731
|
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);
|
|
13359
13732
|
}
|
|
13360
13733
|
}
|
|
13361
13734
|
}
|
|
13735
|
+
var CALIB_EMA_KEEP, CALIB_EMA_NEW, resolveFirstPrompt;
|
|
13362
13736
|
var init_runAgent = __esm({
|
|
13363
13737
|
"../core/src/agent/runAgent.ts"() {
|
|
13364
13738
|
"use strict";
|
|
@@ -13375,9 +13749,23 @@ var init_runAgent = __esm({
|
|
|
13375
13749
|
init_toolScheduling();
|
|
13376
13750
|
init_streamInference();
|
|
13377
13751
|
init_agentNudges();
|
|
13752
|
+
init_runLedger();
|
|
13378
13753
|
init_usageLog();
|
|
13379
|
-
init_prices();
|
|
13380
13754
|
init_createModel();
|
|
13755
|
+
CALIB_EMA_KEEP = 0.6;
|
|
13756
|
+
CALIB_EMA_NEW = 0.4;
|
|
13757
|
+
resolveFirstPrompt = (message, options) => {
|
|
13758
|
+
if ((options.depth ?? 0) !== 0) return "";
|
|
13759
|
+
const explicit = options.currentUserText?.trim();
|
|
13760
|
+
if (explicit) return explicit;
|
|
13761
|
+
const arr = message;
|
|
13762
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
13763
|
+
if (arr[i]?.role !== "user") continue;
|
|
13764
|
+
const t = msgText(arr[i]?.content);
|
|
13765
|
+
if (t.trim()) return t;
|
|
13766
|
+
}
|
|
13767
|
+
return "";
|
|
13768
|
+
};
|
|
13381
13769
|
}
|
|
13382
13770
|
});
|
|
13383
13771
|
|
|
@@ -13435,11 +13823,12 @@ var init_lineage = __esm({
|
|
|
13435
13823
|
});
|
|
13436
13824
|
|
|
13437
13825
|
// ../core/src/agent/subagent.ts
|
|
13438
|
-
var SUBAGENT_DENYLIST, activeSubagents, failText, RESUME_NOTE, createProgressThrottler, runSubagent;
|
|
13826
|
+
var SUBAGENT_DENYLIST, filterToolsForSubagent, activeSubagents, failText, RESUME_NOTE, createProgressThrottler, runSubagent;
|
|
13439
13827
|
var init_subagent = __esm({
|
|
13440
13828
|
"../core/src/agent/subagent.ts"() {
|
|
13441
13829
|
"use strict";
|
|
13442
13830
|
init_runAgent();
|
|
13831
|
+
init_config();
|
|
13443
13832
|
init_content();
|
|
13444
13833
|
init_transcript();
|
|
13445
13834
|
init_common();
|
|
@@ -13447,7 +13836,14 @@ var init_subagent = __esm({
|
|
|
13447
13836
|
init_registry4();
|
|
13448
13837
|
init_hooks();
|
|
13449
13838
|
init_lineage();
|
|
13450
|
-
SUBAGENT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path"]);
|
|
13839
|
+
SUBAGENT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path", "run_in_background"]);
|
|
13840
|
+
filterToolsForSubagent = (allTools, manifest) => {
|
|
13841
|
+
if (manifest && manifest.tools.length > 0) {
|
|
13842
|
+
const wantsMcp = manifest.tools.some((t) => t === "mcp_call" || t === "mcp_list_tools" || t.startsWith("mcp__"));
|
|
13843
|
+
return allTools.filter((t) => manifest.tools.includes(t.function.name) || wantsMcp && (t.function.name === "mcp_call" || t.function.name === "mcp_list_tools"));
|
|
13844
|
+
}
|
|
13845
|
+
return allTools.filter((t) => t.function.subagentAllowed !== false && !SUBAGENT_DENYLIST.has(t.function.name));
|
|
13846
|
+
};
|
|
13451
13847
|
activeSubagents = /* @__PURE__ */ new Set();
|
|
13452
13848
|
failText = (msg) => toolFailure(msg).content;
|
|
13453
13849
|
RESUME_NOTE = [
|
|
@@ -13551,8 +13947,7 @@ var init_subagent = __esm({
|
|
|
13551
13947
|
...resuming ? [RESUME_NOTE] : []
|
|
13552
13948
|
].filter(Boolean).join("\n\n");
|
|
13553
13949
|
const allTools = getGlobalTools();
|
|
13554
|
-
const
|
|
13555
|
-
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));
|
|
13950
|
+
const toolSchemas = filterToolsForSubagent(allTools, manifest);
|
|
13556
13951
|
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)}..."`);
|
|
13557
13952
|
activeSubagents.add(subSessionId);
|
|
13558
13953
|
ctx.onUIEvent?.({ type: "subagent.count", running: activeSubagents.size, phase: "start", name: manifest?.name, depth: ctx.depth + 1 });
|
|
@@ -13567,7 +13962,9 @@ var init_subagent = __esm({
|
|
|
13567
13962
|
// ★ 透传父级工作目录,子 agent 的 hook/工具相对路径与父级一致
|
|
13568
13963
|
toolSchemas,
|
|
13569
13964
|
abortSignal: ctx.abortSignal,
|
|
13570
|
-
|
|
13965
|
+
// ★ P1-4:manifest.model 覆盖时窗口按该模型解析(MODEL_WINDOWS 表 / 全局回退);
|
|
13966
|
+
// 未覆盖(与父同模型)直接继承父窗口,行为不变。
|
|
13967
|
+
modelWindow: manifest?.model ? resolveModelWindow(manifest.model) : ctx.modelWindow,
|
|
13571
13968
|
depth: ctx.depth + 1,
|
|
13572
13969
|
keepRecentUnits: ctx.keepRecentUnits,
|
|
13573
13970
|
compactRatio: ctx.compactRatio,
|
|
@@ -13585,13 +13982,15 @@ var init_subagent = __esm({
|
|
|
13585
13982
|
// ★ 子 agent final 是交付父级的汇报,EARLY_FINAL 误推一轮纯浪费;父可经续跑纠错
|
|
13586
13983
|
model: manifest?.model,
|
|
13587
13984
|
// ★ per-agent 模型覆盖;undefined 时 model.ts 回退全局 MODEL_NAME
|
|
13588
|
-
costSink: ctx.reportCost
|
|
13985
|
+
costSink: ctx.reportCost,
|
|
13589
13986
|
// ★ 子 run 成本回灌父累计器(成本信任链第二环 E1):父预算自动继承约束
|
|
13987
|
+
budgetCheck: ctx.budgetCheck
|
|
13988
|
+
// ★ P1-5 预算熔断链式透传:子 runAgent 轮边界探测父预算,超支即收尾(不等烧完)
|
|
13590
13989
|
};
|
|
13591
13990
|
const startCtx = { sessionId: subSessionId, parentSessionId: ctx.sessionId, task, depth: ctx.depth + 1, name: manifest?.name, cwd: ctx.cwd };
|
|
13592
13991
|
await dispatch("SubagentStart", startCtx);
|
|
13593
13992
|
let result;
|
|
13594
|
-
const progress = createProgressThrottler((line) => ctx.onUIEvent?.({ type: "subagent.progress", text: line }));
|
|
13993
|
+
const progress = createProgressThrottler((line) => ctx.onUIEvent?.({ type: "subagent.progress", text: line, agentKey: subSessionId, name: manifest?.name }));
|
|
13595
13994
|
try {
|
|
13596
13995
|
for await (const e of runAgent(subMessages, subOptions)) {
|
|
13597
13996
|
if (ctx.abortSignal?.aborted) {
|
|
@@ -13615,7 +14014,7 @@ var init_subagent = __esm({
|
|
|
13615
14014
|
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 };
|
|
13616
14015
|
} finally {
|
|
13617
14016
|
try {
|
|
13618
|
-
ctx.onUIEvent?.({ type: "subagent.progress", text: "", done: true });
|
|
14017
|
+
ctx.onUIEvent?.({ type: "subagent.progress", text: "", done: true, agentKey: subSessionId });
|
|
13619
14018
|
} catch {
|
|
13620
14019
|
}
|
|
13621
14020
|
if (result) {
|
|
@@ -14861,14 +15260,15 @@ var createWebRequestApproval;
|
|
|
14861
15260
|
var init_webHost = __esm({
|
|
14862
15261
|
"../core/src/host/webHost.ts"() {
|
|
14863
15262
|
"use strict";
|
|
14864
|
-
createWebRequestApproval = (onUIEvent, abortSignal) => {
|
|
15263
|
+
createWebRequestApproval = (onUIEvent, abortSignal, opts) => {
|
|
14865
15264
|
return async (detail, meta) => {
|
|
14866
15265
|
onUIEvent?.({
|
|
14867
15266
|
type: "approval_request",
|
|
14868
15267
|
sessionId: meta.sessionId,
|
|
14869
15268
|
toolsId: meta.toolCallId,
|
|
14870
15269
|
toolName: meta.toolName,
|
|
14871
|
-
detail
|
|
15270
|
+
detail,
|
|
15271
|
+
...opts?.includeFullDetail && meta.fullDetail ? { fullDetail: meta.fullDetail } : {}
|
|
14872
15272
|
});
|
|
14873
15273
|
const { waitForUserApproval: waitForUserApproval2 } = await Promise.resolve().then(() => (init_approvalGate(), approvalGate_exports));
|
|
14874
15274
|
return waitForUserApproval2(meta.sessionId, meta.toolCallId, abortSignal);
|
|
@@ -15012,7 +15412,8 @@ ${envLines.join("\n")}`;
|
|
|
15012
15412
|
// 宿主可按需裁剪(挂机批处理);缺省全量,行为不变
|
|
15013
15413
|
abortSignal,
|
|
15014
15414
|
// ← 透传中止信号
|
|
15015
|
-
modelWindow:
|
|
15415
|
+
modelWindow: resolveModelWindow(opts?.model),
|
|
15416
|
+
// ★ P1-4:窗口按本轮模型解析(MODEL_WINDOWS 表 / 全局回退)
|
|
15016
15417
|
keepRecentUnits: appConfig.KEEP_RECENT_UNITS,
|
|
15017
15418
|
compactRatio: appConfig.COMPACT_RATIO,
|
|
15018
15419
|
parentSystemPrompt: sysPrompt,
|
|
@@ -15031,8 +15432,11 @@ ${envLines.join("\n")}`;
|
|
|
15031
15432
|
thinkingLevel: opts?.thinkingLevel,
|
|
15032
15433
|
locale: opts?.locale,
|
|
15033
15434
|
outputStyle: opts?.outputStyle,
|
|
15034
|
-
budgetYuan: opts?.budgetYuan
|
|
15435
|
+
budgetYuan: opts?.budgetYuan,
|
|
15035
15436
|
// ★ 预算制运行(成本信任链第二环 E1):主 run 传入,子 run 经 costSink 回灌
|
|
15437
|
+
// ★ P0-1:本轮用户输入原文(inbound.content 已过 slash 展开与 hook 注入定型),供 firstPrompt
|
|
15438
|
+
// 判定取「当前轮」而非消息数组首条(首条复杂 + 本轮寒暄曾误触发 PLAN_FIRST/EARLY_FINAL)。
|
|
15439
|
+
currentUserText: inbound.content
|
|
15036
15440
|
};
|
|
15037
15441
|
await runWithSessionContext(sessionId, async () => {
|
|
15038
15442
|
try {
|
|
@@ -15189,76 +15593,216 @@ var init_fork = __esm({
|
|
|
15189
15593
|
if (prefix.unclosedRunId) {
|
|
15190
15594
|
await appendEvent(newId, { dscEvent: "run.abandoned", runId: prefix.unclosedRunId, reason: "forked from interrupted run" });
|
|
15191
15595
|
}
|
|
15192
|
-
return { sessionId: newId, copiedLines: prefix.lines.length, archivedMessageCount };
|
|
15596
|
+
return { sessionId: newId, copiedLines: prefix.lines.length, archivedMessageCount };
|
|
15597
|
+
};
|
|
15598
|
+
}
|
|
15599
|
+
});
|
|
15600
|
+
|
|
15601
|
+
// ../core/src/commands/bashDirect.ts
|
|
15602
|
+
import { exec } from "node:child_process";
|
|
15603
|
+
var OUTPUT_MAX_CHARS, timeoutMs, truncateOutput, decodeBuffer, runBashDirect, formatBashEntry, recordBashEntry;
|
|
15604
|
+
var init_bashDirect = __esm({
|
|
15605
|
+
"../core/src/commands/bashDirect.ts"() {
|
|
15606
|
+
"use strict";
|
|
15607
|
+
init_transcript();
|
|
15608
|
+
init_guard();
|
|
15609
|
+
OUTPUT_MAX_CHARS = 4e3;
|
|
15610
|
+
timeoutMs = () => {
|
|
15611
|
+
const n = Number(process.env.DEEP_SEEK_BANG_TIMEOUT_MS);
|
|
15612
|
+
return Number.isFinite(n) && n > 0 ? n : 6e4;
|
|
15613
|
+
};
|
|
15614
|
+
truncateOutput = (s) => {
|
|
15615
|
+
if (s.length <= OUTPUT_MAX_CHARS) return s;
|
|
15616
|
+
const half = Math.floor(OUTPUT_MAX_CHARS / 2);
|
|
15617
|
+
return `${s.slice(0, half)}
|
|
15618
|
+
\u2026\uFF08\u4E2D\u6BB5\u7701\u7565 ${s.length - OUTPUT_MAX_CHARS} \u5B57\u7B26\uFF09\u2026
|
|
15619
|
+
${s.slice(-half)}`;
|
|
15620
|
+
};
|
|
15621
|
+
decodeBuffer = (buf) => {
|
|
15622
|
+
const utf8 = buf.toString("utf8");
|
|
15623
|
+
if (!utf8.includes("\uFFFD")) return utf8;
|
|
15624
|
+
try {
|
|
15625
|
+
return new TextDecoder("gbk").decode(buf);
|
|
15626
|
+
} catch {
|
|
15627
|
+
return utf8;
|
|
15628
|
+
}
|
|
15629
|
+
};
|
|
15630
|
+
runBashDirect = (cmd, cwd, signal) => new Promise((resolve) => {
|
|
15631
|
+
let settled = false;
|
|
15632
|
+
const finish = (r) => {
|
|
15633
|
+
if (!settled) {
|
|
15634
|
+
settled = true;
|
|
15635
|
+
resolve(r);
|
|
15636
|
+
}
|
|
15637
|
+
};
|
|
15638
|
+
const start = Date.now();
|
|
15639
|
+
const child = exec(cmd, { cwd, timeout: timeoutMs(), maxBuffer: 4 * 1024 * 1024, windowsHide: true, env: scrubCommandEnv(), encoding: "buffer" }, (err, stdout, stderr) => {
|
|
15640
|
+
const durationMs = Date.now() - start;
|
|
15641
|
+
const out = stdout;
|
|
15642
|
+
const errBuf = stderr;
|
|
15643
|
+
const timedOut = err != null && err.killed === true && signal?.aborted !== true;
|
|
15644
|
+
const combined = errBuf.length ? (out.length ? `${decodeBuffer(out)}
|
|
15645
|
+
` : "") + decodeBuffer(errBuf) : decodeBuffer(out);
|
|
15646
|
+
const exitCode = err == null ? 0 : typeof err.code === "number" ? err.code : null;
|
|
15647
|
+
finish({ ok: err == null, output: truncateOutput(combined ?? ""), exitCode, timedOut, durationMs });
|
|
15648
|
+
});
|
|
15649
|
+
if (signal) {
|
|
15650
|
+
const onAbort = () => {
|
|
15651
|
+
try {
|
|
15652
|
+
child.kill();
|
|
15653
|
+
} catch {
|
|
15654
|
+
}
|
|
15655
|
+
};
|
|
15656
|
+
if (signal.aborted) onAbort();
|
|
15657
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
15658
|
+
}
|
|
15659
|
+
});
|
|
15660
|
+
formatBashEntry = (cmd, r) => `!bash $ ${cmd}
|
|
15661
|
+
` + (r.timedOut ? `[\u8D85\u65F6\u4E2D\u6B62\uFF08>${Math.round(r.durationMs / 1e3)}s\uFF09]
|
|
15662
|
+
` : "") + (r.exitCode != null && r.exitCode !== 0 ? `[\u9000\u51FA\u7801 ${r.exitCode}]
|
|
15663
|
+
` : "") + (r.output.trim() || "(\u65E0\u8F93\u51FA)");
|
|
15664
|
+
recordBashEntry = async (sessionId, text) => {
|
|
15665
|
+
await appendMessage({ sessionId, role: "user", content: text });
|
|
15666
|
+
};
|
|
15667
|
+
}
|
|
15668
|
+
});
|
|
15669
|
+
|
|
15670
|
+
// src/lineDiff.ts
|
|
15671
|
+
var splitLines, lineDiff, collapseContext;
|
|
15672
|
+
var init_lineDiff = __esm({
|
|
15673
|
+
"src/lineDiff.ts"() {
|
|
15674
|
+
"use strict";
|
|
15675
|
+
splitLines = (s) => {
|
|
15676
|
+
const t = String(s ?? "").replace(/\r\n/g, "\n");
|
|
15677
|
+
return t === "" ? [] : t.split("\n");
|
|
15678
|
+
};
|
|
15679
|
+
lineDiff = (oldStr, newStr) => {
|
|
15680
|
+
const a = splitLines(oldStr);
|
|
15681
|
+
const b = splitLines(newStr);
|
|
15682
|
+
let s = 0;
|
|
15683
|
+
while (s < a.length && s < b.length && a[s] === b[s]) s++;
|
|
15684
|
+
let e = 0;
|
|
15685
|
+
while (e < a.length - s && e < b.length - s && a[a.length - 1 - e] === b[b.length - 1 - e]) e++;
|
|
15686
|
+
const m1 = a.slice(s, a.length - e);
|
|
15687
|
+
const m2 = b.slice(s, b.length - e);
|
|
15688
|
+
const rows = [];
|
|
15689
|
+
for (let i = 0; i < s; i++) rows.push({ t: "ctx", s: a[i] });
|
|
15690
|
+
const n = m1.length, m = m2.length;
|
|
15691
|
+
if (n === 0 && m === 0) {
|
|
15692
|
+
} else if (n * m > 4e6) {
|
|
15693
|
+
for (const l of m1) rows.push({ t: "del", s: l });
|
|
15694
|
+
for (const l of m2) rows.push({ t: "add", s: l });
|
|
15695
|
+
} else {
|
|
15696
|
+
const w = m + 1;
|
|
15697
|
+
const dp = new Uint32Array((n + 1) * w);
|
|
15698
|
+
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
15699
|
+
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
15700
|
+
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]);
|
|
15701
|
+
}
|
|
15702
|
+
}
|
|
15703
|
+
let i = 0, j = 0;
|
|
15704
|
+
while (i < n && j < m) {
|
|
15705
|
+
if (m1[i] === m2[j]) {
|
|
15706
|
+
rows.push({ t: "ctx", s: m1[i] });
|
|
15707
|
+
i++;
|
|
15708
|
+
j++;
|
|
15709
|
+
} else if (dp[(i + 1) * w + j] >= dp[i * w + j + 1]) {
|
|
15710
|
+
rows.push({ t: "del", s: m1[i] });
|
|
15711
|
+
i++;
|
|
15712
|
+
} else {
|
|
15713
|
+
rows.push({ t: "add", s: m2[j] });
|
|
15714
|
+
j++;
|
|
15715
|
+
}
|
|
15716
|
+
}
|
|
15717
|
+
while (i < n) {
|
|
15718
|
+
rows.push({ t: "del", s: m1[i] });
|
|
15719
|
+
i++;
|
|
15720
|
+
}
|
|
15721
|
+
while (j < m) {
|
|
15722
|
+
rows.push({ t: "add", s: m2[j] });
|
|
15723
|
+
j++;
|
|
15724
|
+
}
|
|
15725
|
+
}
|
|
15726
|
+
for (let k = 0; k < e; k++) rows.push({ t: "ctx", s: a[a.length - e + k] });
|
|
15727
|
+
return rows;
|
|
15728
|
+
};
|
|
15729
|
+
collapseContext = (rows, keep = 2) => {
|
|
15730
|
+
const out = [];
|
|
15731
|
+
let i = 0;
|
|
15732
|
+
while (i < rows.length) {
|
|
15733
|
+
if (rows[i].t !== "ctx") {
|
|
15734
|
+
out.push(rows[i]);
|
|
15735
|
+
i++;
|
|
15736
|
+
continue;
|
|
15737
|
+
}
|
|
15738
|
+
let j = i;
|
|
15739
|
+
while (j < rows.length && rows[j].t === "ctx") j++;
|
|
15740
|
+
const run = j - i;
|
|
15741
|
+
if (run > keep * 2 + 1) {
|
|
15742
|
+
for (let k = 0; k < keep; k++) out.push(rows[i + k]);
|
|
15743
|
+
out.push({ t: "ellip", n: run - keep * 2 });
|
|
15744
|
+
for (let k = run - keep; k < run; k++) out.push(rows[i + k]);
|
|
15745
|
+
} else {
|
|
15746
|
+
for (let k = 0; k < run; k++) out.push(rows[i + k]);
|
|
15747
|
+
}
|
|
15748
|
+
i = j;
|
|
15749
|
+
}
|
|
15750
|
+
return out;
|
|
15193
15751
|
};
|
|
15194
15752
|
}
|
|
15195
15753
|
});
|
|
15196
15754
|
|
|
15197
|
-
//
|
|
15198
|
-
import
|
|
15199
|
-
|
|
15200
|
-
var
|
|
15201
|
-
|
|
15755
|
+
// src/diffSummary.ts
|
|
15756
|
+
import fs48 from "fs/promises";
|
|
15757
|
+
import path49 from "path";
|
|
15758
|
+
var readTextIfExists, buildRunDiff;
|
|
15759
|
+
var init_diffSummary = __esm({
|
|
15760
|
+
"src/diffSummary.ts"() {
|
|
15202
15761
|
"use strict";
|
|
15203
|
-
|
|
15204
|
-
|
|
15205
|
-
|
|
15206
|
-
timeoutMs = () => {
|
|
15207
|
-
const n = Number(process.env.DEEP_SEEK_BANG_TIMEOUT_MS);
|
|
15208
|
-
return Number.isFinite(n) && n > 0 ? n : 6e4;
|
|
15209
|
-
};
|
|
15210
|
-
truncateOutput = (s) => {
|
|
15211
|
-
if (s.length <= OUTPUT_MAX_CHARS) return s;
|
|
15212
|
-
const half = Math.floor(OUTPUT_MAX_CHARS / 2);
|
|
15213
|
-
return `${s.slice(0, half)}
|
|
15214
|
-
\u2026\uFF08\u4E2D\u6BB5\u7701\u7565 ${s.length - OUTPUT_MAX_CHARS} \u5B57\u7B26\uFF09\u2026
|
|
15215
|
-
${s.slice(-half)}`;
|
|
15216
|
-
};
|
|
15217
|
-
decodeBuffer = (buf) => {
|
|
15218
|
-
const utf8 = buf.toString("utf8");
|
|
15219
|
-
if (!utf8.includes("\uFFFD")) return utf8;
|
|
15762
|
+
init_lineDiff();
|
|
15763
|
+
init_store2();
|
|
15764
|
+
readTextIfExists = async (p) => {
|
|
15220
15765
|
try {
|
|
15221
|
-
return
|
|
15766
|
+
return await fs48.readFile(p, "utf-8");
|
|
15222
15767
|
} catch {
|
|
15223
|
-
return
|
|
15768
|
+
return null;
|
|
15224
15769
|
}
|
|
15225
15770
|
};
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
const
|
|
15229
|
-
if (
|
|
15230
|
-
|
|
15231
|
-
|
|
15771
|
+
buildRunDiff = async (sessionId, records) => {
|
|
15772
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
15773
|
+
for (const r of records) {
|
|
15774
|
+
if (r.restored) continue;
|
|
15775
|
+
const prev = byPath.get(r.relativePath);
|
|
15776
|
+
if (!prev || (r.timestamp || "") < (prev.timestamp || "")) byPath.set(r.relativePath, r);
|
|
15777
|
+
}
|
|
15778
|
+
const root = process.env.WORKSPACE_ROOT ?? process.cwd();
|
|
15779
|
+
const out = [];
|
|
15780
|
+
for (const r of [...byPath.values()].sort((a, b) => a.relativePath.localeCompare(b.relativePath))) {
|
|
15781
|
+
const abs = path49.join(root, ...r.relativePath.split("/"));
|
|
15782
|
+
if (r.backupKind === "directory_tree") {
|
|
15783
|
+
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 });
|
|
15784
|
+
continue;
|
|
15232
15785
|
}
|
|
15233
|
-
|
|
15234
|
-
|
|
15235
|
-
|
|
15236
|
-
|
|
15237
|
-
|
|
15238
|
-
|
|
15239
|
-
|
|
15240
|
-
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15246
|
-
|
|
15247
|
-
|
|
15248
|
-
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
};
|
|
15252
|
-
if (signal.aborted) onAbort();
|
|
15253
|
-
else signal.addEventListener("abort", onAbort, { once: true });
|
|
15786
|
+
const disk = await readTextIfExists(abs);
|
|
15787
|
+
if (disk === null) {
|
|
15788
|
+
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 });
|
|
15789
|
+
continue;
|
|
15790
|
+
}
|
|
15791
|
+
let before;
|
|
15792
|
+
if (r.backupKind === "creation_marker") {
|
|
15793
|
+
before = "";
|
|
15794
|
+
} else {
|
|
15795
|
+
before = await readTextIfExists(path49.join(getUndoBackupRoot(sessionId), r.undoId, "content"));
|
|
15796
|
+
}
|
|
15797
|
+
if (before === null) {
|
|
15798
|
+
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 });
|
|
15799
|
+
continue;
|
|
15800
|
+
}
|
|
15801
|
+
const rows = collapseContext(lineDiff(before, disk));
|
|
15802
|
+
if (!rows.some((c) => c.t === "add" || c.t === "del")) continue;
|
|
15803
|
+
out.push({ path: r.relativePath, rows });
|
|
15254
15804
|
}
|
|
15255
|
-
|
|
15256
|
-
formatBashEntry = (cmd, r) => `!bash $ ${cmd}
|
|
15257
|
-
` + (r.timedOut ? `[\u8D85\u65F6\u4E2D\u6B62\uFF08>${Math.round(r.durationMs / 1e3)}s\uFF09]
|
|
15258
|
-
` : "") + (r.exitCode != null && r.exitCode !== 0 ? `[\u9000\u51FA\u7801 ${r.exitCode}]
|
|
15259
|
-
` : "") + (r.output.trim() || "(\u65E0\u8F93\u51FA)");
|
|
15260
|
-
recordBashEntry = async (sessionId, text) => {
|
|
15261
|
-
await appendMessage({ sessionId, role: "user", content: text });
|
|
15805
|
+
return out;
|
|
15262
15806
|
};
|
|
15263
15807
|
}
|
|
15264
15808
|
});
|
|
@@ -15269,7 +15813,7 @@ var init_cliHost = __esm({
|
|
|
15269
15813
|
"src/cliHost.ts"() {
|
|
15270
15814
|
"use strict";
|
|
15271
15815
|
createCliRequestApproval = (askApproval) => {
|
|
15272
|
-
return async (detail, meta) => askApproval(detail, meta.toolName);
|
|
15816
|
+
return async (detail, meta) => askApproval(detail, meta.toolName, meta.fullDetail);
|
|
15273
15817
|
};
|
|
15274
15818
|
createCliRequestQuestion = (askQuestion) => {
|
|
15275
15819
|
return async (req) => askQuestion(req);
|
|
@@ -15461,6 +16005,9 @@ var init_useChatState = __esm({
|
|
|
15461
16005
|
init_guard();
|
|
15462
16006
|
init_createModel();
|
|
15463
16007
|
init_tool();
|
|
16008
|
+
init_permissions();
|
|
16009
|
+
init_restore();
|
|
16010
|
+
init_diffSummary();
|
|
15464
16011
|
init_cliHost();
|
|
15465
16012
|
init_prefs();
|
|
15466
16013
|
init_strings();
|
|
@@ -15486,6 +16033,7 @@ var init_useChatState = __esm({
|
|
|
15486
16033
|
const [pendingSessions, setPendingSessions] = useState(null);
|
|
15487
16034
|
const [pendingFork, setPendingFork] = useState(null);
|
|
15488
16035
|
const [pendingModel, setPendingModel] = useState(null);
|
|
16036
|
+
const [pendingRewind, setPendingRewind] = useState(null);
|
|
15489
16037
|
const nextId = useRef(1);
|
|
15490
16038
|
const assistantStreamingId = useRef(null);
|
|
15491
16039
|
const thinkingStreamingId = useRef(null);
|
|
@@ -15513,6 +16061,7 @@ var init_useChatState = __esm({
|
|
|
15513
16061
|
const budgetYuanRef = useRef(void 0);
|
|
15514
16062
|
const lastRunCacheHitRef = useRef(void 0);
|
|
15515
16063
|
const contextUsageRef = useRef(void 0);
|
|
16064
|
+
const lastSubmitAtRef = useRef(0);
|
|
15516
16065
|
const compactProgressIdRef = useRef(null);
|
|
15517
16066
|
const dropCompactProgressRow = useCallback(() => {
|
|
15518
16067
|
const pid = compactProgressIdRef.current;
|
|
@@ -15520,12 +16069,20 @@ var init_useChatState = __esm({
|
|
|
15520
16069
|
compactProgressIdRef.current = null;
|
|
15521
16070
|
setRows((prev) => prev.filter((r) => r.id !== pid));
|
|
15522
16071
|
}, []);
|
|
15523
|
-
const
|
|
15524
|
-
const dropSubagentProgressRow = useCallback(() => {
|
|
15525
|
-
const
|
|
15526
|
-
if (
|
|
15527
|
-
|
|
15528
|
-
|
|
16072
|
+
const subagentProgressRowsRef = useRef(/* @__PURE__ */ new Map());
|
|
16073
|
+
const dropSubagentProgressRow = useCallback((key) => {
|
|
16074
|
+
const m = subagentProgressRowsRef.current;
|
|
16075
|
+
if (key) {
|
|
16076
|
+
const rid = m.get(key);
|
|
16077
|
+
if (rid == null) return;
|
|
16078
|
+
m.delete(key);
|
|
16079
|
+
setRows((prev) => prev.filter((r) => r.id !== rid));
|
|
16080
|
+
return;
|
|
16081
|
+
}
|
|
16082
|
+
if (m.size === 0) return;
|
|
16083
|
+
const ids = new Set(m.values());
|
|
16084
|
+
m.clear();
|
|
16085
|
+
setRows((prev) => prev.filter((r) => !ids.has(r.id)));
|
|
15529
16086
|
}, []);
|
|
15530
16087
|
const outputStyleRef = useRef(void 0);
|
|
15531
16088
|
const lastUsageRef = useRef(null);
|
|
@@ -15631,6 +16188,24 @@ var init_useChatState = __esm({
|
|
|
15631
16188
|
scheduleFlush();
|
|
15632
16189
|
break;
|
|
15633
16190
|
}
|
|
16191
|
+
case "text.reset": {
|
|
16192
|
+
if (flushTimer.current) {
|
|
16193
|
+
clearTimeout(flushTimer.current);
|
|
16194
|
+
flushTimer.current = null;
|
|
16195
|
+
}
|
|
16196
|
+
textBuffer.current = "";
|
|
16197
|
+
thinkingBuffer.current = "";
|
|
16198
|
+
const raId = assistantStreamingId.current;
|
|
16199
|
+
const rthId = thinkingStreamingId.current;
|
|
16200
|
+
if (raId != null || rthId != null) {
|
|
16201
|
+
setRows((prev) => prev.map((row) => {
|
|
16202
|
+
if (row.id === raId && row.kind === "assistant") return { ...row, text: "" };
|
|
16203
|
+
if (row.id === rthId && row.kind === "thinking") return { ...row, text: "" };
|
|
16204
|
+
return row;
|
|
16205
|
+
}));
|
|
16206
|
+
}
|
|
16207
|
+
break;
|
|
16208
|
+
}
|
|
15634
16209
|
case "tool.start": {
|
|
15635
16210
|
closeStreaming();
|
|
15636
16211
|
const id = newRowId();
|
|
@@ -15700,16 +16275,19 @@ var init_useChatState = __esm({
|
|
|
15700
16275
|
const durText = durMs >= 6e4 ? `${Math.floor(durMs / 6e4)}m${Math.round(durMs % 6e4 / 1e3)}s` : `${(durMs / 1e3).toFixed(1)}s`;
|
|
15701
16276
|
const cost = obj.costYuan;
|
|
15702
16277
|
const toolCost = typeof obj.toolCostYuan === "number" && obj.toolCostYuan > 0 ? obj.toolCostYuan : null;
|
|
16278
|
+
const auxCost = typeof obj.auxCostYuan === "number" && obj.auxCostYuan > 0 ? obj.auxCostYuan : null;
|
|
16279
|
+
const subCost = typeof obj.subCostYuan === "number" && obj.subCostYuan > 0 ? obj.subCostYuan : null;
|
|
15703
16280
|
const tokTotal = promptT + completionT;
|
|
15704
16281
|
const tokText = tokTotal >= 1e4 ? `${(tokTotal / 1e3).toFixed(1)}k` : String(tokTotal);
|
|
15705
16282
|
const costText = typeof cost === "number" ? `\xA5${cost.toFixed(cost < 5e-3 ? 4 : 3)}` : `${tokText} tok`;
|
|
15706
16283
|
setRows((prev) => [...prev, {
|
|
15707
16284
|
id: newRowId(),
|
|
15708
16285
|
kind: "meta",
|
|
15709
|
-
text: `\u{1F4B0} \u672C\u6B21 ${rounds} \u8F6E \xB7 ${costText}${toolCost != null ? ` \xB7 \u8054\u7F51 \xA5${toolCost.toFixed(3)}` : ""} \xB7 \u7F13\u5B58\u547D\u4E2D ${hitPct}% \xB7 ${durText}${obj.budgetTruncated ? " \xB7 \u9884\u7B97\u622A\u65AD" : ""}`
|
|
16286
|
+
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" : ""}`
|
|
15710
16287
|
}]);
|
|
15711
|
-
lastRunCostRef.current = typeof cost === "number" ? cost : void 0;
|
|
16288
|
+
lastRunCostRef.current = typeof cost === "number" ? cost + (auxCost ?? 0) : auxCost != null ? auxCost : void 0;
|
|
15712
16289
|
sessionCostRef.current = typeof cost === "number" ? (sessionCostRef.current ?? 0) + cost : sessionCostRef.current;
|
|
16290
|
+
if (auxCost != null) sessionCostRef.current = (sessionCostRef.current ?? 0) + auxCost;
|
|
15713
16291
|
lastRunCacheHitRef.current = typeof obj.cacheHitRate === "number" ? obj.cacheHitRate : void 0;
|
|
15714
16292
|
break;
|
|
15715
16293
|
}
|
|
@@ -15738,19 +16316,23 @@ var init_useChatState = __esm({
|
|
|
15738
16316
|
}
|
|
15739
16317
|
case "subagent.progress": {
|
|
15740
16318
|
flush();
|
|
16319
|
+
const agentKey = typeof obj.agentKey === "string" && obj.agentKey ? obj.agentKey : "__default__";
|
|
15741
16320
|
if (obj.done) {
|
|
15742
|
-
dropSubagentProgressRow();
|
|
16321
|
+
dropSubagentProgressRow(agentKey);
|
|
15743
16322
|
break;
|
|
15744
16323
|
}
|
|
15745
16324
|
const ptext = String(obj.text ?? "").slice(0, 160);
|
|
15746
16325
|
if (!ptext) break;
|
|
15747
|
-
|
|
16326
|
+
const agentName = typeof obj.name === "string" && obj.name ? `\u27E8${obj.name}\u27E9 ` : "";
|
|
16327
|
+
const pline = `${agentName}${ptext}`;
|
|
16328
|
+
const rowMap = subagentProgressRowsRef.current;
|
|
16329
|
+
const existingId = rowMap.get(agentKey);
|
|
16330
|
+
if (existingId == null) {
|
|
15748
16331
|
const id = newRowId();
|
|
15749
|
-
|
|
15750
|
-
setRows((prev) => [...prev, { id, kind: "subagent-progress", text:
|
|
16332
|
+
rowMap.set(agentKey, id);
|
|
16333
|
+
setRows((prev) => [...prev, { id, kind: "subagent-progress", text: pline }]);
|
|
15751
16334
|
} else {
|
|
15752
|
-
|
|
15753
|
-
setRows((prev) => prev.map((r) => r.id === pid && r.kind === "subagent-progress" ? { ...r, text: ptext } : r));
|
|
16335
|
+
setRows((prev) => prev.map((r) => r.id === existingId && r.kind === "subagent-progress" ? { ...r, text: pline } : r));
|
|
15754
16336
|
}
|
|
15755
16337
|
break;
|
|
15756
16338
|
}
|
|
@@ -15830,7 +16412,7 @@ var init_useChatState = __esm({
|
|
|
15830
16412
|
break;
|
|
15831
16413
|
}
|
|
15832
16414
|
}, [closeStreaming, flush, scheduleFlush, pushInfo]);
|
|
15833
|
-
const askApproval = useCallback((detail, toolName) => new Promise((resolve) => setPendingApproval({ detail, toolName, resolve })), []);
|
|
16415
|
+
const askApproval = useCallback((detail, toolName, fullDetail) => new Promise((resolve) => setPendingApproval({ detail, toolName, fullDetail, resolve })), []);
|
|
15834
16416
|
const resolveApproval = useCallback((v) => {
|
|
15835
16417
|
setPendingApproval((prev) => {
|
|
15836
16418
|
prev?.resolve(v);
|
|
@@ -15839,7 +16421,7 @@ var init_useChatState = __esm({
|
|
|
15839
16421
|
}, []);
|
|
15840
16422
|
const autoRequestApproval = useCallback(async (_detail, meta) => {
|
|
15841
16423
|
const t = findTool(meta.toolName);
|
|
15842
|
-
if (!t || t.function.safetyLevel === "danger" /* DANGER */) return askApproval(_detail, meta.toolName);
|
|
16424
|
+
if (!t || t.function.safetyLevel === "danger" /* DANGER */) return askApproval(_detail, meta.toolName, meta.fullDetail);
|
|
15843
16425
|
return "allow-once";
|
|
15844
16426
|
}, [askApproval]);
|
|
15845
16427
|
const askQuestion = useCallback((req) => new Promise((resolve) => setPendingQuestion({ req, resolve })), []);
|
|
@@ -15935,9 +16517,12 @@ ${finalPlan}`, false, res.autoExecute);
|
|
|
15935
16517
|
const runPlanStage = useCallback(async (sid, researchPrompt) => {
|
|
15936
16518
|
await runOnce(sid, researchPrompt, true);
|
|
15937
16519
|
const plan = takeProposedPlan();
|
|
15938
|
-
if (plan == null)
|
|
16520
|
+
if (plan == null) {
|
|
16521
|
+
pushInfo(S.planNoProposal);
|
|
16522
|
+
return false;
|
|
16523
|
+
}
|
|
15939
16524
|
return await approveAndImplement(sid, plan);
|
|
15940
|
-
}, [runOnce, takeProposedPlan, approveAndImplement]);
|
|
16525
|
+
}, [runOnce, takeProposedPlan, approveAndImplement, pushInfo]);
|
|
15941
16526
|
const queueInput = useCallback((content) => {
|
|
15942
16527
|
const text = content.trim();
|
|
15943
16528
|
const sid = sessionIdRef.current;
|
|
@@ -15952,6 +16537,7 @@ ${finalPlan}`, false, res.autoExecute);
|
|
|
15952
16537
|
if (!text || busy) return;
|
|
15953
16538
|
setBusy(true);
|
|
15954
16539
|
const myGen = ++turnGenRef.current;
|
|
16540
|
+
lastSubmitAtRef.current = Date.now();
|
|
15955
16541
|
try {
|
|
15956
16542
|
if (sessionIdRef.current == null) {
|
|
15957
16543
|
sessionIdRef.current = await getOrCreateSessionId(initialSessionId ?? void 0);
|
|
@@ -16022,6 +16608,8 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
16022
16608
|
toolRowByCallId.current.clear();
|
|
16023
16609
|
}, [flush]);
|
|
16024
16610
|
const loadSession = useCallback(async (id) => {
|
|
16611
|
+
const prev = sessionIdRef.current;
|
|
16612
|
+
if (prev && prev !== id) clearSessionPermissionRules(prev);
|
|
16025
16613
|
sessionIdRef.current = id;
|
|
16026
16614
|
lastRunCostRef.current = void 0;
|
|
16027
16615
|
sessionCostRef.current = void 0;
|
|
@@ -16052,6 +16640,8 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
16052
16640
|
if (picked) await loadSession(picked);
|
|
16053
16641
|
}, [pushInfo, loadSession]);
|
|
16054
16642
|
const newSession = useCallback(async () => {
|
|
16643
|
+
const prev = sessionIdRef.current;
|
|
16644
|
+
if (prev) clearSessionPermissionRules(prev);
|
|
16055
16645
|
sessionIdRef.current = await getOrCreateSessionId(void 0);
|
|
16056
16646
|
lastRunCostRef.current = void 0;
|
|
16057
16647
|
sessionCostRef.current = void 0;
|
|
@@ -16109,6 +16699,60 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
16109
16699
|
}
|
|
16110
16700
|
return picked;
|
|
16111
16701
|
}, [pushInfo, setModelOverride]);
|
|
16702
|
+
const resolveRewind = useCallback((undoId) => {
|
|
16703
|
+
setPendingRewind((prev) => {
|
|
16704
|
+
prev?.resolve(undoId);
|
|
16705
|
+
return null;
|
|
16706
|
+
});
|
|
16707
|
+
}, []);
|
|
16708
|
+
const openRewindPicker = useCallback(async () => {
|
|
16709
|
+
const sid = sessionIdRef.current;
|
|
16710
|
+
if (!sid) {
|
|
16711
|
+
pushInfo(S.rewindNoSession);
|
|
16712
|
+
return;
|
|
16713
|
+
}
|
|
16714
|
+
let records = [];
|
|
16715
|
+
try {
|
|
16716
|
+
records = await listUndoable(sid);
|
|
16717
|
+
} catch {
|
|
16718
|
+
}
|
|
16719
|
+
if (records.length === 0) {
|
|
16720
|
+
pushInfo(S.rewindEmpty);
|
|
16721
|
+
return;
|
|
16722
|
+
}
|
|
16723
|
+
const picked = await new Promise((resolve) => setPendingRewind({ records, resolve }));
|
|
16724
|
+
if (!picked) return;
|
|
16725
|
+
try {
|
|
16726
|
+
pushInfo(await restoreByUndoId(picked, sid));
|
|
16727
|
+
} catch (e) {
|
|
16728
|
+
pushInfo(S.rewindFail(e?.message ?? String(e)));
|
|
16729
|
+
}
|
|
16730
|
+
}, [pushInfo]);
|
|
16731
|
+
const openDiff = useCallback(async () => {
|
|
16732
|
+
const sid = sessionIdRef.current;
|
|
16733
|
+
const sinceMs = lastSubmitAtRef.current;
|
|
16734
|
+
if (!sid || !sinceMs) {
|
|
16735
|
+
pushInfo(S.diffEmpty);
|
|
16736
|
+
return;
|
|
16737
|
+
}
|
|
16738
|
+
let records = [];
|
|
16739
|
+
try {
|
|
16740
|
+
records = await listUndoable(sid);
|
|
16741
|
+
} catch {
|
|
16742
|
+
}
|
|
16743
|
+
const sinceIso = new Date(sinceMs).toISOString();
|
|
16744
|
+
const runRecords = records.filter((r) => (r.timestamp || "") >= sinceIso);
|
|
16745
|
+
if (runRecords.length === 0) {
|
|
16746
|
+
pushInfo(S.diffEmpty);
|
|
16747
|
+
return;
|
|
16748
|
+
}
|
|
16749
|
+
const files = await buildRunDiff(sid, runRecords);
|
|
16750
|
+
if (files.length === 0) {
|
|
16751
|
+
pushInfo(S.diffEmpty);
|
|
16752
|
+
return;
|
|
16753
|
+
}
|
|
16754
|
+
setRows((prev) => [...prev, { id: newRowId(), kind: "diff-summary", title: S.diffTitle, files }]);
|
|
16755
|
+
}, [pushInfo]);
|
|
16112
16756
|
const setPlanMode = useCallback((on) => {
|
|
16113
16757
|
planModeRef.current = on;
|
|
16114
16758
|
}, []);
|
|
@@ -16147,6 +16791,7 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
16147
16791
|
pendingSessions,
|
|
16148
16792
|
pendingFork,
|
|
16149
16793
|
pendingModel,
|
|
16794
|
+
pendingRewind,
|
|
16150
16795
|
sessionIdRef,
|
|
16151
16796
|
// 动作
|
|
16152
16797
|
submit,
|
|
@@ -16186,7 +16831,10 @@ ${r.output.trim() || S.bangNoOutput}`);
|
|
|
16186
16831
|
resolveFork,
|
|
16187
16832
|
newSession,
|
|
16188
16833
|
openModelPicker,
|
|
16189
|
-
resolveModel
|
|
16834
|
+
resolveModel,
|
|
16835
|
+
openRewindPicker,
|
|
16836
|
+
resolveRewind,
|
|
16837
|
+
openDiff
|
|
16190
16838
|
};
|
|
16191
16839
|
};
|
|
16192
16840
|
}
|
|
@@ -16569,91 +17217,6 @@ var init_ThinkingBlock = __esm({
|
|
|
16569
17217
|
}
|
|
16570
17218
|
});
|
|
16571
17219
|
|
|
16572
|
-
// src/lineDiff.ts
|
|
16573
|
-
var splitLines, lineDiff, collapseContext;
|
|
16574
|
-
var init_lineDiff = __esm({
|
|
16575
|
-
"src/lineDiff.ts"() {
|
|
16576
|
-
"use strict";
|
|
16577
|
-
splitLines = (s) => {
|
|
16578
|
-
const t = String(s ?? "").replace(/\r\n/g, "\n");
|
|
16579
|
-
return t === "" ? [] : t.split("\n");
|
|
16580
|
-
};
|
|
16581
|
-
lineDiff = (oldStr, newStr) => {
|
|
16582
|
-
const a = splitLines(oldStr);
|
|
16583
|
-
const b = splitLines(newStr);
|
|
16584
|
-
let s = 0;
|
|
16585
|
-
while (s < a.length && s < b.length && a[s] === b[s]) s++;
|
|
16586
|
-
let e = 0;
|
|
16587
|
-
while (e < a.length - s && e < b.length - s && a[a.length - 1 - e] === b[b.length - 1 - e]) e++;
|
|
16588
|
-
const m1 = a.slice(s, a.length - e);
|
|
16589
|
-
const m2 = b.slice(s, b.length - e);
|
|
16590
|
-
const rows = [];
|
|
16591
|
-
for (let i = 0; i < s; i++) rows.push({ t: "ctx", s: a[i] });
|
|
16592
|
-
const n = m1.length, m = m2.length;
|
|
16593
|
-
if (n === 0 && m === 0) {
|
|
16594
|
-
} else if (n * m > 4e6) {
|
|
16595
|
-
for (const l of m1) rows.push({ t: "del", s: l });
|
|
16596
|
-
for (const l of m2) rows.push({ t: "add", s: l });
|
|
16597
|
-
} else {
|
|
16598
|
-
const w = m + 1;
|
|
16599
|
-
const dp = new Uint32Array((n + 1) * w);
|
|
16600
|
-
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
16601
|
-
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
16602
|
-
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]);
|
|
16603
|
-
}
|
|
16604
|
-
}
|
|
16605
|
-
let i = 0, j = 0;
|
|
16606
|
-
while (i < n && j < m) {
|
|
16607
|
-
if (m1[i] === m2[j]) {
|
|
16608
|
-
rows.push({ t: "ctx", s: m1[i] });
|
|
16609
|
-
i++;
|
|
16610
|
-
j++;
|
|
16611
|
-
} else if (dp[(i + 1) * w + j] >= dp[i * w + j + 1]) {
|
|
16612
|
-
rows.push({ t: "del", s: m1[i] });
|
|
16613
|
-
i++;
|
|
16614
|
-
} else {
|
|
16615
|
-
rows.push({ t: "add", s: m2[j] });
|
|
16616
|
-
j++;
|
|
16617
|
-
}
|
|
16618
|
-
}
|
|
16619
|
-
while (i < n) {
|
|
16620
|
-
rows.push({ t: "del", s: m1[i] });
|
|
16621
|
-
i++;
|
|
16622
|
-
}
|
|
16623
|
-
while (j < m) {
|
|
16624
|
-
rows.push({ t: "add", s: m2[j] });
|
|
16625
|
-
j++;
|
|
16626
|
-
}
|
|
16627
|
-
}
|
|
16628
|
-
for (let k = 0; k < e; k++) rows.push({ t: "ctx", s: a[a.length - e + k] });
|
|
16629
|
-
return rows;
|
|
16630
|
-
};
|
|
16631
|
-
collapseContext = (rows, keep = 2) => {
|
|
16632
|
-
const out = [];
|
|
16633
|
-
let i = 0;
|
|
16634
|
-
while (i < rows.length) {
|
|
16635
|
-
if (rows[i].t !== "ctx") {
|
|
16636
|
-
out.push(rows[i]);
|
|
16637
|
-
i++;
|
|
16638
|
-
continue;
|
|
16639
|
-
}
|
|
16640
|
-
let j = i;
|
|
16641
|
-
while (j < rows.length && rows[j].t === "ctx") j++;
|
|
16642
|
-
const run = j - i;
|
|
16643
|
-
if (run > keep * 2 + 1) {
|
|
16644
|
-
for (let k = 0; k < keep; k++) out.push(rows[i + k]);
|
|
16645
|
-
out.push({ t: "ellip", n: run - keep * 2 });
|
|
16646
|
-
for (let k = run - keep; k < run; k++) out.push(rows[i + k]);
|
|
16647
|
-
} else {
|
|
16648
|
-
for (let k = 0; k < run; k++) out.push(rows[i + k]);
|
|
16649
|
-
}
|
|
16650
|
-
i = j;
|
|
16651
|
-
}
|
|
16652
|
-
return out;
|
|
16653
|
-
};
|
|
16654
|
-
}
|
|
16655
|
-
});
|
|
16656
|
-
|
|
16657
17220
|
// src/diffView.ts
|
|
16658
17221
|
var EDIT_TOOL_NAMES, extractEditPairs, pairsToDisplayRows, parseApprovalDiff;
|
|
16659
17222
|
var init_diffView = __esm({
|
|
@@ -16812,7 +17375,7 @@ var init_TodosPanel = __esm({
|
|
|
16812
17375
|
// src/components/ApprovalModal.tsx
|
|
16813
17376
|
import { Box as Box5, Text as Text7 } from "ink";
|
|
16814
17377
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
16815
|
-
var OPTIONS, MAX_DIFF_LINES, ApprovalModal;
|
|
17378
|
+
var OPTIONS, MAX_DIFF_LINES, MAX_FULL_LINES, ApprovalModal;
|
|
16816
17379
|
var init_ApprovalModal = __esm({
|
|
16817
17380
|
"src/components/ApprovalModal.tsx"() {
|
|
16818
17381
|
"use strict";
|
|
@@ -16822,14 +17385,20 @@ var init_ApprovalModal = __esm({
|
|
|
16822
17385
|
init_diffView();
|
|
16823
17386
|
OPTIONS = [
|
|
16824
17387
|
{ label: S.approvalAllow, value: "allow-once", color: THEME.ok },
|
|
17388
|
+
// ★ I-4:会话级记忆(对标 CC「本会话不再询问」)——纯内存,会话结束失效
|
|
17389
|
+
{ label: S.approvalAllowSession, value: "allow-session", color: THEME.ok },
|
|
16825
17390
|
{ label: S.approvalAllowAlways, value: "allow-always", color: THEME.warn },
|
|
16826
17391
|
{ label: S.approvalDeny, value: "deny", color: THEME.danger }
|
|
16827
17392
|
];
|
|
16828
17393
|
MAX_DIFF_LINES = 18;
|
|
16829
|
-
|
|
17394
|
+
MAX_FULL_LINES = 400;
|
|
17395
|
+
ApprovalModal = ({ toolName, detail, fullDetail, expanded, selectedIndex, wrapW }) => {
|
|
16830
17396
|
const contentW = Math.max(16, wrapW);
|
|
16831
|
-
const
|
|
17397
|
+
const showFull = expanded && !!fullDetail;
|
|
17398
|
+
const sourceText = showFull ? fullDetail : detail;
|
|
17399
|
+
const parsed = parseApprovalDiff(sourceText, toolName);
|
|
16832
17400
|
const diffRows = parsed ? pairsToDisplayRows(parsed.sections) : null;
|
|
17401
|
+
const cap = showFull ? MAX_FULL_LINES : MAX_DIFF_LINES;
|
|
16833
17402
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: THEME.warn, paddingX: 1, paddingY: 1, marginTop: 1, children: [
|
|
16834
17403
|
/* @__PURE__ */ jsxs5(Text7, { color: THEME.warn, bold: true, children: [
|
|
16835
17404
|
S.approvalTitle,
|
|
@@ -16838,9 +17407,10 @@ var init_ApprovalModal = __esm({
|
|
|
16838
17407
|
] }),
|
|
16839
17408
|
/* @__PURE__ */ jsx7(Box5, { flexDirection: "column", marginTop: 0.5, marginBottom: 0.5, children: parsed && diffRows ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
16840
17409
|
wrapText(parsed.header, contentW).map((line, j) => /* @__PURE__ */ jsx7(Text7, { color: THEME.white, children: line }, `h-${j}`)),
|
|
16841
|
-
diffRows.slice(0,
|
|
16842
|
-
diffRows.length >
|
|
16843
|
-
] }) : wrapText(
|
|
17410
|
+
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}`)),
|
|
17411
|
+
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
|
|
17412
|
+
] }) : wrapText(sourceText || "(\u65E0\u8BF4\u660E)", contentW).map((line, j) => /* @__PURE__ */ jsx7(Text7, { color: THEME.white, children: line }, j)) }),
|
|
17413
|
+
fullDetail ? /* @__PURE__ */ jsx7(Text7, { color: THEME.grayDim, children: S.approvalFullHint }) : null,
|
|
16844
17414
|
/* @__PURE__ */ jsx7(Text7, { color: THEME.grayDim, children: S.approvalPrompt }),
|
|
16845
17415
|
OPTIONS.map((opt, i) => {
|
|
16846
17416
|
const active = i === selectedIndex;
|
|
@@ -17157,9 +17727,74 @@ var init_ModelPicker = __esm({
|
|
|
17157
17727
|
}
|
|
17158
17728
|
});
|
|
17159
17729
|
|
|
17160
|
-
// src/components/
|
|
17730
|
+
// src/components/RewindPicker.tsx
|
|
17161
17731
|
import { Box as Box13, Text as Text15 } from "ink";
|
|
17162
17732
|
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
17733
|
+
var PAGE_SIZE4, kindMark, RewindPicker;
|
|
17734
|
+
var init_RewindPicker = __esm({
|
|
17735
|
+
"src/components/RewindPicker.tsx"() {
|
|
17736
|
+
"use strict";
|
|
17737
|
+
init_theme();
|
|
17738
|
+
init_strings();
|
|
17739
|
+
init_util();
|
|
17740
|
+
PAGE_SIZE4 = 8;
|
|
17741
|
+
kindMark = (k) => k === "creation_marker" ? "\uFF0B" : k === "directory_tree" ? "\u25A4" : "~";
|
|
17742
|
+
RewindPicker = ({ records, selectedIndex, wrapW }) => {
|
|
17743
|
+
const start = Math.floor(Math.max(0, selectedIndex) / PAGE_SIZE4) * PAGE_SIZE4;
|
|
17744
|
+
const visible = records.slice(start, start + PAGE_SIZE4);
|
|
17745
|
+
const contentW = Math.max(24, wrapW);
|
|
17746
|
+
const metaSample = " \xB7 edit_file \xB7 59 \u5206\u949F\u524D";
|
|
17747
|
+
const pathW = Math.max(16, contentW - strWidth(metaSample) - 4);
|
|
17748
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", borderStyle: "round", borderColor: THEME.warn, paddingX: 1, paddingY: 1, marginTop: 1, children: [
|
|
17749
|
+
/* @__PURE__ */ jsx15(Text15, { color: THEME.warn, bold: true, children: S.rewindTitle }),
|
|
17750
|
+
/* @__PURE__ */ jsx15(Text15, { color: THEME.grayDim, children: S.rewindPrompt }),
|
|
17751
|
+
/* @__PURE__ */ jsx15(Box13, { flexDirection: "column", marginTop: 0.5, children: visible.map((r, i) => {
|
|
17752
|
+
const idx = start + i;
|
|
17753
|
+
const active = idx === selectedIndex;
|
|
17754
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", children: [
|
|
17755
|
+
/* @__PURE__ */ jsx15(Text15, { color: active ? THEME.coralBright : void 0, bold: true, children: active ? "\u203A " : " " }),
|
|
17756
|
+
/* @__PURE__ */ jsxs13(Text15, { color: THEME.grayDim, children: [
|
|
17757
|
+
kindMark(r.backupKind),
|
|
17758
|
+
" "
|
|
17759
|
+
] }),
|
|
17760
|
+
/* @__PURE__ */ jsx15(Text15, { color: active ? THEME.white : THEME.gray, bold: active, children: truncateMiddle(r.relativePath, pathW) }),
|
|
17761
|
+
/* @__PURE__ */ jsx15(Text15, { color: THEME.grayDim, children: ` \xB7 ${truncateMiddle(r.operationType, 20)} \xB7 ${S.relTime(r.timestamp)}` })
|
|
17762
|
+
] }, r.undoId);
|
|
17763
|
+
}) })
|
|
17764
|
+
] });
|
|
17765
|
+
};
|
|
17766
|
+
}
|
|
17767
|
+
});
|
|
17768
|
+
|
|
17769
|
+
// src/components/DiffSummaryCard.tsx
|
|
17770
|
+
import { Box as Box14, Text as Text16 } from "ink";
|
|
17771
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
17772
|
+
var MAX_ROWS_PER_FILE, DiffSummaryCard;
|
|
17773
|
+
var init_DiffSummaryCard = __esm({
|
|
17774
|
+
"src/components/DiffSummaryCard.tsx"() {
|
|
17775
|
+
"use strict";
|
|
17776
|
+
init_theme();
|
|
17777
|
+
init_util();
|
|
17778
|
+
MAX_ROWS_PER_FILE = 40;
|
|
17779
|
+
DiffSummaryCard = ({ title, files, wrapW }) => {
|
|
17780
|
+
const contentW = Math.max(16, wrapW);
|
|
17781
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: 1, children: [
|
|
17782
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, bold: true, children: title }),
|
|
17783
|
+
files.map((f) => /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: 0.5, children: [
|
|
17784
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.white, bold: true, children: truncateMiddle(f.path, Math.max(20, contentW)) }),
|
|
17785
|
+
f.note ? /* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: ` ${f.note}` }) : f.rows ? /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
17786
|
+
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)),
|
|
17787
|
+
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
|
|
17788
|
+
] }) : null
|
|
17789
|
+
] }, f.path))
|
|
17790
|
+
] });
|
|
17791
|
+
};
|
|
17792
|
+
}
|
|
17793
|
+
});
|
|
17794
|
+
|
|
17795
|
+
// src/components/SlashMenu.tsx
|
|
17796
|
+
import { Box as Box15, Text as Text17 } from "ink";
|
|
17797
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
17163
17798
|
var MAX_ROWS, SlashMenu;
|
|
17164
17799
|
var init_SlashMenu = __esm({
|
|
17165
17800
|
"src/components/SlashMenu.tsx"() {
|
|
@@ -17171,22 +17806,22 @@ var init_SlashMenu = __esm({
|
|
|
17171
17806
|
const visible = entries.slice(start, start + MAX_ROWS);
|
|
17172
17807
|
const cmdW = Math.min(22, Math.max(8, ...visible.map((e) => e.name.length)) + 1);
|
|
17173
17808
|
const descMax = Math.max(12, cols - cmdW - 6);
|
|
17174
|
-
return /* @__PURE__ */
|
|
17809
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", borderStyle: "round", borderColor: THEME.coral, paddingX: 1, paddingY: 0, marginTop: 1, children: [
|
|
17175
17810
|
visible.map((e, i) => {
|
|
17176
17811
|
const active = i + start === selectedIndex;
|
|
17177
17812
|
const desc = e.description.length > descMax ? `${e.description.slice(0, descMax - 1)}\u2026` : e.description;
|
|
17178
17813
|
const gap = Math.max(1, cmdW - e.name.length);
|
|
17179
|
-
return /* @__PURE__ */
|
|
17180
|
-
/* @__PURE__ */
|
|
17181
|
-
/* @__PURE__ */
|
|
17814
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "row", children: [
|
|
17815
|
+
/* @__PURE__ */ jsx17(Text17, { color: active ? THEME.coralBright : void 0, bold: true, children: active ? "\u276F " : " " }),
|
|
17816
|
+
/* @__PURE__ */ jsxs15(Text17, { color: active ? THEME.white : THEME.gray, bold: active, children: [
|
|
17182
17817
|
"/",
|
|
17183
17818
|
e.name
|
|
17184
17819
|
] }),
|
|
17185
|
-
/* @__PURE__ */
|
|
17186
|
-
/* @__PURE__ */
|
|
17820
|
+
/* @__PURE__ */ jsx17(Text17, { color: THEME.grayDim, children: " ".repeat(gap) }),
|
|
17821
|
+
/* @__PURE__ */ jsx17(Text17, { color: active ? THEME.gray : THEME.grayDim, children: desc })
|
|
17187
17822
|
] }, e.name);
|
|
17188
17823
|
}),
|
|
17189
|
-
entries.length > MAX_ROWS ? /* @__PURE__ */
|
|
17824
|
+
entries.length > MAX_ROWS ? /* @__PURE__ */ jsxs15(Text17, { color: THEME.coralMuted, children: [
|
|
17190
17825
|
"\u2191\u2193 \u6EDA\u52A8 \xB7 ",
|
|
17191
17826
|
selectedIndex + 1,
|
|
17192
17827
|
"/",
|
|
@@ -17199,8 +17834,8 @@ var init_SlashMenu = __esm({
|
|
|
17199
17834
|
});
|
|
17200
17835
|
|
|
17201
17836
|
// src/components/StatusStrip.tsx
|
|
17202
|
-
import { Box as
|
|
17203
|
-
import { jsx as
|
|
17837
|
+
import { Box as Box16, Text as Text18 } from "ink";
|
|
17838
|
+
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
17204
17839
|
var StatusStrip;
|
|
17205
17840
|
var init_StatusStrip = __esm({
|
|
17206
17841
|
"src/components/StatusStrip.tsx"() {
|
|
@@ -17217,18 +17852,18 @@ var init_StatusStrip = __esm({
|
|
|
17217
17852
|
`state: ${stateText}`,
|
|
17218
17853
|
sessionShort ? `session: ${sessionShort}` : ""
|
|
17219
17854
|
].filter(Boolean);
|
|
17220
|
-
return /* @__PURE__ */
|
|
17221
|
-
/* @__PURE__ */
|
|
17222
|
-
/* @__PURE__ */
|
|
17223
|
-
/* @__PURE__ */
|
|
17855
|
+
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", flexShrink: 0, children: [
|
|
17856
|
+
/* @__PURE__ */ jsx18(Text18, { color: THEME.coralMuted, children: customLine && customLine.trim() ? customLine : bits.join(" \xB7 ") }),
|
|
17857
|
+
/* @__PURE__ */ jsx18(Text18, { color: stateColor, children: "" }),
|
|
17858
|
+
/* @__PURE__ */ jsx18(Text18, { color: THEME.grayDim, children: S.cmdHint })
|
|
17224
17859
|
] });
|
|
17225
17860
|
};
|
|
17226
17861
|
}
|
|
17227
17862
|
});
|
|
17228
17863
|
|
|
17229
17864
|
// src/components/TopPanel.tsx
|
|
17230
|
-
import { Box as
|
|
17231
|
-
import { jsx as
|
|
17865
|
+
import { Box as Box17, Text as Text19 } from "ink";
|
|
17866
|
+
import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
17232
17867
|
var ROBOT, INNER_H, TopPanel;
|
|
17233
17868
|
var init_TopPanel = __esm({
|
|
17234
17869
|
"src/components/TopPanel.tsx"() {
|
|
@@ -17248,36 +17883,36 @@ var init_TopPanel = __esm({
|
|
|
17248
17883
|
const leftW = Math.floor(innerW * 0.33);
|
|
17249
17884
|
const rightW = innerW - leftW - 1;
|
|
17250
17885
|
const ruleLen = Math.max(8, rightW - 2);
|
|
17251
|
-
const VDivider = /* @__PURE__ */
|
|
17252
|
-
return /* @__PURE__ */
|
|
17253
|
-
/* @__PURE__ */
|
|
17254
|
-
/* @__PURE__ */
|
|
17255
|
-
/* @__PURE__ */
|
|
17256
|
-
/* @__PURE__ */
|
|
17257
|
-
/* @__PURE__ */
|
|
17258
|
-
/* @__PURE__ */
|
|
17886
|
+
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)) });
|
|
17887
|
+
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", width: panelW, marginBottom: 1, children: [
|
|
17888
|
+
/* @__PURE__ */ jsxs17(Text19, { children: [
|
|
17889
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.coralBright, bold: true, children: "* Welcome to " }),
|
|
17890
|
+
/* @__PURE__ */ jsx19(Text19, { bold: true, color: THEME.white, children: "deep" }),
|
|
17891
|
+
/* @__PURE__ */ jsx19(Text19, { bold: true, color: THEME.coralBright, children: "Seek" }),
|
|
17892
|
+
/* @__PURE__ */ jsx19(Text19, { bold: true, color: THEME.white, children: "Code" }),
|
|
17893
|
+
/* @__PURE__ */ jsxs17(Text19, { color: THEME.coralMuted, children: [
|
|
17259
17894
|
" ",
|
|
17260
17895
|
VERSION
|
|
17261
17896
|
] })
|
|
17262
17897
|
] }),
|
|
17263
|
-
/* @__PURE__ */
|
|
17264
|
-
/* @__PURE__ */
|
|
17265
|
-
/* @__PURE__ */
|
|
17266
|
-
/* @__PURE__ */
|
|
17267
|
-
ROBOT.map((l, i) => /* @__PURE__ */
|
|
17268
|
-
/* @__PURE__ */
|
|
17269
|
-
/* @__PURE__ */
|
|
17898
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.grayDim, children: cwd }),
|
|
17899
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "row", borderStyle: "round", borderColor: THEME.coral, marginTop: 1, paddingX: 0, paddingY: 0, children: [
|
|
17900
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", width: leftW, height: INNER_H, alignItems: "center", justifyContent: "center", children: [
|
|
17901
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.white, bold: true, children: "Welcome back!" }),
|
|
17902
|
+
ROBOT.map((l, i) => /* @__PURE__ */ jsx19(Text19, { color: THEME.coralBright, children: l }, i)),
|
|
17903
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.grayDim, children: "Ctrl+C \u9000\u51FA \xB7 Esc \u4E2D\u6B62" }),
|
|
17904
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.grayDim, children: "\u8F93\u5165 / \u67E5\u770B\u547D\u4EE4" })
|
|
17270
17905
|
] }),
|
|
17271
17906
|
VDivider,
|
|
17272
|
-
/* @__PURE__ */
|
|
17273
|
-
/* @__PURE__ */
|
|
17274
|
-
/* @__PURE__ */
|
|
17275
|
-
/* @__PURE__ */
|
|
17907
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", width: rightW, height: INNER_H, justifyContent: "space-between", paddingX: 1, children: [
|
|
17908
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
17909
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.coralBright, bold: true, children: "Tips for getting started" }),
|
|
17910
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.white, children: "\u8F93\u5165\u9700\u6C42\u5F00\u59CB\uFF0C\u8F93\u5165 / \u5524\u51FA\u547D\u4EE4\u3002" })
|
|
17276
17911
|
] }),
|
|
17277
|
-
/* @__PURE__ */
|
|
17278
|
-
/* @__PURE__ */
|
|
17279
|
-
/* @__PURE__ */
|
|
17280
|
-
/* @__PURE__ */
|
|
17912
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.grayDim, children: "\u2500".repeat(ruleLen) }),
|
|
17913
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
17914
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.coralBright, bold: true, children: "What's new" }),
|
|
17915
|
+
/* @__PURE__ */ jsx19(Text19, { color: THEME.white, children: "\u5DF2\u5BF9\u63A5\u672C\u5730 Tool Call \u667A\u80FD\u51FD\u6570\u8C03\u7528\u3002" })
|
|
17281
17916
|
] })
|
|
17282
17917
|
] })
|
|
17283
17918
|
] })
|
|
@@ -17287,7 +17922,7 @@ var init_TopPanel = __esm({
|
|
|
17287
17922
|
});
|
|
17288
17923
|
|
|
17289
17924
|
// ../core/src/observability/inspect.ts
|
|
17290
|
-
import
|
|
17925
|
+
import fs49 from "fs";
|
|
17291
17926
|
var OBS, obsT, CWD, fmt, readTrace, fmtK, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
|
|
17292
17927
|
var init_inspect = __esm({
|
|
17293
17928
|
"../core/src/observability/inspect.ts"() {
|
|
@@ -17410,7 +18045,7 @@ var init_inspect = __esm({
|
|
|
17410
18045
|
if (!sessionId) return [];
|
|
17411
18046
|
try {
|
|
17412
18047
|
const p = await getTraceStorePath(sessionId);
|
|
17413
|
-
const text = await
|
|
18048
|
+
const text = await fs49.promises.readFile(p, "utf8");
|
|
17414
18049
|
const out = [];
|
|
17415
18050
|
for (const line of text.split("\n")) {
|
|
17416
18051
|
if (!line.trim()) continue;
|
|
@@ -17519,7 +18154,7 @@ var init_inspect = __esm({
|
|
|
17519
18154
|
}
|
|
17520
18155
|
let sidecarCount = 0;
|
|
17521
18156
|
try {
|
|
17522
|
-
sidecarCount = (await
|
|
18157
|
+
sidecarCount = (await fs49.promises.readdir(getToolOutputSidecarDir(sessionId))).filter((f) => f.endsWith(".txt")).length;
|
|
17523
18158
|
} catch {
|
|
17524
18159
|
}
|
|
17525
18160
|
const window = appConfig.MAX_HISTORY_TOKENS;
|
|
@@ -17594,8 +18229,8 @@ __export(App_exports, {
|
|
|
17594
18229
|
App: () => App
|
|
17595
18230
|
});
|
|
17596
18231
|
import { useCallback as useCallback2, useEffect as useEffect4, useMemo, useRef as useRef2, useState as useState4 } from "react";
|
|
17597
|
-
import { Box as
|
|
17598
|
-
import { Fragment as
|
|
18232
|
+
import { Box as Box18, Static, Text as Text20, useApp, useInput as useInput2, useStdout } from "ink";
|
|
18233
|
+
import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
17599
18234
|
var CWD2, RowView, isDynamicRow, SPINNER_FRAMES, App;
|
|
17600
18235
|
var init_App = __esm({
|
|
17601
18236
|
"src/App.tsx"() {
|
|
@@ -17622,23 +18257,27 @@ var init_App = __esm({
|
|
|
17622
18257
|
init_SessionPicker();
|
|
17623
18258
|
init_ForkPicker();
|
|
17624
18259
|
init_ModelPicker();
|
|
18260
|
+
init_RewindPicker();
|
|
18261
|
+
init_DiffSummaryCard();
|
|
17625
18262
|
init_SlashMenu();
|
|
17626
18263
|
init_MultilineInput();
|
|
17627
18264
|
init_StatusStrip();
|
|
17628
18265
|
init_TopPanel();
|
|
17629
18266
|
init_inspect();
|
|
18267
|
+
init_restore();
|
|
17630
18268
|
CWD2 = process.cwd();
|
|
17631
18269
|
RowView = ({ row, wrapW, streamTail, showThinking }) => {
|
|
17632
|
-
if (row.kind === "todos") return /* @__PURE__ */
|
|
17633
|
-
if (row.kind === "tool") return /* @__PURE__ */
|
|
17634
|
-
if (row.kind === "
|
|
18270
|
+
if (row.kind === "todos") return /* @__PURE__ */ jsx20(TodosPanel, { todos: row.todos, wrapW });
|
|
18271
|
+
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 });
|
|
18272
|
+
if (row.kind === "diff-summary") return /* @__PURE__ */ jsx20(DiffSummaryCard, { title: row.title, files: row.files, wrapW });
|
|
18273
|
+
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 });
|
|
17635
18274
|
if (row.kind === "user") {
|
|
17636
|
-
return /* @__PURE__ */
|
|
17637
|
-
/* @__PURE__ */
|
|
17638
|
-
/* @__PURE__ */
|
|
18275
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, children: [
|
|
18276
|
+
/* @__PURE__ */ jsx20(Text20, { color: THEME.grayDim, children: "\u2500".repeat(Math.max(8, wrapW)) }),
|
|
18277
|
+
/* @__PURE__ */ jsx20(MessageBlock, { row, wrapW, streamTail })
|
|
17639
18278
|
] });
|
|
17640
18279
|
}
|
|
17641
|
-
return /* @__PURE__ */
|
|
18280
|
+
return /* @__PURE__ */ jsx20(MessageBlock, { row, wrapW, streamTail });
|
|
17642
18281
|
};
|
|
17643
18282
|
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";
|
|
17644
18283
|
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -17667,6 +18306,7 @@ var init_App = __esm({
|
|
|
17667
18306
|
const [planEditing, setPlanEditing] = useState4(false);
|
|
17668
18307
|
const [planDraft, setPlanDraft] = useState4("");
|
|
17669
18308
|
const [planCursor, setPlanCursor] = useState4(0);
|
|
18309
|
+
const [approvalFull, setApprovalFull] = useState4(false);
|
|
17670
18310
|
const [qCursor, setQCursor] = useState4(0);
|
|
17671
18311
|
const [qChecked, setQChecked] = useState4(/* @__PURE__ */ new Set());
|
|
17672
18312
|
const [qEditing, setQEditing] = useState4(false);
|
|
@@ -17778,8 +18418,14 @@ var init_App = __esm({
|
|
|
17778
18418
|
return S.cmdSessions;
|
|
17779
18419
|
case "fork":
|
|
17780
18420
|
return S.cmdFork;
|
|
18421
|
+
case "undo":
|
|
18422
|
+
return S.cmdUndo;
|
|
17781
18423
|
case "usage":
|
|
17782
18424
|
return S.cmdUsage;
|
|
18425
|
+
case "rewind":
|
|
18426
|
+
return S.cmdRewind;
|
|
18427
|
+
case "diff":
|
|
18428
|
+
return S.cmdDiff;
|
|
17783
18429
|
case "budget":
|
|
17784
18430
|
return S.cmdBudget;
|
|
17785
18431
|
case "context":
|
|
@@ -17812,7 +18458,7 @@ var init_App = __esm({
|
|
|
17812
18458
|
const q = input.slice(1).toLowerCase();
|
|
17813
18459
|
return menuEntries.filter((c) => c.name.toLowerCase().startsWith(q));
|
|
17814
18460
|
}, [input, menuEntries]);
|
|
17815
|
-
const menuActive = state.pendingApproval != null || state.pendingQuestion != null || state.pendingPlan != null || state.pendingSessions != null || state.pendingFork != null || state.pendingModel != null;
|
|
18461
|
+
const menuActive = state.pendingApproval != null || state.pendingQuestion != null || state.pendingPlan != null || state.pendingSessions != null || state.pendingFork != null || state.pendingModel != null || state.pendingRewind != null;
|
|
17816
18462
|
const [busyClock, setBusyClock] = useState4({ frame: 0, sec: 0 });
|
|
17817
18463
|
const busyStartRef = useRef2(0);
|
|
17818
18464
|
useEffect4(() => {
|
|
@@ -17836,7 +18482,8 @@ var init_App = __esm({
|
|
|
17836
18482
|
useEffect4(() => {
|
|
17837
18483
|
setSelectIdx(0);
|
|
17838
18484
|
setPlanEditing(false);
|
|
17839
|
-
|
|
18485
|
+
setApprovalFull(false);
|
|
18486
|
+
}, [state.pendingApproval, state.pendingQuestion, state.pendingPlan, state.pendingSessions, state.pendingFork, state.pendingModel, state.pendingRewind, slashVisible, filteredCommands.length]);
|
|
17840
18487
|
useEffect4(() => {
|
|
17841
18488
|
setQCursor(0);
|
|
17842
18489
|
setQChecked(/* @__PURE__ */ new Set());
|
|
@@ -17874,6 +18521,21 @@ var init_App = __esm({
|
|
|
17874
18521
|
case "/fork":
|
|
17875
18522
|
void state.openForkPicker();
|
|
17876
18523
|
return true;
|
|
18524
|
+
case "/undo": {
|
|
18525
|
+
const sid = state.sessionIdRef.current;
|
|
18526
|
+
if (!sid) {
|
|
18527
|
+
state.pushInfo(S.undoNoSession);
|
|
18528
|
+
return true;
|
|
18529
|
+
}
|
|
18530
|
+
state.pushInfo(await restoreLast(sid));
|
|
18531
|
+
return true;
|
|
18532
|
+
}
|
|
18533
|
+
case "/rewind":
|
|
18534
|
+
void state.openRewindPicker();
|
|
18535
|
+
return true;
|
|
18536
|
+
case "/diff":
|
|
18537
|
+
void state.openDiff();
|
|
18538
|
+
return true;
|
|
17877
18539
|
case "/help":
|
|
17878
18540
|
state.pushInfo(S.helpText(modelDisplay, state.getThinkingLevel(), getLocale()));
|
|
17879
18541
|
return true;
|
|
@@ -18096,7 +18758,11 @@ ${listText}`);
|
|
|
18096
18758
|
return;
|
|
18097
18759
|
}
|
|
18098
18760
|
if (state.pendingApproval) {
|
|
18099
|
-
const APPROVAL_DECISIONS = ["allow-once", "allow-always", "deny"];
|
|
18761
|
+
const APPROVAL_DECISIONS = ["allow-once", "allow-session", "allow-always", "deny"];
|
|
18762
|
+
if (ch === "f" || ch === "F") {
|
|
18763
|
+
setApprovalFull((v) => !v);
|
|
18764
|
+
return;
|
|
18765
|
+
}
|
|
18100
18766
|
if (key.upArrow) setSelectIdx((i) => (i - 1 + APPROVAL_DECISIONS.length) % APPROVAL_DECISIONS.length);
|
|
18101
18767
|
else if (key.downArrow) setSelectIdx((i) => (i + 1) % APPROVAL_DECISIONS.length);
|
|
18102
18768
|
else if (key.return) state.resolveApproval(APPROVAL_DECISIONS[selectIdxRef.current] ?? "deny");
|
|
@@ -18190,6 +18856,16 @@ ${listText}`);
|
|
|
18190
18856
|
} else if (key.escape || key.ctrl && ch === "g") state.resolveModel(null);
|
|
18191
18857
|
return;
|
|
18192
18858
|
}
|
|
18859
|
+
if (state.pendingRewind) {
|
|
18860
|
+
const list = state.pendingRewind.records;
|
|
18861
|
+
if (key.upArrow) setSelectIdx((i) => (i - 1 + list.length) % list.length);
|
|
18862
|
+
else if (key.downArrow) setSelectIdx((i) => (i + 1) % list.length);
|
|
18863
|
+
else if (key.return) {
|
|
18864
|
+
const sel = list[Math.min(selectIdxRef.current, list.length - 1)];
|
|
18865
|
+
state.resolveRewind(sel?.undoId ?? null);
|
|
18866
|
+
} else if (key.escape || key.ctrl && ch === "g") state.resolveRewind(null);
|
|
18867
|
+
return;
|
|
18868
|
+
}
|
|
18193
18869
|
if (key.ctrl && ch === "t") {
|
|
18194
18870
|
state.toggleShowThinking();
|
|
18195
18871
|
return;
|
|
@@ -18230,14 +18906,14 @@ ${listText}`);
|
|
|
18230
18906
|
}
|
|
18231
18907
|
});
|
|
18232
18908
|
const sessionShort = state.sessionIdRef.current ? truncateMiddle(state.sessionIdRef.current, 12) : "";
|
|
18233
|
-
return /* @__PURE__ */
|
|
18234
|
-
/* @__PURE__ */
|
|
18235
|
-
/* @__PURE__ */
|
|
18236
|
-
menuActive ? null : /* @__PURE__ */
|
|
18237
|
-
dynamicRows.map((row) => /* @__PURE__ */
|
|
18238
|
-
state.busy ? /* @__PURE__ */
|
|
18909
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", width: cols, children: [
|
|
18910
|
+
/* @__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) }),
|
|
18911
|
+
/* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", width: cols, children: [
|
|
18912
|
+
menuActive ? null : /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", paddingX: 1, children: [
|
|
18913
|
+
dynamicRows.map((row) => /* @__PURE__ */ jsx20(RowView, { row, wrapW, streamTail, showThinking: state.showThinkingText }, row.id)),
|
|
18914
|
+
state.busy ? /* @__PURE__ */ jsx20(Box18, { marginTop: 0.5, children: /* @__PURE__ */ jsxs18(Text20, { color: THEME.coralBright, children: [
|
|
18239
18915
|
`${SPINNER_FRAMES[busyClock.frame]} ${S.thinkVerbs[Math.floor(busyClock.sec / 3) % S.thinkVerbs.length]}\u2026 (${busyClock.sec}s \xB7 ${S.escInterrupt})`,
|
|
18240
|
-
state.runningAgents > 0 ? /* @__PURE__ */
|
|
18916
|
+
state.runningAgents > 0 ? /* @__PURE__ */ jsxs18(Text20, { color: "green", children: [
|
|
18241
18917
|
" \xB7 \u25CF ",
|
|
18242
18918
|
state.runningAgents,
|
|
18243
18919
|
" agent",
|
|
@@ -18245,9 +18921,9 @@ ${listText}`);
|
|
|
18245
18921
|
] }) : null
|
|
18246
18922
|
] }) }) : null
|
|
18247
18923
|
] }),
|
|
18248
|
-
/* @__PURE__ */
|
|
18249
|
-
state.pendingApproval ? /* @__PURE__ */
|
|
18250
|
-
state.pendingQuestion ? /* @__PURE__ */
|
|
18924
|
+
/* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", paddingX: 1, flexShrink: 0, children: [
|
|
18925
|
+
state.pendingApproval ? /* @__PURE__ */ jsx20(ApprovalModal, { toolName: state.pendingApproval.toolName, detail: state.pendingApproval.detail, fullDetail: state.pendingApproval.fullDetail, expanded: approvalFull, selectedIndex: selectIdx, wrapW }) : null,
|
|
18926
|
+
state.pendingQuestion ? /* @__PURE__ */ jsx20(
|
|
18251
18927
|
QuestionModal,
|
|
18252
18928
|
{
|
|
18253
18929
|
question: state.pendingQuestion.req.question,
|
|
@@ -18266,7 +18942,7 @@ ${listText}`);
|
|
|
18266
18942
|
onDraftSubmit: confirmQuestionDraft
|
|
18267
18943
|
}
|
|
18268
18944
|
) : null,
|
|
18269
|
-
state.pendingPlan ? planEditing ? /* @__PURE__ */
|
|
18945
|
+
state.pendingPlan ? planEditing ? /* @__PURE__ */ jsx20(
|
|
18270
18946
|
PlanEditor,
|
|
18271
18947
|
{
|
|
18272
18948
|
value: planDraft,
|
|
@@ -18277,16 +18953,17 @@ ${listText}`);
|
|
|
18277
18953
|
},
|
|
18278
18954
|
onSubmit: confirmPlanEdit
|
|
18279
18955
|
}
|
|
18280
|
-
) : /* @__PURE__ */
|
|
18281
|
-
state.pendingSessions ? /* @__PURE__ */
|
|
18282
|
-
state.pendingFork ? /* @__PURE__ */
|
|
18283
|
-
state.pendingModel ? /* @__PURE__ */
|
|
18284
|
-
|
|
18956
|
+
) : /* @__PURE__ */ jsx20(PlanModal, { selectedIndex: selectIdx }) : null,
|
|
18957
|
+
state.pendingSessions ? /* @__PURE__ */ jsx20(SessionPicker, { sessions: state.pendingSessions.sessions, selectedIndex: selectIdx, wrapW }) : null,
|
|
18958
|
+
state.pendingFork ? /* @__PURE__ */ jsx20(ForkPicker, { anchors: state.pendingFork.anchors, selectedIndex: selectIdx, wrapW }) : null,
|
|
18959
|
+
state.pendingModel ? /* @__PURE__ */ jsx20(ModelPicker, { models: state.pendingModel.models, currentModel: modelDisplay, selectedIndex: selectIdx, wrapW }) : null,
|
|
18960
|
+
state.pendingRewind ? /* @__PURE__ */ jsx20(RewindPicker, { records: state.pendingRewind.records, selectedIndex: selectIdx, wrapW }) : null,
|
|
18961
|
+
slashVisible ? /* @__PURE__ */ jsx20(SlashMenu, { entries: filteredCommands, selectedIndex: selectIdx, cols }) : null
|
|
18285
18962
|
] }),
|
|
18286
|
-
/* @__PURE__ */
|
|
18963
|
+
/* @__PURE__ */ jsx20(Box18, { flexDirection: "column", paddingX: 1, marginTop: 1, flexShrink: 0, children: (() => {
|
|
18287
18964
|
const planOn = state.getPlanMode();
|
|
18288
18965
|
const autoOn = !planOn && state.getAutoMode();
|
|
18289
|
-
const inputEl = /* @__PURE__ */
|
|
18966
|
+
const inputEl = /* @__PURE__ */ jsx20(
|
|
18290
18967
|
MultilineInput,
|
|
18291
18968
|
{
|
|
18292
18969
|
value: input,
|
|
@@ -18304,17 +18981,17 @@ ${listText}`);
|
|
|
18304
18981
|
}
|
|
18305
18982
|
);
|
|
18306
18983
|
if (planOn || autoOn) {
|
|
18307
|
-
return /* @__PURE__ */
|
|
18308
|
-
/* @__PURE__ */
|
|
18984
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", borderStyle: "round", borderColor: planOn ? THEME.thinking : THEME.warn, paddingX: 1, children: [
|
|
18985
|
+
/* @__PURE__ */ jsx20(Text20, { color: planOn ? THEME.thinking : THEME.warn, children: planOn ? S.inputBadgePlan : S.inputBadgeAuto }),
|
|
18309
18986
|
inputEl
|
|
18310
18987
|
] });
|
|
18311
18988
|
}
|
|
18312
|
-
return /* @__PURE__ */
|
|
18313
|
-
/* @__PURE__ */
|
|
18989
|
+
return /* @__PURE__ */ jsxs18(Fragment4, { children: [
|
|
18990
|
+
/* @__PURE__ */ jsx20(Text20, { dimColor: true, color: THEME.gray, children: dimRule(cols) }),
|
|
18314
18991
|
inputEl
|
|
18315
18992
|
] });
|
|
18316
18993
|
})() }),
|
|
18317
|
-
/* @__PURE__ */
|
|
18994
|
+
/* @__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 }) })
|
|
18318
18995
|
] })
|
|
18319
18996
|
] });
|
|
18320
18997
|
};
|
|
@@ -18327,7 +19004,7 @@ __export(batchRunner_exports, {
|
|
|
18327
19004
|
parseWaitWindow: () => parseWaitWindow,
|
|
18328
19005
|
runBatchQueue: () => runBatchQueue
|
|
18329
19006
|
});
|
|
18330
|
-
import
|
|
19007
|
+
import fs50 from "fs";
|
|
18331
19008
|
import { spawn as spawn6 } from "child_process";
|
|
18332
19009
|
var BATCH_EDIT_TOOLS, BATCH_DEFAULT_DENYLIST, ACCEPT_TIMEOUT_MIN, ACCEPT_TAIL_LINES, emit2, ts, runAcceptCommand, collectSessionCost, buildReport, runOneTask, parseWaitWindow, nowMinutes, sleep, waitUntilWindow, runBatchQueue;
|
|
18333
19010
|
var init_batchRunner = __esm({
|
|
@@ -18335,6 +19012,7 @@ var init_batchRunner = __esm({
|
|
|
18335
19012
|
"use strict";
|
|
18336
19013
|
init_chatProcessing();
|
|
18337
19014
|
init_tool();
|
|
19015
|
+
init_autoPermission();
|
|
18338
19016
|
init_manager();
|
|
18339
19017
|
init_sessionRegistry();
|
|
18340
19018
|
init_background();
|
|
@@ -18346,7 +19024,7 @@ var init_batchRunner = __esm({
|
|
|
18346
19024
|
init_batchStrings();
|
|
18347
19025
|
init_strings();
|
|
18348
19026
|
BATCH_EDIT_TOOLS = /* @__PURE__ */ new Set(["edit_file", "create_file", "write_file", "move_file", "notebook_edit"]);
|
|
18349
|
-
BATCH_DEFAULT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path"]);
|
|
19027
|
+
BATCH_DEFAULT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path", "run_in_background"]);
|
|
18350
19028
|
ACCEPT_TIMEOUT_MIN = 10;
|
|
18351
19029
|
ACCEPT_TAIL_LINES = 60;
|
|
18352
19030
|
emit2 = (s = "") => {
|
|
@@ -18396,8 +19074,8 @@ ${lines.slice(-ACCEPT_TAIL_LINES).join("\n")}` : text;
|
|
|
18396
19074
|
});
|
|
18397
19075
|
};
|
|
18398
19076
|
collectSessionCost = async (sessionId) => {
|
|
18399
|
-
const recs = (await readUsageRecords(2)).filter((r) => r.sessionId === sessionId);
|
|
18400
|
-
const c = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: null, toolCostYuan: 0, partialPrice: false, durationMs: 0 };
|
|
19077
|
+
const recs = (await readUsageRecords(2)).filter((r) => r.sessionId === sessionId || r.sessionId.startsWith(sessionId + "__sub__"));
|
|
19078
|
+
const c = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: null, toolCostYuan: 0, auxCostYuan: 0, partialPrice: false, durationMs: 0 };
|
|
18401
19079
|
for (const r of recs) {
|
|
18402
19080
|
c.rounds += r.rounds || 0;
|
|
18403
19081
|
if (r.depth === 0) c.mainRounds += r.rounds || 0;
|
|
@@ -18407,6 +19085,7 @@ ${lines.slice(-ACCEPT_TAIL_LINES).join("\n")}` : text;
|
|
|
18407
19085
|
c.cachedTokens += r.cachedTokens ?? 0;
|
|
18408
19086
|
c.durationMs += r.durationMs || 0;
|
|
18409
19087
|
c.toolCostYuan += r.toolCostYuan ?? 0;
|
|
19088
|
+
c.auxCostYuan += r.auxCostYuan ?? 0;
|
|
18410
19089
|
if (typeof r.costYuan === "number") c.costYuan = (c.costYuan ?? 0) + r.costYuan;
|
|
18411
19090
|
else c.partialPrice = true;
|
|
18412
19091
|
}
|
|
@@ -18469,10 +19148,10 @@ ${diff}
|
|
|
18469
19148
|
};
|
|
18470
19149
|
runOneTask = async (task) => {
|
|
18471
19150
|
const T = batchT(getLocale());
|
|
18472
|
-
const source = await
|
|
19151
|
+
const source = await fs50.promises.readFile(task.path, "utf-8");
|
|
18473
19152
|
const parsed = parseBatchTicket(source, getLocale());
|
|
18474
19153
|
if (!parsed.ok) {
|
|
18475
|
-
const cost2 = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: 0, toolCostYuan: 0, partialPrice: false, durationMs: 0 };
|
|
19154
|
+
const cost2 = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: 0, toolCostYuan: 0, auxCostYuan: 0, partialPrice: false, durationMs: 0 };
|
|
18476
19155
|
await finishTicket(task.id, `---
|
|
18477
19156
|
id: ${task.id}
|
|
18478
19157
|
title: ${task.id}
|
|
@@ -18491,10 +19170,31 @@ ${parsed.error}`);
|
|
|
18491
19170
|
emit2(`\u25B6 [${task.id}] ${t.title}`);
|
|
18492
19171
|
emit2(T.taskMeta(t.budgetYuan, timeoutMin, t.accept));
|
|
18493
19172
|
const unlocked = new Set(t.tools ?? []);
|
|
18494
|
-
const
|
|
18495
|
-
const toolSchemas = agentTools.filter((x) => !denySchemas.has(x?.function?.name));
|
|
19173
|
+
const toolSchemas = agentTools.filter((x) => unlocked.has(x?.function?.name) || x?.function?.batchAllowed !== false && !BATCH_DEFAULT_DENYLIST.has(x?.function?.name));
|
|
18496
19174
|
const allowApproval = /* @__PURE__ */ new Set([...BATCH_EDIT_TOOLS, ...unlocked]);
|
|
18497
19175
|
const deniedTools = [];
|
|
19176
|
+
const wrapInterpreterGate = (x) => {
|
|
19177
|
+
const name = x?.function?.name;
|
|
19178
|
+
if (name !== "run_command" && name !== "run_in_background") return x;
|
|
19179
|
+
const innerExecute = x.function.execute.bind(x.function);
|
|
19180
|
+
return {
|
|
19181
|
+
...x,
|
|
19182
|
+
function: {
|
|
19183
|
+
...x.function,
|
|
19184
|
+
execute: async (args, ctx) => {
|
|
19185
|
+
const cmd = typeof args?.command === "string" ? args.command : "";
|
|
19186
|
+
if (cmd && isInterpreterRepoScript(cmd) && !unlocked.has("interpreter")) {
|
|
19187
|
+
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`;
|
|
19188
|
+
deniedTools.push({ tool: name, detail: why.replace(/\n+/g, " ").slice(0, 140) });
|
|
19189
|
+
emit2(`\u{1F6AB} [${task.id}] \u62E6\u622A\u89E3\u91CA\u5668\u811A\u672C\uFF1A${cmd.slice(0, 80)}`);
|
|
19190
|
+
return why;
|
|
19191
|
+
}
|
|
19192
|
+
return innerExecute(args, ctx);
|
|
19193
|
+
}
|
|
19194
|
+
}
|
|
19195
|
+
};
|
|
19196
|
+
};
|
|
19197
|
+
const gatedSchemas = toolSchemas.map(wrapInterpreterGate);
|
|
18498
19198
|
let wt = null;
|
|
18499
19199
|
try {
|
|
18500
19200
|
wt = await createWorktree(sessionId, task.id);
|
|
@@ -18576,7 +19276,7 @@ ${s.output}
|
|
|
18576
19276
|
if (evt.type === "run.receipt") receipt = evt;
|
|
18577
19277
|
},
|
|
18578
19278
|
budgetYuan: t.budgetYuan,
|
|
18579
|
-
toolSchemas
|
|
19279
|
+
toolSchemas: gatedSchemas
|
|
18580
19280
|
}
|
|
18581
19281
|
);
|
|
18582
19282
|
} catch (e) {
|
|
@@ -18756,9 +19456,9 @@ init_updateCheck();
|
|
|
18756
19456
|
init_trust();
|
|
18757
19457
|
import { render } from "ink";
|
|
18758
19458
|
import os5 from "os";
|
|
18759
|
-
import
|
|
19459
|
+
import path50 from "path";
|
|
18760
19460
|
import { emitKeypressEvents } from "node:readline";
|
|
18761
|
-
import { jsx as
|
|
19461
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
18762
19462
|
var parseArgs = (argv) => {
|
|
18763
19463
|
const out = {};
|
|
18764
19464
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -18905,7 +19605,7 @@ var main = async () => {
|
|
|
18905
19605
|
process.stdout.write("\x1B[2J\x1B[3J\x1B[1;1H");
|
|
18906
19606
|
const cwdIsHome = () => {
|
|
18907
19607
|
try {
|
|
18908
|
-
return
|
|
19608
|
+
return path50.resolve(cwd).toLowerCase() === path50.resolve(os5.homedir()).toLowerCase();
|
|
18909
19609
|
} catch {
|
|
18910
19610
|
return false;
|
|
18911
19611
|
}
|
|
@@ -18924,7 +19624,7 @@ var main = async () => {
|
|
|
18924
19624
|
resumeId = await getMostRecentSessionId2() ?? void 0;
|
|
18925
19625
|
}
|
|
18926
19626
|
const { waitUntilExit } = render(
|
|
18927
|
-
/* @__PURE__ */
|
|
19627
|
+
/* @__PURE__ */ jsx21(App2, { resumeSessionId: resumeId, initialPlanMode: args.plan, initialAutoMode: args.auto, initialIncludeProject: includeProject, engineReady }),
|
|
18928
19628
|
// exitOnCtrlC:false:Ctrl+C 交由 App useInput 处理(统一退出/中止语义);
|
|
18929
19629
|
// patchConsole:false:避免 console 劫持与全屏重绘叠加闪屏。
|
|
18930
19630
|
{ exitOnCtrlC: false, patchConsole: false }
|