# How UI-TARS Handles User‑Initiated Stops During Agent Execution

> Learn how UI-TARS desktop handles user initiated stops. The agent sets a stopped flag in AgentContext to break the execution loop and emit a TASK_CANCEL event.

- Repository: [Bytedance Inc./UI-TARS-desktop](https://github.com/bytedance/UI-TARS-desktop)
- Tags: internals
- Published: 2026-05-10

---

**The UI‑TARS agent handles user‑initiated stops by setting a `stopped` flag in the shared `AgentContext`, which the `Executor` checks at the start of every iteration to break the main loop and emit a `TASK_CANCEL` event.**

When building autonomous browser agents in the `bytedance/UI-TARS-desktop` repository, allowing users to interrupt a running task is critical for safety and control. The system implements a cooperative cancellation pattern where a shared context stores the stop state, and the execution pipeline polls this flag to halt gracefully without orphaning browser sessions.

## Where the Stop Flag Is Stored

### The AgentContext Class

The source of truth for a user‑initiated stop lives in the `AgentContext` class defined in [`packages/agent-infra/browser-use/src/agent/types.ts`](https://github.com/bytedance/UI-TARS-desktop/blob/main/packages/agent-infra/browser-use/src/agent/types.ts). This shared context object carries a `stopped` boolean property that defaults to **false** and is set to **true** when the user requests cancellation.

```ts
// packages/agent-infra/browser-use/src/agent/types.ts
export class AgentContext {
  // ...
  stopped: boolean;
  // ...

  async stop() {
    this.stopped = true;                 // ← set by the stop request
  }
}

```

Any component with access to this context can invoke `stop()` to signal that the agent should cease operations. This design centralizes state management, ensuring that all executor and agent subcomponents read from the same flag.

## How the Executor Checks for User‑Initiated Stops

### The shouldStop() Guard

Before every step, the `Executor` calls `shouldStop()` to determine whether the run should continue. This private method returns **true** immediately when `context.stopped` is detected, preventing any further logic from executing.

```ts
// packages/agent-infra/browser-use/src/agent/executor.ts
private async shouldStop(): Promise<boolean> {
  if (this.context.stopped) {
    logger.info('Agent stopped');
    return true;                       // ← halts the loop
  }
  // ...
}

```

### Breaking the Main Execution Loop

The main entry point `Executor.execute()` runs a `for` loop that iterates up to `allowedMaxSteps`. At the top of each iteration, it awaits `shouldStop()`, breaking out of the loop instantly when the flag is set.

```ts
// packages/agent-infra/browser-use/src/agent/executor.ts
for (let step = 0; step < allowedMaxSteps; step++) {
  // ...
  if (await this.shouldStop()) {
    break;                             // ← stop the task
  }
  // ...
}

```

This polling strategy guarantees that the agent responds to a user‑initiated stop within a single iteration, typically after the current browser action completes but before the next LLM call is issued.

## Agent‑Level Stop Guards

Individual agents also respect the stop flag to avoid performing redundant work. The system embeds checks at multiple layers of the call stack.

### Navigation Step Validation

Before attempting to navigate to a new URL, the executor verifies that the task has not been paused or stopped:

```ts
// packages/agent-infra/browser-use/src/agent/executor.ts
if (context.paused || context.stopped) {
  return false;                        // ← abort navigation
}

```

### Navigator Agent Protection

The `Navigator` agent—responsible for determining the next browser action—performs an early exit when it detects a stop request:

```ts
// packages/agent-infra/browser-use/src/agent/agents/navigator.ts
if (this.context.paused || this.context.stopped) {
  return { result: null, error: null }; // ← early exit
}

```

Other specialized agents, such as the `Planner` and `Validator`, implement identical guards. This ensures that once `stop()` is invoked, **no further LLM calls or browser interactions are scheduled**.

## API Surface for Canceling Execution

Three primary entry points allow users or internal components to trigger a stop:

- **`Executor.cancel()`** – Located in [`packages/agent-infra/browser-use/src/agent/executor.ts`](https://github.com/bytedance/UI-TARS-desktop/blob/main/packages/agent-infra/browser-use/src/agent/executor.ts), this method simply forwards the call to `context.stop()`, setting the boolean flag that the execution loop will pick up on the next iteration.
- **`GUIAgent.stop()`** – Exposed in [`packages/ui-tars/sdk/src/GUIAgent.ts`](https://github.com/bytedance/UI-TARS-desktop/blob/main/packages/ui-tars/sdk/src/GUIAgent.ts), this public SDK method delegates to the underlying executor’s `cancel()`. It is the standard mechanism for UI buttons or keyboard shortcuts that let users abort a task.
- **Direct `context.stop()`** – Any internal component holding a reference to the shared `AgentContext` can call this method directly, making the cancellation system extensible for programmatic timeouts or error‑handling scenarios.

## What Happens When a Stop Is Requested

Once the `stopped` flag is raised, the following sequence occurs:

1. **Immediate Effect** – The current iteration finishes if it is already mid‑step, but the loop exits before the next step begins.
2. **Event Emission** – The executor emits a `TASK_CANCEL` event via the context’s event emitter to notify listeners (e.g., the UI layer) that the agent has halted:

```ts
this.context.emitEvent(
  Actors.SYSTEM,
  ExecutionState.TASK_CANCEL,
  'Task cancelled',
  browserState,
);

```

3. **Cleanup** – After exiting the loop, `Executor` may still run `cleanup()` to close browser pages and release resources, but no further actions are scheduled or executed.

## Summary

- **Centralized State** – The `AgentContext.stopped` boolean in [`packages/agent-infra/browser-use/src/agent/types.ts`](https://github.com/bytedance/UI-TARS-desktop/blob/main/packages/agent-infra/browser-use/src/agent/types.ts) acts as the single source of truth for cancellation.
- **Polling Pattern** – `Executor.shouldStop()` checks the flag at the start of every loop iteration in [`packages/agent-infra/browser-use/src/agent/executor.ts`](https://github.com/bytedance/UI-TARS-desktop/blob/main/packages/agent-infra/browser-use/src/agent/executor.ts).
- **Layered Guards** – Individual agents like `Navigator` verify `context.stopped` before performing actions, preventing stray LLM or browser calls.
- **Public API** – **`GUIAgent.stop()`** provides the user‑facing hook, routing to **`Executor.cancel()`** to set the stop flag.
- **Graceful Exit** – Stopping emits a `TASK_CANCEL` event and allows cleanup code to run, ensuring browser resources are released properly.

## Frequently Asked Questions

### How quickly does the UI-TARS agent respond to a user-initiated stop?

The agent responds at the boundary of the current step. The `Executor.shouldStop()` method is checked at the top of each iteration in [`packages/agent-infra/browser-use/src/agent/executor.ts`](https://github.com/bytedance/UI-TARS-desktop/blob/main/packages/agent-infra/browser-use/src/agent/executor.ts), so the loop breaks immediately after the current browser action and LLM call complete, but before the next step begins.

### What is the difference between Executor.cancel() and AgentContext.stop()?

**`AgentContext.stop()`** is the low‑level method that sets the `stopped` boolean flag in the shared state. **`Executor.cancel()`** is a convenience wrapper that calls `this.context.stop()` and is exposed on the executor instance. **`GUIAgent.stop()`** in turn calls `Executor.cancel()`, creating a clean chain from the UI to the core state.

### Does stopping the agent close the browser immediately?

Stopping does not forcibly terminate the browser mid‑action. Instead, the executor exits its main loop and then runs optional `cleanup()` logic, which gracefully closes pages and releases resources. This prevents orphaning browser processes while ensuring no new actions are issued after the stop request.

### Can the agent stop itself, or can only the user initiate a stop?

While designed for user‑initiated cancellation, any component with access to the `AgentContext` can call `context.stop()`. This allows the system to support programmatic stops, such as timeouts or error‑recovery routines, without requiring user interaction.