@@ -56,7 +56,11 @@ import {
5656} from "../routing/session-key.js" ;
5757import { normalizeSessionKeyPreservingOpaquePeerIds } from "../sessions/session-key-utils.js" ;
5858import type { DB as OpenClawStateKyselyDatabase } from "../state/openclaw-state-db.generated.js" ;
59- import { runOpenClawStateWriteTransaction } from "../state/openclaw-state-db.js" ;
59+ import {
60+ detectOpenClawStateDatabaseSchemaMigrations ,
61+ repairOpenClawStateDatabaseSchema ,
62+ runOpenClawStateWriteTransaction ,
63+ } from "../state/openclaw-state-db.js" ;
6064import { expandHomePrefix } from "./home-dir.js" ;
6165import {
6266 executeSqliteQuerySync ,
@@ -110,6 +114,10 @@ export type LegacyStateDetection = {
110114 sourcePath : string ;
111115 hasLegacy : boolean ;
112116 } ;
117+ stateSchema : {
118+ hasLegacy : boolean ;
119+ preview : string [ ] ;
120+ } ;
113121 taskStateSidecars : {
114122 taskRunsPath : string ;
115123 flowRunsPath : string ;
@@ -2627,6 +2635,9 @@ export async function detectLegacyStateMigrations(params: {
26272635 const hasPluginStateSidecar = fileExists ( pluginStateSidecarPath ) ;
26282636 const pluginInstallIndexPath = resolveLegacyInstalledPluginIndexStorePath ( { stateDir } ) ;
26292637 const hasPluginInstallIndex = fileExists ( pluginInstallIndexPath ) ;
2638+ const stateSchemaMigrations = detectOpenClawStateDatabaseSchemaMigrations ( {
2639+ env : { ...env , OPENCLAW_STATE_DIR : stateDir } ,
2640+ } ) ;
26302641 const taskRunsSidecarPath = resolveLegacyTaskRunsSidecarPath ( stateDir ) ;
26312642 const flowRunsSidecarPath = resolveLegacyFlowRunsSidecarPath ( stateDir ) ;
26322643 const hasTaskStateSidecars = fileExists ( taskRunsSidecarPath ) || fileExists ( flowRunsSidecarPath ) ;
@@ -2645,12 +2656,15 @@ export async function detectLegacyStateMigrations(params: {
26452656 stateDir,
26462657 oauthDir,
26472658 } ) ;
2648- const pluginPlans = await collectPluginDoctorStateMigrationPlans ( {
2649- cfg : params . cfg ,
2650- env,
2651- stateDir,
2652- oauthDir,
2653- } ) ;
2659+ const pluginPlans =
2660+ stateSchemaMigrations . length > 0
2661+ ? [ ]
2662+ : await collectPluginDoctorStateMigrationPlans ( {
2663+ cfg : params . cfg ,
2664+ env,
2665+ stateDir,
2666+ oauthDir,
2667+ } ) ;
26542668
26552669 const preview : string [ ] = [ ] ;
26562670 if ( hasLegacySessions ) {
@@ -2668,6 +2682,12 @@ export async function detectLegacyStateMigrations(params: {
26682682 if ( hasPluginInstallIndex ) {
26692683 preview . push ( `- Plugin install index: ${ pluginInstallIndexPath } → shared SQLite state` ) ;
26702684 }
2685+ if ( stateSchemaMigrations . length > 0 ) {
2686+ preview . push ( "- Shared SQLite schema: agent database registry primary key → agent_id,path" ) ;
2687+ preview . push (
2688+ "- Rerun doctor after shared SQLite schema repair to detect plugin state migrations" ,
2689+ ) ;
2690+ }
26712691 if ( fileExists ( taskRunsSidecarPath ) ) {
26722692 preview . push ( `- Task registry sidecar: ${ taskRunsSidecarPath } → shared SQLite state` ) ;
26732693 }
@@ -2719,6 +2739,10 @@ export async function detectLegacyStateMigrations(params: {
27192739 sourcePath : pluginInstallIndexPath ,
27202740 hasLegacy : hasPluginInstallIndex ,
27212741 } ,
2742+ stateSchema : {
2743+ hasLegacy : stateSchemaMigrations . length > 0 ,
2744+ preview : stateSchemaMigrations . map ( ( migration ) => migration . path ) ,
2745+ } ,
27222746 taskStateSidecars : {
27232747 taskRunsPath : taskRunsSidecarPath ,
27242748 flowRunsPath : flowRunsSidecarPath ,
@@ -2969,6 +2993,15 @@ async function runPluginDoctorStateMigrationPlans(params: {
29692993 return { changes, warnings } ;
29702994}
29712995
2996+ function migrateLegacyStateSchema ( detected : LegacyStateDetection ) : {
2997+ changes : string [ ] ;
2998+ warnings : string [ ] ;
2999+ } {
3000+ return repairOpenClawStateDatabaseSchema ( {
3001+ env : { ...process . env , OPENCLAW_STATE_DIR : detected . stateDir } ,
3002+ } ) ;
3003+ }
3004+
29723005export async function runLegacyStateMigrations ( params : {
29733006 detected : LegacyStateDetection ;
29743007 config ?: OpenClawConfig ;
@@ -2977,6 +3010,10 @@ export async function runLegacyStateMigrations(params: {
29773010} ) : Promise < { changes : string [ ] ; warnings : string [ ] } > {
29783011 const now = params . now ?? ( ( ) => Date . now ( ) ) ;
29793012 const detected = params . detected ;
3013+ const stateSchema = migrateLegacyStateSchema ( detected ) ;
3014+ if ( detected . stateSchema . hasLegacy && stateSchema . warnings . length > 0 ) {
3015+ return stateSchema ;
3016+ }
29803017 const pluginStateSidecar = await migrateLegacyPluginStateSidecar ( {
29813018 stateDir : detected . stateDir ,
29823019 } ) ;
@@ -2992,10 +3029,12 @@ export async function runLegacyStateMigrations(params: {
29923029 const preSessionChannelPlans = await runLegacyMigrationPlans (
29933030 detected . channelPlans . plans . filter ( ( plan ) => plan . kind === "plugin-state-import" ) ,
29943031 ) ;
2995- const pluginPlans = await runPluginDoctorStateMigrationPlans ( {
2996- detected,
2997- config : params . config ?? ( { } as OpenClawConfig ) ,
2998- } ) ;
3032+ const pluginPlans = detected . stateSchema . hasLegacy
3033+ ? { changes : [ ] , warnings : [ ] }
3034+ : await runPluginDoctorStateMigrationPlans ( {
3035+ detected,
3036+ config : params . config ?? ( { } as OpenClawConfig ) ,
3037+ } ) ;
29993038 const sessions = await migrateLegacySessions ( detected , now , {
30003039 recoverCorruptTargetStore : params . recoverCorruptTargetStore ,
30013040 } ) ;
@@ -3010,6 +3049,7 @@ export async function runLegacyStateMigrations(params: {
30103049 ) ;
30113050 return {
30123051 changes : [
3052+ ...stateSchema . changes ,
30133053 ...pluginStateSidecar . changes ,
30143054 ...pluginInstallIndex . changes ,
30153055 ...taskStateSidecars . changes ,
@@ -3022,6 +3062,7 @@ export async function runLegacyStateMigrations(params: {
30223062 ...channelPlans . changes ,
30233063 ] ,
30243064 warnings : [
3065+ ...stateSchema . warnings ,
30253066 ...pluginStateSidecar . warnings ,
30263067 ...pluginInstallIndex . warnings ,
30273068 ...taskStateSidecars . warnings ,
@@ -3306,6 +3347,10 @@ export async function autoMigrateLegacyState(params: {
33063347 homedir : params . homedir ,
33073348 log : params . log ,
33083349 } ) ;
3350+ const stateDir = resolveStateDir ( env , params . homedir ?? os . homedir ) ;
3351+ const stateSchema = repairOpenClawStateDatabaseSchema ( {
3352+ env : { ...env , OPENCLAW_STATE_DIR : stateDir } ,
3353+ } ) ;
33093354
33103355 // Canonicalize orphaned session keys regardless of whether legacy migration
33113356 // is needed — the orphan-key bug (#29683) affects all installs with
@@ -3362,6 +3407,7 @@ export async function autoMigrateLegacyState(params: {
33623407 } ) ;
33633408 const changes = [
33643409 ...stateDirResult . changes ,
3410+ ...stateSchema . changes ,
33653411 ...orphanKeys . changes ,
33663412 ...acpSessionMetadata . changes ,
33673413 ...pluginStateSidecar . changes ,
@@ -3373,6 +3419,7 @@ export async function autoMigrateLegacyState(params: {
33733419 ] ;
33743420 const warnings = [
33753421 ...stateDirResult . warnings ,
3422+ ...stateSchema . warnings ,
33763423 ...orphanKeys . warnings ,
33773424 ...acpSessionMetadata . warnings ,
33783425 ...pluginStateSidecar . warnings ,
@@ -3386,6 +3433,7 @@ export async function autoMigrateLegacyState(params: {
33863433 return {
33873434 migrated :
33883435 stateDirResult . migrated ||
3436+ stateSchema . changes . length > 0 ||
33893437 orphanKeys . changes . length > 0 ||
33903438 acpSessionMetadata . changes . length > 0 ||
33913439 pluginStateSidecar . changes . length > 0 ||
@@ -3406,23 +3454,27 @@ export async function autoMigrateLegacyState(params: {
34063454 ! detected . pluginPlans ?. hasLegacy &&
34073455 ! detected . pluginStateSidecar . hasLegacy &&
34083456 ! detected . pluginInstallIndex . hasLegacy &&
3457+ ! detected . stateSchema . hasLegacy &&
34093458 ! detected . taskStateSidecars . hasLegacy &&
34103459 ! detected . deliveryQueues . hasLegacy
34113460 ) {
34123461 const changes = [
34133462 ...stateDirResult . changes ,
3463+ ...stateSchema . changes ,
34143464 ...orphanKeys . changes ,
34153465 ...acpSessionMetadata . changes ,
34163466 ] ;
34173467 const warnings = [
34183468 ...stateDirResult . warnings ,
3469+ ...stateSchema . warnings ,
34193470 ...orphanKeys . warnings ,
34203471 ...acpSessionMetadata . warnings ,
34213472 ] ;
34223473 logMigrationResults ( changes , warnings ) ;
34233474 return {
34243475 migrated :
34253476 stateDirResult . migrated ||
3477+ stateSchema . changes . length > 0 ||
34263478 orphanKeys . changes . length > 0 ||
34273479 acpSessionMetadata . changes . length > 0 ,
34283480 skipped : false ,
@@ -3465,6 +3517,7 @@ export async function autoMigrateLegacyState(params: {
34653517 ) ;
34663518 const changes = [
34673519 ...stateDirResult . changes ,
3520+ ...stateSchema . changes ,
34683521 ...orphanKeys . changes ,
34693522 ...acpSessionMetadata . changes ,
34703523 ...pluginStateSidecar . changes ,
@@ -3480,6 +3533,7 @@ export async function autoMigrateLegacyState(params: {
34803533 ] ;
34813534 const warnings = [
34823535 ...stateDirResult . warnings ,
3536+ ...stateSchema . warnings ,
34833537 ...orphanKeys . warnings ,
34843538 ...acpSessionMetadata . warnings ,
34853539 ...pluginStateSidecar . warnings ,
0 commit comments