Skill Runtime 架构调研报告
September 4, 2026 · View on GitHub
只读调研。所有结论以当前生产代码真实行为为准,与文档冲突处已标注文档漂移。 生成日期:基于
src/、agents/、presets/、analysis/skill-curation/、docs/deepseek-harness/的真实代码与运行验证。
1. Executive Summary
当前 dsh-pentester 有一套自研的、基于文件系统快照的 Skill 机制,与 DSH 原生 Skill Runtime(@deepseek-ai/dsh-skill*)完全解耦:
- 发现/解析:
skill-loader.ts递归扫描*.skill/bundle,*.skills/仅作 UI 分组。 - 注册:
AgentLibrary内存 Map(builtin + custom 全局 namespace,custom 不得覆盖 builtin)。 - 持久化:Skill 无独立 store;Worker 创建时把 bundle 原样复制到 delegation 目录
input/skills/<name>.skill/。 - 挂载:Worker persona 只注入 Skill Catalog(路径 + description 列表),Skill 全文不进入 system prompt;Worker 经
pentester_container_exec按需cat SKILL.md。
目标架构的三大缺口(详见 Gap Analysis):
- Bundle Assignment 不存在 —— Agent Profile 的
skills全是 Leaf ID,没有.skillsCollection 的递归展开语义。 - Runtime Skill search 不存在 —— 没有
pentester_skill_search,没有把 curation 数据接入 runtime。 - DSH 原生 Skill Runtime 未复用 —— preset 未 mount
tool-skill,pentester 自实现了 skill loader + snapshot,与 DSH 的 registry/tool 无任何连接。
好消息:DSH 原生 Skill Runtime 完整且可复用(ctx.skills registry + skill({name}) tool + progressive disclosure),pentester 的 961 skill 中有 959 个 name 符合 DSH kebab-case 契约,仅 2 个(security_reporter、hello_js_reverse_skill)含下划线需处理。
2. Current Skill Filesystem Model
根目录解析(src/agent-library/paths.ts)
| 根 | 函数 | 路径 |
|---|---|---|
| Builtin skills | builtinSkillsRoot() | npm 包根 skills/(探测链:<root>/skills 或 lib/../skills) |
| Custom skills | customSkillsRoot() | $DSH_HOME/dsh-pentester/skills(DSH_HOME 缺省 ~/.dsh) |
| Builtin agents | builtinAgentsRoot() | npm 包根 agents/ |
| Custom agents | customAgentsRoot() | $DSH_HOME/dsh-pentester/agents |
实际 skills/ 顶层(13 项):
6 个 *.skills 集合:anthropic-cybersecurity, ctf-skills, hack-skills,
pentest-skills, reverse-skills, strix
5 个 standalone *.skill:hello_js_reverse, hxbai-knowledge,
secknowledge-skill, security-reporter, src-hunter
2 个普通 md:README.md, PTES-skills.md
扫描契约(src/agent-library/skill-loader.ts → scanSkillRoot())
逐条回答 A 部分:
| # | 问题 | 结论 | 证据 |
|---|---|---|---|
| 1 | skills/ 根在哪解析 | builtinSkillsRoot() / customSkillsRoot() | paths.ts |
| 2 | Builtin 根 | npm 包 skills/ | paths.ts |
| 3 | Custom 根 | $DSH_HOME/dsh-pentester/skills | paths.ts |
| 4 | *.skills/ collection 语义 | PARTIAL — 有 collection 概念,但只是 SkillDefinition.collection 数组(UI 分组),不是可分配的 Bundle/Registry 实体 | skill-loader.ts walk() |
| 5 | 递归扫描 .skills/ | YES — walk() 对任意深度递归,collection 从外到内累积 | skill-loader.ts:101-133 |
| 6 | 遇 *.skill/ 停止递归 | YES — loadBundle() 后 STOP,不深入 references/scripts | skill-loader.ts:109-121 |
| 7 | *.skill/ 必须含 SKILL.md | YES — 缺 → skill_manifest_missing | skill-loader.ts loadBundle() |
| 8 | 根 .skill 与 .skills 内 .skill 等价 | YES — identity 来自 frontmatter.name,不依赖位置 | loadBundle |
| 9 | README/普通 .md 忽略 | YES — 只认 .skill 目录,其他目录继续递归但普通文件忽略 | walk() |
| 10 | canonical ID 来源 | SKILL.md frontmatter.name(id === name) | loadBundle |
| 11 | 用 name frontmatter | YES — parseSkillFrontmatter() 解析 name/description | skill-loader.ts |
| 12 | folder name ≠ name | load + warning(skill_bundle_name_mismatch),不阻断 | loadBundle |
| 13 | duplicate canonical name | reject — 冲突项全部移除 + skill_duplicate_name 诊断 | registerSkill |
| 14 | builtin/custom 重名 | custom 拒绝 → skill_name_reserved_by_builtin(全局 namespace 不允许覆盖) | library.ts rescanCustom |
| 15 | size limits / diagnostics / skip | YES — bundle 上限 builtin 32MiB / custom 10MiB;SKILL.md 256KiB;symlink 拒绝;invalid 只产生 diagnostic 不阻断启动 | validateBundle / types.ts |
| 16 | 扫描 references/scripts/assets | 扫描但不解析 — validateBundle 统计 size,copySkillBundleTo 保留内容;不 parse | validateBundle / copySkillBundleTo |
| 17 | Bundle/Collection Registry | NO — 没有 .skills 集合的 registry 实体 | 无对应类型 |
| 18 | 是否丢失 .skills 父级结构 | NO — collection: string[] 保留(从外到内,如 ['hack-skills']) | SkillDefinition |
3. Current Skill Registry
数据模型(src/agent-library/types.ts)
interface SkillDefinition {
id: string // === name(canonical)
name: string // SKILL.md frontmatter.name
description: string // SKILL.md frontmatter.description
source: 'builtin' | 'custom'
bundlePath: string // bundle 目录绝对路径(含 *.skill)
collection: string[] // 祖先 *.skills 目录名(去后缀,从外到内)
revision: string // SKILL.md 内容 sha256
}
逐条回答 B 部分:
| # | 问题 | 结论 |
|---|---|---|
| 1 | SkillDefinition 字段 | id, name, description, source, bundlePath, collection[], revision(7 字段) |
| 2 | id 与 name 关系 | id === name(canonical identity = frontmatter.name;id: 字段仅 legacy 兼容) |
| 3 | source builtin/custom | YES |
| 4 | 保存 path | YES — bundlePath(绝对路径,含 .skill) |
| 5 | 保存 bundle/parent collection | YES — collection: string[] |
| 6 | 保存 description | YES |
| 7 | 保存 SKILL.md body | NO — 只存 revision(sha256),body 不驻留内存 |
| 8 | lazy load | NO — scan 时读 SKILL.md 但只提取 frontmatter,body 丢弃 |
| 9 | 启动一次性读完整 body | NO — 只读 frontmatter(name/description),body 不读入 |
| 10 | registry cache | YES — AgentLibrary.builtinSkills/customSkills(内存 Map) |
| 11 | snapshot | PARTIAL — LibrarySnapshot(RPC 视图),非 immutable runtime snapshot |
| 12 | immutable skill snapshot | PARTIAL — 只有 delegation 时 copySkillBundleTo 的磁盘副本(input/skills/) |
| 13 | content 进入模型时间点 | Worker 运行中,经 pentester_container_exec 按需 cat SKILL.md(spawn 时只注入路径+description) |
完整调用链(filesystem → model)
filesystem (skills/**/*.skill/SKILL.md)
→ scanSkillRoot() [skill-loader.ts] 递归扫描 + frontmatter 解析
→ AgentLibrary.builtinSkills/customSkills [library.ts] 内存 Map(id===name)
→ AgentLibrary.listAgents()/resolveAgent() [library.ts] 查询
→ profile-resolver.resolveAgentProfile() [profile-resolver.ts] 继承解析 → resolved.skills
→ DelegationService.dispatch() [delegations.ts] snapshotAssignedSkills()
→ copySkillBundleTo() [library.ts] 复制 bundle 到 input/skills/
→ buildSkillCatalog() [delegations.ts] 生成路径列表
→ buildWorkerPersona() [delegations.ts] 注入 catalog(路径+description)
→ DshClient.startContinuableWorker() [dsh.ts] ctx.subagents.startContinuable(spec)
→ Worker 运行中 cat SKILL.md [pentester_container_exec]
→ model context(按需,非 spawn 全文)
4. Current Agent Profile Model
Schema(src/agent-library/types.ts → AgentProfileDefinition)
interface AgentProfileDefinition {
id: string
name: string
description: string
extends?: string // 单继承
systemPrompt?: string // 完整替换父 prompt
systemPromptAppend?: string // 追加到父 prompt
skills?: string[] // 完整替换 inherited
skillsAdd?: string[] // 追加
skillsRemove?: string[] // 移除
model?: string
}
逐条回答 C 部分:
| # | 问题 | 结论 |
|---|---|---|
| 1 | Builtin 从哪加载 | npm 包 agents/<id>/profile.yml(scanBuiltinProfiles) |
| 2 | Custom 从哪加载 | $DSH_HOME/dsh-pentester/agents/<id>/profile.yml(scanCustomProfiles) |
| 3 | schema 字段 | 见上(9 字段) |
| 4 | extends/skills/skillsAdd/skillsRemove/model | 全有;prompt 无(叫 systemPrompt);mcp 无 |
| 5 | 单/多继承 | 单继承(extends 单个 id) |
| 6 | 继承解析顺序 | inheritanceChain() 从根到子 unshift;resolveAgentProfile() 顺序遍历 |
| 7 | skills 语义 | 完整替换 inherited |
| 8 | skillsAdd 语义 | skills 不存在时 = inherited - skillsRemove + skillsAdd |
| 9 | skillsRemove 语义 | skills 不存在时从 inherited 过滤 |
| 10 | remove 在 assignment 层还是 leaf 层 | leaf ID 层(skillsRemove 里是 leaf id,无 bundle 概念) |
| 11 | builtin 用户修改是 Overlay | 是 Overlay 模式(builtin 只读,用户 extends 新建 custom),不是原地修改 |
| 12 | 用户实际会改 builtin 文件吗 | UI 禁止(builtin 锁定);技术上古可改但 npm 包内文件会随升级覆盖 |
| 13 | custom 继承 builtin | extends: web(test 已验证:extendsChain ['web','custom-web']) |
| 14 | 产生 Resolved profile | YES — ResolvedAgentProfile |
| 15 | Resolved 有 skillIds | YES — resolved.skills: readonly string[] |
继承解析核心(profile-resolver.ts resolveAgentProfile())
for (const entry of chain) {
if (definition.systemPrompt !== undefined) systemPrompt = definition.systemPrompt
else if (systemPromptAppend) systemPrompt += systemPromptAppend
if (definition.skills !== undefined) skills = definition.skills // 完整替换
else {
skills = skills.filter(s => !remove.has(s)) // - skillsRemove
skills = [...skills, ...(definition.skillsAdd ?? [])] // + skillsAdd
}
if (definition.model !== undefined) model = definition.model
}
注意(文档漂移点):skills + skillsAdd 同时存在时,skills 会完整替换,skillsAdd 被忽略(if skills !== undefined 分支不进入 else)。目标架构 §3 描述的 "Builtin 默认 + skillsAdd" 语义在当前实现中仅当 child 不写 skills 字段时成立。这是实现细节,需在实现 Bundle Resolver 时明确。
5. Builtin Agent Skill Assignment Inventory
实际运行 AgentLibrary.create() + listAgents() 统计(非手写):
| Agent ID | Role | Parent | skills | skillsAdd | skillsRemove | Resolved Skill Count | Model | Stage bindings |
|---|---|---|---|---|---|---|---|---|
| web | Web & API Agent | (root) | 48 leaf ids | — | — | 48 | — | intelligence-gathering, vulnerability-analysis |
| impact | Post Exploitation Agent | (root) | 16 leaf ids | — | — | 16 | — | post-exploitation |
| recon | Recon Agent | (root) | 11 leaf ids | — | — | 11 | — | intelligence-gathering |
| validation | Exploitation Validation Agent | (root) | 9 leaf ids | — | — | 9 | — | exploitation |
| vulnerability | Vulnerability Analysis Agent | (root) | 8 leaf ids | — | — | 8 | — | vulnerability-analysis |
| reporting | Reporting Agent | (root) | 4 leaf ids | — | — | 4 | — | reporting |
| threat-model | Threat Modeling Agent | (root) | 2 leaf ids | — | — | 2 | — | threat-model |
回答 D 部分:
- Builtin Agent 总数:7。
- 无 Skill 的 Agent:0(全部有 skill)。
- Skill 很多的 Agent:web=48(最多)。
- Assignment 类型:全部 Leaf IDs(运行验证:0 个
.skills结尾的 bundle 引用)。 - Bundle Assignment:不存在(无任何 agent 用
.skills集合 ID)。 - 把
.skills作为 Agent Skill ID 使用:没有。 - 若给
skills写 Collection ID,runtime 会发生:resolveValidated()抛profile_skill_missing: profile=X skill=<collection-id>(因为 registry 里没有该 id 的 skill)→ 该 profile unavailable + diagnostic(builtin 不启动失败,custom last-known-good)。
6. Stage → Agent Relationship
- 7 个 PTES Stage 绑定:
src/stages.tsDEFAULT_STAGE_AGENTS(默认值),实际存于settings.jsonV2stageAgents。 - 语义:Agent allowlist(Root 只能 delegate 当前 stage allowlist 内的 agent;
delegate()里allowed.has(profile.id)校验)。 - Root 选 Agent:
pentester_delegate的assignments[].agent必须是 allowlist 内 id;Root 通过注入的 run-state 看到 "Available AgentProfiles"。 - Skill 与 Stage 不耦合(skill 是 agent 属性,stage 只限制可用 agent)。
- Skill 不受 Stage 过滤(stage 过滤的是 agent,不是 skill)。
stageAgentsschema:Record<StageId, string[]>(settings.json V2)。- Settings 修改实时生效:
PentesterSettingsStore.save()立即更新内存 + notify;Root 下一轮 prompt 注入经listAgentsForStage()同步读。已运行 Worker 不受影响(skill 在 dispatch 时 snapshot)。
PTES Stage (run.currentStage)
→ settings.stageAgents[stage] [settings-store.ts] allowlist
→ manager.listAgentsForStage(stage) [manager.ts] 过滤 dangling
→ delegate() 校验 agent ∈ allowlist [delegations.ts]
→ resolveAgent(agent) [library.ts] ResolvedAgentProfile
→ snapshotAssignedSkills(skills) [delegations.ts] 复制 bundle
→ startContinuableWorker [dsh.ts] Worker spawn
7. Worker Spawn Skill Flow
完整链路(pentester_delegate → ctx.subagents.startContinuable):
Root agent
→ pentester_delegate tool [tools.ts delegate()]
→ requireRootCaller(exec) [dsh.ts trustedCallerFromExec]
→ requireTargetRun(caller) [tools.ts → store/targets]
→ DelegationService.delegate() [delegations.ts]
→ 校验 agent ∈ stage allowlist
→ scaffoldDelegationDir() 创建 input/work/artifacts/evidence + task_prompt.md
→ saveRun() 持久化 Delegation(agentId, taskPrompt, objective)
→ docker.ensureImage/ensureRunContainer
→ dispatch() [delegations.ts]
→ snapshotAssignedSkills() 复制每个 skill bundle → input/skills/<name>.skill/
→ buildSkillCatalog() 生成 "## Assigned Skills" 路径列表
→ writeAgentProfileSnapshot() 写 input/agent-profile.json(skills 的 id+revision)
→ buildWorkerPersona() WORKER_BASELINE + profile.systemPrompt + skillCatalog
→ buildWorkerTaskPrompt() standalone task context
→ dsh.startContinuableWorker(input) [dsh.ts]
→ ctx.subagents.startContinuable(spec) 官方 provider "spawn"
回答 F 部分:
| # | 问题 | 结论 |
|---|---|---|
| 1 | Task 创建时如何 resolve agent | delegate() → manager.resolveAgent(assignment.agent) |
| 2 | Skill 在哪个函数 resolve | dispatch() → snapshotAssignedSkills()(读 profile.skills,逐个 getSkill()) |
| 3 | skillIds 出现在哪个对象 | ResolvedAgentProfile.skills;agent-profile.json snapshot 的 skills[];Delegation.agentId(无独立 skillIds 字段) |
| 4 | WorkerLaunchSpec / AgentRun / session binding / snapshot | 无 WorkerLaunchSpec / AgentRun 类型;有 agent-profile.json snapshot + Delegation.sessionId binding |
| 5 | skillIds 持久化 | PARTIAL — agent-profile.json 有 skills 的 id+revision;run.json 的 Delegation 只存 agentId(不存 skill 列表) |
| 6 | session registry 存 skillIds | NO — session 只绑定 sessionId ↔ delegationId(经 run.delegations) |
| 7 | continue 时校验 skill config | NO — continue 走 DSH send_message,不重走 dispatch,不重校验 |
| 8 | spawn 后改 skill:运行中 worker 变吗 / continue 变吗 | 均不变 — bundle 已复制到 input/skills/(磁盘快照),persona 已固定 |
| 9 | spawn 时加载 skill body | NO — 只复制 bundle + 注入路径列表 |
| 10 | system prompt 列出所有 skill | PARTIAL — 只列 name+description+路径(Skill Catalog),非全文 |
| 11 | Worker 看到 description | YES — Skill Catalog 含 description |
| 12 | Worker 拥有 DSH 原生 skill tool | NO — preset 未 mount tool-skill |
| 13 | Skill 怎么被模型"调用" | 经 shell cat — Worker 用 pentester_container_exec 读 /workspace/.../input/skills/<name>.skill/SKILL.md |
8. DSH Native Skill Runtime
存在性(CONFIRMED FROM API)
DSH 官方仓库有完整 skill 家族(docs/deepseek-harness/packages/skill/):
| 包 | 角色 |
|---|---|
@deepseek-ai/dsh-skill | 纯 provider registry(ctx.skills) |
@deepseek-ai/dsh-skill-filesystem | 本地 filesystem provider |
@deepseek-ai/dsh-tool-skill | durable session catalog + model-facing skill tool |
@deepseek-ai/dsh-skill-badge | packaged badge provider |
权威契约:docs/deepseek-harness/docs/subsystems/skills.md。
ctx.skills registry API(CONFIRMED FROM API)
registerProvider(create: (control) => SkillProvider): () => void
register(skill: SkillRegistration): () => void // runtime in-process skill
list(options?): Promise<SkillSummary[]> // 全量 summaries(name+description+whenToUse+invocation+source+provider+resourceBase)
snapshot(options?): Promise<SkillCatalogSnapshot> // list + complete 状态
get(name, options?): Promise<SkillDefinition | undefined> // 加载完整 body(content)
skill 工具(CONFIRMED FROM API,tool-skill/src/index.ts)
name: 'skill'
parameters: { name: { type: 'string', required: true } }
output: { name, provider, resourceBase?, content } // content = 完整 SKILL.md body
execute: ctx.skills.list({cwd, scope: agent}).find(name) → ctx.skills.get(name)
逐条回答 G 部分
| # | 问题 | 结论 |
|---|---|---|
| 1 | pentester preset 启用这些组件 | NO — agent.cordis.yml 只有 persona / tool-ask-user / tool-subagent-control / run-state |
| 2 | Worker child 继承它们 | NO — preset 无 skill 行,Worker 继承 preset scope 也无 skill |
| 3 | DSH Skill Registry API | 见上(registerProvider/register/list/snapshot/get) |
| 4 | 动态 register/list/search/load/invoke | register ✓ / list ✓ / load(get) ✓ / invoke(经 tool) ✓ / search ✗ |
| 5 | model-facing tool schema | skill({name: string}) → {name, provider, resourceBase?, content} |
| 6 | tool 返回什么 | 返回 SKILL.md content(<skill_content> + <skill_resources> + <skill_instructions>),非注入 prompt,非临时 context —— 是 tool result |
| 7 | 一次多个 skill | NO — 一次一个 name |
| 8 | 同一 Worker 后续追加 skill | YES(catalog 每 pre-step 重新 snapshot,registry 变化会追加 replacement catalog) |
| 9 | 已加载 skill 状态记录 | YES — tool result 进入 session history(durable) |
| 10 | skill load event | NO — 只有 skills/change(registry invalidation,无 diff),无 "skill loaded" event |
| 11 | skill unload | NO — 无 unload API |
| 12 | 部分 registry 暴露给 agent | PARTIAL — registry 是 host+per-scope layered(nearest layer wins),但无 per-agent allowlist;可见性 = preset 是否 mount tool-skill + provider 注册在哪层 |
| 13 | per-agent skill allowlist | NO — DSH 无此概念(scope 是 preset 层,不是 agent 层) |
| 14 | 搜索能力 | NO — 只有 list() 全量 + get(name) 按名 |
| 15 | 若搜索,搜什么 | N/A(无搜索) |
| 16 | 最接近的扩展 seam | 自建 provider(registerProvider)+ 自建 search tool(pentester 自己做语义搜索,用 curation 数据,DSH 只负责 get() 加载) |
关键格式差异(CONFIRMED FROM API)
- DSH local provider 格式:
<name>/SKILL.md(目录名 = name,kebab-case)或<name>.md(flat)。不支持.skill后缀,不支持递归嵌套**/SKILL.md。 - Pentester 格式:
<name>.skill/SKILL.md(identity = frontmatter.name,目录名可不同)。 - name 契约:DSH
SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/(kebab-case)。pentester 961 skill 中 959 合规,2 个含下划线:security_reporter(被 reporting agent 引用)、hello_js_reverse_skill(standalone)。这 2 个会触发 DSHregister()抛invalid skill name。
9. Worker Tool / Trusted Caller Model
工具注册与 allowlist
- Worker toolFilter:
src/dsh.tsWORKER_TOOL_FILTER = { deny: [...] }(deny-list:deny 5 个 Root 工具 + ask_user_question)。 - Worker 继承 pentester preset standing scope → 拥有
pentester_container_exec(worker-tools.ts注册)。 - Skill tool 不在 allowlist(preset 未 mount)。
- 新增
pentester_skill_search的注册位置:应仿照registerContainerExecTool在run-state.mjs的ctx.pentester.registerContainerExecTool(ctx.tools)旁注册(preset scope 内),Worker 经 deny-list 自动继承,Root 经ROOT_DENY隐藏。
Trusted Caller seam(src/dsh.ts trustedCallerFromExec)
interface TrustedCaller {
sessionId: string
agentPreset?: string
isSubagent: boolean
isPentesterRoot: boolean
cwd?: string
}
从 exec.agent.session(host-injected,非 model-supplied)提取。可进一步经已有链路反查:
| 目标字段 | 可得? | 链路 |
|---|---|---|
| sessionId | YES | caller.sessionId |
| agentId | YES | resolveWorkerTargetContext(cwd, sessionId) → run.delegations.find(d => d.sessionId===sessionId).agentId |
| taskId | YES | delegation.id(D-001) |
| agentRunId | NO(pentester 无 AgentRun 概念) | — |
| engagementId | YES | run.id / ctx.targetId |
结论:host 已有可靠 trusted caller seam(worker-tools.ts 的 makeContainerExecExecutor 完整示范了 sessionId → delegation → agent 的链路)。Model 无需传 agentId/taskId,宿主从 sessionId 反查,可信。
10. Settings / Config Behavior
- 用户配置 Agent Skill 的方式:Settings → Agent Library → Agents(新建 custom agent 时填
skills逗号分隔文本,或 Use as Base 继承 builtin)。 - UI 列所有 skill:YES(Skills tab 按 collection 分组列出全部,builtin 🔒 / custom 可编辑)。
- 搜索:NO(无 search 输入框,全部列出)。
- Add/Remove:PARTIAL — 编辑 agent 时用逗号分隔文本手动改 skills 列表;skill 本身可 create/update/delete。
- 区分 builtin/custom:YES(source 字段 + 🔒 标记)。
- 显示来源:YES(source)。
- Bundle 支持:NO(UI 无 bundle 分配,只有 collection 分组展示)。
- 树形目录:NO(collection 分组是扁平列表,非树)。
- Builtin 直接编辑:NO(锁定)。
- Custom 创建/继承:YES(AgentForm 支持 extends = Use as Base)。
- Settings 保存生效:立即(live store 内存更新 + notify;watcher 200ms debounce + 5s 轮询兜底 rescan)。
- 需 reopen/recovery:NO(custom agent/skill 文件变化经 watcher 自动 rescan;stageAgents 经 live store 立即生效)。
- Stage policy vs Agent config 生效机制:不同 — stageAgents 走 settings.json(live store);agent/skill 定义走文件系统 + watcher。
11. Curation Data Runtime Readiness
真实存在的文件(analysis/skill-curation/):
| 文件 | 内容 | 规模 |
|---|---|---|
global-index.json | 961 canonical index(skill_index S0001-S0961, skill_id, source_path, collection) | 961 |
runs/2026-08-30-step1-full/cards/*.json | Skill Cards(primary_domain, domain_tags, capability_tags, technique_tags, tools, target_types, role, workflow_focus, summary 等 16 字段) | 961 |
final/skill-family-membership.json | Family membership | 961 entries |
final/skill-relations.json | Relation graph | 890 edges |
final/families.json | Families | 379 |
final/decision-ledger.json | 决策台账 | 961 |
final/related-skills.json | 相关 skill | 961 |
step2/, step3/, step4/, step4.1/ | 中间产物 | — |
回答 J 部分:
- 真实存在:见上表。
- 961 canonical index:
global-index.json。 - 含 primary_domain/domain_tags/capability_tags/technique_tags/tools/target_types/role/workflow_focus/summary:Skill Cards(
runs/.../cards/S0001.json等 961 张)。 - Family membership:
final/skill-family-membership.json。 - Relation graph:
final/skill-relations.json。 - canonical ID 与 runtime name 一致:100% 一致(运行验证:global-index 961 id ↔ runtime 961 name,0 stale / 0 missing 双向)。
- stale ID:0。
- 961 全映射回真实 skill:YES(0 stale / 0 missing)。
- 适合做 search 静态索引:YES — Card 的 summary/capability_tags/technique_tags/tools/role/workflow_focus 是理想检索字段。
- 应 runtime 直接读 / build-time 生成 / package 生成:build-time 生成(见下)。
- npm 发布含 analysis/:NO —
package.jsonfiles = ['lib','presets','agents','skills','docker',...],不含 analysis/。 - 最合理生成位置:build-time 把精简 runtime index 生成到
lib/(或skills/旁),例如lib/skill-search-index.json,随 npm 发布;或 package 时把analysis/skill-curation/final/的搜索所需字段(global-index + card 精简版 + families)合并成单个lib/skill-search-index.json。
12. Proposed Integration Seams
Bundle Resolver(推荐位置)
- 推荐文件:
src/agent-library/profile-resolver.ts(或新增src/agent-library/bundle-resolver.ts)。 - 推荐函数:
resolveSkillGrants(profile.skills, library) → { allowedSkillIds: string[], grants: Grant[] }。 - 输入:
readonly string[](可能含 leaf id 或.skillsbundle id)。 - 输出:
{ allowedSkillIds: string[], grants: { skillId, grantedBy: [{type:'builtin-bundle'|'user-explicit', id}] }[] }。 - 为什么:
resolveAgentProfile已产出skills,Bundle 展开是它下游的纯函数;AgentLibrary.listSkills()已有collection: string[],bundle → leaf 的映射 = 遍历所有 skill 找collection前缀匹配(如 bundlepentest-skills→ 所有collection含'pentest-skills'的 skill)。不需要新建.skillsregistry 实体 —— collection 数组已够展开。
User Overlay(skillsAdd/skillsRemove)
- 已存在:
resolveAgentProfile()已实现skills 完整替换 / inherited - skillsRemove + skillsAdd。 - 最小缺口:
skills+skillsAdd共存时skillsAdd被忽略(当前语义skills完整替换)。若目标要求 "builtin 默认 + skillsAdd 追加",需决定:保留 "skills 完整替换" 还是改为 "skills 也是追加"。建议保持现状(skills = 完整替换是清晰的显式语义),custom agent 用extends+skillsAdd实现追加。
Provenance
- 当前无 provenance 字段。
- 判断:resolve-time derived 即可(无需持久化)—— 每次 resolve 时从 bundle 展开 + 用户显式 skillsAdd 计算
grantedBy,供 search 排序 boost。持久化到 run.json 会引入不必要的 schema 演进。
Search Scope source of truth
- 推荐:Worker launch spec / snapshot(即
agent-profile.json已持久化的 skills 列表 + delegation 目录input/skills/)。 - 理由:原则 "正在运行的 Worker 不应因用户后来改 Profile 而静默改变能力边界"。
agent-profile.json在 dispatch 时写死(immutable),是 Worker 能力边界的权威快照。pentester_skill_search应读取该 snapshot 的 allowedSkillIds,而非重新 resolve 当前 config。 - 次选:
run.json的 Delegation(但当前只存 agentId,需扩展存 skillIds)。
13. Test Coverage Matrix
| 行为 | 测试文件 | 覆盖 | 缺口 |
|---|---|---|---|
nested .skills scan | agent-library.test.ts:163 | ✅ | — |
leaf .skill scan | agent-library.test.ts:163 | ✅ | — |
.skill 内不递归 | agent-library.test.ts:184 | ✅ | — |
| duplicate canonical name | agent-library.test.ts:404 | ✅ | — |
| builtin/custom conflict | agent-library.test.ts:416 | ✅ | — |
| agent inheritance | agent-library.test.ts:64 | ✅ | — |
| skillsAdd | delegations.test.ts:439 (Phase 45) | ✅ | — |
| skillsRemove | agent-library.test.ts:86 | ✅ | — |
| worker snapshot contains skill IDs | delegations.test.ts:389, 439 | ✅ | agent-profile.json skills[] |
| continue configuration matching | — | ❌ | 无 continue 重校验测试 |
| config changes don't mutate running Worker | — | ⚠️ 隐含 | 代码保证(snapshot),无显式测试 |
| Skill runtime actual load | — | ❌ | NOT_IMPLEMENTED(无 DSH skill tool) |
| Custom Skill load | delegations.test.ts:439 | ✅ | — |
| invalid Skill diagnostics | agent-library.test.ts:194, 221, 235, 380, 394 | ✅ | — |
| bundle assignment | — | ❌ | NOT_IMPLEMENTED |
14. Gap Analysis
按目标架构:
Bundle + Leaf Assignment → Needs new code(Bundle Resolver,纯函数)
Agent Skill Grant → Needs new code(grant 展开 + provenance derived)
Worker immutable allowedSkillIds → Reusable with small change(agent-profile.json 已存,需扩展为 allowedSkillIds 数组)
Subagent runtime search → Needs new code(pentester_skill_search + curation runtime index)
multiple Skill results → Needs new code(search 返回 Top N)
dynamic Skill load → Reusable(DSH skill tool + ctx.skills registry)
| 能力 | 状态 | 说明 |
|---|---|---|
| Skill scan + frontmatter 解析 | Already Exists | skill-loader.ts |
| Skill registry(内存) | Already Exists | AgentLibrary |
| skillsAdd / skillsRemove | Already Exists | profile-resolver.ts |
| Bundle Assignment | Needs new code | 纯函数展开 collection → leaf |
| Skill Grant provenance | Needs new code(resolve-time) | 不需要持久化 |
| Immutable Worker allowedSkillIds | Reusable with small change | 扩展 agent-profile.json |
| Runtime Skill search | Needs new code | pentester_skill_search + build-time index |
| Dynamic Skill load | Reusable(DSH) | ctx.skills + tool-skill,需 mount + register + 格式适配 |
| Per-agent allowlist | Needs new code(pentester 层) | DSH 无此概念,search 层过滤 |
| Semantic search | Needs new code(pentester 层) | DSH 无 search,用 curation Card |
| Full-text/name search | Needs new code | 同上 |
| 2 个非 kebab-case name | Should not be in DSH registry | security_reporter / hello_js_reverse_skill 需改名或 wrapper |
| DSH registry 是否在 pentester 进程可 inject | Unknown until verified | 需确认 out-of-tree plugin 能否 inject: ['skills'] |
15. Recommended Next Implementation Order
- Bundle Resolver(
profile-resolver.ts纯函数)—— 展开collection→ leaf,产出allowedSkillIds + grants。 - Worker snapshot 扩展 ——
agent-profile.json增加allowedSkillIds(resolve 后的最终列表),保持 immutable。 - Build-time runtime index —— 把 curation
global-index.json+ 961 Card 精简字段(summary/capability_tags/technique_tags/tools/role/workflow_focus)生成lib/skill-search-index.json(进 npmfiles)。 pentester_skill_search工具 —— 仿worker-tools.ts注册,trusted caller 反查 delegation → allowedSkillIds → 过滤 + 排序(用户显式 grant boost)→ 返回 Top N。- DSH Skill Runtime 集成(最后,风险最高)—— 先验证
ctx.skills可 inject;mounttool-skill+ 自定义 provider 注册 961 skills;处理 2 个 kebab-case name。
16. Open Questions
- DSH
ctx.skills在 out-of-tree plugin 中是否可inject? 需验证 pentester 作为 repository plugin 能否 mount@deepseek-ai/dsh-skill(权威文档显示 host+per-scope layered,repository plugin 落在 global layer —— 但 pentester 当前 inject 列表只有['tools','subagents','typert'])。 - per-agent allowlist 如何映射 DSH layer? DSH registry 无 per-agent allowlist;若 DSH
skill工具对 Worker 可见,会暴露全部注册的 961 skills,而非 Worker 的 allowed subset。方案:pentester_skill_search在 pentester 层过滤(不依赖 DSH 限制),DSHskill工具只负责加载已被 search 确认的 skill;或每个 Worker 用ctx.skills.register()注册自己 allowed 的 skill(但这会污染 global layer)。 - 2 个非 kebab-case name 的处理方式(改名 vs 排除 vs wrapper provider 做 name 映射)。
skills完整替换 vs 追加 的最终语义(当前实现 = 完整替换;目标 §3 描述 = 默认 + skillsAdd)。- search 索引是否含 body(当前 Card 无 body,body 需从 SKILL.md 或 snapshot 加载)。
最终数字(运行验证,非手写)
Total Leaf Skills: 961
Total top-level *.skills Bundles: 6
Total standalone top-level *.skill: 5
Total Builtin Agents: 7
Agents with Skill assignments: 7
Agents without Skill assignments: 0
Maximum resolved Skills on one Agent: 48 (web)
Minimum: 2 (threat-model)
Median: 9
Raw assignment count: 98 (全部 leaf,无 bundle)
Resolved leaf count: 98 (= raw,无 bundle 可展开)
SKILL_RUNTIME_INVESTIGATION_COMPLETE
Current architecture:
自研文件系统快照机制:scan → 内存 registry → dispatch 时复制 bundle → Worker 经 shell cat SKILL.md(不接 DSH 原生 skill runtime)
Bundle assignment support:
NO(profile.skills 全是 leaf ID;.skills 仅 UI 分组)
Leaf assignment support:
YES(resolveValidated 校验 leaf id 存在;dispatch 复制 bundle)
Builtin Agent defaults:
YES(7 builtin 各带 leaf skill 列表,共 98 引用全部有效)
skillsAdd / skillsRemove:
YES(profile-resolver 单继承,skills 完整替换 / else -remove +add)
Immutable Worker skill grant:
PARTIAL(dispatch 时磁盘快照 + persona 固定;但 run.json 不存 skillIds,continue 不重校验)
Runtime Skill search:
NO(无 pentester_skill_search;curation 数据未接入 runtime)
Dynamic Skill loading:
NO(pentester 无 DSH skill tool;DSH 原生支持但未 mount)
DSH native Skill runtime reusable:
PARTIAL(ctx.skills + tool-skill 完整;但 2 个 name 非 kebab-case、格式差异、per-agent allowlist 缺失、ctx.skills inject 未验证)
Recommended implementation entrypoint:
src/agent-library/profile-resolver.ts(Bundle Resolver)+ src/worker-tools.ts(pentester_skill_search)
Highest-risk unknown:
out-of-tree plugin 能否 inject DSH ctx.skills 并按 per-agent 暴露 subset(DSH registry 无 per-agent allowlist)
Ready for implementation design:
YES