diff --git a/src/gateway/server-restart-sentinel.ts b/src/gateway/server-restart-sentinel.ts index 28719290e..a093f6467 100644 --- a/src/gateway/server-restart-sentinel.ts +++ b/src/gateway/server-restart-sentinel.ts @@ -14,7 +14,24 @@ import { defaultRuntime } from "../runtime.js"; import { deliveryContextFromSession, mergeDeliveryContext } from "../utils/delivery-context.js"; import { loadSessionEntry } from "./session-utils.js"; -export async function scheduleRestartSentinelWake(params: { deps: CliDeps }) { +export type AddChatRunFn = ( + runId: string, + entry: { sessionKey: string; clientRunId: string }, +) => void; + +export type BroadcastFn = ( + event: string, + payload: unknown, + opts?: { dropIfSlow?: boolean }, +) => void; + +export interface RestartSentinelParams { + deps: CliDeps; + addChatRun?: AddChatRunFn; + broadcast?: BroadcastFn; +} + +export async function scheduleRestartSentinelWake(params: RestartSentinelParams) { const sentinel = await consumeRestartSentinel(); if (!sentinel) return; const payload = sentinel.payload; @@ -61,7 +78,27 @@ export async function scheduleRestartSentinelWake(params: { deps: CliDeps }) { const channel = channelRaw ? normalizeChannelId(channelRaw) : null; const to = origin?.to; if (!channel || !to) { + // No outbound channel (e.g., webchat sessions use WebSocket, not channel delivery). + // For webchat: register the run with addChatRun so responses stream to the client. enqueueSystemEvent(message, { sessionKey }); + if (params.addChatRun && params.broadcast) { + const runId = `restart-${Date.now()}`; + params.addChatRun(runId, { sessionKey, clientRunId: runId }); + try { + await agentCommand( + { + message: `System: ${summary}`, + sessionKey, + runId, + deliver: false, + }, + defaultRuntime, + params.deps, + ); + } catch { + // Agent turn failed; system event already queued as fallback context + } + } return; } diff --git a/src/gateway/server-startup.ts b/src/gateway/server-startup.ts index 8cd30e338..11f4789a2 100644 --- a/src/gateway/server-startup.ts +++ b/src/gateway/server-startup.ts @@ -19,6 +19,8 @@ import type { loadOpenClawPlugins } from "../plugins/loader.js"; import { type PluginServicesHandle, startPluginServices } from "../plugins/services.js"; import { startBrowserControlServerIfEnabled } from "./server-browser.js"; import { + type AddChatRunFn, + type BroadcastFn, scheduleRestartSentinelWake, shouldWakeFromRestartSentinel, } from "./server-restart-sentinel.js"; @@ -37,6 +39,8 @@ export async function startGatewaySidecars(params: { }; logChannels: { info: (msg: string) => void; error: (msg: string) => void }; logBrowser: { error: (msg: string) => void }; + addChatRun?: AddChatRunFn; + broadcast?: BroadcastFn; }) { // Start OpenClaw browser control server (unless disabled via config). let browserControl: Awaited> = null; @@ -152,7 +156,11 @@ export async function startGatewaySidecars(params: { if (shouldWakeFromRestartSentinel()) { setTimeout(() => { - void scheduleRestartSentinelWake({ deps: params.deps }); + void scheduleRestartSentinelWake({ + deps: params.deps, + addChatRun: params.addChatRun, + broadcast: params.broadcast, + }); }, 750); } diff --git a/src/gateway/server.impl.ts b/src/gateway/server.impl.ts index efa91be76..711304b47 100644 --- a/src/gateway/server.impl.ts +++ b/src/gateway/server.impl.ts @@ -504,6 +504,8 @@ export async function startGatewayServer( logHooks, logChannels, logBrowser, + addChatRun, + broadcast, })); const { applyHotReload, requestGatewayRestart } = createGatewayReloadHandlers({