@yeaft/webchat-agent 0.0.187 → 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.
Files changed (2) hide show
  1. package/crew.js +89 -35
  2. 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: 'running', // running | paused | waiting_human | completed | stopped
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
- // 通知 server
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
- // 持久化到索引和 session.json
734
- await upsertCrewIndex(session);
735
- await saveSessionMeta(session);
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
- if (goal && roles.length > 0) {
739
- const firstRole = roles.find(r => r.name === 'pm') || roles[0];
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,21 +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
1354
  prompt += `\n\n# 工具使用
1316
- PM 可以使用所有工具,包括 Read、Grep、Glob、Bash、Edit、Write。代码文件的改动仍建议 ROUTE 给 developer 执行,但不做硬性限制。`;
1355
+ PM 可以使用所有工具,包括 Read、Grep、Glob、Bash、Edit、Write。${isDevTeam ? '代码文件的改动仍建议 ROUTE 给 developer 执行,但不做硬性限制。' : ''}`;
1317
1356
 
1318
1357
  prompt += `\n\n# 决策者职责
1319
1358
  你是团队的决策者。其他角色遇到不确定的情况会请求你的决策。
1320
1359
  - 如果你有足够的信息做出决策,直接决定并 @相关角色执行
1321
1360
  - 如果你需要更多信息,@具体角色请求补充
1322
1361
  - 如果问题超出你的能力范围或需要业务判断,@human 请人类决定
1323
- - 你可以随时审查其他角色的工作并给出反馈
1362
+ - 你可以随时审查其他角色的工作并给出反馈`;
1363
+
1364
+ if (isDevTeam) {
1365
+ prompt += `
1324
1366
  - PM 不做代码分析。收到需求后直接将原始需求 ROUTE 给空闲 dev 做技术分析,dev 分析完返回 PM,PM 再拆分任务并直接分配执行。
1325
1367
  - PM 拥有 commit + push + tag 的自主权。只要修改没有大的 regression 影响(测试全通过),PM 可以自行决定 commit、push 和 tag,无需等待人工确认。只有当改动会直接影响对话交互逻辑时,才需要人工介入审核。`;
1368
+ }
1326
1369
 
1327
- // 多实例模式:注入开发组状态和调度规则
1328
- if (hasMultiInstance) {
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) {
1329
1383
  // 构建开发组实时状态
1330
1384
  const maxGroup = Math.max(...allRoles.map(r => r.groupIndex));
1331
1385
  const groupLines = [];
@@ -1375,9 +1429,8 @@ summary: 请实现注册页面,包括邮箱验证
1375
1429
  prompt += `\n
1376
1430
  # 工作流终结点
1377
1431
  团队的工作流有明确的结束条件。当以下任一条件满足时,你应该给出总结并结束当前工作流:
1378
- 1. **代码已提交** - 所有代码修改已经 commit(如需要,可让 developer 执行 git commit
1379
- 2. **需要用户输入** - 遇到需要用户决定的问题时,@human 提出具体问题,等待用户回复
1380
- 3. **任务完成** - 所有任务已完成,给出完成总结(列出完成了什么、变更了哪些文件、还有什么后续建议)
1432
+ ${isDevTeam ? '1. **代码已提交** - 所有代码修改已经 commit(如需要,可让 developer 执行 git commit)\n' : ''}${isDevTeam ? '2' : '1'}. **需要用户输入** - 遇到需要用户决定的问题时,@human 提出具体问题,等待用户回复
1433
+ ${isDevTeam ? '3' : '2'}. **任务完成** - 所有任务已完成,给出完成总结(列出完成了什么${isDevTeam ? '、变更了哪些文件' : ''}、还有什么后续建议)
1381
1434
 
1382
1435
  重要:不要无限循环地在角色之间传递。当工作实质性完成时,主动给出总结并结束。
1383
1436
 
@@ -1988,7 +2041,7 @@ function buildRoutePrompt(fromRole, summary, session) {
1988
2041
  * 向角色发送消息
1989
2042
  */
1990
2043
  async function dispatchToRole(session, roleName, content, fromSource, taskId, taskTitle) {
1991
- if (session.status === 'paused' || session.status === 'stopped') {
2044
+ if (session.status === 'paused' || session.status === 'stopped' || session.status === 'initializing') {
1992
2045
  console.log(`[Crew] Session ${session.status}, skipping dispatch to ${roleName}`);
1993
2046
  return;
1994
2047
  }
@@ -2769,7 +2822,8 @@ function sendStatusUpdate(session) {
2769
2822
  .filter(([, s]) => s.turnActive && s.currentTool)
2770
2823
  .map(([name, s]) => [name, s.currentTool])
2771
2824
  ),
2772
- features: Array.from(session.features.values())
2825
+ features: Array.from(session.features.values()),
2826
+ initProgress: session.initProgress || null
2773
2827
  });
2774
2828
 
2775
2829
  // 异步更新持久化
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.0.187",
3
+ "version": "0.0.195",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",