Stabilize agent history sync after app backgrounding

This commit is contained in:
Mohamed Boudra
2026-02-09 15:44:29 +07:00
parent 8403ece4ed
commit 4fa8859d14
8 changed files with 198 additions and 105 deletions
@@ -772,6 +772,7 @@ export class DaemonClient {
focusedAgentId: string | null;
lastActivityAt: string;
appVisible: boolean;
appVisibilityChangedAt?: string;
}): void {
this.sendSessionMessage({
type: "client_heartbeat",
@@ -779,6 +780,7 @@ export class DaemonClient {
focusedAgentId: params.focusedAgentId,
lastActivityAt: params.lastActivityAt,
appVisible: params.appVisible,
appVisibilityChangedAt: params.appVisibilityChangedAt,
});
}
+15 -4
View File
@@ -375,7 +375,9 @@ export class Session {
focusedAgentId: string | null;
lastActivityAt: Date;
appVisible: boolean;
appVisibilityChangedAt: Date;
} | null = null;
private readonly MOBILE_BACKGROUND_STREAM_GRACE_MS = 60_000;
private readonly terminalManager: TerminalManager | null;
private terminalSubscriptions: Map<string, () => void> = new Map();
private readonly voiceAgentMcpStdio: VoiceMcpStdioConfig | null;
@@ -474,6 +476,7 @@ export class Session {
focusedAgentId: string | null;
lastActivityAt: Date;
appVisible: boolean;
appVisibilityChangedAt: Date;
} | null {
return this.clientActivity;
}
@@ -646,21 +649,24 @@ export class Session {
}
// Reduce bandwidth/CPU on mobile: only forward high-frequency agent stream events
// while the app is visible and the user is focused on that agent.
// for the focused agent, with a short grace window while backgrounded.
//
// History catch-up is handled via explicit `initialize_agent_request` which emits a
// batched `agent_stream_snapshot`.
const activity = this.clientActivity;
if (activity?.deviceType === "mobile") {
if (!activity.appVisible) {
return;
}
if (!activity.focusedAgentId) {
return;
}
if (activity.focusedAgentId !== event.agentId) {
return;
}
if (!activity.appVisible) {
const hiddenForMs = Date.now() - activity.appVisibilityChangedAt.getTime();
if (hiddenForMs >= this.MOBILE_BACKGROUND_STREAM_GRACE_MS) {
return;
}
}
}
const serializedEvent = serializeAgentStreamEvent(event.event);
@@ -2643,12 +2649,17 @@ export class Session {
focusedAgentId: string | null;
lastActivityAt: string;
appVisible: boolean;
appVisibilityChangedAt?: string;
}): void {
const appVisibilityChangedAt = msg.appVisibilityChangedAt
? new Date(msg.appVisibilityChangedAt)
: new Date(msg.lastActivityAt);
this.clientActivity = {
deviceType: msg.deviceType,
focusedAgentId: msg.focusedAgentId,
lastActivityAt: new Date(msg.lastActivityAt),
appVisible: msg.appVisible,
appVisibilityChangedAt,
};
}
+1
View File
@@ -864,6 +864,7 @@ export const ClientHeartbeatMessageSchema = z.object({
focusedAgentId: z.string().nullable(),
lastActivityAt: z.string(),
appVisible: z.boolean(),
appVisibilityChangedAt: z.string().optional(),
});
export const PingMessageSchema = z.object({