crewly 1.20.163 → 1.20.164

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 (132) hide show
  1. package/config/roles/orchestrator/prompt.md +2 -0
  2. package/config/roles/team-leader/prompt.md +2 -0
  3. package/config/roles/team-leader/tl-addon.md +2 -0
  4. package/config/skills/agent/core/project-tickets/SKILL.md +4 -0
  5. package/config/skills/orchestrator/set-team-lead/SKILL.md +58 -0
  6. package/config/skills/orchestrator/set-team-lead/execute.sh +46 -0
  7. package/config/skills/orchestrator/set-team-lead/execute.test.sh +77 -0
  8. package/dist/backend/backend/src/constants.d.ts +31 -1
  9. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  10. package/dist/backend/backend/src/constants.js +39 -2
  11. package/dist/backend/backend/src/constants.js.map +1 -1
  12. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts +1 -1
  13. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
  14. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +6 -1
  15. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
  16. package/dist/backend/backend/src/controllers/team/team.controller.d.ts +15 -0
  17. package/dist/backend/backend/src/controllers/team/team.controller.d.ts.map +1 -1
  18. package/dist/backend/backend/src/controllers/team/team.controller.js +110 -15
  19. package/dist/backend/backend/src/controllers/team/team.controller.js.map +1 -1
  20. package/dist/backend/backend/src/controllers/team/team.routes.d.ts.map +1 -1
  21. package/dist/backend/backend/src/controllers/team/team.routes.js +3 -1
  22. package/dist/backend/backend/src/controllers/team/team.routes.js.map +1 -1
  23. package/dist/backend/backend/src/index.d.ts.map +1 -1
  24. package/dist/backend/backend/src/index.js +9 -15
  25. package/dist/backend/backend/src/index.js.map +1 -1
  26. package/dist/backend/backend/src/models/Team.d.ts.map +1 -1
  27. package/dist/backend/backend/src/models/Team.js +5 -9
  28. package/dist/backend/backend/src/models/Team.js.map +1 -1
  29. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  30. package/dist/backend/backend/src/services/agent/agent-registration.service.js +20 -28
  31. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  32. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts +26 -0
  33. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  34. package/dist/backend/backend/src/services/agent/idle-detection.service.js +43 -0
  35. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  36. package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts +18 -6
  37. package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -1
  38. package/dist/backend/backend/src/services/ai/prompt-builder.service.js +21 -23
  39. package/dist/backend/backend/src/services/ai/prompt-builder.service.js.map +1 -1
  40. package/dist/backend/backend/src/services/chat-v2/huddle-leader.d.ts +23 -0
  41. package/dist/backend/backend/src/services/chat-v2/huddle-leader.d.ts.map +1 -0
  42. package/dist/backend/backend/src/services/chat-v2/huddle-leader.js +37 -0
  43. package/dist/backend/backend/src/services/chat-v2/huddle-leader.js.map +1 -0
  44. package/dist/backend/backend/src/services/core/storage.service.d.ts.map +1 -1
  45. package/dist/backend/backend/src/services/core/storage.service.js +3 -0
  46. package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
  47. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts +28 -7
  48. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts.map +1 -1
  49. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js +51 -15
  50. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js.map +1 -1
  51. package/dist/backend/backend/src/services/project-tickets/ticket-assignee-waker.d.ts +70 -0
  52. package/dist/backend/backend/src/services/project-tickets/ticket-assignee-waker.d.ts.map +1 -0
  53. package/dist/backend/backend/src/services/project-tickets/ticket-assignee-waker.js +64 -0
  54. package/dist/backend/backend/src/services/project-tickets/ticket-assignee-waker.js.map +1 -0
  55. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts +24 -0
  56. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts.map +1 -1
  57. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js +38 -1
  58. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js.map +1 -1
  59. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts +14 -2
  60. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts.map +1 -1
  61. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js +33 -3
  62. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js.map +1 -1
  63. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts +13 -2
  64. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts.map +1 -1
  65. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js +43 -12
  66. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -1
  67. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.js +2 -2
  68. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.js.map +1 -1
  69. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +10 -1
  70. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  71. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +19 -8
  72. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  73. package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.d.ts.map +1 -1
  74. package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.js +4 -3
  75. package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.js.map +1 -1
  76. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.d.ts +4 -3
  77. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.d.ts.map +1 -1
  78. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.js +6 -7
  79. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.js.map +1 -1
  80. package/dist/backend/backend/src/services/wiki/wiki-owner.resolver.d.ts.map +1 -1
  81. package/dist/backend/backend/src/services/wiki/wiki-owner.resolver.js +2 -1
  82. package/dist/backend/backend/src/services/wiki/wiki-owner.resolver.js.map +1 -1
  83. package/dist/backend/backend/src/types/index.d.ts +3 -2
  84. package/dist/backend/backend/src/types/index.d.ts.map +1 -1
  85. package/dist/backend/backend/src/types/index.js.map +1 -1
  86. package/dist/backend/backend/src/utils/defaultPrompts.d.ts.map +1 -1
  87. package/dist/backend/backend/src/utils/defaultPrompts.js +8 -0
  88. package/dist/backend/backend/src/utils/defaultPrompts.js.map +1 -1
  89. package/dist/backend/backend/src/utils/member-default-model.utils.d.ts +5 -5
  90. package/dist/backend/backend/src/utils/member-default-model.utils.d.ts.map +1 -1
  91. package/dist/backend/backend/src/utils/member-default-model.utils.js +4 -4
  92. package/dist/backend/backend/src/utils/member-default-model.utils.js.map +1 -1
  93. package/dist/backend/backend/src/utils/team.utils.d.ts +129 -18
  94. package/dist/backend/backend/src/utils/team.utils.d.ts.map +1 -1
  95. package/dist/backend/backend/src/utils/team.utils.js +240 -28
  96. package/dist/backend/backend/src/utils/team.utils.js.map +1 -1
  97. package/dist/backend/build-info.json +2 -2
  98. package/dist/cli/backend/src/constants.d.ts +31 -1
  99. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  100. package/dist/cli/backend/src/constants.js +39 -2
  101. package/dist/cli/backend/src/constants.js.map +1 -1
  102. package/dist/cli/backend/src/models/Team.d.ts.map +1 -1
  103. package/dist/cli/backend/src/models/Team.js +5 -9
  104. package/dist/cli/backend/src/models/Team.js.map +1 -1
  105. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  106. package/dist/cli/backend/src/services/agent/agent-registration.service.js +20 -28
  107. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  108. package/dist/cli/backend/src/services/ai/prompt-builder.service.d.ts +18 -6
  109. package/dist/cli/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -1
  110. package/dist/cli/backend/src/services/ai/prompt-builder.service.js +21 -23
  111. package/dist/cli/backend/src/services/ai/prompt-builder.service.js.map +1 -1
  112. package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -1
  113. package/dist/cli/backend/src/services/core/storage.service.js +3 -0
  114. package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
  115. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +10 -1
  116. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  117. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +19 -8
  118. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  119. package/dist/cli/backend/src/types/index.d.ts +3 -2
  120. package/dist/cli/backend/src/types/index.d.ts.map +1 -1
  121. package/dist/cli/backend/src/types/index.js.map +1 -1
  122. package/dist/cli/backend/src/utils/member-default-model.utils.d.ts +5 -5
  123. package/dist/cli/backend/src/utils/member-default-model.utils.d.ts.map +1 -1
  124. package/dist/cli/backend/src/utils/member-default-model.utils.js +4 -4
  125. package/dist/cli/backend/src/utils/member-default-model.utils.js.map +1 -1
  126. package/dist/cli/backend/src/utils/team.utils.d.ts +129 -18
  127. package/dist/cli/backend/src/utils/team.utils.d.ts.map +1 -1
  128. package/dist/cli/backend/src/utils/team.utils.js +240 -28
  129. package/dist/cli/backend/src/utils/team.utils.js.map +1 -1
  130. package/frontend/dist/assets/{index-64c8a616.js → index-9153caca.js} +263 -263
  131. package/frontend/dist/index.html +1 -1
  132. package/package.json +1 -1
