diff --git a/frontend-web/src/position-roundtable/pages/PositionRoundtablePage.tsx b/frontend-web/src/position-roundtable/pages/PositionRoundtablePage.tsx index ff015745..4e5488e4 100644 --- a/frontend-web/src/position-roundtable/pages/PositionRoundtablePage.tsx +++ b/frontend-web/src/position-roundtable/pages/PositionRoundtablePage.tsx @@ -1276,10 +1276,6 @@ function PositionDeliveryOverview({ chain: BusinessChain | null; session: PositionRoundtableSession | null; }) { - const seatsByAgentId = useMemo( - () => new Map((chain?.seats ?? []).map((seat) => [seat.agent_id, seat])), - [chain], - ); const items = useMemo(() => { const sortedNodes = [...nodes].sort( (left, right) => @@ -1287,39 +1283,110 @@ function PositionDeliveryOverview({ left.seatIndex - right.seatIndex || left.agentId.localeCompare(right.agentId), ); - const nodeItems = sortedNodes.map((node, index): DeliveryOverviewItem => { - const seat = seatsByAgentId.get(node.agentId); - const agentName = seat?.name ?? node.agentId; - const markdownArtifact = node.artifactManifest.find(artifactIsMarkdown); + const usedNodeKeys = new Set(); + const makeNodeItem = ( + node: PositionRoundtableNode | null, + fallback: { + agentId: string; + name?: string | null; + positionId?: string | null; + stageIndex: number; + seatIndex: number; + }, + index: number, + ): DeliveryOverviewItem => { + if (node) usedNodeKeys.add(node.nodeKey); + const agentName = fallback.name?.trim() || node?.agentId || fallback.agentId; + const status = node?.status ?? "ready"; + const markdownArtifact = node?.artifactManifest.find(artifactIsMarkdown); const deliveredArtifactName = artifactName(markdownArtifact); - const statusLabel = nodeStatusMeta(node.status).label; const summary = - node.latestAnswer?.trim() || - (node.status === "locked" - ? node.invalidatedBy - ? "上游交付已被驳回或重做,等待前序节点重新完成。" - : "等待上游阶段完成后解锁。" - : node.status === "running" + node?.latestAnswer?.trim() || + (!node + ? "该席位已按业务链配置排入流程,等待生成会话节点或开始问答。" + : status === "locked" + ? node.invalidatedBy + ? "上游交付已被驳回或重做,等待前序节点重新完成。" + : "等待上游阶段完成后解锁。" + : status === "running" ? "智能体正在问答并生成 Markdown 报告。" - : node.status === "stale" + : status === "stale" ? "上游产物已更新,该席位需要基于最新结果重做。" - : node.status === "rejected" + : status === "rejected" ? "产物已被驳回,需要重新问答并交付。" : "该席位尚未交付 Markdown 报告。"); return { - id: `node:${node.nodeKey}`, + id: node + ? `node:${node.nodeKey}` + : `seat:${fallback.stageIndex}:${fallback.seatIndex}:${fallback.agentId}`, kind: "node", agentName, productName: deliveredArtifactName || - (node.status === "done" ? `${agentName}报告` : `待交付:${agentName} Markdown报告`), + (status === "done" ? `${agentName}报告` : `待交付:${agentName} Markdown报告`), summary, - positionId: node.positionId, - updatedAt: node.updatedAt, - status: node.status, + positionId: node?.positionId ?? fallback.positionId ?? null, + updatedAt: node?.updatedAt, + status, accent: FLOW_NODE_COLORS[index % FLOW_NODE_COLORS.length]!, }; - }); + }; + const stagedSeatItems = chain + ? chainStages(chain).flatMap((stage, stageIndex) => + stage.seats.map((seat, seatIndex) => { + const matchedNode = + nodes.find( + (node) => + node.stageIndex === stageIndex && + node.seatIndex === seatIndex && + node.agentId === seat.agent_id, + ) ?? + nodes.find( + (node) => + node.agentId === seat.agent_id && !usedNodeKeys.has(node.nodeKey), + ) ?? + null; + return makeNodeItem( + matchedNode, + { + agentId: seat.agent_id, + name: seat.name, + positionId: seat.position_id, + stageIndex, + seatIndex, + }, + stageIndex + seatIndex, + ); + }), + ) + : sortedNodes.map((node, index) => + makeNodeItem( + node, + { + agentId: node.agentId, + positionId: node.positionId, + stageIndex: node.stageIndex, + seatIndex: node.seatIndex, + }, + index, + ), + ); + const orphanNodeItems = chain + ? sortedNodes + .filter((node) => !usedNodeKeys.has(node.nodeKey) && !isIntentPositionNode(node)) + .map((node, index) => + makeNodeItem( + node, + { + agentId: node.agentId, + positionId: node.positionId, + stageIndex: node.stageIndex, + seatIndex: node.seatIndex, + }, + stagedSeatItems.length + index, + ), + ) + : []; const allDelivered = allExecutableNodesDone(nodes); const summarySnapshot = session?.summarySnapshot; const summaryArtifact = snapshotArtifact(summarySnapshot, "primary_artifact"); @@ -1342,7 +1409,8 @@ function PositionDeliveryOverview({ status: intent ? "done" : "ready", accent: "#8b5cf6", }, - ...nodeItems, + ...stagedSeatItems, + ...orphanNodeItems, { id: "builtin:summary", kind: "summary", @@ -1380,7 +1448,7 @@ function PositionDeliveryOverview({ accent: "#0ea5e9", }, ]; - }, [chain, intent, intentCompletedAt, nodes, seatsByAgentId, session]); + }, [chain, intent, intentCompletedAt, nodes, session]); const deliveredCount = items.filter((item) => item.status === "done").length; return (