fix(desktop): handle slash exec dispatch payloads (#49358)

This commit is contained in:
Harish Kukreja 2026-06-19 22:11:16 -04:00 committed by GitHub
parent 857d0244af
commit 1b7b4d138a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 99 additions and 25 deletions

View file

@ -205,6 +205,67 @@ describe('usePromptActions /title', () => {
})
})
describe('usePromptActions slash.exec dispatch payloads', () => {
afterEach(() => {
cleanup()
$busy.set(false)
vi.restoreAllMocks()
})
it('submits /goal send directives returned directly by slash.exec instead of rendering no output', async () => {
const calls: { method: string; params?: Record<string, unknown> }[] = []
const states: Record<string, unknown>[] = []
const requestGateway = vi.fn(async (method: string, params?: Record<string, unknown>) => {
calls.push({ method, params })
if (method === 'slash.exec') {
return {
type: 'send',
notice: '⊙ Goal set. Starting now.',
message: 'write the implementation plan'
} as never
}
return {} as never
})
let handle: HarnessHandle | null = null
render(
<Harness
onReady={h => (handle = h)}
onSeedState={s => states.push(s)}
refreshSessions={async () => undefined}
requestGateway={requestGateway}
/>
)
await handle!.submitText('/goal write the implementation plan')
expect(calls.map(c => c.method)).toEqual(['slash.exec', 'prompt.submit'])
expect(calls[0]?.params).toEqual({
command: 'goal write the implementation plan',
session_id: RUNTIME_SESSION_ID
})
expect(calls[1]?.params).toEqual({
session_id: RUNTIME_SESSION_ID,
text: 'write the implementation plan'
})
const renderedText = states
.flatMap(state => {
const messages = Array.isArray(state.messages)
? (state.messages as Array<{ parts?: Array<{ text?: string }> }>)
: []
return messages.flatMap(message => (message.parts ?? []).map(part => part.text ?? ''))
})
.join('\n')
expect(renderedText).toContain('⊙ Goal set. Starting now.')
expect(renderedText).not.toContain('/goal: no output')
})
})
describe('usePromptActions desktop slash pickers', () => {
beforeEach(() => {
setSessions(() => [sessionInfo({ id: '20260610_120000_abcdef', title: 'Loaded session' })])

View file

@ -915,31 +915,7 @@ export function usePromptActions({
return
}
try {
const result = await requestGateway<SlashExecResponse>('slash.exec', {
session_id: sessionId,
command: command.replace(/^\/+/, '')
})
const body = result?.output || `/${name}: no output`
renderSlashOutput(result?.warning ? `warning: ${result.warning}\n${body}` : body)
return
} catch {
// Fall back to command.dispatch for skill/send/alias directives.
}
try {
const dispatch = parseCommandDispatch(
await requestGateway<unknown>('command.dispatch', { session_id: sessionId, name, arg })
)
if (!dispatch) {
renderSlashOutput('error: invalid response: command.dispatch')
return
}
const handleDispatch = async (dispatch: NonNullable<ReturnType<typeof parseCommandDispatch>>): Promise<void> => {
if (dispatch.type === 'exec' || dispatch.type === 'plugin') {
renderSlashOutput(dispatch.output ?? '(no output)')
@ -991,6 +967,43 @@ export function usePromptActions({
}
await submitPromptText(message)
}
try {
const result = await requestGateway<unknown>('slash.exec', {
session_id: sessionId,
command: command.replace(/^\/+/, '')
})
const dispatch = parseCommandDispatch(result)
if (dispatch) {
await handleDispatch(dispatch)
return
}
const output = result && typeof result === 'object' ? (result as SlashExecResponse) : null
const body = output?.output || `/${name}: no output`
renderSlashOutput(output?.warning ? `warning: ${output.warning}\n${body}` : body)
return
} catch {
// Fall back to command.dispatch for skill/send/alias directives.
}
try {
const dispatch = parseCommandDispatch(
await requestGateway<unknown>('command.dispatch', { session_id: sessionId, name, arg })
)
if (!dispatch) {
renderSlashOutput('error: invalid response: command.dispatch')
return
}
await handleDispatch(dispatch)
} catch (err) {
renderSlashOutput(`error: ${err instanceof Error ? err.message : String(err)}`)
}