@yeaft/webchat-agent 0.0.186 → 0.0.195
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/crew.js +91 -40
- package/package.json +1 -1
package/crew.js
CHANGED
|
@@ -324,6 +324,7 @@ async function saveSessionMeta(session) {
|
|
|
324
324
|
userId: session.userId,
|
|
325
325
|
username: session.username,
|
|
326
326
|
agentId: session.agentId || null,
|
|
327
|
+
teamType: session.teamType || 'dev',
|
|
327
328
|
costUsd: session.costUsd,
|
|
328
329
|
totalInputTokens: session.totalInputTokens,
|
|
329
330
|
totalOutputTokens: session.totalOutputTokens,
|
|
@@ -551,6 +552,7 @@ export async function resumeCrewSession(msg) {
|
|
|
551
552
|
userId: userId || meta.userId,
|
|
552
553
|
username: username || meta.username,
|
|
553
554
|
agentId: meta.agentId || ctx.CONFIG?.agentName || null,
|
|
555
|
+
teamType: meta.teamType || 'dev',
|
|
554
556
|
createdAt: meta.createdAt || Date.now()
|
|
555
557
|
};
|
|
556
558
|
crewSessions.set(sessionId, session);
|
|
@@ -635,6 +637,7 @@ export async function createCrewSession(msg) {
|
|
|
635
637
|
sharedKnowledge,
|
|
636
638
|
roles: rawRoles = [], // [{ name, displayName, icon, description, claudeMd, model, budget, isDecisionMaker, count }]
|
|
637
639
|
maxRounds = 20,
|
|
640
|
+
teamType = 'dev',
|
|
638
641
|
userId,
|
|
639
642
|
username
|
|
640
643
|
} = msg;
|
|
@@ -655,23 +658,10 @@ export async function createCrewSession(msg) {
|
|
|
655
658
|
? sharedDirRel
|
|
656
659
|
: join(projectDir, sharedDirRel || '.crew');
|
|
657
660
|
|
|
658
|
-
// 初始化共享区
|
|
659
|
-
await initSharedDir(sharedDir, goal, roles, projectDir, sharedKnowledge);
|
|
660
|
-
|
|
661
|
-
// 初始化 git worktrees(所有 EXPANDABLE_ROLES 都会获得独立 worktree)
|
|
662
|
-
const worktreeMap = await initWorktrees(projectDir, roles);
|
|
663
|
-
// 回填 workDir:同组的 dev/rev/test 共享同一个 worktree
|
|
664
|
-
for (const role of roles) {
|
|
665
|
-
if (role.groupIndex > 0 && worktreeMap.has(role.groupIndex)) {
|
|
666
|
-
role.workDir = worktreeMap.get(role.groupIndex);
|
|
667
|
-
// 重新写入 CLAUDE.md(加入工作目录信息)
|
|
668
|
-
await writeRoleClaudeMd(sharedDir, role);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
661
|
// 找到决策者
|
|
673
662
|
const decisionMaker = roles.find(r => r.isDecisionMaker)?.name || roles[0]?.name || null;
|
|
674
663
|
|
|
664
|
+
// ★ 阶段1:立即构建 session 并通知前端,让 UI 先显示
|
|
675
665
|
const session = {
|
|
676
666
|
id: sessionId,
|
|
677
667
|
projectDir,
|
|
@@ -682,7 +672,7 @@ export async function createCrewSession(msg) {
|
|
|
682
672
|
roles: new Map(roles.map(r => [r.name, r])),
|
|
683
673
|
roleStates: new Map(),
|
|
684
674
|
decisionMaker,
|
|
685
|
-
status: '
|
|
675
|
+
status: 'initializing', // ← 新增初始化状态
|
|
686
676
|
round: 0,
|
|
687
677
|
maxRounds,
|
|
688
678
|
costUsd: 0,
|
|
@@ -694,15 +684,17 @@ export async function createCrewSession(msg) {
|
|
|
694
684
|
waitingHumanContext: null, // { fromRole, reason, message }
|
|
695
685
|
pendingRoutes: [], // [{ fromRole, route }] — 暂停时未完成的路由
|
|
696
686
|
features: new Map(), // taskId → { taskId, taskTitle, createdAt } — 持久化 feature 列表
|
|
687
|
+
initProgress: null, // 'roles' | 'worktrees' | null — 初始化阶段
|
|
697
688
|
userId,
|
|
698
689
|
username,
|
|
699
690
|
agentId: ctx.CONFIG?.agentName || null,
|
|
691
|
+
teamType,
|
|
700
692
|
createdAt: Date.now()
|
|
701
693
|
};
|
|
702
694
|
|
|
703
695
|
crewSessions.set(sessionId, session);
|
|
704
696
|
|
|
705
|
-
//
|
|
697
|
+
// 立即通知前端:session 已创建,可以显示 UI
|
|
706
698
|
sendCrewMessage({
|
|
707
699
|
type: 'crew_session_created',
|
|
708
700
|
sessionId,
|
|
@@ -727,20 +719,66 @@ export async function createCrewSession(msg) {
|
|
|
727
719
|
username
|
|
728
720
|
});
|
|
729
721
|
|
|
730
|
-
// 发送状态
|
|
731
722
|
sendStatusUpdate(session);
|
|
732
723
|
|
|
733
|
-
//
|
|
734
|
-
|
|
735
|
-
|
|
724
|
+
// ★ 阶段2:异步完成文件系统和 worktree 初始化
|
|
725
|
+
try {
|
|
726
|
+
// 初始化共享区(角色目录 + CLAUDE.md)
|
|
727
|
+
session.initProgress = 'roles';
|
|
728
|
+
sendStatusUpdate(session);
|
|
729
|
+
await initSharedDir(sharedDir, goal, roles, projectDir, sharedKnowledge);
|
|
730
|
+
|
|
731
|
+
// 初始化 git worktrees
|
|
732
|
+
const groupIndices = [...new Set(roles.filter(r => r.groupIndex > 0).map(r => r.groupIndex))];
|
|
733
|
+
if (groupIndices.length > 0) {
|
|
734
|
+
session.initProgress = 'worktrees';
|
|
735
|
+
sendStatusUpdate(session);
|
|
736
|
+
}
|
|
737
|
+
const worktreeMap = await initWorktrees(projectDir, roles);
|
|
738
|
+
|
|
739
|
+
// 回填 workDir
|
|
740
|
+
for (const role of roles) {
|
|
741
|
+
if (role.groupIndex > 0 && worktreeMap.has(role.groupIndex)) {
|
|
742
|
+
role.workDir = worktreeMap.get(role.groupIndex);
|
|
743
|
+
await writeRoleClaudeMd(sharedDir, role);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// 持久化
|
|
748
|
+
await upsertCrewIndex(session);
|
|
749
|
+
await saveSessionMeta(session);
|
|
736
750
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
if (firstRole) {
|
|
741
|
-
const initialPrompt = buildInitialTask(goal, firstRole, roles);
|
|
742
|
-
await dispatchToRole(session, firstRole.name, initialPrompt, 'system');
|
|
751
|
+
// 初始化完成,仅在 initializing 状态下切换到 running(避免覆盖用户手动暂停/停止)
|
|
752
|
+
if (session.status === 'initializing') {
|
|
753
|
+
session.status = 'running';
|
|
743
754
|
}
|
|
755
|
+
session.initProgress = null;
|
|
756
|
+
sendStatusUpdate(session);
|
|
757
|
+
|
|
758
|
+
// 如果有目标且状态为 running,自动启动第一个角色
|
|
759
|
+
if (goal && roles.length > 0 && session.status === 'running') {
|
|
760
|
+
const firstRole = roles.find(r => r.name === 'pm') || roles[0];
|
|
761
|
+
if (firstRole) {
|
|
762
|
+
const initialPrompt = buildInitialTask(goal, firstRole, roles);
|
|
763
|
+
await dispatchToRole(session, firstRole.name, initialPrompt, 'system');
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
} catch (e) {
|
|
767
|
+
console.error('[Crew] Session initialization failed:', e);
|
|
768
|
+
if (session.status === 'initializing') {
|
|
769
|
+
session.status = 'running';
|
|
770
|
+
}
|
|
771
|
+
session.initProgress = null;
|
|
772
|
+
sendStatusUpdate(session);
|
|
773
|
+
sendCrewMessage({
|
|
774
|
+
type: 'crew_output',
|
|
775
|
+
sessionId,
|
|
776
|
+
roleName: 'system',
|
|
777
|
+
roleIcon: 'S',
|
|
778
|
+
roleDisplayName: '系统',
|
|
779
|
+
content: `工作环境初始化失败: ${e.message}`,
|
|
780
|
+
isTurnEnd: true
|
|
781
|
+
});
|
|
744
782
|
}
|
|
745
783
|
|
|
746
784
|
return session;
|
|
@@ -1311,24 +1349,37 @@ ${routeTargets.map(r => `- ${r.name}: ${roleLabel(r)} — ${r.description}`).joi
|
|
|
1311
1349
|
|
|
1312
1350
|
// 决策者额外 prompt
|
|
1313
1351
|
if (role.isDecisionMaker) {
|
|
1352
|
+
const isDevTeam = session.teamType === 'dev';
|
|
1314
1353
|
|
|
1315
|
-
prompt += `\n\n#
|
|
1316
|
-
|
|
1317
|
-
你**可以**使用这些工具修改文档和配置文件(.md/.json/.yaml/.yml/.toml/.txt/.env/.gitignore 等非代码文件)。
|
|
1318
|
-
你**可以**使用:Read、Grep、Glob、Bash(git 命令和只读命令)。
|
|
1319
|
-
|
|
1320
|
-
代码改动必须 ROUTE 给 developer 执行。文档和配置可以自己改。`;
|
|
1354
|
+
prompt += `\n\n# 工具使用
|
|
1355
|
+
PM 可以使用所有工具,包括 Read、Grep、Glob、Bash、Edit、Write。${isDevTeam ? '代码文件的改动仍建议 ROUTE 给 developer 执行,但不做硬性限制。' : ''}`;
|
|
1321
1356
|
|
|
1322
1357
|
prompt += `\n\n# 决策者职责
|
|
1323
1358
|
你是团队的决策者。其他角色遇到不确定的情况会请求你的决策。
|
|
1324
1359
|
- 如果你有足够的信息做出决策,直接决定并 @相关角色执行
|
|
1325
1360
|
- 如果你需要更多信息,@具体角色请求补充
|
|
1326
1361
|
- 如果问题超出你的能力范围或需要业务判断,@human 请人类决定
|
|
1327
|
-
-
|
|
1362
|
+
- 你可以随时审查其他角色的工作并给出反馈`;
|
|
1363
|
+
|
|
1364
|
+
if (isDevTeam) {
|
|
1365
|
+
prompt += `
|
|
1366
|
+
- PM 不做代码分析。收到需求后直接将原始需求 ROUTE 给空闲 dev 做技术分析,dev 分析完返回 PM,PM 再拆分任务并直接分配执行。
|
|
1328
1367
|
- PM 拥有 commit + push + tag 的自主权。只要修改没有大的 regression 影响(测试全通过),PM 可以自行决定 commit、push 和 tag,无需等待人工确认。只有当改动会直接影响对话交互逻辑时,才需要人工介入审核。`;
|
|
1368
|
+
}
|
|
1329
1369
|
|
|
1330
|
-
//
|
|
1331
|
-
if (
|
|
1370
|
+
// 非开发团队:注入讨论模式 prompt
|
|
1371
|
+
if (!isDevTeam) {
|
|
1372
|
+
prompt += `\n\n# 协作模式
|
|
1373
|
+
这是一个协作讨论团队,不走严格的 PM→执行→审查→测试 工作流。
|
|
1374
|
+
- 角色之间可以自由讨论、相互请教、提出不同意见
|
|
1375
|
+
- 不需要严格的"分配→执行→审查"流程,鼓励角色之间直接对话
|
|
1376
|
+
- 当一个角色需要另一个角色的输入时,直接 ROUTE 给对方并说明需要什么
|
|
1377
|
+
- 决策者负责把控整体方向和最终决策,但日常讨论不需要经过决策者中转
|
|
1378
|
+
- 每次 ROUTE 仍建议包含 task 和 taskTitle 字段,用于消息按 feature 分组显示`;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// 多实例模式(仅开发团队使用):注入开发组状态和调度规则
|
|
1382
|
+
if (isDevTeam && hasMultiInstance) {
|
|
1332
1383
|
// 构建开发组实时状态
|
|
1333
1384
|
const maxGroup = Math.max(...allRoles.map(r => r.groupIndex));
|
|
1334
1385
|
const groupLines = [];
|
|
@@ -1378,9 +1429,8 @@ summary: 请实现注册页面,包括邮箱验证
|
|
|
1378
1429
|
prompt += `\n
|
|
1379
1430
|
# 工作流终结点
|
|
1380
1431
|
团队的工作流有明确的结束条件。当以下任一条件满足时,你应该给出总结并结束当前工作流:
|
|
1381
|
-
1. **代码已提交** - 所有代码修改已经 commit(如需要,可让 developer 执行 git commit
|
|
1382
|
-
2.
|
|
1383
|
-
3. **任务完成** - 所有任务已完成,给出完成总结(列出完成了什么、变更了哪些文件、还有什么后续建议)
|
|
1432
|
+
${isDevTeam ? '1. **代码已提交** - 所有代码修改已经 commit(如需要,可让 developer 执行 git commit)\n' : ''}${isDevTeam ? '2' : '1'}. **需要用户输入** - 遇到需要用户决定的问题时,@human 提出具体问题,等待用户回复
|
|
1433
|
+
${isDevTeam ? '3' : '2'}. **任务完成** - 所有任务已完成,给出完成总结(列出完成了什么${isDevTeam ? '、变更了哪些文件' : ''}、还有什么后续建议)
|
|
1384
1434
|
|
|
1385
1435
|
重要:不要无限循环地在角色之间传递。当工作实质性完成时,主动给出总结并结束。
|
|
1386
1436
|
|
|
@@ -1991,7 +2041,7 @@ function buildRoutePrompt(fromRole, summary, session) {
|
|
|
1991
2041
|
* 向角色发送消息
|
|
1992
2042
|
*/
|
|
1993
2043
|
async function dispatchToRole(session, roleName, content, fromSource, taskId, taskTitle) {
|
|
1994
|
-
if (session.status === 'paused' || session.status === 'stopped') {
|
|
2044
|
+
if (session.status === 'paused' || session.status === 'stopped' || session.status === 'initializing') {
|
|
1995
2045
|
console.log(`[Crew] Session ${session.status}, skipping dispatch to ${roleName}`);
|
|
1996
2046
|
return;
|
|
1997
2047
|
}
|
|
@@ -2772,7 +2822,8 @@ function sendStatusUpdate(session) {
|
|
|
2772
2822
|
.filter(([, s]) => s.turnActive && s.currentTool)
|
|
2773
2823
|
.map(([name, s]) => [name, s.currentTool])
|
|
2774
2824
|
),
|
|
2775
|
-
features: Array.from(session.features.values())
|
|
2825
|
+
features: Array.from(session.features.values()),
|
|
2826
|
+
initProgress: session.initProgress || null
|
|
2776
2827
|
});
|
|
2777
2828
|
|
|
2778
2829
|
// 异步更新持久化
|