This commit is contained in:
konsisumer 2026-04-24 22:35:15 +00:00 committed by GitHub
commit 620bf25ff8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 37 additions and 2 deletions

View file

@ -730,6 +730,41 @@ def test_complete_slash_surfaces_completer_error(monkeypatch):
assert "no completer" in resp["error"]["message"]
def test_complete_slash_display_is_plain_string(monkeypatch):
"""complete.slash must convert prompt_toolkit FormattedText display values
to plain strings before sending them over RPC.
Regression: c.display is a FormattedText object, not a str. Passing it
raw caused JSON serialization failures and broken rendering in the TUI
completion dropdown.
"""
from prompt_toolkit.formatted_text import FormattedText
class _FakeCompletion:
text = "/resume"
display = FormattedText([("class:command", "/resume")])
display_meta = FormattedText([("", "Resume a session")])
with patch("hermes_cli.commands.SlashCommandCompleter") as MockCompleter:
instance = MockCompleter.return_value
instance.get_completions.return_value = [_FakeCompletion()]
resp = server.handle_request(
{"id": "1", "method": "complete.slash", "params": {"text": "/res"}}
)
assert "result" in resp, resp
items = resp["result"]["items"]
assert len(items) >= 1
item = next(i for i in items if i["text"] == "/resume")
assert isinstance(item["display"], str), (
"display must be a plain str — FormattedText breaks JSON serialization"
)
assert item["display"] == "/resume"
assert isinstance(item["meta"], str)
assert item["meta"] == "Resume a session"
def test_input_detect_drop_attaches_image(monkeypatch):
fake_cli = types.ModuleType("cli")
fake_cli._detect_file_drop = lambda raw: {

View file

@ -3650,7 +3650,7 @@ def _(rid, params: dict) -> dict:
items = [
{
"text": c.text,
"display": c.display or c.text,
"display": to_plain_text(c.display) if c.display else c.text,
"meta": to_plain_text(c.display_meta) if c.display_meta else "",
}
for c in completer.get_completions(doc, None)

View file

@ -203,7 +203,7 @@ const ComposerPane = memo(function ComposerPane({
<FloatingOverlays
cols={composer.cols}
compIdx={composer.compIdx}
completions={composer.completions}
completions={isBlocked ? [] : composer.completions}
onModelSelect={actions.onModelSelect}
onPickerSelect={actions.resumeById}
pagerPageSize={composer.pagerPageSize}