fix(app,server): discover server-cached setup snapshots across sessions
807cce6a hides the "Show setup" action when there is no workspace setup
snapshot. That broke the case where setup genuinely ran on a different
WebSocket session than the one the browser reconnected with: the snapshot
was cached per-Session, so discovery returned null and the action stayed
hidden.
Share the setup snapshot cache at the websocket-server level and inject
it into every Session. On focus, the workspace screen now requests
fetchWorkspaceSetupStatus when it has no local snapshot; workspaces with
no setup history still return null, preserving 807cce6a's intent.
This commit is contained in:
@@ -976,6 +976,7 @@ function WorkspaceScreenContent({
|
|||||||
const workspaceSetupSnapshot = useWorkspaceSetupStore((state) =>
|
const workspaceSetupSnapshot = useWorkspaceSetupStore((state) =>
|
||||||
persistenceKey ? (state.snapshots[persistenceKey] ?? null) : null,
|
persistenceKey ? (state.snapshots[persistenceKey] ?? null) : null,
|
||||||
);
|
);
|
||||||
|
const upsertWorkspaceSetupProgress = useWorkspaceSetupStore((state) => state.upsertProgress);
|
||||||
const showWorkspaceSetup = shouldShowWorkspaceSetup(workspaceSetupSnapshot);
|
const showWorkspaceSetup = shouldShowWorkspaceSetup(workspaceSetupSnapshot);
|
||||||
const uiTabs = useMemo(
|
const uiTabs = useMemo(
|
||||||
() => (workspaceLayout ? collectAllTabs(workspaceLayout.root) : EMPTY_UI_TABS),
|
() => (workspaceLayout ? collectAllTabs(workspaceLayout.root) : EMPTY_UI_TABS),
|
||||||
@@ -1191,6 +1192,55 @@ function WorkspaceScreenContent({
|
|||||||
|
|
||||||
const emptyWorkspaceSeedRef = useRef<string | null>(null);
|
const emptyWorkspaceSeedRef = useRef<string | null>(null);
|
||||||
const autoOpenedSetupTabWorkspaceRef = useRef<string | null>(null);
|
const autoOpenedSetupTabWorkspaceRef = useRef<string | null>(null);
|
||||||
|
const requestedWorkspaceSetupStatusKeyRef = useRef<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isRouteFocused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!client || !normalizedServerId || !normalizedWorkspaceId || !persistenceKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (workspaceSetupSnapshot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (requestedWorkspaceSetupStatusKeyRef.current === persistenceKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestedWorkspaceSetupStatusKeyRef.current = persistenceKey;
|
||||||
|
let isCancelled = false;
|
||||||
|
|
||||||
|
client
|
||||||
|
.fetchWorkspaceSetupStatus(normalizedWorkspaceId)
|
||||||
|
.then((response) => {
|
||||||
|
if (isCancelled || response.workspaceId !== normalizedWorkspaceId || !response.snapshot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
upsertWorkspaceSetupProgress({
|
||||||
|
serverId: normalizedServerId,
|
||||||
|
payload: { workspaceId: response.workspaceId, ...response.snapshot },
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (requestedWorkspaceSetupStatusKeyRef.current === persistenceKey) {
|
||||||
|
requestedWorkspaceSetupStatusKeyRef.current = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isCancelled = true;
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
client,
|
||||||
|
isRouteFocused,
|
||||||
|
normalizedServerId,
|
||||||
|
normalizedWorkspaceId,
|
||||||
|
persistenceKey,
|
||||||
|
upsertWorkspaceSetupProgress,
|
||||||
|
workspaceSetupSnapshot,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isRouteFocused) {
|
if (!isRouteFocused) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -508,6 +508,7 @@ export type SessionOptions = {
|
|||||||
providerSnapshotManager?: ProviderSnapshotManager;
|
providerSnapshotManager?: ProviderSnapshotManager;
|
||||||
scriptRouteStore?: ScriptRouteStore;
|
scriptRouteStore?: ScriptRouteStore;
|
||||||
scriptRuntimeStore?: WorkspaceScriptRuntimeStore;
|
scriptRuntimeStore?: WorkspaceScriptRuntimeStore;
|
||||||
|
workspaceSetupSnapshots?: Map<string, WorkspaceSetupSnapshot>;
|
||||||
onBranchChanged?: (
|
onBranchChanged?: (
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
oldBranch: string | null,
|
oldBranch: string | null,
|
||||||
@@ -726,7 +727,7 @@ export class Session {
|
|||||||
);
|
);
|
||||||
private readonly checkoutDiffSubscriptions = new Map<string, () => void>();
|
private readonly checkoutDiffSubscriptions = new Map<string, () => void>();
|
||||||
private readonly workspaceGitWatchTargets = new Map<string, WorkspaceGitWatchTarget>();
|
private readonly workspaceGitWatchTargets = new Map<string, WorkspaceGitWatchTarget>();
|
||||||
private readonly workspaceSetupSnapshots = new Map<string, WorkspaceSetupSnapshot>();
|
private readonly workspaceSetupSnapshots: Map<string, WorkspaceSetupSnapshot>;
|
||||||
private readonly workspaceGitFetchSubscriptions = new Map<string, () => void>();
|
private readonly workspaceGitFetchSubscriptions = new Map<string, () => void>();
|
||||||
private readonly workspaceGitSubscriptions = new Map<string, () => void>();
|
private readonly workspaceGitSubscriptions = new Map<string, () => void>();
|
||||||
private readonly registerVoiceSpeakHandler?: (
|
private readonly registerVoiceSpeakHandler?: (
|
||||||
@@ -775,6 +776,7 @@ export class Session {
|
|||||||
providerSnapshotManager,
|
providerSnapshotManager,
|
||||||
scriptRouteStore,
|
scriptRouteStore,
|
||||||
scriptRuntimeStore,
|
scriptRuntimeStore,
|
||||||
|
workspaceSetupSnapshots,
|
||||||
onBranchChanged,
|
onBranchChanged,
|
||||||
getDaemonTcpPort,
|
getDaemonTcpPort,
|
||||||
getDaemonTcpHost,
|
getDaemonTcpHost,
|
||||||
@@ -816,6 +818,7 @@ export class Session {
|
|||||||
this.providerSnapshotManager = providerSnapshotManager ?? null;
|
this.providerSnapshotManager = providerSnapshotManager ?? null;
|
||||||
this.scriptRouteStore = scriptRouteStore ?? null;
|
this.scriptRouteStore = scriptRouteStore ?? null;
|
||||||
this.scriptRuntimeStore = scriptRuntimeStore ?? null;
|
this.scriptRuntimeStore = scriptRuntimeStore ?? null;
|
||||||
|
this.workspaceSetupSnapshots = workspaceSetupSnapshots ?? new Map();
|
||||||
this.onBranchChanged = onBranchChanged;
|
this.onBranchChanged = onBranchChanged;
|
||||||
this.getDaemonTcpPort = getDaemonTcpPort ?? null;
|
this.getDaemonTcpPort = getDaemonTcpPort ?? null;
|
||||||
this.getDaemonTcpHost = getDaemonTcpHost ?? null;
|
this.getDaemonTcpHost = getDaemonTcpHost ?? null;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import type { DaemonConfigStore, MutableDaemonConfig } from "./daemon-config-sto
|
|||||||
import {
|
import {
|
||||||
type ServerInfoStatusPayload,
|
type ServerInfoStatusPayload,
|
||||||
type SessionOutboundMessage,
|
type SessionOutboundMessage,
|
||||||
|
type WorkspaceSetupSnapshot,
|
||||||
type WSHelloMessage,
|
type WSHelloMessage,
|
||||||
WSInboundMessageSchema,
|
WSInboundMessageSchema,
|
||||||
type ServerCapabilityState,
|
type ServerCapabilityState,
|
||||||
@@ -317,6 +318,7 @@ export class VoiceAssistantWebSocketServer {
|
|||||||
} | null;
|
} | null;
|
||||||
private readonly voiceSpeakHandlers = new Map<string, VoiceSpeakHandler>();
|
private readonly voiceSpeakHandlers = new Map<string, VoiceSpeakHandler>();
|
||||||
private readonly voiceCallerContexts = new Map<string, VoiceCallerContext>();
|
private readonly voiceCallerContexts = new Map<string, VoiceCallerContext>();
|
||||||
|
private readonly workspaceSetupSnapshots = new Map<string, WorkspaceSetupSnapshot>();
|
||||||
private readonly agentProviderRuntimeSettings: AgentProviderRuntimeSettingsMap | undefined;
|
private readonly agentProviderRuntimeSettings: AgentProviderRuntimeSettingsMap | undefined;
|
||||||
private readonly providerOverrides: Record<string, ProviderOverride> | undefined;
|
private readonly providerOverrides: Record<string, ProviderOverride> | undefined;
|
||||||
private readonly isDev: boolean;
|
private readonly isDev: boolean;
|
||||||
@@ -766,6 +768,7 @@ export class VoiceAssistantWebSocketServer {
|
|||||||
providerSnapshotManager: this.providerSnapshotManager,
|
providerSnapshotManager: this.providerSnapshotManager,
|
||||||
scriptRouteStore: this.scriptRouteStore ?? undefined,
|
scriptRouteStore: this.scriptRouteStore ?? undefined,
|
||||||
scriptRuntimeStore: this.scriptRuntimeStore ?? undefined,
|
scriptRuntimeStore: this.scriptRuntimeStore ?? undefined,
|
||||||
|
workspaceSetupSnapshots: this.workspaceSetupSnapshots,
|
||||||
onBranchChanged: this.onBranchChanged ?? undefined,
|
onBranchChanged: this.onBranchChanged ?? undefined,
|
||||||
getDaemonTcpPort: this.getDaemonTcpPort ?? undefined,
|
getDaemonTcpPort: this.getDaemonTcpPort ?? undefined,
|
||||||
getDaemonTcpHost: this.getDaemonTcpHost ?? undefined,
|
getDaemonTcpHost: this.getDaemonTcpHost ?? undefined,
|
||||||
|
|||||||
Reference in New Issue
Block a user