import { ServiceError, NotFoundError, ValidationError } from '../errors.js';
import { prisma } from '../utils/index.js';
import { appendAttachmentMarkdownContext } from './attachment-context.js';
import { emitTeamRunInvalidated } from './team-run-events.js';
import { ensureTaskNotDeleted } from './deleted-task-guard.js';
import { buildTextPreview, TASK_TITLE_MAX_LENGTH } from './task.service.js';
const DEFAULT_CAPABILITIES = {
    readRoom: false,
    postRoomMessage: false,
    mentionMembers: false,
    stopMemberWork: false,
    markReadyForReview: false,
    readFiles: false,
    writeFiles: false,
    runCommands: false,
    readDiff: false,
    mergeWorkspace: false,
};
const DEFAULT_SESSION_POLICY = 'new_per_request';
const DEFAULT_QUEUE_MANAGEMENT_POLICY = 'own_only';
const ACTIVE_MEMBERSHIP_STATUS = 'ACTIVE';
const REMOVED_MEMBERSHIP_STATUS = 'REMOVED';
const ACTIVE_INVOCATION_STATUSES = [
    'QUEUED',
    'RUNNING',
    'SESSION_ENDED',
    'WAITING_ROOM_REPLY',
];
const OPEN_WORK_REQUEST_STATUSES = [
    'PENDING_APPROVAL',
    'QUEUED',
];
function parseJsonField(value, fallback) {
    if (value == null || value === '') {
        return fallback;
    }
    try {
        return JSON.parse(value);
    }
    catch {
        return fallback;
    }
}
function stringifyJson(value) {
    return JSON.stringify(value);
}
function toIso(date) {
    return date.toISOString();
}
function applyStableInstanceNames(snapshots) {
    const totalsByName = new Map();
    for (const snapshot of snapshots) {
        totalsByName.set(snapshot.name, (totalsByName.get(snapshot.name) ?? 0) + 1);
    }
    const seenByName = new Map();
    return snapshots.map((snapshot) => {
        const total = totalsByName.get(snapshot.name) ?? 0;
        if (total <= 1) {
            return snapshot;
        }
        const instanceIndex = (seenByName.get(snapshot.name) ?? 0) + 1;
        seenByName.set(snapshot.name, instanceIndex);
        return {
            ...snapshot,
            name: `${snapshot.name} #${instanceIndex}`,
        };
    });
}
function toConflict(message) {
    return new ServiceError(message, 'CONFLICT', 409);
}
function isUniqueConstraintError(error) {
    return typeof error === 'object'
        && error !== null
        && 'code' in error
        && error.code === 'P2002';
}
function buildInitialTaskRoomMessageContent(task) {
    const title = buildTextPreview(task.title, TASK_TITLE_MAX_LENGTH);
    if (title.length === 0) {
        throw new ValidationError('Task title is required to create a TeamRun');
    }
    const descriptionPreview = buildTextPreview(task.description, 120);
    return [
        title,
        task.description?.trim()
            ? `Details preview: ${descriptionPreview}\n\nFull details are stored on the task description.`
            : '',
    ].filter(Boolean).join('\n\n');
}
function buildInitialTaskMentionSource(task) {
    return [task.title.trim(), task.description?.trim() ?? ''].filter(Boolean).join('\n\n');
}
function extractMentionTokens(content) {
    const tokens = [];
    const tokenPattern = /(^|[^\p{L}\p{N}_-])@([^\s@]+)/gu;
    const trailingPunctuation = /[.,!?;:，。！？；：、)\]}>"'”’）】》]+$/u;
    for (const match of content.matchAll(tokenPattern)) {
        const token = (match[2] ?? '').replace(trailingPunctuation, '');
        if (token.length > 0) {
            tokens.push(token);
        }
    }
    return tokens;
}
function deriveStructuredMentionsFromContent(content, members) {
    const matchesByToken = new Map();
    for (const member of members) {
        const labels = Array.from(new Set([
            member.name,
            ...parseJsonField(member.aliases, []),
        ].map((label) => label.trim()).filter(Boolean)));
        for (const label of labels) {
            const matches = matchesByToken.get(label) ?? [];
            if (!matches.some((match) => match.memberId === member.id)) {
                matches.push({ memberId: member.id, label });
            }
            matchesByToken.set(label, matches);
        }
    }
    const mentions = [];
    const mentionedMemberIds = new Set();
    const tokens = extractMentionTokens(content);
    for (const token of tokens) {
        const matches = matchesByToken.get(token) ?? [];
        if (matches.length !== 1 || mentionedMemberIds.has(matches[0].memberId)) {
            continue;
        }
        const match = matches[0];
        mentions.push({ memberId: match.memberId, label: match.label });
        mentionedMemberIds.add(match.memberId);
    }
    return { mentions, tokenCount: tokens.length };
}
function assertMentionsReferenceMembers(mentions, memberIds) {
    for (const mention of mentions) {
        if (!memberIds.has(mention.memberId)) {
            throw new NotFoundError('TeamMember', mention.memberId);
        }
    }
}
function uniqueMemberIds(memberIds) {
    return Array.from(new Set(memberIds.map((memberId) => memberId.trim()).filter(Boolean)));
}
function isActiveMembershipStatus(value) {
    return value !== REMOVED_MEMBERSHIP_STATUS;
}
function buildRoomMessageVisibilityWhere(teamRunId, viewerMemberId) {
    if (!viewerMemberId) {
        return { teamRunId };
    }
    return {
        teamRunId,
        OR: [
            { visibility: 'PUBLIC' },
            {
                participants: {
                    some: {
                        memberId: viewerMemberId,
                    },
                },
            },
        ],
    };
}
function activeTeamMembersWhere(teamRunId) {
    return {
        teamRunId,
        membershipStatus: ACTIVE_MEMBERSHIP_STATUS,
    };
}
function isRoomMessageVisibleToViewer(message, viewerMemberId) {
    if (!viewerMemberId)
        return true;
    if (message.visibility !== 'PRIVATE')
        return true;
    return (message.participants ?? []).some((participant) => participant.memberId === viewerMemberId);
}
function getVisibleWorkRequestIds(messages, viewerMemberId) {
    if (!viewerMemberId)
        return null;
    const visibleWorkRequestIds = new Set();
    for (const message of messages) {
        if (!isRoomMessageVisibleToViewer(message, viewerMemberId)) {
            continue;
        }
        for (const workRequestId of parseJsonField(message.workRequestIds, [])) {
            visibleWorkRequestIds.add(workRequestId);
        }
    }
    return visibleWorkRequestIds;
}
function resolveRoomMessageTargetRequests({ mentions, members, senderType, requesterMemberId, allowUserMessageFallback = true, }) {
    if (mentions.length > 0) {
        return mentions.map((mention) => ({
            targetMemberId: mention.memberId,
            ifBusy: mention.ifBusy ?? 'queue',
            cancelQueued: mention.cancelQueued ?? false,
        }));
    }
    if (!allowUserMessageFallback) {
        return [];
    }
    if (senderType !== 'user' && senderType !== 'agent') {
        return [];
    }
    return members
        .filter((member) => member.triggerPolicy === 'USER_MESSAGES')
        .filter((member) => member.id !== requesterMemberId)
        .map((member) => ({
        targetMemberId: member.id,
        ifBusy: 'queue',
        cancelQueued: false,
    }));
}
export class TeamRunService {
    async listMemberPresets() {
        const presets = await prisma.memberPreset.findMany({
            orderBy: { createdAt: 'desc' },
        });
        return presets.map((preset) => this.serializeMemberPreset(preset));
    }
    async getMemberPresetById(id) {
        const preset = await prisma.memberPreset.findUnique({ where: { id } });
        if (!preset) {
            throw new NotFoundError('MemberPreset', id);
        }
        return this.serializeMemberPreset(preset);
    }
    async createMemberPreset(input) {
        const preset = await prisma.memberPreset.create({
            data: {
                name: input.name,
                aliases: stringifyJson(input.aliases),
                providerId: input.providerId,
                rolePrompt: input.rolePrompt,
                capabilities: stringifyJson(input.capabilities),
                workspacePolicy: input.workspacePolicy,
                triggerPolicy: input.triggerPolicy,
                sessionPolicy: input.sessionPolicy,
                queueManagementPolicy: input.queueManagementPolicy,
                avatar: input.avatar ?? null,
            },
        });
        return this.serializeMemberPreset(preset);
    }
    async updateMemberPreset(id, input) {
        await this.getMemberPresetById(id);
        if (Object.keys(input).length === 0) {
            throw new ValidationError('At least one member preset field is required');
        }
        const preset = await prisma.memberPreset.update({
            where: { id },
            data: {
                ...(input.name !== undefined ? { name: input.name } : {}),
                ...(input.aliases !== undefined ? { aliases: stringifyJson(input.aliases) } : {}),
                ...(input.providerId !== undefined ? { providerId: input.providerId } : {}),
                ...(input.rolePrompt !== undefined ? { rolePrompt: input.rolePrompt } : {}),
                ...(input.capabilities !== undefined ? { capabilities: stringifyJson(input.capabilities) } : {}),
                ...(input.workspacePolicy !== undefined ? { workspacePolicy: input.workspacePolicy } : {}),
                ...(input.triggerPolicy !== undefined ? { triggerPolicy: input.triggerPolicy } : {}),
                ...(input.sessionPolicy !== undefined ? { sessionPolicy: input.sessionPolicy } : {}),
                ...(input.queueManagementPolicy !== undefined ? { queueManagementPolicy: input.queueManagementPolicy } : {}),
                ...(input.avatar !== undefined ? { avatar: input.avatar } : {}),
            },
        });
        return this.serializeMemberPreset(preset);
    }
    async deleteMemberPreset(id) {
        await this.getMemberPresetById(id);
        const templateMemberCount = await prisma.teamTemplateMember.count({
            where: { memberPresetId: id },
        });
        if (templateMemberCount > 0) {
            throw toConflict(`MemberPreset is used by ${templateMemberCount} TeamTemplate member(s): ${id}`);
        }
        await prisma.memberPreset.delete({ where: { id } });
    }
    async listTeamTemplates() {
        const templates = await prisma.teamTemplate.findMany({
            include: { members: { orderBy: { position: 'asc' } } },
            orderBy: { createdAt: 'desc' },
        });
        return Promise.all(templates.map((template) => this.serializeTeamTemplate(template)));
    }
    async getTeamTemplateById(id) {
        const template = await prisma.teamTemplate.findUnique({
            where: { id },
            include: { members: { orderBy: { position: 'asc' } } },
        });
        if (!template) {
            throw new NotFoundError('TeamTemplate', id);
        }
        return this.serializeTeamTemplate(template);
    }
    async createTeamTemplate(input) {
        const members = this.normalizeTeamTemplateMembers(input);
        await this.assertMemberPresetsExist(members.map((member) => member.memberPresetId));
        const template = await prisma.teamTemplate.create({
            data: {
                name: input.name,
                members: {
                    create: members,
                },
            },
            include: { members: { orderBy: { position: 'asc' } } },
        });
        return this.serializeTeamTemplate(template);
    }
    async updateTeamTemplate(id, input) {
        await this.getTeamTemplateById(id);
        if (input.name === undefined
            && input.memberPresetIds === undefined
            && input.members === undefined) {
            throw new ValidationError('At least one team template field is required');
        }
        const shouldReplaceMembers = input.members !== undefined || input.memberPresetIds !== undefined;
        const members = shouldReplaceMembers ? this.normalizeTeamTemplateMembers(input) : [];
        if (shouldReplaceMembers) {
            await this.assertMemberPresetsExist(members.map((member) => member.memberPresetId));
        }
        await prisma.$transaction(async (tx) => {
            if (input.name !== undefined) {
                await tx.teamTemplate.update({
                    where: { id },
                    data: { name: input.name },
                });
            }
            if (shouldReplaceMembers) {
                await tx.teamTemplateMember.deleteMany({ where: { teamTemplateId: id } });
                if (members.length > 0) {
                    await tx.teamTemplateMember.createMany({
                        data: members.map((member) => ({
                            teamTemplateId: id,
                            memberPresetId: member.memberPresetId,
                            position: member.position,
                        })),
                    });
                }
            }
        });
        return this.getTeamTemplateById(id);
    }
    async deleteTeamTemplate(id) {
        await this.getTeamTemplateById(id);
        await prisma.teamTemplate.delete({ where: { id } });
    }
    async createTeamRun(taskId, input) {
        const task = await prisma.task.findUnique({ where: { id: taskId } });
        if (!task) {
            throw new NotFoundError('Task', taskId);
        }
        ensureTaskNotDeleted(task);
        buildInitialTaskRoomMessageContent(task);
        const existing = await prisma.teamRun.findUnique({ where: { taskId } });
        if (existing) {
            throw toConflict(`Task already has a TeamRun: ${taskId}`);
        }
        const snapshots = applyStableInstanceNames(await this.buildTeamMemberSnapshots(input));
        if (snapshots.length === 0) {
            throw new ValidationError('TeamRun must include at least one member');
        }
        let teamRunId = '';
        try {
            await prisma.$transaction(async (tx) => {
                const teamRun = await tx.teamRun.create({
                    data: {
                        taskId,
                        mode: input.mode,
                    },
                });
                teamRunId = teamRun.id;
                for (const snapshot of snapshots) {
                    await tx.teamMember.create({
                        data: {
                            teamRunId: teamRun.id,
                            presetId: snapshot.presetId,
                            name: snapshot.name,
                            aliases: stringifyJson(snapshot.aliases),
                            providerId: snapshot.providerId,
                            rolePrompt: snapshot.rolePrompt,
                            capabilities: stringifyJson(snapshot.capabilities),
                            workspacePolicy: snapshot.workspacePolicy,
                            triggerPolicy: snapshot.triggerPolicy,
                            sessionPolicy: snapshot.sessionPolicy,
                            queueManagementPolicy: snapshot.queueManagementPolicy,
                            membershipStatus: ACTIVE_MEMBERSHIP_STATUS,
                            avatar: snapshot.avatar,
                        },
                    });
                }
            });
        }
        catch (error) {
            if (isUniqueConstraintError(error)) {
                throw toConflict(`Task already has a TeamRun: ${taskId}`);
            }
            throw error;
        }
        const teamRun = await this.getTeamRunById(teamRunId);
        await emitTeamRunInvalidated({
            teamRunId,
            taskId,
            projectId: task.projectId,
            scopes: ['team-run', 'team-members', 'task'],
            reason: 'team-run-created',
        });
        return teamRun;
    }
    async createTeamRunWithInitialRoomMessage(taskId, input) {
        const snapshots = applyStableInstanceNames(await this.buildTeamMemberSnapshots(input));
        if (snapshots.length === 0) {
            throw new ValidationError('TeamRun must include at least one member');
        }
        let teamRunId = '';
        let projectId = '';
        let createdTeamRun = null;
        try {
            createdTeamRun = await prisma.$transaction(async (tx) => {
                const task = await tx.task.findUnique({ where: { id: taskId } });
                if (!task) {
                    throw new NotFoundError('Task', taskId);
                }
                ensureTaskNotDeleted(task);
                projectId = task.projectId;
                const initialContent = buildInitialTaskRoomMessageContent(task);
                const initialMentionSource = buildInitialTaskMentionSource(task);
                const existing = await tx.teamRun.findUnique({ where: { taskId } });
                if (existing) {
                    throw toConflict(`Task already has a TeamRun: ${taskId}`);
                }
                const teamRun = await tx.teamRun.create({
                    data: {
                        taskId,
                        mode: input.mode,
                    },
                });
                teamRunId = teamRun.id;
                const members = [];
                for (const snapshot of snapshots) {
                    const member = await tx.teamMember.create({
                        data: {
                            teamRunId: teamRun.id,
                            presetId: snapshot.presetId,
                            name: snapshot.name,
                            aliases: stringifyJson(snapshot.aliases),
                            providerId: snapshot.providerId,
                            rolePrompt: snapshot.rolePrompt,
                            capabilities: stringifyJson(snapshot.capabilities),
                            workspacePolicy: snapshot.workspacePolicy,
                            triggerPolicy: snapshot.triggerPolicy,
                            sessionPolicy: snapshot.sessionPolicy,
                            queueManagementPolicy: snapshot.queueManagementPolicy,
                            membershipStatus: ACTIVE_MEMBERSHIP_STATUS,
                            avatar: snapshot.avatar,
                        },
                    });
                    members.push(member);
                }
                const initialMentionParse = deriveStructuredMentionsFromContent(initialMentionSource, members);
                const message = await tx.roomMessage.create({
                    data: {
                        teamRunId: teamRun.id,
                        senderType: 'user',
                        senderId: null,
                        senderInvocationId: null,
                        kind: 'chat',
                        content: initialContent,
                        mentions: stringifyJson(initialMentionParse.mentions),
                        artifactRefs: stringifyJson([]),
                        attachmentIds: stringifyJson([]),
                        workRequestIds: stringifyJson([]),
                    },
                });
                const workRequestStatus = input.mode === 'CONFIRM'
                    ? 'PENDING_APPROVAL'
                    : 'QUEUED';
                const targetRequests = resolveRoomMessageTargetRequests({
                    mentions: initialMentionParse.mentions,
                    members,
                    senderType: 'user',
                    requesterMemberId: null,
                    allowUserMessageFallback: initialMentionParse.tokenCount === 0,
                });
                const workRequestIds = [];
                for (const targetRequest of targetRequests) {
                    const workRequest = await tx.workRequest.create({
                        data: {
                            teamRunId: teamRun.id,
                            requesterMemberId: null,
                            requesterType: 'user',
                            targetMemberId: targetRequest.targetMemberId,
                            triggerMessageId: message.id,
                            instruction: initialContent,
                            ifBusy: targetRequest.ifBusy,
                            cancelQueued: targetRequest.cancelQueued,
                            status: workRequestStatus,
                        },
                    });
                    workRequestIds.push(workRequest.id);
                }
                if (workRequestIds.length > 0) {
                    await tx.roomMessage.update({
                        where: { id: message.id },
                        data: { workRequestIds: stringifyJson(workRequestIds) },
                    });
                }
                const created = await tx.teamRun.findUnique({
                    where: { id: teamRun.id },
                    include: this.teamRunInclude(),
                });
                if (!created) {
                    throw new NotFoundError('TeamRun', teamRun.id);
                }
                return created;
            });
        }
        catch (error) {
            if (isUniqueConstraintError(error)) {
                throw toConflict(`Task already has a TeamRun: ${taskId}`);
            }
            throw error;
        }
        const serialized = this.serializeTeamRun(createdTeamRun);
        await emitTeamRunInvalidated({
            teamRunId,
            taskId,
            projectId,
            scopes: ['team-run', 'team-members', 'room-messages', 'work-requests', 'task'],
            reason: 'team-run-created',
        });
        return serialized;
    }
    async getTaskTeamRun(taskId, options = {}) {
        const teamRun = await prisma.teamRun.findUnique({
            where: { taskId },
            include: this.teamRunInclude(),
        });
        if (!teamRun) {
            throw new NotFoundError('TeamRun for task', taskId);
        }
        return this.serializeTeamRun(teamRun, options);
    }
    async getTeamRunById(id, options = {}) {
        const teamRun = await prisma.teamRun.findUnique({
            where: { id },
            include: this.teamRunInclude(),
        });
        if (!teamRun) {
            throw new NotFoundError('TeamRun', id);
        }
        return this.serializeTeamRun(teamRun, options);
    }
    async listTeamMembers(teamRunId, options = {}) {
        await this.assertTeamRunExists(teamRunId);
        const [members, invocations, workRequests, messages] = await Promise.all([
            prisma.teamMember.findMany({
                where: { teamRunId },
                orderBy: { createdAt: 'asc' },
            }),
            prisma.agentInvocation.findMany({
                where: {
                    teamRunId,
                    status: { in: ACTIVE_INVOCATION_STATUSES },
                },
            }),
            prisma.workRequest.findMany({
                where: {
                    teamRunId,
                    status: { in: OPEN_WORK_REQUEST_STATUSES },
                },
            }),
            options.viewerMemberId
                ? prisma.roomMessage.findMany({
                    where: { teamRunId },
                    include: { participants: true },
                })
                : Promise.resolve([]),
        ]);
        const visibleWorkRequestIds = getVisibleWorkRequestIds(messages, options.viewerMemberId);
        const visibleWorkRequests = visibleWorkRequestIds
            ? workRequests.filter((request) => visibleWorkRequestIds.has(request.id))
            : workRequests;
        const visibleInvocations = visibleWorkRequestIds
            ? invocations.filter((invocation) => visibleWorkRequestIds.has(invocation.workRequestId))
            : invocations;
        const memberStatuses = this.deriveTeamMemberStatuses(members, visibleInvocations, visibleWorkRequests);
        return members.map((member) => this.serializeTeamMember(member, memberStatuses.get(member.id)));
    }
    async addTeamRunMember(teamRunId, input) {
        if ((input.memberPresetId ? 1 : 0) + (input.member ? 1 : 0) !== 1) {
            throw new ValidationError('Exactly one of memberPresetId or member is required');
        }
        const snapshot = await this.buildAddTeamRunMemberSnapshot(input);
        const created = await prisma.$transaction(async (tx) => {
            const teamRun = await tx.teamRun.findUnique({
                where: { id: teamRunId },
                include: { task: true },
            });
            if (!teamRun) {
                throw new NotFoundError('TeamRun', teamRunId);
            }
            ensureTaskNotDeleted(teamRun.task);
            const existingMembers = await tx.teamMember.findMany({
                where: activeTeamMembersWhere(teamRunId),
                select: { name: true },
            });
            const instanceName = this.resolveAddedMemberInstanceName(snapshot.name, existingMembers.map((member) => member.name));
            return tx.teamMember.create({
                data: {
                    teamRunId,
                    presetId: snapshot.presetId,
                    name: instanceName,
                    aliases: stringifyJson(snapshot.aliases),
                    providerId: snapshot.providerId,
                    rolePrompt: snapshot.rolePrompt,
                    capabilities: stringifyJson(snapshot.capabilities),
                    workspacePolicy: snapshot.workspacePolicy,
                    triggerPolicy: snapshot.triggerPolicy,
                    sessionPolicy: snapshot.sessionPolicy,
                    queueManagementPolicy: snapshot.queueManagementPolicy,
                    membershipStatus: ACTIVE_MEMBERSHIP_STATUS,
                    avatar: snapshot.avatar,
                },
            });
        });
        await emitTeamRunInvalidated({
            teamRunId,
            scopes: ['team-run', 'team-members'],
            reason: 'team-members-updated',
        });
        return this.serializeTeamMember(created);
    }
    async patchTeamRunMember(teamRunId, memberId, input) {
        if (Object.keys(input).length === 0) {
            throw new ValidationError('At least one TeamMember field is required');
        }
        const updated = await prisma.$transaction(async (tx) => {
            const member = await tx.teamMember.findFirst({
                where: { id: memberId, teamRunId },
                include: { teamRun: { include: { task: true } } },
            });
            if (!member) {
                const teamRun = await tx.teamRun.findUnique({ where: { id: teamRunId }, select: { id: true } });
                if (!teamRun)
                    throw new NotFoundError('TeamRun', teamRunId);
                throw new NotFoundError('TeamMember', memberId);
            }
            ensureTaskNotDeleted(member.teamRun.task);
            if (!isActiveMembershipStatus(member.membershipStatus)) {
                throw new ServiceError('Removed TeamRun members cannot be updated', 'INVALID_STATE_TRANSITION', 400);
            }
            return tx.teamMember.update({
                where: { id: memberId },
                data: {
                    ...(input.name !== undefined ? { name: input.name } : {}),
                    ...(input.aliases !== undefined ? { aliases: stringifyJson(input.aliases) } : {}),
                    ...(input.providerId !== undefined ? { providerId: input.providerId } : {}),
                    ...(input.rolePrompt !== undefined ? { rolePrompt: input.rolePrompt } : {}),
                    ...(input.capabilities !== undefined ? { capabilities: stringifyJson(input.capabilities) } : {}),
                    ...(input.workspacePolicy !== undefined ? { workspacePolicy: input.workspacePolicy } : {}),
                    ...(input.triggerPolicy !== undefined ? { triggerPolicy: input.triggerPolicy } : {}),
                    ...(input.sessionPolicy !== undefined ? { sessionPolicy: input.sessionPolicy } : {}),
                    ...(input.queueManagementPolicy !== undefined ? { queueManagementPolicy: input.queueManagementPolicy } : {}),
                    ...(input.avatar !== undefined ? { avatar: input.avatar ?? null } : {}),
                },
            });
        });
        await emitTeamRunInvalidated({
            teamRunId,
            scopes: ['team-run', 'team-members'],
            reason: 'team-members-updated',
        });
        return this.serializeTeamMember(updated);
    }
    async softRemoveTeamRunMember(teamRunId, memberId, options = {}) {
        const result = await prisma.$transaction(async (tx) => {
            const member = await tx.teamMember.findFirst({
                where: { id: memberId, teamRunId },
                include: { teamRun: { include: { task: true } } },
            });
            if (!member) {
                const teamRun = await tx.teamRun.findUnique({ where: { id: teamRunId }, select: { id: true } });
                if (!teamRun)
                    throw new NotFoundError('TeamRun', teamRunId);
                throw new NotFoundError('TeamMember', memberId);
            }
            ensureTaskNotDeleted(member.teamRun.task);
            const cancellableRequests = options.cancelQueued === false
                ? []
                : await tx.workRequest.findMany({
                    where: {
                        teamRunId,
                        targetMemberId: memberId,
                        status: { in: OPEN_WORK_REQUEST_STATUSES },
                    },
                    select: { id: true },
                });
            const cancellableRequestIds = cancellableRequests.map((request) => request.id);
            if (cancellableRequestIds.length > 0) {
                await tx.workRequest.updateMany({
                    where: { id: { in: cancellableRequestIds } },
                    data: { status: 'CANCELLED' },
                });
            }
            const updated = await tx.teamMember.update({
                where: { id: memberId },
                data: {
                    membershipStatus: REMOVED_MEMBERSHIP_STATUS,
                    status: 'REMOVED',
                },
            });
            return { member: updated, cancelledWorkRequestIds: cancellableRequestIds };
        });
        await emitTeamRunInvalidated({
            teamRunId,
            scopes: ['team-run', 'team-members', 'work-requests'],
            reason: 'team-members-updated',
        });
        return {
            member: this.serializeTeamMember(result.member, 'REMOVED'),
            cancelledWorkRequestIds: result.cancelledWorkRequestIds,
        };
    }
    async listRoomMessages(teamRunId, options = {}) {
        const [teamRun, messages] = await Promise.all([
            prisma.teamRun.findUnique({
                where: { id: teamRunId },
                select: { id: true },
            }),
            prisma.roomMessage.findMany({
                where: buildRoomMessageVisibilityWhere(teamRunId, options.viewerMemberId),
                include: { participants: true },
                orderBy: { createdAt: 'asc' },
            }),
        ]);
        if (!teamRun) {
            throw new NotFoundError('TeamRun', teamRunId);
        }
        return messages.map((message) => this.serializeRoomMessage(message));
    }
    async getRoomMessage(teamRunId, messageId, options = {}) {
        const [teamRun, message] = await Promise.all([
            prisma.teamRun.findUnique({
                where: { id: teamRunId },
                select: { id: true },
            }),
            prisma.roomMessage.findFirst({
                where: {
                    id: messageId,
                    ...buildRoomMessageVisibilityWhere(teamRunId, options.viewerMemberId),
                },
                include: { participants: true },
            }),
        ]);
        if (!teamRun) {
            throw new NotFoundError('TeamRun', teamRunId);
        }
        if (!message) {
            throw new NotFoundError('RoomMessage', messageId);
        }
        return this.serializeRoomMessage(message, {
            includeFullContent: true,
        });
    }
    async listWorkRequests(teamRunId, options = {}) {
        await this.assertTeamRunExists(teamRunId);
        const workRequests = await prisma.workRequest.findMany({
            where: { teamRunId },
            orderBy: { createdAt: 'asc' },
        });
        if (!options.viewerMemberId) {
            return workRequests.map((workRequest) => this.serializeWorkRequest(workRequest));
        }
        const messages = await prisma.roomMessage.findMany({
            where: buildRoomMessageVisibilityWhere(teamRunId, options.viewerMemberId),
            include: { participants: true },
        });
        const visibleWorkRequestIds = getVisibleWorkRequestIds(messages, options.viewerMemberId) ?? new Set();
        return workRequests
            .filter((workRequest) => visibleWorkRequestIds.has(workRequest.id))
            .map((workRequest) => this.serializeWorkRequest(workRequest));
    }
    async listQueuedWorkRequestsForMember(teamRunId, memberId) {
        const member = await this.getTeamMemberOrThrow(teamRunId, memberId);
        if (!isActiveMembershipStatus(member.membershipStatus)) {
            throw new ServiceError('Removed TeamRun members cannot access the work request queue', 'FORBIDDEN', 403);
        }
        const queueManagementPolicy = this.resolveQueueManagementPolicy(member.queueManagementPolicy);
        const canManageTeamRunQueue = queueManagementPolicy === 'team_pending';
        const workRequests = await prisma.workRequest.findMany({
            where: {
                teamRunId,
                status: { in: OPEN_WORK_REQUEST_STATUSES },
                ...(canManageTeamRunQueue ? {} : { targetMemberId: memberId }),
            },
            orderBy: [{ createdAt: 'asc' }, { id: 'asc' }],
        });
        const triggerMessageIds = Array.from(new Set(workRequests.map((workRequest) => workRequest.triggerMessageId)));
        const targetMemberIds = Array.from(new Set(workRequests.map((workRequest) => workRequest.targetMemberId)));
        const [triggerMessages, targetMembers] = await Promise.all([
            triggerMessageIds.length > 0
                ? prisma.roomMessage.findMany({
                    where: {
                        id: { in: triggerMessageIds },
                        ...buildRoomMessageVisibilityWhere(teamRunId, memberId),
                    },
                    include: { participants: true },
                })
                : [],
            targetMemberIds.length > 0
                ? prisma.teamMember.findMany({
                    where: { id: { in: targetMemberIds }, teamRunId },
                    select: { id: true, name: true },
                })
                : [],
        ]);
        const triggerMessageById = new Map(triggerMessages.map((message) => [message.id, message]));
        const targetMemberById = new Map(targetMembers.map((targetMember) => [targetMember.id, targetMember]));
        const visibleTriggerMessageIds = new Set(triggerMessages.map((message) => message.id));
        const visibleWorkRequests = workRequests.filter((workRequest) => visibleTriggerMessageIds.has(workRequest.triggerMessageId));
        return {
            teamRunId,
            currentMemberId: memberId,
            queueManagementPolicy,
            canManageTeamRunQueue,
            workRequests: visibleWorkRequests.map((workRequest) => (this.serializeWorkRequestQueueItem(workRequest, triggerMessageById.get(workRequest.triggerMessageId) ?? null, targetMemberById.get(workRequest.targetMemberId) ?? null))),
        };
    }
    async listAgentInvocations(teamRunId, options = {}) {
        await this.assertTeamRunExists(teamRunId);
        const invocations = await prisma.agentInvocation.findMany({
            where: { teamRunId },
            orderBy: { createdAt: 'asc' },
        });
        if (!options.viewerMemberId) {
            return invocations.map((invocation) => this.serializeAgentInvocation(invocation));
        }
        const messages = await prisma.roomMessage.findMany({
            where: buildRoomMessageVisibilityWhere(teamRunId, options.viewerMemberId),
            include: { participants: true },
        });
        const visibleWorkRequestIds = getVisibleWorkRequestIds(messages, options.viewerMemberId) ?? new Set();
        return invocations
            .filter((invocation) => visibleWorkRequestIds.has(invocation.workRequestId))
            .map((invocation) => this.serializeAgentInvocation(invocation));
    }
    async createRoomMessage(teamRunId, input) {
        const teamRun = await prisma.teamRun.findUnique({
            where: { id: teamRunId },
            include: { task: true },
        });
        if (!teamRun) {
            throw new NotFoundError('TeamRun', teamRunId);
        }
        ensureTaskNotDeleted(teamRun.task);
        const mentions = input.mentions ?? [];
        const senderType = input.senderType ?? 'user';
        const kind = input.kind ?? (mentions.length > 0 ? 'work_request' : 'chat');
        const workRequestStatus = teamRun.mode === 'CONFIRM'
            ? 'PENDING_APPROVAL'
            : 'QUEUED';
        await appendAttachmentMarkdownContext(input.content, input.attachmentIds);
        const workRequestInstruction = this.contentPreview(input.content);
        let messageId = '';
        await prisma.$transaction(async (tx) => {
            const liveTeamRun = await tx.teamRun.findFirst({
                where: { id: teamRunId, task: { deletedAt: null } },
                select: { id: true },
            });
            if (!liveTeamRun) {
                throw new NotFoundError('Task', teamRun.taskId);
            }
            const members = await tx.teamMember.findMany({ where: activeTeamMembersWhere(teamRunId) });
            const memberIds = new Set(members.map((member) => member.id));
            assertMentionsReferenceMembers(mentions, memberIds);
            const requesterMemberId = senderType === 'agent'
                && input.senderId != null
                && memberIds.has(input.senderId)
                ? input.senderId
                : null;
            if (senderType === 'agent') {
                if (!input.senderId || !memberIds.has(input.senderId)) {
                    throw new ValidationError('Agent RoomMessage senderId must be a TeamMember in this TeamRun');
                }
                if (input.senderInvocationId) {
                    const invocation = await tx.agentInvocation.findFirst({
                        where: {
                            id: input.senderInvocationId,
                            teamRunId,
                            memberId: input.senderId,
                        },
                        select: { id: true },
                    });
                    if (!invocation) {
                        throw new ValidationError('Agent RoomMessage senderInvocationId must belong to the sender member in this TeamRun');
                    }
                }
            }
            const message = await tx.roomMessage.create({
                data: {
                    teamRunId,
                    senderType,
                    senderId: input.senderId ?? null,
                    senderInvocationId: input.senderInvocationId ?? null,
                    kind,
                    visibility: 'PUBLIC',
                    content: input.content,
                    mentions: stringifyJson(mentions),
                    artifactRefs: stringifyJson(input.artifactRefs ?? []),
                    attachmentIds: stringifyJson(input.attachmentIds ?? []),
                    workRequestIds: stringifyJson([]),
                },
            });
            messageId = message.id;
            const targetRequests = resolveRoomMessageTargetRequests({
                mentions,
                members,
                senderType,
                requesterMemberId,
            });
            const workRequestIds = [];
            for (const targetRequest of targetRequests) {
                const workRequest = await tx.workRequest.create({
                    data: {
                        teamRunId,
                        requesterMemberId,
                        requesterType: senderType,
                        targetMemberId: targetRequest.targetMemberId,
                        triggerMessageId: message.id,
                        instruction: workRequestInstruction,
                        ifBusy: targetRequest.ifBusy,
                        cancelQueued: targetRequest.cancelQueued,
                        status: workRequestStatus,
                    },
                });
                workRequestIds.push(workRequest.id);
            }
            if (workRequestIds.length > 0) {
                await tx.roomMessage.update({
                    where: { id: message.id },
                    data: { workRequestIds: stringifyJson(workRequestIds) },
                });
            }
        });
        const message = await prisma.roomMessage.findUnique({ where: { id: messageId } });
        if (!message) {
            throw new NotFoundError('RoomMessage', messageId);
        }
        const serialized = this.serializeRoomMessage(message);
        await emitTeamRunInvalidated({
            teamRunId,
            taskId: teamRun.taskId,
            scopes: ['room-messages', 'work-requests', 'team-run'],
            reason: 'room-message-created',
        });
        return serialized;
    }
    async createPrivateRoomMessage(teamRunId, input) {
        const teamRun = await prisma.teamRun.findUnique({ where: { id: teamRunId } });
        if (!teamRun) {
            throw new NotFoundError('TeamRun', teamRunId);
        }
        const recipientMemberIds = uniqueMemberIds(input.recipientMemberIds);
        if (recipientMemberIds.length === 0) {
            throw new ValidationError('At least one private message recipient is required');
        }
        const senderType = input.senderType ?? 'user';
        const workRequestStatus = teamRun.mode === 'CONFIRM'
            ? 'PENDING_APPROVAL'
            : 'QUEUED';
        const workRequestInstruction = this.contentPreview(input.content);
        let messageId = '';
        await prisma.$transaction(async (tx) => {
            const members = await tx.teamMember.findMany({ where: activeTeamMembersWhere(teamRunId) });
            const memberIds = new Set(members.map((member) => member.id));
            for (const recipientMemberId of recipientMemberIds) {
                if (!memberIds.has(recipientMemberId)) {
                    throw new NotFoundError('TeamMember', recipientMemberId);
                }
            }
            const requesterMemberId = senderType === 'agent'
                && input.senderId != null
                && memberIds.has(input.senderId)
                ? input.senderId
                : null;
            let normalizedSenderId = input.senderId ?? null;
            let normalizedSenderInvocationId = input.senderInvocationId ?? null;
            let senderParticipantMemberId = null;
            if (senderType === 'agent') {
                if (!input.senderId || !memberIds.has(input.senderId)) {
                    throw new ValidationError('Agent RoomMessage senderId must be a TeamMember in this TeamRun');
                }
                senderParticipantMemberId = input.senderId;
                if (input.senderInvocationId) {
                    const invocation = await tx.agentInvocation.findFirst({
                        where: {
                            id: input.senderInvocationId,
                            teamRunId,
                            memberId: input.senderId,
                        },
                        select: { id: true },
                    });
                    if (!invocation) {
                        throw new ValidationError('Agent RoomMessage senderInvocationId must belong to the sender member in this TeamRun');
                    }
                }
            }
            else if (senderType === 'user') {
                if (input.senderInvocationId) {
                    throw new ValidationError('Only agent private messages may include senderInvocationId');
                }
                if (input.senderId != null) {
                    if (!memberIds.has(input.senderId)) {
                        throw new ValidationError('Private message senderId must be a TeamMember in this TeamRun');
                    }
                    senderParticipantMemberId = input.senderId;
                }
                normalizedSenderInvocationId = null;
            }
            else {
                normalizedSenderId = null;
                normalizedSenderInvocationId = null;
            }
            const message = await tx.roomMessage.create({
                data: {
                    teamRunId,
                    senderType,
                    senderId: normalizedSenderId,
                    senderInvocationId: normalizedSenderInvocationId,
                    kind: 'work_request',
                    visibility: 'PRIVATE',
                    content: input.content,
                    mentions: stringifyJson([]),
                    artifactRefs: stringifyJson(input.artifactRefs ?? []),
                    attachmentIds: stringifyJson(input.attachmentIds ?? []),
                    workRequestIds: stringifyJson([]),
                },
            });
            messageId = message.id;
            const participantByMemberId = new Map();
            if (senderParticipantMemberId && !recipientMemberIds.includes(senderParticipantMemberId)) {
                participantByMemberId.set(senderParticipantMemberId, 'sender');
            }
            for (const recipientMemberId of recipientMemberIds) {
                participantByMemberId.set(recipientMemberId, 'recipient');
            }
            if (participantByMemberId.size > 0) {
                await tx.roomMessageParticipant.createMany({
                    data: Array.from(participantByMemberId.entries()).map(([memberId, role]) => ({
                        teamRunId,
                        roomMessageId: message.id,
                        memberId,
                        role,
                    })),
                });
            }
            const workRequestIds = [];
            for (const recipientMemberId of recipientMemberIds) {
                const workRequest = await tx.workRequest.create({
                    data: {
                        teamRunId,
                        requesterMemberId,
                        requesterType: senderType,
                        targetMemberId: recipientMemberId,
                        triggerMessageId: message.id,
                        instruction: workRequestInstruction,
                        ifBusy: input.ifBusy ?? 'queue',
                        cancelQueued: input.cancelQueued ?? false,
                        status: workRequestStatus,
                    },
                });
                workRequestIds.push(workRequest.id);
            }
            await tx.roomMessage.update({
                where: { id: message.id },
                data: { workRequestIds: stringifyJson(workRequestIds) },
            });
        });
        const message = await prisma.roomMessage.findUnique({
            where: { id: messageId },
            include: { participants: true },
        });
        if (!message) {
            throw new NotFoundError('RoomMessage', messageId);
        }
        const serialized = this.serializeRoomMessage(message);
        await emitTeamRunInvalidated({
            teamRunId,
            taskId: teamRun.taskId,
            scopes: ['room-messages', 'work-requests', 'team-run'],
            reason: 'room-message-created',
        });
        return serialized;
    }
    normalizeTeamTemplateMembers(input) {
        const members = input.members
            ?? input.memberPresetIds?.map((memberPresetId, index) => ({ memberPresetId, position: index }))
            ?? [];
        return members.map((member, index) => ({
            memberPresetId: member.memberPresetId,
            position: member.position ?? index,
        }));
    }
    async buildTeamMemberSnapshots(input) {
        const snapshots = [];
        if (input.teamTemplateId) {
            const template = await prisma.teamTemplate.findUnique({
                where: { id: input.teamTemplateId },
                include: { members: { orderBy: { position: 'asc' } } },
            });
            if (!template) {
                throw new NotFoundError('TeamTemplate', input.teamTemplateId);
            }
            const presets = await this.findMemberPresetsByIds(template.members.map((member) => member.memberPresetId));
            snapshots.push(...presets.map((preset) => this.memberPresetToSnapshot(preset)));
        }
        if (input.memberPresetIds && input.memberPresetIds.length > 0) {
            const presets = await this.findMemberPresetsByIds(input.memberPresetIds);
            snapshots.push(...presets.map((preset) => this.memberPresetToSnapshot(preset)));
        }
        if (input.members && input.members.length > 0) {
            snapshots.push(...input.members.map((member) => ({
                presetId: null,
                name: member.name,
                aliases: member.aliases,
                providerId: member.providerId,
                rolePrompt: member.rolePrompt,
                capabilities: member.capabilities,
                workspacePolicy: member.workspacePolicy,
                triggerPolicy: member.triggerPolicy,
                sessionPolicy: member.sessionPolicy,
                queueManagementPolicy: member.queueManagementPolicy,
                avatar: member.avatar ?? null,
            })));
        }
        return snapshots;
    }
    async buildAddTeamRunMemberSnapshot(input) {
        if (input.memberPresetId) {
            const preset = await prisma.memberPreset.findUnique({ where: { id: input.memberPresetId } });
            if (!preset) {
                throw new NotFoundError('MemberPreset', input.memberPresetId);
            }
            return this.memberPresetToSnapshot(preset);
        }
        if (!input.member) {
            throw new ValidationError('member is required when memberPresetId is not provided');
        }
        return {
            presetId: null,
            name: input.member.name,
            aliases: input.member.aliases,
            providerId: input.member.providerId,
            rolePrompt: input.member.rolePrompt,
            capabilities: input.member.capabilities,
            workspacePolicy: input.member.workspacePolicy,
            triggerPolicy: input.member.triggerPolicy,
            sessionPolicy: input.member.sessionPolicy,
            queueManagementPolicy: input.member.queueManagementPolicy,
            avatar: input.member.avatar ?? null,
        };
    }
    resolveAddedMemberInstanceName(baseName, existingNames) {
        const trimmedBaseName = baseName.trim();
        if (!existingNames.includes(trimmedBaseName)) {
            return trimmedBaseName;
        }
        let index = 2;
        while (existingNames.includes(`${trimmedBaseName} #${index}`)) {
            index += 1;
        }
        return `${trimmedBaseName} #${index}`;
    }
    memberPresetToSnapshot(preset) {
        return {
            presetId: preset.id,
            name: preset.name,
            aliases: parseJsonField(preset.aliases, []),
            providerId: preset.providerId,
            rolePrompt: preset.rolePrompt,
            capabilities: parseJsonField(preset.capabilities, DEFAULT_CAPABILITIES),
            workspacePolicy: preset.workspacePolicy,
            triggerPolicy: preset.triggerPolicy,
            sessionPolicy: preset.sessionPolicy || DEFAULT_SESSION_POLICY,
            queueManagementPolicy: this.resolveQueueManagementPolicy(preset.queueManagementPolicy),
            avatar: preset.avatar ?? null,
        };
    }
    async findMemberPresetsByIds(ids) {
        if (ids.length === 0) {
            return [];
        }
        const presets = await prisma.memberPreset.findMany({
            where: { id: { in: ids } },
        });
        const presetById = new Map(presets.map((preset) => [preset.id, preset]));
        const missing = ids.find((id) => !presetById.has(id));
        if (missing) {
            throw new NotFoundError('MemberPreset', missing);
        }
        return ids.map((id) => presetById.get(id));
    }
    async assertMemberPresetsExist(ids) {
        await this.findMemberPresetsByIds(ids);
    }
    async assertTeamRunExists(id) {
        const count = await prisma.teamRun.count({ where: { id } });
        if (count === 0) {
            throw new NotFoundError('TeamRun', id);
        }
    }
    async getTeamMemberOrThrow(teamRunId, memberId) {
        const member = await prisma.teamMember.findFirst({ where: { id: memberId, teamRunId } });
        if (member) {
            return member;
        }
        await this.assertTeamRunExists(teamRunId);
        throw new NotFoundError('TeamMember', memberId);
    }
    async resolveAgentInvocationIdentity(teamRunId, invocationId) {
        if (!invocationId) {
            return null;
        }
        const invocation = await prisma.agentInvocation.findFirst({
            where: { id: invocationId, teamRunId },
            select: { id: true, memberId: true },
        });
        return invocation
            ? { invocationId: invocation.id, memberId: invocation.memberId }
            : null;
    }
    async resolveViewerMemberIdFromInvocation(teamRunId, invocationId) {
        const identity = await this.resolveAgentInvocationIdentity(teamRunId, invocationId);
        return identity?.memberId ?? null;
    }
    resolveQueueManagementPolicy(value) {
        return value === 'team_pending' ? 'team_pending' : DEFAULT_QUEUE_MANAGEMENT_POLICY;
    }
    contentPreview(content) {
        return buildTextPreview(content);
    }
    compactContentPreview(content) {
        return buildTextPreview(content.replace(/\s+/g, ' '));
    }
    serializePreviewField(content) {
        const preview = this.contentPreview(content);
        return {
            value: preview,
            preview,
            isTruncated: content !== preview,
        };
    }
    teamRunInclude() {
        return {
            members: { orderBy: { createdAt: 'asc' } },
            messages: { orderBy: { createdAt: 'asc' }, include: { participants: true } },
            workRequests: { orderBy: { createdAt: 'asc' } },
            invocations: { orderBy: { createdAt: 'asc' } },
        };
    }
    serializeMemberPreset(preset) {
        return {
            ...preset,
            aliases: parseJsonField(preset.aliases, []),
            capabilities: parseJsonField(preset.capabilities, DEFAULT_CAPABILITIES),
            workspacePolicy: preset.workspacePolicy,
            triggerPolicy: preset.triggerPolicy,
            sessionPolicy: preset.sessionPolicy || DEFAULT_SESSION_POLICY,
            queueManagementPolicy: this.resolveQueueManagementPolicy(preset.queueManagementPolicy),
            avatar: preset.avatar ?? null,
            createdAt: toIso(preset.createdAt),
            updatedAt: toIso(preset.updatedAt),
        };
    }
    async serializeTeamTemplate(template) {
        const members = template.members ?? await prisma.teamTemplateMember.findMany({
            where: { teamTemplateId: template.id },
            orderBy: { position: 'asc' },
        });
        const presetIds = members.map((member) => member.memberPresetId);
        const presets = presetIds.length > 0
            ? await prisma.memberPreset.findMany({ where: { id: { in: presetIds } } })
            : [];
        const presetById = new Map(presets.map((preset) => [preset.id, preset]));
        return {
            ...template,
            createdAt: toIso(template.createdAt),
            updatedAt: toIso(template.updatedAt),
            members: members.map((member) => {
                const preset = presetById.get(member.memberPresetId);
                return {
                    ...member,
                    memberPreset: preset ? this.serializeMemberPreset(preset) : undefined,
                };
            }),
        };
    }
    serializeTeamRun(teamRun, options = {}) {
        const visibleMessages = options.viewerMemberId
            ? (teamRun.messages ?? []).filter((message) => isRoomMessageVisibleToViewer(message, options.viewerMemberId))
            : teamRun.messages;
        const visibleWorkRequestIds = getVisibleWorkRequestIds(visibleMessages ?? [], options.viewerMemberId);
        const visibleWorkRequests = visibleWorkRequestIds
            ? (teamRun.workRequests ?? []).filter((workRequest) => visibleWorkRequestIds.has(workRequest.id))
            : teamRun.workRequests;
        const visibleInvocations = visibleWorkRequestIds
            ? (teamRun.invocations ?? []).filter((invocation) => visibleWorkRequestIds.has(invocation.workRequestId))
            : teamRun.invocations;
        const memberStatuses = this.deriveTeamMemberStatuses(teamRun.members ?? [], visibleInvocations ?? [], visibleWorkRequests ?? []);
        return {
            ...teamRun,
            mode: teamRun.mode,
            reviewReason: teamRun.reviewReason,
            createdAt: toIso(teamRun.createdAt),
            updatedAt: toIso(teamRun.updatedAt),
            members: teamRun.members?.map((member) => this.serializeTeamMember(member, memberStatuses.get(member.id))),
            messages: visibleMessages?.map((message) => this.serializeRoomMessage(message)),
            workRequests: visibleWorkRequests?.map((workRequest) => this.serializeWorkRequest(workRequest)),
            invocations: visibleInvocations?.map((invocation) => this.serializeAgentInvocation(invocation)),
        };
    }
    deriveTeamMemberStatuses(members, invocations = [], workRequests = []) {
        const statuses = new Map();
        for (const member of members) {
            statuses.set(member.id, this.deriveTeamMemberStatus(member.id, invocations, workRequests, member.membershipStatus));
        }
        return statuses;
    }
    deriveTeamMemberStatus(memberId, invocations, workRequests, membershipStatus) {
        if (!isActiveMembershipStatus(membershipStatus))
            return 'REMOVED';
        const memberInvocations = invocations.filter((invocation) => invocation.memberId === memberId);
        if (memberInvocations.some((invocation) => invocation.status === 'RUNNING'))
            return 'RUNNING';
        if (memberInvocations.some((invocation) => invocation.status === 'WAITING_ROOM_REPLY'))
            return 'WAITING_ROOM_REPLY';
        if (memberInvocations.some((invocation) => invocation.status === 'SESSION_ENDED'))
            return 'SESSION_ENDED';
        if (memberInvocations.some((invocation) => invocation.status === 'QUEUED'))
            return 'QUEUED';
        const memberWorkRequests = workRequests.filter((request) => request.targetMemberId === memberId);
        if (memberWorkRequests.some((request) => request.status === 'QUEUED'))
            return 'QUEUED';
        if (memberWorkRequests.some((request) => request.status === 'PENDING_APPROVAL'))
            return 'PENDING_APPROVAL';
        return 'IDLE';
    }
    serializeTeamMember(member, status) {
        return {
            ...member,
            aliases: parseJsonField(member.aliases, []),
            capabilities: parseJsonField(member.capabilities, DEFAULT_CAPABILITIES),
            workspacePolicy: member.workspacePolicy,
            triggerPolicy: member.triggerPolicy,
            sessionPolicy: member.sessionPolicy || DEFAULT_SESSION_POLICY,
            queueManagementPolicy: this.resolveQueueManagementPolicy(member.queueManagementPolicy),
            membershipStatus: (isActiveMembershipStatus(member.membershipStatus)
                ? ACTIVE_MEMBERSHIP_STATUS
                : REMOVED_MEMBERSHIP_STATUS),
            avatar: member.avatar ?? null,
            status: status ?? 'IDLE',
            createdAt: toIso(member.createdAt),
            updatedAt: toIso(member.updatedAt),
        };
    }
    serializeRoomMessage(message, options = {}) {
        const participants = (message.participants ?? []).map((participant) => this.serializeRoomMessageParticipant(participant));
        const recipientMemberIds = participants
            .filter((participant) => participant.role === 'recipient')
            .map((participant) => participant.memberId);
        const participantMemberIds = participants.map((participant) => participant.memberId);
        const serializedContent = this.serializePreviewField(message.content);
        return {
            ...message,
            content: options.includeFullContent ? message.content : serializedContent.value,
            contentPreview: serializedContent.preview,
            isTruncated: serializedContent.isTruncated,
            senderType: message.senderType,
            kind: message.kind,
            visibility: message.visibility,
            mentions: parseJsonField(message.mentions, []),
            recipientMemberIds,
            participantMemberIds,
            participants,
            workRequestIds: parseJsonField(message.workRequestIds, null),
            artifactRefs: parseJsonField(message.artifactRefs, null),
            attachmentIds: parseJsonField(message.attachmentIds, null),
            createdAt: toIso(message.createdAt),
        };
    }
    serializeRoomMessageParticipant(participant) {
        return {
            ...participant,
            role: participant.role,
            createdAt: toIso(participant.createdAt),
        };
    }
    serializeWorkRequest(workRequest) {
        const serializedInstruction = this.serializePreviewField(workRequest.instruction);
        return {
            ...workRequest,
            instruction: serializedInstruction.value,
            instructionPreview: serializedInstruction.preview,
            isTruncated: serializedInstruction.isTruncated,
            requesterType: workRequest.requesterType,
            ifBusy: workRequest.ifBusy,
            status: workRequest.status,
            createdAt: toIso(workRequest.createdAt),
            updatedAt: toIso(workRequest.updatedAt),
        };
    }
    serializeWorkRequestQueueItem(workRequest, triggerMessage, targetMember) {
        return {
            ...this.serializeWorkRequest(workRequest),
            triggerMessage: triggerMessage
                ? {
                    id: triggerMessage.id,
                    senderType: triggerMessage.senderType,
                    senderId: triggerMessage.senderId,
                    senderInvocationId: triggerMessage.senderInvocationId,
                    kind: triggerMessage.kind,
                    contentPreview: this.compactContentPreview(triggerMessage.content),
                    createdAt: toIso(triggerMessage.createdAt),
                }
                : null,
            targetMember: targetMember
                ? {
                    id: targetMember.id,
                    name: targetMember.name,
                    label: targetMember.name,
                }
                : null,
        };
    }
    serializeAgentInvocation(invocation) {
        return {
            ...invocation,
            status: invocation.status,
            createdAt: toIso(invocation.createdAt),
            updatedAt: toIso(invocation.updatedAt),
            nextRoomReplyReminderAt: invocation.nextRoomReplyReminderAt
                ? toIso(invocation.nextRoomReplyReminderAt)
                : null,
        };
    }
}
//# sourceMappingURL=team-run.service.js.map