fix(kimi-code): queue submissions until the slash-command catalog is ready - #3480
fix(kimi-code): queue submissions until the slash-command catalog is ready#34807Sageer wants to merge 1 commit into
Conversation
…ready At TUI startup the dynamic (skill/plugin) command catalog loads asynchronously while dispatchInput reads skillCommandMap synchronously, so a slash command submitted inside that window was mis-dispatched to the model as a plain user prompt. dispatchInput now defers submissions while dynamicCommandsReady is pending and drains them in submission order once the gate clears; the ready path is unchanged. The gate is infallible and carries a 10s fallback that clears it with a warning, so a wedged catalog load can never queue input forever. Startup and post-login bootstrap both arm the gate via refreshDynamicCommands.
|
@codex review |
🦋 Changeset detectedLatest commit: 946b949 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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 946b949e2d
ℹ️ 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".
| void pending.then(() => { | ||
| dispatchInput(host, text); | ||
| }); |
There was a problem hiding this comment.
Serialize deferred submissions before redispatching
When multiple inputs arrive during this gate, these independent promise callbacks preserve only callback-start order, not actual submission order. For example, with an active session, if the first prompt contains freshly pasted media, sendNormalUserInput pauses at pendingMediaIngestions (lines 1352–1364), while a following plain prompt proceeds synchronously and starts the turn; when the first resumes, it is queued behind the second. Drain deferred inputs serially through the point where each input is accepted or queued so the FIFO guarantee is maintained for asynchronous preparation paths.
Useful? React with 👍 / 👎.
Related Issue
No tracking issue — internal fix from a CI/harness race analysis: slash commands submitted right after TUI startup could be mis-dispatched as plain user prompts when the async skill/plugin catalog had not finished loading.
Problem
At TUI startup,
refreshSkillCommands/refreshPluginCommandsload the dynamic command catalog asynchronously (nothing blocks the UI), whiledispatchInputreadsskillCommandMapsynchronously. A submission arriving before the catalog settles (a ~230ms window in the harness, unbounded under CPU saturation) is treated as unknown slash text and sent to the model as a plain user prompt — the skill activation is silently lost. Fast typists hit the same race.What changed
dispatchInputdefers every submission whiledynamicCommandsReadyis pending; queued drains re-dispatch in submission order once the gate clears, so global input ordering is preserved. The ready path is byte-identical to before.KimiTUI.refreshDynamicCommandsarms the gate over the combined skill+plugin refresh viacreateDynamicCommandsGate: the gate is infallible (load success or failure both resolve it, so queued input can never be dropped by a rejection) and carries a 10s fallback — on timeout the gate clears and a warning is shown (Skill and plugin catalogs are still loading — slash commands may be incomplete for a moment.), so a wedged catalog load (e.g. stuck IPC) can never block input forever. A load that settles after the timeout still applies its results.finishStartup, post-login bootstrap inauth-flow) arm the gate throughrefreshDynamicCommands; mid-session refreshes keep calling the individual methods since the maps are already populated.goal.test.ts(defers while pending, FIFO drain order, plain-text ordering relative to slash submissions) and gate unit tests indynamic-commands-gate.test.ts(timeout warning fires once, settle-before-timeout stays quiet, load rejection still resolves the gate).Checklist
gen-changesetsskill, or this PR needs no changeset. (changeset included:@moonshot-ai/kimi-codepatch)gen-docsskill, or this PR needs no doc update. (internal timing fix, no user-facing behavior change beyond the fix itself)