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
|
@@ -615,7 +615,7 @@ export async function runAllPhases(projectRoot, ctx, options = {}) {
|
|
|
615
615
|
// ── 清除旧数据 (if requested) ──
|
|
616
616
|
if (options.clearOldData) {
|
|
617
617
|
try {
|
|
618
|
-
const { clearCheckpoints, clearSnapshots } = await import('
|
|
618
|
+
const { clearCheckpoints, clearSnapshots } = await import('../pipeline/orchestrator.js');
|
|
619
619
|
await clearCheckpoints(projectRoot);
|
|
620
620
|
await clearSnapshots(projectRoot, ctx);
|
|
621
621
|
ctx.logger.info('[Bootstrap] Cleared old checkpoints and snapshots');
|
|
@@ -636,10 +636,12 @@ export async function runAllPhases(projectRoot, ctx, options = {}) {
|
|
|
636
636
|
allFiles, langStats,
|
|
637
637
|
primaryLang: null, discoverer, allTargets,
|
|
638
638
|
astProjectSummary: null, astContext: '',
|
|
639
|
-
codeEntityResult: null,
|
|
639
|
+
codeEntityResult: null, callGraphResult: null,
|
|
640
|
+
depGraphData: null, depEdgesWritten: 0,
|
|
640
641
|
guardAudit: null, guardEngine: null,
|
|
641
642
|
activeDimensions: [], enhancementPackInfo: [],
|
|
642
|
-
enhancementPatterns: [],
|
|
643
|
+
enhancementPatterns: [], enhancementGuardRules: [],
|
|
644
|
+
langProfile: {},
|
|
643
645
|
targetsSummary: [], warnings,
|
|
644
646
|
report: report || {},
|
|
645
647
|
incrementalPlan: null,
|
|
@@ -652,12 +654,11 @@ export async function runAllPhases(projectRoot, ctx, options = {}) {
|
|
|
652
654
|
let incrementalPlan = null;
|
|
653
655
|
if (options.incremental) {
|
|
654
656
|
try {
|
|
655
|
-
const { IncrementalBootstrap } = await import('
|
|
657
|
+
const { IncrementalBootstrap } = await import('../pipeline/IncrementalBootstrap.js');
|
|
656
658
|
const db = ctx.container?.resolve?.('db') ?? ctx.db;
|
|
657
659
|
if (db) {
|
|
658
660
|
const ib = new IncrementalBootstrap(db, projectRoot, { logger: ctx.logger });
|
|
659
|
-
const dimIds = (
|
|
660
|
-
?.map(d => d.id) || [];
|
|
661
|
+
const dimIds = baseDimensions.map(d => d.id);
|
|
661
662
|
incrementalPlan = await ib.evaluate(allFiles, dimIds);
|
|
662
663
|
if (report) report.phases.incremental = { plan: incrementalPlan };
|
|
663
664
|
ctx.logger.info(`[Bootstrap] Incremental mode: ${incrementalPlan.mode}, affected: ${incrementalPlan.affectedDimensions?.length || 0}`);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 参考:
|
|
8
8
|
* - MetaGPT SOP 驱动模式 (docs/design/external-agent-quality-gap.md §4.2)
|
|
9
|
-
* - 内部 Agent 的
|
|
9
|
+
* - 内部 Agent 的 PipelineStrategy (Analyze → QualityGate → Produce → RejectionGate)
|
|
10
10
|
*
|
|
11
11
|
* 设计原则:
|
|
12
12
|
* - 每个维度 4 个阶段: 扫描 → 验证 → 异常检测 → 提交
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 调用方:
|
|
8
8
|
* - CLI: `asd bootstrap --knowledge`
|
|
9
|
-
* -
|
|
9
|
+
* - AgentRuntime: `bootstrapKnowledgeTool` (infrastructure.js)
|
|
10
10
|
* - Dashboard HTTP: POST /api/bootstrap/knowledge
|
|
11
11
|
*
|
|
12
12
|
* 外部 Agent 路径(Cursor/Copilot 等 IDE Agent)请参见:
|
|
@@ -280,7 +280,7 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
280
280
|
expectedOutput: `候选知识(微观代码维度:code-pattern/best-practice/event-and-data-flow + 语言条件扫描)+ Project Skills(宏观叙事维度:code-standard/architecture/project-profile/agent-guidelines + 语言条件扫描)— 共 ${dimensions.length} 个维度`,
|
|
281
281
|
},
|
|
282
282
|
|
|
283
|
-
// AST 代码结构分析上下文(供
|
|
283
|
+
// AST 代码结构分析上下文(供 Agent 使用)
|
|
284
284
|
astContext: astContext || null,
|
|
285
285
|
astSummary: astProjectSummary
|
|
286
286
|
? {
|
|
@@ -291,7 +291,7 @@ export async function dimensionComplete(ctx, args) {
|
|
|
291
291
|
if (isComplete) {
|
|
292
292
|
// R4: 自动触发 Cursor Delivery
|
|
293
293
|
try {
|
|
294
|
-
const { getServiceContainer } = await import('
|
|
294
|
+
const { getServiceContainer } = await import('../../../injection/ServiceContainer.js');
|
|
295
295
|
const container = getServiceContainer();
|
|
296
296
|
if (container.services.cursorDeliveryPipeline) {
|
|
297
297
|
const pipeline = container.get('cursorDeliveryPipeline');
|
|
@@ -311,9 +311,9 @@ export async function dimensionComplete(ctx, args) {
|
|
|
311
311
|
// R5: 自动触发 Wiki 生成 (fire-and-forget)
|
|
312
312
|
setImmediate(async () => {
|
|
313
313
|
try {
|
|
314
|
-
const { getServiceContainer: getWikiContainer } = await import('
|
|
314
|
+
const { getServiceContainer: getWikiContainer } = await import('../../../injection/ServiceContainer.js');
|
|
315
315
|
const wikiContainer = getWikiContainer();
|
|
316
|
-
const { WikiGenerator } = await import('
|
|
316
|
+
const { WikiGenerator } = await import('../../../service/wiki/WikiGenerator.js');
|
|
317
317
|
const moduleService = wikiContainer.get?.('moduleService');
|
|
318
318
|
const knowledgeService = wikiContainer.get?.('knowledgeService');
|
|
319
319
|
if (moduleService && knowledgeService) {
|
|
@@ -334,11 +334,11 @@ export async function dimensionComplete(ctx, args) {
|
|
|
334
334
|
// R6: 全量 Semantic Memory 固化 (fire-and-forget)
|
|
335
335
|
setImmediate(async () => {
|
|
336
336
|
try {
|
|
337
|
-
const { EpisodicConsolidator } = await import('../../../service/
|
|
337
|
+
const { EpisodicConsolidator } = await import('../../../service/agent/domain/EpisodicConsolidator.js');
|
|
338
338
|
const db = ctx.container.get?.('database') ?? ctx.container.get?.('db');
|
|
339
339
|
if (db && session.sessionStore) {
|
|
340
|
-
const {
|
|
341
|
-
const semanticMemory = new
|
|
340
|
+
const { PersistentMemory } = await import('../../../service/agent/memory/PersistentMemory.js');
|
|
341
|
+
const semanticMemory = new PersistentMemory(db, { logger });
|
|
342
342
|
const consolidator = new EpisodicConsolidator(semanticMemory, { logger });
|
|
343
343
|
const result = await consolidator.consolidate(session.sessionStore, {
|
|
344
344
|
bootstrapSession: session.id,
|
package/lib/http/HttpServer.js
CHANGED
|
@@ -36,7 +36,7 @@ import recipesRouter from './routes/recipes.js';
|
|
|
36
36
|
import searchRouter from './routes/search.js';
|
|
37
37
|
import skillsRouter from './routes/skills.js';
|
|
38
38
|
import snippetRouter from './routes/snippets.js';
|
|
39
|
-
import spmRouter from '
|
|
39
|
+
import spmRouter from '../platform/ios/routes/spm.js';
|
|
40
40
|
import taskRouter from './routes/task.js';
|
|
41
41
|
import remoteRouter from './routes/remote.js';
|
|
42
42
|
import violationsRouter from './routes/violations.js';
|
package/lib/http/routes/ai.js
CHANGED
|
@@ -9,6 +9,18 @@ import express from 'express';
|
|
|
9
9
|
import { createProvider } from '../../external/ai/AiFactory.js';
|
|
10
10
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
11
11
|
import { getServiceContainer } from '../../injection/ServiceContainer.js';
|
|
12
|
+
import { AgentMessage, Channel } from '../../service/agent/AgentMessage.js';
|
|
13
|
+
import { PRESETS } from '../../service/agent/presets.js';
|
|
14
|
+
import { ContextWindow } from '../../service/agent/context/ContextWindow.js';
|
|
15
|
+
import { ConversationStore } from '../../service/agent/ConversationStore.js';
|
|
16
|
+
import { buildProjectBriefing, cleanFinalAnswer } from '../../service/agent/core/ChatAgentPrompts.js';
|
|
17
|
+
import {
|
|
18
|
+
taskCheckAndSubmit,
|
|
19
|
+
taskDiscoverAllRelations,
|
|
20
|
+
taskFullEnrich,
|
|
21
|
+
taskQualityAudit,
|
|
22
|
+
taskGuardFullScan,
|
|
23
|
+
} from '../../service/agent/domain/ChatAgentTasks.js';
|
|
12
24
|
import { ValidationError } from '../../shared/errors/index.js';
|
|
13
25
|
import { asyncHandler } from '../middleware/errorHandler.js';
|
|
14
26
|
import { createStreamSession, getStreamSession } from '../utils/sse-sessions.js';
|
|
@@ -16,10 +28,9 @@ import { createStreamSession, getStreamSession } from '../utils/sse-sessions.js'
|
|
|
16
28
|
const router = express.Router();
|
|
17
29
|
const logger = Logger.getInstance();
|
|
18
30
|
|
|
19
|
-
/** 获取
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
return container.get('chatAgent');
|
|
31
|
+
/** 获取 DI 容器 */
|
|
32
|
+
function getContainer() {
|
|
33
|
+
return getServiceContainer();
|
|
23
34
|
}
|
|
24
35
|
|
|
25
36
|
// ═══════════════════════════════════════════════════════
|
|
@@ -33,8 +44,8 @@ function getChatAgent() {
|
|
|
33
44
|
router.get(
|
|
34
45
|
'/lang',
|
|
35
46
|
asyncHandler(async (req, res) => {
|
|
36
|
-
const
|
|
37
|
-
res.json({ success: true, data: { lang:
|
|
47
|
+
const container = getContainer();
|
|
48
|
+
res.json({ success: true, data: { lang: container.getLang() || 'zh' } });
|
|
38
49
|
})
|
|
39
50
|
);
|
|
40
51
|
|
|
@@ -49,8 +60,8 @@ router.post(
|
|
|
49
60
|
if (lang !== 'zh' && lang !== 'en') {
|
|
50
61
|
throw new ValidationError('lang must be "zh" or "en"');
|
|
51
62
|
}
|
|
52
|
-
const
|
|
53
|
-
|
|
63
|
+
const container = getContainer();
|
|
64
|
+
container.setLang(lang);
|
|
54
65
|
logger.info(`UI language preference updated to "${lang}"`);
|
|
55
66
|
res.json({ success: true, data: { lang } });
|
|
56
67
|
})
|
|
@@ -168,8 +179,13 @@ router.post(
|
|
|
168
179
|
throw new ValidationError('code is required');
|
|
169
180
|
}
|
|
170
181
|
|
|
171
|
-
const
|
|
172
|
-
const
|
|
182
|
+
const container = getContainer();
|
|
183
|
+
const factory = container.get('agentFactory');
|
|
184
|
+
const result = await factory.scanKnowledge({
|
|
185
|
+
label: 'code',
|
|
186
|
+
files: [{ name: 'code', content: code, language }],
|
|
187
|
+
task: 'summarize',
|
|
188
|
+
});
|
|
173
189
|
|
|
174
190
|
if (result?.error) {
|
|
175
191
|
throw new ValidationError(result.error);
|
|
@@ -196,8 +212,9 @@ router.post(
|
|
|
196
212
|
}
|
|
197
213
|
|
|
198
214
|
try {
|
|
199
|
-
const
|
|
200
|
-
const
|
|
215
|
+
const container = getContainer();
|
|
216
|
+
const factory = container.get('agentFactory');
|
|
217
|
+
const result = await factory.translateToEnglish(summary, usageGuide);
|
|
201
218
|
|
|
202
219
|
if (result?.error) {
|
|
203
220
|
// AI 不可用,降级返回原文
|
|
@@ -224,26 +241,123 @@ router.post(
|
|
|
224
241
|
/**
|
|
225
242
|
* POST /api/v1/ai/chat
|
|
226
243
|
* AI 对话(RAG 模式,结合项目知识库)
|
|
244
|
+
*
|
|
245
|
+
* 增强特性 (Engine Migration):
|
|
246
|
+
* - 对话持久化 (ConversationStore)
|
|
247
|
+
* - ContextWindow 上下文窗口管理
|
|
248
|
+
* - Token 用量持久化
|
|
249
|
+
* - 项目概况注入 (buildProjectBriefing)
|
|
250
|
+
* - SSE 流式最终回答 (text:start/delta/end)
|
|
251
|
+
* - MemoryCoordinator 记忆提取
|
|
227
252
|
*/
|
|
228
253
|
router.post(
|
|
229
254
|
'/chat',
|
|
230
255
|
asyncHandler(async (req, res) => {
|
|
231
|
-
const { prompt, history = [], lang } = req.body;
|
|
256
|
+
const { prompt, history = [], lang, conversationId } = req.body;
|
|
232
257
|
|
|
233
258
|
if (!prompt) {
|
|
234
259
|
throw new ValidationError('prompt is required');
|
|
235
260
|
}
|
|
236
261
|
|
|
237
|
-
const
|
|
238
|
-
const
|
|
262
|
+
const container = getContainer();
|
|
263
|
+
const factory = container.get('agentFactory');
|
|
264
|
+
|
|
265
|
+
// ── 对话持久化: 从 ConversationStore 加载历史 ──
|
|
266
|
+
let convStore = null;
|
|
267
|
+
let effectiveHistory = history;
|
|
268
|
+
let effectiveConvId = conversationId || null;
|
|
269
|
+
try {
|
|
270
|
+
const projectRoot = container.get('projectRoot') || process.cwd();
|
|
271
|
+
convStore = new ConversationStore(projectRoot);
|
|
272
|
+
if (effectiveConvId) {
|
|
273
|
+
effectiveHistory = convStore.load(effectiveConvId);
|
|
274
|
+
convStore.append(effectiveConvId, { role: 'user', content: prompt });
|
|
275
|
+
} else {
|
|
276
|
+
effectiveConvId = convStore.create({ category: 'user', title: prompt.slice(0, 50) });
|
|
277
|
+
convStore.append(effectiveConvId, { role: 'user', content: prompt });
|
|
278
|
+
}
|
|
279
|
+
} catch {
|
|
280
|
+
/* ConversationStore 不可用时静默降级 */
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ── 项目概况刷新 ──
|
|
284
|
+
let projectBriefing = '';
|
|
285
|
+
try {
|
|
286
|
+
projectBriefing = await buildProjectBriefing({ container });
|
|
287
|
+
} catch { /* 静默降级 */ }
|
|
288
|
+
|
|
289
|
+
// ── 创建 ContextWindow ──
|
|
290
|
+
const contextWindow = factory.createContextWindow({ isSystem: false });
|
|
291
|
+
|
|
292
|
+
// ── 创建 Runtime 并注入 onProgress ──
|
|
293
|
+
const message = AgentMessage.fromHttp(req);
|
|
294
|
+
// 加载持久化历史到 message
|
|
295
|
+
if (effectiveHistory.length > 0) {
|
|
296
|
+
message.history = effectiveHistory;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const runtime = factory.createChat({
|
|
300
|
+
lang,
|
|
301
|
+
onProgress: (event) => {
|
|
302
|
+
// SSE 流式进度 (如果前端通过 SSE 建立了连接)
|
|
303
|
+
try {
|
|
304
|
+
const sessionId = req.body.sseSessionId;
|
|
305
|
+
if (sessionId) {
|
|
306
|
+
const session = getStreamSession(sessionId);
|
|
307
|
+
if (session) session.send(event);
|
|
308
|
+
}
|
|
309
|
+
} catch { /* SSE 不可用时静默 */ }
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
const result = await runtime.execute(message);
|
|
313
|
+
|
|
314
|
+
// ── 持久化 assistant 回复 ──
|
|
315
|
+
if (convStore && effectiveConvId && result.reply) {
|
|
316
|
+
try {
|
|
317
|
+
convStore.append(effectiveConvId, { role: 'assistant', content: result.reply });
|
|
318
|
+
} catch { /* 静默降级 */ }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ── MemoryCoordinator: 提取记忆 ──
|
|
322
|
+
try {
|
|
323
|
+
const memoryCoordinator = container.get('memoryCoordinator');
|
|
324
|
+
if (memoryCoordinator) {
|
|
325
|
+
memoryCoordinator.extractFromConversation?.(prompt, result.reply, 'user');
|
|
326
|
+
}
|
|
327
|
+
} catch { /* 静默降级 */ }
|
|
328
|
+
|
|
329
|
+
// ── Token 用量持久化 ──
|
|
330
|
+
try {
|
|
331
|
+
const tokenStore = container.get('tokenUsageStore');
|
|
332
|
+
if (tokenStore && result.tokenUsage) {
|
|
333
|
+
const aiProvider = container.singletons?.aiProvider;
|
|
334
|
+
tokenStore.record({
|
|
335
|
+
source: 'user',
|
|
336
|
+
dimension: null,
|
|
337
|
+
provider: aiProvider?.name || null,
|
|
338
|
+
model: aiProvider?.model || null,
|
|
339
|
+
inputTokens: result.tokenUsage.input || 0,
|
|
340
|
+
outputTokens: result.tokenUsage.output || 0,
|
|
341
|
+
durationMs: result.durationMs || 0,
|
|
342
|
+
toolCalls: result.toolCalls?.length || 0,
|
|
343
|
+
sessionId: effectiveConvId,
|
|
344
|
+
});
|
|
345
|
+
// 通知前端 token 用量变化
|
|
346
|
+
try {
|
|
347
|
+
const realtime = container.get('realtimeService');
|
|
348
|
+
realtime?.broadcastTokenUsageUpdated?.();
|
|
349
|
+
} catch { /* optional */ }
|
|
350
|
+
}
|
|
351
|
+
} catch { /* token logging should never break execution */ }
|
|
239
352
|
|
|
240
353
|
res.json({
|
|
241
354
|
success: true,
|
|
242
355
|
data: {
|
|
243
356
|
reply: result.reply,
|
|
244
|
-
hasContext: result.hasContext,
|
|
245
357
|
toolCalls: result.toolCalls,
|
|
246
|
-
|
|
358
|
+
iterations: result.iterations || null,
|
|
359
|
+
conversationId: effectiveConvId,
|
|
360
|
+
tokenUsage: result.tokenUsage || null,
|
|
247
361
|
},
|
|
248
362
|
});
|
|
249
363
|
})
|
|
@@ -263,8 +377,9 @@ router.post(
|
|
|
263
377
|
throw new ValidationError('tool name is required');
|
|
264
378
|
}
|
|
265
379
|
|
|
266
|
-
const
|
|
267
|
-
const
|
|
380
|
+
const container = getContainer();
|
|
381
|
+
const factory = container.get('agentFactory');
|
|
382
|
+
const result = await factory.invokeAgent(tool, params);
|
|
268
383
|
|
|
269
384
|
res.json({ success: true, data: result });
|
|
270
385
|
})
|
|
@@ -274,7 +389,19 @@ router.post(
|
|
|
274
389
|
* POST /api/v1/ai/agent/task
|
|
275
390
|
* 执行预定义任务流(查重提交 / 批量关系发现 / 批量补全)
|
|
276
391
|
* Body: { task: string, params: object }
|
|
392
|
+
*
|
|
393
|
+
* 支持两种任务类型:
|
|
394
|
+
* 1. ToolRegistry 注册的工具 (直接通过 toolName 调用)
|
|
395
|
+
* 2. ChatAgentTasks 的 5 个预定义 DAG 任务
|
|
277
396
|
*/
|
|
397
|
+
const DAG_TASK_HANDLERS = {
|
|
398
|
+
check_and_submit: taskCheckAndSubmit,
|
|
399
|
+
discover_all_relations: taskDiscoverAllRelations,
|
|
400
|
+
full_enrich: taskFullEnrich,
|
|
401
|
+
quality_audit: taskQualityAudit,
|
|
402
|
+
guard_full_scan: taskGuardFullScan,
|
|
403
|
+
};
|
|
404
|
+
|
|
278
405
|
router.post(
|
|
279
406
|
'/agent/task',
|
|
280
407
|
asyncHandler(async (req, res) => {
|
|
@@ -284,8 +411,25 @@ router.post(
|
|
|
284
411
|
throw new ValidationError('task name is required');
|
|
285
412
|
}
|
|
286
413
|
|
|
287
|
-
const
|
|
288
|
-
const
|
|
414
|
+
const container = getContainer();
|
|
415
|
+
const factory = container.get('agentFactory');
|
|
416
|
+
|
|
417
|
+
// 优先尝试 DAG 任务
|
|
418
|
+
const dagHandler = DAG_TASK_HANDLERS[task];
|
|
419
|
+
if (dagHandler) {
|
|
420
|
+
const aiProvider = container.singletons?.aiProvider;
|
|
421
|
+
const taskContext = {
|
|
422
|
+
invokeAgent: (name, p) => factory.invokeAgent(name, p),
|
|
423
|
+
aiProvider,
|
|
424
|
+
container,
|
|
425
|
+
logger,
|
|
426
|
+
};
|
|
427
|
+
const result = await dagHandler(taskContext, params);
|
|
428
|
+
return res.json({ success: true, data: result });
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// 回退到 Agent 工具执行
|
|
432
|
+
const result = await factory.invokeAgent(task, params);
|
|
289
433
|
|
|
290
434
|
res.json({ success: true, data: result });
|
|
291
435
|
})
|
|
@@ -298,8 +442,29 @@ router.post(
|
|
|
298
442
|
router.get(
|
|
299
443
|
'/agent/capabilities',
|
|
300
444
|
asyncHandler(async (req, res) => {
|
|
301
|
-
const
|
|
302
|
-
|
|
445
|
+
const container = getContainer();
|
|
446
|
+
const toolRegistry = container.get('toolRegistry');
|
|
447
|
+
const tools = toolRegistry.getToolSchemas();
|
|
448
|
+
const presets = Object.entries(PRESETS).map(([name, p]) => ({
|
|
449
|
+
name,
|
|
450
|
+
description: p.description,
|
|
451
|
+
capabilities: p.capabilities,
|
|
452
|
+
strategy: p.strategy?.type || 'single',
|
|
453
|
+
}));
|
|
454
|
+
res.json({
|
|
455
|
+
success: true,
|
|
456
|
+
data: {
|
|
457
|
+
tools,
|
|
458
|
+
presets,
|
|
459
|
+
tasks: [
|
|
460
|
+
{ name: 'check_and_submit', description: '提交候选前自动查重 + 质量预评' },
|
|
461
|
+
{ name: 'discover_all_relations', description: '批量发现 Recipe 之间的知识图谱关系' },
|
|
462
|
+
{ name: 'full_enrich', description: '批量 AI 语义补全候选字段' },
|
|
463
|
+
{ name: 'quality_audit', description: '批量质量审计全部 Recipe,标记低分项' },
|
|
464
|
+
{ name: 'guard_full_scan', description: '用全部 Guard 规则扫描指定代码,生成完整报告' },
|
|
465
|
+
],
|
|
466
|
+
},
|
|
467
|
+
});
|
|
303
468
|
})
|
|
304
469
|
);
|
|
305
470
|
|
|
@@ -511,7 +676,7 @@ router.post(
|
|
|
511
676
|
|
|
512
677
|
/**
|
|
513
678
|
* POST /api/v1/ai/chat/stream
|
|
514
|
-
* 启动 AI 对话流 — 创建 session,后台执行
|
|
679
|
+
* 启动 AI 对话流 — 创建 session,后台执行 AgentRuntime,立即返回 sessionId
|
|
515
680
|
*
|
|
516
681
|
* 客户端拿到 sessionId 后通过 GET /chat/events/:sessionId (EventSource) 消费事件
|
|
517
682
|
*
|
|
@@ -537,27 +702,67 @@ router.post(
|
|
|
537
702
|
throw new ValidationError('prompt is required');
|
|
538
703
|
}
|
|
539
704
|
|
|
540
|
-
const
|
|
705
|
+
const container = getContainer();
|
|
706
|
+
const factory = container.get('agentFactory');
|
|
541
707
|
const session = createStreamSession('chat');
|
|
542
708
|
|
|
543
709
|
logger.debug('SSE session created', { sessionId: session.sessionId });
|
|
544
710
|
|
|
545
|
-
// 立即返回 sessionId(不等待
|
|
711
|
+
// 立即返回 sessionId(不等待 Agent 执行)
|
|
546
712
|
res.json({ success: true, sessionId: session.sessionId });
|
|
547
713
|
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
}
|
|
714
|
+
// AgentMessage 构建
|
|
715
|
+
const message = new AgentMessage({
|
|
716
|
+
content: prompt,
|
|
717
|
+
channel: Channel.HTTP,
|
|
718
|
+
session: { id: session.sessionId, history },
|
|
719
|
+
sender: { id: req.ip || 'http-user', type: 'user' },
|
|
720
|
+
metadata: { lang, stream: true },
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
// 创建 Runtime — 挂载 onProgress 回调映射到 SSE 事件
|
|
724
|
+
const runtime = factory.createChat({
|
|
725
|
+
lang,
|
|
726
|
+
onProgress: (event) => {
|
|
727
|
+
// 将 AgentRuntime 内部事件映射到前端 SSE 协议
|
|
728
|
+
switch (event.type) {
|
|
729
|
+
case 'thinking':
|
|
730
|
+
session.send({ type: 'step:start', step: event.iteration, maxSteps: event.maxIterations, phase: 'thinking' });
|
|
731
|
+
break;
|
|
732
|
+
case 'tool_call':
|
|
733
|
+
session.send({ type: 'tool:start', tool: event.tool, args: event.args });
|
|
734
|
+
break;
|
|
735
|
+
case 'tool_end':
|
|
736
|
+
session.send({
|
|
737
|
+
type: 'tool:end',
|
|
738
|
+
tool: event.tool,
|
|
739
|
+
status: event.status,
|
|
740
|
+
resultSize: event.resultSize,
|
|
741
|
+
duration: event.duration,
|
|
742
|
+
error: event.error,
|
|
743
|
+
});
|
|
744
|
+
break;
|
|
745
|
+
default:
|
|
746
|
+
session.send(event);
|
|
747
|
+
}
|
|
748
|
+
},
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
// 后台执行 AgentRuntime
|
|
752
|
+
runtime
|
|
753
|
+
.execute(message)
|
|
555
754
|
.then((result) => {
|
|
556
|
-
//
|
|
755
|
+
// 发送最终文本
|
|
756
|
+
if (result.reply) {
|
|
757
|
+
const textId = `text_${Date.now()}`;
|
|
758
|
+
session.send({ type: 'text:start', id: textId, role: 'assistant' });
|
|
759
|
+
session.send({ type: 'text:delta', id: textId, delta: result.reply });
|
|
760
|
+
session.send({ type: 'text:end', id: textId });
|
|
761
|
+
}
|
|
557
762
|
session.end({
|
|
558
763
|
text: result.reply,
|
|
559
764
|
toolCalls: result.toolCalls || [],
|
|
560
|
-
|
|
765
|
+
iterations: result.iterations || 0,
|
|
561
766
|
});
|
|
562
767
|
logger.debug('SSE session completed', {
|
|
563
768
|
sessionId: session.sessionId,
|
|
@@ -75,10 +75,9 @@ router.post(
|
|
|
75
75
|
// 获取用户语言偏好
|
|
76
76
|
let lang = 'en';
|
|
77
77
|
try {
|
|
78
|
-
|
|
79
|
-
lang = chatAgent?.getLang?.() || 'en';
|
|
78
|
+
lang = container.getLang?.() || 'en';
|
|
80
79
|
} catch {
|
|
81
|
-
/*
|
|
80
|
+
/* lang not available */
|
|
82
81
|
}
|
|
83
82
|
enriched = await aiProvider.enrichCandidates(candidates, { lang });
|
|
84
83
|
} catch (err) {
|
|
@@ -17,7 +17,7 @@ const logger = Logger.getInstance();
|
|
|
17
17
|
/**
|
|
18
18
|
* POST /api/v1/extract/path
|
|
19
19
|
* 从文件路径提取代码片段
|
|
20
|
-
* 管线: RecipeParser(MD解析) → AI 提取(
|
|
20
|
+
* 管线: RecipeParser(MD解析) → AI 提取(AgentRuntime) → 原始兜底
|
|
21
21
|
*/
|
|
22
22
|
router.post(
|
|
23
23
|
'/path',
|
|
@@ -55,14 +55,15 @@ router.post(
|
|
|
55
55
|
!items[0].frontmatter?.title;
|
|
56
56
|
|
|
57
57
|
if (isRawFallback) {
|
|
58
|
-
// 3. 尝试
|
|
58
|
+
// 3. 尝试 AI 提取
|
|
59
59
|
try {
|
|
60
|
-
const
|
|
60
|
+
const agentFactory = container.get('agentFactory');
|
|
61
61
|
const file = items[0];
|
|
62
62
|
const fileName = basename(relativePath); // 保留扩展名: BDMineViewController.m
|
|
63
|
-
const aiResult = await
|
|
64
|
-
|
|
63
|
+
const aiResult = await agentFactory.scanKnowledge({
|
|
64
|
+
label: fileName,
|
|
65
65
|
files: [{ name: fileName, content: file.code || '' }],
|
|
66
|
+
task: 'extract',
|
|
66
67
|
comprehensive: true,
|
|
67
68
|
});
|
|
68
69
|
|
|
@@ -102,7 +103,7 @@ router.post(
|
|
|
102
103
|
/**
|
|
103
104
|
* POST /api/v1/extract/text
|
|
104
105
|
* 从文本内容提取代码片段(剪贴板等)
|
|
105
|
-
* 管线: RecipeParser(MD解析) → AI 提取(
|
|
106
|
+
* 管线: RecipeParser(MD解析) → AI 提取(AgentRuntime) → 基础兜底
|
|
106
107
|
*/
|
|
107
108
|
router.post(
|
|
108
109
|
'/text',
|
|
@@ -138,18 +139,19 @@ router.post(
|
|
|
138
139
|
});
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
// 2. Recipe MD 解析失败 → 尝试
|
|
142
|
+
// 2. Recipe MD 解析失败 → 尝试 AI 提取
|
|
142
143
|
try {
|
|
143
|
-
const
|
|
144
|
-
if (
|
|
144
|
+
const agentFactory = container.get('agentFactory');
|
|
145
|
+
if (agentFactory) {
|
|
145
146
|
const lang =
|
|
146
147
|
language ||
|
|
147
148
|
(relativePath ? LanguageService.inferLang(relativePath) || 'unknown' : 'unknown');
|
|
148
149
|
const ext = LanguageService.extForLang(lang) || '.txt';
|
|
149
150
|
const fileName = relativePath ? basename(relativePath) : `clipboard${ext}`;
|
|
150
|
-
const aiResult = await
|
|
151
|
-
|
|
151
|
+
const aiResult = await agentFactory.scanKnowledge({
|
|
152
|
+
label: fileName,
|
|
152
153
|
files: [{ name: fileName, content: text }],
|
|
154
|
+
task: 'extract',
|
|
153
155
|
comprehensive: true,
|
|
154
156
|
});
|
|
155
157
|
|
|
@@ -531,11 +531,11 @@ router.post(
|
|
|
531
531
|
const { maxFiles, skipGuard, contentMaxLines } = req.body || {};
|
|
532
532
|
|
|
533
533
|
const container = getServiceContainer();
|
|
534
|
-
const
|
|
534
|
+
const agentFactory = container.get('agentFactory');
|
|
535
535
|
|
|
536
536
|
logger.info('Bootstrap cold start initiated (ModuleService path)');
|
|
537
537
|
|
|
538
|
-
const bootstrapResult = await
|
|
538
|
+
const bootstrapResult = await agentFactory.bootstrapKnowledge({
|
|
539
539
|
maxFiles: maxFiles || 500,
|
|
540
540
|
skipGuard: skipGuard || false,
|
|
541
541
|
contentMaxLines: contentMaxLines || 120,
|
|
@@ -70,15 +70,15 @@ router.post(
|
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
// 检查
|
|
73
|
+
// 检查 ToolRegistry 是否可用
|
|
74
74
|
const container = getServiceContainer();
|
|
75
|
-
let
|
|
75
|
+
let agentFactory;
|
|
76
76
|
try {
|
|
77
|
-
|
|
77
|
+
agentFactory = container.get('agentFactory');
|
|
78
78
|
} catch {
|
|
79
79
|
return res.json({
|
|
80
80
|
success: true,
|
|
81
|
-
data: { status: 'error', error: '
|
|
81
|
+
data: { status: 'error', error: 'AgentFactory 不可用,请检查 AI Provider 配置' },
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -111,7 +111,11 @@ router.post(
|
|
|
111
111
|
// 异步执行,不 await
|
|
112
112
|
(async () => {
|
|
113
113
|
try {
|
|
114
|
-
const result = await
|
|
114
|
+
const result = await agentFactory.scanKnowledge({
|
|
115
|
+
label: 'knowledge-graph',
|
|
116
|
+
files: [],
|
|
117
|
+
task: 'relations',
|
|
118
|
+
});
|
|
115
119
|
discoverTask.status = 'done';
|
|
116
120
|
discoverTask.finishedAt = new Date().toISOString();
|
|
117
121
|
discoverTask.discovered = result.discovered || 0;
|