From 237fddc91f49080c0689c9b3e65f6d4b61f30f8c Mon Sep 17 00:00:00 2001
From: Twil3akine <156310846+Twil3akine@users.noreply.github.com>
Date: Thu, 15 Oct 2026 10:41:58 +0900
Subject: [PATCH] fix: recover interrupted approval-gated MCP actions
---
src/client/ConnectionActionCard.tsx | 28 ++++--
src/server/connection-routes.ts | 29 +++++-
src/server/connection-store.ts | 59 +++++++++---
tests/connection-card.test.tsx | 61 ++++++++++++-
tests/connections.test.ts | 137 ++++++++++++++++++++++++++++
5 files changed, 286 insertions(+), 28 deletions(-)
diff --git a/src/client/ConnectionActionCard.tsx b/src/client/ConnectionActionCard.tsx
index 3de99dc8..bbf3c275 100644
--- a/src/client/ConnectionActionCard.tsx
+++ b/src/client/ConnectionActionCard.tsx
@@ -9,7 +9,7 @@ import { api } from './api';
import { computerToolResult } from './ComputerToolCard';
type Receipt = {
approvalId: string | null;
- status: 'running' | 'done';
+ status: 'running' | 'interrupted' | 'done';
result: ConnectionActionResult | null;
};
const conversation = (threadId: string) =>
@@ -69,6 +69,7 @@ export function ConnectionActionCard({
const own = mismatch ? null : receipt;
// A receipt that is still running is checked until the server finishes.
const running = own?.status === 'running';
+ const interrupted = own?.status === 'interrupted';
useEffect(() => {
let active = true;
const load = () =>
@@ -110,6 +111,7 @@ export function ConnectionActionCard({
});
return;
}
+ if (!outcome) setReceipt({ approvalId, status: 'running', result: null });
// A previous approval may have run even if its response never arrived.
const value =
outcome ??
@@ -151,11 +153,13 @@ export function ConnectionActionCard({
? 'Declined'
: running
? 'Running'
- : finished
- ? 'Ended'
- : !ready
- ? 'Checking'
- : 'Needs your approval'}
+ : interrupted
+ ? 'Interrupted'
+ : finished
+ ? 'Ended'
+ : !ready
+ ? 'Checking'
+ : 'Needs your approval'}
@@ -218,7 +222,9 @@ export function ConnectionActionCard({
? 'Running…'
: outcome
? 'Continue conversation'
- : 'Approve & run'}
+ : interrupted
+ ? 'Retry & run'
+ : 'Approve & run'}
{!outcome && (