Push-driven checkout diff subscriptions for changes sidebar (#24)

* Implement push-driven checkout diff subscriptions

* Remove legacy checkout diff polling RPC

* Fix checkout diff watcher coverage and fallback refresh
This commit is contained in:
Mohamed Boudra
2026-02-11 14:11:00 +08:00
committed by GitHub
parent 8e5ba622ee
commit a6f60091f2
9 changed files with 1066 additions and 67 deletions
+113 -20
View File
@@ -19,7 +19,6 @@ import type {
GitSetupOptions,
HighlightedDiffResponse,
CheckoutStatusResponse,
CheckoutDiffResponse,
CheckoutCommitResponse,
CheckoutMergeResponse,
CheckoutMergeFromBaseResponse,
@@ -182,7 +181,11 @@ export type CreateAgentRequestOptions = {
type GitDiffPayload = GitDiffResponse["payload"];
type HighlightedDiffPayload = HighlightedDiffResponse["payload"];
type CheckoutStatusPayload = CheckoutStatusResponse["payload"];
type CheckoutDiffPayload = CheckoutDiffResponse["payload"];
type SubscribeCheckoutDiffPayload = Extract<
SessionOutboundMessage,
{ type: "subscribe_checkout_diff_response" }
>["payload"];
type CheckoutDiffPayload = Omit<SubscribeCheckoutDiffPayload, "subscriptionId">;
type CheckoutCommitPayload = CheckoutCommitResponse["payload"];
type CheckoutMergePayload = CheckoutMergeResponse["payload"];
type CheckoutMergeFromBasePayload = CheckoutMergeFromBaseResponse["payload"];
@@ -296,6 +299,10 @@ export class DaemonClient {
string,
{ labels?: Record<string, string>; agentId?: string } | undefined
>();
private checkoutDiffSubscriptions = new Map<
string,
{ cwd: string; compare: { mode: "uncommitted" | "base"; baseRef?: string } }
>();
private logger: Logger;
private pendingSendQueue: PendingSend[] = [];
private relayClientId: string | null = null;
@@ -401,6 +408,7 @@ export class DaemonClient {
this.reconnectAttempt = 0;
this.updateConnectionState({ status: "connected" });
this.resubscribeAgentUpdates();
this.resubscribeCheckoutDiffSubscriptions();
this.flushPendingSendQueue();
this.resolveConnect();
}),
@@ -951,6 +959,22 @@ export class DaemonClient {
}
}
private resubscribeCheckoutDiffSubscriptions(): void {
if (this.checkoutDiffSubscriptions.size === 0) {
return;
}
for (const [subscriptionId, subscription] of this.checkoutDiffSubscriptions) {
const message = SessionInboundMessageSchema.parse({
type: "subscribe_checkout_diff_request",
subscriptionId,
cwd: subscription.cwd,
compare: subscription.compare,
requestId: this.createRequestId(),
});
this.sendSessionMessage(message);
}
}
// ============================================================================
// Agent Lifecycle
// ============================================================================
@@ -1521,32 +1545,101 @@ export class DaemonClient {
return responsePromise;
}
private normalizeCheckoutDiffCompare(
compare: { mode: "uncommitted" | "base"; baseRef?: string }
): { mode: "uncommitted" | "base"; baseRef?: string } {
if (compare.mode === "uncommitted") {
return { mode: "uncommitted" };
}
const trimmedBaseRef = compare.baseRef?.trim();
if (!trimmedBaseRef) {
return { mode: "base" };
}
return { mode: "base", baseRef: trimmedBaseRef };
}
async getCheckoutDiff(
cwd: string,
compare: { mode: "uncommitted" | "base"; baseRef?: string },
requestId?: string
): Promise<CheckoutDiffPayload> {
const resolvedRequestId = this.createRequestId(requestId);
const message = SessionInboundMessageSchema.parse({
type: "checkout_diff_request",
const oneShotSubscriptionId = `oneshot-checkout-diff:${crypto.randomUUID()}`;
try {
const payload = await this.subscribeCheckoutDiff(cwd, compare, {
subscriptionId: oneShotSubscriptionId,
requestId,
});
return {
cwd: payload.cwd,
files: payload.files,
error: payload.error,
requestId: payload.requestId,
};
} finally {
try {
this.unsubscribeCheckoutDiff(oneShotSubscriptionId);
} catch {
// Ignore disconnect races during one-shot cleanup.
}
}
}
async subscribeCheckoutDiff(
cwd: string,
compare: { mode: "uncommitted" | "base"; baseRef?: string },
options?: { subscriptionId?: string; requestId?: string }
): Promise<SubscribeCheckoutDiffPayload> {
const subscriptionId = options?.subscriptionId ?? crypto.randomUUID();
const normalizedCompare = this.normalizeCheckoutDiffCompare(compare);
const previousSubscription = this.checkoutDiffSubscriptions.get(subscriptionId) ?? null;
this.checkoutDiffSubscriptions.set(subscriptionId, {
cwd,
compare,
compare: normalizedCompare,
});
const resolvedRequestId = this.createRequestId(options?.requestId);
const message = SessionInboundMessageSchema.parse({
type: "subscribe_checkout_diff_request",
subscriptionId,
cwd,
compare: normalizedCompare,
requestId: resolvedRequestId,
});
return this.sendRequest({
requestId: resolvedRequestId,
message,
timeout: 60000,
options: { skipQueue: true },
select: (msg) => {
if (msg.type !== "checkout_diff_response") {
return null;
}
if (msg.payload.requestId !== resolvedRequestId) {
return null;
}
return msg.payload;
},
try {
return await this.sendRequest({
requestId: resolvedRequestId,
message,
timeout: 60000,
options: { skipQueue: true },
select: (msg) => {
if (msg.type !== "subscribe_checkout_diff_response") {
return null;
}
if (msg.payload.requestId !== resolvedRequestId) {
return null;
}
if (msg.payload.subscriptionId !== subscriptionId) {
return null;
}
return msg.payload;
},
});
} catch (error) {
if (previousSubscription) {
this.checkoutDiffSubscriptions.set(subscriptionId, previousSubscription);
} else {
this.checkoutDiffSubscriptions.delete(subscriptionId);
}
throw error;
}
}
unsubscribeCheckoutDiff(subscriptionId: string): void {
this.checkoutDiffSubscriptions.delete(subscriptionId);
this.sendSessionMessage({
type: "unsubscribe_checkout_diff_request",
subscriptionId,
});
}