Fix session migration: cache SDK sessions to localStorage for auto-import on native mode startup
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:
@@ -3,7 +3,7 @@ import type { Message } from "../types/message"
|
|||||||
|
|
||||||
import { instances } from "./instances"
|
import { instances } from "./instances"
|
||||||
import { nativeSessionApi } from "../lib/lite-mode"
|
import { nativeSessionApi } from "../lib/lite-mode"
|
||||||
import { needsMigration, migrateSessionsToNative, markMigrated, getExistingSdkSessions } from "./session-migration"
|
import { needsMigration, autoImportCachedSessions, markMigrated, cacheSDKSessions } from "./session-migration"
|
||||||
import { preferences, setAgentModelPreference, getAgentModelPreference } from "./preferences"
|
import { preferences, setAgentModelPreference, getAgentModelPreference } from "./preferences"
|
||||||
import { setSessionCompactionState } from "./session-compaction"
|
import { setSessionCompactionState } from "./session-compaction"
|
||||||
import {
|
import {
|
||||||
@@ -390,23 +390,16 @@ async function fetchSessions(instanceId: string): Promise<void> {
|
|||||||
let responseData: any[] = []
|
let responseData: any[] = []
|
||||||
|
|
||||||
if (isNative) {
|
if (isNative) {
|
||||||
// Check if we need to migrate sessions from SDK mode
|
// Auto-import cached SDK sessions on native mode startup
|
||||||
if (needsMigration(instanceId)) {
|
if (needsMigration(instanceId)) {
|
||||||
const existingSdkSessions = getExistingSdkSessions(instanceId)
|
try {
|
||||||
if (existingSdkSessions.length > 0) {
|
const result = await autoImportCachedSessions(instanceId)
|
||||||
log.info({ instanceId, count: existingSdkSessions.length }, "Migrating SDK sessions to native mode")
|
if (result.imported > 0) {
|
||||||
const migrationData = existingSdkSessions.map(s => ({
|
log.info({ instanceId, result }, "Auto-imported SDK sessions to native mode")
|
||||||
id: s.id,
|
}
|
||||||
title: s.title,
|
} catch (error) {
|
||||||
parentId: s.parentId,
|
log.error({ instanceId, error }, "Failed to auto-import SDK sessions")
|
||||||
time: s.time,
|
markMigrated(instanceId) // Mark as migrated to prevent repeated failures
|
||||||
model: s.model,
|
|
||||||
agent: s.agent
|
|
||||||
}))
|
|
||||||
const result = await migrateSessionsToNative(instanceId, migrationData)
|
|
||||||
log.info({ instanceId, result }, "Migration completed")
|
|
||||||
} else {
|
|
||||||
markMigrated(instanceId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,6 +481,12 @@ async function fetchSessions(instanceId: string): Promise<void> {
|
|||||||
return next
|
return next
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Cache SDK sessions to localStorage for later migration to native mode
|
||||||
|
if (!isNative && sessionMap.size > 0) {
|
||||||
|
cacheSDKSessions(instanceId, Array.from(sessionMap.values()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
setMessagesLoaded((prev) => {
|
setMessagesLoaded((prev) => {
|
||||||
const next = new Map(prev)
|
const next = new Map(prev)
|
||||||
const loadedSet = next.get(instanceId)
|
const loadedSet = next.get(instanceId)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Session Migration - Handles importing sessions when switching between SDK and Native modes
|
* Session Migration - Handles importing sessions when switching between SDK and Native modes
|
||||||
|
*
|
||||||
|
* This module caches SDK session data to localStorage so it can be imported to Native mode
|
||||||
|
* when the user switches modes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { nativeSessionApi } from "../lib/lite-mode"
|
import { nativeSessionApi } from "../lib/lite-mode"
|
||||||
@@ -9,9 +12,22 @@ import type { Session } from "../types/session"
|
|||||||
|
|
||||||
const log = getLogger("session-migration")
|
const log = getLogger("session-migration")
|
||||||
|
|
||||||
|
// LocalStorage key prefix for cached SDK sessions
|
||||||
|
const SDK_SESSION_CACHE_PREFIX = "nomadarch_sdk_sessions_"
|
||||||
|
|
||||||
// Track which workspaces have already been migrated to prevent duplicate migrations
|
// Track which workspaces have already been migrated to prevent duplicate migrations
|
||||||
const migratedWorkspaces = new Set<string>()
|
const migratedWorkspaces = new Set<string>()
|
||||||
|
|
||||||
|
export interface CachedSession {
|
||||||
|
id: string
|
||||||
|
title?: string
|
||||||
|
parentId?: string | null
|
||||||
|
createdAt?: number
|
||||||
|
updatedAt?: number
|
||||||
|
model?: { providerId: string; modelId: string }
|
||||||
|
agent?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface MigrationResult {
|
export interface MigrationResult {
|
||||||
success: boolean
|
success: boolean
|
||||||
imported: number
|
imported: number
|
||||||
@@ -19,6 +35,63 @@ export interface MigrationResult {
|
|||||||
error?: string
|
error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache SDK sessions to localStorage for later migration
|
||||||
|
* This should be called whenever sessions are fetched in SDK mode
|
||||||
|
*/
|
||||||
|
export function cacheSDKSessions(workspaceId: string, sessionList: Session[]): void {
|
||||||
|
if (sessionList.length === 0) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const cached: CachedSession[] = sessionList.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
|
||||||
|
}))
|
||||||
|
|
||||||
|
const key = SDK_SESSION_CACHE_PREFIX + workspaceId
|
||||||
|
localStorage.setItem(key, JSON.stringify(cached))
|
||||||
|
log.info({ workspaceId, count: cached.length }, "Cached SDK sessions for migration")
|
||||||
|
} catch (error) {
|
||||||
|
log.error({ workspaceId, error }, "Failed to cache SDK sessions")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cached SDK sessions from localStorage
|
||||||
|
*/
|
||||||
|
export function getCachedSDKSessions(workspaceId: string): CachedSession[] {
|
||||||
|
try {
|
||||||
|
const key = SDK_SESSION_CACHE_PREFIX + workspaceId
|
||||||
|
const cached = localStorage.getItem(key)
|
||||||
|
if (!cached) return []
|
||||||
|
|
||||||
|
const sessions = JSON.parse(cached) as CachedSession[]
|
||||||
|
log.info({ workspaceId, count: sessions.length }, "Retrieved cached SDK sessions")
|
||||||
|
return sessions
|
||||||
|
} catch (error) {
|
||||||
|
log.error({ workspaceId, error }, "Failed to retrieve cached SDK sessions")
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear cached SDK sessions after successful migration
|
||||||
|
*/
|
||||||
|
export function clearCachedSDKSessions(workspaceId: string): void {
|
||||||
|
try {
|
||||||
|
const key = SDK_SESSION_CACHE_PREFIX + workspaceId
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
log.info({ workspaceId }, "Cleared cached SDK sessions")
|
||||||
|
} catch (error) {
|
||||||
|
log.error({ workspaceId, error }, "Failed to clear cached SDK sessions")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a workspace needs session migration
|
* Check if a workspace needs session migration
|
||||||
*/
|
*/
|
||||||
@@ -52,6 +125,8 @@ export async function migrateSessionsToNative(
|
|||||||
id: string
|
id: string
|
||||||
title?: string
|
title?: string
|
||||||
parentId?: string | null
|
parentId?: string | null
|
||||||
|
createdAt?: number
|
||||||
|
updatedAt?: number
|
||||||
time?: { created?: number; updated?: number }
|
time?: { created?: number; updated?: number }
|
||||||
model?: { providerId: string; modelId: string }
|
model?: { providerId: string; modelId: string }
|
||||||
agent?: string
|
agent?: string
|
||||||
@@ -77,8 +152,8 @@ export async function migrateSessionsToNative(
|
|||||||
id: s.id,
|
id: s.id,
|
||||||
title: s.title,
|
title: s.title,
|
||||||
parentId: s.parentId,
|
parentId: s.parentId,
|
||||||
createdAt: s.time?.created,
|
createdAt: s.createdAt || s.time?.created,
|
||||||
updatedAt: s.time?.updated,
|
updatedAt: s.updatedAt || s.time?.updated,
|
||||||
model: s.model,
|
model: s.model,
|
||||||
agent: s.agent,
|
agent: s.agent,
|
||||||
messages: s.messages?.map(m => ({
|
messages: s.messages?.map(m => ({
|
||||||
@@ -94,6 +169,11 @@ export async function migrateSessionsToNative(
|
|||||||
log.info({ workspaceId, ...result }, "Session migration completed")
|
log.info({ workspaceId, ...result }, "Session migration completed")
|
||||||
markMigrated(workspaceId)
|
markMigrated(workspaceId)
|
||||||
|
|
||||||
|
// Clear the cache after successful migration
|
||||||
|
if (result.success) {
|
||||||
|
clearCachedSDKSessions(workspaceId)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: result.success,
|
success: result.success,
|
||||||
imported: result.imported,
|
imported: result.imported,
|
||||||
@@ -110,6 +190,40 @@ export async function migrateSessionsToNative(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-import cached SDK sessions to native mode
|
||||||
|
* This is the main entry point for automatic migration on startup
|
||||||
|
*/
|
||||||
|
export async function autoImportCachedSessions(workspaceId: string): Promise<MigrationResult> {
|
||||||
|
if (!needsMigration(workspaceId)) {
|
||||||
|
return { success: true, imported: 0, skipped: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get cached sessions from localStorage
|
||||||
|
const cachedSessions = getCachedSDKSessions(workspaceId)
|
||||||
|
|
||||||
|
if (cachedSessions.length === 0) {
|
||||||
|
// Also check in-memory sessions as a fallback
|
||||||
|
const memorySessions = getExistingSdkSessions(workspaceId)
|
||||||
|
if (memorySessions.length > 0) {
|
||||||
|
const migrationData = memorySessions.map(s => ({
|
||||||
|
id: s.id,
|
||||||
|
title: s.title,
|
||||||
|
parentId: s.parentId,
|
||||||
|
time: s.time,
|
||||||
|
model: s.model,
|
||||||
|
agent: s.agent
|
||||||
|
}))
|
||||||
|
return migrateSessionsToNative(workspaceId, migrationData)
|
||||||
|
}
|
||||||
|
|
||||||
|
markMigrated(workspaceId)
|
||||||
|
return { success: true, imported: 0, skipped: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrateSessionsToNative(workspaceId, cachedSessions)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear migration status (for testing or when user explicitly wants to re-migrate)
|
* Clear migration status (for testing or when user explicitly wants to re-migrate)
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user