feat(coordinator): 添加团队节点默认最大工具迭代次数配置

添加 DEFAULT_TEAM_NODE_MAX_TOOL_ITERATIONS 配置项以控制团队节点的最大工具迭代次数,
并修改 LocalAgentRunner 中的逻辑来使用此默认值当 envelope 中未指定时。

fix(runtime): 修复团队节点运行成功判断逻辑

更新运行成功判断条件,将 finish_reason 为 "max_tool_iterations_finalized" 的情况
视为运行失败,并添加对原始工具调用输出的检测,避免将其误判为成功完成。

feat(mcp): 添加团队工作流MCP工具类别支持

增加新的本地MCP工具类别 "team_workflow" 及其对应的工具创建功能,
为团队工作流提供本地工具支持。

refactor(engine): 调整AgentLoop最大工具迭代次数设置

将 AgentProfile 中的默认 max_tool_iterations 从 30 增加到 100,
同时移除 TaskExecutionPlanner 构造函数中的重复参数传递。

perf(mcp): 优化MCP连接管理避免重复连接

添加 mcp_connected 标志来跟踪MCP连接状态,确保 connect_all 只执行一次,
提高性能并避免不必要的重复连接。

refactor(skills): 移除技能团队模板相关功能

移除与技能团队模板相关的代码,包括解析、存储和处理逻辑,
简化技能记录结构和加载流程。

feat(process): 增强会话过程投影器功能

添加技能激活快照事件处理,改进团队运行完成消息显示,
并增强技能激活事件的时间戳记录功能。

refactor(tasks): 简化任务尝试编排器团队执行逻辑

移除团队执行相关代码,将所有任务统一按单步执行处理,
简化任务编排器的复杂度并提升执行效率。

fix(evidence): 修复节点证据评估中需求验证逻辑

更新节点证据评估逻辑,跳过自然语言证据需求的确定性验证,
只执行机器可读的需求验证,避免因自然语言需求导致的节点失败。
This commit is contained in:
2026-06-26 16:36:29 +08:00
parent 53b13e8eac
commit 520a21a027
360 changed files with 13271 additions and 1848 deletions

View File

