mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix(tui): address rollback guard and parity registry review
Load slash command names from the Python registry instead of regex-parsing source, and guard native rollback when no TUI session is active.
This commit is contained in:
@@ -193,7 +193,6 @@ describe('createSlashHandler', () => {
|
||||
it.each([
|
||||
['/browser status', 'browser.manage', { action: 'status' }],
|
||||
['/reload-mcp', 'reload.mcp', { session_id: null }],
|
||||
['/rollback', 'rollback.list', { session_id: null }],
|
||||
['/stop', 'process.stop', {}],
|
||||
['/fast status', 'config.get', { key: 'fast', session_id: null }],
|
||||
['/busy status', 'config.get', { key: 'busy' }]
|
||||
@@ -206,6 +205,16 @@ describe('createSlashHandler', () => {
|
||||
expect(ctx.gateway.gw.request).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('routes /rollback through native RPC when a session is active', () => {
|
||||
patchUiState({ sid: 'sid-abc' })
|
||||
const rpc = vi.fn(() => Promise.resolve({}))
|
||||
const ctx = buildCtx({ gateway: { ...buildGateway(), rpc } })
|
||||
|
||||
expect(createSlashHandler(ctx)('/rollback')).toBe(true)
|
||||
expect(rpc).toHaveBeenCalledWith('rollback.list', { session_id: 'sid-abc' })
|
||||
expect(ctx.gateway.gw.request).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('drops stale slash.exec output after a newer slash', async () => {
|
||||
let resolveLate: (v: { output?: string }) => void
|
||||
let slashExecCalls = 0
|
||||
@@ -412,6 +421,16 @@ describe('createSlashHandler', () => {
|
||||
expect(ctx.transcript.sys).toHaveBeenCalledWith('no active session — nothing to save')
|
||||
})
|
||||
|
||||
it('/rollback without an active session tells the user instead of hitting the RPC', () => {
|
||||
const rpc = vi.fn(() => Promise.resolve({}))
|
||||
const ctx = buildCtx({ gateway: { ...buildGateway(), rpc } })
|
||||
|
||||
createSlashHandler(ctx)('/rollback')
|
||||
|
||||
expect(rpc).not.toHaveBeenCalled()
|
||||
expect(ctx.transcript.sys).toHaveBeenCalledWith('no active session — nothing to rollback')
|
||||
})
|
||||
|
||||
it('/title <name> uses session.title RPC and bypasses slash.exec', async () => {
|
||||
patchUiState({ sid: 'sid-abc' })
|
||||
const rpc = vi.fn(() => Promise.resolve({ pending: false, title: 'my title' }))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
@@ -38,8 +38,17 @@ const MUTATING_COMMANDS = [
|
||||
|
||||
const loadCommandRegistryNames = (): string[] => {
|
||||
const here = dirname(fileURLToPath(import.meta.url))
|
||||
const source = readFileSync(resolve(here, '../../../hermes_cli/commands.py'), 'utf8')
|
||||
const names = [...source.matchAll(/CommandDef\("([^"]+)"/g)].map(match => match[1]!)
|
||||
|
||||
const names = JSON.parse(
|
||||
execFileSync(
|
||||
process.env.PYTHON ?? 'python3',
|
||||
[
|
||||
'-c',
|
||||
'import json; from hermes_cli.commands import COMMAND_REGISTRY; print(json.dumps([c.name for c in COMMAND_REGISTRY]))'
|
||||
],
|
||||
{ cwd: resolve(here, '../../..'), encoding: 'utf8' }
|
||||
)
|
||||
) as string[]
|
||||
|
||||
return [...new Set(names)]
|
||||
}
|
||||
|
||||
@@ -134,6 +134,10 @@ export const opsCommands: SlashCommand[] = [
|
||||
help: 'list, diff, or restore checkpoints',
|
||||
name: 'rollback',
|
||||
run: (arg, ctx) => {
|
||||
if (!ctx.sid) {
|
||||
return ctx.transcript.sys('no active session — nothing to rollback')
|
||||
}
|
||||
|
||||
const trimmed = arg.trim()
|
||||
const [first = '', ...rest] = trimmed.split(/\s+/).filter(Boolean)
|
||||
const lower = first.toLowerCase()
|
||||
|
||||
Reference in New Issue
Block a user