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. 概念
| 概念 | 定义 | 实现位置 |
|---|---|---|
| Assignment | Agent 被授予什么能力(Agent Profile grant / user override) | src/agent-library/ |
| Frozen Grant | Delegation 创建时冻结的 allowedSkillIds + bundle 副本 + manifest | src/delegations.ts dispatch |
| Native Catalog | Worker Session 通过 Harness Skill Registry 看到的 Skill summaries | 官方 tool-skill(durable) |
| Native Load | 模型调用 skill({name}),由 Harness 完成 | 官方 tool-skill tool |
| Search V2.3 | BM25/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 读取)。 entriesinvocation = { 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 利用两个官方公开语义:
- nearest layer 同名 entry 直接胜出(child layer 遮蔽 global 同名项);
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-start经agent.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 之前)同步挂载:
- 以 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。 ctx.get('skills')(官方 optional-service 读取)返回 traced registry; 经它调用registerProvider把注册落在 child agent 自己的 scope layer, 效果归 child fiber —— Activation dispose 自动注销,无 process 级泄漏。- manifest 缺失(V3 及更早 Delegation 冷启动):从 agent-profile.json grant +
快照目录推导 manifest 并写盘(一次性确定性迁移;legacy underscore id 经
LEGACY_NATIVE_SKILL_ALIASEScanonical 化)。推导不出 skill 时写空 manifest —— 边界仍然显式。 - 非 Pentester Delegation 的 subagent → skip;registry 缺失 →
worker_skill_catalog_invalidfail loud(veto 创建)。
3.4 Dispatch 事务(src/delegations.ts)
顺序:resolve profile → validate Skill Pack → allocate D-xxx → reserve child
session id(SessionId(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_invalidveto)。agent/createdworker 分支顺序: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/callname=skill,arguments JSON 的name字段是权威 skill 名 (不靠解析<skill_content>标记)。- 配对
tool/result:error字段或 blockisError→ 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-skillSkillRow 渲染(本插件不注册skillkey,不复制官方 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 id | canonical name | 迁移 |
|---|---|---|
security_reporter | security-reporter | pack 1.0.1:frontmatter + agents/reporting grant |
hello_js_reverse_skill | hello-js-reverse | pack 1.0.1:目录名 + frontmatter + agents/web grant |
LEGACY_NATIVE_SKILL_ALIASES(src/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_namediagnostic,双方都不可用);单 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 resume | deriveLegacyManifest(一次性迁移)→ 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 语义。