fix(cli): track model-sent slash command history - #3826
Conversation
…enLM#1804) When users input file paths starting with '/' (e.g. '/api/apiFunction/...', '/Users/name/path'), they were incorrectly parsed as slash commands, resulting in "Unknown command" errors. The input was discarded instead of being sent to the model for processing. Root cause: isSlashCommand() only checked for a '/' prefix without validating whether the first token actually looks like a command name. Any '/' prefix triggered the slash command flow, and when no matching command was found, the error was shown with no fallback. Fix: Add looksLikeCommandName() that validates command names contain only [a-zA-Z0-9:_-]. Both isSlashCommand() and handleSlashCommand() now check the first token — if it contains path separators, dots, or non-ASCII characters, the input falls through to normal model processing instead of the command dispatcher. Closes QwenLM#1804
Address review feedback: - Allow '.' in looksLikeCommandName() regex to support extension-qualified commands like gcp.deploy (CommandService renames conflicts as ext.cmd) - Add regression tests for dot-named commands in both commandUtils and slashCommandProcessor - Fix prettier formatting in slashCommandProcessor test file
yiliang114
left a comment
There was a problem hiding this comment.
The sentToModel persistence across resume is already wired in the current head (resumeHistoryUtils.ts L243-245). Both regex patterns already include the u flag. CI is green — mind taking another look?
wenshao
left a comment
There was a problem hiding this comment.
Review Summary
This PR adds sentToModel metadata tracking for slash-command history items. Good approach overall, but there is one blocking issue:
Critical
slashCommandProcessor.test.ts: TS2554 — Missing arguments at 3 test call sites (lines 1216, 1284, 1342)
useSlashCommandProcessor was updated to accept 17 arguments (added setSessionName and updateItem), but these 3 test hook calls still pass only 15 arguments. TypeScript reports TS2554: Expected 17 arguments, but got 15 at each location. This leaves setSessionName and updateItem as undefined in those test scenarios, meaning the submit_prompt → updateItem(…, { sentToModel: true }) code path (line 770) is completely uncovered in these tests.
Suggested fix: Append vi.fn() (for setSessionName) and vi.fn() (for updateItem) to the argument list at each of the 3 call sites:
vi.fn(), // setSessionName
vi.fn(), // updateItemAdditional Note
useEditorSettings.test.ts:44 — The type migration from Omit<HistoryItem, 'id'> to HistoryItemWithoutId is incomplete. useEditorSettings.ts was updated but this test file still uses the old pattern. Consider updating for consistency, though this was not part of the diff and is informational only.
— DeepSeek/deepseek-v4-pro via Qwen Code /review
wenshao
left a comment
There was a problem hiding this comment.
[Critical] TypeScript compilation failure — 3 call sites need updating
tsc --noEmit reports TS2554: Expected 17 arguments, but got 15 at three locations in packages/cli/src/ui/hooks/slashCommandProcessor.test.ts:
- Line 1216 —
useSlashCommandProcessor(...)passes 15 args, needssetSessionNameandupdateItem - Line 1284 — same issue
- Line 1342 — same issue
The function signature was expanded to accept setSessionName and updateItem, but these three test call sites were not updated. Tests pass at runtime because JS doesn't enforce arity, but tsc correctly rejects the code.
Fix: Add vi.fn() (or mockUpdateItem) as the two missing arguments at each call site.
— glm-5.1 via Qwen Code /review
08880d6 to
f7b3aaa
Compare
wenshao
left a comment
There was a problem hiding this comment.
No new issues found. All previously reported concerns have been addressed in the existing review rounds. Tests pass, typecheck clean, build succeeds. LGTM ✅
— DeepSeek/deepseek-v4-pro via Qwen Code /review
…llowup-pr' into fix/slash-routing-history-followup-pr
main currently exposes setSessionName as an optional trailing parameter. The earlier addition of updateItem pushed it before updateItem and forced every caller — including AppContainer and all tests — to pass an explicit undefined. Reorder so updateItem stays required and setSessionName remains optional, preserving the prior ergonomic.
Persistence wired in resumeHistoryUtils.ts:240-246; addressed at head 3cd426b.
Addressing remaining suggestions (2026-05-18 round)Thanks for the thorough review. Here's my assessment of the three open suggestions: 1. Optimistic
|
wenshao
left a comment
There was a problem hiding this comment.
Suggestion: The test "still records unrelated commands via the chat recorder (control)" in slashCommandProcessor.test.ts:1588 asserts recordSlashCommand was called but does not verify the sentToModel field in the recorded payload. For non-submitting commands (action returns undefined), sentToModel should be false. Consider updating to expect(recorder.recordSlashCommand).toHaveBeenCalledWith(expect.objectContaining({ phase: 'invocation', rawCommand: '/regular', sentToModel: false })).
— DeepSeek/deepseek-v4-pro via Qwen Code /review
Maintainer Verification ReportPR: #3826 — Build
Unit Tests
Static Analysis
E2E Functional Tests (17/17 PASS)Tested compiled JS modules directly:
Code ReviewArchitecture (18 files, +452/-71):
Security: No issues found. No new external inputs, no auth changes, no injection vectors. Backwards compatibility: Legacy sessions without VerdictAPPROVED — All 93 unit tests pass, 17/17 E2E functional tests pass, typecheck and formatting clean. The |
wenshao
left a comment
There was a problem hiding this comment.
No issues found. LGTM ✅ — qwen3.7-max via Qwen Code /review
Summary
Draft follow-up stacked conceptually on #3743.
This PR tracks whether a visible slash-command user history entry actually
reached the model. Some slash commands show
/commandin UI history but onlyperform local UI work, while commands that return
submit_promptsend generatedprompt content to the model. History rewind should distinguish those two cases.
Why this is split from #3743
#3743 is intentionally kept minimal for #1804: path-like slash input whose first
token contains a path separator should fall through to the model instead of
being treated as a slash command.
This PR handles a separate history correctness issue for slash commands that
submit prompts to the model. It should not broaden #3743's routing behavior.
What changed
sentToModelmetadata to user history items.submit_prompt.updateIteminto the slash command processor explicitly, matching theproduction caller.
sentToModelwhen present, while keeping theexisting lexical fallback for older history.
avoiding duplicate user history and duplicate slash-command recording/logging.
HistoryItemWithoutIdtype at history add/update boundaries.mainresume-session history notifications with the sametyped history boundary.
Out of scope
/data foo./README.md.Those are separate routing/product decisions and are not part of this follow-up.
Reviewer note
Until #3743 lands, GitHub may show both #3743 and this follow-up in the full PR
diff because this PR targets
main. The intended incremental review is thisPR's change on top of #3743.
Validation
cd packages/cli && npx vitest run src/ui/utils/historyMapping.test.ts src/ui/hooks/slashCommandProcessor.test.ts src/ui/hooks/useHistoryManager.test.ts src/ui/hooks/useResumeCommand.test.tsnpm run typechecknpm run lintnpm run build