Reduce workspace, agent, and chat sync traffic (#2028)

* feat(sync): keep live data scoped and current

Only viewed chats receive live timeline rows, while directory state now uses one subscribed bootstrap followed by ordered deltas. Legacy clients and daemons retain their existing behavior through centralized compatibility gates.

* fix(sync): preserve selective delivery boundaries

Keep selective timeline capability app-owned, union viewed sets across shared sockets, and reconcile directory side effects from accepted state. Visibility and archive suppression now follow their existing authoritative boundaries.

* test(app): align browser fixtures with runtime contracts

* fix(sync): preserve mixed-client delivery guarantees

* fix(sync): finish paged history before advancing

* fix(sync): replay live deltas after refresh failures

* test(server): model socket capability lookup

* test(app): use platform shortcut for split-pane coverage

* fix(app): keep hidden timelines dormant

* test(app): normalize split-pane shortcut setup

* fix(app): preserve sync state across retries

* fix(app): preserve background sync state across races

* fix directory bootstrap reconciliation edge cases

* fix selective timeline compatibility races

* fix superseded directory sync races

* fix(sync): centralize directory replica ordering

Own agent and workspace refresh transactions in HostRuntime so reconnect epochs, buffered updates, and lifecycle invalidation share one ordering boundary. Route timeline metadata and mixed-capability stream delivery through their authoritative runtime sources.

* fix(app): keep timeline requests runtime-scoped

* fix(app): close directory bootstrap races
This commit is contained in:
Mohamed Boudra
2026-07-17 17:03:34 +02:00
committed by GitHub
parent 6c99efae52
commit 1977d330ed
57 changed files with 5783 additions and 1343 deletions
@@ -0,0 +1,240 @@
import { describe, expect, it } from "vitest";
import type { FetchAgentsEntry } from "@getpaseo/client/internal/daemon-client";
import type { AgentSnapshotPayload } from "@getpaseo/protocol/messages";
import type { Agent } from "@/stores/session-store";
import { reconcileAgentDirectory } from "./agent-directory-reconciliation";
function snapshot(id: string, status: AgentSnapshotPayload["status"]): AgentSnapshotPayload {
return {
id,
provider: "codex",
cwd: "/repo",
model: null,
createdAt: "2026-07-12T10:00:00.000Z",
updatedAt: "2026-07-12T10:00:00.000Z",
lastUserMessageAt: null,
status,
capabilities: {
supportsStreaming: true,
supportsSessionPersistence: true,
supportsDynamicModes: true,
supportsMcpServers: true,
supportsReasoningStream: true,
supportsToolInvocations: true,
},
currentModeId: null,
availableModes: [],
pendingPermissions: [],
persistence: null,
title: null,
labels: {},
};
}
function entry(id: string, status: AgentSnapshotPayload["status"]): FetchAgentsEntry {
return {
agent: snapshot(id, status),
project: {
projectKey: "/repo",
projectName: "repo",
checkout: {
cwd: "/repo",
isGit: false,
currentBranch: null,
remoteUrl: null,
worktreeRoot: null,
isPaseoOwnedWorktree: false,
mainRepoRoot: null,
},
},
};
}
function replica(id: string, status: Agent["status"]): Agent {
return {
...snapshot(id, status),
serverId: "server",
createdAt: new Date("2026-07-12T10:00:00.000Z"),
updatedAt: new Date("2026-07-12T10:00:00.000Z"),
lastActivityAt: new Date("2026-07-12T10:00:00.000Z"),
lastUserMessageAt: null,
attentionTimestamp: null,
archivedAt: null,
parentAgentId: null,
};
}
describe("agent directory reconciliation", () => {
it("reports snapshot-only and buffered running transitions exactly once", () => {
const result = reconcileAgentDirectory({
previous: new Map([
["snapshot", replica("snapshot", "running")],
["buffered", replica("buffered", "running")],
]),
snapshot: [entry("snapshot", "idle"), entry("buffered", "running")],
deltas: [
{
kind: "upsert",
agent: snapshot("buffered", "idle"),
project: entry("buffered", "idle").project,
},
{
kind: "upsert",
agent: snapshot("buffered", "idle"),
project: entry("buffered", "idle").project,
},
],
});
expect(result.stoppedRunningAgentIds).toEqual(["snapshot", "buffered"]);
expect(result.entries.map(({ agent }) => [agent.id, agent.status])).toEqual([
["snapshot", "idle"],
["buffered", "idle"],
]);
});
it("preserves ordered upserts and removals received after page one", () => {
const result = reconcileAgentDirectory({
previous: new Map(),
snapshot: [entry("updated", "idle"), entry("removed", "idle")],
deltas: [
{
kind: "upsert",
agent: { ...snapshot("updated", "idle"), title: "live" },
project: entry("updated", "idle").project,
},
{ kind: "remove", agentId: "removed" },
],
});
expect(result.entries.map(({ agent }) => [agent.id, agent.title])).toEqual([
["updated", "live"],
]);
});
it("keeps newer page metadata when a stale buffered upsert arrives", () => {
const snapshotEntry = entry("agent", "running");
const staleProject = {
...snapshotEntry.project,
projectName: "stale project",
};
const result = reconcileAgentDirectory({
previous: new Map([["agent", replica("agent", "running")]]),
snapshot: [
{
...entry("agent", "running"),
agent: {
...snapshot("agent", "running"),
title: "newer page",
updatedAt: "2026-07-12T12:00:00.000Z",
},
},
],
deltas: [
{
kind: "upsert",
agent: {
...snapshot("agent", "idle"),
title: "stale live",
updatedAt: "2026-07-12T11:00:00.000Z",
},
project: staleProject,
},
],
});
expect({
title: result.entries[0]?.agent.title,
status: result.entries[0]?.agent.status,
projectName: result.entries[0]?.project.projectName,
stopped: result.stoppedRunningAgentIds,
}).toEqual({ title: "newer page", status: "running", projectName: "repo", stopped: [] });
});
it("clears a snapshot stop when a newer buffered upsert is running", () => {
const result = reconcileAgentDirectory({
previous: new Map([["agent", replica("agent", "running")]]),
snapshot: [entry("agent", "idle")],
deltas: [
{
kind: "upsert",
agent: {
...snapshot("agent", "running"),
updatedAt: "2026-07-12T11:00:00.000Z",
},
project: entry("agent", "running").project,
},
],
});
expect(result.entries[0]?.agent.status).toBe("running");
expect(result.stoppedRunningAgentIds).toEqual([]);
});
it("accepts usage from a stale buffered upsert without regressing metadata", () => {
const result = reconcileAgentDirectory({
previous: new Map(),
snapshot: [
{
...entry("agent", "idle"),
agent: {
...snapshot("agent", "idle"),
title: "newer page",
updatedAt: "2026-07-12T12:00:00.000Z",
lastUsage: { inputTokens: 10, outputTokens: 5 },
},
},
],
deltas: [
{
kind: "upsert",
agent: {
...snapshot("agent", "running"),
title: "stale live",
updatedAt: "2026-07-12T11:00:00.000Z",
lastUsage: { inputTokens: 20, outputTokens: 8 },
},
project: entry("agent", "idle").project,
},
],
});
expect({
title: result.entries[0]?.agent.title,
status: result.entries[0]?.agent.status,
usage: result.entries[0]?.agent.lastUsage,
}).toEqual({
title: "newer page",
status: "idle",
usage: { inputTokens: 20, outputTokens: 8 },
});
});
it("preserves usage when a stale buffered upsert omits it", () => {
const result = reconcileAgentDirectory({
previous: new Map(),
snapshot: [
{
...entry("agent", "idle"),
agent: {
...snapshot("agent", "idle"),
updatedAt: "2026-07-12T12:00:00.000Z",
lastUsage: { inputTokens: 10, outputTokens: 5 },
},
},
],
deltas: [
{
kind: "upsert",
agent: {
...snapshot("agent", "running"),
updatedAt: "2026-07-12T11:00:00.000Z",
},
project: entry("agent", "idle").project,
},
],
});
expect(result.entries[0]?.agent.lastUsage).toEqual({ inputTokens: 10, outputTokens: 5 });
});
});
@@ -0,0 +1,62 @@
import type { FetchAgentsEntry } from "@getpaseo/client/internal/daemon-client";
import type { Agent } from "@/stores/session-store";
import type { AgentDirectoryDelta } from "./agent-directory-sync";
import { acceptAgentDirectoryUpdate } from "./agent-directory-update-policy";
export function reconcileAgentDirectory(input: {
previous: ReadonlyMap<string, Agent>;
snapshot: FetchAgentsEntry[];
deltas: readonly AgentDirectoryDelta[];
}): { entries: FetchAgentsEntry[]; stoppedRunningAgentIds: string[] } {
const entries = new Map(input.snapshot.map((entry) => [entry.agent.id, entry]));
const statuses = new Map(Array.from(input.previous, ([id, agent]) => [id, agent.status]));
const stoppedRunningAgentIds = new Set<string>();
for (const entry of input.snapshot) {
if (statuses.get(entry.agent.id) === "running" && entry.agent.status !== "running") {
stoppedRunningAgentIds.add(entry.agent.id);
}
statuses.set(entry.agent.id, entry.agent.status);
}
for (const delta of input.deltas) {
if (delta.kind === "remove") {
entries.delete(delta.agentId);
statuses.delete(delta.agentId);
stoppedRunningAgentIds.delete(delta.agentId);
continue;
}
const previousEntry = entries.get(delta.agent.id);
const acceptedAgent = acceptAgentDirectoryUpdate(previousEntry?.agent, delta.agent);
if (acceptedAgent.status === "running") {
stoppedRunningAgentIds.delete(delta.agent.id);
} else if (statuses.get(delta.agent.id) === "running") {
stoppedRunningAgentIds.add(delta.agent.id);
}
statuses.set(delta.agent.id, acceptedAgent.status);
const previousProject = previousEntry?.project;
const acceptedProject =
acceptedAgent === delta.agent ? (delta.project ?? previousProject) : previousProject;
entries.set(delta.agent.id, {
agent: acceptedAgent,
project: acceptedProject ?? {
projectKey: delta.agent.cwd,
projectName: /[^/]+$/.exec(delta.agent.cwd)?.[0] ?? delta.agent.cwd,
checkout: {
cwd: delta.agent.cwd,
isGit: false,
currentBranch: null,
remoteUrl: null,
worktreeRoot: null,
isPaseoOwnedWorktree: false,
mainRepoRoot: null,
},
},
});
}
return {
entries: Array.from(entries.values()),
stoppedRunningAgentIds: Array.from(stoppedRunningAgentIds),
};
}
@@ -2,8 +2,12 @@ import { describe, expect, it } from "vitest";
import type { DaemonClient, FetchAgentsEntry } from "@getpaseo/client/internal/daemon-client";
import type { AgentSnapshotPayload } from "@getpaseo/protocol/messages";
import { PARENT_AGENT_ID_LABEL } from "@getpaseo/protocol/agent-labels";
import type { AgentPermissionRequest } from "@getpaseo/protocol/agent-types";
import { useSessionStore } from "@/stores/session-store";
import { replaceFetchedAgentDirectory } from "./agent-directory-sync";
import { normalizeAgentSnapshot } from "@/utils/agent-snapshots";
import { isAgentArchiving, setAgentArchiving } from "@/hooks/use-archive-agent";
import { queryClient } from "@/data/query-client";
import { applyAgentDirectoryDelta, replaceFetchedAgentDirectory } from "./agent-directory-sync";
function createAgentPayload(
input: Partial<Omit<AgentSnapshotPayload, "labels">> & {
@@ -56,7 +60,28 @@ function createEntry(agent: AgentSnapshotPayload): FetchAgentsEntry {
};
}
function permission(id: string): AgentPermissionRequest {
return { id, provider: "codex", name: id, kind: "tool", title: id };
}
describe("replaceFetchedAgentDirectory", () => {
it("preserves timeline initialization while replacing directory state", () => {
const serverId = "server-initializing";
const store = useSessionStore.getState();
store.initializeSession(serverId, null as unknown as DaemonClient);
store.setInitializingAgents(serverId, new Map([["agent", true]]));
replaceFetchedAgentDirectory({
serverId,
entries: [createEntry(createAgentPayload({ id: "agent" }))],
});
expect(useSessionStore.getState().sessions[serverId]?.initializingAgents.get("agent")).toBe(
true,
);
store.clearSession(serverId);
});
it("re-derives parentAgentId every time an agent snapshot is ingested", () => {
const serverId = "server-1";
const store = useSessionStore.getState();
@@ -92,4 +117,142 @@ describe("replaceFetchedAgentDirectory", () => {
store.clearSession(serverId);
});
it("removes every replica-owned artifact for a removed agent", () => {
const serverId = "server-removal";
const agentId = "removed-agent";
const store = useSessionStore.getState();
store.initializeSession(serverId, null as unknown as DaemonClient);
const agent = {
...normalizeAgentSnapshot(createAgentPayload({ id: agentId }), serverId),
projectPlacement: null,
};
store.setAgents(serverId, new Map([[agentId, agent]]));
store.setAgentDetails(serverId, new Map([[agentId, agent]]));
store.setQueuedMessages(
serverId,
new Map([[agentId, [{ id: "queued", text: "next", attachments: [] }]]]),
);
store.setAgentTimelineCursor(
serverId,
new Map([[agentId, { epoch: "epoch", startSeq: 1, endSeq: 2 }]]),
);
store.setPendingPermissions(
serverId,
new Map([["permission", { key: "permission", agentId, request: null as never }]]),
);
store.setInitializingAgents(serverId, new Map([[agentId, true]]));
setAgentArchiving({ queryClient, serverId, agentId, isArchiving: true });
applyAgentDirectoryDelta({ serverId, delta: { kind: "remove", agentId } });
const session = useSessionStore.getState().sessions[serverId];
expect({
agents: session?.agents.has(agentId),
details: session?.agentDetails.has(agentId),
queued: session?.queuedMessages.has(agentId),
cursor: session?.agentTimelineCursor.has(agentId),
permissions: session?.pendingPermissions.size,
initializing: session?.initializingAgents.has(agentId),
archivePending: isAgentArchiving({ queryClient, serverId, agentId }),
}).toEqual({
agents: false,
details: false,
queued: false,
cursor: false,
permissions: 0,
initializing: false,
archivePending: false,
});
store.clearSession(serverId);
});
it("keeps newer metadata while accepting usage-only updates and legacy workspace ownership", () => {
const serverId = "server-usage";
const agentId = "usage-agent";
const store = useSessionStore.getState();
store.initializeSession(serverId, null as unknown as DaemonClient);
store.setWorkspaces(
serverId,
new Map([
[
"legacy-workspace",
{
id: "legacy-workspace",
projectId: "project",
projectDisplayName: "Project",
projectRootPath: "/repo",
workspaceDirectory: "/repo",
projectKind: "git",
workspaceKind: "worktree",
name: "repo",
status: "done",
statusEnteredAt: null,
archivingAt: null,
diffStat: null,
scripts: [],
},
],
]),
);
const current = createAgentPayload({
id: agentId,
title: "current",
status: "running",
updatedAt: "2026-07-12T11:00:00.000Z",
lastUsage: { inputTokens: 10, outputTokens: 5 },
pendingPermissions: [permission("current-permission")],
});
applyAgentDirectoryDelta({
serverId,
delta: { kind: "upsert", agent: current, project: createEntry(current).project },
});
store.flushAgentLastActivity();
setAgentArchiving({ queryClient, serverId, agentId, isArchiving: true });
const staleResult = applyAgentDirectoryDelta({
serverId,
delta: {
kind: "upsert",
agent: {
...current,
title: "stale",
status: "idle",
updatedAt: "2026-07-12T10:00:00.000Z",
lastUsage: { inputTokens: 20, outputTokens: 8 },
pendingPermissions: [permission("stale-permission")],
archivedAt: "2026-07-12T10:00:00.000Z",
},
project: createEntry(current).project,
},
});
store.flushAgentLastActivity();
const state = useSessionStore.getState();
const agent = state.sessions[serverId]?.agents.get(agentId);
expect({
title: agent?.title,
status: agent?.status,
usage: agent?.lastUsage,
workspaceId: agent?.workspaceId,
stoppedRunning: staleResult.stoppedRunning,
permissions: Array.from(state.sessions[serverId]?.pendingPermissions.values() ?? []).map(
({ request }) => request.id,
),
archivePending: isAgentArchiving({ queryClient, serverId, agentId }),
activity: state.agentLastActivity.get(agentId)?.toISOString(),
}).toEqual({
title: "current",
status: "running",
usage: { inputTokens: 20, outputTokens: 8 },
workspaceId: "legacy-workspace",
stoppedRunning: false,
permissions: ["current-permission"],
archivePending: true,
activity: "2026-07-12T11:00:00.000Z",
});
store.clearSession(serverId);
});
});
+130 -1
View File
@@ -2,8 +2,132 @@ import type { FetchAgentsEntry } from "@getpaseo/client/internal/daemon-client";
import { type Agent, useSessionStore } from "@/stores/session-store";
import { derivePendingPermissionKey, normalizeAgentSnapshot } from "@/utils/agent-snapshots";
import { resolveProjectPlacement } from "@/utils/project-placement";
import type { SessionOutboundMessage } from "@getpaseo/protocol/messages";
import { clearArchiveAgentPending } from "@/hooks/use-archive-agent";
import { queryClient } from "@/data/query-client";
import { acceptAgentDirectoryUpdate } from "@/utils/agent-directory-update-policy";
import { buildDraftStoreKey } from "@/stores/draft-keys";
import { useDraftStore } from "@/stores/draft-store";
import { getInitDeferred, getInitKey, rejectInitDeferred } from "@/utils/agent-initialization";
type AgentDirectoryFetchEntry = FetchAgentsEntry;
export type AgentDirectoryDelta = Extract<
SessionOutboundMessage,
{ type: "agent_update" }
>["payload"];
export function applyAgentDirectoryDelta(input: { serverId: string; delta: AgentDirectoryDelta }): {
agentId: string;
stoppedRunning: boolean;
} {
if (input.delta.kind === "remove") {
removeAgentDirectoryReplica(input.serverId, input.delta.agentId);
return { agentId: input.delta.agentId, stoppedRunning: false };
}
return upsertAgentDirectoryReplica(input.serverId, input.delta);
}
type AgentUpsertDelta = Extract<AgentDirectoryDelta, { kind: "upsert" }>;
function upsertAgentDirectoryReplica(
serverId: string,
delta: AgentUpsertDelta,
): { agentId: string; stoppedRunning: boolean } {
const normalized = normalizeAgentSnapshot(delta.agent, serverId);
const session = useSessionStore.getState().sessions[serverId];
const previousAgent =
session?.agents.get(normalized.id) ?? session?.agentDetails.get(normalized.id);
const legacyWorkspaceId =
previousAgent?.workspaceId ??
Array.from(session?.workspaces.values() ?? []).find(
(workspace) =>
session?.serverInfo?.features?.workspaceMultiplicity !== true &&
workspace.workspaceDirectory === normalized.cwd,
)?.id;
const agent: Agent = {
...normalized,
workspaceId: normalized.workspaceId ?? legacyWorkspaceId,
projectPlacement:
resolveProjectPlacement({ projectPlacement: delta.project, cwd: normalized.cwd }) ??
previousAgent?.projectPlacement,
};
const acceptedAgent = upsertAgentReplica(serverId, agent);
if (acceptedAgent.archivedAt) {
clearArchiveAgentPending({ queryClient, serverId, agentId: acceptedAgent.id });
}
replaceAgentPendingPermissions(serverId, acceptedAgent);
useSessionStore.getState().setAgentLastActivity(acceptedAgent.id, acceptedAgent.lastActivityAt);
return {
agentId: acceptedAgent.id,
stoppedRunning: previousAgent?.status === "running" && acceptedAgent.status !== "running",
};
}
export function upsertAgentReplica(serverId: string, agent: Agent): Agent {
let acceptedAgent = agent;
useSessionStore.getState().setAgents(serverId, (current) => {
const currentAgent = current.get(agent.id);
acceptedAgent = acceptAgentDirectoryUpdate(currentAgent, agent);
if (acceptedAgent === currentAgent) return current;
const next = new Map(current);
next.set(agent.id, acceptedAgent);
return next;
});
return acceptedAgent;
}
export function replaceAgentPendingPermissions(serverId: string, agent: Agent): void {
const pendingPermissions = new Map(
useSessionStore.getState().sessions[serverId]?.pendingPermissions,
);
for (const [key, pending] of pendingPermissions) {
if (pending.agentId === agent.id) pendingPermissions.delete(key);
}
for (const request of agent.pendingPermissions) {
const key = derivePendingPermissionKey(agent.id, request);
pendingPermissions.set(key, { key, agentId: agent.id, request });
}
useSessionStore.getState().setPendingPermissions(serverId, pendingPermissions);
}
export function removeAgentDirectoryReplica(serverId: string, agentId: string): void {
const store = useSessionStore.getState();
clearArchiveAgentPending({ queryClient, serverId, agentId });
const removeKey = <T>(current: Map<string, T>): Map<string, T> => {
if (!current.has(agentId)) return current;
const next = new Map(current);
next.delete(agentId);
return next;
};
store.setAgents(serverId, removeKey);
store.setAgentDetails(serverId, removeKey);
store.setQueuedMessages(serverId, removeKey);
store.setAgentTimelineCursor(serverId, removeKey);
store.setInitializingAgents(serverId, removeKey);
store.setPendingPermissions(serverId, (current) => {
const next = new Map(current);
for (const [key, pending] of next) {
if (pending.agentId === agentId) next.delete(key);
}
return next.size === current.size ? current : next;
});
store.setAgentAuthoritativeHistoryApplied(serverId, agentId, false);
store.setAgentStreamTail(serverId, removeKey);
store.clearAgentStreamHead(serverId, agentId);
useSessionStore.setState((state) => {
if (!state.agentLastActivity.has(agentId)) return state;
const agentLastActivity = new Map(state.agentLastActivity);
agentLastActivity.delete(agentId);
return { ...state, agentLastActivity };
});
useDraftStore.getState().clearDraftInput({
draftKey: buildDraftStoreKey({ serverId, agentId }),
});
const initKey = getInitKey(serverId, agentId);
if (getInitDeferred(initKey)) {
rejectInitDeferred(initKey, new Error("Agent was removed during initialization"));
}
}
interface PendingPermissionEntry {
key: string;
@@ -49,6 +173,12 @@ export function replaceFetchedAgentDirectory(input: {
const { agents: fetchedAgents, pendingPermissions } = buildAgentDirectoryState(input);
const store = useSessionStore.getState();
for (const agent of fetchedAgents.values()) {
if (agent.archivedAt) {
clearArchiveAgentPending({ queryClient, serverId: input.serverId, agentId: agent.id });
}
}
store.setAgents(input.serverId, fetchedAgents);
store.setAgentDetails(input.serverId, (prev) => {
let next: Map<string, Agent> | null = null;
@@ -69,7 +199,6 @@ export function replaceFetchedAgentDirectory(input: {
store.setAgentLastActivityBatch(lastActivityByAgentId);
store.setPendingPermissions(input.serverId, new Map(pendingPermissions));
store.setInitializingAgents(input.serverId, new Map());
store.setHasHydratedAgents(input.serverId, true);
return { agents: fetchedAgents };
}
@@ -0,0 +1,21 @@
import equal from "fast-deep-equal";
import type { AgentUsage } from "@getpaseo/protocol/agent-types";
interface AgentUpdateValue {
updatedAt: Date | string;
lastUsage?: AgentUsage;
}
function timestamp(value: Date | string): number {
return value instanceof Date ? value.getTime() : Date.parse(value);
}
export function acceptAgentDirectoryUpdate<T extends AgentUpdateValue>(
current: T | undefined,
incoming: T,
): T {
if (!current || timestamp(incoming.updatedAt) >= timestamp(current.updatedAt)) return incoming;
if (incoming.lastUsage === undefined) return current;
if (equal(incoming.lastUsage, current.lastUsage)) return current;
return { ...current, lastUsage: incoming.lastUsage };
}