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.
- package/config/roles/orchestrator/prompt.md +2 -0
- package/config/roles/team-leader/prompt.md +2 -0
- package/config/roles/team-leader/tl-addon.md +2 -0
- package/config/skills/agent/core/project-tickets/SKILL.md +4 -0
- package/config/skills/orchestrator/set-team-lead/SKILL.md +58 -0
- package/config/skills/orchestrator/set-team-lead/execute.sh +46 -0
- package/config/skills/orchestrator/set-team-lead/execute.test.sh +77 -0
- package/dist/backend/backend/src/constants.d.ts +31 -1
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +39 -2
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +6 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/team/team.controller.d.ts +15 -0
- package/dist/backend/backend/src/controllers/team/team.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/team/team.controller.js +110 -15
- package/dist/backend/backend/src/controllers/team/team.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/team/team.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/team/team.routes.js +3 -1
- package/dist/backend/backend/src/controllers/team/team.routes.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +9 -15
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/models/Team.d.ts.map +1 -1
- package/dist/backend/backend/src/models/Team.js +5 -9
- package/dist/backend/backend/src/models/Team.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +20 -28
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts +26 -0
- package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/idle-detection.service.js +43 -0
- package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts +18 -6
- package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-builder.service.js +21 -23
- package/dist/backend/backend/src/services/ai/prompt-builder.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/huddle-leader.d.ts +23 -0
- package/dist/backend/backend/src/services/chat-v2/huddle-leader.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/huddle-leader.js +37 -0
- package/dist/backend/backend/src/services/chat-v2/huddle-leader.js.map +1 -0
- package/dist/backend/backend/src/services/core/storage.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/core/storage.service.js +3 -0
- package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts +28 -7
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js +51 -15
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-assignee-waker.d.ts +70 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-assignee-waker.d.ts.map +1 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-assignee-waker.js +64 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-assignee-waker.js.map +1 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts +24 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js +38 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts +14 -2
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js +33 -3
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts +13 -2
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js +43 -12
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-instance-registry.service.js +2 -2
- package/dist/backend/backend/src/services/slack/slack-instance-registry.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +10 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +19 -8
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.d.ts.map +1 -1
- package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.js +4 -3
- package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.js.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.d.ts +4 -3
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.js +6 -7
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.js.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-owner.resolver.d.ts.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-owner.resolver.js +2 -1
- package/dist/backend/backend/src/services/wiki/wiki-owner.resolver.js.map +1 -1
- package/dist/backend/backend/src/types/index.d.ts +3 -2
- package/dist/backend/backend/src/types/index.d.ts.map +1 -1
- package/dist/backend/backend/src/types/index.js.map +1 -1
- package/dist/backend/backend/src/utils/defaultPrompts.d.ts.map +1 -1
- package/dist/backend/backend/src/utils/defaultPrompts.js +8 -0
- package/dist/backend/backend/src/utils/defaultPrompts.js.map +1 -1
- package/dist/backend/backend/src/utils/member-default-model.utils.d.ts +5 -5
- package/dist/backend/backend/src/utils/member-default-model.utils.d.ts.map +1 -1
- package/dist/backend/backend/src/utils/member-default-model.utils.js +4 -4
- package/dist/backend/backend/src/utils/member-default-model.utils.js.map +1 -1
- package/dist/backend/backend/src/utils/team.utils.d.ts +129 -18
- package/dist/backend/backend/src/utils/team.utils.d.ts.map +1 -1
- package/dist/backend/backend/src/utils/team.utils.js +240 -28
- package/dist/backend/backend/src/utils/team.utils.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +31 -1
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +39 -2
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/models/Team.d.ts.map +1 -1
- package/dist/cli/backend/src/models/Team.js +5 -9
- package/dist/cli/backend/src/models/Team.js.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.js +20 -28
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-builder.service.d.ts +18 -6
- package/dist/cli/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-builder.service.js +21 -23
- package/dist/cli/backend/src/services/ai/prompt-builder.service.js.map +1 -1
- package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/core/storage.service.js +3 -0
- package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +10 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +19 -8
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/types/index.d.ts +3 -2
- package/dist/cli/backend/src/types/index.d.ts.map +1 -1
- package/dist/cli/backend/src/types/index.js.map +1 -1
- package/dist/cli/backend/src/utils/member-default-model.utils.d.ts +5 -5
- package/dist/cli/backend/src/utils/member-default-model.utils.d.ts.map +1 -1
- package/dist/cli/backend/src/utils/member-default-model.utils.js +4 -4
- package/dist/cli/backend/src/utils/member-default-model.utils.js.map +1 -1
- package/dist/cli/backend/src/utils/team.utils.d.ts +129 -18
- package/dist/cli/backend/src/utils/team.utils.d.ts.map +1 -1
- package/dist/cli/backend/src/utils/team.utils.js +240 -28
- package/dist/cli/backend/src/utils/team.utils.js.map +1 -1
- package/frontend/dist/assets/{index-64c8a616.js → index-9153caca.js} +263 -263
- package/frontend/dist/index.html +1 -1
- 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
|
|
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
|
|
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 (
|
|
995
|
-
const leaders = teamMembers.filter(m => m.role
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
2770
|
-
team.
|
|
2771
|
-
|
|
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.
|