Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions apps/desktop/src/renderer/hooks/useAcpChatEnabled/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export {
type AcpChatAvailability,
useAcpChatEnabled,
useAwaitAcpChatEnabled,
useResolveAcpChatEnabled,
} from "./useAcpChatEnabled";
Original file line number Diff line number Diff line change
@@ -1,39 +1,20 @@
import { FEATURE_FLAGS } from "@superset/shared/constants";
import { useFeatureFlagEnabled, usePostHog } from "posthog-js/react";
import { useCallback } from "react";
import { useAcpChatStore } from "renderer/stores/acp-chat";
import { cloudTrpc } from "renderer/lib/cloud-trpc";

export type AcpChatAvailability = "enabled" | "disabled" | "resolving";

export function useAcpChatEnabled(): AcpChatAvailability {
const flag = useFeatureFlagEnabled(FEATURE_FLAGS.ACP_CHAT);
const chosen = useAcpChatStore((state) => state.enabled);
if (chosen === false) return "disabled";
if (flag === undefined) return "resolving";
return flag ? "enabled" : "disabled";
export function useAcpChatEnabled(): boolean {
const { data } = cloudTrpc.userSettings.get.useQuery();
return data?.experimentalChat === true;
Comment thread
harshithmullapudi marked this conversation as resolved.
Resolved
Comment thread
harshithmullapudi marked this conversation as resolved.
Resolved
}

const RESOLVE_TIMEOUT_MS = 2_000;

export function useAwaitAcpChatEnabled(): () => Promise<boolean> {
const availability = useAcpChatEnabled();
const posthog = usePostHog();

return useCallback(() => {
if (availability !== "resolving")
return Promise.resolve(availability === "enabled");
if (!posthog) return Promise.resolve(false);
return new Promise<boolean>((resolve) => {
let unsubscribe: (() => void) | undefined;
const settle = (enabled: boolean) => {
clearTimeout(timer);
unsubscribe?.();
resolve(enabled);
};
const timer = setTimeout(() => settle(false), RESOLVE_TIMEOUT_MS);
unsubscribe = posthog.onFeatureFlags(() => {
settle(posthog.isFeatureEnabled(FEATURE_FLAGS.ACP_CHAT) === true);
});
});
}, [availability, posthog]);
/** For choosing chat or terminal at launch: waits for the setting instead of reading "off" while it loads. */
export function useResolveAcpChatEnabled(): () => Promise<boolean> {
const utils = cloudTrpc.useUtils();
return useCallback(
() =>
utils.userSettings.get
.ensureData()
.then((settings) => settings.experimentalChat)
.catch(() => false),
[utils],
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const submit = mock((_args: unknown) => ({
const navigate = mock(() => Promise.resolve());
const showError = mock(() => {});

mock.module("renderer/hooks/useAcpChatEnabled", () => ({
useResolveAcpChatEnabled: () => async () => false,
}));
mock.module("renderer/hooks/useV2AgentChoices", () => ({
useV2AgentChoices: () => ({ agents }),
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { errorMessage } from "@superset/i18n/errors";
import { toast } from "@superset/ui/sonner";
import { useNavigate } from "@tanstack/react-router";
import { useRef, useState } from "react";
import { useAwaitAcpChatEnabled } from "renderer/hooks/useAcpChatEnabled";
import { useResolveAcpChatEnabled } from "renderer/hooks/useAcpChatEnabled";
import { useV2AgentChoices } from "renderer/hooks/useV2AgentChoices";
import { AGENT_STORAGE_KEY } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/types";
import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider";
Expand All @@ -25,7 +25,7 @@ export function useCreateAgentSession() {
const { machineId, activeHostUrl } = useLocalHostService();
const { agents } = useV2AgentChoices(activeHostUrl);
const { submit } = useWorkspaceCreates();
const awaitAcpChatEnabled = useAwaitAcpChatEnabled();
const resolveAcpChatEnabled = useResolveAcpChatEnabled();
const busy = useRef(false);
const [isPending, setIsPending] = useState(false);

Expand Down Expand Up @@ -66,7 +66,7 @@ export function useCreateAgentSession() {
const openAsChat =
options.preferChat === true &&
Boolean(acpHarnessForPreset(presetOf(choice))) &&
(await awaitAcpChatEnabled());
(await resolveAcpChatEnabled());
const { workspaceId, completed } = submit({
hostId: machineId,
snapshot: {
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/renderer/hooks/useSignOut/useSignOut.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useLingui } from "@lingui/react/macro";
import { toast } from "@superset/ui/sonner";
import { authClient } from "renderer/lib/auth-client";
import { cloudTrpc } from "renderer/lib/cloud-trpc";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { posthog } from "renderer/lib/posthog";

Expand All @@ -13,6 +14,7 @@ export function useSignOut() {
const { t } = useLingui();
const signOutMutation = electronTrpc.auth.signOut.useMutation();
const setAnalyticsUserId = electronTrpc.analytics.setUserId.useMutation();
const cloudUtils = cloudTrpc.useUtils();

return async () => {
posthog.reset();
Expand All @@ -24,6 +26,7 @@ export function useSignOut() {
window.setTimeout(resolve, SERVER_REVOKE_TIMEOUT_MS),
),
]);
void cloudUtils.userSettings.get.reset();
try {
await signOutMutation.mutateAsync();
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ export const PERSISTED_KEY_REGISTRY: ReadonlyArray<
],
],
["src/renderer/stores/workspace-agents-row.ts", ["workspace-agents-row"]],
["src/renderer/stores/acp-chat.ts", ["acp-chat"]],
["src/renderer/stores/chat-turn-snapshots.ts", ["chat-turn-snapshots"]],
[
"src/renderer/routes/_authenticated/settings/usage/utils/usageLastSection/usageLastSection.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ interface DeadKey {
* the writer.
*/
export const DEAD_KEYS: DeadKey[] = [
// Device-local Chat setting; moved to the account (user_settings.experimental_chat)
{ key: "acp-chat", match: "exact" },
// The task page's inline agent picker; Start work opens New Workspace instead
{ key: "lastSelectedV2TaskAgent", match: "exact" },
{ key: "getting-started-v1", match: "exact" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { toast } from "@superset/ui/sonner";
import { useWorkspaceClient, workspaceTrpc } from "@superset/workspace-client";
import { useQueryClient } from "@tanstack/react-query";
import { useCallback } from "react";
import { useAwaitAcpChatEnabled } from "renderer/hooks/useAcpChatEnabled";
import { useResolveAcpChatEnabled } from "renderer/hooks/useAcpChatEnabled";
import { useTerminalAppearance } from "renderer/hooks/useTerminalAppearance";
import {
useV2AgentConfigs,
Expand Down Expand Up @@ -63,14 +63,14 @@ export function useAgentSessionLauncher({
const { t } = useLingui();
const { mutateAsync: runAgent } = workspaceTrpc.agents.run.useMutation();
const appearance = useTerminalAppearance();
const awaitAcpChatEnabled = useAwaitAcpChatEnabled();
const resolveAcpChatEnabled = useResolveAcpChatEnabled();
const { hostUrl } = useWorkspaceClient();
const { data: agentConfigs } = useV2AgentConfigs(hostUrl);
const queryClient = useQueryClient();

const openAgentChat = useCallback<OpenAgentChat>(
async (input) => {
if (!(await awaitAcpChatEnabled())) return null;
if (!(await resolveAcpChatEnabled())) return null;
const configs = await queryClient
.ensureQueryData(v2AgentConfigsQueryOptions(hostUrl))
.catch(() => agentConfigs ?? []);
Expand Down Expand Up @@ -104,7 +104,7 @@ export function useAgentSessionLauncher({
}
return { terminalId };
},
[awaitAcpChatEnabled, queryClient, hostUrl, agentConfigs, store],
[resolveAcpChatEnabled, queryClient, hostUrl, agentConfigs, store],
);

const openNewChat = useCallback<OpenNewChat>(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ let creates = 0;
const changes = new Set<() => void>();
const connectionListeners = new Set<() => void>();
let refs = 0;
let chatAvailability: "enabled" | "disabled" = "enabled";
type Binding = {
terminalId: string;
agentId: string;
Expand Down Expand Up @@ -88,9 +87,6 @@ const transport = {
mock.module("@superset/workspace-client", () => ({
useWorkspaceClient: () => ({ hostUrl }),
}));
mock.module("renderer/hooks/useAcpChatEnabled", () => ({
useAcpChatEnabled: () => chatAvailability,
}));
mock.module("renderer/hooks/host-service/useWorkspaceHostUrl", () => ({
useWorkspaceHostUrl: () => hostUrl,
}));
Expand Down Expand Up @@ -243,7 +239,6 @@ afterEach(() => {
reads = 0;
outlineReads = 0;
creates = 0;
chatAvailability = "enabled";
terminalAgents = new Map();
localStorage.clear();
});
Expand Down Expand Up @@ -367,14 +362,6 @@ test("a launch whose stored prompt differs is held back and attached at the dead
expect(creates).toBe(0);
});

test("turning agent chat off removes the waiting pane instead of leaving it stuck", async () => {
chatAvailability = "disabled";
const pending = await waitingPane();
show(pending.store, "disabled-workspace");
await waitFor(() => expect(pending.store.getState().tabs).toHaveLength(0));
expect(reads).toBe(0);
});

test("a chat with no message yet backs off, and each first prompt is read once", async () => {
const pending = await waitingPane();
sessions = [session("empty")];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { useEffect, useMemo, useRef } from "react";
import { useTerminalAgentBindings } from "renderer/hooks/host-service/useTerminalAgentBindings";
import { useWorkspaceConnectionRefresh } from "renderer/hooks/host-service/useWorkspaceConnectionRefresh";
import { useWorkspaceEvent } from "renderer/hooks/host-service/useWorkspaceEvent";
import { useAcpChatEnabled } from "renderer/hooks/useAcpChatEnabled";
import {
attachCloudChatSession,
releasePendingCloudChat,
Expand Down Expand Up @@ -65,8 +64,7 @@ export function useAutoAdoptChatSessions({
isLayoutReady,
pendingTimeoutMs = PENDING_CLOUD_CHAT_TIMEOUT_MS,
}: UseAutoAdoptChatSessionsArgs): void {
const chatAvailability = useAcpChatEnabled();
const enabled = chatAvailability === "enabled" && isLayoutReady;
const enabled = isLayoutReady;
const { hostUrl } = useWorkspaceClient();
const { transport } = useChatWiring();
const queryClient = useQueryClient();
Expand Down Expand Up @@ -138,16 +136,6 @@ export function useAutoAdoptChatSessions({
const refresh = useWorkspaceConnectionRefresh(workspaceId, queryKey, enabled);
useWorkspaceEvent("chat:sessions-changed", workspaceId, refresh, enabled);

// biome-ignore lint/correctness/useExhaustiveDependencies: tabs re-runs it when a waiting pane is restored
useEffect(() => {
if (chatAvailability !== "disabled" || !isLayoutReady) return;
for (const pending of pendingPanes([store, ...linkedStores])) {
pending.store
.getState()
.closePane({ tabId: pending.tabId, paneId: pending.paneId });
}
}, [chatAvailability, isLayoutReady, store, linkedStores, tabs]);

useEffect(() => {
const result = sessionsQuery.data;
if (!enabled || !result || sessionsQuery.isFetching) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { WorkspaceStore } from "@superset/panes";
import { useWorkspaceClient, workspaceTrpc } from "@superset/workspace-client";
import { useQuery } from "@tanstack/react-query";
import { useEffect, useRef } from "react";
import { useAcpChatEnabled } from "renderer/hooks/useAcpChatEnabled";
import type { StoreApi } from "zustand/vanilla";
import type { ConsumeSearch, PaneViewerData } from "../../types";
import { focusChatSession } from "../../utils/focusChatSession";
Expand Down Expand Up @@ -35,7 +34,6 @@ export function useConsumeAutomationRunLink({
consumeSearch,
}: UseConsumeAutomationRunLinkArgs): void {
const consumedRef = useRef<Set<string>>(new Set());
const acpChat = useAcpChatEnabled();
const { hostUrl } = useWorkspaceClient();
const { transport } = useChatWiring();
const chatQuery = useQuery({
Expand All @@ -56,16 +54,13 @@ export function useConsumeAutomationRunLink({
: [];
return chatSessionTarget({ result, sessions, sessionId, workspaceId });
},
enabled: !!sessionId && acpChat === "enabled" && isLayoutReady,
enabled: !!sessionId && isLayoutReady,
refetchOnWindowFocus: false,
retry: false,
});
useEffect(() => {
if (!sessionId || !isLayoutReady || acpChat === "resolving") return;
if (
acpChat === "enabled" &&
(chatQuery.isFetching || (!chatQuery.isSuccess && !chatQuery.isError))
)
if (!sessionId || !isLayoutReady) return;
if (chatQuery.isFetching || (!chatQuery.isSuccess && !chatQuery.isError))
return;
const key = `${workspaceId}:${getAutomationRunLinkConsumeKey({
type: "chat",
Expand All @@ -74,14 +69,12 @@ export function useConsumeAutomationRunLink({
})}`;
if (consumedRef.current.has(key)) return;
consumedRef.current.add(key);
const target =
acpChat === "enabled" && chatQuery.isSuccess ? chatQuery.data : null;
const target = chatQuery.isSuccess ? chatQuery.data : null;
consumeSearch(target ? ["sessionId", "terminalId"] : ["sessionId"]);
if (target) focusChatSession({ store, linkedStores, session: target });
}, [
sessionId,
isLayoutReady,
acpChat,
chatQuery.isSuccess,
chatQuery.isError,
chatQuery.isFetching,
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FEATURE_FLAGS } from "@superset/shared/constants";
import { useFeatureFlagEnabled } from "posthog-js/react";
import { useTerminalAgentBinding } from "renderer/hooks/host-service/useTerminalAgentBindings";
import { useWorkspaceHostUrl } from "renderer/hooks/host-service/useWorkspaceHostUrl";
import { useAcpChatEnabled } from "renderer/hooks/useAcpChatEnabled";
import { useV2AgentConfigs } from "renderer/hooks/useV2AgentConfigs";
import type { ChatPaneData, TerminalPaneData } from "../../../../types";
import type { SurfaceForkSource } from "../useSurfaceFork";
Expand All @@ -20,6 +21,7 @@ export function useSurfaceForkSource(
): { enabled: boolean; source: SurfaceForkSource | null } {
const enabled =
useFeatureFlagEnabled(FEATURE_FLAGS.AGENT_SURFACE_FORK) === true;
const acpChatEnabled = useAcpChatEnabled();
const binding = useTerminalAgentBinding(workspaceId, pane.data.terminalId);
const hostUrl = useWorkspaceHostUrl(workspaceId);
const { data: configs = [] } = useV2AgentConfigs(hostUrl);
Expand All @@ -43,6 +45,7 @@ export function useSurfaceForkSource(
}

if (
!acpChatEnabled ||
!binding?.agentSessionId ||
binding.endedAt ||
LOCKED_WHILE_CLI_RUNS.has(binding.agentId) ||
Expand Down
Loading
Loading