@@ -18,8 +18,7 @@ import {
1818 resolveMainSessionKey ,
1919} from "../config/sessions/main-session.js" ;
2020import { resolveStorePath } from "../config/sessions/paths.js" ;
21- import { loadSessionStore , updateSessionStore } from "../config/sessions/store.js" ;
22- import type { SessionEntry } from "../config/sessions/types.js" ;
21+ import { preserveTemporarySessionMapping } from "../config/sessions/session-accessor.js" ;
2322import type { OpenClawConfig } from "../config/types.openclaw.js" ;
2423import { formatErrorMessage } from "../infra/errors.js" ;
2524import { createSubsystemLogger } from "../logging/subsystem.js" ;
@@ -33,14 +32,6 @@ function generateBootSessionId(): string {
3332 return `boot-${ ts } -${ suffix } ` ;
3433}
3534
36- type SessionMappingSnapshot = {
37- storePath : string ;
38- sessionKey : string ;
39- canRestore : boolean ;
40- hadEntry : boolean ;
41- entry ?: SessionEntry ;
42- } ;
43-
4435const log = createSubsystemLogger ( "gateway/boot" ) ;
4536const BOOT_FILENAME = "BOOT.md" ;
4637
@@ -101,68 +92,6 @@ async function loadBootFile(
10192 }
10293}
10394
104- function snapshotSessionMapping ( params : {
105- cfg : OpenClawConfig ;
106- sessionKey : string ;
107- } ) : SessionMappingSnapshot {
108- const agentId = resolveAgentIdFromSessionKey ( params . sessionKey ) ;
109- const storePath = resolveStorePath ( params . cfg . session ?. store , { agentId } ) ;
110- try {
111- const store = loadSessionStore ( storePath , { skipCache : true } ) ;
112- const entry = store [ params . sessionKey ] ;
113- if ( ! entry ) {
114- return {
115- storePath,
116- sessionKey : params . sessionKey ,
117- canRestore : true ,
118- hadEntry : false ,
119- } ;
120- }
121- return {
122- storePath,
123- sessionKey : params . sessionKey ,
124- canRestore : true ,
125- hadEntry : true ,
126- entry : structuredClone ( entry ) ,
127- } ;
128- } catch ( err ) {
129- log . debug ( "boot: could not snapshot session mapping" , {
130- sessionKey : params . sessionKey ,
131- error : String ( err ) ,
132- } ) ;
133- return {
134- storePath,
135- sessionKey : params . sessionKey ,
136- canRestore : false ,
137- hadEntry : false ,
138- } ;
139- }
140- }
141-
142- async function restoreSessionMapping (
143- snapshot : SessionMappingSnapshot ,
144- ) : Promise < string | undefined > {
145- if ( ! snapshot . canRestore ) {
146- return undefined ;
147- }
148- try {
149- await updateSessionStore (
150- snapshot . storePath ,
151- ( store ) => {
152- if ( snapshot . hadEntry && snapshot . entry ) {
153- store [ snapshot . sessionKey ] = snapshot . entry ;
154- return ;
155- }
156- delete store [ snapshot . sessionKey ] ;
157- } ,
158- { activeSessionKey : snapshot . sessionKey } ,
159- ) ;
160- return undefined ;
161- } catch ( err ) {
162- return formatErrorMessage ( err ) ;
163- }
164- }
165-
16695export async function runBootOnce ( params : {
16796 cfg : OpenClawConfig ;
16897 deps : CliDeps ;
@@ -193,39 +122,49 @@ export async function runBootOnce(params: {
193122 const sessionKey = resolveBootSessionKey ( mainSessionKey ) ;
194123 const message = buildBootPrompt ( result . content ?? "" ) ;
195124 const sessionId = generateBootSessionId ( ) ;
196- const mappingSnapshot = snapshotSessionMapping ( {
197- cfg : params . cfg ,
198- sessionKey,
199- } ) ;
125+ const agentId = resolveAgentIdFromSessionKey ( sessionKey ) ;
126+ const storePath = resolveStorePath ( params . cfg . session ?. store , { agentId } ) ;
200127
201- // Register the boot prompt for the message-tool echo guard so the
202- // tool layer can drop fallback-model echoes that copy substantial
203- // BOOT.md content without preserving the wrapper markers above.
204- // Always cleared in finally so a failed run does not leave a stale
205- // entry that mis-fires on an unrelated subsequent run reusing the
206- // same session key. Refs #53732.
207- setBootEchoContextForSession ( sessionKey , message ) ;
208- let agentFailure : string | undefined ;
209- try {
210- await agentCommand (
211- {
212- message,
213- sessionKey,
214- sessionId,
215- deliver : false ,
216- suppressPromptPersistence : true ,
217- } ,
218- bootRuntime ,
219- params . deps ,
220- ) ;
221- } catch ( err ) {
222- agentFailure = formatErrorMessage ( err ) ;
223- log . error ( `boot: agent run failed: ${ agentFailure } ` ) ;
224- } finally {
225- clearBootEchoContextForSession ( sessionKey ) ;
128+ const mappingPreservation = await preserveTemporarySessionMapping (
129+ { storePath, sessionKey } ,
130+ async ( ) => {
131+ // Register the boot prompt for the message-tool echo guard so the
132+ // tool layer can drop fallback-model echoes that copy substantial
133+ // BOOT.md content without preserving the wrapper markers above.
134+ // Always cleared in finally so a failed run does not leave a stale
135+ // entry that mis-fires on an unrelated subsequent run reusing the
136+ // same session key. Refs #53732.
137+ setBootEchoContextForSession ( sessionKey , message ) ;
138+ try {
139+ await agentCommand (
140+ {
141+ message,
142+ sessionKey,
143+ sessionId,
144+ deliver : false ,
145+ suppressPromptPersistence : true ,
146+ } ,
147+ bootRuntime ,
148+ params . deps ,
149+ ) ;
150+ return undefined ;
151+ } catch ( err ) {
152+ const failure = formatErrorMessage ( err ) ;
153+ log . error ( `boot: agent run failed: ${ failure } ` ) ;
154+ return failure ;
155+ } finally {
156+ clearBootEchoContextForSession ( sessionKey ) ;
157+ }
158+ } ,
159+ ) ;
160+ const agentFailure = mappingPreservation . result ;
161+ if ( mappingPreservation . snapshotFailure ) {
162+ log . debug ( "boot: could not snapshot session mapping" , {
163+ sessionKey,
164+ error : mappingPreservation . snapshotFailure ,
165+ } ) ;
226166 }
227-
228- const mappingRestoreFailure = await restoreSessionMapping ( mappingSnapshot ) ;
167+ const mappingRestoreFailure = mappingPreservation . restoreFailure ;
229168 if ( mappingRestoreFailure ) {
230169 log . error ( `boot: failed to restore session mapping: ${ mappingRestoreFailure } ` ) ;
231170 }
0 commit comments