diff --git a/electron/api/routes/channels.ts b/electron/api/routes/channels.ts index ff856db1a..610746b6d 100644 --- a/electron/api/routes/channels.ts +++ b/electron/api/routes/channels.ts @@ -41,9 +41,10 @@ function scheduleGatewayChannelRestart(ctx: HostApiContext, reason: string): voi void reason; } -// Keep reload-first for feishu to avoid restart storms when channel auth/network is flaky. -// GatewayManager.reload() already falls back to restart when reload is unhealthy. -const FORCE_RESTART_CHANNELS = new Set(['dingtalk', 'wecom', 'whatsapp']); +// Plugin-based channels require a full Gateway process restart to properly +// initialize / tear-down plugin connections. SIGUSR1 in-process reload is +// not sufficient for channel plugins (see restartGatewayForAgentDeletion). +const FORCE_RESTART_CHANNELS = new Set(['dingtalk', 'wecom', 'whatsapp', 'feishu', 'qqbot']); function scheduleGatewayChannelSaveRefresh( ctx: HostApiContext, diff --git a/electron/gateway/manager.ts b/electron/gateway/manager.ts index 396f39c15..bca0c9e8f 100644 --- a/electron/gateway/manager.ts +++ b/electron/gateway/manager.ts @@ -242,8 +242,14 @@ export class GatewayManager extends EventEmitter { await this.connect(port, externalToken); }, onConnectedToExistingGateway: () => { - this.ownsProcess = false; - this.setStatus({ pid: undefined }); + // If the existing gateway is actually our own spawned UtilityProcess + // (e.g. after a self-restart code=1012), keep ownership so that + // stop() can still terminate the process during a restart() cycle. + const isOwnProcess = this.process?.pid != null && this.ownsProcess; + if (!isOwnProcess) { + this.ownsProcess = false; + this.setStatus({ pid: undefined }); + } this.startHealthCheck(); }, waitForPortFree: async (port) => { diff --git a/electron/main/ipc-handlers.ts b/electron/main/ipc-handlers.ts index 6247133b4..3b5e26529 100644 --- a/electron/main/ipc-handlers.ts +++ b/electron/main/ipc-handlers.ts @@ -1361,9 +1361,10 @@ function registerGatewayHandlers( * For checking package status and channel configuration */ function registerOpenClawHandlers(gatewayManager: GatewayManager): void { - // Keep reload-first for feishu to avoid restart storms when channel auth/network is flaky. - // GatewayManager.reload() already falls back to restart when reload is unhealthy. - const forceRestartChannels = new Set(['dingtalk', 'wecom', 'whatsapp']); + // Plugin-based channels require a full Gateway process restart to properly + // initialize / tear-down plugin connections. SIGUSR1 in-process reload is + // not sufficient for channel plugins (see restartGatewayForAgentDeletion). + const forceRestartChannels = new Set(['dingtalk', 'wecom', 'whatsapp', 'feishu', 'qqbot']); const scheduleGatewayChannelRestart = (reason: string): void => { if (gatewayManager.getStatus().state !== 'stopped') {