mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-21 10:22:18 +00:00
fix(tui): handle dispatch payloads from slash exec (#49337)
This commit is contained in:
parent
cf58f1a520
commit
857d0244af
2 changed files with 82 additions and 39 deletions
|
|
@ -694,6 +694,42 @@ describe('createSlashHandler', () => {
|
|||
expect(ctx.transcript.send).toHaveBeenCalledWith(skillMessage)
|
||||
})
|
||||
|
||||
it('handles command.dispatch payloads returned directly by slash.exec', async () => {
|
||||
patchUiState({ sid: 'sid-abc' })
|
||||
|
||||
const ctx = buildCtx({
|
||||
gateway: {
|
||||
gw: {
|
||||
getLogTail: vi.fn(() => ''),
|
||||
request: vi.fn((method: string) => {
|
||||
if (method === 'slash.exec') {
|
||||
return Promise.resolve({
|
||||
message: 'complete all the steps and provide a final report',
|
||||
notice: '⊙ Goal set (20-turn budget): complete all the steps and provide a final report',
|
||||
type: 'send'
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.resolve({})
|
||||
})
|
||||
},
|
||||
rpc: vi.fn(() => Promise.resolve({}))
|
||||
}
|
||||
})
|
||||
|
||||
const h = createSlashHandler(ctx)
|
||||
expect(h('/goal complete all the steps and provide a final report')).toBe(true)
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(ctx.transcript.sys).toHaveBeenCalledWith(
|
||||
'⊙ Goal set (20-turn budget): complete all the steps and provide a final report'
|
||||
)
|
||||
})
|
||||
expect(ctx.transcript.send).toHaveBeenCalledWith('complete all the steps and provide a final report')
|
||||
expect(ctx.transcript.sys).not.toHaveBeenCalledWith('/goal: no output')
|
||||
expect(ctx.gateway.gw.request).not.toHaveBeenCalledWith('command.dispatch', expect.anything())
|
||||
})
|
||||
|
||||
it('/history pages the current TUI transcript (user + assistant)', () => {
|
||||
const ctx = buildCtx({
|
||||
local: {
|
||||
|
|
|
|||
|
|
@ -74,12 +74,57 @@ export function createSlashHandler(ctx: SlashHandlerContext): (cmd: string) => b
|
|||
}
|
||||
}
|
||||
|
||||
const handleDispatch = (raw: unknown): void => {
|
||||
const d = asCommandDispatch(raw)
|
||||
|
||||
if (!d) {
|
||||
return sys('error: invalid response: command.dispatch')
|
||||
}
|
||||
|
||||
if (d.type === 'exec' || d.type === 'plugin') {
|
||||
return sys(d.output || '(no output)')
|
||||
}
|
||||
|
||||
if (d.type === 'alias') {
|
||||
return void handler(`/${d.target}${argTail}`)
|
||||
}
|
||||
|
||||
if (d.type === 'skill') {
|
||||
sys(`⚡ loading skill: ${d.name}`)
|
||||
|
||||
return d.message?.trim() ? send(d.message) : sys(`/${parsed.name}: skill payload missing message`)
|
||||
}
|
||||
|
||||
if (d.type === 'send') {
|
||||
if (d.notice?.trim()) {
|
||||
sys(d.notice)
|
||||
}
|
||||
return d.message?.trim() ? send(d.message) : sys(`/${parsed.name}: empty message`)
|
||||
}
|
||||
|
||||
if (d.type === 'prefill') {
|
||||
// /undo returns prefill: drop the backed-up message text into
|
||||
// the composer so the user can edit and resubmit, instead of
|
||||
// submitting it immediately like 'send'.
|
||||
if (d.notice?.trim()) {
|
||||
sys(d.notice)
|
||||
}
|
||||
if (d.message) {
|
||||
ctx.composer.setInput(d.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gw.request<SlashExecResponse>('slash.exec', { command: cmd.slice(1), session_id: sid })
|
||||
.then(r => {
|
||||
if (stale()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (asCommandDispatch(r)) {
|
||||
return handleDispatch(r)
|
||||
}
|
||||
|
||||
const body = r?.output || `/${parsed.name}: no output`
|
||||
const text = r?.warning ? `warning: ${r.warning}\n${body}` : body
|
||||
const long = text.length > 180 || text.split('\n').filter(Boolean).length > 2
|
||||
|
|
@ -93,45 +138,7 @@ export function createSlashHandler(ctx: SlashHandlerContext): (cmd: string) => b
|
|||
return
|
||||
}
|
||||
|
||||
const d = asCommandDispatch(raw)
|
||||
|
||||
if (!d) {
|
||||
return sys('error: invalid response: command.dispatch')
|
||||
}
|
||||
|
||||
if (d.type === 'exec' || d.type === 'plugin') {
|
||||
return sys(d.output || '(no output)')
|
||||
}
|
||||
|
||||
if (d.type === 'alias') {
|
||||
return handler(`/${d.target}${argTail}`)
|
||||
}
|
||||
|
||||
if (d.type === 'skill') {
|
||||
sys(`⚡ loading skill: ${d.name}`)
|
||||
|
||||
return d.message?.trim() ? send(d.message) : sys(`/${parsed.name}: skill payload missing message`)
|
||||
}
|
||||
|
||||
if (d.type === 'send') {
|
||||
if (d.notice?.trim()) {
|
||||
sys(d.notice)
|
||||
}
|
||||
return d.message?.trim() ? send(d.message) : sys(`/${parsed.name}: empty message`)
|
||||
}
|
||||
|
||||
if (d.type === 'prefill') {
|
||||
// /undo returns prefill: drop the backed-up message text into
|
||||
// the composer so the user can edit and resubmit, instead of
|
||||
// submitting it immediately like 'send'.
|
||||
if (d.notice?.trim()) {
|
||||
sys(d.notice)
|
||||
}
|
||||
if (d.message) {
|
||||
ctx.composer.setInput(d.message)
|
||||
}
|
||||
return
|
||||
}
|
||||
handleDispatch(raw)
|
||||
})
|
||||
.catch(guardedErr)
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue