Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
28 changes: 24 additions & 4 deletions apps/kimi-code/src/tui/commands/add-dir.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { NO_ACTIVE_SESSION_MESSAGE } from '../constant/kimi-tui';
import { ChoicePickerComponent } from '../components/dialogs/choice-picker';
import type { SlashCommandHost } from './dispatch';
import { slashBusyMessage, slashCommandBusyReason } from './resolve';

type AddDirChoice = 'session' | 'remember' | 'cancel';

export async function handleAddDirCommand(host: SlashCommandHost, args: string): Promise<void> {
const input = args.trim();
const session = host.session;
let session = host.session;

if (input.length === 0 || input.toLowerCase() === 'list') {
const additionalDirs = session?.summary?.additionalDirs ?? [];
// With no session yet (v2 session-less startup) the pending startup
// directories live in appState and will be passed to the lazy-created
// session; reflect them instead of reporting an empty list.
const additionalDirs = session?.summary?.additionalDirs ?? host.state.appState.additionalDirs;
if (additionalDirs.length === 0) {
host.showStatus('No additional directories configured.');
return;
Expand All @@ -19,8 +23,24 @@ export async function handleAddDirCommand(host: SlashCommandHost, args: string):
}

if (session === undefined) {
host.showError(NO_ACTIVE_SESSION_MESSAGE);
return;
if (!host.engineV2) {
host.showError(NO_ACTIVE_SESSION_MESSAGE);
return;
}
// The path-adding form needs a live session; lazy-create it on first use
// (the read-only `list`/bare forms above tolerate a missing session).
session = await host.ensureSession();
Comment thread
liruifengv marked this conversation as resolved.
if (session === undefined) return;
// A first prompt may have started a turn during the await; /add-dir is
// idle-only, so re-check the busy gate resolved before it.
const busyReason = slashCommandBusyReason({
isStreaming: host.state.appState.streamingPhase !== 'idle',
isCompacting: host.state.appState.isCompacting,
});
if (busyReason !== undefined) {
host.showError(slashBusyMessage('add-dir', busyReason));
return;
}
}

host.mountEditorReplacement(
Expand Down
49 changes: 37 additions & 12 deletions apps/kimi-code/src/tui/commands/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ export async function handlePlanCommand(host: SlashCommandHost, args: string): P
return;
}

// The session may already be in the requested mode (e.g. it was created
// with config.defaultPlanMode applied), and re-entering plan mode throws.
if (host.state.appState.planMode === enabled) {
host.showNotice(`Plan mode is already ${enabled ? 'on' : 'off'}`);
return;
}

await applyPlanMode(host, session, enabled);
}

Expand All @@ -117,10 +124,12 @@ async function applyPlanMode(host: SlashCommandHost, session: Session, enabled:

export async function handleYoloCommand(host: SlashCommandHost, args: string): Promise<void> {
const session = host.session;
if (session === undefined) {
if (session === undefined && !host.engineV2) {
host.showError(NO_ACTIVE_SESSION_MESSAGE);
return;
}
// v2 session-less: the chosen mode is recorded in appState and passed to the
// lazy-created session; apply the runtime permission only when one exists.

const subcmd = args.trim().toLowerCase();
const currentMode = host.state.appState.permissionMode;
Expand All @@ -130,7 +139,7 @@ export async function handleYoloCommand(host: SlashCommandHost, args: string): P
host.showNotice('YOLO mode is already on');
return;
}
await session.setPermission('yolo');
await session?.setPermission('yolo');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply sessionless permission changes after lazy startup settles

When a first v2 prompt is already inside lazy ensureSession(), /yolo on reaches this branch with session === undefined, so this optional call is skipped and only appState is changed. The in-flight create request has already captured the old permission, and the later runtime sync overwrites the optimistic state, so the command can report YOLO while the first session/prompt still runs with the previous mode; wait for the lazy creation to settle and apply the permission to the created session. The adjacent /auto path has the same race.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply sessionless permission changes after lazy startup settles

When a first v2 prompt is already inside lazy ensureSession(), /yolo on reaches this branch with session === undefined, so this optional call is skipped and only appState is changed. The in-flight create request has already captured the old permission, and the later runtime sync overwrites the optimistic state, so the command can report YOLO while the first session/prompt still runs with the previous mode; wait for the lazy creation to settle and apply the permission to the created session. The adjacent /auto path has the same race.

Useful? React with 👍 / 👎.

host.setAppState({ permissionMode: 'yolo' });
host.showNotice('YOLO mode: ON', 'Tool actions auto-approved; the agent may still ask you questions.');
return;
Expand All @@ -141,30 +150,32 @@ export async function handleYoloCommand(host: SlashCommandHost, args: string): P
host.showNotice('YOLO mode is already off');
return;
}
await session.setPermission('manual');
await session?.setPermission('manual');
host.setAppState({ permissionMode: 'manual' });
host.showNotice('YOLO mode: OFF');
return;
}

// toggle
if (currentMode === 'yolo') {
await session.setPermission('manual');
await session?.setPermission('manual');
host.setAppState({ permissionMode: 'manual' });
host.showNotice('YOLO mode: OFF');
} else {
await session.setPermission('yolo');
await session?.setPermission('yolo');
host.setAppState({ permissionMode: 'yolo' });
host.showNotice('YOLO mode: ON', 'Tool actions auto-approved; the agent may still ask you questions.');
}
}

export async function handleAutoCommand(host: SlashCommandHost, args: string): Promise<void> {
const session = host.session;
if (session === undefined) {
if (session === undefined && !host.engineV2) {
host.showError(NO_ACTIVE_SESSION_MESSAGE);
return;
}
// v2 session-less: the chosen mode is recorded in appState and passed to the
// lazy-created session; apply the runtime permission only when one exists.

const subcmd = args.trim().toLowerCase();
const currentMode = host.state.appState.permissionMode;
Expand All @@ -174,7 +185,7 @@ export async function handleAutoCommand(host: SlashCommandHost, args: string): P
host.showNotice('Auto mode is already on');
return;
}
await session.setPermission('auto');
await session?.setPermission('auto');
host.setAppState({ permissionMode: 'auto' });
host.showNotice('Auto mode: ON', 'All actions auto-approved; the agent will not ask you questions.');
return;
Expand All @@ -185,19 +196,19 @@ export async function handleAutoCommand(host: SlashCommandHost, args: string): P
host.showNotice('Auto mode is already off');
return;
}
await session.setPermission('manual');
await session?.setPermission('manual');
host.setAppState({ permissionMode: 'manual' });
host.showNotice('Auto mode: OFF');
return;
}

// toggle
if (currentMode === 'auto') {
await session.setPermission('manual');
await session?.setPermission('manual');
host.setAppState({ permissionMode: 'manual' });
host.showNotice('Auto mode: OFF');
} else {
await session.setPermission('auto');
await session?.setPermission('auto');
host.setAppState({ permissionMode: 'auto' });
host.showNotice('Auto mode: ON', 'All actions auto-approved; the agent will not ask you questions.');
}
Expand Down Expand Up @@ -463,6 +474,14 @@ async function performModelSwitch(
effort: ThinkingEffort,
persist: boolean,
): Promise<void> {
let session = host.session;
if (session === undefined && host.engineV2) {
// A first prompt may still be inside lazy creation: wait it out so the
// switch lands on the new session instead of being overwritten by its
// assembly.
await host.waitForLazyCreation();
session = host.session;
}
if (host.state.appState.streamingPhase !== 'idle') {
host.showError('Cannot switch models while streaming — press Esc or Ctrl-C first.');
return;
Expand All @@ -476,7 +495,6 @@ async function performModelSwitch(
let effectiveAlias = alias;
let effectiveEffort = effort;

const session = host.session;
try {
if (session === undefined && runtimeChanged) {
await host.authFlow.activateModelAfterLogin(alias, effort);
Expand Down Expand Up @@ -875,7 +893,14 @@ async function applyPermissionChoice(host: SlashCommandHost, mode: PermissionMod
}

try {
await host.requireSession().setPermission(mode);
if (host.session !== undefined) {
await host.session.setPermission(mode);
} else if (!host.engineV2) {
host.showError(NO_ACTIVE_SESSION_MESSAGE);
return;
}
// v2 session-less: the chosen mode is recorded in appState and passed to
// the lazy-created session.
} catch (error) {
const msg = formatErrorMessage(error);
host.showError(`Failed to set permission mode: ${msg}`);
Expand Down
Loading
Loading