Merge remote-tracking branch 'origin/master' into worktree/pr823-retarget-latest-20260729
# Conflicts: # docs/config-catalog.md # docs/cordis-catalog/services.md # docs/core-data-structures/skills.i18n.yaml # docs/core-data-structures/skills.md # docs/core-data-structures/skills.zh.md # packages/host/apiproxy/README.i18n.yaml # packages/skill/skill-local/README.i18n.yaml # packages/skill/skill/README.i18n.yaml # packages/skill/skill/README.md # packages/skill/skill/README.zh.md # packages/skill/skill/src/index.ts # packages/skill/skill/tests/skill.spec.ts # packages/skill/tool-skill/README.i18n.yaml # packages/skill/tool-skill/src/index.ts # packages/ui/tui/README.i18n.yaml # packages/ui/tui/README.md # packages/ui/tui/README.zh.md # packages/ui/tui/src/index.ts # packages/ui/tui/tests/tui.spec.ts
This commit is contained in:
@@ -10,11 +10,16 @@ This package owns the `ctx.skills` interface. It does not know whether skills co
|
||||
|
||||
### Public API
|
||||
|
||||
- `ctx.skills.registerProvider(provider): () => void` Registers a readonly provider by unique `provider.name`. Duplicate provider names throw, and `runtime` is reserved for `ctx.skills.register(...)`. The registry borrows the provider object and invokes its methods directly. The registration is effect-scoped and HMR-safe, and the exact Cordis disposer supports ordered composite teardown.
|
||||
- `ctx.skills.list({ cwd?, signal? })` Borrows the readonly lookup options, then returns every winning summary for the current workspace, merged across providers and sorted by name. The result is invocation-neutral; consumers apply `isModelInvocable(skill)` or `isUserInvocable(skill)` at their own boundary.
|
||||
- `ctx.skills.registerProvider(create): () => void` Calls a synchronous provider factory with `{ signal, invalidate }`, then registers its readonly result by unique `provider.name`. Duplicate names throw, `runtime` is reserved, and failed registration aborts the signal. The exact Cordis disposer unregisters the provider, aborts the signal, and preserves ordered composite teardown.
|
||||
- `ctx.skills.snapshot({ cwd?, signal? })` Returns the invocation-neutral `{ skills, complete }` observation. `complete` is false when any provider rejects or explicitly reports incomplete discovery, or when a second catalog revision races the bounded retry; candidates supplied by that observation remain in this result, which is never cached.
|
||||
- `ctx.skills.list({ cwd?, signal? })` Borrows the readonly lookup options, then returns every winning summary for the current workspace, merged across providers and sorted by name. Consumers apply `isModelInvocable(skill)` or `isUserInvocable(skill)` at their own boundary.
|
||||
- `ctx.skills.get(name, { cwd?, signal? })` Uses the same readonly options and winning candidate for discovery and loading, rechecks cancellation after discovery or a cache hit, races provider loading against the signal, validates the loaded definition, then returns it regardless of invocation policy.
|
||||
- `ctx.skills.register(skill): () => void` Registers a readonly runtime embedded skill, adding the all-invocable policy and `provider: "runtime"` when omitted. Same-name runtime registrations are first-wins: a duplicate logs a warning and gets a no-op disposer. Successful registrations return the exact Cordis disposer for ordered composite teardown.
|
||||
|
||||
### Events
|
||||
|
||||
- `skills/change` is an unfiltered invalidation notification emitted after a provider or runtime contribution is registered or disposed and after an active provider's registration control invalidates. It carries no catalog or diff: each consumer refetches `snapshot()` with its own lookup options. Listener throws and rejected promises are logged and cannot veto the registry mutation or starve later listeners.
|
||||
|
||||
### Config
|
||||
|
||||
| Field | Default | Meaning |
|
||||
@@ -36,11 +41,13 @@ This package owns the `ctx.skills` interface. It does not know whether skills co
|
||||
|
||||
## Provider Contract
|
||||
|
||||
A provider registers synchronously and performs remote setup, authentication, and discovery in its awaited `list(options)` call. Provider objects, lookup options, candidates, and definitions are borrowed readonly rather than cloned or rebound. Providers should honor `options.signal`; the registry also stops awaiting uncooperative discovery or loading after cancellation.
|
||||
A provider factory runs synchronously and receives one registration-scoped control. `control.signal` aborts when registration fails or is disposed; `control.invalidate()` clears completed catalogs only while that exact registration remains active, so late callbacks cannot affect a replacement with the same name. Immutable providers may ignore the control. Remote setup, authentication, and discovery belong in the provider's awaited `list(options)` call. An array return is shorthand for complete discovery; a provider that collected usable candidates but could not establish an authoritative observation returns `{ candidates, complete: false }`. Provider objects, lookup options, candidates, and definitions are borrowed readonly rather than cloned or rebound. Providers should honor `options.signal`; the registry also stops awaiting uncooperative discovery or loading after cancellation.
|
||||
|
||||
The registry validates candidates before caching and definitions before returning them. The winning provider receives the same candidate and opaque `locator` it returned from `list()`, allowing backend-specific file, URL, id, or version handles. Callers and providers must preserve the readonly contract.
|
||||
|
||||
Contract violations fail fast. A rejected `list()` is treated as a transient source failure: it is logged, skipped, and not cached. Only completed catalogs are cached; a provider or runtime revision change discards an in-flight result and retries. Duplicate names resolve by rank, provider registration order, then provider-local order. Summaries are sorted by skill name.
|
||||
Contract violations fail fast. A rejected provider `list()` is treated as a transient source failure and omitted. An explicit incomplete observation still contributes its candidates for `list()` and `get()`, but makes the aggregate snapshot incomplete and uncacheable. A provider or runtime revision change discards an in-flight result and retries once. If the retry is also superseded, its candidates are returned incomplete and uncached so a continuously invalidating provider cannot monopolize the caller. Duplicate names resolve by rank, provider registration order, then provider-local order. Summaries are sorted by skill name.
|
||||
|
||||
Definitions remain progressively loaded. `get()` asks the winning provider for the body on every call rather than caching it in this registry. If the returned definition has a different name from the selected candidate, the stale selection is rejected and the registry internally invalidates that exact provider so the next snapshot rediscovers its catalog.
|
||||
|
||||
## Runtime Skills
|
||||
|
||||
@@ -48,19 +55,19 @@ Contract violations fail fast. A rejected `list()` is treated as a transient sou
|
||||
|
||||
## Consumer boundary
|
||||
|
||||
The registry does not render model guidance or register model-facing tools. [`@deepseek-ai/dsh-tool-skill`](../tool-skill) consumes `ctx.skills` to provide the session-prefix catalog and `skill` tool, so providers remain independent of the model surface.
|
||||
The registry does not render model guidance or register model-facing tools. [`@deepseek-ai/dsh-tool-skill`](../tool-skill) consumes `ctx.skills` to provide durable session catalogs and the `skill` tool, so providers remain independent of the model surface.
|
||||
|
||||
## Model Experience
|
||||
|
||||
Indirectly, through `dsh-tool-skill`, which renders provider summaries into the session prefix and loaded instructions into retained tool results.
|
||||
Indirectly, through `dsh-tool-skill`, which renders provider summaries into durable initial or replacement catalog messages and loaded instructions into retained tool results.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
No direct invalidation; the named consumer owns any request-prefix changes.
|
||||
No direct prompt effect. The named consumer owns the durable initial catalog and append-only replacements after invalidation.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Completed catalogs have no TTL or watcher invalidation** — a provider's underlying files or remote data can change without a registration revision, so a cached cwd stays stale until eviction or provider/runtime reload.
|
||||
- **Invalidation is provider-driven** — the registry has no TTL and cannot infer that an arbitrary remote source changed; each mutable provider must retain and call its registration-scoped `invalidate()` capability from its own observation mechanism.
|
||||
- **Providers are queried sequentially** — one slow cooperative provider delays every provider registered after it; cancellation stops the caller's wait but cannot terminate work an uncooperative provider keeps running.
|
||||
- **A provider-list failure removes that whole source for the request** — the registry logs and skips it, with no model-visible diagnostic or partial-catalog recovery contract.
|
||||
- **Incomplete observations are not retained** — rejected providers are omitted and explicitly supplied candidates remain available only to the current lookup; the registry owns neither a last-good catalog nor per-provider diagnostics.
|
||||
- **Duplicate resolution is first-wins** — later lower-priority candidates are logged and hidden; there is no API to inspect all shadowed definitions.
|
||||
|
||||
Reference in New Issue
Block a user