Skip to content
4 changes: 4 additions & 0 deletions core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ export interface Session {
title: string;
workspaceDirectory: string;
history: ChatHistoryItem[];
/** Optional: per-session UI mode (chat/agent/plan/background) */
mode?: MessageModes;
/** Optional: title of the selected chat model for this session */
chatModelTitle?: string | null;
}

export interface BaseSessionMetadata {
Expand Down
9 changes: 8 additions & 1 deletion core/util/history.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as fs from "fs";

import { Session, BaseSessionMetadata } from "../index.js";
import { BaseSessionMetadata, Session } from "../index.js";
import { ListHistoryOptions } from "../protocol/core.js";

import { NEW_SESSION_TITLE } from "./constants.js";
Expand Down Expand Up @@ -108,6 +108,13 @@ export class HistoryManager {
workspaceDirectory: session.workspaceDirectory,
history: session.history,
};
if (session.mode) {
orderedSession.mode = session.mode;
}
if (session.chatModelTitle !== undefined) {
orderedSession.chatModelTitle = session.chatModelTitle;
}

fs.writeFileSync(
getSessionFilePath(session.sessionId),
JSON.stringify(orderedSession, undefined, 2),
Expand Down
3 changes: 3 additions & 0 deletions gui/src/redux/slices/sessionSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,9 @@ export const sessionSlice = createSlice({
state.history = payload.history as any;
state.title = payload.title;
state.id = payload.sessionId;
if (payload.mode) {
state.mode = payload.mode;
}
} else {
state.history = [];
state.title = NEW_SESSION_TITLE;
Expand Down
53 changes: 51 additions & 2 deletions gui/src/redux/thunks/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { NEW_SESSION_TITLE } from "core/util/constants";
import { renderChatMessage } from "core/util/messageContent";
import { IIdeMessenger } from "../../context/IdeMessenger";
import { selectSelectedChatModel } from "../slices/configSlice";
import { selectSelectedProfile } from "../slices/profilesSlice";
import {
deleteSessionMetadata,
newSession,
Expand All @@ -13,6 +14,7 @@ import {
updateSessionMetadata,
} from "../slices/sessionSlice";
import { ThunkApiType } from "../store";
import { updateSelectedModelByRole } from "../thunks/updateSelectedModelByRole";

const MAX_TITLE_LENGTH = 100;

Expand Down Expand Up @@ -103,7 +105,10 @@ export const loadSession = createAsyncThunk<
ThunkApiType
>(
"session/load",
async ({ sessionId, saveCurrentSession: save }, { extra, dispatch }) => {
async (
{ sessionId, saveCurrentSession: save },
{ extra, dispatch, getState },
) => {
if (save) {
const result = await dispatch(
saveCurrentSession({
Expand All @@ -115,6 +120,11 @@ export const loadSession = createAsyncThunk<
}
const session = await getSession(extra.ideMessenger, sessionId);
dispatch(newSession(session));

// Restore selected chat model from session, if present
if (session.chatModelTitle) {
dispatch(selectChatModelForProfile(session.chatModelTitle));
}
},
);

Expand All @@ -127,7 +137,10 @@ export const loadRemoteSession = createAsyncThunk<
ThunkApiType
>(
"session/loadRemote",
async ({ remoteId, saveCurrentSession: save }, { extra, dispatch }) => {
async (
{ remoteId, saveCurrentSession: save },
{ extra, dispatch, getState },
) => {
if (save) {
const result = await dispatch(
saveCurrentSession({
Expand All @@ -139,6 +152,35 @@ export const loadRemoteSession = createAsyncThunk<
}
const session = await getRemoteSession(extra.ideMessenger, remoteId);
dispatch(newSession(session));

// Restore selected chat model from session, if present
if (session.chatModelTitle) {
dispatch(selectChatModelForProfile(session.chatModelTitle));
}
},
);

export const selectChatModelForProfile = createAsyncThunk<
void,
string,
ThunkApiType
>(
"session/selectModelForCurrentProfile",
async (modelTitle, { extra, dispatch, getState }) => {
const state = getState();
const modelMatch = state.config.config?.modelsByRole?.chat?.find(
(m) => m.title === modelTitle,
);
const selectedProfile = selectSelectedProfile(state);
if (selectedProfile && modelMatch) {
await dispatch(
updateSelectedModelByRole({
role: "chat",
modelTitle: modelTitle,
selectedProfile,
}),
);
}
},
);

Expand Down Expand Up @@ -168,6 +210,9 @@ export const loadLastSession = createAsyncThunk<void, void, ThunkApiType>(
session = await getSession(extra.ideMessenger, lastSessionId);
}
dispatch(newSession(session));
if (session.chatModelTitle) {
dispatch(selectChatModelForProfile(session.chatModelTitle));
}
},
);

Expand Down Expand Up @@ -246,11 +291,15 @@ export const saveCurrentSession = createAsyncThunk<
title = NEW_SESSION_TITLE;
}

const selectedChatModel = selectSelectedChatModel(state);

const session: Session = {
sessionId: state.session.id,
title,
workspaceDirectory: window.workspacePaths?.[0] || "",
history: state.session.history,
mode: state.session.mode,
chatModelTitle: selectedChatModel?.title ?? null,
};

const result = await dispatch(updateSession(session));
Expand Down
Loading