autosnippet 3.2.8 → 3.2.9
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/bin/cli.js +6 -5
- package/dashboard/dist/assets/index-BTAsOZv2.js +128 -0
- package/dashboard/dist/assets/index-C_72Ct98.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/lib/cli/AiScanService.js +23 -26
- package/lib/cli/SetupService.js +1 -1
- package/lib/core/AstAnalyzer.js +1 -1
- package/lib/core/discovery/index.js +2 -2
- package/lib/external/ai/AiProvider.js +66 -172
- package/lib/external/ai/providers/GoogleGeminiProvider.js +23 -1
- package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
- package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +287 -204
- package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +7 -6
- package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
- package/lib/external/mcp/handlers/bootstrap-internal.js +2 -2
- package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/middleware/requestLogger.js +1 -0
- package/lib/http/routes/ai.js +240 -35
- package/lib/http/routes/candidates.js +2 -3
- package/lib/http/routes/extract.js +13 -11
- package/lib/http/routes/modules.js +2 -2
- package/lib/http/routes/recipes.js +9 -5
- package/lib/http/routes/remote.js +134 -255
- package/lib/http/routes/violations.js +0 -54
- package/lib/http/utils/sse-sessions.js +1 -1
- package/lib/infrastructure/logging/Logger.js +5 -4
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
- package/lib/injection/ServiceContainer.js +64 -17
- package/lib/platform/ScreenCaptureService.js +177 -0
- package/lib/platform/ios/routes/spm.js +2 -2
- package/lib/service/agent/AgentEventBus.js +207 -0
- package/lib/service/agent/AgentFactory.js +490 -0
- package/lib/service/agent/AgentMessage.js +240 -0
- package/lib/service/agent/AgentRouter.js +228 -0
- package/lib/service/agent/AgentRuntime.js +1016 -0
- package/lib/service/agent/AgentState.js +217 -0
- package/lib/service/agent/IntentClassifier.js +331 -0
- package/lib/service/agent/LarkTransport.js +389 -0
- package/lib/service/agent/capabilities.js +408 -0
- package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
- package/lib/service/{chat → agent/context}/ExplorationTracker.js +25 -14
- package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +1 -1
- package/lib/service/agent/core/LoopContext.js +170 -0
- package/lib/service/agent/core/MessageAdapter.js +223 -0
- package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
- package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +19 -98
- package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
- package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
- package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
- package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +85 -135
- package/lib/service/agent/domain/insight-producer.js +267 -0
- package/lib/service/agent/domain/scan-prompts.js +105 -0
- package/lib/service/agent/forced-summary.js +266 -0
- package/lib/service/agent/index.js +91 -0
- package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
- package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
- package/lib/service/{chat → agent}/memory/SessionStore.js +1 -1
- package/lib/service/{chat → agent}/memory/index.js +1 -1
- package/lib/service/agent/policies.js +442 -0
- package/lib/service/agent/presets.js +303 -0
- package/lib/service/agent/strategies.js +717 -0
- package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
- package/lib/service/agent/tools/ai-analysis.js +75 -0
- package/lib/service/{chat → agent}/tools/composite.js +2 -1
- package/lib/service/{chat → agent}/tools/guard.js +1 -121
- package/lib/service/{chat → agent}/tools/index.js +27 -21
- package/lib/service/{chat → agent}/tools/infrastructure.js +1 -1
- package/lib/service/agent/tools/knowledge-graph.js +112 -0
- package/lib/service/agent/tools/scan-recipe.js +189 -0
- package/lib/service/agent/tools/system-interaction.js +476 -0
- package/lib/service/automation/DirectiveDetector.js +0 -1
- package/lib/service/automation/FileWatcher.js +0 -8
- package/lib/service/automation/handlers/CreateHandler.js +7 -3
- package/lib/service/automation/handlers/DraftHandler.js +7 -6
- package/lib/service/module/ModuleService.js +40 -73
- package/lib/service/skills/SignalCollector.js +26 -19
- package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
- package/lib/shared/FieldSpec.js +1 -1
- package/lib/shared/StyleGuide.js +1 -1
- package/package.json +4 -1
- package/resources/native-ui/screenshot.swift +228 -0
- package/dashboard/dist/assets/index-D5jiDBQG.css +0 -1
- package/dashboard/dist/assets/index-e5OKj-Ni.js +0 -128
- package/lib/core/discovery/SpmDiscoverer.js +0 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -750
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
- package/lib/http/routes/spm.js +0 -5
- package/lib/infrastructure/external/XcodeAutomation.js +0 -15
- package/lib/service/chat/ChatAgent.js +0 -1602
- package/lib/service/chat/Memory.js +0 -161
- package/lib/service/chat/ProducerAgent.js +0 -431
- package/lib/service/chat/ReasoningTrace.js +0 -523
- package/lib/service/chat/TaskPipeline.js +0 -357
- package/lib/service/chat/WorkingMemory.js +0 -359
- package/lib/service/chat/memory/PersistentMemory.js +0 -450
- package/lib/service/chat/tools/ai-analysis.js +0 -267
- package/lib/service/chat/tools/knowledge-graph.js +0 -234
- package/lib/service/chat/tools.js +0 -18
- package/lib/service/snippet/PlaceholderConverter.js +0 -5
- package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
- /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
- /package/lib/service/{chat → agent}/memory/ActiveContext.js +0 -0
- /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
- /package/lib/service/{chat → agent}/tools/ast-graph.js +0 -0
- /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
- /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
- /package/lib/service/{chat → agent}/tools/query.js +0 -0
|
@@ -25,10 +25,10 @@ import { TokenUsageStore } from '../repository/token/TokenUsageStore.js';
|
|
|
25
25
|
// ─── P2: Automation ───────────────────────────────────
|
|
26
26
|
import { AutomationOrchestrator } from '../service/automation/AutomationOrchestrator.js';
|
|
27
27
|
import { BootstrapTaskManager } from '../service/bootstrap/BootstrapTaskManager.js';
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
import { ToolRegistry } from '../service/
|
|
31
|
-
import { ALL_TOOLS } from '../service/
|
|
28
|
+
// ─── P2: Agent 统一架构 (v7.2) ────────────────────────
|
|
29
|
+
import { AgentFactory } from '../service/agent/AgentFactory.js';
|
|
30
|
+
import { ToolRegistry } from '../service/agent/tools/ToolRegistry.js';
|
|
31
|
+
import { ALL_TOOLS } from '../service/agent/tools/index.js';
|
|
32
32
|
// ─── P2: Content Extraction ─────────────────────────────────────
|
|
33
33
|
import { RecipeExtractor } from '../service/context/RecipeExtractor.js';
|
|
34
34
|
// ─── P3: Cursor Delivery Pipeline ──────────────────────
|
|
@@ -59,7 +59,7 @@ import { RetrievalFunnel } from '../service/search/RetrievalFunnel.js';
|
|
|
59
59
|
import { SearchEngine } from '../service/search/SearchEngine.js';
|
|
60
60
|
import { SkillHooks } from '../service/skills/SkillHooks.js';
|
|
61
61
|
import { VSCodeCodec } from '../service/snippet/codecs/VSCodeCodec.js';
|
|
62
|
-
import { XcodeCodec } from '../
|
|
62
|
+
import { XcodeCodec } from '../platform/ios/snippet/XcodeCodec.js';
|
|
63
63
|
import { SnippetFactory } from '../service/snippet/SnippetFactory.js';
|
|
64
64
|
import { SnippetInstaller } from '../service/snippet/SnippetInstaller.js';
|
|
65
65
|
import { DimensionCopy } from '../shared/DimensionCopyRegistry.js';
|
|
@@ -177,12 +177,15 @@ export class ServiceContainer {
|
|
|
177
177
|
// ═══ AI Provider 热重载标记 ═══
|
|
178
178
|
// 记录哪些 singleton key 持有 aiProvider 引用,在 reloadAiProvider() 时需要清除
|
|
179
179
|
this._aiDependentSingletons = [
|
|
180
|
-
'
|
|
180
|
+
'agentFactory',
|
|
181
181
|
'searchEngine',
|
|
182
182
|
'retrievalFunnel',
|
|
183
183
|
'indexingPipeline',
|
|
184
184
|
];
|
|
185
185
|
|
|
186
|
+
// ═══ 容器级语言偏好 ═══
|
|
187
|
+
this.singletons._lang = null;
|
|
188
|
+
|
|
186
189
|
// 注册仓储
|
|
187
190
|
this._registerRepositories();
|
|
188
191
|
|
|
@@ -211,7 +214,7 @@ export class ServiceContainer {
|
|
|
211
214
|
* 流程:
|
|
212
215
|
* 1. 替换 singletons.aiProvider
|
|
213
216
|
* 2. 重新创建 _embedProvider(如果主 provider 不支持 embedding)
|
|
214
|
-
* 3. 清除已缓存的依赖 AI 的 singleton(
|
|
217
|
+
* 3. 清除已缓存的依赖 AI 的 singleton(SearchEngine 等),
|
|
215
218
|
* 下次 get() 时会用新 provider 重新创建
|
|
216
219
|
*
|
|
217
220
|
* @param {import('../external/ai/AiProvider.js').AiProvider} newProvider
|
|
@@ -680,8 +683,8 @@ export class ServiceContainer {
|
|
|
680
683
|
if (!this.singletons.moduleService) {
|
|
681
684
|
const projectRoot = this.singletons._projectRoot || process.cwd();
|
|
682
685
|
this.singletons.moduleService = new ModuleService(projectRoot, {
|
|
683
|
-
|
|
684
|
-
|
|
686
|
+
agentFactory: this.get('agentFactory'),
|
|
687
|
+
container: this,
|
|
685
688
|
qualityScorer: this.get('qualityScorer'),
|
|
686
689
|
recipeExtractor: this.singletons._recipeExtractor || null,
|
|
687
690
|
guardCheckEngine: this.get('guardCheckEngine'),
|
|
@@ -707,7 +710,7 @@ export class ServiceContainer {
|
|
|
707
710
|
return getEnhancementRegistry();
|
|
708
711
|
});
|
|
709
712
|
|
|
710
|
-
// ToolRegistry (
|
|
713
|
+
// ToolRegistry (Agent 工具注册表)
|
|
711
714
|
this.register('toolRegistry', () => {
|
|
712
715
|
if (!this.singletons.toolRegistry) {
|
|
713
716
|
const registry = new ToolRegistry();
|
|
@@ -723,21 +726,23 @@ export class ServiceContainer {
|
|
|
723
726
|
return this.singletons.projectGraph || null;
|
|
724
727
|
});
|
|
725
728
|
|
|
726
|
-
// AI Provider(供 MCP handler /
|
|
729
|
+
// AI Provider(供 MCP handler / AgentRuntime / 任意服务层使用)
|
|
727
730
|
this.register('aiProvider', () => this.singletons.aiProvider || null);
|
|
728
731
|
|
|
729
|
-
//
|
|
730
|
-
this.register('
|
|
731
|
-
if (!this.singletons.
|
|
732
|
+
// AgentFactory (v7.2 统一 Agent 创建工厂 — Preset + DI → AgentRuntime)
|
|
733
|
+
this.register('agentFactory', () => {
|
|
734
|
+
if (!this.singletons.agentFactory) {
|
|
732
735
|
const toolRegistry = this.get('toolRegistry');
|
|
733
736
|
const aiProvider = this.singletons.aiProvider || null;
|
|
734
|
-
this.singletons.
|
|
737
|
+
const projectRoot = this.singletons._projectRoot || process.cwd();
|
|
738
|
+
this.singletons.agentFactory = new AgentFactory({
|
|
739
|
+
container: this,
|
|
735
740
|
toolRegistry,
|
|
736
741
|
aiProvider,
|
|
737
|
-
|
|
742
|
+
projectRoot,
|
|
738
743
|
});
|
|
739
744
|
}
|
|
740
|
-
return this.singletons.
|
|
745
|
+
return this.singletons.agentFactory;
|
|
741
746
|
});
|
|
742
747
|
|
|
743
748
|
// SkillHooks (Skill 生命周期钩子 — 加载 skills/*/hooks.js)
|
|
@@ -814,6 +819,48 @@ export class ServiceContainer {
|
|
|
814
819
|
});
|
|
815
820
|
}
|
|
816
821
|
|
|
822
|
+
// ─── 容器级语言偏好 ─────
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* 获取当前默认 UI 语言偏好
|
|
826
|
+
* @returns {'zh'|'en'|null}
|
|
827
|
+
*/
|
|
828
|
+
getLang() {
|
|
829
|
+
return this.singletons._lang || null;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* 设置默认 UI 语言偏好(影响 Agent 回复语言)
|
|
834
|
+
* @param {'zh'|'en'|null} lang
|
|
835
|
+
*/
|
|
836
|
+
setLang(lang) {
|
|
837
|
+
this.singletons._lang = lang || null;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// ─── 工具执行上下文构建器 ─────────────────────
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* 构建 ToolRegistry.execute() 所需的上下文对象。
|
|
844
|
+
*
|
|
845
|
+
* 工具执行上下文构建
|
|
846
|
+
* 迁移后: 所有直接调用 ToolRegistry 的站点都使用此方法
|
|
847
|
+
*
|
|
848
|
+
* @param {Object} [extras] — 合并到上下文的额外字段
|
|
849
|
+
* @returns {Object} 工具执行上下文
|
|
850
|
+
*/
|
|
851
|
+
buildToolContext(extras = {}) {
|
|
852
|
+
return {
|
|
853
|
+
container: this,
|
|
854
|
+
aiProvider: this.singletons.aiProvider || null,
|
|
855
|
+
projectRoot: this.singletons._projectRoot || process.cwd(),
|
|
856
|
+
logger: this.logger,
|
|
857
|
+
source: extras.source || 'system',
|
|
858
|
+
lang: extras.lang || this.singletons._lang || null,
|
|
859
|
+
fileCache: this.singletons._fileCache || null,
|
|
860
|
+
...extras,
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
|
|
817
864
|
/**
|
|
818
865
|
* 注册服务或工厂函数
|
|
819
866
|
*/
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ScreenCaptureService — macOS 原生截图服务
|
|
3
|
+
*
|
|
4
|
+
* 使用 ScreenCaptureKit (via Swift CLI) 截取窗口/屏幕画面。
|
|
5
|
+
* 息屏可用,无需 OBS。
|
|
6
|
+
*
|
|
7
|
+
* 依赖:resources/native-ui/screenshot.swift 编译产物
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, mkdirSync, statSync as fstatSync } from 'node:fs';
|
|
11
|
+
import { join, dirname } from 'node:path';
|
|
12
|
+
import { execSync, execFileSync } from 'node:child_process';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import Logger from '../infrastructure/logging/Logger.js';
|
|
15
|
+
|
|
16
|
+
const logger = Logger.getInstance();
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = dirname(__filename);
|
|
20
|
+
|
|
21
|
+
/** Swift 源文件路径 */
|
|
22
|
+
const SWIFT_SRC = join(__dirname, '../../resources/native-ui/screenshot.swift');
|
|
23
|
+
/** 编译产物路径 */
|
|
24
|
+
const BINARY_PATH = join(__dirname, '../../resources/native-ui/screenshot');
|
|
25
|
+
|
|
26
|
+
let _binaryReady = false;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 确保 Swift 截图工具已编译
|
|
30
|
+
* @returns {{ready: boolean, error?: string}}
|
|
31
|
+
*/
|
|
32
|
+
function ensureBinary() {
|
|
33
|
+
if (_binaryReady && existsSync(BINARY_PATH)) return { ready: true };
|
|
34
|
+
|
|
35
|
+
if (!existsSync(SWIFT_SRC)) {
|
|
36
|
+
return { ready: false, error: `Swift source not found: ${SWIFT_SRC}` };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 检查是否需要重新编译(源文件比二进制新)
|
|
40
|
+
const needsBuild = !existsSync(BINARY_PATH) ||
|
|
41
|
+
(() => {
|
|
42
|
+
try {
|
|
43
|
+
const srcTime = fstatSync(SWIFT_SRC).mtimeMs;
|
|
44
|
+
const binTime = fstatSync(BINARY_PATH).mtimeMs;
|
|
45
|
+
return srcTime > binTime;
|
|
46
|
+
} catch {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
})();
|
|
50
|
+
|
|
51
|
+
if (needsBuild) {
|
|
52
|
+
logger.info('[Screenshot] Compiling screenshot tool...');
|
|
53
|
+
try {
|
|
54
|
+
execSync(
|
|
55
|
+
`swiftc -O -framework ScreenCaptureKit -framework AppKit "${SWIFT_SRC}" -o "${BINARY_PATH}"`,
|
|
56
|
+
{ timeout: 60000, stdio: ['pipe', 'pipe', 'pipe'] },
|
|
57
|
+
);
|
|
58
|
+
logger.info('[Screenshot] ✅ Compiled successfully');
|
|
59
|
+
} catch (err) {
|
|
60
|
+
const stderr = err.stderr?.toString() || err.message;
|
|
61
|
+
logger.error(`[Screenshot] Compilation failed: ${stderr}`);
|
|
62
|
+
return { ready: false, error: `Swift compilation failed: ${stderr.slice(0, 200)}` };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_binaryReady = true;
|
|
67
|
+
return { ready: true };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 执行截图工具
|
|
72
|
+
* @param {string[]} args — CLI 参数
|
|
73
|
+
* @returns {{success: boolean, data?: object, error?: string}}
|
|
74
|
+
*/
|
|
75
|
+
function exec(args = []) {
|
|
76
|
+
const check = ensureBinary();
|
|
77
|
+
if (!check.ready) return { success: false, error: check.error };
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const stdout = execFileSync(BINARY_PATH, args, {
|
|
81
|
+
timeout: 15000,
|
|
82
|
+
encoding: 'utf-8',
|
|
83
|
+
maxBuffer: 1024 * 1024,
|
|
84
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const result = JSON.parse(stdout.trim());
|
|
88
|
+
return { success: true, data: result };
|
|
89
|
+
} catch (err) {
|
|
90
|
+
// 尝试从 stderr 中解析 JSON 错误
|
|
91
|
+
const stderr = err.stderr?.toString() || '';
|
|
92
|
+
try {
|
|
93
|
+
const errObj = JSON.parse(stderr.trim());
|
|
94
|
+
return { success: false, error: errObj.error || stderr };
|
|
95
|
+
} catch {
|
|
96
|
+
return { success: false, error: stderr || err.message };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ═══════════════════════════════════════════════════════
|
|
102
|
+
// 公开 API
|
|
103
|
+
// ═══════════════════════════════════════════════════════
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 截取屏幕或窗口画面(核心功能 — 息屏可用)
|
|
107
|
+
*
|
|
108
|
+
* @param {object} opts
|
|
109
|
+
* @param {string} [opts.windowTitle] — 窗口标题关键词(模糊匹配),默认截整屏
|
|
110
|
+
* @param {string} [opts.format] — 'jpeg' | 'png'
|
|
111
|
+
* @param {number} [opts.scale] — 缩放因子 (0.25-2.0)
|
|
112
|
+
* @param {string} [opts.outputPath] — 输出路径,默认临时目录
|
|
113
|
+
* @returns {Promise<{success: boolean, path?: string, width?: number, height?: number, error?: string}>}
|
|
114
|
+
*/
|
|
115
|
+
export async function screenshot(opts = {}) {
|
|
116
|
+
const args = [];
|
|
117
|
+
|
|
118
|
+
if (opts.windowTitle) {
|
|
119
|
+
args.push('--window', opts.windowTitle);
|
|
120
|
+
}
|
|
121
|
+
if (opts.format) {
|
|
122
|
+
args.push('--format', opts.format);
|
|
123
|
+
}
|
|
124
|
+
if (opts.scale) {
|
|
125
|
+
args.push('--scale', String(opts.scale));
|
|
126
|
+
}
|
|
127
|
+
if (opts.outputPath) {
|
|
128
|
+
args.push('--output', opts.outputPath);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const result = exec(args);
|
|
132
|
+
|
|
133
|
+
if (result.success && result.data) {
|
|
134
|
+
logger.info(`[Screenshot] ✅ Captured: ${result.data.path} (${result.data.width}x${result.data.height})`);
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
path: result.data.path,
|
|
138
|
+
width: result.data.width,
|
|
139
|
+
height: result.data.height,
|
|
140
|
+
format: result.data.format,
|
|
141
|
+
bytes: result.data.bytes,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
logger.warn(`[Screenshot] ❌ Failed: ${result.error}`);
|
|
146
|
+
return { success: false, error: result.error };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 列出所有可截取的窗口
|
|
151
|
+
* @returns {Promise<{success: boolean, windows?: Array, error?: string}>}
|
|
152
|
+
*/
|
|
153
|
+
export async function listWindows() {
|
|
154
|
+
const result = exec(['--list-windows']);
|
|
155
|
+
|
|
156
|
+
if (result.success && result.data) {
|
|
157
|
+
return { success: true, windows: result.data };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { success: false, error: result.error };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 检查截图服务是否可用
|
|
165
|
+
* @returns {{available: boolean, error?: string}}
|
|
166
|
+
*/
|
|
167
|
+
export function isScreenCaptureAvailable() {
|
|
168
|
+
const check = ensureBinary();
|
|
169
|
+
if (!check.ready) return { available: false, error: check.error };
|
|
170
|
+
|
|
171
|
+
// 尝试列出窗口来验证权限
|
|
172
|
+
const result = exec(['--list-windows']);
|
|
173
|
+
return {
|
|
174
|
+
available: result.success,
|
|
175
|
+
error: result.success ? undefined : result.error,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -379,12 +379,12 @@ router.post(
|
|
|
379
379
|
const { maxFiles, skipGuard, contentMaxLines } = req.body || {};
|
|
380
380
|
|
|
381
381
|
const container = getServiceContainer();
|
|
382
|
-
const
|
|
382
|
+
const agentFactory = container.get('agentFactory');
|
|
383
383
|
|
|
384
384
|
logger.info('Bootstrap cold start initiated (v5: async fill mode)');
|
|
385
385
|
|
|
386
386
|
// ── 同步阶段: 快速执行 Phase 1-4 → 返回骨架 ──
|
|
387
|
-
const bootstrapResult = await
|
|
387
|
+
const bootstrapResult = await agentFactory.bootstrapKnowledge({
|
|
388
388
|
maxFiles: maxFiles || 500,
|
|
389
389
|
skipGuard: skipGuard || false,
|
|
390
390
|
contentMaxLines: contentMaxLines || 120,
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentEventBus — Agent 间事件通信总线
|
|
3
|
+
*
|
|
4
|
+
* 借鉴 AutoGen Core Event-Driven 架构 + RxJS Observable 模式:
|
|
5
|
+
* - Agent 间松耦合通信(publish/subscribe)
|
|
6
|
+
* - 支持同步和异步事件处理
|
|
7
|
+
* - 内置事件过滤、优先级、TTL
|
|
8
|
+
* - 支持 request/reply 模式(Agent 间 RPC)
|
|
9
|
+
*
|
|
10
|
+
* @module AgentEventBus
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { EventEmitter } from 'node:events';
|
|
14
|
+
import Logger from '../../infrastructure/logging/Logger.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 标准事件类型
|
|
18
|
+
*/
|
|
19
|
+
export const AgentEvents = Object.freeze({
|
|
20
|
+
// ── 生命周期 ──
|
|
21
|
+
AGENT_CREATED: 'agent:created',
|
|
22
|
+
AGENT_STARTED: 'agent:started',
|
|
23
|
+
AGENT_COMPLETED: 'agent:completed',
|
|
24
|
+
AGENT_FAILED: 'agent:failed',
|
|
25
|
+
AGENT_ABORTED: 'agent:aborted',
|
|
26
|
+
|
|
27
|
+
// ── 执行 ──
|
|
28
|
+
TOOL_CALL_START: 'tool:call:start',
|
|
29
|
+
TOOL_CALL_END: 'tool:call:end',
|
|
30
|
+
LLM_CALL_START: 'llm:call:start',
|
|
31
|
+
LLM_CALL_END: 'llm:call:end',
|
|
32
|
+
STEP_COMPLETED: 'step:completed',
|
|
33
|
+
|
|
34
|
+
// ── Agent 间交互 ──
|
|
35
|
+
HANDOFF_REQUEST: 'handoff:request',
|
|
36
|
+
HANDOFF_ACCEPT: 'handoff:accept',
|
|
37
|
+
HANDOFF_RESULT: 'handoff:result',
|
|
38
|
+
|
|
39
|
+
// ── 进度 ──
|
|
40
|
+
PROGRESS: 'progress',
|
|
41
|
+
THINKING: 'thinking',
|
|
42
|
+
STREAM_DELTA: 'stream:delta',
|
|
43
|
+
|
|
44
|
+
// ── 外部触发 ──
|
|
45
|
+
USER_INPUT: 'user:input',
|
|
46
|
+
LARK_MESSAGE: 'lark:message',
|
|
47
|
+
SCAN_REQUEST: 'scan:request',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {Object} AgentEvent
|
|
52
|
+
* @property {string} type — 事件类型
|
|
53
|
+
* @property {string} source — 发送者 agentId
|
|
54
|
+
* @property {string} [target] — 目标 agentId(广播时为 null)
|
|
55
|
+
* @property {Object} payload — 事件数据
|
|
56
|
+
* @property {number} timestamp — 事件时间戳
|
|
57
|
+
* @property {string} [correlationId] — 关联 ID(用于 request/reply)
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
export class AgentEventBus extends EventEmitter {
|
|
61
|
+
/** @type {AgentEventBus|null} */
|
|
62
|
+
static #instance = null;
|
|
63
|
+
#logger;
|
|
64
|
+
/** @type {Map<string, Function[]>} topic → handlers */
|
|
65
|
+
#subscriptions = new Map();
|
|
66
|
+
/** @type {Map<string, {resolve: Function, reject: Function, timer: NodeJS.Timeout}>} */
|
|
67
|
+
#pendingReplies = new Map();
|
|
68
|
+
/** @type {number} 事件计数 */
|
|
69
|
+
#eventCount = 0;
|
|
70
|
+
|
|
71
|
+
constructor() {
|
|
72
|
+
super();
|
|
73
|
+
this.setMaxListeners(100);
|
|
74
|
+
this.#logger = Logger.getInstance();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 获取全局单例
|
|
79
|
+
* @returns {AgentEventBus}
|
|
80
|
+
*/
|
|
81
|
+
static getInstance() {
|
|
82
|
+
if (!AgentEventBus.#instance) {
|
|
83
|
+
AgentEventBus.#instance = new AgentEventBus();
|
|
84
|
+
}
|
|
85
|
+
return AgentEventBus.#instance;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 重置单例(测试用)
|
|
90
|
+
*/
|
|
91
|
+
static resetInstance() {
|
|
92
|
+
if (AgentEventBus.#instance) {
|
|
93
|
+
AgentEventBus.#instance.removeAllListeners();
|
|
94
|
+
AgentEventBus.#instance.#subscriptions.clear();
|
|
95
|
+
AgentEventBus.#instance.#pendingReplies.clear();
|
|
96
|
+
}
|
|
97
|
+
AgentEventBus.#instance = null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ─── 发布 ────────────────────────────────
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 发布事件(广播)
|
|
104
|
+
* @param {string} type — 事件类型
|
|
105
|
+
* @param {Object} payload — 事件数据
|
|
106
|
+
* @param {Object} [opts]
|
|
107
|
+
* @param {string} [opts.source] — 发送者 agentId
|
|
108
|
+
* @param {string} [opts.target] — 目标 agentId
|
|
109
|
+
* @param {string} [opts.correlationId] — 关联 ID
|
|
110
|
+
*/
|
|
111
|
+
publish(type, payload = {}, opts = {}) {
|
|
112
|
+
this.#eventCount++;
|
|
113
|
+
const event = {
|
|
114
|
+
type,
|
|
115
|
+
source: opts.source || 'system',
|
|
116
|
+
target: opts.target || null,
|
|
117
|
+
payload,
|
|
118
|
+
timestamp: Date.now(),
|
|
119
|
+
correlationId: opts.correlationId || null,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// 发射到 EventEmitter(通用监听)
|
|
123
|
+
this.emit(type, event);
|
|
124
|
+
this.emit('*', event); // 全局监听
|
|
125
|
+
|
|
126
|
+
// 发射到 topic 订阅者
|
|
127
|
+
const handlers = this.#subscriptions.get(type) || [];
|
|
128
|
+
for (const handler of handlers) {
|
|
129
|
+
try {
|
|
130
|
+
handler(event);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
this.#logger.warn(`[AgentEventBus] Handler error on ${type}: ${err.message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 检查是否有 pending reply
|
|
137
|
+
if (opts.correlationId && this.#pendingReplies.has(opts.correlationId)) {
|
|
138
|
+
const pending = this.#pendingReplies.get(opts.correlationId);
|
|
139
|
+
clearTimeout(pending.timer);
|
|
140
|
+
this.#pendingReplies.delete(opts.correlationId);
|
|
141
|
+
pending.resolve(event);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 订阅事件
|
|
147
|
+
* @param {string} type — 事件类型
|
|
148
|
+
* @param {Function} handler — 处理函数 (event) => void
|
|
149
|
+
* @returns {Function} 取消订阅函数
|
|
150
|
+
*/
|
|
151
|
+
subscribe(type, handler) {
|
|
152
|
+
if (!this.#subscriptions.has(type)) {
|
|
153
|
+
this.#subscriptions.set(type, []);
|
|
154
|
+
}
|
|
155
|
+
this.#subscriptions.get(type).push(handler);
|
|
156
|
+
|
|
157
|
+
return () => {
|
|
158
|
+
const handlers = this.#subscriptions.get(type);
|
|
159
|
+
if (handlers) {
|
|
160
|
+
const idx = handlers.indexOf(handler);
|
|
161
|
+
if (idx >= 0) handlers.splice(idx, 1);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Request/Reply 模式 — 发送请求并等待响应
|
|
168
|
+
* @param {string} requestType — 请求事件类型
|
|
169
|
+
* @param {Object} payload — 请求数据
|
|
170
|
+
* @param {Object} [opts]
|
|
171
|
+
* @param {number} [opts.timeout=30000] — 超时毫秒
|
|
172
|
+
* @param {string} [opts.source] — 发送者
|
|
173
|
+
* @returns {Promise<AgentEvent>} 响应事件
|
|
174
|
+
*/
|
|
175
|
+
async request(requestType, payload = {}, opts = {}) {
|
|
176
|
+
const correlationId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
177
|
+
const timeout = opts.timeout || 30_000;
|
|
178
|
+
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
const timer = setTimeout(() => {
|
|
181
|
+
this.#pendingReplies.delete(correlationId);
|
|
182
|
+
reject(new Error(`AgentEventBus request timeout: ${requestType} (${timeout}ms)`));
|
|
183
|
+
}, timeout);
|
|
184
|
+
|
|
185
|
+
this.#pendingReplies.set(correlationId, { resolve, reject, timer });
|
|
186
|
+
|
|
187
|
+
this.publish(requestType, payload, {
|
|
188
|
+
source: opts.source,
|
|
189
|
+
correlationId,
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 获取事件统计
|
|
196
|
+
* @returns {Object}
|
|
197
|
+
*/
|
|
198
|
+
getStats() {
|
|
199
|
+
return {
|
|
200
|
+
totalEvents: this.#eventCount,
|
|
201
|
+
subscriptionTopics: this.#subscriptions.size,
|
|
202
|
+
pendingReplies: this.#pendingReplies.size,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default AgentEventBus;
|