@ -146,11 +146,6 @@ export default function NotificationDetailPage() {
isThinking={submitting}
messagesEndRef={messagesEndRef}
messageViewportRef={viewportRef}
processRuns={[]}
processEvents={[]}
processArtifacts={[]}
selectedRunId={null}
onSelectRun={() => {}}
onFeedback={() => {}}
onRequestRevision={() => {}}
/>

View File

@ -77,7 +77,6 @@ export default function ChatPage() {
processRuns,
processEvents,
processArtifacts,
selectedRunId,
setSessionId,
setMessages,
addMessage,
@ -128,12 +127,6 @@ export default function ChatPage() {
[processEvents, sessionRunIds]
);
const sessionProcessArtifacts = useMemo(
() => processArtifacts.filter((artifact) => sessionRunIds.has(artifact.run_id)),
[processArtifacts, sessionRunIds]
);
const selectedSessionRunId = selectedRunId && sessionRunIds.has(selectedRunId) ? selectedRunId : null;
const activeTaskTimelineView = useMemo(
() =>
buildTaskTimelineView({
@ -710,11 +703,6 @@ export default function ChatPage() {
isThinking={isThinking || (isLoading && messages[messages.length - 1]?.role === 'user')}
messagesEndRef={messagesEndRef}
messageViewportRef={messageViewportRef}
processRuns={sessionProcessRuns}
processEvents={sessionProcessEvents}
processArtifacts={sessionProcessArtifacts}
selectedRunId={selectedSessionRunId}
onSelectRun={(runId) => setSelectedRunId(selectedSessionRunId === runId ? null : runId)}
onFeedback={handleFeedback}
onRequestRevision={handleRequestRevision}
/>
@ -881,6 +869,8 @@ export default function ChatPage() {
{activeTaskDetail ? (
<CurrentSessionProgressSidebar
task={activeTaskDetail}
process={activeTaskTimelineView?.process ?? null}
cards={activeTaskTimelineView?.cards ?? []}
isLive={Boolean(activeTaskDetail.is_open && wsStatus === 'connected')}
/>

View File

@ -1,20 +1,19 @@
'use client';
import Link from 'next/link';
import { useParams, useRouter } from 'next/navigation';
import { useParams } from 'next/navigation';
import React, { useMemo, useState } from 'react';
import { AlertCircle, ArrowLeft, Loader2, Trash2 } from 'lucide-react';
import { AlertCircle, ArrowLeft, Loader2 } from 'lucide-react';
import {
TaskExecutionWorkspace,
TaskLiveHeader,
TaskSideRail,
TaskTimeline,
type TaskFeedbackItem,
type TaskFeedbackType,
} from '@/components/task-detail';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { deleteBackendTask, getBackendTask, submitChatFeedback } from '@/lib/api';
import { getBackendTask, submitChatFeedback } from '@/lib/api';
import { pickAppText } from '@/lib/i18n/core';
import { useAppI18n } from '@/lib/i18n/provider';
import { useChatStore } from '@/lib/store';
@ -27,7 +26,6 @@ const TASK_RESULT_REVIEW_ID = 'task-result-review';
export default function TaskDetailPage() {
const { locale } = useAppI18n();
const router = useRouter();
const params = useParams<{ taskId: string }>();
const taskId = decodeURIComponent(Array.isArray(params?.taskId) ? params.taskId[0] : params?.taskId ?? '');
const processRuns = useChatStore((state) => state.processRuns);
@ -120,18 +118,6 @@ export default function TaskDetailPage() {
}
};
const deleteCurrentBackendTask = async () => {
if (!backendTask) return;
const title = backendTask.short_title || backendTask.description || backendTask.goal || backendTask.task_id;
if (!window.confirm(pickAppText(locale, `删除任务“${title}”?`, `Delete task "${title}"?`))) {
return;
}
await runAction('delete-backend-task', async () => {
await deleteBackendTask(backendTask.task_id);
router.push('/tasks');
});
};
if (backendTask) {
const feedbackItems = backendTask.feedback || [];
@ -139,64 +125,44 @@ export default function TaskDetailPage() {
<div className="min-h-screen bg-background">
<TaskLiveHeader task={backendTask} activeLabel={activeLabel} durationMs={durationMs} reviewTargetId={TASK_RESULT_REVIEW_ID} />
<main className="mx-auto grid min-w-0 max-w-7xl gap-6 p-4 sm:p-6 xl:grid-cols-[minmax(0,1fr)_360px]">
<div className="min-w-0 space-y-4">
<div className="flex justify-end">
<Button
variant="ghost"
size="sm"
className="h-11 text-destructive hover:text-destructive"
disabled={Boolean(actionBusy)}
onClick={() => void deleteCurrentBackendTask()}
>
<Trash2 className="mr-2 h-4 w-4" />
{pickAppText(locale, '删除任务', 'Delete task')}
</Button>
</div>
<main className="mx-auto min-w-0 max-w-[1720px] space-y-5 px-4 py-1 sm:px-6 lg:px-8">
{actionError ? (
<Card className="border-destructive">
<CardContent className="flex items-center gap-2 p-4 text-sm text-destructive">
<AlertCircle className="h-4 w-4" />
{actionError}
</CardContent>
</Card>
) : null}
{actionError ? (
<Card className="border-destructive">
<CardContent className="flex items-center gap-2 p-4 text-sm text-destructive">
<AlertCircle className="h-4 w-4" />
{actionError}
</CardContent>
</Card>
) : null}
<TaskTimeline
cards={timelineCards}
isLive={isTaskLive && wsStatus === 'connected'}
reviewTargetId={TASK_RESULT_REVIEW_ID}
resultAcceptance={{
sessionId: backendTask.session_id,
runId: feedbackRunId,
taskStatus: backendTask.status,
feedbackItems: feedbackItems as TaskFeedbackItem[],
actionBusy,
revision,
onRevisionChange: setRevision,
onSubmit: (feedbackType: TaskFeedbackType, comment?: string) =>
runAction(`backend-feedback-${feedbackType}`, async () => {
if (!feedbackRunId) throw new Error(pickAppText(locale, '暂无可验收的运行记录。', 'No run is available for acceptance yet.'));
await submitChatFeedback({
sessionId: backendTask.session_id,
runId: feedbackRunId,
feedbackType,
comment,
});
updateMessageFeedback(feedbackRunId, feedbackType);
setRevision('');
await loadBackendTask();
}),
}}
/>
</div>
<TaskSideRail
<TaskExecutionWorkspace
task={backendTask}
runs={timelineView?.process.runs ?? []}
artifacts={timelineView?.process.artifacts ?? []}
process={timelineView?.process ?? { runs: [], events: [], artifacts: [] }}
cards={timelineCards}
isLive={isTaskLive && wsStatus === 'connected'}
reviewTargetId={TASK_RESULT_REVIEW_ID}
resultAcceptance={{
sessionId: backendTask.session_id,
runId: feedbackRunId,
taskStatus: backendTask.status,
feedbackItems: feedbackItems as TaskFeedbackItem[],
actionBusy,
revision,
onRevisionChange: setRevision,
onSubmit: (feedbackType: TaskFeedbackType, comment?: string) =>
runAction(`backend-feedback-${feedbackType}`, async () => {
if (!feedbackRunId) throw new Error(pickAppText(locale, '暂无可验收的运行记录。', 'No run is available for acceptance yet.'));
await submitChatFeedback({
sessionId: backendTask.session_id,
runId: feedbackRunId,
feedbackType,
comment,
});
updateMessageFeedback(feedbackRunId, feedbackType);
setRevision('');
await loadBackendTask();
}),
}}
/>
</main>
</div>