refactor(skill): canonicalize invocation policy
This commit is contained in:
@@ -47,7 +47,7 @@ import { foldSessionTitle } from '@deepseek-ai/dsh-session-title'
|
||||
// Type import also declaration-merges the optional `sessionPersistence`
|
||||
// service onto `Context` so `ctx.get('sessionPersistence')` is typed.
|
||||
import type {} from '@deepseek-ai/dsh-session-persistence'
|
||||
import type { SkillService, SkillSummary } from '@deepseek-ai/dsh-skill'
|
||||
import type { SkillService } from '@deepseek-ai/dsh-skill'
|
||||
// Type import declaration-merges the `userInteraction` service onto `Context`;
|
||||
// the ask-user-question queue is registered by ./chat/questions.
|
||||
import type {} from '@deepseek-ai/dsh-user-interaction'
|
||||
@@ -170,10 +170,6 @@ export type {
|
||||
TuiViewport,
|
||||
} from './extension/types.ts'
|
||||
|
||||
function isSkillUserInvocable(skill: Pick<SkillSummary, 'invocation'>): boolean {
|
||||
return skill.invocation?.userInvocable !== false
|
||||
}
|
||||
|
||||
declare module 'cordis' {
|
||||
interface Context {
|
||||
/** Terminal-only interaction service, available only while a TUI is mounted. */
|
||||
@@ -1076,7 +1072,7 @@ export function createTuiChat(
|
||||
const loadSkillCommands = (service: SkillService): void => {
|
||||
service.list({ cwd, signal: skillAbort.signal }).then(
|
||||
(summaries) => {
|
||||
const invocable = summaries.filter(isSkillUserInvocable)
|
||||
const invocable = summaries.filter(skill => skill.invocation.userInvocable)
|
||||
if (disposed || invocable.length === 0) return
|
||||
// The argument-hint slot shows in the menu but is never inserted on
|
||||
// selection, so it carries the skill's scope instead of an
|
||||
@@ -1277,7 +1273,7 @@ export function createTuiChat(
|
||||
appendNotice(`Unknown skill: ${name}`, 'warning')
|
||||
return
|
||||
}
|
||||
if (!isSkillUserInvocable(summary)) {
|
||||
if (!summary.invocation.userInvocable) {
|
||||
appendNotice(`Skill "${name}" is not available for user invocation.`, 'warning')
|
||||
return
|
||||
}
|
||||
@@ -1288,7 +1284,7 @@ export function createTuiChat(
|
||||
appendNotice(`Unknown skill: ${name}`, 'warning')
|
||||
return
|
||||
}
|
||||
if (!isSkillUserInvocable(skill)) {
|
||||
if (!skill.invocation.userInvocable) {
|
||||
appendNotice(`Skill "${name}" is not available for user invocation.`, 'warning')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4014,7 +4014,13 @@ describe('skill slash command', () => {
|
||||
listCalls += 1
|
||||
if (listCalls === 1) return Promise.resolve<SkillSummary[]>([])
|
||||
if (listCalls === 2) {
|
||||
return Promise.resolve<SkillSummary[]>([{ name: 'demo-skill', description: 'demo', source: 'runtime', provider: 'runtime' }])
|
||||
return Promise.resolve<SkillSummary[]>([{
|
||||
name: 'demo-skill',
|
||||
description: 'demo',
|
||||
invocation: { modelInvocable: true, userInvocable: true },
|
||||
source: 'runtime',
|
||||
provider: 'runtime',
|
||||
}])
|
||||
}
|
||||
return new Promise<SkillSummary[]>((resolve) => { resolvePendingList = resolve })
|
||||
},
|
||||
@@ -4031,8 +4037,21 @@ describe('skill slash command', () => {
|
||||
await tick()
|
||||
await dispose(result)
|
||||
|
||||
resolvePendingList?.([{ name: 'other-skill', description: 'late', source: 'runtime', provider: 'runtime' }])
|
||||
pendingGet[0]?.resolve({ name: 'demo-skill', description: 'late', source: 'runtime', provider: 'runtime', content: 'late body' })
|
||||
resolvePendingList?.([{
|
||||
name: 'other-skill',
|
||||
description: 'late',
|
||||
invocation: { modelInvocable: true, userInvocable: true },
|
||||
source: 'runtime',
|
||||
provider: 'runtime',
|
||||
}])
|
||||
pendingGet[0]?.resolve({
|
||||
name: 'demo-skill',
|
||||
description: 'late',
|
||||
invocation: { modelInvocable: true, userInvocable: true },
|
||||
source: 'runtime',
|
||||
provider: 'runtime',
|
||||
content: 'late body',
|
||||
})
|
||||
await tick()
|
||||
expect(result.agent.sent).toEqual([])
|
||||
expect(result.terminal.output).not.toContain('late body')
|
||||
@@ -4043,6 +4062,7 @@ describe('renderSkillInvocation', () => {
|
||||
const skill: SkillDefinition = {
|
||||
name: 'demo-skill',
|
||||
description: 'Demo skill',
|
||||
invocation: { modelInvocable: true, userInvocable: true },
|
||||
source: 'runtime',
|
||||
provider: 'runtime',
|
||||
content: 'Body text.',
|
||||
|
||||
Reference in New Issue
Block a user