)
DeepChat 共享 Skill 架构解析单一规范包、逻辑绑定与运行时授权边界Shared Skills【免费下载链接】deepchatDeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat本文基于 DeepChat 仓库中的共享 SkillShared Skills架构规格状态已实现系统讲解其全局单一规范包 Agent 逻辑绑定 Session 激活的三层产品模型、Version 3 管理状态结构、目录布局与迁移方案并结合主进程SkillService、路由契约与安全约束的源码实现说明这套设计如何在保证多 Agent 复用同一份 Skill 内容的同时守住 Run 级运行时授权的边界。读完本文你可以完整理解 DeepChat 中 Skill 的存储归属、目录解析、导入/删除/同步等操作流程以及从旧版 Agent 私有根目录向全局共享模型迁移的机制。背景与核心决策DeepChat 只有一套应用级、可变的 Skill 包集合。Agent 拥有的是逻辑绑定和扩展状态Session 决定某次 Run一次运行激活哪些已绑定的 Skill物理包位置不属于任何 Agent包复用也不会创建 Agent 之间的活链接。规格文档见 docs/architecture/shared-skills/spec.md。旧版遗留的.agent-scopes私有根目录在规格中仅作为迁移证据保留迁移与兼容规则保护用户数据同时不再在私有 Agent 根下重新引入运行时发现或 CRUD。核心决策可以概括为一句话每个可变的 Skill 包在全局 Skills 根目录下只存一份canonical packageDeepChat Agent 对 Skill 的拥有退化为一条逻辑绑定不创建 Agent 私有副本也不生成任何符号链接或软链目录只读的内置BundledSkill 与 Plugin 自带的 Skill 保留在各自提供方的根目录但会出现在同一份全局列表中。规格中的总体数据流如下继承自原文档从源码结构看这套决策落在SkillServicesrc/main/skill/index.ts上它维护一份物理目录清单catalog并据此派生出每个 Agent 的目录视图。全局 Skill 的默认存储位置由 src/main/skill/settings.ts 决定读取skillsPath配置未配置时回退到~/.deepchat/skills即上文全局 Skills 根的默认取值。目标与非目标规格明确列出了目标Goals每个全局唯一 Skill 名只保留一份规范的可变包任意多个 DeepChat Agent 通过逻辑绑定复用同一包所有 Skill 出现在同一个默认列表中与当前选中哪个 Agent无关Agent 启用操作收进所选 Skill 的预览面板内主 Plugins 交互面只保留从外部 Agent 快照导入这一条入口保留 per-Agent 扩展状态、Session 过滤、运行时授权、Plugin 归属与既有数据ACP Agent 被明确排除在 DeepChat Skill 绑定与迁移决策之外。同时规格用非目标Non-goals划清边界避免功能蔓延不建独立的全局集合页面、Tab 或实体不做 Agent 分配页、Agent 选择器、已分配/未分配列表态、批量 Apply 步骤不用符号链接、junction、别名或生成的 Agent 目录不做 DeepChat 内部互相拷贝所有 DeepChat Agent 本来就读同一份全局集合;不与外部 Agent 做持续同步或对外发布不做自动内容合并或父子 Agent 继承初始迁移不删除保留下来的遗留.agent-scopes数据。产品模型三个彼此独立的运行时决策尽管只有前两项出现在 Skills 界面上规格强调存在三个独立决策决策所有者含义产品交互Skill 存在应用一份规范包全局可用列表、预览、导入、编辑、删除Agent 能用该 SkillDeepChat Agent一条逻辑绑定被启用在 Skill 预览中增删 AgentSkill 被激活Session下一次 Run 加载已启用 Skill独立的 Session 级控制配套的不变量Invariants是理解授权边界的关键一个全局 Skill 可以存在而没有任何启用的 Agent把 Agent 从某个 Skill 上移除绝不删除包内容或扩展状态删除或编辑共享内容会影响所有已启用该 Skill 的 AgentACP Agent 和已不存在的 Agent 永远不会成为绑定目标Run 只授权从其 Agent 与激活 Skill 名解析出的具体根目录Run 启动时授权的 Skill 名集合在执行期间保持不变即使中途 Agent 绑定被修改后续 Run 才使用更新后的绑定。所有权与存储包目录布局规格给出的物理布局如下继承自原文档skillsRoot/skillName/ # canonical mutable package skillsRoot/.agent-scopes/agentId/ # preserved migration evidence only skillsRoot/.library-migration-v3/ # compatibility recovery journal三点值得注意.agent-scopes只读保留。它对应旧版每个 Agent 一个私有 Skill 根的结构。从源码看常量定义在 src/main/skill/agentSkillRoots.tsBUILTIN_SKILL_AGENT_ID deepchat与AGENT_SKILL_SCOPES_DIR .agent-scopes。resolveAgentSkillsRoot对内置deepchat直接返回 Skills 根其他 Agent 则解析到.agent-scopes/agentId并通过assertPhysicalAgentRootConfinement做物理包含性校验lstat拒绝符号链接、要求是目录、并用realpath确认解析后仍位于 Skills 根内部防止路径逃逸。这与规格遗留根仅作迁移证据、禁止运行时发现的定位一致。.library-migration-v3保留历史名称使中断的开发期迁移可以安全续跑。其日志在包重命名之前以原子替换方式提交因此一次撕裂写torn write不可能成为下一次启动的输入该目录被排除在发现之外永远不会出现在 UI 或公开契约中。Provider 根的归属不变。内置与 Plugin Skill 留在提供方所有根下提供方控制其生命周期与可变性全局列表不转移所有权。用户插件元数据在临时不可用期间保留ownerPluginId插件停用/更新会注销其活跃贡献但保留 Agent 分配与覆盖卸载则移除其拥有的管理状态。被保留的插件 Skill 名不能被其他来源覆盖。执行权限侧会校验当前插件所有者/根与物化的 source ID 是否一致从而阻止过时的 Tape 视图执行已停用、已卸载或被替换的插件修订。这部分与 User Plugins 规格 中Skills 行负责注册、分配保留与过期修订执行拒绝的职责划分相互印证。Version 3 管理状态Version 3 的关键是把全局包元数据与Agent 绑定拆分开。规格给出的接口如下interface SharedSkillManagementItem { name: string canonicalPath: string source: SkillSource } interface AgentSkillBinding { assigned: boolean extension: SkillExtensionConfig runtimeBindingId?: string } interface SkillManagementState { version: 3 skills: Recordstring, SharedSkillManagementItem agents: Recordstring, { bindings: Recordstring, AgentSkillBinding } sync?: SkillSyncDirectoryConfig migration?: SharedSkillMigrationState }这些定义与仓库中的实际类型完全对应见 src/shared/types/skillManagement.ts。几个源码层面的补充细节SharedSkillManagementItem在实现中额外携带可选ownerPluginId用于插件归属校验runtimeBindingId的注释明确它是外部运行时环境值的不透明修订号本身从不包含那些值——这正是规格中为携带密钥的环境值做版本化而不落盘到 Tape的落地方式SkillSource的type字段是闭集枚举取值包括builtin、created、folder-install、zip-install、url-install、git-install、adopted、imported见 SKILL_SOURCE_TYPES覆盖了规格中本地目录 / ZIP / URL / Git / 草稿 / 公开 / CLI 入口保留为兼容或运行时能力的来源分类状态持久化位置在 src/main/skill/settings.ts 的skills.managementState设置键下SkillSettings.getManagementState返回StoredSkillManagementState即SkillManagementState | LegacySkillManagementStateV2 | LegacySkillManagementState三版并集为迁移路径提供类型基础。语义规则规格 源码一致assigned是内部持久化与运行时术语渲染层文案把它表述为某 Agent 对该 Skill 启用使用过旧library字段的 Version 3 开发态数据会被一次性兼容解码并以skills字段写回缺失的绑定等价于assigned: false且扩展配置取默认值移除 Agent 时写入assigned: false并保留其 extension新的内置/插件贡献继承既有的 provider 默认绑定新的可变导入不会自动为任何 Agent 启用。目录与运行时解析SkillService拥有一份物理 catalog并从中派生 Agent catalog规格中的核心公式继承自原文档AgentCatalog(agentId) AvailableGlobalSkills intersect EnabledBindings(agentId) EffectiveSessionSkills(sessionId) PersistedSessionSelection intersect AgentCatalog(Session.agentId)这个派生 Agent catalog 是授权边界作用于 prompt 组装、Skill 工具、允许的工具、脚本与文件系统根。而skills.listAll只是管理视图绝不授予运行时访问——这是管理面与运行时面分离的硬约束。规格进一步规定Route 与 Discover 对派生 Agent catalog 应用受限的渐进披露progressive-disclosure契约而不是对全局管理列表应用激活时只解析一次规范包字节把有效内容与执行包记录进 Tape并把skill_view/skill_run绑定到该请求证据每个 Run 保留 Run 启动时解析出的激活名、内容身份与包权威规范源包不按 Agent 复制只有有界请求执行包会为经过校验的脚本执行而物化Transfer、rebind、fork 与 Subagent 创建都会重新计算目标 Agent 的交集一份缓存与 watcher 同时覆盖全局元数据与内容绑定变更只使受影响 Agent 的视图失效由于 Agent 数量小反向影响用扫描即可不引入反向索引Watcher 的删除事件只是缓存失效不是权威的用户删除仅当事件路径与缓存的 manifest 精确匹配且文件确实已消失时才删除缓存目录项同时保留全局出处、Agent 绑定、扩展状态与运行时绑定身份——这样编辑器原子替换文件后同一 Skill 恢复时授权不变。持久化 Skill 状态的移除只由显式删除与启动对账负责。执行权限的校验有专门实现src/main/skill/skillExecutionAuthority.ts 负责当前插件所有者/根 vs 物化 source ID的一致性验证对应上面插件修订拒绝的语义其测试为 test/main/skill/skillExecutionAuthority.test.ts。操作流程启用与移除 AgentSkill 预览面板发出一个显式的 Skill 名 DeepChat Agent ID 目标布尔值。主进程侧的校验与行为验证 Agent 存在且是 DeepChat Agent启用时拒绝不可用的 Skill 名移除时保留扩展配置并过滤该 Agent 持久化的 Session 选择Agent 被删除时只移除其绑定既有 Agent 生命周期门lifecycle gate围栏并发的 Agent 删除。编辑与删除 Skill共享内容的操作会展示当前启用的 Agent 名。删除是带确认的六步事务继承自原文档接收确认面已确认的启用 Agent ID 列表重新解析影响面拒绝过期的确认stale confirmation把包移动到可恢复的备份位置移除绑定与受影响的 Session 选择提交状态并发布 catalog 事件移除备份若提交失败则回滚恢复备份。provider 所有与内置只读 Skill 不可编辑、不可删除。从外部 Agent 导入外部快照导入是全局的不询问目标 Agent也不把启用 Agent作为副作用用户在导入后的 Skill 预览中自行启用 Agent。规格给出的时序如下冲突处理策略规格原文语义状态/策略行为ready新增一个全局 Skillsame保留完全相同的既有 Skill不改动启用 Agentconflict skip什么都不改conflict rename加入第一个可用且通过校验的全局名默认策略conflict overwrite在确认当前启用 Agent 影响后替换共享内容unavailable禁用该项选择并展示来源校验原因安全要点规格渲染层输入从不提供来源路径或已转换的包字节主进程在执行时重新扫描、重新计算冲突部分失败按 Skill 粒度返回。仓库实现与契约印证了这一点导入服务实现于 src/main/skill/agentSkillImportService.ts外部来源扫描/转换的各宿主适配器Claude Code、Codex、Cursor、Goose、Kilo Code、Kiro、Windsurf 等位于 src/main/skill/sync/adapters/路由契约 src/shared/contracts/routes/skills.routes.ts 中skills.executeAgentImport的输入是source: { kind: external, toolId }加一组items每项为{ skillName, strategy: skip | rename | overwrite, acknowledgedAgentIds? }——注意契约层根本没有目标 Agent或来源路径字段与规格不询问目标 Agent、渲染层不供给来源路径的规则逐条对应选择列表有去重校验Duplicate Skill selection导入相关的测试见 test/main/skill/agentSkillImportService.test.ts。另外本地目录、ZIP、URL、Git、草稿、公开与 CLI 入口仍作为兼容或运行时能力存在但不会作为并列的添加选项出现在主 Skills 页面上。同步目录Sync directory同步目录备份仍是应用级的。次级入口Sync directory会用既有的备份面替换默认列表并提供Back to Skills它不是并列的 Tab。导入/导出只涉及包绝不涉及 Agent 绑定。在目录选择、导入或导出写入进行期间本地 Back 操作与路由导航同时被阻塞保留的界面得以报告最终结果。契约层对应skills.getSyncConfig、skills.setSyncDirectory、skills.previewSyncDirectoryExport/Import与skills.executeSyncDirectoryExport/Import见 skills.routes.ts同步目录配置结构SkillSyncDirectoryConfig固定在layout: multi-skill-repo见 skillManagement.ts。渲染层交互默认视图规格给出的默认 Skills 页如下继承自原文档Plugins / Skills Manage all Skills. Open one to preview it and manage enabled Agents. ------------------------------------------------------------------------------ || Suggest Skill Drafts [off] | || After a task, allow the Agent to suggest temporary reusable Skill drafts. | ------------------------------------------------------------------------------ [Search Skills] [Sync directory] [Import from external Agent] ---------------------------------------------------------------------------------- ┌────────────────────────────────────┐ ┌────────────────────────────────────┐ │ code-review │ │ browser-control │ │ Review a change before merging... │ │ Control an interactive browser... │ └────────────────────────────────────┘ └────────────────────────────────────┘排版与交互约束任务完成后的 Skill Draft 建议设置位于页面描述正下方、搜索与操作按钮之上响应式网格在空间允许时用两列等宽窗口受限时单列每张卡片固定高度只包含 Skill 名与截断到两行的描述无图标、无来源标签、无 Agent 状态、无独立 Preview 按钮整卡是带键盘焦点的语义按钮点击打开 Skill 预览这是唯一的 Skills 管理面Settings 窗口没有 Skills 导航项或路由。Draft 建议开关对应源码中的skillDraftSuggestionsEnabled设置默认falsesrc/main/skill/settings.ts与界面默认[off]一致。Skill 预览┌──────────────────────────────────────────────────────────────────────────────┐ │ code-review [Close] │ │ Review a change before merging │ │ │ │ Enabled Agents [ Add Agent] │ │ [DeepChat ×] [Writer ×] │ │ │ │ /.../skills/code-review/SKILL.md [Edit] [Delete] │ │ ────────────────────────────────────────────────────────────────────────── │ │ # Code review │ │ ... │ └────────────────────────────────────────────────────────────────────────────┘Add Agent只列出尚未启用的 DeepChat Agent×把该 Agent 从 Skill 移除每次变更立即提交并通过既有原语暴露 pending、error、键盘与可访问标签状态。异步变更的作用域规则预览变更只作用于发起它的那个 Skill迟到的响应可以刷新全局列表中的卡片但不能替换更新的预览后台 catalog 删除通过脏草稿守卫请求预览关闭与用户直接关闭同一机制外部导入的影响面文案会把 Agent ID 解析为当前显示名仅在找不到对应 DeepChat Agent 时回退显示 ID。Plugins-hub 的 Skills 路由渲染的是同一份全局界面不从当前选中 Agent 或 ACP 状态推断目标。渲染入口在 src/renderer/src/pages/plugins/SkillsPluginsPage.vue。类型化接口路由契约规格列出的核心类型化接口均能在路由契约文件中找到对应实现src/shared/contracts/routes/skills.routes.ts接口用途契约要点skills.listAll列出所有全局 Skill 及其启用 Agent ID空输入返回UnifiedSkillItem[]skillsListAllRouteskills.listCatalog只列出某个必填agentId已启用的 Skill输入{ agentId }返回派生 catalogskills.setDisabled兼容的单绑定变更渲染客户端使用{ agentId, name, disabled }→{ saved: true }skills.setAssignments兼容的批量绑定变更单 Agent输入skillNames数组上限 512PUBLIC_SKILL_LIST_MAX_ITEMSskills.delete影响面确认后删除一个可变全局 Skill输入nameacknowledgedAgentIds返回含affectedAgentIds的结果skills.listAgentImportSources/skills.previewAgentImport/skills.executeAgentImport全局外部快照导入无目标 Agent ID见上文外部导入一节契约层的命名约束值得注意PublicSkillNameSchema限定 Skill 名为^[a-z0-9][a-z0-9._-]*$小写字母/数字开头最多 255 长度PublicSkillAgentIdSchema限定 Agent ID 为^[A-Za-z0-9][A-Za-z0-9._-]*$且显式禁止..skills.routes.ts——这与主进程侧assertSafeSkillAgentId的路径安全校验agentSkillRoots.ts构成前后一致的双重防线。规格其余条款不存在 DeepChat 到 DeepChat 的复制路由逻辑绑定已经共享规范包catalog 事件只暴露服务自身发出的原因持久化设置中的历史活动输入按历史数据处理导航忽略未再注册的路由名独立的 Settings 窗口需要在主面继续 onboarding 时走类型化窗口路由主进程发布类型化运行时恢复事件并聚焦主窗口跨窗口交接不依赖渲染层sessionStorage内容路由可以把agentId保留为生命周期上下文但包含性与 provider 归属才授权全局读与写扩展路由保留agentId因为扩展状态属于某个绑定。并发、故障与安全规格的安全与并发条款继承自原文档均为硬性约束单一服务变更门mutation gate串行化包与绑定的写入包写入复用既有机制staging、manifest 校验、物理包含性、备份、原子重命名失败的包操作保持绑定不变失败的组合操作回滚备份受管或导入的包根不能是符号链接——这与 agentSkillRoots.ts 中lstat().isSymbolicLink()即抛错实现对齐导入的相对路径经过校验不能逃逸包根整个已配置的 Skills 根受保护普通 Agent 的文件系统写操作不可触达只有当前激活的具体 Skill 根会被加入 Run 的文件系统白名单关闭进行中的导入或处于脏编辑状态时由既有 settings leave 保护机制守卫。迁移与兼容性从 Version 1 / 2 到 3 的迁移是确定性的且可重启续跑的规格 8 步继承自原文档发现既有的全局与私有 DeepChat Agent 包对相同快照去重给不同内容的同名变体分配通过校验的全局名规范重命名前先 staging 并写迁移日志把禁用状态与扩展数据翻译成绑定重映射 DeepChat Agent 的 Session 选择ACP 与孤儿 Session 保持不动只有当包提交成功后才提交 Version 3 状态。配套兼容细节遗留私有根保留为证据排除在运行时发现之外使用过旧library属性的 Version 3 状态按skills读取保护运行过早期开发构建的用户启动时Version 3 中 ID 不在当前 DeepChat Agent 集合内的绑定会先被移除再暴露全局 catalog确保 ACP 与孤儿 ID 不会泄漏进启用 Agent 影响面。源码印证SharedSkillMigrationStateskillManagement.ts携带sourceVersion: 1 | 2、status: planned | committing | completed与agentSkillNames旧名 → 规范全局名的映射与步骤 3、8 直接对应SkillSettings.freezeLegacyMigrationTargetssettings.ts负责在迁移目标集合上落冻结标记去重、排序、排除内置deepchat且当状态已是 version 3 或已有targetAgentIds时直接返回保证幂等。共享 Skill 行为的测试集中在 test/main/skill/skillServiceSharedSkills.test.ts目录根解析的安全测试在 test/main/skill/agentSkillRoots.test.ts。验收标准摘要规格给出的 17 条验收标准中最能体现架构意图的几条是可变包只在全局 Skills 根存在一份新 DeepChat Agent 不再获得私有 Skill 根或包副本默认 Skills 视图是一个包含所有可用 Skill 的列表Skill Draft 建议设置位于搜索与操作按钮之上且不存在任何已分配/未分配的可见状态ACP Agent 永不出现在目标列表、不参与迁移校验主导入入口只接受外部 Agent且全局导入不自动启用 Agent同步目录作为次级视图可达没有顶层 TabPlugins-hub 路由渲染同一全局视图Agent catalog、Session 激活、prompt 组装、工具、脚本与文件访问一律使用启用 Agent 交集而非全局管理列表Route、Discover、Tape 物化与请求绑定脚本执行保持同一授权边界共享编辑与删除必须重新校验当前启用 Agent 影响面Version 1/2 迁移保留包、绑定、扩展、运行时环境修订与有效 Session 选择Version 3library兼容数据无丢失解码插件与内置的所有权与可变性约束持续生效。小结DeepChat 的 Shared Skills 架构可以归纳为三个工程要点一份规范包 逻辑绑定用元数据/绑定分离的 Version 3 状态skillsagents取代 Agent 私有包目录从存储层根除重复与同步问题同时保留.agent-scopes作为可追溯的迁移证据管理面与运行时面严格分离skills.listAll只是管理视图真正授权来自AgentCatalog 全局可用 ∩ 已启用绑定与 Session 选择的二次交集Run 启动时冻结授权快照全链路安全收敛命名白名单、路径包含性断言、符号链接拒绝、staging 原子重命名、备份回滚、过期确认拒绝、插件修订校验覆盖了从渲染层输入到 Tape 执行的每一步。进一步阅读建议规格正文 docs/architecture/shared-skills/spec.md、服务实现 src/main/skill/index.ts、类型定义 src/shared/types/skillManagement.ts、路由契约 src/shared/contracts/routes/skills.routes.ts以及 User Plugins 规格 中 Skills 与插件归属的交叉职责表。【免费下载链接】deepchatDeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考