Keep older chat history and image previews stable (#2490)
* fix(app): keep timeline history and previews stable Treat persisted timeline replicas as display-only so authoritative pagination always comes from the daemon. Require renewed user intent between older-history pages unless the viewport genuinely remains at the history edge. Model assistant image acquisition as loading, loaded, or failed so recreating a preview URL cannot be rendered as an error. * fix(app): keep file image previews current * fix(app): stabilize history pagination edges * fix(app): recover transient timeline loads * fix(app): retain assistant image previews * Stabilize history pagination lifecycle * Stabilize history settlement and image previews * Retry file images after reconnect * Keep active previews and pagination requests alive * Replace image hook tests with typed ports * Integrate timeline hydration with submission authority * Harden pre-hydration timeline reconciliation * Make rewind E2E use real scroll intent * Preserve live timeline state through hydration * Keep mounted image attachments retained * Protect preview persistence and lifecycle hydration * Preserve idless live assistant continuations * Preserve timeline rows across delayed hydration * test(app): cover real assistant image files * Preserve assistant tool ordering during hydration
This commit is contained in:
@@ -72,7 +72,7 @@ import { WorkspaceDraftAgentTab } from "@/composer/draft/workspace-tab";
|
||||
import { useCreateFlowStore } from "@/stores/create-flow-store";
|
||||
import { buildDraftStoreKey, generateDraftId } from "@/stores/draft-keys";
|
||||
import { usePanelStore } from "@/stores/panel-store";
|
||||
import { type Agent, useSessionStore } from "@/stores/session-store";
|
||||
import { selectAgentTimelineState, type Agent, useSessionStore } from "@/stores/session-store";
|
||||
import { useWorkspaceLayoutStore } from "@/stores/workspace-layout-store";
|
||||
import { buildWorkspaceTabPersistenceKey } from "@/workspace-tabs/model";
|
||||
import type { Theme } from "@/styles/theme";
|
||||
@@ -792,11 +792,12 @@ function ChatAgentContent({
|
||||
const historySyncGeneration = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.historySyncGeneration ?? 0,
|
||||
);
|
||||
const hasAppliedAuthoritativeHistory = useSessionStore((state) =>
|
||||
const replicaTimelineStatus = useSessionStore((state) =>
|
||||
agentId
|
||||
? state.sessions[serverId]?.agentAuthoritativeHistoryApplied?.get(agentId) === true
|
||||
: false,
|
||||
? selectAgentTimelineState(state.sessions[serverId], agentId).status
|
||||
: ("cold" as const),
|
||||
);
|
||||
const hasAppliedAuthoritativeHistory = replicaTimelineStatus === "synced";
|
||||
const agentHistorySyncGeneration = useSessionStore((state) =>
|
||||
agentId ? (state.sessions[serverId]?.agentHistorySyncGeneration?.get(agentId) ?? -1) : -1,
|
||||
);
|
||||
@@ -832,7 +833,8 @@ function ChatAgentContent({
|
||||
kind: "idle",
|
||||
});
|
||||
|
||||
const hasHydratedHistoryBefore = hasAppliedAuthoritativeHistory;
|
||||
const hasHydratedHistoryBefore =
|
||||
hasAppliedAuthoritativeHistory || replicaTimelineStatus === "painted";
|
||||
|
||||
const attentionController = useAgentAttentionClear({
|
||||
agentId,
|
||||
|
||||
@@ -104,10 +104,12 @@ function ProviderSubagentPanel() {
|
||||
.catch(() => undefined);
|
||||
}, [client, serverId, supported, target.parentAgentId, target.subagentId]);
|
||||
|
||||
const loadOlder = useCallback(() => {
|
||||
if (!client || !supported || isLoadingOlder || !timeline?.hasOlder || !timeline.epoch) return;
|
||||
const loadOlder = useCallback((): boolean => {
|
||||
if (!client || !supported || isLoadingOlder || !timeline?.hasOlder || !timeline.epoch) {
|
||||
return false;
|
||||
}
|
||||
const firstSeq = timeline.rows.size ? Math.min(...timeline.rows.keys()) : null;
|
||||
if (firstSeq === null) return;
|
||||
if (firstSeq === null) return false;
|
||||
setIsLoadingOlder(true);
|
||||
void client
|
||||
.fetchProviderSubagentTimeline(target.parentAgentId, target.subagentId, {
|
||||
@@ -121,6 +123,7 @@ function ProviderSubagentPanel() {
|
||||
})
|
||||
.catch(() => undefined)
|
||||
.finally(() => setIsLoadingOlder(false));
|
||||
return true;
|
||||
}, [
|
||||
client,
|
||||
isLoadingOlder,
|
||||
|
||||
Reference in New Issue
Block a user