fix(session): restore sandbox-fs after agent-run quiesce

Agent runs quiesced stdio MCP extensions including sandbox-fs, but reconcile
skipped re-adding missing stdio extensions and treated absent listings as OK.
Preserve sandbox-fs across quiesce and re-add other required stdio extensions
on the next reconcile so generate_image and write_file stay available.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-08-07 15:49:04 +08:00
parent 49ff7c9bad
commit d523b9595d
4 changed files with 96 additions and 20 deletions
+9 -4
View File
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
import test from 'node:test';
import {
cancelSessionActiveRequest,
PRESERVED_STDIO_EXTENSIONS,
quiesceSessionStdioExtensions,
sessionStdioExtensionNames,
} from './session-runtime-lifecycle.mjs';
@@ -82,7 +83,7 @@ test('quiesceSessionStdioExtensions removes stdio children without deleting sess
const result = await quiesceSessionStdioExtensions(apiFetch, 'session-1');
assert.deepEqual(result, {
removed: ['sandbox-fs', 'tkmind-search'],
removed: ['tkmind-search'],
skipped: false,
});
assert.deepEqual(
@@ -90,23 +91,27 @@ test('quiesceSessionStdioExtensions removes stdio children without deleting sess
[
'/sessions/session-1/extensions',
'/agent/remove_extension',
'/agent/remove_extension',
],
);
assert.deepEqual(
calls.slice(1).map(({ init }) => JSON.parse(init.body)),
[
{ session_id: 'session-1', name: 'sandbox-fs' },
{ session_id: 'session-1', name: 'tkmind-search' },
],
);
assert.equal(PRESERVED_STDIO_EXTENSIONS.has('sandbox-fs'), true);
assert.equal(calls.some(({ pathname }) => pathname.includes('delete')), false);
});
test('quiesceSessionStdioExtensions fails when upstream removal is not acknowledged', async () => {
const apiFetch = async (pathname) => {
if (pathname.endsWith('/extensions')) {
return jsonResponse({ extensions: [{ name: 'sandbox-fs', type: 'stdio' }] });
return jsonResponse({
extensions: [
{ name: 'sandbox-fs', type: 'stdio' },
{ name: 'tkmind-search', type: 'stdio' },
],
});
}
return jsonResponse({ message: 'remove failed' }, { ok: false, status: 500 });
};