autosnippet 3.2.7 → 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 +13 -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 +26 -29
- package/lib/cli/SetupService.js +1 -1
- package/lib/core/AstAnalyzer.js +27 -5
- package/lib/core/analysis/CallEdgeResolver.js +402 -0
- package/lib/core/analysis/CallGraphAnalyzer.js +367 -0
- package/lib/core/analysis/CallSiteExtractor.js +629 -0
- package/lib/core/analysis/DataFlowInferrer.js +57 -0
- package/lib/core/analysis/ImportPathResolver.js +189 -0
- package/lib/core/analysis/ImportRecord.js +105 -0
- package/lib/core/analysis/SymbolTableBuilder.js +211 -0
- package/lib/core/ast/ProjectGraph.js +8 -0
- package/lib/core/ast/lang-dart.js +352 -5
- package/lib/core/ast/lang-go.js +212 -10
- package/lib/core/ast/lang-java.js +205 -1
- package/lib/core/ast/lang-kotlin.js +330 -1
- package/lib/core/ast/lang-python.js +31 -2
- package/lib/core/ast/lang-rust.js +284 -3
- package/lib/core/ast/lang-swift.js +180 -1
- package/lib/core/ast/lang-typescript.js +290 -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/McpServer.js +1 -0
- 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 +22 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +2 -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 +311 -162
- package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +102 -7
- package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
- package/lib/external/mcp/handlers/bootstrap-external.js +9 -2
- package/lib/external/mcp/handlers/bootstrap-internal.js +19 -8
- package/lib/external/mcp/handlers/consolidated.js +9 -0
- package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
- package/lib/external/mcp/handlers/guard.js +3 -3
- package/lib/external/mcp/handlers/structure.js +62 -0
- package/lib/external/mcp/handlers/wiki-external.js +66 -3
- package/lib/external/mcp/tools.js +36 -1
- 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 +149 -270
- 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 +70 -28
- package/lib/platform/ScreenCaptureService.js +177 -0
- package/lib/platform/ios/index.js +2 -2
- package/lib/platform/ios/routes/spm.js +2 -2
- package/lib/platform/ios/spm/PackageSwiftParser.js +14 -3
- package/lib/platform/ios/spm/SpmDiscoverer.js +123 -17
- package/lib/platform/ios/spm/{SpmService.js → SpmHelper.js} +43 -675
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +1 -1
- 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 +77 -22
- package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +14 -2
- 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} +91 -123
- 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/ActiveContext.js +3 -1
- 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 +5 -4
- 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/ast-graph.js +229 -32
- 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 +33 -22
- package/lib/service/{chat → agent}/tools/infrastructure.js +6 -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/cursor/CursorDeliveryPipeline.js +167 -1
- package/lib/service/knowledge/CodeEntityGraph.js +327 -2
- package/lib/service/knowledge/KnowledgeService.js +5 -1
- package/lib/service/module/ModuleService.js +49 -73
- package/lib/service/skills/SignalCollector.js +26 -19
- package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
- package/lib/service/wiki/WikiGenerator.js +1 -1
- package/lib/shared/FieldSpec.js +1 -1
- package/lib/shared/PathGuard.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-BaGY7kJI.css +0 -1
- package/dashboard/dist/assets/index-DfHY_3ln.js +0 -128
- package/lib/core/discovery/SpmDiscoverer.js +0 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -749
- 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 -357
- 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}/tools/_shared.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
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TaskPipeline — 轻量级 DAG 任务编排引擎
|
|
3
|
-
*
|
|
4
|
-
* 从旧版 ToolOrchestrator 精简而来,保留核心 DAG 能力:
|
|
5
|
-
* 1. 步骤间依赖声明(dependsOn)
|
|
6
|
-
* 2. 参数引用映射('stepName:path' / 'input:path')
|
|
7
|
-
* 3. 拓扑排序 → 同层并行执行(Promise.all)
|
|
8
|
-
* 4. 错误策略(continue / fail)+ 重试
|
|
9
|
-
* 5. 条件跳过(when 回调)
|
|
10
|
-
*
|
|
11
|
-
* 与 ChatAgent / ToolRegistry 的关系:
|
|
12
|
-
* - TaskPipeline 不直接持有 ToolRegistry 引用
|
|
13
|
-
* - 通过 executor 函数抽象:executor(toolName, params) → result
|
|
14
|
-
* - ChatAgent 传入 this.executeTool.bind(this) 即可
|
|
15
|
-
*
|
|
16
|
-
* 使用示例:
|
|
17
|
-
* ```js
|
|
18
|
-
* const pipeline = new TaskPipeline('bootstrap_full_pipeline', [
|
|
19
|
-
* { name: 'bootstrap', tool: 'bootstrap_knowledge', params: { maxFiles: 500, loadSkills: true } },
|
|
20
|
-
* { name: 'enrich', tool: 'enrich_candidate',
|
|
21
|
-
* params: { candidateIds: 'bootstrap:bootstrapCandidates.ids' },
|
|
22
|
-
* dependsOn: ['bootstrap'] },
|
|
23
|
-
* { name: 'refine', tool: 'refine_bootstrap_candidates',
|
|
24
|
-
* params: { userPrompt: 'input:refinePrompt' },
|
|
25
|
-
* dependsOn: ['enrich'],
|
|
26
|
-
* when: (ctx) => ctx._results.bootstrap?.bootstrapCandidates?.created > 0 },
|
|
27
|
-
* ]);
|
|
28
|
-
* const result = await pipeline.execute(executor, { refinePrompt: '...' });
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* 步骤定义 (StepDef):
|
|
32
|
-
* @typedef {object} StepDef
|
|
33
|
-
* @property {string} name — 步骤唯一名称
|
|
34
|
-
* @property {string} tool — 工具名称(传给 executor)
|
|
35
|
-
* @property {object} [params={}] — 参数对象(支持引用:'stepName:path' / 'input:path' / function(ctx))
|
|
36
|
-
* @property {string[]} [dependsOn] — 依赖步骤名称数组
|
|
37
|
-
* @property {Function} [when] — 条件回调 (ctx) => boolean,返回 false 则跳过此步骤
|
|
38
|
-
* @property {string} [errorStrategy='fail'] — 'fail' 中断 | 'continue' 继续
|
|
39
|
-
* @property {number} [retries=1] — 最大执行次数(含首次)
|
|
40
|
-
* @property {number} [retryDelay=0]— 重试间隔 ms
|
|
41
|
-
* @property {Function} [transform] — (result, ctx) => transformedResult,对工具返回值后处理
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
import Logger from '../../infrastructure/logging/Logger.js';
|
|
45
|
-
|
|
46
|
-
export class TaskPipeline {
|
|
47
|
-
/** @type {string} */
|
|
48
|
-
#id;
|
|
49
|
-
/** @type {StepDef[]} */
|
|
50
|
-
#steps;
|
|
51
|
-
/** @type {import('../../infrastructure/logging/Logger.js').default} */
|
|
52
|
-
#logger;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @param {string} id — 管线唯一标识
|
|
56
|
-
* @param {StepDef[]} steps — 步骤定义数组
|
|
57
|
-
*/
|
|
58
|
-
constructor(id, steps) {
|
|
59
|
-
this.#id = id;
|
|
60
|
-
this.#steps = steps;
|
|
61
|
-
this.#logger = Logger.getInstance();
|
|
62
|
-
this.#validate();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ─── 公共 API ─────────────────────────────────────────
|
|
66
|
-
|
|
67
|
-
/** 管线 ID */
|
|
68
|
-
get id() {
|
|
69
|
-
return this.#id;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** 步骤数 */
|
|
73
|
-
get size() {
|
|
74
|
-
return this.#steps.length;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* 执行管线
|
|
79
|
-
*
|
|
80
|
-
* @param {(toolName: string, params: object) => Promise<any>} executor
|
|
81
|
-
* 工具执行函数,通常是 ChatAgent.executeTool.bind(chatAgent)
|
|
82
|
-
* @param {object} [inputs={}] — 管线初始输入(通过 'input:key' 引用)
|
|
83
|
-
* @returns {Promise<PipelineResult>}
|
|
84
|
-
*
|
|
85
|
-
* @typedef {object} PipelineResult
|
|
86
|
-
* @property {boolean} success — 全部步骤成功(或被允许失败)
|
|
87
|
-
* @property {string} pipelineId
|
|
88
|
-
* @property {object} outputs — { stepName: result }
|
|
89
|
-
* @property {object[]} trace — 执行轨迹 [{ step, status, durationMs, error? }]
|
|
90
|
-
* @property {number} durationMs
|
|
91
|
-
*/
|
|
92
|
-
async execute(executor, inputs = {}) {
|
|
93
|
-
const t0 = Date.now();
|
|
94
|
-
const ctx = {
|
|
95
|
-
_pipelineId: this.#id,
|
|
96
|
-
_inputs: inputs,
|
|
97
|
-
_results: {},
|
|
98
|
-
};
|
|
99
|
-
const trace = [];
|
|
100
|
-
|
|
101
|
-
// 拓扑排序 → 按层并行
|
|
102
|
-
const phases = this.#topologicalSort();
|
|
103
|
-
|
|
104
|
-
this.#logger.info(
|
|
105
|
-
`[TaskPipeline] "${this.#id}" start — ${this.#steps.length} steps, ${phases.length} phases`
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
for (const phase of phases) {
|
|
109
|
-
const promises = phase.map((stepName) => this.#executeStep(stepName, executor, ctx, trace));
|
|
110
|
-
const results = await Promise.allSettled(promises);
|
|
111
|
-
|
|
112
|
-
// 检查是否有 fail 策略的步骤失败
|
|
113
|
-
for (const r of results) {
|
|
114
|
-
if (r.status === 'rejected') {
|
|
115
|
-
this.#logger.error(`[TaskPipeline] "${this.#id}" aborted`, { error: r.reason?.message });
|
|
116
|
-
return {
|
|
117
|
-
success: false,
|
|
118
|
-
pipelineId: this.#id,
|
|
119
|
-
outputs: ctx._results,
|
|
120
|
-
trace,
|
|
121
|
-
durationMs: Date.now() - t0,
|
|
122
|
-
error: r.reason?.message,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
this.#logger.info(`[TaskPipeline] "${this.#id}" done — ${Date.now() - t0}ms`);
|
|
129
|
-
return {
|
|
130
|
-
success: true,
|
|
131
|
-
pipelineId: this.#id,
|
|
132
|
-
outputs: ctx._results,
|
|
133
|
-
trace,
|
|
134
|
-
durationMs: Date.now() - t0,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// ─── 内部实现 ─────────────────────────────────────────
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 执行单个步骤(含 when 判断、重试、erorrStrategy)
|
|
142
|
-
*/
|
|
143
|
-
async #executeStep(stepName, executor, ctx, trace) {
|
|
144
|
-
const step = this.#steps.find((s) => s.name === stepName);
|
|
145
|
-
const t0 = Date.now();
|
|
146
|
-
|
|
147
|
-
// ── when 条件 ──
|
|
148
|
-
if (step.when && !step.when(ctx)) {
|
|
149
|
-
const entry = { step: stepName, status: 'skipped', durationMs: 0 };
|
|
150
|
-
trace.push(entry);
|
|
151
|
-
this.#logger.debug(`[TaskPipeline] Step "${stepName}" skipped (when=false)`);
|
|
152
|
-
ctx._results[stepName] = { _skipped: true };
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// ── 解析参数 ──
|
|
157
|
-
const params = this.#resolveParams(step.params || {}, ctx);
|
|
158
|
-
|
|
159
|
-
// ── 执行(含重试)──
|
|
160
|
-
const maxAttempts = step.retries || 1;
|
|
161
|
-
let lastError;
|
|
162
|
-
|
|
163
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
164
|
-
try {
|
|
165
|
-
this.#logger.debug(`[TaskPipeline] Step "${stepName}" attempt ${attempt}/${maxAttempts}`);
|
|
166
|
-
let result = await executor(step.tool, params);
|
|
167
|
-
|
|
168
|
-
// transform 后处理
|
|
169
|
-
if (step.transform) {
|
|
170
|
-
result = step.transform(result, ctx);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
ctx._results[stepName] = result;
|
|
174
|
-
trace.push({ step: stepName, status: 'ok', durationMs: Date.now() - t0, attempt });
|
|
175
|
-
return;
|
|
176
|
-
} catch (err) {
|
|
177
|
-
lastError = err;
|
|
178
|
-
if (attempt < maxAttempts && step.retryDelay) {
|
|
179
|
-
await new Promise((r) => setTimeout(r, step.retryDelay));
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ── 全部重试失败 ──
|
|
185
|
-
const errorMsg = lastError?.message || 'unknown error';
|
|
186
|
-
trace.push({
|
|
187
|
-
step: stepName,
|
|
188
|
-
status: 'failed',
|
|
189
|
-
durationMs: Date.now() - t0,
|
|
190
|
-
error: errorMsg,
|
|
191
|
-
attempts: maxAttempts,
|
|
192
|
-
});
|
|
193
|
-
ctx._results[stepName] = { _error: errorMsg };
|
|
194
|
-
|
|
195
|
-
if ((step.errorStrategy || 'fail') === 'fail') {
|
|
196
|
-
throw new Error(`[TaskPipeline] Step "${stepName}" failed: ${errorMsg}`);
|
|
197
|
-
}
|
|
198
|
-
// errorStrategy: 'continue' → 不抛异常
|
|
199
|
-
this.#logger.warn(`[TaskPipeline] Step "${stepName}" failed (continue): ${errorMsg}`);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* 解析参数引用
|
|
204
|
-
*
|
|
205
|
-
* 规则:
|
|
206
|
-
* 'input:key' → ctx._inputs.key
|
|
207
|
-
* 'stepName:path' → ctx._results.stepName.path (支持嵌套 a.b.c)
|
|
208
|
-
* function(ctx) → 执行函数获取值
|
|
209
|
-
* 其他 → 字面值
|
|
210
|
-
*/
|
|
211
|
-
#resolveParams(params, ctx) {
|
|
212
|
-
const resolved = {};
|
|
213
|
-
for (const [key, value] of Object.entries(params)) {
|
|
214
|
-
if (typeof value === 'function') {
|
|
215
|
-
resolved[key] = value(ctx);
|
|
216
|
-
} else if (typeof value === 'string' && value.includes(':')) {
|
|
217
|
-
const colonIdx = value.indexOf(':');
|
|
218
|
-
const source = value.substring(0, colonIdx);
|
|
219
|
-
const path = value.substring(colonIdx + 1);
|
|
220
|
-
|
|
221
|
-
if (source === 'input') {
|
|
222
|
-
resolved[key] = this.#getNestedValue(ctx._inputs, path);
|
|
223
|
-
} else if (ctx._results[source] !== undefined) {
|
|
224
|
-
resolved[key] = this.#getNestedValue(ctx._results[source], path);
|
|
225
|
-
} else {
|
|
226
|
-
// 无法解析 → 保留原始字符串(可能是普通 URL 等含冒号的值)
|
|
227
|
-
resolved[key] = value;
|
|
228
|
-
}
|
|
229
|
-
} else {
|
|
230
|
-
resolved[key] = value;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return resolved;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* 嵌套取值: 'a.b.c' → obj.a.b.c
|
|
238
|
-
*/
|
|
239
|
-
#getNestedValue(obj, path) {
|
|
240
|
-
if (obj == null) {
|
|
241
|
-
return undefined;
|
|
242
|
-
}
|
|
243
|
-
return path.split('.').reduce((acc, key) => acc?.[key], obj);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* 拓扑排序 → 分层(同层可并行)
|
|
248
|
-
* @returns {string[][]} phases — [[stepA, stepB], [stepC], ...]
|
|
249
|
-
*/
|
|
250
|
-
#topologicalSort() {
|
|
251
|
-
const graph = new Map();
|
|
252
|
-
for (const step of this.#steps) {
|
|
253
|
-
graph.set(step.name, step.dependsOn || []);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// 检测环 + 拓扑排序
|
|
257
|
-
const visited = new Set();
|
|
258
|
-
const visiting = new Set();
|
|
259
|
-
const sorted = [];
|
|
260
|
-
|
|
261
|
-
const visit = (name) => {
|
|
262
|
-
if (visited.has(name)) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (visiting.has(name)) {
|
|
266
|
-
throw new Error(`[TaskPipeline] Circular dependency: ${name}`);
|
|
267
|
-
}
|
|
268
|
-
visiting.add(name);
|
|
269
|
-
for (const dep of graph.get(name) || []) {
|
|
270
|
-
visit(dep);
|
|
271
|
-
}
|
|
272
|
-
visiting.delete(name);
|
|
273
|
-
visited.add(name);
|
|
274
|
-
sorted.push(name);
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
for (const name of graph.keys()) {
|
|
278
|
-
visit(name);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// 分层:按最大依赖深度
|
|
282
|
-
const depth = new Map();
|
|
283
|
-
for (const name of sorted) {
|
|
284
|
-
const deps = graph.get(name) || [];
|
|
285
|
-
const d = deps.length === 0 ? 0 : Math.max(...deps.map((dep) => (depth.get(dep) || 0) + 1));
|
|
286
|
-
depth.set(name, d);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const maxDepth = Math.max(0, ...depth.values());
|
|
290
|
-
const phases = Array.from({ length: maxDepth + 1 }, () => []);
|
|
291
|
-
for (const [name, d] of depth) {
|
|
292
|
-
phases[d].push(name);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return phases.filter((p) => p.length > 0);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* 校验步骤定义
|
|
300
|
-
*/
|
|
301
|
-
#validate() {
|
|
302
|
-
const names = new Set();
|
|
303
|
-
for (const step of this.#steps) {
|
|
304
|
-
if (!step.name) {
|
|
305
|
-
throw new Error('[TaskPipeline] Step must have a name');
|
|
306
|
-
}
|
|
307
|
-
if (!step.tool) {
|
|
308
|
-
throw new Error(`[TaskPipeline] Step "${step.name}" must have a tool`);
|
|
309
|
-
}
|
|
310
|
-
if (names.has(step.name)) {
|
|
311
|
-
throw new Error(`[TaskPipeline] Duplicate step name: "${step.name}"`);
|
|
312
|
-
}
|
|
313
|
-
names.add(step.name);
|
|
314
|
-
}
|
|
315
|
-
// 检查 dependsOn 引用是否存在
|
|
316
|
-
for (const step of this.#steps) {
|
|
317
|
-
for (const dep of step.dependsOn || []) {
|
|
318
|
-
if (!names.has(dep)) {
|
|
319
|
-
throw new Error(`[TaskPipeline] Step "${step.name}" depends on unknown step "${dep}"`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* 获取管线的描述信息(用于调试/日志)
|
|
327
|
-
*/
|
|
328
|
-
describe() {
|
|
329
|
-
const phases = this.#topologicalSort();
|
|
330
|
-
return {
|
|
331
|
-
id: this.#id,
|
|
332
|
-
steps: this.#steps.map((s) => ({
|
|
333
|
-
name: s.name,
|
|
334
|
-
tool: s.tool,
|
|
335
|
-
dependsOn: s.dependsOn || [],
|
|
336
|
-
errorStrategy: s.errorStrategy || 'fail',
|
|
337
|
-
hasWhen: !!s.when,
|
|
338
|
-
})),
|
|
339
|
-
phases: phases.map((p, i) => ({ phase: i, parallel: p })),
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// ─── 便捷工厂函数 ────────────────────────────────────────
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* 快速创建管线(函数式 API)
|
|
348
|
-
*
|
|
349
|
-
* @param {string} id
|
|
350
|
-
* @param {StepDef[]} steps
|
|
351
|
-
* @returns {TaskPipeline}
|
|
352
|
-
*/
|
|
353
|
-
export function createPipeline(id, steps) {
|
|
354
|
-
return new TaskPipeline(id, steps);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
export default TaskPipeline;
|
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WorkingMemory — 会话级工作记忆 (Tier 1)
|
|
3
|
-
*
|
|
4
|
-
* @deprecated Phase 3: 已合并入 ActiveContext (lib/service/chat/memory/ActiveContext.js)
|
|
5
|
-
* - Scratchpad + 工具压缩策略 + buildContext + distill 已迁入 ActiveContext
|
|
6
|
-
* - 本文件保留做向后兼容,新代码请使用 ActiveContext
|
|
7
|
-
*
|
|
8
|
-
* 管理单次 ChatAgent.execute() 中的上下文压缩与关键发现暂存。
|
|
9
|
-
*
|
|
10
|
-
* 策略:
|
|
11
|
-
* 1. 最近 N 轮工具结果保留原文 (由 ContextWindow 自然携带)
|
|
12
|
-
* 2. 更早的工具结果自动压缩为摘要 (≤200 tokens/条)
|
|
13
|
-
* 3. Scratchpad: Agent 通过 note_finding 工具主动标记关键发现
|
|
14
|
-
* 4. 蒸馏: execute 结束时提取结构化数据供 EpisodicMemory 使用
|
|
15
|
-
*
|
|
16
|
-
* 生命周期: 与单次 execute() 调用一致,不持久化。
|
|
17
|
-
*
|
|
18
|
-
* @module WorkingMemory
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import Logger from '../../infrastructure/logging/Logger.js';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 工具特化压缩策略
|
|
25
|
-
* 不同工具返回不同结构,压缩时保留最有价值的部分
|
|
26
|
-
*/
|
|
27
|
-
const TOOL_COMPRESS_STRATEGIES = {
|
|
28
|
-
search_project_code(result) {
|
|
29
|
-
if (typeof result !== 'object') {
|
|
30
|
-
return String(result).substring(0, 600);
|
|
31
|
-
}
|
|
32
|
-
const matches = result.matches || [];
|
|
33
|
-
const batchResults = result.batchResults || {};
|
|
34
|
-
|
|
35
|
-
const lines = [];
|
|
36
|
-
// 单模式搜索
|
|
37
|
-
if (matches.length > 0) {
|
|
38
|
-
lines.push(`搜索到 ${matches.length} 个匹配`);
|
|
39
|
-
const fileGroups = {};
|
|
40
|
-
for (const m of matches) {
|
|
41
|
-
if (!fileGroups[m.file]) {
|
|
42
|
-
fileGroups[m.file] = [];
|
|
43
|
-
}
|
|
44
|
-
fileGroups[m.file].push(m.line);
|
|
45
|
-
}
|
|
46
|
-
for (const [file, lineNums] of Object.entries(fileGroups).slice(0, 8)) {
|
|
47
|
-
lines.push(` ${file}: L${lineNums.slice(0, 3).join(',')}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
// 批量搜索
|
|
51
|
-
for (const [pattern, sub] of Object.entries(batchResults).slice(0, 5)) {
|
|
52
|
-
const subMatches = sub.matches || [];
|
|
53
|
-
lines.push(` [${pattern}] ${subMatches.length} 个匹配`);
|
|
54
|
-
for (const m of subMatches.slice(0, 3)) {
|
|
55
|
-
lines.push(` ${m.file}:${m.line}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return lines.join('\n');
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
read_project_file(result) {
|
|
62
|
-
if (typeof result !== 'object') {
|
|
63
|
-
return String(result).substring(0, 600);
|
|
64
|
-
}
|
|
65
|
-
// 批量读取结果
|
|
66
|
-
if (result.files) {
|
|
67
|
-
const lines = [`读取 ${result.files.length} 个文件`];
|
|
68
|
-
for (const f of result.files.slice(0, 5)) {
|
|
69
|
-
const totalLines = (f.content || '').split('\n').length;
|
|
70
|
-
lines.push(` ${f.path} (${totalLines} 行)`);
|
|
71
|
-
}
|
|
72
|
-
return lines.join('\n');
|
|
73
|
-
}
|
|
74
|
-
// 单文件结果
|
|
75
|
-
const content = result.content || String(result);
|
|
76
|
-
const totalLines = content.split('\n').length;
|
|
77
|
-
return `文件 ${result.path || '?'} (${totalLines} 行)`;
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
get_class_info(result) {
|
|
81
|
-
if (typeof result !== 'object') {
|
|
82
|
-
return String(result).substring(0, 600);
|
|
83
|
-
}
|
|
84
|
-
const lines = [`类 ${result.className || '?'}`];
|
|
85
|
-
if (result.superClass) {
|
|
86
|
-
lines.push(` 继承: ${result.superClass}`);
|
|
87
|
-
}
|
|
88
|
-
if (result.protocols?.length) {
|
|
89
|
-
lines.push(` 协议: ${result.protocols.join(', ')}`);
|
|
90
|
-
}
|
|
91
|
-
if (result.methods?.length) {
|
|
92
|
-
lines.push(` 方法数: ${result.methods.length}`);
|
|
93
|
-
}
|
|
94
|
-
if (result.properties?.length) {
|
|
95
|
-
lines.push(` 属性数: ${result.properties.length}`);
|
|
96
|
-
}
|
|
97
|
-
return lines.join('\n');
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
get_class_hierarchy(result) {
|
|
101
|
-
if (typeof result !== 'object') {
|
|
102
|
-
return String(result).substring(0, 600);
|
|
103
|
-
}
|
|
104
|
-
const classes = result.classes || result.hierarchy || [];
|
|
105
|
-
return `类层级: ${Array.isArray(classes) ? classes.length : 0} 个类`;
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
get_project_overview(result) {
|
|
109
|
-
if (typeof result !== 'object') {
|
|
110
|
-
return String(result).substring(0, 800);
|
|
111
|
-
}
|
|
112
|
-
return JSON.stringify(result).substring(0, 800);
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
list_project_structure(result) {
|
|
116
|
-
if (typeof result !== 'object') {
|
|
117
|
-
return String(result).substring(0, 600);
|
|
118
|
-
}
|
|
119
|
-
const entries = result.entries || result.children || [];
|
|
120
|
-
return `目录结构: ${Array.isArray(entries) ? entries.length : 0} 个条目`;
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 默认压缩 — 截断到 maxChars
|
|
126
|
-
*/
|
|
127
|
-
function defaultCompress(result, maxChars = 600) {
|
|
128
|
-
const str = typeof result === 'string' ? result : JSON.stringify(result);
|
|
129
|
-
if (str.length <= maxChars) {
|
|
130
|
-
return str;
|
|
131
|
-
}
|
|
132
|
-
return `${str.substring(0, maxChars)}…(truncated)`;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export class WorkingMemory {
|
|
136
|
-
/** @type {Array<{toolName: string, result: any, round: number, timestamp: number}>} */
|
|
137
|
-
#recentObservations = [];
|
|
138
|
-
|
|
139
|
-
/** @type {Array<{toolName: string, round: number, summary: string}>} */
|
|
140
|
-
#compressedObservations = [];
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Scratchpad — Agent 主动标记的关键发现
|
|
144
|
-
* @type {Array<{finding: string, evidence: string, importance: number, round: number}>}
|
|
145
|
-
*/
|
|
146
|
-
#scratchpad = [];
|
|
147
|
-
|
|
148
|
-
/** @type {number} 保留最近 N 轮原始观察 */
|
|
149
|
-
#maxRecentRounds = 3;
|
|
150
|
-
|
|
151
|
-
/** @type {number} 总观察计数 */
|
|
152
|
-
#totalObservations = 0;
|
|
153
|
-
|
|
154
|
-
/** @type {import('../../infrastructure/logging/Logger.js').default} */
|
|
155
|
-
#logger;
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* @param {object} [options]
|
|
159
|
-
* @param {number} [options.maxRecentRounds=3] - 保留最近 N 轮原始结果
|
|
160
|
-
*/
|
|
161
|
-
constructor(options = {}) {
|
|
162
|
-
this.#maxRecentRounds = options.maxRecentRounds ?? 3;
|
|
163
|
-
this.#logger = Logger.getInstance();
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// ─── 核心操作 ──────────────────────────────────────────
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* 记录工具调用结果 (Observe)
|
|
170
|
-
*
|
|
171
|
-
* 自动执行滑动窗口压缩:
|
|
172
|
-
* - 最新的 maxRecentRounds 轮保留原文 (由 ContextWindow 内部携带)
|
|
173
|
-
* - 更早轮次的结果自动压缩为摘要
|
|
174
|
-
*
|
|
175
|
-
* @param {string} toolName
|
|
176
|
-
* @param {*} result - 工具返回的原始结果
|
|
177
|
-
* @param {number} round - 当前迭代轮次
|
|
178
|
-
*/
|
|
179
|
-
observe(toolName, result, round) {
|
|
180
|
-
this.#totalObservations++;
|
|
181
|
-
|
|
182
|
-
this.#recentObservations.push({
|
|
183
|
-
toolName,
|
|
184
|
-
result,
|
|
185
|
-
round,
|
|
186
|
-
timestamp: Date.now(),
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
// 滑动窗口压缩
|
|
190
|
-
while (this.#recentObservations.length > this.#maxRecentRounds) {
|
|
191
|
-
const oldest = this.#recentObservations.shift();
|
|
192
|
-
const summary = this.#compress(oldest);
|
|
193
|
-
this.#compressedObservations.push(summary);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Agent 主动记录关键发现 (note_finding 工具入口)
|
|
199
|
-
*
|
|
200
|
-
* @param {string} finding - 关键发现描述
|
|
201
|
-
* @param {string} [evidence] - 证据 (文件路径:行号)
|
|
202
|
-
* @param {number} [importance=5] - 重要性 1-10
|
|
203
|
-
* @param {number} [round=0] - 当前轮次
|
|
204
|
-
*/
|
|
205
|
-
noteKeyFinding(finding, evidence = '', importance = 5, round = 0) {
|
|
206
|
-
this.#scratchpad.push({
|
|
207
|
-
finding,
|
|
208
|
-
evidence,
|
|
209
|
-
importance: Math.min(10, Math.max(1, importance)),
|
|
210
|
-
round,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
this.#logger.debug(
|
|
214
|
-
`[WorkingMemory] 📌 noted finding (${importance}/10): ${finding.substring(0, 80)}`
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// ─── 上下文构建 ────────────────────────────────────────
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* 构建当前 Working Memory 的上下文快照
|
|
222
|
-
* 用于注入到 system prompt 或 user nudge 中
|
|
223
|
-
*
|
|
224
|
-
* @returns {string} Markdown 格式的上下文块,空字符串表示无内容
|
|
225
|
-
*/
|
|
226
|
-
buildContext() {
|
|
227
|
-
const parts = [];
|
|
228
|
-
|
|
229
|
-
// §1: Scratchpad (最高优先级 — 不会被压缩)
|
|
230
|
-
if (this.#scratchpad.length > 0) {
|
|
231
|
-
const sorted = [...this.#scratchpad].sort((a, b) => b.importance - a.importance);
|
|
232
|
-
parts.push('## 📌 已确认的关键发现');
|
|
233
|
-
for (const f of sorted) {
|
|
234
|
-
const badge = f.importance >= 8 ? '⚠️' : f.importance >= 5 ? '📋' : '💡';
|
|
235
|
-
let line = `- ${badge} [${f.importance}/10] ${f.finding}`;
|
|
236
|
-
if (f.evidence) {
|
|
237
|
-
line += ` (${f.evidence})`;
|
|
238
|
-
}
|
|
239
|
-
parts.push(line);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// §2: 压缩后的旧观察摘要 (中等优先级)
|
|
244
|
-
if (this.#compressedObservations.length > 0) {
|
|
245
|
-
parts.push('## 📂 之前的探索摘要');
|
|
246
|
-
// 只展示最近 15 条压缩摘要,避免膨胀
|
|
247
|
-
const recent = this.#compressedObservations.slice(-15);
|
|
248
|
-
for (const s of recent) {
|
|
249
|
-
parts.push(`- [R${s.round}|${s.toolName}] ${s.summary.substring(0, 200)}`);
|
|
250
|
-
}
|
|
251
|
-
if (this.#compressedObservations.length > 15) {
|
|
252
|
-
parts.push(` …(还有 ${this.#compressedObservations.length - 15} 条更早的观察)`);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// 最近原始结果由 ContextWindow 的对话历史自然携带,不重复注入
|
|
257
|
-
return parts.join('\n');
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// ─── 蒸馏 (Working → Episodic) ─────────────────────────
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* 蒸馏 Working Memory 为结构化报告
|
|
264
|
-
* 在 Agent execute 结束时调用,结果写入 EpisodicMemory
|
|
265
|
-
*
|
|
266
|
-
* @returns {{
|
|
267
|
-
* keyFindings: Array<{finding: string, evidence: string, importance: number}>,
|
|
268
|
-
* toolCallSummary: string[],
|
|
269
|
-
* totalObservations: number,
|
|
270
|
-
* compressedCount: number,
|
|
271
|
-
* }}
|
|
272
|
-
*/
|
|
273
|
-
distill() {
|
|
274
|
-
return {
|
|
275
|
-
keyFindings: this.#scratchpad.map((f) => ({
|
|
276
|
-
finding: f.finding,
|
|
277
|
-
evidence: f.evidence,
|
|
278
|
-
importance: f.importance,
|
|
279
|
-
})),
|
|
280
|
-
toolCallSummary: this.#compressedObservations.map(
|
|
281
|
-
(s) => `[${s.toolName}] ${s.summary.substring(0, 150)}`
|
|
282
|
-
),
|
|
283
|
-
totalObservations: this.#totalObservations,
|
|
284
|
-
compressedCount: this.#compressedObservations.length,
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// ─── 查询 ─────────────────────────────────────────────
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* 获取 scratchpad 中的关键发现数量
|
|
292
|
-
* @returns {number}
|
|
293
|
-
*/
|
|
294
|
-
get scratchpadSize() {
|
|
295
|
-
return this.#scratchpad.length;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* 获取总观察数
|
|
300
|
-
* @returns {number}
|
|
301
|
-
*/
|
|
302
|
-
get totalObservations() {
|
|
303
|
-
return this.#totalObservations;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* 获取 scratchpad 中的高重要性发现
|
|
308
|
-
* @param {number} [minImportance=7]
|
|
309
|
-
* @returns {Array<{finding: string, evidence: string, importance: number}>}
|
|
310
|
-
*/
|
|
311
|
-
getHighPriorityFindings(minImportance = 7) {
|
|
312
|
-
return this.#scratchpad
|
|
313
|
-
.filter((f) => f.importance >= minImportance)
|
|
314
|
-
.sort((a, b) => b.importance - a.importance);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* 清空 WorkingMemory — 释放内存
|
|
319
|
-
* 在 Agent execute 结束后调用,避免残留引用导致内存泄漏
|
|
320
|
-
*/
|
|
321
|
-
clear() {
|
|
322
|
-
this.#recentObservations.length = 0;
|
|
323
|
-
this.#compressedObservations.length = 0;
|
|
324
|
-
this.#scratchpad.length = 0;
|
|
325
|
-
this.#totalObservations = 0;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// ─── 内部 ─────────────────────────────────────────────
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* 工具结果压缩 — 使用特化策略
|
|
332
|
-
* @param {{toolName: string, result: any, round: number}} observation
|
|
333
|
-
* @returns {{toolName: string, round: number, summary: string}}
|
|
334
|
-
*/
|
|
335
|
-
#compress(observation) {
|
|
336
|
-
const strategy = TOOL_COMPRESS_STRATEGIES[observation.toolName];
|
|
337
|
-
let summary;
|
|
338
|
-
|
|
339
|
-
try {
|
|
340
|
-
if (strategy) {
|
|
341
|
-
summary = strategy(observation.result);
|
|
342
|
-
} else {
|
|
343
|
-
summary = defaultCompress(observation.result);
|
|
344
|
-
}
|
|
345
|
-
} catch {
|
|
346
|
-
summary = defaultCompress(observation.result);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return {
|
|
350
|
-
toolName: observation.toolName,
|
|
351
|
-
round: observation.round,
|
|
352
|
-
summary,
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
export default WorkingMemory;
|