feat: 升级至 v0.3.2 — 工具体系扩展至 26 个 + Context Window 可配置化
## 主要变更 ### 1. Context Window 可配置化(v0.3.1 延续) - DeepSeek/Agnes contextWindow 不再写死 1M,可在设置中配置(min 4096) - 修复 Engine 128K vs Adapter 1M 不一致 bug,Engine 从 adapter.getContextWindow() 读取 - 热重载 configSig 加入 contextWindow,配置变化即时生效 ### 2. 新增 11 个工具(15 → 26 个) - Git 工具集(4):git_status, git_diff, git_log, git_commit - 开发工具集(3):lint_code, run_tests, project_info - HTTP 请求(1):http_request(Node 18+ fetch + AbortController) - TODO 管理(1):todo_write(会话级内存 + LRU 淘汰) - 结构化思考(1):think(无副作用思考空间) - 图片查看(1):view_image(base64 data URL,多模态 LLM 支持) ### 3. 审计修复(2 FAIL + 14 WARN) - FAIL-1: registry.ts truncateResult 添加 dataUrl 白名单(图片不被截断) - FAIL-2: todo.ts 添加 LRU 策略 + clearSession 静态方法 - WARN-1: run_tests filter 字符白名单校验,防 cmd 元字符注入 - WARN-2: dataUrl 检测前置到 stringify 之前,避免大图片无意义序列化 - WARN-3: todo.ts 实现真正 LRU(访问刷新位置,非 FIFO) - WARN-4: git_diff maxBuffer 提升至 5MB,支持超大变更集 - WARN-5: log.info 移至 DelegateTaskTool 注册后,输出正确的 26 - WARN-6: riskColors 添加 critical: 'error' 键 - WARN-7: 所有 execFileAsync 显式设置 encoding: 'utf-8' - WARN-8: git_log --author 拆分为独立参数 - WARN-9: todo_write 权限从 WRITE 改为 READ - WARN-10: description 区分与 task_manager 的不同用途 ### 4. PolicyEngine 策略 - 新增 11 条策略,26 个工具 + mcp_* 全覆盖 - git_commit: WRITE + requireConfirmation - todo_write: READ(内存操作)
This commit is contained in:
+66
-12
@@ -49,6 +49,13 @@ import {
|
||||
CodeSearchTool,
|
||||
TaskManagerTool,
|
||||
DiffViewerTool,
|
||||
// v0.3.1 新增工具(11 个)
|
||||
GitStatusTool, GitDiffTool, GitLogTool, GitCommitTool,
|
||||
LintCodeTool, RunTestsTool, ProjectInfoTool,
|
||||
HttpRequestTool,
|
||||
TodoWriteTool,
|
||||
ThinkTool,
|
||||
ViewImageTool,
|
||||
} from './harness/tools/built-in';
|
||||
import { AuditLogHook, MemoryTriggerHook, PermissionCheckHook, RateLimitHook } from './harness/hooks';
|
||||
import { ConfirmationHook } from './harness/hooks/confirmation-hook';
|
||||
@@ -144,7 +151,12 @@ async function initialize(): Promise<void> {
|
||||
throw new Error(`API key is required for provider "${provider || 'unknown'}". Please set it in Settings.`);
|
||||
}
|
||||
|
||||
const adapterConfig = { provider, baseURL, apiKey, defaultModel: model };
|
||||
// v0.3.1: 读取 Provider 对应的 contextWindow 配置(Ollama 不使用此字段)
|
||||
const contextWindow = provider !== 'ollama'
|
||||
? configService.get<number>(`${provider}.contextWindow`) ?? undefined
|
||||
: undefined;
|
||||
|
||||
const adapterConfig = { provider, baseURL, apiKey, defaultModel: model, contextWindow };
|
||||
switch (provider) {
|
||||
case 'agnes': return new AgnesAdapter(adapterConfig);
|
||||
case 'ollama': return new OllamaAdapter(adapterConfig);
|
||||
@@ -200,7 +212,26 @@ async function initialize(): Promise<void> {
|
||||
|
||||
// 注册 Web Browser 统一浏览器工具
|
||||
toolRegistry.registerBuiltin(new WebBrowserTool());
|
||||
log.info(`Registered ${toolRegistry.size} built-in tools`);
|
||||
|
||||
// v0.3.1: Git 工具集(4 个)
|
||||
toolRegistry.registerBuiltin(new GitStatusTool());
|
||||
toolRegistry.registerBuiltin(new GitDiffTool());
|
||||
toolRegistry.registerBuiltin(new GitLogTool());
|
||||
toolRegistry.registerBuiltin(new GitCommitTool());
|
||||
|
||||
// v0.3.1: 开发工具集(3 个)
|
||||
toolRegistry.registerBuiltin(new LintCodeTool());
|
||||
toolRegistry.registerBuiltin(new RunTestsTool());
|
||||
toolRegistry.registerBuiltin(new ProjectInfoTool());
|
||||
|
||||
// v0.3.1: 独立工具(4 个)
|
||||
toolRegistry.registerBuiltin(new HttpRequestTool());
|
||||
toolRegistry.registerBuiltin(new TodoWriteTool());
|
||||
toolRegistry.registerBuiltin(new ThinkTool());
|
||||
toolRegistry.registerBuiltin(new ViewImageTool());
|
||||
|
||||
// v0.3.1 修复 WARN-5: DelegateTaskTool 注册后再输出总数(此时才是完整的 26 个)
|
||||
// log.info 移至 DelegateTaskTool 注册后
|
||||
|
||||
// ===== MCP Manager =====
|
||||
const mcpManager = new MCPManager(() => db, toolRegistry);
|
||||
@@ -236,6 +267,8 @@ async function initialize(): Promise<void> {
|
||||
maxIterations: agentMaxIter ?? 20,
|
||||
totalTimeoutMs: agentTimeout ?? 600_000,
|
||||
contextLength: ollamaNumCtx ?? undefined,
|
||||
// v0.3.1: 从 adapter.getContextWindow() 读取,修复 Engine 128K vs Adapter 1M 不一致 bug
|
||||
contextWindow: adapter.getContextWindow(),
|
||||
thinkingEnabled: agentThinkingEnabled ?? true,
|
||||
thinkingEffort: agentThinkingEffort ?? 'high',
|
||||
toolExecutionTimeoutMs: toolExecTimeout ?? 120_000,
|
||||
@@ -255,6 +288,8 @@ async function initialize(): Promise<void> {
|
||||
thinkingEnabled: agentThinkingEnabled ?? true,
|
||||
thinkingEffort: agentThinkingEffort ?? 'high',
|
||||
contextLength: ollamaNumCtx ?? undefined,
|
||||
// v0.3.1: 同步 contextWindow 到 SubAgent 配置
|
||||
contextWindow: adapter.getContextWindow(),
|
||||
},
|
||||
);
|
||||
toolRegistry.registerBuiltin(new DelegateTaskTool(orchestrator));
|
||||
@@ -262,16 +297,27 @@ async function initialize(): Promise<void> {
|
||||
agentLoop.setTools(toolRegistry.listTools());
|
||||
// v0.2.0: 在所有工具(包括 DelegateTaskTool)注册完成后,刷新 ConfirmationHook 的工具定义缓存
|
||||
confirmationHook.setToolDefs(toolRegistry.listAllTools());
|
||||
// v0.3.1: 所有工具(含 DelegateTaskTool)注册完成后输出总数,确保日志显示 26 而非 25
|
||||
log.info(`Registered ${toolRegistry.size} built-in tools`);
|
||||
|
||||
// ===== 热重载 Adapter 回调(设置变更时触发)=====
|
||||
// 记录上次创建 adapter 时的配置快照,用于检测配置是否真的变化
|
||||
let lastProvider = configService.get<string>('llm.provider') ?? '';
|
||||
let lastConfigSig = JSON.stringify({
|
||||
provider: configService.get<string>('llm.provider') ?? '',
|
||||
model: configService.get<string>('llm.model') ?? '',
|
||||
apiKey: configService.get<string>('llm.apiKey') ?? '',
|
||||
baseURL: configService.get<string>('llm.baseURL') ?? '',
|
||||
});
|
||||
// v0.3.1: configSig 加入 contextWindow 配置,使 contextWindow 变化也能触发热重载
|
||||
const buildConfigSig = () => {
|
||||
const provider = configService.get<string>('llm.provider') ?? '';
|
||||
return JSON.stringify({
|
||||
provider,
|
||||
model: configService.get<string>('llm.model') ?? '',
|
||||
apiKey: configService.get<string>('llm.apiKey') ?? '',
|
||||
baseURL: configService.get<string>('llm.baseURL') ?? '',
|
||||
// v0.3.1: 加入 contextWindow 配置(Ollama 用 numCtx,其他 Provider 用 ${provider}.contextWindow)
|
||||
contextWindow: provider === 'ollama'
|
||||
? configService.get<number>('ollama.numCtx')
|
||||
: configService.get<number>(`${provider}.contextWindow`),
|
||||
});
|
||||
};
|
||||
let lastConfigSig = buildConfigSig();
|
||||
const reloadAdapter = (): boolean => {
|
||||
try {
|
||||
// 检测配置是否真的变化(避免每次发消息都重建 adapter 和弹 toast)
|
||||
@@ -281,7 +327,7 @@ async function initialize(): Promise<void> {
|
||||
apiKey: configService.get<string>('llm.apiKey') ?? '',
|
||||
baseURL: configService.get<string>('llm.baseURL') ?? '',
|
||||
};
|
||||
const currentSig = JSON.stringify(currentConfig);
|
||||
const currentSig = buildConfigSig();
|
||||
|
||||
// 配置未变化 — 幂等返回成功,不重建 adapter,不发通知
|
||||
if (currentSig === lastConfigSig) {
|
||||
@@ -292,13 +338,21 @@ async function initialize(): Promise<void> {
|
||||
const newAdapter = createAdapter();
|
||||
agentLoop.setAdapter(newAdapter);
|
||||
memoryConsolidator.setAdapter(newAdapter);
|
||||
// Provider 切换时同步 contextLength:仅 Ollama 使用 numCtx 作为有效上下文窗口
|
||||
// Provider 切换时同步 contextLength 和 contextWindow
|
||||
const provider = currentConfig.provider;
|
||||
if (provider === 'ollama') {
|
||||
const numCtx = configService.get<number>('ollama.numCtx');
|
||||
agentLoop.updateConfig({ contextLength: numCtx ?? undefined });
|
||||
agentLoop.updateConfig({
|
||||
contextLength: numCtx ?? undefined,
|
||||
// v0.3.1: 同步 contextWindow(Ollama 的 getContextWindow 返回固定 4096)
|
||||
contextWindow: newAdapter.getContextWindow(),
|
||||
});
|
||||
} else {
|
||||
agentLoop.updateConfig({ contextLength: undefined });
|
||||
// v0.3.1: DeepSeek/Agnes 无 contextLength,但同步 contextWindow
|
||||
agentLoop.updateConfig({
|
||||
contextLength: undefined,
|
||||
contextWindow: newAdapter.getContextWindow(),
|
||||
});
|
||||
}
|
||||
log.info(`[CONFIG] Adapter reloaded: provider=${provider}`);
|
||||
// 仅在 Provider 真正变化时通知渲染进程
|
||||
|
||||
Reference in New Issue
Block a user