mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-21 10:22:18 +00:00
fix(desktop): handle slash exec dispatch payloads (#49358)
This commit is contained in:
parent
857d0244af
commit
1b7b4d138a
2 changed files with 99 additions and 25 deletions
|
|
@ -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' })])
|
||||
|
|
|
|||
|
|
@ -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)}`)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue