fix(tui): route /learn through command.dispatch so the prompt fires (#52232)

The Desktop GUI (tui_gateway) slash worker subprocess has no reader for
the CLI's _pending_input queue. /learn's CLI handler prints the ack and
puts the built prompt onto that queue, so in the TUI the prompt was
silently dropped — ack shown, no LLM turn, no skill created (#51829).

command.dispatch already handles 'learn' correctly (returns
{type: send, message: build_learn_prompt(arg)}), but 'learn' was missing
from _PENDING_INPUT_COMMANDS, so slash.exec fell through to the worker
instead of routing to command.dispatch. Add it to the frozenset, matching
the existing goal/queue/steer/plan pattern.
This commit is contained in:
Teknium 2026-06-24 18:48:50 -07:00 committed by GitHub
parent d1cac0e5ef
commit a4fa1481e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 34 additions and 1 deletions

View file

@ -1214,7 +1214,7 @@ def test_slash_exec_plugin_handler_error_returns_output(server):
assert worker.calls == []
@pytest.mark.parametrize("cmd", ["retry", "queue hello", "q hello", "steer fix the test", "plan"])
@pytest.mark.parametrize("cmd", ["retry", "queue hello", "q hello", "steer fix the test", "plan", "learn create a skill from https://example.com/docs"])
def test_slash_exec_routes_pending_input_commands_to_dispatch(server, cmd):
"""slash.exec must route _pending_input commands to command.dispatch
internally instead of returning the old 4018 "use command.dispatch"
@ -1288,6 +1288,38 @@ def test_command_dispatch_queue_requires_arg(server):
assert resp["error"]["code"] == 4004
def test_command_dispatch_learn_sends_built_prompt(server):
"""command.dispatch /learn returns {type: 'send', message: <built prompt>}
so the TUI fires a real agent turn (#51829). The CLI handler queues onto
_pending_input a queue the TUI slash worker has no reader for so the
prompt was silently dropped after the ack. Routing through command.dispatch
injects the standards-guided prompt as a normal turn instead.
"""
from agent.learn_prompt import build_learn_prompt
sid = "test-session"
server._sessions[sid] = {"session_key": sid}
arg = "create a skill from https://example.com/docs"
resp = server.handle_request({
"id": "r-learn",
"method": "command.dispatch",
"params": {"name": "learn", "arg": arg, "session_id": sid},
})
assert "error" not in resp
result = resp["result"]
assert result["type"] == "send"
assert result["message"] == build_learn_prompt(arg)
def test_pending_input_commands_includes_learn(server):
"""Guard: _PENDING_INPUT_COMMANDS must list 'learn' — without it slash.exec
routes /learn to the slash worker, which only prints the ack and drops the
prompt onto the dead _pending_input queue (#51829)."""
assert "learn" in server._PENDING_INPUT_COMMANDS
def test_skills_manage_search_uses_tools_hub_sources(server):
result = type("Result", (), {
"description": "Build better terminal demos",

View file

@ -9885,6 +9885,7 @@ _PENDING_INPUT_COMMANDS: frozenset[str] = frozenset(
"plan",
"goal",
"undo",
"learn",
}
)