How to Pause and Resume GUIAgent Execution in UI-TARS Desktop
You can pause and resume GUIAgent execution in UI-TARS Desktop by invoking guiAgent.pause() and guiAgent.resume(), which manipulate an internal isPaused flag and a resumePromise that blocks the main run loop until explicitly resolved.
UI-TARS Desktop provides a robust GUIAgent that automates GUI interactions by capturing screenshots, invoking LLMs, and executing actions. When you need to temporarily halt automation without terminating the session entirely, the SDK offers built-in pause and resume functionality through the GUIAgent class in the @ui-tars/sdk package.
How Pause and Resume Works in GUIAgent
The GUIAgent class extends BaseGUIAgent and implements a cooperative pausing mechanism within its main execution loop. According to the source code in packages/ui-tars/sdk/src/GUIAgent.ts, the agent checks the isPaused flag on every iteration of the run() loop.
When isPaused is true, the agent emits a StatusEnum.PAUSE event and awaits a promise stored in resumePromise. This blocks further execution while maintaining the session state. Once resume() resolves this promise, the loop continues from where it left off.
Implementation Details
The Pause State Flag
The isPaused boolean property indicates whether the agent should halt its loop. In packages/ui-tars/sdk/src/GUIAgent.ts (lines 95-101), the pause() method sets this flag to true and initializes a new Promise assigned to resumePromise:
pause() {
this.isPaused = true;
this.resumePromise = new Promise<void>((resolve) => {
this.resolveResume = resolve;
});
}
The Resume Promise Mechanism
The resume() method resolves the pending promise and clears the pause state:
resume() {
this.resolveResume?.();
this.isPaused = false;
}
When the main loop encounters the paused state, it awaits this promise indefinitely:
if (this.isPaused && this.resumePromise) {
data.status = StatusEnum.PAUSE;
await onData?.({ data });
await this.resumePromise; // Blocks until resume() is called
data.status = StatusEnum.RUNNING;
await onData?.({ data });
}
Distinguishing Pause from Stop
UI-TARS Desktop distinguishes between temporary pauses and hard stops. The isStopped flag, set by GUIAgent.stop() (lines 111-113 in GUIAgent.ts), terminates the loop entirely rather than suspending it. Unlike pause(), stop() cannot be reversed without restarting the agent.
IPC Routes for UI Integration
The desktop application exposes pause and resume functionality through Electron IPC routes defined in apps/ui-tars/src/main/ipcRoutes/agent.ts. These routes allow the renderer process (UI) to control the main process agent instance via GUIAgentManager.
Pause execution:
pauseRun: t.procedure.input<void>().handle(async () => {
const guiAgent = GUIAgentManager.getInstance().getAgent();
if (guiAgent instanceof GUIAgent) {
guiAgent.pause(); // Triggers internal pause
store.setState({ thinking: false });
}
}),
Resume execution:
resumeRun: t.procedure.input<void>().handle(async () => {
const guiAgent = GUIAgentManager.getInstance().getAgent();
if (guiAgent instanceof GUIAgent) {
guiAgent.resume(); // Unblocks the loop
store.setState({ thinking: false });
}
}),
Code Examples
Programmatic Control
To pause and resume the agent from a custom extension or script:
import { GUIAgentManager, GUIAgent } from '@ui-tars/sdk';
import { Operator } from '@ui-tars/sdk/core';
// Assuming agent was previously created and registered
const agent = new GUIAgent<Operator>(config);
GUIAgentManager.getInstance().setAgent(agent);
// Temporarily halt execution
agent.pause();
// ... perform intermediate tasks ...
// Continue automation
agent.resume();
React Component Integration
Bind IPC calls to UI buttons in your React components:
import { useIpc } from '@ui-tars/electron-ipc/react';
const Toolbar = () => {
const ipc = useIpc();
return (
<div>
<button onClick={() => ipc.invoke('pauseRun')}>Pause</button>
<button onClick={() => ipc.invoke('resumeRun')}>Resume</button>
</div>
);
};
Monitoring Agent Status
Check the current execution state using the shared status enum:
import { useSelector } from 'react-redux';
import { StatusEnum } from '@ui-tars/shared/types';
const StatusBanner = () => {
const status = useSelector(state => state.status);
return <div>{status === StatusEnum.PAUSE ? 'Paused' : 'Running'}</div>;
};
Summary
- The GUIAgent class in
@ui-tars/sdkprovides native pause and resume capabilities through thepause()andresume()methods. - The implementation relies on an
isPausedflag and aresumePromisethat blocks the mainrun()loop until resolved. - IPC routes in
apps/ui-tars/src/main/ipcRoutes/agent.tsexpose these controls to the Electron frontend viapauseRunandresumeRuncommands. - StatusEnum.PAUSE allows UI components to reflect the current execution state.
- Unlike
stop(), pausing preserves the session state and allows continuation without restarting the agent.
Frequently Asked Questions
What happens to the LLM call when I pause GUIAgent?
The pause mechanism operates at the loop level in packages/ui-tars/sdk/src/GUIAgent.ts. If an LLM request is already in flight when pause() is called, that request completes, but the agent pauses before processing the next screenshot and action cycle. The resumePromise blocks at the start of the next iteration.
How do I check if the agent is currently paused?
Monitor the StatusEnum value emitted through the onData callback or the global store. When paused, the agent sets data.status = StatusEnum.PAUSE (defined in packages/ui-tars/shared/src/types/agent.ts). You can also check agent.isPaused directly on the GUIAgent instance if you have reference to it.
Can I pause the agent from a custom extension without using the UI?
Yes. Obtain the active agent instance via GUIAgentManager.getInstance().getAgent(), verify it is an instance of GUIAgent, and call agent.pause(). This works from any Node.js context in the main process where the SDK is available.
What is the difference between pause() and stop()?
pause() temporarily suspends the execution loop using a promise-based waiter, allowing you to resume later with resume(). stop() sets the isStopped flag (lines 111-113 in GUIAgent.ts) and breaks the loop entirely, requiring you to instantiate and start a new GUIAgent to resume automation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →