diff --git a/internal/tools/workspace_dir.go b/internal/tools/workspace_dir.go index 98c113b9..df0fd393 100644 --- a/internal/tools/workspace_dir.go +++ b/internal/tools/workspace_dir.go @@ -182,7 +182,7 @@ func resolveWorkspaceScopeFromArgs(ctx context.Context, args map[string]any, ws channel, chatID = resolveWorkspaceScope(ctx) if scope == "team" { if !ws.isShared() { - return "", "", "team workspace_scope is 'isolated' — set to 'shared' in team settings to use scope=team" + return "", "", "team workspace_scope is 'isolated' — retry with scope='channel' (or omit scope) to use your per-user workspace. To enable shared team workspace, ask an admin to set workspace_scope='shared' in team settings." } chatID = "" } diff --git a/ui/web/src/i18n/locales/en/agents.json b/ui/web/src/i18n/locales/en/agents.json index dc3709fe..33e58004 100644 --- a/ui/web/src/i18n/locales/en/agents.json +++ b/ui/web/src/i18n/locales/en/agents.json @@ -193,7 +193,7 @@ "revoke": "Revoke" }, "links": { - "teamsHint": "For multi-agent collaboration with task tracking and quality control, consider using Agent Teams instead. Agent Links are best for simple 1-to-1 delegation between two agents.", + "teamsHint": "Agent Links will be removed soon. Please use Agent Teams for multi-agent collaboration — it provides task tracking, shared workspace, and quality control in a simpler setup.", "createLink": "Create Link", "targetAgent": "Target Agent", "selectAgent": "Select or search agent...", diff --git a/ui/web/src/i18n/locales/en/teams.json b/ui/web/src/i18n/locales/en/teams.json index 90f7df80..91c109a4 100644 --- a/ui/web/src/i18n/locales/en/teams.json +++ b/ui/web/src/i18n/locales/en/teams.json @@ -190,6 +190,13 @@ "followupIntervalHint": "How long to wait before sending each follow-up reminder to the user.", "followupMaxReminders": "Max reminders", "followupMaxRemindersHint": "Maximum number of reminders per task. 0 = unlimited (remind until user replies).", + "workspace": "Workspace", + "workspaceScope": "Workspace Sharing", + "workspaceScopeHint": "Control how agents access files in the team workspace.", + "workspaceScopeIsolated": "Isolated", + "workspaceScopeIsolatedDesc": "Each agent has its own private workspace. Agents cannot read or write files from other agents.", + "workspaceScopeShared": "Shared", + "workspaceScopeSharedDesc": "All agents share a single team workspace. Agents can read and write each other's files.", "teamVersion": "Team Version", "versionBasic": "Basic", "versionAdvanced": "Advanced", diff --git a/ui/web/src/i18n/locales/vi/agents.json b/ui/web/src/i18n/locales/vi/agents.json index 4dfafb0f..cdd62a63 100644 --- a/ui/web/src/i18n/locales/vi/agents.json +++ b/ui/web/src/i18n/locales/vi/agents.json @@ -193,7 +193,7 @@ "revoke": "Thu hồi" }, "links": { - "teamsHint": "Để cộng tác đa agent với theo dõi nhiệm vụ và kiểm soát chất lượng, hãy cân nhắc sử dụng Nhóm agent thay thế. Liên kết agent phù hợp nhất cho ủy quyền đơn giản 1-1 giữa hai agent.", + "teamsHint": "Agent Links sẽ sớm bị gỡ bỏ. Vui lòng sử dụng Agent Teams để cộng tác đa agent — hỗ trợ quản lý nhiệm vụ, workspace chung và kiểm soát chất lượng trong một hệ thống đơn giản hơn.", "createLink": "Tạo liên kết", "targetAgent": "Agent đích", "selectAgent": "Chọn hoặc tìm kiếm agent...", diff --git a/ui/web/src/i18n/locales/vi/teams.json b/ui/web/src/i18n/locales/vi/teams.json index 55e8cbd7..77ef4923 100644 --- a/ui/web/src/i18n/locales/vi/teams.json +++ b/ui/web/src/i18n/locales/vi/teams.json @@ -190,6 +190,13 @@ "followupIntervalHint": "Thời gian chờ trước khi gửi mỗi lần nhắc nhở cho người dùng.", "followupMaxReminders": "Số lần nhắc tối đa", "followupMaxRemindersHint": "Số lần nhắc tối đa cho mỗi nhiệm vụ. 0 = không giới hạn (nhắc cho đến khi người dùng trả lời).", + "workspace": "Không gian làm việc", + "workspaceScope": "Chia sẻ không gian làm việc", + "workspaceScopeHint": "Kiểm soát cách các agent truy cập tệp trong không gian làm việc của team.", + "workspaceScopeIsolated": "Cô lập", + "workspaceScopeIsolatedDesc": "Mỗi agent có không gian làm việc riêng. Không thể đọc hoặc ghi tệp của agent khác.", + "workspaceScopeShared": "Chia sẻ", + "workspaceScopeSharedDesc": "Tất cả agent dùng chung một không gian làm việc. Có thể đọc và ghi tệp của nhau.", "teamVersion": "Phiên bản Team", "versionBasic": "Cơ bản", "versionAdvanced": "Nâng cao", diff --git a/ui/web/src/i18n/locales/zh/agents.json b/ui/web/src/i18n/locales/zh/agents.json index 1417c0af..2b057213 100644 --- a/ui/web/src/i18n/locales/zh/agents.json +++ b/ui/web/src/i18n/locales/zh/agents.json @@ -193,7 +193,7 @@ "revoke": "撤销" }, "links": { - "teamsHint": "对于具有任务跟踪和质量控制的多Agent协作,请考虑使用Agent Team。Agent链接最适合两个Agent之间的简单一对一委派。", + "teamsHint": "Agent Links 即将移除。请使用 Agent Teams 进行多代理协作——它提供任务跟踪、共享工作区和质量控制,且设置更简单。", "createLink": "创建链接", "targetAgent": "目标Agent", "selectAgent": "选择或搜索Agent...", diff --git a/ui/web/src/i18n/locales/zh/teams.json b/ui/web/src/i18n/locales/zh/teams.json index 03453d50..4f8a1aa7 100644 --- a/ui/web/src/i18n/locales/zh/teams.json +++ b/ui/web/src/i18n/locales/zh/teams.json @@ -190,6 +190,13 @@ "followupIntervalHint": "在每次向用户发送跟进提醒前等待的时间。", "followupMaxReminders": "最大提醒次数", "followupMaxRemindersHint": "每个任务的最大提醒次数。0 = 无限制(持续提醒直到用户回复)。", + "workspace": "工作区", + "workspaceScope": "工作区共享", + "workspaceScopeHint": "控制代理如何访问团队工作区中的文件。", + "workspaceScopeIsolated": "隔离", + "workspaceScopeIsolatedDesc": "每个代理拥有独立的工作区。无法读取或写入其他代理的文件。", + "workspaceScopeShared": "共享", + "workspaceScopeSharedDesc": "所有代理共享同一个工作区。可以读取和写入彼此的文件。", "teamVersion": "团队版本", "versionBasic": "基础版", "versionAdvanced": "高级版", diff --git a/ui/web/src/pages/agents/agent-detail/agent-links-tab.tsx b/ui/web/src/pages/agents/agent-detail/agent-links-tab.tsx index 0c029b3f..96a9a7ce 100644 --- a/ui/web/src/pages/agents/agent-detail/agent-links-tab.tsx +++ b/ui/web/src/pages/agents/agent-detail/agent-links-tab.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useMemo } from "react"; import { Link } from "react-router"; -import { Info } from "lucide-react"; +import { TriangleAlert } from "lucide-react"; import { useTranslation } from "react-i18next"; import { ConfirmDialog } from "@/components/shared/confirm-dialog"; import { useAgentLinks } from "../hooks/use-agent-links"; @@ -47,11 +47,11 @@ export function AgentLinksTab({ agentId }: AgentLinksTabProps) { return (
-
- -

+

+ +

{t("links.teamsHint").split("Agent Teams")[0]} - + Agent Teams {t("links.teamsHint").split("Agent Teams")[1]} diff --git a/ui/web/src/pages/teams/team-settings-tab.tsx b/ui/web/src/pages/teams/team-settings-tab.tsx index 3b75e0f8..90bb318f 100644 --- a/ui/web/src/pages/teams/team-settings-tab.tsx +++ b/ui/web/src/pages/teams/team-settings-tab.tsx @@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Switch } from "@/components/ui/switch"; import { Combobox } from "@/components/ui/combobox"; -import { X, Save, Check, Bell, ShieldAlert, Clock, Info } from "lucide-react"; +import { X, Save, Check, Bell, ShieldAlert, Clock, Info, FolderLock, FolderSync } from "lucide-react"; import { useTranslation } from "react-i18next"; import { CHANNEL_TYPES } from "@/constants/channels"; import type { TeamData, TeamAccessSettings, EscalationMode, EscalationAction } from "@/types/team"; @@ -77,6 +77,7 @@ export function TeamSettingsTab({ teamId, team, onSaved }: TeamSettingsTabProps) const [escalationActions, setEscalationActions] = useState(initial.escalation_actions ?? []); const [followupInterval, setFollowupInterval] = useState(initial.followup_interval_minutes ?? 30); const [followupMaxReminders, setFollowupMaxReminders] = useState(initial.followup_max_reminders ?? 0); + const [workspaceScope, setWorkspaceScope] = useState(initial.workspace_scope ?? "isolated"); const isTeamV2 = version >= 2; const [saving, setSaving] = useState(false); @@ -102,6 +103,7 @@ export function TeamSettingsTab({ teamId, team, onSaved }: TeamSettingsTabProps) setEscalationActions(s.escalation_actions ?? []); setFollowupInterval(s.followup_interval_minutes ?? 30); setFollowupMaxReminders(s.followup_max_reminders ?? 0); + setWorkspaceScope(s.workspace_scope ?? "isolated"); setSaved(false); setError(null); }, [team]); @@ -123,6 +125,7 @@ export function TeamSettingsTab({ teamId, team, onSaved }: TeamSettingsTabProps) } if (followupInterval !== 30) settings.followup_interval_minutes = followupInterval; if (followupMaxReminders !== 0) settings.followup_max_reminders = followupMaxReminders; + if (workspaceScope === "shared") settings.workspace_scope = "shared"; if (version >= 2) settings.version = version; await updateTeamSettings(teamId, settings); setSaved(true); @@ -133,7 +136,7 @@ export function TeamSettingsTab({ teamId, team, onSaved }: TeamSettingsTabProps) } finally { setSaving(false); } - }, [teamId, version, allowUserIds, denyUserIds, allowChannels, denyChannels, progressNotifications, escalationMode, escalationActions, followupInterval, followupMaxReminders, updateTeamSettings, onSaved, t]); + }, [teamId, version, allowUserIds, denyUserIds, allowChannels, denyChannels, progressNotifications, escalationMode, escalationActions, followupInterval, followupMaxReminders, workspaceScope, updateTeamSettings, onSaved, t]); const userOptions = knownUsers.map((u) => ({ value: u, label: u })); const channelOptions = CHANNEL_TYPES.map((c) => ({ value: c.value, label: c.label })); @@ -276,6 +279,52 @@ export function TeamSettingsTab({ teamId, team, onSaved }: TeamSettingsTabProps)

+ {/* Workspace Scope */} +
+

{t("settings.workspace")}

+
+
+
+ +
+
+
+ {t("settings.workspaceScope")} +

+ {t("settings.workspaceScopeHint")} +

+
+
+ {([ + { value: "isolated", Icon: FolderLock, labelKey: "workspaceScopeIsolated", descKey: "workspaceScopeIsolatedDesc" }, + { value: "shared", Icon: FolderSync, labelKey: "workspaceScopeShared", descKey: "workspaceScopeSharedDesc" }, + ] as const).map((opt) => ( + + ))} +
+
+
+
+
+ {/* Escalation Policy (V2 only) */} {isTeamV2 &&

{t("settings.escalationPolicy")}