Add automatic session migration when switching from SDK to Native mode
Some checks failed
Release Binaries / release (push) Has been cancelled
Some checks failed
Release Binaries / release (push) Has been cancelled
This commit is contained in:
@@ -165,6 +165,34 @@ export const nativeSessionApi = {
|
||||
return data.messages
|
||||
},
|
||||
|
||||
/**
|
||||
* Import sessions from SDK mode to Native mode
|
||||
*/
|
||||
async importSessions(workspaceId: string, sessions: Array<{
|
||||
id: string
|
||||
title?: string
|
||||
parentId?: string | null
|
||||
createdAt?: number
|
||||
updatedAt?: number
|
||||
model?: { providerId: string; modelId: string }
|
||||
agent?: string
|
||||
messages?: Array<{
|
||||
id: string
|
||||
role: "user" | "assistant" | "system" | "tool"
|
||||
content?: string
|
||||
createdAt?: number
|
||||
}>
|
||||
}>): Promise<{ success: boolean; imported: number; skipped: number }> {
|
||||
const response = await fetch(`${CODENOMAD_API_BASE}/api/native/workspaces/${encodeURIComponent(workspaceId)}/sessions/import`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ sessions })
|
||||
})
|
||||
if (!response.ok) throw new Error("Failed to import sessions")
|
||||
return response.json()
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Send a prompt to the session and get a streaming response
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Message } from "../types/message"
|
||||
|
||||
import { instances } from "./instances"
|
||||
import { nativeSessionApi } from "../lib/lite-mode"
|
||||
import { needsMigration, migrateSessionsToNative, markMigrated, getExistingSdkSessions } from "./session-migration"
|
||||
import { preferences, setAgentModelPreference, getAgentModelPreference } from "./preferences"
|
||||
import { setSessionCompactionState } from "./session-compaction"
|
||||
import {
|
||||
@@ -389,6 +390,26 @@ async function fetchSessions(instanceId: string): Promise<void> {
|
||||
let responseData: any[] = []
|
||||
|
||||
if (isNative) {
|
||||
// Check if we need to migrate sessions from SDK mode
|
||||
if (needsMigration(instanceId)) {
|
||||
const existingSdkSessions = getExistingSdkSessions(instanceId)
|
||||
if (existingSdkSessions.length > 0) {
|
||||
log.info({ instanceId, count: existingSdkSessions.length }, "Migrating SDK sessions to native mode")
|
||||
const migrationData = existingSdkSessions.map(s => ({
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
parentId: s.parentId,
|
||||
time: s.time,
|
||||
model: s.model,
|
||||
agent: s.agent
|
||||
}))
|
||||
const result = await migrateSessionsToNative(instanceId, migrationData)
|
||||
log.info({ instanceId, result }, "Migration completed")
|
||||
} else {
|
||||
markMigrated(instanceId)
|
||||
}
|
||||
}
|
||||
|
||||
const nativeSessions = await nativeSessionApi.listSessions(instanceId)
|
||||
responseData = nativeSessions.map(s => ({
|
||||
id: s.id,
|
||||
|
||||
125
packages/ui/src/stores/session-migration.ts
Normal file
125
packages/ui/src/stores/session-migration.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Session Migration - Handles importing sessions when switching between SDK and Native modes
|
||||
*/
|
||||
|
||||
import { nativeSessionApi } from "../lib/lite-mode"
|
||||
import { sessions } from "./session-state"
|
||||
import { getLogger } from "../lib/logger"
|
||||
import type { Session } from "../types/session"
|
||||
|
||||
const log = getLogger("session-migration")
|
||||
|
||||
// Track which workspaces have already been migrated to prevent duplicate migrations
|
||||
const migratedWorkspaces = new Set<string>()
|
||||
|
||||
export interface MigrationResult {
|
||||
success: boolean
|
||||
imported: number
|
||||
skipped: number
|
||||
error?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a workspace needs session migration
|
||||
*/
|
||||
export function needsMigration(workspaceId: string): boolean {
|
||||
return !migratedWorkspaces.has(workspaceId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a workspace as migrated
|
||||
*/
|
||||
export function markMigrated(workspaceId: string): void {
|
||||
migratedWorkspaces.add(workspaceId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing SDK sessions for a workspace from the local store
|
||||
*/
|
||||
export function getExistingSdkSessions(instanceId: string): Session[] {
|
||||
const instanceSessions = sessions().get(instanceId)
|
||||
if (!instanceSessions) return []
|
||||
return Array.from(instanceSessions.values())
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate sessions from SDK mode to Native mode
|
||||
* This should be called when the user switches from an SDK binary to native mode
|
||||
*/
|
||||
export async function migrateSessionsToNative(
|
||||
workspaceId: string,
|
||||
sdkSessions: Array<{
|
||||
id: string
|
||||
title?: string
|
||||
parentId?: string | null
|
||||
time?: { created?: number; updated?: number }
|
||||
model?: { providerId: string; modelId: string }
|
||||
agent?: string
|
||||
messages?: Array<{
|
||||
id: string
|
||||
role: "user" | "assistant" | "system" | "tool"
|
||||
content?: string
|
||||
timestamp?: number
|
||||
}>
|
||||
}>
|
||||
): Promise<MigrationResult> {
|
||||
if (sdkSessions.length === 0) {
|
||||
log.info({ workspaceId }, "No sessions to migrate")
|
||||
markMigrated(workspaceId)
|
||||
return { success: true, imported: 0, skipped: 0 }
|
||||
}
|
||||
|
||||
try {
|
||||
log.info({ workspaceId, count: sdkSessions.length }, "Starting session migration to native mode")
|
||||
|
||||
// Transform to the format expected by the native API
|
||||
const sessionsToImport = sdkSessions.map(s => ({
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
parentId: s.parentId,
|
||||
createdAt: s.time?.created,
|
||||
updatedAt: s.time?.updated,
|
||||
model: s.model,
|
||||
agent: s.agent,
|
||||
messages: s.messages?.map(m => ({
|
||||
id: m.id,
|
||||
role: m.role,
|
||||
content: m.content,
|
||||
createdAt: m.timestamp
|
||||
}))
|
||||
}))
|
||||
|
||||
const result = await nativeSessionApi.importSessions(workspaceId, sessionsToImport)
|
||||
|
||||
log.info({ workspaceId, ...result }, "Session migration completed")
|
||||
markMigrated(workspaceId)
|
||||
|
||||
return {
|
||||
success: result.success,
|
||||
imported: result.imported,
|
||||
skipped: result.skipped
|
||||
}
|
||||
} catch (error) {
|
||||
log.error({ workspaceId, error }, "Session migration failed")
|
||||
return {
|
||||
success: false,
|
||||
imported: 0,
|
||||
skipped: 0,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear migration status (for testing or when user explicitly wants to re-migrate)
|
||||
*/
|
||||
export function clearMigrationStatus(workspaceId: string): void {
|
||||
migratedWorkspaces.delete(workspaceId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all migration statuses
|
||||
*/
|
||||
export function clearAllMigrationStatuses(): void {
|
||||
migratedWorkspaces.clear()
|
||||
}
|
||||
Reference in New Issue
Block a user