@@ -15,7 +15,15 @@ import {
1515 resolveHunkMcpConfig ,
1616 type ResolvedHunkMcpConfig ,
1717} from "./config" ;
18- import { ensureHunkDaemonAvailable } from "./daemonLauncher" ;
18+ import {
19+ ensureHunkDaemonAvailable ,
20+ readHunkDaemonHealth ,
21+ waitForHunkDaemonShutdown ,
22+ } from "./daemonLauncher" ;
23+ import {
24+ readHunkSessionDaemonCapabilities ,
25+ reportHunkDaemonUpgradeRestart ,
26+ } from "../session/capabilities" ;
1927
2028const DAEMON_STARTUP_TIMEOUT_MS = 3_000 ;
2129const RECONNECT_DELAY_MS = 3_000 ;
@@ -119,9 +127,62 @@ export class HunkHostClient {
119127 config,
120128 timeoutMs : DAEMON_STARTUP_TIMEOUT_MS ,
121129 } ) ;
130+
131+ const capabilities = await readHunkSessionDaemonCapabilities ( config ) ;
132+ if ( ! capabilities ) {
133+ await this . restartIncompatibleDaemon ( config ) ;
134+ await ensureHunkDaemonAvailable ( {
135+ config,
136+ timeoutMs : DAEMON_STARTUP_TIMEOUT_MS ,
137+ } ) ;
138+
139+ if ( ! ( await readHunkSessionDaemonCapabilities ( config ) ) ) {
140+ throw new Error (
141+ "The running Hunk session daemon is incompatible with this Hunk build. " +
142+ "Restart Hunk so it can launch a fresh daemon from the current source tree." ,
143+ ) ;
144+ }
145+ }
146+
122147 this . lastConnectionWarning = null ;
123148 }
124149
150+ private async restartIncompatibleDaemon ( config : ResolvedHunkMcpConfig ) {
151+ reportHunkDaemonUpgradeRestart ( ) ;
152+ const health = await readHunkDaemonHealth ( config ) ;
153+ const pid = health ?. pid ;
154+ if ( pid === process . pid ) {
155+ throw new Error (
156+ "The running Hunk session daemon is incompatible with this Hunk build. " +
157+ "Restart Hunk so it can launch a fresh daemon from the current source tree." ,
158+ ) ;
159+ }
160+
161+ // If the stale daemon already disappeared on its own, let the normal startup path launch a
162+ // fresh one instead of turning that race into a manual restart error.
163+ if ( ! pid ) {
164+ return ;
165+ }
166+
167+ try {
168+ process . kill ( pid , "SIGTERM" ) ;
169+ } catch ( error ) {
170+ if ( ! ( error instanceof Error ) || ! ( "code" in error ) || error . code !== "ESRCH" ) {
171+ throw error ;
172+ }
173+ }
174+
175+ const shutDown = await waitForHunkDaemonShutdown ( {
176+ config,
177+ timeoutMs : DAEMON_STARTUP_TIMEOUT_MS ,
178+ } ) ;
179+ if ( ! shutDown ) {
180+ throw new Error (
181+ "Stopped waiting for the old Hunk session daemon to exit after it was found incompatible." ,
182+ ) ;
183+ }
184+ }
185+
125186 setBridge ( bridge : HunkAppBridge | null ) {
126187 this . bridge = bridge ;
127188 void this . flushQueuedMessages ( ) ;
0 commit comments