openmatrix 0.1.62 → 0.1.64
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/agents/agent-runner.js +19 -10
- package/dist/cli/commands/brainstorm.js +135 -18
- package/dist/cli/commands/complete.js +35 -2
- package/dist/cli/commands/start.js +6 -3
- package/dist/cli/commands/step.js +7 -1
- package/dist/orchestrator/executor.d.ts +19 -0
- package/dist/orchestrator/executor.js +77 -2
- package/dist/orchestrator/git-commit-manager.js +12 -2
- package/dist/orchestrator/phase-executor.d.ts +3 -0
- package/dist/orchestrator/phase-executor.js +445 -349
- package/dist/orchestrator/scheduler.d.ts +15 -1
- package/dist/orchestrator/scheduler.js +91 -18
- package/dist/orchestrator/state-machine.js +2 -0
- package/dist/storage/file-store.d.ts +9 -1
- package/dist/storage/file-store.js +24 -9
- package/dist/storage/state-manager.d.ts +5 -0
- package/dist/storage/state-manager.js +139 -54
- package/dist/types/index.d.ts +6 -0
- package/package.json +1 -1
- package/skills/auto.md +14 -2
- package/skills/brainstorm.md +13 -0
- package/skills/meeting.md +15 -0
- package/skills/start.md +10 -1
|
@@ -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,15 +1,18 @@
|
|
|
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
|
-
taskTimeout:
|
|
15
|
+
taskTimeout: 600000, // 10 分钟
|
|
13
16
|
...config
|
|
14
17
|
};
|
|
15
18
|
}
|
|
@@ -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
|
|
@@ -3,8 +3,16 @@ export declare class FileStore {
|
|
|
3
3
|
constructor(basePath: string);
|
|
4
4
|
ensureDir(path: string): Promise<void>;
|
|
5
5
|
writeJson<T>(path: string, data: T): Promise<void>;
|
|
6
|
-
readJson<T>(path: string): Promise<T | null>;
|
|
7
6
|
writeMarkdown(path: string, content: string): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* 读取 JSON 文件
|
|
9
|
+
* @returns 文件内容,如果文件不存在则返回 null;其他错误会抛出异常
|
|
10
|
+
*/
|
|
11
|
+
readJson<T>(path: string): Promise<T | null>;
|
|
12
|
+
/**
|
|
13
|
+
* 读取 Markdown 文件
|
|
14
|
+
* @returns 文件内容,如果文件不存在则返回 null;其他错误会抛出异常
|
|
15
|
+
*/
|
|
8
16
|
readMarkdown(path: string): Promise<string | null>;
|
|
9
17
|
exists(path: string): Promise<boolean>;
|
|
10
18
|
listFiles(dir: string): Promise<string[]>;
|
|
@@ -18,28 +18,43 @@ class FileStore {
|
|
|
18
18
|
await (0, promises_1.mkdir)((0, path_1.dirname)(fullPath), { recursive: true });
|
|
19
19
|
await (0, promises_1.writeFile)(fullPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
20
20
|
}
|
|
21
|
+
async writeMarkdown(path, content) {
|
|
22
|
+
const fullPath = (0, path_1.join)(this.basePath, path);
|
|
23
|
+
await (0, promises_1.mkdir)((0, path_1.dirname)(fullPath), { recursive: true });
|
|
24
|
+
await (0, promises_1.writeFile)(fullPath, content, 'utf-8');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 读取 JSON 文件
|
|
28
|
+
* @returns 文件内容,如果文件不存在则返回 null;其他错误会抛出异常
|
|
29
|
+
*/
|
|
21
30
|
async readJson(path) {
|
|
22
31
|
const fullPath = (0, path_1.join)(this.basePath, path);
|
|
23
32
|
try {
|
|
24
33
|
const content = await (0, promises_1.readFile)(fullPath, 'utf-8');
|
|
25
34
|
return JSON.parse(content);
|
|
26
35
|
}
|
|
27
|
-
catch {
|
|
28
|
-
|
|
36
|
+
catch (error) {
|
|
37
|
+
// 只隐藏"文件不存在"错误,其他错误(权限、磁盘等)抛出以便调用者处理
|
|
38
|
+
if (error.code === 'ENOENT' || error.code === 'EISDIR') {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
29
42
|
}
|
|
30
43
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
44
|
+
/**
|
|
45
|
+
* 读取 Markdown 文件
|
|
46
|
+
* @returns 文件内容,如果文件不存在则返回 null;其他错误会抛出异常
|
|
47
|
+
*/
|
|
36
48
|
async readMarkdown(path) {
|
|
37
49
|
const fullPath = (0, path_1.join)(this.basePath, path);
|
|
38
50
|
try {
|
|
39
51
|
return await (0, promises_1.readFile)(fullPath, 'utf-8');
|
|
40
52
|
}
|
|
41
|
-
catch {
|
|
42
|
-
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error.code === 'ENOENT' || error.code === 'EISDIR') {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
43
58
|
}
|
|
44
59
|
}
|
|
45
60
|
async exists(path) {
|
|
@@ -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) {
|
|
@@ -30,13 +46,33 @@ class StateManager {
|
|
|
30
46
|
completed: 0,
|
|
31
47
|
inProgress: 0,
|
|
32
48
|
failed: 0,
|
|
33
|
-
pending: 0
|
|
49
|
+
pending: 0,
|
|
50
|
+
scheduled: 0,
|
|
51
|
+
blocked: 0,
|
|
52
|
+
waiting: 0,
|
|
53
|
+
verify: 0,
|
|
54
|
+
accept: 0,
|
|
55
|
+
retry_queue: 0
|
|
34
56
|
}
|
|
35
57
|
};
|
|
36
58
|
await this.store.writeJson('state.json', initialState);
|
|
37
59
|
this.stateCache = initialState;
|
|
38
60
|
}
|
|
39
61
|
else {
|
|
62
|
+
// 合并旧状态的统计字段(兼容旧版本)
|
|
63
|
+
existing.statistics = {
|
|
64
|
+
totalTasks: existing.statistics.totalTasks ?? 0,
|
|
65
|
+
completed: existing.statistics.completed ?? 0,
|
|
66
|
+
inProgress: existing.statistics.inProgress ?? 0,
|
|
67
|
+
failed: existing.statistics.failed ?? 0,
|
|
68
|
+
pending: existing.statistics.pending ?? 0,
|
|
69
|
+
scheduled: existing.statistics.scheduled ?? 0,
|
|
70
|
+
blocked: existing.statistics.blocked ?? 0,
|
|
71
|
+
waiting: existing.statistics.waiting ?? 0,
|
|
72
|
+
verify: existing.statistics.verify ?? 0,
|
|
73
|
+
accept: existing.statistics.accept ?? 0,
|
|
74
|
+
retry_queue: existing.statistics.retry_queue ?? 0
|
|
75
|
+
};
|
|
40
76
|
this.stateCache = existing;
|
|
41
77
|
}
|
|
42
78
|
}
|
|
@@ -47,46 +83,53 @@ class StateManager {
|
|
|
47
83
|
return this.stateCache;
|
|
48
84
|
}
|
|
49
85
|
async updateState(updates) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
86
|
+
await this.withLock(async () => {
|
|
87
|
+
const state = await this.getState();
|
|
88
|
+
const newState = { ...state, ...updates };
|
|
89
|
+
await this.store.writeJson('state.json', newState);
|
|
90
|
+
this.stateCache = newState;
|
|
91
|
+
});
|
|
54
92
|
}
|
|
55
93
|
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
|
-
|
|
94
|
+
return this.withLock(async () => {
|
|
95
|
+
const taskId = this.generateTaskId();
|
|
96
|
+
const now = new Date().toISOString();
|
|
97
|
+
const task = {
|
|
98
|
+
id: taskId,
|
|
99
|
+
title: input.title,
|
|
100
|
+
description: input.description,
|
|
101
|
+
status: 'pending',
|
|
102
|
+
priority: input.priority,
|
|
103
|
+
timeout: input.timeout,
|
|
104
|
+
dependencies: input.dependencies,
|
|
105
|
+
assignedAgent: input.assignedAgent,
|
|
106
|
+
phases: {
|
|
107
|
+
develop: { status: 'pending', duration: null },
|
|
108
|
+
verify: { status: 'pending', duration: null },
|
|
109
|
+
accept: { status: 'pending', duration: null }
|
|
110
|
+
},
|
|
111
|
+
retryCount: 0,
|
|
112
|
+
error: null,
|
|
113
|
+
createdAt: now,
|
|
114
|
+
updatedAt: now
|
|
115
|
+
};
|
|
116
|
+
await this.store.writeJson(`tasks/${taskId}/task.json`, task);
|
|
117
|
+
// Create artifacts subdirectory
|
|
118
|
+
await this.store.ensureDir(`tasks/${taskId}/artifacts`);
|
|
119
|
+
// Update statistics
|
|
120
|
+
const state = await this.getState();
|
|
121
|
+
const newState = {
|
|
122
|
+
...state,
|
|
123
|
+
statistics: {
|
|
124
|
+
...state.statistics,
|
|
125
|
+
totalTasks: state.statistics.totalTasks + 1,
|
|
126
|
+
pending: state.statistics.pending + 1
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
await this.store.writeJson('state.json', newState);
|
|
130
|
+
this.stateCache = newState;
|
|
131
|
+
return task;
|
|
88
132
|
});
|
|
89
|
-
return task;
|
|
90
133
|
}
|
|
91
134
|
async getTask(taskId) {
|
|
92
135
|
// Try subdirectory structure first, fall back to flat file
|
|
@@ -97,21 +140,23 @@ class StateManager {
|
|
|
97
140
|
return task;
|
|
98
141
|
}
|
|
99
142
|
async updateTask(taskId, updates) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
143
|
+
await this.withLock(async () => {
|
|
144
|
+
const task = await this.getTask(taskId);
|
|
145
|
+
if (!task)
|
|
146
|
+
throw new Error(`Task ${taskId} not found`);
|
|
147
|
+
const oldStatus = task.status;
|
|
148
|
+
const updatedTask = {
|
|
149
|
+
...task,
|
|
150
|
+
...updates,
|
|
151
|
+
updatedAt: new Date().toISOString()
|
|
152
|
+
};
|
|
153
|
+
// Always write to subdirectory structure
|
|
154
|
+
await this.store.writeJson(`tasks/${taskId}/task.json`, updatedTask);
|
|
155
|
+
// Update statistics if status changed
|
|
156
|
+
if (updates.status && updates.status !== oldStatus) {
|
|
157
|
+
await this.updateTaskStatistics(oldStatus, updates.status);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
115
160
|
}
|
|
116
161
|
async listTasks() {
|
|
117
162
|
const tasks = [];
|
|
@@ -168,27 +213,67 @@ class StateManager {
|
|
|
168
213
|
return await this.store.readMarkdown(`tasks/${taskId}/context.md`);
|
|
169
214
|
}
|
|
170
215
|
async updateTaskStatistics(oldStatus, newStatus) {
|
|
216
|
+
// 直接写入状态,不经过 updateState(避免重入锁死锁)
|
|
171
217
|
const state = await this.getState();
|
|
172
218
|
const stats = { ...state.statistics };
|
|
219
|
+
// 扩展统计字段(如果不存在则初始化)
|
|
220
|
+
if (!('scheduled' in stats))
|
|
221
|
+
stats.scheduled = 0;
|
|
222
|
+
if (!('blocked' in stats))
|
|
223
|
+
stats.blocked = 0;
|
|
224
|
+
if (!('waiting' in stats))
|
|
225
|
+
stats.waiting = 0;
|
|
226
|
+
if (!('verify' in stats))
|
|
227
|
+
stats.verify = 0;
|
|
228
|
+
if (!('accept' in stats))
|
|
229
|
+
stats.accept = 0;
|
|
230
|
+
if (!('retry_queue' in stats))
|
|
231
|
+
stats.retry_queue = 0;
|
|
173
232
|
// Decrement old status count
|
|
174
233
|
if (oldStatus === 'pending')
|
|
175
234
|
stats.pending--;
|
|
235
|
+
else if (oldStatus === 'scheduled')
|
|
236
|
+
stats.scheduled--;
|
|
176
237
|
else if (oldStatus === 'in_progress')
|
|
177
238
|
stats.inProgress--;
|
|
239
|
+
else if (oldStatus === 'blocked')
|
|
240
|
+
stats.blocked--;
|
|
241
|
+
else if (oldStatus === 'waiting')
|
|
242
|
+
stats.waiting--;
|
|
243
|
+
else if (oldStatus === 'verify')
|
|
244
|
+
stats.verify--;
|
|
245
|
+
else if (oldStatus === 'accept')
|
|
246
|
+
stats.accept--;
|
|
178
247
|
else if (oldStatus === 'completed')
|
|
179
248
|
stats.completed--;
|
|
180
249
|
else if (oldStatus === 'failed')
|
|
181
250
|
stats.failed--;
|
|
251
|
+
else if (oldStatus === 'retry_queue')
|
|
252
|
+
stats.retry_queue--;
|
|
182
253
|
// Increment new status count
|
|
183
254
|
if (newStatus === 'pending')
|
|
184
255
|
stats.pending++;
|
|
256
|
+
else if (newStatus === 'scheduled')
|
|
257
|
+
stats.scheduled++;
|
|
185
258
|
else if (newStatus === 'in_progress')
|
|
186
259
|
stats.inProgress++;
|
|
260
|
+
else if (newStatus === 'blocked')
|
|
261
|
+
stats.blocked++;
|
|
262
|
+
else if (newStatus === 'waiting')
|
|
263
|
+
stats.waiting++;
|
|
264
|
+
else if (newStatus === 'verify')
|
|
265
|
+
stats.verify++;
|
|
266
|
+
else if (newStatus === 'accept')
|
|
267
|
+
stats.accept++;
|
|
187
268
|
else if (newStatus === 'completed')
|
|
188
269
|
stats.completed++;
|
|
189
270
|
else if (newStatus === 'failed')
|
|
190
271
|
stats.failed++;
|
|
191
|
-
|
|
272
|
+
else if (newStatus === 'retry_queue')
|
|
273
|
+
stats.retry_queue++;
|
|
274
|
+
const newState = { ...state, statistics: stats };
|
|
275
|
+
await this.store.writeJson('state.json', newState);
|
|
276
|
+
this.stateCache = newState;
|
|
192
277
|
}
|
|
193
278
|
generateRunId() {
|
|
194
279
|
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
package/dist/types/index.d.ts
CHANGED
|
@@ -85,6 +85,12 @@ export interface GlobalState {
|
|
|
85
85
|
inProgress: number;
|
|
86
86
|
failed: number;
|
|
87
87
|
pending: number;
|
|
88
|
+
scheduled: number;
|
|
89
|
+
blocked: number;
|
|
90
|
+
waiting: number;
|
|
91
|
+
verify: number;
|
|
92
|
+
accept: number;
|
|
93
|
+
retry_queue: number;
|
|
88
94
|
};
|
|
89
95
|
}
|
|
90
96
|
export interface AppConfig {
|
package/package.json
CHANGED
package/skills/auto.md
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: om:auto
|
|
3
|
-
description:
|
|
3
|
+
description: 全自动执行任务指令 - AI 拆分,无阻塞,bypass permissions
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
<NOTE>
|
|
7
|
+
## 注意:区分 `/om:auto` 指令与「全自动执行」模式
|
|
8
|
+
|
|
9
|
+
- **`/om:auto`** 是一个 **Skill 指令**,为 Agent 无障碍执行准备
|
|
10
|
+
- **「全自动执行」**是 `/om:start` 中用户选择的 **执行模式选项**
|
|
11
|
+
|
|
12
|
+
**关键区别**:`/om/auto` 不创建 Meeting 记录,直接跳过阻塞任务。
|
|
13
|
+
</NOTE>
|
|
14
|
+
|
|
6
15
|
<NO-OTHER-SKILLS>
|
|
7
16
|
执行此技能时,不得调用 superpowers、gsd 或其他任务编排相关的技能。OpenMatrix 独立运行,不依赖外部任务编排系统。
|
|
8
17
|
</NO-OTHER-SKILLS>
|
|
@@ -191,7 +200,10 @@ openmatrix step --json
|
|
|
191
200
|
如果返回 `status: "next"` → 继续执行返回的 task
|
|
192
201
|
如果返回 `status: "done"` → 所有任务完成,进入最终提交
|
|
193
202
|
如果返回 `status: "blocked"` → 有阻塞任务,处理 Meeting
|
|
194
|
-
|
|
203
|
+
|
|
204
|
+
**Meeting 处理机制:**
|
|
205
|
+
`/om:auto` 不创建 Meeting 记录,直接跳过阻塞任务,无障碍执行。
|
|
206
|
+
|
|
195
207
|
|
|
196
208
|
**Agent 上下文共享机制 (Agent Memory):**
|
|
197
209
|
|
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:
|