# @deepseek-ai/dsh-skill

[English](README.md) | 中文

纯 agent skill 提供方注册表。

该包负责 `ctx.skills` 接口。它不知道 skill 来自本地文件、嵌入式插件数据、HTTP 还是其他后端；提供方通过 `ctx.skills.registerProvider(...)` 注册这些来源。已发布的本地实现是 [`@deepseek-ai/dsh-skill-local`](../skill-local)。

## 服务：`SkillService`（ctx 键：`skills`）

### 公开 API

- `ctx.skills.registerProvider(provider): () => void` 使用唯一 `provider.name` 注册只读提供方。重复提供方名称会抛错，`runtime` 保留给 `ctx.skills.register(...)`。注册表借用提供方对象，并直接调用其方法。注册作用域绑定到 effect，可安全用于 HMR；精确的 Cordis disposer 支持有序组合拆卸。
- `ctx.skills.invalidateProvider(provider): void` 按实例精确标脏一个活动提供方，并清除已完成目录缓存。已释放或已被替换的提供方实例调用此方法时不执行任何操作，因此延迟到达的 watcher 回调无法使其替代项失效。
- `ctx.skills.snapshot({ cwd?, signal? })` 返回 `{ skills, complete }`。任一提供方发生瞬时失败时，`complete` 为 false；不完整观测绝不缓存，使面向模型的消费方可以保留上一份可用目录，并在下一个请求边界重试。
- `ctx.skills.list({ cwd?, signal? })` 借用只读查找选项，然后返回当前工作区中模型可调用的摘要；这些摘要跨提供方合并，并按名称排序。
- `ctx.skills.get(name, { cwd?, signal? })` 在发现和加载中使用同一组只读选项和胜出候选项；在发现或缓存命中后重新检查取消，让提供方加载与信号竞速，验证已加载定义，然后将其返回，包括已对模型禁用的 skill。
- `ctx.skills.register(skill): () => void` 注册只读运行时嵌入式 skill，省略时添加 `provider: "runtime"`。同名运行时注册使用先到先得：重复项会记录警告，并获得无操作 disposer。成功注册会返回精确的 Cordis disposer，以供有序组合拆卸。

### 事件

- `skills/change` 是一条不带过滤条件的失效通知，在提供方或运行时贡献注册或释放后，以及 `invalidateProvider()` 接受精确活动提供方后发出。它不携带目录或 diff；每个消费方都使用自身的查找选项重新获取 `snapshot()`。监听器抛错或 Promise 拒绝会被记录，既不能否决注册表变更，也不能阻止后续监听器执行。

### 配置

| 字段 | 默认值 | 含义 |
|---|---|---|
| `collectCacheMaxEntries` | `128` | 内存中保留的最大已完成 cwd/提供方目录数。 |

## 提供方契约

提供方同步注册，并在已等待的 `list(options)` 调用中执行远程设置、身份验证和发现。提供方对象、查找选项、候选项和定义都以只读方式借用，而不是克隆或重新绑定。提供方应遵守 `options.signal`；取消后，注册表也会停止等待不协作的发现或加载。

注册表在缓存前验证候选项，在返回前验证定义。胜出提供方会收到同一候选项和不透明 `locator`，两者都是它从 `list()` 返回的内容，从而支持后端专用文件、URL、id 或版本句柄。调用方和提供方必须保持只读契约。

契约违反会快速失败。提供方 `list()` 被拒绝会视为瞬时来源失败：该次观测会省略其条目，`complete` 为 false，结果也不会缓存。提供方或运行时修订发生变更时，会丢弃正在进行的结果并重试后再返回。重复名称按 rank、提供方注册顺序，然后按提供方本地顺序解析。摘要按 skill 名称排序。

定义仍采用渐进式加载。`get()` 每次调用都会向胜出提供方请求正文，而不是在此注册表中缓存正文。若返回定义的名称不同于所选候选项，系统会拒绝该陈旧选择，并使该提供方实例失效，以便下一次快照重新发现其目录。

## 运行时 Skill

`ctx.skills.register(...)` 是嵌入式运行时 skill 的便利接口。运行时 skill 使用 rank `250`：项目提供方可覆盖它们，它们则覆盖已发布本地提供方的自定义根和用户根。运行时定义和嵌套资源元数据均以只读方式借用；服务只实体化提供默认 `provider` 所需的顶层定义。运行时贡献内的注册使用先到先得，因此重复贡献无法通过其 disposer 移除活动项。

## 消费方边界

注册表不渲染模型指引，也不注册面向模型的工具。[`@deepseek-ai/dsh-tool-skill`](../tool-skill) 消费 `ctx.skills` 以提供会话前缀目录和 `skill` 工具，因此提供方仍与模型接口独立。

## 模型体验

通过 `dsh-tool-skill` 间接影响模型；该包将提供方摘要渲染到初始会话前缀或持久的替换目录消息中，并将已加载指令渲染到已保留工具结果中。

#### KV 缓存影响

不直接影响提示词。指定的消费方负责初始前缀组装，以及失效后的仅追加式目录替换。

## 已知限制与待完成工作

- **失效由提供方驱动**：注册表没有 TTL，无法推断任意远程来源是否已发生变化；每个可变提供方都必须由自身的观测机制调用 `invalidateProvider()`。
- **提供方依次查询**：一个缓慢的协作提供方会延迟之后注册的所有提供方；取消会停止调用方等待，但无法终止不协作提供方持续运行的工作。
- **不完整快照会在该次观测中省略失败的提供方**：注册表会报告 `complete: false`，但不负责上一份可用目录或逐提供方诊断；消费方选择是否保留先前状态。
- **重复解析使用先到先得**：系统会记录并隐藏较晚出现的低优先级候选项；不提供检查全部被遮蔽定义的 API。
