openmatrix 0.1.62 → 0.1.63
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/dist/cli/commands/brainstorm.js +121 -0
- package/dist/orchestrator/executor.d.ts +19 -0
- package/dist/orchestrator/executor.js +76 -1
- package/dist/orchestrator/scheduler.d.ts +15 -1
- package/dist/orchestrator/scheduler.js +90 -17
- package/dist/orchestrator/state-machine.js +2 -0
- package/dist/storage/state-manager.d.ts +5 -0
- package/dist/storage/state-manager.js +81 -53
- package/package.json +1 -1
- package/skills/brainstorm.md +13 -0
|
@@ -449,6 +449,9 @@ async function generateSmartQuestions(taskContent, basePath) {
|
|
|
449
449
|
multiSelect: q.type === 'multiple',
|
|
450
450
|
why: ''
|
|
451
451
|
}));
|
|
452
|
+
// 7. 追加领域分析问题(底层逻辑思考)
|
|
453
|
+
const domainQuestions = generateDomainAnalysisQuestions(taskContent);
|
|
454
|
+
questions.push(...domainQuestions);
|
|
452
455
|
return questions;
|
|
453
456
|
}
|
|
454
457
|
catch (error) {
|
|
@@ -457,3 +460,121 @@ async function generateSmartQuestions(taskContent, basePath) {
|
|
|
457
460
|
return generateBrainstormQuestions(taskContent, '');
|
|
458
461
|
}
|
|
459
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* 领域分析问题 — 底层逻辑思考
|
|
465
|
+
*
|
|
466
|
+
* 从任务描述中提取:
|
|
467
|
+
* 1. 核心领域实体及其关系
|
|
468
|
+
* 2. 数据流转路径
|
|
469
|
+
* 3. 关键不变量/约束
|
|
470
|
+
* 4. 核心用户场景链路
|
|
471
|
+
*/
|
|
472
|
+
function generateDomainAnalysisQuestions(taskContent) {
|
|
473
|
+
const questions = [];
|
|
474
|
+
const content = taskContent.toLowerCase();
|
|
475
|
+
// ===== 问题 1: 领域实体建模 =====
|
|
476
|
+
const entityHints = extractEntities(taskContent);
|
|
477
|
+
if (entityHints.length > 0) {
|
|
478
|
+
questions.push({
|
|
479
|
+
id: 'domain_entities',
|
|
480
|
+
question: `从任务描述中识别到以下核心实体,请确认或补充:\n${entityHints.map(e => ` • ${e}`).join('\n')}`,
|
|
481
|
+
header: '领域实体',
|
|
482
|
+
options: [
|
|
483
|
+
{ label: '以上实体正确', description: '已覆盖核心领域实体,无需补充' },
|
|
484
|
+
{ label: '需要补充实体', description: '还有重要实体未被识别,我会在"其他"中补充' },
|
|
485
|
+
{ label: '需要调整实体', description: '部分实体不准确,需要修正' }
|
|
486
|
+
],
|
|
487
|
+
multiSelect: false,
|
|
488
|
+
why: '明确领域实体是设计数据模型和 API 的基础'
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
questions.push({
|
|
493
|
+
id: 'domain_entities',
|
|
494
|
+
question: '这个系统涉及哪些核心领域实体?它们之间是什么关系?(如 用户-订单-商品)',
|
|
495
|
+
header: '领域实体',
|
|
496
|
+
options: [
|
|
497
|
+
{ label: '单一实体', description: '系统围绕一个核心实体(如用户管理)' },
|
|
498
|
+
{ label: '2-3 个实体', description: '少量实体间有简单关系(如用户-文章)' },
|
|
499
|
+
{ label: '多实体复杂关系', description: '多个实体间有多对多等复杂关系' },
|
|
500
|
+
{ label: '我来说明', description: '在"其他"中描述具体实体' }
|
|
501
|
+
],
|
|
502
|
+
multiSelect: false,
|
|
503
|
+
why: '明确领域实体是设计数据模型和 API 的基础'
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
// ===== 问题 2: 数据流分析 =====
|
|
507
|
+
questions.push({
|
|
508
|
+
id: 'data_flow',
|
|
509
|
+
question: '数据在系统中如何流转?(从输入到存储到输出)',
|
|
510
|
+
header: '数据流',
|
|
511
|
+
options: [
|
|
512
|
+
{ label: '用户输入 → 处理 → 存储 → 展示', description: '经典 CRUD 流程(如后台管理、博客)' },
|
|
513
|
+
{ label: '外部 API → 转换 → 存储 → 查询', description: '数据采集/聚合类系统' },
|
|
514
|
+
{ label: '事件触发 → 处理 → 通知/存储', description: '事件驱动型(如消息队列、Webhook)' },
|
|
515
|
+
{ label: '实时流处理', description: '数据持续流入,实时处理(如监控、聊天)' },
|
|
516
|
+
{ label: '我来说明', description: '在"其他"中描述具体数据流' }
|
|
517
|
+
],
|
|
518
|
+
multiSelect: false,
|
|
519
|
+
why: '数据流决定架构选型(请求驱动 vs 事件驱动 vs 流处理)'
|
|
520
|
+
});
|
|
521
|
+
// ===== 问题 3: 不变量/约束 =====
|
|
522
|
+
questions.push({
|
|
523
|
+
id: 'invariants',
|
|
524
|
+
question: '系统中存在哪些关键不变量或业务约束?(什么条件必须永远成立)',
|
|
525
|
+
header: '不变量',
|
|
526
|
+
options: [
|
|
527
|
+
{ label: '数据一致性', description: '如:余额不能为负、库存不能超卖、状态只能单向流转' },
|
|
528
|
+
{ label: '权限控制', description: '如:用户只能操作自己的数据、管理员才能访问后台' },
|
|
529
|
+
{ label: '唯一性约束', description: '如:用户名唯一、订单号不重复、同一时间只能有一个活跃会话' },
|
|
530
|
+
{ label: '无明显约束', description: '纯展示或简单计算,无严格不变量' }
|
|
531
|
+
],
|
|
532
|
+
multiSelect: true,
|
|
533
|
+
why: '不变量决定了哪些地方需要加锁、事务、校验和防御性编程'
|
|
534
|
+
});
|
|
535
|
+
// ===== 问题 4: 核心场景链路 =====
|
|
536
|
+
questions.push({
|
|
537
|
+
id: 'core_scenarios',
|
|
538
|
+
question: '从用户视角,核心操作链路是什么?(用户会走过的关键路径)',
|
|
539
|
+
header: '场景链路',
|
|
540
|
+
options: [
|
|
541
|
+
{ label: '注册→登录→使用→退出', description: '典型用户系统链路' },
|
|
542
|
+
{ label: '浏览→选择→下单→支付→确认', description: '电商/交易类链路' },
|
|
543
|
+
{ label: '创建→编辑→预览→发布', description: '内容管理类链路' },
|
|
544
|
+
{ label: '输入→处理→查看结果', description: '工具/计算类链路' },
|
|
545
|
+
{ label: '我来说明', description: '在"其他"中描述具体场景链路' }
|
|
546
|
+
],
|
|
547
|
+
multiSelect: true,
|
|
548
|
+
why: '核心场景链路决定了 MVP 的功能范围和优先级排序'
|
|
549
|
+
});
|
|
550
|
+
return questions;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* 从任务描述中提取可能的领域实体
|
|
554
|
+
*/
|
|
555
|
+
function extractEntities(taskContent) {
|
|
556
|
+
const entities = [];
|
|
557
|
+
// 中文常见领域实体
|
|
558
|
+
const zhPatterns = [
|
|
559
|
+
{ pattern: /用户|账号|账户|注册|登录|权限|角色/gi, entity: '用户 (User)' },
|
|
560
|
+
{ pattern: /订单|交易|购买|支付|退款|结算/gi, entity: '订单 (Order)' },
|
|
561
|
+
{ pattern: /商品|产品|物品|库存|上架|下架/gi, entity: '商品 (Product)' },
|
|
562
|
+
{ pattern: /文章|帖子|内容|评论|标签|分类/gi, entity: '内容 (Content)' },
|
|
563
|
+
{ pattern: /任务|工单|项目|里程碑|迭代/gi, entity: '任务 (Task)' },
|
|
564
|
+
{ pattern: /消息|通知|推送|邮件|短信/gi, entity: '消息 (Message)' },
|
|
565
|
+
{ pattern: /文件|附件|图片|视频|媒体|上传/gi, entity: '文件 (File)' },
|
|
566
|
+
{ pattern: /配置|设置|参数|选项|规则/gi, entity: '配置 (Config)' },
|
|
567
|
+
{ pattern: /日志|记录|审计|追踪|监控/gi, entity: '日志 (Log)' },
|
|
568
|
+
{ pattern: /数据|报表|统计|分析|仪表盘|dashboard/gi, entity: '数据 (Data)' },
|
|
569
|
+
{ pattern: /API|接口|端点|路由|endpoint/gi, entity: 'API' },
|
|
570
|
+
{ pattern: /数据库|存储|缓存|表|collection/gi, entity: '存储 (Storage)' },
|
|
571
|
+
];
|
|
572
|
+
const seen = new Set();
|
|
573
|
+
for (const { pattern, entity } of zhPatterns) {
|
|
574
|
+
if (pattern.test(taskContent) && !seen.has(entity)) {
|
|
575
|
+
entities.push(entity);
|
|
576
|
+
seen.add(entity);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return entities;
|
|
580
|
+
}
|
|
@@ -39,7 +39,9 @@ export declare class OrchestratorExecutor {
|
|
|
39
39
|
private approvalManager;
|
|
40
40
|
private stateMachine;
|
|
41
41
|
private phaseExecutor;
|
|
42
|
+
private retryManager;
|
|
42
43
|
private config;
|
|
44
|
+
private taskTimers;
|
|
43
45
|
constructor(stateManager: StateManager, approvalManager: ApprovalManager, config?: Partial<ExecutorConfig>);
|
|
44
46
|
/**
|
|
45
47
|
* 获取 PhaseExecutor 实例
|
|
@@ -112,4 +114,21 @@ export declare class OrchestratorExecutor {
|
|
|
112
114
|
* 获取 Scheduler 实例
|
|
113
115
|
*/
|
|
114
116
|
getScheduler(): Scheduler;
|
|
117
|
+
/**
|
|
118
|
+
* 设置任务超时计时器
|
|
119
|
+
*/
|
|
120
|
+
private setupTaskTimeout;
|
|
121
|
+
/**
|
|
122
|
+
* 清除任务超时计时器
|
|
123
|
+
*/
|
|
124
|
+
private clearTaskTimeout;
|
|
125
|
+
/**
|
|
126
|
+
* 处理失败任务的重试
|
|
127
|
+
* @returns 成功加入重试队列的任务数量
|
|
128
|
+
*/
|
|
129
|
+
private processRetries;
|
|
130
|
+
/**
|
|
131
|
+
* 销毁执行器,清理所有资源
|
|
132
|
+
*/
|
|
133
|
+
destroy(): void;
|
|
115
134
|
}
|
|
@@ -6,6 +6,7 @@ const scheduler_js_1 = require("./scheduler.js");
|
|
|
6
6
|
const agent_runner_js_1 = require("../agents/agent-runner.js");
|
|
7
7
|
const state_machine_js_1 = require("./state-machine.js");
|
|
8
8
|
const phase_executor_js_1 = require("./phase-executor.js");
|
|
9
|
+
const retry_manager_js_1 = require("./retry-manager.js");
|
|
9
10
|
/**
|
|
10
11
|
* OrchestratorExecutor - 执行循环核心
|
|
11
12
|
*
|
|
@@ -18,7 +19,9 @@ class OrchestratorExecutor {
|
|
|
18
19
|
approvalManager;
|
|
19
20
|
stateMachine;
|
|
20
21
|
phaseExecutor;
|
|
22
|
+
retryManager;
|
|
21
23
|
config;
|
|
24
|
+
taskTimers = new Map();
|
|
22
25
|
constructor(stateManager, approvalManager, config) {
|
|
23
26
|
this.stateManager = stateManager;
|
|
24
27
|
this.approvalManager = approvalManager;
|
|
@@ -37,6 +40,7 @@ class OrchestratorExecutor {
|
|
|
37
40
|
});
|
|
38
41
|
this.stateMachine = new state_machine_js_1.StateMachine();
|
|
39
42
|
this.phaseExecutor = new phase_executor_js_1.PhaseExecutor(stateManager, approvalManager);
|
|
43
|
+
this.retryManager = new retry_manager_js_1.RetryManager();
|
|
40
44
|
}
|
|
41
45
|
/**
|
|
42
46
|
* 获取 PhaseExecutor 实例
|
|
@@ -64,6 +68,18 @@ class OrchestratorExecutor {
|
|
|
64
68
|
// 4. 检查是否有失败任务需要重试
|
|
65
69
|
const failedTasks = allTasks.filter(t => t.status === 'failed');
|
|
66
70
|
if (failedTasks.length > 0) {
|
|
71
|
+
// 尝试自动重试
|
|
72
|
+
const retried = await this.processRetries(failedTasks);
|
|
73
|
+
if (retried > 0) {
|
|
74
|
+
// 有任务被重新放入队列,继续执行循环
|
|
75
|
+
return {
|
|
76
|
+
status: 'continue',
|
|
77
|
+
subagentTasks: [],
|
|
78
|
+
message: `${retried} 个失败任务已加入重试队列`,
|
|
79
|
+
statistics: this.getStatistics(state)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// 所有重试次数已耗尽
|
|
67
83
|
return this.createRetryNeededResult(failedTasks, state);
|
|
68
84
|
}
|
|
69
85
|
// 5. 检查是否有阻塞任务
|
|
@@ -85,9 +101,10 @@ class OrchestratorExecutor {
|
|
|
85
101
|
}
|
|
86
102
|
// 10. 准备 Subagent 任务
|
|
87
103
|
const subagentTasks = await this.agentRunner.prepareSubagentTasks(executableTasks);
|
|
88
|
-
// 8. 更新任务状态为 scheduled
|
|
104
|
+
// 8. 更新任务状态为 scheduled 并设置超时
|
|
89
105
|
for (const task of executableTasks) {
|
|
90
106
|
await this.scheduler.markTaskStarted(task.id);
|
|
107
|
+
this.setupTaskTimeout(task.id);
|
|
91
108
|
}
|
|
92
109
|
return {
|
|
93
110
|
status: 'continue',
|
|
@@ -241,6 +258,8 @@ class OrchestratorExecutor {
|
|
|
241
258
|
throw new Error(`Task ${taskId} not found`);
|
|
242
259
|
}
|
|
243
260
|
if (result.success) {
|
|
261
|
+
// 清除超时计时器
|
|
262
|
+
this.clearTaskTimeout(taskId);
|
|
244
263
|
// 更新阶段状态
|
|
245
264
|
const currentPhase = this.getCurrentPhase(task);
|
|
246
265
|
const updatedPhases = { ...task.phases };
|
|
@@ -281,6 +300,7 @@ class OrchestratorExecutor {
|
|
|
281
300
|
}
|
|
282
301
|
}
|
|
283
302
|
else {
|
|
303
|
+
this.clearTaskTimeout(taskId);
|
|
284
304
|
await this.scheduler.markTaskFailed(taskId, result.error || 'Unknown error');
|
|
285
305
|
}
|
|
286
306
|
}
|
|
@@ -321,5 +341,60 @@ class OrchestratorExecutor {
|
|
|
321
341
|
getScheduler() {
|
|
322
342
|
return this.scheduler;
|
|
323
343
|
}
|
|
344
|
+
// ============ Timeout Management ============
|
|
345
|
+
/**
|
|
346
|
+
* 设置任务超时计时器
|
|
347
|
+
*/
|
|
348
|
+
setupTaskTimeout(taskId) {
|
|
349
|
+
const timer = setTimeout(async () => {
|
|
350
|
+
console.error(`⏰ 任务超时: ${taskId} (${this.config.taskTimeout / 1000}s)`);
|
|
351
|
+
this.taskTimers.delete(taskId);
|
|
352
|
+
await this.scheduler.markTaskFailed(taskId, `Task timed out after ${this.config.taskTimeout / 1000}s`);
|
|
353
|
+
}, this.config.taskTimeout);
|
|
354
|
+
this.taskTimers.set(taskId, timer);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 清除任务超时计时器
|
|
358
|
+
*/
|
|
359
|
+
clearTaskTimeout(taskId) {
|
|
360
|
+
const timer = this.taskTimers.get(taskId);
|
|
361
|
+
if (timer) {
|
|
362
|
+
clearTimeout(timer);
|
|
363
|
+
this.taskTimers.delete(taskId);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// ============ Retry Management ============
|
|
367
|
+
/**
|
|
368
|
+
* 处理失败任务的重试
|
|
369
|
+
* @returns 成功加入重试队列的任务数量
|
|
370
|
+
*/
|
|
371
|
+
async processRetries(failedTasks) {
|
|
372
|
+
let retried = 0;
|
|
373
|
+
for (const task of failedTasks) {
|
|
374
|
+
this.retryManager.addToQueue(task.id, task.error || 'Unknown error');
|
|
375
|
+
if (this.retryManager.shouldRetry(task.id)) {
|
|
376
|
+
// 将任务从 failed 转为 retry_queue 再转为 pending
|
|
377
|
+
await this.stateManager.updateTask(task.id, {
|
|
378
|
+
status: 'retry_queue',
|
|
379
|
+
retryCount: (task.retryCount || 0) + 1
|
|
380
|
+
});
|
|
381
|
+
await this.stateManager.updateTask(task.id, {
|
|
382
|
+
status: 'pending'
|
|
383
|
+
});
|
|
384
|
+
this.retryManager.removeFromQueue(task.id);
|
|
385
|
+
retried++;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return retried;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* 销毁执行器,清理所有资源
|
|
392
|
+
*/
|
|
393
|
+
destroy() {
|
|
394
|
+
for (const timer of this.taskTimers.values()) {
|
|
395
|
+
clearTimeout(timer);
|
|
396
|
+
}
|
|
397
|
+
this.taskTimers.clear();
|
|
398
|
+
}
|
|
324
399
|
}
|
|
325
400
|
exports.OrchestratorExecutor = OrchestratorExecutor;
|
|
@@ -12,6 +12,7 @@ export interface TaskAssignment {
|
|
|
12
12
|
}
|
|
13
13
|
export declare class Scheduler {
|
|
14
14
|
private stateManager;
|
|
15
|
+
private stateMachine;
|
|
15
16
|
private config;
|
|
16
17
|
private runningTasks;
|
|
17
18
|
constructor(stateManager: StateManager, config?: Partial<SchedulerConfig>);
|
|
@@ -27,6 +28,10 @@ export declare class Scheduler {
|
|
|
27
28
|
* 获取优先级权重
|
|
28
29
|
*/
|
|
29
30
|
private getPriorityWeight;
|
|
31
|
+
/**
|
|
32
|
+
* 通过状态机验证并执行状态转换
|
|
33
|
+
*/
|
|
34
|
+
private transitionTask;
|
|
30
35
|
/**
|
|
31
36
|
* 标记任务开始执行
|
|
32
37
|
*/
|
|
@@ -48,9 +53,18 @@ export declare class Scheduler {
|
|
|
48
53
|
*/
|
|
49
54
|
markTaskBlocked(taskId: string, reason: string): Promise<void>;
|
|
50
55
|
/**
|
|
51
|
-
*
|
|
56
|
+
* 获取所有可并行执行的任务(按优先级排序)
|
|
52
57
|
*/
|
|
53
58
|
getParallelTasks(): Promise<Task[]>;
|
|
59
|
+
/**
|
|
60
|
+
* 检测循环依赖(DFS)
|
|
61
|
+
* 返回所有检测到的循环路径
|
|
62
|
+
*/
|
|
63
|
+
detectCircularDependencies(tasks: Task[]): string[][];
|
|
64
|
+
/**
|
|
65
|
+
* 检测循环依赖并将相关任务标记为 blocked
|
|
66
|
+
*/
|
|
67
|
+
private handleCircularDependencies;
|
|
54
68
|
/**
|
|
55
69
|
* 获取调度状态
|
|
56
70
|
*/
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Scheduler = void 0;
|
|
4
|
+
const state_machine_js_1 = require("./state-machine.js");
|
|
4
5
|
class Scheduler {
|
|
5
6
|
stateManager;
|
|
7
|
+
stateMachine;
|
|
6
8
|
config;
|
|
7
9
|
runningTasks = new Set();
|
|
8
10
|
constructor(stateManager, config) {
|
|
9
11
|
this.stateManager = stateManager;
|
|
12
|
+
this.stateMachine = new state_machine_js_1.StateMachine();
|
|
10
13
|
this.config = {
|
|
11
14
|
maxConcurrentTasks: 3,
|
|
12
15
|
taskTimeout: 120000,
|
|
@@ -18,6 +21,8 @@ class Scheduler {
|
|
|
18
21
|
*/
|
|
19
22
|
async getNextTask() {
|
|
20
23
|
const tasks = await this.stateManager.listTasks();
|
|
24
|
+
// 检测并处理循环依赖
|
|
25
|
+
await this.handleCircularDependencies(tasks);
|
|
21
26
|
// 筛选可执行任务
|
|
22
27
|
const executable = tasks.filter(task => this.canExecute(task, tasks));
|
|
23
28
|
if (executable.length === 0) {
|
|
@@ -62,13 +67,28 @@ class Scheduler {
|
|
|
62
67
|
};
|
|
63
68
|
return weights[priority] || 0;
|
|
64
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* 通过状态机验证并执行状态转换
|
|
72
|
+
*/
|
|
73
|
+
async transitionTask(taskId, event, extraUpdates) {
|
|
74
|
+
const task = await this.stateManager.getTask(taskId);
|
|
75
|
+
if (!task)
|
|
76
|
+
throw new Error(`Task ${taskId} not found`);
|
|
77
|
+
const result = this.stateMachine.transition(task, event);
|
|
78
|
+
if (!result.success) {
|
|
79
|
+
throw new Error(`状态转换失败 [${task.id}]: ${result.error}`);
|
|
80
|
+
}
|
|
81
|
+
await this.stateManager.updateTask(taskId, {
|
|
82
|
+
status: result.toStatus,
|
|
83
|
+
...extraUpdates
|
|
84
|
+
});
|
|
85
|
+
}
|
|
65
86
|
/**
|
|
66
87
|
* 标记任务开始执行
|
|
67
88
|
*/
|
|
68
89
|
async markTaskStarted(taskId) {
|
|
69
90
|
this.runningTasks.add(taskId);
|
|
70
|
-
await this.
|
|
71
|
-
status: 'in_progress',
|
|
91
|
+
await this.transitionTask(taskId, 'start', {
|
|
72
92
|
phases: {
|
|
73
93
|
develop: { status: 'in_progress', duration: null, startedAt: new Date().toISOString() },
|
|
74
94
|
verify: { status: 'pending', duration: null },
|
|
@@ -83,8 +103,7 @@ class Scheduler {
|
|
|
83
103
|
this.runningTasks.delete(taskId);
|
|
84
104
|
const task = await this.stateManager.getTask(taskId);
|
|
85
105
|
if (task) {
|
|
86
|
-
await this.
|
|
87
|
-
status: 'completed',
|
|
106
|
+
await this.transitionTask(taskId, 'accept_done', {
|
|
88
107
|
phases: {
|
|
89
108
|
...task.phases,
|
|
90
109
|
accept: { status: 'completed', duration: 0, completedAt: new Date().toISOString() }
|
|
@@ -97,42 +116,96 @@ class Scheduler {
|
|
|
97
116
|
*/
|
|
98
117
|
async markTaskFailed(taskId, error) {
|
|
99
118
|
this.runningTasks.delete(taskId);
|
|
100
|
-
await this.stateManager.
|
|
101
|
-
|
|
119
|
+
const task = await this.stateManager.getTask(taskId);
|
|
120
|
+
const retryCount = task?.retryCount || 0;
|
|
121
|
+
await this.transitionTask(taskId, 'fail', {
|
|
102
122
|
error,
|
|
103
|
-
retryCount
|
|
123
|
+
retryCount
|
|
104
124
|
});
|
|
105
125
|
}
|
|
106
126
|
/**
|
|
107
127
|
* 标记任务等待确认
|
|
108
128
|
*/
|
|
109
129
|
async markTaskWaiting(taskId) {
|
|
110
|
-
await this.
|
|
111
|
-
status: 'waiting'
|
|
112
|
-
});
|
|
130
|
+
await this.transitionTask(taskId, 'wait');
|
|
113
131
|
}
|
|
114
132
|
/**
|
|
115
133
|
* 标记任务阻塞(需要 Meeting)
|
|
116
134
|
*/
|
|
117
135
|
async markTaskBlocked(taskId, reason) {
|
|
118
136
|
this.runningTasks.delete(taskId);
|
|
119
|
-
await this.
|
|
120
|
-
status: 'blocked',
|
|
137
|
+
await this.transitionTask(taskId, 'block', {
|
|
121
138
|
error: reason
|
|
122
139
|
});
|
|
123
140
|
}
|
|
124
141
|
/**
|
|
125
|
-
*
|
|
142
|
+
* 获取所有可并行执行的任务(按优先级排序)
|
|
126
143
|
*/
|
|
127
144
|
async getParallelTasks() {
|
|
128
145
|
const tasks = await this.stateManager.listTasks();
|
|
129
|
-
|
|
146
|
+
// 检测并处理循环依赖
|
|
147
|
+
await this.handleCircularDependencies(tasks);
|
|
148
|
+
// 筛选可执行任务
|
|
149
|
+
const executable = tasks.filter(task => this.canExecute(task, tasks));
|
|
150
|
+
// 按优先级排序(高优先级优先)
|
|
151
|
+
executable.sort((a, b) => this.getPriorityWeight(b.priority) - this.getPriorityWeight(a.priority));
|
|
152
|
+
// 限制并发数
|
|
153
|
+
return executable.slice(0, this.config.maxConcurrentTasks);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 检测循环依赖(DFS)
|
|
157
|
+
* 返回所有检测到的循环路径
|
|
158
|
+
*/
|
|
159
|
+
detectCircularDependencies(tasks) {
|
|
160
|
+
const cycles = [];
|
|
161
|
+
const visited = new Set();
|
|
162
|
+
const recursionStack = new Set();
|
|
163
|
+
const dfs = (taskId, path) => {
|
|
164
|
+
visited.add(taskId);
|
|
165
|
+
recursionStack.add(taskId);
|
|
166
|
+
const task = tasks.find(t => t.id === taskId);
|
|
167
|
+
if (!task || !task.dependencies) {
|
|
168
|
+
recursionStack.delete(taskId);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
for (const depId of task.dependencies) {
|
|
172
|
+
if (!visited.has(depId)) {
|
|
173
|
+
dfs(depId, [...path, depId]);
|
|
174
|
+
}
|
|
175
|
+
else if (recursionStack.has(depId)) {
|
|
176
|
+
// 找到循环
|
|
177
|
+
const cycleStart = path.indexOf(depId);
|
|
178
|
+
const cycle = cycleStart >= 0
|
|
179
|
+
? [...path.slice(cycleStart), depId]
|
|
180
|
+
: [taskId, depId];
|
|
181
|
+
cycles.push(cycle);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
recursionStack.delete(taskId);
|
|
185
|
+
};
|
|
130
186
|
for (const task of tasks) {
|
|
131
|
-
if (
|
|
132
|
-
|
|
187
|
+
if (!visited.has(task.id)) {
|
|
188
|
+
dfs(task.id, [task.id]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return cycles;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 检测循环依赖并将相关任务标记为 blocked
|
|
195
|
+
*/
|
|
196
|
+
async handleCircularDependencies(tasks) {
|
|
197
|
+
const cycles = this.detectCircularDependencies(tasks);
|
|
198
|
+
for (const cycle of cycles) {
|
|
199
|
+
for (const taskId of cycle) {
|
|
200
|
+
const task = tasks.find(t => t.id === taskId);
|
|
201
|
+
if (task && (task.status === 'pending' || task.status === 'retry_queue')) {
|
|
202
|
+
await this.stateManager.updateTask(taskId, {
|
|
203
|
+
status: 'blocked',
|
|
204
|
+
error: `循环依赖检测: ${cycle.join(' → ')}`
|
|
205
|
+
});
|
|
206
|
+
}
|
|
133
207
|
}
|
|
134
208
|
}
|
|
135
|
-
return available;
|
|
136
209
|
}
|
|
137
210
|
/**
|
|
138
211
|
* 获取调度状态
|
|
@@ -7,6 +7,8 @@ const TRANSITIONS = [
|
|
|
7
7
|
{ from: 'pending', to: 'scheduled', event: 'schedule' },
|
|
8
8
|
// scheduled → in_progress
|
|
9
9
|
{ from: 'scheduled', to: 'in_progress', event: 'start' },
|
|
10
|
+
// pending → in_progress (跳过 scheduled,直接开始)
|
|
11
|
+
{ from: 'pending', to: 'in_progress', event: 'start' },
|
|
10
12
|
// in_progress → verify
|
|
11
13
|
{ from: 'in_progress', to: 'verify', event: 'develop_done' },
|
|
12
14
|
// verify → accept
|
|
@@ -2,7 +2,12 @@ import type { GlobalState, Task, Approval, ApprovalStatus } from '../types/index
|
|
|
2
2
|
export declare class StateManager {
|
|
3
3
|
private store;
|
|
4
4
|
private stateCache;
|
|
5
|
+
private writeLock;
|
|
5
6
|
constructor(basePath: string);
|
|
7
|
+
/**
|
|
8
|
+
* 串行化异步写操作,防止并发 read-modify-write 竞态
|
|
9
|
+
*/
|
|
10
|
+
private withLock;
|
|
6
11
|
initialize(): Promise<void>;
|
|
7
12
|
getState(): Promise<GlobalState>;
|
|
8
13
|
updateState(updates: Partial<GlobalState>): Promise<void>;
|
|
@@ -12,9 +12,25 @@ const DEFAULT_CONFIG = {
|
|
|
12
12
|
class StateManager {
|
|
13
13
|
store;
|
|
14
14
|
stateCache = null;
|
|
15
|
+
writeLock = Promise.resolve();
|
|
15
16
|
constructor(basePath) {
|
|
16
17
|
this.store = new file_store_js_1.FileStore(basePath);
|
|
17
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* 串行化异步写操作,防止并发 read-modify-write 竞态
|
|
21
|
+
*/
|
|
22
|
+
async withLock(fn) {
|
|
23
|
+
const prev = this.writeLock;
|
|
24
|
+
let resolve;
|
|
25
|
+
this.writeLock = new Promise(r => { resolve = r; });
|
|
26
|
+
await prev;
|
|
27
|
+
try {
|
|
28
|
+
return await fn();
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
resolve();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
18
34
|
async initialize() {
|
|
19
35
|
const existing = await this.store.readJson('state.json');
|
|
20
36
|
if (!existing) {
|
|
@@ -47,46 +63,53 @@ class StateManager {
|
|
|
47
63
|
return this.stateCache;
|
|
48
64
|
}
|
|
49
65
|
async updateState(updates) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
await this.withLock(async () => {
|
|
67
|
+
const state = await this.getState();
|
|
68
|
+
const newState = { ...state, ...updates };
|
|
69
|
+
await this.store.writeJson('state.json', newState);
|
|
70
|
+
this.stateCache = newState;
|
|
71
|
+
});
|
|
54
72
|
}
|
|
55
73
|
async createTask(input) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
...state
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
74
|
+
return this.withLock(async () => {
|
|
75
|
+
const taskId = this.generateTaskId();
|
|
76
|
+
const now = new Date().toISOString();
|
|
77
|
+
const task = {
|
|
78
|
+
id: taskId,
|
|
79
|
+
title: input.title,
|
|
80
|
+
description: input.description,
|
|
81
|
+
status: 'pending',
|
|
82
|
+
priority: input.priority,
|
|
83
|
+
timeout: input.timeout,
|
|
84
|
+
dependencies: input.dependencies,
|
|
85
|
+
assignedAgent: input.assignedAgent,
|
|
86
|
+
phases: {
|
|
87
|
+
develop: { status: 'pending', duration: null },
|
|
88
|
+
verify: { status: 'pending', duration: null },
|
|
89
|
+
accept: { status: 'pending', duration: null }
|
|
90
|
+
},
|
|
91
|
+
retryCount: 0,
|
|
92
|
+
error: null,
|
|
93
|
+
createdAt: now,
|
|
94
|
+
updatedAt: now
|
|
95
|
+
};
|
|
96
|
+
await this.store.writeJson(`tasks/${taskId}/task.json`, task);
|
|
97
|
+
// Create artifacts subdirectory
|
|
98
|
+
await this.store.ensureDir(`tasks/${taskId}/artifacts`);
|
|
99
|
+
// Update statistics
|
|
100
|
+
const state = await this.getState();
|
|
101
|
+
const newState = {
|
|
102
|
+
...state,
|
|
103
|
+
statistics: {
|
|
104
|
+
...state.statistics,
|
|
105
|
+
totalTasks: state.statistics.totalTasks + 1,
|
|
106
|
+
pending: state.statistics.pending + 1
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
await this.store.writeJson('state.json', newState);
|
|
110
|
+
this.stateCache = newState;
|
|
111
|
+
return task;
|
|
88
112
|
});
|
|
89
|
-
return task;
|
|
90
113
|
}
|
|
91
114
|
async getTask(taskId) {
|
|
92
115
|
// Try subdirectory structure first, fall back to flat file
|
|
@@ -97,21 +120,23 @@ class StateManager {
|
|
|
97
120
|
return task;
|
|
98
121
|
}
|
|
99
122
|
async updateTask(taskId, updates) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
123
|
+
await this.withLock(async () => {
|
|
124
|
+
const task = await this.getTask(taskId);
|
|
125
|
+
if (!task)
|
|
126
|
+
throw new Error(`Task ${taskId} not found`);
|
|
127
|
+
const oldStatus = task.status;
|
|
128
|
+
const updatedTask = {
|
|
129
|
+
...task,
|
|
130
|
+
...updates,
|
|
131
|
+
updatedAt: new Date().toISOString()
|
|
132
|
+
};
|
|
133
|
+
// Always write to subdirectory structure
|
|
134
|
+
await this.store.writeJson(`tasks/${taskId}/task.json`, updatedTask);
|
|
135
|
+
// Update statistics if status changed
|
|
136
|
+
if (updates.status && updates.status !== oldStatus) {
|
|
137
|
+
await this.updateTaskStatistics(oldStatus, updates.status);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
115
140
|
}
|
|
116
141
|
async listTasks() {
|
|
117
142
|
const tasks = [];
|
|
@@ -168,6 +193,7 @@ class StateManager {
|
|
|
168
193
|
return await this.store.readMarkdown(`tasks/${taskId}/context.md`);
|
|
169
194
|
}
|
|
170
195
|
async updateTaskStatistics(oldStatus, newStatus) {
|
|
196
|
+
// 直接写入状态,不经过 updateState(避免重入锁死锁)
|
|
171
197
|
const state = await this.getState();
|
|
172
198
|
const stats = { ...state.statistics };
|
|
173
199
|
// Decrement old status count
|
|
@@ -188,7 +214,9 @@ class StateManager {
|
|
|
188
214
|
stats.completed++;
|
|
189
215
|
else if (newStatus === 'failed')
|
|
190
216
|
stats.failed++;
|
|
191
|
-
|
|
217
|
+
const newState = { ...state, statistics: stats };
|
|
218
|
+
await this.store.writeJson('state.json', newState);
|
|
219
|
+
this.stateCache = newState;
|
|
192
220
|
}
|
|
193
221
|
generateRunId() {
|
|
194
222
|
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
package/package.json
CHANGED
package/skills/brainstorm.md
CHANGED
|
@@ -269,6 +269,8 @@ $ARGUMENTS
|
|
|
269
269
|
|
|
270
270
|
## 问题类型
|
|
271
271
|
|
|
272
|
+
### 项目配置问题(智能管道生成)
|
|
273
|
+
|
|
272
274
|
| 问题 ID | 目的 | 为什么重要 |
|
|
273
275
|
|---------|------|-----------|
|
|
274
276
|
| objective | 明确任务目标(新功能/修复/重构) | 选择正确的实现策略 |
|
|
@@ -283,6 +285,17 @@ $ARGUMENTS
|
|
|
283
285
|
|
|
284
286
|
> **智能预填**:当 `SmartQuestionAnalyzer` 对某个问题有高置信度推断时,该问题会被自动跳过,不需要用户回答。
|
|
285
287
|
|
|
288
|
+
### 领域分析问题(底层逻辑思考)
|
|
289
|
+
|
|
290
|
+
| 问题 ID | 目的 | 为什么重要 |
|
|
291
|
+
|---------|------|-----------|
|
|
292
|
+
| domain_entities | 核心领域实体建模 | 决定数据模型和 API 设计的基础 |
|
|
293
|
+
| data_flow | 数据流转路径分析 | 决定架构选型(请求驱动 vs 事件驱动 vs 流处理) |
|
|
294
|
+
| invariants | 关键不变量/业务约束 | 决定哪里需要加锁、事务、校验 |
|
|
295
|
+
| core_scenarios | 核心用户场景链路 | 决定 MVP 功能范围和优先级排序 |
|
|
296
|
+
|
|
297
|
+
> **领域分析的价值**:这四个问题帮助 AI 在执行前建立对系统的深层理解,而不是机械地按需求列表编码。答案会作为上下文注入到每个 Agent 的执行提示词中。
|
|
298
|
+
|
|
286
299
|
## 与 start 的集成
|
|
287
300
|
|
|
288
301
|
头脑风暴完成后,收集的信息会传递给 start:
|