From 60c4416d0ffd68db406c37e7099f5f59487ffc11 Mon Sep 17 00:00:00 2001
From: Albumen Kevin
Date: Fri, 1 Aug 2025 22:37:28 +0800
Subject: [PATCH 1/5] feat(jmanus): move tool parameter and description to
resources
---
.../prompt/model/enums/PromptEnum.java | 86 +++++-
.../prompt/model/enums/PromptType.java | 2 +-
.../manus/planning/PlanningFactory.java | 25 +-
.../ai/example/manus/tool/DocLoaderTool.java | 48 ++--
.../ai/example/manus/tool/TerminateTool.java | 18 +-
.../example/manus/tool/ToolPromptManager.java | 140 ++++++++++
.../ai/example/manus/tool/bash/Bash.java | 40 +--
.../manus/tool/browser/BrowserUseTool.java | 246 +-----------------
.../manus/tool/code/PythonExecute.java | 43 +--
.../ai/example/manus/tool/cron/CronTool.java | 41 +--
.../manus/tool/database/DatabaseUseTool.java | 86 +-----
.../tool/innerStorage/FileMergeTool.java | 44 +---
.../innerStorage/InnerStorageContentTool.java | 88 +------
.../tool/textOperator/TextFileOperator.java | 143 +---------
.../prompts/en/descriptions.properties | 30 +++
.../prompts/en/tool/bash-tool-description.txt | 4 +
.../prompts/en/tool/bash-tool-parameters.txt | 10 +
.../en/tool/browser-use-tool-description.txt | 20 ++
.../en/tool/browser-use-tool-parameters.txt | 218 ++++++++++++++++
.../en/tool/cron-tool-tool-description.txt | 1 +
.../en/tool/cron-tool-tool-parameters.txt | 18 ++
.../en/tool/data-split-tool-description.txt | 9 +
.../en/tool/data-split-tool-parameters.txt | 18 ++
.../en/tool/database-use-tool-description.txt | 6 +
.../en/tool/database-use-tool-parameters.txt | 53 ++++
.../en/tool/doc-loader-tool-description.txt | 3 +
.../en/tool/doc-loader-tool-parameters.txt | 14 +
.../en/tool/file-merge-tool-description.txt | 2 +
.../en/tool/file-merge-tool-parameters.txt | 20 ++
.../en/tool/finalize-tool-description.txt | 9 +
.../en/tool/finalize-tool-parameters.txt | 15 ++
...-storage-content-tool-tool-description.txt | 6 +
...r-storage-content-tool-tool-parameters.txt | 58 +++++
.../en/tool/map-output-tool-description.txt | 10 +
.../en/tool/map-output-tool-parameters.txt | 23 ++
.../tool/python-execute-tool-description.txt | 1 +
.../tool/python-execute-tool-parameters.txt | 10 +
.../reduce-operation-tool-description.txt | 11 +
.../tool/reduce-operation-tool-parameters.txt | 19 ++
.../en/tool/terminate-tool-description.txt | 2 +
.../en/tool/terminate-tool-parameters.txt | 24 ++
.../textfileoperator-tool-description.txt | 18 ++
.../tool/textfileoperator-tool-parameters.txt | 84 ++++++
.../prompts/zh/descriptions.properties | 30 +++
.../prompts/zh/tool/bash-tool-description.txt | 4 +
.../prompts/zh/tool/bash-tool-parameters.txt | 10 +
.../zh/tool/browser-use-tool-description.txt | 20 ++
.../zh/tool/browser-use-tool-parameters.txt | 218 ++++++++++++++++
.../zh/tool/cron-tool-tool-description.txt | 1 +
.../zh/tool/cron-tool-tool-parameters.txt | 18 ++
.../zh/tool/data-split-tool-description.txt | 9 +
.../zh/tool/data-split-tool-parameters.txt | 18 ++
.../zh/tool/database-use-tool-description.txt | 6 +
.../zh/tool/database-use-tool-parameters.txt | 53 ++++
.../zh/tool/doc-loader-tool-description.txt | 3 +
.../zh/tool/doc-loader-tool-parameters.txt | 14 +
.../zh/tool/file-merge-tool-description.txt | 2 +
.../zh/tool/file-merge-tool-parameters.txt | 20 ++
.../zh/tool/finalize-tool-description.txt | 9 +
.../zh/tool/finalize-tool-parameters.txt | 15 ++
...-storage-content-tool-tool-description.txt | 6 +
...r-storage-content-tool-tool-parameters.txt | 58 +++++
.../zh/tool/map-output-tool-description.txt | 10 +
.../zh/tool/map-output-tool-parameters.txt | 23 ++
.../tool/python-execute-tool-description.txt | 1 +
.../tool/python-execute-tool-parameters.txt | 10 +
.../reduce-operation-tool-description.txt | 11 +
.../tool/reduce-operation-tool-parameters.txt | 19 ++
.../zh/tool/terminate-tool-description.txt | 2 +
.../zh/tool/terminate-tool-parameters.txt | 24 ++
.../textfileoperator-tool-description.txt | 18 ++
.../tool/textfileoperator-tool-parameters.txt | 84 ++++++
72 files changed, 1768 insertions(+), 714 deletions(-)
create mode 100644 spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/ToolPromptManager.java
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/bash-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/bash-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/browser-use-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/browser-use-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/cron-tool-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/cron-tool-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/data-split-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/data-split-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/database-use-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/database-use-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/doc-loader-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/doc-loader-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/file-merge-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/file-merge-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/finalize-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/finalize-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/inner-storage-content-tool-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/inner-storage-content-tool-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/map-output-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/map-output-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/python-execute-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/python-execute-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/reduce-operation-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/reduce-operation-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/terminate-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/terminate-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/textfileoperator-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/en/tool/textfileoperator-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/bash-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/bash-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/browser-use-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/browser-use-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/cron-tool-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/cron-tool-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/data-split-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/data-split-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/database-use-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/database-use-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/doc-loader-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/doc-loader-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/file-merge-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/file-merge-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/finalize-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/finalize-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/inner-storage-content-tool-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/inner-storage-content-tool-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/map-output-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/map-output-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/python-execute-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/python-execute-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/reduce-operation-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/reduce-operation-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/terminate-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/terminate-tool-parameters.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/textfileoperator-tool-description.txt
create mode 100644 spring-ai-alibaba-jmanus/src/main/resources/prompts/zh/tool/textfileoperator-tool-parameters.txt
diff --git a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/dynamic/prompt/model/enums/PromptEnum.java b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/dynamic/prompt/model/enums/PromptEnum.java
index 888a90f576..5340853e77 100644
--- a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/dynamic/prompt/model/enums/PromptEnum.java
+++ b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/dynamic/prompt/model/enums/PromptEnum.java
@@ -43,7 +43,91 @@ public enum PromptEnum {
FORM_INPUT_TOOL_DESCRIPTION("FORM_INPUT_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.AGENT, true,
"tool/form-input-tool-description.txt"),
FORM_INPUT_TOOL_PARAMETERS("FORM_INPUT_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.AGENT, true,
- "tool/form-input-tool-parameters.txt");
+ "tool/form-input-tool-parameters.txt"),
+
+ // Bash Tool
+ BASH_TOOL_DESCRIPTION("BASH_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION, true,
+ "tool/bash-tool-description.txt"),
+ BASH_TOOL_PARAMETERS("BASH_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/bash-tool-parameters.txt"),
+
+ // Text File Operator Tool
+ TEXTFILEOPERATOR_TOOL_DESCRIPTION("TEXTFILEOPERATOR_TOOL_DESCRIPTION", MessageType.SYSTEM,
+ PromptType.TOOL_DESCRIPTION, true, "tool/textfileoperator-tool-description.txt"),
+ TEXTFILEOPERATOR_TOOL_PARAMETERS("TEXTFILEOPERATOR_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER,
+ true, "tool/textfileoperator-tool-parameters.txt"),
+
+ // Browser Use Tool
+ BROWSER_USE_TOOL_DESCRIPTION("BROWSER_USE_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION, true,
+ "tool/browser-use-tool-description.txt"),
+ BROWSER_USE_TOOL_PARAMETERS("BROWSER_USE_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/browser-use-tool-parameters.txt"),
+
+ // Python Execute Tool
+ PYTHON_EXECUTE_TOOL_DESCRIPTION("PYTHON_EXECUTE_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION,
+ true, "tool/python-execute-tool-description.txt"),
+ PYTHON_EXECUTE_TOOL_PARAMETERS("PYTHON_EXECUTE_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER,
+ true, "tool/python-execute-tool-parameters.txt"),
+
+ // Database Use Tool
+ DATABASE_USE_TOOL_DESCRIPTION("DATABASE_USE_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION,
+ true, "tool/database-use-tool-description.txt"),
+ DATABASE_USE_TOOL_PARAMETERS("DATABASE_USE_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/database-use-tool-parameters.txt"),
+
+ // Cron Tool
+ CRON_TOOL_TOOL_DESCRIPTION("CRON_TOOL_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION, true,
+ "tool/cron-tool-tool-description.txt"),
+ CRON_TOOL_TOOL_PARAMETERS("CRON_TOOL_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/cron-tool-tool-parameters.txt"),
+
+ // Inner Storage Content Tool
+ INNER_STORAGE_CONTENT_TOOL_TOOL_DESCRIPTION("INNER_STORAGE_CONTENT_TOOL_TOOL_DESCRIPTION", MessageType.SYSTEM,
+ PromptType.TOOL_DESCRIPTION, true, "tool/inner-storage-content-tool-tool-description.txt"),
+ INNER_STORAGE_CONTENT_TOOL_TOOL_PARAMETERS("INNER_STORAGE_CONTENT_TOOL_TOOL_PARAMETERS", MessageType.SYSTEM,
+ PromptType.TOOL_PARAMETER, true, "tool/inner-storage-content-tool-tool-parameters.txt"),
+
+ // Doc Loader Tool
+ DOC_LOADER_TOOL_DESCRIPTION("DOC_LOADER_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION, true,
+ "tool/doc-loader-tool-description.txt"),
+ DOC_LOADER_TOOL_PARAMETERS("DOC_LOADER_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/doc-loader-tool-parameters.txt"),
+
+ // File Merge Tool
+ FILE_MERGE_TOOL_DESCRIPTION("FILE_MERGE_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION, true,
+ "tool/file-merge-tool-description.txt"),
+ FILE_MERGE_TOOL_PARAMETERS("FILE_MERGE_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/file-merge-tool-parameters.txt"),
+
+ // Data Split Tool
+ DATA_SPLIT_TOOL_DESCRIPTION("DATA_SPLIT_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION, true,
+ "tool/data-split-tool-description.txt"),
+ DATA_SPLIT_TOOL_PARAMETERS("DATA_SPLIT_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/data-split-tool-parameters.txt"),
+
+ // Map Output Tool
+ MAP_OUTPUT_TOOL_DESCRIPTION("MAP_OUTPUT_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION, true,
+ "tool/map-output-tool-description.txt"),
+ MAP_OUTPUT_TOOL_PARAMETERS("MAP_OUTPUT_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/map-output-tool-parameters.txt"),
+
+ // Reduce Operation Tool
+ REDUCE_OPERATION_TOOL_DESCRIPTION("REDUCE_OPERATION_TOOL_DESCRIPTION", MessageType.SYSTEM,
+ PromptType.TOOL_DESCRIPTION, true, "tool/reduce-operation-tool-description.txt"),
+ REDUCE_OPERATION_TOOL_PARAMETERS("REDUCE_OPERATION_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER,
+ true, "tool/reduce-operation-tool-parameters.txt"),
+
+ // Finalize Tool
+ FINALIZE_TOOL_DESCRIPTION("FINALIZE_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION, true,
+ "tool/finalize-tool-description.txt"),
+ FINALIZE_TOOL_PARAMETERS("FINALIZE_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/finalize-tool-parameters.txt"),
+
+ // Terminate Tool
+ TERMINATE_TOOL_DESCRIPTION("TERMINATE_TOOL_DESCRIPTION", MessageType.SYSTEM, PromptType.TOOL_DESCRIPTION, true,
+ "tool/terminate-tool-description.txt"),
+ TERMINATE_TOOL_PARAMETERS("TERMINATE_TOOL_PARAMETERS", MessageType.SYSTEM, PromptType.TOOL_PARAMETER, true,
+ "tool/terminate-tool-parameters.txt");
private String promptName;
diff --git a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/dynamic/prompt/model/enums/PromptType.java b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/dynamic/prompt/model/enums/PromptType.java
index 5f9f342006..99e008ebcd 100644
--- a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/dynamic/prompt/model/enums/PromptType.java
+++ b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/dynamic/prompt/model/enums/PromptType.java
@@ -17,6 +17,6 @@
public enum PromptType {
- LLM, PLANNING, AGENT
+ LLM, PLANNING, AGENT, TOOL_DESCRIPTION, TOOL_PARAMETER
}
diff --git a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/planning/PlanningFactory.java b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/planning/PlanningFactory.java
index 99a04525b3..334c55d2cd 100644
--- a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/planning/PlanningFactory.java
+++ b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/planning/PlanningFactory.java
@@ -22,6 +22,7 @@
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import com.alibaba.cloud.ai.example.manus.tool.ToolPromptManager;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.HttpClients;
@@ -142,6 +143,9 @@ public class PlanningFactory implements IPlanningFactory {
@Autowired
private StreamingResponseHandler streamingResponseHandler;
+ @Autowired
+ private ToolPromptManager toolPromptManager;
+
@Autowired
@Lazy
private CronService cronService;
@@ -214,15 +218,18 @@ public Map toolCallbackMap(String planId, String ro
return toolCallbackMap;
}
// Add all tool definitions
- toolDefinitions.add(BrowserUseTool.getInstance(chromeDriverService, innerStorageService, objectMapper));
- toolDefinitions.add(DatabaseUseTool.getInstance(dataSourceService, objectMapper));
- toolDefinitions.add(new TerminateTool(planId, terminateColumns));
- toolDefinitions.add(new Bash(unifiedDirectoryManager, objectMapper));
- toolDefinitions.add(new DocLoaderTool());
- toolDefinitions.add(new TextFileOperator(textFileService, innerStorageService, objectMapper));
+ toolDefinitions
+ .add(BrowserUseTool.getInstance(chromeDriverService, innerStorageService, objectMapper, toolPromptManager));
+ toolDefinitions.add(DatabaseUseTool.getInstance(dataSourceService, objectMapper, toolPromptManager));
+ toolDefinitions.add(new TerminateTool(planId, terminateColumns, toolPromptManager));
+ toolDefinitions.add(new Bash(unifiedDirectoryManager, objectMapper, toolPromptManager));
+ toolDefinitions.add(new DocLoaderTool(toolPromptManager));
+ toolDefinitions
+ .add(new TextFileOperator(textFileService, innerStorageService, objectMapper, toolPromptManager));
// toolDefinitions.add(new InnerStorageTool(unifiedDirectoryManager));
- toolDefinitions.add(new InnerStorageContentTool(unifiedDirectoryManager, summaryWorkflow, recorder));
- toolDefinitions.add(new FileMergeTool(unifiedDirectoryManager));
+ toolDefinitions
+ .add(new InnerStorageContentTool(unifiedDirectoryManager, summaryWorkflow, recorder, toolPromptManager));
+ toolDefinitions.add(new FileMergeTool(unifiedDirectoryManager, toolPromptManager));
// toolDefinitions.add(new GoogleSearch());
// toolDefinitions.add(new PythonExecute());
toolDefinitions.add(new FormInputTool(objectMapper, promptService));
@@ -233,7 +240,7 @@ public Map toolCallbackMap(String planId, String ro
toolDefinitions.add(new ReduceOperationTool(planId, manusProperties, sharedStateManager,
unifiedDirectoryManager, terminateColumns));
toolDefinitions.add(new FinalizeTool(planId, manusProperties, sharedStateManager, unifiedDirectoryManager));
- toolDefinitions.add(new CronTool(cronService, objectMapper));
+ toolDefinitions.add(new CronTool(cronService, objectMapper, toolPromptManager));
List functionCallbacks = mcpService.getFunctionCallbacks(planId);
for (McpServiceEntity toolCallback : functionCallbacks) {
diff --git a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/DocLoaderTool.java b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/DocLoaderTool.java
index 00912c1ecc..d68f82140f 100644
--- a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/DocLoaderTool.java
+++ b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/DocLoaderTool.java
@@ -16,6 +16,7 @@
package com.alibaba.cloud.ai.example.manus.tool;
import com.alibaba.cloud.ai.example.manus.tool.code.ToolExecuteResult;
+import com.alibaba.cloud.ai.example.manus.tool.ToolPromptManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
@@ -68,33 +69,14 @@ public void setFilePath(String filePath) {
}
- private static String PARAMETERS = """
- {
- "type": "object",
- "properties": {
- "file_type": {
- "type": "string",
- "description": "(required) File type, only support pdf file."
- },
- "file_path": {
- "type": "string",
- "description": "(required) Get the absolute path of the file from the user request."
- }
- },
- "required": ["file_type","file_path"]
- }
- """;
-
private static final String name = "doc_loader";
- private static final String description = """
- Get the content information of a local file at a specified path.
- Use this tool when you want to get some related information asked by the user.
- This tool accepts the file path and gets the related information content.
- """;
+ private final ToolPromptManager toolPromptManager;
- public static OpenAiApi.FunctionTool getToolDefinition() {
- OpenAiApi.FunctionTool.Function function = new OpenAiApi.FunctionTool.Function(description, name, PARAMETERS);
+ public OpenAiApi.FunctionTool getToolDefinition() {
+ String description = getDescription();
+ String parameters = getParameters();
+ OpenAiApi.FunctionTool.Function function = new OpenAiApi.FunctionTool.Function(description, name, parameters);
OpenAiApi.FunctionTool functionTool = new OpenAiApi.FunctionTool(function);
return functionTool;
}
@@ -102,17 +84,21 @@ public static OpenAiApi.FunctionTool getToolDefinition() {
/**
* Get FunctionToolCallback for Spring AI
*/
- public static FunctionToolCallback getFunctionToolCallback() {
+ public static FunctionToolCallback getFunctionToolCallback(
+ ToolPromptManager toolPromptManager) {
return FunctionToolCallback
.builder(name,
- (DocLoaderInput input, org.springframework.ai.chat.model.ToolContext context) -> new DocLoaderTool()
- .run(input))
- .description(description)
+ (DocLoaderInput input,
+ org.springframework.ai.chat.model.ToolContext context) -> new DocLoaderTool(
+ toolPromptManager)
+ .run(input))
+ .description(toolPromptManager.getToolDescription("doc_loader"))
.inputType(DocLoaderInput.class)
.build();
}
- public DocLoaderTool() {
+ public DocLoaderTool(ToolPromptManager toolPromptManager) {
+ this.toolPromptManager = toolPromptManager;
}
private String lastFilePath = "";
@@ -161,12 +147,12 @@ public String getName() {
@Override
public String getDescription() {
- return description;
+ return toolPromptManager.getToolDescription("doc_loader");
}
@Override
public String getParameters() {
- return PARAMETERS;
+ return toolPromptManager.getToolParameters("doc_loader");
}
@Override
diff --git a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/TerminateTool.java b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/TerminateTool.java
index cd502e9d88..5ea4d1da40 100644
--- a/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/TerminateTool.java
+++ b/spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/TerminateTool.java
@@ -16,6 +16,7 @@
package com.alibaba.cloud.ai.example.manus.tool;
import com.alibaba.cloud.ai.example.manus.tool.code.ToolExecuteResult;
+import com.alibaba.cloud.ai.example.manus.tool.ToolPromptManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,25 +33,21 @@ public class TerminateTool extends AbstractBaseTool
"),a.includes("
")&&(a=`
${a}
`),a},I=async t=>{var a;if(!((a=t.planExecution)!=null&&a.currentPlanId)||!t.planExecution.userInputWaitState){console.error("[ChatComponent] Missing planExecution.currentPlanId or userInputWaitState");return}try{const r={},p=t.planExecution.userInputWaitState.formInputs;p&&p.length>0?Object.entries(G[t.id]).forEach(([N,Z])=>{var M;const j=parseInt(N,10),H=((M=p[j])==null?void 0:M.label)||`input_${N}`;r[H]=Z}):r.genericInput=t.genericInput??"",console.log("[ChatComponent] Submitting user input:",r);const W=await Fe.submitFormInput(t.planExecution.currentPlanId,r);delete t.planExecution.userInputWaitState,delete t.genericInput,delete G[t.id],$.startPolling(),console.log("[ChatComponent] User input submitted successfully:",W)}catch(r){console.error("[ChatComponent] User input submission failed:",r),alert(`${u("common.submitFailed")}: ${(r==null?void 0:r.message)||u("common.unknownError")}`)}};return $e(()=>d.initialPrompt,(t,a)=>{console.log("[ChatComponent] initialPrompt changed from:",a,"to:",t),t&&typeof t=="string"&&t.trim()&&t!==a&&(console.log("[ChatComponent] Processing changed initial prompt:",t),ne(()=>{v(t)}))},{immediate:!1}),Se(()=>{console.log("[ChatComponent] Mounted, setting up event listeners"),oe.setEventCallbacks({onPlanUpdate:me,onPlanCompleted:b,onDialogRoundStart:se,onChatInputUpdateState:t=>{console.log("[ChatComponent] Chat input state update for rootPlanId:",t)},onChatInputClear:()=>{console.log("[ChatComponent] Chat input clear requested")},onPlanError:c}),ne(()=>{ce()}),d.initialPrompt&&typeof d.initialPrompt=="string"&&d.initialPrompt.trim()&&(console.log("[ChatComponent] Processing initial prompt:",d.initialPrompt),ne(()=>{v(d.initialPrompt)}))}),De(()=>{console.log("[ChatComponent] Unmounting, cleaning up resources"),be(),B.value&&clearInterval(B.value),$.cleanup(),Object.keys(G).forEach(t=>delete G[t])}),n({handleSendMessage:v,handlePlanUpdate:me,handlePlanCompleted:b,handleDialogRoundStart:se,addMessage:o,handlePlanError:c}),(t,a)=>(h(),m("div",Fs,[e("div",{class:"messages",ref_key:"messagesRef",ref:x},[(h(!0),m(ge,null,ve(P.value,r=>{var p,W,N,Z,j,H,M,z,ae;return h(),m("div",{key:r.id,class:te(["message",{user:r.type==="user",assistant:r.type==="assistant"}])},[e("div",Vs,[r.type==="user"?(h(),m("div",Os,l(r.content),1)):(h(),m("div",Bs,[r.thinking||((p=r.planExecution)==null?void 0:p.progress)!==void 0||(((N=(W=r.planExecution)==null?void 0:W.steps)==null?void 0:N.length)??0)>0?(h(),m("div",Ws,[e("div",js,[e("div",Hs,[k(i(C),{icon:"carbon:thinking",class:"thinking-icon"})]),e("div",zs,l(t.$t("chat.thinkingLabel")),1)]),e("div",Js,[r.thinking?(h(),m("div",Gs,[k(i(C),{icon:"carbon:thinking",class:"thinking-icon"}),e("span",null,l(r.thinking),1)])):F("",!0),((Z=r.planExecution)==null?void 0:Z.progress)!==void 0?(h(),m("div",Xs,[e("div",Ks,[e("div",{class:"progress-fill",style:Ue({width:r.planExecution.progress+"%"})},null,4)]),e("span",Qs,l(r.planExecution.progressText??t.$t("chat.processing")+"..."),1)])):F("",!0),(((H=(j=r.planExecution)==null?void 0:j.steps)==null?void 0:H.length)??0)>0?(h(),m("div",Ys,[e("h4",Zs,l(t.$t("chat.stepExecutionDetails")),1),(h(!0),m(ge,null,ve((M=r.planExecution)==null?void 0:M.steps,(Ee,J)=>{var Me,Ve,Oe,Be,We,je,He,ze,Je,Ge,Xe,Ke,Qe,Ye,Ze,et,tt,nt,st;return h(),m("div",{key:J,class:te(["ai-section",{running:w(r,J)==="RUNNING",completed:w(r,J)==="FINISHED",pending:w(r,J)==="IDLE"}]),onClick:ie(pe=>L(r,J),["stop"])},[e("div",to,[e("span",no,l(w(r,J)==="FINISHED"?"✓":w(r,J)==="RUNNING"?"▶":"○"),1),e("span",so,l(Ee||`${t.$t("chat.step")} ${J+1}`),1),w(r,J)==="RUNNING"?(h(),m("span",oo,l(t.$t("chat.status.executing")),1)):w(r,J)==="FINISHED"?(h(),m("span",ao,l(t.$t("chat.status.completed")),1)):(h(),m("span",lo,l(t.$t("chat.status.pending")),1))]),r.stepActions&&r.stepActions[J]?(h(),m("div",io,[e("div",co,[e("span",ro,l(((Me=r.stepActions[J])==null?void 0:Me.status)==="current"?"🔄":((Ve=r.stepActions[J])==null?void 0:Ve.status)==="completed"?"✓":"⏳"),1),e("strong",null,l((Oe=r.stepActions[J])==null?void 0:Oe.actionDescription),1)]),(Be=r.stepActions[J])!=null&&Be.toolParameters?(h(),m("div",uo,[a[0]||(a[0]=e("span",{class:"tool-icon"},"⚙️",-1)),e("span",po,l(t.$t("common.parameters"))+":",1),e("pre",ho,l((We=r.stepActions[J])==null?void 0:We.toolParameters),1)])):F("",!0),(je=r.stepActions[J])!=null&&je.thinkOutput?(h(),m("div",go,[e("div",mo,[a[1]||(a[1]=e("span",{class:"think-icon"},"💭",-1)),e("span",vo,l(t.$t("chat.thinkingOutput"))+":",1)]),e("div",fo,[e("pre",bo,l((He=r.stepActions[J])==null?void 0:He.thinkOutput),1)])])):F("",!0)])):F("",!0),((ze=R(r,J))==null?void 0:ze.length)>0?(h(),m("div",ko,[e("div",_o,[k(i(C),{icon:"carbon:tree-view",class:"sub-plan-icon"}),e("span",$o,l(t.$t("rightPanel.subPlan")),1)]),e("div",Po,[(h(!0),m(ge,null,ve(R(r,J),(pe,le)=>(h(),m("div",{key:`sub-${J}-${le}`,class:te(["sub-plan-step-item",{completed:y(r,J,le)==="completed",current:y(r,J,le)==="current",pending:y(r,J,le)==="pending"}]),onClick:ie(ot=>A(r,J,le),["stop"])},[e("div",So,[e("span",yo,l(y(r,J,le)==="completed"?"✓":y(r,J,le)==="current"?"▶":"○"),1),e("span",Eo,l(le+1),1)]),e("div",wo,[e("span",To,l(pe),1),e("span",Io,l(t.$t("rightPanel.subStep")),1)])],10,Co))),128))])])):F("",!0),(Je=r.planExecution)!=null&&Je.userInputWaitState&&w(r,J)==="RUNNING"?(h(),m("div",Do,[e("p",xo,l(((Xe=(Ge=r.planExecution)==null?void 0:Ge.userInputWaitState)==null?void 0:Xe.message)??t.$t("chat.userInput.message")),1),(Qe=(Ke=r.planExecution)==null?void 0:Ke.userInputWaitState)!=null&&Qe.formDescription?(h(),m("p",Ro,l((Ze=(Ye=r.planExecution)==null?void 0:Ye.userInputWaitState)==null?void 0:Ze.formDescription),1)):F("",!0),e("form",{onSubmit:ie(pe=>I(r),["prevent"]),class:"user-input-form"},[(tt=(et=r.planExecution)==null?void 0:et.userInputWaitState)!=null&&tt.formInputs&&r.planExecution.userInputWaitState.formInputs.length>0?(h(!0),m(ge,{key:0},ve((st=(nt=r.planExecution)==null?void 0:nt.userInputWaitState)==null?void 0:st.formInputs,(pe,le)=>(h(),m("div",{key:le,class:"form-group"},[e("label",{for:`form-input-${pe.label.replace(/\W+/g,"_")}`},l(pe.label)+": ",9,Mo),de(e("input",{type:"text",id:`form-input-${pe.label.replace(/\W+/g,"_")}`,name:pe.label,"onUpdate:modelValue":ot=>G[r.id][le]=ot,class:"form-input"},null,8,No),[[fe,G[r.id][le]]])]))),128)):(h(),m("div",Uo,[e("label",Lo,l(t.$t("common.input"))+":",1),de(e("input",{type:"text",id:"form-input-genericInput",name:"genericInput","onUpdate:modelValue":pe=>r.genericInput=pe,class:"form-input"},null,8,qo),[[fe,r.genericInput]])])),e("button",Fo,l(t.$t("chat.userInput.submit")),1)],40,Ao)])):F("",!0)],10,eo)}),128))])):!r.content&&(r.thinking||((z=r.planExecution)==null?void 0:z.progress)!==void 0&&(((ae=r.planExecution)==null?void 0:ae.progress)??0)<100)?(h(),m("div",Vo,[e("div",Oo,[a[2]||(a[2]=e("div",{class:"thinking-dots"},[e("span"),e("span"),e("span")],-1)),e("span",null,l(r.thinking??t.$t("chat.thinkingProcessing")),1)])])):F("",!0)])])):F("",!0),e("div",Bo,[e("div",Wo,[e("div",jo,[k(i(C),{icon:"carbon:bot",class:"bot-icon"})]),e("div",Ho,l(t.$t("chat.botName")),1)]),e("div",zo,[r.content?(h(),m("div",Jo,[e("div",{class:"response-text",innerHTML:_(r.content)},null,8,Go)])):(h(),m("div",Xo,[e("div",Ko,[a[3]||(a[3]=e("div",{class:"typing-dots"},[e("span"),e("span"),e("span")],-1)),e("span",Qo,l(t.$t("chat.thinkingResponse")),1)])]))])])]))])],2)}),128)),O.value?(h(),m("div",Yo,[e("div",Zo,[e("div",ea,[e("div",ta,[e("div",na,[e("div",sa,[k(i(C),{icon:"carbon:thinking",class:"thinking-icon"})]),e("div",oa,l(t.$t("chat.thinkingLabel")),1)]),e("div",aa,[e("div",la,[e("div",ia,[a[4]||(a[4]=e("div",{class:"thinking-dots"},[e("span"),e("span"),e("span")],-1)),e("span",null,l(t.$t("chat.thinking")),1)])])])]),e("div",ca,[e("div",ra,[e("div",ua,[k(i(C),{icon:"carbon:bot",class:"bot-icon"})]),e("div",da,l(t.$t("chat.botName")),1)]),e("div",pa,[e("div",ha,[e("div",ga,[a[5]||(a[5]=e("div",{class:"typing-dots"},[e("span"),e("span"),e("span")],-1)),e("span",ma,l(t.$t("chat.thinkingResponse")),1)])])])])])])])):F("",!0)],512),X.value?(h(),m("div",{key:0,class:"scroll-to-bottom-btn",onClick:V,title:t.$t("chat.scrollToBottom")},[k(i(C),{icon:"carbon:chevron-down"})],8,va)):F("",!0)]))}}),ba=ye(fa,[["__scopeId","data-v-99c33eb1"]]),ka={class:"input-area"},_a={class:"input-container"},$a=["title"],Pa=["placeholder","disabled"],Ca=["title"],Sa=["disabled","title"],ya=Ce({__name:"index",props:{placeholder:{default:""},disabled:{type:Boolean,default:!1},initialValue:{default:""}},emits:["send","clear","update-state","plan-mode-clicked"],setup(T,{expose:n,emit:s}){const{t:d}=Ie(),E=T,u=s,$=D(),x=D(""),O=_e(()=>E.placeholder||d("input.placeholder")),P=D(O.value),B=_e(()=>!!E.disabled),X=()=>{ne(()=>{$.value&&($.value.style.height="auto",$.value.style.height=Math.min($.value.scrollHeight,120)+"px")})},G=V=>{V.key==="Enter"&&!V.shiftKey&&(V.preventDefault(),o())},o=()=>{if(!x.value.trim()||B.value)return;const V=x.value.trim();u("send",V),S()},g=()=>{u("plan-mode-clicked")},S=()=>{x.value="",X(),u("clear")},K=(V,Q)=>{Q&&(P.value=V?Q:d("input.waiting")),u("update-state",V,Q)},U=V=>{x.value=V,X()},q=()=>x.value.trim();return $e(()=>E.initialValue,V=>{V&&V.trim()&&(x.value=V,X())},{immediate:!0}),n({clearInput:S,updateState:K,setInputValue:U,getQuery:q,focus:()=>{var V;return(V=$.value)==null?void 0:V.focus()}}),Se(()=>{}),De(()=>{}),(V,Q)=>(h(),m("div",ka,[e("div",_a,[e("button",{class:"attach-btn",title:V.$t("input.attachFile")},[k(i(C),{icon:"carbon:attachment"})],8,$a),de(e("textarea",{"onUpdate:modelValue":Q[0]||(Q[0]=ce=>x.value=ce),ref_key:"inputRef",ref:$,class:"chat-input",placeholder:P.value,disabled:B.value,onKeydown:G,onInput:X},null,40,Pa),[[fe,x.value]]),e("button",{class:"plan-mode-btn",title:V.$t("input.planMode"),onClick:g},[k(i(C),{icon:"carbon:document"}),Y(" "+l(V.$t("input.planMode")),1)],8,Ca),e("button",{class:"send-button",disabled:!x.value.trim()||B.value,onClick:o,title:V.$t("input.send")},[k(i(C),{icon:"carbon:send-alt"}),Y(" "+l(V.$t("input.send")),1)],8,Sa)])]))}}),Ea=ye(ya,[["__scopeId","data-v-4b2cba42"]]);class we{static async getAllCronTasks(){try{const n=await fetch(this.BASE_URL);return await(await this.handleResponse(n)).json()}catch(n){throw console.error("Failed to get cron tasks:",n),n}}static async getCronTaskById(n){try{const s=await fetch(`${this.BASE_URL}/${n}`);return await(await this.handleResponse(s)).json()}catch(s){throw console.error("Failed to get cron task by id:",s),s}}static async createCronTask(n){try{const s=await fetch(this.BASE_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});return await(await this.handleResponse(s)).json()}catch(s){throw console.error("Failed to create cron task:",s),s}}static async updateCronTask(n,s){try{const d=await fetch(`${this.BASE_URL}/${n}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});return await(await this.handleResponse(d)).json()}catch(d){throw console.error("Failed to update cron task:",d),d}}static async updateTaskStatus(n,s){try{const d=await fetch(`${this.BASE_URL}/${n}/status?status=${s}`,{method:"PUT"});await this.handleResponse(d)}catch(d){throw console.error("Failed to update task status:",d),d}}static async deleteCronTask(n){try{const s=await fetch(`${this.BASE_URL}/${n}`,{method:"DELETE"});await this.handleResponse(s)}catch(s){throw console.error("Failed to delete cron task:",s),s}}static async handleResponse(n){if(!n.ok)try{const s=await n.json();throw new Error(s.message||`API request failed: ${n.status}`)}catch{throw new Error(`API request failed: ${n.status} ${n.statusText}`)}return n}}he(we,"BASE_URL","/api/cron-tasks");const Te={validateCronExpression(T){const n=T.trim().split(/\s+/);return n.length>=5&&n.length<=6},formatTime(T){return new Date(T).toLocaleString()},async saveTask(T){try{let n;return T.id?n=await we.updateCronTask(Number(T.id),T):n=await we.createCronTask(T),n}catch(n){throw console.error("Failed to save cron task:",n),n}},async deleteTask(T){try{await we.deleteCronTask(String(T))}catch(n){throw console.error("Failed to delete cron task:",n),n}},async toggleTaskStatus(T){if(!T.id)throw new Error("Task ID is required");const n=T.status===0?1:0;return await we.updateCronTask(Number(T.id),{...T,status:n})},prepareTaskExecution(T){return T.planTemplateId?{useTemplate:!0,planData:{title:T.cronName||"Scheduled Task Execution",planData:{id:T.planTemplateId,planTemplateId:T.planTemplateId,planId:T.planTemplateId},params:T.executionParams||void 0}}:{useTemplate:!1,taskContent:T.planDesc||T.cronName||""}}},wa={class:"modal-header"},Ta={class:"header-actions"},Ia={class:"status-switch"},Da={class:"status-label"},xa={class:"toggle-switch"},Ra=["checked"],Aa={class:"modal-content"},Ma={class:"form-group"},Na={class:"form-label"},Ua=["placeholder"],La={class:"form-group"},qa={class:"form-label"},Fa=["placeholder"],Va={class:"form-help"},Oa={class:"form-group"},Ba={class:"form-label"},Wa=["placeholder"],ja={class:"form-group"},Ha={class:"form-label"},za={class:"template-toggle"},Ja={key:0,class:"template-selector"},Ga={value:""},Xa=["value"],Ka={class:"form-help"},Qa={key:0,class:"form-group"},Ya={class:"time-info"},Za={class:"time-label"},el={class:"time-value"},tl={key:1,class:"form-group"},nl={class:"time-info"},sl={class:"time-label"},ol={class:"time-value"},al={class:"modal-footer"},ll=["disabled"],il=Ce({__name:"TaskDetailModal",props:{modelValue:{type:Boolean},task:{}},emits:["update:modelValue","save"],setup(T,{emit:n}){const s=T,d=n,E=D(!1),u=D([]),$=D({cronName:"",cronTime:"",planDesc:"",status:1,linkTemplate:!1,templateId:"",planTemplateId:""});Se(async()=>{try{const o=await Ae.getAllPlanTemplates();o&&o.templates&&(u.value=o.templates.map(g=>({id:g.id,name:g.title||"Unnamed Template"})))}catch(o){console.error("Failed to get template list:",o)}});const O=o=>{o.target===o.currentTarget&&d("update:modelValue",!1)},P=()=>{$.value.linkTemplate=!1,$.value.templateId="",$.value.planTemplateId=""},B=()=>$.value.cronName.trim()?$.value.cronTime.trim()?Te.validateCronExpression($.value.cronTime)?$.value.planDesc.trim()?$.value.linkTemplate&&!$.value.templateId?(alert("Please select a plan template"),!1):!0:(alert("Task description cannot be empty"),!1):(alert("Invalid Cron expression format, should be 5-6 parts separated by spaces"),!1):(alert("Cron expression cannot be empty"),!1):(alert("Task name cannot be empty"),!1),X=o=>Te.formatTime(o),G=async()=>{var o;if(B()){E.value=!0;try{const g={...$.value,...((o=s.task)==null?void 0:o.id)!==void 0&&{id:s.task.id},cronName:$.value.cronName.trim(),cronTime:$.value.cronTime.trim(),planDesc:$.value.planDesc.trim(),status:$.value.status,planTemplateId:$.value.linkTemplate&&$.value.templateId||""};d("save",g)}finally{E.value=!1}}};return $e(()=>s.task,o=>{if(o){const g=o.templateId||o.planTemplateId||"";$.value={cronName:o.cronName||"",cronTime:o.cronTime||"",planDesc:o.planDesc||"",status:o.status??1,linkTemplate:!!g,templateId:g,planTemplateId:g}}else $.value={cronName:"",cronTime:"",planDesc:"",status:1,linkTemplate:!1,templateId:"",planTemplateId:""}},{immediate:!0}),$e(()=>s.modelValue,o=>{o||($.value={cronName:"",cronTime:"",planDesc:"",status:1,linkTemplate:!1,templateId:"",planTemplateId:""})}),(o,g)=>(h(),ue(Ne,{to:"body"},[k(xe,{name:"modal"},{default:Re(()=>{var S,K,U;return[o.modelValue?(h(),m("div",{key:0,class:"modal-overlay",onClick:O},[e("div",{class:"modal-container",onClick:g[8]||(g[8]=ie(()=>{},["stop"]))},[e("div",wa,[e("h3",null,l(o.$t("cronTask.taskDetail")),1),e("div",Ta,[e("div",Ia,[e("span",Da,l(o.$t("cronTask.taskStatus")),1),e("label",xa,[e("input",{type:"checkbox",checked:$.value.status===0,onChange:g[0]||(g[0]=q=>$.value.status=$.value.status===0?1:0)},null,40,Ra),g[9]||(g[9]=e("span",{class:"toggle-slider"},null,-1))])]),e("button",{class:"close-btn",onClick:g[1]||(g[1]=q=>o.$emit("update:modelValue",!1))},[k(i(C),{icon:"carbon:close"})])])]),e("div",Aa,[e("form",{onSubmit:ie(G,["prevent"]),class:"task-form"},[e("div",Ma,[e("label",Na,l(o.$t("cronTask.taskName")),1),de(e("input",{"onUpdate:modelValue":g[2]||(g[2]=q=>$.value.cronName=q),type:"text",class:"form-input",placeholder:o.$t("cronTask.taskNamePlaceholder"),required:""},null,8,Ua),[[fe,$.value.cronName]])]),e("div",La,[e("label",qa,l(o.$t("cronTask.cronExpression")),1),de(e("input",{"onUpdate:modelValue":g[3]||(g[3]=q=>$.value.cronTime=q),type:"text",class:"form-input",placeholder:o.$t("cronTask.cronExpressionPlaceholder"),required:""},null,8,Fa),[[fe,$.value.cronTime]]),e("div",Va,l(o.$t("cronTask.cronExpressionHelp")),1)]),e("div",Oa,[e("label",Ba,l(o.$t("cronTask.taskDescription")),1),de(e("textarea",{"onUpdate:modelValue":g[4]||(g[4]=q=>$.value.planDesc=q),class:"form-textarea",placeholder:o.$t("cronTask.taskDescriptionPlaceholder"),rows:"4",required:""},null,8,Wa),[[fe,$.value.planDesc]])]),e("div",ja,[e("label",Ha,l(o.$t("cronTask.planTemplate")),1),e("div",za,[e("button",{type:"button",class:te(["template-btn",$.value.linkTemplate?"active":""]),onClick:g[5]||(g[5]=q=>$.value.linkTemplate=!0)},[k(i(C),{icon:"carbon:checkmark"}),Y(" "+l(o.$t("cronTask.linkTemplate")),1)],2),e("button",{type:"button",class:te(["template-btn",$.value.linkTemplate?"":"active"]),onClick:P},[k(i(C),{icon:"carbon:close"}),Y(" "+l(o.$t("cronTask.noTemplate")),1)],2)]),$.value.linkTemplate?(h(),m("div",Ja,[de(e("select",{"onUpdate:modelValue":g[6]||(g[6]=q=>$.value.templateId=q),class:"form-select"},[e("option",Ga,l(o.$t("cronTask.selectTemplate")),1),(h(!0),m(ge,null,ve(u.value,q=>(h(),m("option",{key:q.id,value:q.id},l(q.name),9,Xa))),128))],512),[[ut,$.value.templateId]]),e("div",Ka,l(o.$t("cronTask.templateHelpText")),1)])):F("",!0)]),(S=o.task)!=null&&S.createTime?(h(),m("div",Qa,[e("div",Ya,[e("span",Za,l(o.$t("cronTask.createTime"))+":",1),e("span",el,l(X(o.task.createTime)),1)])])):F("",!0),(K=o.task)!=null&&K.updateTime?(h(),m("div",tl,[e("div",nl,[e("span",sl,l(o.$t("cronTask.updateTime"))+":",1),e("span",ol,l(X(o.task.updateTime)),1)])])):F("",!0)],32)]),e("div",al,[e("button",{type:"button",class:"cancel-btn",onClick:g[7]||(g[7]=q=>o.$emit("update:modelValue",!1))},l(o.$t("common.cancel")),1),e("button",{type:"button",class:"save-btn",onClick:G,disabled:E.value},[E.value?(h(),ue(i(C),{key:0,icon:"carbon:loading",class:"loading-icon"})):F("",!0),Y(" "+l((U=s.task)!=null&&U.id?o.$t("common.save"):o.$t("common.create")),1)],8,ll)])])])):F("",!0)]}),_:1})]))}}),cl=ye(il,[["__scopeId","data-v-5b32448e"]]),rl={class:"modal-header"},ul={class:"header-actions"},dl={class:"modal-content"},pl={key:0,class:"loading-container"},hl={key:1,class:"empty-container"},gl={key:2,class:"task-list"},ml=["onClick"],vl={class:"task-main"},fl={class:"task-info"},bl={class:"task-header"},kl={class:"task-name"},_l={class:"task-description"},$l={class:"task-time"},Pl=["onClick"],Cl=["onClick","disabled","title"],Sl=["onClick","title"],yl={class:"dropdown-menu"},El=["onClick"],wl=["onClick","disabled"],Tl=["onClick","disabled"],Il={class:"confirm-header"},Dl={class:"confirm-content"},xl={class:"confirm-actions"},Rl=["disabled"],Al={class:"confirm-header"},Ml={class:"confirm-content"},Nl={class:"create-options"},Ul={class:"option-content"},Ll={class:"option-title"},ql={class:"option-desc"},Fl={class:"option-content"},Vl={class:"option-title"},Ol={class:"option-desc"},Bl={class:"confirm-actions"},Wl=Ce({__name:"index",props:{modelValue:{type:Boolean,required:!0}},emits:["update:modelValue"],setup(T,{emit:n}){const s=lt(),d=it(),E=mt(),{t:u}=Ie(),$=T,x=n,O=D([]),P=D(!1),B=D(null),X=D(null),G=D(null),o=D(null),g=D(!1),S=D(null),K=D(!1),U=D(null),q=D(!1),V=c=>{c.target===c.currentTarget&&x("update:modelValue",!1)},Q=async()=>{P.value=!0;try{O.value=await we.getAllCronTasks()}catch(c){console.error("Failed to load cron tasks:",c),E.error(`Failed to load tasks: ${c instanceof Error?c.message:String(c)}`)}finally{P.value=!1}},ce=async c=>{B.value=c;try{const _=O.value.find(a=>a.id===c);if(!_){console.error("Task not found:",c);return}x("update:modelValue",!1);const I=Date.now().toString();await s.push({name:"direct",params:{id:I}}),await new Promise(a=>setTimeout(a,100));const t=Te.prepareTaskExecution(_);t.useTemplate&&t.planData?d.emitPlanExecutionRequested(t.planData):t.taskContent&&d.setTask(t.taskContent)}catch(_){console.error("Failed to execute task:",_),E.error(`Execution failed: ${_ instanceof Error?_.message:String(_)}`)}finally{B.value=null}},be=c=>{S.value={...c},g.value=!0,o.value=null},v=async c=>{try{await Te.saveTask(c),await Q(),g.value=!1,E.success("Task saved successfully")}catch(_){console.error("Failed to save task:",_),E.error(`Save failed: ${_ instanceof Error?_.message:String(_)}`)}},w=c=>{U.value=c,K.value=!0},L=async()=>{var c;if((c=U.value)!=null&&c.id){X.value=U.value.id;try{await Te.deleteTask(U.value.id),await Q(),K.value=!1,U.value=null,E.success("Task deleted successfully")}catch(_){console.error("Failed to delete task:",_),E.error(`Delete failed: ${_ instanceof Error?_.message:String(_)}`)}finally{X.value=null}}},R=()=>{K.value=!1,U.value=null},y=c=>{o.value=o.value===c?null:c},A=async c=>{if(c.id){G.value=c.id;try{await Te.toggleTaskStatus(c),await Q(),o.value=null,E.success(`Task ${c.status===0?"disabled":"enabled"} successfully`)}catch(_){console.error("Failed to toggle task status:",_),E.error(`Status toggle failed: ${_ instanceof Error?_.message:String(_)}`)}finally{G.value=null}}},ee=async c=>{try{await navigator.clipboard.writeText(c),E.success("Cron expression copied successfully")}catch(_){E.error(`Copy failed: ${_ instanceof Error?_.message:String(_)}`)}},se=()=>{q.value=!0},me=()=>{q.value=!1;try{x("update:modelValue",!1);const c=u("cronTask.template");d.setTaskToInput(c);const _=Date.now().toString();s.push({name:"direct",params:{id:_}})}catch(c){console.error("Error in createWithJmanus:",c),E.error(`Creation failed: ${c instanceof Error?c.message:String(c)}`)}},ke=()=>{q.value=!1,S.value={cronName:"",cronTime:"",planDesc:"",status:0,planTemplateId:""},g.value=!0},re=()=>{q.value=!1},b=c=>{const _=c.target;!_.closest(".action-dropdown")&&!_.closest(".dropdown-menu")&&(o.value=null)};return Se(()=>{document.addEventListener("click",b,!0)}),De(()=>{document.removeEventListener("click",b,!0)}),$e(()=>$.modelValue,c=>{c&&Q()}),(c,_)=>(h(),m(ge,null,[(h(),ue(Ne,{to:"body"},[k(xe,{name:"modal"},{default:Re(()=>[T.modelValue?(h(),m("div",{key:0,class:"modal-overlay",onClick:V},[e("div",{class:"modal-container",onClick:_[3]||(_[3]=ie(()=>{},["stop"]))},[e("div",rl,[e("h3",null,l(c.$t("cronTask.title")),1),e("div",ul,[e("button",{class:"add-task-btn",onClick:[se,_[0]||(_[0]=ie(()=>{},["stop"]))]},[k(i(C),{icon:"carbon:add"}),Y(" "+l(c.$t("cronTask.addTask")),1)]),e("button",{class:"close-btn",onClick:_[1]||(_[1]=I=>c.$emit("update:modelValue",!1))},[k(i(C),{icon:"carbon:close"})])])]),e("div",dl,[P.value?(h(),m("div",pl,[k(i(C),{icon:"carbon:loading",class:"loading-icon"}),e("span",null,l(c.$t("common.loading")),1)])):O.value.length===0?(h(),m("div",hl,[k(i(C),{icon:"carbon:time",class:"empty-icon"}),e("span",null,l(c.$t("cronTask.noTasks")),1)])):(h(),m("div",gl,[(h(!0),m(ge,null,ve(O.value,I=>(h(),m("div",{key:I.id||"",class:"task-item",onClick:t=>be(I)},[e("div",vl,[e("div",fl,[e("div",bl,[e("div",kl,l(I.cronName),1),e("div",{class:te(["task-status-badge",I.status===0?"active":"inactive"])},[k(i(C),{icon:I.status===0?"carbon:checkmark-filled":"carbon:pause-filled"},null,8,["icon"]),e("span",null,l(I.status===0?c.$t("cronTask.active"):c.$t("cronTask.inactive")),1)],2)]),e("div",_l,l(I.planDesc),1),e("div",$l,[k(i(C),{icon:"carbon:time"}),e("span",{class:"cron-readable",style:{cursor:"pointer"},onClick:ie(t=>ee(I.cronTime),["stop"])},l(I.cronTime),9,Pl)])])]),e("div",{class:"task-actions",onClick:_[2]||(_[2]=ie(()=>{},["stop"]))},[e("button",{class:"action-btn execute-btn",onClick:t=>ce(I.id),disabled:B.value===I.id,title:c.$t("cronTask.executeOnce")},[k(i(C),{icon:B.value===I.id?"carbon:loading":"carbon:play-filled"},null,8,["icon"]),Y(" "+l(c.$t("cronTask.executeOnce")),1)],8,Cl),e("div",{class:te(["action-dropdown",{active:o.value===I.id}])},[e("button",{class:"action-btn dropdown-btn",onClick:t=>y(I.id),title:c.$t("cronTask.operations")},[k(i(C),{icon:"carbon:overflow-menu-horizontal"}),Y(" "+l(c.$t("cronTask.operations")),1)],8,Sl),de(e("div",yl,[e("button",{class:"dropdown-item edit-btn",onClick:t=>be(I)},[k(i(C),{icon:"carbon:edit"}),Y(" "+l(c.$t("cronTask.edit")),1)],8,El),e("button",{class:"dropdown-item toggle-btn",onClick:t=>A(I),disabled:G.value===I.id},[k(i(C),{icon:G.value===I.id?"carbon:loading":I.status===0?"carbon:pause-filled":"carbon:play-filled"},null,8,["icon"]),Y(" "+l(I.status===0?c.$t("cronTask.disable"):c.$t("cronTask.enable")),1)],8,wl),e("button",{class:"dropdown-item delete-btn",onClick:t=>w(I),disabled:X.value===I.id},[k(i(C),{icon:X.value===I.id?"carbon:loading":"carbon:trash-can"},null,8,["icon"]),Y(" "+l(c.$t("cronTask.delete")),1)],8,Tl)],512),[[dt,o.value===I.id]])],2)])],8,ml))),128))]))])])])):F("",!0)]),_:1})])),k(cl,{modelValue:g.value,"onUpdate:modelValue":_[4]||(_[4]=I=>g.value=I),task:S.value,onSave:v},null,8,["modelValue","task"]),(h(),ue(Ne,{to:"body"},[k(xe,{name:"modal"},{default:Re(()=>{var I,t,a,r;return[K.value?(h(),m("div",{key:0,class:"modal-overlay",onClick:R},[e("div",{class:"confirm-modal",onClick:_[5]||(_[5]=ie(()=>{},["stop"]))},[e("div",Il,[k(i(C),{icon:"carbon:warning",class:"warning-icon"}),e("h3",null,l(c.$t("cronTask.deleteConfirm")),1)]),e("div",Dl,[e("p",null,l(c.$t("cronTask.deleteConfirmMessage",{taskName:((I=U.value)==null?void 0:I.cronName)||((t=U.value)==null?void 0:t.planDesc)||""})),1)]),e("div",xl,[e("button",{class:"confirm-btn cancel-btn",onClick:R},l(c.$t("common.cancel")),1),e("button",{class:"confirm-btn delete-btn",onClick:L,disabled:X.value===((a=U.value)==null?void 0:a.id)},[k(i(C),{icon:X.value===((r=U.value)==null?void 0:r.id)?"carbon:loading":"carbon:trash-can"},null,8,["icon"]),Y(" "+l(c.$t("cronTask.delete")),1)],8,Rl)])])])):F("",!0)]}),_:1})])),(h(),ue(Ne,{to:"body"},[k(xe,{name:"modal"},{default:Re(()=>[q.value?(h(),m("div",{key:0,class:"modal-overlay",onClick:re},[e("div",{class:"confirm-modal create-options-modal",onClick:_[6]||(_[6]=ie(()=>{},["stop"]))},[e("div",Al,[k(i(C),{icon:"carbon:time",class:"create-icon"}),e("h3",null,l(c.$t("cronTask.createTask")),1)]),e("div",Ml,[e("p",null,l(c.$t("cronTask.selectCreateMethod")),1),e("div",Nl,[e("button",{class:"create-option-btn jmanus-btn",onClick:me},[k(i(C),{icon:"carbon:ai-status"}),e("div",Ul,[e("span",Ll,l(c.$t("cronTask.createWithJmanus")),1),e("span",ql,l(c.$t("cronTask.createWithJmanusDesc")),1)])]),e("button",{class:"create-option-btn manual-btn",onClick:ke},[k(i(C),{icon:"carbon:edit"}),e("div",Fl,[e("span",Vl,l(c.$t("cronTask.createManually")),1),e("span",Ol,l(c.$t("cronTask.createManuallyDesc")),1)])])])]),e("div",Bl,[e("button",{class:"confirm-btn cancel-btn",onClick:re},l(c.$t("common.cancel")),1)])])])):F("",!0)]),_:1})]))],64))}}),jl=ye(Wl,[["__scopeId","data-v-837947df"]]),Hl={class:"direct-page"},zl={class:"direct-chat"},Jl={class:"chat-header"},Gl={class:"header-actions"},Xl=["title"],Kl=["title"],Ql={class:"chat-content"},Yl=["title"],Zl={class:"message-content"},ei=Ce({__name:"index",setup(T){const n=pt(),s=lt(),d=it(),{t:E}=Ie(),{message:u}=vt(),$=D(""),x=D(""),O=D(),P=D(),B=D(),X=D(!1),G=D(!1),o=D(null),g=D(!1),S=D(50),K=D(!1),U=D(0),q=D(0);Se(()=>{if(console.log("[Direct] onMounted called"),console.log("[Direct] taskStore.currentTask:",d.currentTask),console.log("[Direct] taskStore.hasUnprocessedTask():",d.hasUnprocessedTask()),oe.setEventCallbacks({onPlanUpdate:c=>{console.log("[Direct] Plan update event received for rootPlanId:",c),v(c)&&(console.log("[Direct] Processing plan update for current rootPlanId:",c),P.value&&typeof P.value.handlePlanUpdate=="function"?(console.log("[Direct] Calling chatRef.handlePlanUpdate with rootPlanId:",c),P.value.handlePlanUpdate(c)):console.warn("[Direct] chatRef.handlePlanUpdate method not available"),O.value&&typeof O.value.updateDisplayedPlanProgress=="function"?(console.log("[Direct] Calling rightPanelRef.updateDisplayedPlanProgress with rootPlanId:",c),O.value.updateDisplayedPlanProgress(c)):console.warn("[Direct] rightPanelRef.updateDisplayedPlanProgress method not available"))},onPlanCompleted:c=>{if(console.log("[Direct] Plan completed event received for rootPlanId:",c),!!v(c)){if(console.log("[Direct] Processing plan completion for current rootPlanId:",c),P.value&&typeof P.value.handlePlanCompleted=="function"){const _=oe.getCachedPlanRecord(c);console.log("[Direct] Calling chatRef.handlePlanCompleted with details:",_),P.value.handlePlanCompleted(_??{planId:c})}else console.warn("[Direct] chatRef.handlePlanCompleted method not available");o.value=null,console.log("[Direct] Cleared currentRootPlanId after plan completion")}},onDialogRoundStart:c=>{console.log("[Direct] Dialog round start event received for rootPlanId:",c),o.value=c,console.log("[Direct] Set currentRootPlanId to:",c),P.value&&typeof P.value.handleDialogRoundStart=="function"?(console.log("[Direct] Calling chatRef.handleDialogRoundStart with planId:",c),P.value.handleDialogRoundStart(c)):console.warn("[Direct] chatRef.handleDialogRoundStart method not available")},onChatInputClear:()=>{console.log("[Direct] Chat input clear event received"),L()},onChatInputUpdateState:c=>{if(console.log("[Direct] Chat input update state event received for rootPlanId:",c),!v(c,!0))return;const _=oe.getCachedUIState(c);_&&y(_.enabled,_.placeholder)},onPlanError:c=>{P.value.handlePlanError(c)}}),console.log("[Direct] Event callbacks registered to planExecutionManager"),f.loadPlanTemplateList(),d.hasUnprocessedTask()&&d.currentTask){const c=d.currentTask.prompt;console.log("[Direct] Found unprocessed task from store:",c),d.markTaskAsProcessed(),ne(()=>{P.value&&typeof P.value.handleSendMessage=="function"?(console.log("[Direct] Directly executing task via chatRef.handleSendMessage:",c),P.value.handleSendMessage(c)):(console.warn("[Direct] chatRef.handleSendMessage method not available, falling back to prompt"),$.value=c)})}else{const c=d.getAndClearTaskToInput();c?(x.value=c,console.log("[Direct] Setting inputOnlyContent for input only:",x.value)):($.value=n.query.prompt||"",console.log("[Direct] Received task from URL:",$.value),console.log("[Direct] No unprocessed task in store"))}const b=localStorage.getItem("directPanelWidth");b&&(S.value=parseFloat(b)),console.log("[Direct] Final prompt value:",$.value),x.value&&ne(()=>{B.value&&typeof B.value.setInputValue=="function"&&(B.value.setInputValue(x.value),console.log("[Direct] Set input value:",x.value),x.value="")}),window.addEventListener("plan-execution-requested",c=>{console.log("[DirectView] Received plan-execution-requested event:",c.detail),re(c.detail)})}),$e(()=>d.currentTask,b=>{if(console.log("[Direct] Watch taskStore.currentTask triggered, newTask:",b),b&&!b.processed){const c=b.prompt;d.markTaskAsProcessed(),console.log("[Direct] Received new task from store:",c),ne(()=>{P.value&&typeof P.value.handleSendMessage=="function"?(console.log("[Direct] Directly executing new task via chatRef.handleSendMessage:",c),P.value.handleSendMessage(c)):console.warn("[Direct] chatRef.handleSendMessage method not available for new task")})}else console.log("[Direct] Task is null or already processed, ignoring")},{immediate:!1}),$e(()=>$.value,(b,c)=>{console.log("[Direct] prompt value changed from:",c,"to:",b)},{immediate:!1}),$e(()=>d.taskToInput,b=>{console.log("[Direct] Watch taskStore.taskToInput triggered, newTaskToInput:",b),b&&b.trim()&&(console.log("[Direct] Setting input value from taskToInput:",b),ne(()=>{B.value&&typeof B.value.setInputValue=="function"&&(B.value.setInputValue(b.trim()),console.log("[Direct] Input value set from taskToInput watch:",b.trim()),d.getAndClearTaskToInput())}))},{immediate:!1}),De(()=>{console.log("[Direct] onUnmounted called, cleaning up resources"),o.value=null,oe.cleanup(),document.removeEventListener("mousemove",Q),document.removeEventListener("mouseup",ce),window.removeEventListener("plan-execution-requested",b=>{re(b.detail)})});const V=b=>{K.value=!0,U.value=b.clientX,q.value=S.value,document.addEventListener("mousemove",Q),document.addEventListener("mouseup",ce),document.body.style.cursor="col-resize",document.body.style.userSelect="none",b.preventDefault()},Q=b=>{if(!K.value)return;const c=window.innerWidth,I=(b.clientX-U.value)/c*100;let t=q.value+I;t=Math.max(20,Math.min(80,t)),S.value=t},ce=()=>{K.value=!1,document.removeEventListener("mousemove",Q),document.removeEventListener("mouseup",ce),document.body.style.cursor="",document.body.style.userSelect="",localStorage.setItem("directPanelWidth",S.value.toString())},be=()=>{S.value=50,localStorage.setItem("directPanelWidth","50")},v=(b,c=!1)=>!o.value||b===o.value||c&&(b==="ui-state"||b==="error")?!0:(console.log("[Direct] Ignoring event for non-current rootPlanId:",b,"current:",o.value),!1),w=b=>{console.log("[DirectView] Send message from input:",b),P.value&&typeof P.value.handleSendMessage=="function"?(console.log("[DirectView] Calling chatRef.handleSendMessage:",b),P.value.handleSendMessage(b)):console.warn("[DirectView] chatRef.handleSendMessage method not available")},L=()=>{console.log("[DirectView] Input cleared"),B.value&&typeof B.value.clear=="function"&&B.value.clear()},R=()=>{console.log("[DirectView] Input focused")},y=(b,c)=>{console.log("[DirectView] Input state updated:",b,c),G.value=!b},A=(b,c)=>{console.log("[DirectView] Step selected:",b,c),O.value&&typeof O.value.handleStepSelected=="function"?(console.log("[DirectView] Forwarding step selection to right panel:",b,c),O.value.handleStepSelected(b,c)):console.warn("[DirectView] rightPanelRef.handleStepSelected method not available")},ee=(b,c,_,I)=>{console.log("[DirectView] Sub plan step selected:",{parentPlanId:b,subPlanId:c,stepIndex:_,subStepIndex:I}),O.value&&typeof O.value.handleSubPlanStepSelected=="function"?(console.log("[DirectView] Forwarding sub plan step selection to right panel:",{parentPlanId:b,subPlanId:c,stepIndex:_,subStepIndex:I}),O.value.handleSubPlanStepSelected(b,c,_,I)):console.warn("[DirectView] rightPanelRef.handleSubPlanStepSelected method not available")},se=()=>{console.log("[DirectView] Plan mode button clicked"),f.toggleSidebar(),console.log("[DirectView] Sidebar toggled, isCollapsed:",f.isCollapsed)},me=()=>{s.push("/home")},ke=()=>{s.push("/configs")},re=async b=>{var _,I,t,a;if(console.log("[DirectView] Plan execution requested:",b),X.value){console.log("[DirectView] Plan execution already in progress, ignoring request");return}X.value=!0;let c=!1;P.value&&typeof P.value.addMessage=="function"?(console.log("[DirectView] Calling chatRef.addMessage for plan execution:",b.title),P.value.addMessage("user",b.title),c=!0):console.warn("[DirectView] chatRef.addMessage method not available");try{const r=((_=b.planData)==null?void 0:_.planTemplateId)||((I=b.planData)==null?void 0:I.id)||((t=b.planData)==null?void 0:t.planId);if(!r)throw new Error(E("direct.planTemplateIdNotFound"));console.log("[Direct] Executing plan with templateId:",r,"params:",b.params),console.log("[Direct] About to call PlanActApiService.executePlan");let p;if((a=b.params)!=null&&a.trim()?(console.log("[Direct] Calling executePlan with params:",b.params.trim()),p=await Ae.executePlan(r,b.params.trim())):(console.log("[Direct] Calling executePlan without params"),p=await Ae.executePlan(r)),console.log("[Direct] Plan execution API response:",p),p.planId)console.log("[Direct] Got planId from response:",p.planId,"starting plan execution"),o.value=p.planId,console.log("[Direct] Set currentRootPlanId to:",p.planId),console.log("[Direct] Delegating plan execution to planExecutionManager"),oe.handlePlanExecutionRequested(p.planId,b.title);else throw console.error("[Direct] No planId in response:",p),new Error(E("direct.executionFailedNoPlanId"))}catch(r){console.error("[Direct] Plan execution failed:",r),console.error("[Direct] Error details:",{message:r.message,stack:r.stack}),o.value=null,P.value&&typeof P.value.addMessage=="function"?(console.log("[Direct] Adding error messages to chat"),c||P.value.addMessage("user",b.title),P.value.addMessage("assistant",`${E("direct.executionFailed")}: ${r.message||E("common.unknownError")}`,{thinking:void 0})):(console.error("[Direct] Chat ref not available, showing alert"),alert(`${E("direct.executionFailed")}: ${r.message||E("common.unknownError")}`))}finally{console.log("[Direct] Plan execution finished, resetting isExecutingPlan flag"),X.value=!1}};return(b,c)=>(h(),m("div",Hl,[e("div",zl,[k(fn,{onPlanExecutionRequested:re}),e("div",{class:"left-panel",style:Ue({width:S.value+"%"})},[e("div",Jl,[e("button",{class:"back-button",onClick:me},[k(i(C),{icon:"carbon:arrow-left"})]),e("h2",null,l(b.$t("conversation")),1),e("div",Gl,[k(gt),e("button",{class:"config-button",onClick:ke,title:b.$t("direct.configuration")},[k(i(C),{icon:"carbon:settings-adjust",width:"20"})],8,Xl),e("button",{class:"cron-task-btn",onClick:c[0]||(c[0]=_=>g.value=!0),title:b.$t("cronTask.title")},[k(i(C),{icon:"carbon:alarm",width:"20"})],8,Kl)])]),e("div",Ql,[k(ba,{ref_key:"chatRef",ref:P,mode:"direct","initial-prompt":$.value||"",onStepSelected:A,onSubPlanStepSelected:ee},null,8,["initial-prompt"])]),(h(),ue(Ea,{key:b.$i18n.locale,ref_key:"inputRef",ref:B,disabled:G.value,placeholder:G.value?i(E)("input.waiting"):i(E)("input.placeholder"),"initial-value":$.value,onSend:w,onClear:L,onFocus:R,onUpdateState:y,onPlanModeClicked:se},null,8,["disabled","placeholder","initial-value"]))],4),e("div",{class:"panel-resizer",onMousedown:V,onDblclick:be,title:b.$t("direct.panelResizeHint")},c[2]||(c[2]=[e("div",{class:"resizer-line"},null,-1)]),40,Yl),k(Ls,{ref_key:"rightPanelRef",ref:O,style:Ue({width:100-S.value+"%"})},null,8,["style"])]),k(jl,{modelValue:g.value,"onUpdate:modelValue":c[1]||(c[1]=_=>g.value=_)},null,8,["modelValue"]),i(u).show?(h(),m("div",{key:0,class:te(["message-toast",i(u).type])},[e("div",Zl,[e("span",null,l(i(u).text),1)])],2)):F("",!0)]))}}),ri=ye(ei,[["__scopeId","data-v-eab98c50"]]);export{ri as default};
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/index-D2fTT4wr.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/index-rqS3tjXd.js
similarity index 94%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/index-D2fTT4wr.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/index-rqS3tjXd.js
index 0f28e89e9b..acdbb4704a 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/index-D2fTT4wr.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/index-rqS3tjXd.js
@@ -1 +1 @@
-import{d as L,u as E,r as p,c as v,P as $,o as B,A as I,a as r,b as l,e as t,f as g,g as h,x as u,t as d,h as x,F as D,l as F,n as N,s as w,Q as U}from"./index-DA9oYm7y.js";import{I as i}from"./iconify-CyasjfC7.js";import{_ as V}from"./_plugin-vue_export-helper-DlAUqK2U.js";const M={class:"language-switcher"},O=["title"],S={class:"current-lang"},z={class:"dropdown-header"},A={class:"language-options"},K=["disabled","onClick"],P={class:"lang-code"},Q={class:"lang-name"},j=L({__name:"index",setup(q){const{locale:_}=E(),a=p(!1),o=v(()=>_.value),f=v(()=>$.opts),b=v(()=>{const e=f.value.find(n=>n.value===o.value);return e?e.title:"Unknown"}),y=()=>{a.value=!a.value},c=p(!1),C=async e=>{if(!(c.value||o.value===e))try{c.value=!0,await U(e),a.value=!1}catch(n){console.error("Failed to change language:",n),a.value=!1}finally{c.value=!1}},k=e=>{e.target.closest(".language-switcher")||(a.value=!1)},m=e=>{e.key==="Escape"&&(a.value=!1)};return B(()=>{document.addEventListener("click",k),document.addEventListener("keydown",m)}),I(()=>{document.removeEventListener("click",k),document.removeEventListener("keydown",m)}),(e,n)=>(l(),r("div",M,[t("button",{class:"language-btn",onClick:y,title:e.$t("language.switch")},[h(u(i),{icon:"carbon:translate",width:"18"}),t("span",S,d(b.value),1),h(u(i),{icon:a.value?"carbon:chevron-up":"carbon:chevron-down",width:"14",class:"chevron"},null,8,["icon"])],8,O),a.value?(l(),r("div",{key:0,class:"language-dropdown",onClick:n[1]||(n[1]=x(()=>{},["stop"]))},[t("div",z,[t("span",null,d(e.$t("language.switch")),1),t("button",{class:"close-btn",onClick:n[0]||(n[0]=s=>a.value=!1)},[h(u(i),{icon:"carbon:close",width:"16"})])]),t("div",A,[(l(!0),r(D,null,F(f.value,s=>(l(),r("button",{key:s.value,class:N(["language-option",{active:o.value===s.value,loading:c.value&&o.value!==s.value}]),disabled:c.value,onClick:G=>C(s.value)},[t("span",P,d(s.value.toUpperCase()),1),t("span",Q,d(s.title),1),c.value&&o.value!==s.value?(l(),w(u(i),{key:0,icon:"carbon:circle-dash",width:"16",class:"loading-icon"})):o.value===s.value?(l(),w(u(i),{key:1,icon:"carbon:checkmark",width:"16",class:"check-icon"})):g("",!0)],10,K))),128))])])):g("",!0),a.value?(l(),r("div",{key:1,class:"backdrop",onClick:n[2]||(n[2]=s=>a.value=!1)})):g("",!0)]))}}),T=V(j,[["__scopeId","data-v-25f759dc"]]);export{T as L};
+import{d as L,u as E,r as p,c as v,P as $,o as B,A as I,a as r,b as l,e as t,f as g,g as h,x as u,t as d,h as x,F as D,l as F,n as N,s as w,Q as U}from"./index-CNsQoPg8.js";import{I as i}from"./iconify-B3l7reUz.js";import{_ as V}from"./_plugin-vue_export-helper-DlAUqK2U.js";const M={class:"language-switcher"},O=["title"],S={class:"current-lang"},z={class:"dropdown-header"},A={class:"language-options"},K=["disabled","onClick"],P={class:"lang-code"},Q={class:"lang-name"},j=L({__name:"index",setup(q){const{locale:_}=E(),a=p(!1),o=v(()=>_.value),f=v(()=>$.opts),b=v(()=>{const e=f.value.find(n=>n.value===o.value);return e?e.title:"Unknown"}),y=()=>{a.value=!a.value},c=p(!1),C=async e=>{if(!(c.value||o.value===e))try{c.value=!0,await U(e),a.value=!1}catch(n){console.error("Failed to change language:",n),a.value=!1}finally{c.value=!1}},k=e=>{e.target.closest(".language-switcher")||(a.value=!1)},m=e=>{e.key==="Escape"&&(a.value=!1)};return B(()=>{document.addEventListener("click",k),document.addEventListener("keydown",m)}),I(()=>{document.removeEventListener("click",k),document.removeEventListener("keydown",m)}),(e,n)=>(l(),r("div",M,[t("button",{class:"language-btn",onClick:y,title:e.$t("language.switch")},[h(u(i),{icon:"carbon:translate",width:"18"}),t("span",S,d(b.value),1),h(u(i),{icon:a.value?"carbon:chevron-up":"carbon:chevron-down",width:"14",class:"chevron"},null,8,["icon"])],8,O),a.value?(l(),r("div",{key:0,class:"language-dropdown",onClick:n[1]||(n[1]=x(()=>{},["stop"]))},[t("div",z,[t("span",null,d(e.$t("language.switch")),1),t("button",{class:"close-btn",onClick:n[0]||(n[0]=s=>a.value=!1)},[h(u(i),{icon:"carbon:close",width:"16"})])]),t("div",A,[(l(!0),r(D,null,F(f.value,s=>(l(),r("button",{key:s.value,class:N(["language-option",{active:o.value===s.value,loading:c.value&&o.value!==s.value}]),disabled:c.value,onClick:G=>C(s.value)},[t("span",P,d(s.value.toUpperCase()),1),t("span",Q,d(s.title),1),c.value&&o.value!==s.value?(l(),w(u(i),{key:0,icon:"carbon:circle-dash",width:"16",class:"loading-icon"})):o.value===s.value?(l(),w(u(i),{key:1,icon:"carbon:checkmark",width:"16",class:"check-icon"})):g("",!0)],10,K))),128))])])):g("",!0),a.value?(l(),r("div",{key:1,class:"backdrop",onClick:n[2]||(n[2]=s=>a.value=!1)})):g("",!0)]))}}),T=V(j,[["__scopeId","data-v-25f759dc"]]);export{T as L};
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/javascript-DNnN4kEa.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/javascript-C0XxeR-n.js
similarity index 81%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/javascript-DNnN4kEa.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/javascript-C0XxeR-n.js
index af74578206..a3b6cd3ad6 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/javascript-DNnN4kEa.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/javascript-C0XxeR-n.js
@@ -1,4 +1,4 @@
-import{conf as t,language as e}from"./typescript-BEZDUO2m.js";import"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+import{conf as t,language as e}from"./typescript-Cfb9k-qV.js";import"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/jsonMode-B8CiFQ0R.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/jsonMode-BNPGVg0I.js
similarity index 99%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/jsonMode-B8CiFQ0R.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/jsonMode-BNPGVg0I.js
index 24b418bf11..b5b40a88c5 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/jsonMode-B8CiFQ0R.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/jsonMode-BNPGVg0I.js
@@ -1,4 +1,4 @@
-var $e=Object.defineProperty;var Ge=(e,n,i)=>n in e?$e(e,n,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[n]=i;var C=(e,n,i)=>Ge(e,typeof n!="symbol"?n+"":n,i);import{m as Qe}from"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+var $e=Object.defineProperty;var Ge=(e,n,i)=>n in e?$e(e,n,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[n]=i;var C=(e,n,i)=>Ge(e,typeof n!="symbol"?n+"":n,i);import{m as Qe}from"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/liquid-B5j_7Uvh.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/liquid-BFayS-os.js
similarity index 94%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/liquid-B5j_7Uvh.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/liquid-BFayS-os.js
index f32c4019a6..717b959387 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/liquid-B5j_7Uvh.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/liquid-BFayS-os.js
@@ -1,4 +1,4 @@
-import{m as l}from"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+import{m as l}from"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/mdx-CDRD_3NO.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/mdx-DeQweMxo.js
similarity index 95%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/mdx-CDRD_3NO.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/mdx-DeQweMxo.js
index 7e765bf251..9f54af3306 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/mdx-CDRD_3NO.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/mdx-DeQweMxo.js
@@ -1,4 +1,4 @@
-import{m as s}from"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+import{m as s}from"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/notFound-BSR8IgwZ.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/notFound-CVrU7YVW.js
similarity index 84%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/notFound-BSR8IgwZ.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/notFound-CVrU7YVW.js
index e366db31ee..389a5ec2c4 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/notFound-BSR8IgwZ.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/notFound-CVrU7YVW.js
@@ -1 +1 @@
-import{_ as n}from"./Java-AI-BYpq8IxI.js";import{d as c,a as i,b as d,e as o,t as r,g as m,i as p,x as l,p as _}from"./index-DA9oYm7y.js";import{I as u}from"./iconify-CyasjfC7.js";import{_ as f}from"./_plugin-vue_export-helper-DlAUqK2U.js";const b={class:"not-found-page"},g={class:"error-container"},h={class:"error-message"},k=c({__name:"notFound",setup(v){const t=_(),a=()=>{t.push("/home")};return(e,s)=>(d(),i("div",b,[o("div",g,[s[0]||(s[0]=o("div",{class:"error-icon"},[o("img",{src:n,alt:"Java-AI",width:"96",height:"96",class:"java-logo"})],-1)),s[1]||(s[1]=o("h1",{class:"error-code"},"404",-1)),o("p",h,r(e.$t("error.notFound")),1),o("button",{class:"back-button",onClick:a},[m(l(u),{icon:"carbon:arrow-left"}),p(" "+r(e.$t("error.backToHome")),1)])])]))}}),N=f(k,[["__scopeId","data-v-57698550"]]);export{N as default};
+import{_ as n}from"./Java-AI-BYpq8IxI.js";import{d as c,a as i,b as d,e as o,t as r,g as m,i as p,x as l,p as _}from"./index-CNsQoPg8.js";import{I as u}from"./iconify-B3l7reUz.js";import{_ as f}from"./_plugin-vue_export-helper-DlAUqK2U.js";const b={class:"not-found-page"},g={class:"error-container"},h={class:"error-message"},k=c({__name:"notFound",setup(v){const t=_(),a=()=>{t.push("/home")};return(e,s)=>(d(),i("div",b,[o("div",g,[s[0]||(s[0]=o("div",{class:"error-icon"},[o("img",{src:n,alt:"Java-AI",width:"96",height:"96",class:"java-logo"})],-1)),s[1]||(s[1]=o("h1",{class:"error-code"},"404",-1)),o("p",h,r(e.$t("error.notFound")),1),o("button",{class:"back-button",onClick:a},[m(l(u),{icon:"carbon:arrow-left"}),p(" "+r(e.$t("error.backToHome")),1)])])]))}}),N=f(k,[["__scopeId","data-v-57698550"]]);export{N as default};
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/python-CM3gi6vP.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/python-a9hcFcOH.js
similarity index 93%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/python-CM3gi6vP.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/python-a9hcFcOH.js
index 090125c037..3e51b60a37 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/python-CM3gi6vP.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/python-a9hcFcOH.js
@@ -1,4 +1,4 @@
-import{m as i}from"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+import{m as i}from"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/razor-Bu5fGBlQ.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/razor-1sBWwWa2.js
similarity index 97%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/razor-Bu5fGBlQ.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/razor-1sBWwWa2.js
index 38a758e4da..2d68bbfda8 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/razor-Bu5fGBlQ.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/razor-1sBWwWa2.js
@@ -1,4 +1,4 @@
-import{m}from"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+import{m}from"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/sidebar-BtIzguw3.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/sidebar-ON4PvQzg.js
similarity index 99%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/sidebar-BtIzguw3.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/sidebar-ON4PvQzg.js
index 8c0dc7d0f6..e4d7550940 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/sidebar-BtIzguw3.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/sidebar-ON4PvQzg.js
@@ -1,2 +1,2 @@
-var m=Object.defineProperty;var T=(n,e,t)=>e in n?m(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>T(n,typeof e!="symbol"?e+"":e,t);import{H as g,r as h,z as P,I as p}from"./index-DA9oYm7y.js";import{L as d}from"./llm-check-BVkAKrj3.js";const V=g("task",()=>{const n=h(null),e=h(""),t=h(!1);return{currentTask:n,taskToInput:e,hasVisitedHome:t,setTask:o=>{console.log("[TaskStore] setTask called with prompt:",o);const u={prompt:o,timestamp:Date.now(),processed:!1};n.value=u,console.log("[TaskStore] Task set, currentTask.value:",n.value)},setTaskToInput:o=>{console.log("[TaskStore] setTaskToInput called with prompt:",o),e.value=o,console.log("[TaskStore] Task to input set:",e.value)},getAndClearTaskToInput:()=>{const o=e.value;return e.value="",console.log("[TaskStore] getAndClearTaskToInput returning:",o),o},markTaskAsProcessed:()=>{console.log("[TaskStore] markTaskAsProcessed called, current task:",n.value),n.value?(n.value.processed=!0,console.log("[TaskStore] Task marked as processed:",n.value)):console.log("[TaskStore] No current task to mark as processed")},clearTask:()=>{n.value=null},hasUnprocessedTask:()=>{const o=n.value&&!n.value.processed;return console.log("[TaskStore] hasUnprocessedTask check - currentTask:",n.value,"result:",o),o},markHomeVisited:()=>{t.value=!0,localStorage.setItem("hasVisitedHome","true")},checkHomeVisited:()=>{const o=localStorage.getItem("hasVisitedHome");return t.value=o==="true",t.value},resetHomeVisited:()=>{t.value=!1,localStorage.removeItem("hasVisitedHome")},emitPlanExecutionRequested:o=>{console.log("[TaskStore] emitPlanExecutionRequested called with payload:",o),window.dispatchEvent(new CustomEvent("plan-execution-requested",{detail:o}))}}});class c{static async generatePlan(e,t){return d.withLlmCheck(async()=>{const s={query:e};t&&(s.existingJson=t);const a=await fetch(`${this.PLAN_TEMPLATE_URL}/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!a.ok)throw new Error(`Failed to generate plan: ${a.status}`);const i=await a.json();if(i.planJson)try{i.plan=JSON.parse(i.planJson)}catch{i.plan={error:"Unable to parse plan data"}}return i})}static async executePlan(e,t){return d.withLlmCheck(async()=>{console.log("[PlanActApiService] executePlan called with:",{planTemplateId:e,rawParam:t});const s={planTemplateId:e};t&&(s.rawParam=t),console.log("[PlanActApiService] Making request to:",`${this.PLAN_TEMPLATE_URL}/executePlanByTemplateId`),console.log("[PlanActApiService] Request body:",s);const a=await fetch(`${this.PLAN_TEMPLATE_URL}/executePlanByTemplateId`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(console.log("[PlanActApiService] Response status:",a.status,a.ok),!a.ok){const l=await a.text();throw console.error("[PlanActApiService] Request failed:",l),new Error(`Failed to execute plan: ${a.status}`)}const i=await a.json();return console.log("[PlanActApiService] executePlan response:",i),i})}static async savePlanTemplate(e,t){const s=await fetch(`${this.PLAN_TEMPLATE_URL}/save`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({planId:e,planJson:t})});if(!s.ok)throw new Error(`Failed to save plan: ${s.status}`);return await s.json()}static async getPlanVersions(e){const t=await fetch(`${this.PLAN_TEMPLATE_URL}/versions`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({planId:e})});if(!t.ok)throw new Error(`Failed to get plan versions: ${t.status}`);return await t.json()}static async getVersionPlan(e,t){const s=await fetch(`${this.PLAN_TEMPLATE_URL}/get-version`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({planId:e,versionIndex:t.toString()})});if(!s.ok)throw new Error(`Failed to get specific version plan: ${s.status}`);return await s.json()}static async getAllPlanTemplates(){const e=await fetch(`${this.PLAN_TEMPLATE_URL}/list`);if(!e.ok)throw new Error(`Failed to get plan template list: ${e.status}`);return await e.json()}static async updatePlanTemplate(e,t,s){return d.withLlmCheck(async()=>{const a={planId:e,query:t};s&&(a.existingJson=s);const i=await fetch(`${this.PLAN_TEMPLATE_URL}/update`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(`Failed to update plan template: ${i.status}`);const l=await i.json();if(l.planJson)try{l.plan=JSON.parse(l.planJson)}catch{l.plan={error:"Unable to parse plan data"}}return l})}static async deletePlanTemplate(e){const t=await fetch(`${this.PLAN_TEMPLATE_URL}/delete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({planId:e})});if(!t.ok)throw new Error(`Failed to delete plan template: ${t.status}`);return await t.json()}static async createCronTask(e){const t=await fetch(this.CRON_TASK_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok)try{const s=await t.json();throw new Error(s.message||`Failed to create cron task: ${t.status}`)}catch{throw new Error(`Failed to create cron task: ${t.status}`)}return await t.json()}}r(c,"PLAN_TEMPLATE_URL","/api/plan-template"),r(c,"CRON_TASK_URL","/api/cron-tasks");class w{constructor(){r(this,"isCollapsed",!0);r(this,"currentTab","list");r(this,"currentPlanTemplateId",null);r(this,"planTemplateList",[]);r(this,"selectedTemplate",null);r(this,"isLoading",!1);r(this,"errorMessage","");r(this,"jsonContent","");r(this,"generatorPrompt","");r(this,"executionParams","");r(this,"isGenerating",!1);r(this,"isExecuting",!1);r(this,"planVersions",[]);r(this,"currentVersionIndex",-1)}parseDateTime(e){return e?Array.isArray(e)&&e.length>=6?new Date(e[0],e[1]-1,e[2],e[3],e[4],e[5],Math.floor(e[6]/1e6)):typeof e=="string"?new Date(e):new Date:new Date}get sortedTemplates(){return[...this.planTemplateList].sort((e,t)=>{const s=this.parseDateTime(e.updateTime??e.createTime);return this.parseDateTime(t.updateTime??t.createTime).getTime()-s.getTime()})}get canRollback(){return this.planVersions.length>1&&this.currentVersionIndex>0}get canRestore(){return this.planVersions.length>1&&this.currentVersionIndex0){const s=this.planVersions[this.planVersions.length-1];this.jsonContent=s,this.currentVersionIndex=this.planVersions.length-1;try{const a=JSON.parse(s);a.prompt&&(this.generatorPrompt=a.prompt),a.params&&(this.executionParams=a.params)}catch{console.warn("Unable to parse JSON content to get prompt information")}}else this.jsonContent="",this.generatorPrompt="",this.executionParams=""}catch(t){throw console.error("Failed to load template data:",t),t}}createNewTemplate(){const e={id:`new-${Date.now()}`,title:p.global.t("sidebar.newTemplateName"),description:p.global.t("sidebar.newTemplateDescription"),createTime:new Date().toISOString(),updateTime:new Date().toISOString()};this.selectedTemplate=e,this.currentPlanTemplateId=null,this.jsonContent="",this.generatorPrompt="",this.executionParams="",this.planVersions=[],this.currentVersionIndex=-1,this.currentTab="config",console.log("[SidebarStore] Created new empty plan template, switching to config tab")}async deleteTemplate(e){if(!e.id){console.warn("[SidebarStore] deleteTemplate: Invalid template object or ID");return}try{await c.deletePlanTemplate(e.id),this.currentPlanTemplateId===e.id&&this.clearSelection(),await this.loadPlanTemplateList(),console.log(`[SidebarStore] Plan template ${e.id} has been deleted`)}catch(t){throw console.error("Failed to delete plan template:",t),await this.loadPlanTemplateList(),t}}clearSelection(){this.currentPlanTemplateId=null,this.selectedTemplate=null,this.jsonContent="",this.generatorPrompt="",this.executionParams="",this.planVersions=[],this.currentVersionIndex=-1,this.currentTab="list"}clearExecutionParams(){this.executionParams=""}rollbackVersion(){this.canRollback&&(this.currentVersionIndex--,this.jsonContent=this.planVersions[this.currentVersionIndex])}restoreVersion(){this.canRestore&&(this.currentVersionIndex++,this.jsonContent=this.planVersions[this.currentVersionIndex])}async saveTemplate(){if(!this.selectedTemplate)return;const e=this.jsonContent.trim();if(!e)throw new Error("Content cannot be empty");try{JSON.parse(e)}catch(t){throw new Error(`Invalid format, please correct and save.
+var m=Object.defineProperty;var T=(n,e,t)=>e in n?m(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>T(n,typeof e!="symbol"?e+"":e,t);import{H as g,r as h,z as P,I as p}from"./index-CNsQoPg8.js";import{L as d}from"./llm-check-BVkAKrj3.js";const V=g("task",()=>{const n=h(null),e=h(""),t=h(!1);return{currentTask:n,taskToInput:e,hasVisitedHome:t,setTask:o=>{console.log("[TaskStore] setTask called with prompt:",o);const u={prompt:o,timestamp:Date.now(),processed:!1};n.value=u,console.log("[TaskStore] Task set, currentTask.value:",n.value)},setTaskToInput:o=>{console.log("[TaskStore] setTaskToInput called with prompt:",o),e.value=o,console.log("[TaskStore] Task to input set:",e.value)},getAndClearTaskToInput:()=>{const o=e.value;return e.value="",console.log("[TaskStore] getAndClearTaskToInput returning:",o),o},markTaskAsProcessed:()=>{console.log("[TaskStore] markTaskAsProcessed called, current task:",n.value),n.value?(n.value.processed=!0,console.log("[TaskStore] Task marked as processed:",n.value)):console.log("[TaskStore] No current task to mark as processed")},clearTask:()=>{n.value=null},hasUnprocessedTask:()=>{const o=n.value&&!n.value.processed;return console.log("[TaskStore] hasUnprocessedTask check - currentTask:",n.value,"result:",o),o},markHomeVisited:()=>{t.value=!0,localStorage.setItem("hasVisitedHome","true")},checkHomeVisited:()=>{const o=localStorage.getItem("hasVisitedHome");return t.value=o==="true",t.value},resetHomeVisited:()=>{t.value=!1,localStorage.removeItem("hasVisitedHome")},emitPlanExecutionRequested:o=>{console.log("[TaskStore] emitPlanExecutionRequested called with payload:",o),window.dispatchEvent(new CustomEvent("plan-execution-requested",{detail:o}))}}});class c{static async generatePlan(e,t){return d.withLlmCheck(async()=>{const s={query:e};t&&(s.existingJson=t);const a=await fetch(`${this.PLAN_TEMPLATE_URL}/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!a.ok)throw new Error(`Failed to generate plan: ${a.status}`);const i=await a.json();if(i.planJson)try{i.plan=JSON.parse(i.planJson)}catch{i.plan={error:"Unable to parse plan data"}}return i})}static async executePlan(e,t){return d.withLlmCheck(async()=>{console.log("[PlanActApiService] executePlan called with:",{planTemplateId:e,rawParam:t});const s={planTemplateId:e};t&&(s.rawParam=t),console.log("[PlanActApiService] Making request to:",`${this.PLAN_TEMPLATE_URL}/executePlanByTemplateId`),console.log("[PlanActApiService] Request body:",s);const a=await fetch(`${this.PLAN_TEMPLATE_URL}/executePlanByTemplateId`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(console.log("[PlanActApiService] Response status:",a.status,a.ok),!a.ok){const l=await a.text();throw console.error("[PlanActApiService] Request failed:",l),new Error(`Failed to execute plan: ${a.status}`)}const i=await a.json();return console.log("[PlanActApiService] executePlan response:",i),i})}static async savePlanTemplate(e,t){const s=await fetch(`${this.PLAN_TEMPLATE_URL}/save`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({planId:e,planJson:t})});if(!s.ok)throw new Error(`Failed to save plan: ${s.status}`);return await s.json()}static async getPlanVersions(e){const t=await fetch(`${this.PLAN_TEMPLATE_URL}/versions`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({planId:e})});if(!t.ok)throw new Error(`Failed to get plan versions: ${t.status}`);return await t.json()}static async getVersionPlan(e,t){const s=await fetch(`${this.PLAN_TEMPLATE_URL}/get-version`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({planId:e,versionIndex:t.toString()})});if(!s.ok)throw new Error(`Failed to get specific version plan: ${s.status}`);return await s.json()}static async getAllPlanTemplates(){const e=await fetch(`${this.PLAN_TEMPLATE_URL}/list`);if(!e.ok)throw new Error(`Failed to get plan template list: ${e.status}`);return await e.json()}static async updatePlanTemplate(e,t,s){return d.withLlmCheck(async()=>{const a={planId:e,query:t};s&&(a.existingJson=s);const i=await fetch(`${this.PLAN_TEMPLATE_URL}/update`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(`Failed to update plan template: ${i.status}`);const l=await i.json();if(l.planJson)try{l.plan=JSON.parse(l.planJson)}catch{l.plan={error:"Unable to parse plan data"}}return l})}static async deletePlanTemplate(e){const t=await fetch(`${this.PLAN_TEMPLATE_URL}/delete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({planId:e})});if(!t.ok)throw new Error(`Failed to delete plan template: ${t.status}`);return await t.json()}static async createCronTask(e){const t=await fetch(this.CRON_TASK_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok)try{const s=await t.json();throw new Error(s.message||`Failed to create cron task: ${t.status}`)}catch{throw new Error(`Failed to create cron task: ${t.status}`)}return await t.json()}}r(c,"PLAN_TEMPLATE_URL","/api/plan-template"),r(c,"CRON_TASK_URL","/api/cron-tasks");class w{constructor(){r(this,"isCollapsed",!0);r(this,"currentTab","list");r(this,"currentPlanTemplateId",null);r(this,"planTemplateList",[]);r(this,"selectedTemplate",null);r(this,"isLoading",!1);r(this,"errorMessage","");r(this,"jsonContent","");r(this,"generatorPrompt","");r(this,"executionParams","");r(this,"isGenerating",!1);r(this,"isExecuting",!1);r(this,"planVersions",[]);r(this,"currentVersionIndex",-1)}parseDateTime(e){return e?Array.isArray(e)&&e.length>=6?new Date(e[0],e[1]-1,e[2],e[3],e[4],e[5],Math.floor(e[6]/1e6)):typeof e=="string"?new Date(e):new Date:new Date}get sortedTemplates(){return[...this.planTemplateList].sort((e,t)=>{const s=this.parseDateTime(e.updateTime??e.createTime);return this.parseDateTime(t.updateTime??t.createTime).getTime()-s.getTime()})}get canRollback(){return this.planVersions.length>1&&this.currentVersionIndex>0}get canRestore(){return this.planVersions.length>1&&this.currentVersionIndex0){const s=this.planVersions[this.planVersions.length-1];this.jsonContent=s,this.currentVersionIndex=this.planVersions.length-1;try{const a=JSON.parse(s);a.prompt&&(this.generatorPrompt=a.prompt),a.params&&(this.executionParams=a.params)}catch{console.warn("Unable to parse JSON content to get prompt information")}}else this.jsonContent="",this.generatorPrompt="",this.executionParams=""}catch(t){throw console.error("Failed to load template data:",t),t}}createNewTemplate(){const e={id:`new-${Date.now()}`,title:p.global.t("sidebar.newTemplateName"),description:p.global.t("sidebar.newTemplateDescription"),createTime:new Date().toISOString(),updateTime:new Date().toISOString()};this.selectedTemplate=e,this.currentPlanTemplateId=null,this.jsonContent="",this.generatorPrompt="",this.executionParams="",this.planVersions=[],this.currentVersionIndex=-1,this.currentTab="config",console.log("[SidebarStore] Created new empty plan template, switching to config tab")}async deleteTemplate(e){if(!e.id){console.warn("[SidebarStore] deleteTemplate: Invalid template object or ID");return}try{await c.deletePlanTemplate(e.id),this.currentPlanTemplateId===e.id&&this.clearSelection(),await this.loadPlanTemplateList(),console.log(`[SidebarStore] Plan template ${e.id} has been deleted`)}catch(t){throw console.error("Failed to delete plan template:",t),await this.loadPlanTemplateList(),t}}clearSelection(){this.currentPlanTemplateId=null,this.selectedTemplate=null,this.jsonContent="",this.generatorPrompt="",this.executionParams="",this.planVersions=[],this.currentVersionIndex=-1,this.currentTab="list"}clearExecutionParams(){this.executionParams=""}rollbackVersion(){this.canRollback&&(this.currentVersionIndex--,this.jsonContent=this.planVersions[this.currentVersionIndex])}restoreVersion(){this.canRestore&&(this.currentVersionIndex++,this.jsonContent=this.planVersions[this.currentVersionIndex])}async saveTemplate(){if(!this.selectedTemplate)return;const e=this.jsonContent.trim();if(!e)throw new Error("Content cannot be empty");try{JSON.parse(e)}catch(t){throw new Error(`Invalid format, please correct and save.
Error: `+t.message)}try{const t=await c.savePlanTemplate(this.selectedTemplate.id,e);return this.currentVersionIndext in e?N(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var _=(e,t,r)=>M(e,typeof t!="symbol"?t+"":t,r);import{t as R,m as K}from"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+var N=Object.defineProperty;var M=(e,t,r)=>t in e?N(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var _=(e,t,r)=>M(e,typeof t!="symbol"?t+"":t,r);import{t as R,m as K}from"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/typescript-BEZDUO2m.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/typescript-Cfb9k-qV.js
similarity index 95%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/typescript-BEZDUO2m.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/typescript-Cfb9k-qV.js
index ec3c94fc35..ca9c0befa2 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/typescript-BEZDUO2m.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/typescript-Cfb9k-qV.js
@@ -1,4 +1,4 @@
-import{m as s}from"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+import{m as s}from"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/useMessage-fgXJFj_8.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/useMessage-BR4qCw-P.js
similarity index 95%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/useMessage-fgXJFj_8.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/useMessage-BR4qCw-P.js
index 12a3592838..b06d7ad866 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/useMessage-fgXJFj_8.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/useMessage-BR4qCw-P.js
@@ -1 +1 @@
-import{d as v,r as n,s as g,b as m,g as d,k as _,a as w,f as k,e as T,x as b,t as x,n as y,T as C,C as M,O as B,z as $}from"./index-DA9oYm7y.js";import{I as N}from"./iconify-CyasjfC7.js";import{_ as V}from"./_plugin-vue_export-helper-DlAUqK2U.js";const z=v({__name:"Toast",setup(e,{expose:s}){const a=n(!1),c=n(""),t=n("success"),l=n("carbon:checkmark"),u=n(3e3),p=(i,r="success",h=3e3)=>{c.value=i,t.value=r,l.value=r==="success"?"carbon:checkmark":"carbon:error",u.value=h,a.value=!0,setTimeout(()=>{a.value=!1},u.value)},f=()=>{a.value=!1};return s({show:p}),(i,r)=>(m(),g(M,{to:"body"},[d(C,{name:"slide"},{default:_(()=>[a.value?(m(),w("div",{key:0,class:y(["toast",`toast--${t.value}`]),onClick:f},[d(b(N),{icon:l.value,class:"toast-icon"},null,8,["icon"]),T("span",null,x(c.value),1)],2)):k("",!0)]),_:1})]))}}),E=V(z,[["__scopeId","data-v-581895ae"]]);let o=null;const O=()=>{if(!o){const e=B(E),s=document.createElement("div");document.body.appendChild(s),o=e.mount(s)}return{success:(e,s)=>{o==null||o.show(e,"success",s)},error:(e,s)=>{o==null||o.show(e,"error",s)}}};function j(){const e=$({show:!1,text:"",type:"success"});return{message:e,showMessage:(a,c="success")=>{console.log(`Showing message: ${a}, Type: ${c}`),e.text=a,e.type=c,e.show=!0;const t=c==="error"?5e3:3e3;console.log(`Message will be automatically hidden after ${t}ms`),setTimeout(()=>{e.show=!1,console.log("Message hidden")},t)}}}export{j as a,O as u};
+import{d as v,r as n,s as g,b as m,g as d,k as _,a as w,f as k,e as T,x as b,t as x,n as y,T as C,C as M,O as B,z as $}from"./index-CNsQoPg8.js";import{I as N}from"./iconify-B3l7reUz.js";import{_ as V}from"./_plugin-vue_export-helper-DlAUqK2U.js";const z=v({__name:"Toast",setup(e,{expose:s}){const a=n(!1),c=n(""),t=n("success"),l=n("carbon:checkmark"),u=n(3e3),p=(i,r="success",h=3e3)=>{c.value=i,t.value=r,l.value=r==="success"?"carbon:checkmark":"carbon:error",u.value=h,a.value=!0,setTimeout(()=>{a.value=!1},u.value)},f=()=>{a.value=!1};return s({show:p}),(i,r)=>(m(),g(M,{to:"body"},[d(C,{name:"slide"},{default:_(()=>[a.value?(m(),w("div",{key:0,class:y(["toast",`toast--${t.value}`]),onClick:f},[d(b(N),{icon:l.value,class:"toast-icon"},null,8,["icon"]),T("span",null,x(c.value),1)],2)):k("",!0)]),_:1})]))}}),E=V(z,[["__scopeId","data-v-581895ae"]]);let o=null;const O=()=>{if(!o){const e=B(E),s=document.createElement("div");document.body.appendChild(s),o=e.mount(s)}return{success:(e,s)=>{o==null||o.show(e,"success",s)},error:(e,s)=>{o==null||o.show(e,"error",s)}}};function j(){const e=$({show:!1,text:"",type:"success"});return{message:e,showMessage:(a,c="success")=>{console.log(`Showing message: ${a}, Type: ${c}`),e.text=a,e.type=c,e.show=!0;const t=c==="error"?5e3:3e3;console.log(`Message will be automatically hidden after ${t}ms`),setTimeout(()=>{e.show=!1,console.log("Message hidden")},t)}}}export{j as a,O as u};
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/xml-IzzTFP6G.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/xml-CXoMhdUk.js
similarity index 90%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/xml-IzzTFP6G.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/xml-CXoMhdUk.js
index f0e2fb3ff6..938bc2887d 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/xml-IzzTFP6G.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/xml-CXoMhdUk.js
@@ -1,4 +1,4 @@
-import{m as r}from"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+import{m as r}from"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/yaml-C27Xr4Pr.js b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/yaml-D4773nTm.js
similarity index 94%
rename from spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/yaml-C27Xr4Pr.js
rename to spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/yaml-D4773nTm.js
index db1d345e08..ef1b046768 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/yaml-C27Xr4Pr.js
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/assets/yaml-D4773nTm.js
@@ -1,4 +1,4 @@
-import{m as i}from"./index-Dqcw8yKQ.js";import"./index-DA9oYm7y.js";import"./iconify-CyasjfC7.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-fgXJFj_8.js";import"./index-D2fTT4wr.js";/*!-----------------------------------------------------------------------------
+import{m as i}from"./index-BGKqtLX6.js";import"./index-CNsQoPg8.js";import"./iconify-B3l7reUz.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./useMessage-BR4qCw-P.js";import"./index-rqS3tjXd.js";/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
* Released under the MIT license
diff --git a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/index.html b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/index.html
index abf26d89c1..caf055c6f7 100644
--- a/spring-ai-alibaba-jmanus/src/main/resources/static/ui/index.html
+++ b/spring-ai-alibaba-jmanus/src/main/resources/static/ui/index.html
@@ -20,7 +20,7 @@
JTaskPoilt
-
+
diff --git a/spring-ai-alibaba-jmanus/ui-vue3/src/base/i18n/index.ts b/spring-ai-alibaba-jmanus/ui-vue3/src/base/i18n/index.ts
index 465fbd1187..7ff1e1e1a7 100644
--- a/spring-ai-alibaba-jmanus/ui-vue3/src/base/i18n/index.ts
+++ b/spring-ai-alibaba-jmanus/ui-vue3/src/base/i18n/index.ts
@@ -55,7 +55,7 @@ export const changeLanguage = async (locale: string) => {
}
/**
- * Change language during initialization and reset all agents
+ * Change language during initialization and reset all agents and prompts
* This function is used during the initial setup process
*/
export const changeLanguageWithAgentReset = async (locale: string) => {
@@ -63,8 +63,24 @@ export const changeLanguageWithAgentReset = async (locale: string) => {
await changeLanguage(locale)
try {
+ // Reset prompts to the new language
+ const promptResponse = await fetch(`/admin/prompts/switch-language?language=${locale}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+
+ if (promptResponse.ok) {
+ console.log(`Successfully reset prompts to language: ${locale}`)
+ } else {
+ const promptError = await promptResponse.text()
+ console.error(`Failed to reset prompts to language: ${locale}`, promptError)
+ // Continue with agent initialization even if prompt reset fails
+ }
+
// Initialize agents with the new language (used during initial setup)
- const response = await fetch('/api/agent-management/initialize', {
+ const agentResponse = await fetch('/api/agent-management/initialize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -72,16 +88,16 @@ export const changeLanguageWithAgentReset = async (locale: string) => {
body: JSON.stringify({ language: locale }),
})
- if (response.ok) {
- const result = await response.json()
+ if (agentResponse.ok) {
+ const result = await agentResponse.json()
console.log(`Successfully initialized agents with language: ${locale}`, result)
} else {
- const error = await response.json()
+ const error = await agentResponse.json()
console.error(`Failed to initialize agents with language: ${locale}`, error)
throw new Error(error.error || 'Failed to initialize agents')
}
} catch (error) {
- console.error('Error initializing agents during language change:', error)
+ console.error('Error initializing agents and prompts during language change:', error)
throw error
}
}
From 5ab2da37a4640dd0efff62c83b2f3189404b75fe Mon Sep 17 00:00:00 2001
From: Albumen Kevin
Date: Fri, 1 Aug 2025 23:07:50 +0800
Subject: [PATCH 5/5] feat(jmanus): move tool parameter and description to
resources
---
.../manus/tool/BrowserUseToolSpringTest.java | 662 ------------------
.../example/manus/tool/TerminateToolTest.java | 236 -------
2 files changed, 898 deletions(-)
delete mode 100644 spring-ai-alibaba-jmanus/src/test/java/com/alibaba/cloud/ai/example/manus/tool/BrowserUseToolSpringTest.java
delete mode 100644 spring-ai-alibaba-jmanus/src/test/java/com/alibaba/cloud/ai/example/manus/tool/TerminateToolTest.java
diff --git a/spring-ai-alibaba-jmanus/src/test/java/com/alibaba/cloud/ai/example/manus/tool/BrowserUseToolSpringTest.java b/spring-ai-alibaba-jmanus/src/test/java/com/alibaba/cloud/ai/example/manus/tool/BrowserUseToolSpringTest.java
deleted file mode 100644
index 966816d676..0000000000
--- a/spring-ai-alibaba-jmanus/src/test/java/com/alibaba/cloud/ai/example/manus/tool/BrowserUseToolSpringTest.java
+++ /dev/null
@@ -1,662 +0,0 @@
-/*
- * Copyright 2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.cloud.ai.example.manus.tool;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.alibaba.cloud.ai.example.manus.dynamic.prompt.service.PromptService;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.MethodOrderer;
-import org.junit.jupiter.api.Order;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-
-import com.alibaba.cloud.ai.example.manus.OpenManusSpringBootApplication;
-import com.alibaba.cloud.ai.example.manus.agent.AgentState;
-import com.alibaba.cloud.ai.example.manus.agent.BaseAgent;
-import com.alibaba.cloud.ai.example.manus.config.ManusProperties;
-import com.alibaba.cloud.ai.example.manus.llm.LlmService;
-import com.alibaba.cloud.ai.example.manus.recorder.PlanExecutionRecorder;
-import com.alibaba.cloud.ai.example.manus.tool.browser.BrowserUseTool;
-import com.alibaba.cloud.ai.example.manus.tool.browser.ChromeDriverService;
-import com.alibaba.cloud.ai.example.manus.tool.browser.actions.BrowserRequestVO;
-import com.alibaba.cloud.ai.example.manus.tool.browser.actions.GetElementPositionByNameAction;
-import com.alibaba.cloud.ai.example.manus.tool.browser.actions.MoveToAndClickAction;
-import com.alibaba.cloud.ai.example.manus.tool.code.ToolExecuteResult;
-import com.alibaba.cloud.ai.example.manus.tool.innerStorage.SmartContentSavingService;
-import com.alibaba.cloud.ai.example.manus.planning.PlanningFactory.ToolCallBackContext;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.microsoft.playwright.Page;
-
-/**
- * Spring integration test class for BrowserUseTool using real Spring context to test
- * BrowserUseTool functionality
- */
-@SpringBootTest(classes = OpenManusSpringBootApplication.class)
-@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-@Disabled("For local testing only, skip in CI environment") // Add this line
-class BrowserUseToolSpringTest {
-
- private static final Logger log = LoggerFactory.getLogger(BrowserUseToolSpringTest.class);
-
- @Autowired
- private ChromeDriverService chromeDriverService;
-
- @Autowired
- private SmartContentSavingService innerStorageService;
-
- private BrowserUseTool browserUseTool;
-
- @Autowired
- private LlmService llmService;
-
- @Autowired
- private PlanExecutionRecorder planExecutionRecorder;
-
- @Autowired
- private ManusProperties manusProperties;
-
- @Autowired
- private PromptService promptService;
-
- @Autowired
- private ObjectMapper objectMapper;
-
- @BeforeEach
- void setUp() {
-
- manusProperties.setBrowserHeadless(false);
- manusProperties.setDebugDetail(true);
- chromeDriverService.setManusProperties(manusProperties);
- browserUseTool = new BrowserUseTool(chromeDriverService, innerStorageService, objectMapper);
- DummyBaseAgent agent = new DummyBaseAgent(llmService, planExecutionRecorder, manusProperties, promptService);
- agent.setCurrentPlanId("plan_123123124124124");
- browserUseTool.setCurrentPlanId(agent.getCurrentPlanId());
-
- }
-
- private static class DummyBaseAgent extends BaseAgent {
-
- public DummyBaseAgent(LlmService llmService, PlanExecutionRecorder planExecutionRecorder,
- ManusProperties manusProperties, PromptService promptService) {
- super(llmService, planExecutionRecorder, manusProperties, new HashMap<>(), promptService);
-
- }
-
- @Override
- public String getName() {
- return "DummyAgent";
- }
-
- @Override
- public String getDescription() {
- return "A dummy agent for testing";
- }
-
- @Override
- protected Message getNextStepWithEnvMessage() {
- return null;
- }
-
- @Override
- public List getToolCallList() {
- return null;
- }
-
- @Override
- public ToolCallBackContext getToolCallBackContext(String toolKey) {
- return null;
- }
-
- @Override
- protected AgentExecResult step() {
- return new AgentExecResult("Dummy step executed", AgentState.COMPLETED);
- }
-
- @Override
- protected Message getThinkMessage() {
- return null;
- }
-
- @Override
- public void clearUp(String planId) {
- return;
- }
-
- }
-
- @Test
- @Order(1)
- @DisplayName("Test browser search for 'Hello World'")
- void testHelloWorldSearch() {
- try {
- // Step 1: Navigate to Baidu
- log.info("Step 1: Navigate to Baidu");
- ToolExecuteResult navigateResult = executeAction("navigate", "https://www.baidu.com");
- Assertions.assertEquals("successfully navigated to https://www.baidu.com", navigateResult.getOutput(),
- "Failed to navigate to Baidu");
- Page page = browserUseTool.getDriver().getCurrentPage();
-
- // Step 2: Get and verify interactive elements
- log.info("Step 2: Get interactive elements and analyze");
- Map state = browserUseTool.getCurrentState(page);
- String elements = (String) state.get("interactive_elements");
- Assertions.assertNotNull(elements, "Failed to get interactive elements");
- log.info("Retrieved interactive elements: {}", elements);
-
- // Step 3: Find the search box
- log.info("Step 3: Locate search box");
- int searchInputIndex = -1;
- String[] elementLines = elements.split("\n");
- for (int i = 0; i < elementLines.length; i++) {
- if (elementLines[i].contains("name=\"wd\"") || elementLines[i].contains("id=\"kw\"")) { // Baidu
- // search
- // box
- // characteristics
- searchInputIndex = i;
- break;
- }
- }
- Assertions.assertNotEquals(-1, searchInputIndex, "Search box not found");
- log.info("Found search box index: {}", searchInputIndex);
-
- // Step 4: Input text in the search box
- log.info("Step 4: Input 'Hello World' in the search box");
- ToolExecuteResult inputResult = executeAction("input_text", null, searchInputIndex, "Hello World");
- Assertions.assertTrue(inputResult.getOutput().contains("Hello World"),
- "Failed to input text in search box");
-
- // Step 5: Re-get state and find search button
- log.info("Step 5: Locate search button");
-
- state = browserUseTool.getCurrentState(page);
- elements = (String) state.get("interactive_elements");
- int searchButtonIndex = -1;
- elementLines = elements.split("\n");
- for (int i = 0; i < elementLines.length; i++) {
- if (elementLines[i].contains("value=\"百度一下\"") || elementLines[i].contains(">百度一下<")) {
- searchButtonIndex = i;
- break;
- }
- }
- Assertions.assertNotEquals(-1, searchButtonIndex, "Search button not found");
- log.info("Found search button index: {}", searchButtonIndex);
-
- // Step 6: Click search button
- log.info("Step 6: Click search button");
- ToolExecuteResult clickResult = executeAction("click", null, searchButtonIndex, null);
- Assertions.assertTrue(clickResult.getOutput().contains("clicked"),
- "Failed to click search button: " + clickResult.getOutput());
-
- // Step 7: Wait and verify search results
- log.info("Step 7: Wait for page load and get search results");
- Thread.sleep(2000); // Wait for page load
- ToolExecuteResult textResult = executeAction("get_text", null);
- String searchResults = textResult.getOutput();
- Assertions.assertTrue(searchResults.contains("Hello World"),
- "Did not find 'Hello World' in search results");
-
- state = browserUseTool.getCurrentState(page);
- elements = (String) state.get("interactive_elements");
- searchButtonIndex = -1;
- elementLines = elements.split("\n");
- for (int i = 0; i < elementLines.length; i++) {
- if (elementLines[i].contains("hello world") && elementLines[i].contains("百度百科")) {
- searchButtonIndex = i;
- break;
- }
- }
- clickResult = executeAction("click", null, searchButtonIndex, null);
- Assertions.assertTrue(clickResult.getOutput().contains("clicked"),
- "Failed to click search button: " + clickResult.getOutput());
-
- log.info("Login success");
-
- }
- catch (Exception e) {
- log.error("Error occurred during test", e);
- Assertions.fail("Test execution failed: " + e.getMessage());
- }
- }
-
- @Test
- @Order(2)
- @DisplayName("Test element positioning by name and click Baidu search button")
- void testGetElementPositionAndMoveToClick() {
- try {
- // Step 1: Navigate to Baidu
- log.info("Step 1: Navigate to Baidu");
- ToolExecuteResult navigateResult = executeAction("navigate", "https://www.baidu.com");
- Assertions.assertEquals("successfully navigated to https://www.baidu.com", navigateResult.getOutput(),
- "Failed to navigate to Baidu");
- Page page = browserUseTool.getDriver().getCurrentPage();
-
- // Step 2: Get and verify interactive elements
- log.info("Step 2: Get interactive elements and analyze");
- Map state = browserUseTool.getCurrentState(page);
- String elements = (String) state.get("interactive_elements");
- Assertions.assertNotNull(elements, "Failed to get interactive elements");
- log.info("Retrieved interactive elements: {}", elements);
-
- // Step 3: Find the search box
- log.info("Step 3: Locate search box");
- int searchInputIndex = -1;
- String[] elementLines = elements.split("\n");
- for (int i = 0; i < elementLines.length; i++) {
- if (elementLines[i].contains("name=\"wd\"") || elementLines[i].contains("id=\"kw\"")) { // Baidu
- // search
- // box
- // characteristics
- searchInputIndex = i;
- break;
- }
- }
- Assertions.assertNotEquals(-1, searchInputIndex, "Search box not found");
- log.info("Found search box index: {}", searchInputIndex);
-
- // Step 4: Input text in search box
- log.info("Step 4: Input 'Hello World' in search box");
- ToolExecuteResult inputResult = executeAction("input_text", null, searchInputIndex, "Hello World");
- Assertions.assertTrue(inputResult.getOutput().contains("Hello World"),
- "Failed to input text in search box");
-
- // Step 5: Use GetElementPositionByNameAction to find search button
- log.info("Step 5: Use GetElementPositionByNameAction to find '百度一下' button");
- BrowserRequestVO positionRequest = new BrowserRequestVO();
- positionRequest.setElementName("百度一下");
- GetElementPositionByNameAction positionAction = new GetElementPositionByNameAction(browserUseTool,
- objectMapper);
- ToolExecuteResult positionResult = positionAction.execute(positionRequest);
- log.info("Retrieved '百度一下' button position info: {}", positionResult.getOutput());
-
- // Parse JSON result to get coordinates
- List> positionsList = objectMapper.readValue(positionResult.getOutput(), new TypeReference>() {
- });
- Assertions.assertFalse(positionsList.isEmpty(), "'百度一下' button not found");
- Map, ?> elementPosition = (Map, ?>) positionsList.get(0);
- Double xNumber = (Double) elementPosition.get("x");
- Double yNumber = (Double) elementPosition.get("y");
- Double x = xNumber.doubleValue();
- Double y = yNumber.doubleValue();
- log.info("'百度一下' button coordinates: x={}, y={}", x, y);
-
- // Step 6: Use MoveToAndClickAction to click search button
- log.info("Step 6: Use MoveToAndClickAction to click '百度一下' button");
- BrowserRequestVO clickRequest = new BrowserRequestVO();
- clickRequest.setPositionX(x);
- clickRequest.setPositionY(y);
- MoveToAndClickAction clickAction = new MoveToAndClickAction(browserUseTool);
- ToolExecuteResult clickResult = clickAction.execute(clickRequest);
- log.info("Click result: {}", clickResult.getOutput());
- Assertions.assertTrue(clickResult.getOutput().contains("Clicked"), "Failed to click '百度一下' button");
-
- // Step 7: Wait and verify search results
- log.info("Step 7: Wait for page load and get search results");
- Thread.sleep(2000); // Wait for page load
- ToolExecuteResult textResult = executeAction("get_text", null);
- String searchResults = textResult.getOutput();
- Assertions.assertTrue(searchResults.contains("Hello World"),
- "Did not find 'Hello World' in search results");
-
- // Step 8: Use GetElementPositionByNameAction to find and click Baidu Baike
- // link
- log.info("Step 8: Use GetElementPositionByNameAction to find '百度百科' link");
- BrowserRequestVO baikePositionRequest = new BrowserRequestVO();
- baikePositionRequest.setElementName("百度百科");
- GetElementPositionByNameAction baikePositionAction = new GetElementPositionByNameAction(browserUseTool,
- objectMapper);
- ToolExecuteResult baikePositionResult = baikePositionAction.execute(baikePositionRequest);
- log.info("Retrieved '百度百科' link position info: {}", baikePositionResult.getOutput());
-
- // Parse JSON result to get Baidu Baike link coordinates
- List> baikePositionsList = objectMapper.readValue(baikePositionResult.getOutput(),
- new TypeReference>() {
- });
-
- if (!baikePositionsList.isEmpty()) {
- // Find Baidu Baike link containing "hello world" related content
- Map, ?> targetPosition = null;
- for (Object positionObj : baikePositionsList) {
- Map, ?> position = (Map, ?>) positionObj;
- String elementText = (String) position.get("elementText");
- if (elementText != null && elementText.toLowerCase().contains("hello world(程序代码调试常用文本) - 百度百科")) {
- targetPosition = position;
- break;
- }
- }
-
- if (targetPosition != null) {
- Double baikeX = (Double) targetPosition.get("x");
- Double baikeY = (Double) targetPosition.get("y");
- log.info("'百度百科' link coordinates: x={}, y={}", baikeX, baikeY);
-
- // Use MoveToAndClickAction to click Baidu Baike link
- log.info("Use MoveToAndClickAction to click '百度百科' link");
- BrowserRequestVO baikeClickRequest = new BrowserRequestVO();
- baikeClickRequest.setPositionX(baikeX);
- baikeClickRequest.setPositionY(baikeY);
- MoveToAndClickAction baikeClickAction = new MoveToAndClickAction(browserUseTool);
- ToolExecuteResult baikeClickResult = baikeClickAction.execute(baikeClickRequest);
- log.info("Baidu Baike link click result: {}", baikeClickResult.getOutput());
- Assertions.assertTrue(baikeClickResult.getOutput().contains("Clicked"),
- "Failed to click '百度百科' link");
- log.info("Successfully clicked Baidu Baike link");
- }
- else {
- log.warn("Did not find Baidu Baike link containing 'hello world'");
- }
- }
- else {
- log.warn("Did not find '百度百科' link");
- }
-
- log.info("Test completed successfully!");
-
- }
- catch (Exception e) {
- log.error("Error occurred during test", e);
- Assertions.fail("Test execution failed: " + e.getMessage());
- }
- }
-
- /**
- * Generic method to navigate to specified URL and verify interactive elements
- * @param tool BrowserUseTool instance
- * @param url Target URL
- * @param expectedElements List of expected element keywords to appear on the page
- * @return Retrieved interactive elements string
- */
- private String navigateAndVerifyElements(BrowserUseTool tool, String url, List expectedElements) {
- // Step 1: Navigate to specified URL
- log.info("Step 1: Navigate to {}", url);
- ToolExecuteResult navigateResult = executeAction("navigate", url);
- Assertions.assertEquals("successfully navigated to " + url, navigateResult.getOutput(), "Navigation failed");
-
- Page page = browserUseTool.getDriver().getCurrentPage();
- // Step 2: Get and verify interactive elements
- log.info("Step 2: Get interactive elements");
- Map state = tool.getCurrentState(page);
- String elements = (String) state.get("interactive_elements");
- Assertions.assertNotNull(elements, "Failed to get interactive elements");
- log.info("Retrieved interactive elements: {}", elements);
-
- // Step 3: Verify expected elements
- log.info("Step 3: Verify expected elements");
- String[] elementLines = elements.split("\n");
- for (String expectedElement : expectedElements) {
- boolean found = false;
- for (String line : elementLines) {
- if (line.contains(expectedElement)) {
- found = true;
- break;
- }
- }
- log.info("Expected element '{}' found: {}", expectedElement, found);
- }
- return elements;
- }
-
- @Test
- @Order(4)
- @DisplayName("Test navigate to specified URL and get interactive elements")
- void testNavigateAndGetElements() {
- // Test navigating to different pages and verify interactive elements
- List baiduExpected = Arrays.asList("Baidu Search", "input");
- String baiduElements = navigateAndVerifyElements(browserUseTool, "https://www.baidu.com", baiduExpected);
- Assertions.assertNotNull(baiduElements, "Failed to get Baidu interactive elements");
-
- List githubExpected = Arrays.asList("search", "Sign");
- String githubElements = navigateAndVerifyElements(browserUseTool, "https://github.com", githubExpected);
- Assertions.assertNotNull(githubElements, "Failed to get GitHub interactive elements");
-
- log.info("All navigation tests passed!");
- }
-
- @Test
- @Order(5)
- @DisplayName("Test GitHub search page elements")
- void testGitHubSearch() {
- // Test simple GitHub page loading
- ToolExecuteResult navigateResult = executeAction("navigate", "https://github.com");
- Assertions.assertEquals("successfully navigated to https://github.com", navigateResult.getOutput(),
- "Failed to navigate to GitHub");
-
- Page page = browserUseTool.getDriver().getCurrentPage();
- Map state = browserUseTool.getCurrentState(page);
- String elements = (String) state.get("interactive_elements");
- Assertions.assertNotNull(elements, "Failed to get GitHub interactive elements");
- log.info("GitHub interactive elements: {}", elements);
- }
-
- @Test
- @Order(6)
- @DisplayName("Test Nacos page elements")
- void testNacosPageLink() {
- // Test simple Nacos page loading
- ToolExecuteResult navigateResult = executeAction("navigate", "https://nacos.io");
- Assertions.assertEquals("successfully navigated to https://nacos.io", navigateResult.getOutput(),
- "Failed to navigate to Nacos");
-
- Page page = browserUseTool.getDriver().getCurrentPage();
- Map state = browserUseTool.getCurrentState(page);
- String elements = (String) state.get("interactive_elements");
- Assertions.assertNotNull(elements, "Failed to get Nacos interactive elements");
- log.info("Nacos interactive elements: {}", elements);
- }
-
- @Test
- @Order(6)
- @DisplayName("Test Baidu homepage elements")
- void testBaiduElements() {
- // Test simple Baidu page loading
- ToolExecuteResult navigateResult = executeAction("navigate", "https://www.baidu.com");
- Assertions.assertEquals("successfully navigated to https://www.baidu.com", navigateResult.getOutput(),
- "Failed to navigate to Baidu");
-
- Page page = browserUseTool.getDriver().getCurrentPage();
- Map state = browserUseTool.getCurrentState(page);
- String elements = (String) state.get("interactive_elements");
- Assertions.assertNotNull(elements, "Failed to get Baidu interactive elements");
- log.info("Baidu interactive elements: {}", elements);
- }
-
- @Test
- @Order(7)
- @DisplayName("Test CSDN website login functionality")
- void testCsdnLogin() {
- try {
- // Step 1: Navigate to CSDN
- log.info("Step 1: Navigate to CSDN");
- ToolExecuteResult navigateResult = executeAction("navigate", "https://passport.csdn.net/login");
- Assertions.assertEquals("successfully navigated to https://passport.csdn.net/login",
- navigateResult.getOutput(), "Failed to navigate to CSDN login");
- Page page = browserUseTool.getDriver().getCurrentPage();
-
- // Step 2: Get and verify interactive elements
- log.info("Step 2: Get interactive elements and analyze");
- Map state = browserUseTool.getCurrentState(page);
- String elements = (String) state.get("interactive_elements");
- Assertions.assertNotNull(elements, "Failed to get interactive elements");
- log.info("Retrieved interactive elements: {}", elements);
-
- // Step 3: Find username input field (not currently used but defined for
- // potential future use)
- log.info("Step 3: Locate username input box");
- @SuppressWarnings("unused")
- int usernameInputIndex = -1;
- String[] elementLines = elements.split("\n");
- int loginButtonIndex = -1;
-
- for (String line : elementLines) {
- // 从每行开头提取元素的实际索引号,格式如 [195] positionsList = objectMapper.readValue(positionResult.getOutput(), new TypeReference>() {
- });
- Assertions.assertFalse(positionsList.isEmpty(), "未找到'APP登录'元素");
-
- // 获取第一个匹配元素的位置信息
- Map, ?> elementPosition = (Map, ?>) positionsList.get(0);
- Double xNumber = (Double) elementPosition.get("x");
- Double yNumber = (Double) elementPosition.get("y");
- log.info("验证码登录元素坐标: x={}, y={}", xNumber, yNumber);
-
- // 使用MoveToAndClickAction通过坐标点击元素
- BrowserRequestVO clickRequest = new BrowserRequestVO();
- clickRequest.setPositionX(xNumber);
- clickRequest.setPositionY(yNumber);
-
- MoveToAndClickAction clickAction = new MoveToAndClickAction(browserUseTool);
- ToolExecuteResult clickResult = clickAction.execute(clickRequest);
- log.info("点击结果: {}", clickResult.getOutput());
- Assertions.assertTrue(clickResult.getOutput().contains("Clicked"), "点击验证码登录元素失败");
-
- // 等待密码登录表单加载
- log.info("等待密码登录表单加载...");
- Thread.sleep(1000);
-
- // 获取更新后的交互元素
- state = browserUseTool.getCurrentState(page);
- elements = (String) state.get("interactive_elements");
- elementLines = elements.split("\n");
-
- // 步骤5: 查找手机号输入框
- log.info("步骤5: 查找手机号输入框");
- int phoneInputIndex = -1;
- int verifyCodeButtonIndex = -1;
-
- for (String line : elementLines) {
- // 从每行开头提取元素的实际索引号,格式如 [195] testColumns = Arrays.asList("name", "age", "city");
- terminateTool = new TerminateTool("test-plan-123", testColumns);
- }
-
- @Test
- @DisplayName("测试 generateParametersJson 方法返回的 JSON 结构")
- void testGenerateParametersJson() {
- // 测试不同的列配置
- List columns1 = Arrays.asList("name", "age");
- List columns2 = Arrays.asList("id", "title", "description", "status");
- List emptyColumns = Arrays.asList();
- List nullColumns = null;
-
- System.out.println("=== generateParametersJson 测试结果 ===");
-
- // 测试1: 普通列
- String json1 = getParametersJsonViaReflection(columns1);
- System.out.println("测试1 - 普通列 [name, age]:");
- System.out.println(json1);
- System.out.println();
-
- // 测试2: 多列
- String json2 = getParametersJsonViaReflection(columns2);
- System.out.println("测试2 - 多列 [id, title, description, status]:");
- System.out.println(json2);
- System.out.println();
-
- // 测试3: 空列表
- String json3 = getParametersJsonViaReflection(emptyColumns);
- System.out.println("测试3 - 空列表 []:");
- System.out.println(json3);
- System.out.println();
-
- // 测试4: null列表
- String json4 = getParametersJsonViaReflection(nullColumns);
- System.out.println("测试4 - null列表:");
- System.out.println(json4);
- System.out.println();
-
- // 验证JSON包含必要的结构
- assertTrue(json1.contains("\"type\": \"object\""));
- assertTrue(json1.contains("\"properties\""));
- assertTrue(json1.contains("\"columns\""));
- assertTrue(json1.contains("\"data\""));
- assertTrue(json1.contains("\"required\": [\"columns\", \"data\"]"));
- }
-
- @Test
- @DisplayName("测试 getCurrentToolStateString 方法在不同状态下的返回内容")
- void testGetCurrentToolStateString() {
- System.out.println("=== getCurrentToolStateString 测试结果 ===");
-
- // 测试1: 初始状态
- String initialState = terminateTool.getCurrentToolStateString();
- System.out.println("测试1 - 初始状态:");
- System.out.println(initialState);
- System.out.println();
-
- // 测试2: 执行终止操作后的状态
- Map terminateInput = new HashMap<>();
- terminateInput.put("columns", Arrays.asList("name", "status"));
- terminateInput.put("data", Arrays.asList(Arrays.asList("Alice", "completed"), Arrays.asList("Bob", "pending")));
-
- terminateTool.run(terminateInput);
- String terminatedState = terminateTool.getCurrentToolStateString();
- System.out.println("测试2 - 终止后状态:");
- System.out.println(terminatedState);
- System.out.println();
-
- // 验证状态变化
- assertFalse(initialState.contains("🛑 Terminated"));
- assertTrue(initialState.contains("⚡ Active"));
-
- assertTrue(terminatedState.contains("🛑 Terminated"));
- assertFalse(terminatedState.contains("⚡ Active"));
- assertTrue(terminatedState.contains("test-plan-123"));
- }
-
- @Test
- @DisplayName("测试不同列配置下的 TerminateTool 行为")
- void testDifferentColumnConfigurations() {
- System.out.println("=== 不同列配置测试 ===");
-
- // 测试1: 默认列(空列表)
- TerminateTool tool1 = new TerminateTool("plan-1", Arrays.asList());
- String state1 = tool1.getCurrentToolStateString();
- System.out.println("测试1 - 默认列配置 (空列表):");
- System.out.println(state1);
- System.out.println();
-
- // 测试2: null列
- TerminateTool tool2 = new TerminateTool("plan-2", null);
- String state2 = tool2.getCurrentToolStateString();
- System.out.println("测试2 - null列配置:");
- System.out.println(state2);
- System.out.println();
-
- // 测试3: 单列
- TerminateTool tool3 = new TerminateTool("plan-3", Arrays.asList("result"));
- String state3 = tool3.getCurrentToolStateString();
- System.out.println("测试3 - 单列配置 [result]:");
- System.out.println(state3);
- System.out.println();
-
- // 验证默认行为
- assertTrue(state1.contains("message")); // 默认列应该是 "message"
- assertTrue(state2.contains("message")); // null时也应该使用默认列
- assertTrue(state3.contains("result"));
- }
-
- @Test
- @DisplayName("测试工具定义生成")
- void testToolDefinition() {
- System.out.println("=== 工具定义测试 ===");
-
- List testColumns = Arrays.asList("task_id", "result", "timestamp");
- var toolDefinition = TerminateTool.getToolDefinition(testColumns);
-
- // FunctionTool 是一个简单的包装类,我们通过反射来获取内部的 function 对象
- try {
- var functionField = toolDefinition.getClass().getDeclaredField("function");
- functionField.setAccessible(true);
- var function = functionField.get(toolDefinition);
-
- var nameField = function.getClass().getDeclaredField("name");
- nameField.setAccessible(true);
- String name = (String) nameField.get(function);
-
- var descriptionField = function.getClass().getDeclaredField("description");
- descriptionField.setAccessible(true);
- String description = (String) descriptionField.get(function);
-
- var parametersField = function.getClass().getDeclaredField("parameters");
- parametersField.setAccessible(true);
- String parameters = (String) parametersField.get(function);
-
- System.out.println("工具名称: " + name);
- System.out.println("工具描述: " + description);
- System.out.println("参数结构:");
- System.out.println(parameters);
- System.out.println();
-
- assertEquals("terminate", name);
- assertNotNull(description);
- assertNotNull(parameters);
- }
- catch (Exception e) {
- System.out.println("Failed to access FunctionTool fields via reflection: " + e.getMessage());
- // 简化测试,只验证工具定义不为null
- assertNotNull(toolDefinition);
- }
- }
-
- @Test
- @DisplayName("测试 JSON 输出的长度和格式")
- void testJsonOutputCharacteristics() {
- System.out.println("=== JSON 输出特征测试 ===");
-
- // 测试不同规模的列配置
- List smallColumns = Arrays.asList("id");
- List mediumColumns = Arrays.asList("id", "name", "status", "created_at");
- List largeColumns = Arrays.asList("id", "name", "email", "phone", "address", "city", "state", "zip",
- "country", "notes");
-
- String smallJson = getParametersJsonViaReflection(smallColumns);
- String mediumJson = getParametersJsonViaReflection(mediumColumns);
- String largeJson = getParametersJsonViaReflection(largeColumns);
-
- System.out.println("小规模列 (1列) JSON长度: " + smallJson.length() + " 字符");
- System.out.println("中等规模列 (4列) JSON长度: " + mediumJson.length() + " 字符");
- System.out.println("大规模列 (10列) JSON长度: " + largeJson.length() + " 字符");
- System.out.println();
-
- // 验证JSON格式正确性(简单验证)
- assertTrue(smallJson.startsWith("{"));
- assertTrue(smallJson.endsWith("}"));
- assertTrue(mediumJson.contains("\"type\": \"object\""));
- assertTrue(largeJson.contains("\"items\": {\"type\": \"string\"}"));
-
- // 确保长度在合理范围内(避免过长导致解析问题)
- assertTrue(smallJson.length() < 1000, "小规模JSON应该小于1000字符");
- assertTrue(mediumJson.length() < 1000, "中等规模JSON应该小于1000字符");
- assertTrue(largeJson.length() < 1000, "大规模JSON应该小于1000字符");
- }
-
- /**
- * 通过反射调用私有静态方法 generateParametersJson
- */
- private String getParametersJsonViaReflection(List columns) {
- try {
- var method = TerminateTool.class.getDeclaredMethod("generateParametersJson", List.class);
- method.setAccessible(true);
- return (String) method.invoke(null, columns);
- }
- catch (Exception e) {
- throw new RuntimeException("Failed to invoke generateParametersJson", e);
- }
- }
-
-}