> ## Documentation Index
> Fetch the complete documentation index at: https://modal-computer-use.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Hand a desktop to a Modal Function

> Borrow an existing desktop for one deployed Modal Function trajectory.

Use a session handoff when a deployed Modal Function runs the complete screenshot, model, and action loop. The owner process keeps lifecycle ownership of the desktop.

## Understand the ownership pattern

1. Your application creates the desktop.
2. The owner calls `session_handle()`.
3. The owner passes the handle to a deployed Modal Function.
4. The Function enters one `borrow_async()` context for the complete trajectory.
5. The Function detaches when the borrow ends.
6. The owner terminates the desktop after the Function reaches a terminal result.

The handle contains routing identity. It is not a bearer credential. Do not log or publish it.

## Define the deployed Function

```python theme={"system"}
import modal

from modal_computer_use import ComputerSessionHandle

function_region = "us-west"
app = modal.App("computer-use-function-handoff")
image = modal.Image.debian_slim().pip_install("modal-computer-use[modal]")


@app.function(
    image=image,
    region=function_region,
    retries=0,
    min_containers=0,
    max_containers=4,
    timeout=900,
)
async def run_trajectory(
    handle: ComputerSessionHandle,
    task: str,
    run_id: str,
) -> dict[str, str]:
    async with handle.borrow_async(
        run_id=run_id,
        function_region=function_region,
    ) as computer:
        for _ in range(3):
            screenshot = await computer.screenshots.full()
            action = await application_model_call(task, screenshot)
            await computer.actions.run([action])
    return {"status": "succeeded"}
```

Replace `application_model_call()` with your application code. Use an async provider client so the Function event loop remains available.

Set the same explicit region on the desktop configuration and the Function. The borrow checks `function_region` against the requested desktop region.

## Deploy the Function

```bash theme={"system"}
uv run modal deploy path/to/handoff_app.py
```

The deployed Function image must include `modal-computer-use[modal]`. The Function must have access to the Modal environment that owns the Sandbox.

## Invoke the Function from the owner

```python theme={"system"}
import uuid

import modal

from modal_computer_use import ComputerConfig, ComputerSandbox

function_region = "us-west"
app_name = "computer-use-function-handoff"
deployed = modal.Function.from_name(app_name, "run_trajectory")

config = ComputerConfig(
    ingress="attested-tunnel",
    runtime={
        "modal_environment": "main",
        "modal_region": function_region,
    },
)

with ComputerSandbox.create(config=config, app_name=app_name) as owner:
    handle = owner.session_handle()
    run_id = f"trajectory_{uuid.uuid4().hex}"
    result = deployed.remote(handle, "replace with your task", run_id)
    print(result["status"])
```

Use one new `run_id` for each trajectory. Do not reuse a durably sealed run ID.

## Keep one trajectory per desktop

The borrow takes an exclusive daemon trajectory lease. Concurrent work must use separate desktop handles.

The Function leaves the desktop running when the borrow ends. Only the original owner terminates it.

<Warning>
  A cancelled Function invocation does not reverse desktop effects that already occurred. Your application must reconcile an ambiguous result before it starts a new trajectory.
</Warning>

Use the [session handoff example](https://github.com/ashtonchew/modal-computer-use/blob/main/examples/modal_function_session_handoff.py) when a Modal Function needs one complete stateful trajectory. It shows dispatch, cancellation, and observation after result loss. The owner must reconcile an ambiguous outcome before it starts another trajectory.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.