Skip to content

Commit 37f8c38

Browse files
committed
fix(gateway): index sessions list child links
1 parent 6945988 commit 37f8c38

1 file changed

Lines changed: 72 additions & 14 deletions

File tree

src/gateway/session-utils.ts

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,8 @@ function shouldKeepStoreOnlyChildLink(entry: SessionEntry, now: number): boolean
333333
);
334334
}
335335

336-
function resolveChildSessionKeys(
336+
function resolveRuntimeChildSessionKeys(
337337
controllerSessionKey: string,
338-
store: Record<string, SessionEntry>,
339338
now = Date.now(),
340339
): string[] | undefined {
341340
const childSessionKeys = new Set<string>();
@@ -364,23 +363,47 @@ function resolveChildSessionKeys(
364363
}
365364
childSessionKeys.add(childSessionKey);
366365
}
366+
const childSessions = Array.from(childSessionKeys);
367+
return childSessions.length > 0 ? childSessions : undefined;
368+
}
369+
370+
function addChildSessionKey(
371+
childSessionsByKey: Map<string, string[]>,
372+
parentKey: string,
373+
childKey: string,
374+
) {
375+
const current = childSessionsByKey.get(parentKey);
376+
if (current) {
377+
if (!current.includes(childKey)) {
378+
current.push(childKey);
379+
}
380+
return;
381+
}
382+
childSessionsByKey.set(parentKey, [childKey]);
383+
}
384+
385+
function buildStoreChildSessionIndex(
386+
store: Record<string, SessionEntry>,
387+
now = Date.now(),
388+
): Map<string, string[]> {
389+
const childSessionsByKey = new Map<string, string[]>();
367390
for (const [key, entry] of Object.entries(store)) {
368-
if (!entry || key === controllerSessionKey) {
391+
if (!entry) {
369392
continue;
370393
}
371-
const spawnedBy = normalizeOptionalString(entry.spawnedBy);
372-
const parentSessionKey = normalizeOptionalString(entry.parentSessionKey);
373-
if (spawnedBy !== controllerSessionKey && parentSessionKey !== controllerSessionKey) {
394+
const parentKeys = [
395+
normalizeOptionalString(entry.spawnedBy),
396+
normalizeOptionalString(entry.parentSessionKey),
397+
].filter((value): value is string => Boolean(value) && value !== key);
398+
if (parentKeys.length === 0) {
374399
continue;
375400
}
376401
const latest = getSessionDisplaySubagentRunByChildSessionKey(key);
402+
let latestControllerSessionKey: string | undefined;
377403
if (latest) {
378-
const latestControllerSessionKey =
404+
latestControllerSessionKey =
379405
normalizeOptionalString(latest.controllerSessionKey) ||
380406
normalizeOptionalString(latest.requesterSessionKey);
381-
if (latestControllerSessionKey !== controllerSessionKey) {
382-
continue;
383-
}
384407
if (
385408
!shouldKeepSubagentRunChildLink(latest, {
386409
activeDescendants: countActiveDescendantRuns(key),
@@ -392,10 +415,37 @@ function resolveChildSessionKeys(
392415
} else if (!shouldKeepStoreOnlyChildLink(entry, now)) {
393416
continue;
394417
}
395-
childSessionKeys.add(key);
418+
for (const parentKey of parentKeys) {
419+
if (latestControllerSessionKey && latestControllerSessionKey !== parentKey) {
420+
continue;
421+
}
422+
addChildSessionKey(childSessionsByKey, parentKey, key);
423+
}
396424
}
397-
const childSessions = Array.from(childSessionKeys);
398-
return childSessions.length > 0 ? childSessions : undefined;
425+
return childSessionsByKey;
426+
}
427+
428+
function mergeChildSessionKeys(
429+
runtimeChildSessions: string[] | undefined,
430+
storeChildSessions: string[] | undefined,
431+
): string[] | undefined {
432+
if (!runtimeChildSessions?.length) {
433+
return storeChildSessions?.length ? storeChildSessions : undefined;
434+
}
435+
if (!storeChildSessions?.length) {
436+
return runtimeChildSessions;
437+
}
438+
return Array.from(new Set([...runtimeChildSessions, ...storeChildSessions]));
439+
}
440+
441+
function resolveChildSessionKeys(
442+
controllerSessionKey: string,
443+
store: Record<string, SessionEntry>,
444+
now = Date.now(),
445+
): string[] | undefined {
446+
const runtimeChildSessions = resolveRuntimeChildSessionKeys(controllerSessionKey, now);
447+
const storeChildSessions = buildStoreChildSessionIndex(store, now).get(controllerSessionKey);
448+
return mergeChildSessionKeys(runtimeChildSessions, storeChildSessions);
399449
}
400450

401451
function resolveTranscriptUsageFallback(params: {
@@ -1313,6 +1363,7 @@ export function buildGatewaySessionRow(params: {
13131363
includeDerivedTitles?: boolean;
13141364
includeLastMessage?: boolean;
13151365
transcriptUsageMaxBytes?: number;
1366+
storeChildSessionsByKey?: Map<string, string[]>;
13161367
}): GatewaySessionRow {
13171368
const { cfg, storePath, store, key, entry } = params;
13181369
const now = params.now ?? Date.now();
@@ -1448,7 +1499,12 @@ export function buildGatewaySessionRow(params: {
14481499
typeof totalTokens === "number" && Number.isFinite(totalTokens) && totalTokens > 0
14491500
? true
14501501
: transcriptUsage?.totalTokensFresh === true;
1451-
const childSessions = resolveChildSessionKeys(key, store, now);
1502+
const childSessions = params.storeChildSessionsByKey
1503+
? mergeChildSessionKeys(
1504+
resolveRuntimeChildSessionKeys(key, now),
1505+
params.storeChildSessionsByKey.get(key),
1506+
)
1507+
: resolveChildSessionKeys(key, store, now);
14521508
const latestCompactionCheckpoint = resolveLatestCompactionCheckpoint(entry);
14531509
const agentRuntime = resolveAgentRuntimeMetadata(cfg, sessionAgentId);
14541510
const selectedOrRuntimeModelProvider = selectedModel?.provider ?? modelProvider;
@@ -1630,6 +1686,7 @@ export function listSessionsFromStore(params: {
16301686
const now = Date.now();
16311687
const sessionListTranscriptUsageMaxBytes = 64 * 1024;
16321688
const sessionListTranscriptFieldRows = 100;
1689+
const storeChildSessionsByKey = buildStoreChildSessionIndex(store, now);
16331690

16341691
const includeGlobal = opts.includeGlobal === true;
16351692
const includeUnknown = opts.includeUnknown === true;
@@ -1738,6 +1795,7 @@ export function listSessionsFromStore(params: {
17381795
includeDerivedTitles: includeTranscriptFields && includeDerivedTitles,
17391796
includeLastMessage: includeTranscriptFields && includeLastMessage,
17401797
transcriptUsageMaxBytes: sessionListTranscriptUsageMaxBytes,
1798+
storeChildSessionsByKey,
17411799
});
17421800
});
17431801

0 commit comments

Comments
 (0)