diff --git a/src/cli/App.tsx b/src/cli/App.tsx index 9f157c3..d3218fe 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -11888,6 +11888,7 @@ ${SYSTEM_REMINDER_CLOSE} model: currentModelId ?? undefined, // Account info billing_tier: billingTier ?? undefined, + server_version: telemetry.getServerVersion() ?? undefined, // Recent chunk log for diagnostics recent_chunks: chunkLog.getEntries(), // Debug log tail for diagnostics diff --git a/src/telemetry/index.ts b/src/telemetry/index.ts index 4d2863b..e49562d 100644 --- a/src/telemetry/index.ts +++ b/src/telemetry/index.ts @@ -1,3 +1,4 @@ +import { getServerUrl } from "../agent/client"; import { getLettaCodeHeaders } from "../agent/http-headers"; import { settingsManager } from "../settings-manager"; import { debugLogFile } from "../utils/debug"; @@ -72,6 +73,7 @@ class TelemetryManager { private toolCallCount = 0; private sessionEndTracked = false; private flushInterval: NodeJS.Timeout | null = null; + private serverVersion: string | null = null; private readonly FLUSH_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes private readonly MAX_BATCH_SIZE = 100; private sessionStatsGetter?: () => { @@ -125,6 +127,9 @@ class TelemetryManager { this.trackSessionStart(); + // Fetch server version for diagnostics (best-effort, non-blocking) + this.fetchServerVersion().catch(() => {}); + // Set up periodic flushing this.flushInterval = setInterval(() => { this.flush().catch((err) => { @@ -209,6 +214,34 @@ class TelemetryManager { this.currentAgentId = agentId; } + /** + * Fetch and cache server version from /v1/health (fire-and-forget, best-effort) + */ + async fetchServerVersion(): Promise { + try { + const baseURL = getServerUrl(); + const settings = await settingsManager.getSettingsWithSecureTokens(); + const apiKey = + process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY || ""; + const res = await fetch(`${baseURL}/v1/health`, { + headers: { Authorization: `Bearer ${apiKey}` }, + signal: AbortSignal.timeout(3000), + }); + if (res.ok) { + const data = (await res.json()) as { version?: string }; + if (data.version) { + this.serverVersion = data.version; + } + } + } catch { + // Best-effort — don't let this affect startup + } + } + + getServerVersion(): string | null { + return this.serverVersion; + } + /** * Set a getter function for session stats (called from App.tsx) * This allows safety net handlers to access stats even if not explicitly passed @@ -438,6 +471,7 @@ class TelemetryManager { }, body: JSON.stringify({ service: "letta-code", + server_version: this.serverVersion || undefined, events: eventsToSend, }), },