Skill Runtime V4

September 6, 2026 · View on GitHub

本文档是 Worker Skill 运行时的当前权威架构(supersede skill-runtime.md 的 V1 运行时与 skill-runtime-v2-implementation.md 的 V2/V3 工具面;V2/V3 的 Agent Library / Bundle / Grant / Search 资产继续有效)。

1. 目标与结果

V4 之前,Pentester Worker 用自研工具(pentester_skill_search / pentester_skill_load) "模仿" Harness Skill。V4 之后,Worker 就是 DeepSeek Harness Skill 生态的一员:

                 Skill Pack (builtin 961 / custom)


                Agent Library

              Agent Skill Grant (allowedSkillIds)


              pentester_delegate


           reserve Child Session ID(spawn 前写 run.json)


          frozen immutable snapshot(bundles + manifest.json)


             child agent/created(创建窗口内)


        scoped Pentester SkillProvider(dsh-pentester-frozen)


                  ctx.skills


            Harness tool-skill(官方)
              ┌──────┴──────┐
              ▼             ▼
      durable skill-catalog   skill({name}) tool
      name+description        on-demand <skill_content>
              │                   │
              ▼                   ▼
                       Worker task

与黄金 DSH 会话一致的链路:Catalog(durable user/message source.kind=skill-catalog, 只含 name+description)→ 模型路由决策 → skill({name})<skill_content> → 任务动作。

2. 概念

概念定义实现位置
AssignmentAgent 被授予什么能力(Agent Profile grant / user override)src/agent-library/
Frozen GrantDelegation 创建时冻结的 allowedSkillIds + bundle 副本 + manifestsrc/delegations.ts dispatch
Native CatalogWorker Session 通过 Harness Skill Registry 看到的 Skill summaries官方 tool-skill(durable)
Native Load模型调用 skill({name}),由 Harness 完成官方 tool-skill tool
Search V2.3BM25/alias/relation/MMR 检索(curation/推荐/离线分析)src/skill-search.ts + generated indexes

Search V2.3 不再是 Worker runtime 工具面的一部分;保留用于 Agent grant curation、 Settings 推荐、benchmark 与未来自动推荐。

3. 组件

