mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-23 05:31:23 +00:00
fix(tui): handle timeout/error subagent statuses in /agents (#26687)
Accept delegation timeout/error statuses in the TUI subagent model, normalize unknown status strings defensively, and harden /agents overlay rendering/sorting so unknown statuses cannot crash glyph/color lookup. Add regression tests for live event normalization and disk snapshot replay.
This commit is contained in:
parent
566d8f0d75
commit
006937f7d0
8 changed files with 173 additions and 14 deletions
|
|
@ -737,6 +737,61 @@ describe('createGatewayEventHandler', () => {
|
|||
expect(getTurnState().activity).toMatchObject([{ text: 'boom', tone: 'error' }])
|
||||
})
|
||||
|
||||
it('accepts timeout/error subagent terminal statuses and ignores stale live events', () => {
|
||||
const appended: Msg[] = []
|
||||
const onEvent = createGatewayEventHandler(buildCtx(appended))
|
||||
|
||||
onEvent({
|
||||
payload: { goal: 'timeout child', subagent_id: 'sa-timeout', task_index: 0 },
|
||||
type: 'subagent.start'
|
||||
} as any)
|
||||
onEvent({
|
||||
payload: { goal: 'timeout child', status: 'timeout', subagent_id: 'sa-timeout', task_index: 0 },
|
||||
type: 'subagent.complete'
|
||||
} as any)
|
||||
|
||||
expect(getTurnState().subagents.find(s => s.id === 'sa-timeout')?.status).toBe('timeout')
|
||||
|
||||
// Late start/spawn updates must not clobber terminal timeout/error states.
|
||||
onEvent({
|
||||
payload: { goal: 'timeout child', subagent_id: 'sa-timeout', task_index: 0 },
|
||||
type: 'subagent.start'
|
||||
} as any)
|
||||
onEvent({
|
||||
payload: { goal: 'timeout child', subagent_id: 'sa-timeout', task_index: 0 },
|
||||
type: 'subagent.spawn_requested'
|
||||
} as any)
|
||||
|
||||
expect(getTurnState().subagents.find(s => s.id === 'sa-timeout')?.status).toBe('timeout')
|
||||
|
||||
onEvent({
|
||||
payload: { goal: 'error child', subagent_id: 'sa-error', task_index: 1 },
|
||||
type: 'subagent.start'
|
||||
} as any)
|
||||
onEvent({
|
||||
payload: { goal: 'error child', status: 'error', subagent_id: 'sa-error', task_index: 1 },
|
||||
type: 'subagent.complete'
|
||||
} as any)
|
||||
|
||||
expect(getTurnState().subagents.find(s => s.id === 'sa-error')?.status).toBe('error')
|
||||
})
|
||||
|
||||
it('normalizes unknown subagent.complete statuses to completed', () => {
|
||||
const appended: Msg[] = []
|
||||
const onEvent = createGatewayEventHandler(buildCtx(appended))
|
||||
|
||||
onEvent({
|
||||
payload: { goal: 'weird child', subagent_id: 'sa-weird', task_index: 2 },
|
||||
type: 'subagent.start'
|
||||
} as any)
|
||||
onEvent({
|
||||
payload: { goal: 'weird child', status: 'mystery_status', subagent_id: 'sa-weird', task_index: 2 },
|
||||
type: 'subagent.complete'
|
||||
} as any)
|
||||
|
||||
expect(getTurnState().subagents.find(s => s.id === 'sa-weird')?.status).toBe('completed')
|
||||
})
|
||||
|
||||
it('drops stale reasoning/tool/todos events after ctrl-c until the next message starts', () => {
|
||||
// Repro for the discord report: ctrl-c interrupts, but late reasoning/tool
|
||||
// events from the still-winding-down agent loop kept populating the UI for
|
||||
|
|
|
|||
46
ui-tui/src/__tests__/spawnHistoryStore.test.ts
Normal file
46
ui-tui/src/__tests__/spawnHistoryStore.test.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import { clearSpawnHistory, getSpawnHistory, pushDiskSnapshot } from '../app/spawnHistoryStore.js'
|
||||
|
||||
describe('spawnHistoryStore status normalization', () => {
|
||||
beforeEach(() => {
|
||||
clearSpawnHistory()
|
||||
})
|
||||
|
||||
it('keeps timeout/error statuses from disk snapshots', () => {
|
||||
pushDiskSnapshot(
|
||||
{
|
||||
finished_at: 1_700_000_001,
|
||||
label: 'status test',
|
||||
session_id: 'sess-1',
|
||||
started_at: 1_700_000_000,
|
||||
subagents: [
|
||||
{ goal: 'timeout child', id: 'sa-timeout', index: 0, status: 'timeout' },
|
||||
{ goal: 'error child', id: 'sa-error', index: 1, status: 'error' }
|
||||
]
|
||||
},
|
||||
'/tmp/snap-timeout-error.json'
|
||||
)
|
||||
|
||||
const statuses = getSpawnHistory()[0]?.subagents.map(s => s.status)
|
||||
|
||||
expect(statuses).toEqual(['timeout', 'error'])
|
||||
})
|
||||
|
||||
it('falls back unknown disk statuses to completed', () => {
|
||||
pushDiskSnapshot(
|
||||
{
|
||||
finished_at: 1_700_000_011,
|
||||
label: 'unknown status test',
|
||||
session_id: 'sess-2',
|
||||
started_at: 1_700_000_010,
|
||||
subagents: [{ goal: 'mystery child', id: 'sa-unknown', index: 0, status: 'mystery_status' }]
|
||||
},
|
||||
'/tmp/snap-unknown.json'
|
||||
)
|
||||
|
||||
const status = getSpawnHistory()[0]?.subagents[0]?.status
|
||||
|
||||
expect(status).toBe('completed')
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue