feat(#216): dynamic commands catalog + mid-turn slash command support (#395)

* feat(ws): add commands.catalog RPC constants and data models

* feat(catalog): fetch commands.catalog on WS gateway ready, store in state

* feat(commands): rewrite dispatcher to forward non-local commands via WS command.dispatch

* feat(input): always-visible send button + compact stop icon, allow / commands mid-turn

* test(commands): update tests for WS-based command dispatch

* fix: add missing CommandCatalog + JsonRpcError imports

* fix(test): assert errorMessage instead of messages for unknown command

* chore: remove unused CommandDispatchResponse data class

---------

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-06-26 14:14:51 +03:00
1 parent ef4c14e550
commit fafba3231b
6 files changed
+241 -98

No files matched your search

@@ -0,0 +1,22 @@
package com.m57.hermescontrol.data.ws
/**
* Response from `commands.catalog` RPC — full catalog of available
* slash commands, subcommands, and alias mappings.
*/
data class CommandCatalog(
val pairs: List<List<String>> = emptyList(),
val sub: Map<String, List<String>> = emptyMap(),
val canon: Map<String, String> = emptyMap(),
val categories: List<CommandCategory> = emptyList(),
val skillCount: Int = 0,
val warning: String = "",
)
/**
* A named category within the command catalog.
*/
data class CommandCategory(
val name: String,
val pairs: List<List<String>>,
)
@@ -2,16 +2,24 @@ package com.m57.hermescontrol.data.ws
/** JSON-RPC method name constants used with [HermesWsClient.send]. */
object WsMethods {
// ── Session ───────────────────────────────────────────────────────────
const val SESSION_LIST = "session.list"
const val SESSION_ACTIVE_LIST = "session.active_list"
const val SESSION_STATUS = "session.status"
const val SESSION_HISTORY = "session.history"
const val SESSION_RESUME = "session.resume"
const val SESSION_CREATE = "session.create"
const val PROMPT_SUBMIT = "prompt.submit"
const val SESSION_INTERRUPT = "session.interrupt"
const val SESSION_DELETE = "session.delete"
const val SESSION_TITLE = "session.title"
// ── Interaction ───────────────────────────────────────────────────────
const val PROMPT_SUBMIT = "prompt.submit"
const val CLARIFY_RESPOND = "clarify.respond"
const val APPROVAL_RESPOND = "approval.respond"
// ── Commands catalog ──────────────────────────────────────────────────
const val COMMANDS_CATALOG = "commands.catalog"
const val COMMAND_DISPATCH = "command.dispatch"
const val COMMAND_RESOLVE = "command.resolve"
}