feat(ui-skill): claim slash skill references into skill.invoke

A menu pick or an entered /name line now claims the composer into an
args-tolerant skill.invoke transaction instead of shipping the literal
text and hoping the model loads the skill. This gives every user-invocable
skill a deterministic entry point — including disable-model-invocation
skills the catalog never shows the model (issue #1470). Candidates carry
a user-only hint, and the unreached legacy <skill> reference codec is
removed (decision 21 removal cut).
This commit is contained in:
Yichen Jiang
2026-08-08 00:59:55 +08:00
parent cc0f6e11b9
commit 56e9e61749
5 changed files with 139 additions and 34 deletions

View File

@@ -198,6 +198,9 @@ export class FakeApiClient implements IApiClient {
onSkillList: (payload: unknown) => Promise<RpcResponse<{ skills: SkillEntry[] }>>
= () => Promise.resolve(ok({ skills: [] }))
onSkillInvoke: (payload: unknown) => Promise<RpcResponse<{ accepted: true }>>
= () => Promise.resolve(ok({ accepted: true as const }))
readonly commands: IApiClient['commands'] = {
list: (payload: unknown) => this.record('command.list', payload, this.onCommandList(payload)),
execute: (payload: unknown) => this.record('command.execute', payload, this.onCommandExecute(payload)),
@@ -205,6 +208,7 @@ export class FakeApiClient implements IApiClient {
readonly skills: IApiClient['skills'] = {
list: (payload: unknown) => this.record('skill.list', payload, this.onSkillList(payload)),
invoke: (payload: unknown) => this.record('skill.invoke', payload, this.onSkillInvoke(payload)),
}
readonly goals: IApiClient['goals'] = {