zapmyco 0.15.0 → 0.16.0
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/index.mjs +1091 -365
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +155 -1765
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{loader-DEuRwvSw.mjs → loader-CWzMinwj.mjs} +749 -238
- package/dist/loader-CWzMinwj.mjs.map +1 -0
- package/package.json +6 -4
- package/dist/loader-DEuRwvSw.mjs.map +0 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as configureLogger, D as eventBus, G as SESSION_DIR_NAME, K as VERSION, L as WebError, O as DEFAULT_COMPACTION_CONFIG, V as loadSkills, W as APP_NAME, d as SubAgentManager, f as SecurityBlockedError, g as runWithToolGuardContext, h as getToolGuardContext, i as AgentLlmFacade, j as logger, m as createToolInfoResolver, n as loadConfig, p as ToolGuard, q as __require, t as HOME_CONFIG_PATH, v as createLlmBasedAgent, z as ZapmycoErrorCode } from "../loader-
|
|
2
|
+
import { A as configureLogger, D as eventBus, G as SESSION_DIR_NAME, K as VERSION, L as WebError, O as DEFAULT_COMPACTION_CONFIG, V as loadSkills, W as APP_NAME, d as SubAgentManager, f as SecurityBlockedError, g as runWithToolGuardContext, h as getToolGuardContext, i as AgentLlmFacade, j as logger, m as createToolInfoResolver, n as loadConfig, p as ToolGuard, q as __require, t as HOME_CONFIG_PATH, v as createLlmBasedAgent, z as ZapmycoErrorCode } from "../loader-CWzMinwj.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
5
|
+
import { mkdir, readFile, readdir, rename, stat, writeFile } from "node:fs/promises";
|
|
6
6
|
import * as os from "node:os";
|
|
7
7
|
import { homedir, tmpdir } from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import { dirname, isAbsolute, join, normalize, relative, resolve } from "node:path";
|
|
10
10
|
import { EventEmitter } from "node:events";
|
|
11
|
-
import { getModels, getProviders } from "@earendil-works/pi-ai";
|
|
12
11
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
13
12
|
import chalk, { Chalk } from "chalk";
|
|
14
13
|
import { Command } from "commander";
|
|
15
14
|
import { execFile, spawn, spawnSync } from "node:child_process";
|
|
16
|
-
import { CombinedAutocompleteProvider, Container, Editor, Input, Key, ProcessTerminal, SelectList, TUI, getKeybindings, matchesKey, truncateToWidth, wrapTextWithAnsi } from "@
|
|
15
|
+
import { CombinedAutocompleteProvider, Container, Editor, Input, Key, ProcessTerminal, SelectList, TUI, getKeybindings, matchesKey, truncateToWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
17
16
|
import i18next from "i18next";
|
|
18
17
|
import TurndownService from "turndown";
|
|
19
18
|
import { lookup } from "node:dns/promises";
|
|
20
19
|
import { promisify } from "node:util";
|
|
21
20
|
import chokidar from "chokidar";
|
|
22
21
|
import { marked } from "marked";
|
|
22
|
+
import "gray-matter";
|
|
23
23
|
import { Client } from "@modelcontextprotocol/sdk/client";
|
|
24
24
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
25
25
|
|
|
26
26
|
//#region src/cli/repl/command-registry.ts
|
|
27
|
-
const log$
|
|
27
|
+
const log$25 = logger.child("repl:command-registry");
|
|
28
28
|
/**
|
|
29
29
|
* 命令注册表
|
|
30
30
|
*/
|
|
@@ -40,7 +40,7 @@ var CommandRegistry = class {
|
|
|
40
40
|
*/
|
|
41
41
|
register(cmd) {
|
|
42
42
|
const canonicalName = cmd.name.toLowerCase();
|
|
43
|
-
if (this.commands.has(canonicalName)) log$
|
|
43
|
+
if (this.commands.has(canonicalName)) log$25.warn(`命令 "${canonicalName}" 已存在,将被覆盖`);
|
|
44
44
|
this.commands.set(canonicalName, cmd);
|
|
45
45
|
for (const alias of cmd.aliases) {
|
|
46
46
|
const lowerAlias = alias.toLowerCase();
|
|
@@ -91,7 +91,7 @@ var CommandRegistry = class {
|
|
|
91
91
|
*/
|
|
92
92
|
async dispatch(parsed) {
|
|
93
93
|
if (parsed.kind !== "command") {
|
|
94
|
-
log$
|
|
94
|
+
log$25.warn("dispatch 收到了非 command 类型的输入");
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
97
|
const cmd = this.getCommand(parsed.name);
|
|
@@ -103,7 +103,7 @@ var CommandRegistry = class {
|
|
|
103
103
|
await cmd.handler(parsed.args, this.session);
|
|
104
104
|
} catch (error) {
|
|
105
105
|
const message = error instanceof Error ? error.message : String(error);
|
|
106
|
-
log$
|
|
106
|
+
log$25.error(`命令 /${cmd.name} 执行出错`, {}, error);
|
|
107
107
|
console.log(`\n 命令执行出错: ${message}\n`);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
@@ -366,7 +366,7 @@ function formatAgentInstanceTree(instances, options = {}) {
|
|
|
366
366
|
for (const inst of instances) {
|
|
367
367
|
const parentKey = inst.parentInstanceId;
|
|
368
368
|
if (!childrenMap.has(parentKey)) childrenMap.set(parentKey, []);
|
|
369
|
-
childrenMap.get(parentKey)
|
|
369
|
+
childrenMap.get(parentKey)?.push(inst);
|
|
370
370
|
}
|
|
371
371
|
for (let i = 0; i < roots.length; i++) {
|
|
372
372
|
const root = roots[i];
|
|
@@ -470,6 +470,49 @@ const noColor = {
|
|
|
470
470
|
reset: ""
|
|
471
471
|
};
|
|
472
472
|
|
|
473
|
+
//#endregion
|
|
474
|
+
//#region src/core/agent-team/types.ts
|
|
475
|
+
/**
|
|
476
|
+
* Agent 默认安全工具集
|
|
477
|
+
*
|
|
478
|
+
* 只读和搜索工具,不含任何可能产生副作用的操作。
|
|
479
|
+
* 子 Agent 默认使用此集合。
|
|
480
|
+
*/
|
|
481
|
+
const AGENT_SAFE_TOOLS = [
|
|
482
|
+
"ReadFile",
|
|
483
|
+
"Glob",
|
|
484
|
+
"Grep",
|
|
485
|
+
"WebFetch",
|
|
486
|
+
"WebSearch",
|
|
487
|
+
"GetCurrentTime",
|
|
488
|
+
"GetWorkdirInfo"
|
|
489
|
+
];
|
|
490
|
+
/**
|
|
491
|
+
* Agent 标准工具集
|
|
492
|
+
*
|
|
493
|
+
* safe + 文件写入 + Shell 执行。
|
|
494
|
+
*/
|
|
495
|
+
const AGENT_STANDARD_TOOLS = [
|
|
496
|
+
...AGENT_SAFE_TOOLS,
|
|
497
|
+
"WriteFile",
|
|
498
|
+
"EditFile",
|
|
499
|
+
"Exec",
|
|
500
|
+
"Process",
|
|
501
|
+
"TaskManage",
|
|
502
|
+
"Memory"
|
|
503
|
+
];
|
|
504
|
+
/**
|
|
505
|
+
* Coordinator 专用工具集
|
|
506
|
+
*
|
|
507
|
+
* 借鉴 Claude Code:Coordinator 只保留编排相关工具,
|
|
508
|
+
* 强制专注于任务分解和结果整合。
|
|
509
|
+
*/
|
|
510
|
+
const COORDINATOR_TOOLS = [
|
|
511
|
+
"AgentTool",
|
|
512
|
+
"SendMessage",
|
|
513
|
+
"TaskStop"
|
|
514
|
+
];
|
|
515
|
+
|
|
473
516
|
//#endregion
|
|
474
517
|
//#region src/core/agent-team/agent-instance-manager.ts
|
|
475
518
|
/**
|
|
@@ -480,7 +523,7 @@ const noColor = {
|
|
|
480
523
|
*
|
|
481
524
|
* @module core/agent-team
|
|
482
525
|
*/
|
|
483
|
-
const log$
|
|
526
|
+
const log$24 = logger.child("agent-instance-manager");
|
|
484
527
|
/**
|
|
485
528
|
* Agent 实例状态转换表
|
|
486
529
|
*
|
|
@@ -538,14 +581,16 @@ var AgentInstanceManager = class extends EventEmitter {
|
|
|
538
581
|
agent,
|
|
539
582
|
inbox: [],
|
|
540
583
|
task,
|
|
541
|
-
createdAt: Date.now()
|
|
584
|
+
createdAt: Date.now(),
|
|
585
|
+
toolCallHistory: [],
|
|
586
|
+
toolCallGroups: []
|
|
542
587
|
};
|
|
543
588
|
this.instances.set(instance.instanceId, instance);
|
|
544
589
|
if (parentInstanceId) {
|
|
545
590
|
const parent = this.instances.get(parentInstanceId);
|
|
546
591
|
if (parent) parent.childInstanceIds.push(instance.instanceId);
|
|
547
592
|
}
|
|
548
|
-
log$
|
|
593
|
+
log$24.debug("注册 Agent 实例", {
|
|
549
594
|
instanceId: instance.instanceId,
|
|
550
595
|
typeId: definition.typeId,
|
|
551
596
|
depth,
|
|
@@ -568,12 +613,12 @@ var AgentInstanceManager = class extends EventEmitter {
|
|
|
568
613
|
transition(instanceId, newStatus) {
|
|
569
614
|
const instance = this.instances.get(instanceId);
|
|
570
615
|
if (!instance) {
|
|
571
|
-
log$
|
|
616
|
+
log$24.warn("状态转换失败:实例不存在", { instanceId });
|
|
572
617
|
return false;
|
|
573
618
|
}
|
|
574
619
|
const allowed = VALID_TRANSITIONS[instance.status];
|
|
575
620
|
if (!allowed.includes(newStatus)) {
|
|
576
|
-
log$
|
|
621
|
+
log$24.warn("状态转换拒绝", {
|
|
577
622
|
instanceId,
|
|
578
623
|
from: instance.status,
|
|
579
624
|
to: newStatus,
|
|
@@ -583,7 +628,7 @@ var AgentInstanceManager = class extends EventEmitter {
|
|
|
583
628
|
}
|
|
584
629
|
const oldStatus = instance.status;
|
|
585
630
|
instance.status = newStatus;
|
|
586
|
-
log$
|
|
631
|
+
log$24.debug("Agent 实例状态转换", {
|
|
587
632
|
instanceId,
|
|
588
633
|
typeId: instance.typeId,
|
|
589
634
|
from: oldStatus,
|
|
@@ -622,6 +667,34 @@ var AgentInstanceManager = class extends EventEmitter {
|
|
|
622
667
|
return this.instances.get(instanceId)?.currentActivity;
|
|
623
668
|
}
|
|
624
669
|
/**
|
|
670
|
+
* 记录工具调用事件到实例历史
|
|
671
|
+
*
|
|
672
|
+
* 将工具调用记录推入 toolCallHistory(ring-buffer 风格),
|
|
673
|
+
* 同步更新 currentActivity,并发射 instance:toolcall 事件。
|
|
674
|
+
*
|
|
675
|
+
* @param instanceId - 实例 ID
|
|
676
|
+
* @param record - 工具调用记录
|
|
677
|
+
*/
|
|
678
|
+
recordToolCall(instanceId, record) {
|
|
679
|
+
const instance = this.instances.get(instanceId);
|
|
680
|
+
if (!instance) return;
|
|
681
|
+
instance.toolCallHistory.push(record);
|
|
682
|
+
if (instance.toolCallHistory.length > 100) instance.toolCallHistory = instance.toolCallHistory.slice(-100);
|
|
683
|
+
instance.toolCallGroups = [];
|
|
684
|
+
const runningCount = instance.toolCallHistory.filter((t) => t.status === "running" || t.status === "completed").length;
|
|
685
|
+
instance.currentActivity = {
|
|
686
|
+
toolName: record.toolName,
|
|
687
|
+
toolUses: runningCount,
|
|
688
|
+
args: record.argsDisplay,
|
|
689
|
+
startedAt: record.startedAt
|
|
690
|
+
};
|
|
691
|
+
this.emit("instance:toolcall", {
|
|
692
|
+
instanceId,
|
|
693
|
+
typeId: instance.typeId,
|
|
694
|
+
record
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
625
698
|
* 获取指定实例
|
|
626
699
|
*
|
|
627
700
|
* @param instanceId - 实例 ID
|
|
@@ -714,7 +787,7 @@ var AgentInstanceManager = class extends EventEmitter {
|
|
|
714
787
|
instance.agent.removeAllListeners();
|
|
715
788
|
instance.agent.systemPromptOverride = null;
|
|
716
789
|
this.instances.delete(instanceId);
|
|
717
|
-
log$
|
|
790
|
+
log$24.debug("Agent 实例已清理", { instanceId });
|
|
718
791
|
}
|
|
719
792
|
/**
|
|
720
793
|
* 清理所有终态的实例
|
|
@@ -1165,7 +1238,7 @@ const BUILTIN_AGENT_TYPES = [
|
|
|
1165
1238
|
*
|
|
1166
1239
|
* @module core/agent-team
|
|
1167
1240
|
*/
|
|
1168
|
-
const log$
|
|
1241
|
+
const log$23 = logger.child("agent-type-registry");
|
|
1169
1242
|
/**
|
|
1170
1243
|
* Agent 类型注册中心
|
|
1171
1244
|
*
|
|
@@ -1189,14 +1262,14 @@ var AgentTypeRegistry = class {
|
|
|
1189
1262
|
*/
|
|
1190
1263
|
register(definition) {
|
|
1191
1264
|
const existing = this.types.get(definition.typeId);
|
|
1192
|
-
if (existing) log$
|
|
1265
|
+
if (existing) log$23.info("覆盖已注册的 Agent 类型", {
|
|
1193
1266
|
typeId: definition.typeId,
|
|
1194
1267
|
oldSource: existing.source,
|
|
1195
1268
|
newSource: definition.source
|
|
1196
1269
|
});
|
|
1197
1270
|
this.types.set(definition.typeId, definition);
|
|
1198
1271
|
this.refreshCache();
|
|
1199
|
-
log$
|
|
1272
|
+
log$23.debug("注册 Agent 类型", {
|
|
1200
1273
|
typeId: definition.typeId,
|
|
1201
1274
|
source: definition.source
|
|
1202
1275
|
});
|
|
@@ -1209,7 +1282,7 @@ var AgentTypeRegistry = class {
|
|
|
1209
1282
|
registerAll(definitions) {
|
|
1210
1283
|
for (const def of definitions) this.types.set(def.typeId, def);
|
|
1211
1284
|
this.refreshCache();
|
|
1212
|
-
log$
|
|
1285
|
+
log$23.info("批量注册 Agent 类型", { count: definitions.length });
|
|
1213
1286
|
}
|
|
1214
1287
|
/**
|
|
1215
1288
|
* 注销 Agent 类型
|
|
@@ -1221,7 +1294,7 @@ var AgentTypeRegistry = class {
|
|
|
1221
1294
|
const result = this.types.delete(typeId);
|
|
1222
1295
|
if (result) {
|
|
1223
1296
|
this.refreshCache();
|
|
1224
|
-
log$
|
|
1297
|
+
log$23.debug("注销 Agent 类型", { typeId });
|
|
1225
1298
|
}
|
|
1226
1299
|
return result;
|
|
1227
1300
|
}
|
|
@@ -1308,7 +1381,7 @@ var AgentTypeRegistry = class {
|
|
|
1308
1381
|
loadBuiltinTypes() {
|
|
1309
1382
|
for (const def of BUILTIN_AGENT_TYPES) this.types.set(def.typeId, def);
|
|
1310
1383
|
this.refreshCache();
|
|
1311
|
-
log$
|
|
1384
|
+
log$23.info("加载内置 Agent 类型", { count: BUILTIN_AGENT_TYPES.length });
|
|
1312
1385
|
}
|
|
1313
1386
|
/**
|
|
1314
1387
|
* 刷新可见类型缓存
|
|
@@ -1427,7 +1500,7 @@ function createCacheCommand() {
|
|
|
1427
1500
|
const lines = [];
|
|
1428
1501
|
lines.push("");
|
|
1429
1502
|
lines.push(chalk.cyan(" Prompt 缓存状态"));
|
|
1430
|
-
lines.push(
|
|
1503
|
+
lines.push(` ${chalk.gray("─".repeat(40))}`);
|
|
1431
1504
|
lines.push(chalk.gray(" 缓存命中率: ") + formatPercent(stats.hitRate));
|
|
1432
1505
|
lines.push(chalk.gray(" 平均缓存读取比例: ") + formatPercent(stats.averageCacheRatio));
|
|
1433
1506
|
lines.push(chalk.gray(" 总调用次数: ") + chalk.white(String(stats.totalCalls)));
|
|
@@ -1528,7 +1601,7 @@ function updateSettingsFile(path, value) {
|
|
|
1528
1601
|
parsedValue = JSON.parse(value);
|
|
1529
1602
|
} catch {}
|
|
1530
1603
|
setByDotPath(config, path, parsedValue);
|
|
1531
|
-
writeFileSync(HOME_CONFIG_PATH, JSON.stringify(config, null, 2)
|
|
1604
|
+
writeFileSync(HOME_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
1532
1605
|
return {
|
|
1533
1606
|
success: true,
|
|
1534
1607
|
message: ""
|
|
@@ -1745,7 +1818,7 @@ function renderSecurityDefault(report) {
|
|
|
1745
1818
|
/** 简单柱状图 */
|
|
1746
1819
|
function barChart(score) {
|
|
1747
1820
|
const filled = Math.round(score / 10);
|
|
1748
|
-
return "█".repeat(filled) + "░".repeat(10 - filled)
|
|
1821
|
+
return `${"█".repeat(filled) + "░".repeat(10 - filled)} ${score}/100`;
|
|
1749
1822
|
}
|
|
1750
1823
|
/**
|
|
1751
1824
|
* 创建 security 命令定义
|
|
@@ -1936,7 +2009,7 @@ var en_default = {
|
|
|
1936
2009
|
"listProvidersUsage": "/settings list-providers — List all known providers",
|
|
1937
2010
|
"listModelsUsage": "/settings list-models <name> — List available models for a provider",
|
|
1938
2011
|
"knownProviders": "Known providers:",
|
|
1939
|
-
"noModelsAvailable": "No models available
|
|
2012
|
+
"noModelsAvailable": "No models available",
|
|
1940
2013
|
"hintConfigureFirst": "Hint: use /settings to configure this provider first",
|
|
1941
2014
|
"availableModels": "available models:"
|
|
1942
2015
|
},
|
|
@@ -2120,7 +2193,7 @@ var zh_CN_default = {
|
|
|
2120
2193
|
"listProvidersUsage": "/settings list-providers — 列出所有已知提供商",
|
|
2121
2194
|
"listModelsUsage": "/settings list-models <name> — 列出提供商可用的模型",
|
|
2122
2195
|
"knownProviders": "已知提供商:",
|
|
2123
|
-
"noModelsAvailable": "
|
|
2196
|
+
"noModelsAvailable": "没有可用的模型",
|
|
2124
2197
|
"hintConfigureFirst": "提示: 使用 /settings 先配置此提供商",
|
|
2125
2198
|
"availableModels": "可用模型:"
|
|
2126
2199
|
},
|
|
@@ -2459,7 +2532,7 @@ function readSettings() {
|
|
|
2459
2532
|
}
|
|
2460
2533
|
/** Write back to settings.json */
|
|
2461
2534
|
function writeSettings(settings) {
|
|
2462
|
-
writeFileSync(HOME_CONFIG_PATH, JSON.stringify(settings, null, 2)
|
|
2535
|
+
writeFileSync(HOME_CONFIG_PATH, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
|
|
2463
2536
|
}
|
|
2464
2537
|
/** Safely set a nested property (prototype-chain safe) */
|
|
2465
2538
|
function _setByDotPath(obj, path, value) {
|
|
@@ -2489,15 +2562,6 @@ function _getByDotPath(obj, path) {
|
|
|
2489
2562
|
|
|
2490
2563
|
//#endregion
|
|
2491
2564
|
//#region src/cli/repl/commands/settings-cmd.ts
|
|
2492
|
-
/**
|
|
2493
|
-
* /settings command — interactive configuration menu
|
|
2494
|
-
*
|
|
2495
|
-
* TUI overlay-based graphical configuration interface:
|
|
2496
|
-
* - View and change the default model
|
|
2497
|
-
* - Configure API keys, models, and base URLs for existing providers
|
|
2498
|
-
* - Add new providers from a curated list
|
|
2499
|
-
* - All changes sync to ~/.zapmyco/settings.json in real-time
|
|
2500
|
-
*/
|
|
2501
2565
|
/** Curated list of known providers (sorted by popularity) */
|
|
2502
2566
|
const KNOWN_PROVIDERS = [
|
|
2503
2567
|
{
|
|
@@ -2574,6 +2638,18 @@ const KNOWN_PROVIDERS = [
|
|
|
2574
2638
|
label: "OpenCode"
|
|
2575
2639
|
}
|
|
2576
2640
|
];
|
|
2641
|
+
/** Built-in model IDs for known providers (fallback when not in config) */
|
|
2642
|
+
const BUILTIN_MODEL_IDS = {
|
|
2643
|
+
anthropic: [
|
|
2644
|
+
"claude-sonnet-4-20250514",
|
|
2645
|
+
"claude-3-5-haiku-latest",
|
|
2646
|
+
"claude-3-opus-latest"
|
|
2647
|
+
],
|
|
2648
|
+
deepseek: ["deepseek-v4-pro", "deepseek-v4-flash"],
|
|
2649
|
+
glm: ["glm-4", "glm-4v"],
|
|
2650
|
+
kimi: ["moonshot-v1-8k", "moonshot-v1-32k"],
|
|
2651
|
+
minimax: ["minimax-text-01"]
|
|
2652
|
+
};
|
|
2577
2653
|
/** Supported language locales */
|
|
2578
2654
|
const SUPPORTED_LOCALES = [{
|
|
2579
2655
|
value: "zh-CN",
|
|
@@ -2599,14 +2675,12 @@ function hasApiKey(config, providerName) {
|
|
|
2599
2675
|
const keyStr = String(key);
|
|
2600
2676
|
return keyStr.length > 0 && keyStr !== "${}";
|
|
2601
2677
|
}
|
|
2602
|
-
/** Get available model IDs for a provider (from config or
|
|
2678
|
+
/** Get available model IDs for a provider (from config or built-in list) */
|
|
2603
2679
|
function getProviderModels(config, providerName) {
|
|
2604
2680
|
const models = _getByDotPath(config, `llm.providers.${providerName}.models`);
|
|
2605
2681
|
if (models && typeof models === "object" && Object.keys(models).length > 0) return Object.keys(models);
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
if (piModels && piModels.length > 0) return piModels.map((m) => m.id);
|
|
2609
|
-
} catch {}
|
|
2682
|
+
const builtinModels = BUILTIN_MODEL_IDS[providerName];
|
|
2683
|
+
if (builtinModels && builtinModels.length > 0) return builtinModels;
|
|
2610
2684
|
return [];
|
|
2611
2685
|
}
|
|
2612
2686
|
/**
|
|
@@ -2790,7 +2864,7 @@ async function handleInteractiveMode(tui, session, config) {
|
|
|
2790
2864
|
label: id,
|
|
2791
2865
|
description: ""
|
|
2792
2866
|
})), { onExit: exitAll });
|
|
2793
|
-
if (selected
|
|
2867
|
+
if (selected?.value) {
|
|
2794
2868
|
const modelId = selected.value;
|
|
2795
2869
|
const settings = readSettings();
|
|
2796
2870
|
_setByDotPath(settings, `llm.providers.${providerName}.models.${modelId}`, { id: modelId });
|
|
@@ -2833,7 +2907,9 @@ async function handleInteractiveMode(tui, session, config) {
|
|
|
2833
2907
|
*/
|
|
2834
2908
|
const handleModelSlotSelect = async (configKey, successMsgKey) => {
|
|
2835
2909
|
const configuredProviders = _getByDotPath(state.current, "llm.providers");
|
|
2836
|
-
const
|
|
2910
|
+
const configuredProviderNames = configuredProviders ? Object.keys(configuredProviders) : [];
|
|
2911
|
+
const builtinProviderNames = Object.keys(BUILTIN_MODEL_IDS);
|
|
2912
|
+
const allProviders = [...new Set([...configuredProviderNames, ...builtinProviderNames])];
|
|
2837
2913
|
const enabledItems = [];
|
|
2838
2914
|
const disabledItems = [];
|
|
2839
2915
|
for (const providerName of allProviders) {
|
|
@@ -2864,7 +2940,7 @@ async function handleInteractiveMode(tui, session, config) {
|
|
|
2864
2940
|
return;
|
|
2865
2941
|
}
|
|
2866
2942
|
const selected = await showSelectList(tui, modelItems, { onExit: exitAll });
|
|
2867
|
-
if (!selected
|
|
2943
|
+
if (!selected?.value) return;
|
|
2868
2944
|
const selectedKey = selected.value;
|
|
2869
2945
|
const slashIndex = selectedKey.indexOf("/");
|
|
2870
2946
|
const providerName = selectedKey.slice(0, slashIndex);
|
|
@@ -3015,7 +3091,7 @@ async function handleInteractiveMode(tui, session, config) {
|
|
|
3015
3091
|
maxVisible: 12,
|
|
3016
3092
|
onExit: exitAll
|
|
3017
3093
|
});
|
|
3018
|
-
if (!selected
|
|
3094
|
+
if (!selected?.value) continue;
|
|
3019
3095
|
const providerName = selected.value;
|
|
3020
3096
|
const existingProviders = _getByDotPath(state.current, "llm.providers");
|
|
3021
3097
|
if (existingProviders && providerName in existingProviders) {
|
|
@@ -3057,7 +3133,7 @@ async function handleInteractiveMode(tui, session, config) {
|
|
|
3057
3133
|
...loc,
|
|
3058
3134
|
label: loc.value === currentLocale ? `${loc.label} ✓` : loc.label
|
|
3059
3135
|
})), { onExit: exitAll });
|
|
3060
|
-
if (!selected
|
|
3136
|
+
if (!selected?.value) continue;
|
|
3061
3137
|
if (selected.value !== currentLocale) {
|
|
3062
3138
|
setConfigValue(session, "locale", selected.value);
|
|
3063
3139
|
setLocale(selected.value);
|
|
@@ -3091,23 +3167,152 @@ function createStatusCommand() {
|
|
|
3091
3167
|
};
|
|
3092
3168
|
}
|
|
3093
3169
|
|
|
3170
|
+
//#endregion
|
|
3171
|
+
//#region src/core/agent-team/agent-tool-categorizer.ts
|
|
3172
|
+
/** 内置工具分类映射表 */
|
|
3173
|
+
const TOOL_CATEGORIES = {
|
|
3174
|
+
ReadFile: {
|
|
3175
|
+
category: "read",
|
|
3176
|
+
label: "Read"
|
|
3177
|
+
},
|
|
3178
|
+
Glob: {
|
|
3179
|
+
category: "search",
|
|
3180
|
+
label: "Search"
|
|
3181
|
+
},
|
|
3182
|
+
Grep: {
|
|
3183
|
+
category: "search",
|
|
3184
|
+
label: "Search"
|
|
3185
|
+
},
|
|
3186
|
+
WriteFile: {
|
|
3187
|
+
category: "write",
|
|
3188
|
+
label: "Write"
|
|
3189
|
+
},
|
|
3190
|
+
EditFile: {
|
|
3191
|
+
category: "edit",
|
|
3192
|
+
label: "Edit"
|
|
3193
|
+
},
|
|
3194
|
+
Exec: {
|
|
3195
|
+
category: "exec",
|
|
3196
|
+
label: "Exec"
|
|
3197
|
+
},
|
|
3198
|
+
Process: {
|
|
3199
|
+
category: "exec",
|
|
3200
|
+
label: "Process"
|
|
3201
|
+
},
|
|
3202
|
+
WebFetch: {
|
|
3203
|
+
category: "web",
|
|
3204
|
+
label: "WebFetch"
|
|
3205
|
+
},
|
|
3206
|
+
WebSearch: {
|
|
3207
|
+
category: "web",
|
|
3208
|
+
label: "WebSearch"
|
|
3209
|
+
},
|
|
3210
|
+
TaskManage: {
|
|
3211
|
+
category: "task",
|
|
3212
|
+
label: "Task"
|
|
3213
|
+
},
|
|
3214
|
+
Memory: {
|
|
3215
|
+
category: "task",
|
|
3216
|
+
label: "Memory"
|
|
3217
|
+
},
|
|
3218
|
+
AgentTool: {
|
|
3219
|
+
category: "other",
|
|
3220
|
+
label: "Spawn"
|
|
3221
|
+
}
|
|
3222
|
+
};
|
|
3223
|
+
/**
|
|
3224
|
+
* 对工具名称进行分类
|
|
3225
|
+
*
|
|
3226
|
+
* @param toolName - 工具名称
|
|
3227
|
+
* @returns 分类信息和展示标签
|
|
3228
|
+
*/
|
|
3229
|
+
function categorizeTool(toolName) {
|
|
3230
|
+
return TOOL_CATEGORIES[toolName] ?? {
|
|
3231
|
+
category: "other",
|
|
3232
|
+
label: toolName
|
|
3233
|
+
};
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
//#endregion
|
|
3237
|
+
//#region src/core/agent-team/agent-progress-processor.ts
|
|
3238
|
+
/**
|
|
3239
|
+
* Agent 工具调用分组处理器
|
|
3240
|
+
*
|
|
3241
|
+
* 将扁平的工具调用记录按分类合为展示分组,
|
|
3242
|
+
* 类似 claude-code 的 processProgressMessages()。
|
|
3243
|
+
*
|
|
3244
|
+
* @module core/agent-team
|
|
3245
|
+
*/
|
|
3246
|
+
/**
|
|
3247
|
+
* 连续相同分类调用的最小分组阈值。
|
|
3248
|
+
* >= 2 时合并为分组(如 "Read 3 files"),
|
|
3249
|
+
* 否则保持独立条目(如 "ReadFile: src/foo.ts")。
|
|
3250
|
+
*/
|
|
3251
|
+
const GROUP_THRESHOLD = 2;
|
|
3252
|
+
/**
|
|
3253
|
+
* 将扁平的工具调用记录处理为展示分组
|
|
3254
|
+
*
|
|
3255
|
+
* @param records - 工具调用记录列表
|
|
3256
|
+
* @returns 有序的展示分组
|
|
3257
|
+
*/
|
|
3258
|
+
function buildToolCallGroups(records) {
|
|
3259
|
+
const groups = [];
|
|
3260
|
+
let i = 0;
|
|
3261
|
+
while (i < records.length) {
|
|
3262
|
+
const record = records[i];
|
|
3263
|
+
if (!record) {
|
|
3264
|
+
i++;
|
|
3265
|
+
continue;
|
|
3266
|
+
}
|
|
3267
|
+
const { category, label } = categorizeTool(record.toolName);
|
|
3268
|
+
let j = i + 1;
|
|
3269
|
+
while (j < records.length) {
|
|
3270
|
+
const next = records[j];
|
|
3271
|
+
if (!next) break;
|
|
3272
|
+
if (categorizeTool(next.toolName).category !== category) break;
|
|
3273
|
+
j++;
|
|
3274
|
+
}
|
|
3275
|
+
const count = j - i;
|
|
3276
|
+
const batch = records.slice(i, j);
|
|
3277
|
+
if (count >= GROUP_THRESHOLD) groups.push({
|
|
3278
|
+
category,
|
|
3279
|
+
label,
|
|
3280
|
+
calls: batch,
|
|
3281
|
+
count,
|
|
3282
|
+
startTime: batch[0]?.startedAt ?? 0,
|
|
3283
|
+
endTime: batch[batch.length - 1]?.endedAt
|
|
3284
|
+
});
|
|
3285
|
+
else for (const r of batch) groups.push({
|
|
3286
|
+
category,
|
|
3287
|
+
label,
|
|
3288
|
+
calls: [r],
|
|
3289
|
+
count: 1,
|
|
3290
|
+
startTime: r.startedAt,
|
|
3291
|
+
endTime: r.endedAt
|
|
3292
|
+
});
|
|
3293
|
+
i = j;
|
|
3294
|
+
}
|
|
3295
|
+
return groups;
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3094
3298
|
//#endregion
|
|
3095
3299
|
//#region src/cli/repl/components/agent-status-bar.ts
|
|
3096
3300
|
/**
|
|
3097
3301
|
* Agent 状态栏组件
|
|
3098
3302
|
*
|
|
3099
3303
|
* 实时显示正在运行的子 Agent 状态,类似 Claude Code 的 "Running N agents…" 效果。
|
|
3304
|
+
* 支持每个 Agent 的工具调用历史展示、分组摘要、"+N more" 折叠展开。
|
|
3100
3305
|
* 固定在 OutputArea 和 Editor 之间,无活跃 Agent 时自动隐藏。
|
|
3101
3306
|
*
|
|
3102
3307
|
* @module cli/repl/components
|
|
3103
3308
|
*/
|
|
3104
|
-
/** 状态图标映射 */
|
|
3309
|
+
/** 状态图标映射 — 纯文字符号,无 emoji */
|
|
3105
3310
|
const STATUS_ICONS$1 = {
|
|
3106
3311
|
idle: "○",
|
|
3107
|
-
running: "
|
|
3312
|
+
running: "●",
|
|
3108
3313
|
paused: "◐",
|
|
3109
|
-
completed: "
|
|
3110
|
-
failed: "
|
|
3314
|
+
completed: "✔",
|
|
3315
|
+
failed: "✘",
|
|
3111
3316
|
cancelled: "◌"
|
|
3112
3317
|
};
|
|
3113
3318
|
/** Loading 动画帧 */
|
|
@@ -3129,21 +3334,48 @@ const TREE_BRANCH = "├── ";
|
|
|
3129
3334
|
const TREE_LAST = "└── ";
|
|
3130
3335
|
const TREE_PIPE = "│ ";
|
|
3131
3336
|
const TREE_SPACE = " ";
|
|
3132
|
-
|
|
3337
|
+
/** 工具名 → 可读动词描述 */
|
|
3338
|
+
const TOOL_DESCRIPTIONS = {
|
|
3339
|
+
AgentTool: "派发子任务",
|
|
3340
|
+
Grep: "搜索代码",
|
|
3341
|
+
ReadFile: "读取文件",
|
|
3342
|
+
Glob: "查找文件",
|
|
3343
|
+
Exec: "执行命令",
|
|
3344
|
+
WriteFile: "写入文件",
|
|
3345
|
+
EditFile: "编辑文件",
|
|
3346
|
+
WebFetch: "抓取网页",
|
|
3347
|
+
WebSearch: "搜索网络",
|
|
3348
|
+
SendMessage: "发送消息",
|
|
3349
|
+
TaskStop: "停止任务"
|
|
3350
|
+
};
|
|
3351
|
+
/** 获取工具的可读描述 */
|
|
3352
|
+
function getToolDescription(toolName) {
|
|
3353
|
+
return TOOL_DESCRIPTIONS[toolName] ?? toolName;
|
|
3354
|
+
}
|
|
3133
3355
|
/**
|
|
3134
3356
|
* Agent 状态栏组件
|
|
3135
3357
|
*
|
|
3136
3358
|
* 从 AgentInstanceManager 读取活跃实例状态,渲染为紧凑状态栏。
|
|
3137
3359
|
*/
|
|
3138
3360
|
var AgentStatusBar = class extends Container {
|
|
3139
|
-
/**
|
|
3140
|
-
#
|
|
3361
|
+
/** AnimationManager 实例(用于渲染周期驱动的动画) */
|
|
3362
|
+
#animationManager;
|
|
3363
|
+
/** 是否展开显示详情(默认展开) */
|
|
3364
|
+
#expanded = true;
|
|
3365
|
+
/** 按实例的工具调用列表展开状态 */
|
|
3366
|
+
#agentExpanded = /* @__PURE__ */ new Map();
|
|
3141
3367
|
/** loading 动画帧索引 */
|
|
3142
3368
|
#loadingFrame = 0;
|
|
3143
|
-
/**
|
|
3144
|
-
#
|
|
3369
|
+
/** AnimationManager 回调注销函数 */
|
|
3370
|
+
#unregLoading = null;
|
|
3371
|
+
/** 上次帧推进时间戳 */
|
|
3372
|
+
#lastLoadingTick = 0;
|
|
3145
3373
|
/** 上次活跃实例快照(用于检测变化) */
|
|
3146
3374
|
#lastActiveCount = 0;
|
|
3375
|
+
constructor(animationManager) {
|
|
3376
|
+
super();
|
|
3377
|
+
this.#animationManager = animationManager;
|
|
3378
|
+
}
|
|
3147
3379
|
/** 当前模型名称 */
|
|
3148
3380
|
#modelName = null;
|
|
3149
3381
|
/** 累积非缓存 input tokens(usage.input = totalInput - cacheRead) */
|
|
@@ -3156,11 +3388,26 @@ var AgentStatusBar = class extends Container {
|
|
|
3156
3388
|
#durationMs = 0;
|
|
3157
3389
|
/**
|
|
3158
3390
|
* 切换展开/折叠状态
|
|
3391
|
+
*
|
|
3392
|
+
* @param instanceId - 指定实例 ID 时,切换该实例的工具调用列表展开/折叠;不传时切换整体状态栏
|
|
3159
3393
|
*/
|
|
3160
|
-
toggle() {
|
|
3161
|
-
|
|
3394
|
+
toggle(instanceId) {
|
|
3395
|
+
if (instanceId) {
|
|
3396
|
+
const current = this.#agentExpanded.get(instanceId) ?? false;
|
|
3397
|
+
this.#agentExpanded.set(instanceId, !current);
|
|
3398
|
+
} else {
|
|
3399
|
+
this.#expanded = !this.#expanded;
|
|
3400
|
+
if (!this.#expanded) this.#agentExpanded.clear();
|
|
3401
|
+
}
|
|
3162
3402
|
this.invalidate();
|
|
3163
3403
|
}
|
|
3404
|
+
/** 展开/折叠当前活跃 Agent 的工具调用详情 */
|
|
3405
|
+
toggleActiveAgentDetails() {
|
|
3406
|
+
const active = getAgentInstanceManager().listActive();
|
|
3407
|
+
if (active.length === 0) return;
|
|
3408
|
+
const first = active[0];
|
|
3409
|
+
if (first) this.toggle(first.instanceId);
|
|
3410
|
+
}
|
|
3164
3411
|
/** 获取当前展开状态 */
|
|
3165
3412
|
get isExpanded() {
|
|
3166
3413
|
return this.#expanded;
|
|
@@ -3230,52 +3477,74 @@ var AgentStatusBar = class extends Container {
|
|
|
3230
3477
|
return lines;
|
|
3231
3478
|
}
|
|
3232
3479
|
/** 渲染折叠模式单行 */
|
|
3233
|
-
#renderCollapsed(frame, count,
|
|
3234
|
-
return ` ${chalk.cyan(`${frame}
|
|
3480
|
+
#renderCollapsed(frame, count, _toolUses, duration) {
|
|
3481
|
+
return ` ${chalk.cyan(`${frame} ${count} agent${count > 1 ? "s" : ""}`)} ${chalk.gray(`· ${duration}`)}`;
|
|
3235
3482
|
}
|
|
3236
3483
|
/** 渲染展开模式多行 */
|
|
3237
3484
|
#renderExpanded(frame, instances, width) {
|
|
3238
3485
|
const lines = [];
|
|
3239
3486
|
const count = instances.length;
|
|
3240
|
-
const
|
|
3241
|
-
const
|
|
3242
|
-
lines.push(truncateToWidth(
|
|
3487
|
+
const totalDuration = this.#formatDuration(Math.max(...instances.map((i) => Date.now() - i.createdAt)));
|
|
3488
|
+
const header = chalk.cyan(` ${frame} ${count} agent${count > 1 ? "s" : ""} · ${totalDuration}`);
|
|
3489
|
+
lines.push(truncateToWidth(header, width));
|
|
3243
3490
|
for (let i = 0; i < instances.length; i++) {
|
|
3244
3491
|
const inst = instances[i];
|
|
3245
3492
|
const isLast = i === instances.length - 1;
|
|
3246
3493
|
const connector = isLast ? TREE_LAST : TREE_BRANCH;
|
|
3247
3494
|
const childPrefix = isLast ? TREE_SPACE : TREE_PIPE;
|
|
3248
|
-
const
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3495
|
+
const agentLines = this.#renderAgentDetail(inst, connector, childPrefix, width);
|
|
3496
|
+
lines.push(...agentLines);
|
|
3497
|
+
}
|
|
3498
|
+
return lines;
|
|
3499
|
+
}
|
|
3500
|
+
/**
|
|
3501
|
+
* 渲染单个 Agent 的详情(header + 工具调用列表)
|
|
3502
|
+
*/
|
|
3503
|
+
#renderAgentDetail(inst, connector, childPrefix, width) {
|
|
3504
|
+
const lines = [];
|
|
3505
|
+
const icon = STATUS_ICONS$1[inst.status] ?? "?";
|
|
3506
|
+
const statusColor = inst.status === "running" ? chalk.yellow : chalk.gray;
|
|
3507
|
+
const typeLabel = chalk.bold(inst.typeId);
|
|
3508
|
+
const act = inst.currentActivity;
|
|
3509
|
+
let activityDesc = "";
|
|
3510
|
+
if (act) activityDesc = `正在${getToolDescription(act.toolName)}`;
|
|
3511
|
+
else {
|
|
3512
|
+
const totalCalls = inst.toolCallHistory.length;
|
|
3513
|
+
if (totalCalls > 0) {
|
|
3514
|
+
if (inst.toolCallHistory.every((t) => t.status === "completed" || t.status === "failed")) activityDesc = `已完成 ${totalCalls} 次调用`;
|
|
3261
3515
|
}
|
|
3262
3516
|
}
|
|
3517
|
+
const duration = chalk.gray(`· ${this.#formatDuration(Date.now() - inst.createdAt)}`);
|
|
3518
|
+
let line1;
|
|
3519
|
+
if (activityDesc) line1 = ` ${chalk.dim(connector)}${statusColor(icon)} ${typeLabel} ${chalk.dim(`· ${activityDesc}`)} ${duration}`;
|
|
3520
|
+
else line1 = ` ${chalk.dim(connector)}${statusColor(icon)} ${typeLabel} ${duration}`;
|
|
3521
|
+
lines.push(truncateToWidth(line1, width));
|
|
3522
|
+
const totalCalls = inst.toolCallHistory.length;
|
|
3523
|
+
if (totalCalls > 0 && !act) {
|
|
3524
|
+
const summaries = buildToolCallGroups(inst.toolCallHistory).map((g) => `${getToolDescription(g.label)} ${g.count}次`);
|
|
3525
|
+
if (summaries.length > 0) lines.push(truncateToWidth(` ${chalk.dim(childPrefix + TREE_SPACE)}${chalk.dim(summaries.join(" "))}`, width));
|
|
3526
|
+
} else if (totalCalls > 0 && act) lines.push(truncateToWidth(` ${chalk.dim(childPrefix + TREE_SPACE)}${chalk.dim(`已完成 ${totalCalls} 次工具调用`)}`, width));
|
|
3527
|
+
if (inst.status === "running" && !act) lines.push(truncateToWidth(` ${chalk.dim(childPrefix + TREE_SPACE)}${chalk.dim("(ctrl+b to run in background)")}`, width));
|
|
3263
3528
|
return lines;
|
|
3264
3529
|
}
|
|
3265
|
-
/** 启动 loading
|
|
3530
|
+
/** 启动 loading 动画(由 animationManager 在渲染周期中驱动) */
|
|
3266
3531
|
#startLoading() {
|
|
3267
|
-
if (this.#
|
|
3268
|
-
this.#
|
|
3532
|
+
if (this.#unregLoading) return;
|
|
3533
|
+
this.#lastLoadingTick = 0;
|
|
3534
|
+
this.#unregLoading = this.#animationManager.register((now) => {
|
|
3535
|
+
if (now - this.#lastLoadingTick < LOADING_INTERVAL_MS$1) return;
|
|
3536
|
+
this.#lastLoadingTick = now;
|
|
3269
3537
|
this.#loadingFrame = (this.#loadingFrame + 1) % LOADING_FRAMES$2.length;
|
|
3270
3538
|
this.invalidate();
|
|
3271
|
-
}
|
|
3539
|
+
});
|
|
3272
3540
|
}
|
|
3273
3541
|
/** 停止 loading 动画 */
|
|
3274
3542
|
#stopLoading() {
|
|
3275
|
-
if (this.#
|
|
3276
|
-
|
|
3277
|
-
this.#
|
|
3543
|
+
if (this.#unregLoading) {
|
|
3544
|
+
this.#unregLoading();
|
|
3545
|
+
this.#unregLoading = null;
|
|
3278
3546
|
}
|
|
3547
|
+
this.#loadingFrame = 0;
|
|
3279
3548
|
}
|
|
3280
3549
|
/** 格式化持续时间为可读字符串 */
|
|
3281
3550
|
#formatDuration(ms) {
|
|
@@ -3314,20 +3583,6 @@ var AgentStatusBar = class extends Container {
|
|
|
3314
3583
|
|
|
3315
3584
|
//#endregion
|
|
3316
3585
|
//#region src/cli/repl/components/custom-editor.ts
|
|
3317
|
-
/**
|
|
3318
|
-
* 自定义编辑器组件
|
|
3319
|
-
*
|
|
3320
|
-
* 继承自 pi-tui 的 Editor,添加 zapmyco 特有的快捷键处理:
|
|
3321
|
-
* - Ctrl+C: 取消任务 / 二次退出
|
|
3322
|
-
* - Ctrl+D: 退出
|
|
3323
|
-
* - Ctrl+O: 打开外部编辑器编辑输入
|
|
3324
|
-
* - Escape: 取消当前输入
|
|
3325
|
-
*
|
|
3326
|
-
* 同时 override render() 以:
|
|
3327
|
-
* - 去掉 Editor 默认的上下边框(───)
|
|
3328
|
-
* - 添加简洁的输入提示符(❯ )
|
|
3329
|
-
* - 执行中时显示 loading spinner
|
|
3330
|
-
*/
|
|
3331
3586
|
/** 输入提示符 */
|
|
3332
3587
|
const PROMPT_PREFIX = "❯ ";
|
|
3333
3588
|
/** loading 动画帧 */
|
|
@@ -3351,36 +3606,42 @@ function isBorderLine(line) {
|
|
|
3351
3606
|
return /^[\s─┌┐├┤└┘↑↓\-0-9a-zA-Z]+$/.test(stripped);
|
|
3352
3607
|
}
|
|
3353
3608
|
var ZapmycoEditor = class extends Editor {
|
|
3609
|
+
/** AnimationManager 实例(用于渲染周期驱动的动画) */
|
|
3610
|
+
#animationManager;
|
|
3611
|
+
/** AnimationManager 回调注销函数 */
|
|
3612
|
+
#unregLoading = null;
|
|
3613
|
+
/** 上次帧推进时间戳 */
|
|
3614
|
+
#lastLoadingTick = 0;
|
|
3615
|
+
constructor(tui, theme, animationManager, options) {
|
|
3616
|
+
super(tui, theme, options);
|
|
3617
|
+
this.#animationManager = animationManager;
|
|
3618
|
+
}
|
|
3354
3619
|
/** Escape 键回调 */
|
|
3355
3620
|
onEscape;
|
|
3356
3621
|
/** Ctrl+C 回调 */
|
|
3357
3622
|
onCtrlC;
|
|
3358
3623
|
/** Ctrl+D 回调 */
|
|
3359
3624
|
onCtrlD;
|
|
3360
|
-
/** Ctrl+
|
|
3625
|
+
/** Ctrl+G 回调(打开外部编辑器) */
|
|
3361
3626
|
onOpenEditor;
|
|
3362
|
-
/** Ctrl+
|
|
3363
|
-
|
|
3364
|
-
/** Ctrl+
|
|
3627
|
+
/** Ctrl+B 回调(后台运行当前任务) */
|
|
3628
|
+
onRunInBackground;
|
|
3629
|
+
/** Ctrl+T 回调(展开/折叠 thinking 内容) */
|
|
3365
3630
|
onToggleThinking;
|
|
3366
|
-
/** Ctrl+Shift+O 回调(展开/折叠 Agent 状态栏) */
|
|
3367
|
-
onToggleAgentBar;
|
|
3368
3631
|
/** 是否正在执行(用于显示 loading) */
|
|
3369
3632
|
#executing = false;
|
|
3370
3633
|
/** 是否显示 spinner(执行期间禁用输入但不一定显示 spinner) */
|
|
3371
3634
|
#showSpinner = true;
|
|
3372
3635
|
/** loading 动画帧索引 */
|
|
3373
3636
|
#loadingFrame = 0;
|
|
3374
|
-
/** loading 动画定时器 */
|
|
3375
|
-
#loadingTimer;
|
|
3376
3637
|
/** 审批模式状态 */
|
|
3377
3638
|
#approvalState = null;
|
|
3378
3639
|
/** 进入审批模式,在编辑器区域显示审批选项 */
|
|
3379
3640
|
enterApprovalMode(title, options) {
|
|
3380
3641
|
this.#executing = false;
|
|
3381
|
-
if (this.#
|
|
3382
|
-
|
|
3383
|
-
this.#
|
|
3642
|
+
if (this.#unregLoading) {
|
|
3643
|
+
this.#unregLoading();
|
|
3644
|
+
this.#unregLoading = null;
|
|
3384
3645
|
}
|
|
3385
3646
|
this.#approvalState = {
|
|
3386
3647
|
title,
|
|
@@ -3415,23 +3676,17 @@ var ZapmycoEditor = class extends Editor {
|
|
|
3415
3676
|
if (this.getText().length === 0 && this.onCtrlD) this.onCtrlD();
|
|
3416
3677
|
return;
|
|
3417
3678
|
}
|
|
3418
|
-
if (matchesKey(data, Key.ctrl("
|
|
3679
|
+
if (matchesKey(data, Key.ctrl("g"))) {
|
|
3419
3680
|
if (this.onOpenEditor) this.onOpenEditor();
|
|
3420
3681
|
return;
|
|
3421
3682
|
}
|
|
3422
|
-
if (matchesKey(data, Key.
|
|
3423
|
-
this.
|
|
3683
|
+
if (matchesKey(data, Key.ctrl("b")) && this.onRunInBackground) {
|
|
3684
|
+
this.onRunInBackground();
|
|
3424
3685
|
return;
|
|
3425
3686
|
}
|
|
3426
|
-
if (matchesKey(data, Key.ctrl("t"))) {
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
return;
|
|
3430
|
-
}
|
|
3431
|
-
if (this.onToggleThinking) {
|
|
3432
|
-
this.onToggleThinking();
|
|
3433
|
-
return;
|
|
3434
|
-
}
|
|
3687
|
+
if (matchesKey(data, Key.ctrl("t")) && this.onToggleThinking) {
|
|
3688
|
+
this.onToggleThinking();
|
|
3689
|
+
return;
|
|
3435
3690
|
}
|
|
3436
3691
|
if (matchesKey(data, Key.ctrl("y")) && this.onToggleThinking) {
|
|
3437
3692
|
this.onToggleThinking();
|
|
@@ -3482,13 +3737,19 @@ var ZapmycoEditor = class extends Editor {
|
|
|
3482
3737
|
this.#showSpinner = showSpinner;
|
|
3483
3738
|
if (executing && showSpinner) {
|
|
3484
3739
|
this.#loadingFrame = 0;
|
|
3485
|
-
this.#
|
|
3740
|
+
this.#lastLoadingTick = 0;
|
|
3741
|
+
this.#unregLoading = this.#animationManager.register((now) => {
|
|
3742
|
+
if (now - this.#lastLoadingTick < 100) return;
|
|
3743
|
+
this.#lastLoadingTick = now;
|
|
3486
3744
|
this.#loadingFrame = (this.#loadingFrame + 1) % LOADING_FRAMES$1.length;
|
|
3487
3745
|
this.tui?.requestRender();
|
|
3488
|
-
}
|
|
3489
|
-
} else
|
|
3490
|
-
|
|
3491
|
-
|
|
3746
|
+
});
|
|
3747
|
+
} else {
|
|
3748
|
+
if (this.#unregLoading) {
|
|
3749
|
+
this.#unregLoading();
|
|
3750
|
+
this.#unregLoading = null;
|
|
3751
|
+
}
|
|
3752
|
+
this.#loadingFrame = 0;
|
|
3492
3753
|
}
|
|
3493
3754
|
this.tui?.requestRender();
|
|
3494
3755
|
}
|
|
@@ -3558,17 +3819,13 @@ var ZapmycoEditor = class extends Editor {
|
|
|
3558
3819
|
* 显示 TaskManage 创建的任务列表,支持折叠/展开两种模式。
|
|
3559
3820
|
* 类似 Claude Code 的 TaskListV2,使用 pi-tui Container 实现。
|
|
3560
3821
|
*
|
|
3561
|
-
*
|
|
3562
|
-
* 📋 3 tasks · ◼ 1 in_progress · ◻ 1 pending · ✔ 1 completed Ctrl+T expand
|
|
3563
|
-
*
|
|
3564
|
-
* 展开模式(Ctrl+T 切换):
|
|
3822
|
+
* 有任务时始终展开(多行):
|
|
3565
3823
|
* ⠋ #1 Search files
|
|
3566
3824
|
* ◻ #2 Core logic
|
|
3567
3825
|
* ◻ #3 Tests ▸ blocked by #1
|
|
3568
3826
|
* ✔ #4 Analysis
|
|
3569
3827
|
*
|
|
3570
3828
|
* 1 in_progress · 1 pending · 1 completed
|
|
3571
|
-
* (Ctrl+T collapse)
|
|
3572
3829
|
*
|
|
3573
3830
|
* 自动隐藏:无任务时 render() 返回 []。
|
|
3574
3831
|
* 进行中任务:显示 loading spinner 动画 + cyan 高亮。
|
|
@@ -3604,34 +3861,22 @@ const STATUS_ICONS = {
|
|
|
3604
3861
|
* 固定在 OutputArea 和 AgentStatusBar 之间。
|
|
3605
3862
|
*/
|
|
3606
3863
|
var TaskStatusBar = class extends Container {
|
|
3607
|
-
/**
|
|
3608
|
-
|
|
3609
|
-
*
|
|
3610
|
-
* - false(默认):根据任务状态自动决定展开/折叠
|
|
3611
|
-
* - true:用户通过 Ctrl+T 手动折叠,覆盖自动行为
|
|
3612
|
-
*/
|
|
3613
|
-
#forceCollapsed = false;
|
|
3864
|
+
/** AnimationManager 实例(用于渲染周期驱动的动画) */
|
|
3865
|
+
#animationManager;
|
|
3614
3866
|
/** TaskStore 引用(只读,不写) */
|
|
3615
3867
|
#store;
|
|
3616
3868
|
/** loading 动画帧索引 */
|
|
3617
3869
|
#loadingFrame = 0;
|
|
3618
|
-
/**
|
|
3619
|
-
#
|
|
3870
|
+
/** AnimationManager 回调注销函数 */
|
|
3871
|
+
#unregLoading = null;
|
|
3872
|
+
/** 上次帧推进时间戳 */
|
|
3873
|
+
#lastLoadingTick = 0;
|
|
3620
3874
|
/** 上次是否有 in_progress 任务(用于启停动画) */
|
|
3621
3875
|
#hadInProgress = false;
|
|
3622
|
-
constructor(store) {
|
|
3876
|
+
constructor(store, animationManager) {
|
|
3623
3877
|
super();
|
|
3624
3878
|
this.#store = store;
|
|
3625
|
-
|
|
3626
|
-
/**
|
|
3627
|
-
* 切换展开/折叠状态
|
|
3628
|
-
*
|
|
3629
|
-
* 用户手动 Ctrl+T 时调用,设置 forceCollapsed 标记。
|
|
3630
|
-
* 当新任务出现时,forceCollapsed 会被自动重置。
|
|
3631
|
-
*/
|
|
3632
|
-
toggle() {
|
|
3633
|
-
this.#forceCollapsed = !this.#forceCollapsed;
|
|
3634
|
-
this.invalidate();
|
|
3879
|
+
this.#animationManager = animationManager;
|
|
3635
3880
|
}
|
|
3636
3881
|
/** 当前是否处于展开状态 */
|
|
3637
3882
|
get isExpanded() {
|
|
@@ -3640,22 +3885,16 @@ var TaskStatusBar = class extends Container {
|
|
|
3640
3885
|
}
|
|
3641
3886
|
/**
|
|
3642
3887
|
* 当 TaskStore 变化时由外部调用
|
|
3643
|
-
*
|
|
3644
|
-
* 如果出现新的活跃任务,自动展开;全部完成后自动折叠。
|
|
3645
3888
|
*/
|
|
3646
3889
|
onTasksChanged() {
|
|
3647
|
-
const summary = this.#store.summary();
|
|
3648
|
-
if (summary.pending > 0 || summary.in_progress > 0) this.#forceCollapsed = false;
|
|
3649
3890
|
this.invalidate();
|
|
3650
3891
|
}
|
|
3651
3892
|
invalidate() {
|
|
3652
3893
|
super.invalidate();
|
|
3653
3894
|
}
|
|
3654
|
-
/**
|
|
3895
|
+
/** 判断当前是否应展开(有任务时始终展开) */
|
|
3655
3896
|
#shouldExpand(summary) {
|
|
3656
|
-
|
|
3657
|
-
if (this.#forceCollapsed) return false;
|
|
3658
|
-
return summary.pending > 0 || summary.in_progress > 0;
|
|
3897
|
+
return summary.total > 0;
|
|
3659
3898
|
}
|
|
3660
3899
|
/**
|
|
3661
3900
|
* 管理 loading 动画的启停
|
|
@@ -3667,21 +3906,24 @@ var TaskStatusBar = class extends Container {
|
|
|
3667
3906
|
if (hasInProgress && !this.#hadInProgress) this.#startLoading();
|
|
3668
3907
|
else if (!hasInProgress && this.#hadInProgress) this.#stopLoading();
|
|
3669
3908
|
}
|
|
3670
|
-
/** 启动 loading
|
|
3909
|
+
/** 启动 loading 动画(由 animationManager 在渲染周期中驱动) */
|
|
3671
3910
|
#startLoading() {
|
|
3672
|
-
if (this.#
|
|
3673
|
-
this.#
|
|
3911
|
+
if (this.#unregLoading) return;
|
|
3912
|
+
this.#lastLoadingTick = 0;
|
|
3913
|
+
this.#unregLoading = this.#animationManager.register((now) => {
|
|
3914
|
+
if (now - this.#lastLoadingTick < LOADING_INTERVAL_MS) return;
|
|
3915
|
+
this.#lastLoadingTick = now;
|
|
3674
3916
|
this.#loadingFrame = (this.#loadingFrame + 1) % LOADING_FRAMES.length;
|
|
3675
3917
|
this.invalidate();
|
|
3676
|
-
}
|
|
3918
|
+
});
|
|
3677
3919
|
}
|
|
3678
3920
|
/** 停止 loading 动画 */
|
|
3679
3921
|
#stopLoading() {
|
|
3680
|
-
if (this.#
|
|
3681
|
-
|
|
3682
|
-
this.#
|
|
3683
|
-
this.#loadingFrame = 0;
|
|
3922
|
+
if (this.#unregLoading) {
|
|
3923
|
+
this.#unregLoading();
|
|
3924
|
+
this.#unregLoading = null;
|
|
3684
3925
|
}
|
|
3926
|
+
this.#loadingFrame = 0;
|
|
3685
3927
|
}
|
|
3686
3928
|
/** 获取当前 in_progress 的图标(可能为动画帧) */
|
|
3687
3929
|
#getInProgressIcon() {
|
|
@@ -3711,8 +3953,7 @@ var TaskStatusBar = class extends Container {
|
|
|
3711
3953
|
if (summary.pending > 0) parts.push(`${STATUS_ICONS.pending} ${summary.pending} pending`);
|
|
3712
3954
|
if (summary.completed > 0) parts.push(chalk.green(`${STATUS_ICONS.completed} ${summary.completed} completed`));
|
|
3713
3955
|
if (summary.cancelled > 0) parts.push(chalk.red.dim(`${STATUS_ICONS.cancelled} ${summary.cancelled} cancelled`));
|
|
3714
|
-
|
|
3715
|
-
return ` ${parts.join(" · ")} ${hint}`;
|
|
3956
|
+
return ` ${parts.join(" · ")}`;
|
|
3716
3957
|
}
|
|
3717
3958
|
/** 渲染展开模式多行 */
|
|
3718
3959
|
#renderExpanded(tasks, summary, width) {
|
|
@@ -3726,7 +3967,6 @@ var TaskStatusBar = class extends Container {
|
|
|
3726
3967
|
if (summary.completed > 0) summaryParts.push(chalk.green(`${summary.completed} completed`));
|
|
3727
3968
|
if (summary.cancelled > 0) summaryParts.push(chalk.red.dim(`${summary.cancelled} cancelled`));
|
|
3728
3969
|
lines.push(` ${summaryParts.join(" · ")}`);
|
|
3729
|
-
lines.push(chalk.dim(" (Ctrl+T collapse)"));
|
|
3730
3970
|
return lines;
|
|
3731
3971
|
}
|
|
3732
3972
|
/** 渲染单个任务行 */
|
|
@@ -3876,7 +4116,7 @@ function parseField(field, spec) {
|
|
|
3876
4116
|
function parseFieldPart(part, spec) {
|
|
3877
4117
|
if (part.startsWith("*/")) {
|
|
3878
4118
|
const step = parseInt(part.slice(2), 10);
|
|
3879
|
-
if (isNaN(step) || step < 1) return null;
|
|
4119
|
+
if (Number.isNaN(step) || step < 1) return null;
|
|
3880
4120
|
return new StepMatcher(step, spec.min);
|
|
3881
4121
|
}
|
|
3882
4122
|
if (part === "*") return new WildcardMatcher();
|
|
@@ -3884,12 +4124,12 @@ function parseFieldPart(part, spec) {
|
|
|
3884
4124
|
if (rangeMatch) {
|
|
3885
4125
|
const start = parseInt(rangeMatch[1] ?? "", 10);
|
|
3886
4126
|
const end = parseInt(rangeMatch[2] ?? "", 10);
|
|
3887
|
-
if (isNaN(start) || isNaN(end)) return null;
|
|
4127
|
+
if (Number.isNaN(start) || Number.isNaN(end)) return null;
|
|
3888
4128
|
if (start < spec.min || end > spec.max || start > end) return null;
|
|
3889
4129
|
return new RangeMatcher(start, end);
|
|
3890
4130
|
}
|
|
3891
4131
|
const value = parseInt(part, 10);
|
|
3892
|
-
if (!isNaN(value) && value >= spec.min && value <= spec.max) return new ValueMatcher([value]);
|
|
4132
|
+
if (!Number.isNaN(value) && value >= spec.min && value <= spec.max) return new ValueMatcher([value]);
|
|
3893
4133
|
return null;
|
|
3894
4134
|
}
|
|
3895
4135
|
/**
|
|
@@ -4080,7 +4320,7 @@ const CRON_CONSTANTS = {
|
|
|
4080
4320
|
*
|
|
4081
4321
|
* @module cli/repl/cron/cron-scheduler
|
|
4082
4322
|
*/
|
|
4083
|
-
const log$
|
|
4323
|
+
const log$22 = logger.child("cron:scheduler");
|
|
4084
4324
|
var CronScheduler = class extends EventEmitter {
|
|
4085
4325
|
store;
|
|
4086
4326
|
jobs = [];
|
|
@@ -4101,7 +4341,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
4101
4341
|
if (this.running) return;
|
|
4102
4342
|
const loadedJobs = await this.store.load();
|
|
4103
4343
|
this.jobs = loadedJobs;
|
|
4104
|
-
log$
|
|
4344
|
+
log$22.info(`调度器启动,加载 ${loadedJobs.length} 个 durable 任务`);
|
|
4105
4345
|
await this.handleMissedJobs();
|
|
4106
4346
|
this.checkAutoExpiry();
|
|
4107
4347
|
this.running = true;
|
|
@@ -4117,7 +4357,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
4117
4357
|
clearInterval(this.timer);
|
|
4118
4358
|
this.timer = null;
|
|
4119
4359
|
}
|
|
4120
|
-
log$
|
|
4360
|
+
log$22.info("调度器已停止");
|
|
4121
4361
|
}
|
|
4122
4362
|
/** 添加任务 */
|
|
4123
4363
|
async addJob(job) {
|
|
@@ -4129,7 +4369,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
4129
4369
|
this.jobs.push(job);
|
|
4130
4370
|
await this.store.persist(this.jobs);
|
|
4131
4371
|
} else this.sessionJobs.push(job);
|
|
4132
|
-
log$
|
|
4372
|
+
log$22.info("任务已添加", {
|
|
4133
4373
|
id: job.id,
|
|
4134
4374
|
cron: job.cron,
|
|
4135
4375
|
durable: job.durable
|
|
@@ -4259,7 +4499,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
4259
4499
|
}, delay);
|
|
4260
4500
|
}
|
|
4261
4501
|
if (toDelete.length > 0) {
|
|
4262
|
-
log$
|
|
4502
|
+
log$22.info(`跳过 ${toDelete.length} 个错过的一次性任务(超出补发上限)`);
|
|
4263
4503
|
this.emit("missed-overflow", {
|
|
4264
4504
|
count: toDelete.length,
|
|
4265
4505
|
jobIds: toDelete.map((m) => m.id)
|
|
@@ -4277,7 +4517,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
4277
4517
|
job.lastFiredAt = now;
|
|
4278
4518
|
job.fireCount++;
|
|
4279
4519
|
this.removeJob(job.id);
|
|
4280
|
-
log$
|
|
4520
|
+
log$22.info("任务已过期并触发最后一次", { id: job.id });
|
|
4281
4521
|
}
|
|
4282
4522
|
}
|
|
4283
4523
|
}
|
|
@@ -4338,7 +4578,7 @@ function applyOneShotJitter(jobId, rawNext) {
|
|
|
4338
4578
|
*
|
|
4339
4579
|
* @module cli/repl/cron/cron-store
|
|
4340
4580
|
*/
|
|
4341
|
-
const log$
|
|
4581
|
+
const log$21 = logger.child("cron:store");
|
|
4342
4582
|
const STORE_FILE = join(join(homedir(), ".zapmyco", "cron"), "scheduled_tasks.json");
|
|
4343
4583
|
var CronStore = class {
|
|
4344
4584
|
filePath;
|
|
@@ -4362,13 +4602,13 @@ var CronStore = class {
|
|
|
4362
4602
|
const raw = await readFile(this.filePath, "utf-8");
|
|
4363
4603
|
const data = JSON.parse(raw);
|
|
4364
4604
|
if (!Array.isArray(data)) {
|
|
4365
|
-
log$
|
|
4605
|
+
log$21.warn("存储文件格式无效(非数组),将使用空列表");
|
|
4366
4606
|
return [];
|
|
4367
4607
|
}
|
|
4368
4608
|
return this.validateJobs(data);
|
|
4369
4609
|
} catch (err) {
|
|
4370
4610
|
if (err.code === "ENOENT") return [];
|
|
4371
|
-
log$
|
|
4611
|
+
log$21.warn("加载定时任务文件失败,将使用空列表", { error: err instanceof Error ? err.message : String(err) });
|
|
4372
4612
|
return [];
|
|
4373
4613
|
}
|
|
4374
4614
|
}
|
|
@@ -4415,7 +4655,7 @@ var CronStore = class {
|
|
|
4415
4655
|
if (typeof obj.maxFires === "number") job.maxFires = obj.maxFires;
|
|
4416
4656
|
valid.push(job);
|
|
4417
4657
|
}
|
|
4418
|
-
if (valid.length < raw.length) log$
|
|
4658
|
+
if (valid.length < raw.length) log$21.warn(`跳过 ${raw.length - valid.length} 个无效任务条目`);
|
|
4419
4659
|
return valid;
|
|
4420
4660
|
}
|
|
4421
4661
|
};
|
|
@@ -4433,7 +4673,7 @@ function getCronStore() {
|
|
|
4433
4673
|
* 基于内存的环形缓冲区,记录 REPL 会话中的用户输入和执行结果。
|
|
4434
4674
|
* 支持文件持久化到 ~/.zapmyco/history.json,跨会话恢复。
|
|
4435
4675
|
*/
|
|
4436
|
-
const log$
|
|
4676
|
+
const log$20 = logger.child("history:store");
|
|
4437
4677
|
/** 默认最大历史条数 */
|
|
4438
4678
|
const DEFAULT_MAX_SIZE = 100;
|
|
4439
4679
|
/** 历史文件存储路径 */
|
|
@@ -4492,13 +4732,13 @@ var HistoryStore = class {
|
|
|
4492
4732
|
if (Array.isArray(data.entries)) {
|
|
4493
4733
|
this.entries = data.entries.slice(-this.maxSize);
|
|
4494
4734
|
this.nextId = typeof data.nextId === "number" ? data.nextId : 1;
|
|
4495
|
-
log$
|
|
4735
|
+
log$20.debug("历史记录已加载", {
|
|
4496
4736
|
count: this.entries.length,
|
|
4497
4737
|
nextId: this.nextId
|
|
4498
4738
|
});
|
|
4499
4739
|
}
|
|
4500
4740
|
} catch {
|
|
4501
|
-
log$
|
|
4741
|
+
log$20.debug("无历史文件或加载失败,使用空历史");
|
|
4502
4742
|
}
|
|
4503
4743
|
}
|
|
4504
4744
|
/** 持久化历史记录到文件 */
|
|
@@ -4511,7 +4751,7 @@ var HistoryStore = class {
|
|
|
4511
4751
|
}, null, 2);
|
|
4512
4752
|
writeFileSync(this.filePath, data, "utf-8");
|
|
4513
4753
|
} catch (err) {
|
|
4514
|
-
log$
|
|
4754
|
+
log$20.warn("历史记录保存失败", { error: err instanceof Error ? err.message : String(err) });
|
|
4515
4755
|
}
|
|
4516
4756
|
}
|
|
4517
4757
|
};
|
|
@@ -4636,7 +4876,7 @@ const OVERLAY_OPTIONS = {
|
|
|
4636
4876
|
/** 截断文本到指定宽度 */
|
|
4637
4877
|
function truncate(text, maxLen) {
|
|
4638
4878
|
if (text.length <= maxLen) return text;
|
|
4639
|
-
return text.slice(0, maxLen - 1)
|
|
4879
|
+
return `${text.slice(0, maxLen - 1)}…`;
|
|
4640
4880
|
}
|
|
4641
4881
|
var AskUserQuestionComponent = class {
|
|
4642
4882
|
tui;
|
|
@@ -5147,7 +5387,7 @@ var OutputFormatter = class {
|
|
|
5147
5387
|
const c = this.getColor();
|
|
5148
5388
|
const lines = [
|
|
5149
5389
|
"",
|
|
5150
|
-
c.bold(`
|
|
5390
|
+
c.bold(` ${t("output.taskGraph.title")}:`),
|
|
5151
5391
|
c.gray(` ${t("output.taskGraph.total", {
|
|
5152
5392
|
count: graph.nodes.size,
|
|
5153
5393
|
layers: graph.layers.length
|
|
@@ -5286,7 +5526,7 @@ var OutputFormatter = class {
|
|
|
5286
5526
|
lines.push(" 分类评分:");
|
|
5287
5527
|
const bar = (s) => {
|
|
5288
5528
|
const filled = Math.round(s / 10);
|
|
5289
|
-
return c.green("█".repeat(filled)) + c.gray("░".repeat(10 - filled))
|
|
5529
|
+
return `${c.green("█".repeat(filled)) + c.gray("░".repeat(10 - filled))} ${s}/100`;
|
|
5290
5530
|
};
|
|
5291
5531
|
lines.push(` permissions: ${bar(report.scores.permissions)}`);
|
|
5292
5532
|
lines.push(` shell: ${bar(report.scores.shell)}`);
|
|
@@ -5393,7 +5633,7 @@ var Renderer = class {
|
|
|
5393
5633
|
*
|
|
5394
5634
|
* @module core/agent-team
|
|
5395
5635
|
*/
|
|
5396
|
-
const log$
|
|
5636
|
+
const log$19 = logger.child("background-store");
|
|
5397
5637
|
/**
|
|
5398
5638
|
* 后台任务持久化存储
|
|
5399
5639
|
*
|
|
@@ -5423,14 +5663,14 @@ var BackgroundTaskStore = class {
|
|
|
5423
5663
|
const entries = JSON.parse(raw);
|
|
5424
5664
|
this.tasks.clear();
|
|
5425
5665
|
for (const entry of entries) this.tasks.set(entry.taskId, entry);
|
|
5426
|
-
log$
|
|
5666
|
+
log$19.debug("后台任务已加载", {
|
|
5427
5667
|
count: entries.length,
|
|
5428
5668
|
path: this.filePath
|
|
5429
5669
|
});
|
|
5430
5670
|
return entries;
|
|
5431
5671
|
}
|
|
5432
5672
|
} catch (err) {
|
|
5433
|
-
log$
|
|
5673
|
+
log$19.warn("后台任务加载失败", {
|
|
5434
5674
|
error: String(err),
|
|
5435
5675
|
path: this.filePath
|
|
5436
5676
|
});
|
|
@@ -5489,7 +5729,7 @@ var BackgroundTaskStore = class {
|
|
|
5489
5729
|
const entries = Array.from(this.tasks.values());
|
|
5490
5730
|
writeFileSync(this.filePath, JSON.stringify(entries, null, 2), "utf-8");
|
|
5491
5731
|
} catch (err) {
|
|
5492
|
-
log$
|
|
5732
|
+
log$19.error("后台任务持久化失败", {
|
|
5493
5733
|
error: String(err),
|
|
5494
5734
|
path: this.filePath
|
|
5495
5735
|
});
|
|
@@ -5515,7 +5755,7 @@ var BackgroundTaskStore = class {
|
|
|
5515
5755
|
}
|
|
5516
5756
|
if (cleaned > 0) {
|
|
5517
5757
|
this.persist();
|
|
5518
|
-
log$
|
|
5758
|
+
log$19.info("清理过期后台任务", { cleaned });
|
|
5519
5759
|
}
|
|
5520
5760
|
return cleaned;
|
|
5521
5761
|
}
|
|
@@ -5532,7 +5772,7 @@ var BackgroundTaskStore = class {
|
|
|
5532
5772
|
*
|
|
5533
5773
|
* @module core/agent-team
|
|
5534
5774
|
*/
|
|
5535
|
-
const log$
|
|
5775
|
+
const log$18 = logger.child("agent-message-bus");
|
|
5536
5776
|
/**
|
|
5537
5777
|
* Agent 消息总线(单例)
|
|
5538
5778
|
*
|
|
@@ -5566,12 +5806,12 @@ var AgentMessageBus = class {
|
|
|
5566
5806
|
};
|
|
5567
5807
|
const targetInstance = getAgentInstanceManager().get(toAgentId);
|
|
5568
5808
|
if (targetInstance) targetInstance.inbox.push(fullMessage);
|
|
5569
|
-
else log$
|
|
5809
|
+
else log$18.warn("目标 Agent 实例不存在,消息丢弃", {
|
|
5570
5810
|
toAgentId,
|
|
5571
5811
|
messageId: fullMessage.messageId
|
|
5572
5812
|
});
|
|
5573
5813
|
this.emitter.emit(`msg:${toAgentId}`, fullMessage);
|
|
5574
|
-
log$
|
|
5814
|
+
log$18.debug("消息已投递", {
|
|
5575
5815
|
from: fromAgentId,
|
|
5576
5816
|
to: toAgentId,
|
|
5577
5817
|
type: fullMessage.type,
|
|
@@ -5654,7 +5894,7 @@ function getAgentMessageBus() {
|
|
|
5654
5894
|
*
|
|
5655
5895
|
* @module core/agent-team
|
|
5656
5896
|
*/
|
|
5657
|
-
const log$
|
|
5897
|
+
const log$17 = logger.child("background-agent-manager");
|
|
5658
5898
|
/**
|
|
5659
5899
|
* 后台 Agent 管理器(单例)
|
|
5660
5900
|
*/
|
|
@@ -5711,7 +5951,7 @@ var BackgroundAgentManager = class {
|
|
|
5711
5951
|
parentAgentId: params.parentInstanceId
|
|
5712
5952
|
});
|
|
5713
5953
|
this.runAsync(taskId, params, abortController, timeoutMs).catch((err) => {
|
|
5714
|
-
log$
|
|
5954
|
+
log$17.error("后台 Agent 意外崩溃", {
|
|
5715
5955
|
taskId,
|
|
5716
5956
|
error: String(err)
|
|
5717
5957
|
});
|
|
@@ -5731,7 +5971,7 @@ var BackgroundAgentManager = class {
|
|
|
5731
5971
|
const orchestrator = this.orchestrator;
|
|
5732
5972
|
const messageBus = getAgentMessageBus();
|
|
5733
5973
|
const timeoutHandle = setTimeout(() => {
|
|
5734
|
-
log$
|
|
5974
|
+
log$17.warn("后台 Agent 超时,自动取消", {
|
|
5735
5975
|
taskId,
|
|
5736
5976
|
timeoutMs
|
|
5737
5977
|
});
|
|
@@ -5775,7 +6015,7 @@ var BackgroundAgentManager = class {
|
|
|
5775
6015
|
taskId,
|
|
5776
6016
|
requiresResponse: false
|
|
5777
6017
|
});
|
|
5778
|
-
log$
|
|
6018
|
+
log$17.info("后台 Agent 完成通知已发送", {
|
|
5779
6019
|
taskId,
|
|
5780
6020
|
instanceId: result.instanceId,
|
|
5781
6021
|
parentId: params.parentInstanceId,
|
|
@@ -5802,7 +6042,7 @@ var BackgroundAgentManager = class {
|
|
|
5802
6042
|
completedAt: Date.now(),
|
|
5803
6043
|
error
|
|
5804
6044
|
});
|
|
5805
|
-
log$
|
|
6045
|
+
log$17.warn("后台 Agent 失败", {
|
|
5806
6046
|
taskId,
|
|
5807
6047
|
error
|
|
5808
6048
|
});
|
|
@@ -5839,7 +6079,7 @@ var BackgroundAgentManager = class {
|
|
|
5839
6079
|
if (runtime.instanceId) try {
|
|
5840
6080
|
await getAgentInstanceManager().cancel(runtime.instanceId);
|
|
5841
6081
|
} catch {}
|
|
5842
|
-
log$
|
|
6082
|
+
log$17.info("后台 Agent 已取消", { taskId });
|
|
5843
6083
|
return true;
|
|
5844
6084
|
}
|
|
5845
6085
|
/**
|
|
@@ -5848,13 +6088,13 @@ var BackgroundAgentManager = class {
|
|
|
5848
6088
|
restore() {
|
|
5849
6089
|
this.store.load();
|
|
5850
6090
|
const stale = this.store.cleanStale();
|
|
5851
|
-
if (stale > 0) log$
|
|
6091
|
+
if (stale > 0) log$17.info("跨会话恢复:清理了过期后台任务", { count: stale });
|
|
5852
6092
|
const active = this.store.listActive();
|
|
5853
6093
|
for (const entry of active) this.store.updateStatus(entry.taskId, "failed", {
|
|
5854
6094
|
completedAt: Date.now(),
|
|
5855
6095
|
error: "会话终止导致任务丢失"
|
|
5856
6096
|
});
|
|
5857
|
-
if (active.length > 0) log$
|
|
6097
|
+
if (active.length > 0) log$17.info("跨会话恢复:标记活跃任务为 failed", { count: active.length });
|
|
5858
6098
|
}
|
|
5859
6099
|
};
|
|
5860
6100
|
/** 全局单例 */
|
|
@@ -6073,7 +6313,7 @@ function createDeferred() {
|
|
|
6073
6313
|
*
|
|
6074
6314
|
* @module core/question/question-manager
|
|
6075
6315
|
*/
|
|
6076
|
-
const log$
|
|
6316
|
+
const log$16 = logger.child("question-manager");
|
|
6077
6317
|
/** 问题超时时间(5 分钟) */
|
|
6078
6318
|
const QUESTION_TIMEOUT_MS = 300 * 1e3;
|
|
6079
6319
|
var QuestionManager = class {
|
|
@@ -6119,13 +6359,13 @@ var QuestionManager = class {
|
|
|
6119
6359
|
requestId,
|
|
6120
6360
|
questionCount: params.questions.length
|
|
6121
6361
|
});
|
|
6122
|
-
log$
|
|
6362
|
+
log$16.debug("提问已发出", {
|
|
6123
6363
|
requestId,
|
|
6124
6364
|
questionCount: params.questions.length
|
|
6125
6365
|
});
|
|
6126
6366
|
const timeout = setTimeout(() => {
|
|
6127
6367
|
if (!deferred.isSettled) {
|
|
6128
|
-
log$
|
|
6368
|
+
log$16.warn("提问超时,自动取消", { requestId });
|
|
6129
6369
|
deferred.reject(/* @__PURE__ */ new Error("提问超时,用户未在 5 分钟内回答"));
|
|
6130
6370
|
this.pending.delete(requestId);
|
|
6131
6371
|
eventBus.emit("question:timeout", { requestId });
|
|
@@ -6139,7 +6379,7 @@ var QuestionManager = class {
|
|
|
6139
6379
|
requestId,
|
|
6140
6380
|
answerCount: Object.keys(result.answers).length
|
|
6141
6381
|
});
|
|
6142
|
-
log$
|
|
6382
|
+
log$16.debug("提问已回答", { requestId });
|
|
6143
6383
|
return result;
|
|
6144
6384
|
} catch (err) {
|
|
6145
6385
|
clearTimeout(timeout);
|
|
@@ -6148,7 +6388,7 @@ var QuestionManager = class {
|
|
|
6148
6388
|
requestId,
|
|
6149
6389
|
reason: err instanceof Error ? err.message : String(err)
|
|
6150
6390
|
});
|
|
6151
|
-
log$
|
|
6391
|
+
log$16.debug("提问已取消", {
|
|
6152
6392
|
requestId,
|
|
6153
6393
|
error: err instanceof Error ? err.message : String(err)
|
|
6154
6394
|
});
|
|
@@ -6170,7 +6410,7 @@ var QuestionManager = class {
|
|
|
6170
6410
|
const count = this.pending.size;
|
|
6171
6411
|
for (const [, entry] of this.pending) if (!entry.deferred.isSettled) entry.deferred.reject(error);
|
|
6172
6412
|
this.pending.clear();
|
|
6173
|
-
if (count > 0) log$
|
|
6413
|
+
if (count > 0) log$16.debug("已清理所有待处理问题", { count });
|
|
6174
6414
|
}
|
|
6175
6415
|
};
|
|
6176
6416
|
let globalQuestionManager = null;
|
|
@@ -7248,7 +7488,7 @@ function isPathWithinWorkdir(resolvedPath, workdir) {
|
|
|
7248
7488
|
try {
|
|
7249
7489
|
normalizedWorkdir = realpathSync(normalizedWorkdir).replace(/\\/g, "/");
|
|
7250
7490
|
} catch {}
|
|
7251
|
-
if (!normalizedPath.startsWith(normalizedWorkdir
|
|
7491
|
+
if (!normalizedPath.startsWith(`${normalizedWorkdir}/`) && normalizedPath !== normalizedWorkdir) return false;
|
|
7252
7492
|
return true;
|
|
7253
7493
|
}
|
|
7254
7494
|
/**
|
|
@@ -9403,7 +9643,7 @@ function runForeground(childProcess, timeoutSec, signal) {
|
|
|
9403
9643
|
childProcess.on("error", (err) => {
|
|
9404
9644
|
settle({
|
|
9405
9645
|
stdout: stdoutChunks.join(""),
|
|
9406
|
-
stderr: stderrChunks.join("")
|
|
9646
|
+
stderr: `${stderrChunks.join("")}\n[进程错误: ${err.message}]`,
|
|
9407
9647
|
exitCode: -1,
|
|
9408
9648
|
signal: null,
|
|
9409
9649
|
timedOut: false,
|
|
@@ -9724,7 +9964,7 @@ function formatDuration(ms) {
|
|
|
9724
9964
|
}
|
|
9725
9965
|
function truncateCommand(command, maxLen = 80) {
|
|
9726
9966
|
if (command.length <= maxLen) return command;
|
|
9727
|
-
return command.slice(0, maxLen - 3)
|
|
9967
|
+
return `${command.slice(0, maxLen - 3)}...`;
|
|
9728
9968
|
}
|
|
9729
9969
|
|
|
9730
9970
|
//#endregion
|
|
@@ -11596,7 +11836,7 @@ var WorktreeStore = class {
|
|
|
11596
11836
|
* @module core/worktree
|
|
11597
11837
|
*/
|
|
11598
11838
|
const execFileAsync = promisify(execFile);
|
|
11599
|
-
const log$
|
|
11839
|
+
const log$15 = logger.child("worktree-manager");
|
|
11600
11840
|
let globalWorktreeManager = null;
|
|
11601
11841
|
/** 获取全局 WorktreeManager 实例 */
|
|
11602
11842
|
function getWorktreeManager() {
|
|
@@ -11632,7 +11872,7 @@ var WorktreeManager = class {
|
|
|
11632
11872
|
throw new WorktreeError("无法确定 git 仓库根目录,请确保在 git 仓库中运行", "NOT_GIT_REPO");
|
|
11633
11873
|
}
|
|
11634
11874
|
try {
|
|
11635
|
-
log$
|
|
11875
|
+
log$15.info("创建 worktree", {
|
|
11636
11876
|
branchName,
|
|
11637
11877
|
worktreePath,
|
|
11638
11878
|
gitRoot
|
|
@@ -11660,14 +11900,14 @@ var WorktreeManager = class {
|
|
|
11660
11900
|
});
|
|
11661
11901
|
} catch (err) {
|
|
11662
11902
|
const msg = err instanceof Error ? err.message : String(err);
|
|
11663
|
-
log$
|
|
11903
|
+
log$15.warn("创建分支失败,清理 worktree", {
|
|
11664
11904
|
branchName,
|
|
11665
11905
|
error: msg
|
|
11666
11906
|
});
|
|
11667
11907
|
try {
|
|
11668
11908
|
await this.removeByPath(worktreePath, branchName, true);
|
|
11669
11909
|
} catch (err) {
|
|
11670
|
-
log$
|
|
11910
|
+
log$15.warn("Worktree 创建失败后清理出错", {
|
|
11671
11911
|
worktreePath,
|
|
11672
11912
|
error: err instanceof Error ? err.message : String(err)
|
|
11673
11913
|
});
|
|
@@ -11692,7 +11932,7 @@ var WorktreeManager = class {
|
|
|
11692
11932
|
createdBy: info.createdBy,
|
|
11693
11933
|
status: "active"
|
|
11694
11934
|
});
|
|
11695
|
-
log$
|
|
11935
|
+
log$15.info("Worktree 创建成功", {
|
|
11696
11936
|
id: info.id,
|
|
11697
11937
|
path: worktreePath
|
|
11698
11938
|
});
|
|
@@ -11704,13 +11944,13 @@ var WorktreeManager = class {
|
|
|
11704
11944
|
async remove(id, discardChanges) {
|
|
11705
11945
|
const info = this.activeWorktrees.get(id);
|
|
11706
11946
|
if (!info) {
|
|
11707
|
-
log$
|
|
11947
|
+
log$15.warn("尝试删除不存在的 worktree", { id });
|
|
11708
11948
|
return;
|
|
11709
11949
|
}
|
|
11710
11950
|
await this.removeByPath(info.worktreePath, info.branchName, discardChanges);
|
|
11711
11951
|
this.activeWorktrees.delete(id);
|
|
11712
11952
|
this.store.delete(id);
|
|
11713
|
-
log$
|
|
11953
|
+
log$15.info("Worktree 已删除", { id });
|
|
11714
11954
|
}
|
|
11715
11955
|
/**
|
|
11716
11956
|
* 通过路径删除 worktree(内部方法)
|
|
@@ -11720,7 +11960,7 @@ var WorktreeManager = class {
|
|
|
11720
11960
|
try {
|
|
11721
11961
|
await execFileAsync("git", ["worktree", "prune"], { timeout: 1e4 });
|
|
11722
11962
|
} catch (err) {
|
|
11723
|
-
log$
|
|
11963
|
+
log$15.warn("Worktree prune 失败(路径不存在)", {
|
|
11724
11964
|
worktreePath,
|
|
11725
11965
|
error: err instanceof Error ? err.message : String(err)
|
|
11726
11966
|
});
|
|
@@ -11734,7 +11974,7 @@ var WorktreeManager = class {
|
|
|
11734
11974
|
await execFileAsync("git", args, { timeout: 15e3 });
|
|
11735
11975
|
} catch (err) {
|
|
11736
11976
|
const msg = err instanceof Error ? err.message : String(err);
|
|
11737
|
-
log$
|
|
11977
|
+
log$15.warn("git worktree remove 失败", {
|
|
11738
11978
|
worktreePath,
|
|
11739
11979
|
error: msg
|
|
11740
11980
|
});
|
|
@@ -11746,7 +11986,7 @@ var WorktreeManager = class {
|
|
|
11746
11986
|
branchName
|
|
11747
11987
|
], { timeout: 1e4 });
|
|
11748
11988
|
} catch (err) {
|
|
11749
|
-
log$
|
|
11989
|
+
log$15.warn("删除 worktree 分支失败", {
|
|
11750
11990
|
branchName,
|
|
11751
11991
|
error: err instanceof Error ? err.message : String(err)
|
|
11752
11992
|
});
|
|
@@ -11754,7 +11994,7 @@ var WorktreeManager = class {
|
|
|
11754
11994
|
try {
|
|
11755
11995
|
await execFileAsync("git", ["worktree", "prune"], { timeout: 1e4 });
|
|
11756
11996
|
} catch (err) {
|
|
11757
|
-
log$
|
|
11997
|
+
log$15.warn("Worktree prune 失败", { error: err instanceof Error ? err.message : String(err) });
|
|
11758
11998
|
}
|
|
11759
11999
|
}
|
|
11760
12000
|
/**
|
|
@@ -11776,7 +12016,7 @@ var WorktreeManager = class {
|
|
|
11776
12016
|
await this.remove(id, true);
|
|
11777
12017
|
return { cleaned: true };
|
|
11778
12018
|
}
|
|
11779
|
-
log$
|
|
12019
|
+
log$15.info("Worktree 有变更,保留", {
|
|
11780
12020
|
id,
|
|
11781
12021
|
path: info.worktreePath
|
|
11782
12022
|
});
|
|
@@ -11796,7 +12036,7 @@ var WorktreeManager = class {
|
|
|
11796
12036
|
});
|
|
11797
12037
|
return stdout.trim().length > 0;
|
|
11798
12038
|
} catch (err) {
|
|
11799
|
-
log$
|
|
12039
|
+
log$15.warn("检查 worktree 变更状态失败", {
|
|
11800
12040
|
worktreePath,
|
|
11801
12041
|
error: err instanceof Error ? err.message : String(err)
|
|
11802
12042
|
});
|
|
@@ -11816,7 +12056,7 @@ var WorktreeManager = class {
|
|
|
11816
12056
|
await this.removeByPath(record.worktreePath, record.branchName, true);
|
|
11817
12057
|
cleaned++;
|
|
11818
12058
|
} catch (err) {
|
|
11819
|
-
log$
|
|
12059
|
+
log$15.warn("过期 worktree 清理失败", {
|
|
11820
12060
|
id: record.id,
|
|
11821
12061
|
path: record.worktreePath,
|
|
11822
12062
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -11825,7 +12065,7 @@ var WorktreeManager = class {
|
|
|
11825
12065
|
this.activeWorktrees.delete(record.id);
|
|
11826
12066
|
this.store.delete(record.id);
|
|
11827
12067
|
}
|
|
11828
|
-
if (cleaned > 0) log$
|
|
12068
|
+
if (cleaned > 0) log$15.info("过期 worktree 清理完成", { cleaned });
|
|
11829
12069
|
return cleaned;
|
|
11830
12070
|
}
|
|
11831
12071
|
listActive() {
|
|
@@ -11842,41 +12082,9 @@ var WorktreeManager = class {
|
|
|
11842
12082
|
}
|
|
11843
12083
|
};
|
|
11844
12084
|
|
|
11845
|
-
//#endregion
|
|
11846
|
-
//#region src/core/agent-team/types.ts
|
|
11847
|
-
/**
|
|
11848
|
-
* Agent 默认安全工具集
|
|
11849
|
-
*
|
|
11850
|
-
* 只读和搜索工具,不含任何可能产生副作用的操作。
|
|
11851
|
-
* 子 Agent 默认使用此集合。
|
|
11852
|
-
*/
|
|
11853
|
-
const AGENT_SAFE_TOOLS = [
|
|
11854
|
-
"ReadFile",
|
|
11855
|
-
"Glob",
|
|
11856
|
-
"Grep",
|
|
11857
|
-
"WebFetch",
|
|
11858
|
-
"WebSearch",
|
|
11859
|
-
"GetCurrentTime",
|
|
11860
|
-
"GetWorkdirInfo"
|
|
11861
|
-
];
|
|
11862
|
-
/**
|
|
11863
|
-
* Agent 标准工具集
|
|
11864
|
-
*
|
|
11865
|
-
* safe + 文件写入 + Shell 执行。
|
|
11866
|
-
*/
|
|
11867
|
-
const AGENT_STANDARD_TOOLS = [
|
|
11868
|
-
...AGENT_SAFE_TOOLS,
|
|
11869
|
-
"WriteFile",
|
|
11870
|
-
"EditFile",
|
|
11871
|
-
"Exec",
|
|
11872
|
-
"Process",
|
|
11873
|
-
"TaskManage",
|
|
11874
|
-
"Memory"
|
|
11875
|
-
];
|
|
11876
|
-
|
|
11877
12085
|
//#endregion
|
|
11878
12086
|
//#region src/core/agent-team/agent-factory.ts
|
|
11879
|
-
const log$
|
|
12087
|
+
const log$14 = logger.child("agent-factory");
|
|
11880
12088
|
/**
|
|
11881
12089
|
* 创建 Agent 实例
|
|
11882
12090
|
*
|
|
@@ -11910,7 +12118,7 @@ function createAgentFromType(definition, instance, parentAgent, availableTools,
|
|
|
11910
12118
|
const tools = resolveTools(definition, availableTools, instance.depth, config);
|
|
11911
12119
|
agent.registerTools(tools);
|
|
11912
12120
|
agent.systemPromptOverride = buildSystemPrompt(definition, instance.task.description, config);
|
|
11913
|
-
log$
|
|
12121
|
+
log$14.debug("创建 Agent 实例", {
|
|
11914
12122
|
typeId: definition.typeId,
|
|
11915
12123
|
instanceId: instance.instanceId,
|
|
11916
12124
|
depth: instance.depth,
|
|
@@ -12004,7 +12212,7 @@ function shareParentResources(subAgent, parentAgent, definition) {
|
|
|
12004
12212
|
const resolvedModel = resolveModelForDefinition(definition.model, parentAgent);
|
|
12005
12213
|
if (resolvedModel) {
|
|
12006
12214
|
subAgent.innerAgent.state.model = resolvedModel;
|
|
12007
|
-
if (resolvedModel.id !== parentInner.state.model?.id) log$
|
|
12215
|
+
if (resolvedModel.id !== parentInner.state.model?.id) log$14.info(`子 Agent [${definition?.typeId}] 模型切换: ${parentInner.state.model?.id ?? "N/A"} → ${resolvedModel.id}`, {
|
|
12008
12216
|
typeId: definition?.typeId,
|
|
12009
12217
|
parentModel: parentInner.state.model?.id,
|
|
12010
12218
|
childModel: resolvedModel.id
|
|
@@ -12019,7 +12227,7 @@ function shareParentResources(subAgent, parentAgent, definition) {
|
|
|
12019
12227
|
if (parentGetApiKey) subAgent.innerAgent.getApiKey = parentGetApiKey;
|
|
12020
12228
|
}
|
|
12021
12229
|
/**
|
|
12022
|
-
* 根据 Agent 类型声明的 model
|
|
12230
|
+
* 根据 Agent 类型声明的 model 值解析本地 Model 对象
|
|
12023
12231
|
*
|
|
12024
12232
|
* 支持三种格式:
|
|
12025
12233
|
* - 语义名称: 'analysis' → 指向 analysisModel 槽位
|
|
@@ -12029,7 +12237,7 @@ function shareParentResources(subAgent, parentAgent, definition) {
|
|
|
12029
12237
|
*
|
|
12030
12238
|
* @param model - Agent 类型声明的 model 值
|
|
12031
12239
|
* @param parentAgent - 父 Agent(用于获取 AgentLlmFacade)
|
|
12032
|
-
* @returns
|
|
12240
|
+
* @returns 解析后的本地 Model 对象,解析失败返回 undefined
|
|
12033
12241
|
*/
|
|
12034
12242
|
function resolveModelForDefinition(model, parentAgent) {
|
|
12035
12243
|
const facade = parentAgent.llmFacade;
|
|
@@ -12043,14 +12251,14 @@ function resolveModelForDefinition(model, parentAgent) {
|
|
|
12043
12251
|
}
|
|
12044
12252
|
} catch (error) {
|
|
12045
12253
|
const message = error instanceof Error ? error.message : String(error);
|
|
12046
|
-
log$
|
|
12254
|
+
log$14.warn(`解析 Agent 类型偏好模型 [${model}] 失败`, { error: message });
|
|
12047
12255
|
return;
|
|
12048
12256
|
}
|
|
12049
12257
|
}
|
|
12050
12258
|
|
|
12051
12259
|
//#endregion
|
|
12052
12260
|
//#region src/core/agent-team/agent-result-aggregator.ts
|
|
12053
|
-
const log$
|
|
12261
|
+
const log$13 = logger.child("agent-result-aggregator");
|
|
12054
12262
|
/** 零值 TokenUsage */
|
|
12055
12263
|
const ZERO_TOKEN = {
|
|
12056
12264
|
inputTokens: 0,
|
|
@@ -12080,7 +12288,7 @@ function aggregateResults(teamId, workerResults) {
|
|
|
12080
12288
|
estimatedCostUsd: sum.estimatedCostUsd + (r.tokenUsage?.estimatedCostUsd ?? 0)
|
|
12081
12289
|
}), { ...ZERO_TOKEN });
|
|
12082
12290
|
const summary = buildTeamSummary(workerResults);
|
|
12083
|
-
log$
|
|
12291
|
+
log$13.debug("Team 结果聚合完成", {
|
|
12084
12292
|
teamId,
|
|
12085
12293
|
total: workerResults.length,
|
|
12086
12294
|
succeeded,
|
|
@@ -12158,7 +12366,7 @@ function escapeMarkdownTable(text) {
|
|
|
12158
12366
|
|
|
12159
12367
|
//#endregion
|
|
12160
12368
|
//#region src/core/agent-team/agent-orchestrator.ts
|
|
12161
|
-
const log$
|
|
12369
|
+
const log$12 = logger.child("agent-orchestrator");
|
|
12162
12370
|
/**
|
|
12163
12371
|
* Agent 编排器
|
|
12164
12372
|
*
|
|
@@ -12191,7 +12399,7 @@ var AgentOrchestrator = class {
|
|
|
12191
12399
|
const startTime = Date.now();
|
|
12192
12400
|
const defaultType = getAgentTypeRegistry().getDefault();
|
|
12193
12401
|
if (!defaultType) throw new Error("无法获取默认 Agent 类型(general-purpose)");
|
|
12194
|
-
log$
|
|
12402
|
+
log$12.info("开始扁平并行执行", {
|
|
12195
12403
|
count: specs.length,
|
|
12196
12404
|
maxConcurrent: this.flatConfig.maxConcurrent,
|
|
12197
12405
|
hasContext: context != null
|
|
@@ -12199,7 +12407,7 @@ var AgentOrchestrator = class {
|
|
|
12199
12407
|
const allResults = [];
|
|
12200
12408
|
for (let i = 0; i < specs.length; i += this.flatConfig.maxConcurrent) {
|
|
12201
12409
|
const batch = specs.slice(i, i + this.flatConfig.maxConcurrent);
|
|
12202
|
-
log$
|
|
12410
|
+
log$12.debug("执行扁平批次", {
|
|
12203
12411
|
batchStart: i,
|
|
12204
12412
|
batchSize: batch.length,
|
|
12205
12413
|
totalSpecs: specs.length
|
|
@@ -12209,7 +12417,7 @@ var AgentOrchestrator = class {
|
|
|
12209
12417
|
}
|
|
12210
12418
|
const succeeded = allResults.filter((r) => r.status === "success").length;
|
|
12211
12419
|
const totalDuration = Date.now() - startTime;
|
|
12212
|
-
log$
|
|
12420
|
+
log$12.info("扁平并行执行完成", {
|
|
12213
12421
|
total: allResults.length,
|
|
12214
12422
|
succeeded,
|
|
12215
12423
|
failed: allResults.length - succeeded,
|
|
@@ -12284,7 +12492,7 @@ var AgentOrchestrator = class {
|
|
|
12284
12492
|
} catch (error) {
|
|
12285
12493
|
const duration = Date.now() - startTime;
|
|
12286
12494
|
const message = error instanceof Error ? error.message : String(error);
|
|
12287
|
-
log$
|
|
12495
|
+
log$12.warn("扁平子任务执行失败", {
|
|
12288
12496
|
specId: spec.id,
|
|
12289
12497
|
error: message,
|
|
12290
12498
|
duration
|
|
@@ -12341,7 +12549,7 @@ var AgentOrchestrator = class {
|
|
|
12341
12549
|
const parentInstanceId = options?.parentInstanceId ?? "";
|
|
12342
12550
|
const depth = (parentInstanceId && instanceManager.get(parentInstanceId) ? instanceManager.get(parentInstanceId)?.depth ?? 0 : 0) + 1;
|
|
12343
12551
|
if (depth > this.teamConfig.maxGlobalDepth) {
|
|
12344
|
-
log$
|
|
12552
|
+
log$12.warn("Worker 创建被拒绝:超过全局最大深度", {
|
|
12345
12553
|
typeId,
|
|
12346
12554
|
depth,
|
|
12347
12555
|
maxGlobalDepth: this.teamConfig.maxGlobalDepth
|
|
@@ -12463,7 +12671,7 @@ var AgentOrchestrator = class {
|
|
|
12463
12671
|
} catch (error) {
|
|
12464
12672
|
const duration = Date.now() - startTime;
|
|
12465
12673
|
const message = error instanceof Error ? error.message : String(error);
|
|
12466
|
-
log$
|
|
12674
|
+
log$12.warn("Worker 执行失败", {
|
|
12467
12675
|
typeId,
|
|
12468
12676
|
instanceId,
|
|
12469
12677
|
error: message,
|
|
@@ -12506,12 +12714,12 @@ var AgentOrchestrator = class {
|
|
|
12506
12714
|
const wm = getWorktreeManager();
|
|
12507
12715
|
if (wm) try {
|
|
12508
12716
|
const cleanResult = await wm.autoCleanIfNoChanges(worktreeInfo.id);
|
|
12509
|
-
if (!cleanResult.cleaned) log$
|
|
12717
|
+
if (!cleanResult.cleaned) log$12.info("Worktree 有变更,保留", {
|
|
12510
12718
|
id: worktreeInfo.id,
|
|
12511
12719
|
path: cleanResult.worktreePath
|
|
12512
12720
|
});
|
|
12513
12721
|
} catch (err) {
|
|
12514
|
-
log$
|
|
12722
|
+
log$12.warn("Worktree 清理失败", {
|
|
12515
12723
|
id: worktreeInfo.id,
|
|
12516
12724
|
error: err instanceof Error ? err.message : String(err)
|
|
12517
12725
|
});
|
|
@@ -12531,7 +12739,7 @@ var AgentOrchestrator = class {
|
|
|
12531
12739
|
*/
|
|
12532
12740
|
async spawnTeam(taskDescription, workerSpecs) {
|
|
12533
12741
|
const teamId = `team-${Date.now()}-${++this.teamCounter}`;
|
|
12534
|
-
log$
|
|
12742
|
+
log$12.info("创建 Team", {
|
|
12535
12743
|
teamId,
|
|
12536
12744
|
workerCount: workerSpecs.length,
|
|
12537
12745
|
taskDescription
|
|
@@ -12564,7 +12772,29 @@ var AgentOrchestrator = class {
|
|
|
12564
12772
|
const instanceManager = getAgentInstanceManager();
|
|
12565
12773
|
const instance = instanceManager.get(instanceId);
|
|
12566
12774
|
if (!instance) return;
|
|
12567
|
-
if (event.
|
|
12775
|
+
if (event.detail?.isStart) instanceManager.recordToolCall(instanceId, {
|
|
12776
|
+
toolName: event.detail.toolName,
|
|
12777
|
+
toolCallId: event.detail.toolCallId,
|
|
12778
|
+
argsDisplay: event.detail.argsDisplay,
|
|
12779
|
+
status: "running",
|
|
12780
|
+
startedAt: Date.now()
|
|
12781
|
+
});
|
|
12782
|
+
else if (event.detail?.isEnd) {
|
|
12783
|
+
const lastRunning = [...instance.toolCallHistory].reverse().find((t) => t.status === "running" && t.toolName === event.detail?.toolName);
|
|
12784
|
+
if (lastRunning) {
|
|
12785
|
+
lastRunning.status = event.detail.isError ? "failed" : "completed";
|
|
12786
|
+
lastRunning.endedAt = Date.now();
|
|
12787
|
+
}
|
|
12788
|
+
instanceManager.emit("instance:toolcall", {
|
|
12789
|
+
instanceId,
|
|
12790
|
+
typeId: instance.typeId,
|
|
12791
|
+
record: {
|
|
12792
|
+
toolName: event.detail.toolName,
|
|
12793
|
+
status: event.detail.isError ? "failed" : "completed",
|
|
12794
|
+
startedAt: 0
|
|
12795
|
+
}
|
|
12796
|
+
});
|
|
12797
|
+
} else if (event.percent === 0) {
|
|
12568
12798
|
const toolName = event.message;
|
|
12569
12799
|
const prevUses = instance.currentActivity?.toolUses ?? 0;
|
|
12570
12800
|
const parenIdx = toolName.indexOf("(");
|
|
@@ -12577,7 +12807,7 @@ var AgentOrchestrator = class {
|
|
|
12577
12807
|
startedAt: Date.now()
|
|
12578
12808
|
};
|
|
12579
12809
|
instanceManager.setActivity(instanceId, activity);
|
|
12580
|
-
}
|
|
12810
|
+
}
|
|
12581
12811
|
};
|
|
12582
12812
|
agent.on(agent.EVENT_PROGRESS, progressHandler);
|
|
12583
12813
|
return () => {
|
|
@@ -12893,7 +13123,7 @@ function createReplBuiltinTools(webConfig, taskStore, skillConfig, parentAgent,
|
|
|
12893
13123
|
*
|
|
12894
13124
|
* @module cli/repl/skill-watcher
|
|
12895
13125
|
*/
|
|
12896
|
-
const log$
|
|
13126
|
+
const log$11 = logger.child("repl:skill-watcher");
|
|
12897
13127
|
/** 防抖延迟(毫秒) */
|
|
12898
13128
|
const DEBOUNCE_MS = 500;
|
|
12899
13129
|
/**
|
|
@@ -12923,15 +13153,15 @@ var SkillWatcher = class {
|
|
|
12923
13153
|
});
|
|
12924
13154
|
this.watcher.on("ready", () => {
|
|
12925
13155
|
this._ready = true;
|
|
12926
|
-
log$
|
|
13156
|
+
log$11.info("技能文件监视已就绪", { dirs: options.watchDirs });
|
|
12927
13157
|
}).on("add", (path) => {
|
|
12928
|
-
log$
|
|
13158
|
+
log$11.debug("技能文件新增", { path });
|
|
12929
13159
|
this.debounceNotify(options.onChanged);
|
|
12930
13160
|
}).on("change", (path) => {
|
|
12931
|
-
log$
|
|
13161
|
+
log$11.debug("技能文件修改", { path });
|
|
12932
13162
|
this.debounceNotify(options.onChanged);
|
|
12933
13163
|
}).on("unlink", (path) => {
|
|
12934
|
-
log$
|
|
13164
|
+
log$11.debug("技能文件删除", { path });
|
|
12935
13165
|
this.debounceNotify(options.onChanged);
|
|
12936
13166
|
});
|
|
12937
13167
|
}
|
|
@@ -13446,6 +13676,233 @@ function createLspTool(lspManager) {
|
|
|
13446
13676
|
};
|
|
13447
13677
|
}
|
|
13448
13678
|
|
|
13679
|
+
//#endregion
|
|
13680
|
+
//#region src/cli/repl/tools/send-message.ts
|
|
13681
|
+
/**
|
|
13682
|
+
* 创建 SendMessage 工具注册
|
|
13683
|
+
*
|
|
13684
|
+
* @param currentAgentInstanceId - 当前 Agent 的实例 ID(用于 fromAgentId)
|
|
13685
|
+
* @returns ToolRegistration
|
|
13686
|
+
*/
|
|
13687
|
+
function createSendMessageTool(currentAgentInstanceId) {
|
|
13688
|
+
return {
|
|
13689
|
+
id: "SendMessage",
|
|
13690
|
+
label: "发送消息",
|
|
13691
|
+
defaultRisk: "medium",
|
|
13692
|
+
description: [
|
|
13693
|
+
"向其他 Agent 发送消息,用于 Agent 间通信。",
|
|
13694
|
+
"",
|
|
13695
|
+
"### 何时使用此工具",
|
|
13696
|
+
"1. 执行过程中需要向父 Agent/Coordinator 确认某些信息",
|
|
13697
|
+
"2. 发现任务描述不够明确,需要澄清",
|
|
13698
|
+
"3. 需要向 Coordinator 报告中间进度或部分结果",
|
|
13699
|
+
"4. 需要告知 Coordinator 任务可以提前结束",
|
|
13700
|
+
"",
|
|
13701
|
+
"### 何时不使用此工具",
|
|
13702
|
+
"- 任务已完全完成(直接返回结果即可)",
|
|
13703
|
+
"- 不需要和其他 Agent 交互",
|
|
13704
|
+
"",
|
|
13705
|
+
"### 参数说明",
|
|
13706
|
+
"- **toAgentId**: 目标 Agent 的实例 ID。使用 `parent` 表示父 Agent",
|
|
13707
|
+
"- **message**: 要发送的消息内容",
|
|
13708
|
+
"- **messageType**: 消息类型",
|
|
13709
|
+
" - `question`: 需要确认或回答的问题",
|
|
13710
|
+
" - `progress`: 进度报告",
|
|
13711
|
+
" - `task_result`: 部分结果或中间产出",
|
|
13712
|
+
"",
|
|
13713
|
+
`### 你的实例 ID: \`${currentAgentInstanceId}\``
|
|
13714
|
+
].join("\n"),
|
|
13715
|
+
parameters: {
|
|
13716
|
+
type: "object",
|
|
13717
|
+
properties: {
|
|
13718
|
+
toAgentId: {
|
|
13719
|
+
type: "string",
|
|
13720
|
+
description: "目标 Agent 实例 ID(或 \"parent\" 表示父 Agent)"
|
|
13721
|
+
},
|
|
13722
|
+
message: {
|
|
13723
|
+
type: "string",
|
|
13724
|
+
description: "消息内容"
|
|
13725
|
+
},
|
|
13726
|
+
messageType: {
|
|
13727
|
+
type: "string",
|
|
13728
|
+
enum: [
|
|
13729
|
+
"question",
|
|
13730
|
+
"progress",
|
|
13731
|
+
"task_result"
|
|
13732
|
+
],
|
|
13733
|
+
description: "消息类型",
|
|
13734
|
+
default: "progress"
|
|
13735
|
+
}
|
|
13736
|
+
},
|
|
13737
|
+
required: ["toAgentId", "message"]
|
|
13738
|
+
},
|
|
13739
|
+
execute: async (_toolCallId, params) => {
|
|
13740
|
+
const p = params;
|
|
13741
|
+
const messageBus = getAgentMessageBus();
|
|
13742
|
+
const msgType = p.messageType === "question" ? "question" : p.messageType === "result" ? "task_result" : "progress";
|
|
13743
|
+
const fullMessage = messageBus.publish(currentAgentInstanceId, p.toAgentId, {
|
|
13744
|
+
type: msgType,
|
|
13745
|
+
payload: p.message,
|
|
13746
|
+
requiresResponse: p.messageType === "question"
|
|
13747
|
+
});
|
|
13748
|
+
return {
|
|
13749
|
+
content: [{
|
|
13750
|
+
type: "text",
|
|
13751
|
+
text: `消息已发送 (ID: ${fullMessage.messageId})\n目标: ${p.toAgentId}\n类型: ${p.messageType ?? "progress"}`
|
|
13752
|
+
}],
|
|
13753
|
+
details: fullMessage
|
|
13754
|
+
};
|
|
13755
|
+
}
|
|
13756
|
+
};
|
|
13757
|
+
}
|
|
13758
|
+
|
|
13759
|
+
//#endregion
|
|
13760
|
+
//#region src/cli/repl/tools/task-stop.ts
|
|
13761
|
+
/**
|
|
13762
|
+
* 创建 TaskStop 工具注册
|
|
13763
|
+
*
|
|
13764
|
+
* @returns ToolRegistration
|
|
13765
|
+
*/
|
|
13766
|
+
function createTaskStopTool() {
|
|
13767
|
+
return {
|
|
13768
|
+
id: "TaskStop",
|
|
13769
|
+
label: "停止任务",
|
|
13770
|
+
defaultRisk: "medium",
|
|
13771
|
+
description: [
|
|
13772
|
+
"停止正在运行中的子 Agent 任务。",
|
|
13773
|
+
"",
|
|
13774
|
+
"### 何时使用此工具",
|
|
13775
|
+
"1. 子 Agent 执行方向错误,需要中止",
|
|
13776
|
+
"2. 用户需求变更,当前子任务不再需要",
|
|
13777
|
+
"3. 子 Agent 陷入死循环或异常状态",
|
|
13778
|
+
"4. 需要立即停止所有子任务以重新规划",
|
|
13779
|
+
"",
|
|
13780
|
+
"### 参数说明",
|
|
13781
|
+
"- **task_id**: 要停止的子 Agent 实例 ID(AgentTool 返回的 instanceId)",
|
|
13782
|
+
"",
|
|
13783
|
+
"### 使用示例",
|
|
13784
|
+
"停止指定任务: `{ \"task_id\": \"agent-xyz-123\" }`"
|
|
13785
|
+
].join("\n"),
|
|
13786
|
+
parameters: {
|
|
13787
|
+
type: "object",
|
|
13788
|
+
properties: { task_id: {
|
|
13789
|
+
type: "string",
|
|
13790
|
+
description: "要停止的子 Agent 实例 ID(AgentTool 返回结果中的 instanceId)"
|
|
13791
|
+
} },
|
|
13792
|
+
required: ["task_id"]
|
|
13793
|
+
},
|
|
13794
|
+
execute: async (_toolCallId, params) => {
|
|
13795
|
+
const { task_id } = params;
|
|
13796
|
+
const instanceManager = getAgentInstanceManager();
|
|
13797
|
+
const instance = instanceManager.get(task_id);
|
|
13798
|
+
if (!instance) return {
|
|
13799
|
+
content: [{
|
|
13800
|
+
type: "text",
|
|
13801
|
+
text: `⚠️ 未找到任务 \`${task_id}\``
|
|
13802
|
+
}],
|
|
13803
|
+
details: {
|
|
13804
|
+
taskId: task_id,
|
|
13805
|
+
found: false
|
|
13806
|
+
}
|
|
13807
|
+
};
|
|
13808
|
+
if (instance.status !== "running" && instance.status !== "idle") return {
|
|
13809
|
+
content: [{
|
|
13810
|
+
type: "text",
|
|
13811
|
+
text: `⚠️ 任务 \`${task_id}\` 当前状态为 \`${instance.status}\`,无法停止`
|
|
13812
|
+
}],
|
|
13813
|
+
details: {
|
|
13814
|
+
taskId: task_id,
|
|
13815
|
+
status: instance.status,
|
|
13816
|
+
stopped: false
|
|
13817
|
+
}
|
|
13818
|
+
};
|
|
13819
|
+
const cancelled = await instanceManager.cancel(task_id);
|
|
13820
|
+
return {
|
|
13821
|
+
content: [{
|
|
13822
|
+
type: "text",
|
|
13823
|
+
text: `✅ 任务 \`${task_id}\` 已停止`
|
|
13824
|
+
}],
|
|
13825
|
+
details: {
|
|
13826
|
+
taskId: task_id,
|
|
13827
|
+
stopped: true,
|
|
13828
|
+
cancelled: cancelled.length
|
|
13829
|
+
}
|
|
13830
|
+
};
|
|
13831
|
+
}
|
|
13832
|
+
};
|
|
13833
|
+
}
|
|
13834
|
+
|
|
13835
|
+
//#endregion
|
|
13836
|
+
//#region src/cli/repl/utils/animation-manager.ts
|
|
13837
|
+
/**
|
|
13838
|
+
* AnimationManager — 将 spinner/计时器动画从 setInterval 迁移到渲染周期驱动
|
|
13839
|
+
*
|
|
13840
|
+
* 原理:
|
|
13841
|
+
* 通过 monkey-patch pi-tui 的 doRender() 方法,在每次实际渲染前统一推进
|
|
13842
|
+
* 所有已注册的 spinner 帧,从而:
|
|
13843
|
+
* 1. 消除对 setInterval (macrotask) 的依赖,避免 Timer 阶段被事件循环饿死
|
|
13844
|
+
* 2. 帧推进与渲染同步,视觉上更流畅
|
|
13845
|
+
* 3. 减少事件循环中的定时器数量,降低调度开销
|
|
13846
|
+
*
|
|
13847
|
+
* 使用方式:
|
|
13848
|
+
* ```typescript
|
|
13849
|
+
* const anim = new AnimationManager();
|
|
13850
|
+
* anim.bind(tui);
|
|
13851
|
+
*
|
|
13852
|
+
* const unsub = anim.register((now) => {
|
|
13853
|
+
* // 根据 now 判断是否推进帧
|
|
13854
|
+
* frame = (frame + 1) % frames.length;
|
|
13855
|
+
* });
|
|
13856
|
+
* ```
|
|
13857
|
+
*/
|
|
13858
|
+
var AnimationManager = class {
|
|
13859
|
+
/** 已注册的帧推进回调 */
|
|
13860
|
+
#callbacks = /* @__PURE__ */ new Set();
|
|
13861
|
+
/** 绑定的 TUI 实例 */
|
|
13862
|
+
#tui = null;
|
|
13863
|
+
/** 备份的原始 doRender */
|
|
13864
|
+
#originalDoRender = null;
|
|
13865
|
+
/**
|
|
13866
|
+
* 注册一个帧推进回调。
|
|
13867
|
+
* 回调会在每次 doRender() 执行前被调用,传入当前 performance.now() 时间戳。
|
|
13868
|
+
* 回调应该根据时间戳自行控制帧推进频率(如每 100ms 推进一帧)。
|
|
13869
|
+
*
|
|
13870
|
+
* @returns 注销函数
|
|
13871
|
+
*/
|
|
13872
|
+
register(cb) {
|
|
13873
|
+
this.#callbacks.add(cb);
|
|
13874
|
+
return () => {
|
|
13875
|
+
this.#callbacks.delete(cb);
|
|
13876
|
+
};
|
|
13877
|
+
}
|
|
13878
|
+
/**
|
|
13879
|
+
* 绑定到 TUI 实例,monkey-patch 其 doRender 方法。
|
|
13880
|
+
* 如果已经绑定到另一个 TUI 实例,会先解除绑定。
|
|
13881
|
+
*/
|
|
13882
|
+
bind(tui) {
|
|
13883
|
+
if (this.#tui) this.unbind();
|
|
13884
|
+
this.#tui = tui;
|
|
13885
|
+
const tuiObj = tui;
|
|
13886
|
+
if (typeof tuiObj.doRender !== "function") return;
|
|
13887
|
+
this.#originalDoRender = tuiObj.doRender.bind(tui);
|
|
13888
|
+
const callbacks = this.#callbacks;
|
|
13889
|
+
const self = this;
|
|
13890
|
+
tuiObj.doRender = function() {
|
|
13891
|
+
const now = performance.now();
|
|
13892
|
+
for (const cb of callbacks) cb(now);
|
|
13893
|
+
self.#originalDoRender?.call(this);
|
|
13894
|
+
};
|
|
13895
|
+
}
|
|
13896
|
+
/**
|
|
13897
|
+
* 解除绑定,恢复 TUI 实例的原始 doRender。
|
|
13898
|
+
*/
|
|
13899
|
+
unbind() {
|
|
13900
|
+
if (this.#originalDoRender && this.#tui) this.#tui.doRender = this.#originalDoRender;
|
|
13901
|
+
this.#originalDoRender = null;
|
|
13902
|
+
this.#tui = null;
|
|
13903
|
+
}
|
|
13904
|
+
};
|
|
13905
|
+
|
|
13449
13906
|
//#endregion
|
|
13450
13907
|
//#region src/cli/repl/utils/markdown-formatter.ts
|
|
13451
13908
|
/**
|
|
@@ -13572,9 +14029,9 @@ function formatTable(token, colorEnabled) {
|
|
|
13572
14029
|
output += "┌";
|
|
13573
14030
|
for (let i = 0; i < columnCount; i++) {
|
|
13574
14031
|
const cw = colWidths[i] ?? 3;
|
|
13575
|
-
output += "─".repeat(cw + 2)
|
|
14032
|
+
output += `${"─".repeat(cw + 2)}┬`;
|
|
13576
14033
|
}
|
|
13577
|
-
output = output.slice(0, -1)
|
|
14034
|
+
output = `${output.slice(0, -1)}┐${EOL}`;
|
|
13578
14035
|
output += "│ ";
|
|
13579
14036
|
for (let i = 0; i < columnCount; i++) {
|
|
13580
14037
|
const cell = token.header[i];
|
|
@@ -13611,9 +14068,9 @@ function formatTable(token, colorEnabled) {
|
|
|
13611
14068
|
output += "└";
|
|
13612
14069
|
for (let i = 0; i < columnCount; i++) {
|
|
13613
14070
|
const cw = colWidths[i] ?? 3;
|
|
13614
|
-
output += "─".repeat(cw + 2)
|
|
14071
|
+
output += `${"─".repeat(cw + 2)}┴`;
|
|
13615
14072
|
}
|
|
13616
|
-
output = output.slice(0, -1)
|
|
14073
|
+
output = `${output.slice(0, -1)}┘${EOL}`;
|
|
13617
14074
|
return output + EOL;
|
|
13618
14075
|
}
|
|
13619
14076
|
/**
|
|
@@ -13680,6 +14137,93 @@ function normalizeMcpConfig(raw) {
|
|
|
13680
14137
|
return servers;
|
|
13681
14138
|
}
|
|
13682
14139
|
|
|
14140
|
+
//#endregion
|
|
14141
|
+
//#region src/core/agent-team/markdown-agent-parser.ts
|
|
14142
|
+
const log$10 = logger.child("markdown-agent-parser");
|
|
14143
|
+
|
|
14144
|
+
//#endregion
|
|
14145
|
+
//#region src/core/agent-team/agent-generator.ts
|
|
14146
|
+
const log$9 = logger.child("agent-generator");
|
|
14147
|
+
|
|
14148
|
+
//#endregion
|
|
14149
|
+
//#region src/core/agent-team/agent-memory.ts
|
|
14150
|
+
const log$8 = logger.child("agent-memory");
|
|
14151
|
+
/** 记忆根目录 */
|
|
14152
|
+
const AGENT_MEMORY_DIR = join(homedir(), ".zapmyco", "memory", "agents");
|
|
14153
|
+
|
|
14154
|
+
//#endregion
|
|
14155
|
+
//#region src/core/agent-team/coordinator-prompt.ts
|
|
14156
|
+
/**
|
|
14157
|
+
* 主 Agent Coordinator 模式系统提示词
|
|
14158
|
+
*
|
|
14159
|
+
* 当 agentTeam.defaultMode === 'coordinator' 时,
|
|
14160
|
+
* 主 REPL Agent 的 system prompt 被替换为此提示词,
|
|
14161
|
+
* 强制其专注于编排而非亲自执行。
|
|
14162
|
+
*
|
|
14163
|
+
* @module core/agent-team
|
|
14164
|
+
*/
|
|
14165
|
+
/**
|
|
14166
|
+
* 获取主 Agent Coordinator 模式系统提示词
|
|
14167
|
+
*
|
|
14168
|
+
* @param workdir - 当前工作目录
|
|
14169
|
+
* @returns 系统提示词文本
|
|
14170
|
+
*/
|
|
14171
|
+
function getMainCoordinatorSystemPrompt(workdir) {
|
|
14172
|
+
return [
|
|
14173
|
+
"你是一个团队协调者(Coordinator),负责编排多个专业子 Agent 协同完成复杂任务。",
|
|
14174
|
+
"",
|
|
14175
|
+
"## 核心原则",
|
|
14176
|
+
"**你绝对不能直接执行任何具体工作。** 你必须将所有任务委派给合适的子 Agent。",
|
|
14177
|
+
"不要使用 ReadFile、WriteFile、EditFile、Exec、Glob、Grep 等具体执行工具。",
|
|
14178
|
+
"你的工具仅限于 AgentTool、SendMessage、TaskStop 三个编排工具。",
|
|
14179
|
+
"",
|
|
14180
|
+
"## 核心职责",
|
|
14181
|
+
"- 分析用户任务并制定执行计划",
|
|
14182
|
+
"- 将复杂任务拆解为可独立执行的子任务",
|
|
14183
|
+
"- 为每个子任务匹配最合适的 Agent 类型",
|
|
14184
|
+
"- 协调子 Agent 执行顺序,处理依赖关系",
|
|
14185
|
+
"- 汇总所有子 Agent 的输出为统一结论",
|
|
14186
|
+
"",
|
|
14187
|
+
"## 可用 Agent 类型",
|
|
14188
|
+
"- **researcher**: 信息搜集、技术调研、文档检索(只有只读工具,不能修改文件)",
|
|
14189
|
+
"- **coder**: 代码生成与修改(有读写工具和 Shell 权限)",
|
|
14190
|
+
"- **reviewer**: 代码审查、质量检查(只有只读工具)",
|
|
14191
|
+
"- **planner**: 方案设计与架构规划(有标准工具集,可派生子 Agent 调研)",
|
|
14192
|
+
"- **general-purpose**: 通用任务执行(有标准工具集)",
|
|
14193
|
+
"",
|
|
14194
|
+
"## 可用工具",
|
|
14195
|
+
"- **AgentTool**: 创建并派遣子 Agent(使用 subagent_type 指定类型)",
|
|
14196
|
+
" - 同步模式(默认): 等待子 Agent 完成后获取完整结果",
|
|
14197
|
+
" - 异步模式(run_in_background=true): 后台执行,立即返回 taskId,完成后自动通知",
|
|
14198
|
+
"- **SendMessage**: 向子 Agent 发送后续指令、回答问题或调整方向(使用其 agent ID 作为 to 参数)",
|
|
14199
|
+
"- **TaskStop**: 停止运行中的子 Agent",
|
|
14200
|
+
"",
|
|
14201
|
+
"## 工作流程",
|
|
14202
|
+
"1. **分析任务**: 理解用户需求,明确目标和约束条件",
|
|
14203
|
+
"2. **拆解子任务**: 将复杂任务分解为可并行或串行执行的独立子任务",
|
|
14204
|
+
"3. **匹配类型**: 为每个子任务选择最合适的 Agent 类型",
|
|
14205
|
+
"4. **派发执行**: 调用 AgentTool 创建子 Agent(无依赖的子任务应并行派发)",
|
|
14206
|
+
"5. **汇总结果**: 整合所有子 Agent 的输出,向用户汇报结论",
|
|
14207
|
+
"",
|
|
14208
|
+
"## 工作规则",
|
|
14209
|
+
"- **绝不亲自执行**: 绝不使用 ReadFile、WriteFile、Exec、Glob、Grep、WebFetch、WebSearch 等直接执行工具",
|
|
14210
|
+
"- **并行优先**: 无依赖的子任务应同时派发以提升效率",
|
|
14211
|
+
"- **先规划后执行**: 在派发子 Agent 之前先制定清晰的执行计划",
|
|
14212
|
+
"- **监控进度**: 通过 SendMessage 接收子 Agent 的进度汇报和提问",
|
|
14213
|
+
"- **务实汇总**: 整合结果时保持客观,不添加未经验证的信息",
|
|
14214
|
+
"- **后台任务**: 长时间运行的任务使用 run_in_background=true,任务完成后会自动收到通知",
|
|
14215
|
+
"- **通知处理**: 当收到后台任务完成的通知消息时,汇总所有结果后一并回复用户",
|
|
14216
|
+
"",
|
|
14217
|
+
`## 工作目录\n${workdir}`,
|
|
14218
|
+
"",
|
|
14219
|
+
"> 记住:你是协调者,不是执行者。你的价值在于编排和整合,而不是亲自干活。"
|
|
14220
|
+
].join("\n");
|
|
14221
|
+
}
|
|
14222
|
+
|
|
14223
|
+
//#endregion
|
|
14224
|
+
//#region src/core/agent-team/user-agent-loader.ts
|
|
14225
|
+
const log$7 = logger.child("user-agent-loader");
|
|
14226
|
+
|
|
13683
14227
|
//#endregion
|
|
13684
14228
|
//#region src/core/lsp/diagnostics.ts
|
|
13685
14229
|
function createDiagnosticCollector() {
|
|
@@ -13942,10 +14486,10 @@ function createLspClient(config) {
|
|
|
13942
14486
|
const reader = createMessageReader(handleMessage, (err) => trace("←", `Reader error: ${err.message}`), () => {
|
|
13943
14487
|
if (!isStopping) rejectAllPending(new LspError("LSP stdout closed", "STDOUT_CLOSED"));
|
|
13944
14488
|
}, (msg) => trace("←", msg));
|
|
13945
|
-
childProcess.stdout
|
|
14489
|
+
childProcess.stdout?.on("data", (chunk) => {
|
|
13946
14490
|
reader.feed(chunk);
|
|
13947
14491
|
});
|
|
13948
|
-
childProcess.stdout
|
|
14492
|
+
childProcess.stdout?.on("end", () => {
|
|
13949
14493
|
if (!isStopping) rejectAllPending(new LspError("LSP stdout closed unexpectedly", "STDOUT_CLOSED"));
|
|
13950
14494
|
});
|
|
13951
14495
|
messageWriter = createMessageWriter(childProcess.stdin, (msg) => trace("→", msg));
|
|
@@ -13999,7 +14543,7 @@ function createLspClient(config) {
|
|
|
13999
14543
|
timer,
|
|
14000
14544
|
method
|
|
14001
14545
|
});
|
|
14002
|
-
messageWriter
|
|
14546
|
+
messageWriter?.write(request).catch((err) => {
|
|
14003
14547
|
clearTimeout(timer);
|
|
14004
14548
|
pendingRequests.delete(id);
|
|
14005
14549
|
reject(err);
|
|
@@ -14039,19 +14583,19 @@ function createLspClient(config) {
|
|
|
14039
14583
|
}
|
|
14040
14584
|
async function shutdown() {
|
|
14041
14585
|
if (!childProcess || isStopping) return;
|
|
14042
|
-
isStopping = true;
|
|
14043
14586
|
try {
|
|
14044
14587
|
await sendRequest("shutdown");
|
|
14045
14588
|
} catch {}
|
|
14046
14589
|
try {
|
|
14047
14590
|
await sendNotification("exit");
|
|
14048
14591
|
} catch {}
|
|
14592
|
+
isStopping = true;
|
|
14049
14593
|
await new Promise((resolve) => {
|
|
14050
14594
|
const timer = setTimeout(() => {
|
|
14051
14595
|
if (childProcess && !childProcess.killed) childProcess.kill("SIGKILL");
|
|
14052
14596
|
resolve();
|
|
14053
14597
|
}, 2e3);
|
|
14054
|
-
childProcess
|
|
14598
|
+
childProcess?.once("exit", () => {
|
|
14055
14599
|
clearTimeout(timer);
|
|
14056
14600
|
resolve();
|
|
14057
14601
|
});
|
|
@@ -14109,7 +14653,7 @@ function createLspServerInstance(config) {
|
|
|
14109
14653
|
if (openedDocuments.get(uri)) return;
|
|
14110
14654
|
await ensureRunning();
|
|
14111
14655
|
try {
|
|
14112
|
-
await client
|
|
14656
|
+
await client?.sendNotification("textDocument/didOpen", { textDocument: {
|
|
14113
14657
|
uri,
|
|
14114
14658
|
languageId,
|
|
14115
14659
|
version: 1,
|
|
@@ -14181,6 +14725,7 @@ function createLspServerInstance(config) {
|
|
|
14181
14725
|
async function request(method, params) {
|
|
14182
14726
|
await ensureRunning();
|
|
14183
14727
|
requestCount++;
|
|
14728
|
+
if (!client) throw new LspError("CLIENT_NOT_INITIALIZED", "LSP 客户端未初始化");
|
|
14184
14729
|
try {
|
|
14185
14730
|
return await client.sendRequest(method, params);
|
|
14186
14731
|
} catch (err) {
|
|
@@ -14533,7 +15078,7 @@ function withTimeout(promise, ms) {
|
|
|
14533
15078
|
/**
|
|
14534
15079
|
* 将单个 MCP Tool 转换为 zapmyco ToolRegistration
|
|
14535
15080
|
*
|
|
14536
|
-
* MCP SDK 的 TextContent / ImageContent
|
|
15081
|
+
* MCP SDK 的 TextContent / ImageContent 与本地类型结构兼容
|
|
14537
15082
|
* (都是 { type, text/data, mimeType }),因此直接透传 content。
|
|
14538
15083
|
*/
|
|
14539
15084
|
function mcpToolToRegistration(mcpTool, serverName, client) {
|
|
@@ -15085,7 +15630,7 @@ var AuditLogger = class {
|
|
|
15085
15630
|
const entries = this.buffer.splice(0);
|
|
15086
15631
|
try {
|
|
15087
15632
|
this.rotateIfNeeded();
|
|
15088
|
-
const lines = entries.map((e) => JSON.stringify(e)).join("\n")
|
|
15633
|
+
const lines = `${entries.map((e) => JSON.stringify(e)).join("\n")}\n`;
|
|
15089
15634
|
appendFileSync(this.filePath, lines, "utf-8");
|
|
15090
15635
|
} catch (err) {
|
|
15091
15636
|
log$3.error("审计日志写入失败", { error: err instanceof Error ? err.message : String(err) });
|
|
@@ -15189,7 +15734,7 @@ function resolveConfig(config = {}) {
|
|
|
15189
15734
|
function matchToolPattern(pattern, toolId) {
|
|
15190
15735
|
if (pattern === toolId) return true;
|
|
15191
15736
|
if (pattern === "*") return true;
|
|
15192
|
-
return new RegExp(
|
|
15737
|
+
return new RegExp(`^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`).test(toolId);
|
|
15193
15738
|
}
|
|
15194
15739
|
/**
|
|
15195
15740
|
* 参数模式匹配
|
|
@@ -15852,7 +16397,7 @@ function threatSeverity(level) {
|
|
|
15852
16397
|
/**
|
|
15853
16398
|
* REPL 会话核心(pi-tui 版)
|
|
15854
16399
|
*
|
|
15855
|
-
* 使用 @
|
|
16400
|
+
* 使用 @earendil-works/pi-tui 框架替代 readline,
|
|
15856
16401
|
* 实现完整的 TUI 交互式 REPL:
|
|
15857
16402
|
* - Editor 组件自带上下边框
|
|
15858
16403
|
* - 差量渲染,无闪烁
|
|
@@ -15880,9 +16425,24 @@ function getApiKeyErrorHelp(errorMessage) {
|
|
|
15880
16425
|
*/
|
|
15881
16426
|
var OutputArea = class extends Container {
|
|
15882
16427
|
lines = [];
|
|
16428
|
+
/** 逐行缓存的渲染结果(避免每帧对所有历史行重新调用 wrapTextWithAnsi) */
|
|
16429
|
+
lineCache = /* @__PURE__ */ new Map();
|
|
16430
|
+
/** 缓存生成时使用的终端宽度,变化时重建 */
|
|
16431
|
+
cacheWidth = 0;
|
|
15883
16432
|
render(width) {
|
|
16433
|
+
if (width !== this.cacheWidth) {
|
|
16434
|
+
this.cacheWidth = width;
|
|
16435
|
+
this.lineCache.clear();
|
|
16436
|
+
}
|
|
15884
16437
|
const result = [];
|
|
15885
|
-
for (
|
|
16438
|
+
for (let i = 0; i < this.lines.length; i++) {
|
|
16439
|
+
let cached = this.lineCache.get(i);
|
|
16440
|
+
if (!cached) {
|
|
16441
|
+
cached = wrapTextWithAnsi(this.lines[i] ?? "", width);
|
|
16442
|
+
this.lineCache.set(i, cached);
|
|
16443
|
+
}
|
|
16444
|
+
result.push(...cached);
|
|
16445
|
+
}
|
|
15886
16446
|
return result;
|
|
15887
16447
|
}
|
|
15888
16448
|
/** 追加多行内容,返回新追加行中第一行的索引 */
|
|
@@ -15895,33 +16455,45 @@ var OutputArea = class extends Container {
|
|
|
15895
16455
|
/** 追加文本到当前行末尾(用于流式输出) */
|
|
15896
16456
|
appendText(text) {
|
|
15897
16457
|
if (this.lines.length === 0) this.lines.push(text);
|
|
15898
|
-
else
|
|
16458
|
+
else {
|
|
16459
|
+
this.lines[this.lines.length - 1] += text;
|
|
16460
|
+
this.lineCache.delete(this.lines.length - 1);
|
|
16461
|
+
}
|
|
15899
16462
|
this.invalidate();
|
|
15900
16463
|
}
|
|
15901
16464
|
/** 替换最后一行的完整内容(用于 spinner 动画和首 chunk 替换),返回行索引 */
|
|
15902
16465
|
replaceLastLine(text) {
|
|
15903
|
-
if (this.lines.length > 0)
|
|
15904
|
-
|
|
16466
|
+
if (this.lines.length > 0) {
|
|
16467
|
+
this.lines[this.lines.length - 1] = text;
|
|
16468
|
+
this.lineCache.delete(this.lines.length - 1);
|
|
16469
|
+
} else this.lines.push(text);
|
|
15905
16470
|
this.invalidate();
|
|
15906
16471
|
return this.lines.length - 1;
|
|
15907
16472
|
}
|
|
15908
16473
|
/** 在指定位置插入/删除/替换行(原子操作) */
|
|
15909
16474
|
spliceLines(startIndex, deleteCount, insertLines) {
|
|
15910
16475
|
this.lines.splice(startIndex, deleteCount, ...insertLines);
|
|
16476
|
+
this.invalidateCacheFrom(startIndex);
|
|
15911
16477
|
this.invalidate();
|
|
15912
16478
|
}
|
|
15913
16479
|
/** 更新指定索引行的内容 */
|
|
15914
16480
|
updateLine(index, text) {
|
|
15915
16481
|
if (index >= 0 && index < this.lines.length) {
|
|
15916
16482
|
this.lines[index] = text;
|
|
16483
|
+
this.lineCache.delete(index);
|
|
15917
16484
|
this.invalidate();
|
|
15918
16485
|
}
|
|
15919
16486
|
}
|
|
15920
16487
|
/** 清空所有内容 */
|
|
15921
16488
|
clear() {
|
|
15922
16489
|
this.lines = [];
|
|
16490
|
+
this.lineCache.clear();
|
|
15923
16491
|
this.invalidate();
|
|
15924
16492
|
}
|
|
16493
|
+
/** 使从指定索引开始的所有缓存行失效 */
|
|
16494
|
+
invalidateCacheFrom(startIndex) {
|
|
16495
|
+
for (const key of this.lineCache.keys()) if (key >= startIndex) this.lineCache.delete(key);
|
|
16496
|
+
}
|
|
15925
16497
|
};
|
|
15926
16498
|
/** 默认提示符(用于显示/格式化) */
|
|
15927
16499
|
const DEFAULT_PROMPT = "❯ ";
|
|
@@ -15945,6 +16517,8 @@ var ReplSession = class {
|
|
|
15945
16517
|
renderer;
|
|
15946
16518
|
history;
|
|
15947
16519
|
currentTaskAbort = null;
|
|
16520
|
+
/** 动画管理器(渲染周期驱动的 spinner/计时器,替代 setInterval) */
|
|
16521
|
+
animationManager = new AnimationManager();
|
|
15948
16522
|
/** 欢迎语逐字输出定时器引用(用于 shutdown 时清理) */
|
|
15949
16523
|
welcomeTypewriterInterval;
|
|
15950
16524
|
/** Agent 实例(会话级复用,替代直接 LLM 调用) */
|
|
@@ -15997,6 +16571,7 @@ var ReplSession = class {
|
|
|
15997
16571
|
const theme = createTheme(this.options.color);
|
|
15998
16572
|
const terminal = new ProcessTerminal();
|
|
15999
16573
|
this.tui = new TUI(terminal);
|
|
16574
|
+
this.animationManager.bind(this.tui);
|
|
16000
16575
|
getKeybindings().setUserBindings({
|
|
16001
16576
|
"tui.select.up": ["up"],
|
|
16002
16577
|
"tui.select.down": ["down"],
|
|
@@ -16005,13 +16580,13 @@ var ReplSession = class {
|
|
|
16005
16580
|
});
|
|
16006
16581
|
this.taskStore = new TaskStore();
|
|
16007
16582
|
this.outputArea = new OutputArea();
|
|
16008
|
-
this.agentStatusBar = new AgentStatusBar();
|
|
16009
|
-
this.taskStatusBar = new TaskStatusBar(this.taskStore);
|
|
16583
|
+
this.agentStatusBar = new AgentStatusBar(this.animationManager);
|
|
16584
|
+
this.taskStatusBar = new TaskStatusBar(this.taskStore, this.animationManager);
|
|
16010
16585
|
this.taskStore.onChange(() => {
|
|
16011
16586
|
this.taskStatusBar.onTasksChanged();
|
|
16012
16587
|
this.tui.requestRender();
|
|
16013
16588
|
});
|
|
16014
|
-
this.editor = new ZapmycoEditor(this.tui, theme.editorTheme);
|
|
16589
|
+
this.editor = new ZapmycoEditor(this.tui, theme.editorTheme, this.animationManager);
|
|
16015
16590
|
const root = new Container();
|
|
16016
16591
|
root.addChild(this.outputArea);
|
|
16017
16592
|
root.addChild(this.taskStatusBar);
|
|
@@ -16130,7 +16705,7 @@ var ReplSession = class {
|
|
|
16130
16705
|
* 彻底清空 Agent 会话上下文。
|
|
16131
16706
|
*
|
|
16132
16707
|
* - 清空消息历史、Token 统计、缓存、授权
|
|
16133
|
-
* -
|
|
16708
|
+
* - 保留配置、记忆、定时任务
|
|
16134
16709
|
* - 效果等价于"新开一个会话"
|
|
16135
16710
|
*
|
|
16136
16711
|
* 由 /clear 命令调用。
|
|
@@ -16153,6 +16728,7 @@ var ReplSession = class {
|
|
|
16153
16728
|
this.questionManager.rejectAll(/* @__PURE__ */ new Error("会话已重置"));
|
|
16154
16729
|
this.editor.setExecuting(false);
|
|
16155
16730
|
this.outputArea.clear();
|
|
16731
|
+
this.taskStore.clear();
|
|
16156
16732
|
this.agentStatusBar.clearTokenStats();
|
|
16157
16733
|
this.tui.requestRender();
|
|
16158
16734
|
}
|
|
@@ -16257,8 +16833,70 @@ var ReplSession = class {
|
|
|
16257
16833
|
const execFailStyle = (s) => colorEnabled ? chalk.red(s) : s;
|
|
16258
16834
|
const dimStyle = (s) => colorEnabled ? chalk.gray(s) : s;
|
|
16259
16835
|
let spinnerActive = true;
|
|
16260
|
-
|
|
16261
|
-
let
|
|
16836
|
+
/** 动画管理器帧推进计时(spinner,100ms间隔) */
|
|
16837
|
+
let lastSpinnerTick = 0;
|
|
16838
|
+
/** thinking 计时动画活跃标志(替代 thinkingElapsedInterval setInterval) */
|
|
16839
|
+
let thinkingTimerActive = false;
|
|
16840
|
+
let thinkingFrame = 0;
|
|
16841
|
+
let lastThinkingTick = 0;
|
|
16842
|
+
/** AnimationManager 回调注销函数集合(finally 中统一清理) */
|
|
16843
|
+
const animCleanup = [];
|
|
16844
|
+
/**
|
|
16845
|
+
* 合并渲染请求——在同一个微任务内的多次调用合并为一次 requestRender。
|
|
16846
|
+
* 流式事件(OUTPUT/THINKING/PROGRESS)短时间密集到达时,避免为每个事件
|
|
16847
|
+
* 都调用 requestRender(即使 pi-tui 内部有 16ms 去抖,仍可减少调度开销)。
|
|
16848
|
+
*/
|
|
16849
|
+
let coalesceRenderPending = false;
|
|
16850
|
+
const requestCoalescedRender = () => {
|
|
16851
|
+
if (coalesceRenderPending) return;
|
|
16852
|
+
coalesceRenderPending = true;
|
|
16853
|
+
queueMicrotask(() => {
|
|
16854
|
+
coalesceRenderPending = false;
|
|
16855
|
+
this.tui.requestRender();
|
|
16856
|
+
});
|
|
16857
|
+
};
|
|
16858
|
+
const mainAgentDef = this.isCoordinatorMode() ? {
|
|
16859
|
+
typeId: "repl-coordinator",
|
|
16860
|
+
displayName: "协调者",
|
|
16861
|
+
whenToUse: "Main REPL coordinator",
|
|
16862
|
+
role: "coordinator",
|
|
16863
|
+
capabilities: [{
|
|
16864
|
+
id: "agent-orchestration",
|
|
16865
|
+
name: "Agent 编排",
|
|
16866
|
+
description: "编排多个子 Agent 协同完成复杂任务",
|
|
16867
|
+
category: "planning"
|
|
16868
|
+
}],
|
|
16869
|
+
toolPolicy: { mode: "inherit" },
|
|
16870
|
+
permissionMode: "inherit",
|
|
16871
|
+
source: "builtin",
|
|
16872
|
+
maxTurns: 100,
|
|
16873
|
+
maxSpawnDepth: 2,
|
|
16874
|
+
getSystemPrompt: () => "",
|
|
16875
|
+
color: "#e67e22"
|
|
16876
|
+
} : {
|
|
16877
|
+
typeId: "Explore",
|
|
16878
|
+
displayName: "Explore Agent",
|
|
16879
|
+
whenToUse: "Main REPL agent",
|
|
16880
|
+
role: "universal",
|
|
16881
|
+
capabilities: [],
|
|
16882
|
+
toolPolicy: { mode: "inherit" },
|
|
16883
|
+
permissionMode: "inherit",
|
|
16884
|
+
source: "builtin",
|
|
16885
|
+
maxTurns: 100,
|
|
16886
|
+
maxSpawnDepth: 0,
|
|
16887
|
+
getSystemPrompt: () => "",
|
|
16888
|
+
color: "#3498db"
|
|
16889
|
+
};
|
|
16890
|
+
const mainAgent = this.agent;
|
|
16891
|
+
const instanceManager = getAgentInstanceManager();
|
|
16892
|
+
const mainInstance = instanceManager.register(mainAgentDef, mainAgent, {
|
|
16893
|
+
taskId,
|
|
16894
|
+
description: rawInput.slice(0, 200),
|
|
16895
|
+
mode: "sync",
|
|
16896
|
+
timeoutMs: this.config.scheduler.taskTimeoutMs,
|
|
16897
|
+
inheritContext: false
|
|
16898
|
+
}, null, 0);
|
|
16899
|
+
instanceManager.transition(mainInstance.instanceId, "running");
|
|
16262
16900
|
/** Token 信息推送定时器 */
|
|
16263
16901
|
let tokenPushInterval;
|
|
16264
16902
|
try {
|
|
@@ -16277,12 +16915,13 @@ var ReplSession = class {
|
|
|
16277
16915
|
this.outputArea.append([userStyle(displayLabel ?? rawInput), LOADING_FRAMES$1[0] ?? ""]);
|
|
16278
16916
|
let spinnerFrame = 0;
|
|
16279
16917
|
spinnerActive = true;
|
|
16280
|
-
|
|
16918
|
+
animCleanup.push(this.animationManager.register((now) => {
|
|
16281
16919
|
if (!spinnerActive) return;
|
|
16920
|
+
if (now - lastSpinnerTick < 100) return;
|
|
16921
|
+
lastSpinnerTick = now;
|
|
16282
16922
|
spinnerFrame = (spinnerFrame + 1) % LOADING_FRAMES$1.length;
|
|
16283
16923
|
this.outputArea.replaceLastLine(LOADING_FRAMES$1[spinnerFrame] ?? "");
|
|
16284
|
-
|
|
16285
|
-
}, 100);
|
|
16924
|
+
}));
|
|
16286
16925
|
let spinnerStopped = false;
|
|
16287
16926
|
let outputAccumulator = "";
|
|
16288
16927
|
let thinkingAccumulator = "";
|
|
@@ -16327,20 +16966,20 @@ var ReplSession = class {
|
|
|
16327
16966
|
};
|
|
16328
16967
|
const outputHandler = (event) => {
|
|
16329
16968
|
if (event.taskId !== taskId || !event.text) return;
|
|
16969
|
+
if (hasExecutedTool) {
|
|
16970
|
+
hasExecutedTool = false;
|
|
16971
|
+
instanceManager.transition(mainInstance.instanceId, "completed");
|
|
16972
|
+
}
|
|
16330
16973
|
if (!spinnerStopped) {
|
|
16331
16974
|
spinnerStopped = true;
|
|
16332
16975
|
spinnerActive = false;
|
|
16333
|
-
clearInterval(spinnerInterval);
|
|
16334
16976
|
streamMode = "response";
|
|
16335
16977
|
thinkingAccumulator = "";
|
|
16336
16978
|
outputAccumulator = event.text;
|
|
16337
16979
|
responseLineIndex = this.outputArea.replaceLastLine(responseStyle(outputAccumulator));
|
|
16338
16980
|
} else if (streamMode !== "response") {
|
|
16339
16981
|
streamMode = "response";
|
|
16340
|
-
|
|
16341
|
-
clearInterval(thinkingElapsedInterval);
|
|
16342
|
-
thinkingElapsedInterval = void 0;
|
|
16343
|
-
}
|
|
16982
|
+
thinkingTimerActive = false;
|
|
16344
16983
|
thinkingAccumulator = "";
|
|
16345
16984
|
outputAccumulator = event.text;
|
|
16346
16985
|
if (thinkingHeaderLineIndex !== null) {
|
|
@@ -16352,7 +16991,7 @@ var ReplSession = class {
|
|
|
16352
16991
|
outputAccumulator += event.text;
|
|
16353
16992
|
this.outputArea.replaceLastLine(responseStyle(outputAccumulator));
|
|
16354
16993
|
}
|
|
16355
|
-
|
|
16994
|
+
requestCoalescedRender();
|
|
16356
16995
|
};
|
|
16357
16996
|
const thinkingHandler = (event) => {
|
|
16358
16997
|
if (event.taskId !== taskId || !event.text) return;
|
|
@@ -16363,7 +17002,6 @@ var ReplSession = class {
|
|
|
16363
17002
|
if (!spinnerStopped) {
|
|
16364
17003
|
spinnerStopped = true;
|
|
16365
17004
|
spinnerActive = false;
|
|
16366
|
-
clearInterval(spinnerInterval);
|
|
16367
17005
|
thinkingHeaderLineIndex = this.outputArea.replaceLastLine(dimStyle(` ${LOADING_FRAMES$1[0] ?? ""} Thinking...`));
|
|
16368
17006
|
} else thinkingHeaderLineIndex = this.outputArea.append([dimStyle(` ${LOADING_FRAMES$1[0] ?? ""} Thinking...`)]);
|
|
16369
17007
|
thinkingAccumulator = event.text;
|
|
@@ -16372,16 +17010,19 @@ var ReplSession = class {
|
|
|
16372
17010
|
this.outputArea.append([dimStyle(THINKING_CONTINUE_PREFIX + thinkingAccumulator)]);
|
|
16373
17011
|
thinkingContentLineCount = 1;
|
|
16374
17012
|
} else if (thinkingDisplayMode === "collapse") {
|
|
16375
|
-
|
|
16376
|
-
|
|
17013
|
+
thinkingTimerActive = true;
|
|
17014
|
+
thinkingFrame = 0;
|
|
17015
|
+
animCleanup.push(this.animationManager.register((now) => {
|
|
17016
|
+
if (!thinkingTimerActive) return;
|
|
16377
17017
|
if (thinkingHeaderLineIndex === null) return;
|
|
16378
17018
|
if (thinkingContentLineCount > 0) return;
|
|
17019
|
+
if (now - lastThinkingTick < 200) return;
|
|
17020
|
+
lastThinkingTick = now;
|
|
16379
17021
|
const elapsed = ((Date.now() - thinkingStartTimeMs) / 1e3).toFixed(1);
|
|
16380
17022
|
const frame = LOADING_FRAMES$1[thinkingFrame % LOADING_FRAMES$1.length];
|
|
16381
17023
|
thinkingFrame++;
|
|
16382
17024
|
this.outputArea.updateLine(thinkingHeaderLineIndex, dimStyle(` ${frame} Thinking... (${elapsed}s)`));
|
|
16383
|
-
|
|
16384
|
-
}, 200);
|
|
17025
|
+
}));
|
|
16385
17026
|
}
|
|
16386
17027
|
} else {
|
|
16387
17028
|
thinkingAccumulator += event.text;
|
|
@@ -16392,38 +17033,87 @@ var ReplSession = class {
|
|
|
16392
17033
|
this.outputArea.replaceLastLine(dimStyle(THINKING_CONTINUE_PREFIX + lastLine));
|
|
16393
17034
|
}
|
|
16394
17035
|
}
|
|
16395
|
-
|
|
17036
|
+
requestCoalescedRender();
|
|
16396
17037
|
};
|
|
16397
17038
|
const errorHandler = (event) => {
|
|
16398
17039
|
if (event.taskId === taskId) log.error("Agent 执行中收到 error 事件", { error: event.error.message });
|
|
16399
17040
|
};
|
|
16400
17041
|
let execLineIndex;
|
|
16401
17042
|
let execMessage;
|
|
17043
|
+
/** 是否执行过工具调用(用于判断输出阶段时是否结束 agent 状态) */
|
|
17044
|
+
let hasExecutedTool = false;
|
|
17045
|
+
/** 连续同类型工具调用的分组跟踪(用于 OutputArea 展示 ⎿ 分组) */
|
|
17046
|
+
let lastToolCategory = "";
|
|
17047
|
+
let lastToolLabel = "";
|
|
17048
|
+
let toolGroupCount = 0;
|
|
17049
|
+
let toolGroupLineIndex = null;
|
|
17050
|
+
let toolGroupSampleArg = "";
|
|
16402
17051
|
const progressHandler = (event) => {
|
|
16403
17052
|
if (event.taskId !== taskId) return;
|
|
17053
|
+
if (event.detail?.isStart) instanceManager.recordToolCall(mainInstance.instanceId, {
|
|
17054
|
+
toolName: event.detail.toolName,
|
|
17055
|
+
toolCallId: event.detail.toolCallId,
|
|
17056
|
+
argsDisplay: event.detail.argsDisplay,
|
|
17057
|
+
status: "running",
|
|
17058
|
+
startedAt: Date.now()
|
|
17059
|
+
});
|
|
17060
|
+
else if (event.detail?.isEnd) {
|
|
17061
|
+
const mainInst = instanceManager.get(mainInstance.instanceId);
|
|
17062
|
+
if (mainInst) {
|
|
17063
|
+
const lastRunning = [...mainInst.toolCallHistory].reverse().find((t) => t.status === "running" && t.toolName === event.detail?.toolName);
|
|
17064
|
+
if (lastRunning) {
|
|
17065
|
+
lastRunning.status = event.detail.isError ? "failed" : "completed";
|
|
17066
|
+
lastRunning.endedAt = Date.now();
|
|
17067
|
+
}
|
|
17068
|
+
}
|
|
17069
|
+
}
|
|
16404
17070
|
if (event.percent === 0) {
|
|
16405
|
-
if (
|
|
16406
|
-
|
|
16407
|
-
thinkingElapsedInterval = void 0;
|
|
17071
|
+
if (thinkingTimerActive && streamMode === "thinking") {
|
|
17072
|
+
thinkingTimerActive = false;
|
|
16408
17073
|
if (thinkingHeaderLineIndex !== null) {
|
|
16409
17074
|
this.outputArea.spliceLines(thinkingHeaderLineIndex, 1, []);
|
|
16410
17075
|
thinkingHeaderLineIndex = null;
|
|
16411
|
-
|
|
17076
|
+
requestCoalescedRender();
|
|
16412
17077
|
}
|
|
16413
17078
|
}
|
|
17079
|
+
hasExecutedTool = true;
|
|
16414
17080
|
if (event.message.startsWith("Exec(")) {
|
|
16415
17081
|
execMessage = event.message;
|
|
16416
17082
|
execLineIndex = this.outputArea.append([execStyle(` ⏺ ${event.message}`)]);
|
|
16417
|
-
|
|
16418
|
-
|
|
16419
|
-
|
|
17083
|
+
lastToolCategory = "";
|
|
17084
|
+
toolGroupCount = 0;
|
|
17085
|
+
toolGroupLineIndex = null;
|
|
17086
|
+
} else if (event.message.startsWith("TaskManage(")) {
|
|
17087
|
+
log.debug("TaskManage 工具调用已记录,跳过 TUI 展示");
|
|
17088
|
+
lastToolCategory = "";
|
|
17089
|
+
toolGroupCount = 0;
|
|
17090
|
+
toolGroupLineIndex = null;
|
|
17091
|
+
} else if (event.detail?.toolName) {
|
|
17092
|
+
const { category, label } = categorizeTool(event.detail.toolName);
|
|
17093
|
+
const argsPart = event.detail.argsDisplay ?? "";
|
|
17094
|
+
if (category === lastToolCategory && toolGroupLineIndex !== null) {
|
|
17095
|
+
toolGroupCount++;
|
|
17096
|
+
const sampleDisplay = toolGroupSampleArg ? ` (e.g. ${toolGroupSampleArg.slice(0, 50)})` : "";
|
|
17097
|
+
const groupedText = toolStyle(` ⎿ ${lastToolLabel} ${toolGroupCount} items${sampleDisplay}`);
|
|
17098
|
+
this.outputArea.updateLine(toolGroupLineIndex, groupedText);
|
|
17099
|
+
} else {
|
|
17100
|
+
lastToolCategory = category;
|
|
17101
|
+
lastToolLabel = label;
|
|
17102
|
+
toolGroupCount = 1;
|
|
17103
|
+
toolGroupSampleArg = argsPart;
|
|
17104
|
+
const sampleDisplay = argsPart ? ` (e.g. ${argsPart.slice(0, 50)})` : "";
|
|
17105
|
+
const lineText = toolStyle(` ⎿ ${label} ${toolGroupCount} item${sampleDisplay}`);
|
|
17106
|
+
toolGroupLineIndex = this.outputArea.append([lineText]);
|
|
17107
|
+
}
|
|
17108
|
+
} else this.outputArea.append([toolStyle(` → ${event.message}`)]);
|
|
17109
|
+
requestCoalescedRender();
|
|
16420
17110
|
} else if (event.percent === 100 && execLineIndex !== void 0 && execMessage) {
|
|
16421
17111
|
if (event.message.startsWith("工具 Exec")) {
|
|
16422
17112
|
const style = event.message.includes("完成") ? execSuccessStyle : execFailStyle;
|
|
16423
17113
|
this.outputArea.updateLine(execLineIndex, style(` ⏺ ${execMessage}`));
|
|
16424
17114
|
execLineIndex = void 0;
|
|
16425
17115
|
execMessage = void 0;
|
|
16426
|
-
|
|
17116
|
+
requestCoalescedRender();
|
|
16427
17117
|
}
|
|
16428
17118
|
}
|
|
16429
17119
|
};
|
|
@@ -16447,9 +17137,32 @@ var ReplSession = class {
|
|
|
16447
17137
|
taskId,
|
|
16448
17138
|
taskDescription: rawInput.slice(0, 100)
|
|
16449
17139
|
});
|
|
17140
|
+
let effectiveTaskDescription = rawInput;
|
|
17141
|
+
if (this.isCoordinatorMode()) {
|
|
17142
|
+
const pendingMessages = getAgentMessageBus().drainInbox("repl-chat-agent");
|
|
17143
|
+
if (pendingMessages.length > 0) effectiveTaskDescription = [
|
|
17144
|
+
"## 以下后台任务已完成",
|
|
17145
|
+
...pendingMessages.map((msg) => {
|
|
17146
|
+
try {
|
|
17147
|
+
const parsed = JSON.parse(msg.payload);
|
|
17148
|
+
if (parsed.type === "task_result") return [
|
|
17149
|
+
`[任务完成通知]`,
|
|
17150
|
+
`任务 ID: ${parsed.taskId}`,
|
|
17151
|
+
`类型: ${parsed.typeId}`,
|
|
17152
|
+
`状态: ${parsed.status}`,
|
|
17153
|
+
parsed.summary ? `\n结果:\n${parsed.summary}` : "",
|
|
17154
|
+
parsed.error ? `错误: ${parsed.error}` : ""
|
|
17155
|
+
].join("\n");
|
|
17156
|
+
} catch {}
|
|
17157
|
+
return `[消息 - ${msg.fromAgentId}]\n${msg.payload}`;
|
|
17158
|
+
}),
|
|
17159
|
+
"---",
|
|
17160
|
+
rawInput
|
|
17161
|
+
].join("\n\n");
|
|
17162
|
+
}
|
|
16450
17163
|
const taskResult = await this.agent.execute({
|
|
16451
17164
|
taskId,
|
|
16452
|
-
taskDescription:
|
|
17165
|
+
taskDescription: effectiveTaskDescription,
|
|
16453
17166
|
workdir: process.cwd(),
|
|
16454
17167
|
options: {
|
|
16455
17168
|
timeout: this.config.scheduler.taskTimeoutMs,
|
|
@@ -16461,6 +17174,12 @@ var ReplSession = class {
|
|
|
16461
17174
|
this.agent.off(this.agent.EVENT_ERROR, errorHandler);
|
|
16462
17175
|
this.agent.off(this.agent.EVENT_PROGRESS, progressHandler);
|
|
16463
17176
|
this.editor.onToggleThinking = originalOnToggleThinking;
|
|
17177
|
+
const mainInst = instanceManager.get(mainInstance.instanceId);
|
|
17178
|
+
if (mainInst && mainInst.status === "running") {
|
|
17179
|
+
const mainAgentStatus = taskResult.status === "success" ? "completed" : "failed";
|
|
17180
|
+
instanceManager.transition(mainInstance.instanceId, mainAgentStatus);
|
|
17181
|
+
}
|
|
17182
|
+
this.tui.requestRender();
|
|
16464
17183
|
log.debug("Agent 执行完成", {
|
|
16465
17184
|
taskId,
|
|
16466
17185
|
status: taskResult.status,
|
|
@@ -16475,7 +17194,6 @@ var ReplSession = class {
|
|
|
16475
17194
|
""
|
|
16476
17195
|
]);
|
|
16477
17196
|
spinnerActive = false;
|
|
16478
|
-
clearInterval(spinnerInterval);
|
|
16479
17197
|
try {
|
|
16480
17198
|
const compactionResult = await this.agent.compact();
|
|
16481
17199
|
if (compactionResult.success) {
|
|
@@ -16487,17 +17205,10 @@ var ReplSession = class {
|
|
|
16487
17205
|
this.outputArea.append([chalk.red(" 上下文整理异常,请手动执行 /compact 后重试"), ""]);
|
|
16488
17206
|
}
|
|
16489
17207
|
spinnerActive = true;
|
|
16490
|
-
spinnerInterval = setInterval(() => {
|
|
16491
|
-
if (!spinnerActive) return;
|
|
16492
|
-
spinnerFrame = (spinnerFrame + 1) % LOADING_FRAMES$1.length;
|
|
16493
|
-
this.outputArea.replaceLastLine(LOADING_FRAMES$1[spinnerFrame] ?? "");
|
|
16494
|
-
this.tui.requestRender();
|
|
16495
|
-
}, 100);
|
|
16496
17208
|
}
|
|
16497
17209
|
const outputText = typeof taskResult.output === "string" ? taskResult.output : taskResult.output != null ? JSON.stringify(taskResult.output) : null;
|
|
16498
17210
|
if (spinnerActive) {
|
|
16499
17211
|
spinnerActive = false;
|
|
16500
|
-
clearInterval(spinnerInterval);
|
|
16501
17212
|
if (outputText) this.outputArea.replaceLastLine(responseStyle(outputText));
|
|
16502
17213
|
else if (taskResult.status !== "success") {
|
|
16503
17214
|
const errorMsg = taskResult.error?.message ?? t("session.agentErrorMessage");
|
|
@@ -16609,11 +17320,7 @@ var ReplSession = class {
|
|
|
16609
17320
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
16610
17321
|
log.error("目标执行失败", { input: rawInput }, err);
|
|
16611
17322
|
spinnerActive = false;
|
|
16612
|
-
|
|
16613
|
-
if (thinkingElapsedInterval) {
|
|
16614
|
-
clearInterval(thinkingElapsedInterval);
|
|
16615
|
-
thinkingElapsedInterval = void 0;
|
|
16616
|
-
}
|
|
17323
|
+
thinkingTimerActive = false;
|
|
16617
17324
|
this.stats.totalRequests++;
|
|
16618
17325
|
this.stats.failureCount++;
|
|
16619
17326
|
eventBus.emit("goal:failed", {
|
|
@@ -16677,11 +17384,9 @@ var ReplSession = class {
|
|
|
16677
17384
|
};
|
|
16678
17385
|
} finally {
|
|
16679
17386
|
spinnerActive = false;
|
|
16680
|
-
|
|
16681
|
-
|
|
16682
|
-
|
|
16683
|
-
thinkingElapsedInterval = void 0;
|
|
16684
|
-
}
|
|
17387
|
+
thinkingTimerActive = false;
|
|
17388
|
+
for (const unregister of animCleanup) unregister();
|
|
17389
|
+
animCleanup.length = 0;
|
|
16685
17390
|
if (tokenPushInterval) {
|
|
16686
17391
|
clearInterval(tokenPushInterval);
|
|
16687
17392
|
tokenPushInterval = void 0;
|
|
@@ -16847,7 +17552,7 @@ var ReplSession = class {
|
|
|
16847
17552
|
/**
|
|
16848
17553
|
* 创建 REPL 专用的 Agent 实例
|
|
16849
17554
|
*
|
|
16850
|
-
* Agent
|
|
17555
|
+
* Agent 复用本地 Model 对象进行 LLM 调用,
|
|
16851
17556
|
* 因此需要从 config.llm 解析 model 并注入到 Agent state。
|
|
16852
17557
|
*/
|
|
16853
17558
|
createReplAgent() {
|
|
@@ -16863,7 +17568,7 @@ var ReplSession = class {
|
|
|
16863
17568
|
runtimeConfig: this.config.agentRuntime ?? {}
|
|
16864
17569
|
});
|
|
16865
17570
|
const facade = new AgentLlmFacade(this.config.llm);
|
|
16866
|
-
agent.innerAgent.state.model = facade.
|
|
17571
|
+
agent.innerAgent.state.model = facade.resolveResolvedModel();
|
|
16867
17572
|
agent.innerAgent.getApiKey = facade.createGetApiKeyFn();
|
|
16868
17573
|
agent.llmFacade = facade;
|
|
16869
17574
|
agent.innerAgent.cacheRetention = this.config.agentRuntime?.cacheRetention;
|
|
@@ -16892,17 +17597,36 @@ var ReplSession = class {
|
|
|
16892
17597
|
return agent;
|
|
16893
17598
|
}
|
|
16894
17599
|
/**
|
|
17600
|
+
* 判断是否处于 Coordinator 模式
|
|
17601
|
+
*
|
|
17602
|
+
* 当 agentTeam.defaultMode === 'coordinator' 时,
|
|
17603
|
+
* 主 Agent 切换为编排模式:工具受限、系统提示词替换。
|
|
17604
|
+
*/
|
|
17605
|
+
isCoordinatorMode() {
|
|
17606
|
+
return this.config.agentTeam?.defaultMode === "coordinator";
|
|
17607
|
+
}
|
|
17608
|
+
/**
|
|
16895
17609
|
* 注册 REPL 场景下的基础工具
|
|
16896
17610
|
*
|
|
16897
17611
|
* 在注册内置工具后,异步初始化 MCP 工具。
|
|
16898
17612
|
* MCP 连接不阻塞内置工具注册——Agent 立即可用内置工具,
|
|
16899
17613
|
* MCP 工具在连接完成后自动追加。
|
|
17614
|
+
*
|
|
17615
|
+
* 在 Coordinator 模式下,主 Agent 仅注册编排工具
|
|
17616
|
+
*(AgentTool + SendMessage + TaskStop),完整工具集
|
|
17617
|
+
* 仍传递给 AgentOrchestrator 供子 Agent 使用。
|
|
16900
17618
|
*/
|
|
16901
17619
|
registerBuiltinTools() {
|
|
16902
17620
|
const rawTools = createReplBuiltinTools(this.config.web, this.taskStore, this.config.skill, this.agent, this.config.subAgent, this.cronScheduler ?? void 0, this.config.agentTeam, this.worktreeManager, this.toolGuard);
|
|
16903
17621
|
this.permissionEngine.setToolInfoResolver(createToolInfoResolver(rawTools));
|
|
16904
|
-
const
|
|
17622
|
+
const mainTools = this.isCoordinatorMode() ? [
|
|
17623
|
+
...rawTools.filter((t) => COORDINATOR_TOOLS.includes(t.id)),
|
|
17624
|
+
createSendMessageTool("repl-chat-agent"),
|
|
17625
|
+
createTaskStopTool()
|
|
17626
|
+
] : rawTools;
|
|
17627
|
+
const guardedTools = this.toolGuard.wrapAll(mainTools);
|
|
16905
17628
|
this.agent.registerTools(guardedTools);
|
|
17629
|
+
if (this.isCoordinatorMode()) this.agent.systemPromptOverride = getMainCoordinatorSystemPrompt(process.cwd());
|
|
16906
17630
|
const mcpServers = this.config.mcp ? normalizeMcpConfig(this.config.mcp) : [];
|
|
16907
17631
|
if (mcpServers.length > 0) initializeMcpTools(mcpServers, this.agent).then((manager) => {
|
|
16908
17632
|
this.mcpManager = manager;
|
|
@@ -17101,14 +17825,14 @@ var ReplSession = class {
|
|
|
17101
17825
|
this.editor.onCtrlD = () => {
|
|
17102
17826
|
this.shutdown("收到 EOF (Ctrl+D)");
|
|
17103
17827
|
};
|
|
17104
|
-
this.editor.onToggleAgentBar = () => {
|
|
17105
|
-
this.agentStatusBar.toggle();
|
|
17106
|
-
this.tui.requestRender();
|
|
17107
|
-
};
|
|
17108
17828
|
this.editor.onOpenEditor = () => this.openInEditor();
|
|
17109
|
-
this.editor.
|
|
17110
|
-
this.
|
|
17111
|
-
|
|
17829
|
+
this.editor.onRunInBackground = () => {
|
|
17830
|
+
if (this.currentTaskId) {
|
|
17831
|
+
const bgTaskId = this.currentTaskId;
|
|
17832
|
+
this.cancelCurrentTask();
|
|
17833
|
+
this.outputArea.append([chalk.gray(` (任务 ${bgTaskId} 已转入后台运行)`), ""]);
|
|
17834
|
+
this.tui.requestRender();
|
|
17835
|
+
}
|
|
17112
17836
|
};
|
|
17113
17837
|
}
|
|
17114
17838
|
/** 设置信号处理 */
|
|
@@ -17128,10 +17852,12 @@ var ReplSession = class {
|
|
|
17128
17852
|
instanceManager.on("instance:registered", refreshStatusBar);
|
|
17129
17853
|
instanceManager.on("instance:transitioned", refreshStatusBar);
|
|
17130
17854
|
instanceManager.on("instance:activity", refreshStatusBar);
|
|
17855
|
+
instanceManager.on("instance:toolcall", refreshStatusBar);
|
|
17131
17856
|
eventBus.on("system:shutdown", () => {
|
|
17132
17857
|
instanceManager.off("instance:registered", refreshStatusBar);
|
|
17133
17858
|
instanceManager.off("instance:transitioned", refreshStatusBar);
|
|
17134
17859
|
instanceManager.off("instance:activity", refreshStatusBar);
|
|
17860
|
+
instanceManager.off("instance:toolcall", refreshStatusBar);
|
|
17135
17861
|
});
|
|
17136
17862
|
}
|
|
17137
17863
|
/** 取消当前正在执行的任务 */
|
|
@@ -17297,7 +18023,7 @@ var ReplSession = class {
|
|
|
17297
18023
|
/**
|
|
17298
18024
|
* 从消息 content 中提取文本内容
|
|
17299
18025
|
*
|
|
17300
|
-
*
|
|
18026
|
+
* 消息 content 可能是 string(UserMessage)或 content blocks 数组(AssistantMessage),
|
|
17301
18027
|
* 此函数统一处理两种格式,提取所有文本块和 thinking 块并拼接。
|
|
17302
18028
|
*
|
|
17303
18029
|
* thinking 内容会被包裹在 <thinking>...</thinking> 标签中以便区分。
|
|
@@ -17352,10 +18078,10 @@ var ConversationLogger = class {
|
|
|
17352
18078
|
this.turnCount++;
|
|
17353
18079
|
try {
|
|
17354
18080
|
this.rotateIfNeeded();
|
|
17355
|
-
const line = JSON.stringify({
|
|
18081
|
+
const line = `${JSON.stringify({
|
|
17356
18082
|
...turn,
|
|
17357
18083
|
turn: this.turnCount
|
|
17358
|
-
})
|
|
18084
|
+
})}\n`;
|
|
17359
18085
|
appendFileSync(this.filePath, line, "utf-8");
|
|
17360
18086
|
} catch (err) {
|
|
17361
18087
|
logger.warn("对话日志写入失败", { error: err instanceof Error ? err.message : String(err) });
|