3.1 Frozen Skill Manifest(src/native-skill/manifest.ts

Dispatch(spawn 之前)与 bundle 快照一起写 manifest.json

{
  "schemaVersion": 1,
  "delegationId": "D-003",
  "agentId": "web",
  "skills": [
    { "name": "jwt-testing", "description": "…", "revision": "<sha256>", "relativeBundle": "jwt-testing.skill" }
  ]
}

不含 Host 绝对路径;provider mount 与 cold resume 都读它 —— Catalog 始终是 Delegation 创建时的 frozen 边界(Settings 后续改动只影响未来 Delegation)。

3.2 Frozen SkillProvider(src/native-skill/provider.ts

Provider name dsh-pentester-frozen,注册在每个 child 的 scope layer:

  • list():只读 manifest metadata(catalog 阶段 0 次 SKILL.md body 读取)。 entries invocation = { modelInvocable: true, userInvocable: false } —— 本轮审计目标是 durable skill tool call,用户 /skill 直调路径统一关闭。
  • get():仅在 skill({name}) 时读取 frozen SKILL.md(一次一个文件),校验 frontmatter name 与 revision(不可变语义:body 变化即拒绝)。永不回读 live Agent Library / Skill Pack。
  • Capability isolation(global mask):DSH SkillRegistry 是 global + scope-chain 合并读取,官方没有 per-scope skill restriction API。Provider 利用两个官方公开语义:
    1. nearest layer 同名 entry 直接胜出(child layer 遮蔽 global 同名项);
    2. invocation = {modelInvocable:false, userInvocable:false} 使 entry 从 model catalog 与 user invocation 双面消失。 因此 list() 除 granted entries 外,还枚举 global-only 视图 (snapshot({cwd}),scope 省略)里非 granted 的名称,返回同名 disabled shadow candidates —— Worker native catalog 严格等于 frozen grant,不泄漏 user/project/bundled skills。mask 枚举失败 → observation incomplete(保留 last-good, 不静默泄漏)。
  • 已知边界(agent-layer runtime 注入):同 profile 的第三方插件(如 browser-skill)可在 agent/session-startagent.ctx 调官方 ctx.skills.register() 把 skill 注入 agent 自己的 layer runtime map—— nearest-layer 语义下它会出现在每个 agent 的 catalog(普通 DSH 会话同样如此)。 官方 runtime register 是同层 first-wins,pentester 侧无法在不 patch harness 的 前提下撤销它(我们注册 disabled runtime entry 的时机晚于 session-start 就已 first-wins 失效)。这是部署组合层的注入,不属于 frozen grant 边界失败; 其工具面(如 browser_)是否对 Worker 可见由 preset 工具组合决定(实测 Worker header.tools 不含 browser_,catalog 条目只是描述性文字)。
  • resourceBase omitted:官方 tool-skill 会把 resourceBase 渲染给模型,因此本 provider 不提供它(模型看到 "Resources are managed by provider …")。Host-private 路径不泄漏。
  • Path traversal 防线:relativeBundle allowlist(<name>.skill + 已知 legacy 目录)
    • 解析后 containment 检查 + revision 匹配。

3.3 Mount seam(src/native-skill/mount.ts

agent/created(创建窗口内、首个 pre-step 之前)同步挂载:

  1. 以 agent 对象为 authority(agent.id / agent.session.header.cwd),解析 child session id → run.json → D-xxx → Host-private frozen snapshot。 dispatch 已在 spawn 前把 delegation.sessionId 写入 run.json(见 §3.4), 所以 agent/created 时映射必然存在;cold resume 同样经过 agent/created 重新 mount 同一 manifest。
  2. ctx.get('skills')(官方 optional-service 读取)返回 traced registry; 经它调用 registerProvider 把注册落在 child agent 自己的 scope layer, 效果归 child fiber —— Activation dispose 自动注销,无 process 级泄漏。
  3. manifest 缺失(V3 及更早 Delegation 冷启动):从 agent-profile.json grant + 快照目录推导 manifest 并写盘(一次性确定性迁移;legacy underscore id 经 LEGACY_NATIVE_SKILL_ALIASES canonical 化)。推导不出 skill 时写空 manifest —— 边界仍然显式。
  4. 非 Pentester Delegation 的 subagent → skip;registry 缺失 → worker_skill_catalog_invalid fail loud(veto 创建)。

3.4 Dispatch 事务(src/delegations.ts

顺序:resolve profile → validate Skill Pack → allocate D-xxx → reserve child session idSessionId(randomUUID()))→ 写 Delegation {sessionId, status: starting} → snapshot frozen skills + manifest + agent-profile.json → startContinuable({ childId: reserved })(官方 ContinuableStartSpec.childId caller-reserved 语义)→ agent/created mount + tool surface validate → 初始 prompt admitted → status = active

childId 预留保证 agent/created 触发时 Host 已能 child id → D-xxx → frozen snapshot —— 没有"provider 晚于 catalog"的 race。spawn 失败时 sessionId 保留为 attempted child identity(delegation.failed 事件带 attemptedSessionId), 官方 AgentRegistry.enter() 是 id 冲突的权威拒绝边界。

3.5 Preset 面(presets/pentester/agent.cordis.yml + run-state.mjs

  • agent.cordis.yml 新增官方行 tool-skill@deepseek-ai/dsh-tool-skill), 注册进本 preset 的 standing scope —— Worker 经 composeFrom 继承可见。
  • ROOT_DENY = ['pentester_container_exec', 'skill']:Root 是纯 Orchestrator。 tools.restrict 的 deny 同时移除工具 schema 与 tool-skill 的 catalog 注入 (官方 tool-visible → catalog 契约:restriction 后 ctx.tools.get('skill', agent) !== skillTool,catalog 不注入)。
  • WORKER_REQUIRED_TOOLS = ['pentester_container_exec', 'skill']:worker 创建窗口校验(缺任一 → worker_tool_surface_invalid veto)。
  • agent/created worker 分支顺序:mount provider(fail loud)→ validate tool surface。

3.6 Native Audit Projection(src/native-skill/audit.ts + ui-view/snapshot.ts

Source of truth = DSH child Session durable events

  • tool/call name=skill,arguments JSON 的 name 字段是权威 skill 名 (不靠解析 <skill_content> 标记)。
  • 配对 tool/resulterror 字段或 block isError → failed attempt; 成功才计入 loadedSkillNames(unique)。
  • 重复 load:timeline 记录每次 call;Loaded Skills 计数按 unique successful names。

preloadSkillActivity(snapshot)按 delegation 读 child session events 投影为 DelegationSkillActivity(native 优先;V3 及更早回退只读 legacy skill-state.json)。成功读取后 content-diff + atomic 刷新 <delegation>/loaded_skills.md(人类可读投影;由 snapshot single-flight 保护, 多 tab 安全)。skill-state.json 不再是 V4 authoritative state。

3.7 Transcript / Trace / Output

  • 新 native skill 调用:完全由官方 @deepseek-ai/dsh-client-ui-skill SkillRow 渲染(本插件不注册 skill key,不复制官方 UI)。
  • 历史 pentester_skill_search / pentester_skill_load 调用: LegacyPentesterSkillToolRow(read-only 回放)。
  • Trace Agent node:Skills N loaded(unique successful native loads); topology 上每个成功加载的 skill 渲染为 agent 分支外侧的一个芯片元素 (chip),由 agent 边缘经短总线用线连接 —— skill 栈不与 delivery 行冲突 (外侧 vs 下方);timeline:Skill <name>(native load 事件)。legacy timeline 显示为 legacy 语义。
  • Output:D-xxx/loaded_skills.md(native audit 投影渲染)。

4. Skill name 语法与 legacy 迁移

Harness native name grammar:^[a-z0-9]+(?:-[a-z0-9]+)*$(kebab-case)。 Builtin pack 1.0.0 有两个 underscore identity:

legacy idcanonical name迁移
security_reportersecurity-reporterpack 1.0.1:frontmatter + agents/reporting grant
hello_js_reverse_skillhello-js-reversepack 1.0.1:目录名 + frontmatter + agents/web grant

LEGACY_NATIVE_SKILL_ALIASESsrc/native-skill/names.ts)只覆盖这两个已知 id: 确定性查表、collision-checked(模块自检 + 测试)、UI diagnostic 明确标注。 禁止通用 replaceAll('_', '-')。旧 grant / user override / 旧 run 记录在 resolve 阶段安全迁移(canonicalizeSkillId),能力不静默消失。

Canonical namespace(单一 ID 系统):

Agent Library Skill ID = SKILL.md frontmatter.name
= Harness native Skill name = Trace skill name = tool call name

pnpm run validate:skills 增加 native name 校验(961/961 必须通过)。

4.1 添加自定义 Skill

Skill 就是一个文件夹 —— 你可以在自己的 skills 目录里随手加一个, 不需要发布 Skill Pack:

$DSH_HOME/dsh-pentester/skills/           # 默认 ~/.dsh/dsh-pentester/skills/
└── my-reverse-proxy-skill.skill/         # 一个文件夹 = 一个 skill
    └── SKILL.md                          # 唯一必需的文件

SKILL.md 只需要 YAML frontmatter 的 name + description,其余是正文:

---
name: my-reverse-proxy-skill
description: >
  Nginx 反向代理配置审计与绕过技巧。当目标存在反代层、需要判断
  后端真实服务或测试路径改写绕过时使用。
---

# 反向代理审计

1. 指纹识别:…
2. 路径改写测试:…

规则与行为:

  • name = 文件夹名(去 .skill 后缀)= 一切 ID(catalog、skill({name})、 Trace、loaded_skills.md 共用一个名字,见 §4 的 canonical namespace)。 name 必须是 kebab-case(^[a-z0-9]+(?:-[a-z0-9]+)*$),否则不进入新 Worker 的 native catalog(加载时产生 diagnostic)。
  • description 是模型的路由依据 —— Worker 看不到 SKILL.md 正文,只看 catalog 里这一行描述来决定要不要 skill({name})。写得具体(何时用、 覆盖什么场景),模型才会正确选中它。
  • 目录 fs.watch 热更新(200ms debounce + 轮询兜底):放进新文件夹约 1–2 秒后 Agent Library 自动看到它,Settings → Pentester → Agent Library → Skills 里出现,无需重启 DSH。
  • 授权即生效路径:在 Settings 里把它 grant 给某个 Agent(或建 Custom Agent extends Builtin + skills: [{type: skill, id: my-reverse-proxy-skill}])→ 之后新建的 Delegation 会把它冻结进 catalog。已存在的 Delegation 不变 (frozen grant 硬性不变量)。
  • Custom skill 与 Builtin 共享全局 namespace:不得与 builtin 重名(重名 产生 skill_duplicate_name diagnostic,双方都不可用);单 bundle 限制 10 MiB(builtin 32 MiB),含 symlink 拒绝加载。
  • 验证:pnpm run validate:skills(也可对单目录跑 DSH_PENTESTER_BUILTIN_SKILLS_DIR=<目录> pnpm run validate:skills)。

调好的 custom skills 想分享给他人时,可发布为外部 Skill Pack (见 external-skill-pack-implementation.md)。

5. 兼容矩阵

场景行为
新 Delegation(V4)frozen snapshot + manifest + native provider + skill tool
V3 Delegation cold resumederiveLegacyManifest(一次性迁移)→ native provider
V2 Delegation(input/skills)同上(read-only 回退读取 input/skills)
旧 Session 回放LegacyPentesterSkillToolRow(durable events,不依赖 runtime)
旧 loaded_skills.md / skill-state.json只读保留;新内容来自 native 投影
Empty grant Delegation空 manifest + provider 仍挂载(mask-only 边界);官方 catalog 不注入条目
Builtin Skill Pack missing + unresolvable grant(builtin skill)pentester_delegate preflight 整批 fail builtin_skill_pack_required(0 side effects,不假装空 catalog)
Builtin Skill Pack missing + custom-only grant正常 delegate(custom skill 不依赖 pack,gate 不误伤)

6. 测试

  • test/native-skill.test.ts:name grammar / alias 迁移 / manifest 读写与 fail-loud / provider progressive disclosure(list 0 次 body、get 1 次)/ frozen revision / capability isolation mask / path traversal / native audit golden 序列 / loaded_skills.md 渲染 / legacy bundle 兼容。
  • test/native-skill-mount.test.ts:agent/created 同步挂载 / V3 manifest 推导迁移 / legacy id canonical 化 / skip 语义 / fail-loud / 空 grant。
  • test/delegations.test.ts:childId 预留(UUID)+ ContinuableStartSpec.childId 一致性 + spawn 前写入 run.json。
  • test/run-state.test.ts / test/tools.test.ts:ROOT_DENY / mount 调用顺序 / worker 工具面契约。
  • E2E:真实 DSH web profile 下验证 durable skill-catalog、native skill call、 官方 Skill Row、Trace / loaded_skills.md、冷启动 frozen 语义。