Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit 71a9410

Browse files
Preserve current cloud permission retries
Generated-By: PostHog Code Task-Id: 40c57a59-b4e1-4760-8e56-ecd03e9c2f0f
1 parent 466df6c commit 71a9410

2 files changed

Lines changed: 75 additions & 7 deletions

File tree

‎packages/core/src/sessions/cloudTaskUpdateNotifications.test.ts‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ describe("cloud task update notifications", () => {
354354
toolCall: { toolCallId: "t1", title: "Run command", kind: "execute" },
355355
options: [],
356356
});
357+
const session = harness.sessions[RUN_ID];
358+
const permission = session?.pendingPermissions.get("t1");
359+
if (permission) permission.receivedAt = Date.now() - 1_000;
360+
if (session) session.pausedDurationMs = 25;
357361
harness.sendCommand.mockResolvedValueOnce({
358362
success: false,
359363
error: "sandbox rejected response",
@@ -362,6 +366,7 @@ describe("cloud task update notifications", () => {
362366
await harness.service.respondToPermission(TASK_ID, "t1", "allow");
363367

364368
expect(harness.sessions[RUN_ID]?.pendingPermissions.has("t1")).toBe(true);
369+
expect(harness.sessions[RUN_ID]?.pausedDurationMs).toBe(25);
365370

366371
await harness.service.respondToPermission(TASK_ID, "t1", "allow");
367372
expect(harness.sendCommand).toHaveBeenLastCalledWith(
@@ -371,4 +376,48 @@ describe("cloud task update notifications", () => {
371376
}),
372377
);
373378
});
379+
380+
it("does not restore a stale permission over a newer request", async () => {
381+
const harness = createHarness();
382+
harness.sendUpdate({
383+
taskId: TASK_ID,
384+
runId: RUN_ID,
385+
kind: "permission_request",
386+
requestId: "r1",
387+
toolCall: { toolCallId: "t1", title: "Run command", kind: "execute" },
388+
options: [],
389+
});
390+
let rejectResponse: (error: Error) => void = () => undefined;
391+
harness.sendCommand.mockImplementationOnce(
392+
() =>
393+
new Promise((_, reject) => {
394+
rejectResponse = reject;
395+
}),
396+
);
397+
398+
const response = harness.service.respondToPermission(
399+
TASK_ID,
400+
"t1",
401+
"allow",
402+
);
403+
await vi.waitFor(() => expect(harness.sendCommand).toHaveBeenCalled());
404+
harness.sendUpdate({
405+
taskId: TASK_ID,
406+
runId: RUN_ID,
407+
kind: "permission_request",
408+
requestId: "r2",
409+
toolCall: { toolCallId: "t1", title: "Run command", kind: "execute" },
410+
options: [],
411+
});
412+
rejectResponse(new Error("failed"));
413+
await response;
414+
415+
await harness.service.respondToPermission(TASK_ID, "t1", "allow");
416+
expect(harness.sendCommand).toHaveBeenLastCalledWith(
417+
expect.objectContaining({
418+
method: "permission_response",
419+
params: { requestId: "r2", optionId: "allow" },
420+
}),
421+
);
422+
});
374423
});

‎packages/core/src/sessions/sessionService.ts‎

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4763,19 +4763,28 @@ export class SessionService {
47634763

47644764
// --- Permissions ---
47654765

4766-
private resolvePermission(session: AgentSession, toolCallId: string): void {
4766+
private resolvePermission(
4767+
session: AgentSession,
4768+
toolCallId: string,
4769+
): {
4770+
previousPausedDurationMs: number;
4771+
resolvedPausedDurationMs: number;
4772+
} {
47674773
const permission = session.pendingPermissions.get(toolCallId);
47684774
const newPermissions = new Map(session.pendingPermissions);
47694775
newPermissions.delete(toolCallId);
47704776
this.d.store.setPendingPermissions(session.taskRunId, newPermissions);
47714777

4778+
const previousPausedDurationMs = session.pausedDurationMs ?? 0;
4779+
const resolvedPausedDurationMs = permission?.receivedAt
4780+
? previousPausedDurationMs + (Date.now() - permission.receivedAt)
4781+
: previousPausedDurationMs;
47724782
if (permission?.receivedAt) {
47734783
this.d.store.updateSession(session.taskRunId, {
4774-
pausedDurationMs:
4775-
(session.pausedDurationMs ?? 0) +
4776-
(Date.now() - permission.receivedAt),
4784+
pausedDurationMs: resolvedPausedDurationMs,
47774785
});
47784786
}
4787+
return { previousPausedDurationMs, resolvedPausedDurationMs };
47794788
}
47804789

47814790
/**
@@ -4801,7 +4810,7 @@ export class SessionService {
48014810
});
48024811

48034812
const cloudRequestId = this.cloudPermissionRequestIds.get(toolCallId);
4804-
this.resolvePermission(session, toolCallId);
4813+
const permissionResolution = this.resolvePermission(session, toolCallId);
48054814

48064815
try {
48074816
const refreshedCloudStatus =
@@ -4877,15 +4886,25 @@ export class SessionService {
48774886
});
48784887
} catch (error) {
48794888
const latestSession = this.d.store.getSessionByTaskId(taskId);
4880-
if (permission && latestSession) {
4889+
if (
4890+
latestSession?.pausedDurationMs ===
4891+
permissionResolution.resolvedPausedDurationMs
4892+
) {
4893+
this.d.store.updateSession(latestSession.taskRunId, {
4894+
pausedDurationMs: permissionResolution.previousPausedDurationMs,
4895+
});
4896+
}
4897+
const currentCloudRequestId =
4898+
this.cloudPermissionRequestIds.get(toolCallId);
4899+
if (permission && latestSession && !currentCloudRequestId) {
48814900
const restoredPermissions = new Map(latestSession.pendingPermissions);
48824901
restoredPermissions.set(toolCallId, permission);
48834902
this.d.store.setPendingPermissions(
48844903
latestSession.taskRunId,
48854904
restoredPermissions,
48864905
);
48874906
}
4888-
if (cloudRequestId) {
4907+
if (cloudRequestId && !currentCloudRequestId) {
48894908
this.cloudPermissionRequestIds.set(toolCallId, cloudRequestId);
48904909
}
48914910
this.d.log.error("Failed to respond to permission", {

0 commit comments

Comments
 (0)