mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-30 03:00:41 -04:00
Merge branch 'fix/copilot-mode-per-session' of https://github.com/Significant-Gravitas/AutoGPT into preview/all-prs
This commit is contained in:
@@ -220,6 +220,62 @@ describe("useCopilotUIStore", () => {
|
||||
const parsed = JSON.parse(raw!) as [string, string][];
|
||||
expect(parsed).toEqual([["session-1", "fast"]]);
|
||||
});
|
||||
|
||||
it("removes a session mode entry and updates localStorage", () => {
|
||||
useCopilotUIStore.getState().setCopilotMode("fast");
|
||||
useCopilotUIStore.getState().recordSessionMode("session-1");
|
||||
useCopilotUIStore.getState().setCopilotMode("extended_thinking");
|
||||
useCopilotUIStore.getState().recordSessionMode("session-2");
|
||||
|
||||
useCopilotUIStore.getState().removeSessionMode("session-1");
|
||||
|
||||
expect(
|
||||
useCopilotUIStore.getState().sessionModes.has("session-1"),
|
||||
).toBe(false);
|
||||
expect(
|
||||
useCopilotUIStore.getState().sessionModes.get("session-2"),
|
||||
).toBe("extended_thinking");
|
||||
// localStorage should only have session-2
|
||||
const raw = window.localStorage.getItem("copilot-session-modes");
|
||||
const parsed = JSON.parse(raw!) as [string, string][];
|
||||
expect(parsed).toEqual([["session-2", "extended_thinking"]]);
|
||||
});
|
||||
|
||||
it("is a no-op when removing a session that was never recorded", () => {
|
||||
useCopilotUIStore.getState().setCopilotMode("fast");
|
||||
useCopilotUIStore.getState().recordSessionMode("session-1");
|
||||
const before = useCopilotUIStore.getState().sessionModes;
|
||||
useCopilotUIStore.getState().removeSessionMode("unknown-session");
|
||||
// State reference should not change (no re-render)
|
||||
expect(useCopilotUIStore.getState().sessionModes).toBe(before);
|
||||
});
|
||||
|
||||
it("ignores invalid mode strings from corrupt localStorage", () => {
|
||||
window.localStorage.setItem(
|
||||
"copilot-session-modes",
|
||||
JSON.stringify([
|
||||
["session-valid", "fast"],
|
||||
["session-bad", "invalid_mode"],
|
||||
["not-a-pair"],
|
||||
"garbage",
|
||||
]),
|
||||
);
|
||||
// Re-initialise by reading state directly via the getter used at init time
|
||||
// (simulate a fresh page load by clearing and re-setting the store)
|
||||
useCopilotUIStore.getState().clearCopilotLocalData();
|
||||
window.localStorage.setItem(
|
||||
"copilot-session-modes",
|
||||
JSON.stringify([
|
||||
["session-valid", "fast"],
|
||||
["session-bad", "invalid_mode"],
|
||||
]),
|
||||
);
|
||||
// Force store re-read by calling the internal persistence path
|
||||
useCopilotUIStore.getState().recordSessionMode("__probe__");
|
||||
// The corrupt entry should never be readable as a valid CopilotMode
|
||||
const state = useCopilotUIStore.getState();
|
||||
expect(state.sessionModes.get("session-bad")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("clearCopilotLocalData", () => {
|
||||
|
||||
@@ -79,12 +79,24 @@ function schedulePanelWidthPersist(width: number) {
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function isCopilotMode(value: unknown): value is CopilotMode {
|
||||
return value === "fast" || value === "extended_thinking";
|
||||
}
|
||||
|
||||
function getPersistedSessionModes(): Map<string, CopilotMode> {
|
||||
if (!isClient) return new Map();
|
||||
try {
|
||||
const raw = storage.get(Key.COPILOT_SESSION_MODES);
|
||||
if (raw) {
|
||||
const entries = JSON.parse(raw) as [string, CopilotMode][];
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (!Array.isArray(parsed)) return new Map();
|
||||
const entries = parsed.filter(
|
||||
(entry): entry is [string, CopilotMode] =>
|
||||
Array.isArray(entry) &&
|
||||
entry.length === 2 &&
|
||||
typeof entry[0] === "string" &&
|
||||
isCopilotMode(entry[1]),
|
||||
);
|
||||
return new Map(entries);
|
||||
}
|
||||
} catch {
|
||||
@@ -164,6 +176,8 @@ interface CopilotUIState {
|
||||
recordSessionMode: (sessionId: string) => void;
|
||||
/** Restore the copilot mode from a previously recorded session. */
|
||||
restoreSessionMode: (sessionId: string) => void;
|
||||
/** Remove the recorded mode for a session (call on session delete). */
|
||||
removeSessionMode: (sessionId: string) => void;
|
||||
|
||||
/** Developer dry-run mode: sessions created with dry_run=true. */
|
||||
isDryRun: boolean;
|
||||
@@ -333,8 +347,17 @@ export const useCopilotUIStore = create<CopilotUIState>((set) => ({
|
||||
storage.set(Key.COPILOT_MODE, mode);
|
||||
return { copilotMode: mode };
|
||||
}
|
||||
// Return same state reference to skip unnecessary re-render
|
||||
return state;
|
||||
}),
|
||||
removeSessionMode: (sessionId) =>
|
||||
set((state) => {
|
||||
if (!state.sessionModes.has(sessionId)) return state;
|
||||
const next = new Map(state.sessionModes);
|
||||
next.delete(sessionId);
|
||||
persistSessionModes(next);
|
||||
return { sessionModes: next };
|
||||
}),
|
||||
|
||||
isDryRun: isClient && storage.get(Key.COPILOT_DRY_RUN) === "true",
|
||||
setIsDryRun: (enabled) => {
|
||||
|
||||
@@ -46,6 +46,7 @@ export function useCopilotPage() {
|
||||
isDryRun,
|
||||
recordSessionMode,
|
||||
restoreSessionMode,
|
||||
removeSessionMode,
|
||||
} = useCopilotUIStore();
|
||||
|
||||
const {
|
||||
@@ -109,6 +110,9 @@ export function useCopilotPage() {
|
||||
if (sessionToDelete?.id === sessionId) {
|
||||
setSessionId(null);
|
||||
}
|
||||
if (sessionToDelete?.id) {
|
||||
removeSessionMode(sessionToDelete.id);
|
||||
}
|
||||
setSessionToDelete(null);
|
||||
},
|
||||
onError: (error) => {
|
||||
@@ -169,7 +173,7 @@ export function useCopilotPage() {
|
||||
} else {
|
||||
sendMessage({ text: msg });
|
||||
}
|
||||
}, [sessionId, pendingMessage, sendMessage]);
|
||||
}, [sessionId, pendingMessage, sendMessage, recordSessionMode]);
|
||||
|
||||
// --- Extract prompt from URL hash on mount (e.g. /copilot#prompt=Hello) ---
|
||||
useWorkflowImportAutoSubmit({
|
||||
|
||||
Reference in New Issue
Block a user