feat: add reload commands#383
Conversation
🦋 Changeset detectedLatest commit: aa19d63 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
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: 51c70c5425
ℹ️ 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".
| async closeForReload(): Promise<void> { | ||
| try { | ||
| await Promise.allSettled( | ||
| Array.from(this.agents.values(), async (agent) => agent.cron?.stop()), | ||
| ); | ||
| await this.flushMetadata(); |
There was a problem hiding this comment.
Preserve background tasks across reload
When /reload is run while a background bash/subagent task is still running, this new close path flushes the task as running but does not stop it or keep the live BackgroundManager; reloadSession then deletes the old Session and resumes a new one, and Agent.resume() reloads persisted tasks via loadFromDisk()/reconcile(), which marks any non-terminal ghost as lost. The result is an orphaned process that is still running but can no longer be listed/stopped or have its output tracked by the reloaded session.
Useful? React with 👍 / 👎.
| this.resetSessionRuntime(); | ||
| this.session = session; | ||
| this.harness.setTelemetryContext({ sessionId: session.id }); | ||
| this.registerSessionHandlers(session); | ||
| await this.syncRuntimeState(session); | ||
| this.refreshSessionTitle(); | ||
| try { | ||
| await this.refreshSkillCommands(session); | ||
| } catch { | ||
| /* keep the reloaded session usable even if dynamic skills fail */ | ||
| } | ||
| this.sessionEventHandler.startSubscription(); |
There was a problem hiding this comment.
Rehydrate the session snapshot after reload
For sessions with persisted UI state such as todos, background task metadata/counts, or replay-derived transcript state, this path calls resetSessionRuntime() and then starts the live subscription without running the same sessionReplay.hydrateFromReplay(session) flow used by startup and session switching. After /reload, those panes and controller maps are cleared and remain stale until new live events arrive, even though session.reloadSession() has just populated session.getResumeState() with the refreshed snapshot.
Useful? React with 👍 / 👎.
| if (active !== undefined) { | ||
| await active.closeForReload(); | ||
| this.sessions.delete(summary.id); | ||
| } | ||
| return this.resumeSession({ sessionId: summary.id }); |
There was a problem hiding this comment.
Keep the active session recoverable on reload failure
If the new config causes resumeSession() to fail after the old active session has been closed and removed here (for example a configured alias now has an invalid provider or MCP startup fails during resume), the SDK/TUI still keep the same Session wrapper in KimiHarness.activeSessions, but core no longer has that session in this.sessions. Subsequent prompts or even harness.resumeSession({id}) keep using the stale wrapper and hit SESSION_NOT_FOUND, so a failed /reload can wedge the current UI until restart instead of leaving the previous session usable or fully replacing the wrapper.
Useful? React with 👍 / 👎.
| host.setAppState({ | ||
| availableModels: config.models ?? {}, | ||
| availableProviders: config.providers ?? {}, |
There was a problem hiding this comment.
Apply refreshed default model when no session is active
In the no-active-session /reload path, this only refreshes the model/provider lists, so appState.model and thinking keep their old values even if config.toml changed default_model or default_thinking. Creating a new session afterwards still reads this.state.appState.model in createSessionFromCurrentState(), which can start with the stale alias (or fail if that alias was removed) despite the command reporting that runtime config was reloaded.
Useful? React with 👍 / 👎.
| const config = await host.harness.getConfig({ reload: true }); | ||
| applyRuntimeConfig(host, config); |
There was a problem hiding this comment.
Reload cached runtime when there is no active session
When /reload is used after closing the current session, this no-active path only calls getConfig({ reload: true }); that re-reads config.toml but does not clear KimiCore's cached ToolServices or reload the plugin manager. The next newly-created session still goes through createSession()/resolveRuntime(), which reuses the old cached services and old plugin-derived skill/MCP/session-start state, so changes to service URLs or plugin files are not actually applied even though the command reports that runtime config was reloaded.
Useful? React with 👍 / 👎.
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aa19d63a5a
ℹ️ 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".
| await active.closeForReload(); | ||
| this.sessions.delete(summary.id); | ||
| } | ||
| return this.resumeSession({ sessionId: summary.id }); |
There was a problem hiding this comment.
Preserve caller-provided MCP servers on reload
When a host creates or resumes a session with mcpServers, those servers are only merged from the create/resume payload before constructing the session runtime; they are not persisted in the session. This new reload path closes the active session and resumes it with only { sessionId }, so any host-scoped MCP servers that were active before /reload disappear after reload, breaking their tools even though the session itself resumes successfully.
Useful? React with 👍 / 👎.
Related Issue
No linked issue.
Problem
Users need a way to apply configuration changes from an active TUI without restarting the CLI, while keeping TUI-only preference reloads separate from full session reloads.
What changed
/reloadto reload the current session and apply updatedconfig.tomlsettings together withtui.tomlUI preferences./reload-tuito reload onlytui.tomlUI preferences without rebuilding the active session.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.