diff --git a/apps/web/README.md b/apps/web/README.md
index 4b4bc8575e..614b383573 100644
--- a/apps/web/README.md
+++ b/apps/web/README.md
@@ -85,7 +85,7 @@ All are Vite build-time vars (`import.meta.env.*`), baked in at build time.
| Var | Required | Purpose |
| --- | --- | --- |
-| `VITE_POSTHOG_API_KEY` | Recommended | Real `phc_…` project key. Enables posthog-js analytics, error/rejection capture, session recording, and real feature flags. The guard in `main.tsx` requires the `phc_` prefix; without it posthog stays uninitialized and the tracker/analytics service no-op, leaving only the host-forced `SYNC_CLOUD_TASKS_FLAG` on (every other flag reads `false`). |
+| `VITE_POSTHOG_API_KEY` | Recommended | Real project key. Enables posthog-js analytics, error/rejection capture, session recording, and real feature flags. Without it posthog stays uninitialized and the tracker/analytics service no-op, leaving only the host-forced `SYNC_CLOUD_TASKS_FLAG` on (every other flag reads `false`). |
| `VITE_POSTHOG_API_HOST` | No | posthog-js ingestion host. Default `https://internal-c.posthog.com`. |
| `VITE_POSTHOG_UI_HOST` | No | posthog-js UI host. Default `https://us.i.posthog.com`. |
| `VITE_POSTHOG_ACCESS_TOKEN_OVERRIDE` | No | Dev/test only: a static access token that bypasses the OAuth flow (`AUTH_TOKEN_OVERRIDE`). Leave unset in production. |
diff --git a/apps/web/index.html b/apps/web/index.html
index 0de4e2aa44..c9e5a865f2 100644
--- a/apps/web/index.html
+++ b/apps/web/index.html
@@ -3,7 +3,28 @@
- PostHog (Web)
+
+ PostHog Desktop: Web Edition
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/public/favicon.png b/apps/web/public/favicon.png
new file mode 100644
index 0000000000..41f42876eb
Binary files /dev/null and b/apps/web/public/favicon.png differ
diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx
index e73e04b601..4e0a220f7e 100644
--- a/apps/web/src/main.tsx
+++ b/apps/web/src/main.tsx
@@ -37,10 +37,8 @@ if (window.location.pathname === OAUTH_CALLBACK_PATH) {
// and analytics service then no-op gracefully rather than firing at a bogus
// endpoint. In production web builds this also enables posthog-js's automatic
// unhandled-error/rejection capture and session recording.
- const posthogKey = import.meta.env.VITE_POSTHOG_API_KEY;
- if (typeof posthogKey === "string" && posthogKey.startsWith("phc_")) {
- initializePostHog();
- }
+ const posthogKey = import.meta.env.VITE_POSTHOG_API_KEY?.trim();
+ if (posthogKey) initializePostHog();
// Restore any persisted session (desktop does this in main-process
// bootstrap); flips authState.bootstrapComplete when done.
diff --git a/apps/web/src/web-container.ts b/apps/web/src/web-container.ts
index ebeacc79b1..f1bedd3a77 100644
--- a/apps/web/src/web-container.ts
+++ b/apps/web/src/web-container.ts
@@ -86,7 +86,9 @@ import {
type GithubConnectClient as OnboardingGithubConnectContract,
} from "@posthog/core/onboarding/identifiers";
import { onboardingModule } from "@posthog/core/onboarding/onboarding.module";
+import { PI_RUNNER } from "@posthog/core/pi-runtime/identifiers";
import { piRuntimeModule } from "@posthog/core/pi-runtime/pi-runtime.module";
+import type { PiRunner } from "@posthog/core/pi-runtime/piRunner";
import {
PI_SESSION_CLIENT,
type PiSessionClient,
@@ -295,6 +297,7 @@ import {
webNotifications,
} from "./web-notifications";
import { WebOAuthFlowService } from "./web-oauth-flow";
+import { webPiRunner } from "./web-pi-runner";
import { webDiffWorkerFactory, webReviewHost } from "./web-review-host";
import {
webBundleLocalSkill,
@@ -313,6 +316,7 @@ import { hostTrpcClient } from "./web-trpc";
interface WebBindings {
[HOST_TRPC_CLIENT]: HostTrpcClient;
+ [PI_RUNNER]: PiRunner;
[PI_SESSION_CLIENT]: PiSessionClient;
[ROOT_LOGGER]: RootLogger;
[HOST_LOGGER]: HostLogger;
@@ -395,6 +399,7 @@ export const container = new TypedContainer({
// here (web-trpc.ts) instead of over Electron IPC.
container.bind(HOST_TRPC_CLIENT).toConstantValue(hostTrpcClient);
container.bind(PI_SESSION_CLIENT).to(TrpcPiSessionClient);
+container.bind(PI_RUNNER).toConstantValue(webPiRunner);
container.load(piRuntimeModule);
// Logger: web uses console; electron uses electron-log. Same RootLogger shape.
diff --git a/apps/web/src/web-pi-runner.ts b/apps/web/src/web-pi-runner.ts
new file mode 100644
index 0000000000..9999159a27
--- /dev/null
+++ b/apps/web/src/web-pi-runner.ts
@@ -0,0 +1,15 @@
+import type { PiRunner } from "@posthog/core/pi-runtime/piRunner";
+
+// There is no local pi runtime in a browser. TASK_SERVICE injects PI_RUNNER and
+// resolves eagerly at the task views, but the runner only fires for local
+// `runtime === "pi"` tasks — web is cloud-only (no local workspaces), so cloud
+// tasks never reach these methods. This stub exists so the eager DI resolution
+// succeeds; its methods reject since no local pi session can exist on web.
+const notSupported = () =>
+ Promise.reject(new Error("Local pi tasks are not available on the web"));
+
+export const webPiRunner: PiRunner = {
+ create: notSupported,
+ resume: notSupported,
+ stop: notSupported,
+};
diff --git a/packages/ui/src/features/setup/useSetupDiscovery.ts b/packages/ui/src/features/setup/useSetupDiscovery.ts
index 1f270201d5..b1253f2b20 100644
--- a/packages/ui/src/features/setup/useSetupDiscovery.ts
+++ b/packages/ui/src/features/setup/useSetupDiscovery.ts
@@ -4,14 +4,17 @@ import { DISCOVERY_RUN_FLAG } from "@posthog/shared";
import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag";
import { useEffect } from "react";
import { useActiveRepoStore } from "../../shell/activeRepoStore";
+import { useHostCapabilities } from "../../shell/useHostCapabilities";
export function useSetupDiscovery() {
const selectedDirectory = useActiveRepoStore((s) => s.path);
const service = useService(SetupRunService);
const discoveryEnabled = useFeatureFlag(DISCOVERY_RUN_FLAG);
+ const { localWorkspaces } = useHostCapabilities();
useEffect(() => {
+ if (!localWorkspaces) return;
if (!selectedDirectory) return;
service.maybeStart(selectedDirectory, discoveryEnabled);
- }, [selectedDirectory, service, discoveryEnabled]);
+ }, [localWorkspaces, selectedDirectory, service, discoveryEnabled]);
}