diff --git a/apps/sim/lib/mothership/tools/client/native-files.test.ts b/apps/sim/lib/mothership/tools/client/native-files.test.ts index 7e7d04d2184..f275c5e1b90 100644 --- a/apps/sim/lib/mothership/tools/client/native-files.test.ts +++ b/apps/sim/lib/mothership/tools/client/native-files.test.ts @@ -125,3 +125,28 @@ it('returns local visual observations through the existing completion path', asy expect(mocks.complete).toHaveBeenCalledWith('tool', 'success', expect.any(String), data) expect(mocks.upload).not.toHaveBeenCalled() }) + +it('an import the user stopped leaves its outcome to Stop instead of reporting a failure', async () => { + const stop = new AbortController() + mocks.invoke.mockResolvedValueOnce({ ok: true, data: manifest }) + mocks.upload.mockImplementationOnce(async () => { + stop.abort('user_stop:client_stopGeneration') + throw new DOMException('The operation was aborted.', 'AbortError') + }) + await executeNativeFileTool('tool', 'import_local_files', stop.signal) + expect(mocks.complete).not.toHaveBeenCalled() + expect(mocks.exit).not.toHaveBeenCalled() +}) + +it('a read the user stopped while the desktop was reading reports nothing', async () => { + const stop = new AbortController() + mocks.invoke.mockImplementationOnce(async () => { + stop.abort('user_stop:client_stopGeneration') + return { + ok: true, + data: { kind: 'read', path: '/notes.txt', representation: 'text', text: 'notes' }, + } + }) + await executeNativeFileTool('tool', 'read_local_file', stop.signal) + expect(mocks.complete).not.toHaveBeenCalled() +}) diff --git a/apps/sim/lib/mothership/tools/client/native-files.ts b/apps/sim/lib/mothership/tools/client/native-files.ts index b1d4eff830b..884aeca5a6d 100644 --- a/apps/sim/lib/mothership/tools/client/native-files.ts +++ b/apps/sim/lib/mothership/tools/client/native-files.ts @@ -145,6 +145,9 @@ export async function executeNativeFileTool( response.data.kind === 'manifest' ? localFileImportCompletion(await importNativeFiles(toolCallId, response.data, signal)) : localFileReadCompletion(response) + // Cancelled by the user's Stop or by signing out: whoever cancelled it settles the call, as for + // browser actions and granted-folder reads. A failure reported here would race Stop's record. + if (signal?.aborted) return await reportClientToolCompletion( toolCallId, completion.status, @@ -153,6 +156,7 @@ export async function executeNativeFileTool( ) settled = true } catch (error) { + if (signal?.aborted) return await reportClientToolCompletion( toolCallId, ASYNC_TOOL_CONFIRMATION_STATUS.error,