Skip to content

Commit 6006871

Browse files
committed
fix: scope selected global web subscriptions
1 parent 9421529 commit 6006871

2 files changed

Lines changed: 79 additions & 8 deletions

File tree

ui/src/ui/controllers/sessions.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,48 @@ describe("syncSelectedSessionMessageSubscription", () => {
154154
expect(state.chatSessionMessageSubscriptionAgentId).toBe("work");
155155
});
156156

157+
it("keeps agent-scoped global alias subscriptions scoped for unsubscribe", async () => {
158+
const request = vi.fn(async (method: string) =>
159+
method === "sessions.messages.subscribe" ? { key: "global" } : { subscribed: false },
160+
);
161+
const state = createState(request, {
162+
sessionKey: "agent:work:main",
163+
assistantAgentId: "main",
164+
sessionsResult: {
165+
ts: 1,
166+
path: "/tmp/sessions.json",
167+
count: 2,
168+
sessions: [
169+
{ key: "agent:work:main", kind: "global", updatedAt: 2 },
170+
{ key: "agent:ops:main", kind: "global", updatedAt: 1 },
171+
],
172+
defaults: { modelProvider: null, model: null, contextTokens: null },
173+
totalCount: 2,
174+
limit: 50,
175+
offset: 0,
176+
hasMore: false,
177+
},
178+
} as Partial<SessionsState & { sessionKey: string }>) as SessionsState & { sessionKey: string };
179+
180+
await syncSelectedSessionMessageSubscription(state);
181+
state.sessionKey = "agent:ops:main";
182+
await syncSelectedSessionMessageSubscription(state);
183+
184+
expect(request).toHaveBeenNthCalledWith(1, "sessions.messages.subscribe", {
185+
key: "agent:work:main",
186+
agentId: "work",
187+
});
188+
expect(request).toHaveBeenNthCalledWith(2, "sessions.messages.unsubscribe", {
189+
key: "global",
190+
agentId: "work",
191+
});
192+
expect(request).toHaveBeenNthCalledWith(3, "sessions.messages.subscribe", {
193+
key: "agent:ops:main",
194+
agentId: "ops",
195+
});
196+
expect(state.chatSessionMessageSubscriptionAgentId).toBe("ops");
197+
});
198+
157199
it("uses the hello default agent for global subscriptions before agents load", async () => {
158200
const request = vi.fn(async () => ({ key: "global" }));
159201
const state = createState(request, {

ui/src/ui/controllers/sessions.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,35 @@ function isGlobalSessionKey(value: string | null | undefined): boolean {
9999
return (value ?? "").trim().toLowerCase() === "global";
100100
}
101101

102+
function resolveSelectedGlobalAliasAgentId(
103+
state: SessionsState,
104+
key: string | null | undefined,
105+
): string | null {
106+
const parsed = parseAgentSessionKey(key);
107+
if (!parsed?.agentId) {
108+
return null;
109+
}
110+
const rest = parsed.rest.toLowerCase();
111+
if (rest === "global") {
112+
return normalizeAgentId(parsed.agentId);
113+
}
114+
if (rest !== "main") {
115+
return null;
116+
}
117+
const row = state.sessionsResult?.sessions.find((session) => session.key === key);
118+
return row?.kind === "global" ? normalizeAgentId(parsed.agentId) : null;
119+
}
120+
121+
function resolveSelectedSessionMessageSubscriptionAgentId(
122+
state: SessionsState,
123+
key: string,
124+
): string | null {
125+
if (isGlobalSessionKey(key)) {
126+
return resolveSelectedGlobalAgentId(state);
127+
}
128+
return resolveSelectedGlobalAliasAgentId(state, key);
129+
}
130+
102131
function resolveSelectedGlobalAgentId(state: SessionsState): string {
103132
const parsed = parseAgentSessionKey(state.sessionKey);
104133
if (parsed?.agentId) {
@@ -158,16 +187,18 @@ function sessionsChangedGlobalAgentMatches(
158187
}
159188

160189
function buildSelectedSessionMessageSubscriptionParams(state: SessionsState, key: string) {
190+
const agentId = resolveSelectedSessionMessageSubscriptionAgentId(state, key);
161191
return {
162192
key,
163-
...(isGlobalSessionKey(key) ? { agentId: resolveSelectedGlobalAgentId(state) } : {}),
193+
...(agentId ? { agentId } : {}),
164194
};
165195
}
166196

167197
function buildSelectedSessionRequestParams(state: SessionsState, key: string) {
198+
const agentId = resolveSelectedSessionMessageSubscriptionAgentId(state, key);
168199
return {
169200
key,
170-
...(isGlobalSessionKey(key) ? { agentId: resolveSelectedGlobalAgentId(state) } : {}),
201+
...(agentId ? { agentId } : {}),
171202
};
172203
}
173204

@@ -192,8 +223,8 @@ function isCurrentSelectedSessionMessageSubscriptionSync(
192223
state.client === params.client &&
193224
state.connected &&
194225
state.sessionKey.trim() === params.requestedKey &&
195-
(!isGlobalSessionKey(params.requestedKey) ||
196-
resolveSelectedGlobalAgentId(state) === (params.requestedAgentId ?? null))
226+
resolveSelectedSessionMessageSubscriptionAgentId(state, params.requestedKey) ===
227+
(params.requestedAgentId ?? null)
197228
);
198229
}
199230

@@ -677,11 +708,9 @@ export async function syncSelectedSessionMessageSubscription(
677708
);
678709
const previousCanonicalKey = normalizeSubscriptionKey(state.chatSessionMessageSubscriptionKey);
679710
const previousSelectedKey = previousRequestedKey ?? previousCanonicalKey;
680-
const nextSubscriptionAgentId = isGlobalSessionKey(nextKey)
681-
? resolveSelectedGlobalAgentId(state)
682-
: null;
711+
const nextSubscriptionAgentId = resolveSelectedSessionMessageSubscriptionAgentId(state, nextKey);
683712
const selectedAgentChanged =
684-
isGlobalSessionKey(nextKey) &&
713+
nextSubscriptionAgentId !== null &&
685714
previousSelectedKey === nextKey &&
686715
(state.chatSessionMessageSubscriptionAgentId ?? null) !== nextSubscriptionAgentId;
687716
const selectedKeyChanged = previousSelectedKey !== null && previousSelectedKey !== nextKey;

0 commit comments

Comments
 (0)