'), '/flow/ 未返回应用入口');
+
+ const assetPath = indexHtml.match(/(?:src|href)="(\/flow\/[^"]+)"/)?.[1];
+ assert(assetPath, '构建产物未使用 /flow/ 静态资源前缀');
+ const assetResponse = await fetch(`${origin}${assetPath}`);
+ assert(assetResponse.ok, `静态资源状态异常: ${assetResponse.status}`);
+
+ const invalidRootShareResponse = await fetch(
+ `${origin}/share/knowledge?shareKey=smoke-test`,
+ );
+ assert(
+ invalidRootShareResponse.status === 404,
+ `根路径分享地址应返回 404,实际为 ${invalidRootShareResponse.status}`,
+ );
+
+ console.log(
+ '部署冒烟通过:根路径重定向、/flow/ 入口、静态资源前缀和错误分享路径均符合预期。',
+ );
+} finally {
+ try {
+ docker(['rm', '--force', containerName]);
+ } catch {
+ // 容器可能因 --rm 已自动删除。
+ }
+}
From ac07899dc46ff17ce860074ca06a4fbdae8b3239 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=AD=90=E9=BB=98?= <925456043@qq.com>
Date: Fri, 24 Jul 2026 19:03:27 +0800
Subject: [PATCH 031/143] =?UTF-8?q?fix:=20=E7=A8=B3=E5=AE=9A=E7=AE=A1?=
=?UTF-8?q?=E7=90=86=E7=AB=AF=E5=90=AF=E5=8A=A8=E9=A1=B5=E5=AD=97=E4=BD=93?=
=?UTF-8?q?=E6=98=BE=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 启动文案固定使用 700 字重
- 复用应用字体栈避免加载前后视觉跳变
---
.../default-loading-progress.html | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/easyflow-ui-admin/internal/vite-config/src/plugins/inject-app-loading/default-loading-progress.html b/easyflow-ui-admin/internal/vite-config/src/plugins/inject-app-loading/default-loading-progress.html
index 4a594bfa..d53a0211 100644
--- a/easyflow-ui-admin/internal/vite-config/src/plugins/inject-app-loading/default-loading-progress.html
+++ b/easyflow-ui-admin/internal/vite-config/src/plugins/inject-app-loading/default-loading-progress.html
@@ -10,6 +10,17 @@
align-items: center;
justify-content: center;
background-color: #f4f7f9;
+ font-family: var(
+ --font-family,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ 'Helvetica Neue',
+ Arial,
+ 'Noto Sans',
+ sans-serif
+ );
/* background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); */
}
@@ -70,7 +81,7 @@
.loadingText {
color: #333;
font-size: 16px;
- font-weight: 500;
+ font-weight: 700;
margin-bottom: 24px;
letter-spacing: 0.5px;
}
From ba4253e13e2459d9f24a0dfcbfb0f8cbb25aa5b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=AD=90=E9=BB=98?= <925456043@qq.com>
Date: Fri, 24 Jul 2026 19:05:39 +0800
Subject: [PATCH 032/143] =?UTF-8?q?chore:=20=E6=B8=85=E7=90=86=E6=B5=8B?=
=?UTF-8?q?=E8=AF=95=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
easyflow-ui-admin/package.json | 1 -
.../scripts/deploy/Dockerfile.smoke | 12 ---
.../deploy/Dockerfile.smoke.dockerignore | 7 --
.../scripts/deploy/smoke-test.mjs | 92 -------------------
4 files changed, 112 deletions(-)
delete mode 100644 easyflow-ui-admin/scripts/deploy/Dockerfile.smoke
delete mode 100644 easyflow-ui-admin/scripts/deploy/Dockerfile.smoke.dockerignore
delete mode 100644 easyflow-ui-admin/scripts/deploy/smoke-test.mjs
diff --git a/easyflow-ui-admin/package.json b/easyflow-ui-admin/package.json
index 3c85f123..f0b3309d 100644
--- a/easyflow-ui-admin/package.json
+++ b/easyflow-ui-admin/package.json
@@ -34,7 +34,6 @@
"test:unit": "vitest run --dom",
"test:e2e": "turbo run test:e2e",
"update:deps": "npx taze -r -w",
- "verify:deployment": "pnpm run test:deployment-contract && pnpm run build:app && docker build --platform linux/amd64 -f scripts/deploy/Dockerfile.smoke -t easyflow-frontend-smoke:local . && cross-env EASYFLOW_FRONTEND_IMAGE=easyflow-frontend-smoke:local node scripts/deploy/smoke-test.mjs",
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile",
"catalog": "pnpx codemod pnpm/catalog"
},
diff --git a/easyflow-ui-admin/scripts/deploy/Dockerfile.smoke b/easyflow-ui-admin/scripts/deploy/Dockerfile.smoke
deleted file mode 100644
index 57ac023a..00000000
--- a/easyflow-ui-admin/scripts/deploy/Dockerfile.smoke
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:stable-alpine
-
-RUN rm -f /etc/nginx/conf.d/default.conf
-
-COPY app/dist/ /usr/share/nginx/html/flow/
-COPY scripts/deploy/nginx.conf /etc/nginx/nginx.conf
-
-RUN nginx -t
-
-EXPOSE 80
-
-CMD ["nginx", "-g", "daemon off;"]
diff --git a/easyflow-ui-admin/scripts/deploy/Dockerfile.smoke.dockerignore b/easyflow-ui-admin/scripts/deploy/Dockerfile.smoke.dockerignore
deleted file mode 100644
index 2b04591d..00000000
--- a/easyflow-ui-admin/scripts/deploy/Dockerfile.smoke.dockerignore
+++ /dev/null
@@ -1,7 +0,0 @@
-**
-!app/
-!app/dist/
-!app/dist/**
-!scripts/
-!scripts/deploy/
-!scripts/deploy/nginx.conf
diff --git a/easyflow-ui-admin/scripts/deploy/smoke-test.mjs b/easyflow-ui-admin/scripts/deploy/smoke-test.mjs
deleted file mode 100644
index 43f5250c..00000000
--- a/easyflow-ui-admin/scripts/deploy/smoke-test.mjs
+++ /dev/null
@@ -1,92 +0,0 @@
-import { execFileSync } from 'node:child_process';
-
-const image = process.env.EASYFLOW_FRONTEND_IMAGE || 'easyflow-frontend:0.1';
-const containerName = `easyflow-frontend-smoke-${process.pid}`;
-
-function docker(args) {
- return execFileSync('docker', args, {
- encoding: 'utf8',
- stdio: ['ignore', 'pipe', 'pipe'],
- }).trim();
-}
-
-async function waitForServer(origin) {
- let lastError;
- for (let attempt = 0; attempt < 40; attempt += 1) {
- try {
- const response = await fetch(`${origin}/flow/`);
- if (response.ok) {
- return;
- }
- lastError = new Error(`HTTP ${response.status}`);
- } catch (error) {
- lastError = error;
- }
- await new Promise((resolve) => setTimeout(resolve, 250));
- }
- throw new Error(`Nginx 未在预期时间内启动: ${String(lastError)}`);
-}
-
-function assert(condition, message) {
- if (!condition) {
- throw new Error(message);
- }
-}
-
-try {
- docker([
- 'run',
- '--detach',
- '--rm',
- '--publish',
- '127.0.0.1::80',
- '--name',
- containerName,
- image,
- ]);
-
- const portBinding = docker(['port', containerName, '80/tcp'])
- .split('\n')
- .find((line) => line.startsWith('127.0.0.1:'));
- const port = portBinding?.match(/:(\d+)$/)?.[1];
- assert(port, `无法解析容器端口: ${portBinding || 'empty'}`);
-
- const origin = `http://127.0.0.1:${port}`;
- await waitForServer(origin);
-
- const rootResponse = await fetch(`${origin}/`, { redirect: 'manual' });
- assert(rootResponse.status === 302, `根路径状态异常: ${rootResponse.status}`);
- const rootLocation = rootResponse.headers.get('location');
- assert(
- rootLocation && new URL(rootLocation, origin).pathname === '/flow/',
- `根路径重定向异常: ${rootLocation}`,
- );
-
- const flowResponse = await fetch(`${origin}/flow/`);
- const indexHtml = await flowResponse.text();
- assert(flowResponse.ok, `/flow/ 状态异常: ${flowResponse.status}`);
- assert(indexHtml.includes('
'), '/flow/ 未返回应用入口');
-
- const assetPath = indexHtml.match(/(?:src|href)="(\/flow\/[^"]+)"/)?.[1];
- assert(assetPath, '构建产物未使用 /flow/ 静态资源前缀');
- const assetResponse = await fetch(`${origin}${assetPath}`);
- assert(assetResponse.ok, `静态资源状态异常: ${assetResponse.status}`);
-
- const invalidRootShareResponse = await fetch(
- `${origin}/share/knowledge?shareKey=smoke-test`,
- );
- assert(
- invalidRootShareResponse.status === 404,
- `根路径分享地址应返回 404,实际为 ${invalidRootShareResponse.status}`,
- );
-
- console.log(
- '部署冒烟通过:根路径重定向、/flow/ 入口、静态资源前缀和错误分享路径均符合预期。',
- );
-} finally {
- try {
- docker(['rm', '--force', containerName]);
- } catch {
- // 容器可能因 --rm 已自动删除。
- }
-}
From dc7e46260b7df3f0317007ab084ab2572034bf0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=AD=90=E9=BB=98?= <925456043@qq.com>
Date: Mon, 27 Jul 2026 15:47:58 +0800
Subject: [PATCH 033/143] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20system=20?=
=?UTF-8?q?=E6=B6=88=E6=81=AF=E6=A0=BC=E5=BC=8F=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 模型管理支持选择字符串或内容块数组格式
- 验证连接携带 system 消息并复用正式运行时模型配置
- 补充配置映射与验证消息角色测试
---
.../agent/runtime/AgentModelSpecMapper.java | 25 +++++++++++++++
...entScopeChatModelConnectivityVerifier.java | 10 ++++--
.../AgentRuntimeCompilerModelConfigTest.java | 31 +++++++++++++++++++
...copeChatModelConnectivityVerifierTest.java | 16 +++++++++-
.../app/src/views/ai/model/AddModelModal.vue | 30 ++++++++++++++++++
5 files changed, 109 insertions(+), 3 deletions(-)
diff --git a/easyflow-modules/easyflow-module-agent/src/main/java/tech/easyflow/agent/runtime/AgentModelSpecMapper.java b/easyflow-modules/easyflow-module-agent/src/main/java/tech/easyflow/agent/runtime/AgentModelSpecMapper.java
index b6ca972b..3578f141 100644
--- a/easyflow-modules/easyflow-module-agent/src/main/java/tech/easyflow/agent/runtime/AgentModelSpecMapper.java
+++ b/easyflow-modules/easyflow-module-agent/src/main/java/tech/easyflow/agent/runtime/AgentModelSpecMapper.java
@@ -3,6 +3,7 @@ package tech.easyflow.agent.runtime;
import com.easyagents.agent.runtime.model.AgentHttpVersionPolicy;
import com.easyagents.agent.runtime.model.AgentModelProviderType;
import com.easyagents.agent.runtime.model.AgentModelSpec;
+import com.easyagents.agent.runtime.model.AgentSystemContentFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.easyflow.ai.entity.Model;
@@ -60,6 +61,7 @@ public final class AgentModelSpecMapper {
spec.setSupportImage(Boolean.TRUE.equals(model.getSupportImage()));
spec.setSupportImageBase64Only(Boolean.TRUE.equals(model.getSupportImageB64Only()));
spec.setHttpVersionPolicy(parseHttpVersionPolicy(model));
+ spec.setSystemContentFormat(parseSystemContentFormat(model));
spec.getMetadata().put("modelId", model.getId());
if (providerType != null && !providerType.isBlank()) {
spec.getMetadata().put("sourceProviderType", providerType);
@@ -90,6 +92,29 @@ public final class AgentModelSpecMapper {
}
}
+ /**
+ * 解析模型配置中的 Agent system content 格式。
+ *
+ * @param model 模型配置
+ * @return system content 格式,缺失或非法时返回 STRING
+ */
+ private static AgentSystemContentFormat parseSystemContentFormat(Model model) {
+ Object rawFormat = model.getOptions() == null
+ ? null
+ : model.getOptions().get("agentSystemContentFormat");
+ if (rawFormat == null || String.valueOf(rawFormat).isBlank()) {
+ return AgentSystemContentFormat.STRING;
+ }
+ String normalizedFormat = String.valueOf(rawFormat).trim().toUpperCase(Locale.ROOT);
+ try {
+ return AgentSystemContentFormat.valueOf(normalizedFormat);
+ } catch (IllegalArgumentException exception) {
+ LOG.warn("Invalid Agent system content format '{}' for model {}, fallback to STRING",
+ rawFormat, model.getId());
+ return AgentSystemContentFormat.STRING;
+ }
+ }
+
/**
* 解析 AgentScope 支持的模型供应商类型。
*
diff --git a/easyflow-modules/easyflow-module-agent/src/main/java/tech/easyflow/agent/runtime/AgentScopeChatModelConnectivityVerifier.java b/easyflow-modules/easyflow-module-agent/src/main/java/tech/easyflow/agent/runtime/AgentScopeChatModelConnectivityVerifier.java
index 4fc51518..52a0187f 100644
--- a/easyflow-modules/easyflow-module-agent/src/main/java/tech/easyflow/agent/runtime/AgentScopeChatModelConnectivityVerifier.java
+++ b/easyflow-modules/easyflow-module-agent/src/main/java/tech/easyflow/agent/runtime/AgentScopeChatModelConnectivityVerifier.java
@@ -43,6 +43,8 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
/** 为未识别关闭思考扩展参数的推理模型保留足够的小额输出预算。 */
private static final int MAX_TOKENS = 256;
private static final Duration DEFAULT_PHASE_TIMEOUT = Duration.ofSeconds(60);
+ private static final String VERIFICATION_SYSTEM_PROMPT =
+ "You are verifying model connectivity. Follow the user's instruction exactly.";
/** AgentScope 模型工厂。 */
private final AgentModelFactory modelFactory;
@@ -128,7 +130,11 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
generationOptions.setMaxTokens(MAX_TOKENS);
io.agentscope.core.model.Model agentScopeModel = modelFactory.create(modelSpec, generationOptions);
- Msg message = messageAdapter.toMsg(verificationMessage);
+ List messages = List.of(
+ messageAdapter.toMsg(AgentMessage.text(
+ AgentMessageRole.SYSTEM,
+ VERIFICATION_SYSTEM_PROMPT)),
+ messageAdapter.toMsg(verificationMessage));
GenerateOptions requestOptions = GenerateOptions.builder()
.stream(stream)
.maxTokens(MAX_TOKENS)
@@ -138,7 +144,7 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
.build())
.build();
List responses = agentScopeModel
- .stream(List.of(message), List.of(), requestOptions)
+ .stream(messages, List.of(), requestOptions)
.timeout(phaseTimeout)
.collectList()
.block(phaseTimeout.plusSeconds(1));
diff --git a/easyflow-modules/easyflow-module-agent/src/test/java/tech/easyflow/agent/runtime/AgentRuntimeCompilerModelConfigTest.java b/easyflow-modules/easyflow-module-agent/src/test/java/tech/easyflow/agent/runtime/AgentRuntimeCompilerModelConfigTest.java
index 00a5e196..34d3a925 100644
--- a/easyflow-modules/easyflow-module-agent/src/test/java/tech/easyflow/agent/runtime/AgentRuntimeCompilerModelConfigTest.java
+++ b/easyflow-modules/easyflow-module-agent/src/test/java/tech/easyflow/agent/runtime/AgentRuntimeCompilerModelConfigTest.java
@@ -4,6 +4,7 @@ import com.easyagents.agent.runtime.memory.AgentMemoryPolicy;
import com.easyagents.agent.runtime.model.AgentGenerationOptions;
import com.easyagents.agent.runtime.model.AgentHttpVersionPolicy;
import com.easyagents.agent.runtime.model.AgentModelSpec;
+import com.easyagents.agent.runtime.model.AgentSystemContentFormat;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.agent.entity.Agent;
@@ -66,6 +67,36 @@ public class AgentRuntimeCompilerModelConfigTest {
Assert.assertEquals(AgentHttpVersionPolicy.AUTO, spec.getHttpVersionPolicy());
}
+ /**
+ * 验证模型 options 中的 system content 格式会编译到中立模型声明。
+ *
+ * @throws Exception 反射调用失败时抛出
+ */
+ @Test
+ public void modelSystemContentFormatShouldCompileFromOptions() throws Exception {
+ Model model = model(Map.of("agentSystemContentFormat", "TEXT_PARTS"));
+ AgentRuntimeCompiler compiler = compiler(model);
+
+ AgentModelSpec spec = invokeModelSpec(compiler);
+
+ Assert.assertEquals(AgentSystemContentFormat.TEXT_PARTS, spec.getSystemContentFormat());
+ }
+
+ /**
+ * 验证未知 system content 格式安全回退到字符串。
+ *
+ * @throws Exception 反射调用失败时抛出
+ */
+ @Test
+ public void unknownSystemContentFormatShouldFallbackToString() throws Exception {
+ Model model = model(Map.of("agentSystemContentFormat", "PARTS"));
+ AgentRuntimeCompiler compiler = compiler(model);
+
+ AgentModelSpec spec = invokeModelSpec(compiler);
+
+ Assert.assertEquals(AgentSystemContentFormat.STRING, spec.getSystemContentFormat());
+ }
+
/**
* 验证 EasyFlow 忽略新旧消息数阈值,仅保留 Token 压缩配置。
*
diff --git a/easyflow-modules/easyflow-module-agent/src/test/java/tech/easyflow/agent/runtime/AgentScopeChatModelConnectivityVerifierTest.java b/easyflow-modules/easyflow-module-agent/src/test/java/tech/easyflow/agent/runtime/AgentScopeChatModelConnectivityVerifierTest.java
index 961f3f7b..26b90cc2 100644
--- a/easyflow-modules/easyflow-module-agent/src/test/java/tech/easyflow/agent/runtime/AgentScopeChatModelConnectivityVerifierTest.java
+++ b/easyflow-modules/easyflow-module-agent/src/test/java/tech/easyflow/agent/runtime/AgentScopeChatModelConnectivityVerifierTest.java
@@ -7,6 +7,7 @@ import com.easyagents.agent.runtime.model.AgentModelSpec;
import io.agentscope.core.message.Base64Source;
import io.agentscope.core.message.ImageBlock;
import io.agentscope.core.message.Msg;
+import io.agentscope.core.message.MsgRole;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.model.ChatResponse;
import io.agentscope.core.model.GenerateOptions;
@@ -48,6 +49,7 @@ public class AgentScopeChatModelConnectivityVerifierTest {
Assert.assertEquals("HTTP_1_1", result.getEffectiveHttpVersion());
Assert.assertEquals(List.of(false, true), factory.getFactoryStreams());
Assert.assertEquals(List.of(false, true), factory.getRequestStreams());
+ Assert.assertEquals(List.of(MsgRole.SYSTEM, MsgRole.USER), factory.getMessageRoles().get(0));
Assert.assertEquals(List.of(false, false), factory.getEnableThinkingValues());
Assert.assertEquals(List.of(false, false), factory.getChatTemplateThinkingValues());
}
@@ -190,6 +192,8 @@ public class AgentScopeChatModelConnectivityVerifierTest {
private final List requestStreams = new ArrayList<>();
/** 模型请求收到的消息。 */
private final List messages = new ArrayList<>();
+ /** 各阶段请求消息的角色顺序。 */
+ private final List> messageRoles = new ArrayList<>();
/** OpenAI-compatible 请求中的思考开关。 */
private final List
From aedefe6b5e0fefb3256adb5aa468873ab62358f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=AD=90=E9=BB=98?= <925456043@qq.com>
Date: Mon, 27 Jul 2026 18:27:01 +0800
Subject: [PATCH 034/143] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=99=BA?=
=?UTF-8?q?=E8=83=BD=E4=BD=93=E4=B8=8E=E5=B7=A5=E4=BD=9C=E6=B5=81=E5=B9=95?=
=?UTF-8?q?=E5=B8=83=E6=B8=B2=E6=9F=93=E6=80=A7=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 分阶段加载智能体配置并按需缓存 MCP 工具
- 合并画布状态更新与节点尺寸监听,启用大图可视区域渲染和静态连线
- 隔离 Tinyflow Store 实例并补充数据同步与回归测试
---
.../app/src/views/ai/agents/AgentDesigner.vue | 248 ++++++++++-----
.../app/src/views/ai/agents/api.ts | 10 +
.../agents/components/AgentInspectorPanel.vue | 79 ++++-
.../ai/agents/components/AgentToolForm.vue | 7 +-
.../agent-studio/AgentStudioCanvas.vue | 285 +++++++++++-------
.../agent-studio/AgentStudioEdgeLayer.vue | 25 +-
.../agent-studio/useAgentStudioLayout.ts | 10 +-
.../agent-studio/useAgentStudioModel.test.ts | 41 +++
.../agent-studio/useAgentStudioModel.ts | 75 +++--
.../src/views/ai/agents/mcpToolLoader.test.ts | 39 +++
.../app/src/views/ai/agents/mcpToolLoader.ts | 35 +++
.../src/views/ai/workflow/WorkflowDesign.vue | 28 +-
.../packages/tinyflow-ui/src/Tinyflow.ts | 57 ++--
.../src/components/TinyflowComponent.svelte | 16 +-
.../src/components/TinyflowCore.svelte | 267 +++++++++++++---
.../core/DefinedParameterItem.svelte | 3 +-
.../src/components/core/NodeWrapper.svelte | 34 +--
.../src/components/nodes/ConditionNode.svelte | 3 +-
.../src/components/nodes/CustomNode.svelte | 6 +-
.../src/components/utils/nodeSizeObserver.ts | 16 +
.../components/utils/useCopyNode.svelte.ts | 3 +-
.../utils/useCopyPasteHandler.svelte.ts | 3 +-
.../components/utils/useDeleteEdge.svelte.ts | 3 +-
.../components/utils/useDeleteNode.svelte.ts | 3 +-
.../useEnsureParentInNodesBefore.svelte.ts | 3 +-
.../utils/useGetEdgesBySource.svelte.ts | 3 +-
.../utils/useGetEdgesByTarget.svelte.ts | 3 +-
.../src/components/utils/useGetNode.svelte.ts | 3 +-
.../useGetNodeRelativePosition.svelte.ts | 3 +-
.../utils/useGetNodesFromSource.svelte.ts | 3 +-
.../utils/useUpdateEdgeData.svelte.ts | 3 +-
.../tinyflow-ui/src/store/stores.svelte.ts | 20 +-
.../src/tinyflow-store-isolation.test.ts | 88 ++++++
.../packages/tinyflow-ui/src/types.ts | 10 +
.../src/utils/nodeInteraction.test.ts | 49 +++
.../tinyflow-ui/src/utils/nodeInteraction.ts | 28 +-
.../packages/tinyflow-vue/src/Tinyflow.vue | 83 ++---
.../tinyflow-vue/src/dataSync.test.ts | 76 +++++
.../packages/tinyflow-vue/src/dataSync.ts | 86 ++++++
39 files changed, 1349 insertions(+), 408 deletions(-)
create mode 100644 easyflow-ui-admin/app/src/views/ai/agents/mcpToolLoader.test.ts
create mode 100644 easyflow-ui-admin/app/src/views/ai/agents/mcpToolLoader.ts
create mode 100644 easyflow-ui-admin/packages/tinyflow-ui/src/components/utils/nodeSizeObserver.ts
create mode 100644 easyflow-ui-admin/packages/tinyflow-ui/src/tinyflow-store-isolation.test.ts
create mode 100644 easyflow-ui-admin/packages/tinyflow-ui/src/utils/nodeInteraction.test.ts
create mode 100644 easyflow-ui-admin/packages/tinyflow-vue/src/dataSync.test.ts
create mode 100644 easyflow-ui-admin/packages/tinyflow-vue/src/dataSync.ts
diff --git a/easyflow-ui-admin/app/src/views/ai/agents/AgentDesigner.vue b/easyflow-ui-admin/app/src/views/ai/agents/AgentDesigner.vue
index 141d141d..dffb17c9 100644
--- a/easyflow-ui-admin/app/src/views/ai/agents/AgentDesigner.vue
+++ b/easyflow-ui-admin/app/src/views/ai/agents/AgentDesigner.vue
@@ -6,7 +6,7 @@ import type {
AgentValidationIssue,
} from './types';
-import { computed, onMounted, ref } from 'vue';
+import { computed, onActivated, onDeactivated, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ElMessage, ElMessageBox } from 'element-plus';
@@ -23,6 +23,8 @@ import {
import {
getAgentDetail,
getAgentModels,
+ getMcpPage,
+ getMcpTools,
getPublishedKnowledgeList,
saveAgent,
submitAgentOfflineApproval,
@@ -35,6 +37,7 @@ import AgentStudioCanvas from './components/agent-studio/AgentStudioCanvas.vue';
import AgentCommandBar from './components/AgentCommandBar.vue';
import AgentInspectorPanel from './components/AgentInspectorPanel.vue';
import { useAgentDesignerState } from './composables/useAgentDesignerState';
+import { createMcpToolLoader } from './mcpToolLoader';
const route = useRoute();
const router = useRouter();
@@ -56,10 +59,13 @@ const {
validate,
} = useAgentDesignerState();
-const pageLoading = ref(false);
+const pageLoading = ref(true);
+const contentReady = ref(false);
+const canvasActive = ref(true);
const saveLoading = ref(false);
const offlineLoading = ref(false);
const publishLoading = ref(false);
+const mcpToolsLoading = ref