feat(app): add chat outline navigation (#2792)
* feat(app): add chat outline navigation Long agent sessions need prompt navigation without coupling viewport movement to disposable server windows. Keep fetched timeline pages in client state so repeated jumps stay local, and settle prompt jumps against virtualized row measurement changes. * fix(app): preserve chat outline timeline coverage * fix(app): reconcile outline window submissions * fix(app): harden outline timeline navigation
This commit is contained in:
@@ -352,6 +352,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
|
||||
const clearAgentTurnLiveness = useSessionStore((state) => state.clearAgentTurnLiveness);
|
||||
const clearAgentStreamHead = useSessionStore((state) => state.clearAgentStreamHead);
|
||||
const setAgentTimelineCursor = useSessionStore((state) => state.setAgentTimelineCursor);
|
||||
const setAgentTimelineHasNewer = useSessionStore((state) => state.setAgentTimelineHasNewer);
|
||||
const setInitializingAgents = useSessionStore((state) => state.setInitializingAgents);
|
||||
const bumpHistorySyncGeneration = useSessionStore((state) => state.bumpHistorySyncGeneration);
|
||||
const markAgentHistorySynchronized = useSessionStore(
|
||||
@@ -626,6 +627,13 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
|
||||
acknowledgedClientMessageIds: result.acknowledgedClientMessageIds,
|
||||
});
|
||||
}
|
||||
if (payload.direction !== "before") {
|
||||
setAgentTimelineHasNewer(serverId, (current) => {
|
||||
const next = new Map(current);
|
||||
next.set(agentId, payload.hasNewer);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
markAgentHistorySynchronized(serverId, agentId);
|
||||
} else {
|
||||
applyAgentTimelineResponseState(serverId, agentId, {
|
||||
@@ -633,6 +641,10 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
|
||||
head: result.head,
|
||||
range: result.cursorChanged ? (result.cursor ?? null) : (currentCursor ?? null),
|
||||
older: result.older,
|
||||
newer:
|
||||
payload.direction === "before"
|
||||
? timeline.status === "synced" && timeline.newer === "available"
|
||||
: payload.hasNewer,
|
||||
synchronized: shouldMarkAuthoritativeHistoryApplied,
|
||||
acknowledgedClientMessageIds: result.acknowledgedClientMessageIds,
|
||||
});
|
||||
@@ -659,6 +671,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
|
||||
recoverTimelineGap,
|
||||
serverId,
|
||||
setAgentStreamState,
|
||||
setAgentTimelineHasNewer,
|
||||
setInitializingAgents,
|
||||
],
|
||||
);
|
||||
@@ -729,6 +742,11 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
|
||||
useEffect(
|
||||
() =>
|
||||
getHostRuntimeStore().subscribeAgentStoppedRunning(serverId, (agentId) => {
|
||||
const session = useSessionStore.getState().sessions[serverId];
|
||||
const timeline = selectAgentTimelineState(session, agentId);
|
||||
if (timeline.status === "synced" && timeline.newer === "available") {
|
||||
return;
|
||||
}
|
||||
viewedTimelineSyncRef.current?.reconcileAgent(agentId);
|
||||
}),
|
||||
[serverId],
|
||||
|
||||
Reference in New Issue
Block a user