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
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolExecutionPipeline — 工具执行的中间件管道
|
|
3
|
+
*
|
|
4
|
+
* 将 reactLoop 中 ~120 行的工具执行逻辑拆分为独立中间件:
|
|
5
|
+
* before → execute → after
|
|
6
|
+
*
|
|
7
|
+
* 每个中间件负责一个横切关注点:
|
|
8
|
+
* 1. EventBusPublisher — 事件发布
|
|
9
|
+
* 2. ProgressEmitter — 进度回调
|
|
10
|
+
* 3. SafetyGate — SafetyPolicy 安全拦截
|
|
11
|
+
* 4. CacheCheck — MemoryCoordinator 缓存命中
|
|
12
|
+
* 5. ObservationRecord — 记忆记录
|
|
13
|
+
* 6. TrackerSignal — ExplorationTracker 信号收集
|
|
14
|
+
* 7. TraceRecord — ActiveContext 推理链记录
|
|
15
|
+
* 8. SubmitDedup — 提交去重
|
|
16
|
+
*
|
|
17
|
+
* @module core/ToolExecutionPipeline
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { SafetyPolicy } from '../policies.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {Object} ToolCall
|
|
24
|
+
* @property {string} name — 工具名称
|
|
25
|
+
* @property {Object} args — 工具参数
|
|
26
|
+
* @property {string} id — 调用 ID
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {Object} ToolExecContext
|
|
31
|
+
* @property {import('../AgentRuntime.js').AgentRuntime} runtime — 运行时实例
|
|
32
|
+
* @property {import('./LoopContext.js').LoopContext} loopCtx — 循环上下文
|
|
33
|
+
* @property {number} iteration — 当前迭代次数
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {Object} ToolMetadata
|
|
38
|
+
* @property {boolean} cacheHit — 是否缓存命中
|
|
39
|
+
* @property {boolean} blocked — 是否被安全策略拦截
|
|
40
|
+
* @property {boolean} isNew — 是否为新信息 (ExplorationTracker)
|
|
41
|
+
* @property {number} durationMs — 执行耗时
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {Object} ToolMiddleware
|
|
46
|
+
* @property {string} name — 中间件名称
|
|
47
|
+
* @property {Function} [before] — 前置钩子: (call, ctx, metadata) => { blocked?, result? } | void
|
|
48
|
+
* @property {Function} [after] — 后置钩子: (call, result, ctx, metadata) => void
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
export class ToolExecutionPipeline {
|
|
52
|
+
/** @type {ToolMiddleware[]} */
|
|
53
|
+
#middlewares = [];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 注册中间件
|
|
57
|
+
* @param {ToolMiddleware} middleware
|
|
58
|
+
* @returns {this}
|
|
59
|
+
*/
|
|
60
|
+
use(middleware) {
|
|
61
|
+
this.#middlewares.push(middleware);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 执行单个工具调用
|
|
67
|
+
*
|
|
68
|
+
* 执行流:
|
|
69
|
+
* 1. 依次调用 before 钩子 — 任一返回 blocked/result 则短路
|
|
70
|
+
* 2. 实际执行工具 (toolRegistry.execute)
|
|
71
|
+
* 3. 依次调用 after 钩子
|
|
72
|
+
*
|
|
73
|
+
* @param {ToolCall} call — { name, args, id }
|
|
74
|
+
* @param {ToolExecContext} context — { runtime, loopCtx, iteration }
|
|
75
|
+
* @returns {Promise<{ result: *, metadata: ToolMetadata }>}
|
|
76
|
+
*/
|
|
77
|
+
async execute(call, context) {
|
|
78
|
+
let toolResult = null;
|
|
79
|
+
const metadata = { cacheHit: false, blocked: false, isNew: false, durationMs: 0 };
|
|
80
|
+
|
|
81
|
+
// ── before 阶段 ──
|
|
82
|
+
for (const mw of this.#middlewares) {
|
|
83
|
+
if (mw.before) {
|
|
84
|
+
const verdict = await mw.before(call, context, metadata);
|
|
85
|
+
if (verdict?.blocked) {
|
|
86
|
+
toolResult = verdict.result;
|
|
87
|
+
metadata.blocked = true;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (verdict?.result !== undefined) {
|
|
91
|
+
toolResult = verdict.result;
|
|
92
|
+
metadata.cacheHit = true;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── execute 阶段 ──
|
|
99
|
+
if (toolResult === null) {
|
|
100
|
+
const t0 = Date.now();
|
|
101
|
+
try {
|
|
102
|
+
const { runtime, loopCtx } = context;
|
|
103
|
+
const safetyPolicy = runtime.policies.get?.(SafetyPolicy) || null;
|
|
104
|
+
toolResult = await runtime.toolRegistry.execute(call.name, call.args, {
|
|
105
|
+
agentId: runtime.id,
|
|
106
|
+
source: loopCtx.source || runtime.presetName,
|
|
107
|
+
container: runtime.container,
|
|
108
|
+
safetyPolicy,
|
|
109
|
+
projectRoot: runtime.projectRoot,
|
|
110
|
+
fileCache: runtime.fileCache,
|
|
111
|
+
lang: runtime.lang,
|
|
112
|
+
logger: runtime.logger || null,
|
|
113
|
+
aiProvider: runtime.aiProvider || null,
|
|
114
|
+
// ── bootstrap 维度上下文 (从 sharedState 透传) ──
|
|
115
|
+
_submittedTitles: loopCtx.sharedState?.submittedTitles || null,
|
|
116
|
+
_submittedPatterns: loopCtx.sharedState?.submittedPatterns || null,
|
|
117
|
+
_sharedState: loopCtx.sharedState || null,
|
|
118
|
+
_dimensionMeta: loopCtx.sharedState?._dimensionMeta || null,
|
|
119
|
+
_projectLanguage: loopCtx.sharedState?._projectLanguage || null,
|
|
120
|
+
_memoryCoordinator: loopCtx.memoryCoordinator || null,
|
|
121
|
+
_dimensionScopeId: loopCtx.sharedState?._dimensionScopeId || null,
|
|
122
|
+
_currentRound: loopCtx.iteration || 0,
|
|
123
|
+
});
|
|
124
|
+
} catch (err) {
|
|
125
|
+
toolResult = { error: err.message };
|
|
126
|
+
}
|
|
127
|
+
metadata.durationMs = Date.now() - t0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── after 阶段 ──
|
|
131
|
+
for (const mw of this.#middlewares) {
|
|
132
|
+
if (mw.after) {
|
|
133
|
+
await mw.after(call, toolResult, context, metadata);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { result: toolResult, metadata };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ─────────────────────────────────────────────
|
|
142
|
+
// 预置中间件
|
|
143
|
+
// ─────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* AllowlistGate — 工具白名单守卫
|
|
147
|
+
*
|
|
148
|
+
* 防止 LLM hallucinate 不在当前 capability 允许列表中的工具调用。
|
|
149
|
+
* 从 LoopContext.toolSchemas 中提取允许的工具名列表,
|
|
150
|
+
* 拒绝不在列表中的调用(返回 error 提示)。
|
|
151
|
+
*
|
|
152
|
+
* before: 如果工具不在白名单中则短路返回 error
|
|
153
|
+
*/
|
|
154
|
+
export const allowlistGate = {
|
|
155
|
+
name: 'allowlistGate',
|
|
156
|
+
before(call, ctx) {
|
|
157
|
+
const schemas = ctx.loopCtx?.toolSchemas;
|
|
158
|
+
// 如果没有 schema 列表(全工具模式),跳过检查
|
|
159
|
+
if (!schemas || schemas.length === 0) return;
|
|
160
|
+
|
|
161
|
+
const allowedNames = new Set(schemas.map(s => s.name || s.function?.name));
|
|
162
|
+
if (!allowedNames.has(call.name)) {
|
|
163
|
+
ctx.runtime.logger.warn(
|
|
164
|
+
`[ToolPipeline] ⛔ Tool "${call.name}" not in allowlist — blocked (hallucinated call)`
|
|
165
|
+
);
|
|
166
|
+
return {
|
|
167
|
+
blocked: true,
|
|
168
|
+
result: {
|
|
169
|
+
error: `工具 "${call.name}" 不可用。当前可用工具: ${[...allowedNames].slice(0, 5).join(', ')}${allowedNames.size > 5 ? '...' : ''}`,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* SafetyGate — SafetyPolicy 安全拦截
|
|
178
|
+
*
|
|
179
|
+
* before: 如果策略拒绝则短路返回 error
|
|
180
|
+
*/
|
|
181
|
+
export const safetyGate = {
|
|
182
|
+
name: 'safetyGate',
|
|
183
|
+
before(call, ctx) {
|
|
184
|
+
const check = ctx.runtime.policies.validateToolCall(call.name, call.args);
|
|
185
|
+
if (!check.ok) {
|
|
186
|
+
ctx.runtime.logger.warn(
|
|
187
|
+
`[ToolPipeline] Tool blocked by Policy: ${call.name} — ${check.reason}`
|
|
188
|
+
);
|
|
189
|
+
return { blocked: true, result: { error: check.reason } };
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* CacheCheck — MemoryCoordinator 缓存命中
|
|
196
|
+
*
|
|
197
|
+
* before: 如果缓存命中则短路返回缓存值
|
|
198
|
+
*/
|
|
199
|
+
export const cacheCheck = {
|
|
200
|
+
name: 'cacheCheck',
|
|
201
|
+
before(call, ctx) {
|
|
202
|
+
const mc = ctx.loopCtx.memoryCoordinator;
|
|
203
|
+
if (!mc) return;
|
|
204
|
+
const cached = mc.getCachedResult?.(call.name, call.args);
|
|
205
|
+
if (cached !== null && cached !== undefined) {
|
|
206
|
+
ctx.runtime.logger.info(
|
|
207
|
+
`[ToolPipeline] 🔧 CACHE HIT: ${call.name} → skipped execution`
|
|
208
|
+
);
|
|
209
|
+
return { result: cached };
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* ObservationRecord — MemoryCoordinator 观察记录
|
|
216
|
+
*
|
|
217
|
+
* after: 记录工具执行观察
|
|
218
|
+
*/
|
|
219
|
+
export const observationRecord = {
|
|
220
|
+
name: 'observationRecord',
|
|
221
|
+
after(call, result, ctx, meta) {
|
|
222
|
+
ctx.loopCtx.memoryCoordinator?.recordObservation?.(
|
|
223
|
+
call.name, call.args, result, ctx.iteration, meta.cacheHit
|
|
224
|
+
);
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* TrackerSignal — ExplorationTracker 信号收集
|
|
230
|
+
*
|
|
231
|
+
* after: 记录工具调用信号,更新 isNew 标记
|
|
232
|
+
*/
|
|
233
|
+
export const trackerSignal = {
|
|
234
|
+
name: 'trackerSignal',
|
|
235
|
+
after(call, result, ctx, meta) {
|
|
236
|
+
if (ctx.loopCtx.tracker) {
|
|
237
|
+
const r = ctx.loopCtx.tracker.recordToolCall(call.name, call.args, result);
|
|
238
|
+
meta.isNew = r.isNew;
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* TraceRecord — ActiveContext 推理链记录
|
|
245
|
+
*
|
|
246
|
+
* after: 记录 Action + Observation 到推理链
|
|
247
|
+
*/
|
|
248
|
+
export const traceRecord = {
|
|
249
|
+
name: 'traceRecord',
|
|
250
|
+
after(call, result, ctx, meta) {
|
|
251
|
+
ctx.loopCtx.trace?.recordToolCall(call.name, call.args, result, meta.isNew);
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* SubmitDedup — 提交去重
|
|
257
|
+
*
|
|
258
|
+
* after: 检查并标记重复提交 (修改 metadata)
|
|
259
|
+
*/
|
|
260
|
+
export const submitDedup = {
|
|
261
|
+
name: 'submitDedup',
|
|
262
|
+
after(call, result, ctx, meta) {
|
|
263
|
+
const { sharedState } = ctx.loopCtx;
|
|
264
|
+
if (!sharedState) return;
|
|
265
|
+
if (call.name !== 'submit_knowledge' && call.name !== 'submit_with_check') return;
|
|
266
|
+
|
|
267
|
+
const title = call.args?.title || call.args?.category || '';
|
|
268
|
+
const isRejected = typeof result === 'object' && result?.status === 'rejected';
|
|
269
|
+
const isError = typeof result === 'object' && (result?.error || result?.status === 'error');
|
|
270
|
+
|
|
271
|
+
if (!isRejected && !isError && sharedState.submittedTitles) {
|
|
272
|
+
const normalizedTitle = title.toLowerCase().trim();
|
|
273
|
+
if (sharedState.submittedTitles.has(normalizedTitle)) {
|
|
274
|
+
meta.dedupMessage = `⚠ 重复提交: "${title}" 已存在。`;
|
|
275
|
+
ctx.runtime.logger.info(`[ToolPipeline] 🔁 duplicate: "${title}"`);
|
|
276
|
+
} else {
|
|
277
|
+
sharedState.submittedTitles.add(normalizedTitle);
|
|
278
|
+
// 模式指纹去重
|
|
279
|
+
const pattern = call.args?.content?.pattern || '';
|
|
280
|
+
if (pattern.length >= 30 && sharedState.submittedPatterns) {
|
|
281
|
+
const fp = pattern
|
|
282
|
+
.replace(/\/\/[^\n]*/g, '')
|
|
283
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
284
|
+
.replace(/[\s]+/g, '')
|
|
285
|
+
.toLowerCase()
|
|
286
|
+
.slice(0, 200);
|
|
287
|
+
if (fp.length >= 20) {
|
|
288
|
+
sharedState.submittedPatterns.add(fp);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
meta.isSubmit = true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* ProgressEmitter — 进度回调 (可选,需 runtime.emitProgress 为 public)
|
|
299
|
+
*
|
|
300
|
+
* NOTE: 默认管道不包含此中间件,因为 tool_end 事件需要 resultStr.length,
|
|
301
|
+
* 而 resultStr 在管道外部计算。由 #processToolCalls 直接处理。
|
|
302
|
+
*/
|
|
303
|
+
export const progressEmitter = {
|
|
304
|
+
name: 'progressEmitter',
|
|
305
|
+
before(call, ctx) {
|
|
306
|
+
ctx.runtime.emitProgress?.('tool_call', { tool: call.name, args: call.args });
|
|
307
|
+
},
|
|
308
|
+
after(call, result, ctx, meta) {
|
|
309
|
+
ctx.runtime.emitProgress?.('tool_end', {
|
|
310
|
+
tool: call.name,
|
|
311
|
+
duration: meta.durationMs,
|
|
312
|
+
status: result?.error ? 'error' : 'ok',
|
|
313
|
+
error: result?.error || undefined,
|
|
314
|
+
});
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* EventBusPublisher — EventBus 事件发布 (可选)
|
|
320
|
+
*
|
|
321
|
+
* NOTE: 默认管道不包含此中间件。由 #processToolCalls 直接处理,
|
|
322
|
+
* 与原始 reactLoop 保持完全一致的事件顺序。
|
|
323
|
+
*/
|
|
324
|
+
export const eventBusPublisher = {
|
|
325
|
+
name: 'eventBusPublisher',
|
|
326
|
+
before(call, ctx) {
|
|
327
|
+
if (ctx.runtime.bus?.publish) {
|
|
328
|
+
ctx.runtime.bus.publish('tool:call:start', {
|
|
329
|
+
agentId: ctx.runtime.id,
|
|
330
|
+
tool: call.name,
|
|
331
|
+
}, { source: ctx.runtime.id });
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
after(call, result, ctx, meta) {
|
|
335
|
+
if (ctx.runtime.bus?.publish) {
|
|
336
|
+
ctx.runtime.bus.publish('tool:call:end', {
|
|
337
|
+
agentId: ctx.runtime.id,
|
|
338
|
+
tool: call.name,
|
|
339
|
+
durationMs: meta.durationMs,
|
|
340
|
+
success: !result?.error,
|
|
341
|
+
}, { source: ctx.runtime.id });
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// ─────────────────────────────────────────────
|
|
347
|
+
// Factory helper
|
|
348
|
+
// ─────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* 创建预配置的工具执行管道
|
|
352
|
+
*
|
|
353
|
+
* 中间件顺序:
|
|
354
|
+
* 1. safetyGate (安全拦截 — 可短路)
|
|
355
|
+
* 2. cacheCheck (缓存检查 — 可短路)
|
|
356
|
+
* 3. observationRecord (记忆记录)
|
|
357
|
+
* 4. trackerSignal (信号收集)
|
|
358
|
+
* 5. traceRecord (推理链)
|
|
359
|
+
* 6. submitDedup (提交去重)
|
|
360
|
+
*
|
|
361
|
+
* NOTE: eventBusPublisher 和 progressEmitter 不在默认管道中,
|
|
362
|
+
* 由 #processToolCalls 直接处理,以保持与原始 reactLoop 完全一致的事件顺序
|
|
363
|
+
* (progress_end 需要 resultStr.length,在管道外计算)。
|
|
364
|
+
*
|
|
365
|
+
* @returns {ToolExecutionPipeline}
|
|
366
|
+
*/
|
|
367
|
+
export function createToolPipeline() {
|
|
368
|
+
return new ToolExecutionPipeline()
|
|
369
|
+
.use(allowlistGate)
|
|
370
|
+
.use(safetyGate)
|
|
371
|
+
.use(cacheCheck)
|
|
372
|
+
.use(observationRecord)
|
|
373
|
+
.use(trackerSignal)
|
|
374
|
+
.use(traceRecord)
|
|
375
|
+
.use(submitDedup);
|
|
376
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ChatAgentTasks —
|
|
2
|
+
* ChatAgentTasks — Agent 预定义任务方法
|
|
3
3
|
*
|
|
4
|
-
* 每个任务接收 context 对象: {
|
|
5
|
-
* -
|
|
4
|
+
* 每个任务接收 context 对象: { invokeAgent, aiProvider, container, logger }
|
|
5
|
+
* - invokeAgent(toolName, params) — 直接执行工具 handler(纯数据工具)
|
|
6
6
|
* - aiProvider — AI Provider 实例
|
|
7
7
|
* - container — ServiceContainer
|
|
8
8
|
* - logger — Logger 实例
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
* 2. 顺便返回质量评估建议
|
|
15
15
|
*/
|
|
16
16
|
export async function taskCheckAndSubmit(context, { candidate, projectRoot }) {
|
|
17
|
-
const {
|
|
17
|
+
const { invokeAgent, aiProvider } = context;
|
|
18
18
|
|
|
19
19
|
// Step 1: 查重
|
|
20
|
-
const duplicates = await
|
|
20
|
+
const duplicates = await invokeAgent('check_duplicate', {
|
|
21
21
|
candidate,
|
|
22
22
|
projectRoot,
|
|
23
23
|
threshold: 0.5,
|
|
@@ -61,102 +61,23 @@ ${highSim.map((s) => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
|
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* 任务: 批量发现 Recipe 间的知识图谱关系
|
|
64
|
-
*
|
|
64
|
+
* 委托给 AgentFactory.scanKnowledge (Agent 多轮推理)
|
|
65
65
|
*/
|
|
66
66
|
export async function taskDiscoverAllRelations(context, { batchSize = 20 } = {}) {
|
|
67
|
-
const {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (!aiProvider) {
|
|
75
|
-
throw new Error('AI Provider 未配置,请先设置 API Key');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// 获取所有活跃知识条目
|
|
79
|
-
const { items = [], data = [] } = await knowledgeService.list(
|
|
80
|
-
{ lifecycle: 'active' },
|
|
81
|
-
{ page: 1, pageSize: 500 }
|
|
82
|
-
);
|
|
83
|
-
const recipes = items.length > 0 ? items : data;
|
|
84
|
-
if (recipes.length < 2) {
|
|
85
|
-
return {
|
|
86
|
-
discovered: 0,
|
|
87
|
-
totalPairs: 0,
|
|
88
|
-
message: `只有 ${recipes.length} 条 Recipe,至少需要 2 条`,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 按 batch 分组分析
|
|
93
|
-
const pairs = [];
|
|
94
|
-
for (let i = 0; i < recipes.length; i++) {
|
|
95
|
-
for (let j = i + 1; j < recipes.length; j++) {
|
|
96
|
-
pairs.push([recipes[i], recipes[j]]);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
let discovered = 0;
|
|
101
|
-
const results = [];
|
|
102
|
-
let batchErrors = 0;
|
|
103
|
-
|
|
104
|
-
// 分批处理,单批失败不终止整体
|
|
105
|
-
for (let b = 0; b < pairs.length; b += batchSize) {
|
|
106
|
-
const batch = pairs.slice(b, b + batchSize);
|
|
107
|
-
try {
|
|
108
|
-
const result = await executeTool('discover_relations', {
|
|
109
|
-
recipePairs: batch.map(([a, b]) => ({
|
|
110
|
-
a: {
|
|
111
|
-
id: a.id,
|
|
112
|
-
title: a.title,
|
|
113
|
-
category: a.category,
|
|
114
|
-
language: a.language,
|
|
115
|
-
code: String(a.content || a.code || '').substring(0, 500),
|
|
116
|
-
},
|
|
117
|
-
b: {
|
|
118
|
-
id: b.id,
|
|
119
|
-
title: b.title,
|
|
120
|
-
category: b.category,
|
|
121
|
-
language: b.language,
|
|
122
|
-
code: String(b.content || b.code || '').substring(0, 500),
|
|
123
|
-
},
|
|
124
|
-
})),
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if (result.error) {
|
|
128
|
-
batchErrors++;
|
|
129
|
-
logger.warn(
|
|
130
|
-
`[DiscoverRelations] Batch ${Math.floor(b / batchSize) + 1} error: ${result.error}`
|
|
131
|
-
);
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (result.relations) {
|
|
135
|
-
discovered += result.relations.length;
|
|
136
|
-
results.push(...result.relations);
|
|
137
|
-
}
|
|
138
|
-
} catch (err) {
|
|
139
|
-
batchErrors++;
|
|
140
|
-
logger.warn(
|
|
141
|
-
`[DiscoverRelations] Batch ${Math.floor(b / batchSize) + 1} threw: ${err.message}`
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
discovered,
|
|
148
|
-
totalPairs: pairs.length,
|
|
149
|
-
totalBatches: Math.ceil(pairs.length / batchSize),
|
|
150
|
-
batchErrors,
|
|
151
|
-
relations: results,
|
|
152
|
-
};
|
|
67
|
+
const { container } = context;
|
|
68
|
+
const agentFactory = container.get('agentFactory');
|
|
69
|
+
return agentFactory.scanKnowledge({
|
|
70
|
+
label: 'knowledge-graph',
|
|
71
|
+
files: [],
|
|
72
|
+
task: 'relations',
|
|
73
|
+
});
|
|
153
74
|
}
|
|
154
75
|
|
|
155
76
|
/**
|
|
156
77
|
* 任务: 批量 AI 补全候选语义字段
|
|
157
78
|
*/
|
|
158
79
|
export async function taskFullEnrich(context, { status = 'pending', maxCount = 50 } = {}) {
|
|
159
|
-
const {
|
|
80
|
+
const { invokeAgent, container } = context;
|
|
160
81
|
|
|
161
82
|
const knowledgeService = container.get('knowledgeService');
|
|
162
83
|
|
|
@@ -179,7 +100,7 @@ export async function taskFullEnrich(context, { status = 'pending', maxCount = 5
|
|
|
179
100
|
return { enriched: 0, message: 'All candidates already enriched' };
|
|
180
101
|
}
|
|
181
102
|
|
|
182
|
-
const result = await
|
|
103
|
+
const result = await invokeAgent('enrich_candidate', {
|
|
183
104
|
candidateIds: needEnrich.map((c) => c.id).slice(0, 20),
|
|
184
105
|
});
|
|
185
106
|
|
|
@@ -191,7 +112,7 @@ export async function taskFullEnrich(context, { status = 'pending', maxCount = 5
|
|
|
191
112
|
* 对活跃 Recipe 逐个评分,返回低于阈值的列表
|
|
192
113
|
*/
|
|
193
114
|
export async function taskQualityAudit(context, { threshold = 0.6, maxCount = 100 } = {}) {
|
|
194
|
-
const {
|
|
115
|
+
const { invokeAgent, container } = context;
|
|
195
116
|
|
|
196
117
|
const knowledgeService = container.get('knowledgeService');
|
|
197
118
|
|
|
@@ -208,7 +129,7 @@ export async function taskQualityAudit(context, { threshold = 0.6, maxCount = 10
|
|
|
208
129
|
const gradeDistribution = { A: 0, B: 0, C: 0, D: 0, F: 0 };
|
|
209
130
|
|
|
210
131
|
for (const recipe of recipes) {
|
|
211
|
-
const scoreResult = await
|
|
132
|
+
const scoreResult = await invokeAgent('quality_score', { recipe });
|
|
212
133
|
if (scoreResult.grade) {
|
|
213
134
|
gradeDistribution[scoreResult.grade] = (gradeDistribution[scoreResult.grade] || 0) + 1;
|
|
214
135
|
}
|
|
@@ -239,14 +160,14 @@ export async function taskQualityAudit(context, { threshold = 0.6, maxCount = 10
|
|
|
239
160
|
* 对代码运行全部 Guard 规则 + 生成修复建议
|
|
240
161
|
*/
|
|
241
162
|
export async function taskGuardFullScan(context, { code, language, filePath } = {}) {
|
|
242
|
-
const {
|
|
163
|
+
const { invokeAgent, aiProvider } = context;
|
|
243
164
|
|
|
244
165
|
if (!code) {
|
|
245
166
|
return { error: 'code is required' };
|
|
246
167
|
}
|
|
247
168
|
|
|
248
169
|
// Step 1: 静态检查
|
|
249
|
-
const checkResult = await
|
|
170
|
+
const checkResult = await invokeAgent('guard_check_code', {
|
|
250
171
|
code,
|
|
251
172
|
language: language || 'unknown',
|
|
252
173
|
scope: 'project',
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
* EpisodicConsolidator — Episodic → Semantic 固化引擎
|
|
3
3
|
*
|
|
4
4
|
* Bootstrap 完成后,将 SessionStore (Tier 2) 中的维度分析结果
|
|
5
|
-
* 提炼为结构化记忆,固化到
|
|
5
|
+
* 提炼为结构化记忆,固化到 PersistentMemory (Tier 3)。
|
|
6
6
|
*
|
|
7
7
|
* 固化策略 (规则化,无需额外 AI 调用):
|
|
8
8
|
* 1. 从每个维度的 findings 提取 fact 记忆
|
|
9
9
|
* 2. 从 Tier Reflections 的 crossDimensionPatterns 提取 insight 记忆
|
|
10
10
|
* 3. 从 analysisText 中提取项目级别事实 (正则匹配)
|
|
11
|
-
* 4. 使用
|
|
11
|
+
* 4. 使用 PersistentMemory.consolidate() 进行去重和合并
|
|
12
12
|
*
|
|
13
13
|
* @module EpisodicConsolidator
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import Logger from '
|
|
16
|
+
import Logger from '../../../infrastructure/logging/Logger.js';
|
|
17
17
|
|
|
18
18
|
// ──────────────────────────────────────────────────────────────
|
|
19
19
|
// 正则: 从分析文本中提取陈述性知识
|
|
@@ -57,14 +57,14 @@ const INSIGHT_PATTERNS = [
|
|
|
57
57
|
// ──────────────────────────────────────────────────────────────
|
|
58
58
|
|
|
59
59
|
export class EpisodicConsolidator {
|
|
60
|
-
/** @type {import('
|
|
60
|
+
/** @type {import('../memory/PersistentMemory.js').PersistentMemory} */
|
|
61
61
|
#semanticMemory;
|
|
62
62
|
|
|
63
63
|
/** @type {import('../../infrastructure/logging/Logger.js').default} */
|
|
64
64
|
#logger;
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* @param {import('
|
|
67
|
+
* @param {import('../memory/PersistentMemory.js').PersistentMemory} semanticMemory
|
|
68
68
|
* @param {object} [opts]
|
|
69
69
|
* @param {object} [opts.logger]
|
|
70
70
|
*/
|
|
@@ -74,9 +74,9 @@ export class EpisodicConsolidator {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
* 执行固化: SessionStore →
|
|
77
|
+
* 执行固化: SessionStore → PersistentMemory
|
|
78
78
|
*
|
|
79
|
-
* @param {import('
|
|
79
|
+
* @param {import('../memory/SessionStore.js').SessionStore} sessionStore
|
|
80
80
|
* @param {object} [opts]
|
|
81
81
|
* @param {string} [opts.bootstrapSession] — Bootstrap session ID
|
|
82
82
|
* @param {boolean} [opts.clearPrevious=false] — 是否先清除旧的 bootstrap 记忆
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* EvidenceCollector.js — 从 Analyst 工具调用中收集结构化证据
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 类型化的证据地图、探索日志和负空间信号,供 Producer
|
|
4
|
+
* Bootstrap 质量门控核心组件: 将 Analyst 阶段的 toolCall 序列转化为
|
|
5
|
+
* 类型化的证据地图、探索日志和负空间信号,供 Producer 阶段直接引用。
|
|
6
|
+
*
|
|
7
|
+
* 被 bootstrap-gate.js (buildAnalysisArtifact) 调用。
|
|
6
8
|
*
|
|
7
9
|
* 设计原则:
|
|
8
10
|
* - 不保留原始工具返回值 (体积过大)
|