deepseeker-code 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +1005 -205
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -52,6 +52,9 @@ var init_strings = __esm({
|
|
|
52
52
|
approvalDeny: "\u62D2\u7EDD",
|
|
53
53
|
errAborted: "\u5DF2\u4E2D\u6B62\u5F53\u524D\u8F6E\u3002",
|
|
54
54
|
busyBlockSend: "\u23F3 \u6B63\u5728\u751F\u6210\uFF0C\u8BF7\u7B49\u5F85\u6216\u6309 Esc \u4E2D\u6B62\u540E\u518D\u53D1\u9001\u3002",
|
|
55
|
+
inboxQueued: "\u{1F4EE} \u5DF2\u6392\u961F\uFF0C\u5C06\u5728\u56DE\u5408\u8FB9\u754C\u9001\u8FBE\u6A21\u578B\uFF08Esc \u4ECD\u53EF\u4E2D\u6B62\uFF09\u3002",
|
|
56
|
+
inboxClaimed: (n) => `\u{1F4EC} \u5DF2\u9001\u8FBE\u6A21\u578B\uFF08${n} \u6761\u8865\u5145\u8F93\u5165\uFF09`,
|
|
57
|
+
inboxCarryover: (n) => `\u{1F4EC} \u672C\u8F6E\u7ED3\u675F\u65F6 ${n} \u6761\u8865\u5145\u8F93\u5165\u672A\u5728\u56DE\u5408\u8FB9\u754C\u9001\u8FBE\uFF0C\u5DF2\u5199\u5165\u4F1A\u8BDD\u5386\u53F2\uFF0C\u4E0B\u8F6E\u5BF9\u8BDD\u81EA\u52A8\u5E26\u5165\u3002`,
|
|
55
58
|
cmdHint: "/help \u5E2E\u52A9 \xB7 /plan \u8BA1\u5212\u6A21\u5F0F \xB7 /auto \u81EA\u52A8\u6A21\u5F0F \xB7 /model \u5207\u6362\u6A21\u578B \xB7 /thinking \u601D\u8003\u7B49\u7EA7 \xB7 /lang \u8BED\u8A00 \xB7 /sessions \u5386\u53F2 \xB7 /clear \u6E05\u5C4F \xB7 /exit \u9000\u51FA",
|
|
56
59
|
statusStreaming: "\u751F\u6210\u4E2D",
|
|
57
60
|
statusIdle: "\u5C31\u7EEA",
|
|
@@ -98,6 +101,12 @@ ${dim} \u8FD0\u884C npm i -g deepseeker-code@latest \u66F4\u65B0${reset}
|
|
|
98
101
|
sessionsPrompt: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u8F7D\u5165 \xB7 Esc \u53D6\u6D88",
|
|
99
102
|
noHistory: "\uFF08\u6682\u65E0\u5386\u53F2\u4F1A\u8BDD\uFF09",
|
|
100
103
|
sessionLoaded: (id) => `\u5DF2\u8F7D\u5165\u4F1A\u8BDD ${id}\uFF08\u7EE7\u7EED\u5BF9\u8BDD\u5C06\u7EED\u63A5\u6B64\u4F1A\u8BDD\uFF09`,
|
|
104
|
+
cmdFork: "\u4ECE\u5F53\u524D\u4F1A\u8BDD\u7684\u67D0\u8F6E\u56DE\u590D\u5904\u5206\u53C9\u51FA\u65B0\u4F1A\u8BDD",
|
|
105
|
+
forkTitle: "\u{1F33F} \u5206\u53C9\u5F53\u524D\u4F1A\u8BDD\uFF08\u2191\u2193 \u9009\u68C0\u67E5\u70B9 \xB7 Enter \u5206\u53C9\uFF09",
|
|
106
|
+
forkPrompt: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u5206\u53C9 \xB7 Esc \u53D6\u6D88",
|
|
107
|
+
forkEmpty: "\uFF08\u5F53\u524D\u4F1A\u8BDD\u6682\u65E0\u53EF\u5206\u53C9\u7684\u68C0\u67E5\u70B9\uFF09",
|
|
108
|
+
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`,
|
|
109
|
+
forkFailed: (e) => `\u5206\u53C9\u5931\u8D25\uFF1A${e}`,
|
|
101
110
|
relTime: (iso) => {
|
|
102
111
|
const t = Date.parse(iso);
|
|
103
112
|
if (!t) return "";
|
|
@@ -133,6 +142,7 @@ DeepSeeker-Code \u5C06\u5728\u6B64\u76EE\u5F55\u8BFB\u53D6\u3001\u7F16\u8F91\u54
|
|
|
133
142
|
`/thinking [off|high|max] \u2014 \u5207\u6362\u601D\u8003\u7B49\u7EA7\uFF08\u5F53\u524D ${thinking}\uFF09`,
|
|
134
143
|
`/lang [zh|en] \u2014 \u5207\u6362\u754C\u9762\u8BED\u8A00\uFF08\u5F53\u524D ${locale2}\uFF09`,
|
|
135
144
|
"/sessions \u2014 \u9009\u62E9\u5E76\u8F7D\u5165\u5386\u53F2\u4F1A\u8BDD\uFF08\u7EED\u63A5\u5BF9\u8BDD\uFF09",
|
|
145
|
+
"/fork \u2014 \u4ECE\u5F53\u524D\u4F1A\u8BDD\u7684\u67D0\u8F6E\u56DE\u590D\u5904\u5206\u53C9\u51FA\u65B0\u4F1A\u8BDD",
|
|
136
146
|
"/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",
|
|
137
147
|
"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"
|
|
138
148
|
].join("\n"),
|
|
@@ -180,6 +190,9 @@ DeepSeeker-Code \u5C06\u5728\u6B64\u76EE\u5F55\u8BFB\u53D6\u3001\u7F16\u8F91\u54
|
|
|
180
190
|
approvalDeny: "Deny",
|
|
181
191
|
errAborted: "Aborted current turn.",
|
|
182
192
|
busyBlockSend: "\u23F3 Still generating\u2014wait or press Esc to abort before sending.",
|
|
193
|
+
inboxQueued: "\u{1F4EE} Queued\u2014will reach the model at the next round boundary (Esc still aborts).",
|
|
194
|
+
inboxClaimed: (n) => `\u{1F4EC} Delivered to the model (${n} supplemental input${n > 1 ? "s" : ""})`,
|
|
195
|
+
inboxCarryover: (n) => `\u{1F4EC} ${n} supplemental input${n > 1 ? "s" : ""} not claimed at a round boundary before this run ended; saved to session history and carried into the next turn.`,
|
|
183
196
|
cmdHint: "/help help \xB7 /plan plan mode \xB7 /auto auto mode \xB7 /model switch model \xB7 /thinking thinking level \xB7 /lang language \xB7 /sessions history \xB7 /clear clear \xB7 /exit quit",
|
|
184
197
|
statusStreaming: "streaming",
|
|
185
198
|
statusIdle: "ready",
|
|
@@ -226,6 +239,12 @@ ${dim} Run npm i -g deepseeker-code@latest to update${reset}
|
|
|
226
239
|
sessionsPrompt: "\u2191\u2193 pick \xB7 Enter resume \xB7 Esc cancel",
|
|
227
240
|
noHistory: "(no past sessions)",
|
|
228
241
|
sessionLoaded: (id) => `Resumed session ${id} (new messages continue it)`,
|
|
242
|
+
cmdFork: "Fork the current session from a past round",
|
|
243
|
+
forkTitle: "\u{1F33F} Fork current session (\u2191\u2193 to pick \xB7 Enter to fork)",
|
|
244
|
+
forkPrompt: "\u2191\u2193 pick \xB7 Enter fork \xB7 Esc cancel",
|
|
245
|
+
forkEmpty: "(no checkpoints to fork from in this session)",
|
|
246
|
+
forkDone: (id, roundNo) => `Forked from round ${roundNo} \u2192 new session ${id} (loaded; new messages go to the fork, source untouched)`,
|
|
247
|
+
forkFailed: (e) => `Fork failed: ${e}`,
|
|
229
248
|
relTime: (iso) => {
|
|
230
249
|
const t = Date.parse(iso);
|
|
231
250
|
if (!t) return "";
|
|
@@ -261,6 +280,7 @@ DeepSeeker-Code will read, edit, and execute files here.`,
|
|
|
261
280
|
`/thinking [off|high|max] \u2014 Switch thinking level (current ${thinking})`,
|
|
262
281
|
`/lang [zh|en] \u2014 Switch interface language (current ${locale2})`,
|
|
263
282
|
"/sessions \u2014 Pick a past session to resume",
|
|
283
|
+
"/fork \u2014 Fork the current session from a past round",
|
|
264
284
|
"/trust \u2014 Manage trusted dirs (project hooks/skills load only when trusted; CI uses --trust)",
|
|
265
285
|
"Ctrl+C exit \xB7 Esc abort/clear input \xB7 Ctrl+G abort \xB7 Ctrl+T toggle thinking"
|
|
266
286
|
].join("\n"),
|
|
@@ -281,7 +301,7 @@ DeepSeeker-Code will read, edit, and execute files here.`,
|
|
|
281
301
|
S = new Proxy({}, {
|
|
282
302
|
get: (_t, k) => typeof k === "string" ? STRINGS[locale][k] : void 0
|
|
283
303
|
});
|
|
284
|
-
LOCAL_COMMAND_NAMES = ["help", "status", "plan", "auto", "model", "thinking", "lang", "output-style", "sessions", "usage", "context", "permissions", "mcp", "hooks", "trust", "debug", "clear", "exit"];
|
|
304
|
+
LOCAL_COMMAND_NAMES = ["help", "status", "plan", "auto", "model", "thinking", "lang", "output-style", "sessions", "fork", "usage", "context", "permissions", "mcp", "hooks", "trust", "debug", "clear", "exit"];
|
|
285
305
|
}
|
|
286
306
|
});
|
|
287
307
|
|
|
@@ -289,13 +309,15 @@ DeepSeeker-Code will read, edit, and execute files here.`,
|
|
|
289
309
|
var config_exports = {};
|
|
290
310
|
__export(config_exports, {
|
|
291
311
|
ENGINE_OVERRIDE_KEYS: () => ENGINE_OVERRIDE_KEYS,
|
|
292
|
-
|
|
312
|
+
ENV_SOURCES: () => ENV_SOURCES,
|
|
313
|
+
appConfig: () => appConfig,
|
|
314
|
+
configSources: () => configSources
|
|
293
315
|
});
|
|
294
316
|
import path2 from "path";
|
|
295
317
|
import os2 from "os";
|
|
296
318
|
import { readFileSync as readFileSync2 } from "fs";
|
|
297
319
|
import { createHash } from "crypto";
|
|
298
|
-
var _DataDir, appConfig, ENGINE_OVERRIDE_VALIDATORS, ENGINE_OVERRIDE_KEYS, applyEngineOverrides;
|
|
320
|
+
var _DataDir, appConfig, ENV_SOURCES, configSources, ENGINE_OVERRIDE_VALIDATORS, ENGINE_OVERRIDE_KEYS, applyEngineOverrides;
|
|
299
321
|
var init_config = __esm({
|
|
300
322
|
"../core/src/config/index.ts"() {
|
|
301
323
|
"use strict";
|
|
@@ -370,8 +392,51 @@ var init_config = __esm({
|
|
|
370
392
|
workflowMaxSteps: Number(process.env.DEEP_SEEK_WORKFLOW_MAX_STEPS) || 8,
|
|
371
393
|
/** P0-3 run_workflow 单个子 agent 结果的字符预算:超出按头尾截断,避免单个巨型结果挤占聚合输出。
|
|
372
394
|
* 最终聚合再受工具 maxOutputCharacters 兜底。 */
|
|
373
|
-
workflowPerStepChars: 6e3
|
|
374
|
-
|
|
395
|
+
workflowPerStepChars: 6e3,
|
|
396
|
+
/** ★ P0-A 计划模式强制位置:'runtime'(默认)= 工具表全会话恒定(两态统一 appendPlanControlTools),
|
|
397
|
+
* 计划期写工具由 processToolCall 执行层按 PLAN_ALLOWED_TOOLS 拒绝——保 DeepSeek 前缀缓存
|
|
398
|
+
* (tools 参数序列化在请求头部,计划模式裁表翻转 = 全会话历史 re-prefill 两次);
|
|
399
|
+
* 'schema' = 旧路径(计划期裁成只读白名单子表)。env DEEP_SEEK_PLAN_ENFORCEMENT=schema 回退。 */
|
|
400
|
+
planEnforcement: process.env.DEEP_SEEK_PLAN_ENFORCEMENT === "schema" ? "schema" : "runtime",
|
|
401
|
+
/** ★ P1-MCP 工具暴露模式:'dispatcher'(默认)= 只注入恒定 schema 的 mcp_list_tools + mcp_call
|
|
402
|
+
* (目录按需查、审批/权限用合成名 mcp__<server>__<tool> 匹配既有规则;工具表不随 MCP server
|
|
403
|
+
* 工具数增长,L1 恒定);'schemas' = 旧路径(每个 MCP 工具独立 schema 常驻)。
|
|
404
|
+
* env DEEP_SEEK_MCP_EXPOSE_MODE=schemas 回退。 */
|
|
405
|
+
mcpExposeMode: process.env.DEEP_SEEK_MCP_EXPOSE_MODE === "schemas" ? "schemas" : "dispatcher",
|
|
406
|
+
/** ★ 事件日志化:transcript 追加 turn/step 边界事件行(run.start/round.end/run.end/compaction/
|
|
407
|
+
* run.abandoned,带 dscEvent 标记,readMessages 自动过滤)——崩溃恢复从盲扫启发式升级为事件确认,
|
|
408
|
+
* compaction 事件携带归档计数+摘要文本使 transcript 自包含(分叉/审计不再押 state.json 单点)。
|
|
409
|
+
* 关闭时 appendEvent 全 no-op、恢复走纯内存启发式(= 改造前行为)。env DEEP_SEEK_TRANSCRIPT_EVENTS=0 回退。 */
|
|
410
|
+
transcriptEvents: process.env.DEEP_SEEK_TRANSCRIPT_EVENTS !== "0",
|
|
411
|
+
/** ★ inbox steering(第二梯队 #2):agent 运行中用户补充输入进 per-session 队列,runAgent 回合边界
|
|
412
|
+
* claim 注入上下文并落盘 transcript(长任务中途补指示不再只能中止重跑)。
|
|
413
|
+
* 关闭时全链路 no-op:端点 501 / CLI 回退 busy 提示 / VSCode 不排队。env DEEP_SEEK_INBOX=0 回退。 */
|
|
414
|
+
inboxSteering: process.env.DEEP_SEEK_INBOX !== "0",
|
|
415
|
+
/** ★ hooks 改写型拦截点(第二梯队 #3):PreToolUse 可改 args(argsOverride,前置到全部安全门禁
|
|
416
|
+
* 之前生效——门禁/审批评估的是改写后参数)、PostToolUse 可改 resultForModel(仅模型视图,
|
|
417
|
+
* 用户视图 resultForUser 不变)。关闭时 PreToolUse 回退旧位(审批后、仅 deny),改写字段全忽略,
|
|
418
|
+
* 行为与改造前一致。env DEEP_SEEK_HOOK_REWRITE=0 回退。 */
|
|
419
|
+
hookRewrite: process.env.DEEP_SEEK_HOOK_REWRITE !== "0"
|
|
420
|
+
};
|
|
421
|
+
ENV_SOURCES = {
|
|
422
|
+
dataDir: "DEEPSEEKER_CODE_DATA_DIR",
|
|
423
|
+
searchProvider: "SEARCH_PROVIDER",
|
|
424
|
+
tavilyApiKey: "TAVILY_API_KEY",
|
|
425
|
+
webFetchAllowPrivate: "WEB_FETCH_ALLOW_PRIVATE",
|
|
426
|
+
parallelSafeTools: "DEEP_SEEK_PARALLEL_SAFE_TOOLS",
|
|
427
|
+
workflowConcurrency: "DEEP_SEEK_WORKFLOW_CONCURRENCY",
|
|
428
|
+
workflowMaxSteps: "DEEP_SEEK_WORKFLOW_MAX_STEPS",
|
|
429
|
+
planEnforcement: "DEEP_SEEK_PLAN_ENFORCEMENT",
|
|
430
|
+
mcpExposeMode: "DEEP_SEEK_MCP_EXPOSE_MODE",
|
|
431
|
+
transcriptEvents: "DEEP_SEEK_TRANSCRIPT_EVENTS",
|
|
432
|
+
inboxSteering: "DEEP_SEEK_INBOX",
|
|
433
|
+
hookRewrite: "DEEP_SEEK_HOOK_REWRITE"
|
|
434
|
+
};
|
|
435
|
+
configSources = {};
|
|
436
|
+
for (const [key, envName] of Object.entries(ENV_SOURCES)) {
|
|
437
|
+
const v = process.env[envName];
|
|
438
|
+
if (v !== void 0 && v !== "") configSources[key] = "env";
|
|
439
|
+
}
|
|
375
440
|
ENGINE_OVERRIDE_VALIDATORS = {
|
|
376
441
|
undoEnabled: (v) => typeof v === "boolean" ? v : void 0,
|
|
377
442
|
undoBackupSensitive: (v) => v === "skip" || v === "deny" || v === "allow" ? v : void 0,
|
|
@@ -382,12 +447,12 @@ var init_config = __esm({
|
|
|
382
447
|
ENGINE_OVERRIDE_KEYS = Object.keys(ENGINE_OVERRIDE_VALIDATORS);
|
|
383
448
|
applyEngineOverrides = (cfg) => {
|
|
384
449
|
const paths = [
|
|
385
|
-
path2.join(cfg.dataDir, "settings.json"),
|
|
450
|
+
{ scope: "settings.global", configPath: path2.join(cfg.dataDir, "settings.json") },
|
|
386
451
|
// 全局用户级
|
|
387
|
-
path2.join(process.cwd(), ".deepseeker-code", "settings.json")
|
|
452
|
+
{ scope: "settings.project", configPath: path2.join(process.cwd(), ".deepseeker-code", "settings.json") }
|
|
388
453
|
// 项目级(覆盖全局)
|
|
389
454
|
];
|
|
390
|
-
for (const configPath of paths) {
|
|
455
|
+
for (const { scope, configPath } of paths) {
|
|
391
456
|
let raw;
|
|
392
457
|
try {
|
|
393
458
|
raw = readFileSync2(configPath, "utf-8");
|
|
@@ -411,6 +476,7 @@ var init_config = __esm({
|
|
|
411
476
|
continue;
|
|
412
477
|
}
|
|
413
478
|
cfg[key] = valid;
|
|
479
|
+
configSources[key] = scope;
|
|
414
480
|
}
|
|
415
481
|
}
|
|
416
482
|
};
|
|
@@ -491,13 +557,20 @@ var init_common = __esm({
|
|
|
491
557
|
injectMarkedBlock = (message, fence, body) => {
|
|
492
558
|
const sys = message[0];
|
|
493
559
|
if (!sys || sys.role !== "system" || typeof sys.content !== "string") return;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
sys.content += `
|
|
560
|
+
const idx = sys.content.indexOf(fence);
|
|
561
|
+
if (idx === -1) {
|
|
562
|
+
sys.content += `
|
|
498
563
|
|
|
499
564
|
${fence}
|
|
500
565
|
${body}`;
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const after = sys.content.slice(idx + fence.length);
|
|
569
|
+
const next = after.match(/\n\n⟦[^\n]*?⟧/);
|
|
570
|
+
const oldBody = next?.index !== void 0 ? after.slice(0, next.index) : after;
|
|
571
|
+
if (oldBody === `
|
|
572
|
+
${body}`) return;
|
|
573
|
+
console.warn(`\u26A0\uFE0F ${fence} \u5DF2\u9501\u5B9A\uFF08\u4F1A\u8BDD\u5185\u4E0D\u53D8\u4EE5\u4FDD\u524D\u7F00\u7F13\u5B58\uFF09\uFF0C\u5185\u5BB9\u53D8\u5316\u5C06\u4E8E\u4E0B\u4E2A\u65B0\u4F1A\u8BDD\u751F\u6548\u3002`);
|
|
501
574
|
};
|
|
502
575
|
decodeProcessOutput = (buf) => {
|
|
503
576
|
const utf8 = buf.toString("utf8");
|
|
@@ -811,17 +884,120 @@ var init_deepseek = __esm({
|
|
|
811
884
|
}
|
|
812
885
|
});
|
|
813
886
|
|
|
887
|
+
// ../core/src/llm/providers/replay/index.ts
|
|
888
|
+
var createReplayProvider;
|
|
889
|
+
var init_replay = __esm({
|
|
890
|
+
"../core/src/llm/providers/replay/index.ts"() {
|
|
891
|
+
"use strict";
|
|
892
|
+
createReplayProvider = (script) => {
|
|
893
|
+
const turns = Array.isArray(script.turns) ? script.turns : [];
|
|
894
|
+
let cursor = 0;
|
|
895
|
+
let attemptCounts = [];
|
|
896
|
+
const calls = [];
|
|
897
|
+
const makeFaultError = (fault) => {
|
|
898
|
+
const msg = fault.message ?? (fault.type === "idle" ? "stream_idle_timeout" : fault.type === "context_length" ? "context_length_exceeded\uFF08\u56DE\u653E\u6CE8\u5165\uFF09" : fault.type === "transient" ? "HTTP 429 rate limit\uFF08\u56DE\u653E\u6CE8\u5165\uFF09" : "\u56DE\u653E\u81F4\u547D\u9519\u8BEF\uFF08fatal\uFF09");
|
|
899
|
+
const e = new Error(msg);
|
|
900
|
+
e.replayFault = fault.type;
|
|
901
|
+
if (fault.type === "transient") e.status = 429;
|
|
902
|
+
return e;
|
|
903
|
+
};
|
|
904
|
+
const streamChat2 = async function* (messages, tools, opts) {
|
|
905
|
+
const turnIndex = cursor;
|
|
906
|
+
calls.push({ turnIndex, messages: [...messages], tools, opts });
|
|
907
|
+
const turn = turns[turnIndex];
|
|
908
|
+
if (!turn) return;
|
|
909
|
+
const attempt = attemptCounts[turnIndex] ?? 0;
|
|
910
|
+
attemptCounts[turnIndex] = attempt + 1;
|
|
911
|
+
const fault = turn.fault;
|
|
912
|
+
const shouldFault = !!fault && (fault.type === "fatal" || attempt < (fault.times ?? 1));
|
|
913
|
+
if (shouldFault) {
|
|
914
|
+
const f = fault;
|
|
915
|
+
const n = Math.min(f.afterChars ?? 0, (turn.content ?? "").length);
|
|
916
|
+
if (n > 0) yield { kind: "text", text: (turn.content ?? "").slice(0, n) };
|
|
917
|
+
throw makeFaultError(f);
|
|
918
|
+
}
|
|
919
|
+
if (turn.reasoning) yield { kind: "reasoning", text: turn.reasoning };
|
|
920
|
+
if (turn.content) yield { kind: "text", text: turn.content };
|
|
921
|
+
const tcs = turn.toolCalls ?? [];
|
|
922
|
+
for (let i = 0; i < tcs.length; i++) {
|
|
923
|
+
const tc = tcs[i];
|
|
924
|
+
yield { kind: "tool_call_delta", index: i, id: tc.id ?? `call_replay_${turnIndex}_${i}`, type: "function", nameDelta: tc.name };
|
|
925
|
+
yield { kind: "tool_call_delta", index: i, argumentsDelta: JSON.stringify(tc.args ?? {}) };
|
|
926
|
+
}
|
|
927
|
+
if (turn.usage) yield { kind: "usage", usage: turn.usage };
|
|
928
|
+
cursor++;
|
|
929
|
+
};
|
|
930
|
+
const summarize2 = async (_messages, _tools, _opts) => ({
|
|
931
|
+
id: "replay-summary",
|
|
932
|
+
object: "chat.completion",
|
|
933
|
+
created: 0,
|
|
934
|
+
// 确定性:固定值(回放产物不掺时间戳,diff 稳定)
|
|
935
|
+
model: "replay",
|
|
936
|
+
choices: [{
|
|
937
|
+
index: 0,
|
|
938
|
+
message: { role: "assistant", content: script.summarizeText ?? "\uFF08\u56DE\u653E\u6458\u8981\uFF1A\u538B\u7F29\u8DEF\u5F84\u7684\u786E\u5B9A\u6027\u5360\u4F4D\u6587\u672C\uFF09", refusal: null },
|
|
939
|
+
finish_reason: "stop",
|
|
940
|
+
logprobs: null
|
|
941
|
+
}],
|
|
942
|
+
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
943
|
+
});
|
|
944
|
+
return {
|
|
945
|
+
id: "replay",
|
|
946
|
+
displayName: "Replay",
|
|
947
|
+
modelLabel: "Replay",
|
|
948
|
+
streamChat: streamChat2,
|
|
949
|
+
summarize: summarize2,
|
|
950
|
+
classifyRisk: async () => script.riskVerdict ?? "safe",
|
|
951
|
+
buildAssistantMessage: (parts) => {
|
|
952
|
+
const m = { role: "assistant", content: parts.content };
|
|
953
|
+
if (parts.reasoning) m.reasoning_content = parts.reasoning;
|
|
954
|
+
if (parts.toolCalls?.length) m.tool_calls = parts.toolCalls;
|
|
955
|
+
return m;
|
|
956
|
+
},
|
|
957
|
+
// 自识别回放注入的故障(streamInference 经 activeProvider 调用这两个分类器驱动重试通道)
|
|
958
|
+
isContextLengthError: (e) => e?.replayFault === "context_length",
|
|
959
|
+
isTransientError: (e) => e?.replayFault === "transient",
|
|
960
|
+
get calls() {
|
|
961
|
+
return calls;
|
|
962
|
+
},
|
|
963
|
+
get cursor() {
|
|
964
|
+
return cursor;
|
|
965
|
+
},
|
|
966
|
+
reset: () => {
|
|
967
|
+
cursor = 0;
|
|
968
|
+
attemptCounts = [];
|
|
969
|
+
calls.length = 0;
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
|
|
814
976
|
// ../core/src/llm/model.ts
|
|
815
|
-
|
|
977
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
978
|
+
var resolveInitialProvider, activeProvider, chatWithModelWithSummary, classifyToolRisk;
|
|
816
979
|
var init_model = __esm({
|
|
817
980
|
"../core/src/llm/model.ts"() {
|
|
818
981
|
"use strict";
|
|
819
982
|
init_deepseek();
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
983
|
+
init_replay();
|
|
984
|
+
resolveInitialProvider = () => {
|
|
985
|
+
if (process.env.DEEP_SEEK_PROVIDER !== "replay") return deepseekProvider;
|
|
986
|
+
const scriptPath = process.env.DEEP_SEEK_REPLAY_SCRIPT;
|
|
987
|
+
if (!scriptPath) {
|
|
988
|
+
throw new Error("DEEP_SEEK_PROVIDER=replay \u9700\u540C\u65F6\u8BBE\u7F6E DEEP_SEEK_REPLAY_SCRIPT=<script.json>\uFF08\u56DE\u653E\u5267\u672C\u8DEF\u5F84\uFF09");
|
|
989
|
+
}
|
|
990
|
+
let raw;
|
|
991
|
+
try {
|
|
992
|
+
raw = JSON.parse(readFileSync3(scriptPath, "utf-8"));
|
|
993
|
+
} catch (e) {
|
|
994
|
+
throw new Error(`\u56DE\u653E\u5267\u672C\u52A0\u8F7D\u5931\u8D25\uFF08${scriptPath}\uFF09: ${e?.message ?? e}`);
|
|
995
|
+
}
|
|
996
|
+
return createReplayProvider(Array.isArray(raw) ? { turns: raw } : raw);
|
|
997
|
+
};
|
|
998
|
+
activeProvider = resolveInitialProvider();
|
|
999
|
+
chatWithModelWithSummary = (messages, tools, opts) => activeProvider.summarize(messages, tools, opts);
|
|
1000
|
+
classifyToolRisk = (toolName, args, detail, signal) => activeProvider.classifyRisk(toolName, args, detail, signal);
|
|
825
1001
|
}
|
|
826
1002
|
});
|
|
827
1003
|
|
|
@@ -1139,6 +1315,7 @@ var init_store = __esm({
|
|
|
1139
1315
|
} catch {
|
|
1140
1316
|
continue;
|
|
1141
1317
|
}
|
|
1318
|
+
if (typeof msg?.dscEvent === "string") continue;
|
|
1142
1319
|
messageCount++;
|
|
1143
1320
|
if (!preview && msg?.role === "user" && typeof msg.content === "string") {
|
|
1144
1321
|
const t = msg.content.replace(/\s+/g, " ").trim();
|
|
@@ -1193,6 +1370,87 @@ var init_store = __esm({
|
|
|
1193
1370
|
}
|
|
1194
1371
|
});
|
|
1195
1372
|
|
|
1373
|
+
// ../core/src/session/transcript.ts
|
|
1374
|
+
import fs5 from "fs/promises";
|
|
1375
|
+
var isEventLine, readTranscriptLines, appendLineWithRetry, appendMessage, appendEvent;
|
|
1376
|
+
var init_transcript = __esm({
|
|
1377
|
+
"../core/src/session/transcript.ts"() {
|
|
1378
|
+
"use strict";
|
|
1379
|
+
init_store();
|
|
1380
|
+
init_common();
|
|
1381
|
+
init_config();
|
|
1382
|
+
isEventLine = (l) => typeof l?.dscEvent === "string";
|
|
1383
|
+
readTranscriptLines = async (sessionId) => {
|
|
1384
|
+
await ensureSessionsDir(sessionId);
|
|
1385
|
+
const p = getTranscriptPath(sessionId);
|
|
1386
|
+
try {
|
|
1387
|
+
const text = await fs5.readFile(p, "utf-8");
|
|
1388
|
+
const lines = [];
|
|
1389
|
+
for (const s of text.split("\n")) {
|
|
1390
|
+
const line = s.trim();
|
|
1391
|
+
if (line.length === 0) continue;
|
|
1392
|
+
try {
|
|
1393
|
+
lines.push(JSON.parse(line));
|
|
1394
|
+
} catch {
|
|
1395
|
+
console.warn(`\u26A0\uFE0F [transcript] \u8DF3\u8FC7\u65E0\u6CD5\u89E3\u6790\u7684\u635F\u574F\u884C: ${line.slice(0, 120)}`);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
return lines;
|
|
1399
|
+
} catch (err) {
|
|
1400
|
+
if (err?.code === "ENOENT") return [];
|
|
1401
|
+
throw err;
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
appendLineWithRetry = async (p, payload) => {
|
|
1405
|
+
const MAX_ATTEMPTS = 3;
|
|
1406
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
1407
|
+
let beforeSize = 0;
|
|
1408
|
+
try {
|
|
1409
|
+
beforeSize = (await fs5.stat(p)).size;
|
|
1410
|
+
} catch {
|
|
1411
|
+
}
|
|
1412
|
+
try {
|
|
1413
|
+
await fs5.appendFile(p, payload, "utf-8");
|
|
1414
|
+
return;
|
|
1415
|
+
} catch (e) {
|
|
1416
|
+
try {
|
|
1417
|
+
await fs5.truncate(p, beforeSize);
|
|
1418
|
+
} catch {
|
|
1419
|
+
}
|
|
1420
|
+
if (attempt === MAX_ATTEMPTS) throw e;
|
|
1421
|
+
await new Promise((r) => setTimeout(r, 50 * attempt));
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
appendMessage = async (entry) => {
|
|
1426
|
+
try {
|
|
1427
|
+
await ensureSessionsDir(entry.sessionId);
|
|
1428
|
+
const { sessionId, ...rest } = entry;
|
|
1429
|
+
const line = { id: createUUID(), ...rest };
|
|
1430
|
+
const p = getTranscriptPath(sessionId);
|
|
1431
|
+
const payload = JSON.stringify(line) + "\n";
|
|
1432
|
+
if (payload.includes("\uFFFD")) {
|
|
1433
|
+
const fffdCount = (payload.match(/\uFFFD/g) || []).length;
|
|
1434
|
+
console.warn(`\u26A0\uFE0F [transcript] \u6D88\u606F\u542B ${fffdCount} \u4E2A\u4E71\u7801\u5B57\u7B26\uFF08U+FFFD\uFF09\uFF0C\u5DF2\u539F\u6837\u5199\u5165\u5386\u53F2\u3002\u6765\u6E90\u53EF\u80FD\u662F GBK \u5B50\u8FDB\u7A0B\u8F93\u51FA\u6216\u4E71\u7801\u7C98\u8D34\u3002`);
|
|
1435
|
+
}
|
|
1436
|
+
await appendLineWithRetry(p, payload);
|
|
1437
|
+
} catch (e) {
|
|
1438
|
+
console.warn(`\u26A0\uFE0F \u6D88\u606F\u843D\u76D8\u5931\u8D25\uFF08\u5DF2\u91CD\u8BD5 3 \u6B21\uFF0C\u5185\u5B58\u4E0E\u78C1\u76D8\u8F6C\u5F55\u53EF\u80FD\u4E0D\u4E00\u81F4\uFF09:`, e);
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
appendEvent = async (sessionId, ev) => {
|
|
1442
|
+
if (!appConfig.transcriptEvents) return;
|
|
1443
|
+
try {
|
|
1444
|
+
await ensureSessionsDir(sessionId);
|
|
1445
|
+
const line = { id: createUUID(), ts: (/* @__PURE__ */ new Date()).toISOString(), ...ev };
|
|
1446
|
+
await appendLineWithRetry(getTranscriptPath(sessionId), JSON.stringify(line) + "\n");
|
|
1447
|
+
} catch (e) {
|
|
1448
|
+
console.warn(`\u26A0\uFE0F [transcript] \u4E8B\u4EF6\u884C\u843D\u76D8\u5931\u8D25\uFF08${ev.dscEvent}\uFF0C\u6062\u590D/\u5206\u53C9\u5C06\u56DE\u9000\u542F\u53D1\u5F0F\uFF09:`, e);
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1196
1454
|
// ../core/src/hooks/types.ts
|
|
1197
1455
|
var INTERCEPTABLE_EVENTS, TOOL_EVENTS, ALL_EVENTS, DEFAULT_TIMEOUT_BY_EVENT;
|
|
1198
1456
|
var init_types = __esm({
|
|
@@ -1340,12 +1598,13 @@ var init_shellExecutor = __esm({
|
|
|
1340
1598
|
});
|
|
1341
1599
|
|
|
1342
1600
|
// ../core/src/hooks/registry.ts
|
|
1343
|
-
var rules, matches, registerHooks, listHooks, dispatch, runPreHooks, HOOK_RESULT_MAX, truncateForHook, runPostHooks;
|
|
1601
|
+
var rules, matches, registerHooks, listHooks, isPlainObject, dispatch, runPreHooks, HOOK_RESULT_MAX, truncateForHook, runPostHooks;
|
|
1344
1602
|
var init_registry = __esm({
|
|
1345
1603
|
"../core/src/hooks/registry.ts"() {
|
|
1346
1604
|
"use strict";
|
|
1347
1605
|
init_types();
|
|
1348
1606
|
init_shellExecutor();
|
|
1607
|
+
init_config();
|
|
1349
1608
|
rules = [];
|
|
1350
1609
|
matches = (matcher, toolName) => {
|
|
1351
1610
|
if (typeof matcher === "string") return matcher === "*" || matcher === toolName;
|
|
@@ -1368,6 +1627,7 @@ var init_registry = __esm({
|
|
|
1368
1627
|
};
|
|
1369
1628
|
return rules.map((r) => ({ event: r.event, matcher: strMatcher(r.matcher), source: r.source, onError: r.onError }));
|
|
1370
1629
|
};
|
|
1630
|
+
isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
1371
1631
|
dispatch = async (event, ctx) => {
|
|
1372
1632
|
const interceptable = INTERCEPTABLE_EVENTS.has(event);
|
|
1373
1633
|
const isToolEvent = TOOL_EVENTS.has(event);
|
|
@@ -1395,14 +1655,29 @@ var init_registry = __esm({
|
|
|
1395
1655
|
)
|
|
1396
1656
|
));
|
|
1397
1657
|
const additions2 = [];
|
|
1398
|
-
|
|
1399
|
-
|
|
1658
|
+
let resultOverride;
|
|
1659
|
+
for (const r of results) {
|
|
1660
|
+
if (r?.contextAdditions) additions2.push(...r.contextAdditions);
|
|
1661
|
+
if (event === "PostToolUse" && appConfig.hookRewrite && typeof r?.resultOverride === "string" && r.resultOverride) {
|
|
1662
|
+
resultOverride = r.resultOverride;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
return { ...pack(additions2), resultOverride };
|
|
1400
1666
|
}
|
|
1401
1667
|
const additions = [];
|
|
1668
|
+
let argsOverride = void 0;
|
|
1402
1669
|
for (const rule of matched) {
|
|
1403
1670
|
try {
|
|
1404
1671
|
const res = await rule.run(ctx);
|
|
1405
1672
|
if (res?.contextAdditions) additions.push(...res.contextAdditions);
|
|
1673
|
+
if (event === "PreToolUse" && appConfig.hookRewrite && res?.argsOverride !== void 0) {
|
|
1674
|
+
if (isPlainObject(res.argsOverride)) {
|
|
1675
|
+
ctx.args = res.argsOverride;
|
|
1676
|
+
argsOverride = res.argsOverride;
|
|
1677
|
+
} else {
|
|
1678
|
+
console.warn(`\u26A0\uFE0F [hook:PreToolUse] argsOverride \u987B\u4E3A\u666E\u901A\u5BF9\u8C61\uFF0C\u5DF2\u5FFD\u7565`);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1406
1681
|
if (res && res.deny) {
|
|
1407
1682
|
return { deny: true, reason: res.reason, contextAdditions: additions.length > 0 ? additions : void 0 };
|
|
1408
1683
|
}
|
|
@@ -1415,7 +1690,7 @@ var init_registry = __esm({
|
|
|
1415
1690
|
console.warn(`\u26A0\uFE0F [hook:${event}] \u6267\u884C\u5F02\u5E38\uFF0C\u6309\u653E\u884C\u5904\u7406: ${msg}`);
|
|
1416
1691
|
}
|
|
1417
1692
|
}
|
|
1418
|
-
return pack(additions);
|
|
1693
|
+
return { ...pack(additions), argsOverride };
|
|
1419
1694
|
};
|
|
1420
1695
|
runPreHooks = async (toolName, args, ctx) => {
|
|
1421
1696
|
return dispatch("PreToolUse", {
|
|
@@ -1431,7 +1706,7 @@ var init_registry = __esm({
|
|
|
1431
1706
|
truncateForHook = (s) => s.length <= HOOK_RESULT_MAX ? s : s.slice(0, HOOK_RESULT_MAX) + `
|
|
1432
1707
|
\u2026[hook \u89C6\u56FE\u622A\u65AD\uFF0C\u5171 ${s.length} \u5B57\u7B26]`;
|
|
1433
1708
|
runPostHooks = async (toolName, args, result, ctx) => {
|
|
1434
|
-
|
|
1709
|
+
return dispatch("PostToolUse", {
|
|
1435
1710
|
sessionId: ctx.sessionId,
|
|
1436
1711
|
cwd: ctx.cwd,
|
|
1437
1712
|
env: ctx.env,
|
|
@@ -1475,6 +1750,7 @@ var init_truncate = __esm({
|
|
|
1475
1750
|
init_model();
|
|
1476
1751
|
init_contextCore();
|
|
1477
1752
|
init_store();
|
|
1753
|
+
init_transcript();
|
|
1478
1754
|
init_hooks();
|
|
1479
1755
|
ANSI_ESCAPE = /\u001b\[[\d;?]*[ -/]*[@-~]|\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g;
|
|
1480
1756
|
getFileAccessRoots = () => {
|
|
@@ -1630,6 +1906,11 @@ ${line}` : line;
|
|
|
1630
1906
|
consecutiveFailures: 0,
|
|
1631
1907
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1632
1908
|
});
|
|
1909
|
+
await appendEvent(event.sessionId, {
|
|
1910
|
+
dscEvent: "compaction",
|
|
1911
|
+
archivedMessageCount: store.archivedMessageCount,
|
|
1912
|
+
summary: summaryMsg.content
|
|
1913
|
+
});
|
|
1633
1914
|
} else if (keep > 1) {
|
|
1634
1915
|
keep--;
|
|
1635
1916
|
continue;
|
|
@@ -1644,6 +1925,11 @@ ${line}` : line;
|
|
|
1644
1925
|
consecutiveFailures: 0,
|
|
1645
1926
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1646
1927
|
});
|
|
1928
|
+
await appendEvent(event.sessionId, {
|
|
1929
|
+
dscEvent: "compaction",
|
|
1930
|
+
archivedMessageCount: store.archivedMessageCount,
|
|
1931
|
+
summary: summaryMsg.content
|
|
1932
|
+
});
|
|
1647
1933
|
} else {
|
|
1648
1934
|
break;
|
|
1649
1935
|
}
|
|
@@ -1750,7 +2036,7 @@ ${line}` : line;
|
|
|
1750
2036
|
});
|
|
1751
2037
|
|
|
1752
2038
|
// ../core/src/tool/permissions.ts
|
|
1753
|
-
import
|
|
2039
|
+
import fs6 from "fs/promises";
|
|
1754
2040
|
import path8 from "path";
|
|
1755
2041
|
var PRIMARY_ARG, rules2, trustedProject, globCache, globToRegex, DANGER_COMMAND_TOOLS, compileRule, readPermissionConfig, toolNameMatches, ruleMatches, initPermissions, checkPermission, listPermissionRules, buildScopedAllowRule, resolveSettingsPath, addPermissionRule;
|
|
1756
2042
|
var init_permissions = __esm({
|
|
@@ -1821,7 +2107,7 @@ var init_permissions = __esm({
|
|
|
1821
2107
|
for (const configPath of paths) {
|
|
1822
2108
|
let raw;
|
|
1823
2109
|
try {
|
|
1824
|
-
raw = await
|
|
2110
|
+
raw = await fs6.readFile(configPath, "utf-8");
|
|
1825
2111
|
} catch {
|
|
1826
2112
|
continue;
|
|
1827
2113
|
}
|
|
@@ -1918,7 +2204,7 @@ var init_permissions = __esm({
|
|
|
1918
2204
|
const arr = Array.isArray(cfg.permissions[kind]) ? cfg.permissions[kind] : cfg.permissions[kind] = [];
|
|
1919
2205
|
if (!arr.includes(ruleStr)) {
|
|
1920
2206
|
arr.push(ruleStr);
|
|
1921
|
-
await
|
|
2207
|
+
await fs6.mkdir(path8.dirname(file), { recursive: true });
|
|
1922
2208
|
await atomicWriteJSON(file, cfg);
|
|
1923
2209
|
}
|
|
1924
2210
|
if (!rules2[kind].some((r) => r.raw === compiled.raw)) rules2[kind].push(compiled);
|
|
@@ -1956,7 +2242,7 @@ var init_sessionRegistry = __esm({
|
|
|
1956
2242
|
});
|
|
1957
2243
|
|
|
1958
2244
|
// ../core/src/tool/guard.ts
|
|
1959
|
-
import
|
|
2245
|
+
import fs7 from "fs/promises";
|
|
1960
2246
|
import fsSync from "fs";
|
|
1961
2247
|
import path9 from "path";
|
|
1962
2248
|
import ignore from "ignore";
|
|
@@ -2134,11 +2420,11 @@ var init_guard = __esm({
|
|
|
2134
2420
|
};
|
|
2135
2421
|
scanIgnoreFilesRecursive = async (dirPath, base, engine) => {
|
|
2136
2422
|
try {
|
|
2137
|
-
const entries = await
|
|
2423
|
+
const entries = await fs7.readdir(dirPath, { withFileTypes: true });
|
|
2138
2424
|
for (const entry of entries) {
|
|
2139
2425
|
if (entry.isFile() && (entry.name === ".gitignore" || entry.name === ".agentignore")) {
|
|
2140
2426
|
const fullPath = path9.join(dirPath, entry.name);
|
|
2141
|
-
const content = await
|
|
2427
|
+
const content = await fs7.readFile(fullPath, "utf-8");
|
|
2142
2428
|
const relDir = path9.relative(base, dirPath).replace(/\\/g, "/");
|
|
2143
2429
|
const rules3 = content.split(/\r?\n/).map((line) => {
|
|
2144
2430
|
const trimmed = line.trim();
|
|
@@ -3035,7 +3321,7 @@ ${capped || JSON.stringify(result)}`;
|
|
|
3035
3321
|
});
|
|
3036
3322
|
|
|
3037
3323
|
// ../core/src/tool/mcp/loader.ts
|
|
3038
|
-
import
|
|
3324
|
+
import fs8 from "fs/promises";
|
|
3039
3325
|
import path11 from "path";
|
|
3040
3326
|
import { homedir } from "os";
|
|
3041
3327
|
function sanitize(s) {
|
|
@@ -3045,7 +3331,7 @@ async function readMcpConfig() {
|
|
|
3045
3331
|
const configPath = process.env.MCP_CONFIG || path11.join(appConfig.dataDir, "mcp.json");
|
|
3046
3332
|
let raw;
|
|
3047
3333
|
try {
|
|
3048
|
-
raw = await
|
|
3334
|
+
raw = await fs8.readFile(configPath, "utf-8");
|
|
3049
3335
|
} catch {
|
|
3050
3336
|
return {};
|
|
3051
3337
|
}
|
|
@@ -3136,9 +3422,14 @@ async function loadMcpTools() {
|
|
|
3136
3422
|
}
|
|
3137
3423
|
async function initMcpTools(into) {
|
|
3138
3424
|
const mcpTools = await loadMcpTools();
|
|
3139
|
-
if (
|
|
3425
|
+
if (appConfig.mcpExposeMode === "dispatcher") {
|
|
3426
|
+
if (clients.length > 0) {
|
|
3427
|
+
into.push(...mcpDispatcherTools);
|
|
3428
|
+
console.log(`\u{1F50C} [MCP] dispatcher \u6A21\u5F0F\uFF1A\u6CE8\u5165 mcp_list_tools + mcp_call\uFF08\u8986\u76D6 ${clients.length} \u4E2A server / ${mcpTools.length} \u4E2A\u5DE5\u5177\uFF09`);
|
|
3429
|
+
}
|
|
3430
|
+
} else if (mcpTools.length > 0) {
|
|
3140
3431
|
into.push(...mcpTools);
|
|
3141
|
-
console.log(`\u{1F50C} [MCP] \u5171\u6CE8\u5165 ${mcpTools.length} \u4E2A MCP \u5DE5\u5177`);
|
|
3432
|
+
console.log(`\u{1F50C} [MCP] \u5171\u6CE8\u5165 ${mcpTools.length} \u4E2A MCP \u5DE5\u5177 schema\uFF08schemas \u6A21\u5F0F\uFF09`);
|
|
3142
3433
|
}
|
|
3143
3434
|
if (clients.some((c) => c.supportsResources)) {
|
|
3144
3435
|
into.push(...mcpResourceTools);
|
|
@@ -3174,7 +3465,7 @@ async function listMcpClients() {
|
|
|
3174
3465
|
}
|
|
3175
3466
|
return out;
|
|
3176
3467
|
}
|
|
3177
|
-
var clients, expandPlaceholders, expandServerConfig, mcpResourceTools, mcpPromptTools;
|
|
3468
|
+
var clients, expandPlaceholders, expandServerConfig, mcpResourceTools, mcpPromptTools, resolveMcpPermissionName, mcpDispatcherTools;
|
|
3178
3469
|
var init_loader = __esm({
|
|
3179
3470
|
"../core/src/tool/mcp/loader.ts"() {
|
|
3180
3471
|
"use strict";
|
|
@@ -3338,6 +3629,88 @@ ${text}`;
|
|
|
3338
3629
|
}
|
|
3339
3630
|
}
|
|
3340
3631
|
];
|
|
3632
|
+
resolveMcpPermissionName = (calledName, args) => {
|
|
3633
|
+
if (calledName !== "mcp_call") return calledName;
|
|
3634
|
+
const server = typeof args?.server === "string" ? sanitize(args.server) : "";
|
|
3635
|
+
const tool = typeof args?.tool === "string" ? sanitize(args.tool) : "";
|
|
3636
|
+
return server && tool ? `mcp__${server}__${tool}` : calledName;
|
|
3637
|
+
};
|
|
3638
|
+
mcpDispatcherTools = [
|
|
3639
|
+
{
|
|
3640
|
+
type: "function",
|
|
3641
|
+
function: {
|
|
3642
|
+
name: "mcp_list_tools",
|
|
3643
|
+
description: "\u5217\u51FA\u5DF2\u8FDE\u63A5 MCP server \u7684\u5916\u90E8\u5DE5\u5177\u76EE\u5F55\uFF08server / \u5DE5\u5177\u540D / \u4E00\u884C\u63CF\u8FF0 / \u53C2\u6570\u540D\uFF0C\u5FC5\u586B\u53C2\u6570\u5E26 *\uFF09\u3002\u8C03\u7528 MCP \u5DE5\u5177\u7528 mcp_call\uFF08\u987B\u4F20\u56DE server \u4E0E\u5DE5\u5177\u539F\u540D\uFF09\u3002\u76EE\u5F55\u5B9E\u65F6\u67E5\u8BE2\u2014\u2014\u65B0\u589E server \u540E\u5373\u67E5\u5373\u89C1\uFF0C\u65E0\u9700\u4EFB\u4F55\u53D8\u66F4\u3002",
|
|
3644
|
+
parameters: {
|
|
3645
|
+
type: "object",
|
|
3646
|
+
properties: {
|
|
3647
|
+
server: { type: "string", description: "\u4EC5\u5217\u51FA\u8BE5 server \u7684\u5DE5\u5177\uFF08\u7F3A\u7701\u5217\u51FA\u5168\u90E8\u5DF2\u8FDE\u63A5 server\uFF09" }
|
|
3648
|
+
}
|
|
3649
|
+
},
|
|
3650
|
+
safetyLevel: "safe" /* SAFE */,
|
|
3651
|
+
isSync: true,
|
|
3652
|
+
async execute(args) {
|
|
3653
|
+
const server = args?.server;
|
|
3654
|
+
const lines = [];
|
|
3655
|
+
for (const c of clients) {
|
|
3656
|
+
if (server && c.serverName !== server) continue;
|
|
3657
|
+
try {
|
|
3658
|
+
const ts = await c.listTools();
|
|
3659
|
+
for (const t of ts) {
|
|
3660
|
+
const props = t?.inputSchema?.properties && typeof t.inputSchema.properties === "object" ? Object.keys(t.inputSchema.properties) : [];
|
|
3661
|
+
const required = Array.isArray(t?.inputSchema?.required) ? t.inputSchema.required : [];
|
|
3662
|
+
const argList = props.map((p) => required.includes(p) ? `${p}*` : p).join(", ");
|
|
3663
|
+
lines.push(`- [${c.serverName}] ${t.name}${t.description ? `\uFF1A${String(t.description).split("\n")[0]}` : ""}${argList ? `\uFF08\u53C2\u6570\uFF1A${argList}\uFF09` : ""}`);
|
|
3664
|
+
}
|
|
3665
|
+
} catch (e) {
|
|
3666
|
+
console.warn(`\u26A0\uFE0F [MCP] listTools "${c.serverName}" \u5931\u8D25\uFF08\u5DF2\u8DF3\u8FC7\uFF09: ${e?.message ?? e}`);
|
|
3667
|
+
}
|
|
3668
|
+
}
|
|
3669
|
+
if (lines.length === 0) return server ? `(server "${server}" \u672A\u8FDE\u63A5\u6216\u65E0\u5DE5\u5177)` : "(\u65E0\u5DF2\u8FDE\u63A5\u7684 MCP server)";
|
|
3670
|
+
return `[MCP tools]
|
|
3671
|
+
${lines.join("\n")}`;
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
},
|
|
3675
|
+
{
|
|
3676
|
+
type: "function",
|
|
3677
|
+
function: {
|
|
3678
|
+
name: "mcp_call",
|
|
3679
|
+
description: '\u8C03\u7528\u6307\u5B9A MCP server \u7684\u5916\u90E8\u5DE5\u5177\u3002server \u4E0E\u5DE5\u5177\u540D\u5FC5\u586B\u2014\u2014\u5747\u6765\u81EA mcp_list_tools \u7684\u76EE\u5F55\u8F93\u51FA\uFF08\u7528\u539F\u540D\uFF0C\u4E0D\u8981\u6539\u5199\uFF09\u3002args \u4E3A\u8BE5\u5DE5\u5177\u53C2\u6570\u7684 JSON \u5B57\u7B26\u4E32\uFF08\u53C2\u6570\u540D\u89C1\u76EE\u5F55\uFF1B\u65E0\u53C2\u6570\u4F20 "{}"\uFF09\u3002\u6267\u884C\u5916\u90E8\u903B\u8F91\uFF0C\u6BCF\u6B21\u8C03\u7528\u9700\u5BA1\u6279\u3002',
|
|
3680
|
+
parameters: {
|
|
3681
|
+
type: "object",
|
|
3682
|
+
properties: {
|
|
3683
|
+
server: { type: "string", description: "MCP server \u540D\uFF08\u89C1 mcp_list_tools \u8F93\u51FA\uFF09" },
|
|
3684
|
+
tool: { type: "string", description: "\u5DE5\u5177\u540D\uFF08\u89C1 mcp_list_tools \u8F93\u51FA\uFF0C\u7528\u539F\u540D\uFF09" },
|
|
3685
|
+
args: { type: "string", description: '\u5DE5\u5177\u53C2\u6570\u7684 JSON \u5B57\u7B26\u4E32\uFF0C\u5982 {"query":"..."}\uFF1B\u65E0\u53C2\u6570\u4F20 "{}"' }
|
|
3686
|
+
},
|
|
3687
|
+
required: ["server", "tool"]
|
|
3688
|
+
},
|
|
3689
|
+
safetyLevel: "danger" /* DANGER */,
|
|
3690
|
+
isSync: true,
|
|
3691
|
+
requireApproval: (args) => `\u26A0\uFE0F\u3010MCP \u5DE5\u5177\u5BA1\u6279\u3011
|
|
3692
|
+
\u670D\u52A1: ${args?.server} / \u5DE5\u5177: ${args?.tool}
|
|
3693
|
+
\u53C2\u6570: ${args?.args}`,
|
|
3694
|
+
async execute(args) {
|
|
3695
|
+
const { server, tool } = args ?? {};
|
|
3696
|
+
if (!server || !tool) return "\u274C [mcp_call] \u7F3A\u5C11 server \u6216 tool\uFF08\u5148\u7528 mcp_list_tools \u67E5\u76EE\u5F55\uFF09\u3002";
|
|
3697
|
+
let parsed = {};
|
|
3698
|
+
if (typeof args.args === "string" && args.args.trim() && args.args.trim() !== "{}") {
|
|
3699
|
+
try {
|
|
3700
|
+
parsed = JSON.parse(args.args);
|
|
3701
|
+
} catch {
|
|
3702
|
+
return `\u274C [mcp_call] args \u4E0D\u662F\u5408\u6CD5 JSON\uFF1A${String(args.args).slice(0, 200)}\u3002\u8BF7\u4F20\u8BE5\u5DE5\u5177\u53C2\u6570\u7684 JSON \u5B57\u7B26\u4E32\uFF08\u65E0\u53C2\u6570\u4F20 "{}"\uFF09\u3002`;
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
const c = findClient(server);
|
|
3706
|
+
if (!c) return `\u274C \u672A\u8FDE\u63A5\u7684 MCP server\uFF1A${server}\uFF08\u7528 mcp_list_tools \u67E5\u770B\u5DF2\u8FDE\u63A5\u5217\u8868\uFF09`;
|
|
3707
|
+
const result = await c.callTool(tool, parsed);
|
|
3708
|
+
return `[MCP ${server}/${tool}]
|
|
3709
|
+
${result}`;
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
];
|
|
3341
3714
|
}
|
|
3342
3715
|
});
|
|
3343
3716
|
|
|
@@ -3373,68 +3746,44 @@ var init_httpExecutor = __esm({
|
|
|
3373
3746
|
}
|
|
3374
3747
|
});
|
|
3375
3748
|
|
|
3376
|
-
// ../core/src/
|
|
3377
|
-
|
|
3378
|
-
var
|
|
3379
|
-
|
|
3380
|
-
"../core/src/session/transcript.ts"() {
|
|
3749
|
+
// ../core/src/agent/inbox.ts
|
|
3750
|
+
var INBOX_MAX_ITEMS, sessionInboxes, pushSessionInbox, claimSessionInbox, flushLeftoverToTranscript;
|
|
3751
|
+
var init_inbox = __esm({
|
|
3752
|
+
"../core/src/agent/inbox.ts"() {
|
|
3381
3753
|
"use strict";
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
lines.push(JSON.parse(line));
|
|
3395
|
-
} catch {
|
|
3396
|
-
console.warn(`\u26A0\uFE0F [transcript] \u8DF3\u8FC7\u65E0\u6CD5\u89E3\u6790\u7684\u635F\u574F\u884C: ${line.slice(0, 120)}`);
|
|
3397
|
-
}
|
|
3398
|
-
}
|
|
3399
|
-
return lines;
|
|
3400
|
-
} catch (err) {
|
|
3401
|
-
if (err?.code === "ENOENT") return [];
|
|
3402
|
-
throw err;
|
|
3754
|
+
init_transcript();
|
|
3755
|
+
init_config();
|
|
3756
|
+
INBOX_MAX_ITEMS = 50;
|
|
3757
|
+
sessionInboxes = /* @__PURE__ */ new Map();
|
|
3758
|
+
pushSessionInbox = (sessionId, content) => {
|
|
3759
|
+
if (!appConfig.inboxSteering) return false;
|
|
3760
|
+
const text = (content ?? "").trim();
|
|
3761
|
+
if (!text) return false;
|
|
3762
|
+
const q = sessionInboxes.get(sessionId) ?? [];
|
|
3763
|
+
if (q.length >= INBOX_MAX_ITEMS) {
|
|
3764
|
+
console.warn(`[inbox] \u4F1A\u8BDD ${sessionId} \u961F\u5217\u5DF2\u6EE1\uFF08${INBOX_MAX_ITEMS}\uFF09\uFF0C\u4E22\u5F03\u65B0\u8F93\u5165`);
|
|
3765
|
+
return false;
|
|
3403
3766
|
}
|
|
3767
|
+
q.push(text);
|
|
3768
|
+
sessionInboxes.set(sessionId, q);
|
|
3769
|
+
return true;
|
|
3404
3770
|
};
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
}
|
|
3416
|
-
const MAX_ATTEMPTS = 3;
|
|
3417
|
-
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
3418
|
-
let beforeSize = 0;
|
|
3419
|
-
try {
|
|
3420
|
-
beforeSize = (await fs8.stat(p)).size;
|
|
3421
|
-
} catch {
|
|
3422
|
-
}
|
|
3423
|
-
try {
|
|
3424
|
-
await fs8.appendFile(p, payload, "utf-8");
|
|
3425
|
-
return;
|
|
3426
|
-
} catch (e) {
|
|
3427
|
-
try {
|
|
3428
|
-
await fs8.truncate(p, beforeSize);
|
|
3429
|
-
} catch {
|
|
3430
|
-
}
|
|
3431
|
-
if (attempt === MAX_ATTEMPTS) throw e;
|
|
3432
|
-
await new Promise((r) => setTimeout(r, 50 * attempt));
|
|
3433
|
-
}
|
|
3434
|
-
}
|
|
3435
|
-
} catch (e) {
|
|
3436
|
-
console.warn(`\u26A0\uFE0F \u6D88\u606F\u843D\u76D8\u5931\u8D25\uFF08\u5DF2\u91CD\u8BD5 3 \u6B21\uFF0C\u5185\u5B58\u4E0E\u78C1\u76D8\u8F6C\u5F55\u53EF\u80FD\u4E0D\u4E00\u81F4\uFF09:`, e);
|
|
3771
|
+
claimSessionInbox = (sessionId) => {
|
|
3772
|
+
if (!appConfig.inboxSteering) return [];
|
|
3773
|
+
const q = sessionInboxes.get(sessionId);
|
|
3774
|
+
if (!q || q.length === 0) return [];
|
|
3775
|
+
sessionInboxes.delete(sessionId);
|
|
3776
|
+
return q;
|
|
3777
|
+
};
|
|
3778
|
+
flushLeftoverToTranscript = async (sessionId) => {
|
|
3779
|
+
const texts = claimSessionInbox(sessionId);
|
|
3780
|
+
for (const t of texts) {
|
|
3781
|
+
await appendMessage({ sessionId, role: "user", content: t });
|
|
3437
3782
|
}
|
|
3783
|
+
if (texts.length > 0) {
|
|
3784
|
+
console.warn(`[inbox] \u4F1A\u8BDD ${sessionId} \u7ED3\u675F\u65F6 ${texts.length} \u6761\u8865\u5145\u8F93\u5165\u672A\u8BA4\u9886\uFF0C\u5DF2\u5199\u5165 transcript\uFF08\u4E0B\u8F6E\u81EA\u52A8\u5E26\u5165\uFF09`);
|
|
3785
|
+
}
|
|
3786
|
+
return texts.length;
|
|
3438
3787
|
};
|
|
3439
3788
|
}
|
|
3440
3789
|
});
|
|
@@ -3561,6 +3910,9 @@ var init_planMode = __esm({
|
|
|
3561
3910
|
"git_status",
|
|
3562
3911
|
"git_log",
|
|
3563
3912
|
"inspect_dependencies",
|
|
3913
|
+
"get_diagnostics",
|
|
3914
|
+
"goto_definition",
|
|
3915
|
+
// ★ P0-A 补漏:LSP 导航/诊断恰是调研阶段最需要的(原白名单漂移漏登记,schema 档与 runtime 档共用此表)
|
|
3564
3916
|
"web_fetch",
|
|
3565
3917
|
"web_search"
|
|
3566
3918
|
// 只读研究类(虽为 DANGER 但不写本地状态;仍走各自审批)
|
|
@@ -3738,7 +4090,7 @@ var init_inject2 = __esm({
|
|
|
3738
4090
|
init_registry4();
|
|
3739
4091
|
AGENT_CATALOG_MARKER = "\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011";
|
|
3740
4092
|
AGENT_CATALOG_FENCE = "\u27E6DSC:AGENT_CATALOG\u27E7";
|
|
3741
|
-
AGENT_USAGE_HINT = '\u5F53\u67D0\u5B50\u4EFB\u52A1\u5339\u914D\u4E0B\u5217\u67D0\u4E2A\u5B50 Agent \u7684\u4E13\u957F\u65F6\uFF0C\u8C03\u7528 spawn_agent \u5E76\u4F20\u5165\u5BF9\u5E94 name \u53C2\u6570\uFF08\u5982 spawn_agent(name="code-reviewer", task="..."\uFF09\uFF09\u5C06\u5176\u59D4\u6D3E\u7ED9\u8BE5\u58F0\u660E\u5F0F\u5B50 Agent\uFF1B\u4E0D\u4F20 name \u5219\u8D70\u9ED8\u8BA4\u901A\u7528\u5B50 agent\u3002';
|
|
4093
|
+
AGENT_USAGE_HINT = '\u5F53\u67D0\u5B50\u4EFB\u52A1\u5339\u914D\u4E0B\u5217\u67D0\u4E2A\u5B50 Agent \u7684\u4E13\u957F\u65F6\uFF0C\u8C03\u7528 spawn_agent \u5E76\u4F20\u5165\u5BF9\u5E94 name \u53C2\u6570\uFF08\u5982 spawn_agent(name="code-reviewer", task="..."\uFF09\uFF09\u5C06\u5176\u59D4\u6D3E\u7ED9\u8BE5\u58F0\u660E\u5F0F\u5B50 Agent\uFF1B\u4E0D\u4F20 name \u5219\u8D70\u9ED8\u8BA4\u901A\u7528\u5B50 agent\u3002\n\u59D4\u6D3E\u4F18\u5148\u539F\u5219\uFF08P1-C\uFF09\uFF1A\u9700\u8981\u4E00\u5927\u5768\u4E13\u7528\u5DE5\u5177\u6216\u72EC\u7ACB\u4E0A\u4E0B\u6587\u7684\u5B50\u4EFB\u52A1\uFF08\u6DF1\u5EA6 web \u8C03\u7814\u3001\u6279\u91CF MCP \u5DE5\u5177\u64CD\u4F5C\u3001\u72EC\u7ACB worktree \u8BD5\u9A8C\u7B49\uFF09\uFF0C\u4F18\u5148\u59D4\u6D3E\u7ED9\u5BF9\u5E94\u5B50 Agent\uFF0C\u800C\u975E\u5728\u4E3B\u4F1A\u8BDD\u5806\u53E0\u8C03\u7528\u2014\u2014\u5B50 Agent \u6709\u81EA\u5DF1\u7684\u5DE5\u5177\u767D\u540D\u5355\u4E0E\u72EC\u7ACB\u4E0A\u4E0B\u6587\uFF0C\u4E3B\u4F1A\u8BDD\u4FDD\u6301\u7CBE\u7B80\u3001\u5DE5\u5177\u8868\u4FDD\u6301\u6052\u5B9A\u3002';
|
|
3742
4094
|
injectAgentCatalog = (message) => {
|
|
3743
4095
|
const catalog = getAgentCatalog();
|
|
3744
4096
|
if (!catalog) return;
|
|
@@ -3894,6 +4246,7 @@ var prepareToolsAndInjections;
|
|
|
3894
4246
|
var init_systemInjections = __esm({
|
|
3895
4247
|
"../core/src/agent/systemInjections.ts"() {
|
|
3896
4248
|
"use strict";
|
|
4249
|
+
init_config();
|
|
3897
4250
|
init_planMode();
|
|
3898
4251
|
init_toolFilter();
|
|
3899
4252
|
init_truncate();
|
|
@@ -3905,7 +4258,7 @@ var init_systemInjections = __esm({
|
|
|
3905
4258
|
init_common();
|
|
3906
4259
|
prepareToolsAndInjections = async (message, options, events) => {
|
|
3907
4260
|
const { toolSchemas, planMode, locale: locale2, outputStyle } = options;
|
|
3908
|
-
const rawToolsPreEnv = planMode ? filterToolsForPlanMode(toolSchemas ?? []) : appendPlanControlTools(toolSchemas ?? []);
|
|
4261
|
+
const rawToolsPreEnv = planMode && appConfig.planEnforcement === "schema" ? filterToolsForPlanMode(toolSchemas ?? []) : appendPlanControlTools(toolSchemas ?? []);
|
|
3909
4262
|
const validationCtx = {
|
|
3910
4263
|
sessionId: options.sessionId,
|
|
3911
4264
|
cwd: options.cwd ?? process.cwd(),
|
|
@@ -8811,6 +9164,9 @@ var init_toolExecution = __esm({
|
|
|
8811
9164
|
init_backgroundTool();
|
|
8812
9165
|
init_backup();
|
|
8813
9166
|
init_autoPermission();
|
|
9167
|
+
init_loader();
|
|
9168
|
+
init_config();
|
|
9169
|
+
init_planMode();
|
|
8814
9170
|
applyPrivacyMasking = (rules3, args, output) => {
|
|
8815
9171
|
if (!rules3) return output;
|
|
8816
9172
|
try {
|
|
@@ -8836,6 +9192,7 @@ var init_toolExecution = __esm({
|
|
|
8836
9192
|
rawTools,
|
|
8837
9193
|
events,
|
|
8838
9194
|
permissionMode,
|
|
9195
|
+
planMode,
|
|
8839
9196
|
onUIEvent,
|
|
8840
9197
|
requestApproval: hostRequestApproval,
|
|
8841
9198
|
requestQuestion: hostRequestQuestion,
|
|
@@ -8862,6 +9219,10 @@ var init_toolExecution = __esm({
|
|
|
8862
9219
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note, resultForUser: note, ok: true, terminal: { kind: "exit_plan_mode", plan } };
|
|
8863
9220
|
}
|
|
8864
9221
|
if (calledName === "enter_plan_mode") {
|
|
9222
|
+
if (planMode) {
|
|
9223
|
+
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";
|
|
9224
|
+
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note2, resultForUser: note2, ok: true };
|
|
9225
|
+
}
|
|
8865
9226
|
const reason = typeof calledArgs?.reason === "string" ? calledArgs.reason : "";
|
|
8866
9227
|
const note = "\u{1F4CB} [\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F] \u6A21\u578B\u8BF7\u6C42\u5148\u4EE5\u53EA\u8BFB\u65B9\u5F0F\u8C03\u7814\u5E76\u89C4\u5212\u65B9\u6848\uFF0C\u5DF2\u5207\u6362\u81F3\u8BA1\u5212\u6A21\u5F0F\u3002";
|
|
8867
9228
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note, resultForUser: note, ok: true, terminal: { kind: "enter_plan_mode", reason } };
|
|
@@ -8870,10 +9231,16 @@ var init_toolExecution = __esm({
|
|
|
8870
9231
|
const placeholder = "\uFF08\u5DF2\u4E2D\u6B62\uFF0C\u672A\u6267\u884C\uFF09";
|
|
8871
9232
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: placeholder, resultForUser: placeholder, ok: false, aborted: true };
|
|
8872
9233
|
}
|
|
9234
|
+
if (!parseFailed && planMode && appConfig.planEnforcement === "runtime" && calledName !== "enter_plan_mode" && calledName !== "exit_plan_mode" && !PLAN_ALLOWED_TOOLS.has(calledName)) {
|
|
9235
|
+
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`;
|
|
9236
|
+
events({ sessionId, eventType: "tool.validation.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round }, payload: { output: note } });
|
|
9237
|
+
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note, resultForUser: note, ok: false };
|
|
9238
|
+
}
|
|
8873
9239
|
const matchedTool = rawTools.find((t) => t.function.name === calledName);
|
|
8874
9240
|
const toolCtx = { sessionId, cwd: getActiveWorkspaceRoot(), abortSignal: signal, depth, keepRecentUnits, compactRatio, modelWindow, parentSystemPrompt, events, onUIEvent, requestApproval: hostRequestApproval, requestQuestion: hostRequestQuestion, emitProgress: (m) => onUIEvent?.({ type: "tool.progress", toolsId: toolCall.id, toolName: calledName, message: m }), permissionMode };
|
|
8875
9241
|
let result = "";
|
|
8876
9242
|
let explicitOk = null;
|
|
9243
|
+
let postHookOverride;
|
|
8877
9244
|
if (parseFailed) {
|
|
8878
9245
|
result = `\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u6A21\u578B\u8FD4\u56DE\u7684 arguments \u4E0D\u662F\u5408\u6CD5 JSON${JSON.stringify(toolCall).slice(0, 300)}`;
|
|
8879
9246
|
events({ sessionId, eventType: "tool.validation.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round }, payload: { output: result } });
|
|
@@ -8881,6 +9248,23 @@ var init_toolExecution = __esm({
|
|
|
8881
9248
|
const level = matchedTool.function.safetyLevel;
|
|
8882
9249
|
let needApproval = level === "mutation" /* MUTATION */ || level === "danger" /* DANGER */;
|
|
8883
9250
|
let denied = false;
|
|
9251
|
+
let preHooksAdvanced = false;
|
|
9252
|
+
if (appConfig.hookRewrite) {
|
|
9253
|
+
preHooksAdvanced = true;
|
|
9254
|
+
let veto;
|
|
9255
|
+
try {
|
|
9256
|
+
veto = await runPreHooks(calledName, calledArgs, toolCtx);
|
|
9257
|
+
} catch (e) {
|
|
9258
|
+
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` };
|
|
9259
|
+
}
|
|
9260
|
+
if (veto.deny) {
|
|
9261
|
+
denied = true;
|
|
9262
|
+
result = veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`;
|
|
9263
|
+
} else if (veto.argsOverride !== void 0 && veto.argsOverride !== null && typeof veto.argsOverride === "object" && !Array.isArray(veto.argsOverride)) {
|
|
9264
|
+
console.log(`\u270F\uFE0F [Pre-hook] [${calledName}] args \u5DF2\u88AB hook \u6539\u5199`);
|
|
9265
|
+
calledArgs = veto.argsOverride;
|
|
9266
|
+
}
|
|
9267
|
+
}
|
|
8884
9268
|
const protectedPaths = calledName === "move_file" ? [calledArgs?.source, calledArgs?.destination] : isUndoTrigger(calledName) ? [calledArgs?.path] : [];
|
|
8885
9269
|
const hitProtected = protectedPaths.find((p) => typeof p === "string" && isProtectedWrite(p, toolCtx.cwd));
|
|
8886
9270
|
if (hitProtected) {
|
|
@@ -8889,7 +9273,7 @@ var init_toolExecution = __esm({
|
|
|
8889
9273
|
}
|
|
8890
9274
|
let perm = null;
|
|
8891
9275
|
try {
|
|
8892
|
-
perm = checkPermission(calledName, calledArgs);
|
|
9276
|
+
perm = checkPermission(resolveMcpPermissionName(calledName, calledArgs), calledArgs);
|
|
8893
9277
|
if (perm === "deny") {
|
|
8894
9278
|
denied = true;
|
|
8895
9279
|
result = `\u274C [\u6743\u9650\u89C4\u5219]\uFF1A[${calledName}] \u88AB\u6743\u9650\u89C4\u5219 deny \u62D2\u7EDD\u3002`;
|
|
@@ -8915,7 +9299,7 @@ var init_toolExecution = __esm({
|
|
|
8915
9299
|
result = `\u{1F512} [\u4E92\u65A5\u9501\u963B\u585E]\uFF1A\u5DF2\u6709\u540E\u53F0\u4EFB\u52A1\u6301\u6709\u9501 [${lockKey}]\uFF0C[${calledName}] \u8C03\u7528\u88AB\u8DF3\u8FC7\u3002`;
|
|
8916
9300
|
}
|
|
8917
9301
|
if (needApproval && !denied) {
|
|
8918
|
-
const auto = await runAutoCheck(calledName, calledArgs, toolCtx, permissionMode === "auto");
|
|
9302
|
+
const auto = await runAutoCheck(resolveMcpPermissionName(calledName, calledArgs), calledArgs, toolCtx, permissionMode === "auto");
|
|
8919
9303
|
if (auto === "allow") {
|
|
8920
9304
|
needApproval = false;
|
|
8921
9305
|
} else if (auto === "deny") {
|
|
@@ -8946,7 +9330,7 @@ var init_toolExecution = __esm({
|
|
|
8946
9330
|
}
|
|
8947
9331
|
}
|
|
8948
9332
|
}
|
|
8949
|
-
if (!denied) {
|
|
9333
|
+
if (!denied && !preHooksAdvanced) {
|
|
8950
9334
|
let veto;
|
|
8951
9335
|
try {
|
|
8952
9336
|
veto = await runPreHooks(calledName, calledArgs, toolCtx);
|
|
@@ -8990,9 +9374,12 @@ ${result}`;
|
|
|
8990
9374
|
result = `\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`;
|
|
8991
9375
|
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 }, payload: { output: result } });
|
|
8992
9376
|
}
|
|
8993
|
-
|
|
9377
|
+
try {
|
|
9378
|
+
const post = await runPostHooks(calledName, calledArgs, result, toolCtx);
|
|
9379
|
+
postHookOverride = post?.resultOverride;
|
|
9380
|
+
} catch (e) {
|
|
8994
9381
|
console.warn(`\u26A0\uFE0F post-hook [${calledName}] \u5F02\u5E38\uFF08\u5DF2\u5FFD\u7565\uFF09:`, e?.message ?? e);
|
|
8995
|
-
}
|
|
9382
|
+
}
|
|
8996
9383
|
}
|
|
8997
9384
|
} else {
|
|
8998
9385
|
result = `\u9519\u8BEF\uFF1A\u672A\u77E5\u5DE5\u5177 "${calledName}" \u6216\u8BE5\u5DE5\u5177\u65E0\u53EF\u6267\u884C\u51FD\u6570`;
|
|
@@ -9013,6 +9400,10 @@ ${result}`;
|
|
|
9013
9400
|
} catch {
|
|
9014
9401
|
}
|
|
9015
9402
|
}
|
|
9403
|
+
if (postHookOverride !== void 0) {
|
|
9404
|
+
console.log(`\u270F\uFE0F [Post-hook] [${calledName}] \u6A21\u578B\u89C6\u56FE\u7ED3\u679C\u5DF2\u88AB hook \u6539\u5199`);
|
|
9405
|
+
resultForModel = truncateToolResult(postHookOverride, matchedTool?.function?.maxOutputCharacters);
|
|
9406
|
+
}
|
|
9016
9407
|
return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel, resultForUser, ok };
|
|
9017
9408
|
};
|
|
9018
9409
|
}
|
|
@@ -9536,6 +9927,18 @@ async function* runAgent(message, options) {
|
|
|
9536
9927
|
let lastRealPromptTokens = persistedCalib.lastRealPromptTokens;
|
|
9537
9928
|
let lastCachedTokens = persistedCalib.lastCachedTokens;
|
|
9538
9929
|
const breaker = createRepeatBreaker({ sessionId, depth, llmDecisionSource, startTime, events });
|
|
9930
|
+
const runId = createUUID();
|
|
9931
|
+
let usageSum;
|
|
9932
|
+
const addUsage = (u) => {
|
|
9933
|
+
if (!u) return;
|
|
9934
|
+
usageSum = {
|
|
9935
|
+
prompt_tokens: (usageSum?.prompt_tokens ?? 0) + (u.prompt_tokens ?? 0),
|
|
9936
|
+
completion_tokens: (usageSum?.completion_tokens ?? 0) + (u.completion_tokens ?? 0),
|
|
9937
|
+
total_tokens: (usageSum?.total_tokens ?? 0) + (u.total_tokens ?? 0),
|
|
9938
|
+
cached_tokens: (usageSum?.cached_tokens ?? 0) + (u.cached_tokens ?? 0)
|
|
9939
|
+
};
|
|
9940
|
+
};
|
|
9941
|
+
await appendEvent(sessionId, { dscEvent: "run.start", runId, depth });
|
|
9539
9942
|
try {
|
|
9540
9943
|
while (true) {
|
|
9541
9944
|
round++;
|
|
@@ -9563,6 +9966,14 @@ async function* runAgent(message, options) {
|
|
|
9563
9966
|
yield { type: "final", text: lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
|
|
9564
9967
|
return;
|
|
9565
9968
|
}
|
|
9969
|
+
const inboxTexts = claimSessionInbox(sessionId);
|
|
9970
|
+
if (inboxTexts.length > 0) {
|
|
9971
|
+
for (const t of inboxTexts) {
|
|
9972
|
+
message.push({ role: "user", content: t });
|
|
9973
|
+
await appendMessage({ sessionId, role: "user", content: t });
|
|
9974
|
+
}
|
|
9975
|
+
yield { type: "inbox.claimed", texts: inboxTexts };
|
|
9976
|
+
}
|
|
9566
9977
|
yield { type: "round.start", round };
|
|
9567
9978
|
try {
|
|
9568
9979
|
await ensureFitsWindow(
|
|
@@ -9629,6 +10040,7 @@ async function* runAgent(message, options) {
|
|
|
9629
10040
|
lastRealPromptTokens = infResult.usage.prompt_tokens;
|
|
9630
10041
|
lastCachedTokens = infResult.usage.cached_tokens;
|
|
9631
10042
|
}
|
|
10043
|
+
addUsage(infResult.usage);
|
|
9632
10044
|
const assistantMessage = infResult.assistantMessage;
|
|
9633
10045
|
message.push(assistantMessage);
|
|
9634
10046
|
await appendMessage({ ...assistantMessage, sessionId });
|
|
@@ -9637,6 +10049,7 @@ async function* runAgent(message, options) {
|
|
|
9637
10049
|
}
|
|
9638
10050
|
const tcCount = assistantMessage.tool_calls?.length ?? 0;
|
|
9639
10051
|
if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
|
|
10052
|
+
await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
|
|
9640
10053
|
const finalText = (typeof assistantMessage.content === "string" ? assistantMessage.content : "") || "";
|
|
9641
10054
|
const intercepted = nudges.interceptFinal(finalText, round);
|
|
9642
10055
|
if (intercepted) continue;
|
|
@@ -9647,6 +10060,11 @@ async function* runAgent(message, options) {
|
|
|
9647
10060
|
const repeatVerdict = breaker.check(assistantMessage.tool_calls, round, lastContent);
|
|
9648
10061
|
if (repeatVerdict.tripped) {
|
|
9649
10062
|
stopReason = "repeat";
|
|
10063
|
+
for (const tc of assistantMessage.tool_calls) {
|
|
10064
|
+
const ph = "\uFF08\u91CD\u590D\u8C03\u7528\u7194\u65AD\uFF0C\u672A\u6267\u884C\uFF09";
|
|
10065
|
+
message.push({ role: "tool", tool_call_id: tc.id, content: ph });
|
|
10066
|
+
await appendMessage({ sessionId, role: "tool", tool_call_id: tc.id, content: ph });
|
|
10067
|
+
}
|
|
9650
10068
|
yield { type: "final", text: repeatVerdict.text };
|
|
9651
10069
|
return;
|
|
9652
10070
|
}
|
|
@@ -9661,6 +10079,8 @@ async function* runAgent(message, options) {
|
|
|
9661
10079
|
rawTools,
|
|
9662
10080
|
events,
|
|
9663
10081
|
permissionMode: options.permissionMode,
|
|
10082
|
+
planMode: !!options.planMode,
|
|
10083
|
+
// ★ P0-A runtime 档:计划期写工具在 processToolCall 执行层拒绝(工具表恒定不裁剪)
|
|
9664
10084
|
onUIEvent: options.onUIEvent,
|
|
9665
10085
|
requestApproval: options.requestApproval,
|
|
9666
10086
|
requestQuestion: options.requestQuestion,
|
|
@@ -9678,6 +10098,7 @@ async function* runAgent(message, options) {
|
|
|
9678
10098
|
yield { type: "final", text: scheduleResult.terminalText };
|
|
9679
10099
|
return;
|
|
9680
10100
|
}
|
|
10101
|
+
await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
|
|
9681
10102
|
}
|
|
9682
10103
|
} catch (toolErr) {
|
|
9683
10104
|
if (signal?.aborted) {
|
|
@@ -9691,6 +10112,14 @@ async function* runAgent(message, options) {
|
|
|
9691
10112
|
\uFF08\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${errMsg}\uFF09` };
|
|
9692
10113
|
return;
|
|
9693
10114
|
} finally {
|
|
10115
|
+
try {
|
|
10116
|
+
await flushLeftoverToTranscript(sessionId);
|
|
10117
|
+
} catch {
|
|
10118
|
+
}
|
|
10119
|
+
try {
|
|
10120
|
+
await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage: usageSum });
|
|
10121
|
+
} catch {
|
|
10122
|
+
}
|
|
9694
10123
|
try {
|
|
9695
10124
|
const reason = signal?.aborted ? "aborted" : stopReason;
|
|
9696
10125
|
await dispatch("Stop", { sessionId, cwd, lastText: lastContent || "", reason });
|
|
@@ -9706,6 +10135,8 @@ var init_runAgent = __esm({
|
|
|
9706
10135
|
"../core/src/agent/runAgent.ts"() {
|
|
9707
10136
|
"use strict";
|
|
9708
10137
|
init_transcript();
|
|
10138
|
+
init_inbox();
|
|
10139
|
+
init_common();
|
|
9709
10140
|
init_contextCore();
|
|
9710
10141
|
init_store();
|
|
9711
10142
|
init_truncate();
|
|
@@ -9718,17 +10149,30 @@ var init_runAgent = __esm({
|
|
|
9718
10149
|
}
|
|
9719
10150
|
});
|
|
9720
10151
|
|
|
9721
|
-
// ../core/src/session/
|
|
9722
|
-
var repairOrphanToolCalls,
|
|
9723
|
-
var
|
|
9724
|
-
"../core/src/session/
|
|
10152
|
+
// ../core/src/session/recovery.ts
|
|
10153
|
+
var detectInterruption, repairOrphanToolCalls, reconcileCompaction, recoverSession;
|
|
10154
|
+
var init_recovery = __esm({
|
|
10155
|
+
"../core/src/session/recovery.ts"() {
|
|
9725
10156
|
"use strict";
|
|
9726
|
-
init_contextCore();
|
|
9727
10157
|
init_transcript();
|
|
9728
10158
|
init_store();
|
|
9729
|
-
|
|
9730
|
-
|
|
10159
|
+
detectInterruption = (lines) => {
|
|
10160
|
+
let sawEvents = false;
|
|
10161
|
+
let lastStart;
|
|
10162
|
+
const closed = /* @__PURE__ */ new Set();
|
|
10163
|
+
for (const l of lines) {
|
|
10164
|
+
if (!isEventLine(l)) continue;
|
|
10165
|
+
sawEvents = true;
|
|
10166
|
+
if (l.dscEvent === "run.start") lastStart = l;
|
|
10167
|
+
else if (l.dscEvent === "run.end" || l.dscEvent === "run.abandoned") closed.add(l.runId);
|
|
10168
|
+
}
|
|
10169
|
+
if (!sawEvents || !lastStart) return { kind: "legacy" };
|
|
10170
|
+
return closed.has(lastStart.runId) ? { kind: "clean" } : { kind: "crashed", runId: lastStart.runId };
|
|
10171
|
+
};
|
|
10172
|
+
repairOrphanToolCalls = (msgs, confirmedCrash) => {
|
|
10173
|
+
const placeholder = confirmedCrash ? "\uFF08\u4E0A\u6B21\u4F1A\u8BDD\u5728\u5DE5\u5177\u6267\u884C\u4E2D\u5D29\u6E83\uFF08run \u672A\u95ED\u5408\uFF09\uFF0C\u8BE5\u8C03\u7528\u672A\u6267\u884C\uFF0C\u5DF2\u8DF3\u8FC7\u3002\uFF09" : "\uFF08\u8BE5\u5DE5\u5177\u8C03\u7528\u56E0\u4E0A\u6B21\u4F1A\u8BDD\u5F02\u5E38\u4E2D\u65AD\u672A\u7559\u4E0B\u7ED3\u679C\uFF0C\u5DF2\u8DF3\u8FC7\u3002\uFF09";
|
|
9731
10174
|
const out = [];
|
|
10175
|
+
const repairedToolCallIds = [];
|
|
9732
10176
|
for (let i = 0; i < msgs.length; i++) {
|
|
9733
10177
|
const m = msgs[i];
|
|
9734
10178
|
out.push(m);
|
|
@@ -9741,13 +10185,60 @@ var init_content = __esm({
|
|
|
9741
10185
|
}
|
|
9742
10186
|
for (const tc of m.tool_calls) {
|
|
9743
10187
|
if (tc.id && !answered.has(tc.id)) {
|
|
9744
|
-
out.push({ role: "tool", tool_call_id: tc.id, content:
|
|
10188
|
+
out.push({ role: "tool", tool_call_id: tc.id, content: placeholder });
|
|
10189
|
+
repairedToolCallIds.push(tc.id);
|
|
9745
10190
|
}
|
|
9746
10191
|
}
|
|
9747
10192
|
}
|
|
9748
10193
|
}
|
|
9749
|
-
return out;
|
|
10194
|
+
return { msgs: out, repairedToolCallIds, confirmedCrash };
|
|
9750
10195
|
};
|
|
10196
|
+
reconcileCompaction = (state, lines) => {
|
|
10197
|
+
let last;
|
|
10198
|
+
for (const l of lines) {
|
|
10199
|
+
if (isEventLine(l) && l.dscEvent === "compaction") last = l;
|
|
10200
|
+
}
|
|
10201
|
+
if (!last) return { archivedMessageCount: state.archivedMessageCount, rollingSummary: state.rollingSummary, desync: "legacy" };
|
|
10202
|
+
let desync = "none";
|
|
10203
|
+
if (last.archivedMessageCount !== state.archivedMessageCount) {
|
|
10204
|
+
desync = state.archivedMessageCount > last.archivedMessageCount ? "state-ahead" : "event-ahead";
|
|
10205
|
+
console.warn(
|
|
10206
|
+
`\u26A0\uFE0F [recovery] \u538B\u7F29\u72B6\u6001 desync\uFF08${desync}\uFF09\uFF1Astate.json archivedMessageCount=${state.archivedMessageCount} vs \u672B\u4E2A compaction \u4E8B\u4EF6=${last.archivedMessageCount}\u3002\u4EE5 state \u4E3A\u51C6\uFF08slice \u6267\u884C\u95F8\u95E8\uFF09\uFF0C\u4E8B\u4EF6\u4FA7\u4EC5\u955C\u50CF\u3002`
|
|
10207
|
+
);
|
|
10208
|
+
}
|
|
10209
|
+
return { archivedMessageCount: state.archivedMessageCount, rollingSummary: state.rollingSummary, desync };
|
|
10210
|
+
};
|
|
10211
|
+
recoverSession = async (sessionId) => {
|
|
10212
|
+
const lines = await readTranscriptLines(sessionId);
|
|
10213
|
+
const interruption = detectInterruption(lines);
|
|
10214
|
+
if (interruption.kind === "crashed") {
|
|
10215
|
+
await appendEvent(sessionId, {
|
|
10216
|
+
dscEvent: "run.abandoned",
|
|
10217
|
+
runId: interruption.runId,
|
|
10218
|
+
reason: "run.end missing: interrupted by process exit/kill"
|
|
10219
|
+
});
|
|
10220
|
+
console.warn(`\u27E6recovery\u27E7 \u68C0\u6D4B\u5230\u672A\u95ED\u5408 run\uFF08${interruption.runId.slice(0, 8)}\u2026\uFF09\u2192 \u5DF2\u8865 run.abandoned \u95ED\u5893\u6807\u8BB0\uFF1B\u5B64\u513F\u4FEE\u590D\u6309\u300C\u5D29\u6E83\u786E\u8BA4\u300D\u6863\u6267\u884C\u3002`);
|
|
10221
|
+
}
|
|
10222
|
+
const state = await getRollingState(sessionId);
|
|
10223
|
+
const compaction = reconcileCompaction(
|
|
10224
|
+
{ archivedMessageCount: state.archivedMessageCount, rollingSummary: state.rollingSummary },
|
|
10225
|
+
lines
|
|
10226
|
+
);
|
|
10227
|
+
return { interruption, compaction };
|
|
10228
|
+
};
|
|
10229
|
+
}
|
|
10230
|
+
});
|
|
10231
|
+
|
|
10232
|
+
// ../core/src/session/content.ts
|
|
10233
|
+
var decayOldToolResults, buildContextMessages;
|
|
10234
|
+
var init_content = __esm({
|
|
10235
|
+
"../core/src/session/content.ts"() {
|
|
10236
|
+
"use strict";
|
|
10237
|
+
init_contextCore();
|
|
10238
|
+
init_transcript();
|
|
10239
|
+
init_store();
|
|
10240
|
+
init_config();
|
|
10241
|
+
init_recovery();
|
|
9751
10242
|
decayOldToolResults = (msgs) => {
|
|
9752
10243
|
const units = groupUnits(msgs);
|
|
9753
10244
|
const cutoffUnitIdx = Math.max(0, units.length - appConfig.KEEP_RECENT_UNITS);
|
|
@@ -9773,21 +10264,28 @@ var init_content = __esm({
|
|
|
9773
10264
|
});
|
|
9774
10265
|
};
|
|
9775
10266
|
buildContextMessages = async (sessionId, currentUserMsg, systemPrompt) => {
|
|
9776
|
-
const
|
|
10267
|
+
const lines = await readTranscriptLines(sessionId);
|
|
9777
10268
|
const store = await getRollingState(sessionId);
|
|
10269
|
+
const eventAware = appConfig.transcriptEvents || lines.some(isEventLine);
|
|
10270
|
+
const recovery = eventAware ? await recoverSession(sessionId) : {
|
|
10271
|
+
interruption: { kind: "legacy" },
|
|
10272
|
+
compaction: { archivedMessageCount: store.archivedMessageCount, rollingSummary: store.rollingSummary, desync: "legacy" }
|
|
10273
|
+
};
|
|
10274
|
+
const all = lines.filter((l) => !isEventLine(l)).map(cleanMsg);
|
|
9778
10275
|
const result = [
|
|
9779
10276
|
{ role: "system", content: systemPrompt },
|
|
9780
10277
|
// 存储系统提示词
|
|
9781
|
-
{ role: "system", content:
|
|
10278
|
+
{ role: "system", content: recovery.compaction.rollingSummary }
|
|
9782
10279
|
// 后续存储摘要使用
|
|
9783
10280
|
];
|
|
9784
|
-
const archivedMessageCount =
|
|
10281
|
+
const archivedMessageCount = recovery.compaction.archivedMessageCount;
|
|
9785
10282
|
const messageAll = all.slice(archivedMessageCount);
|
|
9786
10283
|
if (messageAll.length === 0) {
|
|
9787
10284
|
result.push(currentUserMsg);
|
|
9788
10285
|
return result;
|
|
9789
10286
|
}
|
|
9790
|
-
|
|
10287
|
+
const repair = repairOrphanToolCalls(messageAll, recovery.interruption.kind === "crashed");
|
|
10288
|
+
result.push(...decayOldToolResults(repair.msgs));
|
|
9791
10289
|
result.push(currentUserMsg);
|
|
9792
10290
|
return result;
|
|
9793
10291
|
};
|
|
@@ -9851,7 +10349,8 @@ var init_subagent = __esm({
|
|
|
9851
10349
|
parentSystemPrompt
|
|
9852
10350
|
].join("\n");
|
|
9853
10351
|
const allTools = getGlobalTools();
|
|
9854
|
-
const
|
|
10352
|
+
const wantsMcp = manifest?.tools.some((t) => t === "mcp_call" || t === "mcp_list_tools" || t.startsWith("mcp__")) ?? false;
|
|
10353
|
+
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));
|
|
9855
10354
|
console.log(`\u{1F423} \u6D3E\u751F\u5B50 Agent [\u6DF1\u5EA6: ${ctx.depth + 1}/${MAX_AGENT_DEPTH}]${manifest ? ` \u58F0\u660E\u5F0F=${manifest.name}` : ""} \u4EFB\u52A1: "${task.slice(0, 50)}..."`);
|
|
9856
10355
|
try {
|
|
9857
10356
|
const subMessages = await buildContextMessages(subSessionId, { role: "user", content: task }, subSystem);
|
|
@@ -9917,7 +10416,7 @@ var init_subagent = __esm({
|
|
|
9917
10416
|
// ../core/src/hooks/loader.ts
|
|
9918
10417
|
import fs27 from "fs/promises";
|
|
9919
10418
|
import path29 from "path";
|
|
9920
|
-
var readHooksConfig, appendValidated, validateRule, parseAgentDecision, compileRule2, loadHooks, initHooks;
|
|
10419
|
+
var readHooksConfig, appendValidated, validateRule, parseStdoutDecision, parseAgentDecision, compileRule2, loadHooks, initHooks;
|
|
9921
10420
|
var init_loader4 = __esm({
|
|
9922
10421
|
"../core/src/hooks/loader.ts"() {
|
|
9923
10422
|
"use strict";
|
|
@@ -10061,6 +10560,24 @@ var init_loader4 = __esm({
|
|
|
10061
10560
|
requireApproval: raw.requireApproval === true ? true : void 0
|
|
10062
10561
|
};
|
|
10063
10562
|
};
|
|
10563
|
+
parseStdoutDecision = (stdout) => {
|
|
10564
|
+
const s = (stdout ?? "").trim();
|
|
10565
|
+
if (!s || !s.startsWith("{")) return void 0;
|
|
10566
|
+
try {
|
|
10567
|
+
const parsed = JSON.parse(s);
|
|
10568
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
10569
|
+
const out = {};
|
|
10570
|
+
if (parsed.deny === true) {
|
|
10571
|
+
out.deny = true;
|
|
10572
|
+
if (typeof parsed.reason === "string" && parsed.reason) out.reason = parsed.reason;
|
|
10573
|
+
}
|
|
10574
|
+
if (parsed.argsOverride !== void 0) out.argsOverride = parsed.argsOverride;
|
|
10575
|
+
if (typeof parsed.resultOverride === "string" && parsed.resultOverride) out.resultOverride = parsed.resultOverride;
|
|
10576
|
+
return out.deny !== void 0 || out.argsOverride !== void 0 || out.resultOverride !== void 0 ? out : void 0;
|
|
10577
|
+
} catch {
|
|
10578
|
+
return void 0;
|
|
10579
|
+
}
|
|
10580
|
+
};
|
|
10064
10581
|
parseAgentDecision = (output) => {
|
|
10065
10582
|
const lines = (output ?? "").split(/\r?\n/);
|
|
10066
10583
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
@@ -10129,9 +10646,15 @@ var init_loader4 = __esm({
|
|
|
10129
10646
|
}
|
|
10130
10647
|
try {
|
|
10131
10648
|
const parsed = JSON.parse(res.responseBody);
|
|
10132
|
-
if (parsed && typeof parsed === "object" && parsed
|
|
10133
|
-
|
|
10134
|
-
|
|
10649
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
10650
|
+
if (parsed.deny === true) {
|
|
10651
|
+
const reason = typeof parsed.reason === "string" ? parsed.reason : `hook http \u62D2\u7EDD\uFF08status ${res.status}\uFF09`;
|
|
10652
|
+
return { deny: true, reason };
|
|
10653
|
+
}
|
|
10654
|
+
const out = {};
|
|
10655
|
+
if (parsed.argsOverride !== void 0) out.argsOverride = parsed.argsOverride;
|
|
10656
|
+
if (typeof parsed.resultOverride === "string" && parsed.resultOverride) out.resultOverride = parsed.resultOverride;
|
|
10657
|
+
if (out.argsOverride !== void 0 || out.resultOverride !== void 0) return out;
|
|
10135
10658
|
}
|
|
10136
10659
|
} catch {
|
|
10137
10660
|
}
|
|
@@ -10160,6 +10683,10 @@ var init_loader4 = __esm({
|
|
|
10160
10683
|
const detail = res.stderr ? `\uFF1A${res.stderr.slice(0, 200)}` : "";
|
|
10161
10684
|
return { deny: true, reason: `hook \u9000\u51FA\u7801 ${res.exitCode}${detail}` };
|
|
10162
10685
|
}
|
|
10686
|
+
if (res.exitCode === 0) {
|
|
10687
|
+
const out = parseStdoutDecision(res.stdout);
|
|
10688
|
+
if (out) return out;
|
|
10689
|
+
}
|
|
10163
10690
|
return { deny: false };
|
|
10164
10691
|
}
|
|
10165
10692
|
};
|
|
@@ -10394,9 +10921,9 @@ var init_loader6 = __esm({
|
|
|
10394
10921
|
);
|
|
10395
10922
|
for (const { item: m, file, source } of picked) {
|
|
10396
10923
|
if (m.tools.length > 0) {
|
|
10397
|
-
const unknown = m.tools.filter((t) => !known.has(t));
|
|
10924
|
+
const unknown = m.tools.filter((t) => !known.has(t) && !t.startsWith("mcp__"));
|
|
10398
10925
|
if (unknown.length) console.warn(`\u26A0\uFE0F [agents] ${m.name} \u58F0\u660E\u4E86\u672A\u77E5\u5DE5\u5177 [${unknown.join(", ")}]\uFF0C\u5DF2\u5254\u9664`);
|
|
10399
|
-
m.tools = m.tools.filter((t) => known.has(t));
|
|
10926
|
+
m.tools = m.tools.filter((t) => known.has(t) || t.startsWith("mcp__"));
|
|
10400
10927
|
}
|
|
10401
10928
|
if (source === "project") {
|
|
10402
10929
|
console.warn(`\u26A0\uFE0F\u3010\u5B89\u5168\u63D0\u793A\u3011\u5DF2\u52A0\u8F7D\u9879\u76EE\u7EA7\u58F0\u660E\u5F0F\u5B50 Agent [${m.name}]\uFF08${file}\uFF09\u3002\u5176 tools \u767D\u540D\u5355\u53EF\u663E\u5F0F\u6388\u6743\u9AD8\u5371\u5DE5\u5177\uFF0C\u8BF7\u5728\u4FE1\u4EFB\u8BE5\u9879\u76EE\u65F6\u542F\u7528\uFF1B\u514B\u9686\u672A\u77E5\u4ED3\u5E93\u524D\u8BF7\u6838\u67E5 .deepseeker-code/agents/\u3002`);
|
|
@@ -11262,6 +11789,124 @@ ${lines.join("\n")}
|
|
|
11262
11789
|
}
|
|
11263
11790
|
});
|
|
11264
11791
|
|
|
11792
|
+
// ../core/src/session/fork.ts
|
|
11793
|
+
import fs37 from "fs/promises";
|
|
11794
|
+
var listForkAnchors, derivePrefix, forkSession;
|
|
11795
|
+
var init_fork = __esm({
|
|
11796
|
+
"../core/src/session/fork.ts"() {
|
|
11797
|
+
"use strict";
|
|
11798
|
+
init_common();
|
|
11799
|
+
init_transcript();
|
|
11800
|
+
init_store();
|
|
11801
|
+
listForkAnchors = (lines) => {
|
|
11802
|
+
const anchors = [];
|
|
11803
|
+
let lastUser = "";
|
|
11804
|
+
for (const l of lines) {
|
|
11805
|
+
if (isEventLine(l)) continue;
|
|
11806
|
+
const row = l;
|
|
11807
|
+
if (row?.role === "user") {
|
|
11808
|
+
lastUser = typeof row.content === "string" ? row.content : "";
|
|
11809
|
+
} else if (row?.role === "assistant") {
|
|
11810
|
+
const content = typeof row.content === "string" ? row.content : "";
|
|
11811
|
+
const names = Array.isArray(row.tool_calls) ? row.tool_calls.map((tc) => tc?.function?.name ?? "").filter(Boolean).join(", ") : "";
|
|
11812
|
+
anchors.push({
|
|
11813
|
+
lineId: row.id,
|
|
11814
|
+
roundNo: anchors.length + 1,
|
|
11815
|
+
userPreview: lastUser,
|
|
11816
|
+
assistantPreview: content || (names ? `\u{1F527} ${names}` : "(\u7A7A\u56DE\u590D)")
|
|
11817
|
+
});
|
|
11818
|
+
}
|
|
11819
|
+
}
|
|
11820
|
+
return anchors;
|
|
11821
|
+
};
|
|
11822
|
+
derivePrefix = (lines, upToLineId) => {
|
|
11823
|
+
let endIdx = lines.length - 1;
|
|
11824
|
+
if (upToLineId !== void 0) {
|
|
11825
|
+
const idx = lines.findIndex((l) => l?.id === upToLineId);
|
|
11826
|
+
if (idx < 0) throw new Error(`upToLineId\uFF08${upToLineId}\uFF09\u5728\u6E90\u4F1A\u8BDD\u8F6C\u5F55\u4E2D\u672A\u627E\u5230`);
|
|
11827
|
+
endIdx = idx;
|
|
11828
|
+
} else {
|
|
11829
|
+
let lastEnd = -1;
|
|
11830
|
+
let lastStart = -1;
|
|
11831
|
+
lines.forEach((l, i) => {
|
|
11832
|
+
if (!isEventLine(l)) return;
|
|
11833
|
+
if (l.dscEvent === "run.end" || l.dscEvent === "run.abandoned") lastEnd = i;
|
|
11834
|
+
else if (l.dscEvent === "run.start") lastStart = i;
|
|
11835
|
+
});
|
|
11836
|
+
if (lastEnd >= 0) endIdx = lastEnd;
|
|
11837
|
+
else if (lastStart >= 0) endIdx = lastStart - 1;
|
|
11838
|
+
}
|
|
11839
|
+
const prefix = endIdx < 0 ? [] : lines.slice(0, endIdx + 1);
|
|
11840
|
+
let archivedMessageCount = 0;
|
|
11841
|
+
let rollingSummary = "";
|
|
11842
|
+
let lastStartRunId;
|
|
11843
|
+
const closed = /* @__PURE__ */ new Set();
|
|
11844
|
+
for (const l of prefix) {
|
|
11845
|
+
if (!isEventLine(l)) continue;
|
|
11846
|
+
if (l.dscEvent === "compaction") {
|
|
11847
|
+
archivedMessageCount = l.archivedMessageCount;
|
|
11848
|
+
rollingSummary = l.summary;
|
|
11849
|
+
} else if (l.dscEvent === "run.start") {
|
|
11850
|
+
lastStartRunId = l.runId;
|
|
11851
|
+
} else if (l.dscEvent === "run.end" || l.dscEvent === "run.abandoned") {
|
|
11852
|
+
closed.add(l.runId);
|
|
11853
|
+
}
|
|
11854
|
+
}
|
|
11855
|
+
const unclosedRunId = lastStartRunId && !closed.has(lastStartRunId) ? lastStartRunId : void 0;
|
|
11856
|
+
return { lines: prefix, archivedMessageCount, rollingSummary, unclosedRunId };
|
|
11857
|
+
};
|
|
11858
|
+
forkSession = async (sourceId, upToLineId) => {
|
|
11859
|
+
assertSafeSessionId(sourceId);
|
|
11860
|
+
const lines = await readTranscriptLines(sourceId);
|
|
11861
|
+
if (lines.length === 0) throw new Error("\u6E90\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u8F6C\u5F55\u4E3A\u7A7A");
|
|
11862
|
+
const prefix = derivePrefix(lines, upToLineId);
|
|
11863
|
+
const newId = createUUID();
|
|
11864
|
+
await ensureSessionsDir(newId);
|
|
11865
|
+
const rawText = await fs37.readFile(getTranscriptPath(sourceId), "utf-8");
|
|
11866
|
+
const kept = [];
|
|
11867
|
+
let good = 0;
|
|
11868
|
+
for (const s of rawText.split("\n")) {
|
|
11869
|
+
if (good >= prefix.lines.length) break;
|
|
11870
|
+
kept.push(s);
|
|
11871
|
+
const t = s.trim();
|
|
11872
|
+
if (!t) continue;
|
|
11873
|
+
try {
|
|
11874
|
+
JSON.parse(t);
|
|
11875
|
+
good++;
|
|
11876
|
+
} catch {
|
|
11877
|
+
}
|
|
11878
|
+
}
|
|
11879
|
+
let payload = kept.join("\n");
|
|
11880
|
+
if (payload.length > 0 && !payload.endsWith("\n")) payload += "\n";
|
|
11881
|
+
await fs37.writeFile(getTranscriptPath(newId), payload, "utf-8");
|
|
11882
|
+
const srcState = await readStore(sourceId);
|
|
11883
|
+
const srcRolling = await getRollingState(sourceId);
|
|
11884
|
+
const legacy = !lines.some(isEventLine);
|
|
11885
|
+
const wholeFile = prefix.lines.length === lines.length;
|
|
11886
|
+
const archivedMessageCount = legacy && wholeFile ? srcRolling.archivedMessageCount : prefix.archivedMessageCount;
|
|
11887
|
+
const rollingSummary = legacy && wholeFile ? srcRolling.rollingSummary : prefix.rollingSummary;
|
|
11888
|
+
const srcTitle = typeof srcState?.title === "string" && srcState.title ? srcState.title : "\u4F1A\u8BDD";
|
|
11889
|
+
await writeStore(newId, {
|
|
11890
|
+
sessionId: newId,
|
|
11891
|
+
createAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11892
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11893
|
+
archivedMessageCount,
|
|
11894
|
+
rollingSummary,
|
|
11895
|
+
consecutiveFailures: 0,
|
|
11896
|
+
title: `${srcTitle}(\u5206\u53C9)`.slice(0, 200),
|
|
11897
|
+
// ★ 校准数据透传:fork 首轮压缩判定不从 1.4 重来(纯收益,语义同源会话续接)
|
|
11898
|
+
calibRatio: srcRolling.calibRatio,
|
|
11899
|
+
lastRealPromptTokens: srcRolling.lastRealPromptTokens,
|
|
11900
|
+
lastCachedTokens: srcRolling.lastCachedTokens
|
|
11901
|
+
});
|
|
11902
|
+
if (prefix.unclosedRunId) {
|
|
11903
|
+
await appendEvent(newId, { dscEvent: "run.abandoned", runId: prefix.unclosedRunId, reason: "forked from interrupted run" });
|
|
11904
|
+
}
|
|
11905
|
+
return { sessionId: newId, copiedLines: prefix.lines.length, archivedMessageCount };
|
|
11906
|
+
};
|
|
11907
|
+
}
|
|
11908
|
+
});
|
|
11909
|
+
|
|
11265
11910
|
// src/cliHost.ts
|
|
11266
11911
|
var createCliRequestApproval, createCliRequestQuestion;
|
|
11267
11912
|
var init_cliHost = __esm({
|
|
@@ -11346,13 +11991,47 @@ var init_util = __esm({
|
|
|
11346
11991
|
|
|
11347
11992
|
// src/replay.ts
|
|
11348
11993
|
var buildReplayRows;
|
|
11349
|
-
var
|
|
11994
|
+
var init_replay2 = __esm({
|
|
11350
11995
|
"src/replay.ts"() {
|
|
11351
11996
|
"use strict";
|
|
11352
|
-
|
|
11997
|
+
init_transcript();
|
|
11998
|
+
buildReplayRows = (lines, nid) => {
|
|
11353
11999
|
const rows = [];
|
|
11354
12000
|
const pending = /* @__PURE__ */ new Map();
|
|
11355
|
-
|
|
12001
|
+
let lastStartRunId;
|
|
12002
|
+
const closedRuns = /* @__PURE__ */ new Set();
|
|
12003
|
+
const attachUsage = (u) => {
|
|
12004
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
12005
|
+
const r = rows[i];
|
|
12006
|
+
if (r.kind === "assistant") {
|
|
12007
|
+
rows[i] = {
|
|
12008
|
+
...r,
|
|
12009
|
+
usage: {
|
|
12010
|
+
...r.usage ?? {},
|
|
12011
|
+
prompt_tokens: u.prompt_tokens,
|
|
12012
|
+
completion_tokens: u.completion_tokens,
|
|
12013
|
+
total_tokens: u.total_tokens,
|
|
12014
|
+
prompt_cache_hit_tokens: u.cached_tokens
|
|
12015
|
+
}
|
|
12016
|
+
};
|
|
12017
|
+
return;
|
|
12018
|
+
}
|
|
12019
|
+
}
|
|
12020
|
+
};
|
|
12021
|
+
for (const line of lines) {
|
|
12022
|
+
if (isEventLine(line)) {
|
|
12023
|
+
if (line.dscEvent === "run.start") lastStartRunId = line.runId;
|
|
12024
|
+
else if (line.dscEvent === "run.end" || line.dscEvent === "run.abandoned") closedRuns.add(line.runId);
|
|
12025
|
+
if ((line.dscEvent === "round.end" || line.dscEvent === "run.end") && line.usage && (line.usage.prompt_tokens != null || line.usage.total_tokens != null)) {
|
|
12026
|
+
const last = [...rows].reverse().find((r) => r.kind === "assistant");
|
|
12027
|
+
if (line.dscEvent === "round.end" || !last?.usage) attachUsage(line.usage);
|
|
12028
|
+
}
|
|
12029
|
+
if (line.dscEvent === "run.abandoned") {
|
|
12030
|
+
rows.push({ id: nid(), kind: "info", text: "\u26A0\uFE0F \u6B64\u56DE\u5408\u88AB\u4E2D\u65AD\uFF0C\u672A\u5B8C\u6210" });
|
|
12031
|
+
}
|
|
12032
|
+
continue;
|
|
12033
|
+
}
|
|
12034
|
+
const m = line;
|
|
11356
12035
|
const role = m?.role;
|
|
11357
12036
|
if (role === "user") {
|
|
11358
12037
|
const text = typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.filter((p) => typeof p?.text === "string").map((p) => p.text).join("") : "";
|
|
@@ -11402,6 +12081,9 @@ var init_replay = __esm({
|
|
|
11402
12081
|
rows[idx] = { ...r, result: r.result ?? "", ok: false, status: "done" };
|
|
11403
12082
|
}
|
|
11404
12083
|
}
|
|
12084
|
+
if (lastStartRunId && !closedRuns.has(lastStartRunId)) {
|
|
12085
|
+
rows.push({ id: nid(), kind: "info", text: "\u26A0\uFE0F \u6B64\u56DE\u5408\u88AB\u4E2D\u65AD\uFF0C\u672A\u5B8C\u6210" });
|
|
12086
|
+
}
|
|
11405
12087
|
return rows;
|
|
11406
12088
|
};
|
|
11407
12089
|
}
|
|
@@ -11416,13 +12098,15 @@ var init_useChatState = __esm({
|
|
|
11416
12098
|
init_chatProcessing();
|
|
11417
12099
|
init_store();
|
|
11418
12100
|
init_transcript();
|
|
12101
|
+
init_fork();
|
|
12102
|
+
init_inbox();
|
|
11419
12103
|
init_contextCore();
|
|
11420
12104
|
init_createModel();
|
|
11421
12105
|
init_tool();
|
|
11422
12106
|
init_cliHost();
|
|
11423
12107
|
init_strings();
|
|
11424
12108
|
init_util();
|
|
11425
|
-
|
|
12109
|
+
init_replay2();
|
|
11426
12110
|
FLUSH_MS = 120;
|
|
11427
12111
|
ABORT_GRACE_MS = 8e3;
|
|
11428
12112
|
ENTER_PLAN_RESEARCH_PROMPT = "\uFF08\u5DF2\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F\u3002\u8BF7\u4EE5\u53EA\u8BFB\u65B9\u5F0F\u5B8C\u6210\u8C03\u7814\uFF0C\u7136\u540E\u8C03\u7528 exit_plan_mode \u63D0\u4EA4\u5B8C\u6574\u5B9E\u73B0\u65B9\u6848\u3002\uFF09";
|
|
@@ -11440,6 +12124,7 @@ var init_useChatState = __esm({
|
|
|
11440
12124
|
const [pendingQuestion, setPendingQuestion] = useState(null);
|
|
11441
12125
|
const [pendingPlan, setPendingPlan] = useState(null);
|
|
11442
12126
|
const [pendingSessions, setPendingSessions] = useState(null);
|
|
12127
|
+
const [pendingFork, setPendingFork] = useState(null);
|
|
11443
12128
|
const nextId = useRef(1);
|
|
11444
12129
|
const assistantStreamingId = useRef(null);
|
|
11445
12130
|
const thinkingStreamingId = useRef(null);
|
|
@@ -11452,6 +12137,7 @@ var init_useChatState = __esm({
|
|
|
11452
12137
|
const currentAcRef = useRef(null);
|
|
11453
12138
|
const turnGenRef = useRef(0);
|
|
11454
12139
|
const abortGuardRef = useRef(null);
|
|
12140
|
+
const queuedUnclaimedRef = useRef(0);
|
|
11455
12141
|
const proposedPlanRef = useRef(null);
|
|
11456
12142
|
const enterPlanReasonRef = useRef(null);
|
|
11457
12143
|
const modelRef = useRef("");
|
|
@@ -11466,8 +12152,8 @@ var init_useChatState = __esm({
|
|
|
11466
12152
|
if (!initialSessionId) return;
|
|
11467
12153
|
void (async () => {
|
|
11468
12154
|
try {
|
|
11469
|
-
const
|
|
11470
|
-
const seeded = buildReplayRows(
|
|
12155
|
+
const lines = await readTranscriptLines(initialSessionId);
|
|
12156
|
+
const seeded = buildReplayRows(lines, newRowId);
|
|
11471
12157
|
if (seeded.length) setRows(seeded);
|
|
11472
12158
|
} catch {
|
|
11473
12159
|
}
|
|
@@ -11650,10 +12336,18 @@ var init_useChatState = __esm({
|
|
|
11650
12336
|
}
|
|
11651
12337
|
case "approval_request":
|
|
11652
12338
|
break;
|
|
12339
|
+
case "inbox.claimed": {
|
|
12340
|
+
const n = Array.isArray(obj.texts) ? obj.texts.length : 0;
|
|
12341
|
+
if (n > 0) {
|
|
12342
|
+
queuedUnclaimedRef.current = Math.max(0, queuedUnclaimedRef.current - n);
|
|
12343
|
+
pushInfo(S.inboxClaimed(n));
|
|
12344
|
+
}
|
|
12345
|
+
break;
|
|
12346
|
+
}
|
|
11653
12347
|
default:
|
|
11654
12348
|
break;
|
|
11655
12349
|
}
|
|
11656
|
-
}, [closeStreaming, flush, scheduleFlush]);
|
|
12350
|
+
}, [closeStreaming, flush, scheduleFlush, pushInfo]);
|
|
11657
12351
|
const askApproval = useCallback((detail, toolName) => new Promise((resolve) => setPendingApproval({ detail, toolName, resolve })), []);
|
|
11658
12352
|
const resolveApproval = useCallback((v) => {
|
|
11659
12353
|
setPendingApproval((prev) => {
|
|
@@ -11733,9 +12427,13 @@ var init_useChatState = __esm({
|
|
|
11733
12427
|
clearTimeout(abortGuardRef.current);
|
|
11734
12428
|
abortGuardRef.current = null;
|
|
11735
12429
|
}
|
|
12430
|
+
if (queuedUnclaimedRef.current > 0) {
|
|
12431
|
+
pushInfo(S.inboxCarryover(queuedUnclaimedRef.current));
|
|
12432
|
+
queuedUnclaimedRef.current = 0;
|
|
12433
|
+
}
|
|
11736
12434
|
}
|
|
11737
12435
|
}
|
|
11738
|
-
}, [askApproval, autoRequestApproval, flush, onTrace, pushEvent]);
|
|
12436
|
+
}, [askApproval, autoRequestApproval, flush, onTrace, pushEvent, pushInfo]);
|
|
11739
12437
|
const approveAndImplement = useCallback(async (sid, plan) => {
|
|
11740
12438
|
const res = await setPlan(plan);
|
|
11741
12439
|
if (res.action === "accept") {
|
|
@@ -11753,6 +12451,15 @@ ${finalPlan}`, false, res.autoExecute);
|
|
|
11753
12451
|
const plan = takeProposedPlan();
|
|
11754
12452
|
if (plan != null) await approveAndImplement(sid, plan);
|
|
11755
12453
|
}, [runOnce, takeProposedPlan, approveAndImplement]);
|
|
12454
|
+
const queueInput = useCallback((content) => {
|
|
12455
|
+
const text = content.trim();
|
|
12456
|
+
const sid = sessionIdRef.current;
|
|
12457
|
+
if (!text || !sid || !pushSessionInbox(sid, text)) return false;
|
|
12458
|
+
pushUser(text);
|
|
12459
|
+
pushInfo(S.inboxQueued);
|
|
12460
|
+
queuedUnclaimedRef.current++;
|
|
12461
|
+
return true;
|
|
12462
|
+
}, [pushUser, pushInfo]);
|
|
11756
12463
|
const submit = useCallback(async (content) => {
|
|
11757
12464
|
const text = content.trim();
|
|
11758
12465
|
if (!text || busy) return;
|
|
@@ -11813,8 +12520,8 @@ ${finalPlan}`, false, res.autoExecute);
|
|
|
11813
12520
|
sessionIdRef.current = id;
|
|
11814
12521
|
clearRows();
|
|
11815
12522
|
try {
|
|
11816
|
-
const
|
|
11817
|
-
const seeded = buildReplayRows(
|
|
12523
|
+
const lines = await readTranscriptLines(id);
|
|
12524
|
+
const seeded = buildReplayRows(lines, newRowId);
|
|
11818
12525
|
if (seeded.length) setRows(seeded);
|
|
11819
12526
|
} catch {
|
|
11820
12527
|
}
|
|
@@ -11835,6 +12542,37 @@ ${finalPlan}`, false, res.autoExecute);
|
|
|
11835
12542
|
const picked = await new Promise((resolve) => setPendingSessions({ sessions: sessions2, resolve }));
|
|
11836
12543
|
if (picked) await loadSession(picked);
|
|
11837
12544
|
}, [pushInfo, loadSession]);
|
|
12545
|
+
const resolveFork = useCallback((anchor) => {
|
|
12546
|
+
setPendingFork((prev) => {
|
|
12547
|
+
prev?.resolve(anchor);
|
|
12548
|
+
return null;
|
|
12549
|
+
});
|
|
12550
|
+
}, []);
|
|
12551
|
+
const openForkPicker = useCallback(async () => {
|
|
12552
|
+
const sid = sessionIdRef.current;
|
|
12553
|
+
if (!sid) {
|
|
12554
|
+
pushInfo(S.forkEmpty);
|
|
12555
|
+
return;
|
|
12556
|
+
}
|
|
12557
|
+
let anchors = [];
|
|
12558
|
+
try {
|
|
12559
|
+
anchors = listForkAnchors(await readTranscriptLines(sid));
|
|
12560
|
+
} catch {
|
|
12561
|
+
}
|
|
12562
|
+
if (anchors.length === 0) {
|
|
12563
|
+
pushInfo(S.forkEmpty);
|
|
12564
|
+
return;
|
|
12565
|
+
}
|
|
12566
|
+
const picked = await new Promise((resolve) => setPendingFork({ anchors, resolve }));
|
|
12567
|
+
if (!picked) return;
|
|
12568
|
+
try {
|
|
12569
|
+
const r = await forkSession(sid, picked.lineId);
|
|
12570
|
+
await loadSession(r.sessionId);
|
|
12571
|
+
pushInfo(S.forkDone(truncateMiddle(r.sessionId, 12), picked.roundNo));
|
|
12572
|
+
} catch (e) {
|
|
12573
|
+
pushInfo(S.forkFailed(e?.message ?? String(e)));
|
|
12574
|
+
}
|
|
12575
|
+
}, [pushInfo, loadSession]);
|
|
11838
12576
|
const setModelOverride = useCallback((m) => {
|
|
11839
12577
|
modelRef.current = m;
|
|
11840
12578
|
}, []);
|
|
@@ -11864,9 +12602,11 @@ ${finalPlan}`, false, res.autoExecute);
|
|
|
11864
12602
|
pendingQuestion,
|
|
11865
12603
|
pendingPlan,
|
|
11866
12604
|
pendingSessions,
|
|
12605
|
+
pendingFork,
|
|
11867
12606
|
sessionIdRef,
|
|
11868
12607
|
// 动作
|
|
11869
12608
|
submit,
|
|
12609
|
+
queueInput,
|
|
11870
12610
|
abortCurrent,
|
|
11871
12611
|
pushUser,
|
|
11872
12612
|
pushInfo,
|
|
@@ -11889,7 +12629,9 @@ ${finalPlan}`, false, res.autoExecute);
|
|
|
11889
12629
|
getOutputStyle: getOutputStyle2,
|
|
11890
12630
|
openSessionPicker,
|
|
11891
12631
|
resolveSession,
|
|
11892
|
-
loadSession
|
|
12632
|
+
loadSession,
|
|
12633
|
+
openForkPicker,
|
|
12634
|
+
resolveFork
|
|
11893
12635
|
};
|
|
11894
12636
|
};
|
|
11895
12637
|
}
|
|
@@ -12491,9 +13233,45 @@ var init_SessionPicker = __esm({
|
|
|
12491
13233
|
}
|
|
12492
13234
|
});
|
|
12493
13235
|
|
|
12494
|
-
// src/components/
|
|
13236
|
+
// src/components/ForkPicker.tsx
|
|
12495
13237
|
import { Box as Box11, Text as Text13 } from "ink";
|
|
12496
13238
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
13239
|
+
var PAGE_SIZE2, ForkPicker;
|
|
13240
|
+
var init_ForkPicker = __esm({
|
|
13241
|
+
"src/components/ForkPicker.tsx"() {
|
|
13242
|
+
"use strict";
|
|
13243
|
+
init_theme();
|
|
13244
|
+
init_strings();
|
|
13245
|
+
init_util();
|
|
13246
|
+
PAGE_SIZE2 = 8;
|
|
13247
|
+
ForkPicker = ({ anchors, selectedIndex, wrapW }) => {
|
|
13248
|
+
const start = Math.floor(selectedIndex / PAGE_SIZE2) * PAGE_SIZE2;
|
|
13249
|
+
const visible = anchors.slice(start, start + PAGE_SIZE2);
|
|
13250
|
+
const contentW = Math.max(24, wrapW);
|
|
13251
|
+
const userW = Math.max(10, Math.floor(contentW * 0.45));
|
|
13252
|
+
const replyW = Math.max(10, contentW - userW - 4);
|
|
13253
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", borderStyle: "round", borderColor: THEME.blue, paddingX: 1, paddingY: 1, marginTop: 1, children: [
|
|
13254
|
+
/* @__PURE__ */ jsx13(Text13, { color: THEME.blue, bold: true, children: S.forkTitle }),
|
|
13255
|
+
/* @__PURE__ */ jsx13(Text13, { color: THEME.grayDim, children: S.forkPrompt }),
|
|
13256
|
+
/* @__PURE__ */ jsx13(Box11, { flexDirection: "column", marginTop: 0.5, children: visible.map((a, i) => {
|
|
13257
|
+
const active = start + i === selectedIndex;
|
|
13258
|
+
const user = truncateMiddle(a.userPreview || "(\u65E0\u63D0\u95EE)", userW);
|
|
13259
|
+
const reply = truncateMiddle(a.assistantPreview, replyW);
|
|
13260
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", children: [
|
|
13261
|
+
/* @__PURE__ */ jsx13(Text13, { color: active ? THEME.coralBright : void 0, bold: true, children: active ? "\u203A " : " " }),
|
|
13262
|
+
/* @__PURE__ */ jsx13(Text13, { color: active ? THEME.white : THEME.gray, bold: active, children: `#${a.roundNo} ${user}` }),
|
|
13263
|
+
/* @__PURE__ */ jsx13(Text13, { color: THEME.grayDim, children: ` \u2192 ${reply}` })
|
|
13264
|
+
] }, a.lineId);
|
|
13265
|
+
}) }),
|
|
13266
|
+
anchors.length > PAGE_SIZE2 ? /* @__PURE__ */ jsx13(Text13, { color: THEME.grayDim, children: `\u7B2C ${start + 1}-${Math.min(start + PAGE_SIZE2, anchors.length)} / ${anchors.length} \u4E2A\uFF08\u2191\u2193 \u9009\u62E9\xB7\u81EA\u52A8\u7FFB\u9875\uFF09` }) : null
|
|
13267
|
+
] });
|
|
13268
|
+
};
|
|
13269
|
+
}
|
|
13270
|
+
});
|
|
13271
|
+
|
|
13272
|
+
// src/components/SlashMenu.tsx
|
|
13273
|
+
import { Box as Box12, Text as Text14 } from "ink";
|
|
13274
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
12497
13275
|
var MAX_ROWS, SlashMenu;
|
|
12498
13276
|
var init_SlashMenu = __esm({
|
|
12499
13277
|
"src/components/SlashMenu.tsx"() {
|
|
@@ -12505,22 +13283,22 @@ var init_SlashMenu = __esm({
|
|
|
12505
13283
|
const more = entries.length > MAX_ROWS;
|
|
12506
13284
|
const cmdW = Math.min(22, Math.max(8, ...visible.map((e) => e.name.length)) + 1);
|
|
12507
13285
|
const descMax = Math.max(12, cols - cmdW - 6);
|
|
12508
|
-
return /* @__PURE__ */
|
|
13286
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: THEME.coral, paddingX: 1, paddingY: 0, marginTop: 1, children: [
|
|
12509
13287
|
visible.map((e, i) => {
|
|
12510
13288
|
const active = i === selectedIndex;
|
|
12511
13289
|
const desc = e.description.length > descMax ? `${e.description.slice(0, descMax - 1)}\u2026` : e.description;
|
|
12512
13290
|
const gap = Math.max(1, cmdW - e.name.length);
|
|
12513
|
-
return /* @__PURE__ */
|
|
12514
|
-
/* @__PURE__ */
|
|
12515
|
-
/* @__PURE__ */
|
|
13291
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", children: [
|
|
13292
|
+
/* @__PURE__ */ jsx14(Text14, { color: active ? THEME.coralBright : void 0, bold: true, children: active ? "\u276F " : " " }),
|
|
13293
|
+
/* @__PURE__ */ jsxs12(Text14, { color: active ? THEME.white : THEME.gray, bold: active, children: [
|
|
12516
13294
|
"/",
|
|
12517
13295
|
e.name
|
|
12518
13296
|
] }),
|
|
12519
|
-
/* @__PURE__ */
|
|
12520
|
-
/* @__PURE__ */
|
|
13297
|
+
/* @__PURE__ */ jsx14(Text14, { color: THEME.grayDim, children: " ".repeat(gap) }),
|
|
13298
|
+
/* @__PURE__ */ jsx14(Text14, { color: active ? THEME.gray : THEME.grayDim, children: desc })
|
|
12521
13299
|
] }, e.name);
|
|
12522
13300
|
}),
|
|
12523
|
-
more ? /* @__PURE__ */
|
|
13301
|
+
more ? /* @__PURE__ */ jsxs12(Text14, { color: THEME.coralMuted, children: [
|
|
12524
13302
|
"\u2026 \u8FD8\u6709 ",
|
|
12525
13303
|
entries.length - MAX_ROWS,
|
|
12526
13304
|
" \u6761"
|
|
@@ -12531,8 +13309,8 @@ var init_SlashMenu = __esm({
|
|
|
12531
13309
|
});
|
|
12532
13310
|
|
|
12533
13311
|
// src/components/StatusStrip.tsx
|
|
12534
|
-
import { Box as
|
|
12535
|
-
import { jsx as
|
|
13312
|
+
import { Box as Box13, Text as Text15 } from "ink";
|
|
13313
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
12536
13314
|
var StatusStrip;
|
|
12537
13315
|
var init_StatusStrip = __esm({
|
|
12538
13316
|
"src/components/StatusStrip.tsx"() {
|
|
@@ -12549,18 +13327,18 @@ var init_StatusStrip = __esm({
|
|
|
12549
13327
|
`state: ${stateText}`,
|
|
12550
13328
|
sessionShort ? `session: ${sessionShort}` : ""
|
|
12551
13329
|
].filter(Boolean);
|
|
12552
|
-
return /* @__PURE__ */
|
|
12553
|
-
/* @__PURE__ */
|
|
12554
|
-
/* @__PURE__ */
|
|
12555
|
-
/* @__PURE__ */
|
|
13330
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexShrink: 0, children: [
|
|
13331
|
+
/* @__PURE__ */ jsx15(Text15, { color: THEME.coralMuted, children: customLine && customLine.trim() ? customLine : bits.join(" \xB7 ") }),
|
|
13332
|
+
/* @__PURE__ */ jsx15(Text15, { color: stateColor, children: "" }),
|
|
13333
|
+
/* @__PURE__ */ jsx15(Text15, { color: THEME.grayDim, children: S.cmdHint })
|
|
12556
13334
|
] });
|
|
12557
13335
|
};
|
|
12558
13336
|
}
|
|
12559
13337
|
});
|
|
12560
13338
|
|
|
12561
13339
|
// src/components/TopPanel.tsx
|
|
12562
|
-
import { Box as
|
|
12563
|
-
import { jsx as
|
|
13340
|
+
import { Box as Box14, Text as Text16 } from "ink";
|
|
13341
|
+
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
12564
13342
|
var ROBOT, INNER_H, TopPanel;
|
|
12565
13343
|
var init_TopPanel = __esm({
|
|
12566
13344
|
"src/components/TopPanel.tsx"() {
|
|
@@ -12580,36 +13358,36 @@ var init_TopPanel = __esm({
|
|
|
12580
13358
|
const leftW = Math.floor(innerW * 0.33);
|
|
12581
13359
|
const rightW = innerW - leftW - 1;
|
|
12582
13360
|
const ruleLen = Math.max(8, rightW - 2);
|
|
12583
|
-
const VDivider = /* @__PURE__ */
|
|
12584
|
-
return /* @__PURE__ */
|
|
12585
|
-
/* @__PURE__ */
|
|
12586
|
-
/* @__PURE__ */
|
|
12587
|
-
/* @__PURE__ */
|
|
12588
|
-
/* @__PURE__ */
|
|
12589
|
-
/* @__PURE__ */
|
|
12590
|
-
/* @__PURE__ */
|
|
13361
|
+
const VDivider = /* @__PURE__ */ jsx16(Box14, { flexDirection: "column", width: 1, flexShrink: 0, children: Array.from({ length: INNER_H }).map((_, i) => /* @__PURE__ */ jsx16(Text16, { color: THEME.coral, children: "\u2502" }, i)) });
|
|
13362
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", width: panelW, marginBottom: 1, children: [
|
|
13363
|
+
/* @__PURE__ */ jsxs14(Text16, { children: [
|
|
13364
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.coralBright, bold: true, children: "* Welcome to " }),
|
|
13365
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, color: THEME.white, children: "deep" }),
|
|
13366
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, color: THEME.coralBright, children: "Seek" }),
|
|
13367
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, color: THEME.white, children: "Code" }),
|
|
13368
|
+
/* @__PURE__ */ jsxs14(Text16, { color: THEME.coralMuted, children: [
|
|
12591
13369
|
" ",
|
|
12592
13370
|
VERSION
|
|
12593
13371
|
] })
|
|
12594
13372
|
] }),
|
|
12595
|
-
/* @__PURE__ */
|
|
12596
|
-
/* @__PURE__ */
|
|
12597
|
-
/* @__PURE__ */
|
|
12598
|
-
/* @__PURE__ */
|
|
12599
|
-
ROBOT.map((l, i) => /* @__PURE__ */
|
|
12600
|
-
/* @__PURE__ */
|
|
12601
|
-
/* @__PURE__ */
|
|
13373
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: cwd }),
|
|
13374
|
+
/* @__PURE__ */ jsxs14(Box14, { flexDirection: "row", borderStyle: "round", borderColor: THEME.coral, marginTop: 1, paddingX: 0, paddingY: 0, children: [
|
|
13375
|
+
/* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", width: leftW, height: INNER_H, alignItems: "center", justifyContent: "center", children: [
|
|
13376
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.white, bold: true, children: "Welcome back!" }),
|
|
13377
|
+
ROBOT.map((l, i) => /* @__PURE__ */ jsx16(Text16, { color: THEME.coralBright, children: l }, i)),
|
|
13378
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: "Ctrl+C \u9000\u51FA \xB7 Esc \u4E2D\u6B62" }),
|
|
13379
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: "\u8F93\u5165 / \u67E5\u770B\u547D\u4EE4" })
|
|
12602
13380
|
] }),
|
|
12603
13381
|
VDivider,
|
|
12604
|
-
/* @__PURE__ */
|
|
12605
|
-
/* @__PURE__ */
|
|
12606
|
-
/* @__PURE__ */
|
|
12607
|
-
/* @__PURE__ */
|
|
13382
|
+
/* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", width: rightW, height: INNER_H, justifyContent: "space-between", paddingX: 1, children: [
|
|
13383
|
+
/* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
13384
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.coralBright, bold: true, children: "Tips for getting started" }),
|
|
13385
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.white, children: "\u8F93\u5165\u9700\u6C42\u5F00\u59CB\uFF0C\u8F93\u5165 / \u5524\u51FA\u547D\u4EE4\u3002" })
|
|
12608
13386
|
] }),
|
|
12609
|
-
/* @__PURE__ */
|
|
12610
|
-
/* @__PURE__ */
|
|
12611
|
-
/* @__PURE__ */
|
|
12612
|
-
/* @__PURE__ */
|
|
13387
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: "\u2500".repeat(ruleLen) }),
|
|
13388
|
+
/* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
13389
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.coralBright, bold: true, children: "What's new" }),
|
|
13390
|
+
/* @__PURE__ */ jsx16(Text16, { color: THEME.white, children: "\u5DF2\u5BF9\u63A5\u672C\u5730 Tool Call \u667A\u80FD\u51FD\u6570\u8C03\u7528\u3002" })
|
|
12613
13391
|
] })
|
|
12614
13392
|
] })
|
|
12615
13393
|
] })
|
|
@@ -12619,7 +13397,7 @@ var init_TopPanel = __esm({
|
|
|
12619
13397
|
});
|
|
12620
13398
|
|
|
12621
13399
|
// src/inspect.ts
|
|
12622
|
-
import
|
|
13400
|
+
import fs38 from "fs";
|
|
12623
13401
|
var CWD, fmt, readTrace, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
|
|
12624
13402
|
var init_inspect = __esm({
|
|
12625
13403
|
"src/inspect.ts"() {
|
|
@@ -12637,7 +13415,7 @@ var init_inspect = __esm({
|
|
|
12637
13415
|
if (!sessionId) return [];
|
|
12638
13416
|
try {
|
|
12639
13417
|
const p = await getTraceStorePath(sessionId);
|
|
12640
|
-
const text = await
|
|
13418
|
+
const text = await fs38.promises.readFile(p, "utf8");
|
|
12641
13419
|
const out = [];
|
|
12642
13420
|
for (const line of text.split("\n")) {
|
|
12643
13421
|
if (!line.trim()) continue;
|
|
@@ -12760,8 +13538,8 @@ __export(App_exports, {
|
|
|
12760
13538
|
App: () => App
|
|
12761
13539
|
});
|
|
12762
13540
|
import { useCallback as useCallback2, useEffect as useEffect4, useMemo, useRef as useRef2, useState as useState4 } from "react";
|
|
12763
|
-
import { Box as
|
|
12764
|
-
import { jsx as
|
|
13541
|
+
import { Box as Box15, Static, Text as Text17, useApp, useInput as useInput2, useStdout } from "ink";
|
|
13542
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
12765
13543
|
var KNOWN_MODELS, CWD2, RowView, isDynamicRow, App;
|
|
12766
13544
|
var init_App = __esm({
|
|
12767
13545
|
"src/App.tsx"() {
|
|
@@ -12786,6 +13564,7 @@ var init_App = __esm({
|
|
|
12786
13564
|
init_PlanModal();
|
|
12787
13565
|
init_PlanEditor();
|
|
12788
13566
|
init_SessionPicker();
|
|
13567
|
+
init_ForkPicker();
|
|
12789
13568
|
init_SlashMenu();
|
|
12790
13569
|
init_MultilineInput();
|
|
12791
13570
|
init_StatusStrip();
|
|
@@ -12794,16 +13573,16 @@ var init_App = __esm({
|
|
|
12794
13573
|
KNOWN_MODELS = ["deepseek-v4", "deepseek-v4-flash"];
|
|
12795
13574
|
CWD2 = process.cwd();
|
|
12796
13575
|
RowView = ({ row, wrapW, streamTail, showThinking }) => {
|
|
12797
|
-
if (row.kind === "todos") return /* @__PURE__ */
|
|
12798
|
-
if (row.kind === "tool") return /* @__PURE__ */
|
|
12799
|
-
if (row.kind === "thinking") return /* @__PURE__ */
|
|
13576
|
+
if (row.kind === "todos") return /* @__PURE__ */ jsx17(TodosPanel, { todos: row.todos, wrapW });
|
|
13577
|
+
if (row.kind === "tool") return /* @__PURE__ */ jsx17(ToolCard, { toolName: row.toolName, args: row.args, result: row.result, ok: row.ok, status: row.status, progress: row.progress, wrapW });
|
|
13578
|
+
if (row.kind === "thinking") return /* @__PURE__ */ jsx17(ThinkingBlock, { streaming: row.streaming ?? false, startedAt: row.startedAt, durationMs: row.durationMs, tokens: row.tokens, text: row.text, expanded: showThinking, wrapW });
|
|
12800
13579
|
if (row.kind === "user") {
|
|
12801
|
-
return /* @__PURE__ */
|
|
12802
|
-
/* @__PURE__ */
|
|
12803
|
-
/* @__PURE__ */
|
|
13580
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", marginTop: 1, children: [
|
|
13581
|
+
/* @__PURE__ */ jsx17(Text17, { color: THEME.grayDim, children: "\u2500".repeat(Math.max(8, wrapW)) }),
|
|
13582
|
+
/* @__PURE__ */ jsx17(MessageBlock, { row, wrapW, streamTail })
|
|
12804
13583
|
] });
|
|
12805
13584
|
}
|
|
12806
|
-
return /* @__PURE__ */
|
|
13585
|
+
return /* @__PURE__ */ jsx17(MessageBlock, { row, wrapW, streamTail });
|
|
12807
13586
|
};
|
|
12808
13587
|
isDynamicRow = (r) => r.kind === "assistant" && !!r.streaming || r.kind === "thinking" && !!r.streaming || r.kind === "tool" && r.status === "running" || r.kind === "todos" && !!r.active;
|
|
12809
13588
|
App = ({ resumeSessionId, initialPlanMode, initialAutoMode, initialIncludeProject }) => {
|
|
@@ -12903,6 +13682,8 @@ var init_App = __esm({
|
|
|
12903
13682
|
return S.cmdOutputStyle;
|
|
12904
13683
|
case "sessions":
|
|
12905
13684
|
return S.cmdSessions;
|
|
13685
|
+
case "fork":
|
|
13686
|
+
return S.cmdFork;
|
|
12906
13687
|
case "usage":
|
|
12907
13688
|
return S.cmdUsage;
|
|
12908
13689
|
case "context":
|
|
@@ -12935,13 +13716,13 @@ var init_App = __esm({
|
|
|
12935
13716
|
const q = input.slice(1).toLowerCase();
|
|
12936
13717
|
return menuEntries.filter((c) => c.name.toLowerCase().startsWith(q));
|
|
12937
13718
|
}, [input, menuEntries]);
|
|
12938
|
-
const menuActive = state.pendingApproval != null || state.pendingQuestion != null || state.pendingPlan != null || state.pendingSessions != null;
|
|
13719
|
+
const menuActive = state.pendingApproval != null || state.pendingQuestion != null || state.pendingPlan != null || state.pendingSessions != null || state.pendingFork != null;
|
|
12939
13720
|
const slashVisible = !menuActive && input.startsWith("/") && filteredCommands.length > 0;
|
|
12940
13721
|
const inputActive = !menuActive;
|
|
12941
13722
|
useEffect4(() => {
|
|
12942
13723
|
setSelectIdx(0);
|
|
12943
13724
|
setPlanEditing(false);
|
|
12944
|
-
}, [state.pendingApproval, state.pendingQuestion, state.pendingPlan, state.pendingSessions, slashVisible, filteredCommands.length]);
|
|
13725
|
+
}, [state.pendingApproval, state.pendingQuestion, state.pendingPlan, state.pendingSessions, state.pendingFork, slashVisible, filteredCommands.length]);
|
|
12945
13726
|
useEffect4(() => {
|
|
12946
13727
|
setQCursor(0);
|
|
12947
13728
|
setQChecked(/* @__PURE__ */ new Set());
|
|
@@ -12960,6 +13741,9 @@ var init_App = __esm({
|
|
|
12960
13741
|
case "/sessions":
|
|
12961
13742
|
void state.openSessionPicker();
|
|
12962
13743
|
return true;
|
|
13744
|
+
case "/fork":
|
|
13745
|
+
void state.openForkPicker();
|
|
13746
|
+
return true;
|
|
12963
13747
|
case "/help":
|
|
12964
13748
|
state.pushInfo(S.helpText(modelDisplay, state.getThinkingLevel(), getLocale()));
|
|
12965
13749
|
return true;
|
|
@@ -13108,7 +13892,12 @@ ${listText}`);
|
|
|
13108
13892
|
const v = inputRef.current.trim();
|
|
13109
13893
|
if (!v) return;
|
|
13110
13894
|
if (busyRef.current) {
|
|
13111
|
-
|
|
13895
|
+
if (!v.startsWith("/") && state.queueInput(v)) {
|
|
13896
|
+
setInput("");
|
|
13897
|
+
setCursor(0);
|
|
13898
|
+
} else {
|
|
13899
|
+
state.pushEvent({ type: "error", message: S.busyBlockSend });
|
|
13900
|
+
}
|
|
13112
13901
|
return;
|
|
13113
13902
|
}
|
|
13114
13903
|
setInput("");
|
|
@@ -13190,6 +13979,16 @@ ${listText}`);
|
|
|
13190
13979
|
} else if (key.escape || key.ctrl && ch === "g") state.resolveSession(null);
|
|
13191
13980
|
return;
|
|
13192
13981
|
}
|
|
13982
|
+
if (state.pendingFork) {
|
|
13983
|
+
const list = state.pendingFork.anchors;
|
|
13984
|
+
if (key.upArrow) setSelectIdx((i) => (i - 1 + list.length) % list.length);
|
|
13985
|
+
else if (key.downArrow) setSelectIdx((i) => (i + 1) % list.length);
|
|
13986
|
+
else if (key.return) {
|
|
13987
|
+
const sel = list[Math.min(selectIdxRef.current, list.length - 1)];
|
|
13988
|
+
state.resolveFork(sel ?? null);
|
|
13989
|
+
} else if (key.escape || key.ctrl && ch === "g") state.resolveFork(null);
|
|
13990
|
+
return;
|
|
13991
|
+
}
|
|
13193
13992
|
if (key.ctrl && ch === "t") {
|
|
13194
13993
|
state.toggleShowThinking();
|
|
13195
13994
|
return;
|
|
@@ -13230,19 +14029,19 @@ ${listText}`);
|
|
|
13230
14029
|
}
|
|
13231
14030
|
});
|
|
13232
14031
|
const sessionShort = state.sessionIdRef.current ? truncateMiddle(state.sessionIdRef.current, 12) : "";
|
|
13233
|
-
return /* @__PURE__ */
|
|
13234
|
-
/* @__PURE__ */
|
|
13235
|
-
/* @__PURE__ */
|
|
13236
|
-
menuActive ? null : /* @__PURE__ */
|
|
13237
|
-
dynamicRows.map((row) => /* @__PURE__ */
|
|
13238
|
-
state.busy ? /* @__PURE__ */
|
|
14032
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", width: cols, children: [
|
|
14033
|
+
/* @__PURE__ */ jsx17(Static, { items: staticItems, children: (item) => item.kind === "__header" ? /* @__PURE__ */ jsx17(TopPanel, { cwd: CWD2, cols }, "header") : /* @__PURE__ */ jsx17(RowView, { row: item, wrapW, streamTail, showThinking: state.showThinkingText }, item.id) }),
|
|
14034
|
+
/* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", width: cols, children: [
|
|
14035
|
+
menuActive ? null : /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingX: 1, children: [
|
|
14036
|
+
dynamicRows.map((row) => /* @__PURE__ */ jsx17(RowView, { row, wrapW, streamTail, showThinking: state.showThinkingText }, row.id)),
|
|
14037
|
+
state.busy ? /* @__PURE__ */ jsx17(Box15, { marginTop: 0.5, children: /* @__PURE__ */ jsxs15(Text17, { color: THEME.coralBright, children: [
|
|
13239
14038
|
"\u25CF ",
|
|
13240
14039
|
S.generating
|
|
13241
14040
|
] }) }) : null
|
|
13242
14041
|
] }),
|
|
13243
|
-
/* @__PURE__ */
|
|
13244
|
-
state.pendingApproval ? /* @__PURE__ */
|
|
13245
|
-
state.pendingQuestion ? /* @__PURE__ */
|
|
14042
|
+
/* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingX: 1, flexShrink: 0, children: [
|
|
14043
|
+
state.pendingApproval ? /* @__PURE__ */ jsx17(ApprovalModal, { toolName: state.pendingApproval.toolName, detail: state.pendingApproval.detail, selectedIndex: selectIdx, wrapW }) : null,
|
|
14044
|
+
state.pendingQuestion ? /* @__PURE__ */ jsx17(
|
|
13246
14045
|
QuestionModal,
|
|
13247
14046
|
{
|
|
13248
14047
|
question: state.pendingQuestion.req.question,
|
|
@@ -13253,7 +14052,7 @@ ${listText}`);
|
|
|
13253
14052
|
wrapW
|
|
13254
14053
|
}
|
|
13255
14054
|
) : null,
|
|
13256
|
-
state.pendingPlan ? planEditing ? /* @__PURE__ */
|
|
14055
|
+
state.pendingPlan ? planEditing ? /* @__PURE__ */ jsx17(
|
|
13257
14056
|
PlanEditor,
|
|
13258
14057
|
{
|
|
13259
14058
|
value: planDraft,
|
|
@@ -13264,13 +14063,14 @@ ${listText}`);
|
|
|
13264
14063
|
},
|
|
13265
14064
|
onSubmit: confirmPlanEdit
|
|
13266
14065
|
}
|
|
13267
|
-
) : /* @__PURE__ */
|
|
13268
|
-
state.pendingSessions ? /* @__PURE__ */
|
|
13269
|
-
|
|
14066
|
+
) : /* @__PURE__ */ jsx17(PlanModal, { selectedIndex: selectIdx }) : null,
|
|
14067
|
+
state.pendingSessions ? /* @__PURE__ */ jsx17(SessionPicker, { sessions: state.pendingSessions.sessions, selectedIndex: selectIdx, wrapW }) : null,
|
|
14068
|
+
state.pendingFork ? /* @__PURE__ */ jsx17(ForkPicker, { anchors: state.pendingFork.anchors, selectedIndex: selectIdx, wrapW }) : null,
|
|
14069
|
+
slashVisible ? /* @__PURE__ */ jsx17(SlashMenu, { entries: filteredCommands, selectedIndex: selectIdx, cols }) : null
|
|
13270
14070
|
] }),
|
|
13271
|
-
/* @__PURE__ */
|
|
13272
|
-
/* @__PURE__ */
|
|
13273
|
-
/* @__PURE__ */
|
|
14071
|
+
/* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingX: 1, marginTop: 1, flexShrink: 0, children: [
|
|
14072
|
+
/* @__PURE__ */ jsx17(Text17, { dimColor: true, color: THEME.gray, children: dimRule(cols) }),
|
|
14073
|
+
/* @__PURE__ */ jsx17(
|
|
13274
14074
|
MultilineInput,
|
|
13275
14075
|
{
|
|
13276
14076
|
value: input,
|
|
@@ -13288,7 +14088,7 @@ ${listText}`);
|
|
|
13288
14088
|
}
|
|
13289
14089
|
)
|
|
13290
14090
|
] }),
|
|
13291
|
-
/* @__PURE__ */
|
|
14091
|
+
/* @__PURE__ */ jsx17(Box15, { paddingX: 1, marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx17(StatusStrip, { model: modelDisplay, busy: state.busy, aborting: state.aborting, planMode: state.getPlanMode(), autoMode: state.getAutoMode(), sessionShort, cols, customLine: statusLineText }) })
|
|
13292
14092
|
] })
|
|
13293
14093
|
] });
|
|
13294
14094
|
};
|
|
@@ -13302,7 +14102,7 @@ __export(updateCheck_exports, {
|
|
|
13302
14102
|
readLocalVersion: () => readLocalVersion,
|
|
13303
14103
|
refreshUpdateCache: () => refreshUpdateCache
|
|
13304
14104
|
});
|
|
13305
|
-
import { readFileSync as
|
|
14105
|
+
import { readFileSync as readFileSync4, writeFileSync, mkdirSync } from "node:fs";
|
|
13306
14106
|
import path39 from "node:path";
|
|
13307
14107
|
import os4 from "os";
|
|
13308
14108
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
@@ -13318,7 +14118,7 @@ var init_updateCheck = __esm({
|
|
|
13318
14118
|
readLocalVersion = () => {
|
|
13319
14119
|
try {
|
|
13320
14120
|
const here = path39.dirname(fileURLToPath6(import.meta.url));
|
|
13321
|
-
return JSON.parse(
|
|
14121
|
+
return JSON.parse(readFileSync4(path39.join(here, "..", "package.json"), "utf8")).version ?? "0.0.0";
|
|
13322
14122
|
} catch {
|
|
13323
14123
|
return "0.0.0";
|
|
13324
14124
|
}
|
|
@@ -13334,7 +14134,7 @@ var init_updateCheck = __esm({
|
|
|
13334
14134
|
};
|
|
13335
14135
|
readCache = () => {
|
|
13336
14136
|
try {
|
|
13337
|
-
const obj = JSON.parse(
|
|
14137
|
+
const obj = JSON.parse(readFileSync4(cacheFile(), "utf8"));
|
|
13338
14138
|
return typeof obj.latestVersion === "string" && typeof obj.lastCheck === "number" ? obj : null;
|
|
13339
14139
|
} catch {
|
|
13340
14140
|
return null;
|
|
@@ -13422,7 +14222,7 @@ import { render } from "ink";
|
|
|
13422
14222
|
import os5 from "os";
|
|
13423
14223
|
import path40 from "path";
|
|
13424
14224
|
import { emitKeypressEvents } from "node:readline";
|
|
13425
|
-
import { jsx as
|
|
14225
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
13426
14226
|
var parseArgs = (argv) => {
|
|
13427
14227
|
const out = {};
|
|
13428
14228
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -13566,7 +14366,7 @@ var main = async () => {
|
|
|
13566
14366
|
resumeId = await getMostRecentSessionId2() ?? void 0;
|
|
13567
14367
|
}
|
|
13568
14368
|
const { waitUntilExit } = render(
|
|
13569
|
-
/* @__PURE__ */
|
|
14369
|
+
/* @__PURE__ */ jsx18(App2, { resumeSessionId: resumeId, initialPlanMode: args.plan, initialAutoMode: args.auto, initialIncludeProject: includeProject }),
|
|
13570
14370
|
// exitOnCtrlC:false:Ctrl+C 交由 App useInput 处理(统一退出/中止语义);
|
|
13571
14371
|
// patchConsole:false:避免 console 劫持与全屏重绘叠加闪屏。
|
|
13572
14372
|
{ exitOnCtrlC: false, patchConsole: false }
|