Skip to content

Commit d8b1a41

Browse files
committed
fix(mcp): tighten channel bridge pending cleanup
1 parent 5567d9c commit d8b1a41

3 files changed

Lines changed: 31 additions & 26 deletions

File tree

src/mcp/channel-bridge.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type BridgeInternals = {
2323
event: string;
2424
payload?: Record<string, unknown>;
2525
}) => Promise<void>;
26+
listPendingApprovals: () => unknown[];
2627
close: () => Promise<void>;
2728
};
2829

@@ -126,6 +127,29 @@ describe("OpenClawChannelBridge — pendingClaudePermissions / pendingApprovals
126127
}
127128
});
128129

130+
test("listPendingApprovals filters expired entries before the next sweep tick", async () => {
131+
const bridge = makeBridge();
132+
try {
133+
await bridge.handleGatewayEvent({
134+
event: "exec.approval.requested",
135+
payload: {
136+
id: "approval-early-expiry",
137+
createdAtMs: 0,
138+
expiresAtMs: ONE_MINUTE_MS,
139+
},
140+
});
141+
expect(bridge.pendingApprovals.size).toBe(1);
142+
143+
vi.advanceTimersByTime(2 * ONE_MINUTE_MS);
144+
145+
expect(bridge.listPendingApprovals()).toHaveLength(0);
146+
expect(bridge.pendingApprovals.size).toBe(0);
147+
expect(bridge.pendingSweepInterval).toBeNull();
148+
} finally {
149+
await bridge.close();
150+
}
151+
});
152+
129153
test("close() clears both pending maps, stops the sweeper interval, and leaves no scheduled timers", async () => {
130154
const bridge = makeBridge();
131155
await bridge.handleClaudePermissionRequest({

src/mcp/channel-bridge.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import type {
1414
ApprovalKind,
1515
ChatHistoryResult,
1616
ClaudeChannelMode,
17-
ClaudePermissionRequest,
1817
ConversationDescriptor,
1918
PendingApproval,
2019
QueueEvent,
@@ -31,11 +30,6 @@ type PendingWaiter = {
3130
timeout: NodeJS.Timeout | null;
3231
};
3332

34-
type PendingClaudePermissionEntry = {
35-
request: ClaudePermissionRequest;
36-
createdAtMs: number;
37-
};
38-
3933
type PendingApprovalEntry = {
4034
approval: PendingApproval;
4135
trackedAtMs: number;
@@ -58,7 +52,7 @@ export class OpenClawChannelBridge {
5852
private readonly claudeChannelMode: ClaudeChannelMode;
5953
private readonly queue: QueueEvent[] = [];
6054
private readonly pendingWaiters = new Set<PendingWaiter>();
61-
private readonly pendingClaudePermissions = new Map<string, PendingClaudePermissionEntry>();
55+
private readonly pendingClaudePermissions = new Map<string, number>();
6256
private readonly pendingApprovals = new Map<string, PendingApprovalEntry>();
6357
private pendingSweepInterval: NodeJS.Timeout | null = null;
6458
private server: McpServer | null = null;
@@ -268,6 +262,7 @@ export class OpenClawChannelBridge {
268262
}
269263

270264
listPendingApprovals(): PendingApproval[] {
265+
this.sweepPendingExpired();
271266
return [...this.pendingApprovals.values()]
272267
.map((entry) => entry.approval)
273268
.toSorted((a, b) => {
@@ -330,14 +325,7 @@ export class OpenClawChannelBridge {
330325
if (this.closed) {
331326
return;
332327
}
333-
this.pendingClaudePermissions.set(params.requestId, {
334-
request: {
335-
toolName: params.toolName,
336-
description: params.description,
337-
inputPreview: params.inputPreview,
338-
},
339-
createdAtMs: Date.now(),
340-
});
328+
this.pendingClaudePermissions.set(params.requestId, Date.now());
341329
this.ensurePendingSweeper();
342330
this.enqueue({
343331
cursor: this.nextCursor(),
@@ -459,8 +447,8 @@ export class OpenClawChannelBridge {
459447
}
460448

461449
private sweepPendingExpired(now: number = Date.now()): void {
462-
for (const [id, entry] of this.pendingClaudePermissions) {
463-
if (now - entry.createdAtMs >= PENDING_CLAUDE_PERMISSION_TTL_MS) {
450+
for (const [id, createdAtMs] of this.pendingClaudePermissions) {
451+
if (now - createdAtMs >= PENDING_CLAUDE_PERMISSION_TTL_MS) {
464452
this.pendingClaudePermissions.delete(id);
465453
}
466454
}

src/mcp/channel-server.test.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -489,17 +489,10 @@ describe("openclaw channel mcp server", () => {
489489

490490
(
491491
bridge as unknown as {
492-
pendingClaudePermissions: Map<string, Record<string, unknown>>;
492+
pendingClaudePermissions: Map<string, number>;
493493
server: { server: { notification: ReturnType<typeof vi.fn> } };
494494
}
495-
).pendingClaudePermissions.set("abcde", {
496-
request: {
497-
toolName: "Bash",
498-
description: "run npm test",
499-
inputPreview: '{"cmd":"npm test"}',
500-
},
501-
createdAtMs: Date.now(),
502-
});
495+
).pendingClaudePermissions.set("abcde", Date.now());
503496
(
504497
bridge as unknown as {
505498
server: { server: { notification: ReturnType<typeof vi.fn> } };

0 commit comments

Comments
 (0)