diff --git a/hermes_cli/cli_chat_turn_mixin.py b/hermes_cli/cli_chat_turn_mixin.py index 174c38ca47..5a3e99591e 100644 --- a/hermes_cli/cli_chat_turn_mixin.py +++ b/hermes_cli/cli_chat_turn_mixin.py @@ -504,12 +504,16 @@ class CLIChatTurnMixin: from hermes_cli.gateway_chat_view import lost_admission from hermes_cli.gateway_client import GatewayClientError from hermes_cli.gateway_side import connection_for + admission_id = cmd_original.partition(" ")[2].strip() + if not admission_id: + _cprint(" Usage: /discard ") + return connection = connection_for(self) if connection is None: _cprint(" /discard: no gateway session is open") return try: - lost = lost_admission(connection.reattach(self.session_id), cmd_original.partition(" ")[2].strip()) + lost = lost_admission(connection.reattach(self.session_id), admission_id) connection.call("prompt.resolve_unknown", session_id=self.session_id, admission_id=lost["admission_id"], execution_generation=lost["execution_generation"]) connection.reattach(self.session_id) # the cached pending list the next prompt checks diff --git a/scripts/gen_gateway_command_parity.py b/scripts/gen_gateway_command_parity.py index e19ac6cae8..cc7bc0d25d 100755 --- a/scripts/gen_gateway_command_parity.py +++ b/scripts/gen_gateway_command_parity.py @@ -78,7 +78,7 @@ def _base_note(cmd): return "per-session setting (gateway handler)" if cmd.name in NOT_A_LOCAL_READ: return NOT_A_LOCAL_READ[cmd.name] - if cmd.gateway_only: + if cmd.gateway_only and classic_cell(cmd).startswith("messaging-only"): return "messaging-only" if cmd.cli_only: return "client-local (cli_only)" diff --git a/tests/hermes_cli/test_classic_cli_gateway_session.py b/tests/hermes_cli/test_classic_cli_gateway_session.py index e42918c916..6ab9b4cbdc 100644 --- a/tests/hermes_cli/test_classic_cli_gateway_session.py +++ b/tests/hermes_cli/test_classic_cli_gateway_session.py @@ -106,3 +106,17 @@ def test_a_prompt_behind_a_lost_turn_shows_the_discard_banner_and_discard_lets_i assert ("prompt.resolve_unknown", {"session_id": SID, "admission_id": "adm-lost", "execution_generation": 4}) in client.calls assert run_bounded(agent, "second ask")["final_response"] == "ok" + + +def test_a_bare_discard_prints_usage_and_calls_nothing(gateway, monkeypatch): + conn, client = gateway + from cli import HermesCLI + printed = [] + monkeypatch.setattr("cli._cprint", printed.append) + monkeypatch.setattr("hermes_cli.gateway_side.connection_for", lambda cli: conn) + calls = len(client.calls) + entry = HermesCLI._slash_handler("discard") + assert entry is not None + getattr(HermesCLI, entry[0])(SimpleNamespace(session_id=SID), "/discard") + assert printed == [" Usage: /discard "] + assert len(client.calls) == calls diff --git a/tests/hermes_cli/test_cli_gateway_model_route.py b/tests/hermes_cli/test_cli_gateway_model_route.py index a92b537acb..fab87c32a3 100644 --- a/tests/hermes_cli/test_cli_gateway_model_route.py +++ b/tests/hermes_cli/test_cli_gateway_model_route.py @@ -12,6 +12,8 @@ from hermes_cli.model_switch import ModelSwitchResult class RecordingConnection: + epoch = 1 + def __init__(self, info): from hermes_cli.gateway_connection import SessionState self.events = queue.Queue() @@ -22,6 +24,9 @@ class RecordingConnection: self._state = SessionState("s") self._state.info = dict(info) + def start(self, timeout=90): + pass + def state(self, session_id): return self._state diff --git a/tests/hermes_cli/test_gateway_tools_notify.py b/tests/hermes_cli/test_gateway_tools_notify.py index beec75e5d5..a72c62e877 100644 --- a/tests/hermes_cli/test_gateway_tools_notify.py +++ b/tests/hermes_cli/test_gateway_tools_notify.py @@ -26,7 +26,7 @@ def _gateway_frames(): # _admitted -> _publish_pending: the queued row (its public input text) before any claim. {"jsonrpc": "2.0", "method": "event", "params": { "type": "session.info", "session_id": SID, "payload": { - "running": False, "pending": [{"admission_id": "adm-9", "input_id": "x", "text": MODEL_TEXT}]}}}, + "running": False, "pending": [{"admission_id": "adm-9", "status": "queued", "input_id": "x", "text": MODEL_TEXT}]}}}, frame("message.start", {"display_kind": "process_complete", "display_text": DISPLAY, "text": MODEL_TEXT}, generation=9, admission="adm-9"), frame("message.delta", {"text": "STEP13 notified "}, generation=9, admission="adm-9"), diff --git a/website/docs/developer-guide/gateway-command-parity.md b/website/docs/developer-guide/gateway-command-parity.md index ebf7c5ac9f..d1b7defb5c 100644 --- a/website/docs/developer-guide/gateway-command-parity.md +++ b/website/docs/developer-guide/gateway-command-parity.md @@ -54,7 +54,7 @@ handler does; `client-local (cli_only)` commands are terminal/UI features of a c | `/debug` | control | HermesCLI._handle_debug_command | slash.exec | slash.exec | slash.exec | uploads logs to a public paste | | `/deny` | control | messaging-only: unknown command | slash.exec | slash.exec | slash.exec | messaging-only | | `/diff` | read | HermesCLI._handle_diff_command | slash.exec | slash.exec | slash.exec | | -| `/discard` | refused | HermesCLI._handle_discard_command | slash.exec: refused | slash.exec: refused | prompt.resolve_unknown | messaging-only | +| `/discard` | refused | HermesCLI._handle_discard_command | slash.exec: refused | slash.exec: refused | prompt.resolve_unknown | | | `/egress` | control | HermesCLI._cmd_egress | slash.exec | slash.exec | slash.exec | | | `/export` | refused | HermesCLI._handle_export_command | slash.exec: refused | action: export | slash.exec: refused | client-local (cli_only) | | `/fast` | control | HermesCLI._handle_fast_command | config.get, config.set | unavailable: advanced | slash.exec | per-session setting (gateway handler) |