fix(web): persist input history so recall works after the first message#1015
Conversation
🦋 Changeset detectedLatest commit: aa83b9a The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
The composer has two mutually-exclusive instances: the empty-session composer and the docked composer. The first message of a new session is sent by the empty composer, which unmounts as soon as the first turn appears; the docked composer then mounted with an empty in-memory history, so ArrowUp did nothing until a second message was sent. The history was also lost on every page reload. Persist the history to localStorage as a single global list and re-read it on mount. Global (not per-session) because a new session has no id until after the first submit, so per-session keys would not line up across the empty -> docked handoff. Caps the list at 200 entries. Adds persistence-focused unit tests (surviving a remount, the 200-entry cap, and a malformed stored value).
Move the history.push call ahead of the slash-command branch so that known commands (with or without args, e.g. /goal <task> or /model) are recorded and can be recalled with ArrowUp, instead of only plain messages. Steer already pushed; only the submit slash path was missing it.
fe128dd to
32669b8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 32669b8e09
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // Record for ↑/↓ recall before the slash branch so commands (with or without | ||
| // args) are recallable too, not just plain messages. `push` ignores empty / | ||
| // whitespace, so an image-only send adds nothing. | ||
| history.push(trimmed); |
There was a problem hiding this comment.
Record slash-menu command selections in history
When a user runs a bare slash command from the open slash menu, the keydown handler calls selectSlashCommand and returns before handleSubmit; for non-acceptsInput commands such as /model or /login, that path emits the command directly. Since history is only pushed here, those common menu-selected slash commands still are not recallable even though typed-and-submitted slash commands are; consider pushing the selected command in selectSlashCommand before emitting it.
Useful? React with 👍 / 👎.
Bare slash commands picked from the slash menu (e.g. /model, /login) go through selectSlashCommand and emit directly, never reaching handleSubmit, so they were not recorded even after the typed-slash fix. Push the command name before emitting. acceptsInput commands are still recorded later by handleSubmit together with their argument.
Related Issue
Bug found while reviewing #1011. No standalone issue.
Problem
Pressing ↑ in the composer often did nothing, most notably right after sending the first message of a new session — the very case where you want to recall and edit what you just sent.
Root cause: the composer has two mutually-exclusive instances:
turns.length === 0), andChatDock, once there are turns).Each kept its own in-memory
inputHistory(ref([])), never persisted. So:The history was also wiped on every page reload for the same reason (purely in-memory).
What changed
useInputHistorynow persists the history tolocalStorage(keykimi-web.input-history) and re-reads it on mount.startSessionAndSendPromptruns at submit time), so per-session keys would not line up across the empty → docked handoff (the empty composer would writehistory:__new__while the docked composer readshistory:<realId>). A single global list sidesteps that and also matches shell-style recall.STORAGE_KEYSinlib/storage.ts./goal <task>,/model) — thehistory.pushcall moved ahead of the slash-command branch inhandleSubmit;/model,/login, …) —selectSlashCommandnow pushes before emitting, since those bypasshandleSubmitentirely.Previously only plain messages were recallable.
No change to the recall navigation logic itself — only where the list lives.
Verification
pnpm --filter @moonshot-ai/kimi-web typecheck— pass (vue-tsc --noEmit)pnpm --filter @moonshot-ai/kimi-web test— 55 passed (15 ininput-history.test.ts)pnpm --filter @moonshot-ai/kimi-web build— passoxlint --type-awareon the changed files — 0 warnings, 0 errorsChecklist
test/input-history.test.ts— persistence/remount/cap/corruption cases.)gen-changesetsskill, or this PR needs no changeset. (Added.changeset/fix-web-persist-input-history.md,patchon@moonshot-ai/kimi-code.)gen-docsskill, or this PR needs no doc update. (No user-facing doc change.)