|
|
@@ -314,6 +314,8 @@ export const useCodexChatStore = defineStore('codex-chat', {
|
|
|
this.runDefaults = readRunDefaults();
|
|
|
this.board = readBoard();
|
|
|
this.syncCaseContext();
|
|
|
+ // 历史版本允许无限建空会话,这里清一次账:空会话只保留最新的一个
|
|
|
+ this.pruneEmptySessions();
|
|
|
const remembered = window.localStorage.getItem(ACTIVE_KEY) || '';
|
|
|
this.activeThreadId = this.sessions.some((s) => s.threadId === remembered)
|
|
|
? remembered
|
|
|
@@ -523,6 +525,56 @@ export const useCodexChatStore = defineStore('codex-chat', {
|
|
|
},
|
|
|
|
|
|
/** 新建会话:立刻向主进程开一个 thread,拿到真实 cwd 后落进列表 */
|
|
|
+ /** 会话是否为空(还没有任何提问与回复) */
|
|
|
+ isSessionEmpty(threadId: string): boolean {
|
|
|
+ if (!threadId) return true;
|
|
|
+ return buildCodexMessages(this.events[threadId] ?? [], this.prompts[threadId] ?? []).length === 0;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新建会话(带防重护栏)。
|
|
|
+ *
|
|
|
+ * 空会话没有任何保留价值,放任新建只会堆出一列「新会话」。
|
|
|
+ * 规则:当前会话是空的就不再新建;列表里已有空会话就切过去;都没有才真正创建。
|
|
|
+ * —— 保证任意时刻至多存在一个空会话。
|
|
|
+ */
|
|
|
+ async startNewSession(options?: {
|
|
|
+ cwd?: string | null;
|
|
|
+ model?: string | null;
|
|
|
+ runOptions?: Partial<CodexRunOptions>;
|
|
|
+ }): Promise<{ created: boolean; reason?: 'current-empty' | 'switched-empty'; meta?: CodexSessionMeta }> {
|
|
|
+ if (this.activeThreadId && this.isSessionEmpty(this.activeThreadId)) {
|
|
|
+ return { created: false, reason: 'current-empty' };
|
|
|
+ }
|
|
|
+ const empty = this.sessions.find((item) => this.isSessionEmpty(item.threadId));
|
|
|
+ if (empty) {
|
|
|
+ await this.selectSession(empty.threadId);
|
|
|
+ return { created: false, reason: 'switched-empty' };
|
|
|
+ }
|
|
|
+ const meta = await this.createSession(options);
|
|
|
+ return { created: true, meta };
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清理历史遗留的空会话:只保留最新的一个,其余从列表移除
|
|
|
+ * (不碰 Codex 侧已落盘的历史,与 removeSession 同一原则)。
|
|
|
+ */
|
|
|
+ pruneEmptySessions() {
|
|
|
+ const empty = this.sessions.filter((item) => this.isSessionEmpty(item.threadId));
|
|
|
+ if (empty.length <= 1) return;
|
|
|
+ const keep = [...empty].sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt))[0];
|
|
|
+ const removed = empty.filter((item) => item.threadId !== keep.threadId);
|
|
|
+ this.sessions = this.sessions.filter((item) => !removed.some((r) => r.threadId === item.threadId));
|
|
|
+ removed.forEach((item) => {
|
|
|
+ delete this.events[item.threadId];
|
|
|
+ delete this.prompts[item.threadId];
|
|
|
+ });
|
|
|
+ if (removed.some((item) => item.threadId === this.activeThreadId)) {
|
|
|
+ this.activeThreadId = keep.threadId;
|
|
|
+ }
|
|
|
+ this.persistSessions();
|
|
|
+ },
|
|
|
+
|
|
|
async createSession(options?: {
|
|
|
cwd?: string | null;
|
|
|
model?: string | null;
|