@winspan/claude-forge 2.7.0 → 3.0.0
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/README.md +4 -4
- package/dist/autopilot/intent-engine.js +5 -5
- package/dist/autopilot/intent-engine.js.map +1 -1
- package/dist/autopilot/issue-tracker.d.ts +1 -1
- package/dist/autopilot/issue-tracker.d.ts.map +1 -1
- package/dist/autopilot/issue-tracker.js +2 -2
- package/dist/autopilot/issue-tracker.js.map +1 -1
- package/dist/autopilot/phase-gap-detector.d.ts +2 -1
- package/dist/autopilot/phase-gap-detector.d.ts.map +1 -1
- package/dist/autopilot/phase-gap-detector.js +1 -1
- package/dist/autopilot/phase-gap-detector.js.map +1 -1
- package/dist/autopilot/quality-gate.d.ts +1 -1
- package/dist/autopilot/quality-gate.d.ts.map +1 -1
- package/dist/autopilot/quality-gate.js +4 -4
- package/dist/autopilot/quality-gate.js.map +1 -1
- package/dist/autopilot/task-completion-detector.d.ts +1 -1
- package/dist/autopilot/task-completion-detector.d.ts.map +1 -1
- package/dist/backup/index.d.ts +2 -2
- package/dist/backup/index.d.ts.map +1 -1
- package/dist/backup/index.js +5 -7
- package/dist/backup/index.js.map +1 -1
- package/dist/claudemd/writer.d.ts +1 -0
- package/dist/claudemd/writer.d.ts.map +1 -1
- package/dist/claudemd/writer.js +12 -0
- package/dist/claudemd/writer.js.map +1 -1
- package/dist/convention/convention-manager.d.ts +2 -4
- package/dist/convention/convention-manager.d.ts.map +1 -1
- package/dist/convention/convention-manager.js +6 -25
- package/dist/convention/convention-manager.js.map +1 -1
- package/dist/core/cache-registry.d.ts +53 -0
- package/dist/core/cache-registry.d.ts.map +1 -0
- package/dist/core/cache-registry.js +93 -0
- package/dist/core/cache-registry.js.map +1 -0
- package/dist/core/pending-prompt-sink.d.ts +14 -0
- package/dist/core/pending-prompt-sink.d.ts.map +1 -0
- package/dist/core/pending-prompt-sink.js +7 -0
- package/dist/core/pending-prompt-sink.js.map +1 -0
- package/dist/core/types.d.ts +24 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +24 -0
- package/dist/core/types.js.map +1 -0
- package/dist/daemon/engine-registry/init-pipeline.d.ts.map +1 -1
- package/dist/daemon/engine-registry/init-pipeline.js +3 -1
- package/dist/daemon/engine-registry/init-pipeline.js.map +1 -1
- package/dist/daemon/engine-registry.js +2 -2
- package/dist/daemon/engine-registry.js.map +1 -1
- package/dist/daemon/handlers/post-tool-use-handler.d.ts.map +1 -1
- package/dist/daemon/handlers/post-tool-use-handler.js +4 -3
- package/dist/daemon/handlers/post-tool-use-handler.js.map +1 -1
- package/dist/daemon/handlers/pre-tool-use-handler.d.ts.map +1 -1
- package/dist/daemon/handlers/pre-tool-use-handler.js +18 -48
- package/dist/daemon/handlers/pre-tool-use-handler.js.map +1 -1
- package/dist/daemon/handlers/stages/13-template-route.js +2 -2
- package/dist/daemon/handlers/stages/13-template-route.js.map +1 -1
- package/dist/daemon/handlers/stages/18-complex-task.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/18-complex-task.js +11 -3
- package/dist/daemon/handlers/stages/18-complex-task.js.map +1 -1
- package/dist/daemon/handlers/stop-handler.js +2 -2
- package/dist/daemon/handlers/stop-handler.js.map +1 -1
- package/dist/daemon/handlers/user-prompt-handler.js +6 -6
- package/dist/daemon/handlers/user-prompt-handler.js.map +1 -1
- package/dist/distill/trigger.js +2 -2
- package/dist/distill/trigger.js.map +1 -1
- package/dist/pipeline/artifact-generator.d.ts +5 -1
- package/dist/pipeline/artifact-generator.d.ts.map +1 -1
- package/dist/pipeline/artifact-generator.js +5 -1
- package/dist/pipeline/artifact-generator.js.map +1 -1
- package/dist/pipeline/artifact-validator.d.ts.map +1 -1
- package/dist/pipeline/artifact-validator.js +9 -0
- package/dist/pipeline/artifact-validator.js.map +1 -1
- package/dist/pipeline/completion-detector.d.ts.map +1 -1
- package/dist/pipeline/completion-detector.js +10 -0
- package/dist/pipeline/completion-detector.js.map +1 -1
- package/dist/pipeline/completion-gate.d.ts +56 -0
- package/dist/pipeline/completion-gate.d.ts.map +1 -0
- package/dist/pipeline/completion-gate.js +165 -0
- package/dist/pipeline/completion-gate.js.map +1 -0
- package/dist/pipeline/dynamic-node-executor.d.ts +10 -10
- package/dist/pipeline/dynamic-node-executor.d.ts.map +1 -1
- package/dist/pipeline/dynamic-node-executor.js +132 -202
- package/dist/pipeline/dynamic-node-executor.js.map +1 -1
- package/dist/pipeline/dynamic-node-types.d.ts +2 -0
- package/dist/pipeline/dynamic-node-types.d.ts.map +1 -1
- package/dist/pipeline/event-sourced-engine.d.ts.map +1 -1
- package/dist/pipeline/event-sourced-engine.js +6 -5
- package/dist/pipeline/event-sourced-engine.js.map +1 -1
- package/dist/pipeline/execution-plan-builder.d.ts +20 -0
- package/dist/pipeline/execution-plan-builder.d.ts.map +1 -0
- package/dist/pipeline/execution-plan-builder.js +100 -0
- package/dist/pipeline/execution-plan-builder.js.map +1 -0
- package/dist/pipeline/execution-plan.d.ts +64 -0
- package/dist/pipeline/execution-plan.d.ts.map +1 -0
- package/dist/pipeline/execution-plan.js +57 -0
- package/dist/pipeline/execution-plan.js.map +1 -0
- package/dist/pipeline/i-node-executor.d.ts +2 -0
- package/dist/pipeline/i-node-executor.d.ts.map +1 -1
- package/dist/pipeline/index.d.ts +22 -20
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +157 -284
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/optimized-aggregator.d.ts.map +1 -1
- package/dist/pipeline/optimized-aggregator.js +1 -1
- package/dist/pipeline/optimized-aggregator.js.map +1 -1
- package/dist/pipeline/phase-manager.js +10 -10
- package/dist/pipeline/phase-manager.js.map +1 -1
- package/dist/pipeline/report-generator.d.ts +3 -3
- package/dist/pipeline/report-generator.d.ts.map +1 -1
- package/dist/pipeline/report-generator.js +4 -4
- package/dist/pipeline/report-generator.js.map +1 -1
- package/dist/pipeline/step-gate-keeper.d.ts +1 -1
- package/dist/pipeline/step-gate-keeper.d.ts.map +1 -1
- package/dist/pipeline/step-gate-keeper.js +2 -3
- package/dist/pipeline/step-gate-keeper.js.map +1 -1
- package/dist/pipeline/step-orchestrator.d.ts +6 -1
- package/dist/pipeline/step-orchestrator.d.ts.map +1 -1
- package/dist/pipeline/step-orchestrator.js +13 -5
- package/dist/pipeline/step-orchestrator.js.map +1 -1
- package/dist/pipeline/types.d.ts +5 -4
- package/dist/pipeline/types.d.ts.map +1 -1
- package/dist/pipeline/types.js +1 -17
- package/dist/pipeline/types.js.map +1 -1
- package/dist/storage/repositories/dynamic-pipeline-repository.d.ts.map +1 -1
- package/dist/storage/repositories/dynamic-pipeline-repository.js +11 -23
- package/dist/storage/repositories/dynamic-pipeline-repository.js.map +1 -1
- package/dist/storage/repositories/maintenance-repository.d.ts +1 -0
- package/dist/storage/repositories/maintenance-repository.d.ts.map +1 -1
- package/dist/storage/repositories/maintenance-repository.js +76 -130
- package/dist/storage/repositories/maintenance-repository.js.map +1 -1
- package/dist/storage/schema/migration-manager.d.ts +15 -0
- package/dist/storage/schema/migration-manager.d.ts.map +1 -0
- package/dist/storage/schema/migration-manager.js +868 -0
- package/dist/storage/schema/migration-manager.js.map +1 -0
- package/dist/storage/sqlite.d.ts +0 -5
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +6 -837
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/utils/forge-resume-block.d.ts +2 -1
- package/dist/utils/forge-resume-block.d.ts.map +1 -1
- package/dist/utils/forge-resume-block.js +16 -2
- package/dist/utils/forge-resume-block.js.map +1 -1
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +3 -7
- package/dist/utils/formatter.js.map +1 -1
- package/dist/utils/mutex-lock.d.ts +1 -0
- package/dist/utils/mutex-lock.d.ts.map +1 -1
- package/dist/utils/mutex-lock.js +13 -0
- package/dist/utils/mutex-lock.js.map +1 -1
- package/dist/web-static/assets/{index-nmYJCpsl.js → index-BcPVkZqZ.js} +1 -1
- package/dist/web-static/index.html +1 -1
- package/package.json +1 -1
package/dist/pipeline/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { PipelineStore } from './store.js';
|
|
2
2
|
import { PipelineAnalyzer } from './analyzer.js';
|
|
3
|
-
import { PhaseManager } from './phase-manager.js';
|
|
4
3
|
import { DynamicNodeExecutor } from './dynamic-node-executor.js';
|
|
5
|
-
import { PhaseArtifactGenerator } from './artifact-generator.js';
|
|
6
4
|
import { PipelineReportGenerator } from './report-generator.js';
|
|
7
5
|
import { PHASE_LABELS } from './types.js';
|
|
8
6
|
import { PIPELINE, TIME } from '../constants.js';
|
|
9
7
|
import { logger } from '../utils/logger.js';
|
|
10
8
|
import { ArtifactManager } from '../utils/artifact-manager.js';
|
|
11
|
-
import {
|
|
9
|
+
import { cacheRegistry } from '../core/cache-registry.js';
|
|
12
10
|
import { MutexLock } from '../utils/mutex-lock.js';
|
|
13
11
|
import { buildForgeResumeEnvelope, stripForgeResumeBlocks } from '../utils/forge-resume-block.js';
|
|
12
|
+
import { getPlanPhaseLabels, stringifyExecutionPlanMeta, } from './execution-plan.js';
|
|
13
|
+
import { ExecutionPlanBuilder } from './execution-plan-builder.js';
|
|
14
14
|
import fsPromises from 'fs/promises';
|
|
15
15
|
import path from 'path';
|
|
16
16
|
export { TemplateRegistry } from './template-registry.js';
|
|
@@ -18,7 +18,7 @@ export { TemplateRouter } from './template-router.js';
|
|
|
18
18
|
export class PipelineEngine {
|
|
19
19
|
store;
|
|
20
20
|
analyzer;
|
|
21
|
-
|
|
21
|
+
planBuilder;
|
|
22
22
|
dynamicNodeExecutor;
|
|
23
23
|
api;
|
|
24
24
|
storage;
|
|
@@ -27,7 +27,7 @@ export class PipelineEngine {
|
|
|
27
27
|
/** 上次注入的指令指纹(pipelineId:phase:completed/total),相同则跳过重复注入 */
|
|
28
28
|
lastInstructionKey;
|
|
29
29
|
/** 0/0 状态已通知的 key(projectPath:phase),避免每次 PreToolUse 重复注入空阶段提示(1小时 TTL 自动清理) */
|
|
30
|
-
emptyPhaseNotified =
|
|
30
|
+
emptyPhaseNotified = cacheRegistry.register('pipeline:emptyNotified', {
|
|
31
31
|
max: 100,
|
|
32
32
|
ttl: TIME.HOUR
|
|
33
33
|
});
|
|
@@ -35,24 +35,24 @@ export class PipelineEngine {
|
|
|
35
35
|
pipelineLock = new MutexLock('Pipeline');
|
|
36
36
|
/** CLAUDE.md 写入器(可选,用于统一加锁写入) */
|
|
37
37
|
claudeMdWriter;
|
|
38
|
-
constructor(storage, api, conventionManager, claudeMdWriter) {
|
|
38
|
+
constructor(storage, api, conventionManager, claudeMdWriter, pendingPromptSink) {
|
|
39
39
|
this.store = new PipelineStore(storage);
|
|
40
40
|
this.analyzer = new PipelineAnalyzer(api, conventionManager);
|
|
41
|
-
this.
|
|
42
|
-
this.dynamicNodeExecutor = new DynamicNodeExecutor(api);
|
|
41
|
+
this.planBuilder = new ExecutionPlanBuilder(this.analyzer);
|
|
42
|
+
this.dynamicNodeExecutor = new DynamicNodeExecutor(api, pendingPromptSink);
|
|
43
43
|
this.api = api;
|
|
44
44
|
this.storage = storage;
|
|
45
|
-
this.activePipelines =
|
|
46
|
-
this.lastInstructionKey =
|
|
45
|
+
this.activePipelines = cacheRegistry.register('pipeline:active', { max: 50 });
|
|
46
|
+
this.lastInstructionKey = cacheRegistry.register('pipeline:instruction', { max: 50 });
|
|
47
47
|
this.claudeMdWriter = claudeMdWriter;
|
|
48
48
|
}
|
|
49
49
|
/** 检测到 ESC 中断、等待用户确认意图的项目路径集合(2小时 TTL,用户可能长时间不确认) */
|
|
50
|
-
interruptedProjects =
|
|
50
|
+
interruptedProjects = cacheRegistry.register('pipeline:interrupted', {
|
|
51
51
|
max: 50,
|
|
52
52
|
ttl: TIME.TWO_HOURS
|
|
53
53
|
});
|
|
54
54
|
/** 硬阻断状态(key=projectPath):用于 PreToolUse 拦截后续工具调用 */
|
|
55
|
-
blockedProjects =
|
|
55
|
+
blockedProjects = cacheRegistry.register('pipeline:blocked', {
|
|
56
56
|
max: 50,
|
|
57
57
|
ttl: TIME.TWO_HOURS
|
|
58
58
|
});
|
|
@@ -123,12 +123,11 @@ export class PipelineEngine {
|
|
|
123
123
|
}
|
|
124
124
|
if (!pipeline || pipeline.phase === 'done')
|
|
125
125
|
return null;
|
|
126
|
-
// 刷新任务列表
|
|
127
|
-
pipeline.tasks = this.store.getTasks(pipeline.id);
|
|
128
126
|
// 计算当前进度指纹,相同时跳过注入(避免每次 PreToolUse 重复输出相同进度)
|
|
129
127
|
// 注意:动态流水线(dp_)禁用去重,保证持续注入节点执行指令,避免 Claude 丢失上下文后停写
|
|
130
128
|
const isDynamicPipeline = pipeline.id.startsWith('dp_');
|
|
131
129
|
if (!isDynamicPipeline) {
|
|
130
|
+
pipeline.tasks = this.store.getTasks(pipeline.id);
|
|
132
131
|
// total=0 时使用 emptyPhaseNotified 去重:首次注入后标记,状态变化时清除
|
|
133
132
|
const progress = this.store.getPhaseProgress(pipeline.id, pipeline.phase);
|
|
134
133
|
const instrKey = `${pipeline.id}:${pipeline.phase}:${progress.completed}/${progress.total}`;
|
|
@@ -151,9 +150,13 @@ export class PipelineEngine {
|
|
|
151
150
|
const dynPipeline = await this.storage.repositories.dynamicPipelines.getById(pipeline.id);
|
|
152
151
|
if (!dynPipeline)
|
|
153
152
|
return null;
|
|
153
|
+
pipeline = this.dynamicPipelineToBridge(dynPipeline);
|
|
154
|
+
this.activePipelines.set(projectPath, pipeline);
|
|
154
155
|
return this.dynamicNodeExecutor.getInstruction(dynPipeline);
|
|
155
156
|
}
|
|
156
|
-
|
|
157
|
+
// Legacy Pipeline 不再处理(所有新 Pipeline 均为 dp_ 前缀)
|
|
158
|
+
logger.warn(`[Pipeline] Legacy Pipeline ${pipeline.id} 不再支持,请重新创建`);
|
|
159
|
+
return null;
|
|
157
160
|
}
|
|
158
161
|
/**
|
|
159
162
|
* PostToolUse 处理:跟踪进度,检查阶段推进
|
|
@@ -209,85 +212,9 @@ export class PipelineEngine {
|
|
|
209
212
|
}
|
|
210
213
|
return dynamicResult;
|
|
211
214
|
}
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// 持久化阶段推进状态(重启后可恢复)
|
|
216
|
-
this.persistPhaseState(pipeline.id, pipeline.phase);
|
|
217
|
-
if (result.qualityBlocked) {
|
|
218
|
-
if (result.blocking) {
|
|
219
|
-
this.markBlocked(event.project_path, pipeline.id, result.systemMessage || '[Pipeline 已阻断] 质量门禁 must 规范违反,必须修复后重启 Pipeline');
|
|
220
|
-
}
|
|
221
|
-
return { message: null, qualityBlocked: true, qualityIssues: result.qualityIssues, systemMessage: result.systemMessage, qualityLevel: result.qualityLevel, fixAttempts: result.fixAttempts, blocking: result.blocking };
|
|
222
|
-
}
|
|
223
|
-
if (result.advanced && result.newPhase) {
|
|
224
|
-
const completedPhase = pipeline.phase; // 推进前的阶段
|
|
225
|
-
// 阶段产物生成:异步非阻塞,失败只记录质量问题,不阻断推进
|
|
226
|
-
const pipelineSnapshot = { ...pipeline };
|
|
227
|
-
const artifactQualityIssues = [];
|
|
228
|
-
try {
|
|
229
|
-
await new PhaseArtifactGenerator(pipeline.projectPath, this.api, this.storage, pipelineSnapshot)
|
|
230
|
-
.generateForPhase(completedPhase, pipelineSnapshot, event.session_id);
|
|
231
|
-
}
|
|
232
|
-
catch (err) {
|
|
233
|
-
const issueMsg = `[阶段产物] ${PHASE_LABELS[completedPhase]} 产物生成失败:${err}`;
|
|
234
|
-
logger.warn(`[Forge:阶段产物] ${issueMsg}`);
|
|
235
|
-
artifactQualityIssues.push(issueMsg);
|
|
236
|
-
// 产物失败不阻断推进,继续执行
|
|
237
|
-
}
|
|
238
|
-
pipeline.phase = result.newPhase;
|
|
239
|
-
// 阶段推进后清除指纹缓存和空阶段标记,确保新阶段指令能正常注入
|
|
240
|
-
this.lastInstructionKey.delete(event.project_path);
|
|
241
|
-
this.emptyPhaseNotified.delete(`${event.project_path}:${completedPhase}`);
|
|
242
|
-
// 阶段推进时更新 CLAUDE.md 续接状态,确保跨会话后 Claude 能快速恢复上下文
|
|
243
|
-
this.persistPipelineStateToClaude(pipeline, event.project_path);
|
|
244
|
-
if (result.newPhase === 'done') {
|
|
245
|
-
this.activePipelines.delete(event.project_path);
|
|
246
|
-
this.clearBlocked(event.project_path);
|
|
247
|
-
// 统计数据
|
|
248
|
-
const totalTasks = pipeline.tasks.length;
|
|
249
|
-
const completedTasks = pipeline.tasks.filter(t => t.status === 'completed').length;
|
|
250
|
-
const failedTasks = pipeline.tasks.filter(t => t.status === 'failed').length;
|
|
251
|
-
const completedPhases = pipeline.plannedPhases?.filter(p => {
|
|
252
|
-
const phaseTasks = pipeline.tasks.filter(t => t.phase === p);
|
|
253
|
-
return phaseTasks.length > 0 && phaseTasks.every(t => t.status === 'completed');
|
|
254
|
-
}) || [];
|
|
255
|
-
// Pipeline 完成报告
|
|
256
|
-
let reportMessage = `[Forge Pipeline] ✅ 项目已完成所有阶段!
|
|
257
|
-
📋 需求:${pipeline.requirement}
|
|
258
|
-
📊 统计:
|
|
259
|
-
• 总任务数:${totalTasks}
|
|
260
|
-
• 已完成:${completedTasks}
|
|
261
|
-
• 失败:${failedTasks}
|
|
262
|
-
• 完成阶段:${completedPhases.map(p => PHASE_LABELS[p]).join('、')}`;
|
|
263
|
-
if (qualityGate) {
|
|
264
|
-
try {
|
|
265
|
-
const reportGenerator = new PipelineReportGenerator(qualityGate.getIssueTracker(), this.phaseManager);
|
|
266
|
-
const markdown = reportGenerator.generate(pipeline);
|
|
267
|
-
reportGenerator.persist(pipeline.projectPath, markdown);
|
|
268
|
-
reportMessage = `${reportMessage}\n\n${markdown}`;
|
|
269
|
-
}
|
|
270
|
-
catch (err) {
|
|
271
|
-
logger.warn(`[Pipeline] 完成报告生成失败:${err}`);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return { message: reportMessage };
|
|
275
|
-
}
|
|
276
|
-
return {
|
|
277
|
-
message: `[Forge Pipeline] 阶段推进:${PHASE_LABELS[result.newPhase]}`,
|
|
278
|
-
gapMessage: result.gapMessage,
|
|
279
|
-
qualityLevel: result.qualityLevel,
|
|
280
|
-
advanceReason: result.advanceReason,
|
|
281
|
-
systemMessage: result.systemMessage,
|
|
282
|
-
qualityIssues: artifactQualityIssues.length > 0 ? artifactQualityIssues : undefined,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
// 非阶段推进时,也定期更新 CLAUDE.md(每 20 次工具调用一次)
|
|
286
|
-
const eventCount = this.phaseManager.exportPhaseState(`${pipeline.id}:${pipeline.phase}`)?.eventCount ?? 0;
|
|
287
|
-
if (eventCount > 0 && eventCount % 20 === 0) {
|
|
288
|
-
this.persistPipelineStateToClaude(pipeline, event.project_path);
|
|
289
|
-
}
|
|
290
|
-
return { message: null, qualityLevel: result.qualityLevel };
|
|
215
|
+
// Legacy Pipeline 不再处理(所有新 Pipeline 均为 dp_ 前缀)
|
|
216
|
+
logger.warn(`[Pipeline] Legacy Pipeline ${pipeline.id} 不再支持,请重新创建`);
|
|
217
|
+
return { message: null };
|
|
291
218
|
}
|
|
292
219
|
/**
|
|
293
220
|
* 动态流水线 PostToolUse 处理
|
|
@@ -366,7 +293,7 @@ export class PipelineEngine {
|
|
|
366
293
|
• 已完成:${completedNodes}`;
|
|
367
294
|
if (qualityGate) {
|
|
368
295
|
try {
|
|
369
|
-
const reportGenerator = new PipelineReportGenerator(qualityGate.getIssueTracker(), this.
|
|
296
|
+
const reportGenerator = new PipelineReportGenerator(qualityGate.getIssueTracker(), this.dynamicNodeExecutor);
|
|
370
297
|
const markdown = reportGenerator.generate(pipeline);
|
|
371
298
|
reportGenerator.persist(pipeline.projectPath, markdown);
|
|
372
299
|
reportMessage = `${reportMessage}\n\n${markdown}`;
|
|
@@ -442,81 +369,107 @@ ${taskSummary}
|
|
|
442
369
|
/**
|
|
443
370
|
* 启动新 Pipeline
|
|
444
371
|
*/
|
|
445
|
-
async startPipeline(requirement, projectPath, sessionId) {
|
|
372
|
+
async startPipeline(requirement, projectPath, sessionId, analysis) {
|
|
446
373
|
logger.info(`[Pipeline] 启动中(模式:dynamic):"${requirement}"`);
|
|
447
|
-
this.clearBlocked(projectPath);
|
|
448
|
-
|
|
374
|
+
this.clearBlocked(projectPath);
|
|
375
|
+
const fallbackAnalysis = analysis ?? {
|
|
376
|
+
complexity: 'moderate',
|
|
377
|
+
requiresPipeline: true,
|
|
378
|
+
requirement,
|
|
379
|
+
reasoning: 'PipelineEngine 启动时未提供显式意图分析,使用默认编排分析',
|
|
380
|
+
estimatedFiles: 1,
|
|
381
|
+
suggestedPhases: [],
|
|
382
|
+
};
|
|
383
|
+
const plan = await this.buildExecutionPlan(requirement, projectPath, sessionId, fallbackAnalysis);
|
|
384
|
+
return this.startPipelineFromPlan(plan);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* 生成统一执行计划(动态流水线唯一事实来源)
|
|
388
|
+
*/
|
|
389
|
+
async buildExecutionPlan(requirement, projectPath, sessionId, analysis, template, templateRoute) {
|
|
390
|
+
return this.planBuilder.build({
|
|
391
|
+
requirement,
|
|
392
|
+
projectPath,
|
|
393
|
+
sessionId,
|
|
394
|
+
analysis,
|
|
395
|
+
template,
|
|
396
|
+
templateRoute: templateRoute ?? null,
|
|
397
|
+
});
|
|
449
398
|
}
|
|
450
399
|
/**
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* 使用 DynamicPipelineRepository 创建流水线记录,
|
|
454
|
-
* 通过 analyzer.decompose 分析需求,生成顺序依赖的 DynamicNode 列表,
|
|
455
|
-
* 并以 Bridge Pattern 返回兼容 Pipeline 接口的对象。
|
|
400
|
+
* 从统一执行计划启动动态 Pipeline。
|
|
456
401
|
*/
|
|
457
|
-
async
|
|
458
|
-
logger.info(`[Pipeline:Dynamic] 🚀 启动动态流水线:"${requirement}" | 项目=${projectPath}`);
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
402
|
+
async startPipelineFromPlan(plan) {
|
|
403
|
+
logger.info(`[Pipeline:Dynamic] 🚀 启动动态流水线:"${plan.requirement}" | 项目=${plan.projectPath} | source=${plan.source}`);
|
|
404
|
+
this.clearBlocked(plan.projectPath);
|
|
405
|
+
return this.materializeDynamicPipeline(plan);
|
|
406
|
+
}
|
|
407
|
+
async materializeDynamicPipeline(plan) {
|
|
463
408
|
const pipelineId = `dp_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
464
|
-
|
|
465
|
-
const nodes = plannedPhases.map((phaseId, index) => ({
|
|
409
|
+
const nodes = plan.stages.map((stage, index) => ({
|
|
466
410
|
id: `${pipelineId}_node_${index}`,
|
|
467
|
-
nodeTypeId:
|
|
411
|
+
nodeTypeId: stage.nodeTypeId,
|
|
468
412
|
status: (index === 0 ? 'in_progress' : 'pending'),
|
|
469
413
|
dependencies: index > 0 ? [`${pipelineId}_node_${index - 1}`] : [],
|
|
470
414
|
}));
|
|
471
|
-
|
|
415
|
+
const reasoningMeta = stringifyExecutionPlanMeta({
|
|
416
|
+
currentNodeId: nodes[0]?.id,
|
|
417
|
+
originalReasoning: plan.reasoning,
|
|
418
|
+
executionPlan: plan,
|
|
419
|
+
templateId: plan.templateId,
|
|
420
|
+
templateName: plan.templateName,
|
|
421
|
+
source: plan.source,
|
|
422
|
+
});
|
|
472
423
|
await this.storage.repositories.dynamicPipelines.create({
|
|
473
424
|
id: pipelineId,
|
|
474
|
-
requirement,
|
|
475
|
-
projectPath,
|
|
476
|
-
sessionId,
|
|
477
|
-
|
|
478
|
-
|
|
425
|
+
requirement: plan.requirement,
|
|
426
|
+
projectPath: plan.projectPath,
|
|
427
|
+
sessionId: plan.sessionId,
|
|
428
|
+
templateId: plan.templateId,
|
|
429
|
+
complexity: plan.complexity,
|
|
430
|
+
reasoning: reasoningMeta,
|
|
479
431
|
status: 'in_progress',
|
|
432
|
+
executionPlan: plan,
|
|
480
433
|
});
|
|
481
|
-
// 5. 存储节点
|
|
482
434
|
if (nodes.length > 0) {
|
|
483
435
|
await this.storage.repositories.dynamicPipelines.createNodes(pipelineId, nodes);
|
|
484
436
|
}
|
|
485
|
-
// 6. 设置 currentNodeId
|
|
486
437
|
if (nodes[0]) {
|
|
487
438
|
await this.storage.repositories.dynamicPipelines.updateCurrentNode(pipelineId, nodes[0].id);
|
|
488
439
|
}
|
|
489
|
-
// 7. 构建 Pipeline 兼容对象(Bridge Pattern:DynamicPipeline → Pipeline)
|
|
490
440
|
const now = new Date().toISOString();
|
|
491
|
-
const
|
|
441
|
+
const pipeline = {
|
|
492
442
|
id: pipelineId,
|
|
493
|
-
requirement,
|
|
494
|
-
projectPath,
|
|
495
|
-
sessionId,
|
|
443
|
+
requirement: plan.requirement,
|
|
444
|
+
projectPath: plan.projectPath,
|
|
445
|
+
sessionId: plan.sessionId,
|
|
496
446
|
phase: nodes[0]?.nodeTypeId ?? 'code',
|
|
497
|
-
tasks: nodes.map(node => ({
|
|
447
|
+
tasks: nodes.map((node, index) => ({
|
|
498
448
|
id: node.id,
|
|
499
|
-
title: PHASE_LABELS[node.nodeTypeId] || node.nodeTypeId,
|
|
449
|
+
title: plan.stages[index]?.name ?? (PHASE_LABELS[node.nodeTypeId] || node.nodeTypeId),
|
|
500
450
|
phase: node.nodeTypeId,
|
|
501
451
|
status: node.status === 'in_progress' ? 'in_progress' : node.status === 'completed' ? 'completed' : 'pending',
|
|
502
|
-
description: `动态节点:${node.nodeTypeId}`,
|
|
452
|
+
description: plan.tasks[index]?.description ?? `动态节点:${node.nodeTypeId}`,
|
|
503
453
|
dependencies: node.dependencies,
|
|
454
|
+
source: plan.source,
|
|
504
455
|
})),
|
|
505
|
-
techStack,
|
|
506
|
-
complexity,
|
|
507
|
-
taskType,
|
|
508
|
-
plannedPhases,
|
|
509
|
-
phaseArtifacts,
|
|
510
|
-
reasoning,
|
|
456
|
+
techStack: plan.techStack,
|
|
457
|
+
complexity: plan.complexity,
|
|
458
|
+
taskType: plan.taskType,
|
|
459
|
+
plannedPhases: plan.stages.map(stage => stage.phase),
|
|
460
|
+
phaseArtifacts: Object.fromEntries(plan.stages.map(stage => [stage.phase, stage.derivedReports])),
|
|
461
|
+
reasoning: plan.reasoning,
|
|
462
|
+
templateId: plan.templateId,
|
|
463
|
+
executionPlan: plan,
|
|
511
464
|
createdAt: now,
|
|
512
465
|
updatedAt: now,
|
|
513
466
|
};
|
|
514
|
-
|
|
515
|
-
this.
|
|
516
|
-
this.
|
|
517
|
-
const nodeFlow =
|
|
518
|
-
logger.info(`[Pipeline:Dynamic] ✅ 动态流水线已创建\n📋 需求:${requirement}\n📊 统计:${nodes.length} 个节点 | 复杂度:${complexity}\n🗺️ 节点规划:${nodeFlow}`);
|
|
519
|
-
return
|
|
467
|
+
this.activePipelines.set(plan.projectPath, pipeline);
|
|
468
|
+
this.dynamicNodeExecutor.resetFixAttempts(pipelineId);
|
|
469
|
+
this.writeExecutionPlanArtifact(plan, pipeline);
|
|
470
|
+
const nodeFlow = getPlanPhaseLabels(plan).join(' → ');
|
|
471
|
+
logger.info(`[Pipeline:Dynamic] ✅ 动态流水线已创建\n📋 需求:${plan.requirement}\n📊 统计:${nodes.length} 个节点 | 复杂度:${plan.complexity}\n🗺️ 节点规划:${nodeFlow}`);
|
|
472
|
+
return pipeline;
|
|
520
473
|
}
|
|
521
474
|
/**
|
|
522
475
|
* 将 DynamicPipeline 转换为 Pipeline 兼容对象(冷缓存恢复时使用)
|
|
@@ -527,7 +480,8 @@ ${taskSummary}
|
|
|
527
480
|
: dynPipeline.nodes.find(n => n.status === 'in_progress');
|
|
528
481
|
const phase = currentNode?.nodeTypeId ??
|
|
529
482
|
dynPipeline.nodes[0]?.nodeTypeId ?? 'code';
|
|
530
|
-
const plannedPhases = dynPipeline.
|
|
483
|
+
const plannedPhases = dynPipeline.executionPlan?.stages.map(stage => stage.phase)
|
|
484
|
+
?? dynPipeline.nodes.map(n => n.nodeTypeId);
|
|
531
485
|
return {
|
|
532
486
|
id: dynPipeline.id,
|
|
533
487
|
requirement: dynPipeline.requirement,
|
|
@@ -545,6 +499,7 @@ ${taskSummary}
|
|
|
545
499
|
complexity: dynPipeline.complexity,
|
|
546
500
|
plannedPhases,
|
|
547
501
|
reasoning: dynPipeline.reasoning,
|
|
502
|
+
executionPlan: dynPipeline.executionPlan,
|
|
548
503
|
createdAt: dynPipeline.createdAt,
|
|
549
504
|
updatedAt: dynPipeline.updatedAt,
|
|
550
505
|
};
|
|
@@ -552,87 +507,29 @@ ${taskSummary}
|
|
|
552
507
|
/**
|
|
553
508
|
* 从 PipelineTemplate 启动 Pipeline(Batch 4:模板驱动,跳过 AI 分解)
|
|
554
509
|
*/
|
|
555
|
-
async startPipelineFromTemplate(requirement, projectPath, sessionId, template) {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
.map(p => p.id)
|
|
560
|
-
.filter((id) => ['analyze', 'design', 'profile', 'code', 'test', 'review'].includes(id));
|
|
561
|
-
const plannedPhases = validPhases.length > 0 ? validPhases : ['code', 'test'];
|
|
562
|
-
const complexity = (template.trigger?.complexity?.[0] || 'moderate');
|
|
563
|
-
const taskType = template.taskType || 'other';
|
|
564
|
-
// 生成 Pipeline ID
|
|
565
|
-
const pipelineId = `dp_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
566
|
-
// 将模板 phases 转换为 DynamicNode 列表(顺序依赖)
|
|
567
|
-
const nodes = plannedPhases.map((phaseId, index) => ({
|
|
568
|
-
id: `${pipelineId}_node_${index}`,
|
|
569
|
-
nodeTypeId: phaseId,
|
|
570
|
-
status: (index === 0 ? 'in_progress' : 'pending'),
|
|
571
|
-
dependencies: index > 0 ? [`${pipelineId}_node_${index - 1}`] : [],
|
|
572
|
-
}));
|
|
573
|
-
// 创建 DynamicPipeline 记录
|
|
574
|
-
await this.storage.repositories.dynamicPipelines.create({
|
|
575
|
-
id: pipelineId,
|
|
576
|
-
requirement,
|
|
577
|
-
projectPath,
|
|
578
|
-
sessionId,
|
|
579
|
-
complexity,
|
|
580
|
-
reasoning: JSON.stringify({
|
|
581
|
-
currentNodeId: nodes[0]?.id,
|
|
582
|
-
templateId: template.id,
|
|
583
|
-
templateName: template.name,
|
|
584
|
-
source: template.source
|
|
585
|
-
}),
|
|
586
|
-
status: 'in_progress',
|
|
587
|
-
});
|
|
588
|
-
// 存储节点
|
|
589
|
-
if (nodes.length > 0) {
|
|
590
|
-
await this.storage.repositories.dynamicPipelines.createNodes(pipelineId, nodes);
|
|
591
|
-
}
|
|
592
|
-
// 设置 currentNodeId
|
|
593
|
-
if (nodes[0]) {
|
|
594
|
-
await this.storage.repositories.dynamicPipelines.updateCurrentNode(pipelineId, nodes[0].id);
|
|
595
|
-
}
|
|
596
|
-
// 构建 Pipeline 兼容对象
|
|
597
|
-
const now = new Date().toISOString();
|
|
598
|
-
const pipeline = {
|
|
599
|
-
id: pipelineId,
|
|
510
|
+
async startPipelineFromTemplate(requirement, projectPath, sessionId, template, analysis, templateRoute) {
|
|
511
|
+
const fallbackAnalysis = analysis ?? {
|
|
512
|
+
complexity: 'moderate',
|
|
513
|
+
requiresPipeline: true,
|
|
600
514
|
requirement,
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
tasks: nodes.map(node => ({
|
|
605
|
-
id: node.id,
|
|
606
|
-
title: PHASE_LABELS[node.nodeTypeId] || node.nodeTypeId,
|
|
607
|
-
phase: node.nodeTypeId,
|
|
608
|
-
status: node.status === 'in_progress' ? 'in_progress' : node.status === 'completed' ? 'completed' : 'pending',
|
|
609
|
-
description: `模板节点:${node.nodeTypeId}`,
|
|
610
|
-
dependencies: node.dependencies,
|
|
611
|
-
})),
|
|
612
|
-
complexity,
|
|
613
|
-
taskType,
|
|
614
|
-
plannedPhases,
|
|
615
|
-
reasoning: `来源模板:${template.name}(${template.id})`,
|
|
616
|
-
createdAt: now,
|
|
617
|
-
updatedAt: now,
|
|
515
|
+
reasoning: `模板 ${template.id} 启动时未提供显式意图分析,使用默认编排分析`,
|
|
516
|
+
estimatedFiles: 1,
|
|
517
|
+
suggestedPhases: template.phases.map(phase => phase.id),
|
|
618
518
|
};
|
|
619
|
-
this.
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
519
|
+
const plan = await this.buildExecutionPlan(requirement, projectPath, sessionId, fallbackAnalysis, template, templateRoute);
|
|
520
|
+
return this.startPipelineFromPlan(plan);
|
|
521
|
+
}
|
|
522
|
+
writeExecutionPlanArtifact(plan, pipeline) {
|
|
623
523
|
try {
|
|
624
|
-
const artifactManager = new ArtifactManager(projectPath);
|
|
625
|
-
const phases =
|
|
626
|
-
const taskLines = pipeline.tasks.map(
|
|
627
|
-
const planContent = `#
|
|
628
|
-
artifactManager.writeArtifact('plan', '执行计划.md', planContent, requirement.slice(0, 50), '执行计划');
|
|
524
|
+
const artifactManager = new ArtifactManager(plan.projectPath);
|
|
525
|
+
const phases = getPlanPhaseLabels(plan).join(' → ');
|
|
526
|
+
const taskLines = pipeline.tasks.map(task => `- [${PHASE_LABELS[task.phase] || task.phase}] ${task.title}${task.description ? `:${task.description}` : ''}`).join('\n');
|
|
527
|
+
const planContent = `# 执行计划\n\n**需求**:${plan.requirement}\n\n**来源**:${plan.source}${plan.templateName ? `(${plan.templateName})` : ''}\n\n**阶段规划**:${phases}\n\n## 任务清单\n\n${taskLines}\n`;
|
|
528
|
+
artifactManager.writeArtifact('plan', '执行计划.md', planContent, plan.requirement.slice(0, 50), '执行计划');
|
|
629
529
|
}
|
|
630
530
|
catch (err) {
|
|
631
|
-
logger.warn(`[Pipeline]
|
|
531
|
+
logger.warn(`[Pipeline] 执行计划文档写入失败:${err}`);
|
|
632
532
|
}
|
|
633
|
-
const phaseFlow = plannedPhases.map(p => PHASE_LABELS[p] || p).join(' → ');
|
|
634
|
-
logger.info(`[Pipeline:Template] ✅ 模板驱动启动完成\n📋 需求:${requirement}\n📊 统计:${pipeline.tasks.length} 个节点 | ${plannedPhases.length} 个阶段 | 模板:${template.name}\n🗺️ 阶段规划:${phaseFlow}`);
|
|
635
|
-
return pipeline;
|
|
636
533
|
}
|
|
637
534
|
/**
|
|
638
535
|
* 检查项目是否有活跃 Pipeline
|
|
@@ -641,7 +538,9 @@ ${taskSummary}
|
|
|
641
538
|
if (this.activePipelines.has(projectPath))
|
|
642
539
|
return true;
|
|
643
540
|
const db = this.store.getActivePipeline(projectPath);
|
|
644
|
-
|
|
541
|
+
if (db !== null)
|
|
542
|
+
return true;
|
|
543
|
+
return this.storage.repositories.dynamicPipelines.getActiveByProjectPath(projectPath) !== null;
|
|
645
544
|
}
|
|
646
545
|
/**
|
|
647
546
|
* 获取 Pipeline 状态摘要(用于 resume 注入)
|
|
@@ -722,12 +621,6 @@ ${taskSummary}
|
|
|
722
621
|
}
|
|
723
622
|
return progressLine + gateLine;
|
|
724
623
|
}
|
|
725
|
-
/**
|
|
726
|
-
* 获取 PhaseManager 引用(用于注入知识上下文)
|
|
727
|
-
*/
|
|
728
|
-
getPhaseManager() {
|
|
729
|
-
return this.phaseManager;
|
|
730
|
-
}
|
|
731
624
|
/**
|
|
732
625
|
* 获取 DynamicNodeExecutor 引用
|
|
733
626
|
*/
|
|
@@ -738,38 +631,35 @@ ${taskSummary}
|
|
|
738
631
|
* 获取活跃 Pipeline 对象
|
|
739
632
|
*/
|
|
740
633
|
getActivePipelineForProject(projectPath) {
|
|
741
|
-
|
|
634
|
+
const cached = this.activePipelines.get(projectPath);
|
|
635
|
+
if (cached)
|
|
636
|
+
return cached;
|
|
637
|
+
const legacy = this.store.getActivePipeline(projectPath);
|
|
638
|
+
if (legacy)
|
|
639
|
+
return legacy;
|
|
640
|
+
const dynamic = this.storage.repositories.dynamicPipelines.getActiveByProjectPath(projectPath);
|
|
641
|
+
return dynamic ? this.dynamicPipelineToBridge(dynamic) : null;
|
|
742
642
|
}
|
|
743
643
|
/**
|
|
744
|
-
* ESC 中断后从 DB
|
|
745
|
-
* - phaseToolsSeen:已完成任务的阶段标记为"已见过 write"(允许后续任务正常检测)
|
|
746
|
-
* - totalPhaseEventCounts:按已完成任务数估算(保守值,避免立刻触发兜底推进)
|
|
644
|
+
* ESC 中断后从 DB 恢复时,重建执行器的内存状态。
|
|
747
645
|
*/
|
|
748
646
|
restorePhaseState(pipeline) {
|
|
749
647
|
const phase = pipeline.phase;
|
|
750
648
|
if (phase === 'done')
|
|
751
649
|
return;
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
// 优先从 daemon_state 恢复完整状态
|
|
757
|
-
if (this.loadPhaseState(pipeline.id, phase)) {
|
|
758
|
-
logger.debug(`[Pipeline] 从 daemon_state 恢复阶段状态:${phase}`);
|
|
650
|
+
// 动态 Pipeline 使用 DynamicNodeExecutor 的状态恢复
|
|
651
|
+
if (pipeline.id.startsWith('dp_')) {
|
|
652
|
+
this.dynamicNodeExecutor.resetFixAttempts(pipeline.id);
|
|
653
|
+
logger.debug(`[Pipeline] 动态 Pipeline 状态已恢复:${phase}`);
|
|
759
654
|
return;
|
|
760
655
|
}
|
|
761
|
-
//
|
|
762
|
-
|
|
763
|
-
this.phaseManager.restoreToolsSeen(key, new Set(['explore', 'write', 'bash']));
|
|
764
|
-
}
|
|
765
|
-
// 回退:事件计数按已完成任务数 × 5(保守估算,不触发兜底推进)
|
|
766
|
-
this.phaseManager.restoreEventCount(key, completedCount * 5);
|
|
767
|
-
logger.debug(`[Pipeline] 回退恢复阶段状态:${phase} | 已完成=${completedCount}`);
|
|
656
|
+
// Legacy Pipeline 不再支持状态恢复
|
|
657
|
+
logger.warn(`[Pipeline] Legacy Pipeline ${pipeline.id} 状态恢复已跳过`);
|
|
768
658
|
}
|
|
769
659
|
/** 将当前阶段推进状态持久化到 daemon_state */
|
|
770
660
|
persistPhaseState(pipelineId, phase) {
|
|
771
661
|
const key = `${pipelineId}:${phase}`;
|
|
772
|
-
const state = this.
|
|
662
|
+
const state = this.dynamicNodeExecutor.exportState(key);
|
|
773
663
|
this.storage.setDaemonState(`pipeline:phase-state:${key}`, JSON.stringify(state));
|
|
774
664
|
}
|
|
775
665
|
/** 从 daemon_state 加载阶段推进状态,成功返回 true */
|
|
@@ -780,7 +670,7 @@ ${taskSummary}
|
|
|
780
670
|
return false;
|
|
781
671
|
try {
|
|
782
672
|
const parsed = JSON.parse(raw);
|
|
783
|
-
this.
|
|
673
|
+
this.dynamicNodeExecutor.restoreState(key, parsed);
|
|
784
674
|
return true;
|
|
785
675
|
}
|
|
786
676
|
catch (err) {
|
|
@@ -823,7 +713,7 @@ ${taskSummary}
|
|
|
823
713
|
this.dynamicNodeExecutor.cleanup(interrupted.pipelineId);
|
|
824
714
|
}
|
|
825
715
|
else {
|
|
826
|
-
this.
|
|
716
|
+
this.dynamicNodeExecutor.cleanup(interrupted.pipelineId);
|
|
827
717
|
}
|
|
828
718
|
this.activePipelines.delete(projectPath);
|
|
829
719
|
this.lastInstructionKey.delete(projectPath);
|
|
@@ -842,7 +732,7 @@ ${taskSummary}
|
|
|
842
732
|
this.dynamicNodeExecutor.cleanup(projectPathOrId);
|
|
843
733
|
}
|
|
844
734
|
else {
|
|
845
|
-
this.
|
|
735
|
+
this.dynamicNodeExecutor.cleanup(projectPathOrId);
|
|
846
736
|
}
|
|
847
737
|
this.store.closeById(projectPathOrId);
|
|
848
738
|
// 清理内存缓存(遍历找到对应条目)
|
|
@@ -863,7 +753,7 @@ ${taskSummary}
|
|
|
863
753
|
this.dynamicNodeExecutor.cleanup(pipeline.id);
|
|
864
754
|
}
|
|
865
755
|
else {
|
|
866
|
-
this.
|
|
756
|
+
this.dynamicNodeExecutor.cleanup(pipeline.id);
|
|
867
757
|
}
|
|
868
758
|
this.store.closeById(pipeline.id);
|
|
869
759
|
}
|
|
@@ -886,7 +776,7 @@ ${taskSummary}
|
|
|
886
776
|
this.dynamicNodeExecutor.cleanup(pipelineId);
|
|
887
777
|
}
|
|
888
778
|
else {
|
|
889
|
-
this.
|
|
779
|
+
this.dynamicNodeExecutor.cleanup(pipelineId);
|
|
890
780
|
}
|
|
891
781
|
}
|
|
892
782
|
logger.info(`[Pipeline] 已自动关闭 ${closedIds.length} 个过期 Pipeline(超过 ${ttlMs / 60000} 分钟未更新)`);
|
|
@@ -923,32 +813,17 @@ ${taskSummary}
|
|
|
923
813
|
return false;
|
|
924
814
|
return this.dynamicNodeExecutor.hasArtifactRetry(pipeline.id);
|
|
925
815
|
}
|
|
816
|
+
evaluatePreToolGate(event) {
|
|
817
|
+
const pipeline = this.getActiveDynamicPipelineForProject(event.project_path);
|
|
818
|
+
if (!pipeline)
|
|
819
|
+
return null;
|
|
820
|
+
return this.dynamicNodeExecutor.evaluatePreToolGate(pipeline, event);
|
|
821
|
+
}
|
|
926
822
|
/**
|
|
927
823
|
* 检查 Step 级工具白名单(Step 模式启用时生效)
|
|
928
824
|
*/
|
|
929
825
|
checkStepGate(event) {
|
|
930
|
-
|
|
931
|
-
if (!pipeline)
|
|
932
|
-
return null;
|
|
933
|
-
const currentNode = pipeline.nodes.find(n => n.id === pipeline.currentNodeId);
|
|
934
|
-
if (!currentNode)
|
|
935
|
-
return null;
|
|
936
|
-
// 检查是否启用 Step 模式
|
|
937
|
-
const stepOrchestrator = this.dynamicNodeExecutor.getStepOrchestrator();
|
|
938
|
-
if (!stepOrchestrator.isStepModeEnabled(currentNode)) {
|
|
939
|
-
return null; // 未启用 Step 模式,跳过检查
|
|
940
|
-
}
|
|
941
|
-
// 获取当前 Step
|
|
942
|
-
const currentStep = stepOrchestrator.getCurrentStep(pipeline, currentNode);
|
|
943
|
-
if (!currentStep)
|
|
944
|
-
return null;
|
|
945
|
-
// 加载 Step 定义
|
|
946
|
-
const stepDef = stepOrchestrator.loadStepDefinition(currentNode.nodeTypeId, currentStep.stepDefinitionId);
|
|
947
|
-
if (!stepDef)
|
|
948
|
-
return null;
|
|
949
|
-
// 使用 StepGateKeeper 检查工具
|
|
950
|
-
const stepGateKeeper = this.dynamicNodeExecutor.getStepGateKeeper();
|
|
951
|
-
return stepGateKeeper.checkTool(event, currentStep, stepDef);
|
|
826
|
+
return this.evaluatePreToolGate(event);
|
|
952
827
|
}
|
|
953
828
|
/** 标记项目进入硬阻断状态 */
|
|
954
829
|
markBlocked(projectPath, pipelineId, reason) {
|
|
@@ -1006,11 +881,12 @@ ${taskSummary}
|
|
|
1006
881
|
* 使用与 PreToolUse 相同的进度指纹缓存机制,避免相同进度下重复注入
|
|
1007
882
|
*/
|
|
1008
883
|
async getCurrentPhaseDirective(projectPath) {
|
|
1009
|
-
const pipeline = this.
|
|
884
|
+
const pipeline = this.getActivePipelineForProject(projectPath);
|
|
1010
885
|
if (!pipeline || pipeline.phase === 'done')
|
|
1011
886
|
return null;
|
|
1012
|
-
|
|
1013
|
-
|
|
887
|
+
if (!pipeline.id.startsWith('dp_')) {
|
|
888
|
+
pipeline.tasks = this.store.getTasks(pipeline.id);
|
|
889
|
+
}
|
|
1014
890
|
// 进度指纹去重:与 PreToolUse 共享 lastInstructionKey 缓存
|
|
1015
891
|
const progress = this.store.getPhaseProgress(pipeline.id, pipeline.phase);
|
|
1016
892
|
const instrKey = `${pipeline.id}:${pipeline.phase}:${progress.completed}/${progress.total}:user`;
|
|
@@ -1033,7 +909,9 @@ ${taskSummary}
|
|
|
1033
909
|
return null;
|
|
1034
910
|
return this.dynamicNodeExecutor.getInstruction(dynPipeline);
|
|
1035
911
|
}
|
|
1036
|
-
|
|
912
|
+
// Legacy Pipeline 不再支持
|
|
913
|
+
logger.warn(`[Pipeline] Legacy Pipeline ${pipeline.id} 不再支持`);
|
|
914
|
+
return null;
|
|
1037
915
|
}
|
|
1038
916
|
/**
|
|
1039
917
|
* 获取任务进度摘要(Stage 09 增强上下文使用)
|
|
@@ -1056,20 +934,15 @@ ${taskSummary}
|
|
|
1056
934
|
/**
|
|
1057
935
|
* 获取产物门禁提示(供 stage-scheduler 注入到 AI 上下文)
|
|
1058
936
|
*/
|
|
1059
|
-
getArtifactGateReminder(
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
return null;
|
|
1063
|
-
return this.phaseManager.getArtifactGateReminder(pipeline.id, pipeline.phase);
|
|
937
|
+
getArtifactGateReminder(_projectPath) {
|
|
938
|
+
// Legacy 功能,动态 Pipeline 不使用产物门禁提示
|
|
939
|
+
return null;
|
|
1064
940
|
}
|
|
1065
941
|
/**
|
|
1066
942
|
* 清除产物门禁提示
|
|
1067
943
|
*/
|
|
1068
|
-
clearArtifactGateReminder(
|
|
1069
|
-
|
|
1070
|
-
if (!pipeline || pipeline.phase === 'done')
|
|
1071
|
-
return;
|
|
1072
|
-
this.phaseManager.clearArtifactGateReminder(pipeline.id, pipeline.phase);
|
|
944
|
+
clearArtifactGateReminder(_projectPath) {
|
|
945
|
+
// Legacy 功能,动态 Pipeline 不使用产物门禁提示
|
|
1073
946
|
}
|
|
1074
947
|
/**
|
|
1075
948
|
* 获取当前阶段名称(用于失败信号上下文)
|
|
@@ -1084,7 +957,7 @@ ${taskSummary}
|
|
|
1084
957
|
* 获取 Pipeline 阶段快照(用于复盘分析)
|
|
1085
958
|
*/
|
|
1086
959
|
getSessionSnapshot(pipelineId) {
|
|
1087
|
-
return this.
|
|
960
|
+
return this.dynamicNodeExecutor.getSessionSnapshot(pipelineId);
|
|
1088
961
|
}
|
|
1089
962
|
/**
|
|
1090
963
|
* 发射 Step 级别的事件(STEP_STARTED/COMPLETED/FAILED/VALIDATED/RETRYING/NEED_HUMAN)
|