From 744ca7a2bc744308fde3e51dc2ef2fc888b14843 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 4 Apr 2026 21:12:34 +0700 Subject: [PATCH] fix: send appVersion in probe client hello and update it on session resume buildClientConfig in test-daemon-connection.ts never set appVersion, so probe clients (which become the live client) sent hello without it. The daemon's version gate then hid Pi/Copilot from all these sessions. Also update appVersion on the Session when a client reconnects with a newer version, so stale sessions don't stay gated forever. --- packages/app/src/utils/test-daemon-connection.ts | 2 ++ packages/server/src/server/session.ts | 8 +++++++- packages/server/src/server/websocket-server.ts | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/app/src/utils/test-daemon-connection.ts b/packages/app/src/utils/test-daemon-connection.ts index ef6ed7623..997be2b94 100644 --- a/packages/app/src/utils/test-daemon-connection.ts +++ b/packages/app/src/utils/test-daemon-connection.ts @@ -2,6 +2,7 @@ import { DaemonClient } from "@server/client/daemon-client"; import type { DaemonClientConfig } from "@server/client/daemon-client"; import type { HostConnection } from "@/types/host-connection"; import { getOrCreateClientId } from "./client-id"; +import { resolveAppVersion } from "./app-version"; import { buildDaemonWebSocketUrl, buildRelayWebSocketUrl } from "./daemon-endpoints"; import { buildLocalDaemonTransportUrl, @@ -53,6 +54,7 @@ export async function buildClientConfig( const base = { clientId, clientType: "mobile" as const, + appVersion: resolveAppVersion() ?? undefined, suppressSendErrors: true, reconnect: { enabled: false }, ...(connection.type === "directSocket" || connection.type === "directPipe" diff --git a/packages/server/src/server/session.ts b/packages/server/src/server/session.ts index eebfbecd1..37fa4ee4a 100644 --- a/packages/server/src/server/session.ts +++ b/packages/server/src/server/session.ts @@ -529,7 +529,7 @@ function toAgentPersistenceHandle( */ export class Session { private readonly clientId: string; - private readonly appVersion: string | null; + private appVersion: string | null; private readonly sessionId: string; private readonly onMessage: (msg: SessionOutboundMessage) => void; private readonly onBinaryMessage: ((frame: Uint8Array) => void) | null; @@ -714,6 +714,12 @@ export class Session { this.sessionLogger.trace("Session created"); } + updateAppVersion(appVersion: string | null): void { + if (appVersion && appVersion !== this.appVersion) { + this.appVersion = appVersion; + } + } + /** * Get the client's current activity state */ diff --git a/packages/server/src/server/websocket-server.ts b/packages/server/src/server/websocket-server.ts index 0b9fdcf66..7b024e446 100644 --- a/packages/server/src/server/websocket-server.ts +++ b/packages/server/src/server/websocket-server.ts @@ -745,6 +745,11 @@ export class VoiceAssistantWebSocketServer { clearTimeout(existing.externalDisconnectCleanupTimeout); existing.externalDisconnectCleanupTimeout = null; } + const newAppVersion = message.appVersion ?? null; + if (newAppVersion && newAppVersion !== existing.appVersion) { + existing.appVersion = newAppVersion; + existing.session.updateAppVersion(newAppVersion); + } existing.sockets.add(ws); this.sessions.set(ws, existing); this.sendToClient(ws, this.createServerInfoMessage());