feat(tool-skill): inject user-invoked skills at the pre-step gesture boundary
A whitespace-bounded /name token anywhere in a claimed user message, naming a user-invocable skill in the workspace directory, now injects that skill's renderSkillContent as instructions context appended after every other injection of the step — the same agent/pre-step seam the catalog, workspace instructions, and the runtime snapshot ride. Closed-set matching mirrors the command registry (a miss stays plain prose), only user-source messages are scanned, the policy check runs on the loaded definition, and this is the sole entry point for disable-model-invocation skills. The catalog's no-reload sentence now names the gesture boundary.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/skill/skill/README.md
|
||||
README.md: 0c1b2249d8c46ad9ce8097ceeda2bd988c92eb21
|
||||
README.zh.md: 8fed350d00433206aecdb32819adc81c82745869
|
||||
README.md: 3dc2bcfa5775736717bdebcb92329d5655198234
|
||||
README.zh.md: d11f90d5a8356f06df63aa249a1f8b5851f36f5f
|
||||
|
||||
@@ -39,7 +39,7 @@ This package owns the `ctx.skills` interface. It does not know whether skills co
|
||||
|
||||
### Shared model-facing rendering
|
||||
|
||||
`renderSkillContent(skill)` renders one loaded skill as the canonical `<skill_content>` block (escaped `name` attribute, resource hints, verbatim body). It is the single truth for both loading paths: `dsh-tool-skill` returns it as the `skill` tool result, and the host's user-explicit `skill.invoke` injects it as a user message, so the model sees one shape regardless of who initiated the load. `escapeText` is exported beside it for consumers embedding prose in the same markup frame. The package also declares the `skill-invocation` `MessageSource` kind ({ name, args? }) that user-explicit injection stamps on its messages — transcript consumers present the invocation from this metadata instead of re-parsing the body.
|
||||
`renderSkillContent(skill)` renders one loaded skill as the canonical `<skill_content>` block (escaped `name` attribute, resource hints, verbatim body). It is the single truth for both loading paths: `dsh-tool-skill` returns it as the `skill` tool result and injects it at the user-explicit gesture boundary, so the model sees one shape regardless of who initiated the load. `escapeText` is exported beside it for consumers embedding prose in the same markup frame. The package also declares the `skill-invocation` `MessageSource` kind ({ name, form: 'instructions' }) that user-explicit injection stamps on its messages — transcript consumers present the invocation from this metadata instead of re-parsing the body.
|
||||
|
||||
`isModelInvocable(skill)` and `isUserInvocable(skill)` read the matching positive field directly. `ctx.skills.get()` remains the trusted, policy-neutral loading primitive, so every user- or model-facing consumer must enforce the predicate that matches its surface before exposing or loading a skill.
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
### 共享的面向模型渲染
|
||||
|
||||
`renderSkillContent(skill)` 把一个已加载 skill 渲染为规范的 `<skill_content>` 块(转义后的 `name` 属性、资源提示、原样正文)。它是两条加载路径的唯一真源:`dsh-tool-skill` 将其作为 `skill` 工具结果返回,宿主的用户显式 `skill.invoke` 将其作为用户消息注入,因此无论加载由谁发起,模型看到的都是同一种形态。`escapeText` 随之一并导出,供要在同一标记框架中嵌入文案的消费方使用。该包还声明 `skill-invocation` 这个 `MessageSource` kind({ name, args? }),用户显式注入会把它打在自己的消息上——transcript(文本记录)消费方依据这份元数据呈现该次调用,而不是重新解析正文。
|
||||
`renderSkillContent(skill)` 把一个已加载 skill 渲染为规范的 `<skill_content>` 块(转义后的 `name` 属性、资源提示、原样正文)。它是两条加载路径的唯一真源:`dsh-tool-skill` 将其作为 `skill` 工具结果返回,并在用户显式的手势边界将其注入,因此无论加载由谁发起,模型看到的都是同一种形态。`escapeText` 随之一并导出,供要在同一标记框架中嵌入文案的消费方使用。该包还声明 `skill-invocation` 这个 `MessageSource` kind({ name, form: 'instructions' }),用户显式注入会把它打在自己的消息上——transcript(文本记录)消费方依据这份元数据呈现该次调用,而不是重新解析正文。
|
||||
|
||||
`isModelInvocable(skill)` 和 `isUserInvocable(skill)` 分别直接读取对应的正向字段。`ctx.skills.get()` 仍是受信且与策略无关的加载原语,因此每个面向用户或模型的消费方都必须先执行与自身接口匹配的判定,再暴露或加载 skill。
|
||||
|
||||
|
||||
@@ -121,17 +121,18 @@ export function isUserInvocable(skill: Pick<SkillSummary, 'invocation'>): boolea
|
||||
}
|
||||
|
||||
/**
|
||||
* Durable message source for a user-explicit skill invocation: the host
|
||||
* injects the rendered skill as a user-role message carrying this source, so
|
||||
* transcript consumers present the invocation from metadata instead of
|
||||
* re-parsing the model-facing text.
|
||||
* Durable source for the context message a user-explicit skill invocation
|
||||
* injects: the user's own words ride a plain user message, and the rendered
|
||||
* skill body follows as injected `instructions`-form context carrying this
|
||||
* source, so transcript consumers present the injection from metadata
|
||||
* instead of re-parsing the model-facing text.
|
||||
*/
|
||||
export interface SkillInvocationSource {
|
||||
readonly kind: 'skill-invocation'
|
||||
/** Invoked skill name, validated user-invocable at the injecting boundary. */
|
||||
readonly name: string
|
||||
/** Trailing free text the user submitted after the skill token, when present. */
|
||||
readonly args?: string
|
||||
/** Injected skill bodies are instructions for the model to follow. */
|
||||
readonly form: 'instructions'
|
||||
}
|
||||
|
||||
declare module '@deepseek-ai/dsh-llm' {
|
||||
|
||||
Reference in New Issue
Block a user