diff --git a/packages/launcher/src/main/agent-manager.ts b/packages/launcher/src/main/agent-manager.ts index 507bd4027..25bde2aad 100644 --- a/packages/launcher/src/main/agent-manager.ts +++ b/packages/launcher/src/main/agent-manager.ts @@ -2,6 +2,7 @@ import path from "path" import fs from "fs" import os from "os" import https from "https" +import crypto from "crypto" import { spawnSync } from "child_process" import { withPathEnv } from "./env" import { EventEmitter } from "events" @@ -2131,6 +2132,39 @@ export class AgentManager extends EventEmitter { return removed } + createChatSession(workspaceId: string): ChatSessionMeta { + const ws = this._resolveChatWorkspace(workspaceId) + if (!ws) throw new Error("Workspace not found") + + const dir = path.join(LAUNCHER_SESSIONS_DIR, ws.id) + ensureDir(dir) + + let channelName = "" + let file = "" + do { + channelName = `chat-${Date.now()}-${crypto.randomUUID().slice(0, 8)}` + file = path.join(dir, `${channelName}.json`) + } while (fs.existsSync(file)) + + const now = new Date().toISOString() + const meta: ChatSessionMeta = { + id: `${ws.id}:${channelName}`, + workspaceId: ws.id, + workspaceSlug: ws.slug, + workspaceName: ws.name, + channelName, + title: ws.name || ws.slug || channelName, + lastMessageAt: null, + lastMessagePreview: null, + messageCount: 0, + participants: [], + createdAt: now, + } + + fs.writeFileSync(file, JSON.stringify(meta, null, 2), "utf-8") + return meta + } + private _touchChatSession( ws: WorkspaceConfig, channelName: string, diff --git a/packages/launcher/src/main/index.ts b/packages/launcher/src/main/index.ts index 91b007f08..432c453ec 100644 --- a/packages/launcher/src/main/index.ts +++ b/packages/launcher/src/main/index.ts @@ -1463,6 +1463,9 @@ function setupIPC(): void { ipcMain.handle("session:list", (_e, workspaceId) => requireManager().listChatSessions(workspaceId), ) + ipcMain.handle("session:create", (_e, workspaceId) => + requireManager().createChatSession(workspaceId), + ) ipcMain.handle("session:load", (_e, workspaceId, channelName) => requireManager().loadChatSession(workspaceId, channelName), ) diff --git a/packages/launcher/src/preload/index.ts b/packages/launcher/src/preload/index.ts index 370f93945..a2cc09611 100644 --- a/packages/launcher/src/preload/index.ts +++ b/packages/launcher/src/preload/index.ts @@ -109,6 +109,7 @@ contextBridge.exposeInMainWorld('api', { // ── Sessions ── sessionList: (workspaceId?: string) => ipcRenderer.invoke('session:list', workspaceId), + sessionCreate: (workspaceId: string) => ipcRenderer.invoke('session:create', workspaceId), sessionLoad: (workspaceId: string, channelName: string) => ipcRenderer.invoke('session:load', workspaceId, channelName), sessionDelete: (workspaceId: string, channelName: string) => diff --git a/packages/launcher/src/renderer/pages/chat/index.tsx b/packages/launcher/src/renderer/pages/chat/index.tsx index 6951f9d1c..bd01d509c 100644 --- a/packages/launcher/src/renderer/pages/chat/index.tsx +++ b/packages/launcher/src/renderer/pages/chat/index.tsx @@ -15,8 +15,6 @@ import SessionList from '../../components/chat/SessionList' import { ConfirmDialog } from '../../components/ui/ConfirmDialog' import type { ToastType } from '../../hooks/useToast' -const DEFAULT_CHANNEL = 'main' - interface ChatPageProps { showToast: (msg: string, type?: ToastType) => void } @@ -277,9 +275,16 @@ export default function ChatPage({ showToast }: ChatPageProps): React.JSX.Elemen void activate(workspaceId, channelName) } - const handleNewChat = (): void => { + const handleNewChat = async (): Promise => { if (!selectedWorkspaceId) return - void activate(selectedWorkspaceId, DEFAULT_CHANNEL) + try { + const session = await window.api.sessionCreate(selectedWorkspaceId) + setSelectedWorkspaceId(session.workspaceId) + await refreshSessions() + await activate(session.workspaceId, session.channelName) + } catch (e: unknown) { + showToast(`New chat failed: ${(e as Error).message}`, 'error') + } } const requestDeleteSession = (workspaceId: string, channelName: string): void => { diff --git a/packages/launcher/src/renderer/types/index.ts b/packages/launcher/src/renderer/types/index.ts index 91aa45673..134d7b706 100644 --- a/packages/launcher/src/renderer/types/index.ts +++ b/packages/launcher/src/renderer/types/index.ts @@ -378,6 +378,7 @@ declare global { // ── Sessions ── sessionList(workspaceId?: string): Promise + sessionCreate(workspaceId: string): Promise sessionLoad(workspaceId: string, channelName: string): Promise sessionDelete(workspaceId: string, channelName: string): Promise sessionClear(workspaceId?: string): Promise