Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d375565
feat(tui): lazy-create the session on first use with the v2 engine
liruifengv Jul 31, 2026
79fe2a1
fix(tui): serialize lazy session creation and carry session-only thin…
liruifengv Jul 31, 2026
cd52d72
fix(tui): don't re-enter plan mode when creating the lazy session
liruifengv Jul 31, 2026
e30180d
Merge remote-tracking branch 'origin/main' into feat/lazy-session-cre…
liruifengv Jul 31, 2026
2e21d4c
fix(tui): re-check the busy gate after lazy shell startup, keep /sett…
liruifengv Jul 31, 2026
c5e87a1
fix(tui): re-check busy state after lazy command creation, make /plug…
liruifengv Jul 31, 2026
c7392b6
fix(tui): keep the read-only /add-dir forms session-less
liruifengv Jul 31, 2026
bccb66a
fix(tui): reflect pending dirs in /add-dir list, refresh plugin comma…
liruifengv Aug 1, 2026
5562fd7
test(tui): add pluginCommandMap to the MessageDriver interface
liruifengv Aug 1, 2026
d2cffe2
fix(tui): hydrate lazy defaults on sessionless reload, guard /plan re…
liruifengv Aug 1, 2026
0bce21a
test(tui): widen the getConfig mock return type for the reload case
liruifengv Aug 1, 2026
0fa8827
fix(tui): clear stale lazy defaults when the default model disappears
liruifengv Aug 1, 2026
1f1d31a
fix(tui): reset a removed permission default, don't re-enter plan on …
liruifengv Aug 1, 2026
f43d440
fix(tui): keep read-only and mode commands session-less
liruifengv Aug 1, 2026
0c750fb
fix(tui): wait out lazy assembly in ensureSession, expose workspace MCP
liruifengv Aug 1, 2026
6423c5f
fix(tui): await workspace MCP readiness in the session-less /mcp list
liruifengv Aug 4, 2026
d5b03f2
fix(tui): hydrate the model default thinking effort for the session-l…
liruifengv Aug 4, 2026
c68b20a
style(tui): trim verbose comments in the lazy-default fixes
liruifengv Aug 4, 2026
4089a16
fix(tui): hydrate lazy defaults after login, serialize /new with lazy…
liruifengv Aug 4, 2026
07d509f
fix(tui): re-check the busy gate after lazy /add-dir session creation
liruifengv Aug 4, 2026
ec70188
fix(tui): let Shift-Tab lazy-create the session on a v2 session-less …
liruifengv Aug 4, 2026
0f752cb
fix(tui): hydrate session-less defaults on model-less login, refresh …
liruifengv Aug 4, 2026
27e712f
fix(tui): re-check the idle-only gate for /new after waiting out lazy…
liruifengv Aug 4, 2026
fd60d1b
fix(tui): wait out lazy creation before accepting model/effort switches
liruifengv Aug 4, 2026
1c8dd7d
fix(tui): wait out lazy creation before switching sessions from the p…
liruifengv Aug 4, 2026
fa9dd23
fix(node-sdk): read session-less skills from the workspace skill catalog
liruifengv Aug 4, 2026
c3e271e
feat(tui): show a session-less notice on v2 startup
liruifengv Aug 4, 2026
7ad4c0a
Merge branch 'main' into feat/lazy-session-creation
liruifengv Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 68 additions & 6 deletions apps/kimi-code/src/tui/commands/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ export interface SlashCommandHost {
state: TUIState;
session: Session | undefined;
readonly harness: KimiHarness;
/** agent-core-v2 engine (KIMI_CODE_EXPERIMENTAL_FLAG); enables lazy session creation. */
readonly engineV2: boolean;
cancelInFlight: (() => void) | undefined;
deferUserMessages: boolean;

Expand All @@ -120,6 +122,12 @@ export interface SlashCommandHost {

// Session
requireSession(): Session;
/**
* Lazy-create the session on first use (v2 engine). Returns the existing
* session, or undefined (with the error already surfaced) when creation
* fails.
*/
ensureSession(): Promise<Session | undefined>;
switchToSession(session: Session, message: string): Promise<void>;
reloadCurrentSessionView(session: Session, message: string): Promise<void>;
beginSessionRequest(): void;
Expand Down Expand Up @@ -204,11 +212,15 @@ async function executeSlashCommand(host: SlashCommandHost, input: string): Promi
host.showError(`Invalid slash command: /${intent.commandName}`);
return;
case 'skill': {
const session = host.session;
if (host.state.appState.model.trim().length === 0 || session === undefined) {
if (host.state.appState.model.trim().length === 0) {
host.showError(LLM_NOT_SET_MESSAGE);
return;
}
let session = host.session;
if (session === undefined) {
session = await ensureSessionForCommand(host);
if (session === undefined) return;
}
host.track('input_command', {
command: intent.commandName,
skill_name: intent.skillName,
Expand All @@ -221,10 +233,10 @@ async function executeSlashCommand(host: SlashCommandHost, input: string): Promi
host.showError(LLM_NOT_SET_MESSAGE);
return;
}
const session = host.session;
let session = host.session;
if (session === undefined) {
host.showError(LLM_NOT_SET_MESSAGE);
return;
session = await ensureSessionForCommand(host);
if (session === undefined) return;
}
host.track('input_command', { command: `${intent.pluginId}:${intent.commandName}` });
host.activatePluginCommand(session, intent.pluginId, intent.commandName, intent.args);
Expand Down Expand Up @@ -253,11 +265,54 @@ async function executeSlashCommand(host: SlashCommandHost, input: string): Promi
}
}

/**
* Lazy-create the session for a slash command that needs one (v2 engine).
* v1 keeps the historical "no active session" error; on v2 a missing session
* means the TUI started session-less, so commands create it on first use.
* Returns undefined (error already shown) when creation fails.
*/
async function ensureSessionForCommand(host: SlashCommandHost): Promise<Session | undefined> {
if (!host.engineV2) {
host.showError(LLM_NOT_SET_MESSAGE);
return undefined;
}
return host.ensureSession();
Comment thread
liruifengv marked this conversation as resolved.
}

/** Builtin commands that need an active session; lazy-created on the v2 engine. */
const SESSION_REQUIRING_COMMANDS: ReadonlySet<BuiltinSlashCommandName> = new Set([
Comment thread
liruifengv marked this conversation as resolved.
Comment thread
liruifengv marked this conversation as resolved.
'add-dir',
Comment thread
liruifengv marked this conversation as resolved.
Outdated
'auto',
'btw',
'compact',
'export-debug-zip',
'export-md',
'fork',
'goal',
Comment thread
liruifengv marked this conversation as resolved.
'init',
'mcp',
'permission',
Comment thread
liruifengv marked this conversation as resolved.
Outdated
'plan',
Comment thread
liruifengv marked this conversation as resolved.
'plugins',
Comment thread
liruifengv marked this conversation as resolved.
Outdated
'settings',
Comment thread
liruifengv marked this conversation as resolved.
Outdated
'status',
'swarm',
'title',
'undo',
'usage',
Comment thread
liruifengv marked this conversation as resolved.
Outdated
'web',
'yolo',
]);

async function handleBuiltInSlashCommand(
host: SlashCommandHost,
name: BuiltinSlashCommandName,
args: string,
): Promise<void> {
if (host.session === undefined && SESSION_REQUIRING_COMMANDS.has(name)) {
const session = await ensureSessionForCommand(host);
if (session === undefined) return;
}
switch (name) {
case 'exit':
void host.stop();
Expand All @@ -282,7 +337,14 @@ async function handleBuiltInSlashCommand(
void showMcpServers(host);
return;
case 'plugins':
void handlePluginsCommand(host, args);
// `handlePluginsCommand` throws when no session is active (its own
// requireSession), so catch here instead of letting the `void` call
// reject unhandled.
try {
await handlePluginsCommand(host, args);
} catch (error) {
host.showError(formatErrorMessage(error));
}
return;
case 'add-dir':
await handleAddDirCommand(host, args);
Expand Down
19 changes: 18 additions & 1 deletion apps/kimi-code/src/tui/controllers/auth-flow.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { CreateSessionOptions, KimiHarness, Session } from '@moonshot-ai/kimi-code-sdk';
import type {
CreateSessionOptions,
KimiHarness,
Session,
ThinkingEffort,
} from '@moonshot-ai/kimi-code-sdk';

import { createKimiCodeUserAgent } from '#/cli/version';

Expand All @@ -24,6 +29,7 @@ export interface AuthFlowHost {
session: Session | undefined;
readonly harness: KimiHarness;
readonly options: KimiTUIOptions;
readonly engineV2: boolean;

setAppState(patch: Partial<AppState>): void;
setStartupReady(): void;
Expand Down Expand Up @@ -75,6 +81,17 @@ export class AuthFlowController {
return;
}

if (host.engineV2) {
// Lazy session creation (v2 engine): configure the model only; the
// session is created on the first message.
const patch: Partial<AppState> = { model };
Comment thread
liruifengv marked this conversation as resolved.
if (effort !== undefined) {
patch.thinkingEffort = effort as ThinkingEffort;
}
host.setAppState(patch);
Comment thread
liruifengv marked this conversation as resolved.
return;
}

const options: MutableCreateSessionOptions = {
workDir: host.state.appState.workDir,
model,
Expand Down
Loading
Loading