Skip to content

Commit 046ad2f

Browse files
committed
fix session init bug
1 parent 1cc7797 commit 046ad2f

File tree

2 files changed

+41
-27
lines changed

2 files changed

+41
-27
lines changed

apps/array/src/renderer/features/sessions/stores/sessionStore.ts

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
import { create } from "zustand";
2020
import { immer } from "zustand/middleware/immer";
2121
import { getCloudUrlFromRegion } from "@/constants/oauth";
22-
import { trpcReact, trpcVanilla } from "@/renderer/trpc";
22+
import { trpcVanilla } from "@/renderer/trpc";
2323

2424
const log = logger.scope("session-store");
2525
const CLOUD_POLLING_INTERVAL_MS = 500;
@@ -76,6 +76,41 @@ type SessionStore = SessionState & { actions: SessionActions };
7676

7777
const connectAttempts = new Set<string>();
7878
const cloudPollers = new Map<string, NodeJS.Timeout>();
79+
// Track active tRPC subscriptions for cleanup
80+
const subscriptions = new Map<string, { unsubscribe: () => void }>();
81+
82+
/**
83+
* Subscribe to agent session events via tRPC subscription.
84+
* Called synchronously after session is created, before any prompts are sent.
85+
*/
86+
function subscribeToChannel(taskRunId: string) {
87+
if (subscriptions.has(taskRunId)) return;
88+
89+
const subscription = trpcVanilla.agent.onSessionEvent.subscribe(
90+
{ sessionId: taskRunId },
91+
{
92+
onData: (payload: unknown) => {
93+
useStore.setState((state) => {
94+
const session = state.sessions[taskRunId];
95+
if (session) {
96+
session.events.push(payload as AcpMessage);
97+
}
98+
});
99+
},
100+
onError: (err) => {
101+
log.error("Session subscription error", { taskRunId, error: err });
102+
},
103+
},
104+
);
105+
106+
subscriptions.set(taskRunId, subscription);
107+
}
108+
109+
function unsubscribeFromChannel(taskRunId: string) {
110+
const subscription = subscriptions.get(taskRunId);
111+
subscription?.unsubscribe();
112+
subscriptions.delete(taskRunId);
113+
}
79114

80115
function getAuthCredentials(): AuthCredentials | null {
81116
const authState = useAuthStore.getState();
@@ -397,6 +432,7 @@ const useStore = create<SessionStore>()(
397432
session.logUrl = logUrl;
398433

399434
addSession(session);
435+
subscribeToChannel(taskRunId);
400436

401437
const result = await trpcVanilla.agent.reconnect.mutate({
402438
taskId,
@@ -412,6 +448,7 @@ const useStore = create<SessionStore>()(
412448
if (result) {
413449
updateSession(taskRunId, { status: "connected" });
414450
} else {
451+
unsubscribeFromChannel(taskRunId);
415452
removeSession(taskRunId);
416453
}
417454
};
@@ -450,6 +487,7 @@ const useStore = create<SessionStore>()(
450487
session.model = defaultModel;
451488

452489
addSession(session);
490+
subscribeToChannel(taskRun.id);
453491

454492
if (initialPrompt?.length) {
455493
await get().actions.sendPrompt(taskId, initialPrompt);
@@ -572,6 +610,7 @@ const useStore = create<SessionStore>()(
572610
} catch (error) {
573611
log.error("Failed to cancel session", error);
574612
}
613+
unsubscribeFromChannel(session.taskRunId);
575614
}
576615

577616
removeSession(session.taskRunId);
@@ -690,24 +729,3 @@ useAuthStore.subscribe(
690729
}
691730
},
692731
);
693-
694-
/**
695-
* Hook to subscribe to agent session events via tRPC subscription.
696-
* This should be used in a component that renders when a session is active.
697-
*/
698-
export function useAgentSessionSubscription(sessionId: string | undefined) {
699-
trpcReact.agent.onSessionEvent.useSubscription(
700-
{ sessionId: sessionId ?? "" },
701-
{
702-
enabled: !!sessionId,
703-
onData: (payload) => {
704-
useStore.setState((state) => {
705-
const session = state.sessions[sessionId!];
706-
if (session) {
707-
session.events.push(payload as AcpMessage);
708-
}
709-
});
710-
},
711-
},
712-
);
713-
}

apps/array/src/renderer/features/task-detail/components/TaskDetail.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import { PanelLayout } from "@features/panels";
2-
import {
3-
useAgentSessionSubscription,
4-
useSessionForTask,
5-
} from "@features/sessions/stores/sessionStore";
2+
import { useSessionForTask } from "@features/sessions/stores/sessionStore";
63
import { ExternalAppsOpener } from "@features/task-detail/components/ExternalAppsOpener";
74
import { useTaskData } from "@features/task-detail/hooks/useTaskData";
85
import { StartWorkspaceButton } from "@features/workspace/components/StartWorkspaceButton";
@@ -34,7 +31,6 @@ export function TaskDetail({ task: initialTask }: TaskDetailProps) {
3431
useFileWatcher(effectiveRepoPath, taskData.task.id);
3532

3633
const session = useSessionForTask(taskData.task.id);
37-
useAgentSessionSubscription(session?.taskRunId);
3834
const isRunning =
3935
session?.status === "connected" || session?.status === "connecting";
4036

0 commit comments

Comments
 (0)