mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-07 12:18:44 +02:00
feat: add backend session branching support
Add the backend contract and session-domain support for 'Branch from here': - new branchSession IPC method and sessions:branch channel - branchOrigin metadata on SessionRecord - session branching helper that truncates the transcript at a chosen user message and clears runtime state - AryxAppService branching flow with scratchpad directory support - persistence normalization and regression coverage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -62,6 +62,7 @@ import {
|
||||
} from '@shared/domain/approval';
|
||||
import { isScratchpadProject, type ProjectRecord } from '@shared/domain/project';
|
||||
import {
|
||||
branchSessionRecord,
|
||||
duplicateSessionRecord,
|
||||
querySessions as queryWorkspaceSessions,
|
||||
renameSessionRecord,
|
||||
@@ -703,6 +704,23 @@ export class AryxAppService extends EventEmitter<AppServiceEvents> {
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async branchSession(sessionId: string, messageId: string): Promise<WorkspaceState> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
const session = this.requireSession(workspace, sessionId);
|
||||
const pattern = this.requirePattern(workspace, session.patternId);
|
||||
const branch = branchSessionRecord(session, pattern, createId('session'), messageId, nowIso());
|
||||
if (isScratchpadProject(branch.projectId)) {
|
||||
branch.cwd = undefined;
|
||||
}
|
||||
|
||||
await this.ensureScratchpadSessionDirectory(branch);
|
||||
workspace.sessions.unshift(branch);
|
||||
workspace.selectedProjectId = branch.projectId;
|
||||
workspace.selectedPatternId = branch.patternId;
|
||||
workspace.selectedSessionId = branch.id;
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
async renameSession(sessionId: string, title: string): Promise<WorkspaceState> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
const session = this.requireSession(workspace, sessionId);
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { BrowserWindow } from 'electron';
|
||||
|
||||
import { ipcChannels } from '@shared/contracts/channels';
|
||||
import type {
|
||||
BranchSessionInput,
|
||||
CancelSessionTurnInput,
|
||||
CreateSessionInput,
|
||||
DismissSessionMcpAuthInput,
|
||||
@@ -135,6 +136,9 @@ export function registerIpcHandlers(window: BrowserWindow, service: AryxAppServi
|
||||
ipcMain.handle(ipcChannels.duplicateSession, (_event, input: DuplicateSessionInput) =>
|
||||
service.duplicateSession(input.sessionId),
|
||||
);
|
||||
ipcMain.handle(ipcChannels.branchSession, (_event, input: BranchSessionInput) =>
|
||||
service.branchSession(input.sessionId, input.messageId),
|
||||
);
|
||||
ipcMain.handle(ipcChannels.renameSession, (_event, input: RenameSessionInput) =>
|
||||
service.renameSession(input.sessionId, input.title),
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { isScratchpadProject, mergeScratchpadProject } from '@shared/domain/proj
|
||||
import { normalizeDiscoveredToolingState } from '@shared/domain/discoveredTooling';
|
||||
import { normalizeProjectCustomizationState } from '@shared/domain/projectCustomization';
|
||||
import { normalizeSessionRunRecords } from '@shared/domain/runTimeline';
|
||||
import type { SessionRecord } from '@shared/domain/session';
|
||||
import { normalizeSessionBranchOrigin, type SessionRecord } from '@shared/domain/session';
|
||||
import {
|
||||
normalizeSessionToolingSelection,
|
||||
normalizeWorkspaceSettings,
|
||||
@@ -81,6 +81,7 @@ export class WorkspaceRepository {
|
||||
const sessions = await Promise.all((stored.sessions ?? []).map(async (session): Promise<SessionRecord> => {
|
||||
const normalizedSession: SessionRecord = {
|
||||
...session,
|
||||
branchOrigin: normalizeSessionBranchOrigin(session.branchOrigin),
|
||||
runs: normalizeSessionRunRecords(session.runs),
|
||||
tooling: normalizeSessionToolingSelection(session.tooling),
|
||||
approvalSettings: normalizeSessionApprovalSettings(session.approvalSettings),
|
||||
|
||||
Reference in New Issue
Block a user