@@ -1,7 +1,8 @@
1
- import { memberAgentId } from '../../utils/member-session-name.utils.js';
1
+ import { memberAgentId, resolveMemberSessionName } from '../../utils/member-session-name.utils.js';
2
+ import { canMemberDelegate, getTeamLeadIds, isLeadRole, setTeamLead } from '../../utils/team.utils.js';
2
3
  import { v4 as uuidv4 } from 'uuid';
3
4
  import { ORCHESTRATOR_SESSION_NAME, ORCHESTRATOR_ROLE, RUNTIME_TYPES, NON_RECOVERABLE_ERROR_PATTERNS, CLAUDE_STARTUP_CONSTANTS, RUNTIME_STARTUP_CONSTANTS, } from '../../constants.js';
4
- import { CREWLY_CONSTANTS, AGENT_TIMEOUTS } from '../../constants.js';
5
+ import { CREWLY_CONSTANTS, AGENT_TIMEOUTS, AGENT_WAKE_ERROR_CODES, TEAM_LEAD_CONSTANTS } from '../../constants.js';
5
6
  import { updateAgentHeartbeat } from '../../services/agent/agent-heartbeat.service.js';
6
7
  import { getSessionBackendSync, getSessionStatePersistence } from '../../services/session/index.js';
7
8
  import { removeCrewlyAgentFile } from '../../services/session/session-binding.js';
