@yeaft/webchat-agent 0.1.248 → 0.1.250
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/connection/message-router.js +0 -26
- package/crew/role-query.js +8 -9
- package/index.js +0 -2
- package/package.json +1 -1
- package/conductor/actor.js +0 -478
- package/conductor/claudemd-generator.js +0 -194
- package/conductor/conductor-claude.js +0 -480
- package/conductor/index.js +0 -65
- package/conductor/orchestrator-flows.js +0 -498
- package/conductor/orchestrator.js +0 -888
- package/conductor/persistence.js +0 -297
- package/conductor/personas/dev.js +0 -97
- package/conductor/personas/index.js +0 -77
- package/conductor/personas/trading.js +0 -92
- package/conductor/personas/video.js +0 -75
- package/conductor/personas/writing.js +0 -82
- package/conductor/semaphore.js +0 -73
- package/conductor/session.js +0 -210
- package/conductor/specialties/common.js +0 -350
- package/conductor/specialties/index.js +0 -135
- package/conductor/specialties/trading.js +0 -165
- package/conductor/specialties/video.js +0 -175
- package/conductor/specialties/writing.js +0 -120
- package/conductor/task.js +0 -333
- package/conductor/ui-messages.js +0 -170
- package/conductor/worktree.js +0 -182
- package/conductor.js +0 -35
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Conductor — Video 场景特有 Specialty 定义
|
|
3
|
-
* scripting, storyboarding, prompt-assembly, continuity-check
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const videoSpecialties = {
|
|
7
|
-
scripting: {
|
|
8
|
-
id: 'scripting',
|
|
9
|
-
displayName: 'Scripting(编剧)',
|
|
10
|
-
thinkingMode: 'divergent',
|
|
11
|
-
input: '主题 + 情绪基调 + 时长约束(6-8段×15秒)',
|
|
12
|
-
output: '分段脚本(每段:画面描述 + 旁白文案 + 情绪基调 + 一致性锚点)',
|
|
13
|
-
toolRules: {
|
|
14
|
-
readonly: ['Read', 'Grep', 'Glob'],
|
|
15
|
-
writable: ['内容文件'],
|
|
16
|
-
forbidden: []
|
|
17
|
-
},
|
|
18
|
-
getPersonalizedBehavior(persona) {
|
|
19
|
-
const behaviors = {
|
|
20
|
-
shi: `你用生命的重量写每一个字:
|
|
21
|
-
- **朴素有力**:最日常的语言往往最打动人
|
|
22
|
-
- **留白大于堆砌**:不说的比说的更重要
|
|
23
|
-
- **画面即情感**:每一段画面描述都要让人"看见"情感
|
|
24
|
-
- **内省视角**:旁白是对生命的追问,不是解说词
|
|
25
|
-
- **一致性锚点**:每段标注关键视觉元素,确保跨段统一
|
|
26
|
-
- **时长控制**:6-8段×15秒,每段都要有叙事价值,不浪费一秒`
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
return behaviors[persona.id] || `你用 ${persona.name} 的风格编写脚本:
|
|
30
|
-
- 分段撰写画面描述和旁白
|
|
31
|
-
- 标注每段的情绪基调
|
|
32
|
-
- 确保一致性锚点完整`;
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
getOutputFormat() {
|
|
36
|
-
return `分段脚本:
|
|
37
|
-
逐段(6-8段):
|
|
38
|
-
- 段号 + 时长
|
|
39
|
-
- 画面描述(具体、可视化)
|
|
40
|
-
- 旁白文案
|
|
41
|
-
- 情绪基调
|
|
42
|
-
- 一致性锚点(角色外貌/场景风格/色调/光线)
|
|
43
|
-
整体:
|
|
44
|
-
- 叙事弧线摘要
|
|
45
|
-
- 情绪曲线`;
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
storyboarding: {
|
|
50
|
-
id: 'storyboarding',
|
|
51
|
-
displayName: 'Storyboarding(分镜设计)',
|
|
52
|
-
thinkingMode: 'convergent',
|
|
53
|
-
input: '脚本 + 视觉风格方向',
|
|
54
|
-
output: '分镜方案(景别/镜头运动/构图/转场 + 一致性锚点清单 + AI prompt 要素)',
|
|
55
|
-
toolRules: {
|
|
56
|
-
readonly: ['Read', 'Grep', 'Glob'],
|
|
57
|
-
writable: ['memory.md'],
|
|
58
|
-
forbidden: []
|
|
59
|
-
},
|
|
60
|
-
getPersonalizedBehavior(persona) {
|
|
61
|
-
const behaviors = {
|
|
62
|
-
tsui: `你的脑中永远有画面在运动:
|
|
63
|
-
- **景别选择**:远景建立空间感、中景展现关系、近景传递情感、特写捕捉细节
|
|
64
|
-
- **镜头运动**:推拉摇移跟,每个运动都有叙事目的
|
|
65
|
-
- **构图法则**:三分法、对角线、框中框——但规则是用来打破的
|
|
66
|
-
- **转场设计**:硬切、溶解、匹配剪辑——转场本身也是叙事
|
|
67
|
-
- **视觉冲击**:在关键节点给观众一拳,但必须服务于故事
|
|
68
|
-
- **跨片段统一**:每个镜头是整体的一部分,光线/色调/风格必须连贯
|
|
69
|
-
- **AI prompt 要素**:每个分镜标注可直接用于 AI 视频生成的关键元素`
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
return behaviors[persona.id] || `你用 ${persona.name} 的视觉语言设计分镜:
|
|
73
|
-
- 逐段设计景别、镜头运动、构图
|
|
74
|
-
- 标注转场方式
|
|
75
|
-
- 提取 AI prompt 关键要素`;
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
getOutputFormat() {
|
|
79
|
-
return `分镜方案:
|
|
80
|
-
逐段:
|
|
81
|
-
- 段号
|
|
82
|
-
- 景别(远/中/近/特写)
|
|
83
|
-
- 镜头运动(推/拉/摇/移/跟/固定)
|
|
84
|
-
- 构图描述
|
|
85
|
-
- 转场方式(至下一段)
|
|
86
|
-
- AI prompt 要素清单
|
|
87
|
-
整体:
|
|
88
|
-
- 一致性锚点清单(角色/场景/色调/光线标准)
|
|
89
|
-
- 视觉风格说明`;
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
'prompt-assembly': {
|
|
94
|
-
id: 'prompt-assembly',
|
|
95
|
-
displayName: 'Prompt Assembly(Prompt 组装)',
|
|
96
|
-
thinkingMode: 'convergent',
|
|
97
|
-
input: '分镜方案 + 一致性锚点',
|
|
98
|
-
output: 'AI 视频生成 prompt 列表(6-8条,含一致性前缀 + 场景描述 + 镜头参数 + 风格关键词)',
|
|
99
|
-
toolRules: {
|
|
100
|
-
readonly: ['Read', 'Grep', 'Glob'],
|
|
101
|
-
writable: ['内容文件'],
|
|
102
|
-
forbidden: []
|
|
103
|
-
},
|
|
104
|
-
getPersonalizedBehavior(persona) {
|
|
105
|
-
const behaviors = {
|
|
106
|
-
gu: `你懂每个技术参数背后的情感含义:
|
|
107
|
-
- **一致性前缀**:每条 prompt 的开头必须包含统一的角色/场景/风格锚点
|
|
108
|
-
- **场景描述精准**:具体、可量化——"午后3点的斜阳"比"温暖的光线"好
|
|
109
|
-
- **镜头参数完整**:景别、运动方式、速度、角度,AI 需要这些
|
|
110
|
-
- **风格关键词**:色调、氛围、参考风格,用 AI 能理解的词汇
|
|
111
|
-
- **色彩一致性**:跨段的色调方案必须统一,冷暖变化要有叙事逻辑
|
|
112
|
-
- **节奏控制**:通过 prompt 中的动态/静态平衡控制视觉节奏`
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
return behaviors[persona.id] || `你用 ${persona.name} 的专业组装 AI prompt:
|
|
116
|
-
- 每条包含一致性前缀
|
|
117
|
-
- 场景描述具体精准
|
|
118
|
-
- 镜头参数和风格关键词完整`;
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
getOutputFormat() {
|
|
122
|
-
return `AI 视频生成 Prompt 列表:
|
|
123
|
-
逐条(6-8条):
|
|
124
|
-
- 段号
|
|
125
|
-
- 一致性前缀(角色/场景/风格标准描述)
|
|
126
|
-
- 场景描述(具体画面内容)
|
|
127
|
-
- 镜头参数(景别/运动/速度/角度)
|
|
128
|
-
- 风格关键词(色调/氛围/参考)
|
|
129
|
-
- 时长标注
|
|
130
|
-
附加:
|
|
131
|
-
- 制作指南(工具建议/参数建议/注意事项)`;
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
'continuity-check': {
|
|
136
|
-
id: 'continuity-check',
|
|
137
|
-
displayName: 'Continuity Check(一致性测试)',
|
|
138
|
-
thinkingMode: 'convergent',
|
|
139
|
-
input: '所有 prompt + 一致性锚点清单',
|
|
140
|
-
output: '一致性报告(pass/fail + 不一致项清单)',
|
|
141
|
-
toolRules: {
|
|
142
|
-
readonly: ['Read', 'Grep', 'Glob'],
|
|
143
|
-
writable: [],
|
|
144
|
-
forbidden: ['prompt 文件(你是检查者,不是修改者)']
|
|
145
|
-
},
|
|
146
|
-
getPersonalizedBehavior(persona) {
|
|
147
|
-
const behaviors = {
|
|
148
|
-
gu: `你对不一致零容忍:
|
|
149
|
-
- **锚点完整性**:逐条检查每个 prompt 是否包含所有必要的一致性锚点
|
|
150
|
-
- **色调统一性**:跨段的色调描述是否一致?有意的变化是否有叙事逻辑?
|
|
151
|
-
- **角色描述一致**:同一角色在不同段的外貌描述是否矛盾?
|
|
152
|
-
- **场景连贯性**:场景之间的过渡是否合理?时间/空间跳跃是否有交代?
|
|
153
|
-
- **风格统一性**:整体风格关键词是否一致?
|
|
154
|
-
- **时长合理性**:每段时长是否合理?总时长是否在约束范围内?`
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
return behaviors[persona.id] || `你用 ${persona.name} 的标准做一致性检查:
|
|
158
|
-
- 逐条验证锚点完整性
|
|
159
|
-
- 检查跨段一致性
|
|
160
|
-
- 评估整体连贯性`;
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
getOutputFormat() {
|
|
164
|
-
return `一致性检查报告:
|
|
165
|
-
1. 锚点完整性检查(逐条 pass/fail)
|
|
166
|
-
2. 色调统一性检查(跨段对比)
|
|
167
|
-
3. 角色描述一致性检查
|
|
168
|
-
4. 场景连贯性检查
|
|
169
|
-
5. 风格统一性检查
|
|
170
|
-
6. 时长合理性检查
|
|
171
|
-
7. 不一致项清单(如有:位置 + 描述 + 修改建议)
|
|
172
|
-
8. 结论:pass / fail`;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
};
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Conductor — Writing 场景特有 Specialty 定义
|
|
3
|
-
* writing, pacing, editing
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const writingSpecialties = {
|
|
7
|
-
writing: {
|
|
8
|
-
id: 'writing',
|
|
9
|
-
displayName: 'Writing(撰写)',
|
|
10
|
-
thinkingMode: 'convergent',
|
|
11
|
-
input: '章节大纲 + 节奏方案 + 角色设定',
|
|
12
|
-
output: '正文章节(2000-4000字/章)',
|
|
13
|
-
toolRules: {
|
|
14
|
-
readonly: ['Read', 'Grep', 'Glob'],
|
|
15
|
-
writable: ['内容文件'],
|
|
16
|
-
forbidden: []
|
|
17
|
-
},
|
|
18
|
-
getPersonalizedBehavior(persona) {
|
|
19
|
-
const behaviors = {
|
|
20
|
-
zhouzi: `现在是动笔的时候了:
|
|
21
|
-
- **对白即性格**:每个角色的说话方式不同——用词、语气、口癖都是人设的一部分
|
|
22
|
-
- **幽默从角色长出来**:梗不是硬插的,是角色在那个情境下自然会说的话
|
|
23
|
-
- **搞笑中夹私货**:看起来是段子的句子,可能藏着伏笔或情感种子
|
|
24
|
-
- **节奏如呼吸**:轻松-正经-轻松-燃-刀,节奏错了一切都废
|
|
25
|
-
- **章末钩子必杀**:断章是一门艺术,让读者骂着"这个作者真该死"然后点下一章
|
|
26
|
-
- **按大纲和节奏方案执行**:不自作主张改剧情走向`
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
return behaviors[persona.id] || `你用 ${persona.name} 的风格撰写正文:
|
|
30
|
-
- 按大纲和节奏方案执行
|
|
31
|
-
- 对白和叙述自然流畅
|
|
32
|
-
- 每章保持2000-4000字`;
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
getOutputFormat() {
|
|
36
|
-
return `正文章节产出:
|
|
37
|
-
1. 章节正文(2000-4000字)
|
|
38
|
-
2. 章末钩子(让读者继续看的理由)
|
|
39
|
-
3. 伏笔清单(本章埋设/回收的伏笔)
|
|
40
|
-
4. 自评(节奏感、对白质量、爽点落地情况)`;
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
pacing: {
|
|
45
|
-
id: 'pacing',
|
|
46
|
-
displayName: 'Pacing(节奏设计)',
|
|
47
|
-
thinkingMode: 'divergent',
|
|
48
|
-
input: '卷纲/大纲 + 人物关系',
|
|
49
|
-
output: '爽点节奏方案(每章的爽点类型 + 章末钩子 + 情绪曲线)',
|
|
50
|
-
toolRules: {
|
|
51
|
-
readonly: ['Read', 'Grep', 'Glob'],
|
|
52
|
-
writable: ['memory.md'],
|
|
53
|
-
forbidden: ['内容正文']
|
|
54
|
-
},
|
|
55
|
-
getPersonalizedBehavior(persona) {
|
|
56
|
-
const behaviors = {
|
|
57
|
-
zhouzi: `你是节奏大师,这是你最擅长的:
|
|
58
|
-
- **压3爽7黄金比例**:30%铺垫,70%爽点——不是每章,是整体节奏
|
|
59
|
-
- **爽点类型多样化**:反转、装逼打脸、温情、燃战、下刀——不能单调
|
|
60
|
-
- **章末钩子设计**:每章结尾必须让读者手痒,断章技巧是核心竞争力
|
|
61
|
-
- **情绪过山车**:大爽前必有压抑,大刀前必有温情——反差出效果
|
|
62
|
-
- **阅读体感模拟**:闭眼想象自己是读者,这段看到什么感觉?想跳过吗?`
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return behaviors[persona.id] || `你用 ${persona.name} 的直觉设计节奏:
|
|
66
|
-
- 标注每章的爽点类型和情绪基调
|
|
67
|
-
- 设计章末钩子
|
|
68
|
-
- 绘制整体情绪曲线`;
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
getOutputFormat() {
|
|
72
|
-
return `爽点节奏方案:
|
|
73
|
-
1. 整体情绪曲线(图表描述)
|
|
74
|
-
2. 逐章节奏表:
|
|
75
|
-
- 章节号 + 爽点类型 + 情绪基调 + 章末钩子
|
|
76
|
-
3. 压爽比例分析
|
|
77
|
-
4. 关键转折点标注`;
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
editing: {
|
|
82
|
-
id: 'editing',
|
|
83
|
-
displayName: 'Editing(审稿)',
|
|
84
|
-
thinkingMode: 'convergent',
|
|
85
|
-
input: '正文 + 设定文档 + 伏笔账本',
|
|
86
|
-
output: '审稿报告(设定一致性/时间线/人物逻辑/爽点落地/文字质量)',
|
|
87
|
-
toolRules: {
|
|
88
|
-
readonly: ['Read', 'Grep', 'Glob'],
|
|
89
|
-
writable: [],
|
|
90
|
-
forbidden: ['正文修改(你是审稿者,不是修改者)']
|
|
91
|
-
},
|
|
92
|
-
getPersonalizedBehavior(persona) {
|
|
93
|
-
const behaviors = {
|
|
94
|
-
maboyong: `你用考据级的严格标准审稿:
|
|
95
|
-
- **设定一致性**:世界观圣经是宪法,正文不得与设定矛盾——零容忍
|
|
96
|
-
- **时间线核查**:角色在 A 地做完某事,到 B 地需要多少时间?对得上吗?
|
|
97
|
-
- **人物逻辑**:这个角色在这个情境下会说这句话/做这个事吗?符合 TA 的性格吗?
|
|
98
|
-
- **爽点落地**:大纲设计的爽点在正文中是否真正兑现了?效果如何?
|
|
99
|
-
- **文字质量**:有没有病句、重复用词、节奏拖沓?
|
|
100
|
-
- **毒舌但建设性**:指出问题必须给修改建议,不能只说"这不行"`
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
return behaviors[persona.id] || `你用 ${persona.name} 的标准审稿:
|
|
104
|
-
- 逐项检查设定一致性、时间线、人物逻辑
|
|
105
|
-
- 评估爽点落地和文字质量
|
|
106
|
-
- 指出问题并给出修改建议`;
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
getOutputFormat() {
|
|
110
|
-
return `审稿报告:
|
|
111
|
-
1. 设定一致性检查(pass/fail + 矛盾清单)
|
|
112
|
-
2. 时间线核查(pass/fail + 问题标注)
|
|
113
|
-
3. 人物逻辑检查(pass/fail + 不合理行为清单)
|
|
114
|
-
4. 爽点落地评估(每个爽点:设计 vs 实际效果)
|
|
115
|
-
5. 文字质量评分
|
|
116
|
-
6. 修改建议清单(必须改 + 建议改)
|
|
117
|
-
7. 总体评价:pass / fail`;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
};
|
package/conductor/task.js
DELETED
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Conductor Task — 数据结构与生命周期
|
|
3
|
-
*
|
|
4
|
-
* Task 是 Orchestrator 管理的最小可调度工作单元。
|
|
5
|
-
* 每个 Task 绑定到创建时的工作路径,由 Orchestrator 状态机驱动。
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// =====================================================================
|
|
9
|
-
// Constants
|
|
10
|
-
// =====================================================================
|
|
11
|
-
|
|
12
|
-
/** Task complexity levels — TRIAGE 阶段产出 */
|
|
13
|
-
export const Complexity = Object.freeze({
|
|
14
|
-
TRIVIAL: 'trivial', // 一步完成(改个文案、改个配置)
|
|
15
|
-
SMALL: 'small', // 目标清晰,不需要讨论,直接 planning
|
|
16
|
-
COMPLEX: 'complex' // 需要发散讨论 / 多视角 / 设计先行
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
/** Orchestrator 状态 */
|
|
20
|
-
export const Phase = Object.freeze({
|
|
21
|
-
TRIAGE: 'triage',
|
|
22
|
-
DISCUSSION: 'discussion',
|
|
23
|
-
PLANNING: 'planning',
|
|
24
|
-
EXECUTION: 'execution',
|
|
25
|
-
QUALITY_GATE: 'quality_gate',
|
|
26
|
-
ACCEPTANCE: 'acceptance',
|
|
27
|
-
DONE: 'done',
|
|
28
|
-
FAILED: 'failed'
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
/** Actor 实例状态 */
|
|
32
|
-
export const ActorStatus = Object.freeze({
|
|
33
|
-
WORKING: 'working',
|
|
34
|
-
DONE: 'done',
|
|
35
|
-
FAILED: 'failed'
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
/** 思维模式 */
|
|
39
|
-
export const ThinkingMode = Object.freeze({
|
|
40
|
-
DIVERGENT: 'divergent',
|
|
41
|
-
CONVERGENT: 'convergent'
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// =====================================================================
|
|
45
|
-
// Factory Functions
|
|
46
|
-
// =====================================================================
|
|
47
|
-
|
|
48
|
-
let _taskCounter = 0;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 生成递增的 task ID
|
|
52
|
-
*/
|
|
53
|
-
export function generateTaskId() {
|
|
54
|
-
return `task-${++_taskCounter}`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 创建 Task 实例
|
|
59
|
-
*
|
|
60
|
-
* @param {object} params
|
|
61
|
-
* @param {string} params.title — 任务标题
|
|
62
|
-
* @param {string} params.description — 任务描述(用户原始输入 or Conductor 转译)
|
|
63
|
-
* @param {string} params.scenario — 场景: dev / writing / trading / video
|
|
64
|
-
* @param {string} params.workDir — 工作目录(创建时绑定)
|
|
65
|
-
* @returns {Task}
|
|
66
|
-
*/
|
|
67
|
-
export function createTask({ title, description, scenario, workDir }) {
|
|
68
|
-
const taskId = generateTaskId();
|
|
69
|
-
const now = Date.now();
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
taskId,
|
|
73
|
-
title,
|
|
74
|
-
description,
|
|
75
|
-
scenario,
|
|
76
|
-
workDir,
|
|
77
|
-
|
|
78
|
-
// 状态机
|
|
79
|
-
phase: Phase.TRIAGE,
|
|
80
|
-
complexity: null, // Complexity enum, TRIAGE 阶段填充
|
|
81
|
-
|
|
82
|
-
// Actor 管理
|
|
83
|
-
activeActors: [], // ActorInstance[]
|
|
84
|
-
completedActors: [], // ActorInstance[] (已完成的 snapshot)
|
|
85
|
-
|
|
86
|
-
// 执行计划
|
|
87
|
-
plan: null, // ExecutionPlan, PLANNING 阶段产出
|
|
88
|
-
currentStepIndex: -1, // plan.steps 的当前索引
|
|
89
|
-
|
|
90
|
-
// 迭代控制
|
|
91
|
-
iterations: 0,
|
|
92
|
-
maxIterations: 3,
|
|
93
|
-
|
|
94
|
-
// Quality Gate
|
|
95
|
-
stepFailCounts: new Map(), // stepIndex → fail count
|
|
96
|
-
|
|
97
|
-
// 时间戳
|
|
98
|
-
createdAt: now,
|
|
99
|
-
updatedAt: now,
|
|
100
|
-
completedAt: null,
|
|
101
|
-
|
|
102
|
-
// 结果
|
|
103
|
-
summary: '',
|
|
104
|
-
error: null
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 创建 Actor 实例描述
|
|
110
|
-
*
|
|
111
|
-
* @param {object} params
|
|
112
|
-
* @param {string} params.personaId — persona ID (e.g. 'torvalds')
|
|
113
|
-
* @param {string} params.personaName — 显示名 (e.g. 'Linus Torvalds')
|
|
114
|
-
* @param {string} params.specialty — specialty 标识 (e.g. 'coding')
|
|
115
|
-
* @param {string} params.thinkingMode — 'divergent' | 'convergent'
|
|
116
|
-
* @param {string} params.taskId
|
|
117
|
-
* @param {string} [params.threadId] — 并行线程 ID(coding actor 用)
|
|
118
|
-
* @param {string} [params.worktreePath]
|
|
119
|
-
* @param {string} [params.activity] — 当前活动描述
|
|
120
|
-
* @returns {ActorInstance}
|
|
121
|
-
*/
|
|
122
|
-
export function createActorInstance({
|
|
123
|
-
personaId,
|
|
124
|
-
personaName,
|
|
125
|
-
specialty,
|
|
126
|
-
thinkingMode,
|
|
127
|
-
taskId,
|
|
128
|
-
threadId = null,
|
|
129
|
-
worktreePath = null,
|
|
130
|
-
activity = ''
|
|
131
|
-
}) {
|
|
132
|
-
const suffix = threadId ? `-${threadId}` : '';
|
|
133
|
-
return {
|
|
134
|
-
instanceId: `${specialty}-${personaId}${suffix}-${taskId}`,
|
|
135
|
-
personaId,
|
|
136
|
-
personaName,
|
|
137
|
-
specialty,
|
|
138
|
-
thinkingMode,
|
|
139
|
-
taskId,
|
|
140
|
-
threadId,
|
|
141
|
-
worktreePath,
|
|
142
|
-
claudeSessionId: null,
|
|
143
|
-
status: ActorStatus.WORKING,
|
|
144
|
-
activity,
|
|
145
|
-
result: null, // ActorResult — actor 完成后填充
|
|
146
|
-
startedAt: Date.now(),
|
|
147
|
-
completedAt: null
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* 创建执行计划
|
|
153
|
-
*
|
|
154
|
-
* @param {object} params
|
|
155
|
-
* @param {string} params.analysis — 任务分析文本
|
|
156
|
-
* @param {Array} params.steps — 执行步骤列表
|
|
157
|
-
* @param {Array} params.risks — 风险识别
|
|
158
|
-
* @param {Array} params.successCriteria — 验收标准
|
|
159
|
-
* @returns {ExecutionPlan}
|
|
160
|
-
*/
|
|
161
|
-
export function createExecutionPlan({ analysis, steps, risks = [], successCriteria = [] }) {
|
|
162
|
-
return {
|
|
163
|
-
analysis,
|
|
164
|
-
steps: steps.map((step, idx) => ({
|
|
165
|
-
index: idx,
|
|
166
|
-
title: step.title,
|
|
167
|
-
description: step.description || '',
|
|
168
|
-
assignedPersona: step.assignedPersona, // persona ID
|
|
169
|
-
specialty: step.specialty, // specialty 标识
|
|
170
|
-
parallel: step.parallel || false, // 可否与相邻步骤并行
|
|
171
|
-
parallelGroup: step.parallelGroup || null, // 并行分组标识
|
|
172
|
-
dependencies: step.dependencies || [], // 前置步骤 index[]
|
|
173
|
-
needsReview: step.needsReview !== false, // 默认 true
|
|
174
|
-
needsTesting: step.needsTesting !== false, // 默认 true
|
|
175
|
-
status: 'pending', // pending | executing | completed | failed
|
|
176
|
-
result: null
|
|
177
|
-
})),
|
|
178
|
-
risks,
|
|
179
|
-
successCriteria,
|
|
180
|
-
createdAt: Date.now()
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// =====================================================================
|
|
185
|
-
// Task State Mutations
|
|
186
|
-
// =====================================================================
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* 推进 Task 到下一个 Phase
|
|
190
|
-
*/
|
|
191
|
-
export function transitionPhase(task, newPhase) {
|
|
192
|
-
const valid = getValidTransitions(task.phase);
|
|
193
|
-
if (!valid.includes(newPhase)) {
|
|
194
|
-
throw new Error(
|
|
195
|
-
`Invalid phase transition: ${task.phase} → ${newPhase}. Valid: [${valid.join(', ')}]`
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
task.phase = newPhase;
|
|
199
|
-
task.updatedAt = Date.now();
|
|
200
|
-
if (newPhase === Phase.DONE || newPhase === Phase.FAILED) {
|
|
201
|
-
task.completedAt = Date.now();
|
|
202
|
-
}
|
|
203
|
-
return task;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 获取当前 phase 的合法下一步
|
|
208
|
-
*/
|
|
209
|
-
export function getValidTransitions(phase) {
|
|
210
|
-
switch (phase) {
|
|
211
|
-
case Phase.TRIAGE:
|
|
212
|
-
return [Phase.DISCUSSION, Phase.PLANNING, Phase.EXECUTION, Phase.FAILED];
|
|
213
|
-
case Phase.DISCUSSION:
|
|
214
|
-
return [Phase.PLANNING, Phase.QUALITY_GATE, Phase.EXECUTION, Phase.FAILED];
|
|
215
|
-
case Phase.PLANNING:
|
|
216
|
-
return [Phase.EXECUTION, Phase.DISCUSSION, Phase.QUALITY_GATE, Phase.FAILED];
|
|
217
|
-
case Phase.EXECUTION:
|
|
218
|
-
return [Phase.QUALITY_GATE, Phase.ACCEPTANCE, Phase.FAILED];
|
|
219
|
-
case Phase.QUALITY_GATE:
|
|
220
|
-
return [Phase.EXECUTION, Phase.DISCUSSION, Phase.ACCEPTANCE, Phase.FAILED];
|
|
221
|
-
case Phase.ACCEPTANCE:
|
|
222
|
-
return [Phase.PLANNING, Phase.EXECUTION, Phase.DONE, Phase.FAILED];
|
|
223
|
-
case Phase.DONE:
|
|
224
|
-
case Phase.FAILED:
|
|
225
|
-
return [];
|
|
226
|
-
default:
|
|
227
|
-
return [];
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* 注册活跃 Actor
|
|
233
|
-
*/
|
|
234
|
-
export function addActiveActor(task, actorInstance) {
|
|
235
|
-
task.activeActors.push(actorInstance);
|
|
236
|
-
task.updatedAt = Date.now();
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* 从 activeActors 中移除并转移到 completedActors(共享逻辑)
|
|
241
|
-
*/
|
|
242
|
-
function _transferActor(task, instanceId, status, result) {
|
|
243
|
-
const idx = task.activeActors.findIndex(a => a.instanceId === instanceId);
|
|
244
|
-
if (idx === -1) return null;
|
|
245
|
-
|
|
246
|
-
const actor = task.activeActors.splice(idx, 1)[0];
|
|
247
|
-
actor.status = status;
|
|
248
|
-
actor.result = result;
|
|
249
|
-
actor.completedAt = Date.now();
|
|
250
|
-
task.completedActors.push(actor);
|
|
251
|
-
task.updatedAt = Date.now();
|
|
252
|
-
return actor;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Actor 完成:从 active 移到 completed
|
|
257
|
-
*/
|
|
258
|
-
export function completeActor(task, instanceId, result) {
|
|
259
|
-
return _transferActor(task, instanceId, ActorStatus.DONE, result);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Actor 失败
|
|
264
|
-
*/
|
|
265
|
-
export function failActor(task, instanceId, error) {
|
|
266
|
-
return _transferActor(task, instanceId, ActorStatus.FAILED, { error: error?.message || String(error) });
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* 记录步骤 fail 次数(Quality Gate 用)
|
|
271
|
-
*/
|
|
272
|
-
export function recordStepFail(task, stepIndex) {
|
|
273
|
-
const count = (task.stepFailCounts.get(stepIndex) || 0) + 1;
|
|
274
|
-
task.stepFailCounts.set(stepIndex, count);
|
|
275
|
-
return count;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* 获取步骤 fail 次数
|
|
280
|
-
*/
|
|
281
|
-
export function getStepFailCount(task, stepIndex) {
|
|
282
|
-
return task.stepFailCounts.get(stepIndex) || 0;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// =====================================================================
|
|
286
|
-
// Task Serialization (for status.json / 前端推送)
|
|
287
|
-
// =====================================================================
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* 序列化 Task 为可传输的 plain object
|
|
291
|
-
*/
|
|
292
|
-
export function serializeTask(task) {
|
|
293
|
-
return {
|
|
294
|
-
taskId: task.taskId,
|
|
295
|
-
title: task.title,
|
|
296
|
-
description: task.description,
|
|
297
|
-
scenario: task.scenario,
|
|
298
|
-
workDir: task.workDir,
|
|
299
|
-
phase: task.phase,
|
|
300
|
-
complexity: task.complexity,
|
|
301
|
-
activeActors: task.activeActors.map(serializeActor),
|
|
302
|
-
completedActors: task.completedActors.map(serializeActor),
|
|
303
|
-
plan: task.plan,
|
|
304
|
-
currentStepIndex: task.currentStepIndex,
|
|
305
|
-
iterations: task.iterations,
|
|
306
|
-
maxIterations: task.maxIterations,
|
|
307
|
-
stepFailCounts: Object.fromEntries(task.stepFailCounts),
|
|
308
|
-
createdAt: task.createdAt,
|
|
309
|
-
updatedAt: task.updatedAt,
|
|
310
|
-
completedAt: task.completedAt,
|
|
311
|
-
summary: task.summary,
|
|
312
|
-
error: task.error
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* 序列化 ActorInstance
|
|
318
|
-
*/
|
|
319
|
-
function serializeActor(actor) {
|
|
320
|
-
return {
|
|
321
|
-
instanceId: actor.instanceId,
|
|
322
|
-
personaId: actor.personaId,
|
|
323
|
-
personaName: actor.personaName,
|
|
324
|
-
specialty: actor.specialty,
|
|
325
|
-
thinkingMode: actor.thinkingMode,
|
|
326
|
-
taskId: actor.taskId,
|
|
327
|
-
threadId: actor.threadId,
|
|
328
|
-
status: actor.status,
|
|
329
|
-
activity: actor.activity,
|
|
330
|
-
startedAt: actor.startedAt,
|
|
331
|
-
completedAt: actor.completedAt
|
|
332
|
-
};
|
|
333
|
-
}
|