mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-28 13:47:12 +02:00
feat: add session library backend APIs
Add persisted session and pattern metadata for pinning, archiving, manual titles, and favorite patterns. Expose backend query and mutation APIs for session library features, and cover the shared helpers with tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -15,17 +15,24 @@ import {
|
||||
resolveReasoningEffort,
|
||||
} from '@shared/domain/models';
|
||||
import {
|
||||
buildSessionTitle,
|
||||
isReasoningEffort,
|
||||
type PatternDefinition,
|
||||
type ReasoningEffort,
|
||||
validatePatternDefinition,
|
||||
} from '@shared/domain/pattern';
|
||||
import { isScratchpadProject, type ProjectRecord } from '@shared/domain/project';
|
||||
import {
|
||||
duplicateSessionRecord,
|
||||
querySessions as queryWorkspaceSessions,
|
||||
renameSessionRecord,
|
||||
type QuerySessionsInput,
|
||||
type SessionQueryResult,
|
||||
} from '@shared/domain/sessionLibrary';
|
||||
import type { SessionEventRecord } from '@shared/domain/event';
|
||||
import {
|
||||
applyScratchpadSessionConfig,
|
||||
createScratchpadSessionConfig,
|
||||
resolveSessionTitle,
|
||||
type ChatMessageRecord,
|
||||
type SessionRecord,
|
||||
} from '@shared/domain/session';
|
||||
@@ -137,6 +144,7 @@ export class KopayaAppService extends EventEmitter<AppServiceEvents> {
|
||||
const existingIndex = workspace.patterns.findIndex((current) => current.id === pattern.id);
|
||||
const candidate: PatternDefinition = {
|
||||
...pattern,
|
||||
isFavorite: pattern.isFavorite ?? workspace.patterns[existingIndex]?.isFavorite,
|
||||
createdAt: existingIndex >= 0 ? workspace.patterns[existingIndex].createdAt : nowIso(),
|
||||
updatedAt: nowIso(),
|
||||
};
|
||||
@@ -151,6 +159,14 @@ export class KopayaAppService extends EventEmitter<AppServiceEvents> {
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async setPatternFavorite(patternId: string, isFavorite: boolean): Promise<WorkspaceState> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
const pattern = this.requirePattern(workspace, patternId);
|
||||
pattern.isFavorite = isFavorite;
|
||||
pattern.updatedAt = nowIso();
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async deletePattern(patternId: string): Promise<WorkspaceState> {
|
||||
if (isBuiltinPattern(patternId)) {
|
||||
throw new Error('Built-in patterns cannot be deleted.');
|
||||
@@ -178,6 +194,7 @@ export class KopayaAppService extends EventEmitter<AppServiceEvents> {
|
||||
projectId: project.id,
|
||||
patternId: pattern.id,
|
||||
title: pattern.name,
|
||||
titleSource: 'auto',
|
||||
createdAt: nowIso(),
|
||||
updatedAt: nowIso(),
|
||||
status: 'idle',
|
||||
@@ -194,6 +211,43 @@ export class KopayaAppService extends EventEmitter<AppServiceEvents> {
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async duplicateSession(sessionId: string): Promise<WorkspaceState> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
const session = this.requireSession(workspace, sessionId);
|
||||
const duplicate = duplicateSessionRecord(session, createId('session'), nowIso());
|
||||
|
||||
workspace.sessions.unshift(duplicate);
|
||||
workspace.selectedProjectId = duplicate.projectId;
|
||||
workspace.selectedPatternId = duplicate.patternId;
|
||||
workspace.selectedSessionId = duplicate.id;
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async renameSession(sessionId: string, title: string): Promise<WorkspaceState> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
const session = this.requireSession(workspace, sessionId);
|
||||
const renamed = renameSessionRecord(session, title, nowIso());
|
||||
|
||||
Object.assign(session, renamed);
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async setSessionPinned(sessionId: string, isPinned: boolean): Promise<WorkspaceState> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
const session = this.requireSession(workspace, sessionId);
|
||||
session.isPinned = isPinned;
|
||||
session.updatedAt = nowIso();
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async setSessionArchived(sessionId: string, isArchived: boolean): Promise<WorkspaceState> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
const session = this.requireSession(workspace, sessionId);
|
||||
session.isArchived = isArchived;
|
||||
session.updatedAt = nowIso();
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async sendSessionMessage(sessionId: string, content: string): Promise<void> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
const session = this.requireSession(workspace, sessionId);
|
||||
@@ -213,7 +267,7 @@ export class KopayaAppService extends EventEmitter<AppServiceEvents> {
|
||||
content: trimmed,
|
||||
createdAt: nowIso(),
|
||||
});
|
||||
session.title = buildSessionTitle(effectivePattern, session.messages);
|
||||
session.title = resolveSessionTitle(session, effectivePattern, session.messages);
|
||||
session.status = 'running';
|
||||
session.lastError = undefined;
|
||||
session.updatedAt = nowIso();
|
||||
@@ -301,6 +355,11 @@ export class KopayaAppService extends EventEmitter<AppServiceEvents> {
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async querySessions(input: QuerySessionsInput): Promise<SessionQueryResult[]> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
return queryWorkspaceSessions(workspace, input);
|
||||
}
|
||||
|
||||
async selectProject(projectId?: string): Promise<WorkspaceState> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
workspace.selectedProjectId = projectId;
|
||||
|
||||
@@ -3,10 +3,16 @@ import { BrowserWindow, ipcMain } from 'electron';
|
||||
import { ipcChannels } from '@shared/contracts/channels';
|
||||
import type {
|
||||
CreateSessionInput,
|
||||
DuplicateSessionInput,
|
||||
RenameSessionInput,
|
||||
SavePatternInput,
|
||||
SendSessionMessageInput,
|
||||
SetPatternFavoriteInput,
|
||||
SetSessionArchivedInput,
|
||||
SetSessionPinnedInput,
|
||||
UpdateScratchpadSessionConfigInput,
|
||||
} from '@shared/contracts/ipc';
|
||||
import type { QuerySessionsInput } from '@shared/domain/sessionLibrary';
|
||||
|
||||
import { KopayaAppService } from '@main/KopayaAppService';
|
||||
|
||||
@@ -18,9 +24,24 @@ export function registerIpcHandlers(window: BrowserWindow, service: KopayaAppSer
|
||||
ipcMain.handle(ipcChannels.removeProject, (_event, projectId: string) => service.removeProject(projectId));
|
||||
ipcMain.handle(ipcChannels.savePattern, (_event, input: SavePatternInput) => service.savePattern(input.pattern));
|
||||
ipcMain.handle(ipcChannels.deletePattern, (_event, patternId: string) => service.deletePattern(patternId));
|
||||
ipcMain.handle(ipcChannels.setPatternFavorite, (_event, input: SetPatternFavoriteInput) =>
|
||||
service.setPatternFavorite(input.patternId, input.isFavorite),
|
||||
);
|
||||
ipcMain.handle(ipcChannels.createSession, (_event, input: CreateSessionInput) =>
|
||||
service.createSession(input.projectId, input.patternId),
|
||||
);
|
||||
ipcMain.handle(ipcChannels.duplicateSession, (_event, input: DuplicateSessionInput) =>
|
||||
service.duplicateSession(input.sessionId),
|
||||
);
|
||||
ipcMain.handle(ipcChannels.renameSession, (_event, input: RenameSessionInput) =>
|
||||
service.renameSession(input.sessionId, input.title),
|
||||
);
|
||||
ipcMain.handle(ipcChannels.setSessionPinned, (_event, input: SetSessionPinnedInput) =>
|
||||
service.setSessionPinned(input.sessionId, input.isPinned),
|
||||
);
|
||||
ipcMain.handle(ipcChannels.setSessionArchived, (_event, input: SetSessionArchivedInput) =>
|
||||
service.setSessionArchived(input.sessionId, input.isArchived),
|
||||
);
|
||||
ipcMain.handle(ipcChannels.sendSessionMessage, (_event, input: SendSessionMessageInput) =>
|
||||
service.sendSessionMessage(input.sessionId, input.content),
|
||||
);
|
||||
@@ -29,6 +50,7 @@ export function registerIpcHandlers(window: BrowserWindow, service: KopayaAppSer
|
||||
(_event, input: UpdateScratchpadSessionConfigInput) =>
|
||||
service.updateScratchpadSessionConfig(input.sessionId, input.model, input.reasoningEffort),
|
||||
);
|
||||
ipcMain.handle(ipcChannels.querySessions, (_event, input: QuerySessionsInput) => service.querySessions(input));
|
||||
ipcMain.handle(ipcChannels.selectProject, (_event, projectId?: string) => service.selectProject(projectId));
|
||||
ipcMain.handle(ipcChannels.selectPattern, (_event, patternId?: string) => service.selectPattern(patternId));
|
||||
ipcMain.handle(ipcChannels.selectSession, (_event, sessionId?: string) => service.selectSession(sessionId));
|
||||
|
||||
Reference in New Issue
Block a user