@@ -920,11 +921,11 @@ export async function createTeam(req, res) {
920
921
  }
921
922
  // Validate hierarchical team requirements
922
923
  if (hierarchical) {
923
- const leaders = members.filter(m => m.role === 'team-leader' || m.canDelegate);
924
+ const leaders = members.filter(m => isLeadRole(m.role) || m.canDelegate);
924
925
  if (leaders.length === 0) {
925
926
  res.status(400).json({
926
927
  success: false,
927
- error: 'Hierarchical teams require at least one team-leader or member with canDelegate=true'
928
+ error: 'Hierarchical teams require at least one team-leader / tech-lead or member with canDelegate=true'
928
929
  });
929
930
  return;
930
931
  }
@@ -961,7 +962,7 @@ export async function createTeam(req, res) {
961
962
  for (let i = 0; i < members.length; i++) {
962
963
  const member = members[i];
963
964
  const memberId = uuidv4();
964
- const isLeaderRole = member.role === 'team-leader' || member.canDelegate === true;
965
+ const isLeaderRole = isLeadRole(member.role) || member.canDelegate === true;
965
966
  const teamMember = {
966
967
  id: memberId,
967
968
  name: member.name,
@@ -991,8 +992,8 @@ export async function createTeam(req, res) {
991
992
  let leaderId;
992
993
  let leaderIds;
993
994
  if (hierarchical) {
994
- // Collect all leaders (team-leader role or canDelegate)
995
- const leaders = teamMembers.filter(m => m.role === 'team-leader' || m.canDelegate);
995
+ // Collect all leaders (lead role or canDelegate)
996
+ const leaders = teamMembers.filter(m => isLeadRole(m.role) || m.canDelegate);
996
997
  if (leaders.length > 0) {
997
998
  leaderIds = leaders.map(l => l.id);
998
999
  leaderId = leaderIds[0];
@@ -1817,7 +1818,7 @@ export async function startTeamMember(req, res) {
1817
1818
  res.status(400).json({
1818
1819
  success: false,
1819
1820
  error: gateResult.reason,
1820
- code: 'wake_gate_no_pool_work',
1821
+ code: AGENT_WAKE_ERROR_CODES.NO_POOL_WORK,
1821
1822
  });
1822
1823
  return;
1823
1824
  }
@@ -1893,7 +1894,7 @@ export async function startTeamMember(req, res) {
1893
1894
  res.status(403).json({
1894
1895
  success: false,
1895
1896
  error: decision.reason,
1896
- code: 'commitment_requires_owner_approval',
1897
+ code: AGENT_WAKE_ERROR_CODES.OWNER_APPROVAL_REQUIRED,
1897
1898
  });
1898
1899
  return;
1899
1900
  }
@@ -2279,7 +2280,7 @@ export async function registerMemberStatus(req, res) {
2279
2280
  if (eventBusService && targetTeamId) {
2280
2281
  const TTL_MINUTES = 60 * 24;
2281
2282
  const member = freshTeam?.members.find(m => m.id === targetMemberId);
2282
- const isTeamLead = member?.canDelegate === true;
2283
+ const isTeamLead = !!member && !!freshTeam && canMemberDelegate(freshTeam, member);
2283
2284
  try {
2284
2285
  if (isTeamLead) {
2285
2286
  for (const evtType of ['task:done', 'task:blocked', 'task:failed', 'task:needs_clarification']) {
@@ -2764,15 +2765,19 @@ export async function updateTeam(req, res) {
2764
2765
  team.description = updates.description;
2765
2766
  }
2766
2767
  if (updates.hierarchical !== undefined) {
2768
+ const wasHierarchical = team.hierarchical === true;
2767
2769
  team.hierarchical = updates.hierarchical;
2768
- if (!updates.hierarchical) {
2769
- // Disable hierarchy: clear leaderId, leaderIds, and member hierarchy fields
2770
- team.leaderId = undefined;
2771
- team.leaderIds = undefined;
2770
+ if (!updates.hierarchical && wasHierarchical) {
2771
+ // Disable hierarchy: clear the member hierarchy fields. Who LEADS
2772
+ // the team is not a hierarchy setting (utils/team.utils): the
2773
+ // leaders stay leaders and keep their delegation flag. Saving an
2774
+ // already non-hierarchical team (the edit modal always sends
2775
+ // `hierarchical`) changes nothing.
2776
+ const leadIds = new Set(getTeamLeadIds(team));
2772
2777
  for (const m of team.members) {
2773
2778
  m.parentMemberId = undefined;
2774
2779
  m.hierarchyLevel = undefined;
2775
- m.canDelegate = undefined;
2780
+ m.canDelegate = leadIds.has(m.id) ? true : undefined;
2776
2781
  m.subordinateIds = undefined;
2777
2782
  }
2778
2783
  }
@@ -2953,6 +2958,96 @@ export async function updateTeam(req, res) {
2953
2958
  });
2954
2959
  }
2955
2960
  }
2961
+ /**
2962
+ * Make a member a lead of its team — `POST /api/teams/:id/lead`
2963
+ * `{ memberId | member, mode? }` (specs/2026-09-30-team-lead-rule.md §3).
2964
+ *
2965
+ * `:id` is the team id or its name; `member` may be the member id, name,
2966
+ * session name or agent id. `mode` is `set` (default: the member becomes THE
2967
+ * lead) or `add` (one more lead). Works on any team — no hierarchical mode
2968
+ * needed. Only the owner (no `X-Agent-Session`) or the orchestrator may call
2969
+ * it; agents get 403. The change reaches lead-only prompt modules on the
2970
+ * member's next wake.
2971
+ *
2972
+ * @param req - Express request
2973
+ * @param res - Express response: `{ teamId, teamName, leaderIds, lead, previous }`
2974
+ */
2975
+ export async function setTeamLeadHandler(req, res) {
2976
+ try {
2977
+ const caller = readAgentSessionHeader(req);
2978
+ if (caller && caller !== ORCHESTRATOR_SESSION_NAME) {
2979
+ res.status(403).json({ success: false, error: 'Only the owner or the orchestrator can change who leads a team' });
2980
+ return;
2981
+ }
2982
+ const ref = String(req.params.id ?? '').trim();
2983
+ const body = (req.body ?? {});
2984
+ const memberRef = String(body.memberId ?? body.member ?? '').trim();
2985
+ const mode = (body.mode === undefined ? 'set' : String(body.mode));
2986
+ if (!ref || !memberRef) {
2987
+ res.status(400).json({ success: false, error: 'team id and memberId are required' });
2988
+ return;
2989
+ }
2990
+ if (!TEAM_LEAD_CONSTANTS.SET_LEAD_MODES.includes(mode)) {
2991
+ res.status(400).json({ success: false, error: `mode must be one of: ${TEAM_LEAD_CONSTANTS.SET_LEAD_MODES.join(', ')}` });
2992
+ return;
2993
+ }
2994
+ if (ref === CREWLY_CONSTANTS.AGENT_IDS.ORCHESTRATOR_ID) {
2995
+ res.status(400).json({ success: false, error: 'The Orchestrator Team has no team lead' });
2996
+ return;
2997
+ }
2998
+ const teams = await this.storageService.getTeams();
2999
+ const lower = ref.toLowerCase();
3000
+ const team = teams.find((t) => t.id === ref) ?? teams.find((t) => t.name.toLowerCase() === lower);
3001
+ if (!team) {
3002
+ res.status(404).json({ success: false, error: `Team not found: ${ref}` });
3003
+ return;
3004
+ }
3005
+ const want = memberRef.toLowerCase();
3006
+ const member = team.members.find((m) => m.id === memberRef) ??
3007
+ team.members.find((m) => m.sessionName === memberRef || m.agentId === memberRef || resolveMemberSessionName(team.name, m) === memberRef) ??
3008
+ team.members.find((m) => m.name.toLowerCase() === want);
3009
+ if (!member) {
3010
+ res.status(404).json({ success: false, error: `No member "${memberRef}" on team ${team.name}` });
3011
+ return;
3012
+ }
3013
+ if (member.role === ORCHESTRATOR_ROLE) {
3014
+ res.status(400).json({ success: false, error: 'The orchestrator cannot lead a team' });
3015
+ return;
3016
+ }
3017
+ const result = setTeamLead(team, member.id, mode);
3018
+ team.updatedAt = new Date().toISOString();
3019
+ await this.storageService.saveTeam(team);
3020
+ logger.info('Team lead changed', {
3021
+ teamId: team.id,
3022
+ teamName: team.name,
3023
+ lead: member.name,
3024
+ mode,
3025
+ before: result.before,
3026
+ after: result.after,
3027
+ by: caller ?? 'owner',
3028
+ });
3029
+ const nameOf = (id) => team.members.find((m) => m.id === id)?.name ?? id;
3030
+ res.json({
3031
+ success: true,
3032
+ data: {
3033
+ teamId: team.id,
3034
+ teamName: team.name,
3035
+ leaderIds: result.after,
3036
+ lead: { id: member.id, name: member.name, session: resolveMemberSessionName(team.name, member) },
3037
+ leads: result.after.map(nameOf),
3038
+ previous: result.before.map(nameOf),
3039
+ changed: result.changed,
3040
+ },
3041
+ message: result.changed
3042
+ ? `${member.name} now leads ${team.name}. Takes effect in their prompt on their next wake.`
3043
+ : `${member.name} already leads ${team.name}.`,
3044
+ });
3045
+ }
3046
+ catch (error) {
3047
+ logger.error('Error setting team lead', { error: error instanceof Error ? error.message : String(error) });
3048
+ res.status(500).json({ success: false, error: 'Failed to set team lead' });
3049
+ }
3050
+ }
2956
3051
  /**
2957
3052
  * Archive or unarchive a team (#171).
2958
3053
  * POST /:id/archive — sets archived=true.