From cbdd70379b0904958d92f49d8fe550686d922149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=80=AA=E7=A8=8B=E4=BC=9F?= Date: Mon, 25 May 2026 15:42:01 +0800 Subject: [PATCH] feat(agent): optional agent-level workspace basePath override (#212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(agent): optional agent-level workspace basePath override Add workspaceBasePath field to AgentEntity that optionally overrides the workspace-level basePath. When set, the agent uses its own directory; when null, it inherits the workspace's basePath (existing behavior). - AgentEntity: new workspaceBasePath field with ALWAYS update strategy - AgentGraphBuilder: agent-level override takes priority over workspace - Flyway migration V121 for H2 and MySQL - UI: form input in basic tab with i18n (zh-CN, en-US) * fix(agent): rename migration V121→V125 to avoid Flyway conflict with upstream Upstream already has V121__tool_disclosure_tier.sql. Rename our migration to V125 (next available after V124). * fix(agent): make MySQL V125 migration idempotent Use INFORMATION_SCHEMA check before ADD COLUMN to avoid "Duplicate column name" error on re-deploy. --- .../main/java/vip/mate/agent/AgentGraphBuilder.java | 9 ++++++--- .../main/java/vip/mate/agent/model/AgentEntity.java | 4 ++++ .../migration/h2/V125__agent_workspace_base_path.sql | 3 +++ .../mysql/V125__agent_workspace_base_path.sql | 12 ++++++++++++ mateclaw-ui/src/i18n/locales/en-US.ts | 3 +++ mateclaw-ui/src/i18n/locales/zh-CN.ts | 3 +++ mateclaw-ui/src/types/index.ts | 1 + mateclaw-ui/src/views/Agents.vue | 7 +++++++ 8 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 mateclaw-server/src/main/resources/db/migration/h2/V125__agent_workspace_base_path.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/mysql/V125__agent_workspace_base_path.sql diff --git a/mateclaw-server/src/main/java/vip/mate/agent/AgentGraphBuilder.java b/mateclaw-server/src/main/java/vip/mate/agent/AgentGraphBuilder.java index 5d3648a7..8bcd4c9d 100644 --- a/mateclaw-server/src/main/java/vip/mate/agent/AgentGraphBuilder.java +++ b/mateclaw-server/src/main/java/vip/mate/agent/AgentGraphBuilder.java @@ -358,13 +358,16 @@ public class AgentGraphBuilder { agent.topP = runtimeModel.getTopP(); agent.toolCallingEnabled = toolCallingEnabled; - // 查找工作区活动目录 - if (entity.getWorkspaceId() != null) { + // Agent 级别覆盖优先,否则继承工作区 + if (entity.getWorkspaceBasePath() != null && !entity.getWorkspaceBasePath().isBlank()) { + agent.workspaceBasePath = entity.getWorkspaceBasePath(); + log.info("Agent {} using agent-level basePath: {}", entity.getName(), agent.workspaceBasePath); + } else if (entity.getWorkspaceId() != null) { try { var workspace = workspaceService.getById(entity.getWorkspaceId()); if (workspace != null && workspace.getBasePath() != null && !workspace.getBasePath().isBlank()) { agent.workspaceBasePath = workspace.getBasePath(); - log.info("Agent {} bound to workspace basePath: {}", entity.getName(), agent.workspaceBasePath); + log.info("Agent {} inherited workspace basePath: {}", entity.getName(), agent.workspaceBasePath); } } catch (Exception e) { log.warn("Failed to lookup workspace basePath for agent {}: {}", entity.getName(), e.getMessage()); diff --git a/mateclaw-server/src/main/java/vip/mate/agent/model/AgentEntity.java b/mateclaw-server/src/main/java/vip/mate/agent/model/AgentEntity.java index 0654f451..254a71da 100644 --- a/mateclaw-server/src/main/java/vip/mate/agent/model/AgentEntity.java +++ b/mateclaw-server/src/main/java/vip/mate/agent/model/AgentEntity.java @@ -78,6 +78,10 @@ public class AgentEntity { /** 默认思考深度:off / low / medium / high / max,null 表示跟随模型默认 */ private String defaultThinkingLevel; + /** Agent 级别的工作目录覆盖,为 null 时继承工作区 basePath */ + @TableField(value = "workspace_base_path", updateStrategy = FieldStrategy.ALWAYS) + private String workspaceBasePath; + @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; diff --git a/mateclaw-server/src/main/resources/db/migration/h2/V125__agent_workspace_base_path.sql b/mateclaw-server/src/main/resources/db/migration/h2/V125__agent_workspace_base_path.sql new file mode 100644 index 00000000..9199b958 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/h2/V125__agent_workspace_base_path.sql @@ -0,0 +1,3 @@ +-- V100: Add workspace_base_path column to mate_agent for Agent-level directory override. +-- When set, this overrides the workspace-level basePath for this Agent only. +ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS workspace_base_path VARCHAR(512) DEFAULT NULL; diff --git a/mateclaw-server/src/main/resources/db/migration/mysql/V125__agent_workspace_base_path.sql b/mateclaw-server/src/main/resources/db/migration/mysql/V125__agent_workspace_base_path.sql new file mode 100644 index 00000000..0e77dd4b --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/mysql/V125__agent_workspace_base_path.sql @@ -0,0 +1,12 @@ +-- V125: Add workspace_base_path column to mate_agent for Agent-level directory override. +-- Idempotent: checks INFORMATION_SCHEMA before ADD COLUMN. +SET @col_exists := ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'mate_agent' + AND COLUMN_NAME = 'workspace_base_path' +); +SET @stmt := IF(@col_exists = 0, + 'ALTER TABLE mate_agent ADD COLUMN workspace_base_path VARCHAR(512) DEFAULT NULL', + 'SELECT 1'); +PREPARE s FROM @stmt; EXECUTE s; DEALLOCATE PREPARE s; diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts index 25e02ef6..84fc3355 100644 --- a/mateclaw-ui/src/i18n/locales/en-US.ts +++ b/mateclaw-ui/src/i18n/locales/en-US.ts @@ -1163,6 +1163,8 @@ export default { extraInstructionsHint: 'Optional. Use for output format, process checklists, or boundary rules.', maxIterations: 'Max Iterations', defaultThinkingLevel: 'Default Thinking Level', + workspaceBasePath: 'Working Directory', + workspaceBasePathHint: 'Optional. Set a dedicated working directory for this employee (relative to workspace root). Leave blank to inherit the workspace default.', modelName: 'Model', modelGlobalDefault: 'Use global default', modelHint: 'Override the global default model for this employee. Leave blank to follow Settings → Models.', @@ -1198,6 +1200,7 @@ export default { backstory: 'e.g. Spent 10 years in data — believes in asking the right question before writing SQL...', extraInstructions: 'Optional: output format, process checklist, or boundary rules...', tags: 'tag1,tag2', + workspaceBasePath: 'e.g. projects/code-review', }, messages: { noDescription: 'No description', diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts index b481c8ad..700a0ffd 100644 --- a/mateclaw-ui/src/i18n/locales/zh-CN.ts +++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts @@ -1055,6 +1055,8 @@ export default { extraInstructionsHint: '可选。用于细化输出格式、流程清单或边界规则。', maxIterations: '最大迭代次数', defaultThinkingLevel: '默认思考深度', + workspaceBasePath: '工作目录', + workspaceBasePathHint: '可选。为该员工指定独立的工作目录(相对于工作区根目录)。留空则继承工作区默认目录。', modelName: '模型', modelGlobalDefault: '使用全局默认模型', modelHint: '为该员工单独指定模型,留空则跟随「设置 → 模型」中的全局默认。', @@ -1090,6 +1092,7 @@ export default { backstory: '例:在数据里待了十年,相信先问对问题再写 SQL...', extraInstructions: '可选:补充输出格式、流程清单或边界规则...', tags: 'tag1,tag2', + workspaceBasePath: '例如:projects/code-review', }, messages: { noDescription: '暂无描述', diff --git a/mateclaw-ui/src/types/index.ts b/mateclaw-ui/src/types/index.ts index 1a14dde9..3929a0f7 100644 --- a/mateclaw-ui/src/types/index.ts +++ b/mateclaw-ui/src/types/index.ts @@ -43,6 +43,7 @@ export interface Agent { enabled: boolean icon?: string tags?: string + workspaceBasePath?: string createTime?: string updateTime?: string } diff --git a/mateclaw-ui/src/views/Agents.vue b/mateclaw-ui/src/views/Agents.vue index a705a639..a2e9aeb8 100644 --- a/mateclaw-ui/src/views/Agents.vue +++ b/mateclaw-ui/src/views/Agents.vue @@ -271,6 +271,11 @@ +
+ + +

{{ t('agents.fields.workspaceBasePathHint') }}

+