From 3f8d88b6fefedf2d7769080ae1de8e401d231f94 Mon Sep 17 00:00:00 2001 From: clopen-set <33433326+clopen-set@users.noreply.github.com> Date: Tue, 14 Apr 2026 16:43:37 -0400 Subject: [PATCH] =?UTF-8?q?fix(environments):=20forward=20VELLUM=5FENVIRON?= =?UTF-8?q?MENT=20across=20desktop=E2=86=92CLI=E2=86=92daemon=20handoffs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two call sites were stripping VELLUM_ENVIRONMENT from spawn whitelists, breaking environment isolation for the main desktop launch path: 1. macOS `VellumCli.makeBaseEnvironment()` — `forwardedEnvKeys` did not include `VELLUM_ENVIRONMENT`, so every bundled-CLI command launched from the app (hatch, wake, sleep, retire, …) ran as production even when the app itself was built for a non-production environment. The app's Info.plist sets `VELLUM_ENVIRONMENT` at build time (`build.sh:1054`), so forwarding it is sufficient. 2. `cli/src/lib/local.ts` compiled-daemon spawn — the `daemonEnv` whitelist used when `bun run` is unavailable (packaged desktop builds) also omitted `VELLUM_ENVIRONMENT`. Even when the CLI process itself had the variable set, the spawned daemon fell back to production path/env behavior, so assistant-side env-scoped state (device ID, XDG-backed tokens and config reads) bled into prod. Note: the source/watch daemon spawn path in `local.ts:281` is unaffected — it uses `{...process.env}` and inherits everything. Co-Authored-By: Claude Opus 4.6 (1M context) --- cli/src/lib/local.ts | 6 +++++- clients/macos/vellum-assistant/App/VellumCli.swift | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cli/src/lib/local.ts b/cli/src/lib/local.ts index 875e05a0f51..996192e8d64 100644 --- a/cli/src/lib/local.ts +++ b/cli/src/lib/local.ts @@ -855,11 +855,15 @@ export async function startLocalDaemon( HOME: process.env.HOME || home, PATH: [...extraDirs, basePath].filter(Boolean).join(":"), }; - // Forward optional config env vars the daemon may need + // Forward optional config env vars the daemon may need. + // `VELLUM_ENVIRONMENT` must be forwarded so the daemon resolves + // env-scoped paths (device ID, platform/guardian tokens, XDG + // config dir) to the same location as the CLI that spawned it. for (const key of [ "ANTHROPIC_API_KEY", "APP_VERSION", "BASE_DATA_DIR", + "VELLUM_ENVIRONMENT", "VELLUM_PLATFORM_URL", "QDRANT_HTTP_PORT", "QDRANT_URL", diff --git a/clients/macos/vellum-assistant/App/VellumCli.swift b/clients/macos/vellum-assistant/App/VellumCli.swift index 318a6415d58..20c90186e87 100644 --- a/clients/macos/vellum-assistant/App/VellumCli.swift +++ b/clients/macos/vellum-assistant/App/VellumCli.swift @@ -109,7 +109,12 @@ final class VellumCli: AssistantManagementClient { /// Environment variable keys forwarded from the host process to CLI /// child processes. Centralised so every call site stays in sync. + /// `VELLUM_ENVIRONMENT` must be forwarded so the bundled CLI resolves + /// env-scoped paths (lockfile, device ID, platform/guardian tokens, + /// workspace config) to the same location the desktop app uses. The + /// app's Info.plist sets this at build time (see `build.sh:1054`). nonisolated private static let forwardedEnvKeys: [String] = [ + "VELLUM_ENVIRONMENT", "VELLUM_PLATFORM_URL", "VELLUM_WORKSPACE_DIR", "ASSISTANT_GIT_USER_NAME", "ASSISTANT_GIT_USER_EMAIL",