From 5182ea5d11f3ed7f4752d793c676cdc1f86fd73a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=89=E9=91=AB=E5=8D=87?= Date: Tue, 16 Jun 2026 16:37:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8A=80=E8=83=BD=E6=94=B6=E8=97=8F?= =?UTF-8?q?=E4=B8=8E=E5=B8=B8=E9=A9=BB(always-on)=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 skill_favorites / skill_always_on 迁移与持久化模型字段 - skills 路由、存储、前端设置页支持收藏与常驻 - 调整摘要中间件与 lead agent 提示词 - 同步岗位 position_sync 及相关配置清理 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../settings/skill-settings-page.tsx | 52 +++++++- frontend-web/src/core/positions/types.ts | 2 +- frontend-web/src/core/skills/type.ts | 3 + offline-backend-20260512/backend/CLAUDE.md | 46 ++++--- .../backend/app/gateway/routers/skills.py | 112 +++++++++++------- .../deerflow/agents/lead_agent/prompt.py | 54 ++++++++- .../middlewares/summarization_middleware.py | 43 ++++++- .../packages/harness/deerflow/client.py | 1 - .../deerflow/config/extensions_config.py | 19 --- .../versions/20260616_02_skill_favorites.py | 54 +++++++++ .../versions/20260616_03_skill_always_on.py | 45 +++++++ .../deerflow/persistence/models/__init__.py | 3 +- .../deerflow/persistence/skills/base.py | 20 ++++ .../deerflow/persistence/skills/memory.py | 18 +++ .../deerflow/persistence/skills/model.py | 33 +++++- .../deerflow/persistence/skills/sql.py | 19 ++- .../deerflow/services/position_sync.py | 49 +++++--- .../deerflow/skills/storage/skill_storage.py | 1 - .../scripts/sql/column_additions_mysql.sql | 5 +- 19 files changed, 472 insertions(+), 107 deletions(-) create mode 100644 offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_02_skill_favorites.py create mode 100644 offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_03_skill_always_on.py diff --git a/frontend-web/src/components/workspace/settings/skill-settings-page.tsx b/frontend-web/src/components/workspace/settings/skill-settings-page.tsx index 2a011b25..67db57a4 100644 --- a/frontend-web/src/components/workspace/settings/skill-settings-page.tsx +++ b/frontend-web/src/components/workspace/settings/skill-settings-page.tsx @@ -2,6 +2,7 @@ import { AlertCircle, + Anchor, ArchiveIcon, ArrowDownUpIcon, BookmarkIcon, @@ -77,6 +78,7 @@ import { useGenerateSkillNameZh, useInstallSkillUpload, useReconcileCustomSkills, + useSetSkillAlwaysOn, useSetSkillFeatured, useSetSkillNameZh, useSetSkillPinned, @@ -84,7 +86,11 @@ import { useSkills, useValidateSkillUpload, } from "@/core/skills/hooks"; -import { usePublishSquares, useViewMode } from "@/core/system-settings/hooks"; +import { + usePublishSquares, + useSkillCompressionSettings, + useViewMode, +} from "@/core/system-settings/hooks"; import type { Skill } from "@/core/skills/type"; import { formatDateTime } from "@/core/utils/datetime"; import { env } from "@/env"; @@ -181,8 +187,10 @@ function SkillSettingsList({ onClose }: { onClose?: () => void }) { const isAdmin = storedAuth?.system_role === "admin"; // Name + tag filtering is resolved server-side via the /api/skills query. - // Non-admins get their 岗位 (position) visibility applied (scope=mine): a - // position that binds skill tags restricts which skills they can see. + // A 岗位 (position) no longer *restricts* the list — it grants skills into the + // member's 我的技能 (each such skill comes back with ``favorited: true``), so + // the 广场 always shows every visible skill and the 我的 tab includes the + // position-granted ones. ``scope`` is kept for API compatibility only. const { skills, isLoading, error } = useSkills({ search, tagIds, @@ -217,7 +225,9 @@ function SkillSettingsList({ onClose }: { onClose?: () => void }) { if (isBuiltin || !!skill.published) { all.push(skill); } - if (isOwnedByMe) { + // "我的" = skills I own OR skills granted into my collection by my 岗位 + // (position) — mirroring how favorited agents land in 我的智能体. + if (isOwnedByMe || !!skill.favorited) { mine.push(skill); } } @@ -601,6 +611,8 @@ export function SkillRow({ const { data: cardPublishSquares } = usePublishSquares(); const setPinned = useSetSkillPinned(); const setFeatured = useSetSkillFeatured(); + const setAlwaysOn = useSetSkillAlwaysOn(); + const { data: compression } = useSkillCompressionSettings(); const deleteSkill = useDeleteCustomSkill(); const duplicateSkillMutation = useDuplicateSkill(); const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); @@ -620,6 +632,17 @@ export function SkillRow({ // Admin can edit/delete/unpublish anyone's skill; non-admin only their own. const canManage = isCustom && (isOwned || isAdmin); const canTogglePublish = canManage && !isBuiltin; + // 常驻(免压缩):内置技能仅管理员可设;自定义技能其所有者或管理员可设(与后端一致)。 + const canSetAlwaysOn = isAdmin || isOwned; + + async function handleToggleAlwaysOn(next: boolean) { + try { + await setAlwaysOn.mutateAsync({ skillName: skill.name, alwaysOn: next }); + toast.success(next ? "已设为常驻(免压缩)" : "已取消常驻"); + } catch (err) { + toast.error(err instanceof Error ? err.message : String(err)); + } + } async function handleTogglePublished(next: boolean) { // Publishing: prompt for which square to publish into (when configured). @@ -932,6 +955,27 @@ export function SkillRow({ /> )} + {compression?.enabled && canSetAlwaysOn && ( + + )} {canTogglePublish && (