refactor: split skill providers
This commit is contained in:
@@ -10,19 +10,19 @@ DeepSeek Harness needs the same primitive because project-specific review, plugi
|
||||
|
||||
## Decision
|
||||
|
||||
Add `@deepseek-ai/dsh-skill` as the discovery service (`ctx.skills`) and `@deepseek-ai/dsh-tool-skill` as the model-facing loader tool. `dsh-agent-core` loads both by default so stdio and ACP apps get the same behavior.
|
||||
Add `@deepseek-ai/dsh-skill` as the provider registry (`ctx.skills`), `@deepseek-ai/dsh-skill-local` as the shipped local filesystem provider, and `@deepseek-ai/dsh-tool-skill` as the model-facing loader tool. `dsh-agent-core` loads the registry, local provider, and tool by default so stdio and ACP apps get the same behavior while future providers can contribute embedded or remote skills without changing the registry or tool.
|
||||
|
||||
Discovery scans cwd-sensitive project roots, runtime registrations, user roots, extra roots, and system roots in first-wins priority order: project `.dsh`, project `.agents`, runtime, user `.dsh`, user `.agents`, extra roots, then `~/.dsh/skills/.system`. The user `.dsh/skills` scan skips `.system` so built-ins are not discovered twice. Same-name lower-priority skills are ignored with a warning, which lets project and user skills override built-ins deliberately.
|
||||
Provider catalogs return ranked candidates. The registry validates each candidate, resolves same-name skills first-wins by rank, provider registration order, and provider-local order, then sorts model-visible summaries by skill name for deterministic prompt text. Runtime `ctx.skills.register(...)` remains a convenience for embedded in-process skills and uses project-over-user priority; `runtime` is reserved as the registry-owned provider name.
|
||||
|
||||
The local provider scans cwd-sensitive project roots, custom roots, and user roots in first-wins rank order: project `.dsh`, project `.agents`, `customSkillDirs`, user `.dsh`, then user `.agents`. The user `.dsh/skills` scan skips `.system` so a system-owned directory is not treated as normal user content. DeepSeek Harness does not ship built-in system skills in v1; plugin-authoring skills can be supplied later by another provider.
|
||||
|
||||
Each skill is either `<name>/SKILL.md` or `<name>.md` with YAML frontmatter. `name` and `description` are required; `whenToUse`, `disableModelInvocation`, and `metadata` are optional. Names are kebab-case. YAML frontmatter is parsed with the `yaml` package instead of `js-yaml` or a hand-written parser: `yaml` is the already-declared modern parser for this package's limited frontmatter needs, and a narrow parser would either reject valid YAML users expect to work or grow into an unreviewed YAML subset.
|
||||
|
||||
Skill filesystem I/O goes through `ctx.fs` when a filesystem service is loaded: project-root lookup probes `.git` with `resolve` and `stat`, root discovery uses `listDir`, skill reads use `readText`, and system-skill installation uses `writeText`. The Node filesystem remains a fallback for minimal contexts that mount `dsh-skill` without the fs seam. Missing roots and unreadable or malformed skill files degrade to warn-and-skip so one bad local file does not make every agent request fail.
|
||||
Local skill filesystem I/O goes through `ctx.fs` when a filesystem service is loaded: project-root lookup probes `.git` with `resolve` and `stat`, root discovery uses `listDir`, and skill reads use `readText`. The Node filesystem remains a fallback for minimal contexts that mount `dsh-skill-local` without the fs seam. Missing roots, unreadable or malformed skill files, and transient provider `list()` failures degrade to warn-and-skip so one bad source does not make every agent request fail; malformed candidates still fail fast because they are provider contract violations.
|
||||
|
||||
The service injects a request-time `## Skills` fragment through the existing `system-prompt/assemble` waterfall. It appends a late section for the calling agent instead of mutating `GenerateOptions.system` in `agent/request`, because request configuration is now reconstructable model/sampling state while model-visible content flows through system prompt assembly. The fragment contains only stable routing metadata and is sorted by skill name after first-wins collection, so equivalent workspaces produce deterministic prompt text and better prefix-cache reuse. Full skill bodies are never included in the listing.
|
||||
The service injects a request-time `## Skills` fragment through the existing `system-prompt/assemble` waterfall. It appends a late section for the calling agent instead of mutating `GenerateOptions.system` in `agent/request`, because request configuration is now reconstructable model/sampling state while model-visible content flows through system prompt assembly. The fragment contains only stable routing metadata, splits `{{` / `}}` before template rendering, and is sorted by skill name after first-wins collection, so equivalent workspaces produce deterministic prompt text and better prefix-cache reuse. Full skill bodies are never included in the listing.
|
||||
|
||||
The `skill({ name })` tool loads one full skill for the current agent cwd and returns a `<skill_content name="...">` block with the body plus base-directory guidance. Invalid names, unknown skills, and skills marked `disableModelInvocation` return tool errors. v1 does not additionally inject the loaded body into session context; the tool result is the model-visible disclosure path.
|
||||
|
||||
System skills are ordinary skill files materialized under `~/.dsh/skills/.system` on startup. v1 ships `dsh-plugin-creator` and `dsh-skill-creator` there so the agent can help author DeepSeek Harness plugins and future skills using the same mechanism users can override.
|
||||
The `skill({ name })` tool loads one full skill for the current agent cwd and returns a `<skill_content name="...">` block with the body plus provider resource guidance. Local filesystem skills include base-directory guidance; embedded or remote providers can return URL or opaque provider-managed guidance. Invalid names, unknown skills, and skills marked `disableModelInvocation` return tool errors. v1 does not additionally inject the loaded body into session context; the tool result is the model-visible disclosure path.
|
||||
|
||||
The data structures and prompt/tool contract are documented in [skills.md](../../../core-data-structures/skills.md), with service signatures in the generated [services catalog](../../../cordis-catalog/services.md).
|
||||
|
||||
@@ -32,14 +32,18 @@ The data structures and prompt/tool contract are documented in [skills.md](../..
|
||||
|
||||
**Expose skills only as slash commands.** Rejected for v1 because model-initiated loading is the core capability; slash/ACP command advertisement can layer on later without changing discovery.
|
||||
|
||||
**Put local filesystem scanning directly inside `ctx.skills`.** Rejected because coding agents, web agents, and future plugin ecosystems need different skill sources. A provider registry mirrors the subagent seam: the registry owns conflict resolution and consumers, while implementations own loading.
|
||||
|
||||
**Use a separate system-reminder message.** Rejected for the current loop because the provider-neutral system prompt surface is assembled through `system-prompt/assemble`. A later provider-specific surface can still split this fragment if needed.
|
||||
|
||||
**Materialize built-in DSH authoring skills under `~/.dsh/skills/.system`.** Rejected for v1 because bundled skills should not write user home on startup, and the product can receive those skills from a later embedded or remote provider.
|
||||
|
||||
**Recursively discover nested `**/SKILL.md`.** Rejected for v1. Flat files and one-level directory bundles cover the configured roots while keeping duplicate handling and prompt order easy to reason about.
|
||||
|
||||
**Hand-parse frontmatter.** Rejected because the accepted schema includes an open `metadata` object. A narrow parser would either reject valid YAML users expect to work or grow into an unreviewed YAML subset.
|
||||
|
||||
## Consequences
|
||||
|
||||
The agent-core spine now includes one more request-time contributor and one more model-facing tool. Skill discovery is cwd-sensitive, so tests and callers that create agents with different session cwd values can observe different project skill overrides by design.
|
||||
The agent-core spine now includes one more request-time contributor, one local provider, and one model-facing tool. Skill discovery is cwd-sensitive, so tests and callers that create agents with different session cwd values can observe different project skill overrides by design.
|
||||
|
||||
The prompt fragment is deterministic for a fixed root set and runtime registration revision, but disk changes are not watched; discovery is memoized until runtime registration invalidates the cache or the process restarts. That keeps v1 simple and avoids adding file watching policy before there is a concrete user flow for hot-reloading skills.
|
||||
|
||||
Reference in New Issue
Block a user